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 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 { composed = false, filter, include } = options;
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("Invalid filter. Fallback: no filter function (undefined).");
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("Invalid include. Fallback: no include function (undefined).");
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("Invalid filter. Fallback: no filter function (undefined).");
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("Invalid include. Fallback: no include function (undefined).");
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, { composed, filter, include });
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.1.7
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.1.7
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): boolean;
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.7
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): boolean;
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 { composed = false, filter, include } = options;
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("Invalid filter. Fallback: no filter function (undefined).");
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("Invalid include. Fallback: no include function (undefined).");
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("Invalid filter. Fallback: no filter function (undefined).");
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("Invalid include. Fallback: no include function (undefined).");
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, { composed, filter, include });
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.1.7
456
+ * @version 4.2.0
429
457
  * @author Yusuke Kamiyamane
430
458
  * @license MIT
431
459
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "power-focusable",
3
- "version": "4.1.7",
3
+ "version": "4.2.0",
4
4
  "description": "High-precision focus management utility with full composed tree support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",