@y14e/menu 1.4.8 → 1.5.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 +5 -5
- package/dist/index.cjs +33 -9
- package/dist/index.d.cts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +33 -9
- package/package.json +1 -1
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.
|
|
13
|
+
import Menu from '@y14e/menu@1.5.1';
|
|
14
14
|
// with middleware
|
|
15
|
-
import Menu, { flip, offset, shift } from '@y14e/menu@1.
|
|
15
|
+
import Menu, { flip, offset, shift } from '@y14e/menu@1.5.1';
|
|
16
16
|
|
|
17
17
|
// CDNs
|
|
18
|
-
import Menu from 'https://esm.sh/@y14e/menu@1.
|
|
18
|
+
import Menu from 'https://esm.sh/@y14e/menu@1.5.1';
|
|
19
19
|
// or
|
|
20
|
-
import Menu from 'https://cdn.jsdelivr.net/npm/@y14e/menu@1.
|
|
20
|
+
import Menu from 'https://cdn.jsdelivr.net/npm/@y14e/menu@1.5.1/+esm';
|
|
21
21
|
// or
|
|
22
|
-
import Menu from 'https://esm.unpkg.com/@y14e/menu@1.
|
|
22
|
+
import Menu from 'https://esm.unpkg.com/@y14e/menu@1.5.1';
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Usage
|
package/dist/index.cjs
CHANGED
|
@@ -2624,6 +2624,7 @@ var Menu = class _Menu {
|
|
|
2624
2624
|
};
|
|
2625
2625
|
#settings;
|
|
2626
2626
|
#externalTrigger;
|
|
2627
|
+
#isMenubar;
|
|
2627
2628
|
#isPortal;
|
|
2628
2629
|
#isSubmenu;
|
|
2629
2630
|
#triggerElement;
|
|
@@ -2656,10 +2657,12 @@ var Menu = class _Menu {
|
|
|
2656
2657
|
matchMedia("(prefers-reduced-motion: reduce)").matches && Object.assign(this.#settings.animation, { duration: 0 });
|
|
2657
2658
|
const {
|
|
2658
2659
|
externalTrigger = null,
|
|
2660
|
+
isMenubar = false,
|
|
2659
2661
|
isPortal = false,
|
|
2660
2662
|
isSubmenu = false
|
|
2661
2663
|
} = _internal;
|
|
2662
2664
|
this.#externalTrigger = externalTrigger;
|
|
2665
|
+
this.#isMenubar = isMenubar;
|
|
2663
2666
|
this.#isPortal = isPortal;
|
|
2664
2667
|
this.#isSubmenu = isSubmenu;
|
|
2665
2668
|
const { selector } = this.#settings;
|
|
@@ -2715,8 +2718,8 @@ var Menu = class _Menu {
|
|
|
2715
2718
|
}
|
|
2716
2719
|
this.#initialize();
|
|
2717
2720
|
}
|
|
2718
|
-
open(
|
|
2719
|
-
this.#toggle(true,
|
|
2721
|
+
open(_initialFocus = "first") {
|
|
2722
|
+
this.#toggle(true, { initialFocus: _initialFocus });
|
|
2720
2723
|
}
|
|
2721
2724
|
close() {
|
|
2722
2725
|
this.#toggle(false);
|
|
@@ -2916,7 +2919,7 @@ var Menu = class _Menu {
|
|
|
2916
2919
|
}
|
|
2917
2920
|
event.preventDefault();
|
|
2918
2921
|
event.stopPropagation();
|
|
2919
|
-
this.open(key === "
|
|
2922
|
+
this.open(key === "ArrowDown" ? "first" : "last");
|
|
2920
2923
|
};
|
|
2921
2924
|
#onListKeyDown = (event) => {
|
|
2922
2925
|
const { shiftKey, key } = event;
|
|
@@ -2932,7 +2935,8 @@ var Menu = class _Menu {
|
|
|
2932
2935
|
"Enter",
|
|
2933
2936
|
"Escape",
|
|
2934
2937
|
" ",
|
|
2935
|
-
|
|
2938
|
+
"ArrowLeft",
|
|
2939
|
+
...this.#isMenubar ? ["ArrowRight"] : []
|
|
2936
2940
|
].includes(key)) {
|
|
2937
2941
|
return;
|
|
2938
2942
|
}
|
|
@@ -2945,9 +2949,18 @@ var Menu = class _Menu {
|
|
|
2945
2949
|
switch (key) {
|
|
2946
2950
|
case "Tab":
|
|
2947
2951
|
case "Escape":
|
|
2948
|
-
case "ArrowLeft":
|
|
2949
2952
|
this.close();
|
|
2950
2953
|
return;
|
|
2954
|
+
case "ArrowLeft":
|
|
2955
|
+
if (this.#isMenubar) {
|
|
2956
|
+
this.#closeAndMoveFocus("previous");
|
|
2957
|
+
} else if (this.#isSubmenu && this.#triggerElement) {
|
|
2958
|
+
this.close();
|
|
2959
|
+
}
|
|
2960
|
+
return;
|
|
2961
|
+
case "ArrowRight":
|
|
2962
|
+
this.#closeAndMoveFocus("next");
|
|
2963
|
+
return;
|
|
2951
2964
|
case "Enter":
|
|
2952
2965
|
case " ":
|
|
2953
2966
|
active.click();
|
|
@@ -2987,7 +3000,7 @@ var Menu = class _Menu {
|
|
|
2987
3000
|
i.setAttribute("aria-checked", String(i === item));
|
|
2988
3001
|
});
|
|
2989
3002
|
};
|
|
2990
|
-
#toggle(isOpen,
|
|
3003
|
+
#toggle(isOpen, options = {}) {
|
|
2991
3004
|
if (this.#triggerElement?.ariaExpanded === String(isOpen)) {
|
|
2992
3005
|
return;
|
|
2993
3006
|
}
|
|
@@ -3012,7 +3025,8 @@ var Menu = class _Menu {
|
|
|
3012
3025
|
style.setProperty("display", "block");
|
|
3013
3026
|
style.setProperty("opacity", "0");
|
|
3014
3027
|
this.#triggerElement && this.#updatePopover();
|
|
3015
|
-
|
|
3028
|
+
const { initialFocus = "first" } = options;
|
|
3029
|
+
this.#itemElements.filter(isFocusable3).at(initialFocus === "first" ? 0 : -1)?.focus();
|
|
3016
3030
|
} else {
|
|
3017
3031
|
this.#clearSubmenuTimer();
|
|
3018
3032
|
this.#focusTrigger();
|
|
@@ -3077,8 +3091,18 @@ var Menu = class _Menu {
|
|
|
3077
3091
|
this.#submenuTimer = void 0;
|
|
3078
3092
|
}
|
|
3079
3093
|
}
|
|
3094
|
+
#closeAndMoveFocus(direction) {
|
|
3095
|
+
this.close();
|
|
3096
|
+
requestAnimationFrame(
|
|
3097
|
+
() => (this.#externalTrigger ?? this.#triggerElement)?.dispatchEvent(
|
|
3098
|
+
new KeyboardEvent("keydown", {
|
|
3099
|
+
key: direction === "previous" ? "ArrowLeft" : "ArrowRight"
|
|
3100
|
+
})
|
|
3101
|
+
)
|
|
3102
|
+
);
|
|
3103
|
+
}
|
|
3080
3104
|
#containsRoot(element) {
|
|
3081
|
-
return this.#rootElement.contains(element) || this.#listElement?.contains(element);
|
|
3105
|
+
return this.#rootElement.contains(element) || !!this.#listElement?.contains(element);
|
|
3082
3106
|
}
|
|
3083
3107
|
#focusTrigger() {
|
|
3084
3108
|
const active = getActiveElement4();
|
|
@@ -3241,7 +3265,7 @@ function isFocusable3(element) {
|
|
|
3241
3265
|
* WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript.
|
|
3242
3266
|
* Supports checkbox item, radio item, and infinitely nested menus.
|
|
3243
3267
|
*
|
|
3244
|
-
* @version 1.
|
|
3268
|
+
* @version 1.5.1
|
|
3245
3269
|
* @author Yusuke Kamiyamane
|
|
3246
3270
|
* @license MIT
|
|
3247
3271
|
* @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.
|
|
9
|
+
* @version 1.5.1
|
|
10
10
|
* @author Yusuke Kamiyamane
|
|
11
11
|
* @license MIT
|
|
12
12
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -39,6 +39,7 @@ interface MenuPopoverOptions {
|
|
|
39
39
|
}
|
|
40
40
|
interface InternalOptions {
|
|
41
41
|
readonly externalTrigger?: HTMLElement;
|
|
42
|
+
readonly isMenubar?: boolean;
|
|
42
43
|
readonly isPortal?: boolean;
|
|
43
44
|
readonly isSubmenu?: boolean;
|
|
44
45
|
}
|
|
@@ -50,7 +51,11 @@ declare class Menu {
|
|
|
50
51
|
* @internal Internal constructor
|
|
51
52
|
*/
|
|
52
53
|
constructor(root: HTMLElement, options?: MenuOptions, _internal?: InternalOptions);
|
|
53
|
-
open(
|
|
54
|
+
open(): void;
|
|
55
|
+
/**
|
|
56
|
+
* @internal Internal open
|
|
57
|
+
*/
|
|
58
|
+
open(_initialFocus?: 'first' | 'last'): void;
|
|
54
59
|
close(): void;
|
|
55
60
|
destroy(force?: boolean): Promise<void>;
|
|
56
61
|
}
|
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.
|
|
9
|
+
* @version 1.5.1
|
|
10
10
|
* @author Yusuke Kamiyamane
|
|
11
11
|
* @license MIT
|
|
12
12
|
* @copyright Copyright (c) Yusuke Kamiyamane
|
|
@@ -39,6 +39,7 @@ interface MenuPopoverOptions {
|
|
|
39
39
|
}
|
|
40
40
|
interface InternalOptions {
|
|
41
41
|
readonly externalTrigger?: HTMLElement;
|
|
42
|
+
readonly isMenubar?: boolean;
|
|
42
43
|
readonly isPortal?: boolean;
|
|
43
44
|
readonly isSubmenu?: boolean;
|
|
44
45
|
}
|
|
@@ -50,7 +51,11 @@ declare class Menu {
|
|
|
50
51
|
* @internal Internal constructor
|
|
51
52
|
*/
|
|
52
53
|
constructor(root: HTMLElement, options?: MenuOptions, _internal?: InternalOptions);
|
|
53
|
-
open(
|
|
54
|
+
open(): void;
|
|
55
|
+
/**
|
|
56
|
+
* @internal Internal open
|
|
57
|
+
*/
|
|
58
|
+
open(_initialFocus?: 'first' | 'last'): void;
|
|
54
59
|
close(): void;
|
|
55
60
|
destroy(force?: boolean): Promise<void>;
|
|
56
61
|
}
|
package/dist/index.js
CHANGED
|
@@ -2620,6 +2620,7 @@ var Menu = class _Menu {
|
|
|
2620
2620
|
};
|
|
2621
2621
|
#settings;
|
|
2622
2622
|
#externalTrigger;
|
|
2623
|
+
#isMenubar;
|
|
2623
2624
|
#isPortal;
|
|
2624
2625
|
#isSubmenu;
|
|
2625
2626
|
#triggerElement;
|
|
@@ -2652,10 +2653,12 @@ var Menu = class _Menu {
|
|
|
2652
2653
|
matchMedia("(prefers-reduced-motion: reduce)").matches && Object.assign(this.#settings.animation, { duration: 0 });
|
|
2653
2654
|
const {
|
|
2654
2655
|
externalTrigger = null,
|
|
2656
|
+
isMenubar = false,
|
|
2655
2657
|
isPortal = false,
|
|
2656
2658
|
isSubmenu = false
|
|
2657
2659
|
} = _internal;
|
|
2658
2660
|
this.#externalTrigger = externalTrigger;
|
|
2661
|
+
this.#isMenubar = isMenubar;
|
|
2659
2662
|
this.#isPortal = isPortal;
|
|
2660
2663
|
this.#isSubmenu = isSubmenu;
|
|
2661
2664
|
const { selector } = this.#settings;
|
|
@@ -2711,8 +2714,8 @@ var Menu = class _Menu {
|
|
|
2711
2714
|
}
|
|
2712
2715
|
this.#initialize();
|
|
2713
2716
|
}
|
|
2714
|
-
open(
|
|
2715
|
-
this.#toggle(true,
|
|
2717
|
+
open(_initialFocus = "first") {
|
|
2718
|
+
this.#toggle(true, { initialFocus: _initialFocus });
|
|
2716
2719
|
}
|
|
2717
2720
|
close() {
|
|
2718
2721
|
this.#toggle(false);
|
|
@@ -2912,7 +2915,7 @@ var Menu = class _Menu {
|
|
|
2912
2915
|
}
|
|
2913
2916
|
event.preventDefault();
|
|
2914
2917
|
event.stopPropagation();
|
|
2915
|
-
this.open(key === "
|
|
2918
|
+
this.open(key === "ArrowDown" ? "first" : "last");
|
|
2916
2919
|
};
|
|
2917
2920
|
#onListKeyDown = (event) => {
|
|
2918
2921
|
const { shiftKey, key } = event;
|
|
@@ -2928,7 +2931,8 @@ var Menu = class _Menu {
|
|
|
2928
2931
|
"Enter",
|
|
2929
2932
|
"Escape",
|
|
2930
2933
|
" ",
|
|
2931
|
-
|
|
2934
|
+
"ArrowLeft",
|
|
2935
|
+
...this.#isMenubar ? ["ArrowRight"] : []
|
|
2932
2936
|
].includes(key)) {
|
|
2933
2937
|
return;
|
|
2934
2938
|
}
|
|
@@ -2941,9 +2945,18 @@ var Menu = class _Menu {
|
|
|
2941
2945
|
switch (key) {
|
|
2942
2946
|
case "Tab":
|
|
2943
2947
|
case "Escape":
|
|
2944
|
-
case "ArrowLeft":
|
|
2945
2948
|
this.close();
|
|
2946
2949
|
return;
|
|
2950
|
+
case "ArrowLeft":
|
|
2951
|
+
if (this.#isMenubar) {
|
|
2952
|
+
this.#closeAndMoveFocus("previous");
|
|
2953
|
+
} else if (this.#isSubmenu && this.#triggerElement) {
|
|
2954
|
+
this.close();
|
|
2955
|
+
}
|
|
2956
|
+
return;
|
|
2957
|
+
case "ArrowRight":
|
|
2958
|
+
this.#closeAndMoveFocus("next");
|
|
2959
|
+
return;
|
|
2947
2960
|
case "Enter":
|
|
2948
2961
|
case " ":
|
|
2949
2962
|
active.click();
|
|
@@ -2983,7 +2996,7 @@ var Menu = class _Menu {
|
|
|
2983
2996
|
i.setAttribute("aria-checked", String(i === item));
|
|
2984
2997
|
});
|
|
2985
2998
|
};
|
|
2986
|
-
#toggle(isOpen,
|
|
2999
|
+
#toggle(isOpen, options = {}) {
|
|
2987
3000
|
if (this.#triggerElement?.ariaExpanded === String(isOpen)) {
|
|
2988
3001
|
return;
|
|
2989
3002
|
}
|
|
@@ -3008,7 +3021,8 @@ var Menu = class _Menu {
|
|
|
3008
3021
|
style.setProperty("display", "block");
|
|
3009
3022
|
style.setProperty("opacity", "0");
|
|
3010
3023
|
this.#triggerElement && this.#updatePopover();
|
|
3011
|
-
|
|
3024
|
+
const { initialFocus = "first" } = options;
|
|
3025
|
+
this.#itemElements.filter(isFocusable3).at(initialFocus === "first" ? 0 : -1)?.focus();
|
|
3012
3026
|
} else {
|
|
3013
3027
|
this.#clearSubmenuTimer();
|
|
3014
3028
|
this.#focusTrigger();
|
|
@@ -3073,8 +3087,18 @@ var Menu = class _Menu {
|
|
|
3073
3087
|
this.#submenuTimer = void 0;
|
|
3074
3088
|
}
|
|
3075
3089
|
}
|
|
3090
|
+
#closeAndMoveFocus(direction) {
|
|
3091
|
+
this.close();
|
|
3092
|
+
requestAnimationFrame(
|
|
3093
|
+
() => (this.#externalTrigger ?? this.#triggerElement)?.dispatchEvent(
|
|
3094
|
+
new KeyboardEvent("keydown", {
|
|
3095
|
+
key: direction === "previous" ? "ArrowLeft" : "ArrowRight"
|
|
3096
|
+
})
|
|
3097
|
+
)
|
|
3098
|
+
);
|
|
3099
|
+
}
|
|
3076
3100
|
#containsRoot(element) {
|
|
3077
|
-
return this.#rootElement.contains(element) || this.#listElement?.contains(element);
|
|
3101
|
+
return this.#rootElement.contains(element) || !!this.#listElement?.contains(element);
|
|
3078
3102
|
}
|
|
3079
3103
|
#focusTrigger() {
|
|
3080
3104
|
const active = getActiveElement4();
|
|
@@ -3237,7 +3261,7 @@ function isFocusable3(element) {
|
|
|
3237
3261
|
* WAI-ARIA compliant menu (menu button) pattern implementation in TypeScript.
|
|
3238
3262
|
* Supports checkbox item, radio item, and infinitely nested menus.
|
|
3239
3263
|
*
|
|
3240
|
-
* @version 1.
|
|
3264
|
+
* @version 1.5.1
|
|
3241
3265
|
* @author Yusuke Kamiyamane
|
|
3242
3266
|
* @license MIT
|
|
3243
3267
|
* @copyright Copyright (c) Yusuke Kamiyamane
|