@dxos/react-ui-menu 0.9.0 → 0.9.1-staging.ee54ba693a

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 (51) hide show
  1. package/dist/lib/browser/index.mjs +156 -43
  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 +3 -1
  5. package/dist/lib/browser/translations.mjs.map +2 -2
  6. package/dist/lib/node-esm/index.mjs +156 -43
  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 +3 -1
  10. package/dist/lib/node-esm/translations.mjs.map +2 -2
  11. package/dist/types/src/builder.d.ts +15 -3
  12. package/dist/types/src/builder.d.ts.map +1 -1
  13. package/dist/types/src/components/ActionLabel.d.ts +2 -2
  14. package/dist/types/src/components/ActionLabel.d.ts.map +1 -1
  15. package/dist/types/src/components/Menu.d.ts +4 -4
  16. package/dist/types/src/components/Menu.d.ts.map +1 -1
  17. package/dist/types/src/components/Menu.stories.d.ts +3 -1
  18. package/dist/types/src/components/Menu.stories.d.ts.map +1 -1
  19. package/dist/types/src/components/ToolbarMenu.d.ts +14 -12
  20. package/dist/types/src/components/ToolbarMenu.d.ts.map +1 -1
  21. package/dist/types/src/components/ToolbarMenu.stories.d.ts +7 -1
  22. package/dist/types/src/components/ToolbarMenu.stories.d.ts.map +1 -1
  23. package/dist/types/src/hooks/index.d.ts +1 -0
  24. package/dist/types/src/hooks/index.d.ts.map +1 -1
  25. package/dist/types/src/hooks/useGraphMenuActions.d.ts +31 -0
  26. package/dist/types/src/hooks/useGraphMenuActions.d.ts.map +1 -0
  27. package/dist/types/src/hooks/useGraphMenuActions.test.d.ts +2 -0
  28. package/dist/types/src/hooks/useGraphMenuActions.test.d.ts.map +1 -0
  29. package/dist/types/src/index.d.ts +1 -1
  30. package/dist/types/src/index.d.ts.map +1 -1
  31. package/dist/types/src/translations.d.ts +3 -1
  32. package/dist/types/src/translations.d.ts.map +1 -1
  33. package/dist/types/src/types.d.ts +7 -11
  34. package/dist/types/src/types.d.ts.map +1 -1
  35. package/dist/types/src/util.d.ts +8 -3
  36. package/dist/types/src/util.d.ts.map +1 -1
  37. package/dist/types/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +25 -25
  39. package/src/builder.test.ts +50 -11
  40. package/src/builder.ts +36 -5
  41. package/src/components/ActionLabel.tsx +2 -2
  42. package/src/components/Menu.tsx +7 -12
  43. package/src/components/ToolbarMenu.stories.tsx +31 -2
  44. package/src/components/ToolbarMenu.tsx +125 -66
  45. package/src/hooks/index.ts +1 -0
  46. package/src/hooks/useGraphMenuActions.test.ts +93 -0
  47. package/src/hooks/useGraphMenuActions.ts +96 -0
  48. package/src/index.ts +1 -1
  49. package/src/translations.ts +3 -1
  50. package/src/types.ts +9 -6
  51. package/src/util.ts +1 -6
@@ -1 +1 @@
1
- {"inputs":{"src/util.ts":{"bytes":7939,"imports":[{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true},{"path":"@dxos/effect","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"src/components/ActionLabel.tsx":{"bytes":3895,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/keyboard","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"src/util.ts","kind":"import-statement","original":"../util"}],"format":"esm"},"src/types.ts":{"bytes":4432,"imports":[{"path":"@dxos/app-graph","kind":"import-statement","external":true}],"format":"esm"},"src/components/DropdownMenu.tsx":{"bytes":10853,"imports":[{"path":"@radix-ui/react-use-controllable-state","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"src/util.ts","kind":"import-statement","original":"../util"},{"path":"src/components/ActionLabel.tsx","kind":"import-statement","original":"./ActionLabel"},{"path":"src/components/Menu.tsx","kind":"import-statement","original":"./Menu"}],"format":"esm"},"src/components/ToolbarMenu.tsx":{"bytes":26367,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-attention","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"src/types.ts","kind":"import-statement","original":"../types"},{"path":"src/util.ts","kind":"import-statement","original":"../util"},{"path":"src/components/ActionLabel.tsx","kind":"import-statement","original":"./ActionLabel"},{"path":"src/components/DropdownMenu.tsx","kind":"import-statement","original":"./DropdownMenu"},{"path":"src/components/Menu.tsx","kind":"import-statement","original":"./Menu"}],"format":"esm"},"src/components/Menu.tsx":{"bytes":31593,"imports":[{"path":"@effect-atom/atom-react","kind":"import-statement","external":true},{"path":"@radix-ui/react-context","kind":"import-statement","external":true},{"path":"@radix-ui/react-use-controllable-state","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"src/util.ts","kind":"import-statement","original":"../util"},{"path":"src/components/ActionLabel.tsx","kind":"import-statement","original":"./ActionLabel"},{"path":"src/components/ToolbarMenu.tsx","kind":"import-statement","original":"./ToolbarMenu"}],"format":"esm"},"src/components/index.ts":{"bytes":368,"imports":[{"path":"src/components/Menu.tsx","kind":"import-statement","original":"./Menu"}],"format":"esm"},"src/hooks/useMenuActions.ts":{"bytes":6912,"imports":[{"path":"@effect-atom/atom-react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/index.ts":{"bytes":394,"imports":[{"path":"src/hooks/useMenuActions.ts","kind":"import-statement","original":"./useMenuActions"}],"format":"esm"},"src/builder.ts":{"bytes":11182,"imports":[{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"src/types.ts","kind":"import-statement","original":"./types"},{"path":"src/util.ts","kind":"import-statement","original":"./util"}],"format":"esm"},"src/index.ts":{"bytes":1214,"imports":[{"path":"src/components/index.ts","kind":"import-statement","original":"./components"},{"path":"src/hooks/index.ts","kind":"import-statement","original":"./hooks"},{"path":"src/types.ts","kind":"import-statement","original":"./types"},{"path":"src/util.ts","kind":"import-statement","original":"./util"},{"path":"src/builder.ts","kind":"import-statement","original":"./builder"}],"format":"esm"},"src/translations.ts":{"bytes":968,"imports":[],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":54013},"dist/lib/browser/index.mjs":{"imports":[{"path":"@effect-atom/atom-react","kind":"import-statement","external":true},{"path":"@radix-ui/react-context","kind":"import-statement","external":true},{"path":"@radix-ui/react-use-controllable-state","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true},{"path":"@dxos/effect","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/keyboard","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-attention","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true},{"path":"@radix-ui/react-use-controllable-state","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@effect-atom/atom-react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true}],"exports":["Menu","MenuBuilder","MenuSeparatorType","createGapSeparator","createLineSeparator","createMenuAction","createMenuItemGroup","isMenuGroup","isSeparator","menuContextDefaults","useMenu","useMenuActions","useMenuBuilder","useMenuItems","useMenuScoped"],"entryPoint":"src/index.ts","inputs":{"src/components/Menu.tsx":{"bytesInOutput":6930},"src/util.ts":{"bytesInOutput":1473},"src/components/ActionLabel.tsx":{"bytesInOutput":890},"src/components/ToolbarMenu.tsx":{"bytesInOutput":6383},"src/types.ts":{"bytesInOutput":235},"src/components/DropdownMenu.tsx":{"bytesInOutput":2516},"src/index.ts":{"bytesInOutput":0},"src/hooks/useMenuActions.ts":{"bytesInOutput":955},"src/builder.ts":{"bytesInOutput":2241}},"bytes":22214},"dist/lib/browser/translations.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":523},"dist/lib/browser/translations.mjs":{"imports":[],"exports":["translationKey","translations"],"entryPoint":"src/translations.ts","inputs":{"src/translations.ts":{"bytesInOutput":124}},"bytes":234}}}
1
+ {"inputs":{"src/util.ts":{"bytes":7839,"imports":[{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true},{"path":"@dxos/effect","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"src/components/ActionLabel.tsx":{"bytes":3891,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/keyboard","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"src/util.ts","kind":"import-statement","original":"../util"}],"format":"esm"},"src/types.ts":{"bytes":4616,"imports":[{"path":"@dxos/app-graph","kind":"import-statement","external":true}],"format":"esm"},"src/components/DropdownMenu.tsx":{"bytes":10853,"imports":[{"path":"@radix-ui/react-use-controllable-state","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"src/util.ts","kind":"import-statement","original":"../util"},{"path":"src/components/ActionLabel.tsx","kind":"import-statement","original":"./ActionLabel"},{"path":"src/components/Menu.tsx","kind":"import-statement","original":"./Menu"}],"format":"esm"},"src/components/ToolbarMenu.tsx":{"bytes":31846,"imports":[{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-attention","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"src/types.ts","kind":"import-statement","original":"../types"},{"path":"src/util.ts","kind":"import-statement","original":"../util"},{"path":"src/components/ActionLabel.tsx","kind":"import-statement","original":"./ActionLabel"},{"path":"src/components/DropdownMenu.tsx","kind":"import-statement","original":"./DropdownMenu"},{"path":"src/components/Menu.tsx","kind":"import-statement","original":"./Menu"}],"format":"esm"},"src/components/Menu.tsx":{"bytes":31436,"imports":[{"path":"@effect-atom/atom-react","kind":"import-statement","external":true},{"path":"@radix-ui/react-context","kind":"import-statement","external":true},{"path":"@radix-ui/react-use-controllable-state","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"src/util.ts","kind":"import-statement","original":"../util"},{"path":"src/components/ActionLabel.tsx","kind":"import-statement","original":"./ActionLabel"},{"path":"src/components/ToolbarMenu.tsx","kind":"import-statement","original":"./ToolbarMenu"}],"format":"esm"},"src/components/index.ts":{"bytes":368,"imports":[{"path":"src/components/Menu.tsx","kind":"import-statement","original":"./Menu"}],"format":"esm"},"src/hooks/useMenuActions.ts":{"bytes":6912,"imports":[{"path":"@effect-atom/atom-react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true}],"format":"esm"},"src/hooks/useGraphMenuActions.ts":{"bytes":9917,"imports":[{"path":"@effect-atom/atom-react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true},{"path":"src/hooks/useMenuActions.ts","kind":"import-statement","original":"./useMenuActions"}],"format":"esm"},"src/hooks/index.ts":{"bytes":509,"imports":[{"path":"src/hooks/useGraphMenuActions.ts","kind":"import-statement","original":"./useGraphMenuActions"},{"path":"src/hooks/useMenuActions.ts","kind":"import-statement","original":"./useMenuActions"}],"format":"esm"},"src/builder.ts":{"bytes":13815,"imports":[{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"src/types.ts","kind":"import-statement","original":"./types"},{"path":"src/util.ts","kind":"import-statement","original":"./util"}],"format":"esm"},"src/index.ts":{"bytes":1214,"imports":[{"path":"src/components/index.ts","kind":"import-statement","original":"./components"},{"path":"src/hooks/index.ts","kind":"import-statement","original":"./hooks"},{"path":"src/types.ts","kind":"import-statement","original":"./types"},{"path":"src/util.ts","kind":"import-statement","original":"./util"},{"path":"src/builder.ts","kind":"import-statement","original":"./builder"}],"format":"esm"},"src/translations.ts":{"bytes":1117,"imports":[],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":63377},"dist/lib/browser/index.mjs":{"imports":[{"path":"@effect-atom/atom-react","kind":"import-statement","external":true},{"path":"@radix-ui/react-context","kind":"import-statement","external":true},{"path":"@radix-ui/react-use-controllable-state","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/log","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true},{"path":"@dxos/effect","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/keyboard","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/ui-theme","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@dxos/react-ui-attention","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true},{"path":"@radix-ui/react-use-controllable-state","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/react-ui","kind":"import-statement","external":true},{"path":"@effect-atom/atom-react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true},{"path":"@effect-atom/atom-react","kind":"import-statement","external":true},{"path":"react","kind":"import-statement","external":true},{"path":"@dxos/app-graph","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"#translations","kind":"import-statement","external":true}],"exports":["Menu","MenuBuilder","MenuSeparatorType","buildGraphMenu","createGapSeparator","createLineSeparator","createMenuAction","createMenuItemGroup","graphActions","isMenuGroup","isSeparator","menuContextDefaults","useGraphMenuActions","useMenu","useMenuActions","useMenuBuilder","useMenuItems","useMenuScoped"],"entryPoint":"src/index.ts","inputs":{"src/components/Menu.tsx":{"bytesInOutput":6895},"src/util.ts":{"bytesInOutput":1473},"src/components/ActionLabel.tsx":{"bytesInOutput":890},"src/components/ToolbarMenu.tsx":{"bytesInOutput":7824},"src/types.ts":{"bytesInOutput":235},"src/components/DropdownMenu.tsx":{"bytesInOutput":2516},"src/index.ts":{"bytesInOutput":0},"src/hooks/useGraphMenuActions.ts":{"bytesInOutput":1454},"src/hooks/useMenuActions.ts":{"bytesInOutput":955},"src/builder.ts":{"bytesInOutput":2728}},"bytes":25692},"dist/lib/browser/translations.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":586},"dist/lib/browser/translations.mjs":{"imports":[],"exports":["translationKey","translations"],"entryPoint":"src/translations.ts","inputs":{"src/translations.ts":{"bytesInOutput":171}},"bytes":281}}}
@@ -3,7 +3,9 @@ var translationKey = "@dxos/react-ui-menu";
3
3
  var translations = [
4
4
  {
5
5
  "en-US": {
6
- [translationKey]: {}
6
+ [translationKey]: {
7
+ "toolbar-overflow.menu": "More"
8
+ }
7
9
  }
8
10
  }
9
11
  ];
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/translations.ts"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Resource } from '@dxos/react-ui';\n\nexport const translationKey = '@dxos/react-ui-menu';\n\nexport const translations = [\n {\n 'en-US': {\n [translationKey]: {},\n },\n },\n] as const satisfies Resource[];\n"],
5
- "mappings": ";AAMO,IAAMA,iBAAiB;AAEvB,IAAMC,eAAe;EAC1B;IACE,SAAS;MACP,CAACD,cAAAA,GAAiB,CAAC;IACrB;EACF;;",
4
+ "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Resource } from '@dxos/react-ui';\n\nexport const translationKey = '@dxos/react-ui-menu';\n\nexport const translations = [\n {\n 'en-US': {\n [translationKey]: {\n 'toolbar-overflow.menu': 'More',\n },\n },\n },\n] as const satisfies Resource[];\n"],
5
+ "mappings": ";AAMO,IAAMA,iBAAiB;AAEvB,IAAMC,eAAe;EAC1B;IACE,SAAS;MACP,CAACD,cAAAA,GAAiB;QAChB,yBAAyB;MAC3B;IACF;EACF;;",
6
6
  "names": ["translationKey", "translations"]
7
7
  }
@@ -6,7 +6,7 @@ import { createContextScope } from "@radix-ui/react-context";
6
6
  import { useControllableState as useControllableState2 } from "@radix-ui/react-use-controllable-state";
7
7
  import React4, { createContext, useCallback as useCallback3, useContext, useMemo } from "react";
8
8
  import { log } from "@dxos/log";
9
- import { Icon as Icon3, DropdownMenu as NaturalDropdownMenu2 } from "@dxos/react-ui";
9
+ import { Icon as Icon2, DropdownMenu as NaturalDropdownMenu2 } from "@dxos/react-ui";
10
10
 
11
11
  // src/util.ts
12
12
  import * as Effect from "effect/Effect";
@@ -96,7 +96,7 @@ var actionLabel = (action, t) => {
96
96
 
97
97
  // src/components/ToolbarMenu.tsx
98
98
  import React3, { useCallback as useCallback2 } from "react";
99
- import { Icon as Icon2, Toolbar as NaturalToolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
99
+ import { Input, Toolbar as NaturalToolbar, Tooltip, toLocalizedString as toLocalizedString2, useTranslation as useTranslation2 } from "@dxos/react-ui";
100
100
  import { composable, composableProps } from "@dxos/react-ui";
101
101
  import { useAttention } from "@dxos/react-ui-attention";
102
102
  import { translationKey as translationKey2 } from "#translations";
@@ -221,46 +221,75 @@ var ActionToolbarItem = ({ __menuScope, action }) => {
221
221
  action
222
222
  }));
223
223
  };
224
+ var SwitchToolbarItem = ({ __menuScope, action }) => {
225
+ const { onAction } = useMenuScoped("SwitchToolbarItem", __menuScope);
226
+ const { t } = useTranslation2(translationKey2);
227
+ const { label, iconOnly, disabled, testId, hidden, checked } = action.properties;
228
+ const labelStr = toLocalizedString2(label, t);
229
+ const handleCheckedChange = useCallback2(() => {
230
+ if (onAction) {
231
+ onAction(action, {});
232
+ } else {
233
+ void executeMenuAction(action);
234
+ }
235
+ }, [
236
+ action,
237
+ onAction
238
+ ]);
239
+ if (hidden) {
240
+ return null;
241
+ }
242
+ const switchInput = /* @__PURE__ */ React3.createElement(Input.Switch, {
243
+ checked,
244
+ disabled,
245
+ "aria-label": iconOnly ? labelStr : void 0,
246
+ onCheckedChange: handleCheckedChange,
247
+ ...testId && {
248
+ "data-testid": testId
249
+ }
250
+ });
251
+ return /* @__PURE__ */ React3.createElement(Input.Root, null, iconOnly ? /* @__PURE__ */ React3.createElement(Tooltip.Trigger, {
252
+ asChild: true,
253
+ content: labelStr
254
+ }, /* @__PURE__ */ React3.createElement(Input.Block, null, switchInput)) : /* @__PURE__ */ React3.createElement(Input.Block, null, switchInput), !iconOnly && /* @__PURE__ */ React3.createElement(Input.Label, null, labelStr));
255
+ };
224
256
  var DropdownMenuToolbarItem = ({ __menuScope, group, items: propsItems }) => {
225
- const { iconOnly, disabled, testId } = group.properties;
226
257
  const { t } = useTranslation2(translationKey2);
227
258
  const { iconSize } = useMenuScoped("DropdownMenuToolbarItem", __menuScope);
228
259
  const items = useMenuItems(group, propsItems, "DropdownMenuToolbarItem", __menuScope);
260
+ const { iconOnly, disabled, testId, applyActive, caretDown = true, icon: groupIcon, iconClassNames: groupIconClassNames } = group.properties;
229
261
  const activeItem = items?.find((item) => !!item.properties.checked);
230
- const icon = group.properties.applyActive && // TODO(thure): Handle other menu item types.
231
- activeItem?.properties.icon || group.properties.icon;
232
- const iconClassNames = group.properties.applyActive && activeItem?.properties.iconClassNames || group.properties.iconClassNames;
233
- const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;
234
- const labelAction = group.properties.applyActive && activeItem ? activeItem : group;
235
- const rootProps = icon ? {
236
- icon,
237
- size: iconSize,
238
- iconOnly,
239
- iconClassNames,
240
- label: actionLabel(labelAction, t),
241
- caretDown: true
242
- } : {
243
- children: /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(ActionLabel, {
244
- action: labelAction
245
- }), /* @__PURE__ */ React3.createElement(Icon2, {
246
- size: 3,
247
- icon: "ph--caret-down--bold",
248
- classNames: "ms-1"
249
- }))
250
- };
262
+ const icon = applyActive && // TODO(thure): Handle other menu item types.
263
+ activeItem?.properties.icon || groupIcon;
264
+ const iconClassNames = applyActive && activeItem?.properties.iconClassNames || groupIconClassNames;
265
+ const labelAction = applyActive && activeItem ? activeItem : group;
251
266
  return /* @__PURE__ */ React3.createElement(DropdownMenu.Root, {
252
267
  group,
253
268
  items
254
269
  }, /* @__PURE__ */ React3.createElement(DropdownMenu.Trigger, {
255
270
  asChild: true
256
- }, /* @__PURE__ */ React3.createElement(Root, {
271
+ }, icon ? /* @__PURE__ */ React3.createElement(NaturalToolbar.IconButton, {
257
272
  variant: "ghost",
258
273
  disabled,
259
- ...rootProps,
274
+ icon,
275
+ size: iconSize,
276
+ iconOnly,
277
+ iconClassNames,
278
+ label: actionLabel(labelAction, t),
279
+ caretDown,
260
280
  ...testId && {
261
281
  "data-testid": testId
262
282
  }
263
- })));
283
+ }) : /* @__PURE__ */ React3.createElement(NaturalToolbar.Button, {
284
+ variant: "ghost",
285
+ disabled,
286
+ caretDown,
287
+ ...testId && {
288
+ "data-testid": testId
289
+ }
290
+ }, /* @__PURE__ */ React3.createElement(ActionLabel, {
291
+ action: labelAction
292
+ }))));
264
293
  };
265
294
  var ToggleGroupItem = ({ __menuScope, group, action }) => {
266
295
  const { iconSize, onAction } = useMenuScoped("ToggleGroupItem", __menuScope);
@@ -350,16 +379,25 @@ var ToolbarMenuItem = ({ __menuScope, item }) => {
350
379
  __menuScope,
351
380
  group: item
352
381
  });
353
- } else {
354
- return /* @__PURE__ */ React3.createElement(ToggleGroupToolbarItem, {
355
- __menuScope,
356
- group: item
357
- });
358
382
  }
383
+ return /* @__PURE__ */ React3.createElement(ToggleGroupToolbarItem, {
384
+ __menuScope,
385
+ group: item
386
+ });
387
+ }
388
+ const action = item;
389
+ if (action.properties?.variant === "switch") {
390
+ return /* @__PURE__ */ React3.createElement(SwitchToolbarItem, {
391
+ __menuScope,
392
+ action
393
+ });
394
+ }
395
+ if (action.properties?.variant === "custom" && action.properties.render) {
396
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, action.properties.render());
359
397
  }
360
398
  return /* @__PURE__ */ React3.createElement(ActionToolbarItem, {
361
399
  __menuScope,
362
- action: item
400
+ action
363
401
  });
364
402
  };
365
403
 
@@ -388,12 +426,7 @@ var MenuDropdownContext = /* @__PURE__ */ createContext({
388
426
  var DEFAULT_PRIORITY = 100;
389
427
  var sortMenuItems = (items) => [
390
428
  ...items
391
- ].sort((a, b) => {
392
- if (a.priority !== b.priority) {
393
- return a.priority - b.priority;
394
- }
395
- return a.id.localeCompare(b.id);
396
- });
429
+ ].sort((a, b) => a.priority !== b.priority ? a.priority - b.priority : a.id.localeCompare(b.id));
397
430
  var MenuProvider = ({ children, items = menuContextDefaults.items, iconSize = menuContextDefaults.iconSize, attendableId, alwaysActive, onAction }) => {
398
431
  const { scope } = useMenuScope(void 0);
399
432
  const registry = useContext(RegistryContext);
@@ -446,7 +479,7 @@ var resolveItems = (baseItems, group, entries) => {
446
479
  log.warn("multiple replacement entries found", {
447
480
  ids: replacements.map((r) => r.id).join(", "),
448
481
  using: replacements[0].id
449
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 94, S: void 0 });
482
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 89, S: void 0 });
450
483
  }
451
484
  return replacements[0].items;
452
485
  }
@@ -511,7 +544,7 @@ var MenuContentItem = ({ item, onClick, __menuScope }) => {
511
544
  ...action.properties?.testId && {
512
545
  "data-testid": action.properties.testId
513
546
  }
514
- }, action.properties?.icon && /* @__PURE__ */ React4.createElement(Icon3, {
547
+ }, action.properties?.icon && /* @__PURE__ */ React4.createElement(Icon2, {
515
548
  icon: action.properties.icon,
516
549
  size: iconSize
517
550
  }), /* @__PURE__ */ React4.createElement(ActionLabel, {
@@ -560,6 +593,11 @@ var Menu = {
560
593
  Toolbar: ToolbarMenu
561
594
  };
562
595
 
596
+ // src/hooks/useGraphMenuActions.ts
597
+ import { Atom as Atom3 } from "@effect-atom/atom-react";
598
+ import { useMemo as useMemo3 } from "react";
599
+ import { Node as Node4 } from "@dxos/app-graph";
600
+
563
601
  // src/hooks/useMenuActions.ts
564
602
  import { Atom as Atom2, RegistryContext as RegistryContext2, useAtomValue as useAtomValue2 } from "@effect-atom/atom-react";
565
603
  import { useCallback as useCallback4, useContext as useContext2, useMemo as useMemo2 } from "react";
@@ -591,9 +629,72 @@ var useMenuBuilder = (build, deps) => {
591
629
  return useMenuActions(atom);
592
630
  };
593
631
 
632
+ // src/hooks/useGraphMenuActions.ts
633
+ var buildGraphMenu = (resolve, nodeId, options = {}) => {
634
+ const { rootId = Node4.RootId, filter } = options;
635
+ const nodes = [];
636
+ const edges = [];
637
+ const seen = /* @__PURE__ */ new Set();
638
+ const visit = (parentId, sourceId) => {
639
+ for (const action of resolve(sourceId)) {
640
+ if (filter && !filter(action)) {
641
+ continue;
642
+ }
643
+ edges.push({
644
+ source: parentId,
645
+ target: action.id,
646
+ relation: "child"
647
+ });
648
+ if (seen.has(action.id)) {
649
+ continue;
650
+ }
651
+ seen.add(action.id);
652
+ nodes.push(action);
653
+ if (Node4.isActionGroup(action)) {
654
+ visit(action.id, action.id);
655
+ }
656
+ }
657
+ };
658
+ visit(rootId, nodeId);
659
+ return {
660
+ nodes,
661
+ edges
662
+ };
663
+ };
664
+ var graphActions = (graph, get, nodeId, options) => graph && nodeId ? buildGraphMenu((id) => get(graph.actions(id)), nodeId, options) : {
665
+ nodes: [],
666
+ edges: []
667
+ };
668
+ var useGraphMenuActions = (graph, nodeId, options) => {
669
+ const atom = useMemo3(
670
+ () => Atom3.make((get) => graphActions(graph, get, nodeId, options)),
671
+ // `options` is expected to be stable; `rootId` participates since it reshapes the graph.
672
+ // eslint-disable-next-line react-hooks/exhaustive-deps
673
+ [
674
+ graph,
675
+ nodeId,
676
+ options?.rootId
677
+ ]
678
+ );
679
+ return useMenuActions(atom);
680
+ };
681
+
594
682
  // src/builder.ts
595
683
  import { invariant } from "@dxos/invariant";
684
+ import { translationKey as translationKey3 } from "#translations";
596
685
  var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/react-ui-menu/src/builder.ts";
686
+ var overflowMenuProperties = {
687
+ label: [
688
+ "toolbar-overflow.menu",
689
+ {
690
+ ns: translationKey3
691
+ }
692
+ ],
693
+ icon: "ph--dots-three-vertical--regular",
694
+ iconOnly: true,
695
+ caretDown: false,
696
+ variant: "dropdownMenu"
697
+ };
597
698
  var MenuBuilderImpl = class _MenuBuilderImpl {
598
699
  _data;
599
700
  _rootId;
@@ -603,8 +704,8 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
603
704
  this._rootId = _rootId;
604
705
  }
605
706
  root(props) {
606
- 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'"] });
607
- 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'"] });
707
+ invariant(this._rootId === "root", "Root group can only be at the top level", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 29, S: this, A: ["this._rootId === 'root'", "'Root group can only be at the top level'"] });
708
+ invariant(this._data.nodes.find((node) => node.id === "root") === void 0, "Root group can only be created once", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 30, S: this, A: ["this._data.nodes.find((node)=>node.id === 'root') === undefined", "'Root group can only be created once'"] });
608
709
  this._data.nodes.push(createMenuItemGroup("root", props));
609
710
  return this;
610
711
  }
@@ -620,6 +721,12 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
620
721
  });
621
722
  return this;
622
723
  }
724
+ switch(id, props, invoke) {
725
+ return this.action(id, {
726
+ ...props,
727
+ variant: "switch"
728
+ }, invoke);
729
+ }
623
730
  group(id, props, cb) {
624
731
  this._data.nodes.push(createMenuItemGroup(id, props));
625
732
  this._data.edges.push({
@@ -630,6 +737,9 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
630
737
  cb(new _MenuBuilderImpl(this._data, id));
631
738
  return this;
632
739
  }
740
+ menu(id, cb) {
741
+ return this.group(id, overflowMenuProperties, cb);
742
+ }
633
743
  subgraph(subgraphOrCb) {
634
744
  if (!subgraphOrCb) {
635
745
  return this;
@@ -670,13 +780,16 @@ export {
670
780
  Menu,
671
781
  MenuBuilder,
672
782
  MenuSeparatorType,
783
+ buildGraphMenu,
673
784
  createGapSeparator,
674
785
  createLineSeparator,
675
786
  createMenuAction,
676
787
  createMenuItemGroup,
788
+ graphActions,
677
789
  isMenuGroup,
678
790
  isSeparator,
679
791
  menuContextDefaults,
792
+ useGraphMenuActions,
680
793
  useMenu,
681
794
  useMenuActions,
682
795
  useMenuBuilder,