power-focusable 4.1.8 → 4.2.1
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 +7 -0
- package/dist/index.cjs +31 -7
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +31 -7
- 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
|
+
skipVisibilityCheck?: boolean; // default: false
|
|
42
43
|
wrap?: boolean; // default: false
|
|
43
44
|
}
|
|
44
45
|
```
|
|
@@ -71,6 +72,12 @@ The function should return `true` to include the element, or `false` to ignore i
|
|
|
71
72
|
|
|
72
73
|
Used by `getFocusables`, `getNextFocusable`, `getPreviousFocusable`, and `hasFocusable`.
|
|
73
74
|
|
|
75
|
+
### `skipVisibilityCheck`
|
|
76
|
+
|
|
77
|
+
If `true`, skips `checkVisibility()` when determining focusability. Useful for initializing.
|
|
78
|
+
|
|
79
|
+
Used by `getFocusables`, `getNextFocusable`, `getPreviousFocusable`, `hasFocusable`, and `isFocusable`.
|
|
80
|
+
|
|
74
81
|
### `wrap`
|
|
75
82
|
|
|
76
83
|
If `true`, wraps around to the first or last element when reaching the end.
|
package/dist/index.cjs
CHANGED
|
@@ -44,7 +44,12 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
44
44
|
console.warn("Invalid container element. Fallback: <body> element.");
|
|
45
45
|
container = document.body;
|
|
46
46
|
}
|
|
47
|
-
let {
|
|
47
|
+
let {
|
|
48
|
+
composed = false,
|
|
49
|
+
filter,
|
|
50
|
+
include,
|
|
51
|
+
skipVisibilityCheck = false
|
|
52
|
+
} = options;
|
|
48
53
|
if (typeof composed !== "boolean") {
|
|
49
54
|
console.warn("Invalid composed option. Fallback: false.");
|
|
50
55
|
composed = false;
|
|
@@ -61,11 +66,15 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
61
66
|
);
|
|
62
67
|
include = void 0;
|
|
63
68
|
}
|
|
69
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
70
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
71
|
+
skipVisibilityCheck = false;
|
|
72
|
+
}
|
|
64
73
|
const elements = [];
|
|
65
74
|
if (composed || include) {
|
|
66
75
|
let traverse2 = function(node) {
|
|
67
76
|
if (node instanceof Element) {
|
|
68
|
-
if (isFocusable(node) || include?.(node)) {
|
|
77
|
+
if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
|
|
69
78
|
elements[elements.length] = node;
|
|
70
79
|
}
|
|
71
80
|
}
|
|
@@ -86,7 +95,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
86
95
|
if (!(candidate instanceof Element)) {
|
|
87
96
|
continue;
|
|
88
97
|
}
|
|
89
|
-
if (isFocusable(candidate)) {
|
|
98
|
+
if (isFocusable(candidate, { skipVisibilityCheck })) {
|
|
90
99
|
elements[elements.length] = candidate;
|
|
91
100
|
}
|
|
92
101
|
}
|
|
@@ -132,11 +141,16 @@ function inertOutside(element) {
|
|
|
132
141
|
});
|
|
133
142
|
};
|
|
134
143
|
}
|
|
135
|
-
function isFocusable(element) {
|
|
144
|
+
function isFocusable(element, options = {}) {
|
|
136
145
|
if (!(element instanceof Element)) {
|
|
137
146
|
console.warn("Invalid element");
|
|
138
147
|
return false;
|
|
139
148
|
}
|
|
149
|
+
let { skipVisibilityCheck = false } = options;
|
|
150
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
151
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
152
|
+
skipVisibilityCheck = false;
|
|
153
|
+
}
|
|
140
154
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
141
155
|
return false;
|
|
142
156
|
}
|
|
@@ -149,7 +163,7 @@ function isFocusable(element) {
|
|
|
149
163
|
if (isDisabledDeep(element)) {
|
|
150
164
|
return false;
|
|
151
165
|
}
|
|
152
|
-
if (!element.checkVisibility({
|
|
166
|
+
if (!skipVisibilityCheck && !element.checkVisibility({
|
|
153
167
|
contentVisibilityAuto: true,
|
|
154
168
|
opacityProperty: true,
|
|
155
169
|
visibilityProperty: true
|
|
@@ -168,6 +182,7 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
168
182
|
composed = false,
|
|
169
183
|
filter,
|
|
170
184
|
include,
|
|
185
|
+
skipVisibilityCheck = false,
|
|
171
186
|
wrap = false
|
|
172
187
|
} = options;
|
|
173
188
|
if (!(anchor instanceof Element)) {
|
|
@@ -200,11 +215,20 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
200
215
|
);
|
|
201
216
|
include = void 0;
|
|
202
217
|
}
|
|
218
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
219
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
220
|
+
skipVisibilityCheck = false;
|
|
221
|
+
}
|
|
203
222
|
if (typeof wrap !== "boolean") {
|
|
204
223
|
console.warn("Invalid wrap option. Fallback: false.");
|
|
205
224
|
wrap = false;
|
|
206
225
|
}
|
|
207
|
-
const focusables = getFocusables(container, {
|
|
226
|
+
const focusables = getFocusables(container, {
|
|
227
|
+
composed,
|
|
228
|
+
filter,
|
|
229
|
+
include,
|
|
230
|
+
skipVisibilityCheck
|
|
231
|
+
});
|
|
208
232
|
const { length } = focusables;
|
|
209
233
|
if (!length) {
|
|
210
234
|
return null;
|
|
@@ -435,7 +459,7 @@ function isUngroupedRadio(element) {
|
|
|
435
459
|
* High-precision focus management utility with full composed tree support.
|
|
436
460
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
437
461
|
*
|
|
438
|
-
* @version 4.1
|
|
462
|
+
* @version 4.2.1
|
|
439
463
|
* @author Yusuke Kamiyamane
|
|
440
464
|
* @license MIT
|
|
441
465
|
* @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.1
|
|
6
|
+
* @version 4.2.1
|
|
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 skipVisibilityCheck?: boolean;
|
|
17
18
|
readonly wrap?: boolean;
|
|
18
19
|
}
|
|
19
20
|
declare function createFocusTrap(container?: Element): () => void;
|
|
@@ -22,6 +23,8 @@ declare function getNextFocusable(container?: Element, options?: PowerFocusableO
|
|
|
22
23
|
declare function getPreviousFocusable(container?: Element, options?: PowerFocusableOptions): Element | null;
|
|
23
24
|
declare function hasFocusable(container?: Element, options?: Omit<PowerFocusableOptions, 'anchor' | 'wrap'>): boolean;
|
|
24
25
|
declare function inertOutside(element: Element): () => void;
|
|
25
|
-
declare function isFocusable(element: Element
|
|
26
|
+
declare function isFocusable(element: Element, options?: {
|
|
27
|
+
skipVisibilityCheck?: boolean;
|
|
28
|
+
}): boolean;
|
|
26
29
|
|
|
27
30
|
export { type PowerFocusableOptions, createFocusTrap, getFocusables, getNextFocusable, getPreviousFocusable, hasFocusable, inertOutside, isFocusable };
|
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.1
|
|
6
|
+
* @version 4.2.1
|
|
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 skipVisibilityCheck?: boolean;
|
|
17
18
|
readonly wrap?: boolean;
|
|
18
19
|
}
|
|
19
20
|
declare function createFocusTrap(container?: Element): () => void;
|
|
@@ -22,6 +23,8 @@ declare function getNextFocusable(container?: Element, options?: PowerFocusableO
|
|
|
22
23
|
declare function getPreviousFocusable(container?: Element, options?: PowerFocusableOptions): Element | null;
|
|
23
24
|
declare function hasFocusable(container?: Element, options?: Omit<PowerFocusableOptions, 'anchor' | 'wrap'>): boolean;
|
|
24
25
|
declare function inertOutside(element: Element): () => void;
|
|
25
|
-
declare function isFocusable(element: Element
|
|
26
|
+
declare function isFocusable(element: Element, options?: {
|
|
27
|
+
skipVisibilityCheck?: boolean;
|
|
28
|
+
}): boolean;
|
|
26
29
|
|
|
27
30
|
export { type PowerFocusableOptions, createFocusTrap, getFocusables, getNextFocusable, getPreviousFocusable, hasFocusable, inertOutside, isFocusable };
|
package/dist/index.js
CHANGED
|
@@ -42,7 +42,12 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
42
42
|
console.warn("Invalid container element. Fallback: <body> element.");
|
|
43
43
|
container = document.body;
|
|
44
44
|
}
|
|
45
|
-
let {
|
|
45
|
+
let {
|
|
46
|
+
composed = false,
|
|
47
|
+
filter,
|
|
48
|
+
include,
|
|
49
|
+
skipVisibilityCheck = false
|
|
50
|
+
} = options;
|
|
46
51
|
if (typeof composed !== "boolean") {
|
|
47
52
|
console.warn("Invalid composed option. Fallback: false.");
|
|
48
53
|
composed = false;
|
|
@@ -59,11 +64,15 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
59
64
|
);
|
|
60
65
|
include = void 0;
|
|
61
66
|
}
|
|
67
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
68
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
69
|
+
skipVisibilityCheck = false;
|
|
70
|
+
}
|
|
62
71
|
const elements = [];
|
|
63
72
|
if (composed || include) {
|
|
64
73
|
let traverse2 = function(node) {
|
|
65
74
|
if (node instanceof Element) {
|
|
66
|
-
if (isFocusable(node) || include?.(node)) {
|
|
75
|
+
if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
|
|
67
76
|
elements[elements.length] = node;
|
|
68
77
|
}
|
|
69
78
|
}
|
|
@@ -84,7 +93,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
84
93
|
if (!(candidate instanceof Element)) {
|
|
85
94
|
continue;
|
|
86
95
|
}
|
|
87
|
-
if (isFocusable(candidate)) {
|
|
96
|
+
if (isFocusable(candidate, { skipVisibilityCheck })) {
|
|
88
97
|
elements[elements.length] = candidate;
|
|
89
98
|
}
|
|
90
99
|
}
|
|
@@ -130,11 +139,16 @@ function inertOutside(element) {
|
|
|
130
139
|
});
|
|
131
140
|
};
|
|
132
141
|
}
|
|
133
|
-
function isFocusable(element) {
|
|
142
|
+
function isFocusable(element, options = {}) {
|
|
134
143
|
if (!(element instanceof Element)) {
|
|
135
144
|
console.warn("Invalid element");
|
|
136
145
|
return false;
|
|
137
146
|
}
|
|
147
|
+
let { skipVisibilityCheck = false } = options;
|
|
148
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
149
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
150
|
+
skipVisibilityCheck = false;
|
|
151
|
+
}
|
|
138
152
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
139
153
|
return false;
|
|
140
154
|
}
|
|
@@ -147,7 +161,7 @@ function isFocusable(element) {
|
|
|
147
161
|
if (isDisabledDeep(element)) {
|
|
148
162
|
return false;
|
|
149
163
|
}
|
|
150
|
-
if (!element.checkVisibility({
|
|
164
|
+
if (!skipVisibilityCheck && !element.checkVisibility({
|
|
151
165
|
contentVisibilityAuto: true,
|
|
152
166
|
opacityProperty: true,
|
|
153
167
|
visibilityProperty: true
|
|
@@ -166,6 +180,7 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
166
180
|
composed = false,
|
|
167
181
|
filter,
|
|
168
182
|
include,
|
|
183
|
+
skipVisibilityCheck = false,
|
|
169
184
|
wrap = false
|
|
170
185
|
} = options;
|
|
171
186
|
if (!(anchor instanceof Element)) {
|
|
@@ -198,11 +213,20 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
198
213
|
);
|
|
199
214
|
include = void 0;
|
|
200
215
|
}
|
|
216
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
217
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
218
|
+
skipVisibilityCheck = false;
|
|
219
|
+
}
|
|
201
220
|
if (typeof wrap !== "boolean") {
|
|
202
221
|
console.warn("Invalid wrap option. Fallback: false.");
|
|
203
222
|
wrap = false;
|
|
204
223
|
}
|
|
205
|
-
const focusables = getFocusables(container, {
|
|
224
|
+
const focusables = getFocusables(container, {
|
|
225
|
+
composed,
|
|
226
|
+
filter,
|
|
227
|
+
include,
|
|
228
|
+
skipVisibilityCheck
|
|
229
|
+
});
|
|
206
230
|
const { length } = focusables;
|
|
207
231
|
if (!length) {
|
|
208
232
|
return null;
|
|
@@ -433,7 +457,7 @@ function isUngroupedRadio(element) {
|
|
|
433
457
|
* High-precision focus management utility with full composed tree support.
|
|
434
458
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
435
459
|
*
|
|
436
|
-
* @version 4.1
|
|
460
|
+
* @version 4.2.1
|
|
437
461
|
* @author Yusuke Kamiyamane
|
|
438
462
|
* @license MIT
|
|
439
463
|
* @copyright Copyright (c) Yusuke Kamiyamane
|