@dxos/react-ui-menu 0.8.4-main.fd6878d → 0.8.4-staging.60fe92afc8

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.
Files changed (67) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/browser/index.mjs +546 -403
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/translations.mjs +14 -0
  6. package/dist/lib/browser/translations.mjs.map +7 -0
  7. package/dist/lib/node-esm/index.mjs +546 -403
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/lib/node-esm/translations.mjs +16 -0
  11. package/dist/lib/node-esm/translations.mjs.map +7 -0
  12. package/dist/types/src/builder.d.ts +28 -3
  13. package/dist/types/src/builder.d.ts.map +1 -1
  14. package/dist/types/src/builder.test.d.ts +2 -0
  15. package/dist/types/src/builder.test.d.ts.map +1 -0
  16. package/dist/types/src/components/ActionLabel.d.ts +3 -3
  17. package/dist/types/src/components/ActionLabel.d.ts.map +1 -1
  18. package/dist/types/src/components/DropdownMenu.d.ts +4 -2
  19. package/dist/types/src/components/DropdownMenu.d.ts.map +1 -1
  20. package/dist/types/src/components/Menu.d.ts +50 -0
  21. package/dist/types/src/components/Menu.d.ts.map +1 -0
  22. package/dist/types/src/components/Menu.stories.d.ts +21 -0
  23. package/dist/types/src/components/Menu.stories.d.ts.map +1 -0
  24. package/dist/types/src/components/Menu.test.d.ts +2 -0
  25. package/dist/types/src/components/Menu.test.d.ts.map +1 -0
  26. package/dist/types/src/components/ToolbarMenu.d.ts +7 -6
  27. package/dist/types/src/components/ToolbarMenu.d.ts.map +1 -1
  28. package/dist/types/src/components/ToolbarMenu.stories.d.ts +34 -0
  29. package/dist/types/src/components/ToolbarMenu.stories.d.ts.map +1 -0
  30. package/dist/types/src/components/index.d.ts +1 -3
  31. package/dist/types/src/components/index.d.ts.map +1 -1
  32. package/dist/types/src/hooks/useMenuActions.d.ts +18 -7
  33. package/dist/types/src/hooks/useMenuActions.d.ts.map +1 -1
  34. package/dist/types/src/index.d.ts +1 -2
  35. package/dist/types/src/index.d.ts.map +1 -1
  36. package/dist/types/src/testing/generator.d.ts +20 -14
  37. package/dist/types/src/testing/generator.d.ts.map +1 -1
  38. package/dist/types/src/translations.d.ts +2 -2
  39. package/dist/types/src/translations.d.ts.map +1 -1
  40. package/dist/types/src/types.d.ts +38 -27
  41. package/dist/types/src/types.d.ts.map +1 -1
  42. package/dist/types/src/util.d.ts +15 -6
  43. package/dist/types/src/util.d.ts.map +1 -1
  44. package/dist/types/tsconfig.tsbuildinfo +1 -1
  45. package/package.json +45 -33
  46. package/src/builder.test.ts +224 -0
  47. package/src/builder.ts +76 -26
  48. package/src/components/ActionLabel.tsx +7 -6
  49. package/src/components/DropdownMenu.tsx +12 -6
  50. package/src/components/Menu.stories.tsx +266 -0
  51. package/src/components/Menu.test.tsx +447 -0
  52. package/src/components/Menu.tsx +348 -0
  53. package/src/components/ToolbarMenu.stories.tsx +95 -0
  54. package/src/components/ToolbarMenu.tsx +165 -98
  55. package/src/components/index.ts +1 -3
  56. package/src/hooks/useMenuActions.ts +44 -53
  57. package/src/index.ts +1 -3
  58. package/src/testing/generator.ts +74 -64
  59. package/src/translations.ts +1 -1
  60. package/src/types.ts +39 -27
  61. package/src/util.ts +35 -16
  62. package/dist/types/src/components/MenuContext.d.ts +0 -13
  63. package/dist/types/src/components/MenuContext.d.ts.map +0 -1
  64. package/dist/types/src/components/Menus.stories.d.ts +0 -25
  65. package/dist/types/src/components/Menus.stories.d.ts.map +0 -1
  66. package/src/components/MenuContext.tsx +0 -50
  67. package/src/components/Menus.stories.tsx +0 -69
@@ -1,48 +1,39 @@
1
1
  import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
2
 
3
- // src/index.ts
4
- import { rxFromSignal } from "@dxos/app-graph";
5
-
6
- // src/components/DropdownMenu.tsx
7
- import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
8
- import { useControllableState } from "@radix-ui/react-use-controllable-state";
9
- import React3, { useCallback } from "react";
10
- import { Icon, DropdownMenu as NaturalDropdownMenu } from "@dxos/react-ui";
11
-
12
- // src/components/ActionLabel.tsx
13
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
14
- import React from "react";
15
- import { keySymbols } from "@dxos/keyboard";
16
- import { toLocalizedString, useTranslation } from "@dxos/react-ui";
17
- import { descriptionText, mx } from "@dxos/react-ui-theme";
18
-
19
- // src/translations.ts
20
- var translationKey = "react-ui-menu";
21
- var translations = [
22
- {
23
- "en-US": {
24
- [translationKey]: {}
25
- }
26
- }
27
- ];
3
+ // src/components/Menu.tsx
4
+ import { Atom, RegistryContext, useAtomValue } from "@effect-atom/atom-react";
5
+ import { createContextScope } from "@radix-ui/react-context";
6
+ import { useControllableState as useControllableState2 } from "@radix-ui/react-use-controllable-state";
7
+ import React4, { createContext, useCallback as useCallback3, useContext, useMemo } from "react";
8
+ import { log } from "@dxos/log";
9
+ import { Icon as Icon3, DropdownMenu as NaturalDropdownMenu2 } from "@dxos/react-ui";
28
10
 
29
11
  // src/util.ts
30
- import { ACTION_GROUP_TYPE, ACTION_TYPE, actionGroupSymbol } from "@dxos/app-graph";
12
+ import * as Effect from "effect/Effect";
13
+ import { Node } from "@dxos/app-graph";
14
+ import { EffectEx } from "@dxos/effect";
31
15
  import { getHostPlatform } from "@dxos/util";
16
+ var executeMenuAction = async (action, params = {}) => {
17
+ let effect = action.data(params);
18
+ if (action._actionContext) {
19
+ effect = effect.pipe(Effect.provide(action._actionContext));
20
+ }
21
+ await EffectEx.runAndForwardErrors(effect);
22
+ };
32
23
  var getShortcut = (action) => {
33
24
  return typeof action.properties?.keyBinding === "string" ? action.properties.keyBinding : action.properties?.keyBinding?.[getHostPlatform()];
34
25
  };
35
26
  var createMenuAction = (id, invoke, properties) => ({
36
27
  id,
37
- type: ACTION_TYPE,
28
+ type: Node.ActionType,
38
29
  properties,
39
- data: invoke
30
+ data: () => Effect.sync(invoke)
40
31
  });
41
32
  var createMenuItemGroup = (id, properties) => ({
42
33
  id,
43
- type: ACTION_GROUP_TYPE,
34
+ type: Node.ActionGroupType,
44
35
  properties,
45
- data: actionGroupSymbol
36
+ data: Node.actionGroupSymbol
46
37
  });
47
38
  var createGapSeparator = (id = "gap", source = "root") => ({
48
39
  nodes: [
@@ -58,7 +49,8 @@ var createGapSeparator = (id = "gap", source = "root") => ({
58
49
  edges: [
59
50
  {
60
51
  source,
61
- target: id
52
+ target: id,
53
+ relation: "child"
62
54
  }
63
55
  ]
64
56
  });
@@ -76,128 +68,109 @@ var createLineSeparator = (id = "line", source = "root") => ({
76
68
  edges: [
77
69
  {
78
70
  source,
79
- target: id
71
+ target: id,
72
+ relation: "child"
80
73
  }
81
74
  ]
82
75
  });
83
76
 
84
77
  // src/components/ActionLabel.tsx
78
+ import React from "react";
79
+ import { keySymbols } from "@dxos/keyboard";
80
+ import { toLocalizedString, useTranslation } from "@dxos/react-ui";
81
+ import { mx } from "@dxos/ui-theme";
82
+ import { translationKey } from "#translations";
85
83
  var ActionLabel = ({ action }) => {
86
- var _effect = _useSignals();
87
- try {
88
- const { t } = useTranslation(translationKey);
89
- const shortcut = getShortcut(action);
90
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", {
91
- className: "grow truncate"
92
- }, toLocalizedString(action.properties.label, t)), shortcut && /* @__PURE__ */ React.createElement("span", {
93
- className: mx("shrink-0", descriptionText)
94
- }, keySymbols(shortcut).join("")));
95
- } finally {
96
- _effect.f();
97
- }
84
+ const { t } = useTranslation(translationKey);
85
+ const shortcut = getShortcut(action);
86
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", {
87
+ className: "grow truncate"
88
+ }, toLocalizedString(action.properties.label, t)), shortcut && /* @__PURE__ */ React.createElement("span", {
89
+ className: mx("shrink-0", "text-description")
90
+ }, keySymbols(shortcut).join("")));
98
91
  };
99
92
  var actionLabel = (action, t) => {
100
93
  const shortcut = getShortcut(action);
101
94
  return `${toLocalizedString(action.properties.label, t)}${shortcut ? ` (${keySymbols(shortcut).join("")})` : ""}`;
102
95
  };
103
96
 
104
- // src/components/MenuContext.tsx
105
- import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
106
- import { createContextScope } from "@radix-ui/react-context";
107
- import React2, { useMemo } from "react";
108
- var MENU_NAME = "GraphMenu";
109
- var [createMenuContext, createMenuScope] = createContextScope(MENU_NAME, []);
110
- var [MenuContextProvider, useMenu] = createMenuContext(MENU_NAME);
111
- var menuContextDefaults = {
112
- iconSize: 5,
113
- useGroupItems: () => null
114
- };
115
- var useMenuScope = createMenuScope();
116
- var MenuProvider = ({ children, useGroupItems = menuContextDefaults.useGroupItems, iconSize = menuContextDefaults.iconSize, attendableId }) => {
117
- var _effect = _useSignals2();
118
- try {
119
- const { scope } = useMenuScope(void 0);
120
- return /* @__PURE__ */ React2.createElement(MenuContextProvider, {
121
- useGroupItems,
122
- iconSize,
123
- attendableId,
124
- scope
125
- }, children);
126
- } finally {
127
- _effect.f();
128
- }
129
- };
130
- var useMenuItems = (group, propsItems, consumerName = "useMenuItemConsumer", __menuScope) => {
131
- const { useGroupItems } = useMenu(consumerName, __menuScope);
132
- const groupItems = useGroupItems(group);
133
- return useMemo(() => propsItems ?? groupItems ?? void 0, [
134
- propsItems,
135
- groupItems
136
- ]);
137
- };
97
+ // src/components/ToolbarMenu.tsx
98
+ import React3, { useCallback as useCallback2 } from "react";
99
+ import { Icon as Icon2, Toolbar as NaturalToolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
100
+ import { composable, composableProps } from "@dxos/react-ui";
101
+ import { useAttention } from "@dxos/react-ui-attention";
102
+ import { translationKey as translationKey2 } from "#translations";
103
+
104
+ // src/types.ts
105
+ import { Node as Node2 } from "@dxos/app-graph";
106
+ var MenuSeparatorType = "@dxos/react-ui-toolbar/separator";
107
+ var isSeparator = (node) => node.type === MenuSeparatorType;
108
+ var isMenuGroup = (node) => node.type === Node2.ActionGroupType;
138
109
 
139
110
  // src/components/DropdownMenu.tsx
111
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
112
+ import React2, { useCallback } from "react";
113
+ import { Icon, DropdownMenu as NaturalDropdownMenu } from "@dxos/react-ui";
140
114
  var DropdownMenuItem = ({ item, onClick, __menuScope }) => {
141
- var _effect = _useSignals3();
142
- try {
143
- const action = item;
144
- const handleClick = useCallback((event) => onClick(action, event), [
145
- action,
146
- onClick
147
- ]);
148
- const { iconSize } = useMenu("DropdownMenuItem", __menuScope);
149
- return /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Item, {
150
- onClick: handleClick,
151
- classNames: "gap-2",
152
- disabled: action.properties?.disabled,
153
- ...action.properties?.testId && {
154
- "data-testid": action.properties.testId
155
- }
156
- }, action.properties?.icon && /* @__PURE__ */ React3.createElement(Icon, {
157
- icon: action.properties.icon,
158
- size: iconSize
159
- }), /* @__PURE__ */ React3.createElement(ActionLabel, {
160
- action
161
- }));
162
- } finally {
163
- _effect.f();
164
- }
115
+ const action = item;
116
+ const handleClick = useCallback((event) => onClick(action, event), [
117
+ action,
118
+ onClick
119
+ ]);
120
+ const { iconSize } = useMenuScoped("DropdownMenuItem", __menuScope);
121
+ return /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Item, {
122
+ onClick: handleClick,
123
+ classNames: "gap-2",
124
+ disabled: action.properties?.disabled,
125
+ ...action.properties?.testId && {
126
+ "data-testid": action.properties.testId
127
+ }
128
+ }, action.properties?.icon && /* @__PURE__ */ React2.createElement(Icon, {
129
+ icon: action.properties.icon,
130
+ size: iconSize
131
+ }), /* @__PURE__ */ React2.createElement(ActionLabel, {
132
+ action
133
+ }));
165
134
  };
166
135
  var DropdownMenuRoot = ({ items: propsItems, group, open, defaultOpen, onOpenChange, caller, children, __menuScope, ...naturalProps }) => {
167
- var _effect = _useSignals3();
168
- try {
169
- const [optionsMenuOpen, setOptionsMenuOpen] = useControllableState({
170
- prop: open,
171
- defaultProp: defaultOpen,
172
- onChange: onOpenChange
173
- });
174
- const handleActionClick = useCallback((action, event) => {
175
- if (action.properties?.disabled) {
176
- return;
177
- }
178
- event.stopPropagation();
179
- setOptionsMenuOpen(false);
180
- void action.data?.({
136
+ const [optionsMenuOpen, setOptionsMenuOpen] = useControllableState({
137
+ prop: open,
138
+ defaultProp: defaultOpen,
139
+ onChange: onOpenChange
140
+ });
141
+ const { onAction } = useMenuScoped("DropdownMenuRoot", __menuScope);
142
+ const handleActionClick = useCallback((action, event) => {
143
+ if (action.properties?.disabled) {
144
+ return;
145
+ }
146
+ event.stopPropagation();
147
+ setOptionsMenuOpen(false);
148
+ if (onAction) {
149
+ onAction(action, {
181
150
  parent: group,
182
151
  caller
183
152
  });
184
- }, [
185
- group,
186
- caller
187
- ]);
188
- const items = useMenuItems(group, propsItems);
189
- return /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Root, {
190
- open: optionsMenuOpen,
191
- onOpenChange: setOptionsMenuOpen,
192
- ...naturalProps
193
- }, children, /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Portal, null, /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Content, null, /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Viewport, null, items?.map((item) => /* @__PURE__ */ React3.createElement(DropdownMenuItem, {
194
- key: item.id,
195
- item,
196
- onClick: handleActionClick
197
- }))), /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Arrow, null))));
198
- } finally {
199
- _effect.f();
200
- }
153
+ } else {
154
+ void executeMenuAction(action, {
155
+ parent: group,
156
+ caller
157
+ });
158
+ }
159
+ }, [
160
+ group,
161
+ caller,
162
+ onAction
163
+ ]);
164
+ const items = useMenuItems(group, propsItems);
165
+ return /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Root, {
166
+ open: optionsMenuOpen,
167
+ onOpenChange: setOptionsMenuOpen,
168
+ ...naturalProps
169
+ }, children, /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Portal, null, /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Content, null, /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Viewport, null, items?.map((item) => /* @__PURE__ */ React2.createElement(DropdownMenuItem, {
170
+ key: item.id,
171
+ item,
172
+ onClick: handleActionClick
173
+ }))), /* @__PURE__ */ React2.createElement(NaturalDropdownMenu.Arrow, null))));
201
174
  };
202
175
  var DropdownMenu = {
203
176
  Root: DropdownMenuRoot,
@@ -206,338 +179,508 @@ var DropdownMenu = {
206
179
  };
207
180
 
208
181
  // src/components/ToolbarMenu.tsx
209
- import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
210
- import React4, { Fragment, useCallback as useCallback2 } from "react";
211
- import { Icon as Icon2, Toolbar as NaturalToolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
212
- import { useAttention } from "@dxos/react-ui-attention";
213
- import { mx as mx2, textBlockWidth, toolbarLayout } from "@dxos/react-ui-theme";
214
-
215
- // src/types.ts
216
- import { ACTION_GROUP_TYPE as ACTION_GROUP_TYPE2 } from "@dxos/app-graph";
217
- var MenuSeparatorType = "@dxos/react-ui-toolbar/separator";
218
- var isSeparator = (node) => node.type === MenuSeparatorType;
219
- var isMenuGroup = (node) => node.type === ACTION_GROUP_TYPE2;
220
-
221
- // src/components/ToolbarMenu.tsx
222
- var ActionToolbarItem = ({ action, __menuScope }) => {
223
- var _effect = _useSignals4();
224
- try {
225
- const { iconSize } = useMenu("ActionToolbarItem", __menuScope);
226
- const { t } = useTranslation2(translationKey);
227
- const handleClick = useCallback2(() => action.data?.(), [
228
- action
229
- ]);
230
- const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
231
- const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
232
- const rootProps = icon ? {
233
- icon,
234
- size: iconSize,
235
- iconOnly,
236
- label: actionLabel(action, t)
237
- } : {
238
- children: /* @__PURE__ */ React4.createElement(ActionLabel, {
239
- action
240
- })
241
- };
242
- return hidden ? null : /* @__PURE__ */ React4.createElement(Root, {
243
- key: action.id,
244
- disabled,
245
- onClick: handleClick,
246
- variant: "ghost",
247
- ...testId && {
248
- "data-testid": testId
249
- },
250
- ...rootProps,
251
- classNames
252
- });
253
- } finally {
254
- _effect.f();
182
+ var ActionToolbarItem = ({ __menuScope, action }) => {
183
+ const { iconSize, onAction } = useMenuScoped("ActionToolbarItem", __menuScope);
184
+ const { t } = useTranslation2(translationKey2);
185
+ const { icon, iconOnly = true, disabled, testId, hidden, classNames, iconClassNames } = action.properties;
186
+ const buttonVariant = action.properties.variant === "primary" ? "primary" : "ghost";
187
+ const handleClick = useCallback2(() => {
188
+ if (onAction) {
189
+ onAction(action, {});
190
+ } else {
191
+ void executeMenuAction(action);
192
+ }
193
+ }, [
194
+ action,
195
+ onAction
196
+ ]);
197
+ const commonProps = {
198
+ variant: buttonVariant,
199
+ disabled,
200
+ classNames,
201
+ onClick: handleClick,
202
+ ...testId && {
203
+ "data-testid": testId
204
+ }
205
+ };
206
+ if (hidden) {
207
+ return null;
255
208
  }
209
+ return icon ? /* @__PURE__ */ React3.createElement(NaturalToolbar.IconButton, {
210
+ key: action.id,
211
+ ...commonProps,
212
+ icon,
213
+ size: iconSize,
214
+ iconOnly,
215
+ iconClassNames,
216
+ label: actionLabel(action, t)
217
+ }) : /* @__PURE__ */ React3.createElement(NaturalToolbar.Button, {
218
+ key: action.id,
219
+ ...commonProps
220
+ }, /* @__PURE__ */ React3.createElement(ActionLabel, {
221
+ action
222
+ }));
256
223
  };
257
- var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
258
- var _effect = _useSignals4();
259
- try {
260
- const { iconOnly, disabled, testId } = group.properties;
261
- const { t } = useTranslation2(translationKey);
262
- const { iconSize } = useMenu("DropdownMenuToolbarItem", __menuScope);
263
- const items = useMenuItems(group, propsItems, "DropdownMenuToolbarItem", __menuScope);
264
- const activeItem = items?.find((item) => !!item.properties.checked);
265
- const icon = group.properties.applyActive && // TODO(thure): Handle other menu item types.
266
- activeItem?.properties.icon || group.properties.icon;
267
- const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
268
- const labelAction = group.properties.applyActive && activeItem ? activeItem : group;
269
- const rootProps = icon ? {
270
- icon,
271
- size: iconSize,
272
- iconOnly,
273
- label: actionLabel(labelAction, t),
274
- caretDown: true
275
- } : {
276
- children: /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(ActionLabel, {
277
- action: labelAction
278
- }), /* @__PURE__ */ React4.createElement(Icon2, {
279
- size: 3,
280
- icon: "ph--caret-down--bold",
281
- classNames: "mis-1"
282
- }))
283
- };
284
- return /* @__PURE__ */ React4.createElement(DropdownMenu.Root, {
285
- group,
286
- items
287
- }, /* @__PURE__ */ React4.createElement(DropdownMenu.Trigger, {
288
- asChild: true
289
- }, /* @__PURE__ */ React4.createElement(Root, {
290
- variant: "ghost",
291
- disabled,
292
- ...rootProps,
293
- ...testId && {
294
- "data-testid": testId
295
- }
296
- })));
297
- } finally {
298
- _effect.f();
299
- }
224
+ var DropdownMenuToolbarItem = ({ __menuScope, group, items: propsItems }) => {
225
+ const { iconOnly, disabled, testId } = group.properties;
226
+ const { t } = useTranslation2(translationKey2);
227
+ const { iconSize } = useMenuScoped("DropdownMenuToolbarItem", __menuScope);
228
+ const items = useMenuItems(group, propsItems, "DropdownMenuToolbarItem", __menuScope);
229
+ const activeItem = items?.find((item) => !!item.properties.checked);
230
+ const icon = group.properties.applyActive && // TODO(thure): Handle other menu item types.
231
+ activeItem?.properties.icon || group.properties.icon;
232
+ const iconClassNames = group.properties.applyActive && activeItem?.properties.iconClassNames || group.properties.iconClassNames;
233
+ const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
234
+ const labelAction = group.properties.applyActive && activeItem ? activeItem : group;
235
+ const rootProps = icon ? {
236
+ icon,
237
+ size: iconSize,
238
+ iconOnly,
239
+ iconClassNames,
240
+ label: actionLabel(labelAction, t),
241
+ caretDown: true
242
+ } : {
243
+ children: /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(ActionLabel, {
244
+ action: labelAction
245
+ }), /* @__PURE__ */ React3.createElement(Icon2, {
246
+ size: 3,
247
+ icon: "ph--caret-down--bold",
248
+ classNames: "ms-1"
249
+ }))
250
+ };
251
+ return /* @__PURE__ */ React3.createElement(DropdownMenu.Root, {
252
+ group,
253
+ items
254
+ }, /* @__PURE__ */ React3.createElement(DropdownMenu.Trigger, {
255
+ asChild: true
256
+ }, /* @__PURE__ */ React3.createElement(Root, {
257
+ variant: "ghost",
258
+ disabled,
259
+ ...rootProps,
260
+ ...testId && {
261
+ "data-testid": testId
262
+ }
263
+ })));
300
264
  };
301
- var ToggleGroupItem = ({ group, action, __menuScope }) => {
302
- var _effect = _useSignals4();
303
- try {
304
- const { iconSize } = useMenu("ToggleGroupItem", __menuScope);
305
- const { t } = useTranslation2(translationKey);
306
- const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
307
- const handleClick = useCallback2(() => action.data?.({
308
- parent: group
309
- }), [
310
- action,
311
- group
312
- ]);
313
- const Root = icon ? NaturalToolbar.ToggleGroupIconItem : NaturalToolbar.ToggleGroupItem;
314
- const rootProps = icon ? {
315
- icon,
316
- size: iconSize,
317
- iconOnly,
318
- label: actionLabel(action, t)
319
- } : {
320
- children: /* @__PURE__ */ React4.createElement(ActionLabel, {
321
- action
322
- })
323
- };
324
- return hidden ? null : /* @__PURE__ */ React4.createElement(Root, {
325
- key: action.id,
326
- value: action.id,
327
- disabled,
328
- onClick: handleClick,
329
- variant: "ghost",
330
- ...testId && {
331
- "data-testid": testId
332
- },
333
- ...rootProps,
334
- classNames
335
- });
336
- } finally {
337
- _effect.f();
338
- }
265
+ var ToggleGroupItem = ({ __menuScope, group, action }) => {
266
+ const { iconSize, onAction } = useMenuScoped("ToggleGroupItem", __menuScope);
267
+ const { t } = useTranslation2(translationKey2);
268
+ const { icon, iconOnly = true, disabled, testId, hidden, classNames, iconClassNames } = action.properties;
269
+ const handleClick = useCallback2(() => {
270
+ if (onAction) {
271
+ onAction(action, {
272
+ parent: group
273
+ });
274
+ } else {
275
+ void executeMenuAction(action, {
276
+ parent: group
277
+ });
278
+ }
279
+ }, [
280
+ action,
281
+ group,
282
+ onAction
283
+ ]);
284
+ const commonProps = {
285
+ value: action.id,
286
+ disabled,
287
+ variant: "ghost",
288
+ classNames,
289
+ onClick: handleClick,
290
+ ...testId && {
291
+ "data-testid": testId
292
+ }
293
+ };
294
+ return hidden ? null : icon ? /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroupIconItem, {
295
+ ...commonProps,
296
+ icon,
297
+ size: iconSize,
298
+ iconOnly,
299
+ iconClassNames,
300
+ label: actionLabel(action, t)
301
+ }) : /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroupItem, commonProps, /* @__PURE__ */ React3.createElement(ActionLabel, {
302
+ action
303
+ }));
339
304
  };
340
- var ToggleGroupToolbarItem = ({ group, items: propsItems, __menuScope }) => {
341
- var _effect = _useSignals4();
342
- try {
343
- const items = useMenuItems(group, propsItems, "ToggleGroupToolbarItem", __menuScope);
344
- const { selectCardinality, value } = group.properties;
345
- return (
346
- // TODO(thure): The type here is difficult to manage, what do?
347
- // @ts-ignore
348
- /* @__PURE__ */ React4.createElement(
349
- NaturalToolbar.ToggleGroup,
350
- {
351
- type: selectCardinality,
352
- value
353
- },
354
- // TODO(thure): Handle other menu item types.
355
- items.map((action) => /* @__PURE__ */ React4.createElement(ToggleGroupItem, {
356
- key: action.id,
357
- group,
358
- action
359
- }))
360
- )
361
- );
362
- } finally {
363
- _effect.f();
305
+ var ToggleGroupToolbarItem = ({ __menuScope, group, items: itemsProp }) => {
306
+ const items = useMenuItems(group, itemsProp, "ToggleGroupToolbarItem", __menuScope);
307
+ const { selectCardinality } = group.properties;
308
+ const children = items.map((action) => /* @__PURE__ */ React3.createElement(ToggleGroupItem, {
309
+ key: action.id,
310
+ group,
311
+ action
312
+ }));
313
+ if (selectCardinality === "multiple") {
314
+ return /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroup, {
315
+ type: "multiple",
316
+ value: group.properties.value
317
+ }, children);
318
+ } else {
319
+ return /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroup, {
320
+ type: "single",
321
+ value: group.properties.value
322
+ }, children);
364
323
  }
365
324
  };
366
- var ToolbarMenu = ({ __menuScope, classNames, textBlockWidth: wrapContents, ...props }) => {
367
- var _effect = _useSignals4();
368
- try {
369
- const items = useMenuItems(void 0, void 0, "ToolbarMenu", __menuScope);
370
- const { attendableId } = useMenu("ToolbarMenu", __menuScope);
371
- const { hasAttention } = useAttention(attendableId);
372
- const InnerRoot = wrapContents ? "div" : Fragment;
373
- const innerRootProps = wrapContents ? {
374
- role: "none",
375
- className: mx2(textBlockWidth, toolbarLayout, "bs-full")
376
- } : {};
377
- return /* @__PURE__ */ React4.createElement(NaturalToolbar.Root, {
378
- ...props,
379
- layoutManaged: wrapContents,
380
- classNames: [
381
- attendableId && !hasAttention && "*:opacity-20 !bg-transparent",
382
- classNames
383
- ]
384
- }, /* @__PURE__ */ React4.createElement(InnerRoot, innerRootProps, items?.map((item, i) => isSeparator(item) ? /* @__PURE__ */ React4.createElement(NaturalToolbar.Separator, {
385
- key: i,
325
+ var ToolbarMenu = composable(({ __menuScope, children, ...props }, forwardedRef) => {
326
+ const items = useMenuItems(void 0, void 0, "ToolbarMenu", __menuScope);
327
+ const { attendableId, alwaysActive } = useMenuScoped("ToolbarMenu", __menuScope);
328
+ const { hasAttention } = useAttention(attendableId);
329
+ return /* @__PURE__ */ React3.createElement(NaturalToolbar.Root, {
330
+ ...composableProps(props, {
331
+ classNames: attendableId
332
+ }),
333
+ disabled: !alwaysActive && !hasAttention,
334
+ ref: forwardedRef
335
+ }, items?.map((item) => /* @__PURE__ */ React3.createElement(ToolbarMenuItem, {
336
+ key: item.id,
337
+ __menuScope,
338
+ item
339
+ })), children);
340
+ });
341
+ var ToolbarMenuItem = ({ __menuScope, item }) => {
342
+ if (isSeparator(item)) {
343
+ return /* @__PURE__ */ React3.createElement(NaturalToolbar.Separator, {
386
344
  variant: item.properties.variant
387
- }) : isMenuGroup(item) ? item.properties.variant === "dropdownMenu" ? (
388
- // TODO(thure): Figure out type narrowing that doesn’t require so much use of `as`.
389
- /* @__PURE__ */ React4.createElement(DropdownMenuToolbarItem, {
390
- key: item.id,
345
+ });
346
+ }
347
+ if (isMenuGroup(item)) {
348
+ if (item.properties.variant === "dropdownMenu") {
349
+ return /* @__PURE__ */ React3.createElement(DropdownMenuToolbarItem, {
350
+ __menuScope,
391
351
  group: item
392
- })
393
- ) : (
394
- // TODO(thure): Figure out type narrowing that doesn’t require so much use of `as`.
395
- /* @__PURE__ */ React4.createElement(ToggleGroupToolbarItem, {
396
- key: item.id,
352
+ });
353
+ } else {
354
+ return /* @__PURE__ */ React3.createElement(ToggleGroupToolbarItem, {
355
+ __menuScope,
397
356
  group: item
398
- })
399
- ) : (
400
- // TODO(thure): Figure out type narrowing that doesn’t require so much use of `as`.
401
- /* @__PURE__ */ React4.createElement(ActionToolbarItem, {
402
- key: item.id,
403
- action: item
404
- })
405
- ))));
406
- } finally {
407
- _effect.f();
357
+ });
358
+ }
408
359
  }
360
+ return /* @__PURE__ */ React3.createElement(ActionToolbarItem, {
361
+ __menuScope,
362
+ action: item
363
+ });
409
364
  };
410
365
 
411
- // src/hooks/useMenuActions.ts
412
- import { RegistryContext, useRxValue } from "@effect-rx/rx-react";
413
- import { useCallback as useCallback3, useContext, useEffect, useState } from "react";
414
- import { Graph, ROOT_ID } from "@dxos/app-graph";
415
- var edgesArrayToRecord = (edges) => {
416
- return Object.fromEntries(Object.entries(edges.reduce((acc, { source, target }) => {
417
- if (!acc[source]) {
418
- acc[source] = {
419
- inbound: [],
420
- outbound: []
421
- };
366
+ // src/components/Menu.tsx
367
+ var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-menu/src/components/Menu.tsx";
368
+ var MENU_NAME = "Menu";
369
+ var [createMenuContext, createMenuScope] = createContextScope(MENU_NAME, []);
370
+ var nullItemsAtom = Atom.make(null);
371
+ var defaultItemsAccessor = () => nullItemsAtom;
372
+ var menuContextDefaults = {
373
+ iconSize: 5,
374
+ items: defaultItemsAccessor,
375
+ onAction: void 0,
376
+ menuItemsAtom: Atom.make(/* @__PURE__ */ new Map()),
377
+ addMenuItems: () => {
378
+ },
379
+ removeMenuItems: () => {
380
+ }
381
+ };
382
+ var [MenuContextProvider, useMenuScoped] = createMenuContext(MENU_NAME, menuContextDefaults);
383
+ var useMenuScope = createMenuScope();
384
+ var MenuDropdownContext = /* @__PURE__ */ createContext({
385
+ closeMenu: () => {
386
+ }
387
+ });
388
+ var DEFAULT_PRIORITY = 100;
389
+ var sortMenuItems = (items) => [
390
+ ...items
391
+ ].sort((a, b) => {
392
+ if (a.priority !== b.priority) {
393
+ return a.priority - b.priority;
394
+ }
395
+ return a.id.localeCompare(b.id);
396
+ });
397
+ var MenuProvider = ({ children, items = menuContextDefaults.items, iconSize = menuContextDefaults.iconSize, attendableId, alwaysActive, onAction }) => {
398
+ const { scope } = useMenuScope(void 0);
399
+ const registry = useContext(RegistryContext);
400
+ const menuItemsAtom = useMemo(() => Atom.make(/* @__PURE__ */ new Map()).pipe(Atom.keepAlive), []);
401
+ const addMenuItems = useCallback3((props) => {
402
+ const priority = props.priority ?? DEFAULT_PRIORITY;
403
+ const prev = registry.get(menuItemsAtom);
404
+ const next = new Map(prev);
405
+ next.set(props.id, {
406
+ ...props,
407
+ priority
408
+ });
409
+ registry.set(menuItemsAtom, next);
410
+ }, [
411
+ registry,
412
+ menuItemsAtom
413
+ ]);
414
+ const removeMenuItems = useCallback3((id) => {
415
+ const prev = registry.get(menuItemsAtom);
416
+ const next = new Map(prev);
417
+ next.delete(id);
418
+ registry.set(menuItemsAtom, next);
419
+ }, [
420
+ registry,
421
+ menuItemsAtom
422
+ ]);
423
+ return /* @__PURE__ */ React4.createElement(MenuContextProvider, {
424
+ items,
425
+ iconSize,
426
+ attendableId,
427
+ alwaysActive,
428
+ menuItemsAtom,
429
+ addMenuItems,
430
+ removeMenuItems,
431
+ onAction,
432
+ scope
433
+ }, children);
434
+ };
435
+ var resolveItems = (baseItems, group, entries) => {
436
+ const applicable = [
437
+ ...entries.values()
438
+ ].filter((entry) => !entry.groupFilter || entry.groupFilter(group));
439
+ if (applicable.length === 0) {
440
+ return baseItems;
441
+ }
442
+ const sorted = sortMenuItems(applicable);
443
+ const replacements = sorted.filter((entry) => entry.mode === "replacement");
444
+ if (replacements.length > 0) {
445
+ if (replacements.length > 1) {
446
+ log.warn("multiple replacement entries found", {
447
+ ids: replacements.map((r) => r.id).join(", "),
448
+ using: replacements[0].id
449
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 94, S: void 0 });
450
+ }
451
+ return replacements[0].items;
452
+ }
453
+ const additive = sorted.filter((entry) => entry.mode === "additive");
454
+ const additiveItems = additive.flatMap((entry) => entry.items);
455
+ if (!baseItems || baseItems.length === 0) {
456
+ return additiveItems.length > 0 ? additiveItems : null;
457
+ }
458
+ return [
459
+ ...baseItems,
460
+ ...additiveItems
461
+ ];
462
+ };
463
+ var useMenuItems = (group, propsItems, consumerName = "useMenuItemConsumer", __menuScope) => {
464
+ const { items, menuItemsAtom } = useMenuScoped(consumerName, __menuScope);
465
+ const groupItems = useAtomValue(items(group));
466
+ const entries = useAtomValue(menuItemsAtom) ?? /* @__PURE__ */ new Map();
467
+ const baseItems = useMemo(() => propsItems ?? groupItems ?? null, [
468
+ propsItems,
469
+ groupItems
470
+ ]);
471
+ const resolved = useMemo(() => resolveItems(baseItems, group, entries), [
472
+ baseItems,
473
+ group,
474
+ entries
475
+ ]);
476
+ return resolved ?? void 0;
477
+ };
478
+ var useMenu = (consumerName) => {
479
+ return useMenuScoped(consumerName, void 0);
480
+ };
481
+ var MenuRoot = ({ children, open, defaultOpen, onOpenChange, caller, ...props }) => {
482
+ const [menuOpen, setMenuOpen] = useControllableState2({
483
+ prop: open,
484
+ defaultProp: defaultOpen,
485
+ onChange: onOpenChange
486
+ });
487
+ const closeMenu = useCallback3(() => setMenuOpen(false), [
488
+ setMenuOpen
489
+ ]);
490
+ return /* @__PURE__ */ React4.createElement(MenuProvider, props, /* @__PURE__ */ React4.createElement(MenuDropdownContext.Provider, {
491
+ value: {
492
+ closeMenu,
493
+ caller
422
494
  }
423
- if (!acc[target]) {
424
- acc[target] = {
425
- inbound: [],
426
- outbound: []
427
- };
495
+ }, /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Root, {
496
+ open: menuOpen,
497
+ onOpenChange: setMenuOpen
498
+ }, children)));
499
+ };
500
+ var MenuContentItem = ({ item, onClick, __menuScope }) => {
501
+ const action = item;
502
+ const handleClick = useCallback3((event) => onClick(action, event), [
503
+ action,
504
+ onClick
505
+ ]);
506
+ const { iconSize } = useMenuScoped("MenuContentItem", __menuScope);
507
+ return /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Item, {
508
+ onClick: handleClick,
509
+ classNames: "gap-2",
510
+ disabled: action.properties?.disabled,
511
+ ...action.properties?.testId && {
512
+ "data-testid": action.properties.testId
428
513
  }
429
- const sourceEdges = acc[source];
430
- if (!sourceEdges.outbound.includes(target)) {
431
- sourceEdges.outbound.push(target);
514
+ }, action.properties?.icon && /* @__PURE__ */ React4.createElement(Icon3, {
515
+ icon: action.properties.icon,
516
+ size: iconSize
517
+ }), /* @__PURE__ */ React4.createElement(ActionLabel, {
518
+ action
519
+ }));
520
+ };
521
+ var MenuContent = ({ group, items: propsItems, caller: callerOverride, __menuScope }) => {
522
+ const { closeMenu, caller: contextCaller } = useContext(MenuDropdownContext);
523
+ const caller = callerOverride ?? contextCaller;
524
+ const { onAction } = useMenuScoped("MenuContent", __menuScope);
525
+ const resolvedItems = useMenuItems(group, propsItems, "MenuContent", __menuScope);
526
+ const handleActionClick = useCallback3((action, event) => {
527
+ if (action.properties?.disabled) {
528
+ return;
432
529
  }
433
- const targetEdges = acc[target];
434
- if (!targetEdges.inbound.includes(source)) {
435
- targetEdges.inbound.push(source);
530
+ event.stopPropagation();
531
+ closeMenu();
532
+ if (onAction) {
533
+ onAction(action, {
534
+ parent: group,
535
+ caller
536
+ });
537
+ } else {
538
+ void executeMenuAction(action, {
539
+ parent: group,
540
+ caller
541
+ });
436
542
  }
437
- return acc;
438
- }, {})));
543
+ }, [
544
+ group,
545
+ caller,
546
+ onAction,
547
+ closeMenu
548
+ ]);
549
+ return /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Portal, null, /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Content, null, /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Viewport, null, resolvedItems?.map((item) => /* @__PURE__ */ React4.createElement(MenuContentItem, {
550
+ key: item.id,
551
+ item,
552
+ onClick: handleActionClick
553
+ }))), /* @__PURE__ */ React4.createElement(NaturalDropdownMenu2.Arrow, null)));
439
554
  };
555
+ var Menu = {
556
+ Root: MenuRoot,
557
+ Trigger: NaturalDropdownMenu2.Trigger,
558
+ Content: MenuContent,
559
+ VirtualTrigger: NaturalDropdownMenu2.VirtualTrigger,
560
+ Toolbar: ToolbarMenu
561
+ };
562
+
563
+ // src/hooks/useMenuActions.ts
564
+ import { Atom as Atom2, RegistryContext as RegistryContext2, useAtomValue as useAtomValue2 } from "@effect-atom/atom-react";
565
+ import { useCallback as useCallback4, useContext as useContext2, useMemo as useMemo2 } from "react";
566
+ import { Graph, Node as Node3 } from "@dxos/app-graph";
440
567
  var useMenuActions = (props) => {
441
- const registry = useContext(RegistryContext);
442
- const menuGraphProps = useRxValue(props);
443
- const [graph] = useState(new Graph({
444
- registry,
445
- nodes: menuGraphProps.nodes,
446
- edges: edgesArrayToRecord(menuGraphProps.edges)
447
- }));
448
- useEffect(() => {
449
- graph.addNodes(menuGraphProps.nodes);
450
- graph.addEdges(menuGraphProps.edges);
568
+ const registry = useContext2(RegistryContext2);
569
+ const menuGraphProps = useAtomValue2(props);
570
+ const graph = useMemo2(() => {
571
+ const newGraph = Graph.make({
572
+ registry
573
+ });
574
+ newGraph.pipe(Graph.addNodes(menuGraphProps.nodes), Graph.addEdges(menuGraphProps.edges));
575
+ return newGraph;
451
576
  }, [
577
+ registry,
452
578
  menuGraphProps
453
579
  ]);
454
- const useGroupItems = useCallback3((sourceNode) => {
455
- const items = useRxValue(graph.connections(sourceNode?.id || ROOT_ID));
456
- return items;
580
+ const items = useCallback4((group) => {
581
+ return graph.connections(group?.id || Node3.RootId, "child");
457
582
  }, [
458
583
  graph
459
584
  ]);
460
585
  return {
461
- useGroupItems
586
+ items
462
587
  };
463
588
  };
589
+ var useMenuBuilder = (build, deps) => {
590
+ const atom = useMemo2(() => Atom2.make(build), deps);
591
+ return useMenuActions(atom);
592
+ };
464
593
 
465
594
  // src/builder.ts
466
595
  import { invariant } from "@dxos/invariant";
467
- var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-menu/src/builder.ts";
468
- var MenuBuilder = Object.freeze({
469
- make: () => new MenuBuilderImpl({
470
- nodes: [],
471
- edges: []
472
- }, "root")
473
- });
596
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-menu/src/builder.ts";
474
597
  var MenuBuilderImpl = class _MenuBuilderImpl {
475
598
  _data;
476
599
  _rootId;
600
+ _separatorCount = 0;
477
601
  constructor(_data, _rootId) {
478
602
  this._data = _data;
479
603
  this._rootId = _rootId;
480
604
  }
481
- action(id, invoke, properties) {
482
- this._data.nodes.push(createMenuAction(id, invoke, properties));
605
+ root(props) {
606
+ invariant(this._rootId === "root", "Root group can only be at the top level", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 16, S: this, A: ["this._rootId === 'root'", "'Root group can only be at the top level'"] });
607
+ invariant(this._data.nodes.find((node) => node.id === "root") === void 0, "Root group can only be created once", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 17, S: this, A: ["this._data.nodes.find((node)=>node.id === 'root') === undefined", "'Root group can only be created once'"] });
608
+ this._data.nodes.push(createMenuItemGroup("root", props));
609
+ return this;
610
+ }
611
+ build() {
612
+ return this._data;
613
+ }
614
+ action(id, props, invoke) {
615
+ this._data.nodes.push(createMenuAction(id, invoke, props));
483
616
  this._data.edges.push({
484
617
  source: this._rootId,
485
- target: id
618
+ target: id,
619
+ relation: "child"
486
620
  });
487
621
  return this;
488
622
  }
489
- root(props) {
490
- invariant(this._rootId === "root", "Root group can only be at the top level", {
491
- F: __dxlog_file,
492
- L: 42,
493
- S: this,
494
- A: [
495
- "this._rootId === 'root'",
496
- "'Root group can only be at the top level'"
497
- ]
498
- });
499
- invariant(this._data.nodes.find((node) => node.id === "root") === void 0, "Root group can only be created once", {
500
- F: __dxlog_file,
501
- L: 43,
502
- S: this,
503
- A: [
504
- "this._data.nodes.find((node) => node.id === 'root') === undefined",
505
- "'Root group can only be created once'"
506
- ]
623
+ group(id, props, cb) {
624
+ this._data.nodes.push(createMenuItemGroup(id, props));
625
+ this._data.edges.push({
626
+ source: this._rootId,
627
+ target: id,
628
+ relation: "child"
507
629
  });
508
- this._data.nodes.push(createMenuItemGroup("root", props));
630
+ cb(new _MenuBuilderImpl(this._data, id));
631
+ return this;
632
+ }
633
+ subgraph(subgraphOrCb) {
634
+ if (!subgraphOrCb) {
635
+ return this;
636
+ }
637
+ if (typeof subgraphOrCb === "function") {
638
+ subgraphOrCb(new _MenuBuilderImpl(this._data, this._rootId));
639
+ } else {
640
+ this._data.nodes.push(...subgraphOrCb.nodes);
641
+ this._data.edges.push(...subgraphOrCb.edges);
642
+ }
509
643
  return this;
510
644
  }
511
- group(id, properties, cb) {
512
- this._data.nodes.push(createMenuItemGroup(id, properties));
645
+ separator(variant = "gap") {
646
+ const id = `separator-${++this._separatorCount}`;
647
+ this._data.nodes.push({
648
+ id,
649
+ type: MenuSeparatorType,
650
+ properties: {
651
+ variant
652
+ },
653
+ data: void 0
654
+ });
513
655
  this._data.edges.push({
514
656
  source: this._rootId,
515
- target: id
657
+ target: id,
658
+ relation: "child"
516
659
  });
517
- cb(new _MenuBuilderImpl(this._data, id));
518
660
  return this;
519
661
  }
520
- build() {
521
- return this._data;
522
- }
523
662
  };
663
+ var MenuBuilder = Object.freeze({
664
+ make: () => new MenuBuilderImpl({
665
+ nodes: [],
666
+ edges: []
667
+ }, "root")
668
+ });
524
669
  export {
525
- DropdownMenu,
670
+ Menu,
526
671
  MenuBuilder,
527
- MenuProvider,
528
672
  MenuSeparatorType,
529
- ToolbarMenu,
530
673
  createGapSeparator,
531
674
  createLineSeparator,
532
675
  createMenuAction,
533
676
  createMenuItemGroup,
534
- createMenuScope,
535
677
  isMenuGroup,
536
678
  isSeparator,
537
679
  menuContextDefaults,
538
- rxFromSignal,
539
680
  useMenu,
540
681
  useMenuActions,
541
- useMenuItems
682
+ useMenuBuilder,
683
+ useMenuItems,
684
+ useMenuScoped
542
685
  };
543
686
  //# sourceMappingURL=index.mjs.map