power-focusable 4.1.7 → 4.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/README.md +7 -0
- package/dist/index.cjs +42 -14
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +42 -14
- 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,24 +44,33 @@ 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
|
-
console.warn("Invalid composed. Fallback: false.");
|
|
54
|
+
console.warn("Invalid composed option. Fallback: false.");
|
|
50
55
|
composed = false;
|
|
51
56
|
}
|
|
52
57
|
if (typeof filter !== "undefined" && typeof filter !== "function") {
|
|
53
|
-
console.warn(
|
|
58
|
+
console.warn(
|
|
59
|
+
"Invalid filter function. Fallback: no filter function (undefined)."
|
|
60
|
+
);
|
|
54
61
|
filter = void 0;
|
|
55
62
|
}
|
|
56
63
|
if (typeof include !== "undefined" && typeof include !== "function") {
|
|
57
|
-
console.warn(
|
|
64
|
+
console.warn(
|
|
65
|
+
"Invalid include function. Fallback: no include function (undefined)."
|
|
66
|
+
);
|
|
58
67
|
include = void 0;
|
|
59
68
|
}
|
|
60
69
|
const elements = [];
|
|
61
70
|
if (composed || include) {
|
|
62
71
|
let traverse2 = function(node) {
|
|
63
72
|
if (node instanceof Element) {
|
|
64
|
-
if (isFocusable(node) || include?.(node)) {
|
|
73
|
+
if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
|
|
65
74
|
elements[elements.length] = node;
|
|
66
75
|
}
|
|
67
76
|
}
|
|
@@ -82,7 +91,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
82
91
|
if (!(candidate instanceof Element)) {
|
|
83
92
|
continue;
|
|
84
93
|
}
|
|
85
|
-
if (isFocusable(candidate)) {
|
|
94
|
+
if (isFocusable(candidate, { skipVisibilityCheck })) {
|
|
86
95
|
elements[elements.length] = candidate;
|
|
87
96
|
}
|
|
88
97
|
}
|
|
@@ -128,11 +137,16 @@ function inertOutside(element) {
|
|
|
128
137
|
});
|
|
129
138
|
};
|
|
130
139
|
}
|
|
131
|
-
function isFocusable(element) {
|
|
140
|
+
function isFocusable(element, options = {}) {
|
|
132
141
|
if (!(element instanceof Element)) {
|
|
133
142
|
console.warn("Invalid element");
|
|
134
143
|
return false;
|
|
135
144
|
}
|
|
145
|
+
let { skipVisibilityCheck = false } = options;
|
|
146
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
147
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
148
|
+
skipVisibilityCheck = false;
|
|
149
|
+
}
|
|
136
150
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
137
151
|
return false;
|
|
138
152
|
}
|
|
@@ -145,7 +159,7 @@ function isFocusable(element) {
|
|
|
145
159
|
if (isDisabledDeep(element)) {
|
|
146
160
|
return false;
|
|
147
161
|
}
|
|
148
|
-
if (!element.checkVisibility({
|
|
162
|
+
if (!skipVisibilityCheck && !element.checkVisibility({
|
|
149
163
|
contentVisibilityAuto: true,
|
|
150
164
|
opacityProperty: true,
|
|
151
165
|
visibilityProperty: true
|
|
@@ -164,6 +178,7 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
164
178
|
composed = false,
|
|
165
179
|
filter,
|
|
166
180
|
include,
|
|
181
|
+
skipVisibilityCheck = false,
|
|
167
182
|
wrap = false
|
|
168
183
|
} = options;
|
|
169
184
|
if (!(anchor instanceof Element)) {
|
|
@@ -181,22 +196,35 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
181
196
|
return null;
|
|
182
197
|
}
|
|
183
198
|
if (typeof composed !== "boolean") {
|
|
184
|
-
console.warn("Invalid composed. Fallback: false.");
|
|
199
|
+
console.warn("Invalid composed option. Fallback: false.");
|
|
185
200
|
composed = false;
|
|
186
201
|
}
|
|
187
202
|
if (typeof filter !== "undefined" && typeof filter !== "function") {
|
|
188
|
-
console.warn(
|
|
203
|
+
console.warn(
|
|
204
|
+
"Invalid filter function. Fallback: no filter function (undefined)."
|
|
205
|
+
);
|
|
189
206
|
filter = void 0;
|
|
190
207
|
}
|
|
191
208
|
if (typeof include !== "undefined" && typeof include !== "function") {
|
|
192
|
-
console.warn(
|
|
209
|
+
console.warn(
|
|
210
|
+
"Invalid include function. Fallback: no include function (undefined)."
|
|
211
|
+
);
|
|
193
212
|
include = void 0;
|
|
194
213
|
}
|
|
214
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
215
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
216
|
+
skipVisibilityCheck = false;
|
|
217
|
+
}
|
|
195
218
|
if (typeof wrap !== "boolean") {
|
|
196
|
-
console.warn("Invalid wrap. Fallback: false.");
|
|
219
|
+
console.warn("Invalid wrap option. Fallback: false.");
|
|
197
220
|
wrap = false;
|
|
198
221
|
}
|
|
199
|
-
const focusables = getFocusables(container, {
|
|
222
|
+
const focusables = getFocusables(container, {
|
|
223
|
+
composed,
|
|
224
|
+
filter,
|
|
225
|
+
include,
|
|
226
|
+
skipVisibilityCheck
|
|
227
|
+
});
|
|
200
228
|
const { length } = focusables;
|
|
201
229
|
if (!length) {
|
|
202
230
|
return null;
|
|
@@ -427,7 +455,7 @@ function isUngroupedRadio(element) {
|
|
|
427
455
|
* High-precision focus management utility with full composed tree support.
|
|
428
456
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
429
457
|
*
|
|
430
|
-
* @version 4.
|
|
458
|
+
* @version 4.2.0
|
|
431
459
|
* @author Yusuke Kamiyamane
|
|
432
460
|
* @license MIT
|
|
433
461
|
* @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.2.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 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.
|
|
6
|
+
* @version 4.2.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 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,24 +42,33 @@ 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
|
-
console.warn("Invalid composed. Fallback: false.");
|
|
52
|
+
console.warn("Invalid composed option. Fallback: false.");
|
|
48
53
|
composed = false;
|
|
49
54
|
}
|
|
50
55
|
if (typeof filter !== "undefined" && typeof filter !== "function") {
|
|
51
|
-
console.warn(
|
|
56
|
+
console.warn(
|
|
57
|
+
"Invalid filter function. Fallback: no filter function (undefined)."
|
|
58
|
+
);
|
|
52
59
|
filter = void 0;
|
|
53
60
|
}
|
|
54
61
|
if (typeof include !== "undefined" && typeof include !== "function") {
|
|
55
|
-
console.warn(
|
|
62
|
+
console.warn(
|
|
63
|
+
"Invalid include function. Fallback: no include function (undefined)."
|
|
64
|
+
);
|
|
56
65
|
include = void 0;
|
|
57
66
|
}
|
|
58
67
|
const elements = [];
|
|
59
68
|
if (composed || include) {
|
|
60
69
|
let traverse2 = function(node) {
|
|
61
70
|
if (node instanceof Element) {
|
|
62
|
-
if (isFocusable(node) || include?.(node)) {
|
|
71
|
+
if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
|
|
63
72
|
elements[elements.length] = node;
|
|
64
73
|
}
|
|
65
74
|
}
|
|
@@ -80,7 +89,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
80
89
|
if (!(candidate instanceof Element)) {
|
|
81
90
|
continue;
|
|
82
91
|
}
|
|
83
|
-
if (isFocusable(candidate)) {
|
|
92
|
+
if (isFocusable(candidate, { skipVisibilityCheck })) {
|
|
84
93
|
elements[elements.length] = candidate;
|
|
85
94
|
}
|
|
86
95
|
}
|
|
@@ -126,11 +135,16 @@ function inertOutside(element) {
|
|
|
126
135
|
});
|
|
127
136
|
};
|
|
128
137
|
}
|
|
129
|
-
function isFocusable(element) {
|
|
138
|
+
function isFocusable(element, options = {}) {
|
|
130
139
|
if (!(element instanceof Element)) {
|
|
131
140
|
console.warn("Invalid element");
|
|
132
141
|
return false;
|
|
133
142
|
}
|
|
143
|
+
let { skipVisibilityCheck = false } = options;
|
|
144
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
145
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
146
|
+
skipVisibilityCheck = false;
|
|
147
|
+
}
|
|
134
148
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
135
149
|
return false;
|
|
136
150
|
}
|
|
@@ -143,7 +157,7 @@ function isFocusable(element) {
|
|
|
143
157
|
if (isDisabledDeep(element)) {
|
|
144
158
|
return false;
|
|
145
159
|
}
|
|
146
|
-
if (!element.checkVisibility({
|
|
160
|
+
if (!skipVisibilityCheck && !element.checkVisibility({
|
|
147
161
|
contentVisibilityAuto: true,
|
|
148
162
|
opacityProperty: true,
|
|
149
163
|
visibilityProperty: true
|
|
@@ -162,6 +176,7 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
162
176
|
composed = false,
|
|
163
177
|
filter,
|
|
164
178
|
include,
|
|
179
|
+
skipVisibilityCheck = false,
|
|
165
180
|
wrap = false
|
|
166
181
|
} = options;
|
|
167
182
|
if (!(anchor instanceof Element)) {
|
|
@@ -179,22 +194,35 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
179
194
|
return null;
|
|
180
195
|
}
|
|
181
196
|
if (typeof composed !== "boolean") {
|
|
182
|
-
console.warn("Invalid composed. Fallback: false.");
|
|
197
|
+
console.warn("Invalid composed option. Fallback: false.");
|
|
183
198
|
composed = false;
|
|
184
199
|
}
|
|
185
200
|
if (typeof filter !== "undefined" && typeof filter !== "function") {
|
|
186
|
-
console.warn(
|
|
201
|
+
console.warn(
|
|
202
|
+
"Invalid filter function. Fallback: no filter function (undefined)."
|
|
203
|
+
);
|
|
187
204
|
filter = void 0;
|
|
188
205
|
}
|
|
189
206
|
if (typeof include !== "undefined" && typeof include !== "function") {
|
|
190
|
-
console.warn(
|
|
207
|
+
console.warn(
|
|
208
|
+
"Invalid include function. Fallback: no include function (undefined)."
|
|
209
|
+
);
|
|
191
210
|
include = void 0;
|
|
192
211
|
}
|
|
212
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
213
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
214
|
+
skipVisibilityCheck = false;
|
|
215
|
+
}
|
|
193
216
|
if (typeof wrap !== "boolean") {
|
|
194
|
-
console.warn("Invalid wrap. Fallback: false.");
|
|
217
|
+
console.warn("Invalid wrap option. Fallback: false.");
|
|
195
218
|
wrap = false;
|
|
196
219
|
}
|
|
197
|
-
const focusables = getFocusables(container, {
|
|
220
|
+
const focusables = getFocusables(container, {
|
|
221
|
+
composed,
|
|
222
|
+
filter,
|
|
223
|
+
include,
|
|
224
|
+
skipVisibilityCheck
|
|
225
|
+
});
|
|
198
226
|
const { length } = focusables;
|
|
199
227
|
if (!length) {
|
|
200
228
|
return null;
|
|
@@ -425,7 +453,7 @@ function isUngroupedRadio(element) {
|
|
|
425
453
|
* High-precision focus management utility with full composed tree support.
|
|
426
454
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
427
455
|
*
|
|
428
|
-
* @version 4.
|
|
456
|
+
* @version 4.2.0
|
|
429
457
|
* @author Yusuke Kamiyamane
|
|
430
458
|
* @license MIT
|
|
431
459
|
* @copyright Copyright (c) Yusuke Kamiyamane
|