astralyx-ui 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astralyx-ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "295 accessible React components you copy into your repo, with a CLI and registry that resolve what each one needs.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -51,12 +51,13 @@
|
|
|
51
51
|
"sideEffects": false,
|
|
52
52
|
"scripts": {
|
|
53
53
|
"dev": "vite",
|
|
54
|
-
"build": "tsc -b && vite build && node scripts/build-seo.mjs",
|
|
54
|
+
"build": "node scripts/build-props.mjs && tsc -b && vite build && node scripts/build-seo.mjs",
|
|
55
55
|
"lint": "oxlint",
|
|
56
56
|
"preview": "vite preview",
|
|
57
|
+
"build:props": "node scripts/build-props.mjs",
|
|
57
58
|
"build:registry": "node scripts/build-registry.mjs",
|
|
58
59
|
"build:cli": "tsc -p tsconfig.cli.json",
|
|
59
|
-
"build:package": "npm run build:registry && npm run build:cli",
|
|
60
|
+
"build:package": "npm run build:props && npm run build:registry && npm run build:cli",
|
|
60
61
|
"prepublishOnly": "npm run build:package",
|
|
61
62
|
"cli": "node bin/index.js"
|
|
62
63
|
},
|
package/registry/index.json
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
{
|
|
21
21
|
"path": "components/ui/login-form.tsx",
|
|
22
22
|
"type": "registry:ui",
|
|
23
|
-
"content": "import { useState, type ComponentProps, type FormEvent, type ReactNode } from 'react'\nimport { TriangleAlert } from 'lucide-react'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport { PasswordInput } from '@/components/ui/password-input'\nimport { Separator } from '@/components/ui/separator'\nimport { radius, surface } from '@/lib/styles'\nimport { cn } from '@/lib/utils'\n\n/**\n * Email and password sign-in, with providers.\n *\n * The error is deliberately generic and lives above the form, not on a field.\n * \"No account with that email\" tells an attacker which addresses are\n * registered; a single \"those details do not match\" on the form as a whole\n * leaks nothing and is what every audited implementation does.\n *\n * Field names and autocomplete tokens are the real ones — `username` and\n * `current-password` — because password managers key off them, and a login form\n * they cannot fill is one people work around by choosing weaker passwords.\n */\nfunction LoginForm({\n onSubmit,\n onForgot,\n providers,\n error,\n loading = false,\n title = 'Sign in',\n description,\n footer,\n emailLabel = 'Email',\n passwordLabel = 'Password',\n forgotLabel = 'Forgot password?',\n submitLabel = 'Sign in',\n dividerLabel = 'or',\n className,\n ...props\n}: Omit<ComponentProps<'form'>, 'onSubmit' | 'title'> & {\n onSubmit?: (credentials: { email: string; password: string }) => void\n onForgot?: () => void\n /** Buttons for SSO or OAuth. */\n providers?: ReactNode\n error?: ReactNode\n loading?: boolean\n title?: ReactNode\n description?: ReactNode\n footer?: ReactNode\n emailLabel?: ReactNode\n passwordLabel?: ReactNode\n forgotLabel?: ReactNode\n submitLabel?: ReactNode\n /** Separates the provider buttons from the fields. */\n dividerLabel?: ReactNode\n}) {\n const [email, setEmail] = useState('')\n const [password, setPassword] = useState('')\n\n function submit(event: FormEvent) {\n event.preventDefault()\n onSubmit?.({ email, password })\n }\n\n return (\n <form\n data-slot=\"login-form\"\n onSubmit={submit}\n className={cn(surface, radius.surface, 'flex w-full flex-col gap-4 p-6', className)}\n {...props}\n >\n <div className=\"flex flex-col gap-1\">\n <
|
|
23
|
+
"content": "import { useId, useState, type ComponentProps, type FormEvent, type ReactNode } from 'react'\nimport { TriangleAlert } from 'lucide-react'\nimport { Button } from '@/components/ui/button'\nimport { Input } from '@/components/ui/input'\nimport { Label } from '@/components/ui/label'\nimport { PasswordInput } from '@/components/ui/password-input'\nimport { Separator } from '@/components/ui/separator'\nimport { radius, surface } from '@/lib/styles'\nimport { cn } from '@/lib/utils'\n\n/**\n * Email and password sign-in, with providers.\n *\n * The error is deliberately generic and lives above the form, not on a field.\n * \"No account with that email\" tells an attacker which addresses are\n * registered; a single \"those details do not match\" on the form as a whole\n * leaks nothing and is what every audited implementation does.\n *\n * Field names and autocomplete tokens are the real ones — `username` and\n * `current-password` — because password managers key off them, and a login form\n * they cannot fill is one people work around by choosing weaker passwords.\n */\nfunction LoginForm({\n onSubmit,\n onForgot,\n providers,\n error,\n loading = false,\n title = 'Sign in',\n description,\n footer,\n emailLabel = 'Email',\n passwordLabel = 'Password',\n forgotLabel = 'Forgot password?',\n submitLabel = 'Sign in',\n dividerLabel = 'or',\n titleAs: Title = 'h2',\n className,\n ...props\n}: Omit<ComponentProps<'form'>, 'onSubmit' | 'title'> & {\n onSubmit?: (credentials: { email: string; password: string }) => void\n onForgot?: () => void\n /** Buttons for SSO or OAuth. */\n providers?: ReactNode\n error?: ReactNode\n loading?: boolean\n title?: ReactNode\n description?: ReactNode\n footer?: ReactNode\n emailLabel?: ReactNode\n passwordLabel?: ReactNode\n forgotLabel?: ReactNode\n submitLabel?: ReactNode\n /** Separates the provider buttons from the fields. */\n dividerLabel?: ReactNode\n /**\n * Element for the title.\n *\n * `h2` by default — a form sits inside a page that already owns the `h1`,\n * and a card-level component emitting one hijacks the document outline. Set\n * `'p'` when the surrounding page already announces the same thing, or `'h1'`\n * on a dedicated sign-in page where this really is the heading.\n */\n titleAs?: 'h1' | 'h2' | 'h3' | 'p'\n}) {\n const [email, setEmail] = useState('')\n const [password, setPassword] = useState('')\n\n // Namespaced per instance. Hardcoded ids collide the moment a page shows two\n // of these — a sign-in beside a sign-up — and a colliding `htmlFor` focuses\n // the *first* matching field, so the second form's labels point at the wrong\n // inputs.\n const scope = useId()\n const emailId = `${scope}-email`\n const passwordId = `${scope}-password`\n\n function submit(event: FormEvent) {\n event.preventDefault()\n onSubmit?.({ email, password })\n }\n\n return (\n <form\n data-slot=\"login-form\"\n onSubmit={submit}\n className={cn(surface, radius.surface, 'flex w-full flex-col gap-4 p-6', className)}\n {...props}\n >\n <div className=\"flex flex-col gap-1\">\n <Title className=\"text-lg font-semibold tracking-tight\">{title}</Title>\n {description && <p className=\"text-muted-foreground text-sm\">{description}</p>}\n </div>\n\n {/* One generic message on the form, never per field: naming the wrong\n field tells an attacker which addresses are registered. */}\n {error && (\n <p\n role=\"alert\"\n className={cn(\n 'flex items-start gap-2 p-3 text-sm text-[var(--destructive-soft-foreground)]',\n 'bg-[color-mix(in_oklab,var(--destructive),transparent_92%)]',\n radius.control,\n )}\n >\n <TriangleAlert className=\"mt-0.5 size-4 shrink-0\" aria-hidden=\"true\" />\n {error}\n </p>\n )}\n\n {providers && (\n <>\n <div className=\"flex flex-col gap-2\">{providers}</div>\n <div className=\"flex items-center gap-3\">\n <Separator className=\"flex-1\" />\n <span className=\"text-muted-foreground text-xs\">{dividerLabel}</span>\n <Separator className=\"flex-1\" />\n </div>\n </>\n )}\n\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={emailId}>{emailLabel}</Label>\n <Input\n id={emailId}\n name=\"username\"\n type=\"email\"\n autoComplete=\"username\"\n required\n value={email}\n onChange={(event) => setEmail(event.target.value)}\n />\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"flex items-baseline justify-between gap-2\">\n <Label htmlFor={passwordId}>{passwordLabel}</Label>\n {onForgot && (\n <button\n type=\"button\"\n onClick={onForgot}\n className=\"text-muted-foreground hover:text-foreground text-xs underline-offset-4 hover:underline\"\n >\n {forgotLabel}\n </button>\n )}\n </div>\n <PasswordInput\n id={passwordId}\n name=\"password\"\n autoComplete=\"current-password\"\n required\n value={password}\n onChange={(event) => setPassword(event.target.value)}\n />\n </div>\n\n <Button type=\"submit\" disabled={loading}>\n {loading ? 'Signing in…' : 'Sign in'}\n </Button>\n\n {footer && <div className=\"text-muted-foreground text-center text-sm\">{footer}</div>}\n </form>\n )\n}\n\nexport { LoginForm }\n"
|
|
24
24
|
}
|
|
25
25
|
]
|
|
26
26
|
}
|