{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "search-toolbar",
  "title": "Search toolbar",
  "description": "A controlled search field with clear and focus restoration, loading state, result announcement and action slots.",
  "dependencies": [
    "cn@^0.4.0",
    "lucide-react@^1.46.0"
  ],
  "registryDependencies": [
    "Familis-co/registry/field",
    "Familis-co/registry/input-group",
    "Familis-co/registry/spinner"
  ],
  "files": [
    {
      "path": "registry/familis/blocks/search-toolbar/search-toolbar.tsx",
      "content": "\"use client\"\n\nimport { useId, useRef, type ComponentProps, type ReactNode } from \"react\"\nimport { SearchIcon, XIcon } from \"lucide-react\"\nimport { cn } from \"cn\"\nimport { Field, FieldGroup, FieldLabel } from \"@/registry/familis/ui/field\"\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupInput,\n} from \"@/registry/familis/ui/input-group\"\nimport { Spinner } from \"@/registry/familis/ui/spinner\"\n\nexport interface SearchToolbarProps extends Omit<ComponentProps<\"div\">, \"children\" | \"onChange\"> {\n  query: string\n  onQueryChange: (query: string) => void\n  label?: string\n  placeholder?: string\n  clearLabel?: string\n  resultLabel?: string\n  loading?: boolean\n  disabled?: boolean\n  actions?: ReactNode\n}\n\n/** Controlled search with a clear action. Filtering, debouncing and result labels belong to the caller. */\nexport function SearchToolbar({\n  query,\n  onQueryChange,\n  label = \"Search\",\n  placeholder = \"Search…\",\n  clearLabel = \"Clear search\",\n  resultLabel,\n  loading = false,\n  disabled = false,\n  actions,\n  className,\n  ...props\n}: SearchToolbarProps) {\n  const id = useId()\n  const inputRef = useRef<HTMLInputElement>(null)\n\n  return (\n    <div className={cn(\"flex w-full flex-wrap items-center gap-3\", className)} {...props}>\n      <FieldGroup className=\"min-w-0 flex-1 basis-48\">\n        <Field data-disabled={disabled || undefined}>\n          <FieldLabel htmlFor={id} className=\"sr-only\">\n            {label}\n          </FieldLabel>\n          <InputGroup>\n            <InputGroupInput\n              ref={inputRef}\n              id={id}\n              type=\"search\"\n              value={query}\n              placeholder={placeholder}\n              disabled={disabled}\n              aria-describedby={resultLabel !== undefined ? `${id}-results` : undefined}\n              aria-busy={loading}\n              onChange={(event) => onQueryChange(event.currentTarget.value)}\n            />\n            <InputGroupAddon>\n              {loading ? <Spinner aria-hidden=\"true\" /> : <SearchIcon aria-hidden=\"true\" />}\n            </InputGroupAddon>\n            {query.length > 0 && (\n              <InputGroupAddon align=\"inline-end\">\n                <InputGroupButton\n                  size=\"icon-xs\"\n                  aria-label={clearLabel}\n                  disabled={disabled}\n                  onClick={() => {\n                    onQueryChange(\"\")\n                    inputRef.current?.focus()\n                  }}\n                >\n                  <XIcon aria-hidden=\"true\" />\n                </InputGroupButton>\n              </InputGroupAddon>\n            )}\n          </InputGroup>\n        </Field>\n      </FieldGroup>\n      {actions}\n      {resultLabel !== undefined && (\n        <p id={`${id}-results`} role=\"status\" className=\"w-full text-sm text-muted-foreground\">\n          {resultLabel}\n        </p>\n      )}\n    </div>\n  )\n}\n",
      "type": "registry:component"
    }
  ],
  "meta": {
    "base": "base",
    "storybookId": "ui-blocks-search-toolbar--default"
  },
  "type": "registry:block"
}