@dxos/react-ui-menu 0.8.4-main.e8ec1fe → 0.8.4-main.ef1bc66f44

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 (43) hide show
  1. package/dist/lib/browser/index.mjs +289 -333
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +289 -333
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/builder.d.ts +1 -1
  8. package/dist/types/src/builder.d.ts.map +1 -1
  9. package/dist/types/src/components/ActionLabel.d.ts +3 -3
  10. package/dist/types/src/components/ActionLabel.d.ts.map +1 -1
  11. package/dist/types/src/components/DropdownMenu.d.ts.map +1 -1
  12. package/dist/types/src/components/MenuContext.d.ts +1 -1
  13. package/dist/types/src/components/MenuContext.d.ts.map +1 -1
  14. package/dist/types/src/components/ToolbarMenu.d.ts +3 -2
  15. package/dist/types/src/components/ToolbarMenu.d.ts.map +1 -1
  16. package/dist/types/src/components/ToolbarMenu.stories.d.ts +2 -2
  17. package/dist/types/src/components/ToolbarMenu.stories.d.ts.map +1 -1
  18. package/dist/types/src/hooks/useMenuActions.d.ts +3 -3
  19. package/dist/types/src/hooks/useMenuActions.d.ts.map +1 -1
  20. package/dist/types/src/index.d.ts +0 -1
  21. package/dist/types/src/index.d.ts.map +1 -1
  22. package/dist/types/src/testing/generator.d.ts +12 -10
  23. package/dist/types/src/testing/generator.d.ts.map +1 -1
  24. package/dist/types/src/translations.d.ts +2 -2
  25. package/dist/types/src/translations.d.ts.map +1 -1
  26. package/dist/types/src/types.d.ts +13 -24
  27. package/dist/types/src/types.d.ts.map +1 -1
  28. package/dist/types/src/util.d.ts +12 -5
  29. package/dist/types/src/util.d.ts.map +1 -1
  30. package/dist/types/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +31 -28
  32. package/src/builder.ts +1 -1
  33. package/src/components/ActionLabel.tsx +4 -4
  34. package/src/components/DropdownMenu.tsx +10 -3
  35. package/src/components/MenuContext.tsx +11 -1
  36. package/src/components/ToolbarMenu.stories.tsx +16 -8
  37. package/src/components/ToolbarMenu.tsx +33 -13
  38. package/src/hooks/useMenuActions.ts +14 -43
  39. package/src/index.ts +0 -2
  40. package/src/testing/generator.ts +63 -58
  41. package/src/translations.ts +1 -1
  42. package/src/types.ts +14 -26
  43. package/src/util.ts +26 -13
@@ -1,48 +1,36 @@
1
1
  import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
2
 
3
- // src/index.ts
4
- import { atomFromSignal } from "@dxos/app-graph";
5
-
6
3
  // src/components/DropdownMenu.tsx
7
- import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
8
4
  import { useControllableState } from "@radix-ui/react-use-controllable-state";
9
5
  import React3, { useCallback } from "react";
10
6
  import { Icon, DropdownMenu as NaturalDropdownMenu } from "@dxos/react-ui";
11
7
 
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
- ];
28
-
29
8
  // src/util.ts
30
- import { ACTION_GROUP_TYPE, ACTION_TYPE, actionGroupSymbol } from "@dxos/app-graph";
9
+ import * as Effect from "effect/Effect";
10
+ import { Node } from "@dxos/app-graph";
11
+ import { runAndForwardErrors } from "@dxos/effect";
31
12
  import { getHostPlatform } from "@dxos/util";
13
+ var executeMenuAction = async (action, params = {}) => {
14
+ let effect = action.data(params);
15
+ if (action._actionContext) {
16
+ effect = effect.pipe(Effect.provide(action._actionContext));
17
+ }
18
+ await runAndForwardErrors(effect);
19
+ };
32
20
  var getShortcut = (action) => {
33
21
  return typeof action.properties?.keyBinding === "string" ? action.properties.keyBinding : action.properties?.keyBinding?.[getHostPlatform()];
34
22
  };
35
23
  var createMenuAction = (id, invoke, properties) => ({
36
24
  id,
37
- type: ACTION_TYPE,
25
+ type: Node.ActionType,
38
26
  properties,
39
- data: invoke
27
+ data: () => Effect.sync(invoke)
40
28
  });
41
29
  var createMenuItemGroup = (id, properties) => ({
42
30
  id,
43
- type: ACTION_GROUP_TYPE,
31
+ type: Node.ActionGroupType,
44
32
  properties,
45
- data: actionGroupSymbol
33
+ data: Node.actionGroupSymbol
46
34
  });
47
35
  var createGapSeparator = (id = "gap", source = "root") => ({
48
36
  nodes: [
@@ -82,19 +70,30 @@ var createLineSeparator = (id = "line", source = "root") => ({
82
70
  });
83
71
 
84
72
  // src/components/ActionLabel.tsx
85
- 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();
73
+ import React from "react";
74
+ import { keySymbols } from "@dxos/keyboard";
75
+ import { toLocalizedString, useTranslation } from "@dxos/react-ui";
76
+ import { descriptionText, mx } from "@dxos/ui-theme";
77
+
78
+ // src/translations.ts
79
+ var translationKey = "@dxos/react-ui-menu";
80
+ var translations = [
81
+ {
82
+ "en-US": {
83
+ [translationKey]: {}
84
+ }
97
85
  }
86
+ ];
87
+
88
+ // src/components/ActionLabel.tsx
89
+ var ActionLabel = ({ action }) => {
90
+ const { t } = useTranslation(translationKey);
91
+ const shortcut = getShortcut(action);
92
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", {
93
+ className: "grow truncate"
94
+ }, toLocalizedString(action.properties.label, t)), shortcut && /* @__PURE__ */ React.createElement("span", {
95
+ className: mx("shrink-0", descriptionText)
96
+ }, keySymbols(shortcut).join("")));
98
97
  };
99
98
  var actionLabel = (action, t) => {
100
99
  const shortcut = getShortcut(action);
@@ -102,7 +101,6 @@ var actionLabel = (action, t) => {
102
101
  };
103
102
 
104
103
  // src/components/MenuContext.tsx
105
- import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
106
104
  import { createContextScope } from "@radix-ui/react-context";
107
105
  import React2, { useMemo } from "react";
108
106
  var MENU_NAME = "Menu";
@@ -110,22 +108,20 @@ var [createMenuContext, createMenuScope] = createContextScope(MENU_NAME, []);
110
108
  var [MenuContextProvider, useMenu] = createMenuContext(MENU_NAME);
111
109
  var menuContextDefaults = {
112
110
  iconSize: 5,
113
- useGroupItems: () => null
111
+ useGroupItems: () => null,
112
+ onAction: void 0
114
113
  };
115
114
  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
- }
115
+ var MenuProvider = ({ children, useGroupItems = menuContextDefaults.useGroupItems, iconSize = menuContextDefaults.iconSize, attendableId, alwaysActive, onAction }) => {
116
+ const { scope } = useMenuScope(void 0);
117
+ return /* @__PURE__ */ React2.createElement(MenuContextProvider, {
118
+ useGroupItems,
119
+ iconSize,
120
+ attendableId,
121
+ alwaysActive,
122
+ onAction,
123
+ scope
124
+ }, children);
129
125
  };
130
126
  var useMenuItems = (group, propsItems, consumerName = "useMenuItemConsumer", __menuScope) => {
131
127
  const { useGroupItems } = useMenu(consumerName, __menuScope);
@@ -138,66 +134,65 @@ var useMenuItems = (group, propsItems, consumerName = "useMenuItemConsumer", __m
138
134
 
139
135
  // src/components/DropdownMenu.tsx
140
136
  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
- }
137
+ const action = item;
138
+ const handleClick = useCallback((event) => onClick(action, event), [
139
+ action,
140
+ onClick
141
+ ]);
142
+ const { iconSize } = useMenu("DropdownMenuItem", __menuScope);
143
+ return /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Item, {
144
+ onClick: handleClick,
145
+ classNames: "gap-2",
146
+ disabled: action.properties?.disabled,
147
+ ...action.properties?.testId && {
148
+ "data-testid": action.properties.testId
149
+ }
150
+ }, action.properties?.icon && /* @__PURE__ */ React3.createElement(Icon, {
151
+ icon: action.properties.icon,
152
+ size: iconSize
153
+ }), /* @__PURE__ */ React3.createElement(ActionLabel, {
154
+ action
155
+ }));
165
156
  };
166
157
  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?.({
158
+ const [optionsMenuOpen, setOptionsMenuOpen] = useControllableState({
159
+ prop: open,
160
+ defaultProp: defaultOpen,
161
+ onChange: onOpenChange
162
+ });
163
+ const { onAction } = useMenu("DropdownMenuRoot", __menuScope);
164
+ const handleActionClick = useCallback((action, event) => {
165
+ if (action.properties?.disabled) {
166
+ return;
167
+ }
168
+ event.stopPropagation();
169
+ setOptionsMenuOpen(false);
170
+ if (onAction) {
171
+ onAction(action, {
181
172
  parent: group,
182
173
  caller
183
174
  });
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
- }
175
+ } else {
176
+ void executeMenuAction(action, {
177
+ parent: group,
178
+ caller
179
+ });
180
+ }
181
+ }, [
182
+ group,
183
+ caller,
184
+ onAction
185
+ ]);
186
+ const items = useMenuItems(group, propsItems);
187
+ return /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Root, {
188
+ open: optionsMenuOpen,
189
+ onOpenChange: setOptionsMenuOpen,
190
+ ...naturalProps
191
+ }, 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, {
192
+ key: item.id,
193
+ item,
194
+ onClick: handleActionClick
195
+ }))), /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Arrow, null))));
201
196
  };
202
197
  var DropdownMenu = {
203
198
  Root: DropdownMenuRoot,
@@ -206,261 +201,223 @@ var DropdownMenu = {
206
201
  };
207
202
 
208
203
  // src/components/ToolbarMenu.tsx
209
- import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
210
204
  import React4, { Fragment, useCallback as useCallback2 } from "react";
211
205
  import { Icon as Icon2, Toolbar as NaturalToolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
212
206
  import { useAttention } from "@dxos/react-ui-attention";
213
- import { mx as mx2, textBlockWidth, toolbarInactive, toolbarLayout } from "@dxos/react-ui-theme";
207
+ import { mx as mx2, textBlockWidth, toolbarLayout } from "@dxos/ui-theme";
214
208
 
215
209
  // src/types.ts
216
- import { ACTION_GROUP_TYPE as ACTION_GROUP_TYPE2 } from "@dxos/app-graph";
210
+ import { Node as Node2 } from "@dxos/app-graph";
217
211
  var MenuSeparatorType = "@dxos/react-ui-toolbar/separator";
218
212
  var isSeparator = (node) => node.type === MenuSeparatorType;
219
- var isMenuGroup = (node) => node.type === ACTION_GROUP_TYPE2;
213
+ var isMenuGroup = (node) => node.type === Node2.ActionGroupType;
220
214
 
221
215
  // src/components/ToolbarMenu.tsx
222
216
  var ActionToolbarItem = ({ action, __menuScope }) => {
223
- var _effect = _useSignals4();
224
- try {
225
- const { iconSize } = useMenu("ActionToolbarItem", __menuScope);
226
- const { t } = useTranslation2(translationKey);
227
- const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
228
- const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
229
- const rootProps = icon ? {
230
- icon,
231
- size: iconSize,
232
- iconOnly,
233
- label: actionLabel(action, t)
234
- } : {
235
- children: /* @__PURE__ */ React4.createElement(ActionLabel, {
236
- action
237
- })
238
- };
239
- const handleClick = useCallback2(() => action.data?.(), [
217
+ const { iconSize, onAction } = useMenu("ActionToolbarItem", __menuScope);
218
+ const { t } = useTranslation2(translationKey);
219
+ const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
220
+ const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
221
+ const rootProps = icon ? {
222
+ icon,
223
+ size: iconSize,
224
+ iconOnly,
225
+ label: actionLabel(action, t)
226
+ } : {
227
+ children: /* @__PURE__ */ React4.createElement(ActionLabel, {
240
228
  action
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();
255
- }
229
+ })
230
+ };
231
+ const handleClick = useCallback2(() => {
232
+ if (onAction) {
233
+ onAction(action, {});
234
+ } else {
235
+ void executeMenuAction(action);
236
+ }
237
+ }, [
238
+ action,
239
+ onAction
240
+ ]);
241
+ return hidden ? null : /* @__PURE__ */ React4.createElement(Root, {
242
+ key: action.id,
243
+ disabled,
244
+ onClick: handleClick,
245
+ variant: "ghost",
246
+ ...testId && {
247
+ "data-testid": testId
248
+ },
249
+ ...rootProps,
250
+ classNames
251
+ });
256
252
  };
257
253
  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
- }
254
+ const { iconOnly, disabled, testId } = group.properties;
255
+ const { t } = useTranslation2(translationKey);
256
+ const { iconSize } = useMenu("DropdownMenuToolbarItem", __menuScope);
257
+ const items = useMenuItems(group, propsItems, "DropdownMenuToolbarItem", __menuScope);
258
+ const activeItem = items?.find((item) => !!item.properties.checked);
259
+ const icon = group.properties.applyActive && // TODO(thure): Handle other menu item types.
260
+ activeItem?.properties.icon || group.properties.icon;
261
+ const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
262
+ const labelAction = group.properties.applyActive && activeItem ? activeItem : group;
263
+ const rootProps = icon ? {
264
+ icon,
265
+ size: iconSize,
266
+ iconOnly,
267
+ label: actionLabel(labelAction, t),
268
+ caretDown: true
269
+ } : {
270
+ children: /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(ActionLabel, {
271
+ action: labelAction
272
+ }), /* @__PURE__ */ React4.createElement(Icon2, {
273
+ size: 3,
274
+ icon: "ph--caret-down--bold",
275
+ classNames: "mis-1"
276
+ }))
277
+ };
278
+ return /* @__PURE__ */ React4.createElement(DropdownMenu.Root, {
279
+ group,
280
+ items
281
+ }, /* @__PURE__ */ React4.createElement(DropdownMenu.Trigger, {
282
+ asChild: true
283
+ }, /* @__PURE__ */ React4.createElement(Root, {
284
+ variant: "ghost",
285
+ disabled,
286
+ ...rootProps,
287
+ ...testId && {
288
+ "data-testid": testId
289
+ }
290
+ })));
300
291
  };
301
292
  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
- }
293
+ const { iconSize, onAction } = useMenu("ToggleGroupItem", __menuScope);
294
+ const { t } = useTranslation2(translationKey);
295
+ const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
296
+ const handleClick = useCallback2(() => {
297
+ if (onAction) {
298
+ onAction(action, {
299
+ parent: group
300
+ });
301
+ } else {
302
+ void executeMenuAction(action, {
303
+ parent: group
304
+ });
305
+ }
306
+ }, [
307
+ action,
308
+ group,
309
+ onAction
310
+ ]);
311
+ const Root = icon ? NaturalToolbar.ToggleGroupIconItem : NaturalToolbar.ToggleGroupItem;
312
+ const rootProps = icon ? {
313
+ icon,
314
+ size: iconSize,
315
+ iconOnly,
316
+ label: actionLabel(action, t)
317
+ } : {
318
+ children: /* @__PURE__ */ React4.createElement(ActionLabel, {
319
+ action
320
+ })
321
+ };
322
+ return hidden ? null : /* @__PURE__ */ React4.createElement(Root, {
323
+ key: action.id,
324
+ value: action.id,
325
+ disabled,
326
+ onClick: handleClick,
327
+ variant: "ghost",
328
+ ...testId && {
329
+ "data-testid": testId
330
+ },
331
+ ...rootProps,
332
+ classNames
333
+ });
339
334
  };
340
335
  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): Fix.
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();
364
- }
336
+ const items = useMenuItems(group, propsItems, "ToggleGroupToolbarItem", __menuScope);
337
+ const { selectCardinality, value } = group.properties;
338
+ return (
339
+ // TODO(thure): Fix.
340
+ // @ts-ignore
341
+ /* @__PURE__ */ React4.createElement(
342
+ NaturalToolbar.ToggleGroup,
343
+ {
344
+ type: selectCardinality,
345
+ value
346
+ },
347
+ // TODO(thure): Handle other menu item types.
348
+ items.map((action) => /* @__PURE__ */ React4.createElement(ToggleGroupItem, {
349
+ key: action.id,
350
+ group,
351
+ action
352
+ }))
353
+ )
354
+ );
365
355
  };
366
- var ToolbarMenu = ({ __menuScope, classNames, textBlockWidth: textBlockWidthParam, ...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 = textBlockWidthParam ? "div" : Fragment;
373
- const innerRootProps = textBlockWidthParam ? {
374
- role: "none",
375
- className: mx2(textBlockWidth, toolbarLayout, "bs-full")
376
- } : {};
377
- return /* @__PURE__ */ React4.createElement(NaturalToolbar.Root, {
378
- ...props,
379
- classNames: [
380
- attendableId && !hasAttention && toolbarInactive,
381
- classNames
382
- ],
383
- layoutManaged: textBlockWidthParam
384
- }, /* @__PURE__ */ React4.createElement(InnerRoot, innerRootProps, items?.map((item, i) => /* @__PURE__ */ React4.createElement(ToolbarMenuItem, {
385
- key: item.id ?? i,
386
- item
387
- }))));
388
- } finally {
389
- _effect.f();
390
- }
356
+ var ToolbarMenu = ({ __menuScope, classNames, textBlockWidth: textBlockWidthProp, ...props }) => {
357
+ const items = useMenuItems(void 0, void 0, "ToolbarMenu", __menuScope);
358
+ const { attendableId, alwaysActive } = useMenu("ToolbarMenu", __menuScope);
359
+ const { hasAttention } = useAttention(attendableId);
360
+ const InnerRoot = textBlockWidthProp ? "div" : Fragment;
361
+ const innerRootProps = textBlockWidthProp ? {
362
+ role: "none",
363
+ className: mx2(textBlockWidth, toolbarLayout, "bs-full")
364
+ } : {};
365
+ return /* @__PURE__ */ React4.createElement(NaturalToolbar.Root, {
366
+ ...props,
367
+ classNames: [
368
+ attendableId,
369
+ classNames
370
+ ],
371
+ disabled: !alwaysActive && !hasAttention,
372
+ layoutManaged: textBlockWidthProp
373
+ }, /* @__PURE__ */ React4.createElement(InnerRoot, innerRootProps, items?.map((item, i) => /* @__PURE__ */ React4.createElement(ToolbarMenuItem, {
374
+ key: item.id ?? i,
375
+ item
376
+ }))));
391
377
  };
392
378
  var ToolbarMenuItem = ({ item }) => {
393
- var _effect = _useSignals4();
394
- try {
395
- if (isSeparator(item)) {
396
- return /* @__PURE__ */ React4.createElement(NaturalToolbar.Separator, {
397
- variant: item.properties.variant
379
+ if (isSeparator(item)) {
380
+ return /* @__PURE__ */ React4.createElement(NaturalToolbar.Separator, {
381
+ variant: item.properties.variant
382
+ });
383
+ }
384
+ if (isMenuGroup(item)) {
385
+ if (item.properties.variant === "dropdownMenu") {
386
+ return /* @__PURE__ */ React4.createElement(DropdownMenuToolbarItem, {
387
+ group: item
388
+ });
389
+ } else {
390
+ return /* @__PURE__ */ React4.createElement(ToggleGroupToolbarItem, {
391
+ group: item
398
392
  });
399
393
  }
400
- if (isMenuGroup(item)) {
401
- if (item.properties.variant === "dropdownMenu") {
402
- return /* @__PURE__ */ React4.createElement(DropdownMenuToolbarItem, {
403
- group: item
404
- });
405
- } else {
406
- return /* @__PURE__ */ React4.createElement(ToggleGroupToolbarItem, {
407
- group: item
408
- });
409
- }
410
- }
411
- return /* @__PURE__ */ React4.createElement(ActionToolbarItem, {
412
- action: item
413
- });
414
- } finally {
415
- _effect.f();
416
394
  }
395
+ return /* @__PURE__ */ React4.createElement(ActionToolbarItem, {
396
+ action: item
397
+ });
417
398
  };
418
399
 
419
400
  // src/hooks/useMenuActions.ts
420
401
  import { RegistryContext, useAtomValue } from "@effect-atom/atom-react";
421
- import { useCallback as useCallback3, useContext, useEffect, useState } from "react";
422
- import { Graph, ROOT_ID } from "@dxos/app-graph";
423
- var edgesArrayToRecord = (edges) => {
424
- return Object.fromEntries(Object.entries(edges.reduce((acc, { source, target }) => {
425
- if (!acc[source]) {
426
- acc[source] = {
427
- inbound: [],
428
- outbound: []
429
- };
430
- }
431
- if (!acc[target]) {
432
- acc[target] = {
433
- inbound: [],
434
- outbound: []
435
- };
436
- }
437
- const sourceEdges = acc[source];
438
- if (!sourceEdges.outbound.includes(target)) {
439
- sourceEdges.outbound.push(target);
440
- }
441
- const targetEdges = acc[target];
442
- if (!targetEdges.inbound.includes(source)) {
443
- targetEdges.inbound.push(source);
444
- }
445
- return acc;
446
- }, {})));
447
- };
402
+ import { useCallback as useCallback3, useContext, useRef } from "react";
403
+ import { Graph, Node as Node3 } from "@dxos/app-graph";
448
404
  var useMenuActions = (props) => {
449
405
  const registry = useContext(RegistryContext);
450
406
  const menuGraphProps = useAtomValue(props);
451
- const [graph] = useState(new Graph({
452
- registry,
453
- nodes: menuGraphProps.nodes,
454
- edges: edgesArrayToRecord(menuGraphProps.edges)
455
- }));
456
- useEffect(() => {
457
- graph.addNodes(menuGraphProps.nodes);
458
- graph.addEdges(menuGraphProps.edges);
459
- }, [
460
- menuGraphProps
461
- ]);
407
+ const graphRef = useRef(null);
408
+ if (!graphRef.current || graphRef.current.props !== menuGraphProps) {
409
+ const newGraph = Graph.make({
410
+ registry
411
+ });
412
+ newGraph.pipe(Graph.addNodes(menuGraphProps.nodes), Graph.addEdges(menuGraphProps.edges));
413
+ graphRef.current = {
414
+ graph: newGraph,
415
+ props: menuGraphProps
416
+ };
417
+ }
418
+ const graph = graphRef.current.graph;
462
419
  const useGroupItems = useCallback3((sourceNode) => {
463
- const items = useAtomValue(graph.connections(sourceNode?.id || ROOT_ID));
420
+ const items = useAtomValue(graph.connections(sourceNode?.id || Node3.RootId));
464
421
  return items;
465
422
  }, [
466
423
  graph
@@ -535,7 +492,6 @@ export {
535
492
  MenuProvider,
536
493
  MenuSeparatorType,
537
494
  ToolbarMenu,
538
- atomFromSignal,
539
495
  createGapSeparator,
540
496
  createLineSeparator,
541
497
  createMenuAction,