keyborg 2.4.1 → 2.5.0-canary.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/esm/index.js CHANGED
@@ -31,6 +31,7 @@ var WeakRefInstance = class {
31
31
 
32
32
  // src/FocusEvent.ts
33
33
  var KEYBORG_FOCUSIN = "keyborg:focusin";
34
+ var KEYBORG_FOCUSOUT = "keyborg:focusout";
34
35
  function canOverrideNativeFocus(win) {
35
36
  const HTMLElement = win.HTMLElement;
36
37
  const origFocus = HTMLElement.prototype.focus;
@@ -62,35 +63,62 @@ function setupFocusEvent(win) {
62
63
  return;
63
64
  }
64
65
  kwin.HTMLElement.prototype.focus = focus;
65
- const focusOutShadowRootHandler = (e) => {
66
- const relatedTarget = e.relatedTarget;
67
- const currentTarget = e.currentTarget;
68
- if (!currentTarget.contains(relatedTarget)) {
69
- currentTarget.removeEventListener("focusin", focusInHandler, true);
70
- currentTarget.removeEventListener(
71
- "focusout",
72
- focusOutShadowRootHandler,
73
- true
74
- );
66
+ const shadowTargets = /* @__PURE__ */ new Set();
67
+ const focusOutHandler = (e) => {
68
+ const target = e.target;
69
+ if (!target) {
70
+ return;
75
71
  }
72
+ const event = new CustomEvent(KEYBORG_FOCUSOUT, {
73
+ cancelable: true,
74
+ bubbles: true,
75
+ // Allows the event to bubble past an open shadow root
76
+ composed: true,
77
+ detail: {
78
+ originalEvent: e
79
+ }
80
+ });
81
+ target.dispatchEvent(event);
76
82
  };
77
83
  const focusInHandler = (e) => {
78
- var _a;
79
84
  const target = e.target;
80
85
  if (!target) {
81
86
  return;
82
87
  }
83
- if (target.shadowRoot) {
84
- target.shadowRoot.addEventListener("focusin", focusInHandler, true);
85
- target.shadowRoot.addEventListener(
86
- "focusout",
87
- focusOutShadowRootHandler,
88
- true
89
- );
88
+ let node = target;
89
+ const currentShadows = /* @__PURE__ */ new Set();
90
+ while (node) {
91
+ if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
92
+ currentShadows.add(node);
93
+ node = node.host;
94
+ } else {
95
+ node = node.parentNode;
96
+ }
97
+ }
98
+ for (const shadow of shadowTargets) {
99
+ if (!currentShadows.has(shadow)) {
100
+ shadow.removeEventListener("focusin", focusInHandler, true);
101
+ shadow.removeEventListener("focusout", focusOutHandler, true);
102
+ shadowTargets.delete(shadow);
103
+ }
104
+ }
105
+ onFocusIn(target, e.relatedTarget || void 0);
106
+ };
107
+ const onFocusIn = (target, relatedTarget, originalEvent) => {
108
+ var _a;
109
+ const shadowRoot = target.shadowRoot;
110
+ if (shadowRoot) {
111
+ if (shadowTargets.has(shadowRoot)) {
112
+ return;
113
+ }
114
+ shadowRoot.addEventListener("focusin", focusInHandler, true);
115
+ shadowRoot.addEventListener("focusout", focusOutHandler, true);
116
+ shadowTargets.add(shadowRoot);
90
117
  return;
91
118
  }
92
119
  const details = {
93
- relatedTarget: e.relatedTarget || void 0
120
+ relatedTarget,
121
+ originalEvent
94
122
  };
95
123
  const event = new CustomEvent(KEYBORG_FOCUSIN, {
96
124
  cancelable: true,
@@ -107,13 +135,20 @@ function setupFocusEvent(win) {
107
135
  target.dispatchEvent(event);
108
136
  };
109
137
  const data = kwin.__keyborgData = {
110
- focusInHandler
138
+ focusInHandler,
139
+ focusOutHandler,
140
+ shadowTargets
111
141
  };
112
142
  kwin.document.addEventListener(
113
143
  "focusin",
114
144
  kwin.__keyborgData.focusInHandler,
115
145
  true
116
146
  );
147
+ kwin.document.addEventListener(
148
+ "focusout",
149
+ kwin.__keyborgData.focusOutHandler,
150
+ true
151
+ );
117
152
  function focus() {
118
153
  const keyborgNativeFocusEvent = kwin.__keyborgData;
119
154
  if (keyborgNativeFocusEvent) {
@@ -123,6 +158,11 @@ function setupFocusEvent(win) {
123
158
  }
124
159
  return origFocus.apply(this, arguments);
125
160
  }
161
+ let activeElement = kwin.document.activeElement;
162
+ while (activeElement && activeElement.shadowRoot) {
163
+ onFocusIn(activeElement);
164
+ activeElement = activeElement.shadowRoot.activeElement;
165
+ }
126
166
  focus.__keyborgNativeFocus = origFocus;
127
167
  }
128
168
  function disposeFocusEvent(win) {
@@ -136,6 +176,24 @@ function disposeFocusEvent(win) {
136
176
  keyborgNativeFocusEvent.focusInHandler,
137
177
  true
138
178
  );
179
+ kwin.document.removeEventListener(
180
+ "focusout",
181
+ keyborgNativeFocusEvent.focusOutHandler,
182
+ true
183
+ );
184
+ for (const shadow of keyborgNativeFocusEvent.shadowTargets) {
185
+ shadow.removeEventListener(
186
+ "focusin",
187
+ keyborgNativeFocusEvent.focusInHandler,
188
+ true
189
+ );
190
+ shadow.removeEventListener(
191
+ "focusout",
192
+ keyborgNativeFocusEvent.focusOutHandler,
193
+ true
194
+ );
195
+ }
196
+ keyborgNativeFocusEvent.shadowTargets.clear();
139
197
  delete kwin.__keyborgData;
140
198
  }
141
199
  if (origFocus) {
@@ -408,9 +466,10 @@ function disposeKeyborg(instance) {
408
466
  }
409
467
 
410
468
  // src/index.ts
411
- var version = "2.4.1";
469
+ var version = "2.5.0-canary.0";
412
470
  export {
413
471
  KEYBORG_FOCUSIN,
472
+ KEYBORG_FOCUSOUT,
414
473
  createKeyborg,
415
474
  disposeKeyborg,
416
475
  getLastFocusedProgrammatically,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/WeakRefInstance.ts","../../src/FocusEvent.ts","../../src/Keyborg.ts","../../src/index.ts"],"sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// IE11 compat, checks if WeakRef is supported\nexport const _canUseWeakRef = typeof WeakRef !== \"undefined\";\n\n/**\n * Allows disposable instances to be used\n */\nexport interface Disposable {\n isDisposed?(): boolean;\n}\n\n/**\n * WeakRef wrapper around a HTMLElement that also supports IE11\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef}\n * @internal\n */\nexport class WeakRefInstance<T extends Disposable | object> {\n private _weakRef?: WeakRef<T>;\n private _instance?: T;\n\n constructor(instance: T) {\n if (_canUseWeakRef && typeof instance === \"object\") {\n this._weakRef = new WeakRef(instance);\n } else {\n this._instance = instance;\n }\n }\n\n /**\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef/deref}\n */\n deref(): T | undefined {\n let instance: T | undefined;\n\n if (this._weakRef) {\n instance = this._weakRef?.deref();\n\n if (!instance) {\n delete this._weakRef;\n }\n } else {\n instance = this._instance;\n if ((instance as Disposable)?.isDisposed?.()) {\n delete this._instance;\n }\n }\n\n return instance;\n }\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { WeakRefInstance } from \"./WeakRefInstance\";\n\nexport const KEYBORG_FOCUSIN = \"keyborg:focusin\";\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\ninterface KeyborgFocusEventData {\n focusInHandler: (e: FocusEvent) => void;\n lastFocusedProgrammatically?: WeakRefInstance<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\nfunction canOverrideNativeFocus(win: Window): boolean {\n const HTMLElement = (win as WindowWithKeyborgFocusEvent).HTMLElement;\n const origFocus = HTMLElement.prototype.focus;\n\n let isCustomFocusCalled = false;\n\n HTMLElement.prototype.focus = function focus(): void {\n isCustomFocusCalled = true;\n };\n\n const btn = win.document.createElement(\"button\");\n\n btn.focus();\n\n HTMLElement.prototype.focus = origFocus;\n\n return isCustomFocusCalled;\n}\n\nlet _canOverrideNativeFocus = false;\n\nexport interface KeyborgFocusInEventDetails {\n relatedTarget?: HTMLElement;\n isFocusedProgrammatically?: boolean;\n}\n\nexport interface KeyborgFocusInEvent\n extends CustomEvent<KeyborgFocusInEventDetails> {\n /**\n * @deprecated - used `event.detail`\n */\n details?: KeyborgFocusInEventDetails;\n}\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\n if (!_canOverrideNativeFocus) {\n _canOverrideNativeFocus = canOverrideNativeFocus(kwin);\n }\n\n const origFocus = kwin.HTMLElement.prototype.focus;\n\n if ((origFocus as KeyborgFocus).__keyborgNativeFocus) {\n // Already set up.\n return;\n }\n\n kwin.HTMLElement.prototype.focus = focus;\n\n const focusOutShadowRootHandler = (e: FocusEvent) => {\n const relatedTarget = e.relatedTarget as HTMLElement | null;\n const currentTarget = e.currentTarget as ShadowRoot;\n\n // cleanup polyfill event handlers once focus leaves the shadow root\n if (!currentTarget.contains(relatedTarget)) {\n currentTarget.removeEventListener(\"focusin\", focusInHandler, true);\n currentTarget.removeEventListener(\n \"focusout\",\n focusOutShadowRootHandler,\n true,\n );\n }\n };\n\n const focusInHandler = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n\n if (!target) {\n return;\n }\n\n if (target.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 target.shadowRoot.addEventListener(\"focusin\", focusInHandler, true);\n target.shadowRoot.addEventListener(\n \"focusout\",\n focusOutShadowRootHandler,\n true,\n );\n\n return;\n }\n\n const details: KeyborgFocusInEventDetails = {\n relatedTarget: (e.relatedTarget as HTMLElement) || undefined,\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 // Tabster (and other users) can still use the legacy details field - keeping for backwards compat\n event.details = details;\n\n if (_canOverrideNativeFocus || data.lastFocusedProgrammatically) {\n details.isFocusedProgrammatically =\n target === data.lastFocusedProgrammatically?.deref();\n\n data.lastFocusedProgrammatically = undefined;\n }\n\n target.dispatchEvent(event);\n };\n\n const data: KeyborgFocusEventData = (kwin.__keyborgData = {\n focusInHandler,\n });\n\n kwin.document.addEventListener(\n \"focusin\",\n kwin.__keyborgData.focusInHandler,\n true,\n );\n\n function focus(this: HTMLElement) {\n const keyborgNativeFocusEvent = (kwin as WindowWithKeyborgFocusEvent)\n .__keyborgData;\n\n if (keyborgNativeFocusEvent) {\n keyborgNativeFocusEvent.lastFocusedProgrammatically = new WeakRefInstance(\n this,\n );\n }\n\n // eslint-disable-next-line prefer-rest-params\n return origFocus.apply(this, arguments);\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 keyborgNativeFocusEvent = kwin.__keyborgData;\n\n if (keyborgNativeFocusEvent) {\n kwin.document.removeEventListener(\n \"focusin\",\n keyborgNativeFocusEvent.focusInHandler,\n true,\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 keyborgNativeFocusEvent = (win as WindowWithKeyborgFocusEvent)\n .__keyborgData;\n\n return keyborgNativeFocusEvent\n ? keyborgNativeFocusEvent.lastFocusedProgrammatically?.deref() || null\n : undefined;\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n disposeFocusEvent,\n KeyborgFocusInEvent,\n KEYBORG_FOCUSIN,\n setupFocusEvent,\n} from \"./FocusEvent\";\nimport { Disposable, WeakRefInstance } from \"./WeakRefInstance\";\n\ninterface WindowWithKeyborg extends Window {\n __keyborg?: {\n core: KeyborgCore;\n refs: { [id: string]: Keyborg };\n };\n}\n\nconst _dismissTimeout = 500; // When a key from dismissKeys is pressed and the focus is not moved\n// during _dismissTimeout time, dismiss the keyboard navigation mode.\n\nlet _lastId = 0;\n\nexport interface KeyborgProps {\n // Keys to be used to trigger keyboard navigation mode. By default, any key will trigger\n // it. Could be limited to, for example, just Tab (or Tab and arrow keys).\n triggerKeys?: number[];\n // Keys to be used to dismiss keyboard navigation mode using keyboard (in addition to\n // mouse clicks which dismiss it). For example, Esc could be used to dismiss.\n dismissKeys?: number[];\n}\n\nexport type KeyborgCallback = (isNavigatingWithKeyboard: boolean) => void;\n\n/**\n * Source of truth for all the keyborg core instances and the current keyboard navigation state\n */\nexport class KeyborgState {\n private __keyborgCoreRefs: { [id: string]: WeakRefInstance<KeyborgCore> } =\n {};\n private _isNavigatingWithKeyboard = false;\n\n add(keyborg: KeyborgCore): void {\n const id = keyborg.id;\n\n if (!(id in this.__keyborgCoreRefs)) {\n this.__keyborgCoreRefs[id] = new WeakRefInstance<KeyborgCore>(keyborg);\n }\n }\n\n remove(id: string): void {\n delete this.__keyborgCoreRefs[id];\n\n if (Object.keys(this.__keyborgCoreRefs).length === 0) {\n this._isNavigatingWithKeyboard = false;\n }\n }\n\n setVal(isNavigatingWithKeyboard: boolean): void {\n if (this._isNavigatingWithKeyboard === isNavigatingWithKeyboard) {\n return;\n }\n\n this._isNavigatingWithKeyboard = isNavigatingWithKeyboard;\n\n for (const id of Object.keys(this.__keyborgCoreRefs)) {\n const ref = this.__keyborgCoreRefs[id];\n const keyborg = ref.deref();\n\n if (keyborg) {\n keyborg.update(isNavigatingWithKeyboard);\n } else {\n this.remove(id);\n }\n }\n }\n\n getVal(): boolean {\n return this._isNavigatingWithKeyboard;\n }\n}\n\nconst _state = new KeyborgState();\n\n/**\n * Manages a collection of Keyborg instances in a window/document and updates keyborg state\n */\nclass KeyborgCore implements Disposable {\n readonly id: string;\n\n private _win?: WindowWithKeyborg;\n private _isMouseUsedTimer: number | undefined;\n private _dismissTimer: number | undefined;\n private _triggerKeys?: Set<number>;\n private _dismissKeys?: Set<number>;\n\n constructor(win: WindowWithKeyborg, props?: KeyborgProps) {\n this.id = \"c\" + ++_lastId;\n this._win = win;\n const doc = win.document;\n\n if (props) {\n const triggerKeys = props.triggerKeys;\n const dismissKeys = props.dismissKeys;\n\n if (triggerKeys?.length) {\n this._triggerKeys = new Set(triggerKeys);\n }\n\n if (dismissKeys?.length) {\n this._dismissKeys = new Set(dismissKeys);\n }\n }\n\n doc.addEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!\n doc.addEventListener(\"mousedown\", this._onMouseDown, true); // Capture!\n win.addEventListener(\"keydown\", this._onKeyDown, true); // Capture!\n\n setupFocusEvent(win);\n\n _state.add(this);\n }\n\n dispose(): void {\n const win = this._win;\n\n if (win) {\n if (this._isMouseUsedTimer) {\n win.clearTimeout(this._isMouseUsedTimer);\n this._isMouseUsedTimer = undefined;\n }\n\n if (this._dismissTimer) {\n win.clearTimeout(this._dismissTimer);\n this._dismissTimer = undefined;\n }\n\n disposeFocusEvent(win);\n\n const doc = win.document;\n\n doc.removeEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!\n doc.removeEventListener(\"mousedown\", this._onMouseDown, true); // Capture!\n win.removeEventListener(\"keydown\", this._onKeyDown, true); // Capture!\n\n delete this._win;\n\n _state.remove(this.id);\n }\n }\n\n isDisposed(): boolean {\n return !!this._win;\n }\n\n /**\n * Updates all keyborg instances with the keyboard navigation state\n */\n update(isNavigatingWithKeyboard: boolean): void {\n const keyborgs = this._win?.__keyborg?.refs;\n\n if (keyborgs) {\n for (const id of Object.keys(keyborgs)) {\n Keyborg.update(keyborgs[id], isNavigatingWithKeyboard);\n }\n }\n }\n\n private _onFocusIn = (e: KeyborgFocusInEvent) => {\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 swallow\n // the events when the screen reader shortcuts are used). The screen reader\n // usage is keyboard navigation.\n\n if (this._isMouseUsedTimer) {\n // There was a mouse event recently.\n return;\n }\n\n if (_state.getVal()) {\n return;\n }\n\n const details = e.detail;\n\n if (!details.relatedTarget) {\n return;\n }\n\n if (\n details.isFocusedProgrammatically ||\n details.isFocusedProgrammatically === undefined\n ) {\n // The element is focused programmatically, or the programmatic focus detection\n // is not working.\n return;\n }\n\n _state.setVal(true);\n };\n\n private _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 perform\n // an action on an element, do not dismiss the keyboard navigation mode.\n return;\n }\n\n const win = this._win;\n\n if (win) {\n if (this._isMouseUsedTimer) {\n win.clearTimeout(this._isMouseUsedTimer);\n }\n\n this._isMouseUsedTimer = win.setTimeout(() => {\n delete this._isMouseUsedTimer;\n }, 1000); // Keeping the indication of the mouse usage for some time.\n }\n\n _state.setVal(false);\n };\n\n private _onKeyDown = (e: KeyboardEvent): void => {\n const isNavigatingWithKeyboard = _state.getVal();\n\n if (isNavigatingWithKeyboard) {\n if (this._shouldDismissKeyboardNavigation(e)) {\n this._scheduleDismiss();\n }\n } else {\n if (this._shouldTriggerKeyboardNavigation(e)) {\n _state.setVal(true);\n }\n }\n };\n\n /**\n * @returns whether the keyboard event should trigger keyboard navigation mode\n */\n private _shouldTriggerKeyboardNavigation(e: KeyboardEvent) {\n // TODO Some rich text fields can allow Tab key for indentation so it doesn't\n // need to be a navigation key. If there is a bug regarding that we should revisit\n if (e.key === \"Tab\") {\n return true;\n }\n\n const activeElement = this._win?.document\n .activeElement as HTMLElement | null;\n const isTriggerKey = !this._triggerKeys || this._triggerKeys.has(e.keyCode);\n\n const isEditable =\n activeElement &&\n (activeElement.tagName === \"INPUT\" ||\n activeElement.tagName === \"TEXTAREA\" ||\n activeElement.isContentEditable);\n\n return isTriggerKey && !isEditable;\n }\n\n /**\n * @returns whether the keyboard event should dismiss keyboard navigation mode\n */\n private _shouldDismissKeyboardNavigation(e: KeyboardEvent) {\n return this._dismissKeys?.has(e.keyCode);\n }\n\n private _scheduleDismiss(): void {\n const win = this._win;\n\n if (win) {\n if (this._dismissTimer) {\n win.clearTimeout(this._dismissTimer);\n this._dismissTimer = undefined;\n }\n\n const was = win.document.activeElement;\n\n this._dismissTimer = win.setTimeout(() => {\n this._dismissTimer = undefined;\n\n const cur = win.document.activeElement;\n\n if (was && cur && was === cur) {\n // Esc was pressed, currently focused element hasn't changed.\n // Just dismiss the keyboard navigation mode.\n _state.setVal(false);\n }\n }, _dismissTimeout);\n }\n }\n}\n\n/**\n * Used to determine the keyboard navigation state\n */\nexport class Keyborg {\n private _id: string;\n private _win?: WindowWithKeyborg;\n private _core?: KeyborgCore;\n private _cb: KeyborgCallback[] = [];\n\n static create(win: WindowWithKeyborg, props?: KeyborgProps): Keyborg {\n return new Keyborg(win, props);\n }\n\n static dispose(instance: Keyborg): void {\n instance.dispose();\n }\n\n /**\n * Updates all subscribed callbacks with the keyboard navigation state\n */\n static update(instance: Keyborg, isNavigatingWithKeyboard: boolean): void {\n instance._cb.forEach((callback) => callback(isNavigatingWithKeyboard));\n }\n\n private constructor(win: WindowWithKeyborg, props?: KeyborgProps) {\n this._id = \"k\" + ++_lastId;\n this._win = win;\n\n const current = win.__keyborg;\n\n if (current) {\n this._core = current.core;\n current.refs[this._id] = this;\n } else {\n this._core = new KeyborgCore(win, props);\n win.__keyborg = {\n core: this._core,\n refs: { [this._id]: this },\n };\n }\n }\n\n private dispose(): void {\n const current = this._win?.__keyborg;\n\n if (current?.refs[this._id]) {\n delete current.refs[this._id];\n\n if (Object.keys(current.refs).length === 0) {\n current.core.dispose();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n delete this._win!.__keyborg;\n }\n } else if (process.env.NODE_ENV !== \"production\") {\n console.error(\n `Keyborg instance ${this._id} is being disposed incorrectly.`,\n );\n }\n\n this._cb = [];\n delete this._core;\n delete this._win;\n }\n\n /**\n * @returns Whether the user is navigating with keyboard\n */\n isNavigatingWithKeyboard(): boolean {\n return _state.getVal();\n }\n\n /**\n * @param callback - Called when the keyboard navigation state changes\n */\n subscribe(callback: KeyborgCallback): void {\n this._cb.push(callback);\n }\n\n /**\n * @param callback - Registered with subscribe\n */\n unsubscribe(callback: KeyborgCallback): void {\n const index = this._cb.indexOf(callback);\n\n if (index >= 0) {\n this._cb.splice(index, 1);\n }\n }\n\n /**\n * Manually set the keyboard navigtion state\n */\n setVal(isNavigatingWithKeyboard: boolean): void {\n _state.setVal(isNavigatingWithKeyboard);\n }\n}\n\nexport function createKeyborg(win: Window, props?: KeyborgProps): Keyborg {\n return Keyborg.create(win, props);\n}\n\nexport function disposeKeyborg(instance: Keyborg) {\n Keyborg.dispose(instance);\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport type { Keyborg, KeyborgCallback } from \"./Keyborg\";\nexport { createKeyborg, disposeKeyborg } from \"./Keyborg\";\n\nexport type {\n KeyborgFocusInEvent,\n KeyborgFocusInEventDetails,\n} from \"./FocusEvent\";\nexport {\n getLastFocusedProgrammatically,\n nativeFocus,\n KEYBORG_FOCUSIN,\n} from \"./FocusEvent\";\n\nexport const version = process.env.PKG_VERSION;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/WeakRefInstance.ts","../../src/FocusEvent.ts","../../src/Keyborg.ts","../../src/index.ts"],"sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// IE11 compat, checks if WeakRef is supported\nexport const _canUseWeakRef = typeof WeakRef !== \"undefined\";\n\n/**\n * Allows disposable instances to be used\n */\nexport interface Disposable {\n isDisposed?(): boolean;\n}\n\n/**\n * WeakRef wrapper around a HTMLElement that also supports IE11\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef}\n * @internal\n */\nexport class WeakRefInstance<T extends Disposable | object> {\n private _weakRef?: WeakRef<T>;\n private _instance?: T;\n\n constructor(instance: T) {\n if (_canUseWeakRef && typeof instance === \"object\") {\n this._weakRef = new WeakRef(instance);\n } else {\n this._instance = instance;\n }\n }\n\n /**\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef/deref}\n */\n deref(): T | undefined {\n let instance: T | undefined;\n\n if (this._weakRef) {\n instance = this._weakRef?.deref();\n\n if (!instance) {\n delete this._weakRef;\n }\n } else {\n instance = this._instance;\n if ((instance as Disposable)?.isDisposed?.()) {\n delete this._instance;\n }\n }\n\n return instance;\n }\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { WeakRefInstance } from \"./WeakRefInstance\";\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\ninterface KeyborgFocusEventData {\n focusInHandler: (e: FocusEvent) => void;\n focusOutHandler: (e: FocusEvent) => void;\n lastFocusedProgrammatically?: WeakRefInstance<HTMLElement>;\n shadowTargets: Set<ShadowRoot>;\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\nfunction canOverrideNativeFocus(win: Window): boolean {\n const HTMLElement = (win as WindowWithKeyborgFocusEvent).HTMLElement;\n const origFocus = HTMLElement.prototype.focus;\n\n let isCustomFocusCalled = false;\n\n HTMLElement.prototype.focus = function focus(): void {\n isCustomFocusCalled = true;\n };\n\n const btn = win.document.createElement(\"button\");\n\n btn.focus();\n\n HTMLElement.prototype.focus = origFocus;\n\n return isCustomFocusCalled;\n}\n\nlet _canOverrideNativeFocus = false;\n\nexport interface KeyborgFocusInEventDetails {\n relatedTarget?: HTMLElement;\n isFocusedProgrammatically?: boolean;\n originalEvent?: FocusEvent;\n}\n\nexport interface KeyborgFocusInEvent\n extends CustomEvent<KeyborgFocusInEventDetails> {\n /**\n * @deprecated - used `event.detail`\n */\n details?: KeyborgFocusInEventDetails;\n}\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\n if (!_canOverrideNativeFocus) {\n _canOverrideNativeFocus = canOverrideNativeFocus(kwin);\n }\n\n const origFocus = kwin.HTMLElement.prototype.focus;\n\n if ((origFocus as KeyborgFocus).__keyborgNativeFocus) {\n // Already set up.\n return;\n }\n\n kwin.HTMLElement.prototype.focus = focus;\n\n const shadowTargets: Set<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 = target;\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 shadow of shadowTargets) {\n if (!currentShadows.has(shadow)) {\n shadow.removeEventListener(\"focusin\", focusInHandler, true);\n shadow.removeEventListener(\"focusout\", focusOutHandler, true);\n shadowTargets.delete(shadow);\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 if (shadowTargets.has(shadowRoot)) {\n return;\n }\n\n shadowRoot.addEventListener(\"focusin\", focusInHandler, true);\n shadowRoot.addEventListener(\"focusout\", focusOutHandler, true);\n\n shadowTargets.add(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 // Tabster (and other users) can still use the legacy details field - keeping for backwards compat\n event.details = details;\n\n if (_canOverrideNativeFocus || data.lastFocusedProgrammatically) {\n details.isFocusedProgrammatically =\n target === data.lastFocusedProgrammatically?.deref();\n\n data.lastFocusedProgrammatically = undefined;\n }\n\n target.dispatchEvent(event);\n };\n\n const data: KeyborgFocusEventData = (kwin.__keyborgData = {\n focusInHandler,\n focusOutHandler,\n shadowTargets,\n });\n\n kwin.document.addEventListener(\n \"focusin\",\n kwin.__keyborgData.focusInHandler,\n true,\n );\n\n kwin.document.addEventListener(\n \"focusout\",\n kwin.__keyborgData.focusOutHandler,\n true,\n );\n\n function focus(this: HTMLElement) {\n const keyborgNativeFocusEvent = (kwin as WindowWithKeyborgFocusEvent)\n .__keyborgData;\n\n if (keyborgNativeFocusEvent) {\n keyborgNativeFocusEvent.lastFocusedProgrammatically = new WeakRefInstance(\n this,\n );\n }\n\n // eslint-disable-next-line prefer-rest-params\n return origFocus.apply(this, arguments);\n }\n\n let activeElement = kwin.document.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 keyborgNativeFocusEvent = kwin.__keyborgData;\n\n if (keyborgNativeFocusEvent) {\n kwin.document.removeEventListener(\n \"focusin\",\n keyborgNativeFocusEvent.focusInHandler,\n true,\n );\n\n kwin.document.removeEventListener(\n \"focusout\",\n keyborgNativeFocusEvent.focusOutHandler,\n true,\n );\n\n for (const shadow of keyborgNativeFocusEvent.shadowTargets) {\n shadow.removeEventListener(\n \"focusin\",\n keyborgNativeFocusEvent.focusInHandler,\n true,\n );\n shadow.removeEventListener(\n \"focusout\",\n keyborgNativeFocusEvent.focusOutHandler,\n true,\n );\n }\n\n keyborgNativeFocusEvent.shadowTargets.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 keyborgNativeFocusEvent = (win as WindowWithKeyborgFocusEvent)\n .__keyborgData;\n\n return keyborgNativeFocusEvent\n ? keyborgNativeFocusEvent.lastFocusedProgrammatically?.deref() || null\n : undefined;\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n disposeFocusEvent,\n KeyborgFocusInEvent,\n KEYBORG_FOCUSIN,\n setupFocusEvent,\n} from \"./FocusEvent\";\nimport { Disposable, WeakRefInstance } from \"./WeakRefInstance\";\n\ninterface WindowWithKeyborg extends Window {\n __keyborg?: {\n core: KeyborgCore;\n refs: { [id: string]: Keyborg };\n };\n}\n\nconst _dismissTimeout = 500; // When a key from dismissKeys is pressed and the focus is not moved\n// during _dismissTimeout time, dismiss the keyboard navigation mode.\n\nlet _lastId = 0;\n\nexport interface KeyborgProps {\n // Keys to be used to trigger keyboard navigation mode. By default, any key will trigger\n // it. Could be limited to, for example, just Tab (or Tab and arrow keys).\n triggerKeys?: number[];\n // Keys to be used to dismiss keyboard navigation mode using keyboard (in addition to\n // mouse clicks which dismiss it). For example, Esc could be used to dismiss.\n dismissKeys?: number[];\n}\n\nexport type KeyborgCallback = (isNavigatingWithKeyboard: boolean) => void;\n\n/**\n * Source of truth for all the keyborg core instances and the current keyboard navigation state\n */\nexport class KeyborgState {\n private __keyborgCoreRefs: { [id: string]: WeakRefInstance<KeyborgCore> } =\n {};\n private _isNavigatingWithKeyboard = false;\n\n add(keyborg: KeyborgCore): void {\n const id = keyborg.id;\n\n if (!(id in this.__keyborgCoreRefs)) {\n this.__keyborgCoreRefs[id] = new WeakRefInstance<KeyborgCore>(keyborg);\n }\n }\n\n remove(id: string): void {\n delete this.__keyborgCoreRefs[id];\n\n if (Object.keys(this.__keyborgCoreRefs).length === 0) {\n this._isNavigatingWithKeyboard = false;\n }\n }\n\n setVal(isNavigatingWithKeyboard: boolean): void {\n if (this._isNavigatingWithKeyboard === isNavigatingWithKeyboard) {\n return;\n }\n\n this._isNavigatingWithKeyboard = isNavigatingWithKeyboard;\n\n for (const id of Object.keys(this.__keyborgCoreRefs)) {\n const ref = this.__keyborgCoreRefs[id];\n const keyborg = ref.deref();\n\n if (keyborg) {\n keyborg.update(isNavigatingWithKeyboard);\n } else {\n this.remove(id);\n }\n }\n }\n\n getVal(): boolean {\n return this._isNavigatingWithKeyboard;\n }\n}\n\nconst _state = new KeyborgState();\n\n/**\n * Manages a collection of Keyborg instances in a window/document and updates keyborg state\n */\nclass KeyborgCore implements Disposable {\n readonly id: string;\n\n private _win?: WindowWithKeyborg;\n private _isMouseUsedTimer: number | undefined;\n private _dismissTimer: number | undefined;\n private _triggerKeys?: Set<number>;\n private _dismissKeys?: Set<number>;\n\n constructor(win: WindowWithKeyborg, props?: KeyborgProps) {\n this.id = \"c\" + ++_lastId;\n this._win = win;\n const doc = win.document;\n\n if (props) {\n const triggerKeys = props.triggerKeys;\n const dismissKeys = props.dismissKeys;\n\n if (triggerKeys?.length) {\n this._triggerKeys = new Set(triggerKeys);\n }\n\n if (dismissKeys?.length) {\n this._dismissKeys = new Set(dismissKeys);\n }\n }\n\n doc.addEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!\n doc.addEventListener(\"mousedown\", this._onMouseDown, true); // Capture!\n win.addEventListener(\"keydown\", this._onKeyDown, true); // Capture!\n\n setupFocusEvent(win);\n\n _state.add(this);\n }\n\n dispose(): void {\n const win = this._win;\n\n if (win) {\n if (this._isMouseUsedTimer) {\n win.clearTimeout(this._isMouseUsedTimer);\n this._isMouseUsedTimer = undefined;\n }\n\n if (this._dismissTimer) {\n win.clearTimeout(this._dismissTimer);\n this._dismissTimer = undefined;\n }\n\n disposeFocusEvent(win);\n\n const doc = win.document;\n\n doc.removeEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!\n doc.removeEventListener(\"mousedown\", this._onMouseDown, true); // Capture!\n win.removeEventListener(\"keydown\", this._onKeyDown, true); // Capture!\n\n delete this._win;\n\n _state.remove(this.id);\n }\n }\n\n isDisposed(): boolean {\n return !!this._win;\n }\n\n /**\n * Updates all keyborg instances with the keyboard navigation state\n */\n update(isNavigatingWithKeyboard: boolean): void {\n const keyborgs = this._win?.__keyborg?.refs;\n\n if (keyborgs) {\n for (const id of Object.keys(keyborgs)) {\n Keyborg.update(keyborgs[id], isNavigatingWithKeyboard);\n }\n }\n }\n\n private _onFocusIn = (e: KeyborgFocusInEvent) => {\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 swallow\n // the events when the screen reader shortcuts are used). The screen reader\n // usage is keyboard navigation.\n\n if (this._isMouseUsedTimer) {\n // There was a mouse event recently.\n return;\n }\n\n if (_state.getVal()) {\n return;\n }\n\n const details = e.detail;\n\n if (!details.relatedTarget) {\n return;\n }\n\n if (\n details.isFocusedProgrammatically ||\n details.isFocusedProgrammatically === undefined\n ) {\n // The element is focused programmatically, or the programmatic focus detection\n // is not working.\n return;\n }\n\n _state.setVal(true);\n };\n\n private _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 perform\n // an action on an element, do not dismiss the keyboard navigation mode.\n return;\n }\n\n const win = this._win;\n\n if (win) {\n if (this._isMouseUsedTimer) {\n win.clearTimeout(this._isMouseUsedTimer);\n }\n\n this._isMouseUsedTimer = win.setTimeout(() => {\n delete this._isMouseUsedTimer;\n }, 1000); // Keeping the indication of the mouse usage for some time.\n }\n\n _state.setVal(false);\n };\n\n private _onKeyDown = (e: KeyboardEvent): void => {\n const isNavigatingWithKeyboard = _state.getVal();\n\n if (isNavigatingWithKeyboard) {\n if (this._shouldDismissKeyboardNavigation(e)) {\n this._scheduleDismiss();\n }\n } else {\n if (this._shouldTriggerKeyboardNavigation(e)) {\n _state.setVal(true);\n }\n }\n };\n\n /**\n * @returns whether the keyboard event should trigger keyboard navigation mode\n */\n private _shouldTriggerKeyboardNavigation(e: KeyboardEvent) {\n // TODO Some rich text fields can allow Tab key for indentation so it doesn't\n // need to be a navigation key. If there is a bug regarding that we should revisit\n if (e.key === \"Tab\") {\n return true;\n }\n\n const activeElement = this._win?.document\n .activeElement as HTMLElement | null;\n const isTriggerKey = !this._triggerKeys || this._triggerKeys.has(e.keyCode);\n\n const isEditable =\n activeElement &&\n (activeElement.tagName === \"INPUT\" ||\n activeElement.tagName === \"TEXTAREA\" ||\n activeElement.isContentEditable);\n\n return isTriggerKey && !isEditable;\n }\n\n /**\n * @returns whether the keyboard event should dismiss keyboard navigation mode\n */\n private _shouldDismissKeyboardNavigation(e: KeyboardEvent) {\n return this._dismissKeys?.has(e.keyCode);\n }\n\n private _scheduleDismiss(): void {\n const win = this._win;\n\n if (win) {\n if (this._dismissTimer) {\n win.clearTimeout(this._dismissTimer);\n this._dismissTimer = undefined;\n }\n\n const was = win.document.activeElement;\n\n this._dismissTimer = win.setTimeout(() => {\n this._dismissTimer = undefined;\n\n const cur = win.document.activeElement;\n\n if (was && cur && was === cur) {\n // Esc was pressed, currently focused element hasn't changed.\n // Just dismiss the keyboard navigation mode.\n _state.setVal(false);\n }\n }, _dismissTimeout);\n }\n }\n}\n\n/**\n * Used to determine the keyboard navigation state\n */\nexport class Keyborg {\n private _id: string;\n private _win?: WindowWithKeyborg;\n private _core?: KeyborgCore;\n private _cb: KeyborgCallback[] = [];\n\n static create(win: WindowWithKeyborg, props?: KeyborgProps): Keyborg {\n return new Keyborg(win, props);\n }\n\n static dispose(instance: Keyborg): void {\n instance.dispose();\n }\n\n /**\n * Updates all subscribed callbacks with the keyboard navigation state\n */\n static update(instance: Keyborg, isNavigatingWithKeyboard: boolean): void {\n instance._cb.forEach((callback) => callback(isNavigatingWithKeyboard));\n }\n\n private constructor(win: WindowWithKeyborg, props?: KeyborgProps) {\n this._id = \"k\" + ++_lastId;\n this._win = win;\n\n const current = win.__keyborg;\n\n if (current) {\n this._core = current.core;\n current.refs[this._id] = this;\n } else {\n this._core = new KeyborgCore(win, props);\n win.__keyborg = {\n core: this._core,\n refs: { [this._id]: this },\n };\n }\n }\n\n private dispose(): void {\n const current = this._win?.__keyborg;\n\n if (current?.refs[this._id]) {\n delete current.refs[this._id];\n\n if (Object.keys(current.refs).length === 0) {\n current.core.dispose();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n delete this._win!.__keyborg;\n }\n } else if (process.env.NODE_ENV !== \"production\") {\n console.error(\n `Keyborg instance ${this._id} is being disposed incorrectly.`,\n );\n }\n\n this._cb = [];\n delete this._core;\n delete this._win;\n }\n\n /**\n * @returns Whether the user is navigating with keyboard\n */\n isNavigatingWithKeyboard(): boolean {\n return _state.getVal();\n }\n\n /**\n * @param callback - Called when the keyboard navigation state changes\n */\n subscribe(callback: KeyborgCallback): void {\n this._cb.push(callback);\n }\n\n /**\n * @param callback - Registered with subscribe\n */\n unsubscribe(callback: KeyborgCallback): void {\n const index = this._cb.indexOf(callback);\n\n if (index >= 0) {\n this._cb.splice(index, 1);\n }\n }\n\n /**\n * Manually set the keyboard navigtion state\n */\n setVal(isNavigatingWithKeyboard: boolean): void {\n _state.setVal(isNavigatingWithKeyboard);\n }\n}\n\nexport function createKeyborg(win: Window, props?: KeyborgProps): Keyborg {\n return Keyborg.create(win, props);\n}\n\nexport function disposeKeyborg(instance: Keyborg) {\n Keyborg.dispose(instance);\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport type { Keyborg, KeyborgCallback } from \"./Keyborg\";\nexport { createKeyborg, disposeKeyborg } from \"./Keyborg\";\n\nexport type {\n KeyborgFocusInEvent,\n KeyborgFocusInEventDetails,\n KeyborgFocusOutEvent,\n KeyborgFocusOutEventDetails,\n} from \"./FocusEvent\";\nexport {\n getLastFocusedProgrammatically,\n nativeFocus,\n KEYBORG_FOCUSIN,\n KEYBORG_FOCUSOUT,\n} from \"./FocusEvent\";\n\nexport const version = process.env.PKG_VERSION;\n"],"mappings":"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","names":[]}
package/dist/index.d.mts CHANGED
@@ -90,9 +90,11 @@ declare function createKeyborg(win: Window, props?: KeyborgProps): Keyborg;
90
90
  declare function disposeKeyborg(instance: Keyborg): void;
91
91
 
92
92
  declare const KEYBORG_FOCUSIN = "keyborg:focusin";
93
+ declare const KEYBORG_FOCUSOUT = "keyborg:focusout";
93
94
  interface KeyborgFocusInEventDetails {
94
95
  relatedTarget?: HTMLElement;
95
96
  isFocusedProgrammatically?: boolean;
97
+ originalEvent?: FocusEvent;
96
98
  }
97
99
  interface KeyborgFocusInEvent extends CustomEvent<KeyborgFocusInEventDetails> {
98
100
  /**
@@ -100,6 +102,10 @@ interface KeyborgFocusInEvent extends CustomEvent<KeyborgFocusInEventDetails> {
100
102
  */
101
103
  details?: KeyborgFocusInEventDetails;
102
104
  }
105
+ interface KeyborgFocusOutEventDetails {
106
+ originalEvent: FocusEvent;
107
+ }
108
+ type KeyborgFocusOutEvent = CustomEvent<KeyborgFocusOutEventDetails>;
103
109
  /**
104
110
  * Guarantees that the native `focus` will be used
105
111
  */
@@ -117,4 +123,4 @@ declare function getLastFocusedProgrammatically(win: Window): HTMLElement | null
117
123
 
118
124
  declare const version: string | undefined;
119
125
 
120
- export { KEYBORG_FOCUSIN, Keyborg, type KeyborgCallback, type KeyborgFocusInEvent, type KeyborgFocusInEventDetails, createKeyborg, disposeKeyborg, getLastFocusedProgrammatically, nativeFocus, version };
126
+ export { KEYBORG_FOCUSIN, KEYBORG_FOCUSOUT, Keyborg, type KeyborgCallback, type KeyborgFocusInEvent, type KeyborgFocusInEventDetails, type KeyborgFocusOutEvent, type KeyborgFocusOutEventDetails, createKeyborg, disposeKeyborg, getLastFocusedProgrammatically, nativeFocus, version };
package/dist/index.d.ts CHANGED
@@ -90,9 +90,11 @@ declare function createKeyborg(win: Window, props?: KeyborgProps): Keyborg;
90
90
  declare function disposeKeyborg(instance: Keyborg): void;
91
91
 
92
92
  declare const KEYBORG_FOCUSIN = "keyborg:focusin";
93
+ declare const KEYBORG_FOCUSOUT = "keyborg:focusout";
93
94
  interface KeyborgFocusInEventDetails {
94
95
  relatedTarget?: HTMLElement;
95
96
  isFocusedProgrammatically?: boolean;
97
+ originalEvent?: FocusEvent;
96
98
  }
97
99
  interface KeyborgFocusInEvent extends CustomEvent<KeyborgFocusInEventDetails> {
98
100
  /**
@@ -100,6 +102,10 @@ interface KeyborgFocusInEvent extends CustomEvent<KeyborgFocusInEventDetails> {
100
102
  */
101
103
  details?: KeyborgFocusInEventDetails;
102
104
  }
105
+ interface KeyborgFocusOutEventDetails {
106
+ originalEvent: FocusEvent;
107
+ }
108
+ type KeyborgFocusOutEvent = CustomEvent<KeyborgFocusOutEventDetails>;
103
109
  /**
104
110
  * Guarantees that the native `focus` will be used
105
111
  */
@@ -117,4 +123,4 @@ declare function getLastFocusedProgrammatically(win: Window): HTMLElement | null
117
123
 
118
124
  declare const version: string | undefined;
119
125
 
120
- export { KEYBORG_FOCUSIN, Keyborg, type KeyborgCallback, type KeyborgFocusInEvent, type KeyborgFocusInEventDetails, createKeyborg, disposeKeyborg, getLastFocusedProgrammatically, nativeFocus, version };
126
+ export { KEYBORG_FOCUSIN, KEYBORG_FOCUSOUT, Keyborg, type KeyborgCallback, type KeyborgFocusInEvent, type KeyborgFocusInEventDetails, type KeyborgFocusOutEvent, type KeyborgFocusOutEventDetails, createKeyborg, disposeKeyborg, getLastFocusedProgrammatically, nativeFocus, version };
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var src_exports = {};
21
21
  __export(src_exports, {
22
22
  KEYBORG_FOCUSIN: () => KEYBORG_FOCUSIN,
23
+ KEYBORG_FOCUSOUT: () => KEYBORG_FOCUSOUT,
23
24
  createKeyborg: () => createKeyborg,
24
25
  disposeKeyborg: () => disposeKeyborg,
25
26
  getLastFocusedProgrammatically: () => getLastFocusedProgrammatically,
@@ -61,6 +62,7 @@ var WeakRefInstance = class {
61
62
 
62
63
  // src/FocusEvent.ts
63
64
  var KEYBORG_FOCUSIN = "keyborg:focusin";
65
+ var KEYBORG_FOCUSOUT = "keyborg:focusout";
64
66
  function canOverrideNativeFocus(win) {
65
67
  const HTMLElement = win.HTMLElement;
66
68
  const origFocus = HTMLElement.prototype.focus;
@@ -92,35 +94,62 @@ function setupFocusEvent(win) {
92
94
  return;
93
95
  }
94
96
  kwin.HTMLElement.prototype.focus = focus;
95
- const focusOutShadowRootHandler = (e) => {
96
- const relatedTarget = e.relatedTarget;
97
- const currentTarget = e.currentTarget;
98
- if (!currentTarget.contains(relatedTarget)) {
99
- currentTarget.removeEventListener("focusin", focusInHandler, true);
100
- currentTarget.removeEventListener(
101
- "focusout",
102
- focusOutShadowRootHandler,
103
- true
104
- );
97
+ const shadowTargets = /* @__PURE__ */ new Set();
98
+ const focusOutHandler = (e) => {
99
+ const target = e.target;
100
+ if (!target) {
101
+ return;
105
102
  }
103
+ const event = new CustomEvent(KEYBORG_FOCUSOUT, {
104
+ cancelable: true,
105
+ bubbles: true,
106
+ // Allows the event to bubble past an open shadow root
107
+ composed: true,
108
+ detail: {
109
+ originalEvent: e
110
+ }
111
+ });
112
+ target.dispatchEvent(event);
106
113
  };
107
114
  const focusInHandler = (e) => {
108
- var _a;
109
115
  const target = e.target;
110
116
  if (!target) {
111
117
  return;
112
118
  }
113
- if (target.shadowRoot) {
114
- target.shadowRoot.addEventListener("focusin", focusInHandler, true);
115
- target.shadowRoot.addEventListener(
116
- "focusout",
117
- focusOutShadowRootHandler,
118
- true
119
- );
119
+ let node = target;
120
+ const currentShadows = /* @__PURE__ */ new Set();
121
+ while (node) {
122
+ if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
123
+ currentShadows.add(node);
124
+ node = node.host;
125
+ } else {
126
+ node = node.parentNode;
127
+ }
128
+ }
129
+ for (const shadow of shadowTargets) {
130
+ if (!currentShadows.has(shadow)) {
131
+ shadow.removeEventListener("focusin", focusInHandler, true);
132
+ shadow.removeEventListener("focusout", focusOutHandler, true);
133
+ shadowTargets.delete(shadow);
134
+ }
135
+ }
136
+ onFocusIn(target, e.relatedTarget || void 0);
137
+ };
138
+ const onFocusIn = (target, relatedTarget, originalEvent) => {
139
+ var _a;
140
+ const shadowRoot = target.shadowRoot;
141
+ if (shadowRoot) {
142
+ if (shadowTargets.has(shadowRoot)) {
143
+ return;
144
+ }
145
+ shadowRoot.addEventListener("focusin", focusInHandler, true);
146
+ shadowRoot.addEventListener("focusout", focusOutHandler, true);
147
+ shadowTargets.add(shadowRoot);
120
148
  return;
121
149
  }
122
150
  const details = {
123
- relatedTarget: e.relatedTarget || void 0
151
+ relatedTarget,
152
+ originalEvent
124
153
  };
125
154
  const event = new CustomEvent(KEYBORG_FOCUSIN, {
126
155
  cancelable: true,
@@ -137,13 +166,20 @@ function setupFocusEvent(win) {
137
166
  target.dispatchEvent(event);
138
167
  };
139
168
  const data = kwin.__keyborgData = {
140
- focusInHandler
169
+ focusInHandler,
170
+ focusOutHandler,
171
+ shadowTargets
141
172
  };
142
173
  kwin.document.addEventListener(
143
174
  "focusin",
144
175
  kwin.__keyborgData.focusInHandler,
145
176
  true
146
177
  );
178
+ kwin.document.addEventListener(
179
+ "focusout",
180
+ kwin.__keyborgData.focusOutHandler,
181
+ true
182
+ );
147
183
  function focus() {
148
184
  const keyborgNativeFocusEvent = kwin.__keyborgData;
149
185
  if (keyborgNativeFocusEvent) {
@@ -153,6 +189,11 @@ function setupFocusEvent(win) {
153
189
  }
154
190
  return origFocus.apply(this, arguments);
155
191
  }
192
+ let activeElement = kwin.document.activeElement;
193
+ while (activeElement && activeElement.shadowRoot) {
194
+ onFocusIn(activeElement);
195
+ activeElement = activeElement.shadowRoot.activeElement;
196
+ }
156
197
  focus.__keyborgNativeFocus = origFocus;
157
198
  }
158
199
  function disposeFocusEvent(win) {
@@ -166,6 +207,24 @@ function disposeFocusEvent(win) {
166
207
  keyborgNativeFocusEvent.focusInHandler,
167
208
  true
168
209
  );
210
+ kwin.document.removeEventListener(
211
+ "focusout",
212
+ keyborgNativeFocusEvent.focusOutHandler,
213
+ true
214
+ );
215
+ for (const shadow of keyborgNativeFocusEvent.shadowTargets) {
216
+ shadow.removeEventListener(
217
+ "focusin",
218
+ keyborgNativeFocusEvent.focusInHandler,
219
+ true
220
+ );
221
+ shadow.removeEventListener(
222
+ "focusout",
223
+ keyborgNativeFocusEvent.focusOutHandler,
224
+ true
225
+ );
226
+ }
227
+ keyborgNativeFocusEvent.shadowTargets.clear();
169
228
  delete kwin.__keyborgData;
170
229
  }
171
230
  if (origFocus) {
@@ -438,10 +497,11 @@ function disposeKeyborg(instance) {
438
497
  }
439
498
 
440
499
  // src/index.ts
441
- var version = "2.4.1";
500
+ var version = "2.5.0-canary.0";
442
501
  // Annotate the CommonJS export names for ESM import in node:
443
502
  0 && (module.exports = {
444
503
  KEYBORG_FOCUSIN,
504
+ KEYBORG_FOCUSOUT,
445
505
  createKeyborg,
446
506
  disposeKeyborg,
447
507
  getLastFocusedProgrammatically,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/WeakRefInstance.ts","../src/FocusEvent.ts","../src/Keyborg.ts"],"sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport type { Keyborg, KeyborgCallback } from \"./Keyborg\";\nexport { createKeyborg, disposeKeyborg } from \"./Keyborg\";\n\nexport type {\n KeyborgFocusInEvent,\n KeyborgFocusInEventDetails,\n} from \"./FocusEvent\";\nexport {\n getLastFocusedProgrammatically,\n nativeFocus,\n KEYBORG_FOCUSIN,\n} from \"./FocusEvent\";\n\nexport const version = process.env.PKG_VERSION;\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// IE11 compat, checks if WeakRef is supported\nexport const _canUseWeakRef = typeof WeakRef !== \"undefined\";\n\n/**\n * Allows disposable instances to be used\n */\nexport interface Disposable {\n isDisposed?(): boolean;\n}\n\n/**\n * WeakRef wrapper around a HTMLElement that also supports IE11\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef}\n * @internal\n */\nexport class WeakRefInstance<T extends Disposable | object> {\n private _weakRef?: WeakRef<T>;\n private _instance?: T;\n\n constructor(instance: T) {\n if (_canUseWeakRef && typeof instance === \"object\") {\n this._weakRef = new WeakRef(instance);\n } else {\n this._instance = instance;\n }\n }\n\n /**\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef/deref}\n */\n deref(): T | undefined {\n let instance: T | undefined;\n\n if (this._weakRef) {\n instance = this._weakRef?.deref();\n\n if (!instance) {\n delete this._weakRef;\n }\n } else {\n instance = this._instance;\n if ((instance as Disposable)?.isDisposed?.()) {\n delete this._instance;\n }\n }\n\n return instance;\n }\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { WeakRefInstance } from \"./WeakRefInstance\";\n\nexport const KEYBORG_FOCUSIN = \"keyborg:focusin\";\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\ninterface KeyborgFocusEventData {\n focusInHandler: (e: FocusEvent) => void;\n lastFocusedProgrammatically?: WeakRefInstance<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\nfunction canOverrideNativeFocus(win: Window): boolean {\n const HTMLElement = (win as WindowWithKeyborgFocusEvent).HTMLElement;\n const origFocus = HTMLElement.prototype.focus;\n\n let isCustomFocusCalled = false;\n\n HTMLElement.prototype.focus = function focus(): void {\n isCustomFocusCalled = true;\n };\n\n const btn = win.document.createElement(\"button\");\n\n btn.focus();\n\n HTMLElement.prototype.focus = origFocus;\n\n return isCustomFocusCalled;\n}\n\nlet _canOverrideNativeFocus = false;\n\nexport interface KeyborgFocusInEventDetails {\n relatedTarget?: HTMLElement;\n isFocusedProgrammatically?: boolean;\n}\n\nexport interface KeyborgFocusInEvent\n extends CustomEvent<KeyborgFocusInEventDetails> {\n /**\n * @deprecated - used `event.detail`\n */\n details?: KeyborgFocusInEventDetails;\n}\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\n if (!_canOverrideNativeFocus) {\n _canOverrideNativeFocus = canOverrideNativeFocus(kwin);\n }\n\n const origFocus = kwin.HTMLElement.prototype.focus;\n\n if ((origFocus as KeyborgFocus).__keyborgNativeFocus) {\n // Already set up.\n return;\n }\n\n kwin.HTMLElement.prototype.focus = focus;\n\n const focusOutShadowRootHandler = (e: FocusEvent) => {\n const relatedTarget = e.relatedTarget as HTMLElement | null;\n const currentTarget = e.currentTarget as ShadowRoot;\n\n // cleanup polyfill event handlers once focus leaves the shadow root\n if (!currentTarget.contains(relatedTarget)) {\n currentTarget.removeEventListener(\"focusin\", focusInHandler, true);\n currentTarget.removeEventListener(\n \"focusout\",\n focusOutShadowRootHandler,\n true,\n );\n }\n };\n\n const focusInHandler = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n\n if (!target) {\n return;\n }\n\n if (target.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 target.shadowRoot.addEventListener(\"focusin\", focusInHandler, true);\n target.shadowRoot.addEventListener(\n \"focusout\",\n focusOutShadowRootHandler,\n true,\n );\n\n return;\n }\n\n const details: KeyborgFocusInEventDetails = {\n relatedTarget: (e.relatedTarget as HTMLElement) || undefined,\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 // Tabster (and other users) can still use the legacy details field - keeping for backwards compat\n event.details = details;\n\n if (_canOverrideNativeFocus || data.lastFocusedProgrammatically) {\n details.isFocusedProgrammatically =\n target === data.lastFocusedProgrammatically?.deref();\n\n data.lastFocusedProgrammatically = undefined;\n }\n\n target.dispatchEvent(event);\n };\n\n const data: KeyborgFocusEventData = (kwin.__keyborgData = {\n focusInHandler,\n });\n\n kwin.document.addEventListener(\n \"focusin\",\n kwin.__keyborgData.focusInHandler,\n true,\n );\n\n function focus(this: HTMLElement) {\n const keyborgNativeFocusEvent = (kwin as WindowWithKeyborgFocusEvent)\n .__keyborgData;\n\n if (keyborgNativeFocusEvent) {\n keyborgNativeFocusEvent.lastFocusedProgrammatically = new WeakRefInstance(\n this,\n );\n }\n\n // eslint-disable-next-line prefer-rest-params\n return origFocus.apply(this, arguments);\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 keyborgNativeFocusEvent = kwin.__keyborgData;\n\n if (keyborgNativeFocusEvent) {\n kwin.document.removeEventListener(\n \"focusin\",\n keyborgNativeFocusEvent.focusInHandler,\n true,\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 keyborgNativeFocusEvent = (win as WindowWithKeyborgFocusEvent)\n .__keyborgData;\n\n return keyborgNativeFocusEvent\n ? keyborgNativeFocusEvent.lastFocusedProgrammatically?.deref() || null\n : undefined;\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n disposeFocusEvent,\n KeyborgFocusInEvent,\n KEYBORG_FOCUSIN,\n setupFocusEvent,\n} from \"./FocusEvent\";\nimport { Disposable, WeakRefInstance } from \"./WeakRefInstance\";\n\ninterface WindowWithKeyborg extends Window {\n __keyborg?: {\n core: KeyborgCore;\n refs: { [id: string]: Keyborg };\n };\n}\n\nconst _dismissTimeout = 500; // When a key from dismissKeys is pressed and the focus is not moved\n// during _dismissTimeout time, dismiss the keyboard navigation mode.\n\nlet _lastId = 0;\n\nexport interface KeyborgProps {\n // Keys to be used to trigger keyboard navigation mode. By default, any key will trigger\n // it. Could be limited to, for example, just Tab (or Tab and arrow keys).\n triggerKeys?: number[];\n // Keys to be used to dismiss keyboard navigation mode using keyboard (in addition to\n // mouse clicks which dismiss it). For example, Esc could be used to dismiss.\n dismissKeys?: number[];\n}\n\nexport type KeyborgCallback = (isNavigatingWithKeyboard: boolean) => void;\n\n/**\n * Source of truth for all the keyborg core instances and the current keyboard navigation state\n */\nexport class KeyborgState {\n private __keyborgCoreRefs: { [id: string]: WeakRefInstance<KeyborgCore> } =\n {};\n private _isNavigatingWithKeyboard = false;\n\n add(keyborg: KeyborgCore): void {\n const id = keyborg.id;\n\n if (!(id in this.__keyborgCoreRefs)) {\n this.__keyborgCoreRefs[id] = new WeakRefInstance<KeyborgCore>(keyborg);\n }\n }\n\n remove(id: string): void {\n delete this.__keyborgCoreRefs[id];\n\n if (Object.keys(this.__keyborgCoreRefs).length === 0) {\n this._isNavigatingWithKeyboard = false;\n }\n }\n\n setVal(isNavigatingWithKeyboard: boolean): void {\n if (this._isNavigatingWithKeyboard === isNavigatingWithKeyboard) {\n return;\n }\n\n this._isNavigatingWithKeyboard = isNavigatingWithKeyboard;\n\n for (const id of Object.keys(this.__keyborgCoreRefs)) {\n const ref = this.__keyborgCoreRefs[id];\n const keyborg = ref.deref();\n\n if (keyborg) {\n keyborg.update(isNavigatingWithKeyboard);\n } else {\n this.remove(id);\n }\n }\n }\n\n getVal(): boolean {\n return this._isNavigatingWithKeyboard;\n }\n}\n\nconst _state = new KeyborgState();\n\n/**\n * Manages a collection of Keyborg instances in a window/document and updates keyborg state\n */\nclass KeyborgCore implements Disposable {\n readonly id: string;\n\n private _win?: WindowWithKeyborg;\n private _isMouseUsedTimer: number | undefined;\n private _dismissTimer: number | undefined;\n private _triggerKeys?: Set<number>;\n private _dismissKeys?: Set<number>;\n\n constructor(win: WindowWithKeyborg, props?: KeyborgProps) {\n this.id = \"c\" + ++_lastId;\n this._win = win;\n const doc = win.document;\n\n if (props) {\n const triggerKeys = props.triggerKeys;\n const dismissKeys = props.dismissKeys;\n\n if (triggerKeys?.length) {\n this._triggerKeys = new Set(triggerKeys);\n }\n\n if (dismissKeys?.length) {\n this._dismissKeys = new Set(dismissKeys);\n }\n }\n\n doc.addEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!\n doc.addEventListener(\"mousedown\", this._onMouseDown, true); // Capture!\n win.addEventListener(\"keydown\", this._onKeyDown, true); // Capture!\n\n setupFocusEvent(win);\n\n _state.add(this);\n }\n\n dispose(): void {\n const win = this._win;\n\n if (win) {\n if (this._isMouseUsedTimer) {\n win.clearTimeout(this._isMouseUsedTimer);\n this._isMouseUsedTimer = undefined;\n }\n\n if (this._dismissTimer) {\n win.clearTimeout(this._dismissTimer);\n this._dismissTimer = undefined;\n }\n\n disposeFocusEvent(win);\n\n const doc = win.document;\n\n doc.removeEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!\n doc.removeEventListener(\"mousedown\", this._onMouseDown, true); // Capture!\n win.removeEventListener(\"keydown\", this._onKeyDown, true); // Capture!\n\n delete this._win;\n\n _state.remove(this.id);\n }\n }\n\n isDisposed(): boolean {\n return !!this._win;\n }\n\n /**\n * Updates all keyborg instances with the keyboard navigation state\n */\n update(isNavigatingWithKeyboard: boolean): void {\n const keyborgs = this._win?.__keyborg?.refs;\n\n if (keyborgs) {\n for (const id of Object.keys(keyborgs)) {\n Keyborg.update(keyborgs[id], isNavigatingWithKeyboard);\n }\n }\n }\n\n private _onFocusIn = (e: KeyborgFocusInEvent) => {\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 swallow\n // the events when the screen reader shortcuts are used). The screen reader\n // usage is keyboard navigation.\n\n if (this._isMouseUsedTimer) {\n // There was a mouse event recently.\n return;\n }\n\n if (_state.getVal()) {\n return;\n }\n\n const details = e.detail;\n\n if (!details.relatedTarget) {\n return;\n }\n\n if (\n details.isFocusedProgrammatically ||\n details.isFocusedProgrammatically === undefined\n ) {\n // The element is focused programmatically, or the programmatic focus detection\n // is not working.\n return;\n }\n\n _state.setVal(true);\n };\n\n private _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 perform\n // an action on an element, do not dismiss the keyboard navigation mode.\n return;\n }\n\n const win = this._win;\n\n if (win) {\n if (this._isMouseUsedTimer) {\n win.clearTimeout(this._isMouseUsedTimer);\n }\n\n this._isMouseUsedTimer = win.setTimeout(() => {\n delete this._isMouseUsedTimer;\n }, 1000); // Keeping the indication of the mouse usage for some time.\n }\n\n _state.setVal(false);\n };\n\n private _onKeyDown = (e: KeyboardEvent): void => {\n const isNavigatingWithKeyboard = _state.getVal();\n\n if (isNavigatingWithKeyboard) {\n if (this._shouldDismissKeyboardNavigation(e)) {\n this._scheduleDismiss();\n }\n } else {\n if (this._shouldTriggerKeyboardNavigation(e)) {\n _state.setVal(true);\n }\n }\n };\n\n /**\n * @returns whether the keyboard event should trigger keyboard navigation mode\n */\n private _shouldTriggerKeyboardNavigation(e: KeyboardEvent) {\n // TODO Some rich text fields can allow Tab key for indentation so it doesn't\n // need to be a navigation key. If there is a bug regarding that we should revisit\n if (e.key === \"Tab\") {\n return true;\n }\n\n const activeElement = this._win?.document\n .activeElement as HTMLElement | null;\n const isTriggerKey = !this._triggerKeys || this._triggerKeys.has(e.keyCode);\n\n const isEditable =\n activeElement &&\n (activeElement.tagName === \"INPUT\" ||\n activeElement.tagName === \"TEXTAREA\" ||\n activeElement.isContentEditable);\n\n return isTriggerKey && !isEditable;\n }\n\n /**\n * @returns whether the keyboard event should dismiss keyboard navigation mode\n */\n private _shouldDismissKeyboardNavigation(e: KeyboardEvent) {\n return this._dismissKeys?.has(e.keyCode);\n }\n\n private _scheduleDismiss(): void {\n const win = this._win;\n\n if (win) {\n if (this._dismissTimer) {\n win.clearTimeout(this._dismissTimer);\n this._dismissTimer = undefined;\n }\n\n const was = win.document.activeElement;\n\n this._dismissTimer = win.setTimeout(() => {\n this._dismissTimer = undefined;\n\n const cur = win.document.activeElement;\n\n if (was && cur && was === cur) {\n // Esc was pressed, currently focused element hasn't changed.\n // Just dismiss the keyboard navigation mode.\n _state.setVal(false);\n }\n }, _dismissTimeout);\n }\n }\n}\n\n/**\n * Used to determine the keyboard navigation state\n */\nexport class Keyborg {\n private _id: string;\n private _win?: WindowWithKeyborg;\n private _core?: KeyborgCore;\n private _cb: KeyborgCallback[] = [];\n\n static create(win: WindowWithKeyborg, props?: KeyborgProps): Keyborg {\n return new Keyborg(win, props);\n }\n\n static dispose(instance: Keyborg): void {\n instance.dispose();\n }\n\n /**\n * Updates all subscribed callbacks with the keyboard navigation state\n */\n static update(instance: Keyborg, isNavigatingWithKeyboard: boolean): void {\n instance._cb.forEach((callback) => callback(isNavigatingWithKeyboard));\n }\n\n private constructor(win: WindowWithKeyborg, props?: KeyborgProps) {\n this._id = \"k\" + ++_lastId;\n this._win = win;\n\n const current = win.__keyborg;\n\n if (current) {\n this._core = current.core;\n current.refs[this._id] = this;\n } else {\n this._core = new KeyborgCore(win, props);\n win.__keyborg = {\n core: this._core,\n refs: { [this._id]: this },\n };\n }\n }\n\n private dispose(): void {\n const current = this._win?.__keyborg;\n\n if (current?.refs[this._id]) {\n delete current.refs[this._id];\n\n if (Object.keys(current.refs).length === 0) {\n current.core.dispose();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n delete this._win!.__keyborg;\n }\n } else if (process.env.NODE_ENV !== \"production\") {\n console.error(\n `Keyborg instance ${this._id} is being disposed incorrectly.`,\n );\n }\n\n this._cb = [];\n delete this._core;\n delete this._win;\n }\n\n /**\n * @returns Whether the user is navigating with keyboard\n */\n isNavigatingWithKeyboard(): boolean {\n return _state.getVal();\n }\n\n /**\n * @param callback - Called when the keyboard navigation state changes\n */\n subscribe(callback: KeyborgCallback): void {\n this._cb.push(callback);\n }\n\n /**\n * @param callback - Registered with subscribe\n */\n unsubscribe(callback: KeyborgCallback): void {\n const index = this._cb.indexOf(callback);\n\n if (index >= 0) {\n this._cb.splice(index, 1);\n }\n }\n\n /**\n * Manually set the keyboard navigtion state\n */\n setVal(isNavigatingWithKeyboard: boolean): void {\n _state.setVal(isNavigatingWithKeyboard);\n }\n}\n\nexport function createKeyborg(win: Window, props?: KeyborgProps): Keyborg {\n return Keyborg.create(win, props);\n}\n\nexport function disposeKeyborg(instance: Keyborg) {\n Keyborg.dispose(instance);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/WeakRefInstance.ts","../src/FocusEvent.ts","../src/Keyborg.ts"],"sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport type { Keyborg, KeyborgCallback } from \"./Keyborg\";\nexport { createKeyborg, disposeKeyborg } from \"./Keyborg\";\n\nexport type {\n KeyborgFocusInEvent,\n KeyborgFocusInEventDetails,\n KeyborgFocusOutEvent,\n KeyborgFocusOutEventDetails,\n} from \"./FocusEvent\";\nexport {\n getLastFocusedProgrammatically,\n nativeFocus,\n KEYBORG_FOCUSIN,\n KEYBORG_FOCUSOUT,\n} from \"./FocusEvent\";\n\nexport const version = process.env.PKG_VERSION;\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// IE11 compat, checks if WeakRef is supported\nexport const _canUseWeakRef = typeof WeakRef !== \"undefined\";\n\n/**\n * Allows disposable instances to be used\n */\nexport interface Disposable {\n isDisposed?(): boolean;\n}\n\n/**\n * WeakRef wrapper around a HTMLElement that also supports IE11\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef}\n * @internal\n */\nexport class WeakRefInstance<T extends Disposable | object> {\n private _weakRef?: WeakRef<T>;\n private _instance?: T;\n\n constructor(instance: T) {\n if (_canUseWeakRef && typeof instance === \"object\") {\n this._weakRef = new WeakRef(instance);\n } else {\n this._instance = instance;\n }\n }\n\n /**\n * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef/deref}\n */\n deref(): T | undefined {\n let instance: T | undefined;\n\n if (this._weakRef) {\n instance = this._weakRef?.deref();\n\n if (!instance) {\n delete this._weakRef;\n }\n } else {\n instance = this._instance;\n if ((instance as Disposable)?.isDisposed?.()) {\n delete this._instance;\n }\n }\n\n return instance;\n }\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { WeakRefInstance } from \"./WeakRefInstance\";\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\ninterface KeyborgFocusEventData {\n focusInHandler: (e: FocusEvent) => void;\n focusOutHandler: (e: FocusEvent) => void;\n lastFocusedProgrammatically?: WeakRefInstance<HTMLElement>;\n shadowTargets: Set<ShadowRoot>;\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\nfunction canOverrideNativeFocus(win: Window): boolean {\n const HTMLElement = (win as WindowWithKeyborgFocusEvent).HTMLElement;\n const origFocus = HTMLElement.prototype.focus;\n\n let isCustomFocusCalled = false;\n\n HTMLElement.prototype.focus = function focus(): void {\n isCustomFocusCalled = true;\n };\n\n const btn = win.document.createElement(\"button\");\n\n btn.focus();\n\n HTMLElement.prototype.focus = origFocus;\n\n return isCustomFocusCalled;\n}\n\nlet _canOverrideNativeFocus = false;\n\nexport interface KeyborgFocusInEventDetails {\n relatedTarget?: HTMLElement;\n isFocusedProgrammatically?: boolean;\n originalEvent?: FocusEvent;\n}\n\nexport interface KeyborgFocusInEvent\n extends CustomEvent<KeyborgFocusInEventDetails> {\n /**\n * @deprecated - used `event.detail`\n */\n details?: KeyborgFocusInEventDetails;\n}\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\n if (!_canOverrideNativeFocus) {\n _canOverrideNativeFocus = canOverrideNativeFocus(kwin);\n }\n\n const origFocus = kwin.HTMLElement.prototype.focus;\n\n if ((origFocus as KeyborgFocus).__keyborgNativeFocus) {\n // Already set up.\n return;\n }\n\n kwin.HTMLElement.prototype.focus = focus;\n\n const shadowTargets: Set<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 = target;\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 shadow of shadowTargets) {\n if (!currentShadows.has(shadow)) {\n shadow.removeEventListener(\"focusin\", focusInHandler, true);\n shadow.removeEventListener(\"focusout\", focusOutHandler, true);\n shadowTargets.delete(shadow);\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 if (shadowTargets.has(shadowRoot)) {\n return;\n }\n\n shadowRoot.addEventListener(\"focusin\", focusInHandler, true);\n shadowRoot.addEventListener(\"focusout\", focusOutHandler, true);\n\n shadowTargets.add(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 // Tabster (and other users) can still use the legacy details field - keeping for backwards compat\n event.details = details;\n\n if (_canOverrideNativeFocus || data.lastFocusedProgrammatically) {\n details.isFocusedProgrammatically =\n target === data.lastFocusedProgrammatically?.deref();\n\n data.lastFocusedProgrammatically = undefined;\n }\n\n target.dispatchEvent(event);\n };\n\n const data: KeyborgFocusEventData = (kwin.__keyborgData = {\n focusInHandler,\n focusOutHandler,\n shadowTargets,\n });\n\n kwin.document.addEventListener(\n \"focusin\",\n kwin.__keyborgData.focusInHandler,\n true,\n );\n\n kwin.document.addEventListener(\n \"focusout\",\n kwin.__keyborgData.focusOutHandler,\n true,\n );\n\n function focus(this: HTMLElement) {\n const keyborgNativeFocusEvent = (kwin as WindowWithKeyborgFocusEvent)\n .__keyborgData;\n\n if (keyborgNativeFocusEvent) {\n keyborgNativeFocusEvent.lastFocusedProgrammatically = new WeakRefInstance(\n this,\n );\n }\n\n // eslint-disable-next-line prefer-rest-params\n return origFocus.apply(this, arguments);\n }\n\n let activeElement = kwin.document.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 keyborgNativeFocusEvent = kwin.__keyborgData;\n\n if (keyborgNativeFocusEvent) {\n kwin.document.removeEventListener(\n \"focusin\",\n keyborgNativeFocusEvent.focusInHandler,\n true,\n );\n\n kwin.document.removeEventListener(\n \"focusout\",\n keyborgNativeFocusEvent.focusOutHandler,\n true,\n );\n\n for (const shadow of keyborgNativeFocusEvent.shadowTargets) {\n shadow.removeEventListener(\n \"focusin\",\n keyborgNativeFocusEvent.focusInHandler,\n true,\n );\n shadow.removeEventListener(\n \"focusout\",\n keyborgNativeFocusEvent.focusOutHandler,\n true,\n );\n }\n\n keyborgNativeFocusEvent.shadowTargets.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 keyborgNativeFocusEvent = (win as WindowWithKeyborgFocusEvent)\n .__keyborgData;\n\n return keyborgNativeFocusEvent\n ? keyborgNativeFocusEvent.lastFocusedProgrammatically?.deref() || null\n : undefined;\n}\n","/*!\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n disposeFocusEvent,\n KeyborgFocusInEvent,\n KEYBORG_FOCUSIN,\n setupFocusEvent,\n} from \"./FocusEvent\";\nimport { Disposable, WeakRefInstance } from \"./WeakRefInstance\";\n\ninterface WindowWithKeyborg extends Window {\n __keyborg?: {\n core: KeyborgCore;\n refs: { [id: string]: Keyborg };\n };\n}\n\nconst _dismissTimeout = 500; // When a key from dismissKeys is pressed and the focus is not moved\n// during _dismissTimeout time, dismiss the keyboard navigation mode.\n\nlet _lastId = 0;\n\nexport interface KeyborgProps {\n // Keys to be used to trigger keyboard navigation mode. By default, any key will trigger\n // it. Could be limited to, for example, just Tab (or Tab and arrow keys).\n triggerKeys?: number[];\n // Keys to be used to dismiss keyboard navigation mode using keyboard (in addition to\n // mouse clicks which dismiss it). For example, Esc could be used to dismiss.\n dismissKeys?: number[];\n}\n\nexport type KeyborgCallback = (isNavigatingWithKeyboard: boolean) => void;\n\n/**\n * Source of truth for all the keyborg core instances and the current keyboard navigation state\n */\nexport class KeyborgState {\n private __keyborgCoreRefs: { [id: string]: WeakRefInstance<KeyborgCore> } =\n {};\n private _isNavigatingWithKeyboard = false;\n\n add(keyborg: KeyborgCore): void {\n const id = keyborg.id;\n\n if (!(id in this.__keyborgCoreRefs)) {\n this.__keyborgCoreRefs[id] = new WeakRefInstance<KeyborgCore>(keyborg);\n }\n }\n\n remove(id: string): void {\n delete this.__keyborgCoreRefs[id];\n\n if (Object.keys(this.__keyborgCoreRefs).length === 0) {\n this._isNavigatingWithKeyboard = false;\n }\n }\n\n setVal(isNavigatingWithKeyboard: boolean): void {\n if (this._isNavigatingWithKeyboard === isNavigatingWithKeyboard) {\n return;\n }\n\n this._isNavigatingWithKeyboard = isNavigatingWithKeyboard;\n\n for (const id of Object.keys(this.__keyborgCoreRefs)) {\n const ref = this.__keyborgCoreRefs[id];\n const keyborg = ref.deref();\n\n if (keyborg) {\n keyborg.update(isNavigatingWithKeyboard);\n } else {\n this.remove(id);\n }\n }\n }\n\n getVal(): boolean {\n return this._isNavigatingWithKeyboard;\n }\n}\n\nconst _state = new KeyborgState();\n\n/**\n * Manages a collection of Keyborg instances in a window/document and updates keyborg state\n */\nclass KeyborgCore implements Disposable {\n readonly id: string;\n\n private _win?: WindowWithKeyborg;\n private _isMouseUsedTimer: number | undefined;\n private _dismissTimer: number | undefined;\n private _triggerKeys?: Set<number>;\n private _dismissKeys?: Set<number>;\n\n constructor(win: WindowWithKeyborg, props?: KeyborgProps) {\n this.id = \"c\" + ++_lastId;\n this._win = win;\n const doc = win.document;\n\n if (props) {\n const triggerKeys = props.triggerKeys;\n const dismissKeys = props.dismissKeys;\n\n if (triggerKeys?.length) {\n this._triggerKeys = new Set(triggerKeys);\n }\n\n if (dismissKeys?.length) {\n this._dismissKeys = new Set(dismissKeys);\n }\n }\n\n doc.addEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!\n doc.addEventListener(\"mousedown\", this._onMouseDown, true); // Capture!\n win.addEventListener(\"keydown\", this._onKeyDown, true); // Capture!\n\n setupFocusEvent(win);\n\n _state.add(this);\n }\n\n dispose(): void {\n const win = this._win;\n\n if (win) {\n if (this._isMouseUsedTimer) {\n win.clearTimeout(this._isMouseUsedTimer);\n this._isMouseUsedTimer = undefined;\n }\n\n if (this._dismissTimer) {\n win.clearTimeout(this._dismissTimer);\n this._dismissTimer = undefined;\n }\n\n disposeFocusEvent(win);\n\n const doc = win.document;\n\n doc.removeEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!\n doc.removeEventListener(\"mousedown\", this._onMouseDown, true); // Capture!\n win.removeEventListener(\"keydown\", this._onKeyDown, true); // Capture!\n\n delete this._win;\n\n _state.remove(this.id);\n }\n }\n\n isDisposed(): boolean {\n return !!this._win;\n }\n\n /**\n * Updates all keyborg instances with the keyboard navigation state\n */\n update(isNavigatingWithKeyboard: boolean): void {\n const keyborgs = this._win?.__keyborg?.refs;\n\n if (keyborgs) {\n for (const id of Object.keys(keyborgs)) {\n Keyborg.update(keyborgs[id], isNavigatingWithKeyboard);\n }\n }\n }\n\n private _onFocusIn = (e: KeyborgFocusInEvent) => {\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 swallow\n // the events when the screen reader shortcuts are used). The screen reader\n // usage is keyboard navigation.\n\n if (this._isMouseUsedTimer) {\n // There was a mouse event recently.\n return;\n }\n\n if (_state.getVal()) {\n return;\n }\n\n const details = e.detail;\n\n if (!details.relatedTarget) {\n return;\n }\n\n if (\n details.isFocusedProgrammatically ||\n details.isFocusedProgrammatically === undefined\n ) {\n // The element is focused programmatically, or the programmatic focus detection\n // is not working.\n return;\n }\n\n _state.setVal(true);\n };\n\n private _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 perform\n // an action on an element, do not dismiss the keyboard navigation mode.\n return;\n }\n\n const win = this._win;\n\n if (win) {\n if (this._isMouseUsedTimer) {\n win.clearTimeout(this._isMouseUsedTimer);\n }\n\n this._isMouseUsedTimer = win.setTimeout(() => {\n delete this._isMouseUsedTimer;\n }, 1000); // Keeping the indication of the mouse usage for some time.\n }\n\n _state.setVal(false);\n };\n\n private _onKeyDown = (e: KeyboardEvent): void => {\n const isNavigatingWithKeyboard = _state.getVal();\n\n if (isNavigatingWithKeyboard) {\n if (this._shouldDismissKeyboardNavigation(e)) {\n this._scheduleDismiss();\n }\n } else {\n if (this._shouldTriggerKeyboardNavigation(e)) {\n _state.setVal(true);\n }\n }\n };\n\n /**\n * @returns whether the keyboard event should trigger keyboard navigation mode\n */\n private _shouldTriggerKeyboardNavigation(e: KeyboardEvent) {\n // TODO Some rich text fields can allow Tab key for indentation so it doesn't\n // need to be a navigation key. If there is a bug regarding that we should revisit\n if (e.key === \"Tab\") {\n return true;\n }\n\n const activeElement = this._win?.document\n .activeElement as HTMLElement | null;\n const isTriggerKey = !this._triggerKeys || this._triggerKeys.has(e.keyCode);\n\n const isEditable =\n activeElement &&\n (activeElement.tagName === \"INPUT\" ||\n activeElement.tagName === \"TEXTAREA\" ||\n activeElement.isContentEditable);\n\n return isTriggerKey && !isEditable;\n }\n\n /**\n * @returns whether the keyboard event should dismiss keyboard navigation mode\n */\n private _shouldDismissKeyboardNavigation(e: KeyboardEvent) {\n return this._dismissKeys?.has(e.keyCode);\n }\n\n private _scheduleDismiss(): void {\n const win = this._win;\n\n if (win) {\n if (this._dismissTimer) {\n win.clearTimeout(this._dismissTimer);\n this._dismissTimer = undefined;\n }\n\n const was = win.document.activeElement;\n\n this._dismissTimer = win.setTimeout(() => {\n this._dismissTimer = undefined;\n\n const cur = win.document.activeElement;\n\n if (was && cur && was === cur) {\n // Esc was pressed, currently focused element hasn't changed.\n // Just dismiss the keyboard navigation mode.\n _state.setVal(false);\n }\n }, _dismissTimeout);\n }\n }\n}\n\n/**\n * Used to determine the keyboard navigation state\n */\nexport class Keyborg {\n private _id: string;\n private _win?: WindowWithKeyborg;\n private _core?: KeyborgCore;\n private _cb: KeyborgCallback[] = [];\n\n static create(win: WindowWithKeyborg, props?: KeyborgProps): Keyborg {\n return new Keyborg(win, props);\n }\n\n static dispose(instance: Keyborg): void {\n instance.dispose();\n }\n\n /**\n * Updates all subscribed callbacks with the keyboard navigation state\n */\n static update(instance: Keyborg, isNavigatingWithKeyboard: boolean): void {\n instance._cb.forEach((callback) => callback(isNavigatingWithKeyboard));\n }\n\n private constructor(win: WindowWithKeyborg, props?: KeyborgProps) {\n this._id = \"k\" + ++_lastId;\n this._win = win;\n\n const current = win.__keyborg;\n\n if (current) {\n this._core = current.core;\n current.refs[this._id] = this;\n } else {\n this._core = new KeyborgCore(win, props);\n win.__keyborg = {\n core: this._core,\n refs: { [this._id]: this },\n };\n }\n }\n\n private dispose(): void {\n const current = this._win?.__keyborg;\n\n if (current?.refs[this._id]) {\n delete current.refs[this._id];\n\n if (Object.keys(current.refs).length === 0) {\n current.core.dispose();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n delete this._win!.__keyborg;\n }\n } else if (process.env.NODE_ENV !== \"production\") {\n console.error(\n `Keyborg instance ${this._id} is being disposed incorrectly.`,\n );\n }\n\n this._cb = [];\n delete this._core;\n delete this._win;\n }\n\n /**\n * @returns Whether the user is navigating with keyboard\n */\n isNavigatingWithKeyboard(): boolean {\n return _state.getVal();\n }\n\n /**\n * @param callback - Called when the keyboard navigation state changes\n */\n subscribe(callback: KeyborgCallback): void {\n this._cb.push(callback);\n }\n\n /**\n * @param callback - Registered with subscribe\n */\n unsubscribe(callback: KeyborgCallback): void {\n const index = this._cb.indexOf(callback);\n\n if (index >= 0) {\n this._cb.splice(index, 1);\n }\n }\n\n /**\n * Manually set the keyboard navigtion state\n */\n setVal(isNavigatingWithKeyboard: boolean): void {\n _state.setVal(isNavigatingWithKeyboard);\n }\n}\n\nexport function createKeyborg(win: Window, props?: KeyborgProps): Keyborg {\n return Keyborg.create(win, props);\n}\n\nexport function disposeKeyborg(instance: Keyborg) {\n Keyborg.dispose(instance);\n}\n"],"mappings":"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","names":[]}
@@ -87,9 +87,11 @@ declare class Keyborg {
87
87
  declare function createKeyborg(win: Window, props?: KeyborgProps): Keyborg;
88
88
  declare function disposeKeyborg(instance: Keyborg): void;
89
89
  declare const KEYBORG_FOCUSIN = "keyborg:focusin";
90
+ declare const KEYBORG_FOCUSOUT = "keyborg:focusout";
90
91
  interface KeyborgFocusInEventDetails {
91
92
  relatedTarget?: HTMLElement;
92
93
  isFocusedProgrammatically?: boolean;
94
+ originalEvent?: FocusEvent;
93
95
  }
94
96
  interface KeyborgFocusInEvent extends CustomEvent<KeyborgFocusInEventDetails> {
95
97
  /**
@@ -97,6 +99,10 @@ interface KeyborgFocusInEvent extends CustomEvent<KeyborgFocusInEventDetails> {
97
99
  */
98
100
  details?: KeyborgFocusInEventDetails;
99
101
  }
102
+ interface KeyborgFocusOutEventDetails {
103
+ originalEvent: FocusEvent;
104
+ }
105
+ type KeyborgFocusOutEvent = CustomEvent<KeyborgFocusOutEventDetails>;
100
106
  /**
101
107
  * Guarantees that the native `focus` will be used
102
108
  */
@@ -111,4 +117,4 @@ declare function getLastFocusedProgrammatically(win: Window): HTMLElement | null
111
117
  * Licensed under the MIT License.
112
118
  */
113
119
  declare const version: string | undefined;
114
- export { KEYBORG_FOCUSIN, Keyborg, KeyborgCallback, KeyborgFocusInEvent, KeyborgFocusInEventDetails, createKeyborg, disposeKeyborg, getLastFocusedProgrammatically, nativeFocus, version };
120
+ export { KEYBORG_FOCUSIN, KEYBORG_FOCUSOUT, Keyborg, KeyborgCallback, KeyborgFocusInEvent, KeyborgFocusInEventDetails, KeyborgFocusOutEvent, KeyborgFocusOutEventDetails, createKeyborg, disposeKeyborg, getLastFocusedProgrammatically, nativeFocus, version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keyborg",
3
- "version": "2.4.1",
3
+ "version": "2.5.0-canary.0",
4
4
  "description": "Keyboard Navigation Detection for Web",
5
5
  "author": "Marat Abdullin <marata@microsoft.com>",
6
6
  "license": "MIT",