{
  "name": "autocomplete",
  "type": "registry:ui",
  "title": "Autocomplete",
  "description": "Remote user suggestions and other async results while a user types, with free-text input for search and tagging.",
  "registryDependencies": [
    "@siteplane/base",
    "@siteplane/input",
    "@siteplane/scroll-area"
  ],
  "dependencies": [
    "@base-ui/react@1.5.0",
    "lucide-react@1.18.0"
  ],
  "files": [
    {
      "path": "src/components/ui/autocomplete.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { Autocomplete as AutocompletePrimitive } from \"@base-ui/react/autocomplete\";\nimport { ChevronsUpDownIcon, XIcon } from \"lucide-react\";\nimport type React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  SiteplaneUIPortalScope,\n  useResolvedControlSize,\n} from \"@/components/ui/siteplane-provider\";\nimport { Input } from \"@/components/ui/input\";\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\nimport { surfaceClassNames } from \"@/components/ui/surface\";\n\nexport const Autocomplete: typeof AutocompletePrimitive.Root =\n  AutocompletePrimitive.Root;\n\nexport function AutocompleteInput({\n  className,\n  showTrigger = false,\n  showClear = false,\n  startAddon,\n  size,\n  triggerProps,\n  clearProps,\n  ...props\n}: Omit<AutocompletePrimitive.Input.Props, \"size\"> & {\n  showTrigger?: boolean;\n  showClear?: boolean;\n  startAddon?: React.ReactNode;\n  size?: \"sm\" | \"default\" | \"lg\" | \"xl\" | number;\n  ref?: React.Ref<HTMLInputElement>;\n  triggerProps?: AutocompletePrimitive.Trigger.Props;\n  clearProps?: AutocompletePrimitive.Clear.Props;\n}): React.ReactElement {\n  const visualSize = typeof size === \"number\" ? undefined : size;\n  const nativeSize = typeof size === \"number\" ? size : undefined;\n  const resolvedSize = useResolvedControlSize(visualSize);\n\n  return (\n    <AutocompletePrimitive.InputGroup\n      className=\"relative not-has-[>*.w-full]:w-fit w-full text-foreground has-disabled:opacity-64\"\n      data-slot=\"autocomplete-input-group\"\n    >\n      {startAddon && (\n        <div\n          aria-hidden=\"true\"\n          className=\"pointer-events-none absolute inset-y-0 start-px z-10 flex items-center ps-[calc(--spacing(3)-1px)] opacity-80 has-[+[data-size=sm]]:ps-[calc(--spacing(2.5)-1px)] has-[+[data-size=xl]]:ps-[calc(--spacing(3.5)-1px)] [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:-mx-0.5\"\n          data-slot=\"autocomplete-start-addon\"\n        >\n          {startAddon}\n        </div>\n      )}\n      <AutocompletePrimitive.Input\n        className={cn(\n          startAddon &&\n            \"data-[size=sm]:*:data-[slot=input]:ps-[calc(--spacing(7.5)-1px)] data-[size=lg]:*:data-[slot=input]:ps-[calc(--spacing(9.5)-1px)] data-[size=xl]:*:data-[slot=input]:ps-[calc(--spacing(10.5)-1px)] *:data-[slot=input]:ps-[calc(--spacing(8.5)-1px)] sm:data-[size=sm]:*:data-[slot=input]:ps-[calc(--spacing(7)-1px)] sm:data-[size=lg]:*:data-[slot=input]:ps-[calc(--spacing(9)-1px)] sm:data-[size=xl]:*:data-[slot=input]:ps-[calc(--spacing(10)-1px)] sm:*:data-[slot=input]:ps-[calc(--spacing(8)-1px)]\",\n          resolvedSize === \"sm\"\n            ? \"has-[+[data-slot=autocomplete-trigger],+[data-slot=autocomplete-clear]]:*:data-[slot=input]:pe-6.5\"\n            : resolvedSize === \"xl\"\n              ? \"has-[+[data-slot=autocomplete-trigger],+[data-slot=autocomplete-clear]]:*:data-[slot=input]:pe-8\"\n            : \"has-[+[data-slot=autocomplete-trigger],+[data-slot=autocomplete-clear]]:*:data-[slot=input]:pe-7\",\n          className,\n        )}\n        data-size={resolvedSize}\n        data-slot=\"autocomplete-input\"\n        render={<Input nativeInput size={nativeSize ?? resolvedSize} />}\n        {...props}\n      />\n      {showTrigger && (\n        <AutocompleteTrigger\n          aria-label=\"Show suggestions\"\n          className={cn(\n            \"absolute top-1/2 inline-flex size-8 shrink-0 -translate-y-1/2 cursor-pointer items-center justify-center rounded-md border border-transparent opacity-80 outline-none transition-colors pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:opacity-100 has-[+[data-slot=autocomplete-clear]]:hidden sm:size-7 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n            resolvedSize === \"sm\" ? \"end-0\" : \"end-0.5\",\n            resolvedSize === \"xl\" && \"size-9 sm:size-8\",\n          )}\n          {...triggerProps}\n        >\n          <AutocompletePrimitive.Icon data-slot=\"autocomplete-icon\">\n            <ChevronsUpDownIcon />\n          </AutocompletePrimitive.Icon>\n        </AutocompleteTrigger>\n      )}\n      {showClear && (\n        <AutocompleteClear\n          aria-label=\"Clear input\"\n          className={cn(\n            \"absolute top-1/2 inline-flex size-8 shrink-0 -translate-y-1/2 cursor-pointer items-center justify-center rounded-md border border-transparent opacity-80 outline-none transition-colors pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:opacity-100 has-[+[data-slot=autocomplete-clear]]:hidden sm:size-7 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n            resolvedSize === \"sm\" ? \"end-0\" : \"end-0.5\",\n            resolvedSize === \"xl\" && \"size-9 sm:size-8\",\n          )}\n          {...clearProps}\n        >\n          <XIcon />\n        </AutocompleteClear>\n      )}\n    </AutocompletePrimitive.InputGroup>\n  );\n}\n\nexport function AutocompletePopup({\n  className,\n  children,\n  side = \"bottom\",\n  sideOffset = 4,\n  alignOffset,\n  align = \"start\",\n  anchor,\n  portalProps,\n  ...props\n}: AutocompletePrimitive.Popup.Props & {\n  align?: AutocompletePrimitive.Positioner.Props[\"align\"];\n  sideOffset?: AutocompletePrimitive.Positioner.Props[\"sideOffset\"];\n  alignOffset?: AutocompletePrimitive.Positioner.Props[\"alignOffset\"];\n  side?: AutocompletePrimitive.Positioner.Props[\"side\"];\n  anchor?: AutocompletePrimitive.Positioner.Props[\"anchor\"];\n  portalProps?: AutocompletePrimitive.Portal.Props;\n}): React.ReactElement {\n  return (\n    <AutocompletePrimitive.Portal {...portalProps}>\n      <SiteplaneUIPortalScope>\n        <AutocompletePrimitive.Positioner\n          align={align}\n          alignOffset={alignOffset}\n          anchor={anchor}\n          className=\"z-50 select-none\"\n          data-slot=\"autocomplete-positioner\"\n          side={side}\n          sideOffset={sideOffset}\n        >\n          <AutocompletePrimitive.Popup\n            className={cn(\n              surfaceClassNames.floatingOverlay,\n              \"flex max-h-[min(var(--available-height),23rem)] min-w-(--anchor-width) max-w-(--available-width) origin-(--transform-origin) flex-col rounded-lg text-foreground before:rounded-[inherit]\",\n              className,\n            )}\n            data-slot=\"autocomplete-popup\"\n            {...props}\n          >\n            {children}\n          </AutocompletePrimitive.Popup>\n        </AutocompletePrimitive.Positioner>\n      </SiteplaneUIPortalScope>\n    </AutocompletePrimitive.Portal>\n  );\n}\n\nexport function AutocompleteItem({\n  className,\n  children,\n  ...props\n}: AutocompletePrimitive.Item.Props): React.ReactElement {\n  return (\n    <AutocompletePrimitive.Item\n      className={cn(\n        \"flex min-h-8 cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1 text-base outline-none data-disabled:pointer-events-none data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:opacity-64 sm:min-h-7 sm:text-sm [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n        className,\n      )}\n      data-slot=\"autocomplete-item\"\n      {...props}\n    >\n      {children}\n    </AutocompletePrimitive.Item>\n  );\n}\n\nexport function AutocompleteSeparator({\n  className,\n  ...props\n}: AutocompletePrimitive.Separator.Props): React.ReactElement {\n  return (\n    <AutocompletePrimitive.Separator\n      className={cn(\"mx-2 my-1 h-px bg-border last:hidden\", className)}\n      data-slot=\"autocomplete-separator\"\n      {...props}\n    />\n  );\n}\n\nexport function AutocompleteGroup({\n  className,\n  ...props\n}: AutocompletePrimitive.Group.Props): React.ReactElement {\n  return (\n    <AutocompletePrimitive.Group\n      className={cn(\"[[role=group]+&]:mt-1.5\", className)}\n      data-slot=\"autocomplete-group\"\n      {...props}\n    />\n  );\n}\n\nexport function AutocompleteGroupLabel({\n  className,\n  ...props\n}: AutocompletePrimitive.GroupLabel.Props): React.ReactElement {\n  return (\n    <AutocompletePrimitive.GroupLabel\n      className={cn(\n        \"px-2 py-1.5 font-medium text-muted-foreground text-xs\",\n        className,\n      )}\n      data-slot=\"autocomplete-group-label\"\n      {...props}\n    />\n  );\n}\n\nexport function AutocompleteEmpty({\n  className,\n  ...props\n}: AutocompletePrimitive.Empty.Props): React.ReactElement {\n  return (\n    <AutocompletePrimitive.Empty\n      className={cn(\n        \"not-empty:p-2 text-center text-base text-muted-foreground sm:text-sm\",\n        className,\n      )}\n      data-slot=\"autocomplete-empty\"\n      {...props}\n    />\n  );\n}\n\nexport function AutocompleteRow({\n  className,\n  ...props\n}: AutocompletePrimitive.Row.Props): React.ReactElement {\n  return (\n    <AutocompletePrimitive.Row\n      className={className}\n      data-slot=\"autocomplete-row\"\n      {...props}\n    />\n  );\n}\n\nexport function AutocompleteValue({\n  ...props\n}: AutocompletePrimitive.Value.Props): React.ReactElement {\n  return (\n    <AutocompletePrimitive.Value data-slot=\"autocomplete-value\" {...props} />\n  );\n}\n\nexport function AutocompleteList({\n  className,\n  ...props\n}: AutocompletePrimitive.List.Props): React.ReactElement {\n  return (\n    <ScrollArea scrollbarGutter scrollFade>\n      <AutocompletePrimitive.List\n        className={cn(\n          \"not-empty:scroll-py-1 not-empty:p-1 in-data-has-overflow-y:pe-3\",\n          className,\n        )}\n        data-slot=\"autocomplete-list\"\n        {...props}\n      />\n    </ScrollArea>\n  );\n}\n\nexport function AutocompleteClear({\n  className,\n  ...props\n}: AutocompletePrimitive.Clear.Props): React.ReactElement {\n  return (\n    <AutocompletePrimitive.Clear\n      className={cn(\n        \"absolute end-0.5 top-1/2 inline-flex size-8 shrink-0 -translate-y-1/2 cursor-pointer items-center justify-center rounded-md border border-transparent opacity-80 outline-none transition-[color,background-color,box-shadow,opacity] pointer-coarse:after:absolute pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:opacity-100 sm:size-7 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n        className,\n      )}\n      data-slot=\"autocomplete-clear\"\n      {...props}\n    >\n      <XIcon />\n    </AutocompletePrimitive.Clear>\n  );\n}\n\nexport function AutocompleteStatus({\n  className,\n  ...props\n}: AutocompletePrimitive.Status.Props): React.ReactElement {\n  return (\n    <AutocompletePrimitive.Status\n      className={cn(\n        \"px-3 py-2 font-medium text-muted-foreground text-xs empty:m-0 empty:p-0\",\n        className,\n      )}\n      data-slot=\"autocomplete-status\"\n      {...props}\n    />\n  );\n}\n\nexport function AutocompleteCollection({\n  ...props\n}: AutocompletePrimitive.Collection.Props): React.ReactElement {\n  return (\n    <AutocompletePrimitive.Collection\n      data-slot=\"autocomplete-collection\"\n      {...props}\n    />\n  );\n}\n\nexport function AutocompleteTrigger({\n  className,\n  children,\n  ...props\n}: AutocompletePrimitive.Trigger.Props): React.ReactElement {\n  return (\n    <AutocompletePrimitive.Trigger\n      className={className}\n      data-slot=\"autocomplete-trigger\"\n      {...props}\n    >\n      {children}\n    </AutocompletePrimitive.Trigger>\n  );\n}\n\nexport const useAutocompleteFilter: typeof AutocompletePrimitive.useFilter =\n  AutocompletePrimitive.useFilter;\n\nexport { AutocompletePrimitive };\n"
    }
  ],
  "categories": [
    "form-controls"
  ],
  "docs": "# Autocomplete\n\n> Siteplane UI release `0.1.1`.\n\n## Public Purpose\n\nAutocomplete combines free-text input with filtered suggestions. Use it for\nsearch, tagging, and command-like input where the typed value is meaningful.\nUse Select for a short closed list and Combobox when selection is constrained\nto listed options.\n\n## Import\n\n`import { Autocomplete, AutocompleteEmpty, AutocompleteGroup, AutocompleteGroupLabel, AutocompleteInput, AutocompleteItem, AutocompleteList, AutocompletePopup, AutocompleteRow, AutocompleteStatus, useAutocompleteFilter } from \"@/components/ui/autocomplete\";`\n\n## Public API\n\n- Render `AutocompleteInput`, `AutocompletePopup`, `AutocompleteList`, and\n  `AutocompleteItem` together.\n- `mode` accepts `\"list\"`, `\"both\"`, `\"inline\"`, or `\"none\"` and\n  controls how typed text is completed.\n- `AutocompletePopup` accepts `side`, `align`, `sideOffset`, and\n  `alignOffset`; its defaults are `side=\"bottom\"` and `align=\"start\"`.\n- `AutocompleteItem disabled` represents an unavailable suggestion.\n- `AutocompleteList` provides the bounded ScrollArea, scrollbar gutter, and\n  scroll fade used by long result sets.\n- `AutocompleteRow` supports grid-style result layouts.\n- `AutocompleteStatus` is the status line for loading and result messages;\n  `AutocompleteEmpty` renders the no-results state.\n- Filter through root `items` or `useAutocompleteFilter` with\n  `filteredItems`.\n\n## Public Motion\n\n- Popup scale and fade, highlighted-item states, and reduced-motion behavior are\n  native to the primitive.\n- Filtering, highlighting, status, and empty-state changes do not resize the\n  input.\n- Interactive suggestions and icon actions use a pointer cursor; the text input\n  keeps its text cursor.\n\n## Public Accessibility\n\n- Give every `AutocompleteInput` an accessible name through a visible label,\n  `aria-label`, or `aria-labelledby`.\n- Keep disabled suggestions unavailable to pointer and keyboard selection.\n- Connect invalid state to the input and render a visible `FieldError`.\n- The popup remains keyboard navigable and closes on Escape or outside click.\n\n## Public Agent Guidance\n\n- Always compose the input, popup, list, and items; do not render a raw input\n  without its suggestion list.\n- Use `Field` for a visible label, description, and validation.\n- Preserve native filtering, highlight, cursor, popup, and motion behavior.\n\n## Public Links\n\n- [Documentation and preview](https://ui.siteplane.io/docs/components/form-controls/autocomplete)\n\n- [Registry source](https://ui.siteplane.io/r/autocomplete.json)\n",
  "meta": {
    "siteplane:agentContractVersion": 1,
    "siteplane:docs": "https://ui.siteplane.io/docs/components/form-controls/autocomplete",
    "siteplane:releaseVersion": "0.1.1",
    "siteplane:sourceOwned": true
  },
  "devDependencies": []
}
