@versini/ui-menu 4.0.7 → 4.0.9

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
@@ -1,3 +1,48 @@
1
1
  # @versini/ui-menu
2
2
 
3
- A simple menu component for React.
3
+ [![npm version](https://img.shields.io/npm/v/@versini/ui-menu?style=flat-square)](https://www.npmjs.com/package/@versini/ui-menu)
4
+
5
+ > Accessible and flexible React menu components built with TypeScript and TailwindCSS.
6
+
7
+ The Menu package provides dropdown menus and navigation components with full keyboard navigation, focus management, and customizable styling.
8
+
9
+
10
+ ## Table of Contents
11
+
12
+ - [Features](#features)
13
+ - [Installation](#installation)
14
+ - [Usage](#usage)
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @versini/ui-menu
20
+ ```
21
+
22
+ > **Note**: This component requires TailwindCSS and the `@versini/ui-styles` plugin for proper styling. See the [root README](../../README.md#tailwindcss-setup) for complete setup instructions.
23
+
24
+ ## Usage
25
+
26
+ ### Basic Menu
27
+
28
+ ```tsx
29
+ import { Menu, MenuItem } from "@versini/ui-menu";
30
+ import { ButtonIcon } from "@versini/ui-button";
31
+ import { IconMenu } from "@versini/ui-icons";
32
+
33
+ function App() {
34
+ return (
35
+ <Menu
36
+ trigger={
37
+ <ButtonIcon label="Menu">
38
+ <IconMenu />
39
+ </ButtonIcon>
40
+ }
41
+ >
42
+ <MenuItem label="Profile" onClick={() => console.log('Profile')} />
43
+ <MenuItem label="Settings" onClick={() => console.log('Settings')} />
44
+ <MenuItem label="Logout" onClick={() => console.log('Logout')} />
45
+ </Menu>
46
+ );
47
+ }
48
+ ```
@@ -1,8 +1,8 @@
1
- import { jsxs as J, jsx as i } from "react/jsx-runtime";
2
- import { useFloatingTree as K, useFloatingNodeId as Q, useListItem as V, useFloating as W, autoUpdate as X, offset as Y, flip as Z, shift as $, useClick as O, useRole as ee, useDismiss as te, useListNavigation as se, useTypeahead as oe, useInteractions as ne, useMergeRefs as ie, FloatingNode as re, FloatingList as ae, FloatingPortal as le, FloatingFocusManager as ce, FloatingTree as ue } from "@floating-ui/react";
3
- import fe, { forwardRef as N, useState as f, useRef as v, useContext as de, useEffect as F } from "react";
4
- import { MenuContext as I } from "./MenuContext.js";
5
- const x = (e) => {
1
+ import { jsxs as G, jsx as s } from "react/jsx-runtime";
2
+ import { useFloatingTree as J, useFloatingNodeId as K, useListItem as Q, useFloating as V, autoUpdate as W, offset as X, flip as Y, shift as Z, useClick as $, useRole as ee, useDismiss as te, useListNavigation as ne, useTypeahead as oe, useInteractions as se, useMergeRefs as ie, FloatingNode as ae, FloatingList as re, FloatingPortal as le, FloatingFocusManager as ce, FloatingTree as ue } from "@floating-ui/react";
3
+ import fe, { forwardRef as x, useState as f, useRef as h, useContext as de, useEffect as v } from "react";
4
+ import { MenuContext as F } from "./MenuContext.js";
5
+ const I = (e) => {
6
6
  if (typeof e == "string")
7
7
  return e;
8
8
  if (typeof e == "function")
@@ -13,97 +13,97 @@ const x = (e) => {
13
13
  return t.displayName || t.name || "Component";
14
14
  }
15
15
  return "Element";
16
- }, R = N(
16
+ }, N = x(
17
17
  ({
18
18
  trigger: e,
19
19
  children: t,
20
- label: b = "Open menu",
21
- defaultPlacement: M = "bottom-start",
22
- onOpenChange: s,
23
- mode: h = "system",
24
- focusMode: k = "system",
25
- ...P
26
- }, w) => {
27
- const [o, d] = f(!1), [E, j] = f(!1), [c, u] = f(null), m = v([]), p = v([]), T = de(I), n = K(), l = Q(), B = V(), { floatingStyles: L, refs: y, context: r } = W({
28
- nodeId: l,
29
- open: o,
20
+ label: R = "Open menu",
21
+ defaultPlacement: b = "bottom-start",
22
+ onOpenChange: l,
23
+ mode: M = "system",
24
+ focusMode: C = "system",
25
+ ...k
26
+ }, P) => {
27
+ const [n, d] = f(!1), [w, E] = f(!1), [c, u] = f(null), p = h([]), m = h([]), j = de(F), o = J(), r = K(), T = Q(), { floatingStyles: B, refs: g, context: i } = V({
28
+ nodeId: r,
29
+ open: n,
30
30
  onOpenChange: (a) => {
31
- d(a), s == null || s(a);
31
+ d(a), l?.(a);
32
32
  },
33
- placement: M,
33
+ placement: b,
34
34
  strategy: "fixed",
35
- middleware: [Y({ mainAxis: 10 }), Z(), $()],
36
- whileElementsMounted: X
37
- }), A = O(r, {
35
+ middleware: [X({ mainAxis: 10 }), Y(), Z()],
36
+ whileElementsMounted: W
37
+ }), L = $(i, {
38
38
  event: "mousedown",
39
39
  toggle: !0,
40
40
  ignoreMouse: !1
41
- }), D = ee(r, { role: "menu" }), S = te(r, { bubbles: !0 }), _ = se(r, {
42
- listRef: m,
41
+ }), A = ee(i, { role: "menu" }), D = te(i, { bubbles: !0 }), O = ne(i, {
42
+ listRef: p,
43
43
  activeIndex: c,
44
44
  nested: !1,
45
45
  onNavigate: u,
46
46
  loop: !0
47
- }), H = oe(r, {
48
- listRef: p,
49
- onMatch: o ? u : void 0,
47
+ }), S = oe(i, {
48
+ listRef: m,
49
+ onMatch: n ? u : void 0,
50
50
  activeIndex: c
51
- }), { getReferenceProps: U, getFloatingProps: q, getItemProps: z } = ne([A, D, S, _, H]), g = x(e) === "Button" || x(e) === "ButtonIcon", C = g ? {
52
- noInternalClick: g,
53
- focusMode: k,
54
- mode: h
55
- } : {}, G = fe.cloneElement(
51
+ }), { getReferenceProps: _, getFloatingProps: H, getItemProps: U } = se([L, A, D, O, S]), y = I(e) === "Button" || I(e) === "ButtonIcon", q = y ? {
52
+ noInternalClick: y,
53
+ focusMode: C,
54
+ mode: M
55
+ } : {}, z = fe.cloneElement(
56
56
  e,
57
57
  {
58
- ...C,
59
- "aria-label": b,
60
- "data-open": o ? "" : void 0,
61
- "data-focus-inside": E ? "" : void 0,
62
- ref: ie([y.setReference, B.ref, w]),
63
- ...U(
64
- T.getItemProps({
65
- ...P
58
+ ...q,
59
+ "aria-label": R,
60
+ "data-open": n ? "" : void 0,
61
+ "data-focus-inside": w ? "" : void 0,
62
+ ref: ie([g.setReference, T.ref, P]),
63
+ ..._(
64
+ j.getItemProps({
65
+ ...k
66
66
  })
67
67
  )
68
68
  }
69
69
  );
70
- return F(() => {
71
- if (!n)
70
+ return v(() => {
71
+ if (!o)
72
72
  return;
73
73
  function a() {
74
- d(!1), s == null || s(!1);
74
+ d(!1), l?.(!1);
75
75
  }
76
- return n.events.on("click", a), () => {
77
- n.events.off("click", a);
76
+ return o.events.on("click", a), () => {
77
+ o.events.off("click", a);
78
78
  };
79
- }, [n, s]), F(() => {
80
- o && n && n.events.emit("menuopen", { nodeId: l });
81
- }, [n, o, l]), /* @__PURE__ */ J(re, { id: l, children: [
82
- G,
83
- /* @__PURE__ */ i(
84
- I.Provider,
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,
85
85
  {
86
86
  value: {
87
87
  activeIndex: c,
88
88
  setActiveIndex: u,
89
- getItemProps: z,
90
- setHasFocusInside: j,
91
- isOpen: o
89
+ getItemProps: U,
90
+ setHasFocusInside: E,
91
+ isOpen: n
92
92
  },
93
- children: /* @__PURE__ */ i(ae, { elementsRef: m, labelsRef: p, children: o && /* @__PURE__ */ i(le, { children: /* @__PURE__ */ i(
93
+ children: /* @__PURE__ */ s(re, { elementsRef: p, labelsRef: m, children: n && /* @__PURE__ */ s(le, { children: /* @__PURE__ */ s(
94
94
  ce,
95
95
  {
96
- context: r,
96
+ context: i,
97
97
  modal: !1,
98
98
  initialFocus: 0,
99
99
  returnFocus: !0,
100
- children: /* @__PURE__ */ i(
100
+ children: /* @__PURE__ */ s(
101
101
  "div",
102
102
  {
103
- ref: y.setFloating,
103
+ ref: g.setFloating,
104
104
  className: "rounded-md bg-surface-light shadow-sm shadow-border-dark outline-hidden p-4 sm:p-2",
105
- style: L,
106
- ...q(),
105
+ style: B,
106
+ ...H(),
107
107
  children: t
108
108
  }
109
109
  )
@@ -113,9 +113,9 @@ const x = (e) => {
113
113
  )
114
114
  ] });
115
115
  }
116
- ), me = N((e, t) => /* @__PURE__ */ i(ue, { children: /* @__PURE__ */ i(R, { ...e, ref: t }) }));
117
- me.displayName = "Menu";
118
- R.displayName = "MenuComponent";
116
+ ), pe = x((e, t) => /* @__PURE__ */ s(ue, { children: /* @__PURE__ */ s(N, { ...e, ref: t }) }));
117
+ pe.displayName = "Menu";
118
+ N.displayName = "MenuComponent";
119
119
  export {
120
- me as Menu
120
+ pe as Menu
121
121
  };
@@ -1,34 +1,34 @@
1
- import { jsx as i, Fragment as g, jsxs as _ } from "react/jsx-runtime";
2
- import { useListItem as v, useFloatingTree as C, useMergeRefs as M } from "@floating-ui/react";
3
- import f from "clsx";
4
- import * as w from "react";
5
- import { MenuContext as S } from "./MenuContext.js";
6
- const V = ({
7
- children: t,
8
- fill: s,
9
- viewBox: o,
10
- className: a,
11
- defaultViewBox: r,
12
- size: u,
13
- title: l,
14
- semantic: e = !1,
15
- ...b
1
+ import { jsx as o, Fragment as h, jsxs as p } from "react/jsx-runtime";
2
+ import { useListItem as N, useFloatingTree as g, useMergeRefs as v } from "@floating-ui/react";
3
+ import u from "clsx";
4
+ import * as I from "react";
5
+ import { MenuContext as C } from "./MenuContext.js";
6
+ const M = ({
7
+ children: e,
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
16
  }) => {
17
- const d = f(u, a);
18
- return /* @__PURE__ */ i(g, { children: /* @__PURE__ */ _(
17
+ const a = u(m, l);
18
+ return /* @__PURE__ */ o(h, { children: /* @__PURE__ */ p(
19
19
  "svg",
20
20
  {
21
21
  xmlns: "http://www.w3.org/2000/svg",
22
- className: d,
23
- viewBox: o || r,
24
- fill: s || "currentColor",
22
+ className: a,
23
+ viewBox: s || i,
24
+ fill: t || "currentColor",
25
25
  role: "img",
26
- "aria-hidden": !e,
26
+ "aria-hidden": !r,
27
27
  focusable: !1,
28
- ...b,
28
+ ...d,
29
29
  children: [
30
- l && e && /* @__PURE__ */ i("title", { children: l }),
31
- t
30
+ n && r && /* @__PURE__ */ o("title", { children: n }),
31
+ e
32
32
  ]
33
33
  }
34
34
  ) });
@@ -46,30 +46,30 @@ try {
46
46
  });
47
47
  } catch {
48
48
  }
49
- const y = ({
50
- className: t,
51
- viewBox: s,
52
- title: o,
53
- monotone: a,
54
- ...r
55
- }) => /* @__PURE__ */ _(
56
- V,
49
+ const S = ({
50
+ className: e,
51
+ viewBox: t,
52
+ title: s,
53
+ monotone: l,
54
+ ...i
55
+ }) => /* @__PURE__ */ p(
56
+ M,
57
57
  {
58
58
  defaultViewBox: "0 0 448 512",
59
59
  size: "size-5",
60
- viewBox: s,
61
- className: t,
62
- title: o || "Copied",
63
- ...r,
60
+ viewBox: t,
61
+ className: e,
62
+ title: s || "Copied",
63
+ ...i,
64
64
  children: [
65
- /* @__PURE__ */ i(
65
+ /* @__PURE__ */ o(
66
66
  "path",
67
67
  {
68
68
  d: "M0 96v320c0 35.3 28.7 64 64 64h320c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64C28.7 32 0 60.7 0 96m104 160c0-6.1 2.3-12.3 7-17 9.4-9.4 24.6-9.4 33.9 0l47 47 111-111c4.7-4.7 10.8-7 17-7s12.3 2.3 17 7c2.3 2.3 4.1 5 5.3 7.9.6 1.5 1 2.9 1.3 4.4.2 1.1.3 2.2.3 2.2.1 1.2.1 1.2.1 2.5-.1 1.5-.1 1.9-.1 2.3-.1.7-.2 1.5-.3 2.2-.3 1.5-.7 3-1.3 4.4-1.2 2.9-2.9 5.6-5.3 7.9l-128 128c-4.7 4.7-10.8 7-17 7s-12.3-2.3-17-7l-64-64c-4.7-4.7-7-10.8-7-17z",
69
69
  opacity: ".4"
70
70
  }
71
71
  ),
72
- /* @__PURE__ */ i("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" })
72
+ /* @__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
73
  ]
74
74
  }
75
75
  );
@@ -86,85 +86,82 @@ try {
86
86
  });
87
87
  } catch {
88
88
  }
89
- const R = w.forwardRef(
89
+ const V = I.forwardRef(
90
90
  ({
91
- label: t,
92
- disabled: s,
93
- icon: o,
94
- raw: a = !1,
95
- children: r,
96
- ignoreClick: u = !1,
97
- selected: l = !1,
98
- ...e
99
- }, b) => {
100
- let d = "";
101
- const I = w.useContext(S), x = v({ label: s ? null : t }), c = C(), h = M([x.ref, b]);
102
- if (a && r)
103
- return /* @__PURE__ */ i(
91
+ label: e,
92
+ disabled: t,
93
+ icon: s,
94
+ raw: l = !1,
95
+ children: i,
96
+ ignoreClick: m = !1,
97
+ selected: n = !1,
98
+ ...r
99
+ }, d) => {
100
+ let a = "";
101
+ const f = I.useContext(C), _ = N({ label: t ? null : e }), b = g(), w = v([_.ref, d]);
102
+ if (l && i)
103
+ return /* @__PURE__ */ o(
104
104
  "div",
105
105
  {
106
106
  role: "menuitem",
107
- ...I.getItemProps({
108
- onClick(m) {
109
- var n;
110
- u || ((n = e.onClick) == null || n.call(e, m), c == null || c.events.emit("click"));
107
+ ...f.getItemProps({
108
+ onClick(c) {
109
+ m || (r.onClick?.(c), b?.events.emit("click"));
111
110
  }
112
111
  }),
113
- children: r
112
+ children: i
114
113
  }
115
114
  );
116
- o && (d = "pl-2");
117
- const N = f(
115
+ s && (a = "pl-2");
116
+ const x = u(
118
117
  "items-center flex-row",
119
118
  "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",
120
119
  {
121
- "bg-none": !s && !l
120
+ "bg-none": !t && !n
122
121
  }
123
122
  );
124
- return /* @__PURE__ */ _(
123
+ return /* @__PURE__ */ p(
125
124
  "button",
126
125
  {
127
- ...e,
128
- ref: h,
126
+ ...r,
127
+ ref: w,
129
128
  role: "menuitem",
130
- className: N,
129
+ className: x,
131
130
  tabIndex: 0,
132
- disabled: s,
133
- ...I.getItemProps({
134
- onClick(m) {
135
- var n;
136
- u || ((n = e.onClick) == null || n.call(e, m), c == null || c.events.emit("click"));
131
+ disabled: t,
132
+ ...f.getItemProps({
133
+ onClick(c) {
134
+ m || (r.onClick?.(c), b?.events.emit("click"));
137
135
  },
138
- onFocus(m) {
139
- var n;
140
- (n = e.onFocus) == null || n.call(e, m), I.setHasFocusInside(!0);
136
+ onFocus(c) {
137
+ r.onFocus?.(c), f.setHasFocusInside(!0);
141
138
  }
142
139
  }),
143
140
  children: [
144
- l && /* @__PURE__ */ i(y, { className: "mr-2", size: "size-4" }),
145
- o,
146
- t && /* @__PURE__ */ i("span", { className: d, children: t })
141
+ n && /* @__PURE__ */ o(S, { className: "mr-2", size: "size-4" }),
142
+ s,
143
+ e && /* @__PURE__ */ o("span", { className: a, children: e })
147
144
  ]
148
145
  }
149
146
  );
150
147
  }
151
148
  );
152
- R.displayName = "MenuItem";
153
- const E = ({ className: t, ...s }) => {
154
- const o = f(t, "my-1 border-t border-border-medium");
155
- return /* @__PURE__ */ i("div", { className: o, ...s });
156
- }, F = ({
157
- className: t,
158
- ...s
149
+ V.displayName = "MenuItem";
150
+ const k = ({ className: e, ...t }) => {
151
+ const s = u(e, "my-1 border-t border-border-medium");
152
+ return /* @__PURE__ */ o("div", { className: s, ...t });
153
+ }, B = ({
154
+ className: e,
155
+ ...t
159
156
  }) => {
160
- const o = f(
161
- t,
157
+ const s = u(
158
+ e,
162
159
  "pt-1 mb-2 text-sm text-copy-dark border-b border-border-medium"
163
160
  );
164
- return /* @__PURE__ */ i("div", { className: o, ...s });
161
+ return /* @__PURE__ */ o("div", { className: s, ...t });
165
162
  };
166
163
  export {
167
- F as MenuGroupLabel,
168
- R as MenuItem,
169
- E as MenuSeparator
164
+ B as MenuGroupLabel,
165
+ V as MenuItem,
166
+ k as MenuSeparator
170
167
  };
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 v4.0.7
4
+ @versini/ui-menu v4.0.9
5
5
  © 2025 gizmette.com
6
6
  */
7
7
  try {
8
8
  window.__VERSINI_UI_MENU__ || (window.__VERSINI_UI_MENU__ = {
9
- version: "4.0.7",
10
- buildTime: "07/14/2025 12:04 PM EDT",
9
+ version: "4.0.9",
10
+ buildTime: "08/14/2025 06:06 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": "4.0.7",
3
+ "version": "4.0.9",
4
4
  "license": "MIT",
5
5
  "author": "Arno Versini",
6
6
  "publishConfig": {
@@ -27,6 +27,7 @@
27
27
  "dev:types": "tsup --watch src",
28
28
  "dev": "npm-run-all clean --parallel dev:js dev:types",
29
29
  "lint": "biome lint src",
30
+ "lint:fix": "biome check src --write --no-errors-on-unmatched",
30
31
  "prettier": "biome check --write --no-errors-on-unmatched",
31
32
  "start": "static-server dist --port 5173",
32
33
  "test:coverage:ui": "vitest --coverage --ui",
@@ -39,18 +40,18 @@
39
40
  "react-dom": "^18.3.1 || ^19.0.0"
40
41
  },
41
42
  "devDependencies": {
42
- "@testing-library/jest-dom": "6.6.3",
43
- "@versini/ui-types": "5.0.5"
43
+ "@testing-library/jest-dom": "6.7.0",
44
+ "@versini/ui-types": "../ui-types"
44
45
  },
45
46
  "dependencies": {
46
- "@floating-ui/react": "0.27.13",
47
+ "@floating-ui/react": "0.27.15",
47
48
  "@tailwindcss/typography": "0.5.16",
48
49
  "@versini/ui-icons": "4.10.0",
49
50
  "clsx": "2.1.1",
50
- "tailwindcss": "4.1.11"
51
+ "tailwindcss": "4.1.12"
51
52
  },
52
53
  "sideEffects": [
53
54
  "**/*.css"
54
55
  ],
55
- "gitHead": "097e81ba959c30dc2ee37ff9050981a02420360b"
56
+ "gitHead": "a2a11904039a5bc55ff17a954e4a16073abbe0bf"
56
57
  }