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