@dxos/react-ui-menu 0.8.2-staging.7ac8446 → 0.8.2

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