@dxos/react-ui-menu 0.8.4-main.ae835ea → 0.8.4-main.bc674ce

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 +292 -306
  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 +292 -306
  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 +5 -5
  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 +13 -11
  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 +11 -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 +9 -1
  36. package/src/components/ToolbarMenu.stories.tsx +17 -9
  37. package/src/components/ToolbarMenu.tsx +30 -11
  38. package/src/hooks/useMenuActions.ts +27 -18
  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 +12 -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 { rxFromSignal } 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,19 @@ 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, onAction }) => {
116
+ const { scope } = useMenuScope(void 0);
117
+ return /* @__PURE__ */ React2.createElement(MenuContextProvider, {
118
+ useGroupItems,
119
+ iconSize,
120
+ attendableId,
121
+ onAction,
122
+ scope
123
+ }, children);
129
124
  };
130
125
  var useMenuItems = (group, propsItems, consumerName = "useMenuItemConsumer", __menuScope) => {
131
126
  const { useGroupItems } = useMenu(consumerName, __menuScope);
@@ -138,66 +133,65 @@ var useMenuItems = (group, propsItems, consumerName = "useMenuItemConsumer", __m
138
133
 
139
134
  // src/components/DropdownMenu.tsx
140
135
  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
- }
136
+ const action = item;
137
+ const handleClick = useCallback((event) => onClick(action, event), [
138
+ action,
139
+ onClick
140
+ ]);
141
+ const { iconSize } = useMenu("DropdownMenuItem", __menuScope);
142
+ return /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Item, {
143
+ onClick: handleClick,
144
+ classNames: "gap-2",
145
+ disabled: action.properties?.disabled,
146
+ ...action.properties?.testId && {
147
+ "data-testid": action.properties.testId
148
+ }
149
+ }, action.properties?.icon && /* @__PURE__ */ React3.createElement(Icon, {
150
+ icon: action.properties.icon,
151
+ size: iconSize
152
+ }), /* @__PURE__ */ React3.createElement(ActionLabel, {
153
+ action
154
+ }));
165
155
  };
166
156
  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?.({
157
+ const [optionsMenuOpen, setOptionsMenuOpen] = useControllableState({
158
+ prop: open,
159
+ defaultProp: defaultOpen,
160
+ onChange: onOpenChange
161
+ });
162
+ const { onAction } = useMenu("DropdownMenuRoot", __menuScope);
163
+ const handleActionClick = useCallback((action, event) => {
164
+ if (action.properties?.disabled) {
165
+ return;
166
+ }
167
+ event.stopPropagation();
168
+ setOptionsMenuOpen(false);
169
+ if (onAction) {
170
+ onAction(action, {
181
171
  parent: group,
182
172
  caller
183
173
  });
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
- }
174
+ } else {
175
+ void executeMenuAction(action, {
176
+ parent: group,
177
+ caller
178
+ });
179
+ }
180
+ }, [
181
+ group,
182
+ caller,
183
+ onAction
184
+ ]);
185
+ const items = useMenuItems(group, propsItems);
186
+ return /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Root, {
187
+ open: optionsMenuOpen,
188
+ onOpenChange: setOptionsMenuOpen,
189
+ ...naturalProps
190
+ }, 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, {
191
+ key: item.id,
192
+ item,
193
+ onClick: handleActionClick
194
+ }))), /* @__PURE__ */ React3.createElement(NaturalDropdownMenu.Arrow, null))));
201
195
  };
202
196
  var DropdownMenu = {
203
197
  Root: DropdownMenuRoot,
@@ -206,220 +200,205 @@ var DropdownMenu = {
206
200
  };
207
201
 
208
202
  // src/components/ToolbarMenu.tsx
209
- import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
210
203
  import React4, { Fragment, useCallback as useCallback2 } from "react";
211
204
  import { Icon as Icon2, Toolbar as NaturalToolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
212
205
  import { useAttention } from "@dxos/react-ui-attention";
213
- import { mx as mx2, textBlockWidth, toolbarInactive, toolbarLayout } from "@dxos/react-ui-theme";
206
+ import { mx as mx2, textBlockWidth, toolbarInactive, toolbarLayout } from "@dxos/ui-theme";
214
207
 
215
208
  // src/types.ts
216
- import { ACTION_GROUP_TYPE as ACTION_GROUP_TYPE2 } from "@dxos/app-graph";
209
+ import { Node as Node2 } from "@dxos/app-graph";
217
210
  var MenuSeparatorType = "@dxos/react-ui-toolbar/separator";
218
211
  var isSeparator = (node) => node.type === MenuSeparatorType;
219
- var isMenuGroup = (node) => node.type === ACTION_GROUP_TYPE2;
212
+ var isMenuGroup = (node) => node.type === Node2.ActionGroupType;
220
213
 
221
214
  // src/components/ToolbarMenu.tsx
222
215
  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?.(), [
216
+ const { iconSize, onAction } = useMenu("ActionToolbarItem", __menuScope);
217
+ const { t } = useTranslation2(translationKey);
218
+ const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
219
+ const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
220
+ const rootProps = icon ? {
221
+ icon,
222
+ size: iconSize,
223
+ iconOnly,
224
+ label: actionLabel(action, t)
225
+ } : {
226
+ children: /* @__PURE__ */ React4.createElement(ActionLabel, {
240
227
  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
- }
228
+ })
229
+ };
230
+ const handleClick = useCallback2(() => {
231
+ if (onAction) {
232
+ onAction(action, {});
233
+ } else {
234
+ void executeMenuAction(action);
235
+ }
236
+ }, [
237
+ action,
238
+ onAction
239
+ ]);
240
+ return hidden ? null : /* @__PURE__ */ React4.createElement(Root, {
241
+ key: action.id,
242
+ disabled,
243
+ onClick: handleClick,
244
+ variant: "ghost",
245
+ ...testId && {
246
+ "data-testid": testId
247
+ },
248
+ ...rootProps,
249
+ classNames
250
+ });
256
251
  };
257
252
  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
- }
253
+ const { iconOnly, disabled, testId } = group.properties;
254
+ const { t } = useTranslation2(translationKey);
255
+ const { iconSize } = useMenu("DropdownMenuToolbarItem", __menuScope);
256
+ const items = useMenuItems(group, propsItems, "DropdownMenuToolbarItem", __menuScope);
257
+ const activeItem = items?.find((item) => !!item.properties.checked);
258
+ const icon = group.properties.applyActive && // TODO(thure): Handle other menu item types.
259
+ activeItem?.properties.icon || group.properties.icon;
260
+ const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
261
+ const labelAction = group.properties.applyActive && activeItem ? activeItem : group;
262
+ const rootProps = icon ? {
263
+ icon,
264
+ size: iconSize,
265
+ iconOnly,
266
+ label: actionLabel(labelAction, t),
267
+ caretDown: true
268
+ } : {
269
+ children: /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(ActionLabel, {
270
+ action: labelAction
271
+ }), /* @__PURE__ */ React4.createElement(Icon2, {
272
+ size: 3,
273
+ icon: "ph--caret-down--bold",
274
+ classNames: "mis-1"
275
+ }))
276
+ };
277
+ return /* @__PURE__ */ React4.createElement(DropdownMenu.Root, {
278
+ group,
279
+ items
280
+ }, /* @__PURE__ */ React4.createElement(DropdownMenu.Trigger, {
281
+ asChild: true
282
+ }, /* @__PURE__ */ React4.createElement(Root, {
283
+ variant: "ghost",
284
+ disabled,
285
+ ...rootProps,
286
+ ...testId && {
287
+ "data-testid": testId
288
+ }
289
+ })));
300
290
  };
301
291
  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
- }
292
+ const { iconSize, onAction } = useMenu("ToggleGroupItem", __menuScope);
293
+ const { t } = useTranslation2(translationKey);
294
+ const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
295
+ const handleClick = useCallback2(() => {
296
+ if (onAction) {
297
+ onAction(action, {
298
+ parent: group
299
+ });
300
+ } else {
301
+ void executeMenuAction(action, {
302
+ parent: group
303
+ });
304
+ }
305
+ }, [
306
+ action,
307
+ group,
308
+ onAction
309
+ ]);
310
+ const Root = icon ? NaturalToolbar.ToggleGroupIconItem : NaturalToolbar.ToggleGroupItem;
311
+ const rootProps = icon ? {
312
+ icon,
313
+ size: iconSize,
314
+ iconOnly,
315
+ label: actionLabel(action, t)
316
+ } : {
317
+ children: /* @__PURE__ */ React4.createElement(ActionLabel, {
318
+ action
319
+ })
320
+ };
321
+ return hidden ? null : /* @__PURE__ */ React4.createElement(Root, {
322
+ key: action.id,
323
+ value: action.id,
324
+ disabled,
325
+ onClick: handleClick,
326
+ variant: "ghost",
327
+ ...testId && {
328
+ "data-testid": testId
329
+ },
330
+ ...rootProps,
331
+ classNames
332
+ });
339
333
  };
340
334
  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
- }
335
+ const items = useMenuItems(group, propsItems, "ToggleGroupToolbarItem", __menuScope);
336
+ const { selectCardinality, value } = group.properties;
337
+ return (
338
+ // TODO(thure): Fix.
339
+ // @ts-ignore
340
+ /* @__PURE__ */ React4.createElement(
341
+ NaturalToolbar.ToggleGroup,
342
+ {
343
+ type: selectCardinality,
344
+ value
345
+ },
346
+ // TODO(thure): Handle other menu item types.
347
+ items.map((action) => /* @__PURE__ */ React4.createElement(ToggleGroupItem, {
348
+ key: action.id,
349
+ group,
350
+ action
351
+ }))
352
+ )
353
+ );
365
354
  };
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
- }
355
+ var ToolbarMenu = ({ __menuScope, classNames, textBlockWidth: textBlockWidthProp, ...props }) => {
356
+ const items = useMenuItems(void 0, void 0, "ToolbarMenu", __menuScope);
357
+ const { attendableId } = useMenu("ToolbarMenu", __menuScope);
358
+ const { hasAttention } = useAttention(attendableId);
359
+ const InnerRoot = textBlockWidthProp ? "div" : Fragment;
360
+ const innerRootProps = textBlockWidthProp ? {
361
+ role: "none",
362
+ className: mx2(textBlockWidth, toolbarLayout, "bs-full")
363
+ } : {};
364
+ return /* @__PURE__ */ React4.createElement(NaturalToolbar.Root, {
365
+ ...props,
366
+ classNames: [
367
+ attendableId && !hasAttention && toolbarInactive,
368
+ classNames
369
+ ],
370
+ layoutManaged: textBlockWidthProp
371
+ }, /* @__PURE__ */ React4.createElement(InnerRoot, innerRootProps, items?.map((item, i) => /* @__PURE__ */ React4.createElement(ToolbarMenuItem, {
372
+ key: item.id ?? i,
373
+ item
374
+ }))));
391
375
  };
392
376
  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
377
+ if (isSeparator(item)) {
378
+ return /* @__PURE__ */ React4.createElement(NaturalToolbar.Separator, {
379
+ variant: item.properties.variant
380
+ });
381
+ }
382
+ if (isMenuGroup(item)) {
383
+ if (item.properties.variant === "dropdownMenu") {
384
+ return /* @__PURE__ */ React4.createElement(DropdownMenuToolbarItem, {
385
+ group: item
386
+ });
387
+ } else {
388
+ return /* @__PURE__ */ React4.createElement(ToggleGroupToolbarItem, {
389
+ group: item
398
390
  });
399
391
  }
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
392
  }
393
+ return /* @__PURE__ */ React4.createElement(ActionToolbarItem, {
394
+ action: item
395
+ });
417
396
  };
418
397
 
419
398
  // src/hooks/useMenuActions.ts
420
- import { RegistryContext, useRxValue } from "@effect-rx/rx-react";
421
- import { useCallback as useCallback3, useContext, useEffect, useState } from "react";
422
- import { Graph, ROOT_ID } from "@dxos/app-graph";
399
+ import { RegistryContext, useAtomValue } from "@effect-atom/atom-react";
400
+ import { useCallback as useCallback3, useContext, useEffect, useRef } from "react";
401
+ import { Graph, Node as Node3 } from "@dxos/app-graph";
423
402
  var edgesArrayToRecord = (edges) => {
424
403
  return Object.fromEntries(Object.entries(edges.reduce((acc, { source, target }) => {
425
404
  if (!acc[source]) {
@@ -447,20 +426,28 @@ var edgesArrayToRecord = (edges) => {
447
426
  };
448
427
  var useMenuActions = (props) => {
449
428
  const registry = useContext(RegistryContext);
450
- const menuGraphProps = useRxValue(props);
451
- const [graph] = useState(new Graph({
452
- registry,
453
- nodes: menuGraphProps.nodes,
454
- edges: edgesArrayToRecord(menuGraphProps.edges)
455
- }));
429
+ const menuGraphProps = useAtomValue(props);
430
+ const graphRef = useRef(null);
431
+ if (!graphRef.current) {
432
+ graphRef.current = Graph.make({
433
+ registry,
434
+ nodes: menuGraphProps.nodes,
435
+ edges: edgesArrayToRecord(menuGraphProps.edges)
436
+ });
437
+ }
438
+ const graph = graphRef.current;
439
+ const prevPropsRef = useRef(menuGraphProps);
456
440
  useEffect(() => {
457
- graph.addNodes(menuGraphProps.nodes);
458
- graph.addEdges(menuGraphProps.edges);
441
+ if (prevPropsRef.current !== menuGraphProps) {
442
+ prevPropsRef.current = menuGraphProps;
443
+ graph.pipe(Graph.addNodes(menuGraphProps.nodes), Graph.addEdges(menuGraphProps.edges));
444
+ }
459
445
  }, [
446
+ graph,
460
447
  menuGraphProps
461
448
  ]);
462
449
  const useGroupItems = useCallback3((sourceNode) => {
463
- const items = useRxValue(graph.connections(sourceNode?.id || ROOT_ID));
450
+ const items = useAtomValue(graph.connections(sourceNode?.id || Node3.RootId));
464
451
  return items;
465
452
  }, [
466
453
  graph
@@ -543,7 +530,6 @@ export {
543
530
  isMenuGroup,
544
531
  isSeparator,
545
532
  menuContextDefaults,
546
- rxFromSignal,
547
533
  useMenu,
548
534
  useMenuActions,
549
535
  useMenuItems