Created
December 9, 2025 15:59
-
-
Save saturngod/eaa487e593ba97cfd914c9f4ab09175c to your computer and use it in GitHub Desktop.
shadcn ui autocomplete
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { useState, useEffect, useRef, useCallback } from "react" | |
| import axios from "axios" | |
| import { Check } from "lucide-react" | |
| import { cn } from "@/lib/utils" | |
| import { Input } from "@/components/ui/input" | |
| import { | |
| Command, | |
| CommandGroup, | |
| CommandItem, | |
| CommandList, | |
| } from "@/components/ui/command" | |
| interface Country { | |
| name: string | |
| } | |
| export function AutocompleteSearch() { | |
| const [inputValue, setInputValue] = useState("") | |
| const [results, setResults] = useState<Country[]>([]) | |
| const [open, setOpen] = useState(false) | |
| const inputRef = useRef<HTMLInputElement>(null) | |
| useEffect(() => { | |
| if (!inputValue) { | |
| setResults([]) | |
| setOpen(false) | |
| return | |
| } | |
| const fetchResults = async () => { | |
| try { | |
| const response = await axios.get(`http://127.0.0.1:8001/loc/${inputValue}`) | |
| if (response.data && response.data.result) { | |
| setResults(response.data.result) | |
| setOpen(true) | |
| } else { | |
| setOpen(false) | |
| } | |
| } catch (error) { | |
| console.error("Error fetching data", error) | |
| setResults([]) | |
| } | |
| } | |
| const timer = setTimeout(() => { | |
| fetchResults() | |
| }, 300) | |
| return () => clearTimeout(timer) | |
| }, [inputValue]) | |
| const handleSelect = useCallback((currentValue: string) => { | |
| setInputValue(currentValue) | |
| setOpen(false) | |
| inputRef.current?.focus() | |
| }, []) | |
| const handleBlur = () => { | |
| // Small delay to allow click event on item to fire before closing | |
| setTimeout(() => { | |
| setOpen(false) | |
| }, 200) | |
| } | |
| const handleFocus = () => { | |
| if (inputValue && results.length > 0) { | |
| setOpen(true) | |
| } | |
| } | |
| return ( | |
| <div className="w-full max-w-sm px-4 relative"> | |
| <label className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 mb-2 block text-gray-700"> | |
| Search Country | |
| </label> | |
| <div className="relative group"> | |
| <Input | |
| ref={inputRef} | |
| placeholder="Type to search..." | |
| value={inputValue} | |
| onChange={(e) => setInputValue(e.target.value)} | |
| onBlur={handleBlur} | |
| onFocus={handleFocus} | |
| /> | |
| {open && results.length > 0 && ( | |
| <div className="absolute top-full left-0 w-full mt-1 bg-white border rounded-md shadow-lg z-50 overflow-hidden animate-in fade-in-0 zoom-in-95"> | |
| <Command> | |
| <CommandList> | |
| <CommandGroup heading="Suggestions"> | |
| {results.map((country) => ( | |
| <CommandItem | |
| key={country.name} | |
| value={country.name} | |
| onSelect={handleSelect} | |
| className="cursor-pointer" | |
| > | |
| <Check | |
| className={cn( | |
| "mr-2 h-4 w-4", | |
| inputValue === country.name ? "opacity-100" : "opacity-0" | |
| )} | |
| /> | |
| {country.name} | |
| </CommandItem> | |
| ))} | |
| </CommandGroup> | |
| </CommandList> | |
| </Command> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| ) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment