@versini/ui-menu 5.0.1 โ 5.1.0
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 +73 -10
- package/dist/chunks/index.dzHBmDC4.js +61 -0
- package/dist/components/Menu/Menu.js +214 -82
- package/dist/components/Menu/MenuContext.js +3 -1
- package/dist/components/Menu/MenuItem.js +70 -102
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -3
- package/package.json +7 -11
package/README.md
CHANGED
|
@@ -18,10 +18,11 @@ The Menu package provides dropdown menus and navigation components with full key
|
|
|
18
18
|
## Features
|
|
19
19
|
|
|
20
20
|
- **๐ Composable**: `Menu`, `MenuItem`, `MenuSeparator`, `MenuGroupLabel`
|
|
21
|
+
- **๐ Nested Sub-menus**: Support for multi-level menu hierarchies with automatic positioning
|
|
21
22
|
- **โฟ Accessible**: Built with Floating UI & ARIA roles for robust a11y
|
|
22
23
|
- **โจ๏ธ Keyboard Support**: Arrow navigation, typeahead matching, ESC / click outside close
|
|
23
24
|
- **๐จ Theme & Focus Modes**: Trigger inherits color + separate focus styling
|
|
24
|
-
- **๐งญ Smart Positioning**: Auto flip / shift to remain within viewport
|
|
25
|
+
- **๐งญ Smart Positioning**: Auto flip / shift to remain within viewport, responsive spacing
|
|
25
26
|
- **๐งช Type Safe**: Strongly typed props & label-based typeahead
|
|
26
27
|
|
|
27
28
|
## Installation
|
|
@@ -124,19 +125,81 @@ function AccountMenu() {
|
|
|
124
125
|
</Menu>
|
|
125
126
|
```
|
|
126
127
|
|
|
128
|
+
### Nested Sub-menus
|
|
129
|
+
|
|
130
|
+
Create hierarchical menus by nesting `Menu` components. Simply use a `Menu` with a `label` prop but no `trigger` to create a sub-menu:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { Menu, MenuItem, MenuGroupLabel } from "@versini/ui-menu";
|
|
134
|
+
import { ButtonIcon } from "@versini/ui-button";
|
|
135
|
+
import { IconSettings, IconOpenAI, IconAnthropic } from "@versini/ui-icons";
|
|
136
|
+
|
|
137
|
+
function SettingsMenu() {
|
|
138
|
+
const [engine, setEngine] = useState("openai");
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<Menu
|
|
142
|
+
trigger={
|
|
143
|
+
<ButtonIcon label="Settings">
|
|
144
|
+
<IconSettings />
|
|
145
|
+
</ButtonIcon>
|
|
146
|
+
}
|
|
147
|
+
>
|
|
148
|
+
<MenuItem label="Profile" />
|
|
149
|
+
<MenuItem label="Preferences" />
|
|
150
|
+
|
|
151
|
+
{/* Nested sub-menu */}
|
|
152
|
+
<Menu label="AI Settings">
|
|
153
|
+
<MenuGroupLabel>Engines</MenuGroupLabel>
|
|
154
|
+
<MenuItem
|
|
155
|
+
label="OpenAI"
|
|
156
|
+
icon={<IconOpenAI />}
|
|
157
|
+
selected={engine === "openai"}
|
|
158
|
+
onClick={() => setEngine("openai")}
|
|
159
|
+
/>
|
|
160
|
+
<MenuItem
|
|
161
|
+
label="Anthropic"
|
|
162
|
+
icon={<IconAnthropic />}
|
|
163
|
+
selected={engine === "anthropic"}
|
|
164
|
+
onClick={() => setEngine("anthropic")}
|
|
165
|
+
/>
|
|
166
|
+
</Menu>
|
|
167
|
+
|
|
168
|
+
<MenuItem label="About" />
|
|
169
|
+
</Menu>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Features of nested sub-menus:**
|
|
175
|
+
|
|
176
|
+
- Automatically positioned to the right (or left if no space)
|
|
177
|
+
- Visual chevron indicator (`โ`) shows expandable items
|
|
178
|
+
- Hover or click to open sub-menus
|
|
179
|
+
- Smart positioning adjusts for viewport constraints
|
|
180
|
+
- Keyboard navigation works across all levels
|
|
181
|
+
- Sibling sub-menus auto-close when opening another
|
|
182
|
+
|
|
127
183
|
## API
|
|
128
184
|
|
|
129
185
|
### Menu Props
|
|
130
186
|
|
|
131
|
-
| Prop | Type
|
|
132
|
-
| ------------------ |
|
|
133
|
-
| `trigger` | `React.ReactNode`
|
|
134
|
-
| `children` | `React.ReactNode`
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `onOpenChange` | `(open: boolean) => void`
|
|
187
|
+
| Prop | Type | Default | Description |
|
|
188
|
+
| ------------------ | ----------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------- |
|
|
189
|
+
| `trigger` | `React.ReactNode` | - | Element used to open the menu (Button / ButtonIcon / custom). Optional for nested sub-menus. |
|
|
190
|
+
| `children` | `React.ReactNode` | - | One or more `MenuItem` / `MenuSeparator` / `Menu` (for sub-menus) / custom nodes. |
|
|
191
|
+
| `label` | `string` | `"Open menu"` | Accessible label for the trigger. When used without `trigger`, creates a nested sub-menu item. |
|
|
192
|
+
| `defaultPlacement` | `Placement` (Floating UI) | `"bottom-start"` | Initial preferred placement (only applies to root menu). |
|
|
193
|
+
| `mode` | `"dark" \| "light" \| "system" \| "alt-system"` | `"system"` | Color mode of trigger (when using UI buttons). |
|
|
194
|
+
| `focusMode` | `"dark" \| "light" \| "system" \| "alt-system"` | `"system"` | Focus ring thematic mode (when using UI buttons). |
|
|
195
|
+
| `onOpenChange` | `(open: boolean) => void` | - | Called when menu opens or closes. |
|
|
196
|
+
|
|
197
|
+
**Creating nested sub-menus:**
|
|
198
|
+
|
|
199
|
+
- Use `Menu` with `label` but without `trigger` to create a sub-menu item
|
|
200
|
+
- Sub-menus automatically show a chevron (`โ`) indicator
|
|
201
|
+
- Positioning is automatically handled (right-start, flips to left if needed)
|
|
202
|
+
- Hover or click to open nested menus
|
|
140
203
|
|
|
141
204
|
### MenuItem Props
|
|
142
205
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsxs as I, jsx as a } from "react/jsx-runtime";
|
|
2
|
+
import m from "clsx";
|
|
3
|
+
const d = ({
|
|
4
|
+
children: o,
|
|
5
|
+
fill: s,
|
|
6
|
+
viewBox: t,
|
|
7
|
+
className: n,
|
|
8
|
+
defaultViewBox: _,
|
|
9
|
+
size: l,
|
|
10
|
+
title: e,
|
|
11
|
+
semantic: i = !1,
|
|
12
|
+
...r
|
|
13
|
+
}) => {
|
|
14
|
+
const c = m(l, n);
|
|
15
|
+
return /* @__PURE__ */ I(
|
|
16
|
+
"svg",
|
|
17
|
+
{
|
|
18
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
19
|
+
className: c,
|
|
20
|
+
viewBox: t || _,
|
|
21
|
+
fill: s || "currentColor",
|
|
22
|
+
role: "img",
|
|
23
|
+
"aria-hidden": !i,
|
|
24
|
+
focusable: !1,
|
|
25
|
+
...r,
|
|
26
|
+
children: [
|
|
27
|
+
e && i && /* @__PURE__ */ a("title", { children: e }),
|
|
28
|
+
o
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
/*!
|
|
34
|
+
@versini/ui-svgicon v4.2.2
|
|
35
|
+
ยฉ 2025 gizmette.com
|
|
36
|
+
*/
|
|
37
|
+
try {
|
|
38
|
+
window.__VERSINI_UI_SVGICON__ || (window.__VERSINI_UI_SVGICON__ = {
|
|
39
|
+
version: "4.2.2",
|
|
40
|
+
buildTime: "10/19/2025 01:03 PM EDT",
|
|
41
|
+
homepage: "https://github.com/aversini/ui-components",
|
|
42
|
+
license: "MIT"
|
|
43
|
+
});
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
/*!
|
|
47
|
+
@versini/ui-icons v4.13.0
|
|
48
|
+
ยฉ 2025 gizmette.com
|
|
49
|
+
*/
|
|
50
|
+
try {
|
|
51
|
+
window.__VERSINI_UI_ICONS__ || (window.__VERSINI_UI_ICONS__ = {
|
|
52
|
+
version: "4.13.0",
|
|
53
|
+
buildTime: "10/19/2025 01:03 PM EDT",
|
|
54
|
+
homepage: "https://github.com/aversini/ui-components",
|
|
55
|
+
license: "MIT"
|
|
56
|
+
});
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
d as I
|
|
61
|
+
};
|
|
@@ -1,110 +1,242 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useFloatingTree as
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
1
|
+
import { jsxs as b, jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import { useFloatingTree as se, useFloatingNodeId as ie, useFloatingParentNodeId as W, useListItem as re, useFloating as ae, autoUpdate as le, offset as de, flip as ue, shift as ce, useHover as fe, safePolygon as me, useClick as pe, useRole as he, useDismiss as ve, useListNavigation as ge, useTypeahead as we, useInteractions as ye, useMergeRefs as be, FloatingNode as D, FloatingList as S, FloatingPortal as _, FloatingFocusManager as K, FloatingTree as xe } from "@floating-ui/react";
|
|
3
|
+
import { I as Ie } from "../../chunks/index.dzHBmDC4.js";
|
|
4
|
+
import Fe from "clsx";
|
|
5
|
+
import Ne, { forwardRef as q, useState as y, useRef as U, useContext as Me, useEffect as P } from "react";
|
|
6
|
+
import { MenuContext as R } from "./MenuContext.js";
|
|
7
|
+
const Ee = ({
|
|
8
|
+
className: e,
|
|
9
|
+
viewBox: n,
|
|
10
|
+
title: d,
|
|
11
|
+
monotone: x,
|
|
12
|
+
...f
|
|
13
|
+
}) => /* @__PURE__ */ b(
|
|
14
|
+
Ie,
|
|
15
|
+
{
|
|
16
|
+
defaultViewBox: "0 0 448 512",
|
|
17
|
+
size: "size-5",
|
|
18
|
+
viewBox: n,
|
|
19
|
+
className: e,
|
|
20
|
+
title: d || "Next",
|
|
21
|
+
...f,
|
|
22
|
+
children: [
|
|
23
|
+
/* @__PURE__ */ t(
|
|
24
|
+
"path",
|
|
25
|
+
{
|
|
26
|
+
d: "M0 256c0 17.7 14.3 32 32 32h306.7l32-32-32-32H32c-17.7 0-32 14.3-32 32",
|
|
27
|
+
opacity: x ? "1" : "0.4"
|
|
28
|
+
}
|
|
29
|
+
),
|
|
30
|
+
/* @__PURE__ */ t("path", { d: "M438.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L370.7 256 233.4 118.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l160 160z" })
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
), V = (e) => {
|
|
6
34
|
if (typeof e == "string")
|
|
7
35
|
return e;
|
|
8
36
|
if (typeof e == "function")
|
|
9
37
|
return e.displayName || e.name || "Component";
|
|
10
38
|
if (typeof e == "object" && e !== null && "type" in e) {
|
|
11
|
-
const
|
|
12
|
-
if (typeof
|
|
13
|
-
return
|
|
39
|
+
const n = e.type;
|
|
40
|
+
if (typeof n == "function" || typeof n == "object")
|
|
41
|
+
return n.displayName || n.name || "Component";
|
|
14
42
|
}
|
|
15
43
|
return "Element";
|
|
16
|
-
},
|
|
44
|
+
}, k = q(
|
|
17
45
|
({
|
|
18
46
|
trigger: e,
|
|
19
|
-
children:
|
|
20
|
-
label:
|
|
21
|
-
defaultPlacement:
|
|
22
|
-
onOpenChange:
|
|
23
|
-
mode:
|
|
24
|
-
focusMode:
|
|
25
|
-
...
|
|
26
|
-
},
|
|
27
|
-
const [
|
|
28
|
-
nodeId:
|
|
29
|
-
open:
|
|
30
|
-
onOpenChange: (
|
|
31
|
-
|
|
47
|
+
children: n,
|
|
48
|
+
label: d = "Open menu",
|
|
49
|
+
defaultPlacement: x = "bottom-start",
|
|
50
|
+
onOpenChange: f,
|
|
51
|
+
mode: G = "system",
|
|
52
|
+
focusMode: J = "system",
|
|
53
|
+
...h
|
|
54
|
+
}, Q) => {
|
|
55
|
+
const [o, I] = y(!1), [C, F] = y(!1), [v, g] = y(null), [m, L] = y(!1), N = U([]), M = U([]), a = Me(R), i = se(), u = ie(), p = W(), A = re({ label: d !== "Open menu" ? d : null }), c = p != null, { floatingStyles: H, refs: E, context: l } = ae({
|
|
56
|
+
nodeId: u,
|
|
57
|
+
open: o,
|
|
58
|
+
onOpenChange: (s) => {
|
|
59
|
+
I(s), f?.(s);
|
|
32
60
|
},
|
|
33
|
-
placement:
|
|
61
|
+
placement: c ? "right-start" : x,
|
|
34
62
|
strategy: "fixed",
|
|
35
|
-
middleware: [
|
|
36
|
-
|
|
37
|
-
|
|
63
|
+
middleware: [
|
|
64
|
+
de(() => c ? {
|
|
65
|
+
mainAxis: window.innerWidth < 640 ? 22 : 14,
|
|
66
|
+
alignmentAxis: -4
|
|
67
|
+
} : {
|
|
68
|
+
mainAxis: 10,
|
|
69
|
+
alignmentAxis: 0
|
|
70
|
+
}),
|
|
71
|
+
ue(),
|
|
72
|
+
ce()
|
|
73
|
+
],
|
|
74
|
+
whileElementsMounted: le
|
|
75
|
+
}), X = fe(l, {
|
|
76
|
+
enabled: c && m,
|
|
77
|
+
delay: { open: 75 },
|
|
78
|
+
handleClose: me({ blockPointerEvents: !0 })
|
|
79
|
+
}), Y = pe(l, {
|
|
38
80
|
event: "mousedown",
|
|
39
|
-
toggle: !
|
|
40
|
-
ignoreMouse:
|
|
41
|
-
}),
|
|
42
|
-
listRef:
|
|
43
|
-
activeIndex:
|
|
44
|
-
nested:
|
|
45
|
-
onNavigate:
|
|
81
|
+
toggle: !c || !m,
|
|
82
|
+
ignoreMouse: c && m
|
|
83
|
+
}), Z = he(l, { role: "menu" }), $ = ve(l, { bubbles: !0 }), ee = ge(l, {
|
|
84
|
+
listRef: N,
|
|
85
|
+
activeIndex: v,
|
|
86
|
+
nested: c,
|
|
87
|
+
onNavigate: g,
|
|
46
88
|
loop: !0
|
|
47
|
-
}),
|
|
48
|
-
listRef:
|
|
49
|
-
onMatch:
|
|
50
|
-
activeIndex:
|
|
51
|
-
}), { getReferenceProps:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
89
|
+
}), te = we(l, {
|
|
90
|
+
listRef: M,
|
|
91
|
+
onMatch: o ? g : void 0,
|
|
92
|
+
activeIndex: v
|
|
93
|
+
}), { getReferenceProps: B, getFloatingProps: j, getItemProps: z } = ye([X, Y, Z, $, ee, te]), O = be([E.setReference, A.ref, Q]);
|
|
94
|
+
P(() => {
|
|
95
|
+
if (!i)
|
|
96
|
+
return;
|
|
97
|
+
function s() {
|
|
98
|
+
I(!1), f?.(!1);
|
|
99
|
+
}
|
|
100
|
+
function r(w) {
|
|
101
|
+
w.nodeId !== u && w.parentId === p && I(!1);
|
|
102
|
+
}
|
|
103
|
+
return i.events.on("click", s), i.events.on("menuopen", r), () => {
|
|
104
|
+
i.events.off("click", s), i.events.off("menuopen", r);
|
|
105
|
+
};
|
|
106
|
+
}, [i, u, p, f]), P(() => {
|
|
107
|
+
o && i && i.events.emit("menuopen", { parentId: p, nodeId: u });
|
|
108
|
+
}, [i, o, u, p]), P(() => {
|
|
109
|
+
function s({ pointerType: w }) {
|
|
110
|
+
w !== "touch" && L(!0);
|
|
111
|
+
}
|
|
112
|
+
function r() {
|
|
113
|
+
L(!1);
|
|
114
|
+
}
|
|
115
|
+
return window.addEventListener("pointermove", s, {
|
|
116
|
+
once: !0,
|
|
117
|
+
capture: !0
|
|
118
|
+
}), window.addEventListener("keydown", r, !0), () => {
|
|
119
|
+
window.removeEventListener("pointermove", s, {
|
|
120
|
+
capture: !0
|
|
121
|
+
}), window.removeEventListener("keydown", r, !0);
|
|
122
|
+
};
|
|
123
|
+
}, []);
|
|
124
|
+
const T = V(e) === "Button" || V(e) === "ButtonIcon", ne = T ? {
|
|
125
|
+
noInternalClick: T,
|
|
126
|
+
focusMode: J,
|
|
127
|
+
mode: G
|
|
128
|
+
} : {};
|
|
129
|
+
if (c && !e) {
|
|
130
|
+
const s = Fe(
|
|
131
|
+
"items-center flex-row justify-between",
|
|
132
|
+
"m-0 first:mt-0 mt-2 sm:mt-1 px-2 py-1 flex w-full rounded-md border border-transparent text-left text-base outline-hidden focus:border focus:border-border-medium focus:bg-surface-lighter focus:underline disabled:cursor-not-allowed disabled:text-copy-medium",
|
|
133
|
+
{
|
|
134
|
+
"bg-surface-lighter": o && !C
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
return /* @__PURE__ */ b(D, { id: u, children: [
|
|
138
|
+
/* @__PURE__ */ b(
|
|
139
|
+
"button",
|
|
140
|
+
{
|
|
141
|
+
ref: O,
|
|
142
|
+
"data-open": o ? "" : void 0,
|
|
143
|
+
...B(
|
|
144
|
+
a.getItemProps({
|
|
145
|
+
...h,
|
|
146
|
+
className: s,
|
|
147
|
+
onFocus(r) {
|
|
148
|
+
h.onFocus?.(r), F(!1), a.setHasFocusInside(!0);
|
|
149
|
+
},
|
|
150
|
+
onMouseEnter(r) {
|
|
151
|
+
h.onMouseEnter?.(r), a.allowHover && a.isOpen && a.setActiveIndex(A.index);
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
),
|
|
155
|
+
children: [
|
|
156
|
+
/* @__PURE__ */ t("span", { children: d }),
|
|
157
|
+
/* @__PURE__ */ t(Ee, { className: "ml-2", size: "size-3", monotone: !0 })
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
),
|
|
161
|
+
/* @__PURE__ */ t(
|
|
162
|
+
R.Provider,
|
|
163
|
+
{
|
|
164
|
+
value: {
|
|
165
|
+
activeIndex: v,
|
|
166
|
+
setActiveIndex: g,
|
|
167
|
+
getItemProps: z,
|
|
168
|
+
setHasFocusInside: F,
|
|
169
|
+
isOpen: o,
|
|
170
|
+
allowHover: m,
|
|
171
|
+
parent: a
|
|
172
|
+
},
|
|
173
|
+
children: /* @__PURE__ */ t(S, { elementsRef: N, labelsRef: M, children: o && /* @__PURE__ */ t(_, { children: /* @__PURE__ */ t(
|
|
174
|
+
K,
|
|
175
|
+
{
|
|
176
|
+
context: l,
|
|
177
|
+
modal: !1,
|
|
178
|
+
initialFocus: -1,
|
|
179
|
+
returnFocus: !1,
|
|
180
|
+
children: /* @__PURE__ */ t(
|
|
181
|
+
"div",
|
|
182
|
+
{
|
|
183
|
+
ref: E.setFloating,
|
|
184
|
+
className: "rounded-md bg-surface-light shadow-sm shadow-border-dark outline-hidden p-4 sm:p-2",
|
|
185
|
+
style: H,
|
|
186
|
+
...j(),
|
|
187
|
+
children: n
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
) }) })
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
] });
|
|
195
|
+
}
|
|
196
|
+
const oe = Ne.cloneElement(
|
|
56
197
|
e,
|
|
57
198
|
{
|
|
58
|
-
...
|
|
59
|
-
"aria-label":
|
|
60
|
-
"data-open":
|
|
61
|
-
"data-focus-inside":
|
|
62
|
-
ref:
|
|
63
|
-
...
|
|
64
|
-
|
|
65
|
-
...
|
|
199
|
+
...ne,
|
|
200
|
+
"aria-label": d,
|
|
201
|
+
"data-open": o ? "" : void 0,
|
|
202
|
+
"data-focus-inside": C ? "" : void 0,
|
|
203
|
+
ref: O,
|
|
204
|
+
...B(
|
|
205
|
+
a.getItemProps({
|
|
206
|
+
...h
|
|
66
207
|
})
|
|
67
208
|
)
|
|
68
209
|
}
|
|
69
210
|
);
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
d(!1), l?.(!1);
|
|
75
|
-
}
|
|
76
|
-
return o.events.on("click", a), () => {
|
|
77
|
-
o.events.off("click", a);
|
|
78
|
-
};
|
|
79
|
-
}, [o, l]), v(() => {
|
|
80
|
-
n && o && o.events.emit("menuopen", { nodeId: r });
|
|
81
|
-
}, [o, n, r]), /* @__PURE__ */ G(ae, { id: r, children: [
|
|
82
|
-
z,
|
|
83
|
-
/* @__PURE__ */ s(
|
|
84
|
-
F.Provider,
|
|
211
|
+
return /* @__PURE__ */ b(D, { id: u, children: [
|
|
212
|
+
oe,
|
|
213
|
+
/* @__PURE__ */ t(
|
|
214
|
+
R.Provider,
|
|
85
215
|
{
|
|
86
216
|
value: {
|
|
87
|
-
activeIndex:
|
|
88
|
-
setActiveIndex:
|
|
89
|
-
getItemProps:
|
|
90
|
-
setHasFocusInside:
|
|
91
|
-
isOpen:
|
|
217
|
+
activeIndex: v,
|
|
218
|
+
setActiveIndex: g,
|
|
219
|
+
getItemProps: z,
|
|
220
|
+
setHasFocusInside: F,
|
|
221
|
+
isOpen: o,
|
|
222
|
+
allowHover: m,
|
|
223
|
+
parent: a
|
|
92
224
|
},
|
|
93
|
-
children: /* @__PURE__ */
|
|
94
|
-
|
|
225
|
+
children: /* @__PURE__ */ t(S, { elementsRef: N, labelsRef: M, children: o && /* @__PURE__ */ t(_, { children: /* @__PURE__ */ t(
|
|
226
|
+
K,
|
|
95
227
|
{
|
|
96
|
-
context:
|
|
228
|
+
context: l,
|
|
97
229
|
modal: !1,
|
|
98
230
|
initialFocus: 0,
|
|
99
231
|
returnFocus: !0,
|
|
100
|
-
children: /* @__PURE__ */
|
|
232
|
+
children: /* @__PURE__ */ t(
|
|
101
233
|
"div",
|
|
102
234
|
{
|
|
103
|
-
ref:
|
|
235
|
+
ref: E.setFloating,
|
|
104
236
|
className: "rounded-md bg-surface-light shadow-sm shadow-border-dark outline-hidden p-4 sm:p-2",
|
|
105
|
-
style:
|
|
106
|
-
...
|
|
107
|
-
children:
|
|
237
|
+
style: H,
|
|
238
|
+
...j(),
|
|
239
|
+
children: n
|
|
108
240
|
}
|
|
109
241
|
)
|
|
110
242
|
}
|
|
@@ -113,9 +245,9 @@ const I = (e) => {
|
|
|
113
245
|
)
|
|
114
246
|
] });
|
|
115
247
|
}
|
|
116
|
-
),
|
|
117
|
-
|
|
118
|
-
|
|
248
|
+
), Pe = q((e, n) => W() === null ? /* @__PURE__ */ t(xe, { children: /* @__PURE__ */ t(k, { ...e, ref: n }) }) : /* @__PURE__ */ t(k, { ...e, ref: n }));
|
|
249
|
+
Pe.displayName = "Menu";
|
|
250
|
+
k.displayName = "MenuComponent";
|
|
119
251
|
export {
|
|
120
|
-
|
|
252
|
+
Pe as Menu
|
|
121
253
|
};
|
|
@@ -1,66 +1,24 @@
|
|
|
1
|
-
import { jsxs as
|
|
2
|
-
import { useListItem as
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
fill: t,
|
|
9
|
-
viewBox: s,
|
|
10
|
-
className: l,
|
|
11
|
-
defaultViewBox: i,
|
|
12
|
-
size: m,
|
|
13
|
-
title: n,
|
|
14
|
-
semantic: r = !1,
|
|
15
|
-
...d
|
|
16
|
-
}) => {
|
|
17
|
-
const a = u(m, l);
|
|
18
|
-
return /* @__PURE__ */ p(
|
|
19
|
-
"svg",
|
|
20
|
-
{
|
|
21
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
22
|
-
className: a,
|
|
23
|
-
viewBox: s || i,
|
|
24
|
-
fill: t || "currentColor",
|
|
25
|
-
role: "img",
|
|
26
|
-
"aria-hidden": !r,
|
|
27
|
-
focusable: !1,
|
|
28
|
-
...d,
|
|
29
|
-
children: [
|
|
30
|
-
n && r && /* @__PURE__ */ o("title", { children: n }),
|
|
31
|
-
e
|
|
32
|
-
]
|
|
33
|
-
}
|
|
34
|
-
);
|
|
35
|
-
};
|
|
36
|
-
/*!
|
|
37
|
-
@versini/ui-svgicon v4.2.1
|
|
38
|
-
ยฉ 2025 gizmette.com
|
|
39
|
-
*/
|
|
40
|
-
try {
|
|
41
|
-
window.__VERSINI_UI_SVGICON__ || (window.__VERSINI_UI_SVGICON__ = {
|
|
42
|
-
version: "4.2.1",
|
|
43
|
-
buildTime: "08/27/2025 08:27 AM EDT",
|
|
44
|
-
homepage: "https://github.com/aversini/ui-components",
|
|
45
|
-
license: "MIT"
|
|
46
|
-
});
|
|
47
|
-
} catch {
|
|
48
|
-
}
|
|
49
|
-
const M = ({
|
|
1
|
+
import { jsxs as x, jsx as o } from "react/jsx-runtime";
|
|
2
|
+
import { useListItem as N, useFloatingTree as w, useMergeRefs as y } from "@floating-ui/react";
|
|
3
|
+
import { I as b } from "../../chunks/index.dzHBmDC4.js";
|
|
4
|
+
import l from "clsx";
|
|
5
|
+
import * as p from "react";
|
|
6
|
+
import { MenuContext as M } from "./MenuContext.js";
|
|
7
|
+
const g = ({
|
|
50
8
|
className: e,
|
|
51
9
|
viewBox: t,
|
|
52
10
|
title: s,
|
|
53
|
-
monotone:
|
|
54
|
-
...
|
|
55
|
-
}) => /* @__PURE__ */
|
|
56
|
-
|
|
11
|
+
monotone: n,
|
|
12
|
+
...r
|
|
13
|
+
}) => /* @__PURE__ */ x(
|
|
14
|
+
b,
|
|
57
15
|
{
|
|
58
16
|
defaultViewBox: "0 0 448 512",
|
|
59
17
|
size: "size-5",
|
|
60
18
|
viewBox: t,
|
|
61
19
|
className: e,
|
|
62
|
-
title: s || "
|
|
63
|
-
...
|
|
20
|
+
title: s || "Checked",
|
|
21
|
+
...r,
|
|
64
22
|
children: [
|
|
65
23
|
/* @__PURE__ */ o(
|
|
66
24
|
"path",
|
|
@@ -72,96 +30,106 @@ const M = ({
|
|
|
72
30
|
/* @__PURE__ */ o("path", { d: "M337 175c9.4 9.4 9.4 24.6 0 33.9L209 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L303 175c9.4-9.4 24.6-9.4 33.9 0z" })
|
|
73
31
|
]
|
|
74
32
|
}
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
33
|
+
), k = ({
|
|
34
|
+
className: e,
|
|
35
|
+
viewBox: t,
|
|
36
|
+
title: s,
|
|
37
|
+
monotone: n,
|
|
38
|
+
...r
|
|
39
|
+
}) => /* @__PURE__ */ o(
|
|
40
|
+
b,
|
|
41
|
+
{
|
|
42
|
+
defaultViewBox: "0 0 448 512",
|
|
43
|
+
size: "size-5",
|
|
44
|
+
viewBox: t,
|
|
45
|
+
className: e,
|
|
46
|
+
title: s || "UnChecked",
|
|
47
|
+
...r,
|
|
48
|
+
children: /* @__PURE__ */ o(
|
|
49
|
+
"path",
|
|
50
|
+
{
|
|
51
|
+
d: "M0 96c0-35.3 28.7-64 64-64h320c35.3 0 64 28.7 64 64v320c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64z",
|
|
52
|
+
opacity: ".4"
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
), I = p.forwardRef(
|
|
90
57
|
({
|
|
91
58
|
label: e,
|
|
92
59
|
disabled: t,
|
|
93
60
|
icon: s,
|
|
94
|
-
raw:
|
|
95
|
-
children:
|
|
96
|
-
ignoreClick:
|
|
97
|
-
selected:
|
|
98
|
-
...
|
|
99
|
-
},
|
|
100
|
-
let
|
|
101
|
-
const
|
|
102
|
-
if (
|
|
61
|
+
raw: n = !1,
|
|
62
|
+
children: r,
|
|
63
|
+
ignoreClick: u = !1,
|
|
64
|
+
selected: i,
|
|
65
|
+
...m
|
|
66
|
+
}, h) => {
|
|
67
|
+
let d = "";
|
|
68
|
+
const a = p.useContext(M), C = N({ label: t ? null : e }), f = w(), v = y([C.ref, h]);
|
|
69
|
+
if (n && r)
|
|
103
70
|
return /* @__PURE__ */ o(
|
|
104
71
|
"div",
|
|
105
72
|
{
|
|
106
73
|
role: "menuitem",
|
|
107
|
-
...
|
|
74
|
+
...a.getItemProps({
|
|
108
75
|
onClick(c) {
|
|
109
|
-
|
|
76
|
+
u || (m.onClick?.(c), f?.events.emit("click"));
|
|
110
77
|
}
|
|
111
78
|
}),
|
|
112
|
-
children:
|
|
79
|
+
children: r
|
|
113
80
|
}
|
|
114
81
|
);
|
|
115
|
-
s && (
|
|
116
|
-
const
|
|
82
|
+
s && (d = "pl-2");
|
|
83
|
+
const z = l(
|
|
117
84
|
"items-center flex-row",
|
|
118
85
|
"m-0 first:mt-0 mt-2 sm:mt-1 px-2 py-1 flex w-full rounded-md border border-transparent text-left text-base outline-hidden focus:border focus:border-border-medium focus:bg-surface-lighter focus:underline disabled:cursor-not-allowed disabled:text-copy-medium",
|
|
119
86
|
{
|
|
120
|
-
"bg-none": !t && !
|
|
87
|
+
"bg-none": !t && !i
|
|
121
88
|
}
|
|
122
89
|
);
|
|
123
|
-
return /* @__PURE__ */
|
|
90
|
+
return /* @__PURE__ */ x(
|
|
124
91
|
"button",
|
|
125
92
|
{
|
|
126
|
-
...
|
|
127
|
-
ref:
|
|
93
|
+
...m,
|
|
94
|
+
ref: v,
|
|
128
95
|
role: "menuitem",
|
|
129
|
-
className:
|
|
96
|
+
className: z,
|
|
130
97
|
tabIndex: 0,
|
|
131
98
|
disabled: t,
|
|
132
|
-
...
|
|
99
|
+
...a.getItemProps({
|
|
133
100
|
onClick(c) {
|
|
134
|
-
|
|
101
|
+
u || (m.onClick?.(c), f?.events.emit("click"));
|
|
135
102
|
},
|
|
136
103
|
onFocus(c) {
|
|
137
|
-
|
|
104
|
+
m.onFocus?.(c), a.setHasFocusInside(!0);
|
|
138
105
|
}
|
|
139
106
|
}),
|
|
140
107
|
children: [
|
|
141
|
-
|
|
108
|
+
i === !0 && /* @__PURE__ */ o(g, { className: "text-copy-success mr-2", size: "size-4" }),
|
|
109
|
+
i === !1 && /* @__PURE__ */ o(k, { className: "text-copy-medium mr-2", size: "size-4" }),
|
|
142
110
|
s,
|
|
143
|
-
e && /* @__PURE__ */ o("span", { className:
|
|
111
|
+
e && /* @__PURE__ */ o("span", { className: d, children: e })
|
|
144
112
|
]
|
|
145
113
|
}
|
|
146
114
|
);
|
|
147
115
|
}
|
|
148
116
|
);
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
const s =
|
|
117
|
+
I.displayName = "MenuItem";
|
|
118
|
+
const V = ({ className: e, ...t }) => {
|
|
119
|
+
const s = l(e, "my-1 border-t border-border-medium");
|
|
152
120
|
return /* @__PURE__ */ o("div", { className: s, ...t });
|
|
153
|
-
},
|
|
121
|
+
}, j = ({
|
|
154
122
|
className: e,
|
|
155
123
|
...t
|
|
156
124
|
}) => {
|
|
157
|
-
const s =
|
|
125
|
+
const s = l(
|
|
158
126
|
e,
|
|
159
127
|
"pt-1 mb-2 text-sm text-copy-dark border-b border-border-medium"
|
|
160
128
|
);
|
|
161
129
|
return /* @__PURE__ */ o("div", { className: s, ...t });
|
|
162
130
|
};
|
|
163
131
|
export {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
132
|
+
j as MenuGroupLabel,
|
|
133
|
+
I as MenuItem,
|
|
134
|
+
V as MenuSeparator
|
|
167
135
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -6,8 +6,9 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
|
6
6
|
type MenuProps = {
|
|
7
7
|
/**
|
|
8
8
|
* The component to use to open the menu, e.g. a ButtonIcon, a Button, etc.
|
|
9
|
+
* Required for root menus, omit for nested sub-menus (use label instead).
|
|
9
10
|
*/
|
|
10
|
-
trigger
|
|
11
|
+
trigger?: React__default.ReactNode;
|
|
11
12
|
/**
|
|
12
13
|
* The children to render.
|
|
13
14
|
*/
|
|
@@ -27,7 +28,8 @@ type MenuProps = {
|
|
|
27
28
|
*/
|
|
28
29
|
mode?: "dark" | "light" | "system" | "alt-system";
|
|
29
30
|
/**
|
|
30
|
-
* The label to use for the menu button.
|
|
31
|
+
* The label to use for the menu button (root menu) or the sub-menu trigger text (nested menu).
|
|
32
|
+
* When used without a trigger, this creates a nested sub-menu.
|
|
31
33
|
*/
|
|
32
34
|
label?: string;
|
|
33
35
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Menu as _ } from "./components/Menu/Menu.js";
|
|
2
2
|
import { MenuGroupLabel as n, MenuItem as t, MenuSeparator as u } from "./components/Menu/MenuItem.js";
|
|
3
3
|
/*!
|
|
4
|
-
@versini/ui-menu v5.0
|
|
4
|
+
@versini/ui-menu v5.1.0
|
|
5
5
|
ยฉ 2025 gizmette.com
|
|
6
6
|
*/
|
|
7
7
|
try {
|
|
8
8
|
window.__VERSINI_UI_MENU__ || (window.__VERSINI_UI_MENU__ = {
|
|
9
|
-
version: "5.0
|
|
10
|
-
buildTime: "
|
|
9
|
+
version: "5.1.0",
|
|
10
|
+
buildTime: "10/19/2025 04:18 PM EDT",
|
|
11
11
|
homepage: "https://github.com/aversini/ui-components",
|
|
12
12
|
license: "MIT"
|
|
13
13
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@versini/ui-menu",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Arno Versini",
|
|
6
6
|
"publishConfig": {
|
|
@@ -36,23 +36,19 @@
|
|
|
36
36
|
"test:watch": "vitest",
|
|
37
37
|
"test": "vitest run"
|
|
38
38
|
},
|
|
39
|
-
"peerDependencies": {
|
|
40
|
-
"react": "^19.1.0",
|
|
41
|
-
"react-dom": "^19.1.0"
|
|
42
|
-
},
|
|
43
39
|
"devDependencies": {
|
|
44
|
-
"@testing-library/jest-dom": "6.
|
|
45
|
-
"@versini/ui-types": "6.0.
|
|
40
|
+
"@testing-library/jest-dom": "6.9.1",
|
|
41
|
+
"@versini/ui-types": "6.0.2"
|
|
46
42
|
},
|
|
47
43
|
"dependencies": {
|
|
48
44
|
"@floating-ui/react": "0.27.16",
|
|
49
|
-
"@tailwindcss/typography": "0.5.
|
|
50
|
-
"@versini/ui-icons": "4.
|
|
45
|
+
"@tailwindcss/typography": "0.5.19",
|
|
46
|
+
"@versini/ui-icons": "4.13.0",
|
|
51
47
|
"clsx": "2.1.1",
|
|
52
|
-
"tailwindcss": "4.1.
|
|
48
|
+
"tailwindcss": "4.1.14"
|
|
53
49
|
},
|
|
54
50
|
"sideEffects": [
|
|
55
51
|
"**/*.css"
|
|
56
52
|
],
|
|
57
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "5e38069da8dfd7a151bb67950a96d193b50a0b00"
|
|
58
54
|
}
|