@y14e/menu 1.4.4 → 1.4.6

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/menu
10
10
 
11
11
  ```ts
12
12
  // npm
13
- import Menu from '@y14e/menu@1.4.4';
13
+ import Menu from '@y14e/menu@1.4.6';
14
14
 
15
15
  // CDNs
16
- import Menu from 'https://esm.sh/@y14e/menu@1.4.4';
16
+ import Menu from 'https://esm.sh/@y14e/menu@1.4.6';
17
17
  // or
18
- import Menu from 'https://cdn.jsdelivr.net/npm/@y14e/menu@1.4.4/+esm';
18
+ import Menu from 'https://cdn.jsdelivr.net/npm/@y14e/menu@1.4.6/+esm';
19
19
  // or
20
- import Menu from 'https://esm.unpkg.com/@y14e/menu@1.4.4';
20
+ import Menu from 'https://esm.unpkg.com/@y14e/menu@1.4.6';
21
21
  ```
22
22
 
23
23
  ## Usage
package/dist/index.cjs CHANGED
@@ -2621,8 +2621,9 @@ var Menu = class _Menu {
2621
2621
  }
2622
2622
  };
2623
2623
  #settings;
2624
- #isSubmenu;
2624
+ #externalTrigger;
2625
2625
  #isPortal;
2626
+ #isSubmenu;
2626
2627
  #triggerElement;
2627
2628
  #listElement;
2628
2629
  #itemElements;
@@ -2651,9 +2652,14 @@ var Menu = class _Menu {
2651
2652
  this.#defaults = this.#mergeOptions(this.#defaults, _Menu.defaults);
2652
2653
  this.#settings = this.#mergeOptions(this.#defaults, options);
2653
2654
  matchMedia("(prefers-reduced-motion: reduce)").matches && Object.assign(this.#settings.animation, { duration: 0 });
2654
- const { isSubmenu = false, isPortal = false } = _internal;
2655
- this.#isSubmenu = isSubmenu;
2655
+ const {
2656
+ externalTrigger = null,
2657
+ isPortal = false,
2658
+ isSubmenu = false
2659
+ } = _internal;
2660
+ this.#externalTrigger = externalTrigger;
2656
2661
  this.#isPortal = isPortal;
2662
+ this.#isSubmenu = isSubmenu;
2657
2663
  const { selector } = this.#settings;
2658
2664
  this.#triggerElement = this.#rootElement.querySelector(
2659
2665
  selector[this.#isSubmenu ? "item" : "trigger"]
@@ -2741,6 +2747,7 @@ var Menu = class _Menu {
2741
2747
  }
2742
2748
  this.#animation?.cancel();
2743
2749
  this.#animation = null;
2750
+ this.#externalTrigger = null;
2744
2751
  const elements = this.#itemElements;
2745
2752
  if (this.#triggerElement) {
2746
2753
  elements.push(this.#triggerElement);
@@ -2831,8 +2838,8 @@ var Menu = class _Menu {
2831
2838
  if (parent?.querySelector(this.#settings.selector.list)) {
2832
2839
  this.#submenus.push(
2833
2840
  new _Menu(parent, this.#settings, {
2834
- isSubmenu: true,
2835
- isPortal: !!this.#triggerElement
2841
+ isPortal: !!this.#triggerElement,
2842
+ isSubmenu: true
2836
2843
  })
2837
2844
  );
2838
2845
  } else if (item2.hasAttribute("disabled") || item2.tabIndex < 0) {
@@ -2859,8 +2866,6 @@ var Menu = class _Menu {
2859
2866
  item2.addEventListener("click", this.#onRadioItemClick, { signal });
2860
2867
  });
2861
2868
  this.#resetTabIndex();
2862
- !this.#isSubmenu && this.#rootElement.setAttribute("data-menu-initialized", "");
2863
- _Menu.#menus.push(this);
2864
2869
  const { item, list } = this.#settings.selector;
2865
2870
  this.#cleanupRovingTabIndex = createRovingTabIndex(this.#listElement, {
2866
2871
  direction: "vertical",
@@ -2868,6 +2873,8 @@ var Menu = class _Menu {
2868
2873
  typeahead: true,
2869
2874
  wrap: true
2870
2875
  });
2876
+ _Menu.#menus.push(this);
2877
+ !this.#isSubmenu && this.#rootElement.setAttribute("data-menu-initialized", "");
2871
2878
  }
2872
2879
  #onOutsidePointerDown = (event) => {
2873
2880
  if (!this.#triggerElement || this.#includesRoot(event)) {
@@ -2916,7 +2923,7 @@ var Menu = class _Menu {
2916
2923
  }
2917
2924
  if (shiftKey && key === "Tab") {
2918
2925
  this.close();
2919
- requestAnimationFrame(() => this.#triggerElement?.focus());
2926
+ requestAnimationFrame(() => this.#focusTrigger());
2920
2927
  return;
2921
2928
  }
2922
2929
  if (![
@@ -2990,7 +2997,7 @@ var Menu = class _Menu {
2990
2997
  if (!this.#listElement) {
2991
2998
  return;
2992
2999
  }
2993
- if (!this.#isSubmenu && this.#triggerElement || !this.#isPortal) {
3000
+ if (!this.#isPortal || !this.#isSubmenu && this.#triggerElement) {
2994
3001
  const style2 = this.#listElement.style;
2995
3002
  style2.setProperty("position", "fixed");
2996
3003
  this.#cleanupPortal = createPortal(this.#listElement);
@@ -3006,11 +3013,7 @@ var Menu = class _Menu {
3006
3013
  this.#itemElements.filter(isFocusable3).at(isFocusLastItem ? -1 : 0)?.focus();
3007
3014
  } else {
3008
3015
  this.#clearSubmenuTimer();
3009
- const active = getActiveElement4();
3010
- if (!(active instanceof HTMLElement)) {
3011
- return;
3012
- }
3013
- this.#triggerElement && this.#containsRoot(active) && this.#triggerElement.focus();
3016
+ this.#focusTrigger();
3014
3017
  }
3015
3018
  if (!this.#triggerElement) {
3016
3019
  return;
@@ -3075,6 +3078,14 @@ var Menu = class _Menu {
3075
3078
  #containsRoot(element) {
3076
3079
  return this.#rootElement.contains(element) || this.#listElement?.contains(element);
3077
3080
  }
3081
+ #focusTrigger() {
3082
+ const active = getActiveElement4();
3083
+ if (!(active instanceof HTMLElement)) {
3084
+ return;
3085
+ }
3086
+ const trigger = this.#externalTrigger ?? this.#triggerElement;
3087
+ this.#containsRoot(active) && trigger && trigger.tabIndex >= 0 && trigger.focus();
3088
+ }
3078
3089
  #includesRoot(event) {
3079
3090
  const path = event.composedPath();
3080
3091
  if (!this.#listElement) {
@@ -3229,7 +3240,7 @@ function isFocusable3(element) {
3229
3240
  * WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript.
3230
3241
  * Supports checkbox item, radio item, and infinitely nested menus.
3231
3242
  *
3232
- * @version 1.4.4
3243
+ * @version 1.4.6
3233
3244
  * @author Yusuke Kamiyamane
3234
3245
  * @license MIT
3235
3246
  * @copyright Copyright (c) Yusuke Kamiyamane
package/dist/index.d.cts CHANGED
@@ -5,7 +5,7 @@ import { Middleware, Placement } from '@floating-ui/dom';
5
5
  * WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript.
6
6
  * Supports checkbox item, radio item, and infinitely nested menus.
7
7
  *
8
- * @version 1.4.4
8
+ * @version 1.4.6
9
9
  * @author Yusuke Kamiyamane
10
10
  * @license MIT
11
11
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -37,8 +37,9 @@ interface MenuPopoverOptions {
37
37
  readonly placement?: Placement | string;
38
38
  }
39
39
  interface InternalOptions {
40
- readonly isSubmenu?: boolean;
40
+ readonly externalTrigger?: HTMLElement;
41
41
  readonly isPortal?: boolean;
42
+ readonly isSubmenu?: boolean;
42
43
  }
43
44
  declare class Menu {
44
45
  #private;
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ import { Middleware, Placement } from '@floating-ui/dom';
5
5
  * WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript.
6
6
  * Supports checkbox item, radio item, and infinitely nested menus.
7
7
  *
8
- * @version 1.4.4
8
+ * @version 1.4.6
9
9
  * @author Yusuke Kamiyamane
10
10
  * @license MIT
11
11
  * @copyright Copyright (c) Yusuke Kamiyamane
@@ -37,8 +37,9 @@ interface MenuPopoverOptions {
37
37
  readonly placement?: Placement | string;
38
38
  }
39
39
  interface InternalOptions {
40
- readonly isSubmenu?: boolean;
40
+ readonly externalTrigger?: HTMLElement;
41
41
  readonly isPortal?: boolean;
42
+ readonly isSubmenu?: boolean;
42
43
  }
43
44
  declare class Menu {
44
45
  #private;
package/dist/index.js CHANGED
@@ -2619,8 +2619,9 @@ var Menu = class _Menu {
2619
2619
  }
2620
2620
  };
2621
2621
  #settings;
2622
- #isSubmenu;
2622
+ #externalTrigger;
2623
2623
  #isPortal;
2624
+ #isSubmenu;
2624
2625
  #triggerElement;
2625
2626
  #listElement;
2626
2627
  #itemElements;
@@ -2649,9 +2650,14 @@ var Menu = class _Menu {
2649
2650
  this.#defaults = this.#mergeOptions(this.#defaults, _Menu.defaults);
2650
2651
  this.#settings = this.#mergeOptions(this.#defaults, options);
2651
2652
  matchMedia("(prefers-reduced-motion: reduce)").matches && Object.assign(this.#settings.animation, { duration: 0 });
2652
- const { isSubmenu = false, isPortal = false } = _internal;
2653
- this.#isSubmenu = isSubmenu;
2653
+ const {
2654
+ externalTrigger = null,
2655
+ isPortal = false,
2656
+ isSubmenu = false
2657
+ } = _internal;
2658
+ this.#externalTrigger = externalTrigger;
2654
2659
  this.#isPortal = isPortal;
2660
+ this.#isSubmenu = isSubmenu;
2655
2661
  const { selector } = this.#settings;
2656
2662
  this.#triggerElement = this.#rootElement.querySelector(
2657
2663
  selector[this.#isSubmenu ? "item" : "trigger"]
@@ -2739,6 +2745,7 @@ var Menu = class _Menu {
2739
2745
  }
2740
2746
  this.#animation?.cancel();
2741
2747
  this.#animation = null;
2748
+ this.#externalTrigger = null;
2742
2749
  const elements = this.#itemElements;
2743
2750
  if (this.#triggerElement) {
2744
2751
  elements.push(this.#triggerElement);
@@ -2829,8 +2836,8 @@ var Menu = class _Menu {
2829
2836
  if (parent?.querySelector(this.#settings.selector.list)) {
2830
2837
  this.#submenus.push(
2831
2838
  new _Menu(parent, this.#settings, {
2832
- isSubmenu: true,
2833
- isPortal: !!this.#triggerElement
2839
+ isPortal: !!this.#triggerElement,
2840
+ isSubmenu: true
2834
2841
  })
2835
2842
  );
2836
2843
  } else if (item2.hasAttribute("disabled") || item2.tabIndex < 0) {
@@ -2857,8 +2864,6 @@ var Menu = class _Menu {
2857
2864
  item2.addEventListener("click", this.#onRadioItemClick, { signal });
2858
2865
  });
2859
2866
  this.#resetTabIndex();
2860
- !this.#isSubmenu && this.#rootElement.setAttribute("data-menu-initialized", "");
2861
- _Menu.#menus.push(this);
2862
2867
  const { item, list } = this.#settings.selector;
2863
2868
  this.#cleanupRovingTabIndex = createRovingTabIndex(this.#listElement, {
2864
2869
  direction: "vertical",
@@ -2866,6 +2871,8 @@ var Menu = class _Menu {
2866
2871
  typeahead: true,
2867
2872
  wrap: true
2868
2873
  });
2874
+ _Menu.#menus.push(this);
2875
+ !this.#isSubmenu && this.#rootElement.setAttribute("data-menu-initialized", "");
2869
2876
  }
2870
2877
  #onOutsidePointerDown = (event) => {
2871
2878
  if (!this.#triggerElement || this.#includesRoot(event)) {
@@ -2914,7 +2921,7 @@ var Menu = class _Menu {
2914
2921
  }
2915
2922
  if (shiftKey && key === "Tab") {
2916
2923
  this.close();
2917
- requestAnimationFrame(() => this.#triggerElement?.focus());
2924
+ requestAnimationFrame(() => this.#focusTrigger());
2918
2925
  return;
2919
2926
  }
2920
2927
  if (![
@@ -2988,7 +2995,7 @@ var Menu = class _Menu {
2988
2995
  if (!this.#listElement) {
2989
2996
  return;
2990
2997
  }
2991
- if (!this.#isSubmenu && this.#triggerElement || !this.#isPortal) {
2998
+ if (!this.#isPortal || !this.#isSubmenu && this.#triggerElement) {
2992
2999
  const style2 = this.#listElement.style;
2993
3000
  style2.setProperty("position", "fixed");
2994
3001
  this.#cleanupPortal = createPortal(this.#listElement);
@@ -3004,11 +3011,7 @@ var Menu = class _Menu {
3004
3011
  this.#itemElements.filter(isFocusable3).at(isFocusLastItem ? -1 : 0)?.focus();
3005
3012
  } else {
3006
3013
  this.#clearSubmenuTimer();
3007
- const active = getActiveElement4();
3008
- if (!(active instanceof HTMLElement)) {
3009
- return;
3010
- }
3011
- this.#triggerElement && this.#containsRoot(active) && this.#triggerElement.focus();
3014
+ this.#focusTrigger();
3012
3015
  }
3013
3016
  if (!this.#triggerElement) {
3014
3017
  return;
@@ -3073,6 +3076,14 @@ var Menu = class _Menu {
3073
3076
  #containsRoot(element) {
3074
3077
  return this.#rootElement.contains(element) || this.#listElement?.contains(element);
3075
3078
  }
3079
+ #focusTrigger() {
3080
+ const active = getActiveElement4();
3081
+ if (!(active instanceof HTMLElement)) {
3082
+ return;
3083
+ }
3084
+ const trigger = this.#externalTrigger ?? this.#triggerElement;
3085
+ this.#containsRoot(active) && trigger && trigger.tabIndex >= 0 && trigger.focus();
3086
+ }
3076
3087
  #includesRoot(event) {
3077
3088
  const path = event.composedPath();
3078
3089
  if (!this.#listElement) {
@@ -3227,7 +3238,7 @@ function isFocusable3(element) {
3227
3238
  * WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript.
3228
3239
  * Supports checkbox item, radio item, and infinitely nested menus.
3229
3240
  *
3230
- * @version 1.4.4
3241
+ * @version 1.4.6
3231
3242
  * @author Yusuke Kamiyamane
3232
3243
  * @license MIT
3233
3244
  * @copyright Copyright (c) Yusuke Kamiyamane
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@y14e/menu",
3
- "version": "1.4.4",
3
+ "version": "1.4.6",
4
4
  "description": "WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",