mirror of
https://github.com/cosscom/coss.git
synced 2026-09-14 20:06:50 +08:00
16 lines
4.7 KiB
JSON
16 lines
4.7 KiB
JSON
{
|
|
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
"name": "autocomplete-async",
|
|
"type": "registry:example",
|
|
"description": "Autocomplete with async items loading",
|
|
"registryDependencies": ["@coss/autocomplete"],
|
|
"files": [
|
|
{
|
|
"path": "registry/default/examples/autocomplete-async.tsx",
|
|
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Autocomplete as AutocompletePrimitive } from \"@base-ui-components/react/autocomplete\"\n\nimport {\n Autocomplete,\n AutocompleteInput,\n AutocompleteItem,\n AutocompleteList,\n AutocompletePopup,\n AutocompleteStatus,\n} from \"@/registry/default/ui/autocomplete\"\nimport { Spinner } from \"@/registry/default/ui/spinner\"\n\ntype Movie = { id: string; title: string; year: number }\nconst top100Movies: Movie[] = [\n { id: \"1\", title: \"The Shawshank Redemption\", year: 1994 },\n { id: \"2\", title: \"The Godfather\", year: 1972 },\n { id: \"3\", title: \"The Dark Knight\", year: 2008 },\n { id: \"4\", title: \"The Godfather Part II\", year: 1974 },\n { id: \"5\", title: \"12 Angry Men\", year: 1957 },\n { id: \"8\", title: \"Pulp Fiction\", year: 1994 },\n { id: \"11\", title: \"Forrest Gump\", year: 1994 },\n { id: \"14\", title: \"Inception\", year: 2010 },\n]\n\nasync function searchMovies(\n query: string,\n filter: (item: string, query: string) => boolean\n): Promise<Movie[]> {\n await new Promise((resolve) => setTimeout(resolve, Math.random() * 500 + 100))\n if (Math.random() < 0.01 || query === \"will_error\") {\n throw new Error(\"Network error\")\n }\n return top100Movies.filter(\n (movie) =>\n filter(movie.title, query) || filter(movie.year.toString(), query)\n )\n}\n\nexport default function AutocompleteAsync() {\n const [searchValue, setSearchValue] = React.useState(\"\")\n const [isLoading, setIsLoading] = React.useState(false)\n const [searchResults, setSearchResults] = React.useState<Movie[]>([])\n const [error, setError] = React.useState<string | null>(null)\n\n const { contains } = AutocompletePrimitive.useFilter({ sensitivity: \"base\" })\n\n React.useEffect(() => {\n if (!searchValue) {\n setSearchResults([])\n setIsLoading(false)\n return\n }\n\n setIsLoading(true)\n setError(null)\n let ignore = false\n\n const timeoutId = setTimeout(async () => {\n try {\n const results = await searchMovies(searchValue, contains)\n if (!ignore) setSearchResults(results)\n } catch {\n if (!ignore) {\n setError(\"Failed to fetch movies. Please try again.\")\n setSearchResults([])\n }\n } finally {\n if (!ignore) setIsLoading(false)\n }\n }, 300)\n\n return () => {\n clearTimeout(timeoutId)\n ignore = true\n }\n }, [searchValue, contains])\n\n let status: React.ReactNode = `${searchResults.length} result${searchResults.length === 1 ? \"\" : \"s\"} found`\n if (isLoading) {\n status = (\n <span className=\"flex items-center justify-between gap-2 text-muted-foreground\">\n Searching...\n <Spinner />\n </span>\n )\n } else if (error) {\n status = (\n <span className=\"text-sm font-normal text-destructive\">{error}</span>\n )\n } else if (searchResults.length === 0 && searchValue) {\n status = (\n <span className=\"text-sm font-normal text-muted-foreground\">\n Movie or year \"{searchValue}\" does not exist in the Top 100 IMDb movies\n </span>\n )\n }\n\n const shouldRenderPopup = searchValue !== \"\"\n\n return (\n <Autocomplete\n items={searchResults}\n value={searchValue}\n onValueChange={setSearchValue}\n itemToStringValue={(item: unknown) => (item as Movie).title}\n filter={null}\n >\n <AutocompleteInput placeholder=\"e.g. Pulp Fiction or 1994\" />\n {shouldRenderPopup && (\n <AutocompletePopup aria-busy={isLoading || undefined}>\n <AutocompleteStatus className=\"text-muted-foreground\">\n {status}\n </AutocompleteStatus>\n <AutocompleteList>\n {(movie: Movie) => (\n <AutocompleteItem key={movie.id} value={movie}>\n <div className=\"flex w-full flex-col gap-1\">\n <div className=\"font-medium\">{movie.title}</div>\n <div className=\"text-xs text-muted-foreground\">\n {movie.year}\n </div>\n </div>\n </AutocompleteItem>\n )}\n </AutocompleteList>\n </AutocompletePopup>\n )}\n </Autocomplete>\n )\n}\n",
|
|
"type": "registry:example"
|
|
}
|
|
],
|
|
"categories": ["autocomplete", "input", "async"]
|
|
}
|