import {Input} from '@plunk/ui'; import type {Template} from '@plunk/db'; import type {PaginatedResponse} from '@plunk/types'; import {Command, CommandGroup, CommandItem, CommandList} from '@plunk/ui'; import {useCallback, useRef, useState} from 'react'; import useSWR from 'swr'; interface TemplateSearchPickerProps { /** Currently selected template ID */ value: string; /** Display name for the pre-selected template (avoids a fetch just to show the name) */ initialName?: string; onChange: (id: string) => void; } /** * Inline combobox for picking a template. * Fires a debounced server-side search (/templates?search=…&pageSize=20) * so it works correctly regardless of how many templates exist. */ export function TemplateSearchPicker({value, initialName, onChange}: TemplateSearchPickerProps) { const [query, setQuery] = useState(initialName ?? ''); const [prevInitialName, setPrevInitialName] = useState(initialName); const [debouncedQuery, setDebouncedQuery] = useState(''); const [open, setOpen] = useState(false); const debounceRef = useRef | null>(null); if (initialName !== prevInitialName) { setPrevInitialName(initialName); setQuery(initialName ?? ''); } const handleInput = useCallback((e: React.ChangeEvent) => { const val = e.target.value; setQuery(val); setOpen(true); if (debounceRef.current) clearTimeout(debounceRef.current); debounceRef.current = setTimeout(() => setDebouncedQuery(val), 300); }, []); const {data, isLoading} = useSWR>( open || debouncedQuery ? `/templates?pageSize=20${debouncedQuery ? `&search=${encodeURIComponent(debouncedQuery)}` : ''}` : null, {revalidateOnFocus: false}, ); // When closed, show the selected template's name rather than the raw query const displayValue = open ? query : (value ? (data?.data.find(t => t.id === value)?.name ?? initialName ?? value) : ''); return (
{ setOpen(true); setDebouncedQuery(query); }} onBlur={() => setTimeout(() => setOpen(false), 150)} placeholder="Search templates…" autoComplete="off" /> {open && (
{isLoading ? (
Searching…
) : !data?.data.length ? (
No templates found
) : ( {data.data.map(t => ( { onChange(t.id); setQuery(t.name); setOpen(false); }} > {t.name} {t.type} ))} )} {(data?.total ?? 0) > 20 && (
Showing 20 of {data!.total} — type to narrow results
)}
)}
); }