focus-trap 7.1.0 → 7.2.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/CHANGELOG.md +6 -0
- package/README.md +4 -0
- package/dist/focus-trap.esm.js +62 -17
- package/dist/focus-trap.esm.js.map +1 -1
- package/dist/focus-trap.esm.min.js +2 -2
- package/dist/focus-trap.esm.min.js.map +1 -1
- package/dist/focus-trap.js +62 -17
- package/dist/focus-trap.js.map +1 -1
- package/dist/focus-trap.min.js +2 -2
- package/dist/focus-trap.min.js.map +1 -1
- package/dist/focus-trap.umd.js +62 -17
- package/dist/focus-trap.umd.js.map +1 -1
- package/dist/focus-trap.umd.min.js +2 -2
- package/dist/focus-trap.umd.min.js.map +1 -1
- package/index.d.ts +16 -0
- package/index.js +45 -17
- package/package.json +11 -11
package/index.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { tabbable, focusable, isFocusable, isTabbable } from 'tabbable';
|
|
2
2
|
|
|
3
|
-
const rooTrapStack = [];
|
|
4
|
-
|
|
5
3
|
const activeFocusTraps = {
|
|
6
4
|
activateTrap(trapStack, trap) {
|
|
7
5
|
if (trapStack.length > 0) {
|
|
@@ -49,6 +47,16 @@ const isTabEvent = function (e) {
|
|
|
49
47
|
return e.key === 'Tab' || e.keyCode === 9;
|
|
50
48
|
};
|
|
51
49
|
|
|
50
|
+
// checks for TAB by default
|
|
51
|
+
const isKeyForward = function (e) {
|
|
52
|
+
return isTabEvent(e) && !e.shiftKey;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// checks for SHIFT+TAB by default
|
|
56
|
+
const isKeyBackward = function (e) {
|
|
57
|
+
return isTabEvent(e) && e.shiftKey;
|
|
58
|
+
};
|
|
59
|
+
|
|
52
60
|
const delay = function (fn) {
|
|
53
61
|
return setTimeout(fn, 0);
|
|
54
62
|
};
|
|
@@ -94,17 +102,23 @@ const getActualTarget = function (event) {
|
|
|
94
102
|
: event.target;
|
|
95
103
|
};
|
|
96
104
|
|
|
105
|
+
// NOTE: this must be _outside_ `createFocusTrap()` to make sure all traps in this
|
|
106
|
+
// current instance use the same stack if `userOptions.trapStack` isn't specified
|
|
107
|
+
const internalTrapStack = [];
|
|
108
|
+
|
|
97
109
|
const createFocusTrap = function (elements, userOptions) {
|
|
98
110
|
// SSR: a live trap shouldn't be created in this type of environment so this
|
|
99
111
|
// should be safe code to execute if the `document` option isn't specified
|
|
100
112
|
const doc = userOptions?.document || document;
|
|
101
113
|
|
|
102
|
-
const trapStack = userOptions?.trapStack ||
|
|
114
|
+
const trapStack = userOptions?.trapStack || internalTrapStack;
|
|
103
115
|
|
|
104
116
|
const config = {
|
|
105
117
|
returnFocusOnDeactivate: true,
|
|
106
118
|
escapeDeactivates: true,
|
|
107
119
|
delayInitialFocus: true,
|
|
120
|
+
isKeyForward,
|
|
121
|
+
isKeyBackward,
|
|
108
122
|
...userOptions,
|
|
109
123
|
};
|
|
110
124
|
|
|
@@ -421,12 +435,12 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
421
435
|
}
|
|
422
436
|
};
|
|
423
437
|
|
|
424
|
-
// Hijack
|
|
438
|
+
// Hijack key nav events on the first and last focusable nodes of the trap,
|
|
425
439
|
// in order to prevent focus from escaping. If it escapes for even a
|
|
426
440
|
// moment it can end up scrolling the page and causing confusion so we
|
|
427
441
|
// kind of need to capture the action at the keydown phase.
|
|
428
|
-
const
|
|
429
|
-
const target = getActualTarget(
|
|
442
|
+
const checkKeyNav = function (event, isBackward = false) {
|
|
443
|
+
const target = getActualTarget(event);
|
|
430
444
|
updateTabbableNodes();
|
|
431
445
|
|
|
432
446
|
let destinationNode = null;
|
|
@@ -441,8 +455,8 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
441
455
|
|
|
442
456
|
if (containerIndex < 0) {
|
|
443
457
|
// target not found in any group: quite possible focus has escaped the trap,
|
|
444
|
-
// so bring it back
|
|
445
|
-
if (
|
|
458
|
+
// so bring it back into...
|
|
459
|
+
if (isBackward) {
|
|
446
460
|
// ...the last node in the last group
|
|
447
461
|
destinationNode =
|
|
448
462
|
state.tabbableGroups[state.tabbableGroups.length - 1]
|
|
@@ -451,7 +465,7 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
451
465
|
// ...the first node in the first group
|
|
452
466
|
destinationNode = state.tabbableGroups[0].firstTabbableNode;
|
|
453
467
|
}
|
|
454
|
-
} else if (
|
|
468
|
+
} else if (isBackward) {
|
|
455
469
|
// REVERSE
|
|
456
470
|
|
|
457
471
|
// is the target the first tabbable node in a group?
|
|
@@ -487,6 +501,10 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
487
501
|
|
|
488
502
|
const destinationGroup = state.tabbableGroups[destinationGroupIndex];
|
|
489
503
|
destinationNode = destinationGroup.lastTabbableNode;
|
|
504
|
+
} else if (!isTabEvent(event)) {
|
|
505
|
+
// user must have customized the nav keys so we have to move focus manually _within_
|
|
506
|
+
// the active group: do this based on the order determined by tabbable()
|
|
507
|
+
destinationNode = containerGroup.nextTabbableNode(target, false);
|
|
490
508
|
}
|
|
491
509
|
} else {
|
|
492
510
|
// FORWARD
|
|
@@ -524,33 +542,43 @@ const createFocusTrap = function (elements, userOptions) {
|
|
|
524
542
|
|
|
525
543
|
const destinationGroup = state.tabbableGroups[destinationGroupIndex];
|
|
526
544
|
destinationNode = destinationGroup.firstTabbableNode;
|
|
545
|
+
} else if (!isTabEvent(event)) {
|
|
546
|
+
// user must have customized the nav keys so we have to move focus manually _within_
|
|
547
|
+
// the active group: do this based on the order determined by tabbable()
|
|
548
|
+
destinationNode = containerGroup.nextTabbableNode(target);
|
|
527
549
|
}
|
|
528
550
|
}
|
|
529
551
|
} else {
|
|
552
|
+
// no groups available
|
|
530
553
|
// NOTE: the fallbackFocus option does not support returning false to opt-out
|
|
531
554
|
destinationNode = getNodeForOption('fallbackFocus');
|
|
532
555
|
}
|
|
533
556
|
|
|
534
557
|
if (destinationNode) {
|
|
535
|
-
|
|
558
|
+
if (isTabEvent(event)) {
|
|
559
|
+
// since tab natively moves focus, we wouldn't have a destination node unless we
|
|
560
|
+
// were on the edge of a container and had to move to the next/previous edge, in
|
|
561
|
+
// which case we want to prevent default to keep the browser from moving focus
|
|
562
|
+
// to where it normally would
|
|
563
|
+
event.preventDefault();
|
|
564
|
+
}
|
|
536
565
|
tryFocus(destinationNode);
|
|
537
566
|
}
|
|
538
567
|
// else, let the browser take care of [shift+]tab and move the focus
|
|
539
568
|
};
|
|
540
569
|
|
|
541
|
-
const checkKey = function (
|
|
570
|
+
const checkKey = function (event) {
|
|
542
571
|
if (
|
|
543
|
-
isEscapeEvent(
|
|
544
|
-
valueOrHandler(config.escapeDeactivates,
|
|
572
|
+
isEscapeEvent(event) &&
|
|
573
|
+
valueOrHandler(config.escapeDeactivates, event) !== false
|
|
545
574
|
) {
|
|
546
|
-
|
|
575
|
+
event.preventDefault();
|
|
547
576
|
trap.deactivate();
|
|
548
577
|
return;
|
|
549
578
|
}
|
|
550
579
|
|
|
551
|
-
if (
|
|
552
|
-
|
|
553
|
-
return;
|
|
580
|
+
if (config.isKeyForward(event) || config.isKeyBackward(event)) {
|
|
581
|
+
checkKeyNav(event, config.isKeyBackward(event));
|
|
554
582
|
}
|
|
555
583
|
};
|
|
556
584
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "focus-trap",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"description": "Trap focus within a DOM node.",
|
|
5
5
|
"main": "dist/focus-trap.js",
|
|
6
6
|
"module": "dist/focus-trap.esm.js",
|
|
@@ -67,33 +67,33 @@
|
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@babel/cli": "^7.19.3",
|
|
70
|
-
"@babel/core": "^7.20.
|
|
70
|
+
"@babel/core": "^7.20.5",
|
|
71
71
|
"@babel/eslint-parser": "^7.19.1",
|
|
72
72
|
"@babel/preset-env": "^7.20.2",
|
|
73
73
|
"@changesets/cli": "^2.25.2",
|
|
74
|
-
"@rollup/plugin-babel": "^6.0.
|
|
75
|
-
"@rollup/plugin-commonjs": "^23.0.
|
|
74
|
+
"@rollup/plugin-babel": "^6.0.3",
|
|
75
|
+
"@rollup/plugin-commonjs": "^23.0.3",
|
|
76
76
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
|
77
|
-
"@testing-library/cypress": "^8.0.
|
|
77
|
+
"@testing-library/cypress": "^8.0.7",
|
|
78
78
|
"@types/jquery": "^3.5.14",
|
|
79
79
|
"all-contributors-cli": "^6.24.0",
|
|
80
80
|
"babel-loader": "^9.1.0",
|
|
81
81
|
"cross-env": "^7.0.3",
|
|
82
|
-
"cypress": "^
|
|
82
|
+
"cypress": "^11.2.0",
|
|
83
83
|
"cypress-plugin-tab": "^1.0.5",
|
|
84
|
-
"eslint": "^8.
|
|
84
|
+
"eslint": "^8.28.0",
|
|
85
85
|
"eslint-config-prettier": "^8.5.0",
|
|
86
86
|
"eslint-plugin-cypress": "^2.12.1",
|
|
87
|
-
"eslint-plugin-jest": "^27.1.
|
|
87
|
+
"eslint-plugin-jest": "^27.1.6",
|
|
88
88
|
"onchange": "^7.1.0",
|
|
89
|
-
"prettier": "^2.
|
|
89
|
+
"prettier": "^2.8.0",
|
|
90
90
|
"rollup": "^2.79.1",
|
|
91
91
|
"rollup-plugin-inject-process-env": "^1.3.1",
|
|
92
92
|
"rollup-plugin-livereload": "^2.0.5",
|
|
93
|
-
"rollup-plugin-serve": "^2.0.
|
|
93
|
+
"rollup-plugin-serve": "^2.0.2",
|
|
94
94
|
"rollup-plugin-sourcemaps": "^0.6.3",
|
|
95
95
|
"rollup-plugin-terser": "^7.0.1",
|
|
96
96
|
"start-server-and-test": "^1.14.0",
|
|
97
|
-
"typescript": "^4.
|
|
97
|
+
"typescript": "^4.9.3"
|
|
98
98
|
}
|
|
99
99
|
}
|