@y14e/disclosure-css 1.3.1 → 1.3.2

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
@@ -10,14 +10,14 @@ npm i @y14e/disclosure-css
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import Disclosure from '@y14e/disclosure-css';
13
+ import Disclosure from '@y14e/disclosure-css@1.3.2';
14
14
 
15
15
  // CDNs
16
- import Disclosure from 'https://esm.sh/@y14e/disclosure-css'
16
+ import Disclosure from 'https://esm.sh/@y14e/disclosure-css@1.3.2';
17
17
  // or
18
- import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure-css/+esm';
18
+ import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure-css@1.3.2/+esm';
19
19
  // or
20
- import Disclosure from 'https://unpkg.com/@y14e/disclosure-css/dist/index.js';
20
+ import Disclosure from 'https://esm.unpkg.com/@y14e/disclosure-css@1.3.2';
21
21
  ```
22
22
 
23
23
  ## Usage
package/dist/index.cjs CHANGED
@@ -82,7 +82,13 @@ function getFocusables(container = document.body, options = {}) {
82
82
  console.warn("Invalid container element. Fallback: <body> element.");
83
83
  container = document.body;
84
84
  }
85
- let { composed = false, filter, include } = options;
85
+ let {
86
+ composed = false,
87
+ filter,
88
+ include,
89
+ skipNegativeTabIndexCheck = false,
90
+ skipVisibilityCheck = false
91
+ } = options;
86
92
  if (typeof composed !== "boolean") {
87
93
  console.warn("Invalid composed option. Fallback: false.");
88
94
  composed = false;
@@ -99,11 +105,22 @@ function getFocusables(container = document.body, options = {}) {
99
105
  );
100
106
  include = void 0;
101
107
  }
108
+ if (typeof skipNegativeTabIndexCheck !== "boolean") {
109
+ console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
110
+ skipNegativeTabIndexCheck = false;
111
+ }
112
+ if (typeof skipVisibilityCheck !== "boolean") {
113
+ console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
114
+ skipVisibilityCheck = false;
115
+ }
102
116
  const elements = [];
103
117
  if (composed || include) {
104
118
  let traverse2 = function(node) {
105
119
  if (node instanceof Element) {
106
- if (isFocusable(node) || include?.(node)) {
120
+ if (isFocusable(node, {
121
+ skipNegativeTabIndexCheck,
122
+ skipVisibilityCheck
123
+ }) || include?.(node)) {
107
124
  elements[elements.length] = node;
108
125
  }
109
126
  }
@@ -124,7 +141,10 @@ function getFocusables(container = document.body, options = {}) {
124
141
  if (!(candidate instanceof Element)) {
125
142
  continue;
126
143
  }
127
- if (isFocusable(candidate)) {
144
+ if (isFocusable(candidate, {
145
+ skipNegativeTabIndexCheck,
146
+ skipVisibilityCheck
147
+ })) {
128
148
  elements[elements.length] = candidate;
129
149
  }
130
150
  }
@@ -132,24 +152,35 @@ function getFocusables(container = document.body, options = {}) {
132
152
  const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
133
153
  return filter ? unfiltered.filter(filter) : unfiltered;
134
154
  }
135
- function isFocusable(element) {
155
+ function isFocusable(element, options = {}) {
136
156
  if (!(element instanceof Element)) {
137
157
  console.warn("Invalid element");
138
158
  return false;
139
159
  }
160
+ let { skipNegativeTabIndexCheck = false, skipVisibilityCheck = false } = options;
161
+ if (typeof skipNegativeTabIndexCheck !== "boolean") {
162
+ console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
163
+ skipNegativeTabIndexCheck = false;
164
+ }
165
+ if (typeof skipVisibilityCheck !== "boolean") {
166
+ console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
167
+ skipVisibilityCheck = false;
168
+ }
140
169
  if (element.hasAttribute("hidden") || isInert(element)) {
141
170
  return false;
142
171
  }
143
- if (getTabIndex(element) < 0) {
172
+ if (!skipNegativeTabIndexCheck && getTabIndex(element) < 0) {
144
173
  return false;
145
174
  }
146
- if (!element.matches(FOCUSABLE_SELECTOR)) {
175
+ if (!element.matches(
176
+ skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR.replace(/(,\s*)?\[tabindex="-1"\]/g, "") : FOCUSABLE_SELECTOR
177
+ )) {
147
178
  return false;
148
179
  }
149
180
  if (isDisabledDeep(element)) {
150
181
  return false;
151
182
  }
152
- if (!element.checkVisibility({
183
+ if (!skipVisibilityCheck && !element.checkVisibility({
153
184
  contentVisibilityAuto: true,
154
185
  opacityProperty: true,
155
186
  visibilityProperty: true
@@ -318,13 +349,14 @@ function createRovingTabIndex(container, options = {}) {
318
349
  console.warn("Invalid wrap option. Fallback: false.");
319
350
  wrap = false;
320
351
  }
321
- const roving = new RovingTabIndex(container, {
322
- direction,
323
- navigationOnly,
324
- selector,
325
- typeahead,
326
- wrap
327
- });
352
+ const settings = { navigationOnly, typeahead, wrap };
353
+ if (direction !== void 0) {
354
+ Object.assign(settings, { direction });
355
+ }
356
+ if (selector !== void 0) {
357
+ Object.assign(settings, { selector });
358
+ }
359
+ const roving = new RovingTabIndex(container, settings);
328
360
  return () => roving.destroy();
329
361
  }
330
362
  var RovingTabIndex = class {
@@ -366,8 +398,8 @@ var RovingTabIndex = class {
366
398
  if (!event.composedPath().includes(this.#container)) {
367
399
  return;
368
400
  }
369
- const { key, altKey, ctrlKey, metaKey } = event;
370
- if (altKey || ctrlKey || metaKey) {
401
+ const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
402
+ if (altKey || ctrlKey || metaKey || shiftKey) {
371
403
  return;
372
404
  }
373
405
  const { direction, typeahead, wrap } = this.#options;
@@ -430,13 +462,7 @@ var RovingTabIndex = class {
430
462
  focusElement(focusable);
431
463
  };
432
464
  #update(active) {
433
- const current = /* @__PURE__ */ new Set([
434
- ...this.#getFocusables(),
435
- ...getFocusables(this.#container, {
436
- composed: true,
437
- filter: this.#selectorFilter
438
- })
439
- ]);
465
+ const current = new Set(this.#getFocusables());
440
466
  for (const focusable of this.#focusables) {
441
467
  if (current.has(focusable)) {
442
468
  continue;
@@ -500,7 +526,7 @@ var RovingTabIndex = class {
500
526
  return getFocusables(this.#container, {
501
527
  composed: true,
502
528
  filter: this.#selectorFilter,
503
- include: (element) => this.#focusables.has(element)
529
+ skipNegativeTabIndexCheck: !this.#options.navigationOnly
504
530
  });
505
531
  }
506
532
  };
@@ -644,7 +670,7 @@ function isFocusable2(element) {
644
670
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
645
671
  * Using the <details> and <summary> element.
646
672
  *
647
- * @version 1.3.1
673
+ * @version 1.3.2
648
674
  * @author Yusuke Kamiyamane
649
675
  * @license MIT
650
676
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -656,7 +682,7 @@ function isFocusable2(element) {
656
682
  (**
657
683
  * Attributes Utils
658
684
  *
659
- * @version 1.0.5
685
+ * @version 1.1.0
660
686
  * @author Yusuke Kamiyamane
661
687
  * @license MIT
662
688
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -669,7 +695,7 @@ function isFocusable2(element) {
669
695
  * Lightweight roving tabindex utility with fully focus management.
670
696
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
671
697
  *
672
- * @version 1.3.0
698
+ * @version 2.0.3
673
699
  * @author Yusuke Kamiyamane
674
700
  * @license MIT
675
701
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -681,7 +707,7 @@ function isFocusable2(element) {
681
707
  (**
682
708
  * Attributes Utils
683
709
  *
684
- * @version 1.0.5
710
+ * @version 1.1.0
685
711
  * @author Yusuke Kamiyamane
686
712
  * @license MIT
687
713
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -694,7 +720,7 @@ function isFocusable2(element) {
694
720
  * High-precision focus management utility with full composed tree support.
695
721
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
696
722
  *
697
- * @version 4.1.8
723
+ * @version 4.3.1
698
724
  * @author Yusuke Kamiyamane
699
725
  * @license MIT
700
726
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -3,7 +3,7 @@
3
3
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
4
4
  * Using the <details> and <summary> element.
5
5
  *
6
- * @version 1.3.1
6
+ * @version 1.3.2
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
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
4
4
  * Using the <details> and <summary> element.
5
5
  *
6
- * @version 1.3.1
6
+ * @version 1.3.2
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.js CHANGED
@@ -80,7 +80,13 @@ function getFocusables(container = document.body, options = {}) {
80
80
  console.warn("Invalid container element. Fallback: <body> element.");
81
81
  container = document.body;
82
82
  }
83
- let { composed = false, filter, include } = options;
83
+ let {
84
+ composed = false,
85
+ filter,
86
+ include,
87
+ skipNegativeTabIndexCheck = false,
88
+ skipVisibilityCheck = false
89
+ } = options;
84
90
  if (typeof composed !== "boolean") {
85
91
  console.warn("Invalid composed option. Fallback: false.");
86
92
  composed = false;
@@ -97,11 +103,22 @@ function getFocusables(container = document.body, options = {}) {
97
103
  );
98
104
  include = void 0;
99
105
  }
106
+ if (typeof skipNegativeTabIndexCheck !== "boolean") {
107
+ console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
108
+ skipNegativeTabIndexCheck = false;
109
+ }
110
+ if (typeof skipVisibilityCheck !== "boolean") {
111
+ console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
112
+ skipVisibilityCheck = false;
113
+ }
100
114
  const elements = [];
101
115
  if (composed || include) {
102
116
  let traverse2 = function(node) {
103
117
  if (node instanceof Element) {
104
- if (isFocusable(node) || include?.(node)) {
118
+ if (isFocusable(node, {
119
+ skipNegativeTabIndexCheck,
120
+ skipVisibilityCheck
121
+ }) || include?.(node)) {
105
122
  elements[elements.length] = node;
106
123
  }
107
124
  }
@@ -122,7 +139,10 @@ function getFocusables(container = document.body, options = {}) {
122
139
  if (!(candidate instanceof Element)) {
123
140
  continue;
124
141
  }
125
- if (isFocusable(candidate)) {
142
+ if (isFocusable(candidate, {
143
+ skipNegativeTabIndexCheck,
144
+ skipVisibilityCheck
145
+ })) {
126
146
  elements[elements.length] = candidate;
127
147
  }
128
148
  }
@@ -130,24 +150,35 @@ function getFocusables(container = document.body, options = {}) {
130
150
  const unfiltered = normalizeRadioGroup(sortByTabIndex(elements));
131
151
  return filter ? unfiltered.filter(filter) : unfiltered;
132
152
  }
133
- function isFocusable(element) {
153
+ function isFocusable(element, options = {}) {
134
154
  if (!(element instanceof Element)) {
135
155
  console.warn("Invalid element");
136
156
  return false;
137
157
  }
158
+ let { skipNegativeTabIndexCheck = false, skipVisibilityCheck = false } = options;
159
+ if (typeof skipNegativeTabIndexCheck !== "boolean") {
160
+ console.warn("Invalid skipNegativeTabIndexCheck option. Fallback: false.");
161
+ skipNegativeTabIndexCheck = false;
162
+ }
163
+ if (typeof skipVisibilityCheck !== "boolean") {
164
+ console.warn("Invalid skipVisibilityCheck option. Fallback: false.");
165
+ skipVisibilityCheck = false;
166
+ }
138
167
  if (element.hasAttribute("hidden") || isInert(element)) {
139
168
  return false;
140
169
  }
141
- if (getTabIndex(element) < 0) {
170
+ if (!skipNegativeTabIndexCheck && getTabIndex(element) < 0) {
142
171
  return false;
143
172
  }
144
- if (!element.matches(FOCUSABLE_SELECTOR)) {
173
+ if (!element.matches(
174
+ skipNegativeTabIndexCheck ? FOCUSABLE_SELECTOR.replace(/(,\s*)?\[tabindex="-1"\]/g, "") : FOCUSABLE_SELECTOR
175
+ )) {
145
176
  return false;
146
177
  }
147
178
  if (isDisabledDeep(element)) {
148
179
  return false;
149
180
  }
150
- if (!element.checkVisibility({
181
+ if (!skipVisibilityCheck && !element.checkVisibility({
151
182
  contentVisibilityAuto: true,
152
183
  opacityProperty: true,
153
184
  visibilityProperty: true
@@ -316,13 +347,14 @@ function createRovingTabIndex(container, options = {}) {
316
347
  console.warn("Invalid wrap option. Fallback: false.");
317
348
  wrap = false;
318
349
  }
319
- const roving = new RovingTabIndex(container, {
320
- direction,
321
- navigationOnly,
322
- selector,
323
- typeahead,
324
- wrap
325
- });
350
+ const settings = { navigationOnly, typeahead, wrap };
351
+ if (direction !== void 0) {
352
+ Object.assign(settings, { direction });
353
+ }
354
+ if (selector !== void 0) {
355
+ Object.assign(settings, { selector });
356
+ }
357
+ const roving = new RovingTabIndex(container, settings);
326
358
  return () => roving.destroy();
327
359
  }
328
360
  var RovingTabIndex = class {
@@ -364,8 +396,8 @@ var RovingTabIndex = class {
364
396
  if (!event.composedPath().includes(this.#container)) {
365
397
  return;
366
398
  }
367
- const { key, altKey, ctrlKey, metaKey } = event;
368
- if (altKey || ctrlKey || metaKey) {
399
+ const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
400
+ if (altKey || ctrlKey || metaKey || shiftKey) {
369
401
  return;
370
402
  }
371
403
  const { direction, typeahead, wrap } = this.#options;
@@ -428,13 +460,7 @@ var RovingTabIndex = class {
428
460
  focusElement(focusable);
429
461
  };
430
462
  #update(active) {
431
- const current = /* @__PURE__ */ new Set([
432
- ...this.#getFocusables(),
433
- ...getFocusables(this.#container, {
434
- composed: true,
435
- filter: this.#selectorFilter
436
- })
437
- ]);
463
+ const current = new Set(this.#getFocusables());
438
464
  for (const focusable of this.#focusables) {
439
465
  if (current.has(focusable)) {
440
466
  continue;
@@ -498,7 +524,7 @@ var RovingTabIndex = class {
498
524
  return getFocusables(this.#container, {
499
525
  composed: true,
500
526
  filter: this.#selectorFilter,
501
- include: (element) => this.#focusables.has(element)
527
+ skipNegativeTabIndexCheck: !this.#options.navigationOnly
502
528
  });
503
529
  }
504
530
  };
@@ -642,7 +668,7 @@ function isFocusable2(element) {
642
668
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
643
669
  * Using the <details> and <summary> element.
644
670
  *
645
- * @version 1.3.1
671
+ * @version 1.3.2
646
672
  * @author Yusuke Kamiyamane
647
673
  * @license MIT
648
674
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -654,7 +680,7 @@ function isFocusable2(element) {
654
680
  (**
655
681
  * Attributes Utils
656
682
  *
657
- * @version 1.0.5
683
+ * @version 1.1.0
658
684
  * @author Yusuke Kamiyamane
659
685
  * @license MIT
660
686
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -667,7 +693,7 @@ function isFocusable2(element) {
667
693
  * Lightweight roving tabindex utility with fully focus management.
668
694
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
669
695
  *
670
- * @version 1.3.0
696
+ * @version 2.0.3
671
697
  * @author Yusuke Kamiyamane
672
698
  * @license MIT
673
699
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -679,7 +705,7 @@ function isFocusable2(element) {
679
705
  (**
680
706
  * Attributes Utils
681
707
  *
682
- * @version 1.0.5
708
+ * @version 1.1.0
683
709
  * @author Yusuke Kamiyamane
684
710
  * @license MIT
685
711
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -692,7 +718,7 @@ function isFocusable2(element) {
692
718
  * High-precision focus management utility with full composed tree support.
693
719
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
694
720
  *
695
- * @version 4.1.8
721
+ * @version 4.3.1
696
722
  * @author Yusuke Kamiyamane
697
723
  * @license MIT
698
724
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/disclosure-css",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "WAI-ARIA compliant disclosure pattern implementation in TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -43,8 +43,8 @@
43
43
  },
44
44
  "homepage": "https://github.com/y14e/disclosure-css#readme",
45
45
  "devDependencies": {
46
- "@y14e/attributes-utils": "^1.0.5",
47
- "@y14e/roving-tabindex": "^1.3.0",
46
+ "@y14e/attributes-utils": "^1.1.0",
47
+ "@y14e/roving-tabindex": "^2.0.3",
48
48
  "bun-types": "latest",
49
49
  "tsup": "^8.0.0",
50
50
  "typescript": "^5.6.0"