/** * Logical actions a keyboard interaction can map to. Numeric values are * implementation detail; never serialise them. */ export declare enum LLSelectAction { /** Open the popup (no item activation). */ Open = 0, /** Close the popup (no item activation). */ Close = 1, /** Activate the currently focused option (select + maybe close). */ Select = 2, /** Move focus to the next option (clamps at last). */ Next = 3, /** Move focus to the previous option (clamps at first). */ Previous = 4, /** Move focus to the first option. */ GotoFirst = 5, /** Move focus to the last option. */ GotoLast = 6, /** Jump focus down by a fixed page size. */ PageDown = 7, /** Jump focus up by a fixed page size. */ PageUp = 8 } /** * Map a keydown event to a logical {@link LLSelectAction}, given whether the * popup is currently open. * - Returns `undefined` if the key should be left alone (no preventDefault, * no library reaction). * - Maps the action keys of the ARIA APG combobox pattern. * - The pattern's printable-character typeahead is not mapped here - it needs * the character, which an action enum cannot carry. The keydown handler * runs {@link findTypeaheadIndex} before this mapping. * * @param inTextInput - true when focus is in the editable filter input. There, * Space must type a space and Home/End must move the text caret, so those * keys are NOT mapped to selection / first-last navigation. Selection is * Enter only; option navigation is the arrow / page keys. */ export declare function getActionFromKey(ev: KeyboardEvent, isOpened: boolean, inTextInput?: boolean): LLSelectAction | undefined; /** * Compute a new focused-option index after applying a navigation action. * Clamps to `[0, maxIndex]` (no wrap-around). Returns `-1` if there are no * options (`maxIndex < 0`). The page size for PageUp/PageDown is a fixed * constant. * * @param currentIndex - current focused index (`-1` for "none") * @param maxIndex - largest valid index (`options.length - 1`) */ export declare function getUpdatedIndex(currentIndex: number, maxIndex: number, action: LLSelectAction): number; /** * Pause (in ms) after which the next typed character starts a new typeahead * buffer instead of extending the old one. Native `` typeahead: * - An option matches when its text starts with `buffer`, case-insensitive. * - A one-character buffer searches from the option AFTER `currentIndex`, so * repeated presses of one initial cycle through the options sharing it. * - A buffer of one repeated character (e.g. `"aa"`) behaves exactly like its * single character - it keeps cycling. It is never matched literally (the * W3C APG example tries the literal `"aa"` prefix first; native does not, * and a text really starting `"aa"` is still reached by the cycle). * - Any other longer buffer searches from `currentIndex` itself, so extending * the buffer stays on the current option while it still matches. * - The search wraps around the whole list - deliberately, unlike the clamped * arrow navigation (see `docs/llm/A11Y.md`): a search means "anywhere", and * cycling needs the wrap. * - Indexes where `textAt` returns `undefined` (disabled options) never match. * - `currentIndex` `-1` means no option is active; the search starts at 0. * * The repeated-character test compares CODE POINTS, each lower-cased on its * own: a key whose lower-case form expands to two code units (the Turkish * dotted capital I) still counts, astral-plane characters compare whole, and * a mid-repeat Shift ("aA") still cycles. * * @param textAt - match text of the option at an index, or `undefined` when * that option must never match. */ export declare function findTypeaheadIndex(buffer: string, count: number, currentIndex: number, textAt: (index: number) => string | undefined): number; /** * Scroll `scrollParent` just enough so `child` is fully visible. No-op if * `child` is already in view. Adjusts `scrollTop` directly rather than using * `scrollIntoView`, so the page (window) does not scroll alongside. * * Uses viewport-rect deltas, NOT `offsetTop`: `offsetTop` is relative to the * offset parent, which a theme could change by making a group container * `position: relative` (optgroup), silently breaking the math. Rect deltas are * correct regardless of nesting / theme CSS. `clientTop` / `clientHeight` * exclude the parent's border so a bordered list stays exact. Measured to cost * the same as the old `offsetTop` path (see docs/llm/DESIGN.md "Optgroup"). */ export declare function ensureVisibleInScroll(child: HTMLElement, scrollParent: HTMLElement): void; //# sourceMappingURL=keyboard.d.ts.map