import { createSignal, For, onCleanup, onMount, Show, untrack } from "solid-js"; import type { SearchTextInputDom } from "../../eh"; import { addSearchHistory, loadSearchHistory, removeSearchHistory } from "../../state"; export function SearchHistory(props: { source: SearchTextInputDom }) { let dropdown: HTMLElement | undefined; const [searchValue, setSearchValue] = createSignal( untrack(() => props.source.data.value), ); const [history, setHistory] = createSignal(loadSearchHistory()); const [open, setOpen] = createSignal(false); const [activeIndex, setActiveIndex] = createSignal(-1); const [position, setPosition] = createSignal<{ left: number; top: number; width: number } | null>(null); const itemButtons: HTMLButtonElement[] = []; const visiblePosition = () => open() && !searchValue().trim() && history().length > 0 ? position() : null; const selectHistory = (item: string) => { props.source.handle.applySearchSelection(item); setOpen(false); }; onMount(() => { const updatePosition = () => { setPosition(props.source.handle.readSearchOverlayPosition()); }; const showHistory = () => { updatePosition(); setActiveIndex(-1); setOpen(true); }; const moveSelection = (offset: number) => { const items = history(); if (items.length === 0) { return; } const current = activeIndex(); const next = current < 0 ? (offset > 0 ? 0 : items.length - 1) : (current + offset + items.length) % items.length; setActiveIndex(next); window.requestAnimationFrame(() => itemButtons[next]?.scrollIntoView({ block: "nearest" })); }; const onInputKeyDown = (event: KeyboardEvent) => { if (!visiblePosition()) { return; } if (event.key === "ArrowDown" || event.key === "ArrowUp") { event.preventDefault(); moveSelection(event.key === "ArrowDown" ? 1 : -1); } else if (event.key === "Enter" && activeIndex() >= 0) { event.preventDefault(); const item = history()[activeIndex()]; if (item !== undefined) { selectHistory(item); } } else if (event.key === "Escape") { event.preventDefault(); setOpen(false); } }; const updateSearchValue = (value: string, focused: boolean) => { setSearchValue(value); if (!value.trim() && focused) { showHistory(); } }; const recordSearch = (sourceValue: string) => { const value = sourceValue.trim(); if (!value) { return; } setHistory(addSearchHistory(value)); }; const disconnect = props.source.handle.listenSearchHistoryOverlay({ onFocus: showHistory, onInput: updateSearchValue, onKeyDown: onInputKeyDown, onOutsidePointer: () => setOpen(false), onPositionChange: updatePosition, onSubmit: recordSearch, }, () => dropdown ?? null); updateSearchValue(props.source.data.value, false); onCleanup(disconnect); }); return ( {(currentPosition) => (
{(item, index) => (
)}
)}
); }