keyborg 3.498.0-canary.0 → 3.498.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -37,9 +37,16 @@ function setupFocusEvent(win) {
37
37
  const kwin = win;
38
38
  const doc = kwin.document;
39
39
  const proto = kwin.HTMLElement.prototype;
40
- const origFocus = proto.focus;
41
- if (origFocus.__keyborgNativeFocus) return;
42
- proto.focus = focus;
40
+ const origFocusDescriptor = Object.getOwnPropertyDescriptor(proto, "focus");
41
+ if ((origFocusDescriptor?.value)?.__keyborgNativeFocus) return;
42
+ focus.__keyborgNativeFocus = callNativeFocus;
43
+ focus.__keyborgFocusDescriptor = origFocusDescriptor;
44
+ Object.defineProperty(proto, "focus", {
45
+ configurable: origFocusDescriptor?.configurable ?? true,
46
+ enumerable: origFocusDescriptor?.enumerable ?? false,
47
+ writable: true,
48
+ value: focus
49
+ });
43
50
  const shadowTargets = /* @__PURE__ */ new Set();
44
51
  const focusOutHandler = (e) => {
45
52
  const target = e.target;
@@ -154,14 +161,18 @@ function setupFocusEvent(win) {
154
161
  function focus() {
155
162
  const d = kwin.__keyborgData;
156
163
  if (d) d[LAST_FOCUSED_PROGRAMMATICALLY] = new WeakRef(this);
157
- return origFocus.apply(this, arguments);
164
+ return callNativeFocus.apply(this, arguments);
165
+ }
166
+ function callNativeFocus() {
167
+ const originalFocus = origFocusDescriptor?.get ? origFocusDescriptor.get.call(this) : origFocusDescriptor?.value;
168
+ if (typeof originalFocus !== "function") throw new TypeError("HTMLElement.prototype.focus is not callable");
169
+ return originalFocus.apply(this, arguments);
158
170
  }
159
171
  let activeElement = doc.activeElement;
160
172
  while (activeElement && activeElement.shadowRoot) {
161
173
  onFocusIn(activeElement);
162
174
  activeElement = activeElement.shadowRoot.activeElement;
163
175
  }
164
- focus.__keyborgNativeFocus = origFocus;
165
176
  }
166
177
  /**
167
178
  * Removes keyborg event listeners and custom focus override
@@ -170,7 +181,8 @@ function setupFocusEvent(win) {
170
181
  function disposeFocusEvent(win) {
171
182
  const kwin = win;
172
183
  const proto = kwin.HTMLElement.prototype;
173
- const origFocus = proto.focus.__keyborgNativeFocus;
184
+ const focus = Object.getOwnPropertyDescriptor(proto, "focus")?.value;
185
+ const origFocusDescriptor = focus?.__keyborgFocusDescriptor;
174
186
  const data = kwin.__keyborgData;
175
187
  if (data) {
176
188
  const doc = kwin.document;
@@ -186,7 +198,8 @@ function disposeFocusEvent(win) {
186
198
  data[SHADOW_TARGETS].clear();
187
199
  delete kwin.__keyborgData;
188
200
  }
189
- if (origFocus) proto.focus = origFocus;
201
+ if (focus?.__keyborgNativeFocus) if (origFocusDescriptor) Object.defineProperty(proto, "focus", origFocusDescriptor);
202
+ else delete proto.focus;
190
203
  }
191
204
  /**
192
205
  * @param win The window that stores keyborg focus events
@@ -334,7 +347,7 @@ function disposeKeyborg(instance) {
334
347
  * Copyright (c) Microsoft Corporation. All rights reserved.
335
348
  * Licensed under the MIT License.
336
349
  */
337
- const version = "3.498.0-canary.0";
350
+ const version = "3.498.0";
338
351
  //#endregion
339
352
  exports.KEYBORG_FOCUSIN = KEYBORG_FOCUSIN;
340
353
  exports.KEYBORG_FOCUSOUT = KEYBORG_FOCUSOUT;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/dom.mts","../src/FocusEvent.mts","../src/Keyborg.mts","../src/index.mts"],"sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// All keyborg listeners use the capture phase; centralizing the calls lets the\n// minifier collapse the repeated DOM method names into a single short-named\n// import binding.\nexport const addEventListener = (\n target: EventTarget,\n type: string,\n handler: EventListener,\n): void => {\n target.addEventListener(type, handler, true);\n};\n\nexport const removeEventListener = (\n target: EventTarget,\n type: string,\n handler: EventListener,\n): void => {\n target.removeEventListener(type, handler, true);\n};\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { addEventListener, removeEventListener } from \"./dom.mts\";\n\nexport const KEYBORG_FOCUSIN = \"keyborg:focusin\";\nexport const KEYBORG_FOCUSOUT = \"keyborg:focusout\";\n\ninterface KeyborgFocus {\n /**\n * This is the native `focus` function that is retained so that it can be restored when keyborg is disposed\n */\n __keyborgNativeFocus?: (options?: FocusOptions | undefined) => void;\n}\n\n// Internal data stored on `window.__keyborgData` as a tuple. Nothing outside\n// this module reads these slots, so a tuple drops the property-name strings\n// from the minified output — the indexes below inline as numeric literals.\nconst FOCUS_IN_HANDLER = 0;\nconst FOCUS_OUT_HANDLER = 1;\nconst SHADOW_TARGETS = 2;\nconst LAST_FOCUSED_PROGRAMMATICALLY = 3;\ntype KeyborgFocusEventData = [\n (e: FocusEvent) => void,\n (e: FocusEvent) => void,\n Set<WeakRef<ShadowRoot>>,\n WeakRef<HTMLElement>?,\n];\n\n/**\n * Extends the global window with keyborg focus event data\n */\ninterface WindowWithKeyborgFocusEvent extends Window {\n HTMLElement: typeof HTMLElement;\n __keyborgData?: KeyborgFocusEventData;\n}\n\nexport interface KeyborgFocusInEventDetails {\n relatedTarget?: HTMLElement;\n isFocusedProgrammatically?: boolean;\n originalEvent?: FocusEvent;\n}\n\nexport type KeyborgFocusInEvent = CustomEvent<KeyborgFocusInEventDetails>;\n\nexport interface KeyborgFocusOutEventDetails {\n originalEvent: FocusEvent;\n}\n\nexport type KeyborgFocusOutEvent = CustomEvent<KeyborgFocusOutEventDetails>;\n\n/**\n * Guarantees that the native `focus` will be used\n */\nexport function nativeFocus(element: HTMLElement): void {\n const focus = element.focus as KeyborgFocus;\n\n if (focus.__keyborgNativeFocus) {\n focus.__keyborgNativeFocus.call(element);\n } else {\n element.focus();\n }\n}\n\n/**\n * Overrides the native `focus` and setups the keyborg focus event\n */\nexport function setupFocusEvent(win: Window): void {\n const kwin = win as WindowWithKeyborgFocusEvent;\n const doc = kwin.document;\n const proto = kwin.HTMLElement.prototype;\n const origFocus = proto.focus;\n\n if ((origFocus as KeyborgFocus).__keyborgNativeFocus) {\n // Already set up.\n return;\n }\n\n proto.focus = focus;\n\n const shadowTargets: Set<WeakRef<ShadowRoot>> = new Set();\n\n const focusOutHandler = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n\n if (!target) {\n return;\n }\n\n const event: KeyborgFocusOutEvent = new CustomEvent(KEYBORG_FOCUSOUT, {\n cancelable: true,\n bubbles: true,\n // Allows the event to bubble past an open shadow root\n composed: true,\n detail: {\n originalEvent: e,\n },\n });\n\n target.dispatchEvent(event);\n };\n\n const focusInHandler = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n\n if (!target) {\n return;\n }\n\n let node: Node | null | undefined = e.composedPath()[0] as\n | Node\n | null\n | undefined;\n\n const currentShadows: Set<ShadowRoot> = new Set();\n\n while (node) {\n if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {\n currentShadows.add(node as ShadowRoot);\n node = (node as ShadowRoot).host;\n } else {\n node = node.parentNode;\n }\n }\n\n for (const shadowRootWeakRef of shadowTargets) {\n const shadowRoot = shadowRootWeakRef.deref();\n\n if (!shadowRoot || !currentShadows.has(shadowRoot)) {\n shadowTargets.delete(shadowRootWeakRef);\n\n if (shadowRoot) {\n removeEventListener(shadowRoot, \"focusin\", focusInHandler);\n removeEventListener(shadowRoot, \"focusout\", focusOutHandler);\n }\n }\n }\n\n onFocusIn(target, (e.relatedTarget as HTMLElement | null) || undefined);\n };\n\n const onFocusIn = (\n target: Element,\n relatedTarget?: HTMLElement,\n originalEvent?: FocusEvent,\n ) => {\n const shadowRoot = target.shadowRoot;\n\n if (shadowRoot) {\n /**\n * https://bugs.chromium.org/p/chromium/issues/detail?id=1512028\n * focusin events don't bubble up through an open shadow root once focus is inside\n * once focus moves into a shadow root - we drop the same focusin handler there\n * keyborg's custom event will still bubble up since it is composed\n * event handlers should be cleaned up once focus leaves the shadow root.\n *\n * When a focusin event is dispatched from a shadow root, its target is the shadow root parent.\n * Each shadow root encounter requires a new capture listener.\n * Why capture? - we want to follow the focus event in order or descending nested shadow roots\n * When there are no more shadow root targets - dispatch the keyborg:focusin event\n *\n * 1. no focus event\n * > document - capture listener ✅\n * > shadow root 1\n * > shadow root 2\n * > shadow root 3\n * > focused element\n *\n * 2. focus event received by document listener\n * > document - capture listener ✅ (focus event here)\n * > shadow root 1 - capture listener ✅\n * > shadow root 2\n * > shadow root 3\n * > focused element\n *\n * 3. focus event received by root l1 listener\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅ (focus event here)\n * > shadow root 2 - capture listener ✅\n * > shadow root 3\n * > focused element\n *\n * 4. focus event received by root l2 listener\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅\n * > shadow root 2 - capture listener ✅ (focus event here)\n * > shadow root 3 - capture listener ✅\n * > focused element\n *\n * 5. focus event received by root l3 listener, no more shadow root targets\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅\n * > shadow root 2 - capture listener ✅\n * > shadow root 3 - capture listener ✅ (focus event here)\n * > focused element ✅ (no shadow root - dispatch keyborg event)\n */\n\n for (const shadowRootWeakRef of shadowTargets) {\n if (shadowRootWeakRef.deref() === shadowRoot) {\n return;\n }\n }\n\n addEventListener(shadowRoot, \"focusin\", focusInHandler);\n addEventListener(shadowRoot, \"focusout\", focusOutHandler);\n\n shadowTargets.add(new WeakRef(shadowRoot));\n\n return;\n }\n\n const details: KeyborgFocusInEventDetails = {\n relatedTarget,\n originalEvent,\n };\n\n const event: KeyborgFocusInEvent = new CustomEvent(KEYBORG_FOCUSIN, {\n cancelable: true,\n bubbles: true,\n // Allows the event to bubble past an open shadow root\n composed: true,\n detail: details,\n });\n\n details.isFocusedProgrammatically =\n target === data[LAST_FOCUSED_PROGRAMMATICALLY]?.deref();\n\n data[LAST_FOCUSED_PROGRAMMATICALLY] = undefined;\n\n target.dispatchEvent(event);\n };\n\n const data: KeyborgFocusEventData = [\n focusInHandler,\n focusOutHandler,\n shadowTargets,\n ];\n kwin.__keyborgData = data;\n\n addEventListener(doc, \"focusin\", focusInHandler);\n addEventListener(doc, \"focusout\", focusOutHandler);\n\n function focus(this: HTMLElement) {\n const d = (kwin as WindowWithKeyborgFocusEvent).__keyborgData;\n\n if (d) {\n d[LAST_FOCUSED_PROGRAMMATICALLY] = new WeakRef(this);\n }\n\n // eslint-disable-next-line prefer-rest-params\n return origFocus.apply(this, arguments);\n }\n\n let activeElement = doc.activeElement as Element | null;\n\n // If keyborg is created with the focus inside shadow root, we need\n // to go through the shadows up to make sure all relevant shadows\n // have focus handlers attached.\n while (activeElement && activeElement.shadowRoot) {\n onFocusIn(activeElement);\n activeElement = activeElement.shadowRoot.activeElement;\n }\n\n (focus as KeyborgFocus).__keyborgNativeFocus = origFocus;\n}\n\n/**\n * Removes keyborg event listeners and custom focus override\n * @param win The window that stores keyborg focus events\n */\nexport function disposeFocusEvent(win: Window): void {\n const kwin = win as WindowWithKeyborgFocusEvent;\n const proto = kwin.HTMLElement.prototype;\n const origFocus = (proto.focus as KeyborgFocus).__keyborgNativeFocus;\n const data = kwin.__keyborgData;\n\n if (data) {\n const doc = kwin.document;\n removeEventListener(doc, \"focusin\", data[FOCUS_IN_HANDLER]);\n removeEventListener(doc, \"focusout\", data[FOCUS_OUT_HANDLER]);\n\n for (const shadowRootWeakRef of data[SHADOW_TARGETS]) {\n const shadowRoot = shadowRootWeakRef.deref();\n\n if (shadowRoot) {\n removeEventListener(shadowRoot, \"focusin\", data[FOCUS_IN_HANDLER]);\n removeEventListener(shadowRoot, \"focusout\", data[FOCUS_OUT_HANDLER]);\n }\n }\n\n data[SHADOW_TARGETS].clear();\n\n delete kwin.__keyborgData;\n }\n\n if (origFocus) {\n proto.focus = origFocus;\n }\n}\n\n/**\n * @param win The window that stores keyborg focus events\n * @returns The last element focused with element.focus()\n */\nexport function getLastFocusedProgrammatically(\n win: Window,\n): HTMLElement | null | undefined {\n const data = (win as WindowWithKeyborgFocusEvent).__keyborgData;\n\n return data\n ? data[LAST_FOCUSED_PROGRAMMATICALLY]?.deref() || null\n : undefined;\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { addEventListener, removeEventListener } from \"./dom.mts\";\nimport {\n disposeFocusEvent,\n KeyborgFocusInEvent,\n KEYBORG_FOCUSIN,\n setupFocusEvent,\n} from \"./FocusEvent.mts\";\n\ninterface WindowWithKeyborg extends Window {\n __keyborg?: {\n core: KeyborgCoreHandle;\n refs: { [id: string]: Keyborg };\n };\n}\n\nlet _lastId = 0;\n\nexport type KeyborgCallback = (isNavigatingWithKeyboard: boolean) => void;\n\n/**\n * Internal handle for the per-window core state. Not part of the public API.\n *\n * Shape is part of the `__keyborg.core` slot contract: when multiple keyborg\n * majors share the same window, the second loader reads `core` set by the\n * first. Keep `isNavigatingWithKeyboard` as a writable property accessor and\n * `dispose()` as a method so older majors can still read/write state without\n * TypeError. Changes here are breaking for that interop.\n */\ninterface KeyborgCoreHandle {\n isNavigatingWithKeyboard: boolean;\n dispose(): void;\n}\n\n/**\n * Used to determine the keyboard navigation state.\n */\nexport interface Keyborg {\n /**\n * @returns Whether the user is navigating with keyboard\n */\n isNavigatingWithKeyboard(): boolean;\n /**\n * @param callback - Called when the keyboard navigation state changes\n */\n subscribe(callback: KeyborgCallback): void;\n /**\n * @param callback - Registered with subscribe\n */\n unsubscribe(callback: KeyborgCallback): void;\n /**\n * Manually set the keyboard navigation state\n */\n setVal(isNavigatingWithKeyboard: boolean): void;\n}\n\n// Augments the public Keyborg with internal members that also form the\n// `__keyborg.refs` slot wire protocol. The shape — `_cb` callback array and\n// `dispose()` method — matches keyborg <= 2.6.0's `Keyborg` class so that\n// mixed-version environments can broadcast and tear down each other's\n// instances without TypeError. Changes here are breaking for that interop.\ninterface KeyborgInternal extends Keyborg {\n _cb: KeyborgCallback[];\n dispose(): void;\n}\n\nfunction createKeyborgCore(targetWindow: WindowWithKeyborg): KeyborgCoreHandle {\n let currentTargetWindow: WindowWithKeyborg | undefined = targetWindow;\n let isNavigating = false;\n let isMouseOrTouchUsedTimer: number | undefined;\n\n const broadcast = (): void => {\n const refs = currentTargetWindow?.__keyborg?.refs;\n if (refs) {\n for (const id of Object.keys(refs)) {\n (refs[id] as KeyborgInternal)._cb.forEach((cb) => cb(isNavigating));\n }\n }\n };\n\n const setNavigating = (val: boolean): void => {\n if (isNavigating !== val) {\n isNavigating = val;\n broadcast();\n }\n };\n\n const shouldTrigger = (e: KeyboardEvent): boolean => {\n // TODO Some rich text fields can allow Tab key for indentation so it\n // doesn't need to be a navigation key. If there is a bug regarding that\n // we should revisit.\n if (e.key === \"Tab\") {\n return true;\n }\n const active = currentTargetWindow?.document\n .activeElement as HTMLElement | null;\n const isEditable =\n active &&\n (active.tagName === \"INPUT\" ||\n active.tagName === \"TEXTAREA\" ||\n active.isContentEditable);\n return !isEditable;\n };\n\n const onFocusIn = (e: KeyborgFocusInEvent): void => {\n // When the focus is moved not programmatically and without keydown events,\n // it is likely that the focus is moved by screen reader (as it might\n // swallow the events when the screen reader shortcuts are used). The\n // screen reader usage is keyboard navigation.\n if (isMouseOrTouchUsedTimer) {\n return;\n }\n if (isNavigating) {\n return;\n }\n const details = e.detail;\n if (!details.relatedTarget) {\n return;\n }\n if (\n details.isFocusedProgrammatically ||\n details.isFocusedProgrammatically === undefined\n ) {\n return;\n }\n setNavigating(true);\n };\n\n const onMouseOrTouch = (): void => {\n if (currentTargetWindow) {\n if (isMouseOrTouchUsedTimer) {\n currentTargetWindow.clearTimeout(isMouseOrTouchUsedTimer);\n }\n isMouseOrTouchUsedTimer = currentTargetWindow.setTimeout(() => {\n isMouseOrTouchUsedTimer = undefined;\n }, 1000);\n }\n setNavigating(false);\n };\n\n const onMouseDown = (e: MouseEvent): void => {\n if (\n e.buttons === 0 ||\n (e.clientX === 0 && e.clientY === 0 && e.screenX === 0 && e.screenY === 0)\n ) {\n // This is most likely an event triggered by the screen reader to\n // perform an action on an element, do not dismiss the keyboard\n // navigation mode.\n return;\n }\n onMouseOrTouch();\n };\n\n const onKeyDown = (e: KeyboardEvent): void => {\n if (!isNavigating && shouldTrigger(e)) {\n setNavigating(true);\n }\n };\n\n const targetDocument = targetWindow.document;\n addEventListener(targetDocument, KEYBORG_FOCUSIN, onFocusIn as EventListener);\n addEventListener(targetDocument, \"mousedown\", onMouseDown as EventListener);\n addEventListener(targetWindow, \"keydown\", onKeyDown as EventListener);\n addEventListener(targetDocument, \"touchstart\", onMouseOrTouch);\n addEventListener(targetDocument, \"touchend\", onMouseOrTouch);\n addEventListener(targetDocument, \"touchcancel\", onMouseOrTouch);\n\n setupFocusEvent(targetWindow);\n\n const dispose = (): void => {\n if (!currentTargetWindow) {\n return;\n }\n if (isMouseOrTouchUsedTimer) {\n currentTargetWindow.clearTimeout(isMouseOrTouchUsedTimer);\n isMouseOrTouchUsedTimer = undefined;\n }\n disposeFocusEvent(currentTargetWindow);\n const targetDocument = currentTargetWindow.document;\n removeEventListener(\n targetDocument,\n KEYBORG_FOCUSIN,\n onFocusIn as EventListener,\n );\n removeEventListener(\n targetDocument,\n \"mousedown\",\n onMouseDown as EventListener,\n );\n removeEventListener(\n currentTargetWindow,\n \"keydown\",\n onKeyDown as EventListener,\n );\n removeEventListener(targetDocument, \"touchstart\", onMouseOrTouch);\n removeEventListener(targetDocument, \"touchend\", onMouseOrTouch);\n removeEventListener(targetDocument, \"touchcancel\", onMouseOrTouch);\n currentTargetWindow = undefined;\n };\n\n return {\n dispose,\n get isNavigatingWithKeyboard() {\n return isNavigating;\n },\n set isNavigatingWithKeyboard(val: boolean) {\n setNavigating(val);\n },\n };\n}\n\nexport function createKeyborg(win: Window): Keyborg {\n const kwin = win as WindowWithKeyborg;\n const id = \"k\" + ++_lastId;\n let localWin: WindowWithKeyborg | undefined = kwin;\n let core: KeyborgCoreHandle | undefined;\n // `callbacks` is exposed as `instance._cb` to satisfy the slot wire\n // protocol. We mutate the array in place (push/splice/length=0) so the\n // reference stays stable for foreign-version broadcasts.\n const callbacks: KeyborgCallback[] = [];\n\n const existing = kwin.__keyborg;\n if (existing) {\n core = existing.core;\n } else {\n core = createKeyborgCore(kwin);\n }\n\n const instance: KeyborgInternal = {\n isNavigatingWithKeyboard() {\n return !!core?.isNavigatingWithKeyboard;\n },\n subscribe(callback) {\n callbacks.push(callback);\n },\n unsubscribe(callback) {\n const index = callbacks.indexOf(callback);\n if (index >= 0) {\n callbacks.splice(index, 1);\n }\n },\n setVal(val) {\n if (core) {\n core.isNavigatingWithKeyboard = val;\n }\n },\n _cb: callbacks,\n dispose() {\n const wkb = localWin?.__keyborg;\n if (wkb?.refs[id]) {\n delete wkb.refs[id];\n if (Object.keys(wkb.refs).length === 0) {\n wkb.core.dispose();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n delete localWin!.__keyborg;\n }\n } else if (process.env.NODE_ENV !== \"production\") {\n console.error(`Keyborg instance ${id} is being disposed incorrectly.`);\n }\n callbacks.length = 0;\n core = undefined;\n localWin = undefined;\n },\n };\n\n if (existing) {\n existing.refs[id] = instance;\n } else {\n kwin.__keyborg = {\n core,\n refs: { [id]: instance },\n };\n }\n\n return instance;\n}\n\nexport function disposeKeyborg(instance: Keyborg): void {\n (instance as KeyborgInternal).dispose();\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport type { Keyborg, KeyborgCallback } from \"./Keyborg.mts\";\nexport { createKeyborg, disposeKeyborg } from \"./Keyborg.mts\";\n\nexport type {\n KeyborgFocusInEvent,\n KeyborgFocusInEventDetails,\n KeyborgFocusOutEvent,\n KeyborgFocusOutEventDetails,\n} from \"./FocusEvent.mts\";\nexport {\n getLastFocusedProgrammatically,\n nativeFocus,\n KEYBORG_FOCUSIN,\n KEYBORG_FOCUSOUT,\n} from \"./FocusEvent.mts\";\n\nexport const version = process.env.PKG_VERSION;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/dom.mts","../src/FocusEvent.mts","../src/Keyborg.mts","../src/index.mts"],"sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// All keyborg listeners use the capture phase; centralizing the calls lets the\n// minifier collapse the repeated DOM method names into a single short-named\n// import binding.\nexport const addEventListener = (\n target: EventTarget,\n type: string,\n handler: EventListener,\n): void => {\n target.addEventListener(type, handler, true);\n};\n\nexport const removeEventListener = (\n target: EventTarget,\n type: string,\n handler: EventListener,\n): void => {\n target.removeEventListener(type, handler, true);\n};\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { addEventListener, removeEventListener } from \"./dom.mts\";\n\nexport const KEYBORG_FOCUSIN = \"keyborg:focusin\";\nexport const KEYBORG_FOCUSOUT = \"keyborg:focusout\";\n\ninterface KeyborgFocus {\n /**\n * Calls the original `focus` implementation without marking focus as programmatic.\n */\n __keyborgNativeFocus?: (options?: FocusOptions | undefined) => void;\n /**\n * The original descriptor, retained so it can be restored when keyborg is disposed.\n */\n __keyborgFocusDescriptor?: PropertyDescriptor;\n}\n\n// Internal data stored on `window.__keyborgData` as a tuple. Nothing outside\n// this module reads these slots, so a tuple drops the property-name strings\n// from the minified output — the indexes below inline as numeric literals.\nconst FOCUS_IN_HANDLER = 0;\nconst FOCUS_OUT_HANDLER = 1;\nconst SHADOW_TARGETS = 2;\nconst LAST_FOCUSED_PROGRAMMATICALLY = 3;\ntype KeyborgFocusEventData = [\n (e: FocusEvent) => void,\n (e: FocusEvent) => void,\n Set<WeakRef<ShadowRoot>>,\n WeakRef<HTMLElement>?,\n];\n\n/**\n * Extends the global window with keyborg focus event data\n */\ninterface WindowWithKeyborgFocusEvent extends Window {\n HTMLElement: typeof HTMLElement;\n __keyborgData?: KeyborgFocusEventData;\n}\n\nexport interface KeyborgFocusInEventDetails {\n relatedTarget?: HTMLElement;\n isFocusedProgrammatically?: boolean;\n originalEvent?: FocusEvent;\n}\n\nexport type KeyborgFocusInEvent = CustomEvent<KeyborgFocusInEventDetails>;\n\nexport interface KeyborgFocusOutEventDetails {\n originalEvent: FocusEvent;\n}\n\nexport type KeyborgFocusOutEvent = CustomEvent<KeyborgFocusOutEventDetails>;\n\n/**\n * Guarantees that the native `focus` will be used\n */\nexport function nativeFocus(element: HTMLElement): void {\n const focus = element.focus as KeyborgFocus;\n\n if (focus.__keyborgNativeFocus) {\n focus.__keyborgNativeFocus.call(element);\n } else {\n element.focus();\n }\n}\n\n/**\n * Overrides the native `focus` and setups the keyborg focus event\n */\nexport function setupFocusEvent(win: Window): void {\n const kwin = win as WindowWithKeyborgFocusEvent;\n const doc = kwin.document;\n const proto = kwin.HTMLElement.prototype;\n const origFocusDescriptor = Object.getOwnPropertyDescriptor(proto, \"focus\");\n const currentFocus = origFocusDescriptor?.value as KeyborgFocus | undefined;\n\n if (currentFocus?.__keyborgNativeFocus) {\n // Already set up.\n return;\n }\n\n (focus as KeyborgFocus).__keyborgNativeFocus = callNativeFocus;\n (focus as KeyborgFocus).__keyborgFocusDescriptor = origFocusDescriptor;\n\n Object.defineProperty(proto, \"focus\", {\n configurable: origFocusDescriptor?.configurable ?? true,\n enumerable: origFocusDescriptor?.enumerable ?? false,\n writable: true,\n value: focus,\n });\n\n const shadowTargets: Set<WeakRef<ShadowRoot>> = new Set();\n\n const focusOutHandler = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n\n if (!target) {\n return;\n }\n\n const event: KeyborgFocusOutEvent = new CustomEvent(KEYBORG_FOCUSOUT, {\n cancelable: true,\n bubbles: true,\n // Allows the event to bubble past an open shadow root\n composed: true,\n detail: {\n originalEvent: e,\n },\n });\n\n target.dispatchEvent(event);\n };\n\n const focusInHandler = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n\n if (!target) {\n return;\n }\n\n let node: Node | null | undefined = e.composedPath()[0] as\n Node | null | undefined;\n\n const currentShadows: Set<ShadowRoot> = new Set();\n\n while (node) {\n if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {\n currentShadows.add(node as ShadowRoot);\n node = (node as ShadowRoot).host;\n } else {\n node = node.parentNode;\n }\n }\n\n for (const shadowRootWeakRef of shadowTargets) {\n const shadowRoot = shadowRootWeakRef.deref();\n\n if (!shadowRoot || !currentShadows.has(shadowRoot)) {\n shadowTargets.delete(shadowRootWeakRef);\n\n if (shadowRoot) {\n removeEventListener(shadowRoot, \"focusin\", focusInHandler);\n removeEventListener(shadowRoot, \"focusout\", focusOutHandler);\n }\n }\n }\n\n onFocusIn(target, (e.relatedTarget as HTMLElement | null) || undefined);\n };\n\n const onFocusIn = (\n target: Element,\n relatedTarget?: HTMLElement,\n originalEvent?: FocusEvent,\n ) => {\n const shadowRoot = target.shadowRoot;\n\n if (shadowRoot) {\n /**\n * https://bugs.chromium.org/p/chromium/issues/detail?id=1512028\n * focusin events don't bubble up through an open shadow root once focus is inside\n * once focus moves into a shadow root - we drop the same focusin handler there\n * keyborg's custom event will still bubble up since it is composed\n * event handlers should be cleaned up once focus leaves the shadow root.\n *\n * When a focusin event is dispatched from a shadow root, its target is the shadow root parent.\n * Each shadow root encounter requires a new capture listener.\n * Why capture? - we want to follow the focus event in order or descending nested shadow roots\n * When there are no more shadow root targets - dispatch the keyborg:focusin event\n *\n * 1. no focus event\n * > document - capture listener ✅\n * > shadow root 1\n * > shadow root 2\n * > shadow root 3\n * > focused element\n *\n * 2. focus event received by document listener\n * > document - capture listener ✅ (focus event here)\n * > shadow root 1 - capture listener ✅\n * > shadow root 2\n * > shadow root 3\n * > focused element\n *\n * 3. focus event received by root l1 listener\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅ (focus event here)\n * > shadow root 2 - capture listener ✅\n * > shadow root 3\n * > focused element\n *\n * 4. focus event received by root l2 listener\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅\n * > shadow root 2 - capture listener ✅ (focus event here)\n * > shadow root 3 - capture listener ✅\n * > focused element\n *\n * 5. focus event received by root l3 listener, no more shadow root targets\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅\n * > shadow root 2 - capture listener ✅\n * > shadow root 3 - capture listener ✅ (focus event here)\n * > focused element ✅ (no shadow root - dispatch keyborg event)\n */\n\n for (const shadowRootWeakRef of shadowTargets) {\n if (shadowRootWeakRef.deref() === shadowRoot) {\n return;\n }\n }\n\n addEventListener(shadowRoot, \"focusin\", focusInHandler);\n addEventListener(shadowRoot, \"focusout\", focusOutHandler);\n\n shadowTargets.add(new WeakRef(shadowRoot));\n\n return;\n }\n\n const details: KeyborgFocusInEventDetails = {\n relatedTarget,\n originalEvent,\n };\n\n const event: KeyborgFocusInEvent = new CustomEvent(KEYBORG_FOCUSIN, {\n cancelable: true,\n bubbles: true,\n // Allows the event to bubble past an open shadow root\n composed: true,\n detail: details,\n });\n\n details.isFocusedProgrammatically =\n target === data[LAST_FOCUSED_PROGRAMMATICALLY]?.deref();\n\n data[LAST_FOCUSED_PROGRAMMATICALLY] = undefined;\n\n target.dispatchEvent(event);\n };\n\n const data: KeyborgFocusEventData = [\n focusInHandler,\n focusOutHandler,\n shadowTargets,\n ];\n kwin.__keyborgData = data;\n\n addEventListener(doc, \"focusin\", focusInHandler);\n addEventListener(doc, \"focusout\", focusOutHandler);\n\n function focus(this: HTMLElement) {\n const d = (kwin as WindowWithKeyborgFocusEvent).__keyborgData;\n\n if (d) {\n d[LAST_FOCUSED_PROGRAMMATICALLY] = new WeakRef(this);\n }\n\n // eslint-disable-next-line prefer-rest-params\n return callNativeFocus.apply(this, arguments);\n }\n\n function callNativeFocus(this: HTMLElement) {\n const originalFocus = origFocusDescriptor?.get\n ? origFocusDescriptor.get.call(this)\n : origFocusDescriptor?.value;\n\n if (typeof originalFocus !== \"function\") {\n throw new TypeError(\"HTMLElement.prototype.focus is not callable\");\n }\n\n // eslint-disable-next-line prefer-rest-params\n return originalFocus.apply(this, arguments);\n }\n\n let activeElement = doc.activeElement as Element | null;\n\n // If keyborg is created with the focus inside shadow root, we need\n // to go through the shadows up to make sure all relevant shadows\n // have focus handlers attached.\n while (activeElement && activeElement.shadowRoot) {\n onFocusIn(activeElement);\n activeElement = activeElement.shadowRoot.activeElement;\n }\n}\n\n/**\n * Removes keyborg event listeners and custom focus override\n * @param win The window that stores keyborg focus events\n */\nexport function disposeFocusEvent(win: Window): void {\n const kwin = win as WindowWithKeyborgFocusEvent;\n const proto = kwin.HTMLElement.prototype;\n const focusDescriptor = Object.getOwnPropertyDescriptor(proto, \"focus\");\n const focus = focusDescriptor?.value as KeyborgFocus | undefined;\n const origFocusDescriptor = focus?.__keyborgFocusDescriptor;\n const data = kwin.__keyborgData;\n\n if (data) {\n const doc = kwin.document;\n removeEventListener(doc, \"focusin\", data[FOCUS_IN_HANDLER]);\n removeEventListener(doc, \"focusout\", data[FOCUS_OUT_HANDLER]);\n\n for (const shadowRootWeakRef of data[SHADOW_TARGETS]) {\n const shadowRoot = shadowRootWeakRef.deref();\n\n if (shadowRoot) {\n removeEventListener(shadowRoot, \"focusin\", data[FOCUS_IN_HANDLER]);\n removeEventListener(shadowRoot, \"focusout\", data[FOCUS_OUT_HANDLER]);\n }\n }\n\n data[SHADOW_TARGETS].clear();\n\n delete kwin.__keyborgData;\n }\n\n if (focus?.__keyborgNativeFocus) {\n if (origFocusDescriptor) {\n Object.defineProperty(proto, \"focus\", origFocusDescriptor);\n } else {\n delete proto.focus;\n }\n }\n}\n\n/**\n * @param win The window that stores keyborg focus events\n * @returns The last element focused with element.focus()\n */\nexport function getLastFocusedProgrammatically(\n win: Window,\n): HTMLElement | null | undefined {\n const data = (win as WindowWithKeyborgFocusEvent).__keyborgData;\n\n return data\n ? data[LAST_FOCUSED_PROGRAMMATICALLY]?.deref() || null\n : undefined;\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { addEventListener, removeEventListener } from \"./dom.mts\";\nimport {\n disposeFocusEvent,\n KeyborgFocusInEvent,\n KEYBORG_FOCUSIN,\n setupFocusEvent,\n} from \"./FocusEvent.mts\";\n\ninterface WindowWithKeyborg extends Window {\n __keyborg?: {\n core: KeyborgCoreHandle;\n refs: { [id: string]: Keyborg };\n };\n}\n\nlet _lastId = 0;\n\nexport type KeyborgCallback = (isNavigatingWithKeyboard: boolean) => void;\n\n/**\n * Internal handle for the per-window core state. Not part of the public API.\n *\n * Shape is part of the `__keyborg.core` slot contract: when multiple keyborg\n * majors share the same window, the second loader reads `core` set by the\n * first. Keep `isNavigatingWithKeyboard` as a writable property accessor and\n * `dispose()` as a method so older majors can still read/write state without\n * TypeError. Changes here are breaking for that interop.\n */\ninterface KeyborgCoreHandle {\n isNavigatingWithKeyboard: boolean;\n dispose(): void;\n}\n\n/**\n * Used to determine the keyboard navigation state.\n */\nexport interface Keyborg {\n /**\n * @returns Whether the user is navigating with keyboard\n */\n isNavigatingWithKeyboard(): boolean;\n /**\n * @param callback - Called when the keyboard navigation state changes\n */\n subscribe(callback: KeyborgCallback): void;\n /**\n * @param callback - Registered with subscribe\n */\n unsubscribe(callback: KeyborgCallback): void;\n /**\n * Manually set the keyboard navigation state\n */\n setVal(isNavigatingWithKeyboard: boolean): void;\n}\n\n// Augments the public Keyborg with internal members that also form the\n// `__keyborg.refs` slot wire protocol. The shape — `_cb` callback array and\n// `dispose()` method — matches keyborg <= 2.6.0's `Keyborg` class so that\n// mixed-version environments can broadcast and tear down each other's\n// instances without TypeError. Changes here are breaking for that interop.\ninterface KeyborgInternal extends Keyborg {\n _cb: KeyborgCallback[];\n dispose(): void;\n}\n\nfunction createKeyborgCore(targetWindow: WindowWithKeyborg): KeyborgCoreHandle {\n let currentTargetWindow: WindowWithKeyborg | undefined = targetWindow;\n let isNavigating = false;\n let isMouseOrTouchUsedTimer: number | undefined;\n\n const broadcast = (): void => {\n const refs = currentTargetWindow?.__keyborg?.refs;\n if (refs) {\n for (const id of Object.keys(refs)) {\n (refs[id] as KeyborgInternal)._cb.forEach((cb) => cb(isNavigating));\n }\n }\n };\n\n const setNavigating = (val: boolean): void => {\n if (isNavigating !== val) {\n isNavigating = val;\n broadcast();\n }\n };\n\n const shouldTrigger = (e: KeyboardEvent): boolean => {\n // TODO Some rich text fields can allow Tab key for indentation so it\n // doesn't need to be a navigation key. If there is a bug regarding that\n // we should revisit.\n if (e.key === \"Tab\") {\n return true;\n }\n const active = currentTargetWindow?.document\n .activeElement as HTMLElement | null;\n const isEditable =\n active &&\n (active.tagName === \"INPUT\" ||\n active.tagName === \"TEXTAREA\" ||\n active.isContentEditable);\n return !isEditable;\n };\n\n const onFocusIn = (e: KeyborgFocusInEvent): void => {\n // When the focus is moved not programmatically and without keydown events,\n // it is likely that the focus is moved by screen reader (as it might\n // swallow the events when the screen reader shortcuts are used). The\n // screen reader usage is keyboard navigation.\n if (isMouseOrTouchUsedTimer) {\n return;\n }\n if (isNavigating) {\n return;\n }\n const details = e.detail;\n if (!details.relatedTarget) {\n return;\n }\n if (\n details.isFocusedProgrammatically ||\n details.isFocusedProgrammatically === undefined\n ) {\n return;\n }\n setNavigating(true);\n };\n\n const onMouseOrTouch = (): void => {\n if (currentTargetWindow) {\n if (isMouseOrTouchUsedTimer) {\n currentTargetWindow.clearTimeout(isMouseOrTouchUsedTimer);\n }\n isMouseOrTouchUsedTimer = currentTargetWindow.setTimeout(() => {\n isMouseOrTouchUsedTimer = undefined;\n }, 1000);\n }\n setNavigating(false);\n };\n\n const onMouseDown = (e: MouseEvent): void => {\n if (\n e.buttons === 0 ||\n (e.clientX === 0 && e.clientY === 0 && e.screenX === 0 && e.screenY === 0)\n ) {\n // This is most likely an event triggered by the screen reader to\n // perform an action on an element, do not dismiss the keyboard\n // navigation mode.\n return;\n }\n onMouseOrTouch();\n };\n\n const onKeyDown = (e: KeyboardEvent): void => {\n if (!isNavigating && shouldTrigger(e)) {\n setNavigating(true);\n }\n };\n\n const targetDocument = targetWindow.document;\n addEventListener(targetDocument, KEYBORG_FOCUSIN, onFocusIn as EventListener);\n addEventListener(targetDocument, \"mousedown\", onMouseDown as EventListener);\n addEventListener(targetWindow, \"keydown\", onKeyDown as EventListener);\n addEventListener(targetDocument, \"touchstart\", onMouseOrTouch);\n addEventListener(targetDocument, \"touchend\", onMouseOrTouch);\n addEventListener(targetDocument, \"touchcancel\", onMouseOrTouch);\n\n setupFocusEvent(targetWindow);\n\n const dispose = (): void => {\n if (!currentTargetWindow) {\n return;\n }\n if (isMouseOrTouchUsedTimer) {\n currentTargetWindow.clearTimeout(isMouseOrTouchUsedTimer);\n isMouseOrTouchUsedTimer = undefined;\n }\n disposeFocusEvent(currentTargetWindow);\n const targetDocument = currentTargetWindow.document;\n removeEventListener(\n targetDocument,\n KEYBORG_FOCUSIN,\n onFocusIn as EventListener,\n );\n removeEventListener(\n targetDocument,\n \"mousedown\",\n onMouseDown as EventListener,\n );\n removeEventListener(\n currentTargetWindow,\n \"keydown\",\n onKeyDown as EventListener,\n );\n removeEventListener(targetDocument, \"touchstart\", onMouseOrTouch);\n removeEventListener(targetDocument, \"touchend\", onMouseOrTouch);\n removeEventListener(targetDocument, \"touchcancel\", onMouseOrTouch);\n currentTargetWindow = undefined;\n };\n\n return {\n dispose,\n get isNavigatingWithKeyboard() {\n return isNavigating;\n },\n set isNavigatingWithKeyboard(val: boolean) {\n setNavigating(val);\n },\n };\n}\n\nexport function createKeyborg(win: Window): Keyborg {\n const kwin = win as WindowWithKeyborg;\n const id = \"k\" + ++_lastId;\n let localWin: WindowWithKeyborg | undefined = kwin;\n let core: KeyborgCoreHandle | undefined;\n // `callbacks` is exposed as `instance._cb` to satisfy the slot wire\n // protocol. We mutate the array in place (push/splice/length=0) so the\n // reference stays stable for foreign-version broadcasts.\n const callbacks: KeyborgCallback[] = [];\n\n const existing = kwin.__keyborg;\n if (existing) {\n core = existing.core;\n } else {\n core = createKeyborgCore(kwin);\n }\n\n const instance: KeyborgInternal = {\n isNavigatingWithKeyboard() {\n return !!core?.isNavigatingWithKeyboard;\n },\n subscribe(callback) {\n callbacks.push(callback);\n },\n unsubscribe(callback) {\n const index = callbacks.indexOf(callback);\n if (index >= 0) {\n callbacks.splice(index, 1);\n }\n },\n setVal(val) {\n if (core) {\n core.isNavigatingWithKeyboard = val;\n }\n },\n _cb: callbacks,\n dispose() {\n const wkb = localWin?.__keyborg;\n if (wkb?.refs[id]) {\n delete wkb.refs[id];\n if (Object.keys(wkb.refs).length === 0) {\n wkb.core.dispose();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n delete localWin!.__keyborg;\n }\n } else if (process.env.NODE_ENV !== \"production\") {\n console.error(`Keyborg instance ${id} is being disposed incorrectly.`);\n }\n callbacks.length = 0;\n core = undefined;\n localWin = undefined;\n },\n };\n\n if (existing) {\n existing.refs[id] = instance;\n } else {\n kwin.__keyborg = {\n core,\n refs: { [id]: instance },\n };\n }\n\n return instance;\n}\n\nexport function disposeKeyborg(instance: Keyborg): void {\n (instance as KeyborgInternal).dispose();\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport type { Keyborg, KeyborgCallback } from \"./Keyborg.mts\";\nexport { createKeyborg, disposeKeyborg } from \"./Keyborg.mts\";\n\nexport type {\n KeyborgFocusInEvent,\n KeyborgFocusInEventDetails,\n KeyborgFocusOutEvent,\n KeyborgFocusOutEventDetails,\n} from \"./FocusEvent.mts\";\nexport {\n getLastFocusedProgrammatically,\n nativeFocus,\n KEYBORG_FOCUSIN,\n KEYBORG_FOCUSOUT,\n} from \"./FocusEvent.mts\";\n\nexport const version = process.env.PKG_VERSION;\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -36,9 +36,16 @@ function setupFocusEvent(win) {
36
36
  const kwin = win;
37
37
  const doc = kwin.document;
38
38
  const proto = kwin.HTMLElement.prototype;
39
- const origFocus = proto.focus;
40
- if (origFocus.__keyborgNativeFocus) return;
41
- proto.focus = focus;
39
+ const origFocusDescriptor = Object.getOwnPropertyDescriptor(proto, "focus");
40
+ if ((origFocusDescriptor?.value)?.__keyborgNativeFocus) return;
41
+ focus.__keyborgNativeFocus = callNativeFocus;
42
+ focus.__keyborgFocusDescriptor = origFocusDescriptor;
43
+ Object.defineProperty(proto, "focus", {
44
+ configurable: origFocusDescriptor?.configurable ?? true,
45
+ enumerable: origFocusDescriptor?.enumerable ?? false,
46
+ writable: true,
47
+ value: focus
48
+ });
42
49
  const shadowTargets = /* @__PURE__ */ new Set();
43
50
  const focusOutHandler = (e) => {
44
51
  const target = e.target;
@@ -153,14 +160,18 @@ function setupFocusEvent(win) {
153
160
  function focus() {
154
161
  const d = kwin.__keyborgData;
155
162
  if (d) d[LAST_FOCUSED_PROGRAMMATICALLY] = new WeakRef(this);
156
- return origFocus.apply(this, arguments);
163
+ return callNativeFocus.apply(this, arguments);
164
+ }
165
+ function callNativeFocus() {
166
+ const originalFocus = origFocusDescriptor?.get ? origFocusDescriptor.get.call(this) : origFocusDescriptor?.value;
167
+ if (typeof originalFocus !== "function") throw new TypeError("HTMLElement.prototype.focus is not callable");
168
+ return originalFocus.apply(this, arguments);
157
169
  }
158
170
  let activeElement = doc.activeElement;
159
171
  while (activeElement && activeElement.shadowRoot) {
160
172
  onFocusIn(activeElement);
161
173
  activeElement = activeElement.shadowRoot.activeElement;
162
174
  }
163
- focus.__keyborgNativeFocus = origFocus;
164
175
  }
165
176
  /**
166
177
  * Removes keyborg event listeners and custom focus override
@@ -169,7 +180,8 @@ function setupFocusEvent(win) {
169
180
  function disposeFocusEvent(win) {
170
181
  const kwin = win;
171
182
  const proto = kwin.HTMLElement.prototype;
172
- const origFocus = proto.focus.__keyborgNativeFocus;
183
+ const focus = Object.getOwnPropertyDescriptor(proto, "focus")?.value;
184
+ const origFocusDescriptor = focus?.__keyborgFocusDescriptor;
173
185
  const data = kwin.__keyborgData;
174
186
  if (data) {
175
187
  const doc = kwin.document;
@@ -185,7 +197,8 @@ function disposeFocusEvent(win) {
185
197
  data[SHADOW_TARGETS].clear();
186
198
  delete kwin.__keyborgData;
187
199
  }
188
- if (origFocus) proto.focus = origFocus;
200
+ if (focus?.__keyborgNativeFocus) if (origFocusDescriptor) Object.defineProperty(proto, "focus", origFocusDescriptor);
201
+ else delete proto.focus;
189
202
  }
190
203
  /**
191
204
  * @param win The window that stores keyborg focus events
@@ -333,7 +346,7 @@ function disposeKeyborg(instance) {
333
346
  * Copyright (c) Microsoft Corporation. All rights reserved.
334
347
  * Licensed under the MIT License.
335
348
  */
336
- const version = "3.498.0-canary.0";
349
+ const version = "3.498.0";
337
350
  //#endregion
338
351
  export { KEYBORG_FOCUSIN, KEYBORG_FOCUSOUT, createKeyborg, disposeKeyborg, getLastFocusedProgrammatically, nativeFocus, version };
339
352
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/dom.mts","../src/FocusEvent.mts","../src/Keyborg.mts","../src/index.mts"],"sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// All keyborg listeners use the capture phase; centralizing the calls lets the\n// minifier collapse the repeated DOM method names into a single short-named\n// import binding.\nexport const addEventListener = (\n target: EventTarget,\n type: string,\n handler: EventListener,\n): void => {\n target.addEventListener(type, handler, true);\n};\n\nexport const removeEventListener = (\n target: EventTarget,\n type: string,\n handler: EventListener,\n): void => {\n target.removeEventListener(type, handler, true);\n};\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { addEventListener, removeEventListener } from \"./dom.mts\";\n\nexport const KEYBORG_FOCUSIN = \"keyborg:focusin\";\nexport const KEYBORG_FOCUSOUT = \"keyborg:focusout\";\n\ninterface KeyborgFocus {\n /**\n * This is the native `focus` function that is retained so that it can be restored when keyborg is disposed\n */\n __keyborgNativeFocus?: (options?: FocusOptions | undefined) => void;\n}\n\n// Internal data stored on `window.__keyborgData` as a tuple. Nothing outside\n// this module reads these slots, so a tuple drops the property-name strings\n// from the minified output — the indexes below inline as numeric literals.\nconst FOCUS_IN_HANDLER = 0;\nconst FOCUS_OUT_HANDLER = 1;\nconst SHADOW_TARGETS = 2;\nconst LAST_FOCUSED_PROGRAMMATICALLY = 3;\ntype KeyborgFocusEventData = [\n (e: FocusEvent) => void,\n (e: FocusEvent) => void,\n Set<WeakRef<ShadowRoot>>,\n WeakRef<HTMLElement>?,\n];\n\n/**\n * Extends the global window with keyborg focus event data\n */\ninterface WindowWithKeyborgFocusEvent extends Window {\n HTMLElement: typeof HTMLElement;\n __keyborgData?: KeyborgFocusEventData;\n}\n\nexport interface KeyborgFocusInEventDetails {\n relatedTarget?: HTMLElement;\n isFocusedProgrammatically?: boolean;\n originalEvent?: FocusEvent;\n}\n\nexport type KeyborgFocusInEvent = CustomEvent<KeyborgFocusInEventDetails>;\n\nexport interface KeyborgFocusOutEventDetails {\n originalEvent: FocusEvent;\n}\n\nexport type KeyborgFocusOutEvent = CustomEvent<KeyborgFocusOutEventDetails>;\n\n/**\n * Guarantees that the native `focus` will be used\n */\nexport function nativeFocus(element: HTMLElement): void {\n const focus = element.focus as KeyborgFocus;\n\n if (focus.__keyborgNativeFocus) {\n focus.__keyborgNativeFocus.call(element);\n } else {\n element.focus();\n }\n}\n\n/**\n * Overrides the native `focus` and setups the keyborg focus event\n */\nexport function setupFocusEvent(win: Window): void {\n const kwin = win as WindowWithKeyborgFocusEvent;\n const doc = kwin.document;\n const proto = kwin.HTMLElement.prototype;\n const origFocus = proto.focus;\n\n if ((origFocus as KeyborgFocus).__keyborgNativeFocus) {\n // Already set up.\n return;\n }\n\n proto.focus = focus;\n\n const shadowTargets: Set<WeakRef<ShadowRoot>> = new Set();\n\n const focusOutHandler = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n\n if (!target) {\n return;\n }\n\n const event: KeyborgFocusOutEvent = new CustomEvent(KEYBORG_FOCUSOUT, {\n cancelable: true,\n bubbles: true,\n // Allows the event to bubble past an open shadow root\n composed: true,\n detail: {\n originalEvent: e,\n },\n });\n\n target.dispatchEvent(event);\n };\n\n const focusInHandler = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n\n if (!target) {\n return;\n }\n\n let node: Node | null | undefined = e.composedPath()[0] as\n | Node\n | null\n | undefined;\n\n const currentShadows: Set<ShadowRoot> = new Set();\n\n while (node) {\n if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {\n currentShadows.add(node as ShadowRoot);\n node = (node as ShadowRoot).host;\n } else {\n node = node.parentNode;\n }\n }\n\n for (const shadowRootWeakRef of shadowTargets) {\n const shadowRoot = shadowRootWeakRef.deref();\n\n if (!shadowRoot || !currentShadows.has(shadowRoot)) {\n shadowTargets.delete(shadowRootWeakRef);\n\n if (shadowRoot) {\n removeEventListener(shadowRoot, \"focusin\", focusInHandler);\n removeEventListener(shadowRoot, \"focusout\", focusOutHandler);\n }\n }\n }\n\n onFocusIn(target, (e.relatedTarget as HTMLElement | null) || undefined);\n };\n\n const onFocusIn = (\n target: Element,\n relatedTarget?: HTMLElement,\n originalEvent?: FocusEvent,\n ) => {\n const shadowRoot = target.shadowRoot;\n\n if (shadowRoot) {\n /**\n * https://bugs.chromium.org/p/chromium/issues/detail?id=1512028\n * focusin events don't bubble up through an open shadow root once focus is inside\n * once focus moves into a shadow root - we drop the same focusin handler there\n * keyborg's custom event will still bubble up since it is composed\n * event handlers should be cleaned up once focus leaves the shadow root.\n *\n * When a focusin event is dispatched from a shadow root, its target is the shadow root parent.\n * Each shadow root encounter requires a new capture listener.\n * Why capture? - we want to follow the focus event in order or descending nested shadow roots\n * When there are no more shadow root targets - dispatch the keyborg:focusin event\n *\n * 1. no focus event\n * > document - capture listener ✅\n * > shadow root 1\n * > shadow root 2\n * > shadow root 3\n * > focused element\n *\n * 2. focus event received by document listener\n * > document - capture listener ✅ (focus event here)\n * > shadow root 1 - capture listener ✅\n * > shadow root 2\n * > shadow root 3\n * > focused element\n *\n * 3. focus event received by root l1 listener\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅ (focus event here)\n * > shadow root 2 - capture listener ✅\n * > shadow root 3\n * > focused element\n *\n * 4. focus event received by root l2 listener\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅\n * > shadow root 2 - capture listener ✅ (focus event here)\n * > shadow root 3 - capture listener ✅\n * > focused element\n *\n * 5. focus event received by root l3 listener, no more shadow root targets\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅\n * > shadow root 2 - capture listener ✅\n * > shadow root 3 - capture listener ✅ (focus event here)\n * > focused element ✅ (no shadow root - dispatch keyborg event)\n */\n\n for (const shadowRootWeakRef of shadowTargets) {\n if (shadowRootWeakRef.deref() === shadowRoot) {\n return;\n }\n }\n\n addEventListener(shadowRoot, \"focusin\", focusInHandler);\n addEventListener(shadowRoot, \"focusout\", focusOutHandler);\n\n shadowTargets.add(new WeakRef(shadowRoot));\n\n return;\n }\n\n const details: KeyborgFocusInEventDetails = {\n relatedTarget,\n originalEvent,\n };\n\n const event: KeyborgFocusInEvent = new CustomEvent(KEYBORG_FOCUSIN, {\n cancelable: true,\n bubbles: true,\n // Allows the event to bubble past an open shadow root\n composed: true,\n detail: details,\n });\n\n details.isFocusedProgrammatically =\n target === data[LAST_FOCUSED_PROGRAMMATICALLY]?.deref();\n\n data[LAST_FOCUSED_PROGRAMMATICALLY] = undefined;\n\n target.dispatchEvent(event);\n };\n\n const data: KeyborgFocusEventData = [\n focusInHandler,\n focusOutHandler,\n shadowTargets,\n ];\n kwin.__keyborgData = data;\n\n addEventListener(doc, \"focusin\", focusInHandler);\n addEventListener(doc, \"focusout\", focusOutHandler);\n\n function focus(this: HTMLElement) {\n const d = (kwin as WindowWithKeyborgFocusEvent).__keyborgData;\n\n if (d) {\n d[LAST_FOCUSED_PROGRAMMATICALLY] = new WeakRef(this);\n }\n\n // eslint-disable-next-line prefer-rest-params\n return origFocus.apply(this, arguments);\n }\n\n let activeElement = doc.activeElement as Element | null;\n\n // If keyborg is created with the focus inside shadow root, we need\n // to go through the shadows up to make sure all relevant shadows\n // have focus handlers attached.\n while (activeElement && activeElement.shadowRoot) {\n onFocusIn(activeElement);\n activeElement = activeElement.shadowRoot.activeElement;\n }\n\n (focus as KeyborgFocus).__keyborgNativeFocus = origFocus;\n}\n\n/**\n * Removes keyborg event listeners and custom focus override\n * @param win The window that stores keyborg focus events\n */\nexport function disposeFocusEvent(win: Window): void {\n const kwin = win as WindowWithKeyborgFocusEvent;\n const proto = kwin.HTMLElement.prototype;\n const origFocus = (proto.focus as KeyborgFocus).__keyborgNativeFocus;\n const data = kwin.__keyborgData;\n\n if (data) {\n const doc = kwin.document;\n removeEventListener(doc, \"focusin\", data[FOCUS_IN_HANDLER]);\n removeEventListener(doc, \"focusout\", data[FOCUS_OUT_HANDLER]);\n\n for (const shadowRootWeakRef of data[SHADOW_TARGETS]) {\n const shadowRoot = shadowRootWeakRef.deref();\n\n if (shadowRoot) {\n removeEventListener(shadowRoot, \"focusin\", data[FOCUS_IN_HANDLER]);\n removeEventListener(shadowRoot, \"focusout\", data[FOCUS_OUT_HANDLER]);\n }\n }\n\n data[SHADOW_TARGETS].clear();\n\n delete kwin.__keyborgData;\n }\n\n if (origFocus) {\n proto.focus = origFocus;\n }\n}\n\n/**\n * @param win The window that stores keyborg focus events\n * @returns The last element focused with element.focus()\n */\nexport function getLastFocusedProgrammatically(\n win: Window,\n): HTMLElement | null | undefined {\n const data = (win as WindowWithKeyborgFocusEvent).__keyborgData;\n\n return data\n ? data[LAST_FOCUSED_PROGRAMMATICALLY]?.deref() || null\n : undefined;\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { addEventListener, removeEventListener } from \"./dom.mts\";\nimport {\n disposeFocusEvent,\n KeyborgFocusInEvent,\n KEYBORG_FOCUSIN,\n setupFocusEvent,\n} from \"./FocusEvent.mts\";\n\ninterface WindowWithKeyborg extends Window {\n __keyborg?: {\n core: KeyborgCoreHandle;\n refs: { [id: string]: Keyborg };\n };\n}\n\nlet _lastId = 0;\n\nexport type KeyborgCallback = (isNavigatingWithKeyboard: boolean) => void;\n\n/**\n * Internal handle for the per-window core state. Not part of the public API.\n *\n * Shape is part of the `__keyborg.core` slot contract: when multiple keyborg\n * majors share the same window, the second loader reads `core` set by the\n * first. Keep `isNavigatingWithKeyboard` as a writable property accessor and\n * `dispose()` as a method so older majors can still read/write state without\n * TypeError. Changes here are breaking for that interop.\n */\ninterface KeyborgCoreHandle {\n isNavigatingWithKeyboard: boolean;\n dispose(): void;\n}\n\n/**\n * Used to determine the keyboard navigation state.\n */\nexport interface Keyborg {\n /**\n * @returns Whether the user is navigating with keyboard\n */\n isNavigatingWithKeyboard(): boolean;\n /**\n * @param callback - Called when the keyboard navigation state changes\n */\n subscribe(callback: KeyborgCallback): void;\n /**\n * @param callback - Registered with subscribe\n */\n unsubscribe(callback: KeyborgCallback): void;\n /**\n * Manually set the keyboard navigation state\n */\n setVal(isNavigatingWithKeyboard: boolean): void;\n}\n\n// Augments the public Keyborg with internal members that also form the\n// `__keyborg.refs` slot wire protocol. The shape — `_cb` callback array and\n// `dispose()` method — matches keyborg <= 2.6.0's `Keyborg` class so that\n// mixed-version environments can broadcast and tear down each other's\n// instances without TypeError. Changes here are breaking for that interop.\ninterface KeyborgInternal extends Keyborg {\n _cb: KeyborgCallback[];\n dispose(): void;\n}\n\nfunction createKeyborgCore(targetWindow: WindowWithKeyborg): KeyborgCoreHandle {\n let currentTargetWindow: WindowWithKeyborg | undefined = targetWindow;\n let isNavigating = false;\n let isMouseOrTouchUsedTimer: number | undefined;\n\n const broadcast = (): void => {\n const refs = currentTargetWindow?.__keyborg?.refs;\n if (refs) {\n for (const id of Object.keys(refs)) {\n (refs[id] as KeyborgInternal)._cb.forEach((cb) => cb(isNavigating));\n }\n }\n };\n\n const setNavigating = (val: boolean): void => {\n if (isNavigating !== val) {\n isNavigating = val;\n broadcast();\n }\n };\n\n const shouldTrigger = (e: KeyboardEvent): boolean => {\n // TODO Some rich text fields can allow Tab key for indentation so it\n // doesn't need to be a navigation key. If there is a bug regarding that\n // we should revisit.\n if (e.key === \"Tab\") {\n return true;\n }\n const active = currentTargetWindow?.document\n .activeElement as HTMLElement | null;\n const isEditable =\n active &&\n (active.tagName === \"INPUT\" ||\n active.tagName === \"TEXTAREA\" ||\n active.isContentEditable);\n return !isEditable;\n };\n\n const onFocusIn = (e: KeyborgFocusInEvent): void => {\n // When the focus is moved not programmatically and without keydown events,\n // it is likely that the focus is moved by screen reader (as it might\n // swallow the events when the screen reader shortcuts are used). The\n // screen reader usage is keyboard navigation.\n if (isMouseOrTouchUsedTimer) {\n return;\n }\n if (isNavigating) {\n return;\n }\n const details = e.detail;\n if (!details.relatedTarget) {\n return;\n }\n if (\n details.isFocusedProgrammatically ||\n details.isFocusedProgrammatically === undefined\n ) {\n return;\n }\n setNavigating(true);\n };\n\n const onMouseOrTouch = (): void => {\n if (currentTargetWindow) {\n if (isMouseOrTouchUsedTimer) {\n currentTargetWindow.clearTimeout(isMouseOrTouchUsedTimer);\n }\n isMouseOrTouchUsedTimer = currentTargetWindow.setTimeout(() => {\n isMouseOrTouchUsedTimer = undefined;\n }, 1000);\n }\n setNavigating(false);\n };\n\n const onMouseDown = (e: MouseEvent): void => {\n if (\n e.buttons === 0 ||\n (e.clientX === 0 && e.clientY === 0 && e.screenX === 0 && e.screenY === 0)\n ) {\n // This is most likely an event triggered by the screen reader to\n // perform an action on an element, do not dismiss the keyboard\n // navigation mode.\n return;\n }\n onMouseOrTouch();\n };\n\n const onKeyDown = (e: KeyboardEvent): void => {\n if (!isNavigating && shouldTrigger(e)) {\n setNavigating(true);\n }\n };\n\n const targetDocument = targetWindow.document;\n addEventListener(targetDocument, KEYBORG_FOCUSIN, onFocusIn as EventListener);\n addEventListener(targetDocument, \"mousedown\", onMouseDown as EventListener);\n addEventListener(targetWindow, \"keydown\", onKeyDown as EventListener);\n addEventListener(targetDocument, \"touchstart\", onMouseOrTouch);\n addEventListener(targetDocument, \"touchend\", onMouseOrTouch);\n addEventListener(targetDocument, \"touchcancel\", onMouseOrTouch);\n\n setupFocusEvent(targetWindow);\n\n const dispose = (): void => {\n if (!currentTargetWindow) {\n return;\n }\n if (isMouseOrTouchUsedTimer) {\n currentTargetWindow.clearTimeout(isMouseOrTouchUsedTimer);\n isMouseOrTouchUsedTimer = undefined;\n }\n disposeFocusEvent(currentTargetWindow);\n const targetDocument = currentTargetWindow.document;\n removeEventListener(\n targetDocument,\n KEYBORG_FOCUSIN,\n onFocusIn as EventListener,\n );\n removeEventListener(\n targetDocument,\n \"mousedown\",\n onMouseDown as EventListener,\n );\n removeEventListener(\n currentTargetWindow,\n \"keydown\",\n onKeyDown as EventListener,\n );\n removeEventListener(targetDocument, \"touchstart\", onMouseOrTouch);\n removeEventListener(targetDocument, \"touchend\", onMouseOrTouch);\n removeEventListener(targetDocument, \"touchcancel\", onMouseOrTouch);\n currentTargetWindow = undefined;\n };\n\n return {\n dispose,\n get isNavigatingWithKeyboard() {\n return isNavigating;\n },\n set isNavigatingWithKeyboard(val: boolean) {\n setNavigating(val);\n },\n };\n}\n\nexport function createKeyborg(win: Window): Keyborg {\n const kwin = win as WindowWithKeyborg;\n const id = \"k\" + ++_lastId;\n let localWin: WindowWithKeyborg | undefined = kwin;\n let core: KeyborgCoreHandle | undefined;\n // `callbacks` is exposed as `instance._cb` to satisfy the slot wire\n // protocol. We mutate the array in place (push/splice/length=0) so the\n // reference stays stable for foreign-version broadcasts.\n const callbacks: KeyborgCallback[] = [];\n\n const existing = kwin.__keyborg;\n if (existing) {\n core = existing.core;\n } else {\n core = createKeyborgCore(kwin);\n }\n\n const instance: KeyborgInternal = {\n isNavigatingWithKeyboard() {\n return !!core?.isNavigatingWithKeyboard;\n },\n subscribe(callback) {\n callbacks.push(callback);\n },\n unsubscribe(callback) {\n const index = callbacks.indexOf(callback);\n if (index >= 0) {\n callbacks.splice(index, 1);\n }\n },\n setVal(val) {\n if (core) {\n core.isNavigatingWithKeyboard = val;\n }\n },\n _cb: callbacks,\n dispose() {\n const wkb = localWin?.__keyborg;\n if (wkb?.refs[id]) {\n delete wkb.refs[id];\n if (Object.keys(wkb.refs).length === 0) {\n wkb.core.dispose();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n delete localWin!.__keyborg;\n }\n } else if (process.env.NODE_ENV !== \"production\") {\n console.error(`Keyborg instance ${id} is being disposed incorrectly.`);\n }\n callbacks.length = 0;\n core = undefined;\n localWin = undefined;\n },\n };\n\n if (existing) {\n existing.refs[id] = instance;\n } else {\n kwin.__keyborg = {\n core,\n refs: { [id]: instance },\n };\n }\n\n return instance;\n}\n\nexport function disposeKeyborg(instance: Keyborg): void {\n (instance as KeyborgInternal).dispose();\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport type { Keyborg, KeyborgCallback } from \"./Keyborg.mts\";\nexport { createKeyborg, disposeKeyborg } from \"./Keyborg.mts\";\n\nexport type {\n KeyborgFocusInEvent,\n KeyborgFocusInEventDetails,\n KeyborgFocusOutEvent,\n KeyborgFocusOutEventDetails,\n} from \"./FocusEvent.mts\";\nexport {\n getLastFocusedProgrammatically,\n nativeFocus,\n KEYBORG_FOCUSIN,\n KEYBORG_FOCUSOUT,\n} from \"./FocusEvent.mts\";\n\nexport const version = process.env.PKG_VERSION;\n"],"mappings":";;;;;AAQA,MAAa,oBACX,QACA,MACA,YACS;CACT,OAAO,iBAAiB,MAAM,SAAS,KAAK;;AAG9C,MAAa,uBACX,QACA,MACA,YACS;CACT,OAAO,oBAAoB,MAAM,SAAS,KAAK;;;;;;;;ACdjD,MAAa,kBAAkB;AAC/B,MAAa,mBAAmB;AAYhC,MAAM,mBAAmB;AACzB,MAAM,oBAAoB;AAC1B,MAAM,iBAAiB;AACvB,MAAM,gCAAgC;;;;AAiCtC,SAAgB,YAAY,SAA4B;CACtD,MAAM,QAAQ,QAAQ;CAEtB,IAAI,MAAM,sBACR,MAAM,qBAAqB,KAAK,QAAQ;MAExC,QAAQ,OAAO;;;;;AAOnB,SAAgB,gBAAgB,KAAmB;CACjD,MAAM,OAAO;CACb,MAAM,MAAM,KAAK;CACjB,MAAM,QAAQ,KAAK,YAAY;CAC/B,MAAM,YAAY,MAAM;CAExB,IAAK,UAA2B,sBAE9B;CAGF,MAAM,QAAQ;CAEd,MAAM,gCAA0C,IAAI,KAAK;CAEzD,MAAM,mBAAmB,MAAkB;EACzC,MAAM,SAAS,EAAE;EAEjB,IAAI,CAAC,QACH;EAGF,MAAM,QAA8B,IAAI,YAAY,kBAAkB;GACpE,YAAY;GACZ,SAAS;GAET,UAAU;GACV,QAAQ,EACN,eAAe,GAChB;GACF,CAAC;EAEF,OAAO,cAAc,MAAM;;CAG7B,MAAM,kBAAkB,MAAkB;EACxC,MAAM,SAAS,EAAE;EAEjB,IAAI,CAAC,QACH;EAGF,IAAI,OAAgC,EAAE,cAAc,CAAC;EAKrD,MAAM,iCAAkC,IAAI,KAAK;EAEjD,OAAO,MACL,IAAI,KAAK,aAAa,KAAK,wBAAwB;GACjD,eAAe,IAAI,KAAmB;GACtC,OAAQ,KAAoB;SAE5B,OAAO,KAAK;EAIhB,KAAK,MAAM,qBAAqB,eAAe;GAC7C,MAAM,aAAa,kBAAkB,OAAO;GAE5C,IAAI,CAAC,cAAc,CAAC,eAAe,IAAI,WAAW,EAAE;IAClD,cAAc,OAAO,kBAAkB;IAEvC,IAAI,YAAY;KACd,oBAAoB,YAAY,WAAW,eAAe;KAC1D,oBAAoB,YAAY,YAAY,gBAAgB;;;;EAKlE,UAAU,QAAS,EAAE,iBAAwC,KAAA,EAAU;;CAGzE,MAAM,aACJ,QACA,eACA,kBACG;EACH,MAAM,aAAa,OAAO;EAE1B,IAAI,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDd,KAAK,MAAM,qBAAqB,eAC9B,IAAI,kBAAkB,OAAO,KAAK,YAChC;GAIJ,iBAAiB,YAAY,WAAW,eAAe;GACvD,iBAAiB,YAAY,YAAY,gBAAgB;GAEzD,cAAc,IAAI,IAAI,QAAQ,WAAW,CAAC;GAE1C;;EAGF,MAAM,UAAsC;GAC1C;GACA;GACD;EAED,MAAM,QAA6B,IAAI,YAAY,iBAAiB;GAClE,YAAY;GACZ,SAAS;GAET,UAAU;GACV,QAAQ;GACT,CAAC;EAEF,QAAQ,4BACN,WAAW,KAAK,gCAAgC,OAAO;EAEzD,KAAK,iCAAiC,KAAA;EAEtC,OAAO,cAAc,MAAM;;CAG7B,MAAM,OAA8B;EAClC;EACA;EACA;EACD;CACD,KAAK,gBAAgB;CAErB,iBAAiB,KAAK,WAAW,eAAe;CAChD,iBAAiB,KAAK,YAAY,gBAAgB;CAElD,SAAS,QAAyB;EAChC,MAAM,IAAK,KAAqC;EAEhD,IAAI,GACF,EAAE,iCAAiC,IAAI,QAAQ,KAAK;EAItD,OAAO,UAAU,MAAM,MAAM,UAAU;;CAGzC,IAAI,gBAAgB,IAAI;CAKxB,OAAO,iBAAiB,cAAc,YAAY;EAChD,UAAU,cAAc;EACxB,gBAAgB,cAAc,WAAW;;CAG3C,MAAwB,uBAAuB;;;;;;AAOjD,SAAgB,kBAAkB,KAAmB;CACnD,MAAM,OAAO;CACb,MAAM,QAAQ,KAAK,YAAY;CAC/B,MAAM,YAAa,MAAM,MAAuB;CAChD,MAAM,OAAO,KAAK;CAElB,IAAI,MAAM;EACR,MAAM,MAAM,KAAK;EACjB,oBAAoB,KAAK,WAAW,KAAK,kBAAkB;EAC3D,oBAAoB,KAAK,YAAY,KAAK,mBAAmB;EAE7D,KAAK,MAAM,qBAAqB,KAAK,iBAAiB;GACpD,MAAM,aAAa,kBAAkB,OAAO;GAE5C,IAAI,YAAY;IACd,oBAAoB,YAAY,WAAW,KAAK,kBAAkB;IAClE,oBAAoB,YAAY,YAAY,KAAK,mBAAmB;;;EAIxE,KAAK,gBAAgB,OAAO;EAE5B,OAAO,KAAK;;CAGd,IAAI,WACF,MAAM,QAAQ;;;;;;AAQlB,SAAgB,+BACd,KACgC;CAChC,MAAM,OAAQ,IAAoC;CAElD,OAAO,OACH,KAAK,gCAAgC,OAAO,IAAI,OAChD,KAAA;;;;;;;;ACrSN,IAAI,UAAU;AAkDd,SAAS,kBAAkB,cAAoD;CAC7E,IAAI,sBAAqD;CACzD,IAAI,eAAe;CACnB,IAAI;CAEJ,MAAM,kBAAwB;EAC5B,MAAM,OAAO,qBAAqB,WAAW;EAC7C,IAAI,MACF,KAAK,MAAM,MAAM,OAAO,KAAK,KAAK,EAChC,KAAM,IAAwB,IAAI,SAAS,OAAO,GAAG,aAAa,CAAC;;CAKzE,MAAM,iBAAiB,QAAuB;EAC5C,IAAI,iBAAiB,KAAK;GACxB,eAAe;GACf,WAAW;;;CAIf,MAAM,iBAAiB,MAA8B;EAInD,IAAI,EAAE,QAAQ,OACZ,OAAO;EAET,MAAM,SAAS,qBAAqB,SACjC;EAMH,OAAO,EAJL,WACC,OAAO,YAAY,WAClB,OAAO,YAAY,cACnB,OAAO;;CAIb,MAAM,aAAa,MAAiC;EAKlD,IAAI,yBACF;EAEF,IAAI,cACF;EAEF,MAAM,UAAU,EAAE;EAClB,IAAI,CAAC,QAAQ,eACX;EAEF,IACE,QAAQ,6BACR,QAAQ,8BAA8B,KAAA,GAEtC;EAEF,cAAc,KAAK;;CAGrB,MAAM,uBAA6B;EACjC,IAAI,qBAAqB;GACvB,IAAI,yBACF,oBAAoB,aAAa,wBAAwB;GAE3D,0BAA0B,oBAAoB,iBAAiB;IAC7D,0BAA0B,KAAA;MACzB,IAAK;;EAEV,cAAc,MAAM;;CAGtB,MAAM,eAAe,MAAwB;EAC3C,IACE,EAAE,YAAY,KACb,EAAE,YAAY,KAAK,EAAE,YAAY,KAAK,EAAE,YAAY,KAAK,EAAE,YAAY,GAKxE;EAEF,gBAAgB;;CAGlB,MAAM,aAAa,MAA2B;EAC5C,IAAI,CAAC,gBAAgB,cAAc,EAAE,EACnC,cAAc,KAAK;;CAIvB,MAAM,iBAAiB,aAAa;CACpC,iBAAiB,gBAAgB,iBAAiB,UAA2B;CAC7E,iBAAiB,gBAAgB,aAAa,YAA6B;CAC3E,iBAAiB,cAAc,WAAW,UAA2B;CACrE,iBAAiB,gBAAgB,cAAc,eAAe;CAC9D,iBAAiB,gBAAgB,YAAY,eAAe;CAC5D,iBAAiB,gBAAgB,eAAe,eAAe;CAE/D,gBAAgB,aAAa;CAE7B,MAAM,gBAAsB;EAC1B,IAAI,CAAC,qBACH;EAEF,IAAI,yBAAyB;GAC3B,oBAAoB,aAAa,wBAAwB;GACzD,0BAA0B,KAAA;;EAE5B,kBAAkB,oBAAoB;EACtC,MAAM,iBAAiB,oBAAoB;EAC3C,oBACE,gBACA,iBACA,UACD;EACD,oBACE,gBACA,aACA,YACD;EACD,oBACE,qBACA,WACA,UACD;EACD,oBAAoB,gBAAgB,cAAc,eAAe;EACjE,oBAAoB,gBAAgB,YAAY,eAAe;EAC/D,oBAAoB,gBAAgB,eAAe,eAAe;EAClE,sBAAsB,KAAA;;CAGxB,OAAO;EACL;EACA,IAAI,2BAA2B;GAC7B,OAAO;;EAET,IAAI,yBAAyB,KAAc;GACzC,cAAc,IAAI;;EAErB;;AAGH,SAAgB,cAAc,KAAsB;CAClD,MAAM,OAAO;CACb,MAAM,KAAK,MAAM,EAAE;CACnB,IAAI,WAA0C;CAC9C,IAAI;CAIJ,MAAM,YAA+B,EAAE;CAEvC,MAAM,WAAW,KAAK;CACtB,IAAI,UACF,OAAO,SAAS;MAEhB,OAAO,kBAAkB,KAAK;CAGhC,MAAM,WAA4B;EAChC,2BAA2B;GACzB,OAAO,CAAC,CAAC,MAAM;;EAEjB,UAAU,UAAU;GAClB,UAAU,KAAK,SAAS;;EAE1B,YAAY,UAAU;GACpB,MAAM,QAAQ,UAAU,QAAQ,SAAS;GACzC,IAAI,SAAS,GACX,UAAU,OAAO,OAAO,EAAE;;EAG9B,OAAO,KAAK;GACV,IAAI,MACF,KAAK,2BAA2B;;EAGpC,KAAK;EACL,UAAU;GACR,MAAM,MAAM,UAAU;GACtB,IAAI,KAAK,KAAK,KAAK;IACjB,OAAO,IAAI,KAAK;IAChB,IAAI,OAAO,KAAK,IAAI,KAAK,CAAC,WAAW,GAAG;KACtC,IAAI,KAAK,SAAS;KAElB,OAAO,SAAU;;UAEd,IAAI,QAAQ,IAAI,aAAa,cAClC,QAAQ,MAAM,oBAAoB,GAAG,iCAAiC;GAExE,UAAU,SAAS;GACnB,OAAO,KAAA;GACP,WAAW,KAAA;;EAEd;CAED,IAAI,UACF,SAAS,KAAK,MAAM;MAEpB,KAAK,YAAY;EACf;EACA,MAAM,GAAG,KAAK,UAAU;EACzB;CAGH,OAAO;;AAGT,SAAgB,eAAe,UAAyB;CACtD,SAA8B,SAAS;;;;;;;;ACrQzC,MAAa,UAAA"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/dom.mts","../src/FocusEvent.mts","../src/Keyborg.mts","../src/index.mts"],"sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// All keyborg listeners use the capture phase; centralizing the calls lets the\n// minifier collapse the repeated DOM method names into a single short-named\n// import binding.\nexport const addEventListener = (\n target: EventTarget,\n type: string,\n handler: EventListener,\n): void => {\n target.addEventListener(type, handler, true);\n};\n\nexport const removeEventListener = (\n target: EventTarget,\n type: string,\n handler: EventListener,\n): void => {\n target.removeEventListener(type, handler, true);\n};\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { addEventListener, removeEventListener } from \"./dom.mts\";\n\nexport const KEYBORG_FOCUSIN = \"keyborg:focusin\";\nexport const KEYBORG_FOCUSOUT = \"keyborg:focusout\";\n\ninterface KeyborgFocus {\n /**\n * Calls the original `focus` implementation without marking focus as programmatic.\n */\n __keyborgNativeFocus?: (options?: FocusOptions | undefined) => void;\n /**\n * The original descriptor, retained so it can be restored when keyborg is disposed.\n */\n __keyborgFocusDescriptor?: PropertyDescriptor;\n}\n\n// Internal data stored on `window.__keyborgData` as a tuple. Nothing outside\n// this module reads these slots, so a tuple drops the property-name strings\n// from the minified output — the indexes below inline as numeric literals.\nconst FOCUS_IN_HANDLER = 0;\nconst FOCUS_OUT_HANDLER = 1;\nconst SHADOW_TARGETS = 2;\nconst LAST_FOCUSED_PROGRAMMATICALLY = 3;\ntype KeyborgFocusEventData = [\n (e: FocusEvent) => void,\n (e: FocusEvent) => void,\n Set<WeakRef<ShadowRoot>>,\n WeakRef<HTMLElement>?,\n];\n\n/**\n * Extends the global window with keyborg focus event data\n */\ninterface WindowWithKeyborgFocusEvent extends Window {\n HTMLElement: typeof HTMLElement;\n __keyborgData?: KeyborgFocusEventData;\n}\n\nexport interface KeyborgFocusInEventDetails {\n relatedTarget?: HTMLElement;\n isFocusedProgrammatically?: boolean;\n originalEvent?: FocusEvent;\n}\n\nexport type KeyborgFocusInEvent = CustomEvent<KeyborgFocusInEventDetails>;\n\nexport interface KeyborgFocusOutEventDetails {\n originalEvent: FocusEvent;\n}\n\nexport type KeyborgFocusOutEvent = CustomEvent<KeyborgFocusOutEventDetails>;\n\n/**\n * Guarantees that the native `focus` will be used\n */\nexport function nativeFocus(element: HTMLElement): void {\n const focus = element.focus as KeyborgFocus;\n\n if (focus.__keyborgNativeFocus) {\n focus.__keyborgNativeFocus.call(element);\n } else {\n element.focus();\n }\n}\n\n/**\n * Overrides the native `focus` and setups the keyborg focus event\n */\nexport function setupFocusEvent(win: Window): void {\n const kwin = win as WindowWithKeyborgFocusEvent;\n const doc = kwin.document;\n const proto = kwin.HTMLElement.prototype;\n const origFocusDescriptor = Object.getOwnPropertyDescriptor(proto, \"focus\");\n const currentFocus = origFocusDescriptor?.value as KeyborgFocus | undefined;\n\n if (currentFocus?.__keyborgNativeFocus) {\n // Already set up.\n return;\n }\n\n (focus as KeyborgFocus).__keyborgNativeFocus = callNativeFocus;\n (focus as KeyborgFocus).__keyborgFocusDescriptor = origFocusDescriptor;\n\n Object.defineProperty(proto, \"focus\", {\n configurable: origFocusDescriptor?.configurable ?? true,\n enumerable: origFocusDescriptor?.enumerable ?? false,\n writable: true,\n value: focus,\n });\n\n const shadowTargets: Set<WeakRef<ShadowRoot>> = new Set();\n\n const focusOutHandler = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n\n if (!target) {\n return;\n }\n\n const event: KeyborgFocusOutEvent = new CustomEvent(KEYBORG_FOCUSOUT, {\n cancelable: true,\n bubbles: true,\n // Allows the event to bubble past an open shadow root\n composed: true,\n detail: {\n originalEvent: e,\n },\n });\n\n target.dispatchEvent(event);\n };\n\n const focusInHandler = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n\n if (!target) {\n return;\n }\n\n let node: Node | null | undefined = e.composedPath()[0] as\n Node | null | undefined;\n\n const currentShadows: Set<ShadowRoot> = new Set();\n\n while (node) {\n if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {\n currentShadows.add(node as ShadowRoot);\n node = (node as ShadowRoot).host;\n } else {\n node = node.parentNode;\n }\n }\n\n for (const shadowRootWeakRef of shadowTargets) {\n const shadowRoot = shadowRootWeakRef.deref();\n\n if (!shadowRoot || !currentShadows.has(shadowRoot)) {\n shadowTargets.delete(shadowRootWeakRef);\n\n if (shadowRoot) {\n removeEventListener(shadowRoot, \"focusin\", focusInHandler);\n removeEventListener(shadowRoot, \"focusout\", focusOutHandler);\n }\n }\n }\n\n onFocusIn(target, (e.relatedTarget as HTMLElement | null) || undefined);\n };\n\n const onFocusIn = (\n target: Element,\n relatedTarget?: HTMLElement,\n originalEvent?: FocusEvent,\n ) => {\n const shadowRoot = target.shadowRoot;\n\n if (shadowRoot) {\n /**\n * https://bugs.chromium.org/p/chromium/issues/detail?id=1512028\n * focusin events don't bubble up through an open shadow root once focus is inside\n * once focus moves into a shadow root - we drop the same focusin handler there\n * keyborg's custom event will still bubble up since it is composed\n * event handlers should be cleaned up once focus leaves the shadow root.\n *\n * When a focusin event is dispatched from a shadow root, its target is the shadow root parent.\n * Each shadow root encounter requires a new capture listener.\n * Why capture? - we want to follow the focus event in order or descending nested shadow roots\n * When there are no more shadow root targets - dispatch the keyborg:focusin event\n *\n * 1. no focus event\n * > document - capture listener ✅\n * > shadow root 1\n * > shadow root 2\n * > shadow root 3\n * > focused element\n *\n * 2. focus event received by document listener\n * > document - capture listener ✅ (focus event here)\n * > shadow root 1 - capture listener ✅\n * > shadow root 2\n * > shadow root 3\n * > focused element\n *\n * 3. focus event received by root l1 listener\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅ (focus event here)\n * > shadow root 2 - capture listener ✅\n * > shadow root 3\n * > focused element\n *\n * 4. focus event received by root l2 listener\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅\n * > shadow root 2 - capture listener ✅ (focus event here)\n * > shadow root 3 - capture listener ✅\n * > focused element\n *\n * 5. focus event received by root l3 listener, no more shadow root targets\n * > document - capture listener ✅\n * > shadow root 1 - capture listener ✅\n * > shadow root 2 - capture listener ✅\n * > shadow root 3 - capture listener ✅ (focus event here)\n * > focused element ✅ (no shadow root - dispatch keyborg event)\n */\n\n for (const shadowRootWeakRef of shadowTargets) {\n if (shadowRootWeakRef.deref() === shadowRoot) {\n return;\n }\n }\n\n addEventListener(shadowRoot, \"focusin\", focusInHandler);\n addEventListener(shadowRoot, \"focusout\", focusOutHandler);\n\n shadowTargets.add(new WeakRef(shadowRoot));\n\n return;\n }\n\n const details: KeyborgFocusInEventDetails = {\n relatedTarget,\n originalEvent,\n };\n\n const event: KeyborgFocusInEvent = new CustomEvent(KEYBORG_FOCUSIN, {\n cancelable: true,\n bubbles: true,\n // Allows the event to bubble past an open shadow root\n composed: true,\n detail: details,\n });\n\n details.isFocusedProgrammatically =\n target === data[LAST_FOCUSED_PROGRAMMATICALLY]?.deref();\n\n data[LAST_FOCUSED_PROGRAMMATICALLY] = undefined;\n\n target.dispatchEvent(event);\n };\n\n const data: KeyborgFocusEventData = [\n focusInHandler,\n focusOutHandler,\n shadowTargets,\n ];\n kwin.__keyborgData = data;\n\n addEventListener(doc, \"focusin\", focusInHandler);\n addEventListener(doc, \"focusout\", focusOutHandler);\n\n function focus(this: HTMLElement) {\n const d = (kwin as WindowWithKeyborgFocusEvent).__keyborgData;\n\n if (d) {\n d[LAST_FOCUSED_PROGRAMMATICALLY] = new WeakRef(this);\n }\n\n // eslint-disable-next-line prefer-rest-params\n return callNativeFocus.apply(this, arguments);\n }\n\n function callNativeFocus(this: HTMLElement) {\n const originalFocus = origFocusDescriptor?.get\n ? origFocusDescriptor.get.call(this)\n : origFocusDescriptor?.value;\n\n if (typeof originalFocus !== \"function\") {\n throw new TypeError(\"HTMLElement.prototype.focus is not callable\");\n }\n\n // eslint-disable-next-line prefer-rest-params\n return originalFocus.apply(this, arguments);\n }\n\n let activeElement = doc.activeElement as Element | null;\n\n // If keyborg is created with the focus inside shadow root, we need\n // to go through the shadows up to make sure all relevant shadows\n // have focus handlers attached.\n while (activeElement && activeElement.shadowRoot) {\n onFocusIn(activeElement);\n activeElement = activeElement.shadowRoot.activeElement;\n }\n}\n\n/**\n * Removes keyborg event listeners and custom focus override\n * @param win The window that stores keyborg focus events\n */\nexport function disposeFocusEvent(win: Window): void {\n const kwin = win as WindowWithKeyborgFocusEvent;\n const proto = kwin.HTMLElement.prototype;\n const focusDescriptor = Object.getOwnPropertyDescriptor(proto, \"focus\");\n const focus = focusDescriptor?.value as KeyborgFocus | undefined;\n const origFocusDescriptor = focus?.__keyborgFocusDescriptor;\n const data = kwin.__keyborgData;\n\n if (data) {\n const doc = kwin.document;\n removeEventListener(doc, \"focusin\", data[FOCUS_IN_HANDLER]);\n removeEventListener(doc, \"focusout\", data[FOCUS_OUT_HANDLER]);\n\n for (const shadowRootWeakRef of data[SHADOW_TARGETS]) {\n const shadowRoot = shadowRootWeakRef.deref();\n\n if (shadowRoot) {\n removeEventListener(shadowRoot, \"focusin\", data[FOCUS_IN_HANDLER]);\n removeEventListener(shadowRoot, \"focusout\", data[FOCUS_OUT_HANDLER]);\n }\n }\n\n data[SHADOW_TARGETS].clear();\n\n delete kwin.__keyborgData;\n }\n\n if (focus?.__keyborgNativeFocus) {\n if (origFocusDescriptor) {\n Object.defineProperty(proto, \"focus\", origFocusDescriptor);\n } else {\n delete proto.focus;\n }\n }\n}\n\n/**\n * @param win The window that stores keyborg focus events\n * @returns The last element focused with element.focus()\n */\nexport function getLastFocusedProgrammatically(\n win: Window,\n): HTMLElement | null | undefined {\n const data = (win as WindowWithKeyborgFocusEvent).__keyborgData;\n\n return data\n ? data[LAST_FOCUSED_PROGRAMMATICALLY]?.deref() || null\n : undefined;\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { addEventListener, removeEventListener } from \"./dom.mts\";\nimport {\n disposeFocusEvent,\n KeyborgFocusInEvent,\n KEYBORG_FOCUSIN,\n setupFocusEvent,\n} from \"./FocusEvent.mts\";\n\ninterface WindowWithKeyborg extends Window {\n __keyborg?: {\n core: KeyborgCoreHandle;\n refs: { [id: string]: Keyborg };\n };\n}\n\nlet _lastId = 0;\n\nexport type KeyborgCallback = (isNavigatingWithKeyboard: boolean) => void;\n\n/**\n * Internal handle for the per-window core state. Not part of the public API.\n *\n * Shape is part of the `__keyborg.core` slot contract: when multiple keyborg\n * majors share the same window, the second loader reads `core` set by the\n * first. Keep `isNavigatingWithKeyboard` as a writable property accessor and\n * `dispose()` as a method so older majors can still read/write state without\n * TypeError. Changes here are breaking for that interop.\n */\ninterface KeyborgCoreHandle {\n isNavigatingWithKeyboard: boolean;\n dispose(): void;\n}\n\n/**\n * Used to determine the keyboard navigation state.\n */\nexport interface Keyborg {\n /**\n * @returns Whether the user is navigating with keyboard\n */\n isNavigatingWithKeyboard(): boolean;\n /**\n * @param callback - Called when the keyboard navigation state changes\n */\n subscribe(callback: KeyborgCallback): void;\n /**\n * @param callback - Registered with subscribe\n */\n unsubscribe(callback: KeyborgCallback): void;\n /**\n * Manually set the keyboard navigation state\n */\n setVal(isNavigatingWithKeyboard: boolean): void;\n}\n\n// Augments the public Keyborg with internal members that also form the\n// `__keyborg.refs` slot wire protocol. The shape — `_cb` callback array and\n// `dispose()` method — matches keyborg <= 2.6.0's `Keyborg` class so that\n// mixed-version environments can broadcast and tear down each other's\n// instances without TypeError. Changes here are breaking for that interop.\ninterface KeyborgInternal extends Keyborg {\n _cb: KeyborgCallback[];\n dispose(): void;\n}\n\nfunction createKeyborgCore(targetWindow: WindowWithKeyborg): KeyborgCoreHandle {\n let currentTargetWindow: WindowWithKeyborg | undefined = targetWindow;\n let isNavigating = false;\n let isMouseOrTouchUsedTimer: number | undefined;\n\n const broadcast = (): void => {\n const refs = currentTargetWindow?.__keyborg?.refs;\n if (refs) {\n for (const id of Object.keys(refs)) {\n (refs[id] as KeyborgInternal)._cb.forEach((cb) => cb(isNavigating));\n }\n }\n };\n\n const setNavigating = (val: boolean): void => {\n if (isNavigating !== val) {\n isNavigating = val;\n broadcast();\n }\n };\n\n const shouldTrigger = (e: KeyboardEvent): boolean => {\n // TODO Some rich text fields can allow Tab key for indentation so it\n // doesn't need to be a navigation key. If there is a bug regarding that\n // we should revisit.\n if (e.key === \"Tab\") {\n return true;\n }\n const active = currentTargetWindow?.document\n .activeElement as HTMLElement | null;\n const isEditable =\n active &&\n (active.tagName === \"INPUT\" ||\n active.tagName === \"TEXTAREA\" ||\n active.isContentEditable);\n return !isEditable;\n };\n\n const onFocusIn = (e: KeyborgFocusInEvent): void => {\n // When the focus is moved not programmatically and without keydown events,\n // it is likely that the focus is moved by screen reader (as it might\n // swallow the events when the screen reader shortcuts are used). The\n // screen reader usage is keyboard navigation.\n if (isMouseOrTouchUsedTimer) {\n return;\n }\n if (isNavigating) {\n return;\n }\n const details = e.detail;\n if (!details.relatedTarget) {\n return;\n }\n if (\n details.isFocusedProgrammatically ||\n details.isFocusedProgrammatically === undefined\n ) {\n return;\n }\n setNavigating(true);\n };\n\n const onMouseOrTouch = (): void => {\n if (currentTargetWindow) {\n if (isMouseOrTouchUsedTimer) {\n currentTargetWindow.clearTimeout(isMouseOrTouchUsedTimer);\n }\n isMouseOrTouchUsedTimer = currentTargetWindow.setTimeout(() => {\n isMouseOrTouchUsedTimer = undefined;\n }, 1000);\n }\n setNavigating(false);\n };\n\n const onMouseDown = (e: MouseEvent): void => {\n if (\n e.buttons === 0 ||\n (e.clientX === 0 && e.clientY === 0 && e.screenX === 0 && e.screenY === 0)\n ) {\n // This is most likely an event triggered by the screen reader to\n // perform an action on an element, do not dismiss the keyboard\n // navigation mode.\n return;\n }\n onMouseOrTouch();\n };\n\n const onKeyDown = (e: KeyboardEvent): void => {\n if (!isNavigating && shouldTrigger(e)) {\n setNavigating(true);\n }\n };\n\n const targetDocument = targetWindow.document;\n addEventListener(targetDocument, KEYBORG_FOCUSIN, onFocusIn as EventListener);\n addEventListener(targetDocument, \"mousedown\", onMouseDown as EventListener);\n addEventListener(targetWindow, \"keydown\", onKeyDown as EventListener);\n addEventListener(targetDocument, \"touchstart\", onMouseOrTouch);\n addEventListener(targetDocument, \"touchend\", onMouseOrTouch);\n addEventListener(targetDocument, \"touchcancel\", onMouseOrTouch);\n\n setupFocusEvent(targetWindow);\n\n const dispose = (): void => {\n if (!currentTargetWindow) {\n return;\n }\n if (isMouseOrTouchUsedTimer) {\n currentTargetWindow.clearTimeout(isMouseOrTouchUsedTimer);\n isMouseOrTouchUsedTimer = undefined;\n }\n disposeFocusEvent(currentTargetWindow);\n const targetDocument = currentTargetWindow.document;\n removeEventListener(\n targetDocument,\n KEYBORG_FOCUSIN,\n onFocusIn as EventListener,\n );\n removeEventListener(\n targetDocument,\n \"mousedown\",\n onMouseDown as EventListener,\n );\n removeEventListener(\n currentTargetWindow,\n \"keydown\",\n onKeyDown as EventListener,\n );\n removeEventListener(targetDocument, \"touchstart\", onMouseOrTouch);\n removeEventListener(targetDocument, \"touchend\", onMouseOrTouch);\n removeEventListener(targetDocument, \"touchcancel\", onMouseOrTouch);\n currentTargetWindow = undefined;\n };\n\n return {\n dispose,\n get isNavigatingWithKeyboard() {\n return isNavigating;\n },\n set isNavigatingWithKeyboard(val: boolean) {\n setNavigating(val);\n },\n };\n}\n\nexport function createKeyborg(win: Window): Keyborg {\n const kwin = win as WindowWithKeyborg;\n const id = \"k\" + ++_lastId;\n let localWin: WindowWithKeyborg | undefined = kwin;\n let core: KeyborgCoreHandle | undefined;\n // `callbacks` is exposed as `instance._cb` to satisfy the slot wire\n // protocol. We mutate the array in place (push/splice/length=0) so the\n // reference stays stable for foreign-version broadcasts.\n const callbacks: KeyborgCallback[] = [];\n\n const existing = kwin.__keyborg;\n if (existing) {\n core = existing.core;\n } else {\n core = createKeyborgCore(kwin);\n }\n\n const instance: KeyborgInternal = {\n isNavigatingWithKeyboard() {\n return !!core?.isNavigatingWithKeyboard;\n },\n subscribe(callback) {\n callbacks.push(callback);\n },\n unsubscribe(callback) {\n const index = callbacks.indexOf(callback);\n if (index >= 0) {\n callbacks.splice(index, 1);\n }\n },\n setVal(val) {\n if (core) {\n core.isNavigatingWithKeyboard = val;\n }\n },\n _cb: callbacks,\n dispose() {\n const wkb = localWin?.__keyborg;\n if (wkb?.refs[id]) {\n delete wkb.refs[id];\n if (Object.keys(wkb.refs).length === 0) {\n wkb.core.dispose();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n delete localWin!.__keyborg;\n }\n } else if (process.env.NODE_ENV !== \"production\") {\n console.error(`Keyborg instance ${id} is being disposed incorrectly.`);\n }\n callbacks.length = 0;\n core = undefined;\n localWin = undefined;\n },\n };\n\n if (existing) {\n existing.refs[id] = instance;\n } else {\n kwin.__keyborg = {\n core,\n refs: { [id]: instance },\n };\n }\n\n return instance;\n}\n\nexport function disposeKeyborg(instance: Keyborg): void {\n (instance as KeyborgInternal).dispose();\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport type { Keyborg, KeyborgCallback } from \"./Keyborg.mts\";\nexport { createKeyborg, disposeKeyborg } from \"./Keyborg.mts\";\n\nexport type {\n KeyborgFocusInEvent,\n KeyborgFocusInEventDetails,\n KeyborgFocusOutEvent,\n KeyborgFocusOutEventDetails,\n} from \"./FocusEvent.mts\";\nexport {\n getLastFocusedProgrammatically,\n nativeFocus,\n KEYBORG_FOCUSIN,\n KEYBORG_FOCUSOUT,\n} from \"./FocusEvent.mts\";\n\nexport const version = process.env.PKG_VERSION;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keyborg",
3
- "version": "3.498.0-canary.0",
3
+ "version": "3.498.0",
4
4
  "description": "Keyboard Navigation Detection for Web",
5
5
  "author": "Marat Abdullin <marata@microsoft.com>",
6
6
  "license": "MIT",
@@ -38,31 +38,31 @@
38
38
  },
39
39
  "devDependencies": {
40
40
  "@eslint/js": "^10.0.1",
41
- "@playwright/test": "^1.60.0",
42
- "@storybook/react-vite": "^10.4.0",
41
+ "@playwright/test": "^1.62.0",
42
+ "@storybook/react-vite": "^10.5.5",
43
43
  "@types/prop-types": "^15",
44
44
  "@types/react": "^19.2.14",
45
45
  "@types/react-dom": "^19.2.3",
46
46
  "@vitejs/plugin-react": "^6",
47
- "eslint": "^10.4.0",
47
+ "eslint": "^10.8.0",
48
48
  "eslint-config-prettier": "^10.1.8",
49
49
  "eslint-import-resolver-typescript": "^4.4.4",
50
- "eslint-plugin-import-x": "^4.16.2",
51
- "monosize": "^0.9.0",
52
- "monosize-bundler-webpack": "^0.4.0",
53
- "monosize-storage-git": "^0.3.4",
54
- "playwright": "^1.60.0",
55
- "prettier": "^3.8.1",
50
+ "eslint-plugin-import-x": "^4.17.0",
51
+ "monosize": "^0.10.0",
52
+ "monosize-bundler-webpack": "^0.4.1",
53
+ "monosize-storage-git": "^0.3.5",
54
+ "playwright": "^1.62.0",
55
+ "prettier": "^3.9.4",
56
56
  "prop-types": "^15",
57
57
  "react": "^19.2.6",
58
58
  "react-dom": "^19.2.6",
59
59
  "react-shadow": "^20.6.0",
60
- "release-it": "^20.2.0",
61
- "storybook": "^10.4.0",
60
+ "release-it": "^21.0.0",
61
+ "storybook": "^10.5.5",
62
62
  "tsdown": "^0.22",
63
63
  "typescript": "^6.0.3",
64
- "typescript-eslint": "^8.60.1",
65
- "vite": "^8.0.10"
64
+ "typescript-eslint": "^8.65.0",
65
+ "vite": "^8.1.0"
66
66
  },
67
67
  "packageManager": "yarn@4.15.0"
68
68
  }