@y14e/disclosure 1.3.2 → 1.3.4

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
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import Disclosure from '@y14e/disclosure';
13
+ import Disclosure from '@y14e/disclosure@1.3.3';
14
14
 
15
15
  // CDNs
16
- import Disclosure from 'https://esm.sh/@y14e/disclosure'
16
+ import Disclosure from 'https://esm.sh/@y14e/disclosure@1.3.3';
17
17
  // or
18
- import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure/+esm';
18
+ import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure@1.3.3/+esm';
19
19
  // or
20
- import Disclosure from 'https://unpkg.com/@y14e/disclosure/dist/index.js';
20
+ import Disclosure from 'https://esm.unpkg.com/@y14e/disclosure@1.3.3';
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
  };
@@ -665,7 +691,7 @@ var Disclosure = class _Disclosure {
665
691
  this.#cleanupRovingTabIndex = createRovingTabIndex(this.#rootElement, {
666
692
  direction: "vertical",
667
693
  navigationOnly: true,
668
- selector: `summary:not(:scope summary + * *)`,
694
+ selector: "summary:not(:scope summary + * *)",
669
695
  wrap: true
670
696
  });
671
697
  this.#rootElement.setAttribute("data-disclosure-initialized", "");
@@ -788,7 +814,7 @@ function waitAnimationFinish(animation) {
788
814
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
789
815
  * Using the <details> and <summary> element.
790
816
  *
791
- * @version 1.3.2
817
+ * @version 1.3.4
792
818
  * @author Yusuke Kamiyamane
793
819
  * @license MIT
794
820
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -800,7 +826,7 @@ function waitAnimationFinish(animation) {
800
826
  (**
801
827
  * Attributes Utils
802
828
  *
803
- * @version 1.0.5
829
+ * @version 1.1.0
804
830
  * @author Yusuke Kamiyamane
805
831
  * @license MIT
806
832
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -813,7 +839,7 @@ function waitAnimationFinish(animation) {
813
839
  * Lightweight roving tabindex utility with fully focus management.
814
840
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
815
841
  *
816
- * @version 1.3.0
842
+ * @version 2.0.3
817
843
  * @author Yusuke Kamiyamane
818
844
  * @license MIT
819
845
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -825,7 +851,7 @@ function waitAnimationFinish(animation) {
825
851
  (**
826
852
  * Attributes Utils
827
853
  *
828
- * @version 1.0.5
854
+ * @version 1.1.0
829
855
  * @author Yusuke Kamiyamane
830
856
  * @license MIT
831
857
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -838,7 +864,7 @@ function waitAnimationFinish(animation) {
838
864
  * High-precision focus management utility with full composed tree support.
839
865
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
840
866
  *
841
- * @version 4.1.8
867
+ * @version 4.3.1
842
868
  * @author Yusuke Kamiyamane
843
869
  * @license MIT
844
870
  * @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.2
6
+ * @version 1.3.4
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.2
6
+ * @version 1.3.4
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
  };
@@ -663,7 +689,7 @@ var Disclosure = class _Disclosure {
663
689
  this.#cleanupRovingTabIndex = createRovingTabIndex(this.#rootElement, {
664
690
  direction: "vertical",
665
691
  navigationOnly: true,
666
- selector: `summary:not(:scope summary + * *)`,
692
+ selector: "summary:not(:scope summary + * *)",
667
693
  wrap: true
668
694
  });
669
695
  this.#rootElement.setAttribute("data-disclosure-initialized", "");
@@ -786,7 +812,7 @@ function waitAnimationFinish(animation) {
786
812
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
787
813
  * Using the <details> and <summary> element.
788
814
  *
789
- * @version 1.3.2
815
+ * @version 1.3.4
790
816
  * @author Yusuke Kamiyamane
791
817
  * @license MIT
792
818
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -798,7 +824,7 @@ function waitAnimationFinish(animation) {
798
824
  (**
799
825
  * Attributes Utils
800
826
  *
801
- * @version 1.0.5
827
+ * @version 1.1.0
802
828
  * @author Yusuke Kamiyamane
803
829
  * @license MIT
804
830
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -811,7 +837,7 @@ function waitAnimationFinish(animation) {
811
837
  * Lightweight roving tabindex utility with fully focus management.
812
838
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
813
839
  *
814
- * @version 1.3.0
840
+ * @version 2.0.3
815
841
  * @author Yusuke Kamiyamane
816
842
  * @license MIT
817
843
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -823,7 +849,7 @@ function waitAnimationFinish(animation) {
823
849
  (**
824
850
  * Attributes Utils
825
851
  *
826
- * @version 1.0.5
852
+ * @version 1.1.0
827
853
  * @author Yusuke Kamiyamane
828
854
  * @license MIT
829
855
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -836,7 +862,7 @@ function waitAnimationFinish(animation) {
836
862
  * High-precision focus management utility with full composed tree support.
837
863
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
838
864
  *
839
- * @version 4.1.8
865
+ * @version 4.3.1
840
866
  * @author Yusuke Kamiyamane
841
867
  * @license MIT
842
868
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/disclosure",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
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#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",