power-focusable 4.3.28 → 4.3.29

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
@@ -24,24 +24,24 @@ import {
24
24
  } from 'power-focusable';
25
25
 
26
26
  // CDNs
27
- import { ... } 'https://esm.sh/power-focusable@4.3.28';
27
+ import { ... } 'https://esm.sh/power-focusable@4.3.29';
28
28
  // or
29
- import { ... } 'https://cdn.jsdelivr.net/npm/power-focusable@4.3.28/+esm';
29
+ import { ... } 'https://cdn.jsdelivr.net/npm/power-focusable@4.3.29/+esm';
30
30
  // or
31
- import { ... } 'https://esm.unpkg.com/power-focusable@4.3.28';
31
+ import { ... } 'https://esm.unpkg.com/power-focusable@4.3.29';
32
32
  ```
33
33
 
34
34
  ## 🪄 Options
35
35
 
36
36
  ```ts
37
37
  interface PowerFocusableOptions {
38
- anchor: Element | null; // default: DocumentOrShadowRoot.activeElement
39
- composed: boolean; // default: false
40
- filter: PredicateFunction; // default: undefined
41
- include: PredicateFunction; // default: undefined
42
- skipNegativeTabIndexCheck: boolean; // default: false
43
- skipVisibilityCheck: boolean; // default: false
44
- wrap: boolean; // default: false
38
+ anchor: Element | null; // default: DocumentOrShadowRoot.activeElement
39
+ composed: boolean; // default: false
40
+ filter: PredicateFunction | undefined; // default: undefined
41
+ include: PredicateFunction | undefined; // default: undefined
42
+ skipNegativeTabIndexCheck: boolean; // default: false
43
+ skipVisibilityCheck: boolean; // default: false
44
+ wrap: boolean; // default: false
45
45
  }
46
46
 
47
47
  type PredicateFunction = (element: Element) => boolean;
package/dist/index.cjs CHANGED
@@ -64,37 +64,13 @@ function getFocusables(container = document.body, options = {}) {
64
64
  console.warn("Invalid container element. Fallback: <body> element.");
65
65
  container = document.body;
66
66
  }
67
- let {
68
- composed = false,
67
+ const {
68
+ composed,
69
69
  filter,
70
70
  include,
71
- skipNegativeTabIndexCheck = false,
72
- skipVisibilityCheck = false
73
- } = options;
74
- if (typeof composed !== "boolean") {
75
- console.warn("Invalid composed option. Fallback: false.");
76
- composed = false;
77
- }
78
- if (typeof filter !== "undefined" && typeof filter !== "function") {
79
- console.warn(
80
- "Invalid filter function. Fallback: no filter function (undefined)."
81
- );
82
- filter = void 0;
83
- }
84
- if (typeof include !== "undefined" && typeof include !== "function") {
85
- console.warn(
86
- "Invalid include function. Fallback: no include function (undefined)."
87
- );
88
- include = void 0;
89
- }
90
- if (typeof skipNegativeTabIndexCheck !== "boolean") {
91
- console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
92
- skipNegativeTabIndexCheck = false;
93
- }
94
- if (typeof skipVisibilityCheck !== "boolean") {
95
- console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
96
- skipVisibilityCheck = false;
97
- }
71
+ skipNegativeTabIndexCheck,
72
+ skipVisibilityCheck
73
+ } = resolveOptions(options);
98
74
  const candidates = [];
99
75
  if (composed || include) {
100
76
  let traverse2 = function(node) {
@@ -171,15 +147,7 @@ function isFocusable(element, options = {}) {
171
147
  console.warn("Invalid element");
172
148
  return false;
173
149
  }
174
- let { skipNegativeTabIndexCheck = false, skipVisibilityCheck = false } = options;
175
- if (typeof skipNegativeTabIndexCheck !== "boolean") {
176
- console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
177
- skipNegativeTabIndexCheck = false;
178
- }
179
- if (typeof skipVisibilityCheck !== "boolean") {
180
- console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
181
- skipVisibilityCheck = false;
182
- }
150
+ const { skipNegativeTabIndexCheck, skipVisibilityCheck } = resolveOptions(options);
183
151
  if (element.hasAttribute("hidden") || isInert(element)) {
184
152
  return false;
185
153
  }
@@ -208,65 +176,30 @@ function getRelativeFocusable(container, offset, options) {
208
176
  console.warn("Invalid container element. Fallback: <body> element.");
209
177
  container = document.body;
210
178
  }
211
- let {
212
- anchor = getActiveElement(),
213
- composed = false,
179
+ const {
180
+ anchor,
181
+ composed,
214
182
  filter,
215
183
  include,
216
- skipNegativeTabIndexCheck = false,
217
- skipVisibilityCheck = false,
218
- wrap = false
219
- } = options;
184
+ skipNegativeTabIndexCheck,
185
+ skipVisibilityCheck,
186
+ wrap
187
+ } = resolveOptions(options);
220
188
  if (!(anchor instanceof Element)) {
221
- const active = getActiveElement();
222
- if (active instanceof Element) {
223
- console.warn("Invalid anchor element. Fallback: active element.");
224
- anchor = active;
225
- } else {
226
- console.warn("Invalid anchor element");
227
- return null;
228
- }
189
+ console.warn("Invalid anchor element");
190
+ return null;
229
191
  }
230
192
  if (!containsComposed(container, anchor)) {
231
193
  console.warn("Anchor (active) element not within container");
232
194
  return null;
233
195
  }
234
- if (typeof composed !== "boolean") {
235
- console.warn("Invalid composed option. Fallback: false.");
236
- composed = false;
237
- }
238
- if (typeof filter !== "undefined" && typeof filter !== "function") {
239
- console.warn(
240
- "Invalid filter function. Fallback: no filter function (undefined)."
241
- );
242
- filter = void 0;
243
- }
244
- if (typeof include !== "undefined" && typeof include !== "function") {
245
- console.warn(
246
- "Invalid include function. Fallback: no include function (undefined)."
247
- );
248
- include = void 0;
249
- }
250
- if (typeof skipNegativeTabIndexCheck !== "boolean") {
251
- console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
252
- skipNegativeTabIndexCheck = false;
253
- }
254
- if (typeof skipVisibilityCheck !== "boolean") {
255
- console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
256
- skipVisibilityCheck = false;
257
- }
258
- if (typeof wrap !== "boolean") {
259
- console.warn("Invalid wrap option. Fallback: false.");
260
- wrap = false;
261
- }
262
- const settings = {
196
+ const focusables = getFocusables(container, {
263
197
  composed,
198
+ filter,
199
+ include,
264
200
  skipNegativeTabIndexCheck,
265
201
  skipVisibilityCheck
266
- };
267
- filter && Object.assign(settings, { filter });
268
- include && Object.assign(settings, { include });
269
- const focusables = getFocusables(container, settings);
202
+ });
270
203
  const { length } = focusables;
271
204
  if (!length) {
272
205
  return null;
@@ -489,12 +422,67 @@ function isInert(element) {
489
422
  function isUngroupedRadio(element) {
490
423
  return element.type === "radio" && !!element.name;
491
424
  }
425
+ function resolveOptions(options) {
426
+ let {
427
+ anchor = getActiveElement(),
428
+ composed = false,
429
+ filter,
430
+ include,
431
+ skipNegativeTabIndexCheck = false,
432
+ skipVisibilityCheck = false,
433
+ wrap = false
434
+ } = options;
435
+ if (!(anchor instanceof Element)) {
436
+ const active = getActiveElement();
437
+ if (active instanceof Element) {
438
+ console.warn("Invalid anchor element. Fallback: active element.");
439
+ anchor = active;
440
+ }
441
+ }
442
+ if (typeof composed !== "boolean") {
443
+ console.warn("Invalid composed option. Fallback: false.");
444
+ composed = false;
445
+ }
446
+ if (filter !== void 0 && typeof filter !== "function") {
447
+ console.warn(
448
+ "Invalid filter function. Fallback: no filter function (undefined)."
449
+ );
450
+ filter = void 0;
451
+ }
452
+ if (include !== void 0 && typeof include !== "function") {
453
+ console.warn(
454
+ "Invalid include function. Fallback: no include function (undefined)."
455
+ );
456
+ include = void 0;
457
+ }
458
+ if (typeof skipNegativeTabIndexCheck !== "boolean") {
459
+ console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
460
+ skipNegativeTabIndexCheck = false;
461
+ }
462
+ if (typeof skipVisibilityCheck !== "boolean") {
463
+ console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
464
+ skipVisibilityCheck = false;
465
+ }
466
+ if (typeof wrap !== "boolean") {
467
+ console.warn("Invalid wrap option. Fallback: false.");
468
+ wrap = false;
469
+ }
470
+ return {
471
+ anchor,
472
+ composed,
473
+ filter,
474
+ include,
475
+ skipNegativeTabIndexCheck,
476
+ skipVisibilityCheck,
477
+ wrap
478
+ };
479
+ }
492
480
  /**
493
481
  * Power Focusable
494
482
  * High-precision focus management utility with full composed tree support.
495
483
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
496
484
  *
497
- * @version 4.3.28
485
+ * @version 4.3.29
498
486
  * @author Yusuke Kamiyamane
499
487
  * @license MIT
500
488
  * @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.3.28
6
+ * @version 4.3.29
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -12,8 +12,8 @@
12
12
  interface PowerFocusableOptions {
13
13
  anchor: Element | null;
14
14
  composed: boolean;
15
- filter: PredicateFunction;
16
- include: PredicateFunction;
15
+ filter: PredicateFunction | undefined;
16
+ include: PredicateFunction | undefined;
17
17
  skipNegativeTabIndexCheck: boolean;
18
18
  skipVisibilityCheck: boolean;
19
19
  wrap: boolean;
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.3.28
6
+ * @version 4.3.29
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -12,8 +12,8 @@
12
12
  interface PowerFocusableOptions {
13
13
  anchor: Element | null;
14
14
  composed: boolean;
15
- filter: PredicateFunction;
16
- include: PredicateFunction;
15
+ filter: PredicateFunction | undefined;
16
+ include: PredicateFunction | undefined;
17
17
  skipNegativeTabIndexCheck: boolean;
18
18
  skipVisibilityCheck: boolean;
19
19
  wrap: boolean;
package/dist/index.js CHANGED
@@ -62,37 +62,13 @@ function getFocusables(container = document.body, options = {}) {
62
62
  console.warn("Invalid container element. Fallback: <body> element.");
63
63
  container = document.body;
64
64
  }
65
- let {
66
- composed = false,
65
+ const {
66
+ composed,
67
67
  filter,
68
68
  include,
69
- skipNegativeTabIndexCheck = false,
70
- skipVisibilityCheck = false
71
- } = options;
72
- if (typeof composed !== "boolean") {
73
- console.warn("Invalid composed option. Fallback: false.");
74
- composed = false;
75
- }
76
- if (typeof filter !== "undefined" && typeof filter !== "function") {
77
- console.warn(
78
- "Invalid filter function. Fallback: no filter function (undefined)."
79
- );
80
- filter = void 0;
81
- }
82
- if (typeof include !== "undefined" && typeof include !== "function") {
83
- console.warn(
84
- "Invalid include function. Fallback: no include function (undefined)."
85
- );
86
- include = void 0;
87
- }
88
- if (typeof skipNegativeTabIndexCheck !== "boolean") {
89
- console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
90
- skipNegativeTabIndexCheck = false;
91
- }
92
- if (typeof skipVisibilityCheck !== "boolean") {
93
- console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
94
- skipVisibilityCheck = false;
95
- }
69
+ skipNegativeTabIndexCheck,
70
+ skipVisibilityCheck
71
+ } = resolveOptions(options);
96
72
  const candidates = [];
97
73
  if (composed || include) {
98
74
  let traverse2 = function(node) {
@@ -169,15 +145,7 @@ function isFocusable(element, options = {}) {
169
145
  console.warn("Invalid element");
170
146
  return false;
171
147
  }
172
- let { skipNegativeTabIndexCheck = false, skipVisibilityCheck = false } = options;
173
- if (typeof skipNegativeTabIndexCheck !== "boolean") {
174
- console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
175
- skipNegativeTabIndexCheck = false;
176
- }
177
- if (typeof skipVisibilityCheck !== "boolean") {
178
- console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
179
- skipVisibilityCheck = false;
180
- }
148
+ const { skipNegativeTabIndexCheck, skipVisibilityCheck } = resolveOptions(options);
181
149
  if (element.hasAttribute("hidden") || isInert(element)) {
182
150
  return false;
183
151
  }
@@ -206,65 +174,30 @@ function getRelativeFocusable(container, offset, options) {
206
174
  console.warn("Invalid container element. Fallback: <body> element.");
207
175
  container = document.body;
208
176
  }
209
- let {
210
- anchor = getActiveElement(),
211
- composed = false,
177
+ const {
178
+ anchor,
179
+ composed,
212
180
  filter,
213
181
  include,
214
- skipNegativeTabIndexCheck = false,
215
- skipVisibilityCheck = false,
216
- wrap = false
217
- } = options;
182
+ skipNegativeTabIndexCheck,
183
+ skipVisibilityCheck,
184
+ wrap
185
+ } = resolveOptions(options);
218
186
  if (!(anchor instanceof Element)) {
219
- const active = getActiveElement();
220
- if (active instanceof Element) {
221
- console.warn("Invalid anchor element. Fallback: active element.");
222
- anchor = active;
223
- } else {
224
- console.warn("Invalid anchor element");
225
- return null;
226
- }
187
+ console.warn("Invalid anchor element");
188
+ return null;
227
189
  }
228
190
  if (!containsComposed(container, anchor)) {
229
191
  console.warn("Anchor (active) element not within container");
230
192
  return null;
231
193
  }
232
- if (typeof composed !== "boolean") {
233
- console.warn("Invalid composed option. Fallback: false.");
234
- composed = false;
235
- }
236
- if (typeof filter !== "undefined" && typeof filter !== "function") {
237
- console.warn(
238
- "Invalid filter function. Fallback: no filter function (undefined)."
239
- );
240
- filter = void 0;
241
- }
242
- if (typeof include !== "undefined" && typeof include !== "function") {
243
- console.warn(
244
- "Invalid include function. Fallback: no include function (undefined)."
245
- );
246
- include = void 0;
247
- }
248
- if (typeof skipNegativeTabIndexCheck !== "boolean") {
249
- console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
250
- skipNegativeTabIndexCheck = false;
251
- }
252
- if (typeof skipVisibilityCheck !== "boolean") {
253
- console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
254
- skipVisibilityCheck = false;
255
- }
256
- if (typeof wrap !== "boolean") {
257
- console.warn("Invalid wrap option. Fallback: false.");
258
- wrap = false;
259
- }
260
- const settings = {
194
+ const focusables = getFocusables(container, {
261
195
  composed,
196
+ filter,
197
+ include,
262
198
  skipNegativeTabIndexCheck,
263
199
  skipVisibilityCheck
264
- };
265
- filter && Object.assign(settings, { filter });
266
- include && Object.assign(settings, { include });
267
- const focusables = getFocusables(container, settings);
200
+ });
268
201
  const { length } = focusables;
269
202
  if (!length) {
270
203
  return null;
@@ -487,12 +420,67 @@ function isInert(element) {
487
420
  function isUngroupedRadio(element) {
488
421
  return element.type === "radio" && !!element.name;
489
422
  }
423
+ function resolveOptions(options) {
424
+ let {
425
+ anchor = getActiveElement(),
426
+ composed = false,
427
+ filter,
428
+ include,
429
+ skipNegativeTabIndexCheck = false,
430
+ skipVisibilityCheck = false,
431
+ wrap = false
432
+ } = options;
433
+ if (!(anchor instanceof Element)) {
434
+ const active = getActiveElement();
435
+ if (active instanceof Element) {
436
+ console.warn("Invalid anchor element. Fallback: active element.");
437
+ anchor = active;
438
+ }
439
+ }
440
+ if (typeof composed !== "boolean") {
441
+ console.warn("Invalid composed option. Fallback: false.");
442
+ composed = false;
443
+ }
444
+ if (filter !== void 0 && typeof filter !== "function") {
445
+ console.warn(
446
+ "Invalid filter function. Fallback: no filter function (undefined)."
447
+ );
448
+ filter = void 0;
449
+ }
450
+ if (include !== void 0 && typeof include !== "function") {
451
+ console.warn(
452
+ "Invalid include function. Fallback: no include function (undefined)."
453
+ );
454
+ include = void 0;
455
+ }
456
+ if (typeof skipNegativeTabIndexCheck !== "boolean") {
457
+ console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
458
+ skipNegativeTabIndexCheck = false;
459
+ }
460
+ if (typeof skipVisibilityCheck !== "boolean") {
461
+ console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
462
+ skipVisibilityCheck = false;
463
+ }
464
+ if (typeof wrap !== "boolean") {
465
+ console.warn("Invalid wrap option. Fallback: false.");
466
+ wrap = false;
467
+ }
468
+ return {
469
+ anchor,
470
+ composed,
471
+ filter,
472
+ include,
473
+ skipNegativeTabIndexCheck,
474
+ skipVisibilityCheck,
475
+ wrap
476
+ };
477
+ }
490
478
  /**
491
479
  * Power Focusable
492
480
  * High-precision focus management utility with full composed tree support.
493
481
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
494
482
  *
495
- * @version 4.3.28
483
+ * @version 4.3.29
496
484
  * @author Yusuke Kamiyamane
497
485
  * @license MIT
498
486
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "power-focusable",
3
- "version": "4.3.28",
3
+ "version": "4.3.29",
4
4
  "description": "High-precision focus management utility with full composed tree support",
5
5
  "keywords": [
6
6
  "a11y",