keyborg 2.13.0-canary.0 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,506 +1,386 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __export = (target, all) => {
6
- for (var name in all)
7
- __defProp(target, name, { get: all[name], enumerable: true });
8
- };
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") {
11
- for (let key of __getOwnPropNames(from))
12
- if (!__hasOwnProp.call(to, key) && key !== except)
13
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
- }
15
- return to;
1
+ //#region src/dom.mts
2
+ /*!
3
+ * Copyright (c) Microsoft Corporation. All rights reserved.
4
+ * Licensed under the MIT License.
5
+ */
6
+ const addEventListener = (target, type, handler) => {
7
+ target.addEventListener(type, handler, true);
16
8
  };
17
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
-
19
- // src/index.ts
20
- var src_exports = {};
21
- __export(src_exports, {
22
- KEYBORG_FOCUSIN: () => KEYBORG_FOCUSIN,
23
- KEYBORG_FOCUSOUT: () => KEYBORG_FOCUSOUT,
24
- createKeyborg: () => createKeyborg,
25
- disposeKeyborg: () => disposeKeyborg,
26
- getLastFocusedProgrammatically: () => getLastFocusedProgrammatically,
27
- nativeFocus: () => nativeFocus,
28
- version: () => version
29
- });
30
- module.exports = __toCommonJS(src_exports);
31
-
32
- // src/WeakRefInstance.ts
33
- var _canUseWeakRef = typeof WeakRef !== "undefined";
34
- var WeakRefInstance = class {
35
- constructor(instance) {
36
- if (_canUseWeakRef && typeof instance === "object") {
37
- this._weakRef = new WeakRef(instance);
38
- } else {
39
- this._instance = instance;
40
- }
41
- }
42
- /**
43
- * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef/deref}
44
- */
45
- deref() {
46
- var _a, _b;
47
- let instance;
48
- if (this._weakRef) {
49
- instance = (_a = this._weakRef) == null ? void 0 : _a.deref();
50
- if (!instance) {
51
- delete this._weakRef;
52
- }
53
- } else {
54
- instance = this._instance;
55
- if ((_b = instance == null ? void 0 : instance.isDisposed) == null ? void 0 : _b.call(instance)) {
56
- delete this._instance;
57
- }
58
- }
59
- return instance;
60
- }
9
+ const removeEventListener = (target, type, handler) => {
10
+ target.removeEventListener(type, handler, true);
61
11
  };
62
12
 
63
- // src/FocusEvent.ts
64
- var KEYBORG_FOCUSIN = "keyborg:focusin";
65
- var KEYBORG_FOCUSOUT = "keyborg:focusout";
13
+ //#endregion
14
+ //#region src/FocusEvent.mts
15
+ /*!
16
+ * Copyright (c) Microsoft Corporation. All rights reserved.
17
+ * Licensed under the MIT License.
18
+ */
19
+ const KEYBORG_FOCUSIN = "keyborg:focusin";
20
+ const KEYBORG_FOCUSOUT = "keyborg:focusout";
21
+ const FOCUS_IN_HANDLER = 0;
22
+ const FOCUS_OUT_HANDLER = 1;
23
+ const SHADOW_TARGETS = 2;
24
+ const LAST_FOCUSED_PROGRAMMATICALLY = 3;
66
25
  function canOverrideNativeFocus(win) {
67
- const HTMLElement = win.HTMLElement;
68
- const origFocus = HTMLElement.prototype.focus;
69
- let isCustomFocusCalled = false;
70
- HTMLElement.prototype.focus = function focus() {
71
- isCustomFocusCalled = true;
72
- };
73
- const btn = win.document.createElement("button");
74
- btn.focus();
75
- HTMLElement.prototype.focus = origFocus;
76
- return isCustomFocusCalled;
26
+ const HTMLElement = win.HTMLElement;
27
+ const origFocus = HTMLElement.prototype.focus;
28
+ let isCustomFocusCalled = false;
29
+ HTMLElement.prototype.focus = function focus() {
30
+ isCustomFocusCalled = true;
31
+ };
32
+ win.document.createElement("button").focus();
33
+ HTMLElement.prototype.focus = origFocus;
34
+ return isCustomFocusCalled;
77
35
  }
78
- var _canOverrideNativeFocus = false;
36
+ let _canOverrideNativeFocus = false;
37
+ /**
38
+ * Guarantees that the native `focus` will be used
39
+ */
79
40
  function nativeFocus(element) {
80
- const focus = element.focus;
81
- if (focus.__keyborgNativeFocus) {
82
- focus.__keyborgNativeFocus.call(element);
83
- } else {
84
- element.focus();
85
- }
41
+ const focus = element.focus;
42
+ if (focus.__keyborgNativeFocus) focus.__keyborgNativeFocus.call(element);
43
+ else element.focus();
86
44
  }
45
+ /**
46
+ * Overrides the native `focus` and setups the keyborg focus event
47
+ */
87
48
  function setupFocusEvent(win) {
88
- const kwin = win;
89
- if (!_canOverrideNativeFocus) {
90
- _canOverrideNativeFocus = canOverrideNativeFocus(kwin);
91
- }
92
- const origFocus = kwin.HTMLElement.prototype.focus;
93
- if (origFocus.__keyborgNativeFocus) {
94
- return;
95
- }
96
- kwin.HTMLElement.prototype.focus = focus;
97
- const shadowTargets = /* @__PURE__ */ new Set();
98
- const focusOutHandler = (e) => {
99
- const target = e.target;
100
- if (!target) {
101
- return;
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);
113
- };
114
- const focusInHandler = (e) => {
115
- const target = e.target;
116
- if (!target) {
117
- return;
118
- }
119
- let node = e.composedPath()[0];
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 shadowRootWeakRef of shadowTargets) {
130
- const shadowRoot = shadowRootWeakRef.deref();
131
- if (!shadowRoot || !currentShadows.has(shadowRoot)) {
132
- shadowTargets.delete(shadowRootWeakRef);
133
- if (shadowRoot) {
134
- shadowRoot.removeEventListener("focusin", focusInHandler, true);
135
- shadowRoot.removeEventListener("focusout", focusOutHandler, true);
136
- }
137
- }
138
- }
139
- onFocusIn(target, e.relatedTarget || void 0);
140
- };
141
- const onFocusIn = (target, relatedTarget, originalEvent) => {
142
- var _a;
143
- const shadowRoot = target.shadowRoot;
144
- if (shadowRoot) {
145
- for (const shadowRootWeakRef of shadowTargets) {
146
- if (shadowRootWeakRef.deref() === shadowRoot) {
147
- return;
148
- }
149
- }
150
- shadowRoot.addEventListener("focusin", focusInHandler, true);
151
- shadowRoot.addEventListener("focusout", focusOutHandler, true);
152
- shadowTargets.add(new WeakRefInstance(shadowRoot));
153
- return;
154
- }
155
- const details = {
156
- relatedTarget,
157
- originalEvent
158
- };
159
- const event = new CustomEvent(KEYBORG_FOCUSIN, {
160
- cancelable: true,
161
- bubbles: true,
162
- // Allows the event to bubble past an open shadow root
163
- composed: true,
164
- detail: details
165
- });
166
- event.details = details;
167
- if (_canOverrideNativeFocus || data.lastFocusedProgrammatically) {
168
- details.isFocusedProgrammatically = target === ((_a = data.lastFocusedProgrammatically) == null ? void 0 : _a.deref());
169
- data.lastFocusedProgrammatically = void 0;
170
- }
171
- target.dispatchEvent(event);
172
- };
173
- const data = kwin.__keyborgData = {
174
- focusInHandler,
175
- focusOutHandler,
176
- shadowTargets
177
- };
178
- kwin.document.addEventListener(
179
- "focusin",
180
- kwin.__keyborgData.focusInHandler,
181
- true
182
- );
183
- kwin.document.addEventListener(
184
- "focusout",
185
- kwin.__keyborgData.focusOutHandler,
186
- true
187
- );
188
- function focus() {
189
- const keyborgNativeFocusEvent = kwin.__keyborgData;
190
- if (keyborgNativeFocusEvent) {
191
- keyborgNativeFocusEvent.lastFocusedProgrammatically = new WeakRefInstance(
192
- this
193
- );
194
- }
195
- return origFocus.apply(this, arguments);
196
- }
197
- let activeElement = kwin.document.activeElement;
198
- while (activeElement && activeElement.shadowRoot) {
199
- onFocusIn(activeElement);
200
- activeElement = activeElement.shadowRoot.activeElement;
201
- }
202
- focus.__keyborgNativeFocus = origFocus;
49
+ const kwin = win;
50
+ const doc = kwin.document;
51
+ const proto = kwin.HTMLElement.prototype;
52
+ if (!_canOverrideNativeFocus) _canOverrideNativeFocus = canOverrideNativeFocus(kwin);
53
+ const origFocus = proto.focus;
54
+ if (origFocus.__keyborgNativeFocus) return;
55
+ proto.focus = focus;
56
+ const shadowTargets = /* @__PURE__ */ new Set();
57
+ const focusOutHandler = (e) => {
58
+ const target = e.target;
59
+ if (!target) return;
60
+ const event = new CustomEvent(KEYBORG_FOCUSOUT, {
61
+ cancelable: true,
62
+ bubbles: true,
63
+ composed: true,
64
+ detail: { originalEvent: e }
65
+ });
66
+ target.dispatchEvent(event);
67
+ };
68
+ const focusInHandler = (e) => {
69
+ const target = e.target;
70
+ if (!target) return;
71
+ let node = e.composedPath()[0];
72
+ const currentShadows = /* @__PURE__ */ new Set();
73
+ while (node) if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
74
+ currentShadows.add(node);
75
+ node = node.host;
76
+ } else node = node.parentNode;
77
+ for (const shadowRootWeakRef of shadowTargets) {
78
+ const shadowRoot = shadowRootWeakRef.deref();
79
+ if (!shadowRoot || !currentShadows.has(shadowRoot)) {
80
+ shadowTargets.delete(shadowRootWeakRef);
81
+ if (shadowRoot) {
82
+ removeEventListener(shadowRoot, "focusin", focusInHandler);
83
+ removeEventListener(shadowRoot, "focusout", focusOutHandler);
84
+ }
85
+ }
86
+ }
87
+ onFocusIn(target, e.relatedTarget || void 0);
88
+ };
89
+ const onFocusIn = (target, relatedTarget, originalEvent) => {
90
+ const shadowRoot = target.shadowRoot;
91
+ if (shadowRoot) {
92
+ /**
93
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=1512028
94
+ * focusin events don't bubble up through an open shadow root once focus is inside
95
+ * once focus moves into a shadow root - we drop the same focusin handler there
96
+ * keyborg's custom event will still bubble up since it is composed
97
+ * event handlers should be cleaned up once focus leaves the shadow root.
98
+ *
99
+ * When a focusin event is dispatched from a shadow root, its target is the shadow root parent.
100
+ * Each shadow root encounter requires a new capture listener.
101
+ * Why capture? - we want to follow the focus event in order or descending nested shadow roots
102
+ * When there are no more shadow root targets - dispatch the keyborg:focusin event
103
+ *
104
+ * 1. no focus event
105
+ * > document - capture listener ✅
106
+ * > shadow root 1
107
+ * > shadow root 2
108
+ * > shadow root 3
109
+ * > focused element
110
+ *
111
+ * 2. focus event received by document listener
112
+ * > document - capture listener ✅ (focus event here)
113
+ * > shadow root 1 - capture listener ✅
114
+ * > shadow root 2
115
+ * > shadow root 3
116
+ * > focused element
117
+ *
118
+ * 3. focus event received by root l1 listener
119
+ * > document - capture listener ✅
120
+ * > shadow root 1 - capture listener ✅ (focus event here)
121
+ * > shadow root 2 - capture listener ✅
122
+ * > shadow root 3
123
+ * > focused element
124
+ *
125
+ * 4. focus event received by root l2 listener
126
+ * > document - capture listener ✅
127
+ * > shadow root 1 - capture listener ✅
128
+ * > shadow root 2 - capture listener ✅ (focus event here)
129
+ * > shadow root 3 - capture listener ✅
130
+ * > focused element
131
+ *
132
+ * 5. focus event received by root l3 listener, no more shadow root targets
133
+ * > document - capture listener ✅
134
+ * > shadow root 1 - capture listener ✅
135
+ * > shadow root 2 - capture listener ✅
136
+ * > shadow root 3 - capture listener ✅ (focus event here)
137
+ * > focused element ✅ (no shadow root - dispatch keyborg event)
138
+ */
139
+ for (const shadowRootWeakRef of shadowTargets) if (shadowRootWeakRef.deref() === shadowRoot) return;
140
+ addEventListener(shadowRoot, "focusin", focusInHandler);
141
+ addEventListener(shadowRoot, "focusout", focusOutHandler);
142
+ shadowTargets.add(new WeakRef(shadowRoot));
143
+ return;
144
+ }
145
+ const details = {
146
+ relatedTarget,
147
+ originalEvent
148
+ };
149
+ const event = new CustomEvent(KEYBORG_FOCUSIN, {
150
+ cancelable: true,
151
+ bubbles: true,
152
+ composed: true,
153
+ detail: details
154
+ });
155
+ event.details = details;
156
+ if (_canOverrideNativeFocus || data[LAST_FOCUSED_PROGRAMMATICALLY]) {
157
+ details.isFocusedProgrammatically = target === data[LAST_FOCUSED_PROGRAMMATICALLY]?.deref();
158
+ data[LAST_FOCUSED_PROGRAMMATICALLY] = void 0;
159
+ }
160
+ target.dispatchEvent(event);
161
+ };
162
+ const data = [
163
+ focusInHandler,
164
+ focusOutHandler,
165
+ shadowTargets
166
+ ];
167
+ kwin.__keyborgData = data;
168
+ addEventListener(doc, "focusin", focusInHandler);
169
+ addEventListener(doc, "focusout", focusOutHandler);
170
+ function focus() {
171
+ const d = kwin.__keyborgData;
172
+ if (d) d[LAST_FOCUSED_PROGRAMMATICALLY] = new WeakRef(this);
173
+ return origFocus.apply(this, arguments);
174
+ }
175
+ let activeElement = doc.activeElement;
176
+ while (activeElement && activeElement.shadowRoot) {
177
+ onFocusIn(activeElement);
178
+ activeElement = activeElement.shadowRoot.activeElement;
179
+ }
180
+ focus.__keyborgNativeFocus = origFocus;
203
181
  }
182
+ /**
183
+ * Removes keyborg event listeners and custom focus override
184
+ * @param win The window that stores keyborg focus events
185
+ */
204
186
  function disposeFocusEvent(win) {
205
- const kwin = win;
206
- const proto = kwin.HTMLElement.prototype;
207
- const origFocus = proto.focus.__keyborgNativeFocus;
208
- const keyborgNativeFocusEvent = kwin.__keyborgData;
209
- if (keyborgNativeFocusEvent) {
210
- kwin.document.removeEventListener(
211
- "focusin",
212
- keyborgNativeFocusEvent.focusInHandler,
213
- true
214
- );
215
- kwin.document.removeEventListener(
216
- "focusout",
217
- keyborgNativeFocusEvent.focusOutHandler,
218
- true
219
- );
220
- for (const shadowRootWeakRef of keyborgNativeFocusEvent.shadowTargets) {
221
- const shadowRoot = shadowRootWeakRef.deref();
222
- if (shadowRoot) {
223
- shadowRoot.removeEventListener(
224
- "focusin",
225
- keyborgNativeFocusEvent.focusInHandler,
226
- true
227
- );
228
- shadowRoot.removeEventListener(
229
- "focusout",
230
- keyborgNativeFocusEvent.focusOutHandler,
231
- true
232
- );
233
- }
234
- }
235
- keyborgNativeFocusEvent.shadowTargets.clear();
236
- delete kwin.__keyborgData;
237
- }
238
- if (origFocus) {
239
- proto.focus = origFocus;
240
- }
187
+ const kwin = win;
188
+ const proto = kwin.HTMLElement.prototype;
189
+ const origFocus = proto.focus.__keyborgNativeFocus;
190
+ const data = kwin.__keyborgData;
191
+ if (data) {
192
+ const doc = kwin.document;
193
+ removeEventListener(doc, "focusin", data[FOCUS_IN_HANDLER]);
194
+ removeEventListener(doc, "focusout", data[FOCUS_OUT_HANDLER]);
195
+ for (const shadowRootWeakRef of data[SHADOW_TARGETS]) {
196
+ const shadowRoot = shadowRootWeakRef.deref();
197
+ if (shadowRoot) {
198
+ removeEventListener(shadowRoot, "focusin", data[FOCUS_IN_HANDLER]);
199
+ removeEventListener(shadowRoot, "focusout", data[FOCUS_OUT_HANDLER]);
200
+ }
201
+ }
202
+ data[SHADOW_TARGETS].clear();
203
+ delete kwin.__keyborgData;
204
+ }
205
+ if (origFocus) proto.focus = origFocus;
241
206
  }
207
+ /**
208
+ * @param win The window that stores keyborg focus events
209
+ * @returns The last element focused with element.focus()
210
+ */
242
211
  function getLastFocusedProgrammatically(win) {
243
- var _a;
244
- const keyborgNativeFocusEvent = win.__keyborgData;
245
- return keyborgNativeFocusEvent ? ((_a = keyborgNativeFocusEvent.lastFocusedProgrammatically) == null ? void 0 : _a.deref()) || null : void 0;
212
+ const data = win.__keyborgData;
213
+ return data ? data[LAST_FOCUSED_PROGRAMMATICALLY]?.deref() || null : void 0;
246
214
  }
247
215
 
248
- // src/Keyborg.ts
249
- var _dismissTimeout = 500;
250
- var _lastId = 0;
251
- var KeyborgCore = class {
252
- constructor(win, props) {
253
- this._isNavigatingWithKeyboard_DO_NOT_USE = false;
254
- this._onFocusIn = (e) => {
255
- if (this._isMouseOrTouchUsedTimer) {
256
- return;
257
- }
258
- if (this.isNavigatingWithKeyboard) {
259
- return;
260
- }
261
- const details = e.detail;
262
- if (!details.relatedTarget) {
263
- return;
264
- }
265
- if (details.isFocusedProgrammatically || details.isFocusedProgrammatically === void 0) {
266
- return;
267
- }
268
- this.isNavigatingWithKeyboard = true;
269
- };
270
- this._onMouseDown = (e) => {
271
- if (e.buttons === 0 || e.clientX === 0 && e.clientY === 0 && e.screenX === 0 && e.screenY === 0) {
272
- return;
273
- }
274
- this._onMouseOrTouch();
275
- };
276
- this._onMouseOrTouch = () => {
277
- const win = this._win;
278
- if (win) {
279
- if (this._isMouseOrTouchUsedTimer) {
280
- win.clearTimeout(this._isMouseOrTouchUsedTimer);
281
- }
282
- this._isMouseOrTouchUsedTimer = win.setTimeout(() => {
283
- delete this._isMouseOrTouchUsedTimer;
284
- }, 1e3);
285
- }
286
- this.isNavigatingWithKeyboard = false;
287
- };
288
- this._onKeyDown = (e) => {
289
- const isNavigatingWithKeyboard = this.isNavigatingWithKeyboard;
290
- if (isNavigatingWithKeyboard) {
291
- if (this._shouldDismissKeyboardNavigation(e)) {
292
- this._scheduleDismiss();
293
- }
294
- } else {
295
- if (this._shouldTriggerKeyboardNavigation(e)) {
296
- this.isNavigatingWithKeyboard = true;
297
- }
298
- }
299
- };
300
- this.id = "c" + ++_lastId;
301
- this._win = win;
302
- const doc = win.document;
303
- if (props) {
304
- const triggerKeys = props.triggerKeys;
305
- const dismissKeys = props.dismissKeys;
306
- if (triggerKeys == null ? void 0 : triggerKeys.length) {
307
- this._triggerKeys = new Set(triggerKeys);
308
- }
309
- if (dismissKeys == null ? void 0 : dismissKeys.length) {
310
- this._dismissKeys = new Set(dismissKeys);
311
- }
312
- }
313
- doc.addEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true);
314
- doc.addEventListener("mousedown", this._onMouseDown, true);
315
- win.addEventListener("keydown", this._onKeyDown, true);
316
- doc.addEventListener("touchstart", this._onMouseOrTouch, true);
317
- doc.addEventListener("touchend", this._onMouseOrTouch, true);
318
- doc.addEventListener("touchcancel", this._onMouseOrTouch, true);
319
- setupFocusEvent(win);
320
- }
321
- get isNavigatingWithKeyboard() {
322
- return this._isNavigatingWithKeyboard_DO_NOT_USE;
323
- }
324
- set isNavigatingWithKeyboard(val) {
325
- if (this._isNavigatingWithKeyboard_DO_NOT_USE !== val) {
326
- this._isNavigatingWithKeyboard_DO_NOT_USE = val;
327
- this.update();
328
- }
329
- }
330
- dispose() {
331
- const win = this._win;
332
- if (win) {
333
- if (this._isMouseOrTouchUsedTimer) {
334
- win.clearTimeout(this._isMouseOrTouchUsedTimer);
335
- this._isMouseOrTouchUsedTimer = void 0;
336
- }
337
- if (this._dismissTimer) {
338
- win.clearTimeout(this._dismissTimer);
339
- this._dismissTimer = void 0;
340
- }
341
- disposeFocusEvent(win);
342
- const doc = win.document;
343
- doc.removeEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true);
344
- doc.removeEventListener("mousedown", this._onMouseDown, true);
345
- win.removeEventListener("keydown", this._onKeyDown, true);
346
- doc.removeEventListener("touchstart", this._onMouseOrTouch, true);
347
- doc.removeEventListener("touchend", this._onMouseOrTouch, true);
348
- doc.removeEventListener("touchcancel", this._onMouseOrTouch, true);
349
- delete this._win;
350
- }
351
- }
352
- isDisposed() {
353
- return !!this._win;
354
- }
355
- /**
356
- * Updates all keyborg instances with the keyboard navigation state
357
- */
358
- update() {
359
- var _a, _b;
360
- const keyborgs = (_b = (_a = this._win) == null ? void 0 : _a.__keyborg) == null ? void 0 : _b.refs;
361
- if (keyborgs) {
362
- for (const id of Object.keys(keyborgs)) {
363
- Keyborg.update(keyborgs[id], this.isNavigatingWithKeyboard);
364
- }
365
- }
366
- }
367
- /**
368
- * @returns whether the keyboard event should trigger keyboard navigation mode
369
- */
370
- _shouldTriggerKeyboardNavigation(e) {
371
- var _a;
372
- if (e.key === "Tab") {
373
- return true;
374
- }
375
- const activeElement = (_a = this._win) == null ? void 0 : _a.document.activeElement;
376
- const isTriggerKey = !this._triggerKeys || this._triggerKeys.has(e.keyCode);
377
- const isEditable = activeElement && (activeElement.tagName === "INPUT" || activeElement.tagName === "TEXTAREA" || activeElement.isContentEditable);
378
- return isTriggerKey && !isEditable;
379
- }
380
- /**
381
- * @returns whether the keyboard event should dismiss keyboard navigation mode
382
- */
383
- _shouldDismissKeyboardNavigation(e) {
384
- var _a;
385
- return (_a = this._dismissKeys) == null ? void 0 : _a.has(e.keyCode);
386
- }
387
- _scheduleDismiss() {
388
- const win = this._win;
389
- if (win) {
390
- if (this._dismissTimer) {
391
- win.clearTimeout(this._dismissTimer);
392
- this._dismissTimer = void 0;
393
- }
394
- const was = win.document.activeElement;
395
- this._dismissTimer = win.setTimeout(() => {
396
- this._dismissTimer = void 0;
397
- const cur = win.document.activeElement;
398
- if (was && cur && was === cur) {
399
- this.isNavigatingWithKeyboard = false;
400
- }
401
- }, _dismissTimeout);
402
- }
403
- }
404
- };
405
- var Keyborg = class _Keyborg {
406
- constructor(win, props) {
407
- this._cb = [];
408
- this._id = "k" + ++_lastId;
409
- this._win = win;
410
- const current = win.__keyborg;
411
- if (current) {
412
- this._core = current.core;
413
- current.refs[this._id] = this;
414
- } else {
415
- this._core = new KeyborgCore(win, props);
416
- win.__keyborg = {
417
- core: this._core,
418
- refs: { [this._id]: this }
419
- };
420
- }
421
- }
422
- static create(win, props) {
423
- return new _Keyborg(win, props);
424
- }
425
- static dispose(instance) {
426
- instance.dispose();
427
- }
428
- /**
429
- * Updates all subscribed callbacks with the keyboard navigation state
430
- */
431
- static update(instance, isNavigatingWithKeyboard) {
432
- instance._cb.forEach((callback) => callback(isNavigatingWithKeyboard));
433
- }
434
- dispose() {
435
- var _a;
436
- const current = (_a = this._win) == null ? void 0 : _a.__keyborg;
437
- if (current == null ? void 0 : current.refs[this._id]) {
438
- delete current.refs[this._id];
439
- if (Object.keys(current.refs).length === 0) {
440
- current.core.dispose();
441
- delete this._win.__keyborg;
442
- }
443
- } else if (process.env.NODE_ENV !== "production") {
444
- console.error(
445
- `Keyborg instance ${this._id} is being disposed incorrectly.`
446
- );
447
- }
448
- this._cb = [];
449
- delete this._core;
450
- delete this._win;
451
- }
452
- /**
453
- * @returns Whether the user is navigating with keyboard
454
- */
455
- isNavigatingWithKeyboard() {
456
- var _a;
457
- return !!((_a = this._core) == null ? void 0 : _a.isNavigatingWithKeyboard);
458
- }
459
- /**
460
- * @param callback - Called when the keyboard navigation state changes
461
- */
462
- subscribe(callback) {
463
- this._cb.push(callback);
464
- }
465
- /**
466
- * @param callback - Registered with subscribe
467
- */
468
- unsubscribe(callback) {
469
- const index = this._cb.indexOf(callback);
470
- if (index >= 0) {
471
- this._cb.splice(index, 1);
472
- }
473
- }
474
- /**
475
- * Manually set the keyboard navigtion state
476
- */
477
- setVal(isNavigatingWithKeyboard) {
478
- if (this._core) {
479
- this._core.isNavigatingWithKeyboard = isNavigatingWithKeyboard;
480
- }
481
- }
482
- };
216
+ //#endregion
217
+ //#region src/Keyborg.mts
218
+ /*!
219
+ * Copyright (c) Microsoft Corporation. All rights reserved.
220
+ * Licensed under the MIT License.
221
+ */
222
+ const _dismissTimeout = 500;
223
+ let _lastId = 0;
224
+ function createKeyborgCore(targetWindow, props) {
225
+ let currentTargetWindow = targetWindow;
226
+ let isNavigating = false;
227
+ let isMouseOrTouchUsedTimer;
228
+ let dismissTimer;
229
+ let triggerKeys;
230
+ let dismissKeys;
231
+ if (props) {
232
+ if (props.triggerKeys?.length) triggerKeys = new Set(props.triggerKeys);
233
+ if (props.dismissKeys?.length) dismissKeys = new Set(props.dismissKeys);
234
+ }
235
+ const broadcast = () => {
236
+ const refs = currentTargetWindow?.__keyborg?.refs;
237
+ if (refs) for (const id of Object.keys(refs)) refs[id]._notify(isNavigating);
238
+ };
239
+ const setNavigating = (val) => {
240
+ if (isNavigating !== val) {
241
+ isNavigating = val;
242
+ broadcast();
243
+ }
244
+ };
245
+ const shouldTrigger = (e) => {
246
+ if (e.key === "Tab") return true;
247
+ const active = currentTargetWindow?.document.activeElement;
248
+ const isTriggerKey = !triggerKeys || triggerKeys.has(e.keyCode);
249
+ const isEditable = active && (active.tagName === "INPUT" || active.tagName === "TEXTAREA" || active.isContentEditable);
250
+ return isTriggerKey && !isEditable;
251
+ };
252
+ const shouldDismiss = (e) => {
253
+ return !!dismissKeys?.has(e.keyCode);
254
+ };
255
+ const scheduleDismiss = () => {
256
+ const targetWindow = currentTargetWindow;
257
+ if (!targetWindow) return;
258
+ if (dismissTimer) {
259
+ targetWindow.clearTimeout(dismissTimer);
260
+ dismissTimer = void 0;
261
+ }
262
+ const previousActiveElement = targetWindow.document.activeElement;
263
+ dismissTimer = targetWindow.setTimeout(() => {
264
+ dismissTimer = void 0;
265
+ const currentActiveElement = targetWindow.document.activeElement;
266
+ if (previousActiveElement && currentActiveElement && previousActiveElement === currentActiveElement) setNavigating(false);
267
+ }, _dismissTimeout);
268
+ };
269
+ const onFocusIn = (e) => {
270
+ if (isMouseOrTouchUsedTimer) return;
271
+ if (isNavigating) return;
272
+ const details = e.detail;
273
+ if (!details.relatedTarget) return;
274
+ if (details.isFocusedProgrammatically || details.isFocusedProgrammatically === void 0) return;
275
+ setNavigating(true);
276
+ };
277
+ const onMouseOrTouch = () => {
278
+ if (currentTargetWindow) {
279
+ if (isMouseOrTouchUsedTimer) currentTargetWindow.clearTimeout(isMouseOrTouchUsedTimer);
280
+ isMouseOrTouchUsedTimer = currentTargetWindow.setTimeout(() => {
281
+ isMouseOrTouchUsedTimer = void 0;
282
+ }, 1e3);
283
+ }
284
+ setNavigating(false);
285
+ };
286
+ const onMouseDown = (e) => {
287
+ if (e.buttons === 0 || e.clientX === 0 && e.clientY === 0 && e.screenX === 0 && e.screenY === 0) return;
288
+ onMouseOrTouch();
289
+ };
290
+ const onKeyDown = (e) => {
291
+ if (isNavigating) {
292
+ if (shouldDismiss(e)) scheduleDismiss();
293
+ } else if (shouldTrigger(e)) setNavigating(true);
294
+ };
295
+ const targetDocument = targetWindow.document;
296
+ addEventListener(targetDocument, KEYBORG_FOCUSIN, onFocusIn);
297
+ addEventListener(targetDocument, "mousedown", onMouseDown);
298
+ addEventListener(targetWindow, "keydown", onKeyDown);
299
+ addEventListener(targetDocument, "touchstart", onMouseOrTouch);
300
+ addEventListener(targetDocument, "touchend", onMouseOrTouch);
301
+ addEventListener(targetDocument, "touchcancel", onMouseOrTouch);
302
+ setupFocusEvent(targetWindow);
303
+ const dispose = () => {
304
+ if (!currentTargetWindow) return;
305
+ if (isMouseOrTouchUsedTimer) {
306
+ currentTargetWindow.clearTimeout(isMouseOrTouchUsedTimer);
307
+ isMouseOrTouchUsedTimer = void 0;
308
+ }
309
+ if (dismissTimer) {
310
+ currentTargetWindow.clearTimeout(dismissTimer);
311
+ dismissTimer = void 0;
312
+ }
313
+ disposeFocusEvent(currentTargetWindow);
314
+ const targetDocument = currentTargetWindow.document;
315
+ removeEventListener(targetDocument, KEYBORG_FOCUSIN, onFocusIn);
316
+ removeEventListener(targetDocument, "mousedown", onMouseDown);
317
+ removeEventListener(currentTargetWindow, "keydown", onKeyDown);
318
+ removeEventListener(targetDocument, "touchstart", onMouseOrTouch);
319
+ removeEventListener(targetDocument, "touchend", onMouseOrTouch);
320
+ removeEventListener(targetDocument, "touchcancel", onMouseOrTouch);
321
+ currentTargetWindow = void 0;
322
+ };
323
+ return {
324
+ dispose,
325
+ getNavigating: () => isNavigating,
326
+ setNavigating
327
+ };
328
+ }
483
329
  function createKeyborg(win, props) {
484
- return Keyborg.create(win, props);
330
+ const kwin = win;
331
+ const id = "k" + ++_lastId;
332
+ let localWin = kwin;
333
+ let core;
334
+ let callbacks = [];
335
+ const existing = kwin.__keyborg;
336
+ if (existing) core = existing.core;
337
+ else core = createKeyborgCore(kwin, props);
338
+ const instance = {
339
+ isNavigatingWithKeyboard() {
340
+ return !!core?.getNavigating();
341
+ },
342
+ subscribe(callback) {
343
+ callbacks.push(callback);
344
+ },
345
+ unsubscribe(callback) {
346
+ const index = callbacks.indexOf(callback);
347
+ if (index >= 0) callbacks.splice(index, 1);
348
+ },
349
+ setVal(val) {
350
+ core?.setNavigating(val);
351
+ },
352
+ _notify(val) {
353
+ callbacks.forEach((cb) => cb(val));
354
+ },
355
+ _dispose() {
356
+ const wkb = localWin?.__keyborg;
357
+ if (wkb?.refs[id]) {
358
+ delete wkb.refs[id];
359
+ if (Object.keys(wkb.refs).length === 0) {
360
+ wkb.core.dispose();
361
+ delete localWin.__keyborg;
362
+ }
363
+ } else if (process.env.NODE_ENV !== "production") console.error(`Keyborg instance ${id} is being disposed incorrectly.`);
364
+ callbacks = [];
365
+ core = void 0;
366
+ localWin = void 0;
367
+ }
368
+ };
369
+ if (existing) existing.refs[id] = instance;
370
+ else kwin.__keyborg = {
371
+ core,
372
+ refs: { [id]: instance }
373
+ };
374
+ return instance;
485
375
  }
486
376
  function disposeKeyborg(instance) {
487
- Keyborg.dispose(instance);
377
+ instance._dispose();
488
378
  }
489
379
 
490
- // src/index.ts
491
- var version = "2.13.0-canary.0";
492
- // Annotate the CommonJS export names for ESM import in node:
493
- 0 && (module.exports = {
494
- KEYBORG_FOCUSIN,
495
- KEYBORG_FOCUSOUT,
496
- createKeyborg,
497
- disposeKeyborg,
498
- getLastFocusedProgrammatically,
499
- nativeFocus,
500
- version
501
- });
502
- /*!
503
- * Copyright (c) Microsoft Corporation. All rights reserved.
504
- * Licensed under the MIT License.
505
- */
380
+ //#endregion
381
+ //#region src/index.mts
382
+ const version = "2.14.0";
383
+
384
+ //#endregion
385
+ export { KEYBORG_FOCUSIN, KEYBORG_FOCUSOUT, createKeyborg, disposeKeyborg, getLastFocusedProgrammatically, nativeFocus, version };
506
386
  //# sourceMappingURL=index.js.map