grain.social is a photo sharing platform built on atproto.
1import { cn } from "@bigmoves/bff/components";
2import type { JSX } from "preact";
3import { Button } from "./Button.tsx";
4import { Input } from "./Input.tsx";
5
6export type LoginProps =
7 & JSX.HTMLAttributes<HTMLFormElement>
8 & Readonly<{
9 inputPlaceholder?: string;
10 submitText?: string;
11 infoText?: string;
12 error?: string;
13 errorClass?: string;
14 infoClass?: string;
15 }>;
16
17export function Login(
18 {
19 inputPlaceholder = "Handle (e.g., user.bsky.social)",
20 submitText = "Login with Bluesky",
21 infoText = "",
22 error,
23 errorClass,
24 infoClass,
25 ...rest
26 }: LoginProps,
27): JSX.Element {
28 return (
29 <form
30 id="login-form"
31 hx-post="/oauth/login"
32 hx-target="#login-form"
33 hx-swap="outerHTML"
34 {...rest}
35 class={cn(
36 "tw:mx-4 tw:sm:mx-0 tw:w-full tw:sm:max-w-[300px] tw:space-y-2",
37 rest.class,
38 )}
39 >
40 <div>
41 <label htmlFor="handle" class="tw:sr-only">
42 Handle
43 </label>
44 <Input
45 id="handle"
46 class="bg-white text-zinc-900"
47 placeholder={inputPlaceholder}
48 name="handle"
49 />
50 </div>
51 <Button
52 variant="primary"
53 id="submit"
54 type="submit"
55 class="tw:w-full"
56 >
57 {submitText}
58 </Button>
59 {infoText && (
60 <div class={cn("tw:text-sm tw:text", infoClass)}>
61 {infoText}
62 </div>
63 )}
64 <div className="tw:h-4">
65 {error
66 ? (
67 <div className={cn("tw:text-sm tw:font-mono", errorClass)}>
68 {error}
69 </div>
70 )
71 : null}
72 </div>
73 </form>
74 );
75}