@y14e/menu 1.5.2 → 1.5.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,16 +10,16 @@ npm i @y14e/menu
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import Menu from '@y14e/menu@1.5.2';
13
+ import Menu from '@y14e/menu@1.5.4';
14
14
  // with middleware
15
- import Menu, { flip, offset, shift } from '@y14e/menu@1.5.2';
15
+ import Menu, { flip, offset, shift } from '@y14e/menu@1.5.4';
16
16
 
17
17
  // CDNs
18
- import Menu from 'https://esm.sh/@y14e/menu@1.5.2';
18
+ import Menu from 'https://esm.sh/@y14e/menu@1.5.4';
19
19
  // or
20
- import Menu from 'https://cdn.jsdelivr.net/npm/@y14e/menu@1.5.2/+esm';
20
+ import Menu from 'https://cdn.jsdelivr.net/npm/@y14e/menu@1.5.4/+esm';
21
21
  // or
22
- import Menu from 'https://esm.unpkg.com/@y14e/menu@1.5.2';
22
+ import Menu from 'https://esm.unpkg.com/@y14e/menu@1.5.4';
23
23
  ```
24
24
 
25
25
  ## Usage
package/dist/index.cjs CHANGED
@@ -2627,6 +2627,7 @@ var Menu = class _Menu {
2627
2627
  #isMenubar;
2628
2628
  #isPortal;
2629
2629
  #isSubmenu;
2630
+ #popoverRef;
2630
2631
  #triggerElement;
2631
2632
  #listElement;
2632
2633
  #itemElements;
@@ -2635,14 +2636,14 @@ var Menu = class _Menu {
2635
2636
  #radioItemElementsByGroup = /* @__PURE__ */ new WeakMap();
2636
2637
  #arrowElement;
2637
2638
  #controller = null;
2639
+ #cleanupPopover = null;
2640
+ #cleanupPortal = null;
2641
+ #cleanupRovingTabIndex = null;
2638
2642
  #animation = null;
2643
+ #buttons = [];
2639
2644
  #submenus = [];
2640
2645
  #submenuTimer;
2641
2646
  #isDestroyed = false;
2642
- #cleanupPortal = null;
2643
- #cleanupRovingTabIndex = null;
2644
- #cleanupPopover = null;
2645
- #buttons = [];
2646
2647
  constructor(root, options = {}, _internal = {}) {
2647
2648
  if (!(root instanceof HTMLElement)) {
2648
2649
  throw new TypeError("Invalid root element");
@@ -2669,6 +2670,7 @@ var Menu = class _Menu {
2669
2670
  this.#triggerElement = this.#rootElement.querySelector(
2670
2671
  selector[this.#isSubmenu ? "item" : "trigger"]
2671
2672
  );
2673
+ this.#popoverRef = _internal.popoverRef ?? this.#triggerElement;
2672
2674
  this.#listElement = this.#rootElement.querySelector(
2673
2675
  selector.list
2674
2676
  );
@@ -2752,15 +2754,16 @@ var Menu = class _Menu {
2752
2754
  }
2753
2755
  this.#animation?.cancel();
2754
2756
  this.#animation = null;
2755
- this.#externalTrigger = null;
2757
+ const trigger = this.#getTrigger();
2756
2758
  const elements = this.#itemElements;
2757
- if (this.#triggerElement) {
2758
- elements.push(this.#triggerElement);
2759
+ if (trigger) {
2760
+ elements.push(trigger);
2759
2761
  }
2760
2762
  if (this.#listElement) {
2761
2763
  elements.push(this.#listElement);
2762
2764
  }
2763
2765
  restoreAttributes(elements);
2766
+ this.#externalTrigger = null;
2764
2767
  this.#triggerElement = null;
2765
2768
  this.#listElement = null;
2766
2769
  this.#itemElements.length = 0;
@@ -2786,9 +2789,10 @@ var Menu = class _Menu {
2786
2789
  return;
2787
2790
  }
2788
2791
  saveAttributes([this.#listElement], ["aria-labelledby", "id", "role"]);
2789
- if (this.#triggerElement) {
2792
+ const trigger = this.#getTrigger();
2793
+ if (trigger && this.#triggerElement) {
2790
2794
  saveAttributes(
2791
- [this.#triggerElement],
2795
+ [trigger],
2792
2796
  [
2793
2797
  "aria-controls",
2794
2798
  "aria-disabled",
@@ -2801,18 +2805,14 @@ var Menu = class _Menu {
2801
2805
  );
2802
2806
  const id = Math.random().toString(36).slice(-8);
2803
2807
  this.#listElement.id ||= `menu-list-${id}`;
2804
- addTokenToAttribute(
2805
- this.#triggerElement,
2806
- "aria-controls",
2807
- this.#listElement.id
2808
- );
2809
- this.#triggerElement.setAttribute("aria-expanded", "false");
2810
- this.#triggerElement.setAttribute("aria-haspopup", "true");
2811
- this.#triggerElement.id ||= `menu-trigger-${id}`;
2812
- if (!isFocusable3(this.#triggerElement)) {
2813
- this.#triggerElement.setAttribute("aria-disabled", "true");
2814
- this.#triggerElement.setAttribute("tabindex", "-1");
2815
- this.#triggerElement.style.setProperty("pointer-events", "none");
2808
+ addTokenToAttribute(trigger, "aria-controls", this.#listElement.id);
2809
+ trigger.setAttribute("aria-expanded", "false");
2810
+ trigger.setAttribute("aria-haspopup", "true");
2811
+ trigger.id ||= `menu-trigger-${id}`;
2812
+ if (!isFocusable3(trigger)) {
2813
+ trigger.setAttribute("aria-disabled", "true");
2814
+ trigger.setAttribute("tabindex", "-1");
2815
+ trigger.style.setProperty("pointer-events", "none");
2816
2816
  }
2817
2817
  this.#triggerElement.addEventListener("click", this.#onTriggerClick, {
2818
2818
  signal
@@ -2820,11 +2820,7 @@ var Menu = class _Menu {
2820
2820
  this.#triggerElement.addEventListener("keydown", this.#onTriggerKeyDown, {
2821
2821
  signal
2822
2822
  });
2823
- addTokenToAttribute(
2824
- this.#listElement,
2825
- "aria-labelledby",
2826
- this.#triggerElement.id
2827
- );
2823
+ addTokenToAttribute(this.#listElement, "aria-labelledby", trigger.id);
2828
2824
  this.#buttons.push(new Button(this.#triggerElement));
2829
2825
  }
2830
2826
  this.#listElement.setAttribute("role", "menu");
@@ -2907,7 +2903,7 @@ var Menu = class _Menu {
2907
2903
  #onTriggerClick = (event) => {
2908
2904
  event.preventDefault();
2909
2905
  this.#toggle(
2910
- this.#isSubmenu ? event.currentTarget === this.#triggerElement : this.#triggerElement?.ariaExpanded !== "true"
2906
+ this.#isSubmenu ? event.currentTarget === this.#triggerElement : this.#getTrigger()?.ariaExpanded !== "true"
2911
2907
  );
2912
2908
  };
2913
2909
  #onTriggerKeyDown = (event) => {
@@ -3001,10 +2997,11 @@ var Menu = class _Menu {
3001
2997
  });
3002
2998
  };
3003
2999
  #toggle(isOpen, options = {}) {
3004
- if (this.#triggerElement?.ariaExpanded === String(isOpen)) {
3000
+ const trigger = this.#getTrigger();
3001
+ if (trigger?.ariaExpanded === String(isOpen)) {
3005
3002
  return;
3006
3003
  }
3007
- this.#triggerElement?.setAttribute("aria-expanded", String(isOpen));
3004
+ trigger?.setAttribute("aria-expanded", String(isOpen));
3008
3005
  if (isOpen) {
3009
3006
  _Menu.#menus.filter((m) => !m.#containsRoot(this.#rootElement)).forEach((menu) => {
3010
3007
  menu.close();
@@ -3094,7 +3091,7 @@ var Menu = class _Menu {
3094
3091
  #closeAndMoveFocus(direction) {
3095
3092
  this.close();
3096
3093
  requestAnimationFrame(
3097
- () => (this.#externalTrigger ?? this.#triggerElement)?.dispatchEvent(
3094
+ () => this.#getTrigger()?.dispatchEvent(
3098
3095
  new KeyboardEvent("keydown", {
3099
3096
  key: direction === "previous" ? "ArrowLeft" : "ArrowRight"
3100
3097
  })
@@ -3109,7 +3106,10 @@ var Menu = class _Menu {
3109
3106
  if (!(active instanceof HTMLElement)) {
3110
3107
  return;
3111
3108
  }
3112
- this.#containsRoot(active) && (this.#externalTrigger ?? this.#triggerElement)?.focus();
3109
+ this.#containsRoot(active) && this.#getTrigger()?.focus();
3110
+ }
3111
+ #getTrigger() {
3112
+ return this.#externalTrigger ?? this.#triggerElement;
3113
3113
  }
3114
3114
  #includesRoot(event) {
3115
3115
  const path = event.composedPath();
@@ -3164,15 +3164,15 @@ var Menu = class _Menu {
3164
3164
  }
3165
3165
  }
3166
3166
  #updatePopover() {
3167
- if (!this.#triggerElement) {
3167
+ if (!this.#popoverRef) {
3168
3168
  return;
3169
3169
  }
3170
3170
  const compute = () => {
3171
- if (!this.#triggerElement || !this.#listElement) {
3171
+ if (!this.#popoverRef || !this.#listElement) {
3172
3172
  return;
3173
3173
  }
3174
3174
  const options = this.#settings.popover[this.#isSubmenu ? "submenu" : "menu"];
3175
- computePosition2(this.#triggerElement, this.#listElement, {
3175
+ computePosition2(this.#popoverRef, this.#listElement, {
3176
3176
  ...options,
3177
3177
  placement: options.placement
3178
3178
  }).then(
@@ -3240,10 +3240,9 @@ var Menu = class _Menu {
3240
3240
  }
3241
3241
  );
3242
3242
  };
3243
- compute();
3244
3243
  if (!this.#cleanupPopover) {
3245
3244
  this.#cleanupPopover = autoUpdate(
3246
- this.#triggerElement,
3245
+ this.#popoverRef,
3247
3246
  this.#listElement,
3248
3247
  compute
3249
3248
  );
@@ -3265,7 +3264,7 @@ function isFocusable3(element) {
3265
3264
  * WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript.
3266
3265
  * Supports checkbox item, radio item, and infinitely nested menus.
3267
3266
  *
3268
- * @version 1.5.2
3267
+ * @version 1.5.4
3269
3268
  * @author Yusuke Kamiyamane
3270
3269
  * @license MIT
3271
3270
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -6,7 +6,7 @@ export { flip, offset, shift } from '@floating-ui/dom';
6
6
  * WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript.
7
7
  * Supports checkbox item, radio item, and infinitely nested menus.
8
8
  *
9
- * @version 1.5.2
9
+ * @version 1.5.4
10
10
  * @author Yusuke Kamiyamane
11
11
  * @license MIT
12
12
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -42,6 +42,7 @@ interface InternalOptions {
42
42
  readonly isMenubar?: boolean;
43
43
  readonly isPortal?: boolean;
44
44
  readonly isSubmenu?: boolean;
45
+ readonly popoverRef?: HTMLElement | null;
45
46
  }
46
47
  declare class Menu {
47
48
  #private;
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ export { flip, offset, shift } from '@floating-ui/dom';
6
6
  * WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript.
7
7
  * Supports checkbox item, radio item, and infinitely nested menus.
8
8
  *
9
- * @version 1.5.2
9
+ * @version 1.5.4
10
10
  * @author Yusuke Kamiyamane
11
11
  * @license MIT
12
12
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -42,6 +42,7 @@ interface InternalOptions {
42
42
  readonly isMenubar?: boolean;
43
43
  readonly isPortal?: boolean;
44
44
  readonly isSubmenu?: boolean;
45
+ readonly popoverRef?: HTMLElement | null;
45
46
  }
46
47
  declare class Menu {
47
48
  #private;
package/dist/index.js CHANGED
@@ -2623,6 +2623,7 @@ var Menu = class _Menu {
2623
2623
  #isMenubar;
2624
2624
  #isPortal;
2625
2625
  #isSubmenu;
2626
+ #popoverRef;
2626
2627
  #triggerElement;
2627
2628
  #listElement;
2628
2629
  #itemElements;
@@ -2631,14 +2632,14 @@ var Menu = class _Menu {
2631
2632
  #radioItemElementsByGroup = /* @__PURE__ */ new WeakMap();
2632
2633
  #arrowElement;
2633
2634
  #controller = null;
2635
+ #cleanupPopover = null;
2636
+ #cleanupPortal = null;
2637
+ #cleanupRovingTabIndex = null;
2634
2638
  #animation = null;
2639
+ #buttons = [];
2635
2640
  #submenus = [];
2636
2641
  #submenuTimer;
2637
2642
  #isDestroyed = false;
2638
- #cleanupPortal = null;
2639
- #cleanupRovingTabIndex = null;
2640
- #cleanupPopover = null;
2641
- #buttons = [];
2642
2643
  constructor(root, options = {}, _internal = {}) {
2643
2644
  if (!(root instanceof HTMLElement)) {
2644
2645
  throw new TypeError("Invalid root element");
@@ -2665,6 +2666,7 @@ var Menu = class _Menu {
2665
2666
  this.#triggerElement = this.#rootElement.querySelector(
2666
2667
  selector[this.#isSubmenu ? "item" : "trigger"]
2667
2668
  );
2669
+ this.#popoverRef = _internal.popoverRef ?? this.#triggerElement;
2668
2670
  this.#listElement = this.#rootElement.querySelector(
2669
2671
  selector.list
2670
2672
  );
@@ -2748,15 +2750,16 @@ var Menu = class _Menu {
2748
2750
  }
2749
2751
  this.#animation?.cancel();
2750
2752
  this.#animation = null;
2751
- this.#externalTrigger = null;
2753
+ const trigger = this.#getTrigger();
2752
2754
  const elements = this.#itemElements;
2753
- if (this.#triggerElement) {
2754
- elements.push(this.#triggerElement);
2755
+ if (trigger) {
2756
+ elements.push(trigger);
2755
2757
  }
2756
2758
  if (this.#listElement) {
2757
2759
  elements.push(this.#listElement);
2758
2760
  }
2759
2761
  restoreAttributes(elements);
2762
+ this.#externalTrigger = null;
2760
2763
  this.#triggerElement = null;
2761
2764
  this.#listElement = null;
2762
2765
  this.#itemElements.length = 0;
@@ -2782,9 +2785,10 @@ var Menu = class _Menu {
2782
2785
  return;
2783
2786
  }
2784
2787
  saveAttributes([this.#listElement], ["aria-labelledby", "id", "role"]);
2785
- if (this.#triggerElement) {
2788
+ const trigger = this.#getTrigger();
2789
+ if (trigger && this.#triggerElement) {
2786
2790
  saveAttributes(
2787
- [this.#triggerElement],
2791
+ [trigger],
2788
2792
  [
2789
2793
  "aria-controls",
2790
2794
  "aria-disabled",
@@ -2797,18 +2801,14 @@ var Menu = class _Menu {
2797
2801
  );
2798
2802
  const id = Math.random().toString(36).slice(-8);
2799
2803
  this.#listElement.id ||= `menu-list-${id}`;
2800
- addTokenToAttribute(
2801
- this.#triggerElement,
2802
- "aria-controls",
2803
- this.#listElement.id
2804
- );
2805
- this.#triggerElement.setAttribute("aria-expanded", "false");
2806
- this.#triggerElement.setAttribute("aria-haspopup", "true");
2807
- this.#triggerElement.id ||= `menu-trigger-${id}`;
2808
- if (!isFocusable3(this.#triggerElement)) {
2809
- this.#triggerElement.setAttribute("aria-disabled", "true");
2810
- this.#triggerElement.setAttribute("tabindex", "-1");
2811
- this.#triggerElement.style.setProperty("pointer-events", "none");
2804
+ addTokenToAttribute(trigger, "aria-controls", this.#listElement.id);
2805
+ trigger.setAttribute("aria-expanded", "false");
2806
+ trigger.setAttribute("aria-haspopup", "true");
2807
+ trigger.id ||= `menu-trigger-${id}`;
2808
+ if (!isFocusable3(trigger)) {
2809
+ trigger.setAttribute("aria-disabled", "true");
2810
+ trigger.setAttribute("tabindex", "-1");
2811
+ trigger.style.setProperty("pointer-events", "none");
2812
2812
  }
2813
2813
  this.#triggerElement.addEventListener("click", this.#onTriggerClick, {
2814
2814
  signal
@@ -2816,11 +2816,7 @@ var Menu = class _Menu {
2816
2816
  this.#triggerElement.addEventListener("keydown", this.#onTriggerKeyDown, {
2817
2817
  signal
2818
2818
  });
2819
- addTokenToAttribute(
2820
- this.#listElement,
2821
- "aria-labelledby",
2822
- this.#triggerElement.id
2823
- );
2819
+ addTokenToAttribute(this.#listElement, "aria-labelledby", trigger.id);
2824
2820
  this.#buttons.push(new Button(this.#triggerElement));
2825
2821
  }
2826
2822
  this.#listElement.setAttribute("role", "menu");
@@ -2903,7 +2899,7 @@ var Menu = class _Menu {
2903
2899
  #onTriggerClick = (event) => {
2904
2900
  event.preventDefault();
2905
2901
  this.#toggle(
2906
- this.#isSubmenu ? event.currentTarget === this.#triggerElement : this.#triggerElement?.ariaExpanded !== "true"
2902
+ this.#isSubmenu ? event.currentTarget === this.#triggerElement : this.#getTrigger()?.ariaExpanded !== "true"
2907
2903
  );
2908
2904
  };
2909
2905
  #onTriggerKeyDown = (event) => {
@@ -2997,10 +2993,11 @@ var Menu = class _Menu {
2997
2993
  });
2998
2994
  };
2999
2995
  #toggle(isOpen, options = {}) {
3000
- if (this.#triggerElement?.ariaExpanded === String(isOpen)) {
2996
+ const trigger = this.#getTrigger();
2997
+ if (trigger?.ariaExpanded === String(isOpen)) {
3001
2998
  return;
3002
2999
  }
3003
- this.#triggerElement?.setAttribute("aria-expanded", String(isOpen));
3000
+ trigger?.setAttribute("aria-expanded", String(isOpen));
3004
3001
  if (isOpen) {
3005
3002
  _Menu.#menus.filter((m) => !m.#containsRoot(this.#rootElement)).forEach((menu) => {
3006
3003
  menu.close();
@@ -3090,7 +3087,7 @@ var Menu = class _Menu {
3090
3087
  #closeAndMoveFocus(direction) {
3091
3088
  this.close();
3092
3089
  requestAnimationFrame(
3093
- () => (this.#externalTrigger ?? this.#triggerElement)?.dispatchEvent(
3090
+ () => this.#getTrigger()?.dispatchEvent(
3094
3091
  new KeyboardEvent("keydown", {
3095
3092
  key: direction === "previous" ? "ArrowLeft" : "ArrowRight"
3096
3093
  })
@@ -3105,7 +3102,10 @@ var Menu = class _Menu {
3105
3102
  if (!(active instanceof HTMLElement)) {
3106
3103
  return;
3107
3104
  }
3108
- this.#containsRoot(active) && (this.#externalTrigger ?? this.#triggerElement)?.focus();
3105
+ this.#containsRoot(active) && this.#getTrigger()?.focus();
3106
+ }
3107
+ #getTrigger() {
3108
+ return this.#externalTrigger ?? this.#triggerElement;
3109
3109
  }
3110
3110
  #includesRoot(event) {
3111
3111
  const path = event.composedPath();
@@ -3160,15 +3160,15 @@ var Menu = class _Menu {
3160
3160
  }
3161
3161
  }
3162
3162
  #updatePopover() {
3163
- if (!this.#triggerElement) {
3163
+ if (!this.#popoverRef) {
3164
3164
  return;
3165
3165
  }
3166
3166
  const compute = () => {
3167
- if (!this.#triggerElement || !this.#listElement) {
3167
+ if (!this.#popoverRef || !this.#listElement) {
3168
3168
  return;
3169
3169
  }
3170
3170
  const options = this.#settings.popover[this.#isSubmenu ? "submenu" : "menu"];
3171
- computePosition2(this.#triggerElement, this.#listElement, {
3171
+ computePosition2(this.#popoverRef, this.#listElement, {
3172
3172
  ...options,
3173
3173
  placement: options.placement
3174
3174
  }).then(
@@ -3236,10 +3236,9 @@ var Menu = class _Menu {
3236
3236
  }
3237
3237
  );
3238
3238
  };
3239
- compute();
3240
3239
  if (!this.#cleanupPopover) {
3241
3240
  this.#cleanupPopover = autoUpdate(
3242
- this.#triggerElement,
3241
+ this.#popoverRef,
3243
3242
  this.#listElement,
3244
3243
  compute
3245
3244
  );
@@ -3261,7 +3260,7 @@ function isFocusable3(element) {
3261
3260
  * WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript.
3262
3261
  * Supports checkbox item, radio item, and infinitely nested menus.
3263
3262
  *
3264
- * @version 1.5.2
3263
+ * @version 1.5.4
3265
3264
  * @author Yusuke Kamiyamane
3266
3265
  * @license MIT
3267
3266
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/menu",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "description": "WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",