keyborg 1.1.0-alpha.0 → 1.1.0-alpha.4
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/FocusEvent.d.ts +26 -0
- package/dist/Keyborg.d.ts +81 -0
- package/dist/WeakRefInstance.d.ts +25 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +501 -0
- package/dist/index.js.map +1 -0
- package/dist/keyborg.esm.js +491 -0
- package/dist/keyborg.esm.js.map +1 -0
- package/package.json +9 -9
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const KEYBORG_FOCUSIN = "keyborg:focusin";
|
|
2
|
+
export interface KeyborgFocusInEventDetails {
|
|
3
|
+
relatedTarget?: HTMLElement;
|
|
4
|
+
isFocusedProgrammatically?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface KeyborgFocusInEvent extends Event {
|
|
7
|
+
details: KeyborgFocusInEventDetails;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Guarantees that the native `focus` will be used
|
|
11
|
+
*/
|
|
12
|
+
export declare function nativeFocus(element: HTMLElement): void;
|
|
13
|
+
/**
|
|
14
|
+
* Overrides the native `focus` and setups the keyborg focus event
|
|
15
|
+
*/
|
|
16
|
+
export declare function setupFocusEvent(win: Window): void;
|
|
17
|
+
/**
|
|
18
|
+
* Removes keyborg event listeners and custom focus override
|
|
19
|
+
* @param win The window that stores keyborg focus events
|
|
20
|
+
*/
|
|
21
|
+
export declare function disposeFocusEvent(win: Window): void;
|
|
22
|
+
/**
|
|
23
|
+
* @param win The window that stores keyborg focus events
|
|
24
|
+
* @returns The last element focused with element.focus()
|
|
25
|
+
*/
|
|
26
|
+
export declare function getLastFocusedProgrammatically(win: Window): HTMLElement | null | undefined;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { Disposable } from "./WeakRefInstance";
|
|
6
|
+
interface WindowWithKeyborg extends Window {
|
|
7
|
+
__keyborg?: {
|
|
8
|
+
core: KeyborgCore;
|
|
9
|
+
refs: {
|
|
10
|
+
[id: string]: Keyborg;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export declare type KeyborgCallback = (isNavigatingWithKeyboard: boolean) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Source of truth for all the keyborg core instances and the current keyboard navigation state
|
|
17
|
+
*/
|
|
18
|
+
export declare class KeyborgState {
|
|
19
|
+
private __keyborgCoreRefs;
|
|
20
|
+
private _isNavigatingWithKeyboard;
|
|
21
|
+
add(keyborg: KeyborgCore): void;
|
|
22
|
+
remove(id: string): void;
|
|
23
|
+
setVal(isNavigatingWithKeyboard: boolean): void;
|
|
24
|
+
getVal(): boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Manages a collection of Keyborg instances in a window/document and updates keyborg state
|
|
28
|
+
*/
|
|
29
|
+
declare class KeyborgCore implements Disposable {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
private _win?;
|
|
32
|
+
private _isMouseUsed;
|
|
33
|
+
private _dismissTimer;
|
|
34
|
+
constructor(win: WindowWithKeyborg);
|
|
35
|
+
dispose(): void;
|
|
36
|
+
isDisposed(): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Updates all keyborg instances with the keyboard navigation state
|
|
39
|
+
*/
|
|
40
|
+
update(isNavigatingWithKeyboard: boolean): void;
|
|
41
|
+
private _onFocusIn;
|
|
42
|
+
private _onMouseDown;
|
|
43
|
+
private _onKeyDown;
|
|
44
|
+
private _scheduleDismiss;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Used to determine the keyboard navigation state
|
|
48
|
+
*/
|
|
49
|
+
export declare class Keyborg {
|
|
50
|
+
private _id;
|
|
51
|
+
private _win?;
|
|
52
|
+
private _core?;
|
|
53
|
+
private _cb;
|
|
54
|
+
static create(win: WindowWithKeyborg): Keyborg;
|
|
55
|
+
static dispose(instance: Keyborg): void;
|
|
56
|
+
/**
|
|
57
|
+
* Updates all subscribed callbacks with the keyboard navigation state
|
|
58
|
+
*/
|
|
59
|
+
static update(instance: Keyborg, isNavigatingWithKeyboard: boolean): void;
|
|
60
|
+
private constructor();
|
|
61
|
+
private dispose;
|
|
62
|
+
/**
|
|
63
|
+
* @returns Whether the user is navigating with keyboard
|
|
64
|
+
*/
|
|
65
|
+
isNavigatingWithKeyboard(): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* @param callback - Called when the keyboard navigation state changes
|
|
68
|
+
*/
|
|
69
|
+
subscribe(callback: KeyborgCallback): void;
|
|
70
|
+
/**
|
|
71
|
+
* @param callback - Registered with subscribe
|
|
72
|
+
*/
|
|
73
|
+
unsubscribe(callback: KeyborgCallback): void;
|
|
74
|
+
/**
|
|
75
|
+
* Manually set the keyboard navigtion state
|
|
76
|
+
*/
|
|
77
|
+
setVal(isNavigatingWithKeyboard: boolean): void;
|
|
78
|
+
}
|
|
79
|
+
export declare function createKeyborg(win: Window): Keyborg;
|
|
80
|
+
export declare function disposeKeyborg(instance: Keyborg): void;
|
|
81
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
export declare const _canUseWeakRef: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Allows disposable instances to be used
|
|
8
|
+
*/
|
|
9
|
+
export interface Disposable {
|
|
10
|
+
isDisposed?(): boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* WeakRef wrapper around a HTMLElement that also supports IE11
|
|
14
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef}
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export declare class WeakRefInstance<T extends Disposable | object> {
|
|
18
|
+
private _weakRef?;
|
|
19
|
+
private _instance?;
|
|
20
|
+
constructor(instance: T);
|
|
21
|
+
/**
|
|
22
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef/deref}
|
|
23
|
+
*/
|
|
24
|
+
deref(): T | undefined;
|
|
25
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
export { Keyborg, createKeyborg, disposeKeyborg } from "./Keyborg";
|
|
6
|
+
export { getLastFocusedProgrammatically, nativeFocus, KEYBORG_FOCUSIN, KeyborgFocusInEvent, KeyborgFocusInEventDetails, } from "./FocusEvent";
|
|
7
|
+
export declare const version: string;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
/*!
|
|
6
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
7
|
+
* Licensed under the MIT License.
|
|
8
|
+
*/
|
|
9
|
+
// IE11 compat, checks if WeakRef is supported
|
|
10
|
+
const _canUseWeakRef = typeof WeakRef !== "undefined";
|
|
11
|
+
/**
|
|
12
|
+
* WeakRef wrapper around a HTMLElement that also supports IE11
|
|
13
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef}
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
class WeakRefInstance {
|
|
18
|
+
constructor(instance) {
|
|
19
|
+
if (_canUseWeakRef && typeof instance === "object") {
|
|
20
|
+
this._weakRef = new WeakRef(instance);
|
|
21
|
+
} else {
|
|
22
|
+
this._instance = instance;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef/deref}
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
deref() {
|
|
31
|
+
var _a, _b, _c;
|
|
32
|
+
|
|
33
|
+
let instance;
|
|
34
|
+
|
|
35
|
+
if (this._weakRef) {
|
|
36
|
+
instance = (_a = this._weakRef) === null || _a === void 0 ? void 0 : _a.deref();
|
|
37
|
+
|
|
38
|
+
if (!instance) {
|
|
39
|
+
delete this._weakRef;
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
instance = this._instance;
|
|
43
|
+
|
|
44
|
+
if ((_c = (_b = instance) === null || _b === void 0 ? void 0 : _b.isDisposed) === null || _c === void 0 ? void 0 : _c.call(_b)) {
|
|
45
|
+
delete this._instance;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return instance;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/*!
|
|
55
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
56
|
+
* Licensed under the MIT License.
|
|
57
|
+
*/
|
|
58
|
+
const KEYBORG_FOCUSIN = "keyborg:focusin";
|
|
59
|
+
|
|
60
|
+
function canOverrideNativeFocus(win) {
|
|
61
|
+
const HTMLElement = win.HTMLElement;
|
|
62
|
+
const origFocus = HTMLElement.prototype.focus;
|
|
63
|
+
let isCustomFocusCalled = false;
|
|
64
|
+
|
|
65
|
+
HTMLElement.prototype.focus = function focus() {
|
|
66
|
+
isCustomFocusCalled = true;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const btn = win.document.createElement("button");
|
|
70
|
+
btn.focus();
|
|
71
|
+
HTMLElement.prototype.focus = origFocus;
|
|
72
|
+
return isCustomFocusCalled;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let _canOverrideNativeFocus = false;
|
|
76
|
+
/**
|
|
77
|
+
* Guarantees that the native `focus` will be used
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
function nativeFocus(element) {
|
|
81
|
+
const focus = element.focus;
|
|
82
|
+
|
|
83
|
+
if (focus.__keyborgNativeFocus) {
|
|
84
|
+
focus.__keyborgNativeFocus.call(element);
|
|
85
|
+
} else {
|
|
86
|
+
element.focus();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Overrides the native `focus` and setups the keyborg focus event
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
function setupFocusEvent(win) {
|
|
94
|
+
const kwin = win;
|
|
95
|
+
|
|
96
|
+
if (!_canOverrideNativeFocus) {
|
|
97
|
+
_canOverrideNativeFocus = canOverrideNativeFocus(kwin);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const origFocus = kwin.HTMLElement.prototype.focus;
|
|
101
|
+
|
|
102
|
+
if (origFocus.__keyborgNativeFocus) {
|
|
103
|
+
// Already set up.
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
kwin.HTMLElement.prototype.focus = focus;
|
|
108
|
+
const data = kwin.__keyborgData = {
|
|
109
|
+
focusInHandler: e => {
|
|
110
|
+
var _a;
|
|
111
|
+
|
|
112
|
+
const target = e.target;
|
|
113
|
+
|
|
114
|
+
if (!target) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const event = document.createEvent("HTMLEvents");
|
|
119
|
+
event.initEvent(KEYBORG_FOCUSIN, true, true);
|
|
120
|
+
const details = {
|
|
121
|
+
relatedTarget: e.relatedTarget || undefined
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (_canOverrideNativeFocus || data.lastFocusedProgrammatically) {
|
|
125
|
+
details.isFocusedProgrammatically = target === ((_a = data.lastFocusedProgrammatically) === null || _a === void 0 ? void 0 : _a.deref());
|
|
126
|
+
data.lastFocusedProgrammatically = undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
event.details = details;
|
|
130
|
+
target.dispatchEvent(event);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
kwin.document.addEventListener("focusin", kwin.__keyborgData.focusInHandler, true);
|
|
134
|
+
|
|
135
|
+
function focus() {
|
|
136
|
+
const keyborgNativeFocusEvent = kwin.__keyborgData;
|
|
137
|
+
|
|
138
|
+
if (keyborgNativeFocusEvent) {
|
|
139
|
+
keyborgNativeFocusEvent.lastFocusedProgrammatically = new WeakRefInstance(this);
|
|
140
|
+
} // eslint-disable-next-line prefer-rest-params
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
return origFocus.apply(this, arguments);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
focus.__keyborgNativeFocus = origFocus;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Removes keyborg event listeners and custom focus override
|
|
150
|
+
* @param win The window that stores keyborg focus events
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
function disposeFocusEvent(win) {
|
|
154
|
+
const kwin = win;
|
|
155
|
+
const proto = kwin.HTMLElement.prototype;
|
|
156
|
+
const origFocus = proto.focus.__keyborgNativeFocus;
|
|
157
|
+
const keyborgNativeFocusEvent = kwin.__keyborgData;
|
|
158
|
+
|
|
159
|
+
if (keyborgNativeFocusEvent) {
|
|
160
|
+
kwin.document.removeEventListener("focusin", keyborgNativeFocusEvent.focusInHandler, true);
|
|
161
|
+
delete kwin.__keyborgData;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (origFocus) {
|
|
165
|
+
proto.focus = origFocus;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* @param win The window that stores keyborg focus events
|
|
170
|
+
* @returns The last element focused with element.focus()
|
|
171
|
+
*/
|
|
172
|
+
|
|
173
|
+
function getLastFocusedProgrammatically(win) {
|
|
174
|
+
var _a;
|
|
175
|
+
|
|
176
|
+
const keyborgNativeFocusEvent = win.__keyborgData;
|
|
177
|
+
return keyborgNativeFocusEvent ? ((_a = keyborgNativeFocusEvent.lastFocusedProgrammatically) === null || _a === void 0 ? void 0 : _a.deref()) || null : undefined;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/*!
|
|
181
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
182
|
+
* Licensed under the MIT License.
|
|
183
|
+
*/
|
|
184
|
+
const KeyTab = 9;
|
|
185
|
+
const KeyEsc = 27;
|
|
186
|
+
const _dismissTimeout = 500; // When Esc is pressed and the focused is not moved
|
|
187
|
+
// during _dismissTimeout time, dismiss the keyboard
|
|
188
|
+
// navigation mode.
|
|
189
|
+
|
|
190
|
+
let _lastId = 0;
|
|
191
|
+
/**
|
|
192
|
+
* Source of truth for all the keyborg core instances and the current keyboard navigation state
|
|
193
|
+
*/
|
|
194
|
+
|
|
195
|
+
class KeyborgState {
|
|
196
|
+
constructor() {
|
|
197
|
+
this.__keyborgCoreRefs = {};
|
|
198
|
+
this._isNavigatingWithKeyboard = false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
add(keyborg) {
|
|
202
|
+
const id = keyborg.id;
|
|
203
|
+
|
|
204
|
+
if (!(id in this.__keyborgCoreRefs)) {
|
|
205
|
+
this.__keyborgCoreRefs[id] = new WeakRefInstance(keyborg);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
remove(id) {
|
|
210
|
+
delete this.__keyborgCoreRefs[id];
|
|
211
|
+
|
|
212
|
+
if (Object.keys(this.__keyborgCoreRefs).length === 0) {
|
|
213
|
+
this._isNavigatingWithKeyboard = false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
setVal(isNavigatingWithKeyboard) {
|
|
218
|
+
if (this._isNavigatingWithKeyboard === isNavigatingWithKeyboard) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
this._isNavigatingWithKeyboard = isNavigatingWithKeyboard;
|
|
223
|
+
|
|
224
|
+
for (const id of Object.keys(this.__keyborgCoreRefs)) {
|
|
225
|
+
const ref = this.__keyborgCoreRefs[id];
|
|
226
|
+
const keyborg = ref.deref();
|
|
227
|
+
|
|
228
|
+
if (keyborg) {
|
|
229
|
+
keyborg.update(isNavigatingWithKeyboard);
|
|
230
|
+
} else {
|
|
231
|
+
this.remove(id);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
getVal() {
|
|
237
|
+
return this._isNavigatingWithKeyboard;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const _state = /*#__PURE__*/new KeyborgState();
|
|
243
|
+
/**
|
|
244
|
+
* Manages a collection of Keyborg instances in a window/document and updates keyborg state
|
|
245
|
+
*/
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class KeyborgCore {
|
|
249
|
+
constructor(win) {
|
|
250
|
+
this._isMouseUsed = false;
|
|
251
|
+
|
|
252
|
+
this._onFocusIn = e => {
|
|
253
|
+
if (this._isMouseUsed) {
|
|
254
|
+
this._isMouseUsed = false;
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (_state.getVal()) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const details = e.details;
|
|
263
|
+
|
|
264
|
+
if (!details.relatedTarget) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (details.isFocusedProgrammatically || details.isFocusedProgrammatically === undefined) {
|
|
269
|
+
// The element is focused programmatically, or the programmatic focus detection
|
|
270
|
+
// is not working.
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
_state.setVal(true);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
this._onMouseDown = e => {
|
|
278
|
+
if (e.buttons === 0 || e.clientX === 0 && e.clientY === 0 && e.screenX === 0 && e.screenY === 0) {
|
|
279
|
+
// This is most likely an event triggered by the screen reader to perform
|
|
280
|
+
// an action on an element, do not dismiss the keyboard navigation mode.
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
this._isMouseUsed = true;
|
|
285
|
+
|
|
286
|
+
_state.setVal(false);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
this._onKeyDown = e => {
|
|
290
|
+
const isNavigatingWithKeyboard = _state.getVal();
|
|
291
|
+
|
|
292
|
+
if (!isNavigatingWithKeyboard && e.keyCode === KeyTab) {
|
|
293
|
+
_state.setVal(true);
|
|
294
|
+
} else if (isNavigatingWithKeyboard && e.keyCode === KeyEsc) {
|
|
295
|
+
this._scheduleDismiss();
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
this.id = "c" + ++_lastId;
|
|
300
|
+
this._win = win;
|
|
301
|
+
const doc = win.document;
|
|
302
|
+
doc.addEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!
|
|
303
|
+
|
|
304
|
+
doc.addEventListener("mousedown", this._onMouseDown, true); // Capture!
|
|
305
|
+
|
|
306
|
+
win.addEventListener("keydown", this._onKeyDown, true); // Capture!
|
|
307
|
+
|
|
308
|
+
setupFocusEvent(win);
|
|
309
|
+
|
|
310
|
+
_state.add(this);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
dispose() {
|
|
314
|
+
const win = this._win;
|
|
315
|
+
|
|
316
|
+
if (win) {
|
|
317
|
+
if (this._dismissTimer) {
|
|
318
|
+
win.clearTimeout(this._dismissTimer);
|
|
319
|
+
this._dismissTimer = undefined;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
disposeFocusEvent(win);
|
|
323
|
+
const doc = win.document;
|
|
324
|
+
doc.removeEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!
|
|
325
|
+
|
|
326
|
+
doc.removeEventListener("mousedown", this._onMouseDown, true); // Capture!
|
|
327
|
+
|
|
328
|
+
win.removeEventListener("keydown", this._onKeyDown, true); // Capture!
|
|
329
|
+
|
|
330
|
+
delete this._win;
|
|
331
|
+
|
|
332
|
+
_state.remove(this.id);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
isDisposed() {
|
|
337
|
+
return !!this._win;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Updates all keyborg instances with the keyboard navigation state
|
|
341
|
+
*/
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
update(isNavigatingWithKeyboard) {
|
|
345
|
+
var _a, _b;
|
|
346
|
+
|
|
347
|
+
const keyborgs = (_b = (_a = this._win) === null || _a === void 0 ? void 0 : _a.__keyborg) === null || _b === void 0 ? void 0 : _b.refs;
|
|
348
|
+
|
|
349
|
+
if (keyborgs) {
|
|
350
|
+
for (const id of Object.keys(keyborgs)) {
|
|
351
|
+
Keyborg.update(keyborgs[id], isNavigatingWithKeyboard);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
_scheduleDismiss() {
|
|
357
|
+
const win = this._win;
|
|
358
|
+
|
|
359
|
+
if (win) {
|
|
360
|
+
if (this._dismissTimer) {
|
|
361
|
+
win.clearTimeout(this._dismissTimer);
|
|
362
|
+
this._dismissTimer = undefined;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const was = win.document.activeElement;
|
|
366
|
+
this._dismissTimer = win.setTimeout(() => {
|
|
367
|
+
this._dismissTimer = undefined;
|
|
368
|
+
const cur = win.document.activeElement;
|
|
369
|
+
|
|
370
|
+
if (was && cur && was === cur) {
|
|
371
|
+
// Esc was pressed, currently focused element hasn't changed.
|
|
372
|
+
// Just dismiss the keyboard navigation mode.
|
|
373
|
+
_state.setVal(false);
|
|
374
|
+
}
|
|
375
|
+
}, _dismissTimeout);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Used to determine the keyboard navigation state
|
|
382
|
+
*/
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
class Keyborg {
|
|
386
|
+
constructor(win) {
|
|
387
|
+
this._cb = [];
|
|
388
|
+
this._id = "k" + ++_lastId;
|
|
389
|
+
this._win = win;
|
|
390
|
+
const current = win.__keyborg;
|
|
391
|
+
|
|
392
|
+
if (current) {
|
|
393
|
+
this._core = current.core;
|
|
394
|
+
current.refs[this._id] = this;
|
|
395
|
+
} else {
|
|
396
|
+
this._core = new KeyborgCore(win);
|
|
397
|
+
win.__keyborg = {
|
|
398
|
+
core: this._core,
|
|
399
|
+
refs: {
|
|
400
|
+
[this._id]: this
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
static create(win) {
|
|
407
|
+
return new Keyborg(win);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
static dispose(instance) {
|
|
411
|
+
instance.dispose();
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Updates all subscribed callbacks with the keyboard navigation state
|
|
415
|
+
*/
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
static update(instance, isNavigatingWithKeyboard) {
|
|
419
|
+
instance._cb.forEach(callback => callback(isNavigatingWithKeyboard));
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
dispose() {
|
|
423
|
+
var _a;
|
|
424
|
+
|
|
425
|
+
const current = (_a = this._win) === null || _a === void 0 ? void 0 : _a.__keyborg;
|
|
426
|
+
|
|
427
|
+
if (current === null || current === void 0 ? void 0 : current.refs[this._id]) {
|
|
428
|
+
delete current.refs[this._id];
|
|
429
|
+
|
|
430
|
+
if (Object.keys(current.refs).length === 0) {
|
|
431
|
+
current.core.dispose(); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
432
|
+
|
|
433
|
+
delete this._win.__keyborg;
|
|
434
|
+
}
|
|
435
|
+
} else if (process.env.NODE_ENV === 'development') {
|
|
436
|
+
console.error("Keyborg instance " + this._id + " is being disposed incorrectly.");
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
this._cb = [];
|
|
440
|
+
delete this._core;
|
|
441
|
+
delete this._win;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* @returns Whether the user is navigating with keyboard
|
|
445
|
+
*/
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
isNavigatingWithKeyboard() {
|
|
449
|
+
return _state.getVal();
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* @param callback - Called when the keyboard navigation state changes
|
|
453
|
+
*/
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
subscribe(callback) {
|
|
457
|
+
this._cb.push(callback);
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* @param callback - Registered with subscribe
|
|
461
|
+
*/
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
unsubscribe(callback) {
|
|
465
|
+
const index = this._cb.indexOf(callback);
|
|
466
|
+
|
|
467
|
+
if (index >= 0) {
|
|
468
|
+
this._cb.splice(index, 1);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Manually set the keyboard navigtion state
|
|
473
|
+
*/
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
setVal(isNavigatingWithKeyboard) {
|
|
477
|
+
_state.setVal(isNavigatingWithKeyboard);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
}
|
|
481
|
+
function createKeyborg(win) {
|
|
482
|
+
return Keyborg.create(win);
|
|
483
|
+
}
|
|
484
|
+
function disposeKeyborg(instance) {
|
|
485
|
+
Keyborg.dispose(instance);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/*!
|
|
489
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
490
|
+
* Licensed under the MIT License.
|
|
491
|
+
*/
|
|
492
|
+
const version = "1.1.0-alpha.4";
|
|
493
|
+
|
|
494
|
+
exports.KEYBORG_FOCUSIN = KEYBORG_FOCUSIN;
|
|
495
|
+
exports.Keyborg = Keyborg;
|
|
496
|
+
exports.createKeyborg = createKeyborg;
|
|
497
|
+
exports.disposeKeyborg = disposeKeyborg;
|
|
498
|
+
exports.getLastFocusedProgrammatically = getLastFocusedProgrammatically;
|
|
499
|
+
exports.nativeFocus = nativeFocus;
|
|
500
|
+
exports.version = version;
|
|
501
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
// IE11 compat, checks if WeakRef is supported
|
|
6
|
+
const _canUseWeakRef = typeof WeakRef !== "undefined";
|
|
7
|
+
/**
|
|
8
|
+
* WeakRef wrapper around a HTMLElement that also supports IE11
|
|
9
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef}
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
class WeakRefInstance {
|
|
14
|
+
constructor(instance) {
|
|
15
|
+
if (_canUseWeakRef && typeof instance === "object") {
|
|
16
|
+
this._weakRef = new WeakRef(instance);
|
|
17
|
+
} else {
|
|
18
|
+
this._instance = instance;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef/deref}
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
deref() {
|
|
27
|
+
var _a, _b, _c;
|
|
28
|
+
|
|
29
|
+
let instance;
|
|
30
|
+
|
|
31
|
+
if (this._weakRef) {
|
|
32
|
+
instance = (_a = this._weakRef) === null || _a === void 0 ? void 0 : _a.deref();
|
|
33
|
+
|
|
34
|
+
if (!instance) {
|
|
35
|
+
delete this._weakRef;
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
instance = this._instance;
|
|
39
|
+
|
|
40
|
+
if ((_c = (_b = instance) === null || _b === void 0 ? void 0 : _b.isDisposed) === null || _c === void 0 ? void 0 : _c.call(_b)) {
|
|
41
|
+
delete this._instance;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return instance;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/*!
|
|
51
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
52
|
+
* Licensed under the MIT License.
|
|
53
|
+
*/
|
|
54
|
+
const KEYBORG_FOCUSIN = "keyborg:focusin";
|
|
55
|
+
|
|
56
|
+
function canOverrideNativeFocus(win) {
|
|
57
|
+
const HTMLElement = win.HTMLElement;
|
|
58
|
+
const origFocus = HTMLElement.prototype.focus;
|
|
59
|
+
let isCustomFocusCalled = false;
|
|
60
|
+
|
|
61
|
+
HTMLElement.prototype.focus = function focus() {
|
|
62
|
+
isCustomFocusCalled = true;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const btn = win.document.createElement("button");
|
|
66
|
+
btn.focus();
|
|
67
|
+
HTMLElement.prototype.focus = origFocus;
|
|
68
|
+
return isCustomFocusCalled;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let _canOverrideNativeFocus = false;
|
|
72
|
+
/**
|
|
73
|
+
* Guarantees that the native `focus` will be used
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
function nativeFocus(element) {
|
|
77
|
+
const focus = element.focus;
|
|
78
|
+
|
|
79
|
+
if (focus.__keyborgNativeFocus) {
|
|
80
|
+
focus.__keyborgNativeFocus.call(element);
|
|
81
|
+
} else {
|
|
82
|
+
element.focus();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Overrides the native `focus` and setups the keyborg focus event
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
function setupFocusEvent(win) {
|
|
90
|
+
const kwin = win;
|
|
91
|
+
|
|
92
|
+
if (!_canOverrideNativeFocus) {
|
|
93
|
+
_canOverrideNativeFocus = canOverrideNativeFocus(kwin);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const origFocus = kwin.HTMLElement.prototype.focus;
|
|
97
|
+
|
|
98
|
+
if (origFocus.__keyborgNativeFocus) {
|
|
99
|
+
// Already set up.
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
kwin.HTMLElement.prototype.focus = focus;
|
|
104
|
+
const data = kwin.__keyborgData = {
|
|
105
|
+
focusInHandler: e => {
|
|
106
|
+
var _a;
|
|
107
|
+
|
|
108
|
+
const target = e.target;
|
|
109
|
+
|
|
110
|
+
if (!target) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const event = document.createEvent("HTMLEvents");
|
|
115
|
+
event.initEvent(KEYBORG_FOCUSIN, true, true);
|
|
116
|
+
const details = {
|
|
117
|
+
relatedTarget: e.relatedTarget || undefined
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
if (_canOverrideNativeFocus || data.lastFocusedProgrammatically) {
|
|
121
|
+
details.isFocusedProgrammatically = target === ((_a = data.lastFocusedProgrammatically) === null || _a === void 0 ? void 0 : _a.deref());
|
|
122
|
+
data.lastFocusedProgrammatically = undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
event.details = details;
|
|
126
|
+
target.dispatchEvent(event);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
kwin.document.addEventListener("focusin", kwin.__keyborgData.focusInHandler, true);
|
|
130
|
+
|
|
131
|
+
function focus() {
|
|
132
|
+
const keyborgNativeFocusEvent = kwin.__keyborgData;
|
|
133
|
+
|
|
134
|
+
if (keyborgNativeFocusEvent) {
|
|
135
|
+
keyborgNativeFocusEvent.lastFocusedProgrammatically = new WeakRefInstance(this);
|
|
136
|
+
} // eslint-disable-next-line prefer-rest-params
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
return origFocus.apply(this, arguments);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
focus.__keyborgNativeFocus = origFocus;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Removes keyborg event listeners and custom focus override
|
|
146
|
+
* @param win The window that stores keyborg focus events
|
|
147
|
+
*/
|
|
148
|
+
|
|
149
|
+
function disposeFocusEvent(win) {
|
|
150
|
+
const kwin = win;
|
|
151
|
+
const proto = kwin.HTMLElement.prototype;
|
|
152
|
+
const origFocus = proto.focus.__keyborgNativeFocus;
|
|
153
|
+
const keyborgNativeFocusEvent = kwin.__keyborgData;
|
|
154
|
+
|
|
155
|
+
if (keyborgNativeFocusEvent) {
|
|
156
|
+
kwin.document.removeEventListener("focusin", keyborgNativeFocusEvent.focusInHandler, true);
|
|
157
|
+
delete kwin.__keyborgData;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (origFocus) {
|
|
161
|
+
proto.focus = origFocus;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* @param win The window that stores keyborg focus events
|
|
166
|
+
* @returns The last element focused with element.focus()
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
function getLastFocusedProgrammatically(win) {
|
|
170
|
+
var _a;
|
|
171
|
+
|
|
172
|
+
const keyborgNativeFocusEvent = win.__keyborgData;
|
|
173
|
+
return keyborgNativeFocusEvent ? ((_a = keyborgNativeFocusEvent.lastFocusedProgrammatically) === null || _a === void 0 ? void 0 : _a.deref()) || null : undefined;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/*!
|
|
177
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
178
|
+
* Licensed under the MIT License.
|
|
179
|
+
*/
|
|
180
|
+
const KeyTab = 9;
|
|
181
|
+
const KeyEsc = 27;
|
|
182
|
+
const _dismissTimeout = 500; // When Esc is pressed and the focused is not moved
|
|
183
|
+
// during _dismissTimeout time, dismiss the keyboard
|
|
184
|
+
// navigation mode.
|
|
185
|
+
|
|
186
|
+
let _lastId = 0;
|
|
187
|
+
/**
|
|
188
|
+
* Source of truth for all the keyborg core instances and the current keyboard navigation state
|
|
189
|
+
*/
|
|
190
|
+
|
|
191
|
+
class KeyborgState {
|
|
192
|
+
constructor() {
|
|
193
|
+
this.__keyborgCoreRefs = {};
|
|
194
|
+
this._isNavigatingWithKeyboard = false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
add(keyborg) {
|
|
198
|
+
const id = keyborg.id;
|
|
199
|
+
|
|
200
|
+
if (!(id in this.__keyborgCoreRefs)) {
|
|
201
|
+
this.__keyborgCoreRefs[id] = new WeakRefInstance(keyborg);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
remove(id) {
|
|
206
|
+
delete this.__keyborgCoreRefs[id];
|
|
207
|
+
|
|
208
|
+
if (Object.keys(this.__keyborgCoreRefs).length === 0) {
|
|
209
|
+
this._isNavigatingWithKeyboard = false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
setVal(isNavigatingWithKeyboard) {
|
|
214
|
+
if (this._isNavigatingWithKeyboard === isNavigatingWithKeyboard) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
this._isNavigatingWithKeyboard = isNavigatingWithKeyboard;
|
|
219
|
+
|
|
220
|
+
for (const id of Object.keys(this.__keyborgCoreRefs)) {
|
|
221
|
+
const ref = this.__keyborgCoreRefs[id];
|
|
222
|
+
const keyborg = ref.deref();
|
|
223
|
+
|
|
224
|
+
if (keyborg) {
|
|
225
|
+
keyborg.update(isNavigatingWithKeyboard);
|
|
226
|
+
} else {
|
|
227
|
+
this.remove(id);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
getVal() {
|
|
233
|
+
return this._isNavigatingWithKeyboard;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const _state = /*#__PURE__*/new KeyborgState();
|
|
239
|
+
/**
|
|
240
|
+
* Manages a collection of Keyborg instances in a window/document and updates keyborg state
|
|
241
|
+
*/
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class KeyborgCore {
|
|
245
|
+
constructor(win) {
|
|
246
|
+
this._isMouseUsed = false;
|
|
247
|
+
|
|
248
|
+
this._onFocusIn = e => {
|
|
249
|
+
if (this._isMouseUsed) {
|
|
250
|
+
this._isMouseUsed = false;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (_state.getVal()) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const details = e.details;
|
|
259
|
+
|
|
260
|
+
if (!details.relatedTarget) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (details.isFocusedProgrammatically || details.isFocusedProgrammatically === undefined) {
|
|
265
|
+
// The element is focused programmatically, or the programmatic focus detection
|
|
266
|
+
// is not working.
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
_state.setVal(true);
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
this._onMouseDown = e => {
|
|
274
|
+
if (e.buttons === 0 || e.clientX === 0 && e.clientY === 0 && e.screenX === 0 && e.screenY === 0) {
|
|
275
|
+
// This is most likely an event triggered by the screen reader to perform
|
|
276
|
+
// an action on an element, do not dismiss the keyboard navigation mode.
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
this._isMouseUsed = true;
|
|
281
|
+
|
|
282
|
+
_state.setVal(false);
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
this._onKeyDown = e => {
|
|
286
|
+
const isNavigatingWithKeyboard = _state.getVal();
|
|
287
|
+
|
|
288
|
+
if (!isNavigatingWithKeyboard && e.keyCode === KeyTab) {
|
|
289
|
+
_state.setVal(true);
|
|
290
|
+
} else if (isNavigatingWithKeyboard && e.keyCode === KeyEsc) {
|
|
291
|
+
this._scheduleDismiss();
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
this.id = "c" + ++_lastId;
|
|
296
|
+
this._win = win;
|
|
297
|
+
const doc = win.document;
|
|
298
|
+
doc.addEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!
|
|
299
|
+
|
|
300
|
+
doc.addEventListener("mousedown", this._onMouseDown, true); // Capture!
|
|
301
|
+
|
|
302
|
+
win.addEventListener("keydown", this._onKeyDown, true); // Capture!
|
|
303
|
+
|
|
304
|
+
setupFocusEvent(win);
|
|
305
|
+
|
|
306
|
+
_state.add(this);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
dispose() {
|
|
310
|
+
const win = this._win;
|
|
311
|
+
|
|
312
|
+
if (win) {
|
|
313
|
+
if (this._dismissTimer) {
|
|
314
|
+
win.clearTimeout(this._dismissTimer);
|
|
315
|
+
this._dismissTimer = undefined;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
disposeFocusEvent(win);
|
|
319
|
+
const doc = win.document;
|
|
320
|
+
doc.removeEventListener(KEYBORG_FOCUSIN, this._onFocusIn, true); // Capture!
|
|
321
|
+
|
|
322
|
+
doc.removeEventListener("mousedown", this._onMouseDown, true); // Capture!
|
|
323
|
+
|
|
324
|
+
win.removeEventListener("keydown", this._onKeyDown, true); // Capture!
|
|
325
|
+
|
|
326
|
+
delete this._win;
|
|
327
|
+
|
|
328
|
+
_state.remove(this.id);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
isDisposed() {
|
|
333
|
+
return !!this._win;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Updates all keyborg instances with the keyboard navigation state
|
|
337
|
+
*/
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
update(isNavigatingWithKeyboard) {
|
|
341
|
+
var _a, _b;
|
|
342
|
+
|
|
343
|
+
const keyborgs = (_b = (_a = this._win) === null || _a === void 0 ? void 0 : _a.__keyborg) === null || _b === void 0 ? void 0 : _b.refs;
|
|
344
|
+
|
|
345
|
+
if (keyborgs) {
|
|
346
|
+
for (const id of Object.keys(keyborgs)) {
|
|
347
|
+
Keyborg.update(keyborgs[id], isNavigatingWithKeyboard);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
_scheduleDismiss() {
|
|
353
|
+
const win = this._win;
|
|
354
|
+
|
|
355
|
+
if (win) {
|
|
356
|
+
if (this._dismissTimer) {
|
|
357
|
+
win.clearTimeout(this._dismissTimer);
|
|
358
|
+
this._dismissTimer = undefined;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const was = win.document.activeElement;
|
|
362
|
+
this._dismissTimer = win.setTimeout(() => {
|
|
363
|
+
this._dismissTimer = undefined;
|
|
364
|
+
const cur = win.document.activeElement;
|
|
365
|
+
|
|
366
|
+
if (was && cur && was === cur) {
|
|
367
|
+
// Esc was pressed, currently focused element hasn't changed.
|
|
368
|
+
// Just dismiss the keyboard navigation mode.
|
|
369
|
+
_state.setVal(false);
|
|
370
|
+
}
|
|
371
|
+
}, _dismissTimeout);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Used to determine the keyboard navigation state
|
|
378
|
+
*/
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class Keyborg {
|
|
382
|
+
constructor(win) {
|
|
383
|
+
this._cb = [];
|
|
384
|
+
this._id = "k" + ++_lastId;
|
|
385
|
+
this._win = win;
|
|
386
|
+
const current = win.__keyborg;
|
|
387
|
+
|
|
388
|
+
if (current) {
|
|
389
|
+
this._core = current.core;
|
|
390
|
+
current.refs[this._id] = this;
|
|
391
|
+
} else {
|
|
392
|
+
this._core = new KeyborgCore(win);
|
|
393
|
+
win.__keyborg = {
|
|
394
|
+
core: this._core,
|
|
395
|
+
refs: {
|
|
396
|
+
[this._id]: this
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
static create(win) {
|
|
403
|
+
return new Keyborg(win);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
static dispose(instance) {
|
|
407
|
+
instance.dispose();
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Updates all subscribed callbacks with the keyboard navigation state
|
|
411
|
+
*/
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
static update(instance, isNavigatingWithKeyboard) {
|
|
415
|
+
instance._cb.forEach(callback => callback(isNavigatingWithKeyboard));
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
dispose() {
|
|
419
|
+
var _a;
|
|
420
|
+
|
|
421
|
+
const current = (_a = this._win) === null || _a === void 0 ? void 0 : _a.__keyborg;
|
|
422
|
+
|
|
423
|
+
if (current === null || current === void 0 ? void 0 : current.refs[this._id]) {
|
|
424
|
+
delete current.refs[this._id];
|
|
425
|
+
|
|
426
|
+
if (Object.keys(current.refs).length === 0) {
|
|
427
|
+
current.core.dispose(); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
428
|
+
|
|
429
|
+
delete this._win.__keyborg;
|
|
430
|
+
}
|
|
431
|
+
} else if (process.env.NODE_ENV === 'development') {
|
|
432
|
+
console.error("Keyborg instance " + this._id + " is being disposed incorrectly.");
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
this._cb = [];
|
|
436
|
+
delete this._core;
|
|
437
|
+
delete this._win;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* @returns Whether the user is navigating with keyboard
|
|
441
|
+
*/
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
isNavigatingWithKeyboard() {
|
|
445
|
+
return _state.getVal();
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* @param callback - Called when the keyboard navigation state changes
|
|
449
|
+
*/
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
subscribe(callback) {
|
|
453
|
+
this._cb.push(callback);
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* @param callback - Registered with subscribe
|
|
457
|
+
*/
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
unsubscribe(callback) {
|
|
461
|
+
const index = this._cb.indexOf(callback);
|
|
462
|
+
|
|
463
|
+
if (index >= 0) {
|
|
464
|
+
this._cb.splice(index, 1);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Manually set the keyboard navigtion state
|
|
469
|
+
*/
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
setVal(isNavigatingWithKeyboard) {
|
|
473
|
+
_state.setVal(isNavigatingWithKeyboard);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
}
|
|
477
|
+
function createKeyborg(win) {
|
|
478
|
+
return Keyborg.create(win);
|
|
479
|
+
}
|
|
480
|
+
function disposeKeyborg(instance) {
|
|
481
|
+
Keyborg.dispose(instance);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/*!
|
|
485
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
486
|
+
* Licensed under the MIT License.
|
|
487
|
+
*/
|
|
488
|
+
const version = "1.1.0-alpha.4";
|
|
489
|
+
|
|
490
|
+
export { KEYBORG_FOCUSIN, Keyborg, createKeyborg, disposeKeyborg, getLastFocusedProgrammatically, nativeFocus, version };
|
|
491
|
+
//# sourceMappingURL=keyborg.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyborg.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keyborg",
|
|
3
|
-
"version": "1.1.0-alpha.
|
|
3
|
+
"version": "1.1.0-alpha.4",
|
|
4
4
|
"description": "Keyboard Navigation Detection for Web",
|
|
5
5
|
"author": "Marat Abdullin <marata@microsoft.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"sideEffects": false,
|
|
8
|
-
"main": "./
|
|
9
|
-
"module": "./
|
|
10
|
-
"typings": "./
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/keyborg.esm.js",
|
|
10
|
+
"typings": "./dist/index.d.ts",
|
|
11
11
|
"files": [
|
|
12
12
|
"dist"
|
|
13
13
|
],
|
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
"url": "git+https://github.com/microsoft/keyborg"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
+
"clean": "rimraf dist",
|
|
19
20
|
"build": "npm run clean && rollup -c",
|
|
20
|
-
"bundle-size": "bundle-size measure",
|
|
21
|
-
"lint": "eslint src/",
|
|
22
|
-
"lint:fix": "eslint src/ --fix",
|
|
21
|
+
"bundle-size": "npm run build && bundle-size measure",
|
|
23
22
|
"format": "prettier --check .",
|
|
24
23
|
"format:fix": "prettier --write .",
|
|
24
|
+
"lint": "eslint src/",
|
|
25
|
+
"lint:fix": "eslint src/ --fix",
|
|
25
26
|
"prepublishOnly": "npm run lint && npm run format && npm run build",
|
|
26
|
-
"release": "release-it"
|
|
27
|
-
"clean": "rimraf lib && rimraf lib-commonjs"
|
|
27
|
+
"release": "release-it --verbose"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@babel/core": "^7.16.0",
|