import * as React from 'react'; import { __ } from '@wordpress/i18n'; export interface SearchItem { key: string | number; name: string; description?: string; } /** Same accessible search behavior; callers supply only their permitted customer projection. */ export function Autocomplete({ label, query, onQuery, load, item, onSelect, disabled = false, focus = false, refreshKey = '', selectedKeys = [], actionLabel, dismissKey = 0 }: { label: string; query: string; onQuery: (q: string) => void; load: (q: string) => Promise; item: (value: T) => SearchItem; onSelect: (value: T) => void; disabled?: boolean; focus?: boolean; refreshKey?: string; selectedKeys?: Array; actionLabel?: string; dismissKey?: number; }) { const [rows, setRows] = React.useState([]), [busy, setBusy] = React.useState(false), [error, setError] = React.useState(''), [active, setActive] = React.useState(-1), [open, setOpen] = React.useState(true); const id = React.useId(); React.useEffect(() => { let alive = true; setRows([]); setActive(-1); setError(''); setBusy(false); if (query.trim().length < 2 || disabled) return; setBusy(true); const timer = setTimeout(() => load(query).then(value => { if (alive) setRows(value); }).catch(e => { if (alive) setError(e.message); }).finally(() => { if (alive) setBusy(false); }), 250); return () => { alive = false; clearTimeout(timer); }; }, [load, query, disabled, refreshKey]); React.useEffect(() => { if (dismissKey) setOpen(false); }, [dismissKey]); function choose(value: T) { if (selectedKeys.includes(item(value).key)) return; onSelect(value); setOpen(false); setActive(-1); } return
{busy &&

{__('Searching…', 'abece-booking')}

}{error &&

{error}

}{open && !busy && query.trim().length >= 2 && !rows.length && !error &&

{__('No matching customers.', 'abece-booking')}

} {open && rows.length > 0 &&
    {rows.map((value, index) => { const result = item(value), chosen = selectedKeys.includes(result.key); return
  • {result.name}{result.description && {result.description}}
  • ; })}
}
; }