({ notes, onPick, onCancel }: NotePickerOverlayProps)
| 745 | } |
| 746 | |
| 747 | function NotePickerOverlay({ notes, onPick, onCancel }: NotePickerOverlayProps): JSX.Element { |
| 748 | const [query, setQuery] = useState('') |
| 749 | const [active, setActive] = useState(0) |
| 750 | const inputRef = useRef<HTMLInputElement | null>(null) |
| 751 | |
| 752 | useEffect(() => { |
| 753 | inputRef.current?.focus() |
| 754 | }, []) |
| 755 | |
| 756 | const searchIndex = useMemo(() => buildNoteSearchIndex(notes), [notes]) |
| 757 | |
| 758 | const results = useMemo(() => { |
| 759 | return searchNoteIndex(searchIndex, query, { |
| 760 | limit: 30, |
| 761 | defaultOrder: 'quick-first-recent' |
| 762 | }) |
| 763 | }, [query, searchIndex]) |
| 764 | |
| 765 | useEffect(() => setActive(0), [query]) |
| 766 | |
| 767 | const onKeyDown = (e: React.KeyboardEvent<HTMLInputElement>): void => { |
| 768 | // While composing (IME), let the input own Enter/Arrows. (#183) |
| 769 | if (isImeComposing(e)) return |
| 770 | if (isPaletteNextKey(e)) { |
| 771 | e.preventDefault() |
| 772 | setActive((i) => Math.min(results.length - 1, i + 1)) |
| 773 | } else if (isPalettePreviousKey(e)) { |
| 774 | e.preventDefault() |
| 775 | setActive((i) => Math.max(0, i - 1)) |
| 776 | } else if (e.key === 'Enter') { |
| 777 | e.preventDefault() |
| 778 | const picked = results[active] |
| 779 | if (picked) onPick(picked) |
| 780 | } else if (e.key === 'Escape') { |
| 781 | // Stop the native event so the window-level Esc listener doesn't |
| 782 | // also run and try to save+hide the underlying buffer. |
| 783 | e.preventDefault() |
| 784 | e.stopPropagation() |
| 785 | e.nativeEvent.stopImmediatePropagation() |
| 786 | onCancel() |
| 787 | } |
| 788 | } |
| 789 | |
| 790 | return ( |
| 791 | <OverlayShell> |
| 792 | <div className="border-b border-paper-300/70 px-4 py-2"> |
| 793 | <input |
| 794 | ref={inputRef} |
| 795 | type="text" |
| 796 | value={query} |
| 797 | placeholder="Search notes — type, or use #tag filters" |
| 798 | onChange={(e) => setQuery(e.target.value)} |
| 799 | onKeyDown={onKeyDown} |
| 800 | className="w-full bg-transparent text-sm outline-none placeholder:text-ink-400" |
| 801 | /> |
| 802 | </div> |
| 803 | <div className="min-h-0 flex-1 overflow-y-auto"> |
| 804 | {results.length === 0 ? ( |
nothing calls this directly
no test coverage detected