@dxos/react-ui-menu 0.8.4-main.d05673bc65 → 0.8.4-main.dfabb4ec29

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 (50) hide show
  1. package/dist/lib/browser/index.mjs +104 -134
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/translations.mjs +14 -0
  5. package/dist/lib/browser/translations.mjs.map +7 -0
  6. package/dist/lib/node-esm/index.mjs +104 -134
  7. package/dist/lib/node-esm/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/meta.json +1 -1
  9. package/dist/lib/node-esm/translations.mjs +16 -0
  10. package/dist/lib/node-esm/translations.mjs.map +7 -0
  11. package/dist/types/src/builder.d.ts +23 -4
  12. package/dist/types/src/builder.d.ts.map +1 -1
  13. package/dist/types/src/builder.test.d.ts +2 -0
  14. package/dist/types/src/builder.test.d.ts.map +1 -0
  15. package/dist/types/src/components/ActionLabel.d.ts.map +1 -1
  16. package/dist/types/src/components/DropdownMenu.d.ts +3 -1
  17. package/dist/types/src/components/DropdownMenu.d.ts.map +1 -1
  18. package/dist/types/src/components/Menu.d.ts +8 -4
  19. package/dist/types/src/components/Menu.d.ts.map +1 -1
  20. package/dist/types/src/components/{MenuItems.stories.d.ts → Menu.stories.d.ts} +1 -1
  21. package/dist/types/src/components/Menu.stories.d.ts.map +1 -0
  22. package/dist/types/src/components/ToolbarMenu.d.ts +4 -4
  23. package/dist/types/src/components/ToolbarMenu.d.ts.map +1 -1
  24. package/dist/types/src/components/ToolbarMenu.stories.d.ts +3 -1
  25. package/dist/types/src/components/ToolbarMenu.stories.d.ts.map +1 -1
  26. package/dist/types/src/hooks/useMenuActions.d.ts.map +1 -1
  27. package/dist/types/src/index.d.ts +1 -1
  28. package/dist/types/src/index.d.ts.map +1 -1
  29. package/dist/types/src/testing/generator.d.ts +8 -4
  30. package/dist/types/src/testing/generator.d.ts.map +1 -1
  31. package/dist/types/src/types.d.ts +2 -1
  32. package/dist/types/src/types.d.ts.map +1 -1
  33. package/dist/types/src/util.d.ts +2 -2
  34. package/dist/types/src/util.d.ts.map +1 -1
  35. package/dist/types/tsconfig.tsbuildinfo +1 -1
  36. package/package.json +27 -21
  37. package/src/builder.test.ts +224 -0
  38. package/src/builder.ts +41 -18
  39. package/src/components/ActionLabel.tsx +2 -1
  40. package/src/components/DropdownMenu.tsx +0 -1
  41. package/src/components/{MenuItems.stories.tsx → Menu.stories.tsx} +2 -1
  42. package/src/components/Menu.test.tsx +0 -1
  43. package/src/components/Menu.tsx +9 -10
  44. package/src/components/ToolbarMenu.stories.tsx +15 -12
  45. package/src/components/ToolbarMenu.tsx +111 -86
  46. package/src/hooks/useMenuActions.ts +6 -7
  47. package/src/index.ts +1 -1
  48. package/src/testing/generator.ts +15 -10
  49. package/src/types.ts +1 -0
  50. package/dist/types/src/components/MenuItems.stories.d.ts.map +0 -1
@@ -2,7 +2,7 @@
2
2
  import { Atom, RegistryContext, useAtomValue } from "@effect-atom/atom-react";
3
3
  import { createContextScope } from "@radix-ui/react-context";
4
4
  import { useControllableState as useControllableState2 } from "@radix-ui/react-use-controllable-state";
5
- import React4, { useCallback as useCallback3, useContext, useMemo } from "react";
5
+ import React4, { createContext, useCallback as useCallback3, useContext, useMemo } from "react";
6
6
  import { log } from "@dxos/log";
7
7
  import { Icon as Icon3, DropdownMenu as NaturalDropdownMenu2 } from "@dxos/react-ui";
8
8
 
@@ -77,18 +77,7 @@ import React from "react";
77
77
  import { keySymbols } from "@dxos/keyboard";
78
78
  import { toLocalizedString, useTranslation } from "@dxos/react-ui";
79
79
  import { mx } from "@dxos/ui-theme";
80
-
81
- // src/translations.ts
82
- var translationKey = "@dxos/react-ui-menu";
83
- var translations = [
84
- {
85
- "en-US": {
86
- [translationKey]: {}
87
- }
88
- }
89
- ];
90
-
91
- // src/components/ActionLabel.tsx
80
+ import { translationKey } from "#translations";
92
81
  var ActionLabel = ({ action }) => {
93
82
  const { t } = useTranslation(translationKey);
94
83
  const shortcut = getShortcut(action);
@@ -104,10 +93,11 @@ var actionLabel = (action, t) => {
104
93
  };
105
94
 
106
95
  // src/components/ToolbarMenu.tsx
107
- import React3, { Fragment, useCallback as useCallback2 } from "react";
96
+ import React3, { useCallback as useCallback2 } from "react";
108
97
  import { Icon as Icon2, Toolbar as NaturalToolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
109
98
  import { useAttention } from "@dxos/react-ui-attention";
110
- import { mx as mx2, textBlockWidth, toolbarLayout } from "@dxos/ui-theme";
99
+ import { composable, composableProps } from "@dxos/ui-theme";
100
+ import { translationKey as translationKey2 } from "#translations";
111
101
 
112
102
  // src/types.ts
113
103
  import { Node as Node2 } from "@dxos/app-graph";
@@ -187,21 +177,11 @@ var DropdownMenu = {
187
177
  };
188
178
 
189
179
  // src/components/ToolbarMenu.tsx
190
- var ActionToolbarItem = ({ action, __menuScope }) => {
180
+ var ActionToolbarItem = ({ __menuScope, action }) => {
191
181
  const { iconSize, onAction } = useMenuScoped("ActionToolbarItem", __menuScope);
192
- const { t } = useTranslation2(translationKey);
193
- const { icon, iconOnly = true, disabled, testId, hidden, classNames, variant } = action.properties;
194
- const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
195
- const rootProps = icon ? {
196
- icon,
197
- size: iconSize,
198
- iconOnly,
199
- label: actionLabel(action, t)
200
- } : {
201
- children: /* @__PURE__ */ React3.createElement(ActionLabel, {
202
- action
203
- })
204
- };
182
+ const { t } = useTranslation2(translationKey2);
183
+ const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
184
+ const buttonVariant = action.properties.variant === "primary" ? "primary" : "ghost";
205
185
  const handleClick = useCallback2(() => {
206
186
  if (onAction) {
207
187
  onAction(action, {});
@@ -212,21 +192,35 @@ var ActionToolbarItem = ({ action, __menuScope }) => {
212
192
  action,
213
193
  onAction
214
194
  ]);
215
- return hidden ? null : /* @__PURE__ */ React3.createElement(Root, {
216
- key: action.id,
195
+ const commonProps = {
196
+ variant: buttonVariant,
217
197
  disabled,
198
+ classNames,
218
199
  onClick: handleClick,
219
- variant: variant ?? "ghost",
220
200
  ...testId && {
221
201
  "data-testid": testId
222
- },
223
- ...rootProps,
224
- classNames
225
- });
202
+ }
203
+ };
204
+ if (hidden) {
205
+ return null;
206
+ }
207
+ return icon ? /* @__PURE__ */ React3.createElement(NaturalToolbar.IconButton, {
208
+ key: action.id,
209
+ ...commonProps,
210
+ icon,
211
+ size: iconSize,
212
+ iconOnly,
213
+ label: actionLabel(action, t)
214
+ }) : /* @__PURE__ */ React3.createElement(NaturalToolbar.Button, {
215
+ key: action.id,
216
+ ...commonProps
217
+ }, /* @__PURE__ */ React3.createElement(ActionLabel, {
218
+ action
219
+ }));
226
220
  };
227
- var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
221
+ var DropdownMenuToolbarItem = ({ __menuScope, group, items: propsItems }) => {
228
222
  const { iconOnly, disabled, testId } = group.properties;
229
- const { t } = useTranslation2(translationKey);
223
+ const { t } = useTranslation2(translationKey2);
230
224
  const { iconSize } = useMenuScoped("DropdownMenuToolbarItem", __menuScope);
231
225
  const items = useMenuItems(group, propsItems, "DropdownMenuToolbarItem", __menuScope);
232
226
  const activeItem = items?.find((item) => !!item.properties.checked);
@@ -263,9 +257,9 @@ var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
263
257
  }
264
258
  })));
265
259
  };
266
- var ToggleGroupItem = ({ group, action, __menuScope }) => {
260
+ var ToggleGroupItem = ({ __menuScope, group, action }) => {
267
261
  const { iconSize, onAction } = useMenuScoped("ToggleGroupItem", __menuScope);
268
- const { t } = useTranslation2(translationKey);
262
+ const { t } = useTranslation2(translationKey2);
269
263
  const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
270
264
  const handleClick = useCallback2(() => {
271
265
  if (onAction) {
@@ -282,74 +276,63 @@ var ToggleGroupItem = ({ group, action, __menuScope }) => {
282
276
  group,
283
277
  onAction
284
278
  ]);
285
- const Root = icon ? NaturalToolbar.ToggleGroupIconItem : NaturalToolbar.ToggleGroupItem;
286
- const rootProps = icon ? {
287
- icon,
288
- size: iconSize,
289
- iconOnly,
290
- label: actionLabel(action, t)
291
- } : {
292
- children: /* @__PURE__ */ React3.createElement(ActionLabel, {
293
- action
294
- })
295
- };
296
- return hidden ? null : /* @__PURE__ */ React3.createElement(Root, {
297
- key: action.id,
279
+ const commonProps = {
298
280
  value: action.id,
299
281
  disabled,
300
- onClick: handleClick,
301
282
  variant: "ghost",
283
+ classNames,
284
+ onClick: handleClick,
302
285
  ...testId && {
303
286
  "data-testid": testId
304
- },
305
- ...rootProps,
306
- classNames
307
- });
287
+ }
288
+ };
289
+ return hidden ? null : icon ? /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroupIconItem, {
290
+ ...commonProps,
291
+ icon,
292
+ size: iconSize,
293
+ iconOnly,
294
+ label: actionLabel(action, t)
295
+ }) : /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroupItem, commonProps, /* @__PURE__ */ React3.createElement(ActionLabel, {
296
+ action
297
+ }));
308
298
  };
309
- var ToggleGroupToolbarItem = ({ group, items: propsItems, __menuScope }) => {
310
- const items = useMenuItems(group, propsItems, "ToggleGroupToolbarItem", __menuScope);
311
- const { selectCardinality, value } = group.properties;
312
- return (
313
- // TODO(thure): Fix.
314
- // @ts-ignore
315
- /* @__PURE__ */ React3.createElement(
316
- NaturalToolbar.ToggleGroup,
317
- {
318
- type: selectCardinality,
319
- value
320
- },
321
- // TODO(thure): Handle other menu item types.
322
- items.map((action) => /* @__PURE__ */ React3.createElement(ToggleGroupItem, {
323
- key: action.id,
324
- group,
325
- action
326
- }))
327
- )
328
- );
299
+ var ToggleGroupToolbarItem = ({ __menuScope, group, items: itemsProp }) => {
300
+ const items = useMenuItems(group, itemsProp, "ToggleGroupToolbarItem", __menuScope);
301
+ const { selectCardinality } = group.properties;
302
+ const children = items.map((action) => /* @__PURE__ */ React3.createElement(ToggleGroupItem, {
303
+ key: action.id,
304
+ group,
305
+ action
306
+ }));
307
+ if (selectCardinality === "multiple") {
308
+ return /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroup, {
309
+ type: "multiple",
310
+ value: group.properties.value
311
+ }, children);
312
+ } else {
313
+ return /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroup, {
314
+ type: "single",
315
+ value: group.properties.value
316
+ }, children);
317
+ }
329
318
  };
330
- var ToolbarMenu = ({ __menuScope, classNames, textBlockWidth: textBlockWidthProp, ...props }) => {
319
+ var ToolbarMenu = composable(({ __menuScope, children, ...props }, forwardedRef) => {
331
320
  const items = useMenuItems(void 0, void 0, "ToolbarMenu", __menuScope);
332
321
  const { attendableId, alwaysActive } = useMenuScoped("ToolbarMenu", __menuScope);
333
322
  const { hasAttention } = useAttention(attendableId);
334
- const InnerRoot = textBlockWidthProp ? "div" : Fragment;
335
- const innerRootProps = textBlockWidthProp ? {
336
- role: "none",
337
- className: mx2(textBlockWidth, toolbarLayout, "h-full")
338
- } : {};
339
323
  return /* @__PURE__ */ React3.createElement(NaturalToolbar.Root, {
340
- ...props,
341
- classNames: [
342
- attendableId,
343
- classNames
344
- ],
324
+ ...composableProps(props, {
325
+ classNames: attendableId
326
+ }),
345
327
  disabled: !alwaysActive && !hasAttention,
346
- layoutManaged: textBlockWidthProp
347
- }, /* @__PURE__ */ React3.createElement(InnerRoot, innerRootProps, items?.map((item, i) => /* @__PURE__ */ React3.createElement(ToolbarMenuItem, {
348
- key: item.id ?? i,
328
+ ref: forwardedRef
329
+ }, items?.map((item) => /* @__PURE__ */ React3.createElement(ToolbarMenuItem, {
330
+ key: item.id,
331
+ __menuScope,
349
332
  item
350
- }))));
351
- };
352
- var ToolbarMenuItem = ({ item }) => {
333
+ })), children);
334
+ });
335
+ var ToolbarMenuItem = ({ __menuScope, item }) => {
353
336
  if (isSeparator(item)) {
354
337
  return /* @__PURE__ */ React3.createElement(NaturalToolbar.Separator, {
355
338
  variant: item.properties.variant
@@ -358,15 +341,18 @@ var ToolbarMenuItem = ({ item }) => {
358
341
  if (isMenuGroup(item)) {
359
342
  if (item.properties.variant === "dropdownMenu") {
360
343
  return /* @__PURE__ */ React3.createElement(DropdownMenuToolbarItem, {
344
+ __menuScope,
361
345
  group: item
362
346
  });
363
347
  } else {
364
348
  return /* @__PURE__ */ React3.createElement(ToggleGroupToolbarItem, {
349
+ __menuScope,
365
350
  group: item
366
351
  });
367
352
  }
368
353
  }
369
354
  return /* @__PURE__ */ React3.createElement(ActionToolbarItem, {
355
+ __menuScope,
370
356
  action: item
371
357
  });
372
358
  };
@@ -389,7 +375,7 @@ var menuContextDefaults = {
389
375
  };
390
376
  var [MenuContextProvider, useMenuScoped] = createMenuContext(MENU_NAME, menuContextDefaults);
391
377
  var useMenuScope = createMenuScope();
392
- var MenuDropdownContext = /* @__PURE__ */ React4.createContext({
378
+ var MenuDropdownContext = /* @__PURE__ */ createContext({
393
379
  closeMenu: () => {
394
380
  }
395
381
  });
@@ -454,12 +440,7 @@ var resolveItems = (baseItems, group, entries) => {
454
440
  log.warn("multiple replacement entries found", {
455
441
  ids: replacements.map((r) => r.id).join(", "),
456
442
  using: replacements[0].id
457
- }, {
458
- F: __dxlog_file,
459
- L: 152,
460
- S: void 0,
461
- C: (f, a) => f(...a)
462
- });
443
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 94, S: void 0 });
463
444
  }
464
445
  return replacements[0].items;
465
446
  }
@@ -575,23 +556,21 @@ var Menu = {
575
556
 
576
557
  // src/hooks/useMenuActions.ts
577
558
  import { RegistryContext as RegistryContext2, useAtomValue as useAtomValue2 } from "@effect-atom/atom-react";
578
- import { useCallback as useCallback4, useContext as useContext2, useRef } from "react";
559
+ import { useCallback as useCallback4, useContext as useContext2, useMemo as useMemo2 } from "react";
579
560
  import { Graph, Node as Node3 } from "@dxos/app-graph";
580
561
  var useMenuActions = (props) => {
581
562
  const registry = useContext2(RegistryContext2);
582
563
  const menuGraphProps = useAtomValue2(props);
583
- const graphRef = useRef(null);
584
- if (!graphRef.current || graphRef.current.props !== menuGraphProps) {
564
+ const graph = useMemo2(() => {
585
565
  const newGraph = Graph.make({
586
566
  registry
587
567
  });
588
568
  newGraph.pipe(Graph.addNodes(menuGraphProps.nodes), Graph.addEdges(menuGraphProps.edges));
589
- graphRef.current = {
590
- graph: newGraph,
591
- props: menuGraphProps
592
- };
593
- }
594
- const graph = graphRef.current.graph;
569
+ return newGraph;
570
+ }, [
571
+ registry,
572
+ menuGraphProps
573
+ ]);
595
574
  const items = useCallback4((group) => {
596
575
  return graph.connections(group?.id || Node3.RootId, "child");
597
576
  }, [
@@ -614,24 +593,8 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
614
593
  this._rootId = _rootId;
615
594
  }
616
595
  root(props) {
617
- invariant(this._rootId === "root", "Root group can only be at the top level", {
618
- F: __dxlog_file2,
619
- L: 41,
620
- S: this,
621
- A: [
622
- "this._rootId === 'root'",
623
- "'Root group can only be at the top level'"
624
- ]
625
- });
626
- invariant(this._data.nodes.find((node) => node.id === "root") === void 0, "Root group can only be created once", {
627
- F: __dxlog_file2,
628
- L: 42,
629
- S: this,
630
- A: [
631
- "this._data.nodes.find((node) => node.id === 'root') === undefined",
632
- "'Root group can only be created once'"
633
- ]
634
- });
596
+ 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'"] });
597
+ 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'"] });
635
598
  this._data.nodes.push(createMenuItemGroup("root", props));
636
599
  return this;
637
600
  }
@@ -657,13 +620,20 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
657
620
  cb(new _MenuBuilderImpl(this._data, id));
658
621
  return this;
659
622
  }
660
- subgraph(subgraph) {
661
- this._data.nodes.push(...subgraph.nodes);
662
- this._data.edges.push(...subgraph.edges);
623
+ subgraph(subgraphOrCb) {
624
+ if (!subgraphOrCb) {
625
+ return this;
626
+ }
627
+ if (typeof subgraphOrCb === "function") {
628
+ subgraphOrCb(new _MenuBuilderImpl(this._data, this._rootId));
629
+ } else {
630
+ this._data.nodes.push(...subgraphOrCb.nodes);
631
+ this._data.edges.push(...subgraphOrCb.edges);
632
+ }
663
633
  return this;
664
634
  }
665
- separator(id, variant = "gap") {
666
- id ??= `separator-${++this._separatorCount}`;
635
+ separator(variant = "gap") {
636
+ const id = `separator-${++this._separatorCount}`;
667
637
  this._data.nodes.push({
668
638
  id,
669
639
  type: MenuSeparatorType,