power-focusable 4.1.8 → 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 +27 -7
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +27 -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;
|
|
@@ -65,7 +70,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
65
70
|
if (composed || include) {
|
|
66
71
|
let traverse2 = function(node) {
|
|
67
72
|
if (node instanceof Element) {
|
|
68
|
-
if (isFocusable(node) || include?.(node)) {
|
|
73
|
+
if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
|
|
69
74
|
elements[elements.length] = node;
|
|
70
75
|
}
|
|
71
76
|
}
|
|
@@ -86,7 +91,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
86
91
|
if (!(candidate instanceof Element)) {
|
|
87
92
|
continue;
|
|
88
93
|
}
|
|
89
|
-
if (isFocusable(candidate)) {
|
|
94
|
+
if (isFocusable(candidate, { skipVisibilityCheck })) {
|
|
90
95
|
elements[elements.length] = candidate;
|
|
91
96
|
}
|
|
92
97
|
}
|
|
@@ -132,11 +137,16 @@ function inertOutside(element) {
|
|
|
132
137
|
});
|
|
133
138
|
};
|
|
134
139
|
}
|
|
135
|
-
function isFocusable(element) {
|
|
140
|
+
function isFocusable(element, options = {}) {
|
|
136
141
|
if (!(element instanceof Element)) {
|
|
137
142
|
console.warn("Invalid element");
|
|
138
143
|
return false;
|
|
139
144
|
}
|
|
145
|
+
let { skipVisibilityCheck = false } = options;
|
|
146
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
147
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
148
|
+
skipVisibilityCheck = false;
|
|
149
|
+
}
|
|
140
150
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
141
151
|
return false;
|
|
142
152
|
}
|
|
@@ -149,7 +159,7 @@ function isFocusable(element) {
|
|
|
149
159
|
if (isDisabledDeep(element)) {
|
|
150
160
|
return false;
|
|
151
161
|
}
|
|
152
|
-
if (!element.checkVisibility({
|
|
162
|
+
if (!skipVisibilityCheck && !element.checkVisibility({
|
|
153
163
|
contentVisibilityAuto: true,
|
|
154
164
|
opacityProperty: true,
|
|
155
165
|
visibilityProperty: true
|
|
@@ -168,6 +178,7 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
168
178
|
composed = false,
|
|
169
179
|
filter,
|
|
170
180
|
include,
|
|
181
|
+
skipVisibilityCheck = false,
|
|
171
182
|
wrap = false
|
|
172
183
|
} = options;
|
|
173
184
|
if (!(anchor instanceof Element)) {
|
|
@@ -200,11 +211,20 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
200
211
|
);
|
|
201
212
|
include = void 0;
|
|
202
213
|
}
|
|
214
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
215
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
216
|
+
skipVisibilityCheck = false;
|
|
217
|
+
}
|
|
203
218
|
if (typeof wrap !== "boolean") {
|
|
204
219
|
console.warn("Invalid wrap option. Fallback: false.");
|
|
205
220
|
wrap = false;
|
|
206
221
|
}
|
|
207
|
-
const focusables = getFocusables(container, {
|
|
222
|
+
const focusables = getFocusables(container, {
|
|
223
|
+
composed,
|
|
224
|
+
filter,
|
|
225
|
+
include,
|
|
226
|
+
skipVisibilityCheck
|
|
227
|
+
});
|
|
208
228
|
const { length } = focusables;
|
|
209
229
|
if (!length) {
|
|
210
230
|
return null;
|
|
@@ -435,7 +455,7 @@ function isUngroupedRadio(element) {
|
|
|
435
455
|
* High-precision focus management utility with full composed tree support.
|
|
436
456
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
437
457
|
*
|
|
438
|
-
* @version 4.
|
|
458
|
+
* @version 4.2.0
|
|
439
459
|
* @author Yusuke Kamiyamane
|
|
440
460
|
* @license MIT
|
|
441
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,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;
|
|
@@ -63,7 +68,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
63
68
|
if (composed || include) {
|
|
64
69
|
let traverse2 = function(node) {
|
|
65
70
|
if (node instanceof Element) {
|
|
66
|
-
if (isFocusable(node) || include?.(node)) {
|
|
71
|
+
if (isFocusable(node, { skipVisibilityCheck }) || include?.(node)) {
|
|
67
72
|
elements[elements.length] = node;
|
|
68
73
|
}
|
|
69
74
|
}
|
|
@@ -84,7 +89,7 @@ function getFocusables(container = document.body, options = {}) {
|
|
|
84
89
|
if (!(candidate instanceof Element)) {
|
|
85
90
|
continue;
|
|
86
91
|
}
|
|
87
|
-
if (isFocusable(candidate)) {
|
|
92
|
+
if (isFocusable(candidate, { skipVisibilityCheck })) {
|
|
88
93
|
elements[elements.length] = candidate;
|
|
89
94
|
}
|
|
90
95
|
}
|
|
@@ -130,11 +135,16 @@ function inertOutside(element) {
|
|
|
130
135
|
});
|
|
131
136
|
};
|
|
132
137
|
}
|
|
133
|
-
function isFocusable(element) {
|
|
138
|
+
function isFocusable(element, options = {}) {
|
|
134
139
|
if (!(element instanceof Element)) {
|
|
135
140
|
console.warn("Invalid element");
|
|
136
141
|
return false;
|
|
137
142
|
}
|
|
143
|
+
let { skipVisibilityCheck = false } = options;
|
|
144
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
145
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
146
|
+
skipVisibilityCheck = false;
|
|
147
|
+
}
|
|
138
148
|
if (element.hasAttribute("hidden") || isInert(element)) {
|
|
139
149
|
return false;
|
|
140
150
|
}
|
|
@@ -147,7 +157,7 @@ function isFocusable(element) {
|
|
|
147
157
|
if (isDisabledDeep(element)) {
|
|
148
158
|
return false;
|
|
149
159
|
}
|
|
150
|
-
if (!element.checkVisibility({
|
|
160
|
+
if (!skipVisibilityCheck && !element.checkVisibility({
|
|
151
161
|
contentVisibilityAuto: true,
|
|
152
162
|
opacityProperty: true,
|
|
153
163
|
visibilityProperty: true
|
|
@@ -166,6 +176,7 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
166
176
|
composed = false,
|
|
167
177
|
filter,
|
|
168
178
|
include,
|
|
179
|
+
skipVisibilityCheck = false,
|
|
169
180
|
wrap = false
|
|
170
181
|
} = options;
|
|
171
182
|
if (!(anchor instanceof Element)) {
|
|
@@ -198,11 +209,20 @@ function getRelativeFocusable(container, offset, options) {
|
|
|
198
209
|
);
|
|
199
210
|
include = void 0;
|
|
200
211
|
}
|
|
212
|
+
if (typeof skipVisibilityCheck !== "boolean") {
|
|
213
|
+
console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
|
|
214
|
+
skipVisibilityCheck = false;
|
|
215
|
+
}
|
|
201
216
|
if (typeof wrap !== "boolean") {
|
|
202
217
|
console.warn("Invalid wrap option. Fallback: false.");
|
|
203
218
|
wrap = false;
|
|
204
219
|
}
|
|
205
|
-
const focusables = getFocusables(container, {
|
|
220
|
+
const focusables = getFocusables(container, {
|
|
221
|
+
composed,
|
|
222
|
+
filter,
|
|
223
|
+
include,
|
|
224
|
+
skipVisibilityCheck
|
|
225
|
+
});
|
|
206
226
|
const { length } = focusables;
|
|
207
227
|
if (!length) {
|
|
208
228
|
return null;
|
|
@@ -433,7 +453,7 @@ function isUngroupedRadio(element) {
|
|
|
433
453
|
* High-precision focus management utility with full composed tree support.
|
|
434
454
|
* Handles complex focus rules including tabindex ordering, radio groups, inert.
|
|
435
455
|
*
|
|
436
|
-
* @version 4.
|
|
456
|
+
* @version 4.2.0
|
|
437
457
|
* @author Yusuke Kamiyamane
|
|
438
458
|
* @license MIT
|
|
439
459
|
* @copyright Copyright (c) Yusuke Kamiyamane
|