{
  "name": "autocomplete",
  "type": "registry:ui",
  "dependencies": [
    "use-debounce"
  ],
  "registryDependencies": [
    "input",
    "button"
  ],
  "files": [
    {
      "type": "registry:ui",
      "content": "'use client'\n\nimport { useState, useCallback, useEffect } from 'react'\nimport { useDebounce } from 'use-debounce'\nimport { Input } from '@/components/ui/input'\nimport { Button } from '@/components/ui/button'\nimport { Search } from 'lucide-react'\n\n// Simulated API call\nconst fetchSuggestions = async (query: string): Promise<string[]> => {\n  await new Promise((resolve) => setTimeout(resolve, 300)) // Simulate network delay\n  const allSuggestions = [\n    'React',\n    'Redux',\n    'Next.js',\n    'TypeScript',\n    'JavaScript',\n    'Node.js',\n    'Express',\n    'MongoDB',\n    'PostgreSQL',\n    'GraphQL',\n    'Vue.js',\n    'Angular',\n    'Svelte',\n    'Tailwind CSS',\n    'Sass',\n    'Webpack',\n    'Babel',\n    'ESLint',\n    'Jest',\n    'Cypress',\n  ]\n  return allSuggestions.filter((suggestion) =>\n    suggestion.toLowerCase().includes(query.toLowerCase()),\n  )\n}\n\ninterface AutoCompleteProps {\n  value?: string\n  onChange?: (value: string) => void\n}\n\nexport default function Autocomplete({ value = '', onChange }: AutoCompleteProps) {\n  const [query, setQuery] = useState(value)\n  const [debouncedQuery] = useDebounce(query, 300)\n  const [suggestions, setSuggestions] = useState<string[]>([])\n  const [selectedIndex, setSelectedIndex] = useState(-1)\n  const [isLoading, setIsLoading] = useState(false)\n  const [isFocused, setIsFocused] = useState(false)\n\n  const fetchSuggestionsCallback = useCallback(async (q: string) => {\n    if (q.trim() === '') {\n      setSuggestions([])\n      return\n    }\n    setIsLoading(true)\n    const results = await fetchSuggestions(q)\n    setSuggestions(results)\n    setIsLoading(false)\n  }, [])\n\n  useEffect(() => {\n    if (debouncedQuery && isFocused) {\n      fetchSuggestionsCallback(debouncedQuery)\n    } else {\n      setSuggestions([])\n    }\n  }, [debouncedQuery, fetchSuggestionsCallback, isFocused])\n\n  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n    const newValue = e.target.value\n    setQuery(newValue)\n    onChange?.(newValue)\n    setSelectedIndex(-1)\n  }\n\n  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n    if (e.key === 'ArrowDown') {\n      e.preventDefault()\n      setSelectedIndex((prev) =>\n        prev < suggestions.length - 1 ? prev + 1 : prev,\n      )\n    } else if (e.key === 'ArrowUp') {\n      e.preventDefault()\n      setSelectedIndex((prev) => (prev > 0 ? prev - 1 : -1))\n    } else if (e.key === 'Enter' && selectedIndex >= 0) {\n      setQuery(suggestions[selectedIndex])\n      setSuggestions([])\n      setSelectedIndex(-1)\n    } else if (e.key === 'Escape') {\n      setSuggestions([])\n      setSelectedIndex(-1)\n    }\n  }\n\n  const handleSuggestionClick = (suggestion: string) => {\n    setQuery(suggestion)\n    onChange?.(suggestion)\n    setSuggestions([])\n    setSelectedIndex(-1)\n  }\n\n  const handleFocus = () => {\n    setIsFocused(true)\n  }\n\n  const handleBlur = () => {\n    // Delay hiding suggestions to allow for click events on suggestions\n    setTimeout(() => {\n      setIsFocused(false)\n      setSuggestions([])\n      setSelectedIndex(-1)\n    }, 200)\n  }\n\n  return (\n    <div className=\"w-full max-w-xs mx-auto\">\n      <div className=\"relative\">\n        <Input\n          type=\"text\"\n          placeholder=\"Search...\"\n          value={query}\n          onChange={handleInputChange}\n          onKeyDown={handleKeyDown}\n          onFocus={handleFocus}\n          onBlur={handleBlur}\n          className=\"pr-10\"\n          aria-label=\"Search input\"\n          aria-autocomplete=\"list\"\n          aria-controls=\"suggestions-list\"\n          aria-expanded={suggestions.length > 0}\n        />\n        <Button\n          size=\"icon\"\n          variant=\"ghost\"\n          className=\"absolute right-0 top-0 h-full\"\n          aria-label=\"Search\"\n        >\n          <Search className=\"h-4 w-4\" />\n        </Button>\n      </div>\n      {isLoading && isFocused && (\n        <div\n          className=\"mt-2 p-2 bg-background border rounded-md shadow-sm absolute z-10\"\n          aria-live=\"polite\"\n        >\n          Loading...\n        </div>\n      )}\n      {suggestions.length > 0 && !isLoading && isFocused && (\n        <ul\n          id=\"suggestions-list\"\n          className=\"mt-2 bg-background border rounded-md shadow-sm absolute z-10\"\n          role=\"listbox\"\n        >\n          {suggestions.map((suggestion, index) => (\n            <li\n              key={suggestion}\n              className={`px-4 py-2 cursor-pointer hover:bg-muted ${\n                index === selectedIndex ? 'bg-muted' : ''\n              }`}\n              onClick={() => handleSuggestionClick(suggestion)}\n              role=\"option\"\n              aria-selected={index === selectedIndex}\n            >\n              {suggestion}\n            </li>\n          ))}\n        </ul>\n      )}\n    </div>\n  )\n}\n",
      "path": "ui/autocomplete.tsx",
      "target": "components/ui/autocomplete.tsx"
    }
  ]
}