power-focusable 4.3.29 → 4.3.31

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,11 +24,11 @@ import {
24
24
  } from 'power-focusable';
25
25
 
26
26
  // CDNs
27
- import { ... } 'https://esm.sh/power-focusable@4.3.29';
27
+ import { ... } 'https://esm.sh/power-focusable@4.3.31';
28
28
  // or
29
- import { ... } 'https://cdn.jsdelivr.net/npm/power-focusable@4.3.29/+esm';
29
+ import { ... } 'https://cdn.jsdelivr.net/npm/power-focusable@4.3.31/+esm';
30
30
  // or
31
- import { ... } 'https://esm.unpkg.com/power-focusable@4.3.29';
31
+ import { ... } 'https://esm.unpkg.com/power-focusable@4.3.31';
32
32
  ```
33
33
 
34
34
  ## 🪄 Options
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  // src/index.ts
4
- var FOCUSABLE_SELECTOR = `:is(a[href], area[href], button, embed, iframe, input:not([type="hidden" i]), object, select, details > summary:first-of-type, textarea, [contenteditable]:not([contenteditable="false" i]), [controls], [tabindex]):not(:disabled, [hidden], [inert], [tabindex="-1"])`;
4
+ var FOCUSABLE_SELECTOR = ':is(a[href], area[href], button, embed, iframe, input:not([type="hidden" i]), object, select, details > summary:first-of-type, textarea, [contenteditable]:not([contenteditable="false" i]), [controls], [tabindex]):not(:disabled, [hidden], [inert], [tabindex="-1"])';
5
5
  var FOCUSABLE_SELECTOR_WITH_NEGATIVE_TABINDEX = FOCUSABLE_SELECTOR.replace(
6
6
  /(,\s*)?\[tabindex="-1"\]/g,
7
7
  ""
@@ -78,31 +78,21 @@ function getFocusables(container = document.body, options = {}) {
78
78
  skipNegativeTabIndexCheck,
79
79
  skipVisibilityCheck
80
80
  }) || include?.(node)) {
81
- candidates[candidates.length] = node;
82
- }
83
- const children2 = composed ? getComposedChildren(node) : getChildren(node);
84
- for (let i = 0, l = children2.length; i < l; i++) {
85
- const child = children2[i];
86
- child && traverse2(child);
81
+ candidates.push(node);
87
82
  }
83
+ (composed ? getComposedChildren(node) : [...node.children]).map(traverse2);
88
84
  };
89
- const children = composed ? getComposedChildren(container) : getChildren(container);
90
- for (let i = 0, l = children.length; i < l; i++) {
91
- const child = children[i];
92
- child && traverse2(child);
93
- }
85
+ (composed ? getComposedChildren(container) : [...container.children]).map(
86
+ traverse2
87
+ );
94
88
  } else {
95
- const matches = container.querySelectorAll(
89
+ for (const m of container.querySelectorAll(
96
90
  skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR_WITH_NEGATIVE_TABINDEX : FOCUSABLE_SELECTOR
97
- );
98
- for (let i = 0, l = matches.length; i < l; i++) {
99
- const matched = matches[i];
100
- if (matched && isFocusable(matched, {
91
+ )) {
92
+ m && isFocusable(m, {
101
93
  skipNegativeTabIndexCheck,
102
94
  skipVisibilityCheck
103
- })) {
104
- candidates[candidates.length] = matched;
105
- }
95
+ }) && candidates.push(m);
106
96
  }
107
97
  }
108
98
  return normalizeRadioGroup(
@@ -127,20 +117,13 @@ function inertOutside(element) {
127
117
  function traverse(node, fn) {
128
118
  const parent = getComposedParent(node);
129
119
  if (parent) {
130
- for (const sibling of getComposedSiblings(node)) {
131
- fn(sibling);
132
- }
120
+ getComposedSiblings(node).map(fn);
133
121
  traverse(parent, fn);
134
122
  }
135
123
  }
136
124
  const elements = [];
137
125
  traverse(element, (node) => node && applyInert(node) && elements.push(node));
138
- return () => {
139
- for (let i = 0, l = elements.length; i < l; i++) {
140
- const element2 = elements[i];
141
- element2 && restoreInert(element2);
142
- }
143
- };
126
+ return () => elements.map(restoreInert);
144
127
  }
145
128
  function isFocusable(element, options = {}) {
146
129
  if (!(element instanceof Element)) {
@@ -245,74 +228,60 @@ function isDisabledDeep(element) {
245
228
  }
246
229
  function normalizeRadioGroup(elements) {
247
230
  let map = null;
248
- for (let i = 0, l = elements.length; i < l; i++) {
249
- const element = elements[i];
250
- if (!(element instanceof HTMLInputElement)) {
231
+ for (const e of elements) {
232
+ if (!(e instanceof HTMLInputElement)) {
251
233
  continue;
252
234
  }
253
- if (!isUngroupedRadio(element)) {
235
+ if (!isUngroupedRadio(e)) {
254
236
  continue;
255
237
  }
256
238
  if (!map) {
257
239
  map = /* @__PURE__ */ new Map();
258
240
  }
259
- const root = element.getRootNode();
260
- let value = map.get(root);
261
- if (!value) {
262
- value = /* @__PURE__ */ new Map();
263
- map.set(root, value);
241
+ const root = e.getRootNode();
242
+ let groups = map.get(root);
243
+ if (!groups) {
244
+ groups = /* @__PURE__ */ new Map();
245
+ map.set(root, groups);
264
246
  }
265
- let v = value.get(element.form);
266
- if (!v) {
267
- v = /* @__PURE__ */ new Map();
268
- value.set(element.form, v);
247
+ let group = groups.get(e.form);
248
+ if (!group) {
249
+ group = /* @__PURE__ */ new Map();
250
+ groups.set(e.form, group);
269
251
  }
270
- let vv = v.get(element.name);
271
- if (!vv) {
272
- vv = [];
273
- v.set(element.name, vv);
252
+ let radios = group.get(e.name);
253
+ if (!radios) {
254
+ radios = [];
255
+ group.set(e.name, radios);
274
256
  }
275
- vv[vv.length] = element;
257
+ radios.push(e);
276
258
  }
277
259
  if (!map) {
278
260
  return elements;
279
261
  }
280
262
  const placeholder = /* @__PURE__ */ new Set();
281
- for (const value of map.values()) {
282
- for (const v of value.values()) {
283
- for (const vv of v.values()) {
284
- const radio = vv.find((element) => element.checked) ?? vv[0];
263
+ for (const v of map.values()) {
264
+ for (const w of v.values()) {
265
+ for (const x of w.values()) {
266
+ const radio = x.find((r) => r.checked) ?? x[0];
285
267
  radio && placeholder.add(radio);
286
268
  }
287
269
  }
288
270
  }
289
- return elements.filter((element) => {
290
- if (!(element instanceof HTMLInputElement)) {
271
+ return elements.filter((e) => {
272
+ if (!(e instanceof HTMLInputElement)) {
291
273
  return true;
292
274
  }
293
- return !isUngroupedRadio(element) || placeholder.has(element);
275
+ return !isUngroupedRadio(e) || placeholder.has(e);
294
276
  });
295
277
  }
296
278
  function sortByTabIndex(elements) {
297
279
  const ordered = [];
298
280
  const natural = [];
299
- for (let i = 0, l = elements.length; i < l; i++) {
300
- const element = elements[i];
301
- if (element) {
302
- const target = getTabIndex(element) > 0 ? ordered : natural;
303
- target[target.length] = element;
304
- }
305
- }
306
- ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
307
- let count = 0;
308
- const sorted = new Array(ordered.length + natural.length);
309
- for (let i = 0, l = ordered.length; i < l; i++) {
310
- sorted[count++] = ordered[i];
281
+ for (const e of elements) {
282
+ (getTabIndex(e) > 0 ? ordered : natural).push(e);
311
283
  }
312
- for (let i = 0, l = natural.length; i < l; i++) {
313
- sorted[count++] = natural[i];
314
- }
315
- return sorted;
284
+ return ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b)).concat(natural);
316
285
  }
317
286
  function containsComposed(container, element) {
318
287
  let current = element;
@@ -327,7 +296,7 @@ function containsComposed(container, element) {
327
296
  }
328
297
  function getComposedChildren(node) {
329
298
  if (node instanceof ShadowRoot) {
330
- return getChildren(node);
299
+ return [...node.children];
331
300
  }
332
301
  if (!(node instanceof Element)) {
333
302
  return [];
@@ -339,9 +308,9 @@ function getComposedChildren(node) {
339
308
  }
340
309
  }
341
310
  if (node instanceof HTMLElement && node.shadowRoot?.mode === "open") {
342
- return getChildren(node.shadowRoot);
311
+ return [...node.shadowRoot.children];
343
312
  }
344
- return getChildren(node);
313
+ return [...node.children];
345
314
  }
346
315
  function getComposedParent(node) {
347
316
  if (node instanceof Element && node.assignedSlot) {
@@ -356,13 +325,9 @@ function getComposedSiblings(node) {
356
325
  if (!parent) {
357
326
  return [];
358
327
  }
359
- const siblings = parent instanceof HTMLSlotElement ? parent.assignedElements({ flatten: true }) : getComposedChildren(parent);
360
328
  const filtered = [];
361
- for (let i = 0, l = siblings.length; i < l; i++) {
362
- const sibling = siblings[i];
363
- if (sibling && sibling !== node) {
364
- filtered[filtered.length] = sibling;
365
- }
329
+ for (const s of (parent instanceof HTMLSlotElement ? parent.assignedElements({ flatten: true }) : getComposedChildren(parent)).filter((s2) => s2 !== node)) {
330
+ filtered.push(s);
366
331
  }
367
332
  return filtered;
368
333
  }
@@ -399,13 +364,6 @@ function getActiveElement() {
399
364
  }
400
365
  return current;
401
366
  }
402
- function getChildren(node) {
403
- const elements = [];
404
- for (let child = node.firstElementChild; child; child = child.nextElementSibling) {
405
- elements[elements.length] = child;
406
- }
407
- return elements;
408
- }
409
367
  function getTabIndex(element) {
410
368
  return "tabIndex" in element ? Number(element.tabIndex) : 0;
411
369
  }
@@ -482,7 +440,7 @@ function resolveOptions(options) {
482
440
  * High-precision focus management utility with full composed tree support.
483
441
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
484
442
  *
485
- * @version 4.3.29
443
+ * @version 4.3.31
486
444
  * @author Yusuke Kamiyamane
487
445
  * @license MIT
488
446
  * @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.29
6
+ * @version 4.3.31
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
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.29
6
+ * @version 4.3.31
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- var FOCUSABLE_SELECTOR = `:is(a[href], area[href], button, embed, iframe, input:not([type="hidden" i]), object, select, details > summary:first-of-type, textarea, [contenteditable]:not([contenteditable="false" i]), [controls], [tabindex]):not(:disabled, [hidden], [inert], [tabindex="-1"])`;
2
+ var FOCUSABLE_SELECTOR = ':is(a[href], area[href], button, embed, iframe, input:not([type="hidden" i]), object, select, details > summary:first-of-type, textarea, [contenteditable]:not([contenteditable="false" i]), [controls], [tabindex]):not(:disabled, [hidden], [inert], [tabindex="-1"])';
3
3
  var FOCUSABLE_SELECTOR_WITH_NEGATIVE_TABINDEX = FOCUSABLE_SELECTOR.replace(
4
4
  /(,\s*)?\[tabindex="-1"\]/g,
5
5
  ""
@@ -76,31 +76,21 @@ function getFocusables(container = document.body, options = {}) {
76
76
  skipNegativeTabIndexCheck,
77
77
  skipVisibilityCheck
78
78
  }) || include?.(node)) {
79
- candidates[candidates.length] = node;
80
- }
81
- const children2 = composed ? getComposedChildren(node) : getChildren(node);
82
- for (let i = 0, l = children2.length; i < l; i++) {
83
- const child = children2[i];
84
- child && traverse2(child);
79
+ candidates.push(node);
85
80
  }
81
+ (composed ? getComposedChildren(node) : [...node.children]).map(traverse2);
86
82
  };
87
- const children = composed ? getComposedChildren(container) : getChildren(container);
88
- for (let i = 0, l = children.length; i < l; i++) {
89
- const child = children[i];
90
- child && traverse2(child);
91
- }
83
+ (composed ? getComposedChildren(container) : [...container.children]).map(
84
+ traverse2
85
+ );
92
86
  } else {
93
- const matches = container.querySelectorAll(
87
+ for (const m of container.querySelectorAll(
94
88
  skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR_WITH_NEGATIVE_TABINDEX : FOCUSABLE_SELECTOR
95
- );
96
- for (let i = 0, l = matches.length; i < l; i++) {
97
- const matched = matches[i];
98
- if (matched && isFocusable(matched, {
89
+ )) {
90
+ m && isFocusable(m, {
99
91
  skipNegativeTabIndexCheck,
100
92
  skipVisibilityCheck
101
- })) {
102
- candidates[candidates.length] = matched;
103
- }
93
+ }) && candidates.push(m);
104
94
  }
105
95
  }
106
96
  return normalizeRadioGroup(
@@ -125,20 +115,13 @@ function inertOutside(element) {
125
115
  function traverse(node, fn) {
126
116
  const parent = getComposedParent(node);
127
117
  if (parent) {
128
- for (const sibling of getComposedSiblings(node)) {
129
- fn(sibling);
130
- }
118
+ getComposedSiblings(node).map(fn);
131
119
  traverse(parent, fn);
132
120
  }
133
121
  }
134
122
  const elements = [];
135
123
  traverse(element, (node) => node && applyInert(node) && elements.push(node));
136
- return () => {
137
- for (let i = 0, l = elements.length; i < l; i++) {
138
- const element2 = elements[i];
139
- element2 && restoreInert(element2);
140
- }
141
- };
124
+ return () => elements.map(restoreInert);
142
125
  }
143
126
  function isFocusable(element, options = {}) {
144
127
  if (!(element instanceof Element)) {
@@ -243,74 +226,60 @@ function isDisabledDeep(element) {
243
226
  }
244
227
  function normalizeRadioGroup(elements) {
245
228
  let map = null;
246
- for (let i = 0, l = elements.length; i < l; i++) {
247
- const element = elements[i];
248
- if (!(element instanceof HTMLInputElement)) {
229
+ for (const e of elements) {
230
+ if (!(e instanceof HTMLInputElement)) {
249
231
  continue;
250
232
  }
251
- if (!isUngroupedRadio(element)) {
233
+ if (!isUngroupedRadio(e)) {
252
234
  continue;
253
235
  }
254
236
  if (!map) {
255
237
  map = /* @__PURE__ */ new Map();
256
238
  }
257
- const root = element.getRootNode();
258
- let value = map.get(root);
259
- if (!value) {
260
- value = /* @__PURE__ */ new Map();
261
- map.set(root, value);
239
+ const root = e.getRootNode();
240
+ let groups = map.get(root);
241
+ if (!groups) {
242
+ groups = /* @__PURE__ */ new Map();
243
+ map.set(root, groups);
262
244
  }
263
- let v = value.get(element.form);
264
- if (!v) {
265
- v = /* @__PURE__ */ new Map();
266
- value.set(element.form, v);
245
+ let group = groups.get(e.form);
246
+ if (!group) {
247
+ group = /* @__PURE__ */ new Map();
248
+ groups.set(e.form, group);
267
249
  }
268
- let vv = v.get(element.name);
269
- if (!vv) {
270
- vv = [];
271
- v.set(element.name, vv);
250
+ let radios = group.get(e.name);
251
+ if (!radios) {
252
+ radios = [];
253
+ group.set(e.name, radios);
272
254
  }
273
- vv[vv.length] = element;
255
+ radios.push(e);
274
256
  }
275
257
  if (!map) {
276
258
  return elements;
277
259
  }
278
260
  const placeholder = /* @__PURE__ */ new Set();
279
- for (const value of map.values()) {
280
- for (const v of value.values()) {
281
- for (const vv of v.values()) {
282
- const radio = vv.find((element) => element.checked) ?? vv[0];
261
+ for (const v of map.values()) {
262
+ for (const w of v.values()) {
263
+ for (const x of w.values()) {
264
+ const radio = x.find((r) => r.checked) ?? x[0];
283
265
  radio && placeholder.add(radio);
284
266
  }
285
267
  }
286
268
  }
287
- return elements.filter((element) => {
288
- if (!(element instanceof HTMLInputElement)) {
269
+ return elements.filter((e) => {
270
+ if (!(e instanceof HTMLInputElement)) {
289
271
  return true;
290
272
  }
291
- return !isUngroupedRadio(element) || placeholder.has(element);
273
+ return !isUngroupedRadio(e) || placeholder.has(e);
292
274
  });
293
275
  }
294
276
  function sortByTabIndex(elements) {
295
277
  const ordered = [];
296
278
  const natural = [];
297
- for (let i = 0, l = elements.length; i < l; i++) {
298
- const element = elements[i];
299
- if (element) {
300
- const target = getTabIndex(element) > 0 ? ordered : natural;
301
- target[target.length] = element;
302
- }
303
- }
304
- ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b));
305
- let count = 0;
306
- const sorted = new Array(ordered.length + natural.length);
307
- for (let i = 0, l = ordered.length; i < l; i++) {
308
- sorted[count++] = ordered[i];
279
+ for (const e of elements) {
280
+ (getTabIndex(e) > 0 ? ordered : natural).push(e);
309
281
  }
310
- for (let i = 0, l = natural.length; i < l; i++) {
311
- sorted[count++] = natural[i];
312
- }
313
- return sorted;
282
+ return ordered.sort((a, b) => getTabIndex(a) - getTabIndex(b)).concat(natural);
314
283
  }
315
284
  function containsComposed(container, element) {
316
285
  let current = element;
@@ -325,7 +294,7 @@ function containsComposed(container, element) {
325
294
  }
326
295
  function getComposedChildren(node) {
327
296
  if (node instanceof ShadowRoot) {
328
- return getChildren(node);
297
+ return [...node.children];
329
298
  }
330
299
  if (!(node instanceof Element)) {
331
300
  return [];
@@ -337,9 +306,9 @@ function getComposedChildren(node) {
337
306
  }
338
307
  }
339
308
  if (node instanceof HTMLElement && node.shadowRoot?.mode === "open") {
340
- return getChildren(node.shadowRoot);
309
+ return [...node.shadowRoot.children];
341
310
  }
342
- return getChildren(node);
311
+ return [...node.children];
343
312
  }
344
313
  function getComposedParent(node) {
345
314
  if (node instanceof Element && node.assignedSlot) {
@@ -354,13 +323,9 @@ function getComposedSiblings(node) {
354
323
  if (!parent) {
355
324
  return [];
356
325
  }
357
- const siblings = parent instanceof HTMLSlotElement ? parent.assignedElements({ flatten: true }) : getComposedChildren(parent);
358
326
  const filtered = [];
359
- for (let i = 0, l = siblings.length; i < l; i++) {
360
- const sibling = siblings[i];
361
- if (sibling && sibling !== node) {
362
- filtered[filtered.length] = sibling;
363
- }
327
+ for (const s of (parent instanceof HTMLSlotElement ? parent.assignedElements({ flatten: true }) : getComposedChildren(parent)).filter((s2) => s2 !== node)) {
328
+ filtered.push(s);
364
329
  }
365
330
  return filtered;
366
331
  }
@@ -397,13 +362,6 @@ function getActiveElement() {
397
362
  }
398
363
  return current;
399
364
  }
400
- function getChildren(node) {
401
- const elements = [];
402
- for (let child = node.firstElementChild; child; child = child.nextElementSibling) {
403
- elements[elements.length] = child;
404
- }
405
- return elements;
406
- }
407
365
  function getTabIndex(element) {
408
366
  return "tabIndex" in element ? Number(element.tabIndex) : 0;
409
367
  }
@@ -480,7 +438,7 @@ function resolveOptions(options) {
480
438
  * High-precision focus management utility with full composed tree support.
481
439
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
482
440
  *
483
- * @version 4.3.29
441
+ * @version 4.3.31
484
442
  * @author Yusuke Kamiyamane
485
443
  * @license MIT
486
444
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "power-focusable",
3
- "version": "4.3.29",
3
+ "version": "4.3.31",
4
4
  "description": "High-precision focus management utility with full composed tree support",
5
5
  "keywords": [
6
6
  "a11y",