UNPKG

solid-suggest

Version:

Headless search suggestion dropdown UI library for SolidJS

143 lines (139 loc) 4.78 kB
'use strict'; var web = require('solid-js/web'); var solidJs = require('solid-js'); var _tmpl$ = /*#__PURE__*/web.template(`<div class=s-sug-container role=combobox aria-haspopup=listbox><input type=search class=s-sug-search aria-autocomplete=list>`), _tmpl$2 = /*#__PURE__*/web.template(`<ul class=s-sug-suggestions role=listbox>`), _tmpl$3 = /*#__PURE__*/web.template(`<li class=s-sug-suggestion role=option>`); function Suggest(props) { // Suggestions to be rendered const [suggestions, setSuggestions] = solidJs.createSignal([]); const numSuggestions = solidJs.createMemo(() => suggestions().length); // Basic signals for component state const [query, setQuery] = solidJs.createSignal(''); const [staged, setStaged] = solidJs.createSignal(null); // Internal signal for user input, separate from query to handle optional debounce const [debouncedQuery, setDebouncedQuery] = solidJs.createSignal(''); // Just a variable to store debounce timeout, if applicable let debounceTimeout = null; // Effect to update suggestions when debouncedQuery changes solidJs.createEffect(() => { const result = props.onQuery(debouncedQuery()); if (result instanceof Promise) { result.then(setSuggestions); } else { setSuggestions(result); } }); function handleInput(e) { const value = e.currentTarget.value; setQuery(value); if (typeof props.debounceMs === 'number' && props.debounceMs > 0) { if (debounceTimeout) clearTimeout(debounceTimeout); debounceTimeout = setTimeout(() => { setDebouncedQuery(value); }, props.debounceMs); } else { setDebouncedQuery(value); } } function stageSuggestion(index) { setStaged(index); } function stageNextSuggestion() { setStaged(s => { if (suggestions().length === 0) return null; const current = s ?? -1; return (current + 1) % numSuggestions(); }); } function stagePrevSuggestion() { setStaged(s => { if (numSuggestions() === 0) return null; const current = s ?? 0; return (current - 1 + numSuggestions()) % numSuggestions(); }); } function selectStagedSuggestion() { const index = staged(); if (index !== null) { props.onSelect(suggestions()[index]); reset(); } } function reset() { setQuery(''); setDebouncedQuery(''); setStaged(null); if (debounceTimeout) clearTimeout(debounceTimeout); } // Handles special input for suggestion behavior function handleKeyDown(e) { const keyInputReversed = props.reverseKeyInput ?? false; if (e.key === 'ArrowDown') { e.preventDefault(); if (keyInputReversed) { stagePrevSuggestion(); } else { stageNextSuggestion(); } } else if (e.key === 'ArrowUp') { e.preventDefault(); if (keyInputReversed) { stageNextSuggestion(); } else { stagePrevSuggestion(); } } else if (e.key === 'Enter') { e.preventDefault(); selectStagedSuggestion(); } else if (e.key === 'Escape') { e.preventDefault(); reset(); } // else -> propagates up to allow input } return (() => { var _el$ = _tmpl$(), _el$2 = _el$.firstChild; web.addEventListener(_el$2, "keydown", handleKeyDown); web.addEventListener(_el$2, "input", handleInput); web.insert(_el$, (() => { var _c$ = web.memo(() => numSuggestions() > 0); return () => _c$() && (() => { var _el$3 = _tmpl$2(); web.insert(_el$3, () => suggestions().map((s, i) => (() => { var _el$4 = _tmpl$3(); web.addEventListener(_el$4, "click", selectStagedSuggestion); web.addEventListener(_el$4, "mouseenter", () => stageSuggestion(i)); web.insert(_el$4, () => props.renderSuggestion(s)); web.effect(_p$ => { var _v$3 = staged() === i, _v$4 = staged() === i ? 'true' : 'false'; _v$3 !== _p$.e && web.setAttribute(_el$4, "data-staged", _p$.e = _v$3); _v$4 !== _p$.t && web.setAttribute(_el$4, "aria-selected", _p$.t = _v$4); return _p$; }, { e: undefined, t: undefined }); return _el$4; })())); return _el$3; })(); })(), null); web.effect(_p$ => { var _v$ = numSuggestions() > 0, _v$2 = props.placeholder ?? ''; _v$ !== _p$.e && web.setAttribute(_el$, "aria-expanded", _p$.e = _v$); _v$2 !== _p$.t && web.setAttribute(_el$2, "placeholder", _p$.t = _v$2); return _p$; }, { e: undefined, t: undefined }); web.effect(() => _el$2.value = query()); return _el$; })(); } module.exports = Suggest; //# sourceMappingURL=index.cjs.map