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 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 { 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
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, { composed, filter, include });
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.1.8
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.1.8
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.8
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,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 { 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
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, { composed, filter, include });
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.1.8
456
+ * @version 4.2.0
437
457
  * @author Yusuke Kamiyamane
438
458
  * @license MIT
439
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.8",
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",