useTypeahead#
useTypeahead is the type-to-select hook useListNavigation uses internally, exported for composing your own keyboard navigation. It returns an onChar(char) callback that accumulates printable characters into a query string and returns the current query; the buffer resets after a short idle gap. The caller matches the returned query against its item labels and moves the active item.
Demo#
- Argon
- Boron
- Calcium
- Carbon
- Cobalt
- Neon
buffer: (empty)focus the list and type; it resets after 500ms idle
import { useTypeahead } from 'hono-preact-ui';
import type { JSX } from 'preact';
import { useState } from 'preact/hooks';
const ITEMS = ['Argon', 'Boron', 'Calcium', 'Carbon', 'Cobalt', 'Neon'];
// useTypeahead returns an onChar(char) callback that accumulates printable
// characters into a query and resets after an idle gap (default 500ms). Type
// while the list is focused to jump to the first matching item. The buffer
// readout shows the accumulation and the idle reset. Styling: .docs-typeahead*.
export function UseTypeaheadDemo() {
const onChar = useTypeahead();
const [query, setQuery] = useState('');
const [activeIndex, setActiveIndex] = useState(0);
const handleKeyDown = (e: JSX.TargetedKeyboardEvent<HTMLUListElement>) => {
if (e.key.length !== 1) return; // ignore non-printable keys (Arrow, Enter, ...)
const q = onChar(e.key);
setQuery(q);
const idx = ITEMS.findIndex((it) =>
it.toLowerCase().startsWith(q.toLowerCase())
);
if (idx >= 0) setActiveIndex(idx);
};
return (
<div class="docs-typeahead">
<ul
class="docs-typeahead-list"
tabIndex={0}
role="listbox"
aria-label="Elements"
aria-activedescendant={`docs-typeahead-${activeIndex}`}
onKeyDown={handleKeyDown}
>
{ITEMS.map((it, i) => (
<li
key={it}
id={`docs-typeahead-${i}`}
role="option"
aria-selected={i === activeIndex}
data-active={i === activeIndex ? '' : undefined}
class="docs-typeahead-option"
>
{it}
</li>
))}
</ul>
<p class="docs-typeahead-readout">
buffer: <code>{query || '(empty)'}</code>
<span class="docs-typeahead-hint">
focus the list and type; it resets after 500ms idle
</span>
</p>
</div>
);
}
Example#
import { useTypeahead } from 'hono-preact-ui';
import { useState } from 'preact/hooks';
const ITEMS = ['Argon', 'Boron', 'Calcium', 'Carbon'];
function Typeahead() {
const onChar = useTypeahead();
const [active, setActive] = useState(0);
return (
<ul
tabIndex={0}
role="listbox"
onKeyDown={(e) => {
if (e.key.length !== 1) return; // printable characters only
const query = onChar(e.key);
const idx = ITEMS.findIndex((it) =>
it.toLowerCase().startsWith(query.toLowerCase())
);
if (idx >= 0) setActive(idx);
}}
>
{ITEMS.map((it, i) => (
<li key={it} role="option" aria-selected={i === active}>
{it}
</li>
))}
</ul>
);
}
See also: useListNavigation, which folds typeahead together with arrow-key and Home/End navigation.
Signature#
import { useTypeahead } from 'hono-preact-ui';
function useTypeahead(opts?: UseTypeaheadOptions): (char: string) => string;
interface UseTypeaheadOptions {
idleMs?: number; // reset the buffer after this idle gap, default 500
}
Call the returned function with each printable character (for example from a keydown handler, skipping non-printable keys); it appends the character to the buffer, schedules a reset after idleMs of no input, and returns the current query.
Options#
| Option | Type | Default | Notes |
|---|---|---|---|
idleMs | number | 500 | Reset the accumulated query after this idle gap. |
Returns (char: string) => string: a stable callback that accumulates char into the query and returns it.