{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "settings-form",
  "type": "registry:block",
  "title": "Settings form",
  "description": "A settings panel of real form controls — text fields, a searchable select, a radio group, a slider and a switch — that reads its own fields on submit, so none of it needs state.",
  "author": "Box Kite <https://www.box-kite.dev>",
  "categories": [
    "form",
    "settings"
  ],
  "dependencies": [
    "@box-kite/react"
  ],
  "files": [
    {
      "path": "registry/blocks/settings-form/settings-form.tsx",
      "content": "'use client';\nimport Button from '@box-kite/react/components/button';\nimport Dropdown from '@box-kite/react/components/dropdown';\nimport Flex from '@box-kite/react/components/flex';\nimport Form from '@box-kite/react/components/form';\nimport RadioGroup from '@box-kite/react/components/radioGroup';\nimport { H2, Label, P, Span } from '@box-kite/react/components/semantics';\nimport Slider from '@box-kite/react/components/slider';\nimport Switch from '@box-kite/react/components/switch';\nimport Textarea from '@box-kite/react/components/textarea';\nimport Textbox from '@box-kite/react/components/textbox';\nimport { toast } from '@box-kite/react/components/toaster';\nimport { type ReactNode } from 'react';\n\n/**\n * A settings panel: text fields, a searchable select, a radio group, a slider and a switch, each a real\n * form control. `Form` reads its own fields when it submits, so none of this needs state — `values` is\n * built from the elements carrying a `name`.\n *\n * `<Toaster />` has to be mounted once near the root of the app for the confirmation to show.\n */\nexport interface Settings {\n  name: string;\n  email: string;\n  bio: string;\n  timezone: string;\n  theme: string;\n  density: string;\n  notify: boolean;\n}\n\nconst TIMEZONES = ['Europe/Chisinau', 'Europe/London', 'America/New_York', 'Asia/Tokyo'];\n\n/**\n * A text field and its name. The control sits *inside* the `<label>`, so the two are associated with no\n * `htmlFor`/`id` pair to keep in step. `Dropdown`, `RadioGroup` and `Switch` draw their own label from a\n * `label` prop, so they are written bare below.\n */\nfunction Field({ label, hint, children }: { label: string; hint?: string; children: ReactNode }) {\n  return (\n    <Label display=\"flex\" d=\"column\" gap={2} width=\"fit\">\n      <Span fontSize={14} fontWeight={500}>\n        {label}\n      </Span>\n      {children}\n      {hint && (\n        <Span fontSize={13} color=\"slate-500\" theme={{ dark: { color: 'slate-400' } }}>\n          {hint}\n        </Span>\n      )}\n    </Label>\n  );\n}\n\n/**\n * A `Slider` is the exception: its `label` is the thumb's accessible name and draws nothing, and a\n * `<label>` cannot wrap it because there is no form control inside to attach to. So the caption is an\n * element with an id and `labelledBy` points at it — one name, read and seen.\n */\nconst DENSITY_LABEL = 'settings-density-label';\n\nexport default function SettingsForm() {\n  return (\n    <Form<Settings>\n      p={6}\n      b={1}\n      borderRadius={3}\n      borderColor=\"slate-200\"\n      bgColor=\"white\"\n      theme={{ dark: { borderColor: 'slate-800', bgColor: 'slate-900' } }}\n      onSubmit={(values) => toast.success('Settings saved', { description: `${values.name || 'Your profile'} is up to date.` })}\n    >\n      <H2 fontSize={20} fontWeight={600}>\n        Settings\n      </H2>\n      <P mt={1} mb={5} fontSize={14} color=\"slate-600\" theme={{ dark: { color: 'slate-400' } }}>\n        Every control here is a native element with its name attached, so the keyboard, the focus ring and the screen reader are the\n        platform&apos;s.\n      </P>\n\n      <Flex d=\"column\" gap={5}>\n        <Flex d=\"column\" gap={5} md={{ d: 'row' }}>\n          <Field label=\"Display name\">\n            <Textbox name=\"name\" defaultValue=\"Ada Lovelace\" width=\"fit\" />\n          </Field>\n          <Field label=\"Email\">\n            <Textbox name=\"email\" type=\"email\" defaultValue=\"ada@example.com\" width=\"fit\" />\n          </Field>\n        </Flex>\n\n        <Field label=\"About\" hint=\"Shown on your public profile.\">\n          <Textarea name=\"bio\" rows={3} placeholder=\"A sentence or two.\" width=\"fit\" />\n        </Field>\n\n        <Dropdown<string> name=\"timezone\" label=\"Time zone\" defaultValue={TIMEZONES[0]} isSearchable searchPlaceholder=\"Search zones…\">\n          {TIMEZONES.map((zone) => (\n            <Dropdown.Item key={zone} value={zone}>\n              {zone}\n            </Dropdown.Item>\n          ))}\n        </Dropdown>\n\n        <RadioGroup label=\"Theme\" name=\"theme\" defaultValue=\"system\" orientation=\"horizontal\">\n          <RadioGroup.Item value=\"system\" label=\"Follow the system\" />\n          <RadioGroup.Item value=\"light\" label=\"Light\" />\n          <RadioGroup.Item value=\"dark\" label=\"Dark\" />\n        </RadioGroup>\n\n        <Flex d=\"column\" gap={2}>\n          <Span id={DENSITY_LABEL} fontSize={14} fontWeight={500}>\n            Row density\n          </Span>\n          <Slider name=\"density\" labelledBy={DENSITY_LABEL} defaultValue={44} min={32} max={64} step={4} format={(value) => `${value}px`} />\n          <Span fontSize={13} color=\"slate-500\" theme={{ dark: { color: 'slate-400' } }}>\n            How tall a row is in tables across the app.\n          </Span>\n        </Flex>\n\n        <Switch name=\"notify\" label=\"Email me when something needs a decision\" defaultChecked />\n      </Flex>\n\n      <Flex mt={6} gap={3} jc=\"end\">\n        <Button variant=\"secondary\" type=\"reset\">\n          Reset\n        </Button>\n        <Button type=\"submit\">Save changes</Button>\n      </Flex>\n    </Form>\n  );\n}\n",
      "type": "registry:block",
      "target": "@components/box-kite/settings-form.tsx"
    }
  ],
  "docs": "Render <SettingsForm /> and replace the `onSubmit` handler. It calls toast(), so mount <Toaster /> once near the root of the app: import Toaster from \"@box-kite/react/components/toaster\"."
}
