@destyler/focus-trap 0.2.11 → 0.2.13
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.d.mts +6 -4
- package/dist/index.mjs +30 -23
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -194,10 +194,12 @@ type PauseOptions = Pick<FocusTrapOptions, 'onPause' | 'onPostPause'>;
|
|
|
194
194
|
type UnpauseOptions = Pick<FocusTrapOptions, 'onUnpause' | 'onPostUnpause'>;
|
|
195
195
|
//#endregion
|
|
196
196
|
//#region src/focus-trap.d.ts
|
|
197
|
-
declare
|
|
197
|
+
declare const activationIdKey: unique symbol;
|
|
198
|
+
export declare class FocusTrap {
|
|
198
199
|
private trapStack;
|
|
199
200
|
private config;
|
|
200
201
|
private doc;
|
|
202
|
+
private [activationIdKey];
|
|
201
203
|
private state;
|
|
202
204
|
get active(): boolean;
|
|
203
205
|
get paused(): boolean;
|
|
@@ -230,7 +232,7 @@ declare class FocusTrap {
|
|
|
230
232
|
//#endregion
|
|
231
233
|
//#region index.d.ts
|
|
232
234
|
type ElementOrGetter = HTMLElement | null | (() => HTMLElement | null);
|
|
233
|
-
interface TrapFocusOptions extends Omit<FocusTrapOptions, 'document'> {}
|
|
234
|
-
declare function trapFocus(el: ElementOrGetter, options?: TrapFocusOptions): () => void;
|
|
235
|
+
export interface TrapFocusOptions extends Omit<FocusTrapOptions, 'document'> {}
|
|
236
|
+
export declare function trapFocus(el: ElementOrGetter, options?: TrapFocusOptions): () => void;
|
|
235
237
|
//#endregion
|
|
236
|
-
export {
|
|
238
|
+
export type { FocusTrapOptions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { addDomEvent, getActiveElement, getDocument, getEventTarget, getFocusables, getTabIndex, getTabbables, isDocument, isFocusable, isTabbable, raf } from "@destyler/dom";
|
|
2
|
-
|
|
3
2
|
//#region src/focus-trap.ts
|
|
4
3
|
const isTabEvent = (event) => event.key === "Tab";
|
|
5
4
|
const valueOrHandler = (value, ...params) => typeof value === "function" ? value(...params) : value;
|
|
@@ -28,6 +27,7 @@ const activeFocusTraps = {
|
|
|
28
27
|
}
|
|
29
28
|
};
|
|
30
29
|
const sharedTrapStack = [];
|
|
30
|
+
const activationIdKey = Symbol("activationId");
|
|
31
31
|
var FocusTrap = class {
|
|
32
32
|
get active() {
|
|
33
33
|
return this.state.active;
|
|
@@ -36,6 +36,7 @@ var FocusTrap = class {
|
|
|
36
36
|
return this.state.paused;
|
|
37
37
|
}
|
|
38
38
|
constructor(elements, options) {
|
|
39
|
+
this[activationIdKey] = 0;
|
|
39
40
|
this.state = {
|
|
40
41
|
containers: [],
|
|
41
42
|
containerGroups: [],
|
|
@@ -124,7 +125,8 @@ var FocusTrap = class {
|
|
|
124
125
|
}
|
|
125
126
|
};
|
|
126
127
|
this.setupMutationObserver = () => {
|
|
127
|
-
|
|
128
|
+
const win = this.doc.defaultView || window;
|
|
129
|
+
this._mutationObserver = new win.MutationObserver((mutations) => {
|
|
128
130
|
if (mutations.some((mutation) => {
|
|
129
131
|
return Array.from(mutation.removedNodes).includes(this.state.mostRecentlyFocusedNode);
|
|
130
132
|
})) this.tryFocus(this.getInitialFocusNode());
|
|
@@ -142,12 +144,13 @@ var FocusTrap = class {
|
|
|
142
144
|
this.getInitialFocusNode = () => {
|
|
143
145
|
let node = this.getNodeForOption("initialFocus", { hasFallback: true });
|
|
144
146
|
if (node === false) return false;
|
|
145
|
-
if (node === void 0 || node && !isFocusable(node))
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
147
|
+
if (node === void 0 || node && !isFocusable(node)) {
|
|
148
|
+
if (this.findContainerIndex(this.doc.activeElement) >= 0) node = this.doc.activeElement;
|
|
149
|
+
else {
|
|
150
|
+
const firstTabbableGroup = this.state.tabbableGroups[0];
|
|
151
|
+
node = firstTabbableGroup && firstTabbableGroup.firstTabbableNode || this.getNodeForOption("fallbackFocus");
|
|
152
|
+
}
|
|
153
|
+
} else if (node === null) node = this.getNodeForOption("fallbackFocus");
|
|
151
154
|
if (!node) throw new Error("Your focus-trap needs to have at least one focusable element");
|
|
152
155
|
if (!node.isConnected) node = this.getNodeForOption("fallbackFocus");
|
|
153
156
|
return node;
|
|
@@ -165,23 +168,21 @@ var FocusTrap = class {
|
|
|
165
168
|
};
|
|
166
169
|
this.deactivate = (deactivateOptions) => {
|
|
167
170
|
if (!this.state.active) return this;
|
|
168
|
-
const options
|
|
171
|
+
const options = {
|
|
169
172
|
onDeactivate: this.config.onDeactivate,
|
|
170
173
|
onPostDeactivate: this.config.onPostDeactivate,
|
|
171
174
|
checkCanReturnFocus: this.config.checkCanReturnFocus,
|
|
172
175
|
...deactivateOptions
|
|
173
176
|
};
|
|
174
|
-
clearTimeout(this.state.delayInitialFocusTimer);
|
|
175
|
-
this.state.delayInitialFocusTimer = void 0;
|
|
176
177
|
this.removeListeners();
|
|
177
178
|
this.state.active = false;
|
|
178
179
|
this.state.paused = false;
|
|
179
180
|
this.updateObservedNodes();
|
|
180
181
|
activeFocusTraps.deactivateTrap(this.trapStack, this);
|
|
181
|
-
const onDeactivate = this.getOption(options
|
|
182
|
-
const onPostDeactivate = this.getOption(options
|
|
183
|
-
const checkCanReturnFocus = this.getOption(options
|
|
184
|
-
const returnFocus = this.getOption(options
|
|
182
|
+
const onDeactivate = this.getOption(options, "onDeactivate");
|
|
183
|
+
const onPostDeactivate = this.getOption(options, "onPostDeactivate");
|
|
184
|
+
const checkCanReturnFocus = this.getOption(options, "checkCanReturnFocus");
|
|
185
|
+
const returnFocus = this.getOption(options, "returnFocus", "returnFocusOnDeactivate");
|
|
185
186
|
onDeactivate?.();
|
|
186
187
|
const finishDeactivation = () => {
|
|
187
188
|
delay(() => {
|
|
@@ -264,9 +265,10 @@ var FocusTrap = class {
|
|
|
264
265
|
if (this.state.tabbableGroups.length > 0) {
|
|
265
266
|
const containerIndex = this.findContainerIndex(target, event);
|
|
266
267
|
const containerGroup = containerIndex >= 0 ? this.state.containerGroups[containerIndex] : void 0;
|
|
267
|
-
if (containerIndex < 0)
|
|
268
|
-
|
|
269
|
-
|
|
268
|
+
if (containerIndex < 0) {
|
|
269
|
+
if (isBackward) destinationNode = this.state.tabbableGroups[this.state.tabbableGroups.length - 1].lastTabbableNode;
|
|
270
|
+
else destinationNode = this.state.tabbableGroups[0].firstTabbableNode;
|
|
271
|
+
} else if (isBackward) {
|
|
270
272
|
let startOfGroupIndex = this.state.tabbableGroups.findIndex(({ firstTabbableNode }) => target === firstTabbableNode);
|
|
271
273
|
if (startOfGroupIndex < 0 && (containerGroup?.container === target || isFocusable(target) && !isTabbable(target) && !containerGroup?.nextTabbableNode(target, false))) startOfGroupIndex = containerIndex;
|
|
272
274
|
if (startOfGroupIndex >= 0) {
|
|
@@ -316,7 +318,7 @@ var FocusTrap = class {
|
|
|
316
318
|
const lastTabbableNode = tabbableNodes.length > 0 ? tabbableNodes[tabbableNodes.length - 1] : void 0;
|
|
317
319
|
const firstDomTabbableNode = focusableNodes.find((node) => isTabbable(node));
|
|
318
320
|
const lastDomTabbableNode = focusableNodes.slice().reverse().find((node) => isTabbable(node));
|
|
319
|
-
const posTabIndexesFound =
|
|
321
|
+
const posTabIndexesFound = tabbableNodes.some((node) => getTabIndex(node) > 0);
|
|
320
322
|
function nextTabbableNode(node, forward = true) {
|
|
321
323
|
const nodeIdx = tabbableNodes.indexOf(node);
|
|
322
324
|
if (nodeIdx < 0) {
|
|
@@ -339,7 +341,7 @@ var FocusTrap = class {
|
|
|
339
341
|
});
|
|
340
342
|
this.state.tabbableGroups = this.state.containerGroups.filter((group) => group.tabbableNodes.length > 0);
|
|
341
343
|
if (this.state.tabbableGroups.length <= 0 && !this.getNodeForOption("fallbackFocus")) throw new Error("Your focus-trap must have at least one container with at least one tabbable node in it at all times");
|
|
342
|
-
if (this.state.containerGroups.
|
|
344
|
+
if (this.state.containerGroups.some((g) => g.posTabIndexesFound) && this.state.containerGroups.length > 1) throw new Error("At least one node with a positive tabindex was found in one of your focus-trap's multiple containers. Positive tabindexes are only supported in single-container focus-traps.");
|
|
343
345
|
}
|
|
344
346
|
addListeners() {
|
|
345
347
|
if (!this.state.active) return;
|
|
@@ -364,6 +366,8 @@ var FocusTrap = class {
|
|
|
364
366
|
}
|
|
365
367
|
removeListeners() {
|
|
366
368
|
if (!this.state.active) return;
|
|
369
|
+
clearTimeout(this.state.delayInitialFocusTimer);
|
|
370
|
+
this.state.delayInitialFocusTimer = void 0;
|
|
367
371
|
this.listenerCleanups.forEach((cleanup) => cleanup());
|
|
368
372
|
this.listenerCleanups = [];
|
|
369
373
|
return this;
|
|
@@ -373,12 +377,17 @@ var FocusTrap = class {
|
|
|
373
377
|
const onActivate = this.getOption(activateOptions, "onActivate");
|
|
374
378
|
const onPostActivate = this.getOption(activateOptions, "onPostActivate");
|
|
375
379
|
const checkCanFocusTrap = this.getOption(activateOptions, "checkCanFocusTrap");
|
|
380
|
+
const activationId = ++this[activationIdKey];
|
|
381
|
+
const isCurrentActivation = () => this.state.active && this[activationIdKey] === activationId;
|
|
376
382
|
if (!checkCanFocusTrap) this.updateTabbableNodes();
|
|
383
|
+
if (this[activationIdKey] !== activationId) return this;
|
|
377
384
|
this.state.active = true;
|
|
378
385
|
this.state.paused = false;
|
|
379
386
|
this.state.nodeFocusedBeforeActivation = this.doc.activeElement || null;
|
|
380
387
|
onActivate?.();
|
|
388
|
+
if (!isCurrentActivation()) return this;
|
|
381
389
|
const finishActivation = () => {
|
|
390
|
+
if (!isCurrentActivation()) return;
|
|
382
391
|
if (checkCanFocusTrap) this.updateTabbableNodes();
|
|
383
392
|
this.addListeners();
|
|
384
393
|
this.updateObservedNodes();
|
|
@@ -392,7 +401,6 @@ var FocusTrap = class {
|
|
|
392
401
|
return this;
|
|
393
402
|
}
|
|
394
403
|
};
|
|
395
|
-
|
|
396
404
|
//#endregion
|
|
397
405
|
//#region index.ts
|
|
398
406
|
function trapFocus(el, options = {}) {
|
|
@@ -419,6 +427,5 @@ function trapFocus(el, options = {}) {
|
|
|
419
427
|
cleanup();
|
|
420
428
|
};
|
|
421
429
|
}
|
|
422
|
-
|
|
423
430
|
//#endregion
|
|
424
|
-
export { FocusTrap, trapFocus };
|
|
431
|
+
export { FocusTrap, trapFocus };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@destyler/focus-trap",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.13",
|
|
5
5
|
"description": "Focus trap util",
|
|
6
6
|
"author": "Elone Hoo <elonehoo@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dist"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@destyler/dom": "0.2.
|
|
25
|
+
"@destyler/dom": "0.2.13"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|