@diwauhris/ui 1.7.20 → 1.7.22
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/lib/index.cjs +64 -18
- package/lib/index.mjs +64 -18
- package/lib/types/ui-library/gallery/menu/Menu.d.ts +21 -4
- package/package.json +1 -1
package/lib/index.cjs
CHANGED
|
@@ -3924,9 +3924,15 @@ function Dropdown({ trigger, isOpen, onToggle, onClose, children, triggerClassNa
|
|
|
3924
3924
|
* Menu — Design System Component
|
|
3925
3925
|
*
|
|
3926
3926
|
* A dropdown menu anchored to a trigger. Opens on trigger click,
|
|
3927
|
-
* closes on outside-click or
|
|
3928
|
-
* ArrowDown/ArrowUp
|
|
3929
|
-
* Escape
|
|
3927
|
+
* closes on outside-click, Escape, or scroll. Full keyboard navigation:
|
|
3928
|
+
* ArrowDown/ArrowUp move between items (skipping disabled), Home/End jump to
|
|
3929
|
+
* the first/last enabled item, Escape closes. When focus is on a closed
|
|
3930
|
+
* trigger, Enter/Space/ArrowDown open it and focus the first item; ArrowUp
|
|
3931
|
+
* opens it and focuses the last item.
|
|
3932
|
+
*
|
|
3933
|
+
* The panel renders in a body portal with fixed positioning: it flips above
|
|
3934
|
+
* the trigger when there isn't room below, and clamps horizontally so it never
|
|
3935
|
+
* overflows the viewport.
|
|
3930
3936
|
*
|
|
3931
3937
|
* Follows WAI-ARIA Menu Button pattern:
|
|
3932
3938
|
* - trigger: role="button", aria-haspopup="menu", aria-expanded
|
|
@@ -3942,7 +3948,7 @@ function Dropdown({ trigger, isOpen, onToggle, onClose, children, triggerClassNa
|
|
|
3942
3948
|
* ]}
|
|
3943
3949
|
* />
|
|
3944
3950
|
*/
|
|
3945
|
-
function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: controlledOpen, onOpenChange, panelClassName = "" }) {
|
|
3951
|
+
function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: controlledOpen, onOpenChange, closeOnSelect = true, panelClassName = "" }) {
|
|
3946
3952
|
const [internalOpen, setInternalOpen] = (0, react.useState)(false);
|
|
3947
3953
|
const isControlled = controlledOpen !== void 0;
|
|
3948
3954
|
const open = isControlled ? controlledOpen : internalOpen;
|
|
@@ -3955,6 +3961,7 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
3955
3961
|
const panelRef = (0, react.useRef)(null);
|
|
3956
3962
|
const itemRefs = (0, react.useRef)([]);
|
|
3957
3963
|
const [panelStyle, setPanelStyle] = (0, react.useState)({});
|
|
3964
|
+
const pendingFocusRef = (0, react.useRef)("first");
|
|
3958
3965
|
(0, react.useEffect)(() => {
|
|
3959
3966
|
if (!open || !triggerRef.current) return;
|
|
3960
3967
|
const GAP = 4;
|
|
@@ -3969,21 +3976,28 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
3969
3976
|
const panelHeight = panelRef.current?.offsetHeight ?? items.length * ROW_HEIGHT + HEADER_HEIGHT;
|
|
3970
3977
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
3971
3978
|
const flipUp = spaceBelow < panelHeight + GAP + MARGIN && rect.top > spaceBelow;
|
|
3979
|
+
const available = window.innerWidth - 16;
|
|
3980
|
+
const horizontal = align === "left" ? {
|
|
3981
|
+
left: Math.max(MARGIN, Math.min(rect.left, window.innerWidth - MARGIN)),
|
|
3982
|
+
right: "auto"
|
|
3983
|
+
} : {
|
|
3984
|
+
right: Math.max(MARGIN, window.innerWidth - rect.right),
|
|
3985
|
+
left: "auto"
|
|
3986
|
+
};
|
|
3972
3987
|
setPanelStyle({
|
|
3973
3988
|
position: "fixed",
|
|
3974
3989
|
...flipUp ? { bottom: Math.max(MARGIN, window.innerHeight - rect.top + GAP) } : { top: rect.bottom + GAP },
|
|
3975
|
-
|
|
3990
|
+
...horizontal,
|
|
3976
3991
|
zIndex: 9999,
|
|
3977
|
-
minWidth:
|
|
3992
|
+
minWidth: PANEL_MIN_WIDTH,
|
|
3993
|
+
maxWidth: available
|
|
3978
3994
|
});
|
|
3979
3995
|
};
|
|
3980
3996
|
reposition();
|
|
3981
3997
|
const raf = window.requestAnimationFrame(reposition);
|
|
3982
|
-
window.addEventListener("scroll", reposition, true);
|
|
3983
3998
|
window.addEventListener("resize", reposition);
|
|
3984
3999
|
return () => {
|
|
3985
4000
|
window.cancelAnimationFrame(raf);
|
|
3986
|
-
window.removeEventListener("scroll", reposition, true);
|
|
3987
4001
|
window.removeEventListener("resize", reposition);
|
|
3988
4002
|
};
|
|
3989
4003
|
}, [
|
|
@@ -4007,33 +4021,64 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
4007
4021
|
(triggerRef.current?.querySelector("button,a,[tabindex]"))?.focus();
|
|
4008
4022
|
}
|
|
4009
4023
|
};
|
|
4024
|
+
const handleScroll = (e) => {
|
|
4025
|
+
if (panelRef.current && e.target instanceof Node && panelRef.current.contains(e.target)) return;
|
|
4026
|
+
setOpen(false);
|
|
4027
|
+
};
|
|
4010
4028
|
document.addEventListener("mousedown", handlePointerDown);
|
|
4011
4029
|
document.addEventListener("keydown", handleKeyDown);
|
|
4030
|
+
window.addEventListener("scroll", handleScroll, true);
|
|
4012
4031
|
window.setTimeout(() => {
|
|
4013
|
-
itemRefs.current.
|
|
4032
|
+
const enabled = itemRefs.current.map((node, i) => ({
|
|
4033
|
+
node,
|
|
4034
|
+
i
|
|
4035
|
+
})).filter((x) => x.node && !x.node.disabled);
|
|
4036
|
+
(pendingFocusRef.current === "last" ? enabled[enabled.length - 1] : enabled[0])?.node?.focus();
|
|
4037
|
+
pendingFocusRef.current = "first";
|
|
4014
4038
|
}, 0);
|
|
4015
4039
|
return () => {
|
|
4016
4040
|
document.removeEventListener("mousedown", handlePointerDown);
|
|
4017
4041
|
document.removeEventListener("keydown", handleKeyDown);
|
|
4042
|
+
window.removeEventListener("scroll", handleScroll, true);
|
|
4018
4043
|
};
|
|
4019
4044
|
}, [open]);
|
|
4020
|
-
const
|
|
4021
|
-
const
|
|
4022
|
-
|
|
4045
|
+
const focusFrom = (index, dir) => {
|
|
4046
|
+
const n = items.length;
|
|
4047
|
+
if (n === 0) return;
|
|
4048
|
+
for (let step = 0; step < n; step++) {
|
|
4049
|
+
const next = ((index + dir * step) % n + n) % n;
|
|
4050
|
+
const node = itemRefs.current[next];
|
|
4051
|
+
if (node && !node.disabled) {
|
|
4052
|
+
node.focus();
|
|
4053
|
+
return;
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4023
4056
|
};
|
|
4024
4057
|
const handleItemKeyDown = (e, index) => {
|
|
4025
4058
|
if (e.key === "ArrowDown") {
|
|
4026
4059
|
e.preventDefault();
|
|
4027
|
-
|
|
4060
|
+
focusFrom(index + 1, 1);
|
|
4028
4061
|
} else if (e.key === "ArrowUp") {
|
|
4029
4062
|
e.preventDefault();
|
|
4030
|
-
|
|
4063
|
+
focusFrom(index - 1, -1);
|
|
4031
4064
|
} else if (e.key === "Home") {
|
|
4032
4065
|
e.preventDefault();
|
|
4033
|
-
|
|
4066
|
+
focusFrom(0, 1);
|
|
4034
4067
|
} else if (e.key === "End") {
|
|
4035
4068
|
e.preventDefault();
|
|
4036
|
-
|
|
4069
|
+
focusFrom(items.length - 1, -1);
|
|
4070
|
+
}
|
|
4071
|
+
};
|
|
4072
|
+
const handleTriggerKeyDown = (e) => {
|
|
4073
|
+
if (open) return;
|
|
4074
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " " || e.key === "Spacebar") {
|
|
4075
|
+
e.preventDefault();
|
|
4076
|
+
pendingFocusRef.current = "first";
|
|
4077
|
+
setOpen(true);
|
|
4078
|
+
} else if (e.key === "ArrowUp") {
|
|
4079
|
+
e.preventDefault();
|
|
4080
|
+
pendingFocusRef.current = "last";
|
|
4081
|
+
setOpen(true);
|
|
4037
4082
|
}
|
|
4038
4083
|
};
|
|
4039
4084
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -4042,6 +4087,7 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
4042
4087
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4043
4088
|
ref: triggerRef,
|
|
4044
4089
|
onClick: () => setOpen(!open),
|
|
4090
|
+
onKeyDown: handleTriggerKeyDown,
|
|
4045
4091
|
className: "cursor-pointer",
|
|
4046
4092
|
children: (0, react.isValidElement)(trigger) ? (0, react.cloneElement)(trigger, {
|
|
4047
4093
|
"aria-expanded": open,
|
|
@@ -4061,7 +4107,7 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
4061
4107
|
role: "menuitem",
|
|
4062
4108
|
disabled: item.disabled,
|
|
4063
4109
|
onClick: () => {
|
|
4064
|
-
setOpen(false);
|
|
4110
|
+
if (closeOnSelect) setOpen(false);
|
|
4065
4111
|
item.onClick();
|
|
4066
4112
|
},
|
|
4067
4113
|
onKeyDown: (e) => handleItemKeyDown(e, index),
|
|
@@ -4077,7 +4123,7 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
4077
4123
|
"aria-hidden": "true",
|
|
4078
4124
|
children: item.icon
|
|
4079
4125
|
}), item.label]
|
|
4080
|
-
}, item.label))
|
|
4126
|
+
}, item.id ?? item.label ?? index))
|
|
4081
4127
|
}), document.body)]
|
|
4082
4128
|
});
|
|
4083
4129
|
}
|
package/lib/index.mjs
CHANGED
|
@@ -3900,9 +3900,15 @@ function Dropdown({ trigger, isOpen, onToggle, onClose, children, triggerClassNa
|
|
|
3900
3900
|
* Menu — Design System Component
|
|
3901
3901
|
*
|
|
3902
3902
|
* A dropdown menu anchored to a trigger. Opens on trigger click,
|
|
3903
|
-
* closes on outside-click or
|
|
3904
|
-
* ArrowDown/ArrowUp
|
|
3905
|
-
* Escape
|
|
3903
|
+
* closes on outside-click, Escape, or scroll. Full keyboard navigation:
|
|
3904
|
+
* ArrowDown/ArrowUp move between items (skipping disabled), Home/End jump to
|
|
3905
|
+
* the first/last enabled item, Escape closes. When focus is on a closed
|
|
3906
|
+
* trigger, Enter/Space/ArrowDown open it and focus the first item; ArrowUp
|
|
3907
|
+
* opens it and focuses the last item.
|
|
3908
|
+
*
|
|
3909
|
+
* The panel renders in a body portal with fixed positioning: it flips above
|
|
3910
|
+
* the trigger when there isn't room below, and clamps horizontally so it never
|
|
3911
|
+
* overflows the viewport.
|
|
3906
3912
|
*
|
|
3907
3913
|
* Follows WAI-ARIA Menu Button pattern:
|
|
3908
3914
|
* - trigger: role="button", aria-haspopup="menu", aria-expanded
|
|
@@ -3918,7 +3924,7 @@ function Dropdown({ trigger, isOpen, onToggle, onClose, children, triggerClassNa
|
|
|
3918
3924
|
* ]}
|
|
3919
3925
|
* />
|
|
3920
3926
|
*/
|
|
3921
|
-
function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: controlledOpen, onOpenChange, panelClassName = "" }) {
|
|
3927
|
+
function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: controlledOpen, onOpenChange, closeOnSelect = true, panelClassName = "" }) {
|
|
3922
3928
|
const [internalOpen, setInternalOpen] = useState(false);
|
|
3923
3929
|
const isControlled = controlledOpen !== void 0;
|
|
3924
3930
|
const open = isControlled ? controlledOpen : internalOpen;
|
|
@@ -3931,6 +3937,7 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
3931
3937
|
const panelRef = useRef(null);
|
|
3932
3938
|
const itemRefs = useRef([]);
|
|
3933
3939
|
const [panelStyle, setPanelStyle] = useState({});
|
|
3940
|
+
const pendingFocusRef = useRef("first");
|
|
3934
3941
|
useEffect(() => {
|
|
3935
3942
|
if (!open || !triggerRef.current) return;
|
|
3936
3943
|
const GAP = 4;
|
|
@@ -3945,21 +3952,28 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
3945
3952
|
const panelHeight = panelRef.current?.offsetHeight ?? items.length * ROW_HEIGHT + HEADER_HEIGHT;
|
|
3946
3953
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
3947
3954
|
const flipUp = spaceBelow < panelHeight + GAP + MARGIN && rect.top > spaceBelow;
|
|
3955
|
+
const available = window.innerWidth - 16;
|
|
3956
|
+
const horizontal = align === "left" ? {
|
|
3957
|
+
left: Math.max(MARGIN, Math.min(rect.left, window.innerWidth - MARGIN)),
|
|
3958
|
+
right: "auto"
|
|
3959
|
+
} : {
|
|
3960
|
+
right: Math.max(MARGIN, window.innerWidth - rect.right),
|
|
3961
|
+
left: "auto"
|
|
3962
|
+
};
|
|
3948
3963
|
setPanelStyle({
|
|
3949
3964
|
position: "fixed",
|
|
3950
3965
|
...flipUp ? { bottom: Math.max(MARGIN, window.innerHeight - rect.top + GAP) } : { top: rect.bottom + GAP },
|
|
3951
|
-
|
|
3966
|
+
...horizontal,
|
|
3952
3967
|
zIndex: 9999,
|
|
3953
|
-
minWidth:
|
|
3968
|
+
minWidth: PANEL_MIN_WIDTH,
|
|
3969
|
+
maxWidth: available
|
|
3954
3970
|
});
|
|
3955
3971
|
};
|
|
3956
3972
|
reposition();
|
|
3957
3973
|
const raf = window.requestAnimationFrame(reposition);
|
|
3958
|
-
window.addEventListener("scroll", reposition, true);
|
|
3959
3974
|
window.addEventListener("resize", reposition);
|
|
3960
3975
|
return () => {
|
|
3961
3976
|
window.cancelAnimationFrame(raf);
|
|
3962
|
-
window.removeEventListener("scroll", reposition, true);
|
|
3963
3977
|
window.removeEventListener("resize", reposition);
|
|
3964
3978
|
};
|
|
3965
3979
|
}, [
|
|
@@ -3983,33 +3997,64 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
3983
3997
|
(triggerRef.current?.querySelector("button,a,[tabindex]"))?.focus();
|
|
3984
3998
|
}
|
|
3985
3999
|
};
|
|
4000
|
+
const handleScroll = (e) => {
|
|
4001
|
+
if (panelRef.current && e.target instanceof Node && panelRef.current.contains(e.target)) return;
|
|
4002
|
+
setOpen(false);
|
|
4003
|
+
};
|
|
3986
4004
|
document.addEventListener("mousedown", handlePointerDown);
|
|
3987
4005
|
document.addEventListener("keydown", handleKeyDown);
|
|
4006
|
+
window.addEventListener("scroll", handleScroll, true);
|
|
3988
4007
|
window.setTimeout(() => {
|
|
3989
|
-
itemRefs.current.
|
|
4008
|
+
const enabled = itemRefs.current.map((node, i) => ({
|
|
4009
|
+
node,
|
|
4010
|
+
i
|
|
4011
|
+
})).filter((x) => x.node && !x.node.disabled);
|
|
4012
|
+
(pendingFocusRef.current === "last" ? enabled[enabled.length - 1] : enabled[0])?.node?.focus();
|
|
4013
|
+
pendingFocusRef.current = "first";
|
|
3990
4014
|
}, 0);
|
|
3991
4015
|
return () => {
|
|
3992
4016
|
document.removeEventListener("mousedown", handlePointerDown);
|
|
3993
4017
|
document.removeEventListener("keydown", handleKeyDown);
|
|
4018
|
+
window.removeEventListener("scroll", handleScroll, true);
|
|
3994
4019
|
};
|
|
3995
4020
|
}, [open]);
|
|
3996
|
-
const
|
|
3997
|
-
const
|
|
3998
|
-
|
|
4021
|
+
const focusFrom = (index, dir) => {
|
|
4022
|
+
const n = items.length;
|
|
4023
|
+
if (n === 0) return;
|
|
4024
|
+
for (let step = 0; step < n; step++) {
|
|
4025
|
+
const next = ((index + dir * step) % n + n) % n;
|
|
4026
|
+
const node = itemRefs.current[next];
|
|
4027
|
+
if (node && !node.disabled) {
|
|
4028
|
+
node.focus();
|
|
4029
|
+
return;
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
3999
4032
|
};
|
|
4000
4033
|
const handleItemKeyDown = (e, index) => {
|
|
4001
4034
|
if (e.key === "ArrowDown") {
|
|
4002
4035
|
e.preventDefault();
|
|
4003
|
-
|
|
4036
|
+
focusFrom(index + 1, 1);
|
|
4004
4037
|
} else if (e.key === "ArrowUp") {
|
|
4005
4038
|
e.preventDefault();
|
|
4006
|
-
|
|
4039
|
+
focusFrom(index - 1, -1);
|
|
4007
4040
|
} else if (e.key === "Home") {
|
|
4008
4041
|
e.preventDefault();
|
|
4009
|
-
|
|
4042
|
+
focusFrom(0, 1);
|
|
4010
4043
|
} else if (e.key === "End") {
|
|
4011
4044
|
e.preventDefault();
|
|
4012
|
-
|
|
4045
|
+
focusFrom(items.length - 1, -1);
|
|
4046
|
+
}
|
|
4047
|
+
};
|
|
4048
|
+
const handleTriggerKeyDown = (e) => {
|
|
4049
|
+
if (open) return;
|
|
4050
|
+
if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " " || e.key === "Spacebar") {
|
|
4051
|
+
e.preventDefault();
|
|
4052
|
+
pendingFocusRef.current = "first";
|
|
4053
|
+
setOpen(true);
|
|
4054
|
+
} else if (e.key === "ArrowUp") {
|
|
4055
|
+
e.preventDefault();
|
|
4056
|
+
pendingFocusRef.current = "last";
|
|
4057
|
+
setOpen(true);
|
|
4013
4058
|
}
|
|
4014
4059
|
};
|
|
4015
4060
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -4018,6 +4063,7 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
4018
4063
|
children: [/* @__PURE__ */ jsx("div", {
|
|
4019
4064
|
ref: triggerRef,
|
|
4020
4065
|
onClick: () => setOpen(!open),
|
|
4066
|
+
onKeyDown: handleTriggerKeyDown,
|
|
4021
4067
|
className: "cursor-pointer",
|
|
4022
4068
|
children: isValidElement(trigger) ? cloneElement(trigger, {
|
|
4023
4069
|
"aria-expanded": open,
|
|
@@ -4037,7 +4083,7 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
4037
4083
|
role: "menuitem",
|
|
4038
4084
|
disabled: item.disabled,
|
|
4039
4085
|
onClick: () => {
|
|
4040
|
-
setOpen(false);
|
|
4086
|
+
if (closeOnSelect) setOpen(false);
|
|
4041
4087
|
item.onClick();
|
|
4042
4088
|
},
|
|
4043
4089
|
onKeyDown: (e) => handleItemKeyDown(e, index),
|
|
@@ -4053,7 +4099,7 @@ function Menu({ trigger, items, ariaLabel = "Menu", align = "right", open: contr
|
|
|
4053
4099
|
"aria-hidden": "true",
|
|
4054
4100
|
children: item.icon
|
|
4055
4101
|
}), item.label]
|
|
4056
|
-
}, item.label))
|
|
4102
|
+
}, item.id ?? item.label ?? index))
|
|
4057
4103
|
}), document.body)]
|
|
4058
4104
|
});
|
|
4059
4105
|
}
|
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
* Menu — Design System Component
|
|
3
3
|
*
|
|
4
4
|
* A dropdown menu anchored to a trigger. Opens on trigger click,
|
|
5
|
-
* closes on outside-click or
|
|
6
|
-
* ArrowDown/ArrowUp
|
|
7
|
-
* Escape
|
|
5
|
+
* closes on outside-click, Escape, or scroll. Full keyboard navigation:
|
|
6
|
+
* ArrowDown/ArrowUp move between items (skipping disabled), Home/End jump to
|
|
7
|
+
* the first/last enabled item, Escape closes. When focus is on a closed
|
|
8
|
+
* trigger, Enter/Space/ArrowDown open it and focus the first item; ArrowUp
|
|
9
|
+
* opens it and focuses the last item.
|
|
10
|
+
*
|
|
11
|
+
* The panel renders in a body portal with fixed positioning: it flips above
|
|
12
|
+
* the trigger when there isn't room below, and clamps horizontally so it never
|
|
13
|
+
* overflows the viewport.
|
|
8
14
|
*
|
|
9
15
|
* Follows WAI-ARIA Menu Button pattern:
|
|
10
16
|
* - trigger: role="button", aria-haspopup="menu", aria-expanded
|
|
@@ -23,6 +29,12 @@
|
|
|
23
29
|
import { type ReactNode } from 'react';
|
|
24
30
|
export type MenuItemTone = 'default' | 'danger';
|
|
25
31
|
export interface MenuItem {
|
|
32
|
+
/**
|
|
33
|
+
* Stable identity for the item. Used for the React key and focus tracking.
|
|
34
|
+
* Recommended when labels can repeat or the item list is dynamic; falls back
|
|
35
|
+
* to `label` then index when omitted.
|
|
36
|
+
*/
|
|
37
|
+
id?: string;
|
|
26
38
|
label: string;
|
|
27
39
|
icon?: ReactNode;
|
|
28
40
|
onClick: () => void;
|
|
@@ -46,7 +58,12 @@ export interface MenuProps {
|
|
|
46
58
|
open?: boolean;
|
|
47
59
|
/** Called when the menu should open or close. */
|
|
48
60
|
onOpenChange?: (open: boolean) => void;
|
|
61
|
+
/**
|
|
62
|
+
* Close the menu after an item is selected. Default `true`.
|
|
63
|
+
* Set `false` for menus that stay open across clicks (e.g. multi-toggle filters).
|
|
64
|
+
*/
|
|
65
|
+
closeOnSelect?: boolean;
|
|
49
66
|
/** Additional class on the floating panel. */
|
|
50
67
|
panelClassName?: string;
|
|
51
68
|
}
|
|
52
|
-
export declare function Menu({ trigger, items, ariaLabel, align, open: controlledOpen, onOpenChange, panelClassName, }: MenuProps): import("react").JSX.Element;
|
|
69
|
+
export declare function Menu({ trigger, items, ariaLabel, align, open: controlledOpen, onOpenChange, closeOnSelect, panelClassName, }: MenuProps): import("react").JSX.Element;
|