import { useEffect, useRef, useState } from "preact/hooks"; import type { GraphPayload } from "../../shared/wire"; import { useFocusTrap } from "../shell/FocusTrap"; import { fetchSearch } from "../api"; import type { FetchLike } from "../api"; import type { SearchRowView } from "./search.model"; import { DEBOUNCE_MS, PALETTE_HINT, PALETTE_PLACEHOLDER, PALETTE_TITLE, initialSearchState, paletteModel, resultIdAt, searchKey } from "./search.model"; import type { SearchState } from "./search.model"; export interface SearchPaletteProps { graph: GraphPayload | null; onSelect: (id: string) => void; onClose: () => void; ports: { fetch: FetchLike }; } function Row({ row, onPick, onHover }: { row: SearchRowView; onPick: () => void; onHover: () => void }) { return (
  • {row.badge} {row.label} {row.detail}
  • ); } export function SearchPalette(props: SearchPaletteProps) { const [state, setState] = useState(initialSearchState); const requestId = useRef(0); const abort = useRef(null); const trap = useFocusTrap(); const input = useRef(null); useEffect(() => void input.current?.focus(), []); useEffect(() => { const query = state.query.trim(); let active = true; if (!query) { abort.current?.abort(); setState((current) => ({ ...current, hits: [], answered: "", loading: false, failed: false })); return () => { active = false; }; } const timer = window.setTimeout(async () => { const id = ++requestId.current; abort.current?.abort(); const controller = new AbortController(); abort.current = controller; setState((current) => ({ ...current, loading: true, failed: false })); const result = await fetchSearch(props.ports.fetch, query, controller.signal); if (!active || controller.signal.aborted || id !== requestId.current) return; setState((current) => result.ok ? { ...current, hits: result.data.hits, answered: query, loading: false, failed: false, cursor: 0 } : { ...current, loading: false, failed: true }); }, DEBOUNCE_MS); return () => { active = false; window.clearTimeout(timer); abort.current?.abort(); }; }, [state.query, props.ports.fetch]); const model = paletteModel(state, props.graph); const pick = (index: number | null): void => { const id = resultIdAt(model.rows, index); if (id !== null) props.onSelect(id); props.onClose(); }; return (
    pick(null)}>
    ); }