From 09f52e66f1e4d4030f6d02c1e3d320e1c7ef5bb3 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 18 May 2026 12:17:11 +0200 Subject: [PATCH] perf: drop KeyborgProps (triggerKeys/dismissKeys) and dismiss timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KeyborgProps was the only argument to `createKeyborg`/`createKeyborgCore`, gating two features: * `triggerKeys` — a custom set of keys that should opt the window into keyboard-navigation mode in place of "any non-Tab key on a non-editable element". Never used by any known consumer; the default predicate is already the right behaviour for screen-reader and Tab-driven flows. * `dismissKeys` — a key set that scheduled a 500 ms timer to dismiss keyboard mode if focus did not move (typically wired to Escape). This hand-rolled latch duplicated work consumers already do at the application level. Drop the props interface, the `shouldDismiss`/`scheduleDismiss`/ `dismissTimer`/`_dismissTimeout` machinery, and the `triggerKeys` membership check in `shouldTrigger`. `onKeyDown` collapses to a single guard: enter keyboard mode if not already in it and the key should trigger. `KeyborgProps` was never re-exported from `src/index.mts`, so the public surface stays compatible aside from the (still-typed) extra `createKeyborg` parameter going away. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Keyborg.mts | 78 ++++--------------------------------------------- 1 file changed, 5 insertions(+), 73 deletions(-) diff --git a/src/Keyborg.mts b/src/Keyborg.mts index 76d33586f..76c6a5bfc 100644 --- a/src/Keyborg.mts +++ b/src/Keyborg.mts @@ -18,23 +18,8 @@ interface WindowWithKeyborg extends Window { }; } -// When a key from dismissKeys is pressed and the focus is not moved during -// _dismissTimeout ms, keyboard navigation mode is dismissed. -const _dismissTimeout = 500; - let _lastId = 0; -export interface KeyborgProps { - // Keys to be used to trigger keyboard navigation mode. By default, any key - // will trigger it. Could be limited to, for example, just Tab (or Tab and - // arrow keys). - triggerKeys?: number[]; - // Keys to be used to dismiss keyboard navigation mode using keyboard (in - // addition to mouse clicks which dismiss it). For example, Esc could be used - // to dismiss. - dismissKeys?: number[]; -} - export type KeyborgCallback = (isNavigatingWithKeyboard: boolean) => void; /** @@ -83,25 +68,10 @@ interface KeyborgInternal extends Keyborg { dispose(): void; } -function createKeyborgCore( - targetWindow: WindowWithKeyborg, - props?: KeyborgProps, -): KeyborgCoreHandle { +function createKeyborgCore(targetWindow: WindowWithKeyborg): KeyborgCoreHandle { let currentTargetWindow: WindowWithKeyborg | undefined = targetWindow; let isNavigating = false; let isMouseOrTouchUsedTimer: number | undefined; - let dismissTimer: number | undefined; - let triggerKeys: Set | undefined; - let dismissKeys: Set | undefined; - - if (props) { - if (props.triggerKeys?.length) { - triggerKeys = new Set(props.triggerKeys); - } - if (props.dismissKeys?.length) { - dismissKeys = new Set(props.dismissKeys); - } - } const broadcast = (): void => { const refs = currentTargetWindow?.__keyborg?.refs; @@ -128,42 +98,12 @@ function createKeyborgCore( } const active = currentTargetWindow?.document .activeElement as HTMLElement | null; - const isTriggerKey = !triggerKeys || triggerKeys.has(e.keyCode); const isEditable = active && (active.tagName === "INPUT" || active.tagName === "TEXTAREA" || active.isContentEditable); - return isTriggerKey && !isEditable; - }; - - const shouldDismiss = (e: KeyboardEvent): boolean => { - return !!dismissKeys?.has(e.keyCode); - }; - - const scheduleDismiss = (): void => { - const targetWindow = currentTargetWindow; - if (!targetWindow) { - return; - } - if (dismissTimer) { - targetWindow.clearTimeout(dismissTimer); - dismissTimer = undefined; - } - const previousActiveElement = targetWindow.document.activeElement; - dismissTimer = targetWindow.setTimeout(() => { - dismissTimer = undefined; - const currentActiveElement = targetWindow.document.activeElement; - if ( - previousActiveElement && - currentActiveElement && - previousActiveElement === currentActiveElement - ) { - // Esc was pressed, currently focused element hasn't changed. - // Just dismiss the keyboard navigation mode. - setNavigating(false); - } - }, _dismissTimeout); + return !isEditable; }; const onFocusIn = (e: KeyborgFocusInEvent): void => { @@ -216,11 +156,7 @@ function createKeyborgCore( }; const onKeyDown = (e: KeyboardEvent): void => { - if (isNavigating) { - if (shouldDismiss(e)) { - scheduleDismiss(); - } - } else if (shouldTrigger(e)) { + if (!isNavigating && shouldTrigger(e)) { setNavigating(true); } }; @@ -243,10 +179,6 @@ function createKeyborgCore( currentTargetWindow.clearTimeout(isMouseOrTouchUsedTimer); isMouseOrTouchUsedTimer = undefined; } - if (dismissTimer) { - currentTargetWindow.clearTimeout(dismissTimer); - dismissTimer = undefined; - } disposeFocusEvent(currentTargetWindow); const targetDocument = currentTargetWindow.document; removeEventListener( @@ -281,7 +213,7 @@ function createKeyborgCore( }; } -export function createKeyborg(win: Window, props?: KeyborgProps): Keyborg { +export function createKeyborg(win: Window): Keyborg { const kwin = win as WindowWithKeyborg; const id = "k" + ++_lastId; let localWin: WindowWithKeyborg | undefined = kwin; @@ -295,7 +227,7 @@ export function createKeyborg(win: Window, props?: KeyborgProps): Keyborg { if (existing) { core = existing.core; } else { - core = createKeyborgCore(kwin, props); + core = createKeyborgCore(kwin); } const instance: KeyborgInternal = {