power-focusable 4.2.0 → 4.3.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/README.md +12 -5
- package/dist/index.cjs +33 -6
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +33 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,6 +39,7 @@ interface PowerFocusableOptions {
|
|
|
39
39
|
composed?: boolean; // default: false
|
|
40
40
|
filter?: (element: Element) => boolean;
|
|
41
41
|
include?: (element: Element) => boolean;
|
|
42
|
+
skipNegativeTabIndexCheck?: boolean; // default: false
|
|
42
43
|
skipVisibilityCheck?: boolean; // default: false
|
|
43
44
|
wrap?: boolean; // default: false
|
|
44
45
|
}
|
|
@@ -72,9 +73,15 @@ The function should return `true` to include the element, or `false` to ignore i
|
|
|
72
73
|
|
|
73
74
|
Used by `getFocusables`, `getNextFocusable`, `getPreviousFocusable`, and `hasFocusable`.
|
|
74
75
|
|
|
76
|
+
### `skipNegativeTabIndexCheck`
|
|
77
|
+
|
|
78
|
+
If `true`, skips the negative `tabindex` check when determining focusability.
|
|
79
|
+
|
|
80
|
+
Used by `getFocusables`, `getNextFocusable`, `getPreviousFocusable`, `hasFocusable`, and `isFocusable`.
|
|
81
|
+
|
|
75
82
|
### `skipVisibilityCheck`
|
|
76
83
|
|
|
77
|
-
If `true`, skips `checkVisibility()` when determining focusability.
|
|
84
|
+
If `true`, skips `checkVisibility()` when determining focusability.
|
|
78
85
|
|
|
79
86
|
Used by `getFocusables`, `getNextFocusable`, `getPreviousFocusable`, `hasFocusable`, and `isFocusable`.
|
|
80
87
|
|
|
@@ -114,7 +121,7 @@ getFocusables(container, { composed: true });
|
|
|
114
121
|
getFocusables(container, { filter: (element) => !element.matches('[data-skip-focus]') });
|
|
115
122
|
|
|
116
123
|
// Uses custom include function
|
|
117
|
-
getFocusables(container, { filter: (element) => element.matches('[data-
|
|
124
|
+
getFocusables(container, { filter: (element) => element.matches('[data-force-focusable]') });
|
|
118
125
|
```
|
|
119
126
|
|
|
120
127
|
### `getNextFocusable`
|
|
@@ -137,7 +144,7 @@ getNextFocusable(container, { composed: true });
|
|
|
137
144
|
getNextFocusable(container, { filter: (element) => !element.matches('[data-skip-focus]') });
|
|
138
145
|
|
|
139
146
|
// Uses custom include function
|
|
140
|
-
getNextFocusable(container, { filter: (element) => element.matches('[data-
|
|
147
|
+
getNextFocusable(container, { filter: (element) => element.matches('[data-force-focusable]') });
|
|
141
148
|
|
|
142
149
|
// Wraps around to the first element when reaching the end
|
|
143
150
|
getNextFocusable(container, { wrap: true });
|
|
@@ -163,7 +170,7 @@ getPreviousFocusable(container, { composed: true });
|
|
|
163
170
|
getPreviousFocusable(container, { filter: (element) => !element.matches('[data-skip-focus]') });
|
|
164
171
|
|
|
165
172
|
// Uses custom include function
|
|
166
|
-
getPreviousFocusable(container, { filter: (element) => element.matches('[data-
|
|
173
|
+
getPreviousFocusable(container, { filter: (element) => element.matches('[data-force-focusable]') });
|
|
167
174
|
|
|
168
175
|
// Wraps around to the last element when reaching the end
|
|
169
176
|
getPreviousFocusable(container, { wrap: true });
|
|
@@ -187,7 +194,7 @@ hasFocusable(container, { composed: true });
|
|
|
187
194
|
hasFocusable(container, { filter: (element) => !element.matches('[data-skip-focus]') });
|
|
188
195
|
|
|
189
196
|
// Uses custom include function
|
|
190
|
-
hasFocusable(container, { filter: (element) => element.matches('[data-
|
|
197
|
+
hasFocusable(container, { filter: (element) => element.matches('[data-force-focusable]') });
|
|
191
198
|
```
|
|
192
199
|
|
|
193
200
|
### `inertOutside`
|
package/dist/index.cjs
CHANGED
|
@@ -48,6 +48,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
48
48
|
composed = false,
|
|
49
49
|
filter,
|
|
50
50
|
include,
|
|
51
|
+
skipNegativeTabIndexCheck = false,
|
|
51
52
|
skipVisibilityCheck = false
|
|
52
53
|
} = options;
|
|
53
54
|
if (typeof composed !== "boolean") {
|
|
@@ -66,11 +67,22 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
66
67
|
);
|
|
67
68
|
include = void 0;
|
|
68
69
|
}
|
|
70
|
+
if (typeof skipNegativeTabIndexCheck !== "boolean") {
|
|
71
|
+
console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
|
|
72
|
+
skipNegativeTabIndexCheck = false;
|
|
73
|
+
}
|
|
74
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
75
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
76
|
+
skipVisibilityCheck = false;
|
|
77
|
+
}
|
|
69
78
|
const elements = [];
|
|
70
79
|
if (composed || include) {
|
|
71
80
|
let traverse2 = function(node) {
|
|
72
81
|
if (node instanceof Element) {
|
|
73
|
-
if (isFocusable(node, {
|
|
82
|
+
if (isFocusable(node, {
|
|
83
|
+
skipNegativeTabIndexCheck,
|
|
84
|
+
skipVisibilityCheck
|
|
85
|
+
}) || include?.(node)) {
|
|
74
86
|
elements[elements.length] = node;
|
|
75
87
|
}
|
|
76
88
|
}
|
|
@@ -91,7 +103,10 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
91
103
|
if (!(candidate instanceof Element)) {
|
|
92
104
|
continue;
|
|
93
105
|
}
|
|
94
|
-
if (isFocusable(candidate, {
|
|
106
|
+
if (isFocusable(candidate, {
|
|
107
|
+
skipNegativeTabIndexCheck,
|
|
108
|
+
skipVisibilityCheck
|
|
109
|
+
})) {
|
|
95
110
|
elements[elements.length] = candidate;
|
|
96
111
|
}
|
|
97
112
|
}
|
|
@@ -142,7 +157,11 @@ function isFocusable(element, options = {}) {
|
|
|
142
157
|
console.warn("Invalid element");
|
|
143
158
|
return false;
|
|
144
159
|
}
|
|
145
|
-
let { skipVisibilityCheck = false } = options;
|
|
160
|
+
let { skipNegativeTabIndexCheck = false, skipVisibilityCheck = false } = options;
|
|
161
|
+
if (typeof skipNegativeTabIndexCheck !== "boolean") {
|
|
162
|
+
console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
|
|
163
|
+
skipNegativeTabIndexCheck = false;
|
|
164
|
+
}
|
|
146
165
|
if (typeof skipVisibilityCheck !== "boolean") {
|
|
147
166
|
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
148
167
|
skipVisibilityCheck = false;
|
|
@@ -150,10 +169,12 @@ function isFocusable(element, options = {}) {
|
|
|
150
169
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
151
170
|
return false;
|
|
152
171
|
}
|
|
153
|
-
if (getTabIndex(element) < 0) {
|
|
172
|
+
if (!skipNegativeTabIndexCheck && getTabIndex(element) < 0) {
|
|
154
173
|
return false;
|
|
155
174
|
}
|
|
156
|
-
if (!element.matches(
|
|
175
|
+
if (!element.matches(
|
|
176
|
+
skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR.replace(/(,\s*)?\[tabindex="-1"\]/g, "") : FOCUSABLE_SELECTOR
|
|
177
|
+
)) {
|
|
157
178
|
return false;
|
|
158
179
|
}
|
|
159
180
|
if (isDisabledDeep(element)) {
|
|
@@ -178,6 +199,7 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
178
199
|
composed = false,
|
|
179
200
|
filter,
|
|
180
201
|
include,
|
|
202
|
+
skipNegativeTabIndexCheck = false,
|
|
181
203
|
skipVisibilityCheck = false,
|
|
182
204
|
wrap = false
|
|
183
205
|
} = options;
|
|
@@ -211,6 +233,10 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
211
233
|
);
|
|
212
234
|
include = void 0;
|
|
213
235
|
}
|
|
236
|
+
if (typeof skipNegativeTabIndexCheck !== "boolean") {
|
|
237
|
+
console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
|
|
238
|
+
skipNegativeTabIndexCheck = false;
|
|
239
|
+
}
|
|
214
240
|
if (typeof skipVisibilityCheck !== "boolean") {
|
|
215
241
|
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
216
242
|
skipVisibilityCheck = false;
|
|
@@ -223,6 +249,7 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
223
249
|
composed,
|
|
224
250
|
filter,
|
|
225
251
|
include,
|
|
252
|
+
skipNegativeTabIndexCheck,
|
|
226
253
|
skipVisibilityCheck
|
|
227
254
|
});
|
|
228
255
|
const { length } = focusables;
|
|
@@ -455,7 +482,7 @@ function isUngroupedRadio(element) {
|
|
|
455
482
|
* High-precision focus management utility with full composed tree support.
|
|
456
483
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
457
484
|
*
|
|
458
|
-
* @version 4.
|
|
485
|
+
* @version 4.3.0
|
|
459
486
|
* @author Yusuke Kamiyamane
|
|
460
487
|
* @license MIT
|
|
461
488
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
package/dist/index.d.cts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* High-precision focus management utility with full composed tree support.
|
|
4
4
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
5
5
|
*
|
|
6
|
-
* @version 4.
|
|
6
|
+
* @version 4.3.0
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -14,6 +14,7 @@ interface PowerFocusableOptions {
|
|
|
14
14
|
readonly composed?: boolean;
|
|
15
15
|
readonly filter?: ((element: Element) => boolean) | undefined;
|
|
16
16
|
readonly include?: ((element: Element) => boolean) | undefined;
|
|
17
|
+
readonly skipNegativeTabIndexCheck?: boolean;
|
|
17
18
|
readonly skipVisibilityCheck?: boolean;
|
|
18
19
|
readonly wrap?: boolean;
|
|
19
20
|
}
|
|
@@ -24,6 +25,7 @@ declare function getPreviousFocusable(container?: Element, options?: PowerFocusa
|
|
|
24
25
|
declare function hasFocusable(container?: Element, options?: Omit<PowerFocusableOptions, 'anchor' | 'wrap'>): boolean;
|
|
25
26
|
declare function inertOutside(element: Element): () => void;
|
|
26
27
|
declare function isFocusable(element: Element, options?: {
|
|
28
|
+
skipNegativeTabIndexCheck?: boolean;
|
|
27
29
|
skipVisibilityCheck?: boolean;
|
|
28
30
|
}): boolean;
|
|
29
31
|
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* High-precision focus management utility with full composed tree support.
|
|
4
4
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
5
5
|
*
|
|
6
|
-
* @version 4.
|
|
6
|
+
* @version 4.3.0
|
|
7
7
|
* @author Yusuke Kamiyamane
|
|
8
8
|
* @license MIT
|
|
9
9
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -14,6 +14,7 @@ interface PowerFocusableOptions {
|
|
|
14
14
|
readonly composed?: boolean;
|
|
15
15
|
readonly filter?: ((element: Element) => boolean) | undefined;
|
|
16
16
|
readonly include?: ((element: Element) => boolean) | undefined;
|
|
17
|
+
readonly skipNegativeTabIndexCheck?: boolean;
|
|
17
18
|
readonly skipVisibilityCheck?: boolean;
|
|
18
19
|
readonly wrap?: boolean;
|
|
19
20
|
}
|
|
@@ -24,6 +25,7 @@ declare function getPreviousFocusable(container?: Element, options?: PowerFocusa
|
|
|
24
25
|
declare function hasFocusable(container?: Element, options?: Omit<PowerFocusableOptions, 'anchor' | 'wrap'>): boolean;
|
|
25
26
|
declare function inertOutside(element: Element): () => void;
|
|
26
27
|
declare function isFocusable(element: Element, options?: {
|
|
28
|
+
skipNegativeTabIndexCheck?: boolean;
|
|
27
29
|
skipVisibilityCheck?: boolean;
|
|
28
30
|
}): boolean;
|
|
29
31
|
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
46
46
|
composed = false,
|
|
47
47
|
filter,
|
|
48
48
|
include,
|
|
49
|
+
skipNegativeTabIndexCheck = false,
|
|
49
50
|
skipVisibilityCheck = false
|
|
50
51
|
} = options;
|
|
51
52
|
if (typeof composed !== "boolean") {
|
|
@@ -64,11 +65,22 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
64
65
|
);
|
|
65
66
|
include = void 0;
|
|
66
67
|
}
|
|
68
|
+
if (typeof skipNegativeTabIndexCheck !== "boolean") {
|
|
69
|
+
console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
|
|
70
|
+
skipNegativeTabIndexCheck = false;
|
|
71
|
+
}
|
|
72
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
73
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
74
|
+
skipVisibilityCheck = false;
|
|
75
|
+
}
|
|
67
76
|
const elements = [];
|
|
68
77
|
if (composed || include) {
|
|
69
78
|
let traverse2 = function(node) {
|
|
70
79
|
if (node instanceof Element) {
|
|
71
|
-
if (isFocusable(node, {
|
|
80
|
+
if (isFocusable(node, {
|
|
81
|
+
skipNegativeTabIndexCheck,
|
|
82
|
+
skipVisibilityCheck
|
|
83
|
+
}) || include?.(node)) {
|
|
72
84
|
elements[elements.length] = node;
|
|
73
85
|
}
|
|
74
86
|
}
|
|
@@ -89,7 +101,10 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
89
101
|
if (!(candidate instanceof Element)) {
|
|
90
102
|
continue;
|
|
91
103
|
}
|
|
92
|
-
if (isFocusable(candidate, {
|
|
104
|
+
if (isFocusable(candidate, {
|
|
105
|
+
skipNegativeTabIndexCheck,
|
|
106
|
+
skipVisibilityCheck
|
|
107
|
+
})) {
|
|
93
108
|
elements[elements.length] = candidate;
|
|
94
109
|
}
|
|
95
110
|
}
|
|
@@ -140,7 +155,11 @@ function isFocusable(element, options = {}) {
|
|
|
140
155
|
console.warn("Invalid element");
|
|
141
156
|
return false;
|
|
142
157
|
}
|
|
143
|
-
let { skipVisibilityCheck = false } = options;
|
|
158
|
+
let { skipNegativeTabIndexCheck = false, skipVisibilityCheck = false } = options;
|
|
159
|
+
if (typeof skipNegativeTabIndexCheck !== "boolean") {
|
|
160
|
+
console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
|
|
161
|
+
skipNegativeTabIndexCheck = false;
|
|
162
|
+
}
|
|
144
163
|
if (typeof skipVisibilityCheck !== "boolean") {
|
|
145
164
|
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
146
165
|
skipVisibilityCheck = false;
|
|
@@ -148,10 +167,12 @@ function isFocusable(element, options = {}) {
|
|
|
148
167
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
149
168
|
return false;
|
|
150
169
|
}
|
|
151
|
-
if (getTabIndex(element) < 0) {
|
|
170
|
+
if (!skipNegativeTabIndexCheck && getTabIndex(element) < 0) {
|
|
152
171
|
return false;
|
|
153
172
|
}
|
|
154
|
-
if (!element.matches(
|
|
173
|
+
if (!element.matches(
|
|
174
|
+
skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR.replace(/(,\s*)?\[tabindex="-1"\]/g, "") : FOCUSABLE_SELECTOR
|
|
175
|
+
)) {
|
|
155
176
|
return false;
|
|
156
177
|
}
|
|
157
178
|
if (isDisabledDeep(element)) {
|
|
@@ -176,6 +197,7 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
176
197
|
composed = false,
|
|
177
198
|
filter,
|
|
178
199
|
include,
|
|
200
|
+
skipNegativeTabIndexCheck = false,
|
|
179
201
|
skipVisibilityCheck = false,
|
|
180
202
|
wrap = false
|
|
181
203
|
} = options;
|
|
@@ -209,6 +231,10 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
209
231
|
);
|
|
210
232
|
include = void 0;
|
|
211
233
|
}
|
|
234
|
+
if (typeof skipNegativeTabIndexCheck !== "boolean") {
|
|
235
|
+
console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
|
|
236
|
+
skipNegativeTabIndexCheck = false;
|
|
237
|
+
}
|
|
212
238
|
if (typeof skipVisibilityCheck !== "boolean") {
|
|
213
239
|
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
214
240
|
skipVisibilityCheck = false;
|
|
@@ -221,6 +247,7 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
221
247
|
composed,
|
|
222
248
|
filter,
|
|
223
249
|
include,
|
|
250
|
+
skipNegativeTabIndexCheck,
|
|
224
251
|
skipVisibilityCheck
|
|
225
252
|
});
|
|
226
253
|
const { length } = focusables;
|
|
@@ -453,7 +480,7 @@ function isUngroupedRadio(element) {
|
|
|
453
480
|
* High-precision focus management utility with full composed tree support.
|
|
454
481
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
455
482
|
*
|
|
456
|
-
* @version 4.
|
|
483
|
+
* @version 4.3.0
|
|
457
484
|
* @author Yusuke Kamiyamane
|
|
458
485
|
* @license MIT
|
|
459
486
|
* @copyright Copyright (c) Yusuke Kamiyamane
|