{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "settings-form",
  "title": "Settings form",
  "description": "Editable workspace settings with validation, cancel, saving, and local feedback. Demo persistence is in memory only.",
  "registryDependencies": [
    "http://127.0.0.1:3017/r/button.json",
    "http://127.0.0.1:3017/r/input.json",
    "http://127.0.0.1:3017/r/field.json",
    "http://127.0.0.1:3017/r/select.json",
    "http://127.0.0.1:3017/r/switch.json",
    "http://127.0.0.1:3017/r/spinner.json"
  ],
  "files": [
    {
      "path": "registry/new-york/blocks/settings-form/settings-form.tsx",
      "content": "'use client';\n\nimport { useId, useRef, useState, type FormEvent } from 'react';\nimport { Button } from '@/registry/new-york/ui/button';\nimport { Input } from '@/registry/new-york/ui/input';\nimport { Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSet } from '@/registry/new-york/ui/field';\nimport { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/registry/new-york/ui/select';\nimport { Switch } from '@/registry/new-york/ui/switch';\nimport { Spinner } from '@/registry/new-york/ui/spinner';\n\nconst workspaces = ['design', 'engineering', 'operations'] as const;\ntype Workspace = typeof workspaces[number];\ntype Settings = { name: string; workspace: Workspace; notifications: boolean };\nconst initial: Settings = { name: '', workspace: 'design', notifications: true };\n\nexport function SettingsForm() {\n  const id = useId();\n  const [settings, setSettings] = useState(initial);\n  const [saved, setSaved] = useState(initial);\n  const [error, setError] = useState('');\n  const [status, setStatus] = useState('');\n  const [saving, setSaving] = useState(false);\n  const savingRef = useRef(false);\n  const nameRef = useRef<HTMLInputElement>(null);\n  const dirty = settings.name !== saved.name || settings.workspace !== saved.workspace || settings.notifications !== saved.notifications;\n\n  function change(patch: Partial<Settings>) {\n    setSettings(current => ({ ...current, ...patch }));\n    setStatus('');\n  }\n\n  async function submit(event: FormEvent<HTMLFormElement>) {\n    event.preventDefault();\n    if (savingRef.current) return;\n    if (settings.name.trim().length < 2) {\n      setError('Enter at least two characters for your display name.');\n      setStatus('');\n      nameRef.current?.focus();\n      return;\n    }\n    setError('');\n    savingRef.current = true;\n    setSaving(true);\n    setStatus('Saving demo settings…');\n    // Local simulation only. Replace with a server request and validate there too.\n    await new Promise(resolve => setTimeout(resolve, 800));\n    const next = { ...settings, name: settings.name.trim() };\n    setSettings(next);\n    setSaved(next);\n    setStatus('Settings saved for this demo session. Nothing was sent to a server.');\n    setSaving(false);\n    savingRef.current = false;\n  }\n\n  return (\n    <form onSubmit={submit} noValidate aria-busy={saving} className=\"flex w-full max-w-lg flex-col gap-6\">\n      <FieldSet disabled={saving}>\n        <FieldLegend>Workspace settings</FieldLegend>\n        <FieldDescription>Manage your profile and preferences. This example saves only in memory.</FieldDescription>\n        <FieldGroup>\n          <Field data-invalid={!!error}>\n            <FieldLabel htmlFor={`${id}-name`}>Display name (required)</FieldLabel>\n            <Input ref={nameRef} id={`${id}-name`} name=\"displayName\" autoComplete=\"nickname\" value={settings.name} onChange={event => { change({ name: event.target.value }); setError(''); }} required minLength={2} aria-invalid={!!error} aria-describedby={`${id}-name-help${error ? ` ${id}-name-error` : ''}`} />\n            <FieldDescription id={`${id}-name-help`}>Use at least two characters.</FieldDescription>\n            <FieldError id={`${id}-name-error`}>{error}</FieldError>\n          </Field>\n          <Field data-disabled={saving}>\n            <FieldLabel htmlFor={`${id}-workspace`}>Default workspace</FieldLabel>\n            <Select name=\"workspace\" value={settings.workspace} disabled={saving} onValueChange={value => { if (workspaces.includes(value as Workspace)) change({ workspace: value as Workspace }); }}>\n              <SelectTrigger id={`${id}-workspace`} aria-describedby={`${id}-workspace-help`}><SelectValue /></SelectTrigger>\n              <SelectContent>\n                <SelectGroup>\n                  <SelectItem value=\"design\">Design</SelectItem>\n                  <SelectItem value=\"engineering\">Engineering</SelectItem>\n                  <SelectItem value=\"operations\">Operations</SelectItem>\n                </SelectGroup>\n              </SelectContent>\n            </Select>\n            <FieldDescription id={`${id}-workspace-help`}>Opened when you sign in.</FieldDescription>\n          </Field>\n          <Field orientation=\"horizontal\" data-disabled={saving}>\n            <Switch id={`${id}-notifications`} name=\"notifications\" disabled={saving} checked={settings.notifications} onCheckedChange={notifications => change({ notifications })} aria-describedby={`${id}-notifications-help`} />\n            <FieldContent>\n              <FieldLabel htmlFor={`${id}-notifications`}>Email notifications</FieldLabel>\n              <FieldDescription id={`${id}-notifications-help`}>Include workspace activity in your email updates.</FieldDescription>\n            </FieldContent>\n          </Field>\n        </FieldGroup>\n      </FieldSet>\n      <div className=\"flex flex-wrap gap-2\">\n        <Button type=\"submit\" disabled={saving || !dirty} aria-busy={saving}>\n          {saving && <Spinner data-icon=\"inline-start\" aria-hidden=\"true\" />}\n          {saving ? 'Saving settings' : 'Save settings'}\n        </Button>\n        <Button type=\"button\" variant=\"outline\" disabled={saving || !dirty} onClick={() => { setSettings(saved); setError(''); setStatus('Unsaved changes discarded.'); }}>Cancel</Button>\n      </div>\n      <p role=\"status\" aria-live=\"polite\" className=\"text-sm text-muted-foreground\">{status || 'Change a setting to enable saving. Reloading resets this demo.'}</p>\n    </form>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/settings-form.tsx"
    }
  ],
  "docs": "Import SettingsForm from your configured components directory. Demo-only in-memory saving; replace the simulated request with your server operation, validate on the server, and handle failures before production use.",
  "type": "registry:block"
}