@dxos/plugin-navtree 0.8.4-main.c4373fc → 0.8.4-main.d05673bc65

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 (233) hide show
  1. package/dist/lib/browser/CommandsDialogContent-2DKIOLJL.mjs +113 -0
  2. package/dist/lib/browser/CommandsDialogContent-2DKIOLJL.mjs.map +7 -0
  3. package/dist/lib/browser/CommandsTrigger-ABODVVMP.mjs +33 -0
  4. package/dist/lib/browser/CommandsTrigger-ABODVVMP.mjs.map +7 -0
  5. package/dist/lib/browser/NavTreeContainer-TLSYS4KK.mjs +12 -0
  6. package/dist/lib/browser/NavTreeContainer-TLSYS4KK.mjs.map +7 -0
  7. package/dist/lib/browser/NavTreeDocumentTitle-L6NLLWLD.mjs +26 -0
  8. package/dist/lib/browser/NavTreeDocumentTitle-L6NLLWLD.mjs.map +7 -0
  9. package/dist/lib/browser/app-graph-builder-Z43OTNI2.mjs +44 -0
  10. package/dist/lib/browser/app-graph-builder-Z43OTNI2.mjs.map +7 -0
  11. package/dist/lib/browser/chunk-A4VDCIJC.mjs +19 -0
  12. package/dist/lib/browser/chunk-A4VDCIJC.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-AYF2TX65.mjs +14 -0
  14. package/dist/lib/browser/chunk-AYF2TX65.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-JT37R5HS.mjs +1468 -0
  16. package/dist/lib/browser/chunk-JT37R5HS.mjs.map +7 -0
  17. package/dist/lib/browser/chunk-KF26WA4O.mjs +23 -0
  18. package/dist/lib/browser/chunk-KF26WA4O.mjs.map +7 -0
  19. package/dist/lib/browser/index.mjs +86 -91
  20. package/dist/lib/browser/index.mjs.map +4 -4
  21. package/dist/lib/browser/{keyboard-AHSAWKXA.mjs → keyboard-HYGFQO7T.mjs} +25 -19
  22. package/dist/lib/browser/keyboard-HYGFQO7T.mjs.map +7 -0
  23. package/dist/lib/browser/meta.json +1 -1
  24. package/dist/lib/browser/operation-resolver-VDWVUII5.mjs +49 -0
  25. package/dist/lib/browser/operation-resolver-VDWVUII5.mjs.map +7 -0
  26. package/dist/lib/browser/react-surface-D2RLMV4Z.mjs +59 -0
  27. package/dist/lib/browser/react-surface-D2RLMV4Z.mjs.map +7 -0
  28. package/dist/lib/browser/state-CWMNNF7K.mjs +131 -0
  29. package/dist/lib/browser/state-CWMNNF7K.mjs.map +7 -0
  30. package/dist/types/src/NavTreePlugin.d.ts +2 -1
  31. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  32. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +6 -0
  33. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +1 -0
  34. package/dist/types/src/capabilities/app-graph-builder/index.d.ts +3 -0
  35. package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +1 -0
  36. package/dist/types/src/capabilities/index.d.ts +5 -108
  37. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  38. package/dist/types/src/capabilities/keyboard/index.d.ts +3 -0
  39. package/dist/types/src/capabilities/keyboard/index.d.ts.map +1 -0
  40. package/dist/types/src/capabilities/keyboard/keyboard.d.ts +5 -0
  41. package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +1 -0
  42. package/dist/types/src/capabilities/operation-resolver/index.d.ts +3 -0
  43. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +1 -0
  44. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +5 -0
  45. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -0
  46. package/dist/types/src/capabilities/react-surface/index.d.ts +3 -0
  47. package/dist/types/src/capabilities/react-surface/index.d.ts.map +1 -0
  48. package/dist/types/src/capabilities/react-surface/react-surface.d.ts +5 -0
  49. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -0
  50. package/dist/types/src/capabilities/state/index.d.ts +3 -0
  51. package/dist/types/src/capabilities/state/index.d.ts.map +1 -0
  52. package/dist/types/src/capabilities/state/state.d.ts +6 -0
  53. package/dist/types/src/capabilities/state/state.d.ts.map +1 -0
  54. package/dist/types/src/components/NavTree/NavTree.d.ts +11 -4
  55. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  56. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +2 -2
  57. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  58. package/dist/types/src/components/{NavTreeContext.d.ts → NavTreeContext/NavTreeContext.d.ts} +1 -1
  59. package/dist/types/src/components/NavTreeContext/NavTreeContext.d.ts.map +1 -0
  60. package/dist/types/src/components/NavTreeContext/index.d.ts +2 -0
  61. package/dist/types/src/components/NavTreeContext/index.d.ts.map +1 -0
  62. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +10 -8
  63. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  64. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +3 -2
  65. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  66. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  67. package/dist/types/src/components/Sidebar/L0Menu.d.ts +8 -5
  68. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  69. package/dist/types/src/components/Sidebar/L1Panel.d.ts +4 -7
  70. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  71. package/dist/types/src/components/Sidebar/L1Tabs.d.ts +5 -4
  72. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  73. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  74. package/dist/types/src/components/index.d.ts +0 -3
  75. package/dist/types/src/components/index.d.ts.map +1 -1
  76. package/dist/types/src/components/types.d.ts +8 -16
  77. package/dist/types/src/components/types.d.ts.map +1 -1
  78. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts +6 -0
  79. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -0
  80. package/dist/types/src/containers/CommandsDialogContent/index.d.ts +3 -0
  81. package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -0
  82. package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -0
  83. package/dist/types/src/containers/CommandsTrigger/index.d.ts +3 -0
  84. package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -0
  85. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts +9 -0
  86. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -0
  87. package/dist/types/src/containers/NavTreeContainer/index.d.ts +3 -0
  88. package/dist/types/src/containers/NavTreeContainer/index.d.ts.map +1 -0
  89. package/dist/types/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.d.ts +1 -1
  90. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -0
  91. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +3 -0
  92. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -0
  93. package/dist/types/src/containers/index.d.ts +7 -0
  94. package/dist/types/src/containers/index.d.ts.map +1 -0
  95. package/dist/types/src/experimental/Tree.stories.d.ts +15 -4
  96. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  97. package/dist/types/src/hooks/index.d.ts +5 -0
  98. package/dist/types/src/hooks/index.d.ts.map +1 -0
  99. package/dist/types/src/hooks/useActions.d.ts +5 -0
  100. package/dist/types/src/hooks/useActions.d.ts.map +1 -0
  101. package/dist/types/src/hooks/useLoadDescendents.d.ts +6 -0
  102. package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -0
  103. package/dist/types/src/hooks/useNavTreeModel.d.ts +7 -0
  104. package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -0
  105. package/dist/types/src/hooks/useNavTreeState.d.ts +22 -0
  106. package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -0
  107. package/dist/types/src/index.d.ts +1 -2
  108. package/dist/types/src/index.d.ts.map +1 -1
  109. package/dist/types/src/meta.d.ts +2 -2
  110. package/dist/types/src/meta.d.ts.map +1 -1
  111. package/dist/types/src/testing/app-graph-builder.d.ts +2 -3
  112. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  113. package/dist/types/src/testing/generator.d.ts +5 -6
  114. package/dist/types/src/testing/generator.d.ts.map +1 -1
  115. package/dist/types/src/testing/update-state.d.ts +2 -2
  116. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  117. package/dist/types/src/types/capabilities.d.ts +16 -0
  118. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  119. package/dist/types/src/types/events.d.ts +5 -0
  120. package/dist/types/src/types/events.d.ts.map +1 -0
  121. package/dist/types/src/types/index.d.ts +4 -0
  122. package/dist/types/src/types/index.d.ts.map +1 -0
  123. package/dist/types/src/{types.d.ts → types/schema.d.ts} +7 -7
  124. package/dist/types/src/types/schema.d.ts.map +1 -0
  125. package/dist/types/src/util.d.ts +11 -7
  126. package/dist/types/src/util.d.ts.map +1 -1
  127. package/dist/types/tsconfig.tsbuildinfo +1 -1
  128. package/package.json +55 -53
  129. package/src/NavTreePlugin.tsx +54 -73
  130. package/src/capabilities/app-graph-builder/app-graph-builder.ts +42 -0
  131. package/src/capabilities/app-graph-builder/index.ts +7 -0
  132. package/src/capabilities/index.ts +5 -9
  133. package/src/capabilities/keyboard/index.ts +7 -0
  134. package/src/capabilities/keyboard/keyboard.ts +66 -0
  135. package/src/capabilities/operation-resolver/index.ts +7 -0
  136. package/src/capabilities/operation-resolver/operation-resolver.ts +45 -0
  137. package/src/capabilities/react-surface/index.ts +7 -0
  138. package/src/capabilities/react-surface/react-surface.tsx +53 -0
  139. package/src/capabilities/state/index.ts +7 -0
  140. package/src/capabilities/state/state.ts +149 -0
  141. package/src/components/NavTree/NavTree.stories.tsx +44 -51
  142. package/src/components/NavTree/NavTree.tsx +63 -32
  143. package/src/components/{NavTreeContext.tsx → NavTreeContext/NavTreeContext.tsx} +1 -1
  144. package/src/components/NavTreeContext/index.ts +5 -0
  145. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +18 -8
  146. package/src/components/NavTreeItem/NavTreeItemAction.tsx +55 -32
  147. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +42 -16
  148. package/src/components/Sidebar/L0Menu.tsx +145 -155
  149. package/src/components/Sidebar/L1Panel.tsx +247 -93
  150. package/src/components/Sidebar/L1Tabs.tsx +18 -33
  151. package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +2 -3
  152. package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +1 -1
  153. package/src/components/index.ts +1 -3
  154. package/src/components/types.ts +14 -19
  155. package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +135 -0
  156. package/src/containers/CommandsDialogContent/index.ts +7 -0
  157. package/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.tsx +5 -14
  158. package/src/containers/CommandsTrigger/index.ts +7 -0
  159. package/src/containers/NavTreeContainer/NavTreeContainer.tsx +271 -0
  160. package/src/containers/NavTreeContainer/index.ts +6 -0
  161. package/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.tsx +4 -3
  162. package/src/containers/NavTreeDocumentTitle/index.ts +7 -0
  163. package/src/containers/index.ts +12 -0
  164. package/src/experimental/Tree.stories.tsx +54 -60
  165. package/src/experimental/Tree.tsx +3 -3
  166. package/src/hooks/index.ts +8 -0
  167. package/src/hooks/useActions.ts +34 -0
  168. package/src/hooks/useLoadDescendents.ts +23 -0
  169. package/src/hooks/useNavTreeModel.ts +118 -0
  170. package/src/hooks/useNavTreeState.ts +42 -0
  171. package/src/index.ts +1 -2
  172. package/src/meta.ts +9 -4
  173. package/src/testing/app-graph-builder.ts +159 -235
  174. package/src/testing/generator.ts +19 -17
  175. package/src/testing/update-state.ts +6 -6
  176. package/src/types/capabilities.ts +21 -0
  177. package/src/types/events.ts +12 -0
  178. package/src/types/index.ts +7 -0
  179. package/src/{types.ts → types/schema.ts} +6 -6
  180. package/src/util.ts +27 -16
  181. package/dist/lib/browser/app-graph-builder-YAPMENYT.mjs +0 -46
  182. package/dist/lib/browser/app-graph-builder-YAPMENYT.mjs.map +0 -7
  183. package/dist/lib/browser/chunk-IMFXKPSB.mjs +0 -1482
  184. package/dist/lib/browser/chunk-IMFXKPSB.mjs.map +0 -7
  185. package/dist/lib/browser/chunk-LPAREX6J.mjs +0 -15
  186. package/dist/lib/browser/chunk-LPAREX6J.mjs.map +0 -7
  187. package/dist/lib/browser/chunk-ZENLR72O.mjs +0 -14
  188. package/dist/lib/browser/chunk-ZENLR72O.mjs.map +0 -7
  189. package/dist/lib/browser/intent-resolver-TCQXGF6U.mjs +0 -47
  190. package/dist/lib/browser/intent-resolver-TCQXGF6U.mjs.map +0 -7
  191. package/dist/lib/browser/keyboard-AHSAWKXA.mjs.map +0 -7
  192. package/dist/lib/browser/react-surface-AH2TP73M.mjs +0 -51
  193. package/dist/lib/browser/react-surface-AH2TP73M.mjs.map +0 -7
  194. package/dist/lib/browser/state-2Q623V7U.mjs +0 -117
  195. package/dist/lib/browser/state-2Q623V7U.mjs.map +0 -7
  196. package/dist/types/src/capabilities/app-graph-builder.d.ts +0 -4
  197. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +0 -1
  198. package/dist/types/src/capabilities/capabilities.d.ts +0 -21
  199. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  200. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  201. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  202. package/dist/types/src/capabilities/keyboard.d.ts +0 -4
  203. package/dist/types/src/capabilities/keyboard.d.ts.map +0 -1
  204. package/dist/types/src/capabilities/react-surface.d.ts +0 -4
  205. package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
  206. package/dist/types/src/capabilities/state.d.ts +0 -20
  207. package/dist/types/src/capabilities/state.d.ts.map +0 -1
  208. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts +0 -5
  209. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +0 -1
  210. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +0 -1
  211. package/dist/types/src/components/CommandsDialog/index.d.ts +0 -3
  212. package/dist/types/src/components/CommandsDialog/index.d.ts.map +0 -1
  213. package/dist/types/src/components/NavTreeContainer.d.ts +0 -9
  214. package/dist/types/src/components/NavTreeContainer.d.ts.map +0 -1
  215. package/dist/types/src/components/NavTreeContext.d.ts.map +0 -1
  216. package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +0 -1
  217. package/dist/types/src/events.d.ts +0 -4
  218. package/dist/types/src/events.d.ts.map +0 -1
  219. package/dist/types/src/hooks.d.ts +0 -3
  220. package/dist/types/src/hooks.d.ts.map +0 -1
  221. package/dist/types/src/types.d.ts.map +0 -1
  222. package/src/capabilities/app-graph-builder.ts +0 -53
  223. package/src/capabilities/capabilities.ts +0 -20
  224. package/src/capabilities/intent-resolver.ts +0 -37
  225. package/src/capabilities/keyboard.ts +0 -59
  226. package/src/capabilities/react-surface.tsx +0 -45
  227. package/src/capabilities/state.ts +0 -105
  228. package/src/components/CommandsDialog/CommandsDialog.tsx +0 -118
  229. package/src/components/CommandsDialog/index.ts +0 -6
  230. package/src/components/NavTreeContainer.tsx +0 -346
  231. package/src/events.ts +0 -11
  232. package/src/hooks.ts +0 -21
  233. /package/dist/types/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.d.ts +0 -0
@@ -0,0 +1,149 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Atom } from '@effect-atom/atom-react';
6
+ import * as Effect from 'effect/Effect';
7
+
8
+ import { Capabilities, Capability } from '@dxos/app-framework';
9
+ import { AppCapabilities } from '@dxos/app-toolkit';
10
+ import { Graph, Node } from '@dxos/plugin-graph';
11
+ import { Path } from '@dxos/react-ui-list';
12
+
13
+ import { meta } from '../../meta';
14
+ import { type NavTreeCapabilities as NC, NavTreeCapabilities } from '../../types';
15
+
16
+ const KEY = `${meta.id}.state.v1`;
17
+
18
+ /** Default item state for new entries. */
19
+ const defaultItemState: NC.NavTreeItemState = { open: false, current: false, alternateTree: false };
20
+
21
+ /** L0 (top-level workspace) paths are direct children of root — not part of the expandable tree model. */
22
+ const isTopLevelPath = (path: string[]): boolean => path.length === 2 && path[0] === Node.RootId;
23
+
24
+ /** Default state entries for initial tree structure. */
25
+ // TODO(thure): Initialize these dynamically.
26
+ const defaultStateEntries: [string, NC.NavTreeItemState][] = [['root', { open: true, current: false }]];
27
+
28
+ const getInitialState = (): Map<string, NC.NavTreeItemState> => {
29
+ const stringified = localStorage.getItem(KEY);
30
+ if (!stringified) {
31
+ return new Map(defaultStateEntries);
32
+ }
33
+
34
+ try {
35
+ const cached: [string, { open: boolean; current: boolean }][] = JSON.parse(stringified);
36
+ return new Map(cached.map(([key, value]) => [key, { open: value.open, current: false }]));
37
+ } catch {
38
+ return new Map(defaultStateEntries);
39
+ }
40
+ };
41
+
42
+ export default Capability.makeModule(
43
+ Effect.fnUntraced(function* () {
44
+ const registry = yield* Capability.get(Capabilities.AtomRegistry);
45
+ const layoutAtom = yield* Capability.get(AppCapabilities.Layout);
46
+
47
+ // Backing state for persistence (not reactive).
48
+ const backingState = getInitialState();
49
+
50
+ // Per-path atom family for fine-grained reactivity.
51
+ // keepAlive prevents atoms from being garbage collected when components unmount,
52
+ // ensuring state is preserved across deletion/restoration cycles.
53
+ const itemAtomFamily = Atom.family((pathString: string) =>
54
+ Atom.make<NC.NavTreeItemState>(backingState.get(pathString) ?? { ...defaultItemState }).pipe(Atom.keepAlive),
55
+ );
56
+
57
+ const getItemAtom = (path: string[]): Atom.Atom<NC.NavTreeItemState> => {
58
+ const pathString = Path.create(...path);
59
+ if (!backingState.has(pathString)) {
60
+ backingState.set(pathString, { ...defaultItemState });
61
+ }
62
+ return itemAtomFamily(pathString);
63
+ };
64
+
65
+ const getItem = (path: string[]): NC.NavTreeItemState => {
66
+ return registry.get(getItemAtom(path));
67
+ };
68
+
69
+ const setItem = (path: string[], key: 'open' | 'current' | 'alternateTree', next: boolean) => {
70
+ const pathString = Path.create(...path);
71
+ const atom = itemAtomFamily(pathString);
72
+ const currentValue = registry.get(atom);
73
+ const newValue = { ...currentValue, [key]: next };
74
+
75
+ registry.set(atom, newValue);
76
+
77
+ if (!(key === 'open' && isTopLevelPath(path))) {
78
+ backingState.set(pathString, newValue);
79
+ localStorage.setItem(KEY, JSON.stringify(Array.from(backingState.entries())));
80
+ }
81
+ };
82
+
83
+ // Subscribe to layout changes to update current state.
84
+ let previous: string[] = [];
85
+ const unsubscribe = registry.subscribe(layoutAtom, (layout) => {
86
+ const removed = previous.filter((id) => !layout.active.includes(id));
87
+ previous = layout.active;
88
+
89
+ const handleUpdate = () => {
90
+ // Mark removed items as not current.
91
+ removed.forEach((id) => {
92
+ const keys = Array.from(backingState.keys()).filter((key) => Path.last(key) === id);
93
+ keys.forEach((key) => {
94
+ setItem(Path.parts(key), 'current', false);
95
+ });
96
+ });
97
+
98
+ // Mark active items as current.
99
+ layout.active.forEach((id: string) => {
100
+ const keys = Array.from(new Set([...backingState.keys(), id])).filter((key) => Path.last(key) === id);
101
+ keys.forEach((key) => {
102
+ setItem(Path.parts(key), 'current', true);
103
+ });
104
+ });
105
+ };
106
+
107
+ // TODO(wittjosiah): This is setTimeout because there's a race between the keys being initialized.
108
+ // Keys are initialized on the first render of an item in the navtree.
109
+ // This could be avoided if the location was a path as well and not just an id.
110
+ const timeout = setTimeout(handleUpdate, 500);
111
+ handleUpdate();
112
+ return () => clearTimeout(timeout);
113
+ });
114
+
115
+ // Once graph is ready, expand every node marked open in state so the graph has children loaded for rendering.
116
+ yield* Effect.gen(function* () {
117
+ const { graph } = yield* Capability.waitFor(AppCapabilities.AppGraph);
118
+
119
+ // Always expand the active workspace so its subtree is initialized.
120
+ const layout = registry.get(layoutAtom);
121
+ if (layout.workspace) {
122
+ Graph.expand(graph, layout.workspace, 'child');
123
+ }
124
+
125
+ // Expand persisted open nodes, skipping inactive workspace tabs.
126
+ const openPaths = Array.from(backingState.entries())
127
+ .filter(([, state]) => state.open)
128
+ .map(([pathString]) => Path.parts(pathString))
129
+ .filter((path) => !isTopLevelPath(path));
130
+ for (const path of openPaths) {
131
+ const nodeId = path[path.length - 1];
132
+ if (!nodeId) {
133
+ continue;
134
+ }
135
+ Graph.expand(graph, nodeId, 'child');
136
+ }
137
+ }).pipe(Effect.forkDaemon);
138
+
139
+ return Capability.contributes(
140
+ NavTreeCapabilities.State,
141
+ {
142
+ getItem,
143
+ getItemAtom,
144
+ setItem,
145
+ },
146
+ () => Effect.sync(() => unsubscribe()),
147
+ );
148
+ }),
149
+ );
@@ -2,42 +2,33 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { Atom, type Registry } from '@effect-atom/atom-react';
5
6
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
- import * as Schema from 'effect/Schema';
7
+ import * as Effect from 'effect/Effect';
7
8
  import React, { type KeyboardEvent, useCallback, useRef } from 'react';
8
9
  import { expect, userEvent, within } from 'storybook/test';
9
10
 
10
- import {
11
- Capabilities,
12
- IntentPlugin,
13
- LayoutAction,
14
- SettingsPlugin,
15
- contributes,
16
- createResolver,
17
- defineCapability,
18
- useCapability,
19
- } from '@dxos/app-framework';
11
+ import { Capabilities, Capability } from '@dxos/app-framework';
20
12
  import { withPluginManager } from '@dxos/app-framework/testing';
21
- import { live } from '@dxos/live-object';
22
- import { AttentionPlugin } from '@dxos/plugin-attention';
23
- import { GraphPlugin } from '@dxos/plugin-graph';
24
- import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
25
- import { ThemePlugin } from '@dxos/plugin-theme';
13
+ import { useAtomCapability } from '@dxos/app-framework/ui';
14
+ import { AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';
15
+ import { OperationResolver } from '@dxos/operation';
16
+ import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
26
17
  import { faker } from '@dxos/random';
27
18
  import { IconButton, Input, Main, Toolbar } from '@dxos/react-ui';
28
- import { withTheme } from '@dxos/react-ui/testing';
19
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
29
20
  import { useAttention, useAttentionAttributes } from '@dxos/react-ui-attention';
30
21
  import { Stack, StackItem } from '@dxos/react-ui-stack';
31
- import { defaultTx, mx } from '@dxos/react-ui-theme';
22
+ import { mx } from '@dxos/ui-theme';
32
23
 
24
+ import { NavTreeContainer } from '../../containers';
33
25
  import { NavTreePlugin } from '../../NavTreePlugin';
34
26
  import { storybookGraphBuilders } from '../../testing';
35
27
  import { translations } from '../../translations';
36
- import { NavTreeContainer } from '../NavTreeContainer';
37
28
 
38
29
  faker.seed(1234);
39
30
 
40
- const StoryState = defineCapability<{ tab: string }>('story-state');
31
+ const StoryState = Capability.make<Atom.Atom<{ tab: string }>>('story-state');
41
32
 
42
33
  // TODO(burdon): Fix outline (e.g., button in sidebar nav is clipped when focused).
43
34
  // TODO(burdon): Consider similar containment of: Table, Sheet, Kanban Column, Form, etc.
@@ -56,7 +47,7 @@ const StoryPlankHeading = ({ attendableId }: { attendableId: string }) => {
56
47
  label='Test'
57
48
  iconOnly
58
49
  variant={hasAttention ? 'primary' : 'ghost'}
59
- classNames='is-[--rail-action] bs-[--rail-action]'
50
+ classNames='w-(--dx-rail-action) h-(--dx-rail-action)'
60
51
  />
61
52
  <StackItem.ResizeHandle />
62
53
  </div>
@@ -79,22 +70,22 @@ const StoryPlank = ({ attendableId }: { attendableId: string }) => {
79
70
  <StackItem.Root
80
71
  item={{ id: attendableId }}
81
72
  {...attentionAttrs}
82
- classNames='bg-baseSurface border-ie border-separator'
73
+ classNames='bg-base-surface border-e border-separator'
83
74
  size={30}
84
75
  onKeyDown={handleKeyDown}
85
76
  ref={rootElement}
86
77
  >
87
78
  <StoryPlankHeading attendableId={attendableId} />
88
79
  <StackItem.Content toolbar>
89
- <Toolbar.Root classNames='border-b border-subduedSeparator'>
80
+ <Toolbar.Root classNames='border-b border-subdued-separator'>
90
81
  <Toolbar.Button>Test</Toolbar.Button>
91
82
  </Toolbar.Root>
92
83
 
93
- <div className={mx(container, 'm-2 bg-activeSurface')}>
84
+ <div className={mx(container, 'm-2 bg-active-surface')}>
94
85
  <Input.Root>
95
86
  <Input.Label>Level 1 (group)</Input.Label>
96
87
  </Input.Root>
97
- <div className={mx(container, 'bg-baseSurface')}>
88
+ <div className={mx(container, 'bg-base-surface')}>
98
89
  <Input.Root>
99
90
  <Input.Label>Level 2 (base)</Input.Label>
100
91
  <Input.TextArea placeholder='Enter text' />
@@ -107,10 +98,10 @@ const StoryPlank = ({ attendableId }: { attendableId: string }) => {
107
98
  };
108
99
 
109
100
  const DefaultStory = () => {
110
- const state = useCapability(StoryState);
101
+ const state = useAtomCapability(StoryState);
111
102
 
112
103
  return (
113
- <Main.Root complementarySidebarState='closed'>
104
+ <Main.Root navigationSidebarState='expanded'>
114
105
  <Main.NavigationSidebar label='Navigation' classNames='grid'>
115
106
  <NavTreeContainer tab={state.tab} />
116
107
  </Main.NavigationSidebar>
@@ -125,36 +116,38 @@ const DefaultStory = () => {
125
116
  };
126
117
 
127
118
  const meta = {
128
- title: 'plugins/plugin-navtree/NavTree',
119
+ title: 'plugins/plugin-navtree/components/NavTree',
129
120
  component: NavTreeContainer,
130
121
  render: DefaultStory,
131
122
  decorators: [
132
- withTheme,
123
+ withTheme(),
124
+ withLayout({ layout: 'fullscreen' }),
133
125
  withPluginManager({
134
126
  plugins: [
135
- ThemePlugin({ tx: defaultTx }),
136
- GraphPlugin(),
137
- IntentPlugin(),
138
- SettingsPlugin(),
139
- AttentionPlugin(),
127
+ ...corePlugins(),
128
+ StorybookPlugin({
129
+ initialState: { sidebarState: 'expanded' },
130
+ }),
131
+
140
132
  NavTreePlugin(),
141
- StorybookLayoutPlugin({ initialState: { sidebarState: 'expanded' } }),
142
- ],
143
- capabilities: (context) => [
144
- contributes(StoryState, live({ tab: 'space-0' })),
145
- contributes(Capabilities.AppGraphBuilder, storybookGraphBuilders(context)),
146
- contributes(Capabilities.IntentResolver, [
147
- createResolver({
148
- intent: LayoutAction.UpdateLayout,
149
- filter: (data): data is Schema.Schema.Type<typeof LayoutAction.SwitchWorkspace.fields.input> =>
150
- Schema.is(LayoutAction.SwitchWorkspace.fields.input)(data),
151
- resolve: ({ subject }) => {
152
- const state = context.getCapability(StoryState);
153
- state.tab = subject;
154
- },
155
- }),
156
- ]),
157
133
  ],
134
+ capabilities: () => {
135
+ const storyStateAtom = Atom.make({ tab: 'root/space-0' }).pipe(Atom.keepAlive);
136
+ return [
137
+ Capability.contributes(StoryState, storyStateAtom),
138
+ Capability.contributes(AppCapabilities.AppGraphBuilder, storybookGraphBuilders()),
139
+ Capability.contributes(Capabilities.OperationResolver, [
140
+ OperationResolver.make({
141
+ operation: LayoutOperation.SwitchWorkspace,
142
+ handler: ({ subject }) =>
143
+ Effect.gen(function* () {
144
+ const registry: Registry.Registry = yield* Capability.get(Capabilities.AtomRegistry);
145
+ registry.set(storyStateAtom, { tab: subject });
146
+ }),
147
+ }),
148
+ ]),
149
+ ];
150
+ },
158
151
  }),
159
152
  ],
160
153
  parameters: {
@@ -168,7 +161,7 @@ export default meta;
168
161
  type Story = StoryObj<typeof NavTreeContainer>;
169
162
 
170
163
  export const Default: Story = {
171
- play: async ({ canvasElement }) => {
164
+ play: async ({ canvasElement }: { canvasElement: HTMLElement }) => {
172
165
  const canvas = within(canvasElement);
173
166
 
174
167
  // Find the element with treegrid role and click on its parent
@@ -2,56 +2,35 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useMemo } from 'react';
5
+ import React, { forwardRef, useMemo } from 'react';
6
6
 
7
- import { type TreeProps } from '@dxos/react-ui-list';
7
+ import { useAppGraph } from '@dxos/app-toolkit/ui';
8
+ import { Node, useConnections, useActions as useGraphActions } from '@dxos/plugin-graph';
8
9
  import { Tabs } from '@dxos/react-ui-tabs';
10
+ import { byPosition } from '@dxos/util';
9
11
 
10
12
  import { useLoadDescendents } from '../../hooks';
11
- import { type NavTreeItemGraphNode } from '../../types';
12
13
  import { useNavTreeContext } from '../NavTreeContext';
13
14
  import { L0Menu, L1Tabs, type L1TabsProps } from '../Sidebar';
14
15
 
15
16
  export const NAV_TREE_ITEM = 'NavTreeItem';
16
17
 
17
- export type NavTreeProps = Pick<TreeProps<NavTreeItemGraphNode>, 'id' | 'root'> & Pick<L1TabsProps, 'open'>;
18
+ export type NavTreeProps = { id: string; root?: Node.Node; tab: string } & Pick<L1TabsProps, 'open'>;
18
19
 
19
- export const NavTree = ({ id, root, ...props }: NavTreeProps) => {
20
- const { tab, useItems, onBack } = useNavTreeContext();
21
- const topLevelActions = useItems(root, { disposition: 'menu', sort: true });
22
- const topLevelCollections = useItems(root, { disposition: 'collection' });
23
- const topLevelWorkspaces = useItems(root, { disposition: 'workspace' });
24
- const topLevelNavigation = useItems(root, { disposition: 'navigation' });
25
- const l0Items = useMemo(
26
- () => [
27
- // prettier-ignore
28
- ...topLevelCollections,
29
- ...topLevelWorkspaces,
30
- ...topLevelNavigation,
31
- ],
32
- [topLevelCollections, topLevelWorkspaces, topLevelNavigation],
33
- );
34
- const pinnedItems = useItems(root, { disposition: 'pin-end', sort: true });
35
- const userAccountItem = useItems(root, { disposition: 'user-account' })[0];
36
- const topLevelItems = useMemo(
37
- () => [
38
- // prettier-ignore
39
- ...l0Items,
40
- ...pinnedItems,
41
- ...(userAccountItem ? [userAccountItem] : []),
42
- ],
43
- [l0Items, pinnedItems, userAccountItem],
44
- );
20
+ // TODO(wittjosiah): Refactor to Radix-style.
21
+ export const NavTree = forwardRef<HTMLDivElement, NavTreeProps>(({ id, root, tab, ...props }, forwardedRef) => {
22
+ const { onBack, onItemHover } = useNavTreeContext();
23
+ const { topLevelActions, l0Items, pinnedItems, userAccountItem, topLevelItems } = useTopLevelNavItems(root);
45
24
 
46
25
  useLoadDescendents(root);
47
26
  const path = useMemo(() => [id], [id]);
48
27
 
49
28
  return (
50
29
  // TODO(thure): `Tabs.Root` forces all items that should be able to receive focus to use `Tabs.Tab(Primitive)` since
51
- // it uses RovingFocus and doesn’t support moving focus to an item that is not a tab. Assess whether this situation
30
+ // it uses RovingFocus and doesn't support moving focus to an item that is not a tab. Assess whether this situation
52
31
  // should change including whether it should motivate a change in the design/taxonomy, or if this means this should
53
32
  // not use `react-ui-tabs` at all.
54
- <Tabs.Root value={tab} orientation='vertical' verticalVariant='stateless' classNames='relative'>
33
+ <Tabs.Root value={tab} orientation='vertical' verticalVariant='stateless' classNames='relative' ref={forwardedRef}>
55
34
  <L0Menu
56
35
  menuActions={topLevelActions}
57
36
  topLevelItems={l0Items}
@@ -59,8 +38,60 @@ export const NavTree = ({ id, root, ...props }: NavTreeProps) => {
59
38
  userAccountItem={userAccountItem}
60
39
  path={path}
61
40
  parent={root}
41
+ onItemHover={onItemHover}
62
42
  />
63
43
  <L1Tabs topLevelItems={topLevelItems} path={path} currentItemId={tab} onBack={onBack} {...props} />
64
44
  </Tabs.Root>
65
45
  );
46
+ });
47
+
48
+ /**
49
+ * Partitions root children into workspaces, pinned items, user-account, and top-level actions.
50
+ */
51
+ const useTopLevelNavItems = (root?: Node.Node) => {
52
+ const { graph } = useAppGraph();
53
+ const rootId = root?.id ?? Node.RootId;
54
+ const rootOutboundItems = useConnections(graph, rootId, 'child');
55
+ const rootActions = useGraphActions(graph, rootId);
56
+
57
+ const { topLevelActions, l0Items, pinnedItems, userAccountItem } = useMemo(() => {
58
+ const topLevelWorkspaces: Node.Node[] = [];
59
+ const outboundPinnedItems: Node.Node[] = [];
60
+ let userAccountItem: Node.Node | undefined;
61
+ for (const node of rootOutboundItems) {
62
+ switch (node.properties.disposition) {
63
+ case 'workspace':
64
+ topLevelWorkspaces.push(node);
65
+ break;
66
+ case 'pin-end':
67
+ outboundPinnedItems.push(node);
68
+ break;
69
+ case 'user-account':
70
+ userAccountItem ??= node;
71
+ break;
72
+ }
73
+ }
74
+
75
+ const topLevelActions = rootActions
76
+ .filter((action) => action.properties.disposition === 'menu')
77
+ .toSorted((a, b) => byPosition(a.properties, b.properties));
78
+ const pinnedItems = [
79
+ ...outboundPinnedItems,
80
+ ...rootActions.filter((action) => action.properties.disposition === 'pin-end'),
81
+ ].toSorted((a, b) => byPosition(a.properties, b.properties));
82
+
83
+ return {
84
+ topLevelActions,
85
+ l0Items: topLevelWorkspaces,
86
+ pinnedItems,
87
+ userAccountItem,
88
+ };
89
+ }, [rootActions, rootOutboundItems]);
90
+
91
+ const topLevelItems = useMemo(
92
+ () => [...l0Items, ...pinnedItems, ...(userAccountItem ? [userAccountItem] : [])],
93
+ [l0Items, pinnedItems, userAccountItem],
94
+ );
95
+
96
+ return { topLevelActions, l0Items, pinnedItems, userAccountItem, topLevelItems };
66
97
  };
@@ -6,7 +6,7 @@ import { createContext, useContext } from 'react';
6
6
 
7
7
  import { raise } from '@dxos/debug';
8
8
 
9
- import { type NavTreeContextValue } from './types';
9
+ import { type NavTreeContextValue } from '../types';
10
10
 
11
11
  export const NavTreeContext = createContext<NavTreeContextValue | null>(null);
12
12
 
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './NavTreeContext';
@@ -3,8 +3,12 @@
3
3
  //
4
4
 
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Effect from 'effect/Effect';
6
7
 
7
- import { type NodeArg } from '@dxos/app-graph';
8
+ import { RuntimePlugin } from '@dxos/app-framework';
9
+ import { withPluginManager } from '@dxos/app-framework/testing';
10
+ import { type Node } from '@dxos/app-graph';
11
+ import { corePlugins } from '@dxos/plugin-testing';
8
12
  import { faker } from '@dxos/random';
9
13
  import { withTheme } from '@dxos/react-ui/testing';
10
14
 
@@ -18,7 +22,7 @@ const parent = {
18
22
  label: faker.lorem.words(2),
19
23
  icon: 'ph--circle--regular',
20
24
  },
21
- } satisfies NodeArg<any>;
25
+ } satisfies Node.NodeArg<any>;
22
26
 
23
27
  // TODO(burdon): Factor out across tests.
24
28
  const menuActions = faker.helpers.multiple(
@@ -26,19 +30,20 @@ const menuActions = faker.helpers.multiple(
26
30
  ({
27
31
  id: faker.string.uuid(),
28
32
  type: 'action',
29
- data: () => {
30
- console.log('invoke');
31
- },
33
+ data: () =>
34
+ Effect.sync(() => {
35
+ console.log('invoke');
36
+ }),
32
37
  properties: {
33
38
  label: faker.lorem.words(2),
34
39
  icon: 'ph--circle--regular',
35
40
  },
36
- }) satisfies NodeArg<any>,
41
+ }) satisfies Node.NodeArg<any>,
37
42
  { count: 20 },
38
43
  );
39
44
 
40
45
  const meta = {
41
- title: 'plugins/plugin-navtree/NavTreeItemAction',
46
+ title: 'plugins/plugin-navtree/components/NavTreeItemAction',
42
47
  component: NavTreeItemAction,
43
48
  args: {
44
49
  icon: 'ph--list--regular',
@@ -46,7 +51,12 @@ const meta = {
46
51
  menuActions,
47
52
  label: 'Select action',
48
53
  } satisfies Partial<NavTreeItemActionMenuProps>,
49
- decorators: [withTheme],
54
+ decorators: [
55
+ withTheme(),
56
+ withPluginManager({
57
+ plugins: [...corePlugins(), RuntimePlugin()],
58
+ }),
59
+ ],
50
60
  parameters: {
51
61
  layout: 'centered',
52
62
  },
@@ -2,21 +2,23 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import React from 'react';
5
+ import React, { useCallback } from 'react';
6
6
 
7
- import { type Action, type Node } from '@dxos/app-graph';
7
+ import { type Node } from '@dxos/app-graph';
8
+ import { useActionRunner } from '@dxos/plugin-graph';
8
9
  import { IconButton, toLocalizedString, useDensityContext, useTranslation } from '@dxos/react-ui';
9
- import { DropdownMenu, type MenuItem, MenuProvider } from '@dxos/react-ui-menu';
10
- import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/react-ui-theme';
10
+ import { Menu, type MenuItem } from '@dxos/react-ui-menu';
11
+ import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/ui-theme';
11
12
 
12
13
  import { meta } from '../../meta';
13
14
  import { type ActionProperties } from '../../types';
14
15
 
15
16
  export type NavTreeItemActionMenuProps = ActionProperties & {
16
- parent: Node;
17
+ parent: Node.Node;
18
+ path?: string[];
17
19
  caller?: string;
18
20
  monolithic?: boolean;
19
- menuActions?: Action[];
21
+ menuActions?: Node.Action[];
20
22
  };
21
23
 
22
24
  const fallbackIcon = 'ph--placeholder--regular';
@@ -32,6 +34,7 @@ const coarseActionButtonProps = {
32
34
 
33
35
  export const NavTreeItemActionDropdownMenu = ({
34
36
  parent,
37
+ path,
35
38
  label,
36
39
  icon,
37
40
  testId,
@@ -40,32 +43,46 @@ export const NavTreeItemActionDropdownMenu = ({
40
43
  }: NavTreeItemActionMenuProps) => {
41
44
  const { t } = useTranslation(meta.id);
42
45
  const density = useDensityContext();
46
+ const runAction = useActionRunner();
47
+ const handleAction = useCallback(
48
+ (action: Node.Action, params: Node.InvokeProps = {}) => runAction(action, { ...params, path }),
49
+ [runAction, path],
50
+ );
51
+
43
52
  return (
44
- <MenuProvider>
45
- <DropdownMenu.Root group={parent} items={menuActions as MenuItem[]} caller={caller}>
46
- <DropdownMenu.Trigger asChild>
47
- <IconButton
48
- {...(density === 'coarse' ? coarseActionButtonProps : fineActionButtonProps)}
49
- classNames={['shrink-0 pli-2 pointer-fine:pli-1', hoverableControlItem, hoverableOpenControlItem]}
50
- variant='ghost'
51
- icon={icon ?? fallbackIcon}
52
- iconOnly
53
- label={toLocalizedString(label, t)}
54
- data-testid={testId}
55
- />
56
- </DropdownMenu.Trigger>
57
- </DropdownMenu.Root>
58
- </MenuProvider>
53
+ <Menu.Root caller={caller} onAction={handleAction}>
54
+ <Menu.Trigger asChild>
55
+ <IconButton
56
+ {...(density === 'coarse' ? coarseActionButtonProps : fineActionButtonProps)}
57
+ classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
58
+ variant='ghost'
59
+ icon={icon ?? fallbackIcon}
60
+ iconOnly
61
+ label={toLocalizedString(label, t)}
62
+ data-testid={testId}
63
+ />
64
+ </Menu.Trigger>
65
+ <Menu.Content group={parent} items={menuActions as MenuItem[]} />
66
+ </Menu.Root>
59
67
  );
60
68
  };
61
69
 
62
- export const NavTreeItemMonolithicAction = ({
63
- parent,
64
- properties: { disabled, caller, testId, icon, variant = 'ghost', iconOnly = true } = { label: 'never' },
65
- data: invoke,
66
- baseLabel,
67
- }: Action & { parent: Node; onAction?: (action: Action) => void; baseLabel: string }) => {
70
+ export const NavTreeItemMonolithicAction = (
71
+ props: Node.Action & {
72
+ parent: Node.Node;
73
+ path?: string[];
74
+ onAction?: (action: Node.Action) => void;
75
+ baseLabel: string;
76
+ },
77
+ ) => {
78
+ const {
79
+ parent,
80
+ path,
81
+ properties: { disabled, caller, testId, icon, variant = 'ghost', iconOnly = true } = { label: 'never' },
82
+ baseLabel,
83
+ } = props;
68
84
  const density = useDensityContext();
85
+ const runAction = useActionRunner();
69
86
  return (
70
87
  <IconButton
71
88
  {...(density === 'coarse' ? coarseActionButtonProps : fineActionButtonProps)}
@@ -74,7 +91,7 @@ export const NavTreeItemMonolithicAction = ({
74
91
  'shrink-0',
75
92
  hoverableControlItem,
76
93
  hoverableOpenControlItem,
77
- iconOnly ? 'pli-2 pointer-fine:pli-1' : 'p-2 pointer-fine:p-2 mie-1',
94
+ iconOnly ? 'px-2 pointer-fine:px-1' : 'p-2 pointer-fine:p-2 me-1',
78
95
  ]}
79
96
  icon={icon ?? fallbackIcon}
80
97
  iconOnly={iconOnly}
@@ -86,22 +103,28 @@ export const NavTreeItemMonolithicAction = ({
86
103
  return;
87
104
  }
88
105
 
89
- void invoke?.(caller ? { parent, caller } : { parent });
106
+ void runAction(props, caller ? { parent, caller, path } : { parent, path });
90
107
  }}
91
108
  data-testid={testId}
92
109
  />
93
110
  );
94
111
  };
95
112
 
96
- export const NavTreeItemAction = ({ monolithic, menuActions, parent: node, ...props }: NavTreeItemActionMenuProps) => {
113
+ export const NavTreeItemAction = ({
114
+ monolithic,
115
+ menuActions,
116
+ parent: node,
117
+ path,
118
+ ...props
119
+ }: NavTreeItemActionMenuProps) => {
97
120
  const { t } = useTranslation(meta.id);
98
121
 
99
122
  const monolithicAction = menuActions?.length === 1 && menuActions[0];
100
123
  const baseLabel = toLocalizedString(monolithicAction ? monolithicAction.properties!.label : props.label, t);
101
124
 
102
125
  return monolithic && menuActions?.length === 1 ? (
103
- <NavTreeItemMonolithicAction baseLabel={baseLabel} parent={node} {...menuActions[0]} />
126
+ <NavTreeItemMonolithicAction baseLabel={baseLabel} parent={node} path={path} {...menuActions[0]} />
104
127
  ) : (
105
- <NavTreeItemActionDropdownMenu {...props} label={baseLabel} parent={node} menuActions={menuActions} />
128
+ <NavTreeItemActionDropdownMenu {...props} label={baseLabel} parent={node} path={path} menuActions={menuActions} />
106
129
  );
107
130
  };