asterui 0.12.43 → 0.12.45

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.
@@ -21,8 +21,6 @@ export interface ContextMenuProps {
21
21
  className?: string;
22
22
  }
23
23
  export interface ContextMenuItemProps {
24
- /** Unique key for the item */
25
- itemKey: string;
26
24
  /** Item content */
27
25
  children: React.ReactNode;
28
26
  /** Icon to display before label */
@@ -33,14 +31,14 @@ export interface ContextMenuItemProps {
33
31
  danger?: boolean;
34
32
  /** Additional CSS classes */
35
33
  className?: string;
34
+ /** @internal */
35
+ _key?: string;
36
36
  }
37
37
  export interface ContextMenuDividerProps {
38
38
  /** Additional CSS classes */
39
39
  className?: string;
40
40
  }
41
41
  export interface ContextMenuSubMenuProps {
42
- /** Unique key for the submenu */
43
- itemKey: string;
44
42
  /** Submenu label */
45
43
  label: React.ReactNode;
46
44
  /** Icon to display before label */
@@ -51,6 +49,8 @@ export interface ContextMenuSubMenuProps {
51
49
  children: React.ReactNode;
52
50
  /** Additional CSS classes */
53
51
  className?: string;
52
+ /** @internal */
53
+ _key?: string;
54
54
  }
55
55
  export declare const ContextMenu: React.FC<ContextMenuProps> & {
56
56
  Item: React.FC<ContextMenuItemProps>;
@@ -1,25 +1,25 @@
1
- import { jsxs as b, Fragment as P, jsx as n } from "react/jsx-runtime";
2
- import T, { useState as g, useRef as C, useCallback as M, useEffect as N, createContext as W, useContext as z } from "react";
3
- import { createPortal as B } from "react-dom";
4
- const E = W(null), I = () => {
5
- const e = z(E);
1
+ import { jsxs as b, Fragment as B, jsx as n } from "react/jsx-runtime";
2
+ import C, { useState as g, useRef as y, useCallback as M, useEffect as E, createContext as I, useContext as K } from "react";
3
+ import { createPortal as V } from "react-dom";
4
+ const L = I(null), A = () => {
5
+ const e = K(L);
6
6
  if (!e)
7
7
  throw new Error("ContextMenu compound components must be used within a ContextMenu");
8
8
  return e;
9
- }, A = ({
10
- itemKey: e,
11
- children: u,
12
- icon: l,
9
+ }, S = ({
10
+ children: e,
11
+ icon: c,
13
12
  disabled: s = !1,
14
13
  danger: d = !1,
15
- className: o = ""
14
+ className: h = "",
15
+ _key: l
16
16
  }) => {
17
- const { onSelect: h, onClose: t } = I();
18
- return /* @__PURE__ */ n("li", { className: o, children: /* @__PURE__ */ b(
17
+ const { onSelect: m, onClose: o } = A();
18
+ return /* @__PURE__ */ n("li", { className: h, children: /* @__PURE__ */ b(
19
19
  "button",
20
20
  {
21
21
  onClick: () => {
22
- s || (h(e), t());
22
+ s || !l || (m(l), o());
23
23
  },
24
24
  disabled: s,
25
25
  className: `
@@ -28,30 +28,30 @@ const E = W(null), I = () => {
28
28
  ${d ? "text-error hover:bg-error/10" : ""}
29
29
  `,
30
30
  children: [
31
- l && /* @__PURE__ */ n("span", { className: "w-4 h-4", children: l }),
32
- /* @__PURE__ */ n("span", { className: "flex-1", children: u })
31
+ c && /* @__PURE__ */ n("span", { className: "w-4 h-4", children: c }),
32
+ /* @__PURE__ */ n("span", { className: "flex-1", children: e })
33
33
  ]
34
34
  }
35
35
  ) });
36
- }, H = ({ className: e = "" }) => /* @__PURE__ */ n("li", { className: `divider my-1 ${e}` }), K = ({
37
- itemKey: e,
38
- label: u,
39
- icon: l,
36
+ }, H = ({ className: e = "" }) => /* @__PURE__ */ n("li", { className: `divider my-1 ${e}` }), R = ({
37
+ label: e,
38
+ icon: c,
40
39
  disabled: s = !1,
41
40
  children: d,
42
- className: o = ""
41
+ className: h = "",
42
+ _key: l
43
43
  }) => {
44
- const [h, t] = g(!1), a = C(null), c = () => {
45
- s || (a.current && clearTimeout(a.current), t(!0));
46
- }, m = () => {
47
- a.current = setTimeout(() => t(!1), 100);
44
+ const [m, o] = g(!1), a = y(null), u = () => {
45
+ s || (a.current && clearTimeout(a.current), o(!0));
46
+ }, f = () => {
47
+ a.current = setTimeout(() => o(!1), 100);
48
48
  };
49
49
  return /* @__PURE__ */ b(
50
50
  "li",
51
51
  {
52
- onMouseEnter: c,
53
- onMouseLeave: m,
54
- className: `relative ${o}`,
52
+ onMouseEnter: u,
53
+ onMouseLeave: f,
54
+ className: `relative ${h}`,
55
55
  children: [
56
56
  /* @__PURE__ */ b(
57
57
  "button",
@@ -62,46 +62,46 @@ const E = W(null), I = () => {
62
62
  ${s ? "opacity-50 cursor-not-allowed" : "hover:bg-base-200"}
63
63
  `,
64
64
  children: [
65
- l && /* @__PURE__ */ n("span", { className: "w-4 h-4", children: l }),
66
- /* @__PURE__ */ n("span", { className: "flex-1", children: u }),
65
+ c && /* @__PURE__ */ n("span", { className: "w-4 h-4", children: c }),
66
+ /* @__PURE__ */ n("span", { className: "flex-1", children: e }),
67
67
  /* @__PURE__ */ n("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ n("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
68
68
  ]
69
69
  }
70
70
  ),
71
- h && /* @__PURE__ */ n(
71
+ m && /* @__PURE__ */ n(
72
72
  "ul",
73
73
  {
74
74
  className: "menu bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1",
75
- onMouseEnter: c,
76
- onMouseLeave: m,
75
+ onMouseEnter: u,
76
+ onMouseLeave: f,
77
77
  children: d
78
78
  }
79
79
  )
80
80
  ]
81
81
  }
82
82
  );
83
- }, L = ({ item: e, onSelect: u, onClose: l }) => {
84
- const [s, d] = g(!1), o = C(null);
83
+ }, $ = ({ item: e, onSelect: c, onClose: s }) => {
84
+ const [d, h] = g(!1), l = y(null);
85
85
  if (e.divider)
86
86
  return /* @__PURE__ */ n("li", { className: "divider my-1" });
87
- const h = () => {
88
- e.disabled || e.children && e.children.length > 0 || (u(e.key), l());
89
- }, t = e.children && e.children.length > 0, a = () => {
90
- t && (o.current && clearTimeout(o.current), d(!0));
91
- }, c = () => {
92
- t && (o.current = setTimeout(() => d(!1), 100));
87
+ const m = () => {
88
+ e.disabled || e.children && e.children.length > 0 || (c(e.key), s());
89
+ }, o = e.children && e.children.length > 0, a = () => {
90
+ o && (l.current && clearTimeout(l.current), h(!0));
91
+ }, u = () => {
92
+ o && (l.current = setTimeout(() => h(!1), 100));
93
93
  };
94
94
  return /* @__PURE__ */ b(
95
95
  "li",
96
96
  {
97
97
  onMouseEnter: a,
98
- onMouseLeave: c,
98
+ onMouseLeave: u,
99
99
  className: "relative",
100
100
  children: [
101
101
  /* @__PURE__ */ b(
102
102
  "button",
103
103
  {
104
- onClick: h,
104
+ onClick: m,
105
105
  disabled: e.disabled,
106
106
  className: `
107
107
  flex items-center gap-2 w-full px-4 py-2 text-left text-sm
@@ -111,17 +111,17 @@ const E = W(null), I = () => {
111
111
  children: [
112
112
  e.icon && /* @__PURE__ */ n("span", { className: "w-4 h-4", children: e.icon }),
113
113
  /* @__PURE__ */ n("span", { className: "flex-1", children: e.label }),
114
- t && /* @__PURE__ */ n("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ n("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
114
+ o && /* @__PURE__ */ n("svg", { className: "w-4 h-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ n("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })
115
115
  ]
116
116
  }
117
117
  ),
118
- t && s && /* @__PURE__ */ n(
118
+ o && d && /* @__PURE__ */ n(
119
119
  "ul",
120
120
  {
121
121
  className: "menu bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1",
122
122
  onMouseEnter: a,
123
- onMouseLeave: c,
124
- children: e.children.map((m) => /* @__PURE__ */ n(L, { item: m, onSelect: u, onClose: l }, m.key))
123
+ onMouseLeave: u,
124
+ children: e.children.map((f) => /* @__PURE__ */ n($, { item: f, onSelect: c, onClose: s }, f.key))
125
125
  }
126
126
  )
127
127
  ]
@@ -129,69 +129,76 @@ const E = W(null), I = () => {
129
129
  );
130
130
  }, O = ({
131
131
  children: e,
132
- items: u,
133
- onSelect: l,
134
- disabled: s = !1,
135
- className: d = ""
132
+ items: c,
133
+ onSelect: s,
134
+ disabled: d = !1,
135
+ className: h = ""
136
136
  }) => {
137
- const [o, h] = g(!1), [t, a] = g({ x: 0, y: 0 }), c = C(null), m = C(null), S = M(
137
+ const [l, m] = g(!1), [o, a] = g({ x: 0, y: 0 }), u = y(null), f = y(null), j = M(
138
138
  (r) => {
139
- if (s) return;
139
+ if (d) return;
140
140
  r.preventDefault(), r.stopPropagation();
141
- let i = r.clientX, f = r.clientY;
142
- a({ x: i, y: f }), h(!0);
141
+ let t = r.clientX, i = r.clientY;
142
+ a({ x: t, y: i }), m(!0);
143
143
  },
144
- [s]
145
- ), x = M(() => {
146
- h(!1);
147
- }, []), y = M(
144
+ [d]
145
+ ), p = M(() => {
146
+ m(!1);
147
+ }, []), k = M(
148
148
  (r) => {
149
- l?.(r);
149
+ s?.(r);
150
150
  },
151
- [l]
151
+ [s]
152
152
  );
153
- N(() => {
154
- if (o && c.current) {
155
- const i = c.current.getBoundingClientRect(), f = window.innerWidth, p = window.innerHeight;
156
- let { x: v, y: w } = t;
157
- v + i.width > f && (v = f - i.width - 8), w + i.height > p && (w = p - i.height - 8), (v !== t.x || w !== t.y) && a({ x: v, y: w });
153
+ E(() => {
154
+ if (l && u.current) {
155
+ const t = u.current.getBoundingClientRect(), i = window.innerWidth, x = window.innerHeight;
156
+ let { x: v, y: w } = o;
157
+ v + t.width > i && (v = i - t.width - 8), w + t.height > x && (w = x - t.height - 8), (v !== o.x || w !== o.y) && a({ x: v, y: w });
158
158
  }
159
- }, [o, t]), N(() => {
160
- if (!o) return;
161
- const r = (p) => {
162
- c.current && !c.current.contains(p.target) && x();
163
- }, i = (p) => {
164
- p.key === "Escape" && x();
165
- }, f = () => {
166
- x();
159
+ }, [l, o]), E(() => {
160
+ if (!l) return;
161
+ const r = (x) => {
162
+ u.current && !u.current.contains(x.target) && p();
163
+ }, t = (x) => {
164
+ x.key === "Escape" && p();
165
+ }, i = () => {
166
+ p();
167
167
  };
168
- return document.addEventListener("mousedown", r), document.addEventListener("keydown", i), document.addEventListener("scroll", f, !0), () => {
169
- document.removeEventListener("mousedown", r), document.removeEventListener("keydown", i), document.removeEventListener("scroll", f, !0);
168
+ return document.addEventListener("mousedown", r), document.addEventListener("keydown", t), document.addEventListener("scroll", i, !0), () => {
169
+ document.removeEventListener("mousedown", r), document.removeEventListener("keydown", t), document.removeEventListener("scroll", i, !0);
170
170
  };
171
- }, [o, x]);
172
- const k = T.Children.toArray(e), R = k[0], $ = k.slice(1), j = u && u.length > 0, D = {
173
- onSelect: y,
174
- onClose: x
171
+ }, [l, p]);
172
+ const D = (r) => C.Children.map(r, (t) => {
173
+ if (C.isValidElement(t)) {
174
+ const i = t.key != null ? String(t.key) : void 0;
175
+ if (t.type === S || t.type === R)
176
+ return C.cloneElement(t, { _key: i });
177
+ }
178
+ return t;
179
+ }), N = C.Children.toArray(e), W = N[0], P = D(N.slice(1)), T = c && c.length > 0, z = {
180
+ onSelect: k,
181
+ onClose: p
175
182
  };
176
- return /* @__PURE__ */ b(P, { children: [
177
- /* @__PURE__ */ n("div", { ref: m, onContextMenu: S, className: "inline-block", children: R }),
178
- o && B(
179
- /* @__PURE__ */ n(E.Provider, { value: D, children: /* @__PURE__ */ n(
183
+ return /* @__PURE__ */ b(B, { children: [
184
+ /* @__PURE__ */ n("div", { ref: f, onContextMenu: j, className: "inline-block", children: W }),
185
+ l && V(
186
+ /* @__PURE__ */ n(L.Provider, { value: z, children: /* @__PURE__ */ n(
180
187
  "ul",
181
188
  {
182
- ref: c,
183
- className: `menu bg-base-100 rounded-box shadow-lg border border-base-300 min-w-[160px] p-1 fixed z-[9999] ${d}`,
184
- style: { left: t.x, top: t.y },
185
- children: j ? u.map((r) => /* @__PURE__ */ n(L, { item: r, onSelect: y, onClose: x }, r.key)) : $
189
+ ref: u,
190
+ className: `menu bg-base-100 rounded-box shadow-lg border border-base-300 min-w-[160px] p-1 fixed z-[9999] ${h}`,
191
+ style: { left: o.x, top: o.y },
192
+ children: T ? c.map((r) => /* @__PURE__ */ n($, { item: r, onSelect: k, onClose: p }, r.key)) : P
186
193
  }
187
194
  ) }),
188
195
  document.body
189
196
  )
190
197
  ] });
191
198
  }, Y = Object.assign(O, {
192
- Item: A,
199
+ Item: S,
193
200
  Divider: H,
194
- SubMenu: K
201
+ SubMenu: R
195
202
  });
196
203
  export {
197
204
  Y as ContextMenu
@@ -1 +1 @@
1
- {"version":3,"file":"ContextMenu.js","sources":["../../src/components/ContextMenu.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useCallback, createContext, useContext } from 'react'\nimport { createPortal } from 'react-dom'\n\nexport interface ContextMenuItem {\n key: string\n label: React.ReactNode\n icon?: React.ReactNode\n disabled?: boolean\n danger?: boolean\n divider?: boolean\n children?: ContextMenuItem[]\n}\n\nexport interface ContextMenuProps {\n /** Element that triggers the context menu on right-click */\n children: React.ReactNode\n /** Menu items (data-driven pattern) */\n items?: ContextMenuItem[]\n /** Callback when an item is selected */\n onSelect?: (key: string) => void\n /** Whether the context menu is disabled */\n disabled?: boolean\n /** Additional CSS classes for the menu */\n className?: string\n}\n\nexport interface ContextMenuItemProps {\n /** Unique key for the item */\n itemKey: string\n /** Item content */\n children: React.ReactNode\n /** Icon to display before label */\n icon?: React.ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Show as danger/destructive action */\n danger?: boolean\n /** Additional CSS classes */\n className?: string\n}\n\nexport interface ContextMenuDividerProps {\n /** Additional CSS classes */\n className?: string\n}\n\nexport interface ContextMenuSubMenuProps {\n /** Unique key for the submenu */\n itemKey: string\n /** Submenu label */\n label: React.ReactNode\n /** Icon to display before label */\n icon?: React.ReactNode\n /** Whether the submenu is disabled */\n disabled?: boolean\n /** Submenu items */\n children: React.ReactNode\n /** Additional CSS classes */\n className?: string\n}\n\ninterface ContextMenuContextValue {\n onSelect: (key: string) => void\n onClose: () => void\n}\n\ninterface MenuPosition {\n x: number\n y: number\n}\n\nconst ContextMenuContext = createContext<ContextMenuContextValue | null>(null)\n\nconst useContextMenuContext = () => {\n const context = useContext(ContextMenuContext)\n if (!context) {\n throw new Error('ContextMenu compound components must be used within a ContextMenu')\n }\n return context\n}\n\n// Compound pattern components\nconst ContextMenuItemComponent: React.FC<ContextMenuItemProps> = ({\n itemKey,\n children,\n icon,\n disabled = false,\n danger = false,\n className = '',\n}) => {\n const { onSelect, onClose } = useContextMenuContext()\n\n const handleClick = () => {\n if (disabled) return\n onSelect(itemKey)\n onClose()\n }\n\n return (\n <li className={className}>\n <button\n onClick={handleClick}\n disabled={disabled}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n ${danger ? 'text-error hover:bg-error/10' : ''}\n `}\n >\n {icon && <span className=\"w-4 h-4\">{icon}</span>}\n <span className=\"flex-1\">{children}</span>\n </button>\n </li>\n )\n}\n\nconst ContextMenuDividerComponent: React.FC<ContextMenuDividerProps> = ({ className = '' }) => {\n return <li className={`divider my-1 ${className}`}></li>\n}\n\nconst ContextMenuSubMenuComponent: React.FC<ContextMenuSubMenuProps> = ({\n itemKey: _itemKey,\n label,\n icon,\n disabled = false,\n children,\n className = '',\n}) => {\n const [showSubmenu, setShowSubmenu] = useState(false)\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const handleMouseEnter = () => {\n if (disabled) return\n if (timeoutRef.current) clearTimeout(timeoutRef.current)\n setShowSubmenu(true)\n }\n\n const handleMouseLeave = () => {\n timeoutRef.current = setTimeout(() => setShowSubmenu(false), 100)\n }\n\n return (\n <li\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n className={`relative ${className}`}\n >\n <button\n disabled={disabled}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n `}\n >\n {icon && <span className=\"w-4 h-4\">{icon}</span>}\n <span className=\"flex-1\">{label}</span>\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n {showSubmenu && (\n <ul\n className=\"menu bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1\"\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n >\n {children}\n </ul>\n )}\n </li>\n )\n}\n\n// Data-driven pattern internal component\nconst MenuItem: React.FC<{\n item: ContextMenuItem\n onSelect: (key: string) => void\n onClose: () => void\n}> = ({ item, onSelect, onClose }) => {\n const [showSubmenu, setShowSubmenu] = useState(false)\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n if (item.divider) {\n return <li className=\"divider my-1\"></li>\n }\n\n const handleClick = () => {\n if (item.disabled) return\n if (item.children && item.children.length > 0) return\n onSelect(item.key)\n onClose()\n }\n\n const hasSubmenu = item.children && item.children.length > 0\n\n const handleMouseEnter = () => {\n if (!hasSubmenu) return\n if (timeoutRef.current) clearTimeout(timeoutRef.current)\n setShowSubmenu(true)\n }\n\n const handleMouseLeave = () => {\n if (!hasSubmenu) return\n timeoutRef.current = setTimeout(() => setShowSubmenu(false), 100)\n }\n\n return (\n <li\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n className=\"relative\"\n >\n <button\n onClick={handleClick}\n disabled={item.disabled}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${item.disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n ${item.danger ? 'text-error hover:bg-error/10' : ''}\n `}\n >\n {item.icon && <span className=\"w-4 h-4\">{item.icon}</span>}\n <span className=\"flex-1\">{item.label}</span>\n {hasSubmenu && (\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n )}\n </button>\n {hasSubmenu && showSubmenu && (\n <ul\n className=\"menu bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1\"\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n >\n {item.children!.map((child) => (\n <MenuItem key={child.key} item={child} onSelect={onSelect} onClose={onClose} />\n ))}\n </ul>\n )}\n </li>\n )\n}\n\nconst ContextMenuRoot: React.FC<ContextMenuProps> = ({\n children,\n items,\n onSelect,\n disabled = false,\n className = '',\n}) => {\n const [visible, setVisible] = useState(false)\n const [position, setPosition] = useState<MenuPosition>({ x: 0, y: 0 })\n const menuRef = useRef<HTMLUListElement>(null)\n const triggerRef = useRef<HTMLDivElement>(null)\n\n const handleContextMenu = useCallback(\n (e: React.MouseEvent) => {\n if (disabled) return\n e.preventDefault()\n e.stopPropagation()\n\n // Calculate position, ensuring menu stays within viewport\n let x = e.clientX\n let y = e.clientY\n\n // We'll adjust after render when we know menu dimensions\n setPosition({ x, y })\n setVisible(true)\n },\n [disabled]\n )\n\n const handleClose = useCallback(() => {\n setVisible(false)\n }, [])\n\n const handleSelect = useCallback(\n (key: string) => {\n onSelect?.(key)\n },\n [onSelect]\n )\n\n // Adjust position after menu renders to keep it in viewport\n useEffect(() => {\n if (visible && menuRef.current) {\n const menu = menuRef.current\n const rect = menu.getBoundingClientRect()\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n let { x, y } = position\n\n if (x + rect.width > viewportWidth) {\n x = viewportWidth - rect.width - 8\n }\n if (y + rect.height > viewportHeight) {\n y = viewportHeight - rect.height - 8\n }\n\n if (x !== position.x || y !== position.y) {\n setPosition({ x, y })\n }\n }\n }, [visible, position])\n\n // Close on click outside or escape\n useEffect(() => {\n if (!visible) return\n\n const handleClickOutside = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n handleClose()\n }\n }\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n handleClose()\n }\n }\n\n const handleScroll = () => {\n handleClose()\n }\n\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n document.addEventListener('scroll', handleScroll, true)\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n document.removeEventListener('scroll', handleScroll, true)\n }\n }, [visible, handleClose])\n\n // Determine if using data-driven or compound pattern\n // Find menu content children (not the trigger element)\n const childArray = React.Children.toArray(children)\n const triggerChild = childArray[0]\n const menuChildren = childArray.slice(1)\n const useDataDriven = items && items.length > 0\n\n const contextValue: ContextMenuContextValue = {\n onSelect: handleSelect,\n onClose: handleClose,\n }\n\n return (\n <>\n <div ref={triggerRef} onContextMenu={handleContextMenu} className=\"inline-block\">\n {triggerChild}\n </div>\n {visible &&\n createPortal(\n <ContextMenuContext.Provider value={contextValue}>\n <ul\n ref={menuRef}\n className={`menu bg-base-100 rounded-box shadow-lg border border-base-300 min-w-[160px] p-1 fixed z-[9999] ${className}`}\n style={{ left: position.x, top: position.y }}\n >\n {useDataDriven\n ? items!.map((item) => (\n <MenuItem key={item.key} item={item} onSelect={handleSelect} onClose={handleClose} />\n ))\n : menuChildren}\n </ul>\n </ContextMenuContext.Provider>,\n document.body\n )}\n </>\n )\n}\n\n// Assign compound components\nexport const ContextMenu = Object.assign(ContextMenuRoot, {\n Item: ContextMenuItemComponent,\n Divider: ContextMenuDividerComponent,\n SubMenu: ContextMenuSubMenuComponent,\n})\n"],"names":["ContextMenuContext","createContext","useContextMenuContext","context","useContext","ContextMenuItemComponent","itemKey","children","icon","disabled","danger","className","onSelect","onClose","jsx","jsxs","ContextMenuDividerComponent","ContextMenuSubMenuComponent","_itemKey","label","showSubmenu","setShowSubmenu","useState","timeoutRef","useRef","handleMouseEnter","handleMouseLeave","MenuItem","item","handleClick","hasSubmenu","child","ContextMenuRoot","items","visible","setVisible","position","setPosition","menuRef","triggerRef","handleContextMenu","useCallback","e","x","y","handleClose","handleSelect","key","useEffect","rect","viewportWidth","viewportHeight","handleClickOutside","handleEscape","handleScroll","childArray","React","triggerChild","menuChildren","useDataDriven","contextValue","Fragment","createPortal","ContextMenu"],"mappings":"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"}
1
+ {"version":3,"file":"ContextMenu.js","sources":["../../src/components/ContextMenu.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect, useCallback, createContext, useContext } from 'react'\nimport { createPortal } from 'react-dom'\n\nexport interface ContextMenuItem {\n key: string\n label: React.ReactNode\n icon?: React.ReactNode\n disabled?: boolean\n danger?: boolean\n divider?: boolean\n children?: ContextMenuItem[]\n}\n\nexport interface ContextMenuProps {\n /** Element that triggers the context menu on right-click */\n children: React.ReactNode\n /** Menu items (data-driven pattern) */\n items?: ContextMenuItem[]\n /** Callback when an item is selected */\n onSelect?: (key: string) => void\n /** Whether the context menu is disabled */\n disabled?: boolean\n /** Additional CSS classes for the menu */\n className?: string\n}\n\nexport interface ContextMenuItemProps {\n /** Item content */\n children: React.ReactNode\n /** Icon to display before label */\n icon?: React.ReactNode\n /** Whether the item is disabled */\n disabled?: boolean\n /** Show as danger/destructive action */\n danger?: boolean\n /** Additional CSS classes */\n className?: string\n /** @internal */\n _key?: string\n}\n\nexport interface ContextMenuDividerProps {\n /** Additional CSS classes */\n className?: string\n}\n\nexport interface ContextMenuSubMenuProps {\n /** Submenu label */\n label: React.ReactNode\n /** Icon to display before label */\n icon?: React.ReactNode\n /** Whether the submenu is disabled */\n disabled?: boolean\n /** Submenu items */\n children: React.ReactNode\n /** Additional CSS classes */\n className?: string\n /** @internal */\n _key?: string\n}\n\ninterface ContextMenuContextValue {\n onSelect: (key: string) => void\n onClose: () => void\n}\n\ninterface MenuPosition {\n x: number\n y: number\n}\n\nconst ContextMenuContext = createContext<ContextMenuContextValue | null>(null)\n\nconst useContextMenuContext = () => {\n const context = useContext(ContextMenuContext)\n if (!context) {\n throw new Error('ContextMenu compound components must be used within a ContextMenu')\n }\n return context\n}\n\n// Compound pattern components\nconst ContextMenuItemComponent: React.FC<ContextMenuItemProps> = ({\n children,\n icon,\n disabled = false,\n danger = false,\n className = '',\n _key,\n}) => {\n const { onSelect, onClose } = useContextMenuContext()\n\n const handleClick = () => {\n if (disabled || !_key) return\n onSelect(_key)\n onClose()\n }\n\n return (\n <li className={className}>\n <button\n onClick={handleClick}\n disabled={disabled}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n ${danger ? 'text-error hover:bg-error/10' : ''}\n `}\n >\n {icon && <span className=\"w-4 h-4\">{icon}</span>}\n <span className=\"flex-1\">{children}</span>\n </button>\n </li>\n )\n}\n\nconst ContextMenuDividerComponent: React.FC<ContextMenuDividerProps> = ({ className = '' }) => {\n return <li className={`divider my-1 ${className}`}></li>\n}\n\nconst ContextMenuSubMenuComponent: React.FC<ContextMenuSubMenuProps> = ({\n label,\n icon,\n disabled = false,\n children,\n className = '',\n _key: _unusedKey,\n}) => {\n const [showSubmenu, setShowSubmenu] = useState(false)\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const handleMouseEnter = () => {\n if (disabled) return\n if (timeoutRef.current) clearTimeout(timeoutRef.current)\n setShowSubmenu(true)\n }\n\n const handleMouseLeave = () => {\n timeoutRef.current = setTimeout(() => setShowSubmenu(false), 100)\n }\n\n return (\n <li\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n className={`relative ${className}`}\n >\n <button\n disabled={disabled}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n `}\n >\n {icon && <span className=\"w-4 h-4\">{icon}</span>}\n <span className=\"flex-1\">{label}</span>\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n {showSubmenu && (\n <ul\n className=\"menu bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1\"\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n >\n {children}\n </ul>\n )}\n </li>\n )\n}\n\n// Data-driven pattern internal component\nconst MenuItem: React.FC<{\n item: ContextMenuItem\n onSelect: (key: string) => void\n onClose: () => void\n}> = ({ item, onSelect, onClose }) => {\n const [showSubmenu, setShowSubmenu] = useState(false)\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n if (item.divider) {\n return <li className=\"divider my-1\"></li>\n }\n\n const handleClick = () => {\n if (item.disabled) return\n if (item.children && item.children.length > 0) return\n onSelect(item.key)\n onClose()\n }\n\n const hasSubmenu = item.children && item.children.length > 0\n\n const handleMouseEnter = () => {\n if (!hasSubmenu) return\n if (timeoutRef.current) clearTimeout(timeoutRef.current)\n setShowSubmenu(true)\n }\n\n const handleMouseLeave = () => {\n if (!hasSubmenu) return\n timeoutRef.current = setTimeout(() => setShowSubmenu(false), 100)\n }\n\n return (\n <li\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n className=\"relative\"\n >\n <button\n onClick={handleClick}\n disabled={item.disabled}\n className={`\n flex items-center gap-2 w-full px-4 py-2 text-left text-sm\n ${item.disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-base-200'}\n ${item.danger ? 'text-error hover:bg-error/10' : ''}\n `}\n >\n {item.icon && <span className=\"w-4 h-4\">{item.icon}</span>}\n <span className=\"flex-1\">{item.label}</span>\n {hasSubmenu && (\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n )}\n </button>\n {hasSubmenu && showSubmenu && (\n <ul\n className=\"menu bg-base-100 rounded-box shadow-lg border border-base-300 absolute left-full top-0 min-w-[160px] z-50 p-1\"\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n >\n {item.children!.map((child) => (\n <MenuItem key={child.key} item={child} onSelect={onSelect} onClose={onClose} />\n ))}\n </ul>\n )}\n </li>\n )\n}\n\nconst ContextMenuRoot: React.FC<ContextMenuProps> = ({\n children,\n items,\n onSelect,\n disabled = false,\n className = '',\n}) => {\n const [visible, setVisible] = useState(false)\n const [position, setPosition] = useState<MenuPosition>({ x: 0, y: 0 })\n const menuRef = useRef<HTMLUListElement>(null)\n const triggerRef = useRef<HTMLDivElement>(null)\n\n const handleContextMenu = useCallback(\n (e: React.MouseEvent) => {\n if (disabled) return\n e.preventDefault()\n e.stopPropagation()\n\n // Calculate position, ensuring menu stays within viewport\n let x = e.clientX\n let y = e.clientY\n\n // We'll adjust after render when we know menu dimensions\n setPosition({ x, y })\n setVisible(true)\n },\n [disabled]\n )\n\n const handleClose = useCallback(() => {\n setVisible(false)\n }, [])\n\n const handleSelect = useCallback(\n (key: string) => {\n onSelect?.(key)\n },\n [onSelect]\n )\n\n // Adjust position after menu renders to keep it in viewport\n useEffect(() => {\n if (visible && menuRef.current) {\n const menu = menuRef.current\n const rect = menu.getBoundingClientRect()\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n let { x, y } = position\n\n if (x + rect.width > viewportWidth) {\n x = viewportWidth - rect.width - 8\n }\n if (y + rect.height > viewportHeight) {\n y = viewportHeight - rect.height - 8\n }\n\n if (x !== position.x || y !== position.y) {\n setPosition({ x, y })\n }\n }\n }, [visible, position])\n\n // Close on click outside or escape\n useEffect(() => {\n if (!visible) return\n\n const handleClickOutside = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n handleClose()\n }\n }\n\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n handleClose()\n }\n }\n\n const handleScroll = () => {\n handleClose()\n }\n\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n document.addEventListener('scroll', handleScroll, true)\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n document.removeEventListener('scroll', handleScroll, true)\n }\n }, [visible, handleClose])\n\n // Clone children to extract keys\n const cloneChildrenWithKeys = (children: React.ReactNode): React.ReactNode => {\n return React.Children.map(children, (child) => {\n if (React.isValidElement(child)) {\n const childKey = child.key != null ? String(child.key) : undefined\n if (child.type === ContextMenuItemComponent || child.type === ContextMenuSubMenuComponent) {\n return React.cloneElement(child as React.ReactElement<any>, { _key: childKey })\n }\n }\n return child\n })\n }\n\n // Determine if using data-driven or compound pattern\n // Find menu content children (not the trigger element)\n const childArray = React.Children.toArray(children)\n const triggerChild = childArray[0]\n const menuChildren = cloneChildrenWithKeys(childArray.slice(1))\n const useDataDriven = items && items.length > 0\n\n const contextValue: ContextMenuContextValue = {\n onSelect: handleSelect,\n onClose: handleClose,\n }\n\n return (\n <>\n <div ref={triggerRef} onContextMenu={handleContextMenu} className=\"inline-block\">\n {triggerChild}\n </div>\n {visible &&\n createPortal(\n <ContextMenuContext.Provider value={contextValue}>\n <ul\n ref={menuRef}\n className={`menu bg-base-100 rounded-box shadow-lg border border-base-300 min-w-[160px] p-1 fixed z-[9999] ${className}`}\n style={{ left: position.x, top: position.y }}\n >\n {useDataDriven\n ? items!.map((item) => (\n <MenuItem key={item.key} item={item} onSelect={handleSelect} onClose={handleClose} />\n ))\n : menuChildren}\n </ul>\n </ContextMenuContext.Provider>,\n document.body\n )}\n </>\n )\n}\n\n// Assign compound components\nexport const ContextMenu = Object.assign(ContextMenuRoot, {\n Item: ContextMenuItemComponent,\n Divider: ContextMenuDividerComponent,\n SubMenu: ContextMenuSubMenuComponent,\n})\n"],"names":["ContextMenuContext","createContext","useContextMenuContext","context","useContext","ContextMenuItemComponent","children","icon","disabled","danger","className","_key","onSelect","onClose","jsx","jsxs","ContextMenuDividerComponent","ContextMenuSubMenuComponent","label","_unusedKey","showSubmenu","setShowSubmenu","useState","timeoutRef","useRef","handleMouseEnter","handleMouseLeave","MenuItem","item","handleClick","hasSubmenu","child","ContextMenuRoot","items","visible","setVisible","position","setPosition","menuRef","triggerRef","handleContextMenu","useCallback","e","x","y","handleClose","handleSelect","key","useEffect","rect","viewportWidth","viewportHeight","handleClickOutside","handleEscape","handleScroll","cloneChildrenWithKeys","React","childKey","childArray","triggerChild","menuChildren","useDataDriven","contextValue","Fragment","createPortal","ContextMenu"],"mappings":";;;AAuEA,MAAMA,IAAqBC,EAA8C,IAAI,GAEvEC,IAAwB,MAAM;AAClC,QAAMC,IAAUC,EAAWJ,CAAkB;AAC7C,MAAI,CAACG;AACH,UAAM,IAAI,MAAM,mEAAmE;AAErF,SAAOA;AACT,GAGME,IAA2D,CAAC;AAAA,EAChE,UAAAC;AAAA,EACA,MAAAC;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,QAAAC,IAAS;AAAA,EACT,WAAAC,IAAY;AAAA,EACZ,MAAAC;AACF,MAAM;AACJ,QAAM,EAAE,UAAAC,GAAU,SAAAC,EAAA,IAAYX,EAAA;AAQ9B,SACE,gBAAAY,EAAC,QAAG,WAAAJ,GACF,UAAA,gBAAAK;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,SATc,MAAM;AACxB,QAAIP,KAAY,CAACG,MACjBC,EAASD,CAAI,GACbE,EAAA;AAAA,MACF;AAAA,MAMM,UAAAL;AAAA,MACA,WAAW;AAAA;AAAA,YAEPA,IAAW,kCAAkC,mBAAmB;AAAA,YAChEC,IAAS,iCAAiC,EAAE;AAAA;AAAA,MAG/C,UAAA;AAAA,QAAAF,KAAQ,gBAAAO,EAAC,QAAA,EAAK,WAAU,WAAW,UAAAP,GAAK;AAAA,QACzC,gBAAAO,EAAC,QAAA,EAAK,WAAU,UAAU,UAAAR,EAAA,CAAS;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA,GAEvC;AAEJ,GAEMU,IAAiE,CAAC,EAAE,WAAAN,IAAY,SAC7E,gBAAAI,EAAC,MAAA,EAAG,WAAW,gBAAgBJ,CAAS,IAAI,GAG/CO,IAAiE,CAAC;AAAA,EACtE,OAAAC;AAAA,EACA,MAAAX;AAAA,EACA,UAAAC,IAAW;AAAA,EACX,UAAAF;AAAA,EACA,WAAAI,IAAY;AAAA,EACZ,MAAMS;AACR,MAAM;AACJ,QAAM,CAACC,GAAaC,CAAc,IAAIC,EAAS,EAAK,GAC9CC,IAAaC,EAA6C,IAAI,GAE9DC,IAAmB,MAAM;AAC7B,IAAIjB,MACAe,EAAW,WAAS,aAAaA,EAAW,OAAO,GACvDF,EAAe,EAAI;AAAA,EACrB,GAEMK,IAAmB,MAAM;AAC7B,IAAAH,EAAW,UAAU,WAAW,MAAMF,EAAe,EAAK,GAAG,GAAG;AAAA,EAClE;AAEA,SACE,gBAAAN;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,cAAcU;AAAA,MACd,cAAcC;AAAA,MACd,WAAW,YAAYhB,CAAS;AAAA,MAEhC,UAAA;AAAA,QAAA,gBAAAK;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,UAAAP;AAAA,YACA,WAAW;AAAA;AAAA,YAEPA,IAAW,kCAAkC,mBAAmB;AAAA;AAAA,YAGnE,UAAA;AAAA,cAAAD,KAAQ,gBAAAO,EAAC,QAAA,EAAK,WAAU,WAAW,UAAAP,GAAK;AAAA,cACzC,gBAAAO,EAAC,QAAA,EAAK,WAAU,UAAU,UAAAI,GAAM;AAAA,cAChC,gBAAAJ,EAAC,SAAI,WAAU,WAAU,MAAK,QAAO,SAAQ,aAAY,QAAO,gBAC9D,4BAAC,QAAA,EAAK,eAAc,SAAQ,gBAAe,SAAQ,aAAa,GAAG,GAAE,gBAAe,EAAA,CACtF;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAEDM,KACC,gBAAAN;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,cAAcW;AAAA,YACd,cAAcC;AAAA,YAEb,UAAApB;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA,EAAA;AAIR,GAGMqB,IAID,CAAC,EAAE,MAAAC,GAAM,UAAAhB,GAAU,SAAAC,QAAc;AACpC,QAAM,CAACO,GAAaC,CAAc,IAAIC,EAAS,EAAK,GAC9CC,IAAaC,EAA6C,IAAI;AAEpE,MAAII,EAAK;AACP,WAAO,gBAAAd,EAAC,MAAA,EAAG,WAAU,eAAA,CAAe;AAGtC,QAAMe,IAAc,MAAM;AACxB,IAAID,EAAK,YACLA,EAAK,YAAYA,EAAK,SAAS,SAAS,MAC5ChB,EAASgB,EAAK,GAAG,GACjBf,EAAA;AAAA,EACF,GAEMiB,IAAaF,EAAK,YAAYA,EAAK,SAAS,SAAS,GAErDH,IAAmB,MAAM;AAC7B,IAAKK,MACDP,EAAW,WAAS,aAAaA,EAAW,OAAO,GACvDF,EAAe,EAAI;AAAA,EACrB,GAEMK,IAAmB,MAAM;AAC7B,IAAKI,MACLP,EAAW,UAAU,WAAW,MAAMF,EAAe,EAAK,GAAG,GAAG;AAAA,EAClE;AAEA,SACE,gBAAAN;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,cAAcU;AAAA,MACd,cAAcC;AAAA,MACd,WAAU;AAAA,MAEV,UAAA;AAAA,QAAA,gBAAAX;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,SAASc;AAAA,YACT,UAAUD,EAAK;AAAA,YACf,WAAW;AAAA;AAAA,YAEPA,EAAK,WAAW,kCAAkC,mBAAmB;AAAA,YACrEA,EAAK,SAAS,iCAAiC,EAAE;AAAA;AAAA,YAGpD,UAAA;AAAA,cAAAA,EAAK,QAAQ,gBAAAd,EAAC,QAAA,EAAK,WAAU,WAAW,YAAK,MAAK;AAAA,cACnD,gBAAAA,EAAC,QAAA,EAAK,WAAU,UAAU,YAAK,OAAM;AAAA,cACpCgB,uBACE,OAAA,EAAI,WAAU,WAAU,MAAK,QAAO,SAAQ,aAAY,QAAO,gBAC9D,UAAA,gBAAAhB,EAAC,QAAA,EAAK,eAAc,SAAQ,gBAAe,SAAQ,aAAa,GAAG,GAAE,eAAA,CAAe,EAAA,CACtF;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAGHgB,KAAcV,KACb,gBAAAN;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,cAAcW;AAAA,YACd,cAAcC;AAAA,YAEb,UAAAE,EAAK,SAAU,IAAI,CAACG,MACnB,gBAAAjB,EAACa,GAAA,EAAyB,MAAMI,GAAO,UAAAnB,GAAoB,SAAAC,EAAA,GAA5CkB,EAAM,GAAwD,CAC9E;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA,EAAA;AAIR,GAEMC,IAA8C,CAAC;AAAA,EACnD,UAAA1B;AAAA,EACA,OAAA2B;AAAA,EACA,UAAArB;AAAA,EACA,UAAAJ,IAAW;AAAA,EACX,WAAAE,IAAY;AACd,MAAM;AACJ,QAAM,CAACwB,GAASC,CAAU,IAAIb,EAAS,EAAK,GACtC,CAACc,GAAUC,CAAW,IAAIf,EAAuB,EAAE,GAAG,GAAG,GAAG,GAAG,GAC/DgB,IAAUd,EAAyB,IAAI,GACvCe,IAAaf,EAAuB,IAAI,GAExCgB,IAAoBC;AAAA,IACxB,CAACC,MAAwB;AACvB,UAAIlC,EAAU;AACd,MAAAkC,EAAE,eAAA,GACFA,EAAE,gBAAA;AAGF,UAAIC,IAAID,EAAE,SACNE,IAAIF,EAAE;AAGV,MAAAL,EAAY,EAAE,GAAAM,GAAG,GAAAC,GAAG,GACpBT,EAAW,EAAI;AAAA,IACjB;AAAA,IACA,CAAC3B,CAAQ;AAAA,EAAA,GAGLqC,IAAcJ,EAAY,MAAM;AACpC,IAAAN,EAAW,EAAK;AAAA,EAClB,GAAG,CAAA,CAAE,GAECW,IAAeL;AAAA,IACnB,CAACM,MAAgB;AACf,MAAAnC,IAAWmC,CAAG;AAAA,IAChB;AAAA,IACA,CAACnC,CAAQ;AAAA,EAAA;AAIX,EAAAoC,EAAU,MAAM;AACd,QAAId,KAAWI,EAAQ,SAAS;AAE9B,YAAMW,IADOX,EAAQ,QACH,sBAAA,GACZY,IAAgB,OAAO,YACvBC,IAAiB,OAAO;AAE9B,UAAI,EAAE,GAAAR,GAAG,GAAAC,EAAA,IAAMR;AAEf,MAAIO,IAAIM,EAAK,QAAQC,MACnBP,IAAIO,IAAgBD,EAAK,QAAQ,IAE/BL,IAAIK,EAAK,SAASE,MACpBP,IAAIO,IAAiBF,EAAK,SAAS,KAGjCN,MAAMP,EAAS,KAAKQ,MAAMR,EAAS,MACrCC,EAAY,EAAE,GAAAM,GAAG,GAAAC,GAAG;AAAA,IAExB;AAAA,EACF,GAAG,CAACV,GAASE,CAAQ,CAAC,GAGtBY,EAAU,MAAM;AACd,QAAI,CAACd,EAAS;AAEd,UAAMkB,IAAqB,CAACV,MAAkB;AAC5C,MAAIJ,EAAQ,WAAW,CAACA,EAAQ,QAAQ,SAASI,EAAE,MAAc,KAC/DG,EAAA;AAAA,IAEJ,GAEMQ,IAAe,CAACX,MAAqB;AACzC,MAAIA,EAAE,QAAQ,YACZG,EAAA;AAAA,IAEJ,GAEMS,IAAe,MAAM;AACzB,MAAAT,EAAA;AAAA,IACF;AAEA,oBAAS,iBAAiB,aAAaO,CAAkB,GACzD,SAAS,iBAAiB,WAAWC,CAAY,GACjD,SAAS,iBAAiB,UAAUC,GAAc,EAAI,GAE/C,MAAM;AACX,eAAS,oBAAoB,aAAaF,CAAkB,GAC5D,SAAS,oBAAoB,WAAWC,CAAY,GACpD,SAAS,oBAAoB,UAAUC,GAAc,EAAI;AAAA,IAC3D;AAAA,EACF,GAAG,CAACpB,GAASW,CAAW,CAAC;AAGzB,QAAMU,IAAwB,CAACjD,MACtBkD,EAAM,SAAS,IAAIlD,GAAU,CAACyB,MAAU;AAC7C,QAAIyB,EAAM,eAAezB,CAAK,GAAG;AAC/B,YAAM0B,IAAW1B,EAAM,OAAO,OAAO,OAAOA,EAAM,GAAG,IAAI;AACzD,UAAIA,EAAM,SAAS1B,KAA4B0B,EAAM,SAASd;AAC5D,eAAOuC,EAAM,aAAazB,GAAkC,EAAE,MAAM0B,GAAU;AAAA,IAElF;AACA,WAAO1B;AAAA,EACT,CAAC,GAKG2B,IAAaF,EAAM,SAAS,QAAQlD,CAAQ,GAC5CqD,IAAeD,EAAW,CAAC,GAC3BE,IAAeL,EAAsBG,EAAW,MAAM,CAAC,CAAC,GACxDG,IAAgB5B,KAASA,EAAM,SAAS,GAExC6B,IAAwC;AAAA,IAC5C,UAAUhB;AAAA,IACV,SAASD;AAAA,EAAA;AAGX,SACE,gBAAA9B,EAAAgD,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAjD,EAAC,SAAI,KAAKyB,GAAY,eAAeC,GAAmB,WAAU,gBAC/D,UAAAmB,EAAA,CACH;AAAA,IACCzB,KACC8B;AAAA,MACE,gBAAAlD,EAACd,EAAmB,UAAnB,EAA4B,OAAO8D,GAClC,UAAA,gBAAAhD;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,KAAKwB;AAAA,UACL,WAAW,kGAAkG5B,CAAS;AAAA,UACtH,OAAO,EAAE,MAAM0B,EAAS,GAAG,KAAKA,EAAS,EAAA;AAAA,UAExC,UAAAyB,IACG5B,EAAO,IAAI,CAACL,MACV,gBAAAd,EAACa,GAAA,EAAwB,MAAAC,GAAY,UAAUkB,GAAc,SAASD,EAAA,GAAvDjB,EAAK,GAA+D,CACpF,IACDgC;AAAA,QAAA;AAAA,MAAA,GAER;AAAA,MACA,SAAS;AAAA,IAAA;AAAA,EACX,GACJ;AAEJ,GAGaK,IAAc,OAAO,OAAOjC,GAAiB;AAAA,EACxD,MAAM3B;AAAA,EACN,SAASW;AAAA,EACT,SAASC;AACX,CAAC;"}
@@ -56,8 +56,6 @@ export interface DropdownMenuProps {
56
56
  }
57
57
  export interface DropdownItemProps {
58
58
  children?: React.ReactNode;
59
- /** Unique key for the item */
60
- itemKey?: string;
61
59
  /** Icon to display before label */
62
60
  icon?: React.ReactNode;
63
61
  /** Item label (alternative to children) */
@@ -67,18 +65,21 @@ export interface DropdownItemProps {
67
65
  disabled?: boolean;
68
66
  danger?: boolean;
69
67
  className?: string;
68
+ /** @internal */
70
69
  _index?: number;
70
+ /** @internal */
71
+ _key?: string;
71
72
  }
72
73
  export interface DropdownSubMenuProps {
73
74
  children: React.ReactNode;
74
- /** Unique key for the submenu */
75
- itemKey?: string;
76
75
  /** Submenu title/label */
77
76
  title: React.ReactNode;
78
77
  /** Icon to display before title */
79
78
  icon?: React.ReactNode;
80
79
  disabled?: boolean;
81
80
  className?: string;
81
+ /** @internal */
82
+ _key?: string;
82
83
  }
83
84
  export interface DropdownDividerProps {
84
85
  className?: string;
@@ -90,8 +91,8 @@ declare function DropdownTrigger({ children, className }: DropdownTriggerProps):
90
91
  className?: string;
91
92
  }, string | React.JSXElementConstructor<any>>;
92
93
  declare function DropdownMenu({ children, className }: DropdownMenuProps): import("react/jsx-runtime").JSX.Element;
93
- declare function DropdownItem({ children, itemKey, icon, label, onClick, active, disabled, danger, className, }: DropdownItemProps): import("react/jsx-runtime").JSX.Element;
94
- declare function DropdownSubMenu({ children, itemKey, title, icon, disabled, className, }: DropdownSubMenuProps): import("react/jsx-runtime").JSX.Element;
94
+ declare function DropdownItem({ children, icon, label, onClick, active, disabled, danger, className, _key, }: DropdownItemProps): import("react/jsx-runtime").JSX.Element;
95
+ declare function DropdownSubMenu({ children, title, icon, disabled, className, _key, }: DropdownSubMenuProps): import("react/jsx-runtime").JSX.Element;
95
96
  declare function DropdownDivider({ className }: DropdownDividerProps): import("react/jsx-runtime").JSX.Element;
96
97
  export declare const Dropdown: typeof DropdownRoot & {
97
98
  Trigger: typeof DropdownTrigger;