@y14e/accordion 1.4.18 → 2.0.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
@@ -13,11 +13,11 @@ npm i @y14e/accordion
13
13
  import Accordion from '@y14e/accordion';
14
14
 
15
15
  // CDNs
16
- import Accordion from 'https://esm.sh/@y14e/accordion@1.4.18';
16
+ import Accordion from 'https://esm.sh/@y14e/accordion@2.0.2';
17
17
  // or
18
- import Accordion from 'https://cdn.jsdelivr.net/npm/@y14e/accordion@1.4.18/+esm';
18
+ import Accordion from 'https://cdn.jsdelivr.net/npm/@y14e/accordion@2.0.2/+esm';
19
19
  // or
20
- import Accordion from 'https://esm.unpkg.com/@y14e/accordion@1.4.18';
20
+ import Accordion from 'https://esm.unpkg.com/@y14e/accordion@2.0.2';
21
21
  ```
22
22
 
23
23
  ## Usage
@@ -36,12 +36,13 @@ new Accordion(root, options);
36
36
  ```ts
37
37
  interface AccordionOptions {
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
  selector?: {
43
- content?: string; // default: ':has(> [data-accordion-trigger]) + *'
44
- trigger?: string; // default: '[data-accordion-trigger]'
44
+ content?: string; // default: '[data-accordion-content]'
45
+ trigger?: string; // default: '[data-accordion-trigger]'
45
46
  };
46
47
  }
47
48
  ```
@@ -68,10 +69,10 @@ new Accordion(root);
68
69
 
69
70
  ## 📦 APIs
70
71
 
71
- ### `close`
72
+ ### `collapse`
72
73
 
73
74
  ```ts
74
- accordion.close(trigger);
75
+ accordion.collapse(trigger);
75
76
  // => void
76
77
  //
77
78
  // trigger: HTMLElement
@@ -88,10 +89,10 @@ accordion.destroy(force);
88
89
  // force (optional): If true, skips waiting for animations to finish.
89
90
  ```
90
91
 
91
- ### `open`
92
+ ### `expand`
92
93
 
93
94
  ```ts
94
- accordion.open(trigger);
95
+ accordion.expand(trigger);
95
96
  // => void
96
97
  //
97
98
  // trigger: HTMLElement
@@ -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();
@@ -402,13 +402,7 @@ var RovingTabIndex = class _RovingTabIndex {
402
402
  console.warn("Invalid wrap option. Fallback: false.");
403
403
  wrap = false;
404
404
  }
405
- this.#settings = {
406
- navigationOnly,
407
- noMemory,
408
- noStart,
409
- typeahead,
410
- wrap
411
- };
405
+ this.#settings = { navigationOnly, noMemory, noStart, typeahead, wrap };
412
406
  direction && Object.assign(this.#settings, { direction });
413
407
  selector && Object.assign(this.#settings, { selector });
414
408
  this.#selectorFilter = this.#createSelectorFilter();
@@ -427,29 +421,32 @@ var RovingTabIndex = class _RovingTabIndex {
427
421
  }
428
422
  #initialize() {
429
423
  this.#update(getActiveElement());
430
- if (!(this.#container instanceof HTMLElement)) {
431
- return;
432
- }
433
424
  this.#controller = new AbortController();
434
425
  const { signal } = this.#controller;
435
- document.addEventListener("focusin", this.#onFocusIn, { signal });
436
- this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, { signal });
426
+ this.#container.addEventListener("focusin", this.#onFocusIn, { signal });
427
+ this.#settings.noMemory && this.#container.addEventListener("focusout", this.#onFocusOut, {
428
+ signal
429
+ });
437
430
  this.#container.addEventListener("keydown", this.#onKeyDown, { signal });
438
431
  }
439
432
  #onFocusIn = (event) => {
440
- const { target } = event;
441
- if (!(target instanceof Element)) {
433
+ if (!(event instanceof FocusEvent)) {
442
434
  return;
443
435
  }
444
- const isFocusable2 = this.#focusables.has(target);
445
- this.#settings.noMemory && !isFocusable2 ? this.#update() : isFocusable2 && this.#update(target);
436
+ const { target } = event;
437
+ target instanceof Element && this.#update(target);
446
438
  };
447
439
  #onFocusOut = (event) => {
448
- if (!event.relatedTarget) {
449
- this.#update();
440
+ if (!(event instanceof FocusEvent)) {
441
+ return;
450
442
  }
443
+ const target = event.relatedTarget;
444
+ (!(target instanceof Element) || !this.#focusables.has(target)) && this.#update();
451
445
  };
452
446
  #onKeyDown = (event) => {
447
+ if (!(event instanceof KeyboardEvent)) {
448
+ return;
449
+ }
453
450
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
454
451
  if (altKey || ctrlKey || metaKey || shiftKey) {
455
452
  return;
@@ -468,7 +465,7 @@ var RovingTabIndex = class _RovingTabIndex {
468
465
  }
469
466
  }
470
467
  const active = getActiveElement();
471
- if (!(active instanceof HTMLElement)) {
468
+ if (!(active instanceof Element)) {
472
469
  return;
473
470
  }
474
471
  const current = this.#getFocusables();
@@ -588,8 +585,9 @@ var Accordion = class _Accordion {
588
585
  #rootElement;
589
586
  #defaults = {
590
587
  animation: { duration: 300, easing: "ease" },
588
+ collapsible: true,
591
589
  selector: {
592
- content: ":has(> [data-accordion-trigger]) + *",
590
+ content: "[data-accordion-content]",
593
591
  trigger: "[data-accordion-trigger]"
594
592
  }
595
593
  };
@@ -645,7 +643,7 @@ var Accordion = class _Accordion {
645
643
  });
646
644
  this.#initialize();
647
645
  }
648
- close(trigger) {
646
+ collapse(trigger) {
649
647
  if (this.#isDestroyed) {
650
648
  return;
651
649
  }
@@ -680,7 +678,7 @@ var Accordion = class _Accordion {
680
678
  this.#contentElements.length = 0;
681
679
  this.#rootElement.removeAttribute("data-accordion-initialized");
682
680
  }
683
- open(trigger) {
681
+ expand(trigger) {
684
682
  if (this.#isDestroyed) {
685
683
  return;
686
684
  }
@@ -765,21 +763,24 @@ var Accordion = class _Accordion {
765
763
  }
766
764
  binding.trigger.ariaExpanded === "false" && this.#toggle(binding.trigger, true, true);
767
765
  };
768
- #toggle(trigger, isOpen, isMatch = false) {
769
- if (trigger.ariaExpanded === String(isOpen)) {
766
+ #toggle(trigger, isExpand, isMatch = false, isProgrammatic = false) {
767
+ if (trigger.ariaExpanded === String(isExpand)) {
768
+ return;
769
+ }
770
+ if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#triggerElements.filter((trigger2) => trigger2.ariaExpanded === "true").length <= 1) {
770
771
  return;
771
772
  }
772
773
  const name = trigger.getAttribute("data-accordion-name");
773
- if (name && isOpen) {
774
- const open = this.#triggerElements.find(
774
+ if (name && isExpand) {
775
+ const expanded = this.#triggerElements.find(
775
776
  (t) => t !== trigger && t.getAttribute("data-accordion-name") === name && t.ariaExpanded === "true"
776
777
  );
777
- open && this.#toggle(open, false, isMatch);
778
+ expanded && this.#toggle(expanded, false, isMatch, true);
778
779
  }
779
780
  trigger.setAttribute(
780
781
  "aria-label",
781
782
  trigger.getAttribute(
782
- `data-accordion-${isOpen ? "expanded" : "collapsed"}-label`
783
+ `data-accordion-${isExpand ? "expanded" : "collapsed"}-label`
783
784
  ) ?? trigger.ariaLabel ?? ""
784
785
  );
785
786
  const binding = this.#bindings.get(trigger);
@@ -791,7 +792,7 @@ var Accordion = class _Accordion {
791
792
  if (content.hidden) {
792
793
  content.hidden = false;
793
794
  }
794
- const endSize = isOpen ? content.scrollHeight : 0;
795
+ const endSize = isExpand ? content.scrollHeight : 0;
795
796
  binding.animation?.cancel();
796
797
  content.style.setProperty("overflow", "clip");
797
798
  const { duration, easing } = this.#settings.animation;
@@ -800,7 +801,7 @@ var Accordion = class _Accordion {
800
801
  { duration: isMatch ? 0 : duration, easing }
801
802
  );
802
803
  binding.animation = animation;
803
- trigger.setAttribute("aria-expanded", String(isOpen));
804
+ trigger.setAttribute("aria-expanded", String(isExpand));
804
805
  function cleanup() {
805
806
  if (binding?.animation === animation) {
806
807
  binding.animation = null;
@@ -828,6 +829,8 @@ var Accordion = class _Accordion {
828
829
  }
829
830
  #mergeOptions(target, source) {
830
831
  return {
832
+ ...target,
833
+ ...source,
831
834
  animation: { ...target.animation, ...source.animation ?? {} },
832
835
  selector: { ...target.selector, ...source.selector ?? {} }
833
836
  };
@@ -850,7 +853,7 @@ function waitAnimationFinish(animation) {
850
853
  * Accordion
851
854
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
852
855
  *
853
- * @version 1.4.18
856
+ * @version 2.0.2
854
857
  * @author Yusuke Kamiyamane
855
858
  * @license MIT
856
859
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -862,7 +865,7 @@ function waitAnimationFinish(animation) {
862
865
  (**
863
866
  * Attributes Utils
864
867
  *
865
- * @version 1.1.2
868
+ * @version 1.1.3
866
869
  * @author Yusuke Kamiyamane
867
870
  * @license MIT
868
871
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -875,7 +878,7 @@ power-focusable/dist/index.js:
875
878
  * High-precision focus management utility with full composed tree support.
876
879
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
877
880
  *
878
- * @version 4.3.5
881
+ * @version 4.3.8
879
882
  * @author Yusuke Kamiyamane
880
883
  * @license MIT
881
884
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -899,7 +902,7 @@ power-focusable/dist/index.js:
899
902
  * Lightweight roving tabindex utility with fully focus management.
900
903
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
901
904
  *
902
- * @version 3.1.2
905
+ * @version 3.1.6
903
906
  * @author Yusuke Kamiyamane
904
907
  * @license MIT
905
908
  * @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();
@@ -400,13 +400,7 @@ var RovingTabIndex = class _RovingTabIndex {
400
400
  console.warn("Invalid wrap option. Fallback: false.");
401
401
  wrap = false;
402
402
  }
403
- this.#settings = {
404
- navigationOnly,
405
- noMemory,
406
- noStart,
407
- typeahead,
408
- wrap
409
- };
403
+ this.#settings = { navigationOnly, noMemory, noStart, typeahead, wrap };
410
404
  direction && Object.assign(this.#settings, { direction });
411
405
  selector && Object.assign(this.#settings, { selector });
412
406
  this.#selectorFilter = this.#createSelectorFilter();
@@ -425,29 +419,32 @@ var RovingTabIndex = class _RovingTabIndex {
425
419
  }
426
420
  #initialize() {
427
421
  this.#update(getActiveElement());
428
- if (!(this.#container instanceof HTMLElement)) {
429
- return;
430
- }
431
422
  this.#controller = new AbortController();
432
423
  const { signal } = this.#controller;
433
- document.addEventListener("focusin", this.#onFocusIn, { signal });
434
- this.#settings.noMemory && document.addEventListener("focusout", this.#onFocusOut, { signal });
424
+ this.#container.addEventListener("focusin", this.#onFocusIn, { signal });
425
+ this.#settings.noMemory && this.#container.addEventListener("focusout", this.#onFocusOut, {
426
+ signal
427
+ });
435
428
  this.#container.addEventListener("keydown", this.#onKeyDown, { signal });
436
429
  }
437
430
  #onFocusIn = (event) => {
438
- const { target } = event;
439
- if (!(target instanceof Element)) {
431
+ if (!(event instanceof FocusEvent)) {
440
432
  return;
441
433
  }
442
- const isFocusable2 = this.#focusables.has(target);
443
- this.#settings.noMemory && !isFocusable2 ? this.#update() : isFocusable2 && this.#update(target);
434
+ const { target } = event;
435
+ target instanceof Element && this.#update(target);
444
436
  };
445
437
  #onFocusOut = (event) => {
446
- if (!event.relatedTarget) {
447
- this.#update();
438
+ if (!(event instanceof FocusEvent)) {
439
+ return;
448
440
  }
441
+ const target = event.relatedTarget;
442
+ (!(target instanceof Element) || !this.#focusables.has(target)) && this.#update();
449
443
  };
450
444
  #onKeyDown = (event) => {
445
+ if (!(event instanceof KeyboardEvent)) {
446
+ return;
447
+ }
451
448
  const { key, altKey, ctrlKey, metaKey, shiftKey } = event;
452
449
  if (altKey || ctrlKey || metaKey || shiftKey) {
453
450
  return;
@@ -466,7 +463,7 @@ var RovingTabIndex = class _RovingTabIndex {
466
463
  }
467
464
  }
468
465
  const active = getActiveElement();
469
- if (!(active instanceof HTMLElement)) {
466
+ if (!(active instanceof Element)) {
470
467
  return;
471
468
  }
472
469
  const current = this.#getFocusables();
@@ -586,8 +583,9 @@ var Accordion = class _Accordion {
586
583
  #rootElement;
587
584
  #defaults = {
588
585
  animation: { duration: 300, easing: "ease" },
586
+ collapsible: true,
589
587
  selector: {
590
- content: ":has(> [data-accordion-trigger]) + *",
588
+ content: "[data-accordion-content]",
591
589
  trigger: "[data-accordion-trigger]"
592
590
  }
593
591
  };
@@ -643,7 +641,7 @@ var Accordion = class _Accordion {
643
641
  });
644
642
  this.#initialize();
645
643
  }
646
- close(trigger) {
644
+ collapse(trigger) {
647
645
  if (this.#isDestroyed) {
648
646
  return;
649
647
  }
@@ -678,7 +676,7 @@ var Accordion = class _Accordion {
678
676
  this.#contentElements.length = 0;
679
677
  this.#rootElement.removeAttribute("data-accordion-initialized");
680
678
  }
681
- open(trigger) {
679
+ expand(trigger) {
682
680
  if (this.#isDestroyed) {
683
681
  return;
684
682
  }
@@ -763,21 +761,24 @@ var Accordion = class _Accordion {
763
761
  }
764
762
  binding.trigger.ariaExpanded === "false" && this.#toggle(binding.trigger, true, true);
765
763
  };
766
- #toggle(trigger, isOpen, isMatch = false) {
767
- if (trigger.ariaExpanded === String(isOpen)) {
764
+ #toggle(trigger, isExpand, isMatch = false, isProgrammatic = false) {
765
+ if (trigger.ariaExpanded === String(isExpand)) {
766
+ return;
767
+ }
768
+ if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#triggerElements.filter((trigger2) => trigger2.ariaExpanded === "true").length <= 1) {
768
769
  return;
769
770
  }
770
771
  const name = trigger.getAttribute("data-accordion-name");
771
- if (name && isOpen) {
772
- const open = this.#triggerElements.find(
772
+ if (name && isExpand) {
773
+ const expanded = this.#triggerElements.find(
773
774
  (t) => t !== trigger && t.getAttribute("data-accordion-name") === name && t.ariaExpanded === "true"
774
775
  );
775
- open && this.#toggle(open, false, isMatch);
776
+ expanded && this.#toggle(expanded, false, isMatch, true);
776
777
  }
777
778
  trigger.setAttribute(
778
779
  "aria-label",
779
780
  trigger.getAttribute(
780
- `data-accordion-${isOpen ? "expanded" : "collapsed"}-label`
781
+ `data-accordion-${isExpand ? "expanded" : "collapsed"}-label`
781
782
  ) ?? trigger.ariaLabel ?? ""
782
783
  );
783
784
  const binding = this.#bindings.get(trigger);
@@ -789,7 +790,7 @@ var Accordion = class _Accordion {
789
790
  if (content.hidden) {
790
791
  content.hidden = false;
791
792
  }
792
- const endSize = isOpen ? content.scrollHeight : 0;
793
+ const endSize = isExpand ? content.scrollHeight : 0;
793
794
  binding.animation?.cancel();
794
795
  content.style.setProperty("overflow", "clip");
795
796
  const { duration, easing } = this.#settings.animation;
@@ -798,7 +799,7 @@ var Accordion = class _Accordion {
798
799
  { duration: isMatch ? 0 : duration, easing }
799
800
  );
800
801
  binding.animation = animation;
801
- trigger.setAttribute("aria-expanded", String(isOpen));
802
+ trigger.setAttribute("aria-expanded", String(isExpand));
802
803
  function cleanup() {
803
804
  if (binding?.animation === animation) {
804
805
  binding.animation = null;
@@ -826,6 +827,8 @@ var Accordion = class _Accordion {
826
827
  }
827
828
  #mergeOptions(target, source) {
828
829
  return {
830
+ ...target,
831
+ ...source,
829
832
  animation: { ...target.animation, ...source.animation ?? {} },
830
833
  selector: { ...target.selector, ...source.selector ?? {} }
831
834
  };
@@ -848,7 +851,7 @@ function waitAnimationFinish(animation) {
848
851
  * Accordion
849
852
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
850
853
  *
851
- * @version 1.4.18
854
+ * @version 2.0.2
852
855
  * @author Yusuke Kamiyamane
853
856
  * @license MIT
854
857
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -860,7 +863,7 @@ function waitAnimationFinish(animation) {
860
863
  (**
861
864
  * Attributes Utils
862
865
  *
863
- * @version 1.1.2
866
+ * @version 1.1.3
864
867
  * @author Yusuke Kamiyamane
865
868
  * @license MIT
866
869
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -873,7 +876,7 @@ power-focusable/dist/index.js:
873
876
  * High-precision focus management utility with full composed tree support.
874
877
  * Handles complex focus rules including tabindex ordering, radio groups, inert.
875
878
  *
876
- * @version 4.3.5
879
+ * @version 4.3.8
877
880
  * @author Yusuke Kamiyamane
878
881
  * @license MIT
879
882
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -897,7 +900,7 @@ power-focusable/dist/index.js:
897
900
  * Lightweight roving tabindex utility with fully focus management.
898
901
  * Designed for accessible menus, tabs, toolbars, and composite widgets.
899
902
  *
900
- * @version 3.1.2
903
+ * @version 3.1.6
901
904
  * @author Yusuke Kamiyamane
902
905
  * @license MIT
903
906
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.cjs CHANGED
@@ -14,8 +14,9 @@ var Accordion = class _Accordion {
14
14
  #rootElement;
15
15
  #defaults = {
16
16
  animation: { duration: 300, easing: "ease" },
17
+ collapsible: true,
17
18
  selector: {
18
- content: ":has(> [data-accordion-trigger]) + *",
19
+ content: "[data-accordion-content]",
19
20
  trigger: "[data-accordion-trigger]"
20
21
  }
21
22
  };
@@ -71,7 +72,7 @@ var Accordion = class _Accordion {
71
72
  });
72
73
  this.#initialize();
73
74
  }
74
- close(trigger) {
75
+ collapse(trigger) {
75
76
  if (this.#isDestroyed) {
76
77
  return;
77
78
  }
@@ -106,7 +107,7 @@ var Accordion = class _Accordion {
106
107
  this.#contentElements.length = 0;
107
108
  this.#rootElement.removeAttribute("data-accordion-initialized");
108
109
  }
109
- open(trigger) {
110
+ expand(trigger) {
110
111
  if (this.#isDestroyed) {
111
112
  return;
112
113
  }
@@ -191,21 +192,24 @@ var Accordion = class _Accordion {
191
192
  }
192
193
  binding.trigger.ariaExpanded === "false" && this.#toggle(binding.trigger, true, true);
193
194
  };
194
- #toggle(trigger, isOpen, isMatch = false) {
195
- if (trigger.ariaExpanded === String(isOpen)) {
195
+ #toggle(trigger, isExpand, isMatch = false, isProgrammatic = false) {
196
+ if (trigger.ariaExpanded === String(isExpand)) {
197
+ return;
198
+ }
199
+ if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#triggerElements.filter((trigger2) => trigger2.ariaExpanded === "true").length <= 1) {
196
200
  return;
197
201
  }
198
202
  const name = trigger.getAttribute("data-accordion-name");
199
- if (name && isOpen) {
200
- const open = this.#triggerElements.find(
203
+ if (name && isExpand) {
204
+ const expanded = this.#triggerElements.find(
201
205
  (t) => t !== trigger && t.getAttribute("data-accordion-name") === name && t.ariaExpanded === "true"
202
206
  );
203
- open && this.#toggle(open, false, isMatch);
207
+ expanded && this.#toggle(expanded, false, isMatch, true);
204
208
  }
205
209
  trigger.setAttribute(
206
210
  "aria-label",
207
211
  trigger.getAttribute(
208
- `data-accordion-${isOpen ? "expanded" : "collapsed"}-label`
212
+ `data-accordion-${isExpand ? "expanded" : "collapsed"}-label`
209
213
  ) ?? trigger.ariaLabel ?? ""
210
214
  );
211
215
  const binding = this.#bindings.get(trigger);
@@ -217,7 +221,7 @@ var Accordion = class _Accordion {
217
221
  if (content.hidden) {
218
222
  content.hidden = false;
219
223
  }
220
- const endSize = isOpen ? content.scrollHeight : 0;
224
+ const endSize = isExpand ? content.scrollHeight : 0;
221
225
  binding.animation?.cancel();
222
226
  content.style.setProperty("overflow", "clip");
223
227
  const { duration, easing } = this.#settings.animation;
@@ -226,7 +230,7 @@ var Accordion = class _Accordion {
226
230
  { duration: isMatch ? 0 : duration, easing }
227
231
  );
228
232
  binding.animation = animation;
229
- trigger.setAttribute("aria-expanded", String(isOpen));
233
+ trigger.setAttribute("aria-expanded", String(isExpand));
230
234
  function cleanup() {
231
235
  if (binding?.animation === animation) {
232
236
  binding.animation = null;
@@ -254,6 +258,8 @@ var Accordion = class _Accordion {
254
258
  }
255
259
  #mergeOptions(target, source) {
256
260
  return {
261
+ ...target,
262
+ ...source,
257
263
  animation: { ...target.animation, ...source.animation ?? {} },
258
264
  selector: { ...target.selector, ...source.selector ?? {} }
259
265
  };
@@ -276,7 +282,7 @@ function waitAnimationFinish(animation) {
276
282
  * Accordion
277
283
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
278
284
  *
279
- * @version 1.4.18
285
+ * @version 2.0.2
280
286
  * @author Yusuke Kamiyamane
281
287
  * @license MIT
282
288
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Accordion
3
3
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
4
4
  *
5
- * @version 1.4.18
5
+ * @version 2.0.2
6
6
  * @author Yusuke Kamiyamane
7
7
  * @license MIT
8
8
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -13,6 +13,7 @@ interface AccordionOptions {
13
13
  readonly duration?: number;
14
14
  readonly easing?: string;
15
15
  };
16
+ readonly collapsible?: boolean;
16
17
  readonly selector?: {
17
18
  readonly content?: string;
18
19
  readonly trigger?: string;
@@ -22,9 +23,9 @@ declare class Accordion {
22
23
  #private;
23
24
  static defaults: AccordionOptions;
24
25
  constructor(root: HTMLElement, options?: AccordionOptions);
25
- close(trigger: HTMLElement): void;
26
+ collapse(trigger: HTMLElement): void;
26
27
  destroy(force?: boolean): Promise<void>;
27
- open(trigger: HTMLElement): void;
28
+ expand(trigger: HTMLElement): void;
28
29
  }
29
30
 
30
31
  export { type AccordionOptions, Accordion as default };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * Accordion
3
3
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
4
4
  *
5
- * @version 1.4.18
5
+ * @version 2.0.2
6
6
  * @author Yusuke Kamiyamane
7
7
  * @license MIT
8
8
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -13,6 +13,7 @@ interface AccordionOptions {
13
13
  readonly duration?: number;
14
14
  readonly easing?: string;
15
15
  };
16
+ readonly collapsible?: boolean;
16
17
  readonly selector?: {
17
18
  readonly content?: string;
18
19
  readonly trigger?: string;
@@ -22,9 +23,9 @@ declare class Accordion {
22
23
  #private;
23
24
  static defaults: AccordionOptions;
24
25
  constructor(root: HTMLElement, options?: AccordionOptions);
25
- close(trigger: HTMLElement): void;
26
+ collapse(trigger: HTMLElement): void;
26
27
  destroy(force?: boolean): Promise<void>;
27
- open(trigger: HTMLElement): void;
28
+ expand(trigger: HTMLElement): void;
28
29
  }
29
30
 
30
31
  export { type AccordionOptions, Accordion as default };
package/dist/index.js CHANGED
@@ -8,8 +8,9 @@ var Accordion = class _Accordion {
8
8
  #rootElement;
9
9
  #defaults = {
10
10
  animation: { duration: 300, easing: "ease" },
11
+ collapsible: true,
11
12
  selector: {
12
- content: ":has(> [data-accordion-trigger]) + *",
13
+ content: "[data-accordion-content]",
13
14
  trigger: "[data-accordion-trigger]"
14
15
  }
15
16
  };
@@ -65,7 +66,7 @@ var Accordion = class _Accordion {
65
66
  });
66
67
  this.#initialize();
67
68
  }
68
- close(trigger) {
69
+ collapse(trigger) {
69
70
  if (this.#isDestroyed) {
70
71
  return;
71
72
  }
@@ -100,7 +101,7 @@ var Accordion = class _Accordion {
100
101
  this.#contentElements.length = 0;
101
102
  this.#rootElement.removeAttribute("data-accordion-initialized");
102
103
  }
103
- open(trigger) {
104
+ expand(trigger) {
104
105
  if (this.#isDestroyed) {
105
106
  return;
106
107
  }
@@ -185,21 +186,24 @@ var Accordion = class _Accordion {
185
186
  }
186
187
  binding.trigger.ariaExpanded === "false" && this.#toggle(binding.trigger, true, true);
187
188
  };
188
- #toggle(trigger, isOpen, isMatch = false) {
189
- if (trigger.ariaExpanded === String(isOpen)) {
189
+ #toggle(trigger, isExpand, isMatch = false, isProgrammatic = false) {
190
+ if (trigger.ariaExpanded === String(isExpand)) {
191
+ return;
192
+ }
193
+ if (!isExpand && !isProgrammatic && !this.#settings.collapsible && this.#triggerElements.filter((trigger2) => trigger2.ariaExpanded === "true").length <= 1) {
190
194
  return;
191
195
  }
192
196
  const name = trigger.getAttribute("data-accordion-name");
193
- if (name && isOpen) {
194
- const open = this.#triggerElements.find(
197
+ if (name && isExpand) {
198
+ const expanded = this.#triggerElements.find(
195
199
  (t) => t !== trigger && t.getAttribute("data-accordion-name") === name && t.ariaExpanded === "true"
196
200
  );
197
- open && this.#toggle(open, false, isMatch);
201
+ expanded && this.#toggle(expanded, false, isMatch, true);
198
202
  }
199
203
  trigger.setAttribute(
200
204
  "aria-label",
201
205
  trigger.getAttribute(
202
- `data-accordion-${isOpen ? "expanded" : "collapsed"}-label`
206
+ `data-accordion-${isExpand ? "expanded" : "collapsed"}-label`
203
207
  ) ?? trigger.ariaLabel ?? ""
204
208
  );
205
209
  const binding = this.#bindings.get(trigger);
@@ -211,7 +215,7 @@ var Accordion = class _Accordion {
211
215
  if (content.hidden) {
212
216
  content.hidden = false;
213
217
  }
214
- const endSize = isOpen ? content.scrollHeight : 0;
218
+ const endSize = isExpand ? content.scrollHeight : 0;
215
219
  binding.animation?.cancel();
216
220
  content.style.setProperty("overflow", "clip");
217
221
  const { duration, easing } = this.#settings.animation;
@@ -220,7 +224,7 @@ var Accordion = class _Accordion {
220
224
  { duration: isMatch ? 0 : duration, easing }
221
225
  );
222
226
  binding.animation = animation;
223
- trigger.setAttribute("aria-expanded", String(isOpen));
227
+ trigger.setAttribute("aria-expanded", String(isExpand));
224
228
  function cleanup() {
225
229
  if (binding?.animation === animation) {
226
230
  binding.animation = null;
@@ -248,6 +252,8 @@ var Accordion = class _Accordion {
248
252
  }
249
253
  #mergeOptions(target, source) {
250
254
  return {
255
+ ...target,
256
+ ...source,
251
257
  animation: { ...target.animation, ...source.animation ?? {} },
252
258
  selector: { ...target.selector, ...source.selector ?? {} }
253
259
  };
@@ -270,7 +276,7 @@ function waitAnimationFinish(animation) {
270
276
  * Accordion
271
277
  * WAI-ARIA compliant accordion pattern implementation in TypeScript.
272
278
  *
273
- * @version 1.4.18
279
+ * @version 2.0.2
274
280
  * @author Yusuke Kamiyamane
275
281
  * @license MIT
276
282
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/accordion",
3
- "version": "1.4.18",
3
+ "version": "2.0.2",
4
4
  "description": "WAI-ARIA compliant accordion pattern implementation in TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -52,8 +52,8 @@
52
52
  "node": ">=18"
53
53
  },
54
54
  "dependencies": {
55
- "@y14e/attributes-utils": "^1.1.2",
55
+ "@y14e/attributes-utils": "^1.1.3",
56
56
  "@y14e/button": "^1.0.6",
57
- "@y14e/roving-tabindex": "^3.1.2"
57
+ "@y14e/roving-tabindex": "^3.1.6"
58
58
  }
59
59
  }