import { useState, useEffect, useMemo, useRef } from "react"; import { createPortal } from "react-dom"; import { trpc } from "~/lib/trpc/client.js"; import { useAnchoredOverlay } from "~/hooks/useAnchoredOverlay.js"; export function ResourcePersonPicker({ value, onChange, }: { value: string; onChange: (v: string) => void; }) { const [query, setQuery] = useState(value); const [open, setOpen] = useState(false); const [debouncedSearch, setDebouncedSearch] = useState(""); const [isConfirmed, setIsConfirmed] = useState(false); const inputRef = useRef(null); useEffect(() => { const timer = setTimeout(() => setDebouncedSearch(query), 200); return () => clearTimeout(timer); }, [query]); const { data } = trpc.resource.directory.useQuery( { isActive: true, search: debouncedSearch || undefined, limit: 30 }, // eslint-disable-next-line @typescript-eslint/no-explicit-any { staleTime: 15_000, placeholderData: (prev: any) => prev }, ); const filtered = useMemo( () => (data?.resources ?? []) as unknown as Array<{ id: string; displayName: string; eid: string }>, [data], ); useEffect(() => { setQuery(value); if (!value) setIsConfirmed(false); }, [value]); const { panelRef, position } = useAnchoredOverlay({ open, onClose: () => setOpen(false), align: "start", matchTriggerWidth: true, triggerRef: inputRef, }); return (
{ setQuery(e.target.value); onChange(e.target.value); setIsConfirmed(false); setOpen(true); }} onFocus={() => setOpen(true)} placeholder="Search by name or EID…" className={`app-input ${isConfirmed ? "ring-2 ring-green-400 ring-offset-0" : ""}`} /> {isConfirmed && ( )} {open && filtered.length > 0 && typeof document !== "undefined" ? createPortal(
e.preventDefault()} >
    {filtered.map((r) => (
  • ))}
, document.body, ) : null}
); }