mirror of
https://github.com/hanzoai/shadcn.git
synced 2026-11-03 03:18:52 +00:00
Upstream sync from shadcn-ui/ui (1686 commits since last merge base). Key changes: - packages/shadcn upgraded to 4.1.0 (CLI font transformers, scaffold from github, chart colors) - apps/v4 replaces apps/www as the primary docs/registry app - Font system: 25+ heading font registries, font markers utility, @supports override - Chart improvements: recharts v3 compat, radial chart fixes - templates/ updated with monorepo variants (astro, vite, react-router, start) - skills/shadcn AI skill definitions added - deprecated/ removed (auth, auth-firebase, cli, www) Preserved Hanzo-only paths: app/, pkg/, demo/, docs/, template/next/
24 lines
5.7 KiB
JSON
24 lines
5.7 KiB
JSON
{
|
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
"name": "form-rhf-array",
|
|
"dependencies": [
|
|
"react-hook-form",
|
|
"@hookform/resolvers",
|
|
"zod"
|
|
],
|
|
"registryDependencies": [
|
|
"field",
|
|
"input",
|
|
"input-group",
|
|
"button",
|
|
"card"
|
|
],
|
|
"files": [
|
|
{
|
|
"path": "registry/new-york-v4/examples/form-rhf-array.tsx",
|
|
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { zodResolver } from \"@hookform/resolvers/zod\"\nimport { XIcon } from \"lucide-react\"\nimport { Controller, useFieldArray, useForm } from \"react-hook-form\"\nimport { toast } from \"sonner\"\nimport * as z from \"zod\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/registry/new-york-v4/ui/card\"\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLegend,\n FieldSet,\n} from \"@/registry/new-york-v4/ui/field\"\nimport {\n InputGroup,\n InputGroupAddon,\n InputGroupButton,\n InputGroupInput,\n} from \"@/registry/new-york-v4/ui/input-group\"\n\nconst formSchema = z.object({\n emails: z\n .array(\n z.object({\n address: z.string().email(\"Enter a valid email address.\"),\n })\n )\n .min(1, \"Add at least one email address.\")\n .max(5, \"You can add up to 5 email addresses.\"),\n})\n\nexport default function FormRhfArray() {\n const form = useForm<z.infer<typeof formSchema>>({\n resolver: zodResolver(formSchema),\n defaultValues: {\n emails: [{ address: \"\" }, { address: \"\" }],\n },\n })\n\n const { fields, append, remove } = useFieldArray({\n control: form.control,\n name: \"emails\",\n })\n\n function onSubmit(data: z.infer<typeof formSchema>) {\n toast(\"You submitted the following values:\", {\n description: (\n <pre className=\"mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground\">\n <code>{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n position: \"bottom-right\",\n classNames: {\n content: \"flex flex-col gap-2\",\n },\n style: {\n \"--border-radius\": \"calc(var(--radius) + 4px)\",\n } as React.CSSProperties,\n })\n }\n\n return (\n <Card className=\"w-full sm:max-w-md\">\n <CardHeader className=\"border-b\">\n <CardTitle>Contact Emails</CardTitle>\n <CardDescription>Manage your contact email addresses.</CardDescription>\n </CardHeader>\n <CardContent>\n <form id=\"form-rhf-array\" onSubmit={form.handleSubmit(onSubmit)}>\n <FieldSet className=\"gap-4\">\n <FieldLegend variant=\"label\">Email Addresses</FieldLegend>\n <FieldDescription>\n Add up to 5 email addresses where we can contact you.\n </FieldDescription>\n <FieldGroup className=\"gap-4\">\n {fields.map((field, index) => (\n <Controller\n key={field.id}\n name={`emails.${index}.address`}\n control={form.control}\n render={({ field: controllerField, fieldState }) => (\n <Field\n orientation=\"horizontal\"\n data-invalid={fieldState.invalid}\n >\n <FieldContent>\n <InputGroup>\n <InputGroupInput\n {...controllerField}\n id={`form-rhf-array-email-${index}`}\n aria-invalid={fieldState.invalid}\n placeholder=\"[email protected]\"\n type=\"email\"\n autoComplete=\"email\"\n />\n {fields.length > 1 && (\n <InputGroupAddon align=\"inline-end\">\n <InputGroupButton\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-xs\"\n onClick={() => remove(index)}\n aria-label={`Remove email ${index + 1}`}\n >\n <XIcon />\n </InputGroupButton>\n </InputGroupAddon>\n )}\n </InputGroup>\n {fieldState.invalid && (\n <FieldError errors={[fieldState.error]} />\n )}\n </FieldContent>\n </Field>\n )}\n />\n ))}\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => append({ address: \"\" })}\n disabled={fields.length >= 5}\n >\n Add Email Address\n </Button>\n </FieldGroup>\n {form.formState.errors.emails?.root && (\n <FieldError errors={[form.formState.errors.emails.root]} />\n )}\n </FieldSet>\n </form>\n </CardContent>\n <CardFooter className=\"border-t\">\n <Field orientation=\"horizontal\">\n <Button type=\"button\" variant=\"outline\" onClick={() => form.reset()}>\n Reset\n </Button>\n <Button type=\"submit\" form=\"form-rhf-array\">\n Save\n </Button>\n </Field>\n </CardFooter>\n </Card>\n )\n}\n",
|
|
"type": "registry:example"
|
|
}
|
|
],
|
|
"type": "registry:example"
|
|
} |