@dxos/react-ui-menu 0.8.4-main.40e3dcdf1b → 0.8.4-main.422d1c7879

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 (42) hide show
  1. package/dist/lib/browser/index.mjs +99 -98
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +99 -98
  5. package/dist/lib/node-esm/index.mjs.map +3 -3
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/builder.d.ts +23 -4
  8. package/dist/types/src/builder.d.ts.map +1 -1
  9. package/dist/types/src/builder.test.d.ts +2 -0
  10. package/dist/types/src/builder.test.d.ts.map +1 -0
  11. package/dist/types/src/components/DropdownMenu.d.ts +3 -1
  12. package/dist/types/src/components/DropdownMenu.d.ts.map +1 -1
  13. package/dist/types/src/components/Menu.d.ts +8 -4
  14. package/dist/types/src/components/Menu.d.ts.map +1 -1
  15. package/dist/types/src/components/{MenuItems.stories.d.ts → Menu.stories.d.ts} +1 -1
  16. package/dist/types/src/components/Menu.stories.d.ts.map +1 -0
  17. package/dist/types/src/components/ToolbarMenu.d.ts +4 -4
  18. package/dist/types/src/components/ToolbarMenu.d.ts.map +1 -1
  19. package/dist/types/src/components/ToolbarMenu.stories.d.ts +3 -1
  20. package/dist/types/src/components/ToolbarMenu.stories.d.ts.map +1 -1
  21. package/dist/types/src/hooks/useMenuActions.d.ts.map +1 -1
  22. package/dist/types/src/index.d.ts +1 -1
  23. package/dist/types/src/index.d.ts.map +1 -1
  24. package/dist/types/src/testing/generator.d.ts +7 -3
  25. package/dist/types/src/testing/generator.d.ts.map +1 -1
  26. package/dist/types/src/types.d.ts +1 -0
  27. package/dist/types/src/types.d.ts.map +1 -1
  28. package/dist/types/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +17 -20
  30. package/src/builder.test.ts +224 -0
  31. package/src/builder.ts +41 -18
  32. package/src/components/DropdownMenu.tsx +0 -1
  33. package/src/components/Menu.test.tsx +0 -1
  34. package/src/components/Menu.tsx +9 -10
  35. package/src/components/ToolbarMenu.stories.tsx +13 -11
  36. package/src/components/ToolbarMenu.tsx +109 -85
  37. package/src/hooks/useMenuActions.ts +6 -7
  38. package/src/index.ts +1 -1
  39. package/src/testing/generator.ts +15 -10
  40. package/src/types.ts +1 -0
  41. package/dist/types/src/components/MenuItems.stories.d.ts.map +0 -1
  42. /package/src/components/{MenuItems.stories.tsx → Menu.stories.tsx} +0 -0
@@ -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
 
@@ -104,10 +104,10 @@ var actionLabel = (action, t) => {
104
104
  };
105
105
 
106
106
  // src/components/ToolbarMenu.tsx
107
- import React3, { Fragment, useCallback as useCallback2 } from "react";
107
+ import React3, { useCallback as useCallback2 } from "react";
108
108
  import { Icon as Icon2, Toolbar as NaturalToolbar, useTranslation as useTranslation2 } from "@dxos/react-ui";
109
109
  import { useAttention } from "@dxos/react-ui-attention";
110
- import { mx as mx2, textBlockWidth, toolbarLayout } from "@dxos/ui-theme";
110
+ import { composable, composableProps } from "@dxos/ui-theme";
111
111
 
112
112
  // src/types.ts
113
113
  import { Node as Node2 } from "@dxos/app-graph";
@@ -187,21 +187,11 @@ var DropdownMenu = {
187
187
  };
188
188
 
189
189
  // src/components/ToolbarMenu.tsx
190
- var ActionToolbarItem = ({ action, __menuScope }) => {
190
+ var ActionToolbarItem = ({ __menuScope, action }) => {
191
191
  const { iconSize, onAction } = useMenuScoped("ActionToolbarItem", __menuScope);
192
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
- };
193
+ const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
194
+ const buttonVariant = action.properties.variant === "primary" ? "primary" : "ghost";
205
195
  const handleClick = useCallback2(() => {
206
196
  if (onAction) {
207
197
  onAction(action, {});
@@ -212,19 +202,33 @@ var ActionToolbarItem = ({ action, __menuScope }) => {
212
202
  action,
213
203
  onAction
214
204
  ]);
215
- return hidden ? null : /* @__PURE__ */ React3.createElement(Root, {
216
- key: action.id,
205
+ const commonProps = {
206
+ variant: buttonVariant,
217
207
  disabled,
208
+ classNames,
218
209
  onClick: handleClick,
219
- variant: variant ?? "ghost",
220
210
  ...testId && {
221
211
  "data-testid": testId
222
- },
223
- ...rootProps,
224
- classNames
225
- });
212
+ }
213
+ };
214
+ if (hidden) {
215
+ return null;
216
+ }
217
+ return icon ? /* @__PURE__ */ React3.createElement(NaturalToolbar.IconButton, {
218
+ key: action.id,
219
+ ...commonProps,
220
+ icon,
221
+ size: iconSize,
222
+ iconOnly,
223
+ label: actionLabel(action, t)
224
+ }) : /* @__PURE__ */ React3.createElement(NaturalToolbar.Button, {
225
+ key: action.id,
226
+ ...commonProps
227
+ }, /* @__PURE__ */ React3.createElement(ActionLabel, {
228
+ action
229
+ }));
226
230
  };
227
- var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
231
+ var DropdownMenuToolbarItem = ({ __menuScope, group, items: propsItems }) => {
228
232
  const { iconOnly, disabled, testId } = group.properties;
229
233
  const { t } = useTranslation2(translationKey);
230
234
  const { iconSize } = useMenuScoped("DropdownMenuToolbarItem", __menuScope);
@@ -263,7 +267,7 @@ var DropdownMenuToolbarItem = ({ group, items: propsItems, __menuScope }) => {
263
267
  }
264
268
  })));
265
269
  };
266
- var ToggleGroupItem = ({ group, action, __menuScope }) => {
270
+ var ToggleGroupItem = ({ __menuScope, group, action }) => {
267
271
  const { iconSize, onAction } = useMenuScoped("ToggleGroupItem", __menuScope);
268
272
  const { t } = useTranslation2(translationKey);
269
273
  const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;
@@ -282,74 +286,63 @@ var ToggleGroupItem = ({ group, action, __menuScope }) => {
282
286
  group,
283
287
  onAction
284
288
  ]);
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,
289
+ const commonProps = {
298
290
  value: action.id,
299
291
  disabled,
300
- onClick: handleClick,
301
292
  variant: "ghost",
293
+ classNames,
294
+ onClick: handleClick,
302
295
  ...testId && {
303
296
  "data-testid": testId
304
- },
305
- ...rootProps,
306
- classNames
307
- });
297
+ }
298
+ };
299
+ return hidden ? null : icon ? /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroupIconItem, {
300
+ ...commonProps,
301
+ icon,
302
+ size: iconSize,
303
+ iconOnly,
304
+ label: actionLabel(action, t)
305
+ }) : /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroupItem, commonProps, /* @__PURE__ */ React3.createElement(ActionLabel, {
306
+ action
307
+ }));
308
308
  };
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
- );
309
+ var ToggleGroupToolbarItem = ({ __menuScope, group, items: itemsProp }) => {
310
+ const items = useMenuItems(group, itemsProp, "ToggleGroupToolbarItem", __menuScope);
311
+ const { selectCardinality } = group.properties;
312
+ const children = items.map((action) => /* @__PURE__ */ React3.createElement(ToggleGroupItem, {
313
+ key: action.id,
314
+ group,
315
+ action
316
+ }));
317
+ if (selectCardinality === "multiple") {
318
+ return /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroup, {
319
+ type: "multiple",
320
+ value: group.properties.value
321
+ }, children);
322
+ } else {
323
+ return /* @__PURE__ */ React3.createElement(NaturalToolbar.ToggleGroup, {
324
+ type: "single",
325
+ value: group.properties.value
326
+ }, children);
327
+ }
329
328
  };
330
- var ToolbarMenu = ({ __menuScope, classNames, textBlockWidth: textBlockWidthProp, ...props }) => {
329
+ var ToolbarMenu = composable(({ __menuScope, children, ...props }, forwardedRef) => {
331
330
  const items = useMenuItems(void 0, void 0, "ToolbarMenu", __menuScope);
332
331
  const { attendableId, alwaysActive } = useMenuScoped("ToolbarMenu", __menuScope);
333
332
  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
333
  return /* @__PURE__ */ React3.createElement(NaturalToolbar.Root, {
340
- ...props,
341
- classNames: [
342
- attendableId,
343
- classNames
344
- ],
334
+ ...composableProps(props, {
335
+ classNames: attendableId
336
+ }),
345
337
  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,
338
+ ref: forwardedRef
339
+ }, items?.map((item) => /* @__PURE__ */ React3.createElement(ToolbarMenuItem, {
340
+ key: item.id,
341
+ __menuScope,
349
342
  item
350
- }))));
351
- };
352
- var ToolbarMenuItem = ({ item }) => {
343
+ })), children);
344
+ });
345
+ var ToolbarMenuItem = ({ __menuScope, item }) => {
353
346
  if (isSeparator(item)) {
354
347
  return /* @__PURE__ */ React3.createElement(NaturalToolbar.Separator, {
355
348
  variant: item.properties.variant
@@ -358,15 +351,18 @@ var ToolbarMenuItem = ({ item }) => {
358
351
  if (isMenuGroup(item)) {
359
352
  if (item.properties.variant === "dropdownMenu") {
360
353
  return /* @__PURE__ */ React3.createElement(DropdownMenuToolbarItem, {
354
+ __menuScope,
361
355
  group: item
362
356
  });
363
357
  } else {
364
358
  return /* @__PURE__ */ React3.createElement(ToggleGroupToolbarItem, {
359
+ __menuScope,
365
360
  group: item
366
361
  });
367
362
  }
368
363
  }
369
364
  return /* @__PURE__ */ React3.createElement(ActionToolbarItem, {
365
+ __menuScope,
370
366
  action: item
371
367
  });
372
368
  };
@@ -389,7 +385,7 @@ var menuContextDefaults = {
389
385
  };
390
386
  var [MenuContextProvider, useMenuScoped] = createMenuContext(MENU_NAME, menuContextDefaults);
391
387
  var useMenuScope = createMenuScope();
392
- var MenuDropdownContext = /* @__PURE__ */ React4.createContext({
388
+ var MenuDropdownContext = /* @__PURE__ */ createContext({
393
389
  closeMenu: () => {
394
390
  }
395
391
  });
@@ -456,7 +452,7 @@ var resolveItems = (baseItems, group, entries) => {
456
452
  using: replacements[0].id
457
453
  }, {
458
454
  F: __dxlog_file,
459
- L: 152,
455
+ L: 151,
460
456
  S: void 0,
461
457
  C: (f, a) => f(...a)
462
458
  });
@@ -575,23 +571,21 @@ var Menu = {
575
571
 
576
572
  // src/hooks/useMenuActions.ts
577
573
  import { RegistryContext as RegistryContext2, useAtomValue as useAtomValue2 } from "@effect-atom/atom-react";
578
- import { useCallback as useCallback4, useContext as useContext2, useRef } from "react";
574
+ import { useCallback as useCallback4, useContext as useContext2, useMemo as useMemo2 } from "react";
579
575
  import { Graph, Node as Node3 } from "@dxos/app-graph";
580
576
  var useMenuActions = (props) => {
581
577
  const registry = useContext2(RegistryContext2);
582
578
  const menuGraphProps = useAtomValue2(props);
583
- const graphRef = useRef(null);
584
- if (!graphRef.current || graphRef.current.props !== menuGraphProps) {
579
+ const graph = useMemo2(() => {
585
580
  const newGraph = Graph.make({
586
581
  registry
587
582
  });
588
583
  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;
584
+ return newGraph;
585
+ }, [
586
+ registry,
587
+ menuGraphProps
588
+ ]);
595
589
  const items = useCallback4((group) => {
596
590
  return graph.connections(group?.id || Node3.RootId, "child");
597
591
  }, [
@@ -616,7 +610,7 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
616
610
  root(props) {
617
611
  invariant(this._rootId === "root", "Root group can only be at the top level", {
618
612
  F: __dxlog_file2,
619
- L: 41,
613
+ L: 61,
620
614
  S: this,
621
615
  A: [
622
616
  "this._rootId === 'root'",
@@ -625,7 +619,7 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
625
619
  });
626
620
  invariant(this._data.nodes.find((node) => node.id === "root") === void 0, "Root group can only be created once", {
627
621
  F: __dxlog_file2,
628
- L: 42,
622
+ L: 62,
629
623
  S: this,
630
624
  A: [
631
625
  "this._data.nodes.find((node) => node.id === 'root') === undefined",
@@ -657,13 +651,20 @@ var MenuBuilderImpl = class _MenuBuilderImpl {
657
651
  cb(new _MenuBuilderImpl(this._data, id));
658
652
  return this;
659
653
  }
660
- subgraph(subgraph) {
661
- this._data.nodes.push(...subgraph.nodes);
662
- this._data.edges.push(...subgraph.edges);
654
+ subgraph(subgraphOrCb) {
655
+ if (!subgraphOrCb) {
656
+ return this;
657
+ }
658
+ if (typeof subgraphOrCb === "function") {
659
+ subgraphOrCb(new _MenuBuilderImpl(this._data, this._rootId));
660
+ } else {
661
+ this._data.nodes.push(...subgraphOrCb.nodes);
662
+ this._data.edges.push(...subgraphOrCb.edges);
663
+ }
663
664
  return this;
664
665
  }
665
- separator(id, variant = "gap") {
666
- id ??= `separator-${++this._separatorCount}`;
666
+ separator(variant = "gap") {
667
+ const id = `separator-${++this._separatorCount}`;
667
668
  this._data.nodes.push({
668
669
  id,
669
670
  type: MenuSeparatorType,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/components/Menu.tsx", "../../../src/util.ts", "../../../src/components/ActionLabel.tsx", "../../../src/translations.ts", "../../../src/components/ToolbarMenu.tsx", "../../../src/types.ts", "../../../src/components/DropdownMenu.tsx", "../../../src/hooks/useMenuActions.ts", "../../../src/builder.ts"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { Atom, RegistryContext, useAtomValue } from '@effect-atom/atom-react';\nimport { type Scope, createContextScope } from '@radix-ui/react-context';\nimport { useControllableState } from '@radix-ui/react-use-controllable-state';\nimport React, { type MouseEvent, type PropsWithChildren, useCallback, useContext, useMemo } from 'react';\n\nimport { log } from '@dxos/log';\nimport { type DropdownMenuRootProps, Icon, DropdownMenu as NaturalDropdownMenu } from '@dxos/react-ui';\n\nimport {\n type AddMenuItemsProps,\n type MenuAction,\n type MenuContextValue,\n type MenuItem,\n type MenuItemGroup,\n type MenuItems,\n type MenuItemsAccessor,\n type MenuItemsMap,\n} from '../types';\nimport { executeMenuAction } from '../util';\n\nimport { ActionLabel } from './ActionLabel';\nimport { ToolbarMenu } from './ToolbarMenu';\n\n//\n// Scoped context.\n//\n\ntype MenuScopedProps<P> = P & { __menuScope?: Scope };\n\nconst MENU_NAME = 'Menu';\n\nconst [createMenuContext, createMenuScope] = createContextScope(MENU_NAME, []);\n\nconst nullItemsAtom = Atom.make<MenuItem[] | null>(null);\nconst defaultItemsAccessor: MenuItemsAccessor = () => nullItemsAtom;\n\nconst menuContextDefaults: MenuContextValue = {\n iconSize: 5,\n items: defaultItemsAccessor,\n onAction: undefined,\n menuItemsAtom: Atom.make<MenuItemsMap>(new Map()),\n addMenuItems: () => {},\n removeMenuItems: () => {},\n};\n\nconst [MenuContextProvider, useMenuScoped] = createMenuContext<MenuContextValue>(MENU_NAME, menuContextDefaults);\n\nconst useMenuScope = createMenuScope();\n\n//\n// Dropdown context (internal) — allows Menu.Content to close the parent dropdown.\n//\n\ntype MenuDropdownContextValue = {\n closeMenu: () => void;\n caller?: string;\n};\n\nconst MenuDropdownContext = React.createContext<MenuDropdownContextValue>({\n closeMenu: () => {},\n});\n\n//\n// MenuProvider (internal) — the context provider used by Menu.Root.\n//\n\nconst DEFAULT_PRIORITY = 100;\n\nconst sortMenuItems = (items: MenuItems[]) =>\n [...items].sort((a, b) => {\n if (a.priority !== b.priority) {\n return a.priority - b.priority;\n }\n return a.id.localeCompare(b.id);\n });\n\ntype MenuProviderProps = PropsWithChildren<Partial<MenuContextValue>>;\n\nconst MenuProvider = ({\n children,\n items = menuContextDefaults.items,\n iconSize = menuContextDefaults.iconSize,\n attendableId,\n alwaysActive,\n onAction,\n}: MenuProviderProps) => {\n const { scope } = useMenuScope(undefined);\n const registry = useContext(RegistryContext);\n const menuItemsAtom = useMemo(() => Atom.make<MenuItemsMap>(new Map()).pipe(Atom.keepAlive), []);\n\n const addMenuItems = useCallback(\n (props: AddMenuItemsProps) => {\n const priority = props.priority ?? DEFAULT_PRIORITY;\n const prev = registry.get(menuItemsAtom);\n const next = new Map(prev);\n next.set(props.id, { ...props, priority });\n registry.set(menuItemsAtom, next);\n },\n [registry, menuItemsAtom],\n );\n\n const removeMenuItems = useCallback(\n (id: string) => {\n const prev = registry.get(menuItemsAtom);\n const next = new Map(prev);\n next.delete(id);\n registry.set(menuItemsAtom, next);\n },\n [registry, menuItemsAtom],\n );\n\n return (\n <MenuContextProvider\n items={items}\n iconSize={iconSize}\n attendableId={attendableId}\n alwaysActive={alwaysActive}\n menuItemsAtom={menuItemsAtom}\n addMenuItems={addMenuItems}\n removeMenuItems={removeMenuItems}\n onAction={onAction}\n scope={scope}\n >\n {children}\n </MenuContextProvider>\n );\n};\n\n//\n// Item resolution.\n//\n\nconst resolveItems = (\n baseItems: MenuItem[] | null,\n group: MenuItemGroup | undefined,\n entries: ReadonlyMap<string, MenuItems>,\n): MenuItem[] | null => {\n const applicable = [...entries.values()].filter((entry) => !entry.groupFilter || entry.groupFilter(group));\n if (applicable.length === 0) {\n return baseItems;\n }\n\n const sorted = sortMenuItems(applicable);\n\n const replacements = sorted.filter((entry) => entry.mode === 'replacement');\n if (replacements.length > 0) {\n if (replacements.length > 1) {\n log.warn('multiple replacement entries found', {\n ids: replacements.map((r) => r.id).join(', '),\n using: replacements[0].id,\n });\n }\n return replacements[0].items;\n }\n\n const additive = sorted.filter((entry) => entry.mode === 'additive');\n const additiveItems = additive.flatMap((entry) => entry.items);\n\n if (!baseItems || baseItems.length === 0) {\n return additiveItems.length > 0 ? additiveItems : null;\n }\n\n return [...baseItems, ...additiveItems];\n};\n\n//\n// Public hooks.\n//\n\nconst useMenuItems = (\n group?: MenuItemGroup,\n propsItems?: MenuItem[],\n consumerName: string = 'useMenuItemConsumer',\n __menuScope?: Scope,\n) => {\n const { items, menuItemsAtom } = useMenuScoped(consumerName, __menuScope);\n const groupItems = useAtomValue(items(group));\n const entries = useAtomValue(menuItemsAtom) ?? new Map();\n\n const baseItems = useMemo(() => propsItems ?? groupItems ?? null, [propsItems, groupItems]);\n\n const resolved = useMemo(\n () => resolveItems(baseItems, group, entries as ReadonlyMap<string, MenuItems>),\n [baseItems, group, entries],\n );\n\n return resolved ?? undefined;\n};\n\n/** Returns the menu context without Radix scope. */\nconst useMenu = (consumerName: string): MenuContextValue => {\n return useMenuScoped(consumerName, undefined);\n};\n\n//\n// Menu.Root\n//\n\ntype MenuRootProps = PropsWithChildren<\n Omit<MenuProviderProps, 'children'> &\n Pick<DropdownMenuRootProps, 'open' | 'defaultOpen' | 'onOpenChange'> & {\n /** Identifies the component that owns this menu (passed to action handlers). */\n caller?: string;\n }\n>;\n\n/**\n * Menu context boundary.\n *\n * Provides the menu context (action dispatch, contribution registry, icon size, etc.)\n * and an optional dropdown root for use with `Menu.Trigger` + `Menu.Content`.\n */\nconst MenuRoot = ({ children, open, defaultOpen, onOpenChange, caller, ...props }: MenuRootProps) => {\n const [menuOpen, setMenuOpen] = useControllableState({\n prop: open,\n defaultProp: defaultOpen,\n onChange: onOpenChange,\n });\n\n const closeMenu = useCallback(() => setMenuOpen(false), [setMenuOpen]);\n\n return (\n <MenuProvider {...props}>\n <MenuDropdownContext.Provider value={{ closeMenu, caller }}>\n <NaturalDropdownMenu.Root open={menuOpen} onOpenChange={setMenuOpen}>\n {children}\n </NaturalDropdownMenu.Root>\n </MenuDropdownContext.Provider>\n </MenuProvider>\n );\n};\n\n//\n// Menu.Content\n//\n\ntype MenuContentProps = {\n group?: MenuItemGroup;\n items?: MenuItem[];\n caller?: string;\n};\n\nconst MenuContentItem = ({\n item,\n onClick,\n __menuScope,\n}: MenuScopedProps<{\n item: MenuItem;\n onClick: (action: MenuAction, event: MouseEvent) => void;\n}>) => {\n const action = item as MenuAction;\n const handleClick = useCallback((event: MouseEvent) => onClick(action, event), [action, onClick]);\n const { iconSize } = useMenuScoped('MenuContentItem', __menuScope);\n return (\n <NaturalDropdownMenu.Item\n onClick={handleClick}\n classNames='gap-2'\n disabled={action.properties?.disabled}\n {...(action.properties?.testId && { 'data-testid': action.properties.testId })}\n >\n {action.properties?.icon && <Icon icon={action.properties!.icon} size={iconSize} />}\n <ActionLabel action={action} />\n </NaturalDropdownMenu.Item>\n );\n};\n\n/**\n * Renders the dropdown menu portal, content, and graph-backed items.\n *\n * Must be a descendant of `Menu.Root`. Reads items via `useMenuItems` from the\n * nearest menu context, with optional `group`/`items` prop overrides.\n */\nconst MenuContent = ({\n group,\n items: propsItems,\n caller: callerOverride,\n __menuScope,\n}: MenuScopedProps<MenuContentProps>) => {\n const { closeMenu, caller: contextCaller } = useContext(MenuDropdownContext);\n const caller = callerOverride ?? contextCaller;\n const { onAction } = useMenuScoped('MenuContent', __menuScope);\n const resolvedItems = useMenuItems(group, propsItems, 'MenuContent', __menuScope);\n\n const handleActionClick = useCallback(\n (action: MenuAction, event: MouseEvent) => {\n if (action.properties?.disabled) {\n return;\n }\n event.stopPropagation();\n closeMenu();\n if (onAction) {\n onAction(action, { parent: group, caller });\n } else {\n void executeMenuAction(action, { parent: group, caller });\n }\n },\n [group, caller, onAction, closeMenu],\n );\n\n return (\n <NaturalDropdownMenu.Portal>\n <NaturalDropdownMenu.Content>\n <NaturalDropdownMenu.Viewport>\n {resolvedItems?.map((item) => (\n <MenuContentItem key={item.id} item={item} onClick={handleActionClick} />\n ))}\n </NaturalDropdownMenu.Viewport>\n <NaturalDropdownMenu.Arrow />\n </NaturalDropdownMenu.Content>\n </NaturalDropdownMenu.Portal>\n );\n};\n\n//\n// Namespace.\n//\n\n/**\n * Primary namespace export for the menu system.\n *\n * - `Menu.Root` — context boundary (replaces `MenuProvider`); also provides a dropdown root.\n * - `Menu.Trigger` / `Menu.VirtualTrigger` — dropdown trigger (use with `Menu.Content`).\n * - `Menu.Content` — renders graph-backed dropdown items inside a portal.\n * - `Menu.Toolbar` — flat toolbar component with graph-backed item rendering.\n */\nconst Menu = {\n Root: MenuRoot,\n Trigger: NaturalDropdownMenu.Trigger,\n Content: MenuContent,\n VirtualTrigger: NaturalDropdownMenu.VirtualTrigger,\n Toolbar: ToolbarMenu,\n};\n\nexport { Menu, menuContextDefaults, useMenu, useMenuItems, useMenuScoped };\n\nexport type { MenuContentProps, MenuRootProps, MenuScopedProps };\n\nexport type {\n ToolbarMenuProps,\n ToolbarMenuActionGroupProperties,\n ToolbarMenuActionGroupProps,\n ToolbarMenuActionProps,\n ToolbarMenuDropdownMenuActionGroup,\n ToolbarMenuToggleGroupActionGroup,\n} from './ToolbarMenu';\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Node } from '@dxos/app-graph';\nimport { runAndForwardErrors } from '@dxos/effect';\nimport { type MenuActionProperties, type MenuItemGroupProperties } from '@dxos/ui-types';\nimport { getHostPlatform } from '@dxos/util';\n\nimport { type MenuAction, type MenuItemGroup, type MenuSeparator } from './types';\n\n/**\n * Execute a menu action's Effect with its captured context.\n * This provides the `_actionContext` layer if available.\n */\nexport const executeMenuAction = async (action: MenuAction, params: Node.InvokeProps = {}): Promise<void> => {\n let effect = action.data(params);\n\n // Provide captured action context if available.\n if (action._actionContext) {\n effect = effect.pipe(Effect.provide(action._actionContext));\n }\n\n await runAndForwardErrors(effect);\n};\n\nexport const getShortcut = (action: Node.ActionLike) => {\n return typeof action.properties?.keyBinding === 'string'\n ? action.properties.keyBinding\n : action.properties?.keyBinding?.[getHostPlatform()];\n};\n\nexport const fallbackIcon = 'ph--placeholder--regular';\n\nexport const createMenuAction = <P extends {} = {}>(\n id: string,\n invoke: () => void,\n properties: P & MenuActionProperties,\n) =>\n ({\n id,\n type: Node.ActionType,\n properties,\n data: () => Effect.sync(invoke),\n }) satisfies MenuAction;\n\nexport const createMenuItemGroup = <\n P extends MenuItemGroupProperties = MenuItemGroupProperties & Partial<MenuActionProperties>,\n>(\n id: string,\n properties: P,\n) =>\n ({\n id,\n type: Node.ActionGroupType,\n properties,\n data: Node.actionGroupSymbol,\n }) satisfies MenuItemGroup;\n\nexport const createGapSeparator = (id: string = 'gap', source: string = 'root') => ({\n nodes: [\n {\n id,\n type: '@dxos/react-ui-toolbar/separator',\n properties: { variant: 'gap' },\n data: undefined as never,\n } satisfies MenuSeparator,\n ],\n edges: [\n {\n source,\n target: id,\n relation: 'child' as const,\n },\n ],\n});\n\nexport const createLineSeparator = (id: string = 'line', source: string = 'root') => ({\n nodes: [\n {\n id,\n type: '@dxos/react-ui-toolbar/separator',\n properties: { variant: 'line' },\n data: undefined as never,\n } satisfies MenuSeparator,\n ],\n edges: [{ source, target: id, relation: 'child' as const }],\n});\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport React from 'react';\n\nimport { type Node } from '@dxos/app-graph';\nimport { keySymbols } from '@dxos/keyboard';\nimport { type TFunction, toLocalizedString, useTranslation } from '@dxos/react-ui';\nimport { mx } from '@dxos/ui-theme';\nimport { type MenuActionProperties } from '@dxos/ui-types';\n\nimport { translationKey } from '../translations';\nimport { getShortcut } from '../util';\n\ntype Action = Node.Action<MenuActionProperties> | Node.ActionGroup<Omit<MenuActionProperties, 'variant'>>;\n\nexport const ActionLabel = ({ action }: { action: Action }) => {\n const { t } = useTranslation(translationKey);\n const shortcut = getShortcut(action);\n return (\n <>\n <span className='grow truncate'>{toLocalizedString(action.properties!.label, t)}</span>\n {shortcut && <span className={mx('shrink-0', 'text-description')}>{keySymbols(shortcut).join('')}</span>}\n </>\n );\n};\n\nexport const actionLabel = (action: Action, t: TFunction) => {\n const shortcut = getShortcut(action);\n return `${toLocalizedString(action.properties!.label, t)}${shortcut ? ` (${keySymbols(shortcut).join('')})` : ''}`;\n};\n", "//\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", "//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { Fragment, useCallback } from 'react';\n\nimport { Icon, Toolbar as NaturalToolbar, type ToolbarRootProps, useTranslation } from '@dxos/react-ui';\nimport { useAttention } from '@dxos/react-ui-attention';\nimport { mx, textBlockWidth, toolbarLayout } from '@dxos/ui-theme';\nimport { type MenuActionProperties } from '@dxos/ui-types';\n\nimport { translationKey } from '../translations';\nimport {\n type MenuAction,\n type MenuItem,\n type MenuItemGroup,\n type MenuMultipleSelectActionGroup,\n type MenuSingleSelectActionGroup,\n isMenuGroup,\n isSeparator,\n} from '../types';\nimport { executeMenuAction } from '../util';\n\nimport { ActionLabel, actionLabel } from './ActionLabel';\nimport { DropdownMenu } from './DropdownMenu';\nimport { type MenuScopedProps, useMenuItems, useMenuScoped } from './Menu';\n\nexport type ToolbarMenuDropdownMenuActionGroup = Omit<MenuActionProperties, 'variant' | 'icon'> & {\n variant: 'dropdownMenu';\n icon: string;\n applyActive?: boolean;\n};\n\nexport type ToolbarMenuToggleGroupActionGroup = Omit<MenuActionProperties, 'variant'> & {\n variant: 'toggleGroup';\n};\n\nexport type ToolbarMenuActionGroupProperties = (\n | ToolbarMenuDropdownMenuActionGroup\n | ToolbarMenuToggleGroupActionGroup\n) &\n (MenuSingleSelectActionGroup | MenuMultipleSelectActionGroup);\n\nexport type ToolbarMenuProps = ToolbarRootProps & { textBlockWidth?: boolean };\n\nexport type ToolbarMenuActionGroupProps = {\n group: MenuItemGroup<ToolbarMenuActionGroupProperties>;\n items?: MenuItem[];\n};\n\nexport type ToolbarMenuActionProps = {\n group: MenuItemGroup<ToolbarMenuActionGroupProperties>;\n action: MenuAction;\n};\n\nconst ActionToolbarItem = ({ action, __menuScope }: MenuScopedProps<{ action: MenuAction }>) => {\n const { iconSize, onAction } = useMenuScoped('ActionToolbarItem', __menuScope);\n const { t } = useTranslation(translationKey);\n\n const { icon, iconOnly = true, disabled, testId, hidden, classNames, variant } = action.properties;\n const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;\n const rootProps = icon\n ? { icon, size: iconSize, iconOnly, label: actionLabel(action, t) }\n : { children: <ActionLabel action={action} /> };\n\n const handleClick = useCallback(() => {\n if (onAction) {\n onAction(action, {});\n } else {\n void executeMenuAction(action);\n }\n }, [action, onAction]);\n\n return hidden ? null : (\n <Root\n key={action.id}\n disabled={disabled}\n onClick={handleClick}\n variant={variant ?? 'ghost'}\n {...(testId && { 'data-testid': testId })}\n {...(rootProps as any)}\n classNames={classNames}\n />\n );\n};\n\nconst DropdownMenuToolbarItem = ({\n group,\n items: propsItems,\n __menuScope,\n}: MenuScopedProps<ToolbarMenuActionGroupProps>) => {\n const { iconOnly, disabled, testId } = group.properties;\n const { t } = useTranslation(translationKey);\n const { iconSize } = useMenuScoped('DropdownMenuToolbarItem', __menuScope);\n const items = useMenuItems(group, propsItems, 'DropdownMenuToolbarItem', __menuScope);\n const activeItem = items?.find((item) => !!(item as MenuAction).properties.checked);\n const icon =\n ((group.properties as any).applyActive &&\n // TODO(thure): Handle other menu item types.\n (activeItem as MenuAction)?.properties.icon) ||\n group.properties.icon;\n const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;\n const labelAction = (group.properties as any).applyActive && activeItem ? (activeItem as MenuAction) : group;\n const rootProps = icon\n ? {\n icon,\n size: iconSize,\n iconOnly,\n label: actionLabel(labelAction, t),\n caretDown: true,\n }\n : {\n children: (\n <>\n <ActionLabel action={labelAction} />\n <Icon size={3} icon='ph--caret-down--bold' classNames='ms-1' />\n </>\n ),\n };\n\n return (\n <DropdownMenu.Root group={group} items={items}>\n <DropdownMenu.Trigger asChild>\n <Root variant='ghost' disabled={disabled} {...(rootProps as any)} {...(testId && { 'data-testid': testId })} />\n </DropdownMenu.Trigger>\n </DropdownMenu.Root>\n );\n};\n\nconst ToggleGroupItem = ({ group, action, __menuScope }: MenuScopedProps<ToolbarMenuActionProps>) => {\n const { iconSize, onAction } = useMenuScoped('ToggleGroupItem', __menuScope);\n const { t } = useTranslation(translationKey);\n const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;\n const handleClick = useCallback(() => {\n if (onAction) {\n onAction(action, { parent: group });\n } else {\n void executeMenuAction(action, { parent: group });\n }\n }, [action, group, onAction]);\n const Root = icon ? NaturalToolbar.ToggleGroupIconItem : NaturalToolbar.ToggleGroupItem;\n const rootProps = icon\n ? { icon, size: iconSize, iconOnly, label: actionLabel(action, t) }\n : { children: <ActionLabel action={action} /> };\n\n return hidden ? null : (\n <Root\n key={action.id}\n value={action.id as any}\n disabled={disabled}\n onClick={handleClick}\n variant='ghost'\n {...(testId && { 'data-testid': testId })}\n {...(rootProps as any)}\n classNames={classNames}\n />\n );\n};\n\nconst ToggleGroupToolbarItem = ({\n group,\n items: propsItems,\n __menuScope,\n}: MenuScopedProps<ToolbarMenuActionGroupProps>) => {\n const items = useMenuItems(group, propsItems, 'ToggleGroupToolbarItem', __menuScope);\n const { selectCardinality, value } = group.properties;\n\n return (\n // TODO(thure): Fix.\n // @ts-ignore\n <NaturalToolbar.ToggleGroup type={selectCardinality} value={value}>\n {\n // TODO(thure): Handle other menu item types.\n (items as MenuAction[]).map((action) => (\n <ToggleGroupItem key={action.id} group={group} action={action} />\n ))\n }\n </NaturalToolbar.ToggleGroup>\n );\n};\n\n// TODO(burdon): Reconcile with react-ui/Toolbar (incl. textBlockWidth)\nexport const ToolbarMenu = ({\n __menuScope,\n classNames,\n textBlockWidth: textBlockWidthProp,\n ...props\n}: MenuScopedProps<ToolbarMenuProps>) => {\n const items = useMenuItems(undefined, undefined, 'ToolbarMenu', __menuScope);\n const { attendableId, alwaysActive } = useMenuScoped('ToolbarMenu', __menuScope);\n const { hasAttention } = useAttention(attendableId);\n const InnerRoot = textBlockWidthProp ? 'div' : Fragment;\n const innerRootProps = textBlockWidthProp\n ? { role: 'none', className: mx(textBlockWidth, toolbarLayout, 'h-full') }\n : {};\n\n return (\n <NaturalToolbar.Root\n {...props}\n classNames={[attendableId, classNames]}\n disabled={!alwaysActive && !hasAttention}\n layoutManaged={textBlockWidthProp}\n >\n <InnerRoot {...innerRootProps}>\n {items?.map((item: MenuItem, i: number) => (\n <ToolbarMenuItem key={item.id ?? i} item={item} />\n ))}\n </InnerRoot>\n </NaturalToolbar.Root>\n );\n};\n\nconst ToolbarMenuItem = ({ item }: { item: MenuItem }) => {\n if (isSeparator(item)) {\n return <NaturalToolbar.Separator variant={item.properties.variant} />;\n }\n\n // TODO(thure): Figure out type narrowing that doesn’t require so much use of `as`.\n if (isMenuGroup(item)) {\n if (item.properties.variant === 'dropdownMenu') {\n return <DropdownMenuToolbarItem group={item as MenuItemGroup<ToolbarMenuActionGroupProperties>} />;\n } else {\n return <ToggleGroupToolbarItem group={item as MenuItemGroup<ToolbarMenuActionGroupProperties>} />;\n }\n }\n\n return <ActionToolbarItem action={item as MenuAction} />;\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Atom } from '@effect-atom/atom-react';\n\nimport { Node } from '@dxos/app-graph';\nimport { type IconButtonProps, type ToolbarSeparatorProps } from '@dxos/react-ui';\nimport { type MenuActionProperties } from '@dxos/ui-types';\n\nexport type MenuAction<P extends {} = {}> = Node.Action<P & MenuActionProperties>;\n\nexport const MenuSeparatorType = '@dxos/react-ui-toolbar/separator' as const;\n\nexport type MenuSeparator = Node.Node<never, Pick<ToolbarSeparatorProps, 'variant'>> & {\n type: typeof MenuSeparatorType;\n};\n\nexport const isSeparator = (node: Node.Node): node is MenuSeparator => node.type === MenuSeparatorType;\n\nexport type MenuSingleSelectActionGroup = { selectCardinality: 'single'; value: string };\nexport type MenuMultipleSelectActionGroup = { selectCardinality: 'multiple'; value: string[] };\n\nexport type MenuItemGroup<P extends Record<string, any> = Record<string, any>> = Node.ActionGroup<P>;\n\nexport const isMenuGroup = (node: Node.Node): node is MenuItemGroup => node.type === Node.ActionGroupType;\n\nexport type MenuItem = MenuSeparator | MenuAction | MenuItemGroup;\n\n/** Atom-family-style accessor: returns an atom of items for a given group (or root when undefined). */\nexport type MenuItemsAccessor = (group?: MenuItemGroup) => Atom.Atom<MenuItem[] | null>;\n\nexport type ActionExecutor = (action: MenuAction, params: Node.InvokeProps) => void;\n\nexport type MenuItemsMode = 'additive' | 'replacement';\n\nexport type AddMenuItemsProps = {\n id: string;\n mode: MenuItemsMode;\n priority?: number;\n items: MenuItem[];\n groupFilter?: (group?: MenuItemGroup) => boolean;\n};\n\nexport type MenuItems = Omit<AddMenuItemsProps, 'priority'> & { priority: number };\nexport type MenuItemsMap = Map<string, MenuItems>;\n\nexport type MenuContextValue = {\n /** Atom-family accessor for base menu items, keyed by group (similar to Tree model). */\n items: MenuItemsAccessor;\n iconSize: IconButtonProps['size'];\n attendableId?: string;\n /** If true, the menu is always active regardless of attention state. */\n alwaysActive?: boolean;\n /** Atom holding the current set of imperatively added menu items. */\n menuItemsAtom: Atom.Atom<MenuItemsMap>;\n /** Imperatively add menu items to the nearest MenuProvider. */\n addMenuItems: (props: AddMenuItemsProps) => void;\n /** Remove previously added menu items by id. */\n removeMenuItems: (id: string) => void;\n /** Optional action executor. If provided, will be used instead of default execution. */\n onAction?: ActionExecutor;\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { useControllableState } from '@radix-ui/react-use-controllable-state';\nimport React, { type MouseEvent, useCallback } from 'react';\n\nimport { type DropdownMenuRootProps, Icon, DropdownMenu as NaturalDropdownMenu } from '@dxos/react-ui';\n\nimport { type MenuAction, type MenuItem, type MenuItemGroup } from '../types';\nimport { executeMenuAction } from '../util';\n\nimport { ActionLabel } from './ActionLabel';\nimport { type MenuScopedProps, useMenuItems, useMenuScoped } from './Menu';\n\nexport type DropdownMenuProps = DropdownMenuRootProps & {\n group?: MenuItemGroup;\n items?: MenuItem[];\n caller?: string;\n};\n\nconst DropdownMenuItem = ({\n item,\n onClick,\n __menuScope,\n}: MenuScopedProps<{\n item: MenuItem;\n onClick: (action: MenuAction, event: MouseEvent) => void;\n}>) => {\n // TODO(thure): handle other items.\n const action = item as MenuAction;\n const handleClick = useCallback((event: MouseEvent) => onClick(action, event), [action, onClick]);\n const { iconSize } = useMenuScoped('DropdownMenuItem', __menuScope);\n return (\n <NaturalDropdownMenu.Item\n onClick={handleClick}\n classNames='gap-2'\n disabled={action.properties?.disabled}\n {...(action.properties?.testId && { 'data-testid': action.properties.testId })}\n >\n {action.properties?.icon && <Icon icon={action.properties!.icon} size={iconSize} />}\n <ActionLabel action={action} />\n </NaturalDropdownMenu.Item>\n );\n};\n\nconst DropdownMenuRoot = ({\n items: propsItems,\n group,\n open,\n defaultOpen,\n onOpenChange,\n caller,\n children,\n __menuScope,\n ...naturalProps\n}: MenuScopedProps<DropdownMenuProps>) => {\n const [optionsMenuOpen, setOptionsMenuOpen] = useControllableState({\n prop: open,\n defaultProp: defaultOpen,\n onChange: onOpenChange,\n });\n\n const { onAction } = useMenuScoped('DropdownMenuRoot', __menuScope);\n\n const handleActionClick = useCallback(\n (action: MenuAction, event: MouseEvent) => {\n if (action.properties?.disabled) {\n return;\n }\n event.stopPropagation();\n // TODO(thure): Why does Dialog's modal-ness cause issues if we don't explicitly close the menu here?\n setOptionsMenuOpen(false);\n if (onAction) {\n onAction(action, { parent: group, caller });\n } else {\n void executeMenuAction(action, { parent: group, caller });\n }\n },\n [group, caller, onAction],\n );\n\n const items = useMenuItems(group, propsItems);\n\n return (\n <NaturalDropdownMenu.Root open={optionsMenuOpen} onOpenChange={setOptionsMenuOpen} {...naturalProps}>\n {children}\n <NaturalDropdownMenu.Portal>\n <NaturalDropdownMenu.Content>\n <NaturalDropdownMenu.Viewport>\n {items?.map((item) => (\n <DropdownMenuItem key={item.id} item={item} onClick={handleActionClick} />\n ))}\n </NaturalDropdownMenu.Viewport>\n <NaturalDropdownMenu.Arrow />\n </NaturalDropdownMenu.Content>\n </NaturalDropdownMenu.Portal>\n </NaturalDropdownMenu.Root>\n );\n};\n\nexport const DropdownMenu = {\n Root: DropdownMenuRoot,\n Trigger: NaturalDropdownMenu.Trigger,\n VirtualTrigger: NaturalDropdownMenu.VirtualTrigger,\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Atom, RegistryContext, useAtomValue } from '@effect-atom/atom-react';\nimport { useCallback, useContext, useRef } from 'react';\n\nimport { Graph, Node } from '@dxos/app-graph';\n\nimport { type MenuItem, type MenuItemGroup, type MenuItemsAccessor } from '../types';\n\nexport type ActionGraphNodes = Node.NodeArg<any>[];\nexport type ActionGraphEdges = Graph.Edge[];\nexport type ActionGraphProps = {\n nodes: ActionGraphNodes;\n edges: ActionGraphEdges;\n};\n\nexport type MenuActions = {\n items: MenuItemsAccessor;\n};\n\nexport const useMenuActions = (props: Atom.Atom<ActionGraphProps>): MenuActions => {\n const registry = useContext(RegistryContext);\n const menuGraphProps = useAtomValue(props);\n\n // Create a new graph whenever props change to preserve correct order.\n // (Graph.addEdges appends rather than replaces, which breaks ordering on updates.)\n const graphRef = useRef<{ graph: Graph.WritableGraph; props: ActionGraphProps } | null>(null);\n if (!graphRef.current || graphRef.current.props !== menuGraphProps) {\n const newGraph = Graph.make({ registry });\n newGraph.pipe(Graph.addNodes(menuGraphProps.nodes as Node.NodeArg<any>[]), Graph.addEdges(menuGraphProps.edges));\n graphRef.current = { graph: newGraph, props: menuGraphProps };\n }\n\n const graph = graphRef.current.graph;\n\n const items: MenuItemsAccessor = useCallback(\n (group?: MenuItemGroup) => {\n // TODO(wittjosiah): Migrate to using action relation instead of child.\n return graph.connections(group?.id || Node.RootId, 'child') as Atom.Atom<MenuItem[] | null>;\n },\n [graph],\n );\n\n return { items };\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\nimport type { MenuActionProperties, MenuItemGroupProperties } from '@dxos/ui-types';\n\nimport type { ActionGraphProps } from './hooks';\nimport { MenuSeparatorType } from './types';\nimport { createMenuAction, createMenuItemGroup } from './util';\n\nexport interface ActionGroupBuilder {\n /** Add an action node as a child of the current group. */\n action<P extends {} = {}>(id: string, props: P & MenuActionProperties, invoke: () => void): this;\n /** Add a nested action group. */\n group<P extends {} = {}>(\n id: string,\n props: P & MenuItemGroupProperties,\n cb: (builder: ActionGroupBuilder) => void,\n ): this;\n /** Merge pre-built nodes and edges into this builder. */\n subgraph(subgraph: ActionGraphProps): this;\n /** Add a separator. */\n separator(id?: string, variant?: 'gap' | 'line'): this;\n}\n\nexport interface MenuBuilder extends ActionGroupBuilder {\n root(props: MenuItemGroupProperties): this;\n build(): ActionGraphProps;\n}\n\nclass MenuBuilderImpl implements MenuBuilder {\n private _separatorCount = 0;\n\n constructor(\n private readonly _data: ActionGraphProps,\n private readonly _rootId: string,\n ) {}\n\n root(props: MenuItemGroupProperties): this {\n invariant(this._rootId === 'root', 'Root group can only be at the top level');\n invariant(this._data.nodes.find((node) => node.id === 'root') === undefined, 'Root group can only be created once');\n this._data.nodes.push(createMenuItemGroup('root', props));\n return this;\n }\n\n build(): ActionGraphProps {\n return this._data;\n }\n\n action<P extends {} = {}>(id: string, props: P & MenuActionProperties, invoke: () => void): this {\n this._data.nodes.push(createMenuAction(id, invoke, props));\n this._data.edges.push({ source: this._rootId, target: id, relation: 'child' });\n return this;\n }\n\n group<P extends {} = {}>(\n id: string,\n props: P & MenuItemGroupProperties,\n cb: (builder: ActionGroupBuilder) => void,\n ): this {\n this._data.nodes.push(createMenuItemGroup(id, props));\n this._data.edges.push({ source: this._rootId, target: id, relation: 'child' });\n cb(new MenuBuilderImpl(this._data, id));\n return this;\n }\n\n subgraph(subgraph: ActionGraphProps): this {\n this._data.nodes.push(...subgraph.nodes);\n this._data.edges.push(...subgraph.edges);\n return this;\n }\n\n separator(id?: string, variant: 'gap' | 'line' = 'gap'): this {\n id ??= `separator-${++this._separatorCount}`;\n this._data.nodes.push({\n id,\n type: MenuSeparatorType,\n properties: { variant },\n data: undefined as never,\n });\n this._data.edges.push({ source: this._rootId, target: id, relation: 'child' });\n return this;\n }\n}\n\nexport const MenuBuilder = Object.freeze({\n make: (): MenuBuilder => new MenuBuilderImpl({ nodes: [], edges: [] }, 'root'),\n});\n"],
5
- "mappings": "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",
6
- "names": ["Atom", "RegistryContext", "useAtomValue", "createContextScope", "useControllableState", "React", "useCallback", "useContext", "useMemo", "log", "Icon", "DropdownMenu", "NaturalDropdownMenu", "Effect", "Node", "runAndForwardErrors", "getHostPlatform", "executeMenuAction", "action", "params", "effect", "data", "_actionContext", "pipe", "provide", "runAndForwardErrors", "getShortcut", "properties", "keyBinding", "getHostPlatform", "createMenuAction", "id", "invoke", "properties", "type", "Node", "ActionType", "data", "sync", "createMenuItemGroup", "ActionGroupType", "actionGroupSymbol", "createGapSeparator", "source", "nodes", "variant", "undefined", "edges", "target", "relation", "createLineSeparator", "React", "keySymbols", "toLocalizedString", "useTranslation", "mx", "translationKey", "translations", "ActionLabel", "action", "t", "useTranslation", "translationKey", "shortcut", "getShortcut", "span", "className", "toLocalizedString", "properties", "label", "mx", "keySymbols", "join", "actionLabel", "React", "Fragment", "useCallback", "Icon", "Toolbar", "NaturalToolbar", "useTranslation", "useAttention", "mx", "textBlockWidth", "toolbarLayout", "Node", "MenuSeparatorType", "isSeparator", "node", "type", "isMenuGroup", "Node", "ActionGroupType", "useControllableState", "React", "useCallback", "Icon", "DropdownMenu", "NaturalDropdownMenu", "DropdownMenuItem", "item", "onClick", "__menuScope", "action", "handleClick", "useCallback", "event", "iconSize", "useMenuScoped", "React", "NaturalDropdownMenu", "Item", "classNames", "disabled", "properties", "testId", "icon", "Icon", "size", "ActionLabel", "DropdownMenuRoot", "items", "propsItems", "group", "open", "defaultOpen", "onOpenChange", "caller", "children", "naturalProps", "optionsMenuOpen", "setOptionsMenuOpen", "useControllableState", "prop", "defaultProp", "onChange", "onAction", "handleActionClick", "stopPropagation", "parent", "executeMenuAction", "useMenuItems", "Root", "Portal", "Content", "Viewport", "map", "key", "id", "Arrow", "DropdownMenu", "Trigger", "VirtualTrigger", "ActionToolbarItem", "action", "__menuScope", "iconSize", "onAction", "useMenuScoped", "t", "useTranslation", "translationKey", "icon", "iconOnly", "disabled", "testId", "hidden", "classNames", "variant", "properties", "Root", "NaturalToolbar", "IconButton", "Button", "rootProps", "size", "label", "actionLabel", "children", "React", "ActionLabel", "handleClick", "useCallback", "executeMenuAction", "key", "id", "onClick", "DropdownMenuToolbarItem", "group", "items", "propsItems", "useMenuItems", "activeItem", "find", "item", "checked", "applyActive", "labelAction", "caretDown", "Icon", "DropdownMenu", "Trigger", "asChild", "ToggleGroupItem", "parent", "ToggleGroupIconItem", "value", "ToggleGroupToolbarItem", "selectCardinality", "ToggleGroup", "type", "map", "ToolbarMenu", "textBlockWidth", "textBlockWidthProp", "props", "undefined", "attendableId", "alwaysActive", "hasAttention", "useAttention", "InnerRoot", "Fragment", "innerRootProps", "role", "className", "mx", "toolbarLayout", "layoutManaged", "i", "ToolbarMenuItem", "isSeparator", "Separator", "isMenuGroup", "MENU_NAME", "createMenuContext", "createMenuScope", "createContextScope", "nullItemsAtom", "Atom", "make", "defaultItemsAccessor", "menuContextDefaults", "iconSize", "items", "onAction", "undefined", "menuItemsAtom", "Map", "addMenuItems", "removeMenuItems", "MenuContextProvider", "useMenuScoped", "useMenuScope", "MenuDropdownContext", "React", "createContext", "closeMenu", "DEFAULT_PRIORITY", "sortMenuItems", "sort", "a", "b", "priority", "id", "localeCompare", "MenuProvider", "children", "attendableId", "alwaysActive", "scope", "registry", "useContext", "RegistryContext", "useMemo", "pipe", "keepAlive", "useCallback", "props", "prev", "get", "next", "set", "delete", "resolveItems", "baseItems", "group", "entries", "applicable", "values", "filter", "entry", "groupFilter", "length", "sorted", "replacements", "mode", "log", "warn", "ids", "map", "r", "join", "using", "additive", "additiveItems", "flatMap", "useMenuItems", "propsItems", "consumerName", "__menuScope", "groupItems", "useAtomValue", "resolved", "useMenu", "MenuRoot", "open", "defaultOpen", "onOpenChange", "caller", "menuOpen", "setMenuOpen", "useControllableState", "prop", "defaultProp", "onChange", "Provider", "value", "NaturalDropdownMenu", "Root", "MenuContentItem", "item", "onClick", "action", "handleClick", "event", "Item", "classNames", "disabled", "properties", "testId", "icon", "Icon", "size", "ActionLabel", "MenuContent", "callerOverride", "contextCaller", "resolvedItems", "handleActionClick", "stopPropagation", "parent", "executeMenuAction", "Portal", "Content", "Viewport", "key", "Arrow", "Menu", "Trigger", "VirtualTrigger", "Toolbar", "ToolbarMenu", "RegistryContext", "useAtomValue", "useCallback", "useContext", "useRef", "Graph", "Node", "useMenuActions", "props", "registry", "useContext", "RegistryContext", "menuGraphProps", "useAtomValue", "graphRef", "useRef", "current", "newGraph", "Graph", "make", "pipe", "addNodes", "nodes", "addEdges", "edges", "graph", "items", "useCallback", "group", "connections", "id", "Node", "RootId", "invariant", "MenuBuilderImpl", "_separatorCount", "_data", "_rootId", "root", "props", "invariant", "nodes", "find", "node", "id", "undefined", "push", "createMenuItemGroup", "build", "action", "invoke", "createMenuAction", "edges", "source", "target", "relation", "group", "cb", "subgraph", "separator", "variant", "type", "MenuSeparatorType", "properties", "data", "MenuBuilder", "Object", "freeze", "make"]
4
+ "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { Atom, RegistryContext, useAtomValue } from '@effect-atom/atom-react';\nimport { type Scope, createContextScope } from '@radix-ui/react-context';\nimport { useControllableState } from '@radix-ui/react-use-controllable-state';\nimport React, { createContext, type MouseEvent, type PropsWithChildren, useCallback, useContext, useMemo } from 'react';\n\nimport { log } from '@dxos/log';\nimport { type DropdownMenuRootProps, Icon, DropdownMenu as NaturalDropdownMenu } from '@dxos/react-ui';\n\nimport {\n type AddMenuItemsProps,\n type MenuAction,\n type MenuContextValue,\n type MenuItem,\n type MenuItemGroup,\n type MenuItems,\n type MenuItemsAccessor,\n type MenuItemsMap,\n} from '../types';\nimport { executeMenuAction } from '../util';\nimport { ActionLabel } from './ActionLabel';\nimport { ToolbarMenu } from './ToolbarMenu';\n\n//\n// Scoped context.\n//\n\ntype MenuScopedProps<P> = P & { __menuScope?: Scope };\n\nconst MENU_NAME = 'Menu';\n\nconst [createMenuContext, createMenuScope] = createContextScope(MENU_NAME, []);\n\nconst nullItemsAtom = Atom.make<MenuItem[] | null>(null);\nconst defaultItemsAccessor: MenuItemsAccessor = () => nullItemsAtom;\n\nconst menuContextDefaults: MenuContextValue = {\n iconSize: 5,\n items: defaultItemsAccessor,\n onAction: undefined,\n menuItemsAtom: Atom.make<MenuItemsMap>(new Map()),\n addMenuItems: () => {},\n removeMenuItems: () => {},\n};\n\nconst [MenuContextProvider, useMenuScoped] = createMenuContext<MenuContextValue>(MENU_NAME, menuContextDefaults);\n\nconst useMenuScope = createMenuScope();\n\n//\n// Dropdown context (internal) — allows Menu.Content to close the parent dropdown.\n//\n\ntype MenuDropdownContextValue = {\n closeMenu: () => void;\n caller?: string;\n};\n\nconst MenuDropdownContext = createContext<MenuDropdownContextValue>({\n closeMenu: () => {},\n});\n\n//\n// MenuProvider (internal) — the context provider used by Menu.Root.\n//\n\nconst DEFAULT_PRIORITY = 100;\n\nconst sortMenuItems = (items: MenuItems[]) =>\n [...items].sort((a, b) => {\n if (a.priority !== b.priority) {\n return a.priority - b.priority;\n }\n return a.id.localeCompare(b.id);\n });\n\ntype MenuProviderProps = PropsWithChildren<Partial<MenuContextValue>>;\n\nconst MenuProvider = ({\n children,\n items = menuContextDefaults.items,\n iconSize = menuContextDefaults.iconSize,\n attendableId,\n alwaysActive,\n onAction,\n}: MenuProviderProps) => {\n const { scope } = useMenuScope(undefined);\n const registry = useContext(RegistryContext);\n const menuItemsAtom = useMemo(() => Atom.make<MenuItemsMap>(new Map()).pipe(Atom.keepAlive), []);\n\n const addMenuItems = useCallback(\n (props: AddMenuItemsProps) => {\n const priority = props.priority ?? DEFAULT_PRIORITY;\n const prev = registry.get(menuItemsAtom);\n const next = new Map(prev);\n next.set(props.id, { ...props, priority });\n registry.set(menuItemsAtom, next);\n },\n [registry, menuItemsAtom],\n );\n\n const removeMenuItems = useCallback(\n (id: string) => {\n const prev = registry.get(menuItemsAtom);\n const next = new Map(prev);\n next.delete(id);\n registry.set(menuItemsAtom, next);\n },\n [registry, menuItemsAtom],\n );\n\n return (\n <MenuContextProvider\n items={items}\n iconSize={iconSize}\n attendableId={attendableId}\n alwaysActive={alwaysActive}\n menuItemsAtom={menuItemsAtom}\n addMenuItems={addMenuItems}\n removeMenuItems={removeMenuItems}\n onAction={onAction}\n scope={scope}\n >\n {children}\n </MenuContextProvider>\n );\n};\n\n//\n// Item resolution.\n//\n\nconst resolveItems = (\n baseItems: MenuItem[] | null,\n group: MenuItemGroup | undefined,\n entries: ReadonlyMap<string, MenuItems>,\n): MenuItem[] | null => {\n const applicable = [...entries.values()].filter((entry) => !entry.groupFilter || entry.groupFilter(group));\n if (applicable.length === 0) {\n return baseItems;\n }\n\n const sorted = sortMenuItems(applicable);\n\n const replacements = sorted.filter((entry) => entry.mode === 'replacement');\n if (replacements.length > 0) {\n if (replacements.length > 1) {\n log.warn('multiple replacement entries found', {\n ids: replacements.map((r) => r.id).join(', '),\n using: replacements[0].id,\n });\n }\n return replacements[0].items;\n }\n\n const additive = sorted.filter((entry) => entry.mode === 'additive');\n const additiveItems = additive.flatMap((entry) => entry.items);\n\n if (!baseItems || baseItems.length === 0) {\n return additiveItems.length > 0 ? additiveItems : null;\n }\n\n return [...baseItems, ...additiveItems];\n};\n\n//\n// Public hooks.\n//\n\nconst useMenuItems = (\n group?: MenuItemGroup,\n propsItems?: MenuItem[],\n consumerName: string = 'useMenuItemConsumer',\n __menuScope?: Scope,\n) => {\n const { items, menuItemsAtom } = useMenuScoped(consumerName, __menuScope);\n const groupItems = useAtomValue(items(group));\n const entries = useAtomValue(menuItemsAtom) ?? new Map();\n\n const baseItems = useMemo(() => propsItems ?? groupItems ?? null, [propsItems, groupItems]);\n\n const resolved = useMemo(\n () => resolveItems(baseItems, group, entries as ReadonlyMap<string, MenuItems>),\n [baseItems, group, entries],\n );\n\n return resolved ?? undefined;\n};\n\n/** Returns the menu context without Radix scope. */\nconst useMenu = (consumerName: string): MenuContextValue => {\n return useMenuScoped(consumerName, undefined);\n};\n\n//\n// Menu.Root\n//\n\ntype MenuRootProps = MenuProviderProps &\n Pick<DropdownMenuRootProps, 'children' | 'open' | 'defaultOpen' | 'onOpenChange'> & {\n /** Identifies the component that owns this menu (passed to action handlers). */\n caller?: string;\n };\n\n/**\n * Menu context boundary.\n *\n * NOTE: This component is headless since it's root div has `contents`.\n *\n * Provides the menu context (action dispatch, contribution registry, icon size, etc.)\n * and an optional dropdown root for use with `Menu.Trigger` + `Menu.Content`.\n */\nconst MenuRoot = ({ children, open, defaultOpen, onOpenChange, caller, ...props }: MenuRootProps) => {\n const [menuOpen, setMenuOpen] = useControllableState({\n prop: open,\n defaultProp: defaultOpen,\n onChange: onOpenChange,\n });\n\n const closeMenu = useCallback(() => setMenuOpen(false), [setMenuOpen]);\n\n return (\n <MenuProvider {...props}>\n <MenuDropdownContext.Provider value={{ closeMenu, caller }}>\n <NaturalDropdownMenu.Root open={menuOpen} onOpenChange={setMenuOpen}>\n {children}\n </NaturalDropdownMenu.Root>\n </MenuDropdownContext.Provider>\n </MenuProvider>\n );\n};\n\n//\n// Menu.Content\n//\n\ntype MenuContentProps = {\n group?: MenuItemGroup;\n items?: MenuItem[];\n caller?: string;\n};\n\nconst MenuContentItem = ({\n item,\n onClick,\n __menuScope,\n}: MenuScopedProps<{\n item: MenuItem;\n onClick: (action: MenuAction, event: MouseEvent) => void;\n}>) => {\n const action = item as MenuAction;\n const handleClick = useCallback((event: MouseEvent) => onClick(action, event), [action, onClick]);\n const { iconSize } = useMenuScoped('MenuContentItem', __menuScope);\n return (\n <NaturalDropdownMenu.Item\n onClick={handleClick}\n classNames='gap-2'\n disabled={action.properties?.disabled}\n {...(action.properties?.testId && { 'data-testid': action.properties.testId })}\n >\n {action.properties?.icon && <Icon icon={action.properties!.icon} size={iconSize} />}\n <ActionLabel action={action} />\n </NaturalDropdownMenu.Item>\n );\n};\n\n/**\n * Renders the dropdown menu portal, content, and graph-backed items.\n *\n * Must be a descendant of `Menu.Root`. Reads items via `useMenuItems` from the\n * nearest menu context, with optional `group`/`items` prop overrides.\n */\nconst MenuContent = ({\n group,\n items: propsItems,\n caller: callerOverride,\n __menuScope,\n}: MenuScopedProps<MenuContentProps>) => {\n const { closeMenu, caller: contextCaller } = useContext(MenuDropdownContext);\n const caller = callerOverride ?? contextCaller;\n const { onAction } = useMenuScoped('MenuContent', __menuScope);\n const resolvedItems = useMenuItems(group, propsItems, 'MenuContent', __menuScope);\n\n const handleActionClick = useCallback(\n (action: MenuAction, event: MouseEvent) => {\n if (action.properties?.disabled) {\n return;\n }\n event.stopPropagation();\n closeMenu();\n if (onAction) {\n onAction(action, { parent: group, caller });\n } else {\n void executeMenuAction(action, { parent: group, caller });\n }\n },\n [group, caller, onAction, closeMenu],\n );\n\n return (\n <NaturalDropdownMenu.Portal>\n <NaturalDropdownMenu.Content>\n <NaturalDropdownMenu.Viewport>\n {resolvedItems?.map((item) => (\n <MenuContentItem key={item.id} item={item} onClick={handleActionClick} />\n ))}\n </NaturalDropdownMenu.Viewport>\n <NaturalDropdownMenu.Arrow />\n </NaturalDropdownMenu.Content>\n </NaturalDropdownMenu.Portal>\n );\n};\n\n//\n// Namespace.\n//\n\n/**\n * Primary namespace export for the menu system.\n *\n * - `Menu.Root` — context boundary (replaces `MenuProvider`); also provides a dropdown root.\n * - `Menu.Trigger` / `Menu.VirtualTrigger` — dropdown trigger (use with `Menu.Content`).\n * - `Menu.Content` — renders graph-backed dropdown items inside a portal.\n * - `Menu.Toolbar` — flat toolbar component with graph-backed item rendering.\n */\nconst Menu = {\n Root: MenuRoot,\n Trigger: NaturalDropdownMenu.Trigger,\n Content: MenuContent,\n VirtualTrigger: NaturalDropdownMenu.VirtualTrigger,\n Toolbar: ToolbarMenu,\n};\n\nexport { Menu, menuContextDefaults, useMenu, useMenuItems, useMenuScoped };\n\nexport type { MenuContentProps, MenuRootProps, MenuScopedProps };\n\nexport type {\n ToolbarMenuProps,\n ToolbarMenuActionGroupProperties,\n ToolbarMenuActionGroupProps,\n ToolbarMenuActionProps,\n ToolbarMenuDropdownMenuActionGroup,\n ToolbarMenuToggleGroupActionGroup,\n} from './ToolbarMenu';\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Node } from '@dxos/app-graph';\nimport { runAndForwardErrors } from '@dxos/effect';\nimport { type MenuActionProperties, type MenuItemGroupProperties } from '@dxos/ui-types';\nimport { getHostPlatform } from '@dxos/util';\n\nimport { type MenuAction, type MenuItemGroup, type MenuSeparator } from './types';\n\n/**\n * Execute a menu action's Effect with its captured context.\n * This provides the `_actionContext` layer if available.\n */\nexport const executeMenuAction = async (action: MenuAction, params: Node.InvokeProps = {}): Promise<void> => {\n let effect = action.data(params);\n\n // Provide captured action context if available.\n if (action._actionContext) {\n effect = effect.pipe(Effect.provide(action._actionContext));\n }\n\n await runAndForwardErrors(effect);\n};\n\nexport const getShortcut = (action: Node.ActionLike) => {\n return typeof action.properties?.keyBinding === 'string'\n ? action.properties.keyBinding\n : action.properties?.keyBinding?.[getHostPlatform()];\n};\n\nexport const fallbackIcon = 'ph--placeholder--regular';\n\nexport const createMenuAction = <P extends {} = {}>(\n id: string,\n invoke: () => void,\n properties: P & MenuActionProperties,\n) =>\n ({\n id,\n type: Node.ActionType,\n properties,\n data: () => Effect.sync(invoke),\n }) satisfies MenuAction;\n\nexport const createMenuItemGroup = <\n P extends MenuItemGroupProperties = MenuItemGroupProperties & Partial<MenuActionProperties>,\n>(\n id: string,\n properties: P,\n) =>\n ({\n id,\n type: Node.ActionGroupType,\n properties,\n data: Node.actionGroupSymbol,\n }) satisfies MenuItemGroup;\n\nexport const createGapSeparator = (id: string = 'gap', source: string = 'root') => ({\n nodes: [\n {\n id,\n type: '@dxos/react-ui-toolbar/separator',\n properties: { variant: 'gap' },\n data: undefined as never,\n } satisfies MenuSeparator,\n ],\n edges: [\n {\n source,\n target: id,\n relation: 'child' as const,\n },\n ],\n});\n\nexport const createLineSeparator = (id: string = 'line', source: string = 'root') => ({\n nodes: [\n {\n id,\n type: '@dxos/react-ui-toolbar/separator',\n properties: { variant: 'line' },\n data: undefined as never,\n } satisfies MenuSeparator,\n ],\n edges: [{ source, target: id, relation: 'child' as const }],\n});\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport React from 'react';\n\nimport { type Node } from '@dxos/app-graph';\nimport { keySymbols } from '@dxos/keyboard';\nimport { type TFunction, toLocalizedString, useTranslation } from '@dxos/react-ui';\nimport { mx } from '@dxos/ui-theme';\nimport { type MenuActionProperties } from '@dxos/ui-types';\n\nimport { translationKey } from '../translations';\nimport { getShortcut } from '../util';\n\ntype Action = Node.Action<MenuActionProperties> | Node.ActionGroup<Omit<MenuActionProperties, 'variant'>>;\n\nexport const ActionLabel = ({ action }: { action: Action }) => {\n const { t } = useTranslation(translationKey);\n const shortcut = getShortcut(action);\n return (\n <>\n <span className='grow truncate'>{toLocalizedString(action.properties!.label, t)}</span>\n {shortcut && <span className={mx('shrink-0', 'text-description')}>{keySymbols(shortcut).join('')}</span>}\n </>\n );\n};\n\nexport const actionLabel = (action: Action, t: TFunction) => {\n const shortcut = getShortcut(action);\n return `${toLocalizedString(action.properties!.label, t)}${shortcut ? ` (${keySymbols(shortcut).join('')})` : ''}`;\n};\n", "//\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", "//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { useCallback } from 'react';\n\nimport { Icon, Toolbar as NaturalToolbar, type ToolbarRootProps, useTranslation } from '@dxos/react-ui';\nimport { useAttention } from '@dxos/react-ui-attention';\nimport { composable, composableProps } from '@dxos/ui-theme';\nimport { type MenuActionProperties } from '@dxos/ui-types';\n\nimport { translationKey } from '../translations';\nimport {\n type MenuAction,\n type MenuItem,\n type MenuItemGroup,\n type MenuMultipleSelectActionGroup,\n type MenuSingleSelectActionGroup,\n isMenuGroup,\n isSeparator,\n} from '../types';\nimport { executeMenuAction } from '../util';\nimport { ActionLabel, actionLabel } from './ActionLabel';\nimport { DropdownMenu } from './DropdownMenu';\nimport { type MenuScopedProps, useMenuItems, useMenuScoped } from './Menu';\n\nexport type ToolbarMenuDropdownMenuActionGroup = Omit<MenuActionProperties, 'variant' | 'icon'> & {\n variant: 'dropdownMenu';\n icon: string;\n applyActive?: boolean;\n};\n\nexport type ToolbarMenuToggleGroupActionGroup = Omit<MenuActionProperties, 'variant'> & {\n variant: 'toggleGroup';\n};\n\nexport type ToolbarMenuActionGroupProperties = (\n | ToolbarMenuDropdownMenuActionGroup\n | ToolbarMenuToggleGroupActionGroup\n) &\n (MenuSingleSelectActionGroup | MenuMultipleSelectActionGroup);\n\nexport type ToolbarMenuProps = ToolbarRootProps;\n\nexport type ToolbarMenuActionGroupProps = {\n group: MenuItemGroup<ToolbarMenuActionGroupProperties>;\n items?: MenuItem[];\n};\n\nexport type ToolbarMenuActionProps = {\n group: MenuItemGroup<ToolbarMenuActionGroupProperties>;\n action: MenuAction;\n};\n\nconst ActionToolbarItem = ({ __menuScope, action }: MenuScopedProps<{ action: MenuAction }>) => {\n const { iconSize, onAction } = useMenuScoped('ActionToolbarItem', __menuScope);\n const { t } = useTranslation(translationKey);\n\n const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;\n const buttonVariant =\n (action.properties as { variant?: string }).variant === 'primary' ? ('primary' as const) : ('ghost' as const);\n\n const handleClick = useCallback(() => {\n if (onAction) {\n onAction(action, {});\n } else {\n void executeMenuAction(action);\n }\n }, [action, onAction]);\n\n const commonProps = {\n variant: buttonVariant,\n disabled,\n classNames,\n onClick: handleClick,\n ...(testId && { 'data-testid': testId }),\n };\n\n if (hidden) {\n return null;\n }\n\n return icon ? (\n <NaturalToolbar.IconButton\n key={action.id}\n {...commonProps}\n icon={icon}\n size={iconSize}\n iconOnly={iconOnly}\n label={actionLabel(action, t)}\n />\n ) : (\n <NaturalToolbar.Button key={action.id} {...commonProps}>\n <ActionLabel action={action} />\n </NaturalToolbar.Button>\n );\n};\n\nconst DropdownMenuToolbarItem = ({\n __menuScope,\n group,\n items: propsItems,\n}: MenuScopedProps<ToolbarMenuActionGroupProps>) => {\n const { iconOnly, disabled, testId } = group.properties;\n const { t } = useTranslation(translationKey);\n const { iconSize } = useMenuScoped('DropdownMenuToolbarItem', __menuScope);\n const items = useMenuItems(group, propsItems, 'DropdownMenuToolbarItem', __menuScope);\n const activeItem = items?.find((item) => !!(item as MenuAction).properties.checked);\n const icon =\n ((group.properties as any).applyActive &&\n // TODO(thure): Handle other menu item types.\n (activeItem as MenuAction)?.properties.icon) ||\n group.properties.icon;\n const Root = icon ? NaturalToolbar.IconButton : NaturalToolbar.Button;\n const labelAction = (group.properties as any).applyActive && activeItem ? (activeItem as MenuAction) : group;\n const rootProps = icon\n ? {\n icon,\n size: iconSize,\n iconOnly,\n label: actionLabel(labelAction, t),\n caretDown: true,\n }\n : {\n children: (\n <>\n <ActionLabel action={labelAction} />\n <Icon size={3} icon='ph--caret-down--bold' classNames='ms-1' />\n </>\n ),\n };\n\n return (\n <DropdownMenu.Root group={group} items={items}>\n <DropdownMenu.Trigger asChild>\n <Root variant='ghost' disabled={disabled} {...(rootProps as any)} {...(testId && { 'data-testid': testId })} />\n </DropdownMenu.Trigger>\n </DropdownMenu.Root>\n );\n};\n\nconst ToggleGroupItem = ({ __menuScope, group, action }: MenuScopedProps<ToolbarMenuActionProps>) => {\n const { iconSize, onAction } = useMenuScoped('ToggleGroupItem', __menuScope);\n const { t } = useTranslation(translationKey);\n const { icon, iconOnly = true, disabled, testId, hidden, classNames } = action.properties;\n\n const handleClick = useCallback(() => {\n if (onAction) {\n onAction(action, { parent: group });\n } else {\n void executeMenuAction(action, { parent: group });\n }\n }, [action, group, onAction]);\n\n const commonProps = {\n value: action.id,\n disabled,\n variant: 'ghost' as const,\n classNames,\n onClick: handleClick,\n ...(testId && { 'data-testid': testId }),\n };\n\n return hidden ? null : icon ? (\n <NaturalToolbar.ToggleGroupIconItem\n {...commonProps}\n icon={icon}\n size={iconSize}\n iconOnly={iconOnly}\n label={actionLabel(action, t)}\n />\n ) : (\n <NaturalToolbar.ToggleGroupItem {...commonProps}>\n <ActionLabel action={action} />\n </NaturalToolbar.ToggleGroupItem>\n );\n};\n\nconst ToggleGroupToolbarItem = ({\n __menuScope,\n group,\n items: itemsProp,\n}: MenuScopedProps<ToolbarMenuActionGroupProps>) => {\n const items = useMenuItems(group, itemsProp, 'ToggleGroupToolbarItem', __menuScope);\n const { selectCardinality } = group.properties;\n\n // TODO(thure): Handle other menu item types.\n const children = (items as MenuAction[]).map((action) => (\n <ToggleGroupItem key={action.id} group={group} action={action} />\n ));\n\n if (selectCardinality === 'multiple') {\n return (\n <NaturalToolbar.ToggleGroup type='multiple' value={group.properties.value}>\n {children}\n </NaturalToolbar.ToggleGroup>\n );\n } else {\n return (\n <NaturalToolbar.ToggleGroup type='single' value={group.properties.value}>\n {children}\n </NaturalToolbar.ToggleGroup>\n );\n }\n};\n\nexport const ToolbarMenu = composable<HTMLDivElement, MenuScopedProps<ToolbarMenuProps>>(\n ({ __menuScope, children, ...props }, forwardedRef) => {\n const items = useMenuItems(undefined, undefined, 'ToolbarMenu', __menuScope);\n const { attendableId, alwaysActive } = useMenuScoped('ToolbarMenu', __menuScope);\n const { hasAttention } = useAttention(attendableId);\n\n return (\n <NaturalToolbar.Root\n {...composableProps(props, { classNames: attendableId })}\n disabled={!alwaysActive && !hasAttention}\n ref={forwardedRef}\n >\n {items?.map((item: MenuItem) => (\n <ToolbarMenuItem key={item.id} __menuScope={__menuScope} item={item} />\n ))}\n {children}\n </NaturalToolbar.Root>\n );\n },\n);\n\nconst ToolbarMenuItem = ({ __menuScope, item }: MenuScopedProps<{ item: MenuItem }>) => {\n if (isSeparator(item)) {\n return <NaturalToolbar.Separator variant={item.properties.variant} />;\n }\n\n if (isMenuGroup(item)) {\n if (item.properties.variant === 'dropdownMenu') {\n return (\n <DropdownMenuToolbarItem\n __menuScope={__menuScope}\n group={item as MenuItemGroup<ToolbarMenuActionGroupProperties>}\n />\n );\n } else {\n return (\n <ToggleGroupToolbarItem\n __menuScope={__menuScope}\n group={item as MenuItemGroup<ToolbarMenuActionGroupProperties>}\n />\n );\n }\n }\n\n return <ActionToolbarItem __menuScope={__menuScope} action={item as MenuAction} />;\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Atom } from '@effect-atom/atom-react';\n\nimport { Node } from '@dxos/app-graph';\nimport { type IconButtonProps, type ToolbarSeparatorProps } from '@dxos/react-ui';\nimport { type MenuActionProperties } from '@dxos/ui-types';\n\nexport type MenuAction<P extends {} = {}> = Node.Action<P & MenuActionProperties>;\n\nexport const MenuSeparatorType = '@dxos/react-ui-toolbar/separator' as const;\n\nexport type MenuSeparator = Node.Node<never, Pick<ToolbarSeparatorProps, 'variant'>> & {\n type: typeof MenuSeparatorType;\n};\n\nexport const isSeparator = (node: Node.Node): node is MenuSeparator => node.type === MenuSeparatorType;\n\nexport type MenuSingleSelectActionGroup = { selectCardinality: 'single'; value: string };\nexport type MenuMultipleSelectActionGroup = { selectCardinality: 'multiple'; value: string[] };\n\nexport type MenuItemGroup<P extends Record<string, any> = Record<string, any>> = Node.ActionGroup<P>;\n\nexport const isMenuGroup = (node: Node.Node): node is MenuItemGroup => node.type === Node.ActionGroupType;\n\nexport type MenuItem = MenuSeparator | MenuAction | MenuItemGroup;\n\n/** Atom-family-style accessor: returns an atom of items for a given group (or root when undefined). */\nexport type MenuItemsAccessor = (group?: MenuItemGroup) => Atom.Atom<MenuItem[] | null>;\n\nexport type ActionExecutor = (action: MenuAction, params: Node.InvokeProps) => void;\n\nexport type MenuItemsMode = 'additive' | 'replacement';\n\nexport type AddMenuItemsProps = {\n id: string;\n mode: MenuItemsMode;\n priority?: number;\n items: MenuItem[];\n groupFilter?: (group?: MenuItemGroup) => boolean;\n};\n\nexport type MenuItems = Omit<AddMenuItemsProps, 'priority'> & { priority: number };\nexport type MenuItemsMap = Map<string, MenuItems>;\n\nexport type MenuContextValue = {\n /** Atom-family accessor for base menu items, keyed by group (similar to Tree model). */\n items: MenuItemsAccessor;\n iconSize: IconButtonProps['size'];\n attendableId?: string;\n /** TODO(burdon): Remove and assume always active if attendableId is undefined. */\n /** If true, the menu is always active regardless of attention state. */\n alwaysActive?: boolean;\n /** Atom holding the current set of imperatively added menu items. */\n menuItemsAtom: Atom.Atom<MenuItemsMap>;\n /** Imperatively add menu items to the nearest MenuProvider. */\n addMenuItems: (props: AddMenuItemsProps) => void;\n /** Remove previously added menu items by id. */\n removeMenuItems: (id: string) => void;\n /** Optional action executor. If provided, will be used instead of default execution. */\n onAction?: ActionExecutor;\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { useControllableState } from '@radix-ui/react-use-controllable-state';\nimport React, { type MouseEvent, useCallback } from 'react';\n\nimport { type DropdownMenuRootProps, Icon, DropdownMenu as NaturalDropdownMenu } from '@dxos/react-ui';\n\nimport { type MenuAction, type MenuItem, type MenuItemGroup } from '../types';\nimport { executeMenuAction } from '../util';\nimport { ActionLabel } from './ActionLabel';\nimport { type MenuScopedProps, useMenuItems, useMenuScoped } from './Menu';\n\nexport type DropdownMenuProps = DropdownMenuRootProps & {\n group?: MenuItemGroup;\n items?: MenuItem[];\n caller?: string;\n};\n\nconst DropdownMenuItem = ({\n item,\n onClick,\n __menuScope,\n}: MenuScopedProps<{\n item: MenuItem;\n onClick: (action: MenuAction, event: MouseEvent) => void;\n}>) => {\n // TODO(thure): handle other items.\n const action = item as MenuAction;\n const handleClick = useCallback((event: MouseEvent) => onClick(action, event), [action, onClick]);\n const { iconSize } = useMenuScoped('DropdownMenuItem', __menuScope);\n return (\n <NaturalDropdownMenu.Item\n onClick={handleClick}\n classNames='gap-2'\n disabled={action.properties?.disabled}\n {...(action.properties?.testId && { 'data-testid': action.properties.testId })}\n >\n {action.properties?.icon && <Icon icon={action.properties!.icon} size={iconSize} />}\n <ActionLabel action={action} />\n </NaturalDropdownMenu.Item>\n );\n};\n\nconst DropdownMenuRoot = ({\n items: propsItems,\n group,\n open,\n defaultOpen,\n onOpenChange,\n caller,\n children,\n __menuScope,\n ...naturalProps\n}: MenuScopedProps<DropdownMenuProps>) => {\n const [optionsMenuOpen, setOptionsMenuOpen] = useControllableState({\n prop: open,\n defaultProp: defaultOpen,\n onChange: onOpenChange,\n });\n\n const { onAction } = useMenuScoped('DropdownMenuRoot', __menuScope);\n\n const handleActionClick = useCallback(\n (action: MenuAction, event: MouseEvent) => {\n if (action.properties?.disabled) {\n return;\n }\n event.stopPropagation();\n // TODO(thure): Why does Dialog's modal-ness cause issues if we don't explicitly close the menu here?\n setOptionsMenuOpen(false);\n if (onAction) {\n onAction(action, { parent: group, caller });\n } else {\n void executeMenuAction(action, { parent: group, caller });\n }\n },\n [group, caller, onAction],\n );\n\n const items = useMenuItems(group, propsItems);\n\n return (\n <NaturalDropdownMenu.Root open={optionsMenuOpen} onOpenChange={setOptionsMenuOpen} {...naturalProps}>\n {children}\n <NaturalDropdownMenu.Portal>\n <NaturalDropdownMenu.Content>\n <NaturalDropdownMenu.Viewport>\n {items?.map((item) => (\n <DropdownMenuItem key={item.id} item={item} onClick={handleActionClick} />\n ))}\n </NaturalDropdownMenu.Viewport>\n <NaturalDropdownMenu.Arrow />\n </NaturalDropdownMenu.Content>\n </NaturalDropdownMenu.Portal>\n </NaturalDropdownMenu.Root>\n );\n};\n\nexport const DropdownMenu = {\n Root: DropdownMenuRoot,\n Trigger: NaturalDropdownMenu.Trigger,\n VirtualTrigger: NaturalDropdownMenu.VirtualTrigger,\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Atom, RegistryContext, useAtomValue } from '@effect-atom/atom-react';\nimport { useCallback, useContext, useMemo } from 'react';\n\nimport { Graph, Node } from '@dxos/app-graph';\n\nimport { type MenuItem, type MenuItemGroup, type MenuItemsAccessor } from '../types';\n\nexport type ActionGraphNodes = Node.NodeArg<any>[];\nexport type ActionGraphEdges = Graph.Edge[];\nexport type ActionGraphProps = {\n nodes: ActionGraphNodes;\n edges: ActionGraphEdges;\n};\n\nexport type MenuActions = {\n items: MenuItemsAccessor;\n};\n\nexport const useMenuActions = (props: Atom.Atom<ActionGraphProps>): MenuActions => {\n const registry = useContext(RegistryContext);\n const menuGraphProps = useAtomValue(props);\n\n // Create a new graph whenever props change to preserve correct order.\n // (Graph.addEdges appends rather than replaces, which breaks ordering on updates.)\n // NOTE: Using useMemo rather than a ref-mutation pattern to avoid calling registry.set during render,\n // which would trigger atom state updates in other components (setState-in-render React warning).\n const graph = useMemo(() => {\n const newGraph = Graph.make({ registry });\n newGraph.pipe(Graph.addNodes(menuGraphProps.nodes as Node.NodeArg<any>[]), Graph.addEdges(menuGraphProps.edges));\n return newGraph;\n }, [registry, menuGraphProps]);\n\n const items: MenuItemsAccessor = useCallback(\n (group?: MenuItemGroup) => {\n // TODO(wittjosiah): Migrate to using action relation instead of child.\n return graph.connections(group?.id || Node.RootId, 'child') as Atom.Atom<MenuItem[] | null>;\n },\n [graph],\n );\n\n return { items };\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\nimport type { MenuActionProperties, MenuItemGroupProperties } from '@dxos/ui-types';\n\nimport type { ActionGraphProps } from './hooks';\nimport { MenuSeparatorType } from './types';\nimport { createMenuAction, createMenuItemGroup } from './util';\n\n/** Callback that populates an action group builder. */\nexport type ActionGroupBuilderFn = (builder: ActionGroupBuilder) => void;\n\n/**\n * Fluent builder for composing menu action graphs.\n * Subgraph functions return a callback that receives the builder, enabling composition via partial application:\n * ```ts\n * const addHeadings = (state): ActionGroupBuilderFn => (builder) => {\n * builder.group('headings', { ... }, (group) => { ... });\n * };\n *\n * MenuBuilder.make()\n * .subgraph(showHeadings && addHeadings(state))\n * .subgraph(showFormatting && addFormatting(state))\n * .build();\n * ```\n */\nexport interface ActionGroupBuilder {\n /** Add an action node as a child of the current group. */\n action<P extends {} = {}>(id: string, props: P & MenuActionProperties, invoke: () => void): this;\n\n /** Add a nested action group. */\n group<P extends {} = {}>(id: string, props: P & MenuItemGroupProperties, cb: ActionGroupBuilderFn): this;\n\n /** Merge pre-built nodes and edges, or build a subgraph via callback scoped to the current group. Falsy values are ignored. */\n subgraph(subgraphOrCb: ActionGraphProps | ActionGroupBuilderFn | false | null | undefined): this;\n\n /** Add a separator. */\n separator(variant?: 'gap' | 'line'): this;\n}\n\n/** Top-level builder that creates the root group and produces the final action graph. */\nexport interface MenuBuilder extends ActionGroupBuilder {\n /** Set properties on the root menu group. May only be called once. */\n root(props: MenuItemGroupProperties): this;\n\n /** Return the assembled action graph. */\n build(): ActionGraphProps;\n}\n\nclass MenuBuilderImpl implements MenuBuilder {\n private _separatorCount = 0;\n\n constructor(\n private readonly _data: ActionGraphProps,\n private readonly _rootId: string,\n ) {}\n\n root(props: MenuItemGroupProperties): this {\n invariant(this._rootId === 'root', 'Root group can only be at the top level');\n invariant(this._data.nodes.find((node) => node.id === 'root') === undefined, 'Root group can only be created once');\n this._data.nodes.push(createMenuItemGroup('root', props));\n return this;\n }\n\n build(): ActionGraphProps {\n return this._data;\n }\n\n action<P extends {} = {}>(id: string, props: P & MenuActionProperties, invoke: () => void): this {\n this._data.nodes.push(createMenuAction(id, invoke, props));\n this._data.edges.push({ source: this._rootId, target: id, relation: 'child' });\n return this;\n }\n\n group<P extends {} = {}>(id: string, props: P & MenuItemGroupProperties, cb: ActionGroupBuilderFn): this {\n this._data.nodes.push(createMenuItemGroup(id, props));\n this._data.edges.push({ source: this._rootId, target: id, relation: 'child' });\n cb(new MenuBuilderImpl(this._data, id));\n return this;\n }\n\n subgraph(subgraphOrCb: ActionGraphProps | ActionGroupBuilderFn | false | null | undefined): this {\n if (!subgraphOrCb) {\n return this;\n }\n if (typeof subgraphOrCb === 'function') {\n subgraphOrCb(new MenuBuilderImpl(this._data, this._rootId));\n } else {\n this._data.nodes.push(...subgraphOrCb.nodes);\n this._data.edges.push(...subgraphOrCb.edges);\n }\n return this;\n }\n\n separator(variant: 'gap' | 'line' = 'gap'): this {\n const id = `separator-${++this._separatorCount}`;\n this._data.nodes.push({\n id,\n type: MenuSeparatorType,\n properties: { variant },\n data: undefined as never,\n });\n this._data.edges.push({ source: this._rootId, target: id, relation: 'child' });\n return this;\n }\n}\n\nexport const MenuBuilder = Object.freeze({\n make: (): MenuBuilder => new MenuBuilderImpl({ nodes: [], edges: [] }, 'root'),\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["Atom", "RegistryContext", "useAtomValue", "createContextScope", "useControllableState", "React", "createContext", "useCallback", "useContext", "useMemo", "log", "Icon", "DropdownMenu", "NaturalDropdownMenu", "Effect", "Node", "runAndForwardErrors", "getHostPlatform", "executeMenuAction", "action", "params", "effect", "data", "_actionContext", "pipe", "provide", "runAndForwardErrors", "getShortcut", "properties", "keyBinding", "getHostPlatform", "createMenuAction", "id", "invoke", "properties", "type", "Node", "ActionType", "data", "sync", "createMenuItemGroup", "ActionGroupType", "actionGroupSymbol", "createGapSeparator", "source", "nodes", "variant", "undefined", "edges", "target", "relation", "createLineSeparator", "React", "keySymbols", "toLocalizedString", "useTranslation", "mx", "translationKey", "translations", "ActionLabel", "action", "t", "useTranslation", "translationKey", "shortcut", "getShortcut", "span", "className", "toLocalizedString", "properties", "label", "mx", "keySymbols", "join", "actionLabel", "React", "useCallback", "Icon", "Toolbar", "NaturalToolbar", "useTranslation", "useAttention", "composable", "composableProps", "Node", "MenuSeparatorType", "isSeparator", "node", "type", "isMenuGroup", "Node", "ActionGroupType", "useControllableState", "React", "useCallback", "Icon", "DropdownMenu", "NaturalDropdownMenu", "DropdownMenuItem", "item", "onClick", "__menuScope", "action", "handleClick", "useCallback", "event", "iconSize", "useMenuScoped", "React", "NaturalDropdownMenu", "Item", "classNames", "disabled", "properties", "testId", "icon", "Icon", "size", "ActionLabel", "DropdownMenuRoot", "items", "propsItems", "group", "open", "defaultOpen", "onOpenChange", "caller", "children", "naturalProps", "optionsMenuOpen", "setOptionsMenuOpen", "useControllableState", "prop", "defaultProp", "onChange", "onAction", "handleActionClick", "stopPropagation", "parent", "executeMenuAction", "useMenuItems", "Root", "Portal", "Content", "Viewport", "map", "key", "id", "Arrow", "DropdownMenu", "Trigger", "VirtualTrigger", "ActionToolbarItem", "__menuScope", "action", "iconSize", "onAction", "useMenuScoped", "t", "useTranslation", "translationKey", "icon", "iconOnly", "disabled", "testId", "hidden", "classNames", "properties", "buttonVariant", "variant", "handleClick", "useCallback", "executeMenuAction", "commonProps", "onClick", "React", "NaturalToolbar", "IconButton", "key", "id", "size", "label", "actionLabel", "Button", "ActionLabel", "DropdownMenuToolbarItem", "group", "items", "propsItems", "useMenuItems", "activeItem", "find", "item", "checked", "applyActive", "Root", "labelAction", "rootProps", "caretDown", "children", "Icon", "DropdownMenu", "Trigger", "asChild", "ToggleGroupItem", "parent", "value", "ToggleGroupIconItem", "ToggleGroupToolbarItem", "itemsProp", "selectCardinality", "map", "ToggleGroup", "type", "ToolbarMenu", "composable", "props", "forwardedRef", "undefined", "attendableId", "alwaysActive", "hasAttention", "useAttention", "composableProps", "ref", "ToolbarMenuItem", "isSeparator", "Separator", "isMenuGroup", "MENU_NAME", "createMenuContext", "createMenuScope", "createContextScope", "nullItemsAtom", "Atom", "make", "defaultItemsAccessor", "menuContextDefaults", "iconSize", "items", "onAction", "undefined", "menuItemsAtom", "Map", "addMenuItems", "removeMenuItems", "MenuContextProvider", "useMenuScoped", "useMenuScope", "MenuDropdownContext", "createContext", "closeMenu", "DEFAULT_PRIORITY", "sortMenuItems", "sort", "a", "b", "priority", "id", "localeCompare", "MenuProvider", "children", "attendableId", "alwaysActive", "scope", "registry", "useContext", "RegistryContext", "useMemo", "pipe", "keepAlive", "useCallback", "props", "prev", "get", "next", "set", "delete", "React", "resolveItems", "baseItems", "group", "entries", "applicable", "values", "filter", "entry", "groupFilter", "length", "sorted", "replacements", "mode", "log", "warn", "ids", "map", "r", "join", "using", "additive", "additiveItems", "flatMap", "useMenuItems", "propsItems", "consumerName", "__menuScope", "groupItems", "useAtomValue", "resolved", "useMenu", "MenuRoot", "open", "defaultOpen", "onOpenChange", "caller", "menuOpen", "setMenuOpen", "useControllableState", "prop", "defaultProp", "onChange", "Provider", "value", "NaturalDropdownMenu", "Root", "MenuContentItem", "item", "onClick", "action", "handleClick", "event", "Item", "classNames", "disabled", "properties", "testId", "icon", "Icon", "size", "ActionLabel", "MenuContent", "callerOverride", "contextCaller", "resolvedItems", "handleActionClick", "stopPropagation", "parent", "executeMenuAction", "Portal", "Content", "Viewport", "key", "Arrow", "Menu", "Trigger", "VirtualTrigger", "Toolbar", "ToolbarMenu", "RegistryContext", "useAtomValue", "useCallback", "useContext", "useMemo", "Graph", "Node", "useMenuActions", "props", "registry", "useContext", "RegistryContext", "menuGraphProps", "useAtomValue", "graph", "useMemo", "newGraph", "Graph", "make", "pipe", "addNodes", "nodes", "addEdges", "edges", "items", "useCallback", "group", "connections", "id", "Node", "RootId", "invariant", "MenuBuilderImpl", "_separatorCount", "_data", "_rootId", "root", "props", "invariant", "nodes", "find", "node", "id", "undefined", "push", "createMenuItemGroup", "build", "action", "invoke", "createMenuAction", "edges", "source", "target", "relation", "group", "cb", "subgraph", "subgraphOrCb", "separator", "variant", "type", "MenuSeparatorType", "properties", "data", "MenuBuilder", "Object", "freeze", "make"]
7
7
  }