@y14e/disclosure 2.0.0 → 2.0.1

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
@@ -13,11 +13,11 @@ npm i @y14e/disclosure
13
13
  import Disclosure from '@y14e/disclosure';
14
14
 
15
15
  // CDNs
16
- import Disclosure from 'https://esm.sh/@y14e/disclosure@2.0.0';
16
+ import Disclosure from 'https://esm.sh/@y14e/disclosure@2.0.1';
17
17
  // or
18
- import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure@2.0.0/+esm';
18
+ import Disclosure from 'https://cdn.jsdelivr.net/npm/@y14e/disclosure@2.0.1/+esm';
19
19
  // or
20
- import Disclosure from 'https://esm.unpkg.com/@y14e/disclosure@2.0.0';
20
+ import Disclosure from 'https://esm.unpkg.com/@y14e/disclosure@2.0.1';
21
21
  ```
22
22
 
23
23
  ## Usage
@@ -36,9 +36,10 @@ new Disclosure(root, options);
36
36
  ```ts
37
37
  interface DisclosureOptions {
38
38
  animation?: {
39
- duration?: number; // ms (default: 300)
40
- easing?: string; // <easing-function> (default: 'ease')
39
+ duration?: number; // ms (default: 300)
40
+ easing?: string; // <easing-function> (default: 'ease')
41
41
  };
42
+ collapsible?: boolean; // default: true
42
43
  }
43
44
  ```
44
45
 
@@ -4,12 +4,12 @@
4
4
  var DEFAULT_PARSER = (value) => value.split(/\s+/);
5
5
  var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
6
6
  function addTokenToAttribute(element, attribute, token, options = {}) {
7
+ const value = element.getAttribute(attribute)?.trim();
7
8
  const {
8
9
  caseInsensitive = false,
9
10
  parse = DEFAULT_PARSER,
10
11
  serialize = DEFAULT_SERIALIZER
11
12
  } = options;
12
- const value = element.getAttribute(attribute)?.trim();
13
13
  const tokens = value ? parse(value).filter(Boolean) : [];
14
14
  if (caseInsensitive) {
15
15
  const lower = token.toLowerCase();
@@ -353,13 +353,7 @@ var RovingTabIndex = class _RovingTabIndex {
353
353
  console.warn("Invalid wrap option. Fallback: false.");
354
354
  wrap = false;
355
355
  }
356
- this.#settings = {
357
- navigationOnly,
358
- noMemory,
359
- noStart,
360
- typeahead,
361
- wrap
362
- };
356
+ this.#settings = { navigationOnly, noMemory, noStart, typeahead, wrap };
363
357
  direction && Object.assign(this.#settings, { direction });
364
358
  selector && Object.assign(this.#settings, { selector });
365
359
  this.#selectorFilter = this.#createSelectorFilter();
@@ -378,29 +372,32 @@ var RovingTabIndex = class _RovingTabIndex {
378
372
  }
379
373
  #initialize() {
380
374
  this.#update(getActiveElement());
381
- if (!(this.#container instanceof HTMLElement)) {
382
- return;
383
- }
384
375
  this.#controller = new AbortController();
385
376
  const { signal } = this.#controller;
386
- document.addEventListener("focusin", this.#onFocusIn, { signal });
387
- this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, { signal });
377
+ this.#container.addEventListener("focusin", this.#onFocusIn, { signal });
378
+ this.#settings.noMemory && this.#container.addEventListener("focusout", this.#onFocusOut, {
379
+ signal
380
+ });
388
381
  this.#container.addEventListener("keydown", this.#onKeyDown, { signal });
389
382
  }
390
383
  #onFocusIn = (event) => {
391
- const { target } = event;
392
- if (!(target instanceof Element)) {
384
+ if (!(event instanceof FocusEvent)) {
393
385
  return;
394
386
  }
395
- const isFocusable2 = this.#focusables.has(target);
396
- this.#settings.noMemory && !isFocusable2 ? this.#update() : isFocusable2 && this.#update(target);
387
+ const { target } = event;
388
+ target instanceof Element && this.#update(target);
397
389
  };
398
390
  #onFocusOut = (event) => {
399
- if (!event.relatedTarget) {
400
- this.#update();
391
+ if (!(event instanceof FocusEvent)) {
392
+ return;
401
393
  }
394
+ const target = event.relatedTarget;
395
+ (!(target instanceof Element) || !this.#focusables.has(target)) && this.#update();
402
396
  };
403
397
  #onKeyDown = (event) => {
398
+ if (!(event instanceof KeyboardEvent)) {
399
+ return;
400
+ }
404
401
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
405
402
  if (altKey || ctrlKey || metaKey || shiftKey) {
406
403
  return;
@@ -419,7 +416,7 @@ var RovingTabIndex = class _RovingTabIndex {
419
416
  }
420
417
  }
421
418
  const active = getActiveElement();
422
- if (!(active instanceof HTMLElement)) {
419
+ if (!(active instanceof Element)) {
423
420
  return;
424
421
  }
425
422
  const current = this.#getFocusables();
@@ -541,7 +538,8 @@ var Disclosure = class _Disclosure {
541
538
  animation: {
542
539
  duration: 300,
543
540
  easing: "ease"
544
- }
541
+ },
542
+ collapsible: true
545
543
  };
546
544
  #settings;
547
545
  #detailsElements;
@@ -723,17 +721,22 @@ var Disclosure = class _Disclosure {
723
721
  content.style.removeProperty(name2);
724
722
  });
725
723
  }
726
- #toggle(details, isExpand) {
724
+ #toggle(details, isExpand, isProgrammatic = false) {
727
725
  if (details.hasAttribute("data-disclosure-open") === isExpand) {
728
726
  return;
729
727
  }
728
+ if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#detailsElements.filter(
729
+ (details2) => details2.hasAttribute("data-disclosure-open")
730
+ ).length <= 1) {
731
+ return;
732
+ }
730
733
  const name = details.getAttribute("data-disclosure-name");
731
734
  if (name && isExpand) {
732
735
  details.removeAttribute("name");
733
736
  const expanded = this.#detailsElements.find(
734
737
  (d) => d.hasAttribute("data-disclosure-open") && d.getAttribute("data-disclosure-name") === name
735
738
  );
736
- expanded && this.#toggle(expanded, false);
739
+ expanded && this.#toggle(expanded, false, true);
737
740
  }
738
741
  const binding = this.#bindings.get(details);
739
742
  if (!binding) {
@@ -782,6 +785,8 @@ var Disclosure = class _Disclosure {
782
785
  }
783
786
  #mergeOptions(target, source) {
784
787
  return {
788
+ ...target,
789
+ ...source,
785
790
  animation: { ...target.animation, ...source.animation ?? {} }
786
791
  };
787
792
  }
@@ -804,7 +809,7 @@ function waitAnimationFinish(animation) {
804
809
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
805
810
  * Using the <details> and <summary> element.
806
811
  *
807
- * @version 2.0.0
812
+ * @version 2.0.1
808
813
  * @author Yusuke Kamiyamane
809
814
  * @license MIT
810
815
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -816,7 +821,7 @@ function waitAnimationFinish(animation) {
816
821
  (**
817
822
  * Attributes Utils
818
823
  *
819
- * @version 1.1.2
824
+ * @version 1.1.3
820
825
  * @author Yusuke Kamiyamane
821
826
  * @license MIT
822
827
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -829,7 +834,7 @@ power-focusable/dist/index.js:
829
834
  * High-precision focus management utility with full composed tree support.
830
835
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
831
836
  *
832
- * @version 4.3.5
837
+ * @version 4.3.8
833
838
  * @author Yusuke Kamiyamane
834
839
  * @license MIT
835
840
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -842,7 +847,7 @@ power-focusable/dist/index.js:
842
847
  * Lightweight roving tabindex utility with fully focus management.
843
848
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
844
849
  *
845
- * @version 3.1.2
850
+ * @version 3.1.6
846
851
  * @author Yusuke Kamiyamane
847
852
  * @license MIT
848
853
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -2,12 +2,12 @@
2
2
  var DEFAULT_PARSER = (value) => value.split(/\s+/);
3
3
  var DEFAULT_SERIALIZER = (tokens) => tokens.join(" ");
4
4
  function addTokenToAttribute(element, attribute, token, options = {}) {
5
+ const value = element.getAttribute(attribute)?.trim();
5
6
  const {
6
7
  caseInsensitive = false,
7
8
  parse = DEFAULT_PARSER,
8
9
  serialize = DEFAULT_SERIALIZER
9
10
  } = options;
10
- const value = element.getAttribute(attribute)?.trim();
11
11
  const tokens = value ? parse(value).filter(Boolean) : [];
12
12
  if (caseInsensitive) {
13
13
  const lower = token.toLowerCase();
@@ -351,13 +351,7 @@ var RovingTabIndex = class _RovingTabIndex {
351
351
  console.warn("Invalid wrap option. Fallback: false.");
352
352
  wrap = false;
353
353
  }
354
- this.#settings = {
355
- navigationOnly,
356
- noMemory,
357
- noStart,
358
- typeahead,
359
- wrap
360
- };
354
+ this.#settings = { navigationOnly, noMemory, noStart, typeahead, wrap };
361
355
  direction && Object.assign(this.#settings, { direction });
362
356
  selector && Object.assign(this.#settings, { selector });
363
357
  this.#selectorFilter = this.#createSelectorFilter();
@@ -376,29 +370,32 @@ var RovingTabIndex = class _RovingTabIndex {
376
370
  }
377
371
  #initialize() {
378
372
  this.#update(getActiveElement());
379
- if (!(this.#container instanceof HTMLElement)) {
380
- return;
381
- }
382
373
  this.#controller = new AbortController();
383
374
  const { signal } = this.#controller;
384
- document.addEventListener("focusin", this.#onFocusIn, { signal });
385
- this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, { signal });
375
+ this.#container.addEventListener("focusin", this.#onFocusIn, { signal });
376
+ this.#settings.noMemory && this.#container.addEventListener("focusout", this.#onFocusOut, {
377
+ signal
378
+ });
386
379
  this.#container.addEventListener("keydown", this.#onKeyDown, { signal });
387
380
  }
388
381
  #onFocusIn = (event) => {
389
- const { target } = event;
390
- if (!(target instanceof Element)) {
382
+ if (!(event instanceof FocusEvent)) {
391
383
  return;
392
384
  }
393
- const isFocusable2 = this.#focusables.has(target);
394
- this.#settings.noMemory && !isFocusable2 ? this.#update() : isFocusable2 && this.#update(target);
385
+ const { target } = event;
386
+ target instanceof Element && this.#update(target);
395
387
  };
396
388
  #onFocusOut = (event) => {
397
- if (!event.relatedTarget) {
398
- this.#update();
389
+ if (!(event instanceof FocusEvent)) {
390
+ return;
399
391
  }
392
+ const target = event.relatedTarget;
393
+ (!(target instanceof Element) || !this.#focusables.has(target)) && this.#update();
400
394
  };
401
395
  #onKeyDown = (event) => {
396
+ if (!(event instanceof KeyboardEvent)) {
397
+ return;
398
+ }
402
399
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
403
400
  if (altKey || ctrlKey || metaKey || shiftKey) {
404
401
  return;
@@ -417,7 +414,7 @@ var RovingTabIndex = class _RovingTabIndex {
417
414
  }
418
415
  }
419
416
  const active = getActiveElement();
420
- if (!(active instanceof HTMLElement)) {
417
+ if (!(active instanceof Element)) {
421
418
  return;
422
419
  }
423
420
  const current = this.#getFocusables();
@@ -539,7 +536,8 @@ var Disclosure = class _Disclosure {
539
536
  animation: {
540
537
  duration: 300,
541
538
  easing: "ease"
542
- }
539
+ },
540
+ collapsible: true
543
541
  };
544
542
  #settings;
545
543
  #detailsElements;
@@ -721,17 +719,22 @@ var Disclosure = class _Disclosure {
721
719
  content.style.removeProperty(name2);
722
720
  });
723
721
  }
724
- #toggle(details, isExpand) {
722
+ #toggle(details, isExpand, isProgrammatic = false) {
725
723
  if (details.hasAttribute("data-disclosure-open") === isExpand) {
726
724
  return;
727
725
  }
726
+ if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#detailsElements.filter(
727
+ (details2) => details2.hasAttribute("data-disclosure-open")
728
+ ).length <= 1) {
729
+ return;
730
+ }
728
731
  const name = details.getAttribute("data-disclosure-name");
729
732
  if (name && isExpand) {
730
733
  details.removeAttribute("name");
731
734
  const expanded = this.#detailsElements.find(
732
735
  (d) => d.hasAttribute("data-disclosure-open") && d.getAttribute("data-disclosure-name") === name
733
736
  );
734
- expanded && this.#toggle(expanded, false);
737
+ expanded && this.#toggle(expanded, false, true);
735
738
  }
736
739
  const binding = this.#bindings.get(details);
737
740
  if (!binding) {
@@ -780,6 +783,8 @@ var Disclosure = class _Disclosure {
780
783
  }
781
784
  #mergeOptions(target, source) {
782
785
  return {
786
+ ...target,
787
+ ...source,
783
788
  animation: { ...target.animation, ...source.animation ?? {} }
784
789
  };
785
790
  }
@@ -802,7 +807,7 @@ function waitAnimationFinish(animation) {
802
807
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
803
808
  * Using the <details> and <summary> element.
804
809
  *
805
- * @version 2.0.0
810
+ * @version 2.0.1
806
811
  * @author Yusuke Kamiyamane
807
812
  * @license MIT
808
813
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -814,7 +819,7 @@ function waitAnimationFinish(animation) {
814
819
  (**
815
820
  * Attributes Utils
816
821
  *
817
- * @version 1.1.2
822
+ * @version 1.1.3
818
823
  * @author Yusuke Kamiyamane
819
824
  * @license MIT
820
825
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -827,7 +832,7 @@ power-focusable/dist/index.js:
827
832
  * High-precision focus management utility with full composed tree support.
828
833
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
829
834
  *
830
- * @version 4.3.5
835
+ * @version 4.3.8
831
836
  * @author Yusuke Kamiyamane
832
837
  * @license MIT
833
838
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -840,7 +845,7 @@ power-focusable/dist/index.js:
840
845
  * Lightweight roving tabindex utility with fully focus management.
841
846
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
842
847
  *
843
- * @version 3.1.2
848
+ * @version 3.1.6
844
849
  * @author Yusuke Kamiyamane
845
850
  * @license MIT
846
851
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.cjs CHANGED
@@ -11,7 +11,8 @@ var Disclosure = class _Disclosure {
11
11
  animation: {
12
12
  duration: 300,
13
13
  easing: "ease"
14
- }
14
+ },
15
+ collapsible: true
15
16
  };
16
17
  #settings;
17
18
  #detailsElements;
@@ -193,17 +194,22 @@ var Disclosure = class _Disclosure {
193
194
  content.style.removeProperty(name2);
194
195
  });
195
196
  }
196
- #toggle(details, isExpand) {
197
+ #toggle(details, isExpand, isProgrammatic = false) {
197
198
  if (details.hasAttribute("data-disclosure-open") === isExpand) {
198
199
  return;
199
200
  }
201
+ if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#detailsElements.filter(
202
+ (details2) => details2.hasAttribute("data-disclosure-open")
203
+ ).length <= 1) {
204
+ return;
205
+ }
200
206
  const name = details.getAttribute("data-disclosure-name");
201
207
  if (name && isExpand) {
202
208
  details.removeAttribute("name");
203
209
  const expanded = this.#detailsElements.find(
204
210
  (d) => d.hasAttribute("data-disclosure-open") && d.getAttribute("data-disclosure-name") === name
205
211
  );
206
- expanded && this.#toggle(expanded, false);
212
+ expanded && this.#toggle(expanded, false, true);
207
213
  }
208
214
  const binding = this.#bindings.get(details);
209
215
  if (!binding) {
@@ -252,6 +258,8 @@ var Disclosure = class _Disclosure {
252
258
  }
253
259
  #mergeOptions(target, source) {
254
260
  return {
261
+ ...target,
262
+ ...source,
255
263
  animation: { ...target.animation, ...source.animation ?? {} }
256
264
  };
257
265
  }
@@ -274,7 +282,7 @@ function waitAnimationFinish(animation) {
274
282
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
275
283
  * Using the <details> and <summary> element.
276
284
  *
277
- * @version 2.0.0
285
+ * @version 2.0.1
278
286
  * @author Yusuke Kamiyamane
279
287
  * @license MIT
280
288
  * @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 2.0.0
6
+ * @version 2.0.1
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -14,6 +14,7 @@ interface DisclosureOptions {
14
14
  readonly duration?: number;
15
15
  readonly easing?: string;
16
16
  };
17
+ readonly collapsible?: boolean;
17
18
  }
18
19
  declare class Disclosure {
19
20
  #private;
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 2.0.0
6
+ * @version 2.0.1
7
7
  * @author Yusuke Kamiyamane
8
8
  * @license MIT
9
9
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -14,6 +14,7 @@ interface DisclosureOptions {
14
14
  readonly duration?: number;
15
15
  readonly easing?: string;
16
16
  };
17
+ readonly collapsible?: boolean;
17
18
  }
18
19
  declare class Disclosure {
19
20
  #private;
package/dist/index.js CHANGED
@@ -9,7 +9,8 @@ var Disclosure = class _Disclosure {
9
9
  animation: {
10
10
  duration: 300,
11
11
  easing: "ease"
12
- }
12
+ },
13
+ collapsible: true
13
14
  };
14
15
  #settings;
15
16
  #detailsElements;
@@ -191,17 +192,22 @@ var Disclosure = class _Disclosure {
191
192
  content.style.removeProperty(name2);
192
193
  });
193
194
  }
194
- #toggle(details, isExpand) {
195
+ #toggle(details, isExpand, isProgrammatic = false) {
195
196
  if (details.hasAttribute("data-disclosure-open") === isExpand) {
196
197
  return;
197
198
  }
199
+ if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#detailsElements.filter(
200
+ (details2) => details2.hasAttribute("data-disclosure-open")
201
+ ).length <= 1) {
202
+ return;
203
+ }
198
204
  const name = details.getAttribute("data-disclosure-name");
199
205
  if (name && isExpand) {
200
206
  details.removeAttribute("name");
201
207
  const expanded = this.#detailsElements.find(
202
208
  (d) => d.hasAttribute("data-disclosure-open") && d.getAttribute("data-disclosure-name") === name
203
209
  );
204
- expanded && this.#toggle(expanded, false);
210
+ expanded && this.#toggle(expanded, false, true);
205
211
  }
206
212
  const binding = this.#bindings.get(details);
207
213
  if (!binding) {
@@ -250,6 +256,8 @@ var Disclosure = class _Disclosure {
250
256
  }
251
257
  #mergeOptions(target, source) {
252
258
  return {
259
+ ...target,
260
+ ...source,
253
261
  animation: { ...target.animation, ...source.animation ?? {} }
254
262
  };
255
263
  }
@@ -272,7 +280,7 @@ function waitAnimationFinish(animation) {
272
280
  * WAI-ARIA compliant disclosure pattern implementation in TypeScript.
273
281
  * Using the <details> and <summary> element.
274
282
  *
275
- * @version 2.0.0
283
+ * @version 2.0.1
276
284
  * @author Yusuke Kamiyamane
277
285
  * @license MIT
278
286
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/disclosure",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "WAI-ARIA compliant disclosure pattern implementation in TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -52,7 +52,7 @@
52
52
  "node": ">=18"
53
53
  },
54
54
  "dependencies": {
55
- "@y14e/attributes-utils": "^1.1.2",
56
- "@y14e/roving-tabindex": "^3.1.2"
55
+ "@y14e/attributes-utils": "^1.1.3",
56
+ "@y14e/roving-tabindex": "^3.1.6"
57
57
  }
58
58
  }