@dxos/plugin-navtree 0.8.3-staging.0fa589b → 0.8.4-main.03d5cd7b56

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 (269) hide show
  1. package/dist/lib/neutral/CommandsDialogContent-UBGIQ5FI.mjs +112 -0
  2. package/dist/lib/neutral/CommandsDialogContent-UBGIQ5FI.mjs.map +7 -0
  3. package/dist/lib/neutral/CommandsTrigger-BH5WLMHK.mjs +27 -0
  4. package/dist/lib/neutral/CommandsTrigger-BH5WLMHK.mjs.map +7 -0
  5. package/dist/lib/neutral/NavTreeContainer-6AR4BFDD.mjs +12 -0
  6. package/dist/lib/neutral/NavTreeContainer-6AR4BFDD.mjs.map +7 -0
  7. package/dist/lib/neutral/NavTreeDocumentTitle-H3E6ZLYE.mjs +22 -0
  8. package/dist/lib/neutral/NavTreeDocumentTitle-H3E6ZLYE.mjs.map +7 -0
  9. package/dist/lib/neutral/NavTreePlugin.mjs +56 -0
  10. package/dist/lib/neutral/NavTreePlugin.mjs.map +7 -0
  11. package/dist/lib/neutral/NavTreePlugin.node.mjs +18 -0
  12. package/dist/lib/neutral/NavTreePlugin.node.mjs.map +7 -0
  13. package/dist/lib/neutral/app-graph-builder-Z347LKSF.mjs +42 -0
  14. package/dist/lib/neutral/app-graph-builder-Z347LKSF.mjs.map +7 -0
  15. package/dist/lib/neutral/capabilities/index.mjs +17 -0
  16. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  17. package/dist/lib/neutral/chunk-5OPDKH6B.mjs +273 -0
  18. package/dist/lib/neutral/chunk-5OPDKH6B.mjs.map +7 -0
  19. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  20. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  21. package/dist/lib/neutral/chunk-JOSNCOV4.mjs +79 -0
  22. package/dist/lib/neutral/chunk-JOSNCOV4.mjs.map +7 -0
  23. package/dist/lib/neutral/chunk-T7QS2EFI.mjs +8 -0
  24. package/dist/lib/neutral/chunk-T7QS2EFI.mjs.map +7 -0
  25. package/dist/lib/neutral/chunk-YDE5I3P5.mjs +22 -0
  26. package/dist/lib/neutral/chunk-YDE5I3P5.mjs.map +7 -0
  27. package/dist/lib/neutral/chunk-ZSWYB7JW.mjs +27 -0
  28. package/dist/lib/neutral/chunk-ZSWYB7JW.mjs.map +7 -0
  29. package/dist/lib/neutral/components/index.mjs +878 -0
  30. package/dist/lib/neutral/components/index.mjs.map +7 -0
  31. package/dist/lib/neutral/containers/index.mjs +20 -0
  32. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  33. package/dist/lib/neutral/expose-26FV4W5D.mjs +38 -0
  34. package/dist/lib/neutral/expose-26FV4W5D.mjs.map +7 -0
  35. package/dist/lib/neutral/hooks/index.mjs +169 -0
  36. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  37. package/dist/lib/neutral/index.mjs +34 -0
  38. package/dist/lib/neutral/index.mjs.map +7 -0
  39. package/dist/lib/{browser/keyboard-DDFATRUG.mjs → neutral/keyboard-EJ65BVTC.mjs} +26 -21
  40. package/dist/lib/neutral/keyboard-EJ65BVTC.mjs.map +7 -0
  41. package/dist/lib/neutral/meta.json +1 -0
  42. package/dist/lib/neutral/meta.mjs +12 -0
  43. package/dist/lib/neutral/meta.mjs.map +7 -0
  44. package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs +13 -0
  45. package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs.map +7 -0
  46. package/dist/lib/neutral/operations/index.mjs +8 -0
  47. package/dist/lib/neutral/operations/index.mjs.map +7 -0
  48. package/dist/lib/neutral/plugin.mjs +16 -0
  49. package/dist/lib/neutral/plugin.mjs.map +7 -0
  50. package/dist/lib/neutral/react-surface-3AJ6BHL4.mjs +50 -0
  51. package/dist/lib/neutral/react-surface-3AJ6BHL4.mjs.map +7 -0
  52. package/dist/lib/neutral/state-VVITVXWM.mjs +127 -0
  53. package/dist/lib/neutral/state-VVITVXWM.mjs.map +7 -0
  54. package/dist/lib/neutral/testing/index.mjs +436 -0
  55. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  56. package/dist/lib/neutral/translations.mjs +23 -0
  57. package/dist/lib/neutral/translations.mjs.map +7 -0
  58. package/dist/lib/neutral/types/index.mjs +10 -0
  59. package/dist/lib/neutral/types/index.mjs.map +7 -0
  60. package/dist/types/src/NavTreePlugin.d.ts +3 -1
  61. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  62. package/dist/types/src/NavTreePlugin.node.d.ts +4 -0
  63. package/dist/types/src/NavTreePlugin.node.d.ts.map +1 -0
  64. package/dist/types/src/NavTreePlugin.test.d.ts +2 -0
  65. package/dist/types/src/NavTreePlugin.test.d.ts.map +1 -0
  66. package/dist/types/src/capabilities/app-graph-builder.d.ts +4 -2
  67. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  68. package/dist/types/src/capabilities/index.d.ts +7 -105
  69. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  70. package/dist/types/src/capabilities/keyboard.d.ts +3 -2
  71. package/dist/types/src/capabilities/keyboard.d.ts.map +1 -1
  72. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  73. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  74. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  75. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  76. package/dist/types/src/capabilities/state.d.ts +4 -18
  77. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  78. package/dist/types/src/components/NavTree/NavTree.d.ts +12 -5
  79. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  80. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +25 -5
  81. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  82. package/dist/types/src/components/{NavTreeContext.d.ts → NavTreeContext/NavTreeContext.d.ts} +1 -1
  83. package/dist/types/src/components/NavTreeContext/NavTreeContext.d.ts.map +1 -0
  84. package/dist/types/src/components/NavTreeContext/index.d.ts +2 -0
  85. package/dist/types/src/components/NavTreeContext/index.d.ts.map +1 -0
  86. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +13 -9
  87. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  88. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +33 -4
  89. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  90. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts +2 -2
  91. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  92. package/dist/types/src/components/Sidebar/L0Menu.d.ts +10 -5
  93. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  94. package/dist/types/src/components/Sidebar/L1Panel.d.ts +4 -8
  95. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  96. package/dist/types/src/components/Sidebar/L1Tabs.d.ts +13 -0
  97. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -0
  98. package/dist/types/src/components/Sidebar/index.d.ts +1 -0
  99. package/dist/types/src/components/Sidebar/index.d.ts.map +1 -1
  100. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
  101. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts +8 -5
  102. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  103. package/dist/types/src/components/index.d.ts +1 -4
  104. package/dist/types/src/components/index.d.ts.map +1 -1
  105. package/dist/types/src/components/types.d.ts +8 -17
  106. package/dist/types/src/components/types.d.ts.map +1 -1
  107. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts +6 -0
  108. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -0
  109. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +28 -0
  110. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts.map +1 -0
  111. package/dist/types/src/containers/CommandsDialogContent/index.d.ts +2 -0
  112. package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -0
  113. package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -0
  114. package/dist/types/src/containers/CommandsTrigger/index.d.ts +2 -0
  115. package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -0
  116. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts +9 -0
  117. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -0
  118. package/dist/types/src/containers/NavTreeContainer/index.d.ts +3 -0
  119. package/dist/types/src/containers/NavTreeContainer/index.d.ts.map +1 -0
  120. package/dist/types/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.d.ts +1 -1
  121. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -0
  122. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +2 -0
  123. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -0
  124. package/dist/types/src/containers/index.d.ts +7 -0
  125. package/dist/types/src/containers/index.d.ts.map +1 -0
  126. package/dist/types/src/experimental/Tree.d.ts +5 -6
  127. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  128. package/dist/types/src/experimental/Tree.stories.d.ts +23 -6
  129. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  130. package/dist/types/src/hooks/index.d.ts +5 -0
  131. package/dist/types/src/hooks/index.d.ts.map +1 -0
  132. package/dist/types/src/hooks/useActions.d.ts +5 -0
  133. package/dist/types/src/hooks/useActions.d.ts.map +1 -0
  134. package/dist/types/src/hooks/useLoadDescendents.d.ts +6 -0
  135. package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -0
  136. package/dist/types/src/hooks/useNavTreeModel.d.ts +7 -0
  137. package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -0
  138. package/dist/types/src/hooks/useNavTreeState.d.ts +18 -0
  139. package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -0
  140. package/dist/types/src/index.d.ts +1 -3
  141. package/dist/types/src/index.d.ts.map +1 -1
  142. package/dist/types/src/meta.d.ts +3 -4
  143. package/dist/types/src/meta.d.ts.map +1 -1
  144. package/dist/types/src/operations/expose.d.ts +5 -0
  145. package/dist/types/src/operations/expose.d.ts.map +1 -0
  146. package/dist/types/src/operations/index.d.ts +3 -0
  147. package/dist/types/src/operations/index.d.ts.map +1 -0
  148. package/dist/types/src/plugin.d.ts +4 -0
  149. package/dist/types/src/plugin.d.ts.map +1 -0
  150. package/dist/types/src/testing/app-graph-builder.d.ts +2 -3
  151. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  152. package/dist/types/src/testing/generator.d.ts +5 -6
  153. package/dist/types/src/testing/generator.d.ts.map +1 -1
  154. package/dist/types/src/testing/update-state.d.ts +2 -2
  155. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  156. package/dist/types/src/translations.d.ts +11 -13
  157. package/dist/types/src/translations.d.ts.map +1 -1
  158. package/dist/types/src/types/NavTreeCapabilities.d.ts +13 -0
  159. package/dist/types/src/types/NavTreeCapabilities.d.ts.map +1 -0
  160. package/dist/types/src/types/NavTreeEvents.d.ts +3 -0
  161. package/dist/types/src/types/NavTreeEvents.d.ts.map +1 -0
  162. package/dist/types/src/types/index.d.ts +4 -0
  163. package/dist/types/src/types/index.d.ts.map +1 -0
  164. package/dist/types/src/{types.d.ts → types/schema.d.ts} +10 -9
  165. package/dist/types/src/types/schema.d.ts.map +1 -0
  166. package/dist/types/src/util.d.ts +12 -9
  167. package/dist/types/src/util.d.ts.map +1 -1
  168. package/dist/types/tsconfig.tsbuildinfo +1 -1
  169. package/package.json +124 -63
  170. package/src/NavTreePlugin.node.ts +17 -0
  171. package/src/NavTreePlugin.test.ts +27 -0
  172. package/src/NavTreePlugin.tsx +51 -88
  173. package/src/capabilities/app-graph-builder.ts +34 -44
  174. package/src/capabilities/index.ts +10 -8
  175. package/src/capabilities/keyboard.ts +58 -51
  176. package/src/capabilities/operation-handler.ts +16 -0
  177. package/src/capabilities/react-surface.tsx +46 -38
  178. package/src/capabilities/state.ts +133 -84
  179. package/src/components/NavTree/NavTree.stories.tsx +111 -103
  180. package/src/components/NavTree/NavTree.tsx +71 -35
  181. package/src/components/{NavTreeContext.tsx → NavTreeContext/NavTreeContext.tsx} +1 -1
  182. package/src/components/NavTreeContext/index.ts +5 -0
  183. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +26 -20
  184. package/src/components/NavTreeItem/NavTreeItemAction.tsx +73 -50
  185. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +70 -38
  186. package/src/components/Sidebar/L0Menu.tsx +175 -211
  187. package/src/components/Sidebar/L1Panel.tsx +212 -136
  188. package/src/components/Sidebar/L1Tabs.tsx +42 -0
  189. package/src/components/Sidebar/index.ts +1 -0
  190. package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +9 -9
  191. package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +20 -12
  192. package/src/components/index.ts +2 -4
  193. package/src/components/types.ts +14 -19
  194. package/src/containers/CommandsDialogContent/CommandsDialogContent.stories.tsx +46 -0
  195. package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +127 -0
  196. package/src/containers/CommandsDialogContent/index.ts +5 -0
  197. package/src/containers/CommandsTrigger/CommandsTrigger.tsx +28 -0
  198. package/src/containers/CommandsTrigger/index.ts +5 -0
  199. package/src/containers/NavTreeContainer/NavTreeContainer.tsx +271 -0
  200. package/src/containers/NavTreeContainer/index.ts +6 -0
  201. package/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.tsx +5 -4
  202. package/src/containers/NavTreeDocumentTitle/index.ts +5 -0
  203. package/src/containers/index.ts +12 -0
  204. package/src/experimental/Tree.stories.tsx +111 -116
  205. package/src/experimental/Tree.tsx +17 -35
  206. package/src/hooks/index.ts +8 -0
  207. package/src/hooks/useActions.ts +34 -0
  208. package/src/hooks/useLoadDescendents.ts +23 -0
  209. package/src/hooks/useNavTreeModel.ts +118 -0
  210. package/src/hooks/useNavTreeState.ts +35 -0
  211. package/src/index.ts +1 -3
  212. package/src/meta.ts +12 -8
  213. package/src/operations/expose.ts +42 -0
  214. package/src/operations/index.ts +7 -0
  215. package/src/plugin.ts +11 -0
  216. package/src/testing/app-graph-builder.ts +167 -238
  217. package/src/testing/generator.ts +41 -39
  218. package/src/testing/update-state.ts +6 -6
  219. package/src/translations.ts +13 -12
  220. package/src/types/NavTreeCapabilities.ts +21 -0
  221. package/src/types/NavTreeEvents.ts +12 -0
  222. package/src/types/index.ts +8 -0
  223. package/src/{types.ts → types/schema.ts} +9 -8
  224. package/src/util.ts +26 -33
  225. package/dist/lib/browser/app-graph-builder-P7AY3K72.mjs +0 -45
  226. package/dist/lib/browser/app-graph-builder-P7AY3K72.mjs.map +0 -7
  227. package/dist/lib/browser/chunk-33VLL4IJ.mjs +0 -15
  228. package/dist/lib/browser/chunk-33VLL4IJ.mjs.map +0 -7
  229. package/dist/lib/browser/chunk-IEW3JD5C.mjs +0 -16
  230. package/dist/lib/browser/chunk-IEW3JD5C.mjs.map +0 -7
  231. package/dist/lib/browser/chunk-ZJPL4XJD.mjs +0 -1429
  232. package/dist/lib/browser/chunk-ZJPL4XJD.mjs.map +0 -7
  233. package/dist/lib/browser/index.mjs +0 -149
  234. package/dist/lib/browser/index.mjs.map +0 -7
  235. package/dist/lib/browser/intent-resolver-NAYKKAOP.mjs +0 -47
  236. package/dist/lib/browser/intent-resolver-NAYKKAOP.mjs.map +0 -7
  237. package/dist/lib/browser/keyboard-DDFATRUG.mjs.map +0 -7
  238. package/dist/lib/browser/meta.json +0 -1
  239. package/dist/lib/browser/react-surface-H3JRTVNU.mjs +0 -51
  240. package/dist/lib/browser/react-surface-H3JRTVNU.mjs.map +0 -7
  241. package/dist/lib/browser/state-J6AKZRB4.mjs +0 -117
  242. package/dist/lib/browser/state-J6AKZRB4.mjs.map +0 -7
  243. package/dist/types/src/capabilities/capabilities.d.ts +0 -21
  244. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  245. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  246. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  247. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts +0 -5
  248. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +0 -1
  249. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +0 -1
  250. package/dist/types/src/components/CommandsDialog/index.d.ts +0 -3
  251. package/dist/types/src/components/CommandsDialog/index.d.ts.map +0 -1
  252. package/dist/types/src/components/NavTreeContainer.d.ts +0 -9
  253. package/dist/types/src/components/NavTreeContainer.d.ts.map +0 -1
  254. package/dist/types/src/components/NavTreeContext.d.ts.map +0 -1
  255. package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +0 -1
  256. package/dist/types/src/events.d.ts +0 -4
  257. package/dist/types/src/events.d.ts.map +0 -1
  258. package/dist/types/src/hooks.d.ts +0 -3
  259. package/dist/types/src/hooks.d.ts.map +0 -1
  260. package/dist/types/src/types.d.ts.map +0 -1
  261. package/src/capabilities/capabilities.ts +0 -20
  262. package/src/capabilities/intent-resolver.ts +0 -37
  263. package/src/components/CommandsDialog/CommandsDialog.tsx +0 -118
  264. package/src/components/CommandsDialog/CommandsTrigger.tsx +0 -37
  265. package/src/components/CommandsDialog/index.ts +0 -6
  266. package/src/components/NavTreeContainer.tsx +0 -314
  267. package/src/events.ts +0 -11
  268. package/src/hooks.ts +0 -21
  269. /package/dist/types/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.d.ts +0 -0
@@ -2,52 +2,36 @@
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';
11
+
12
+ import { useLoadDescendents } from '#hooks';
9
13
 
10
- import { useLoadDescendents } from '../../hooks';
11
- import { type NavTreeItemGraphNode } from '../../types';
12
14
  import { useNavTreeContext } from '../NavTreeContext';
13
- import { L0Menu, L1Panels, type L1PanelsProps } from '../Sidebar';
15
+ import { L0Menu, L1Tabs, type L1TabsProps } from '../Sidebar';
14
16
 
15
17
  export const NAV_TREE_ITEM = 'NavTreeItem';
16
18
 
17
- export type NavTreeProps = Pick<TreeProps<NavTreeItemGraphNode>, 'id' | 'root'> & Pick<L1PanelsProps, 'open'>;
18
-
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
- );
19
+ export type NavTreeProps = { id: string; root?: Node.Node; tab: string } & Pick<L1TabsProps, 'open'>;
20
+
21
+ // TODO(wittjosiah): Refactor to Radix-style.
22
+ export const NavTree = forwardRef<HTMLDivElement, NavTreeProps>(({ id, root, tab, ...props }, forwardedRef) => {
23
+ const { onBack, onItemHover } = useNavTreeContext();
24
+ const { topLevelActions, l0Items, pinnedItems, userAccountItem, topLevelItems } = useTopLevelNavItems(root);
45
25
 
46
26
  useLoadDescendents(root);
47
27
  const path = useMemo(() => [id], [id]);
48
28
 
49
29
  return (
50
- <Tabs.Root value={tab} orientation='vertical' verticalVariant='stateless' classNames='relative'>
30
+ // TODO(thure): `Tabs.Root` forces all items that should be able to receive focus to use `Tabs.Tab(Primitive)` since
31
+ // it uses RovingFocus and doesn't support moving focus to an item that is not a tab. Assess whether this situation
32
+ // should change including whether it should motivate a change in the design/taxonomy, or if this means this should
33
+ // not use `react-ui-tabs` at all.
34
+ <Tabs.Root value={tab} orientation='vertical' classNames='relative' ref={forwardedRef}>
51
35
  <L0Menu
52
36
  menuActions={topLevelActions}
53
37
  topLevelItems={l0Items}
@@ -55,8 +39,60 @@ export const NavTree = ({ id, root, ...props }: NavTreeProps) => {
55
39
  userAccountItem={userAccountItem}
56
40
  path={path}
57
41
  parent={root}
42
+ onItemHover={onItemHover}
58
43
  />
59
- <L1Panels topLevelItems={topLevelItems} path={path} currentItemId={tab} onBack={onBack} {...props} />
44
+ <L1Tabs topLevelItems={topLevelItems} path={path} currentItemId={tab} onBack={onBack} {...props} />
60
45
  </Tabs.Root>
61
46
  );
47
+ });
48
+
49
+ /**
50
+ * Partitions root children into workspaces, pinned items, user-account, and top-level actions.
51
+ */
52
+ const useTopLevelNavItems = (root?: Node.Node) => {
53
+ const { graph } = useAppGraph();
54
+ const rootId = root?.id ?? Node.RootId;
55
+ const rootOutboundItems = useConnections(graph, rootId, 'child');
56
+ const rootActions = useGraphActions(graph, rootId);
57
+
58
+ const { topLevelActions, l0Items, pinnedItems, userAccountItem } = useMemo(() => {
59
+ const topLevelWorkspaces: Node.Node[] = [];
60
+ const outboundPinnedItems: Node.Node[] = [];
61
+ let userAccountItem: Node.Node | undefined;
62
+ for (const node of rootOutboundItems) {
63
+ switch (node.properties.disposition) {
64
+ case 'workspace':
65
+ topLevelWorkspaces.push(node);
66
+ break;
67
+ case 'pin-end':
68
+ outboundPinnedItems.push(node);
69
+ break;
70
+ case 'user-account':
71
+ userAccountItem ??= node;
72
+ break;
73
+ }
74
+ }
75
+
76
+ const topLevelActions = rootActions
77
+ .filter((action) => action.properties.disposition === 'menu')
78
+ .toSorted((a, b) => byPosition(a.properties, b.properties));
79
+ const pinnedItems = [
80
+ ...outboundPinnedItems,
81
+ ...rootActions.filter((action) => action.properties.disposition === 'pin-end'),
82
+ ].toSorted((a, b) => byPosition(a.properties, b.properties));
83
+
84
+ return {
85
+ topLevelActions,
86
+ l0Items: topLevelWorkspaces,
87
+ pinnedItems,
88
+ userAccountItem,
89
+ };
90
+ }, [rootActions, rootOutboundItems]);
91
+
92
+ const topLevelItems = useMemo(
93
+ () => [...l0Items, ...pinnedItems, ...(userAccountItem ? [userAccountItem] : [])],
94
+ [l0Items, pinnedItems, userAccountItem],
95
+ );
96
+
97
+ return { topLevelActions, l0Items, pinnedItems, userAccountItem, topLevelItems };
62
98
  };
@@ -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';
@@ -2,57 +2,63 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import * as Effect from 'effect/Effect';
6
7
 
7
- import { type Meta, type StoryObj } from '@storybook/react';
8
-
9
- import { type NodeArg } from '@dxos/app-graph';
10
- import { faker } from '@dxos/random';
11
- import { withLayout, withTheme } from '@dxos/storybook-utils';
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';
12
+ import { random } from '@dxos/random';
12
13
 
13
14
  import { NavTreeItemAction, type NavTreeItemActionMenuProps } from './NavTreeItemAction';
14
15
 
15
16
  const parent = {
16
- id: faker.string.uuid(),
17
+ id: random.string.uuid(),
17
18
  type: 'node',
18
19
  data: null,
19
20
  properties: {
20
- label: faker.lorem.words(2),
21
+ label: random.lorem.words(2),
21
22
  icon: 'ph--circle--regular',
22
23
  },
23
- } satisfies NodeArg<any>;
24
+ } satisfies Node.NodeArg<any>;
24
25
 
25
26
  // TODO(burdon): Factor out across tests.
26
- const menuActions = faker.helpers.multiple(
27
+ const menuActions = random.helpers.multiple(
27
28
  () =>
28
29
  ({
29
- id: faker.string.uuid(),
30
+ id: random.string.uuid(),
30
31
  type: 'action',
31
- data: () => {
32
- console.log('invoke');
33
- },
32
+ data: () =>
33
+ Effect.sync(() => {
34
+ console.log('invoke');
35
+ }),
34
36
  properties: {
35
- label: faker.lorem.words(2),
37
+ label: random.lorem.words(2),
36
38
  icon: 'ph--circle--regular',
37
39
  },
38
- }) satisfies NodeArg<any>,
40
+ }) satisfies Node.NodeArg<any>,
39
41
  { count: 20 },
40
42
  );
41
43
 
42
- const meta: Meta<typeof NavTreeItemAction> = {
43
- title: 'plugins/plugin-navtree/NavTreeItemAction',
44
+ const meta = {
45
+ title: 'plugins/plugin-navtree/components/NavTreeItemAction',
44
46
  component: NavTreeItemAction,
45
- decorators: [withTheme, withLayout()],
46
47
  args: {
47
48
  icon: 'ph--list--regular',
48
49
  parent,
49
50
  menuActions,
50
51
  label: 'Select action',
51
52
  } satisfies Partial<NavTreeItemActionMenuProps>,
53
+ decorators: [
54
+ withPluginManager({
55
+ plugins: [...corePlugins(), RuntimePlugin()],
56
+ }),
57
+ ],
52
58
  parameters: {
53
59
  layout: 'centered',
54
60
  },
55
- };
61
+ } satisfies Meta<typeof NavTreeItemAction>;
56
62
 
57
63
  export default meta;
58
64
 
@@ -2,22 +2,16 @@
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';
8
- import { useTranslation, toLocalizedString, IconButton, useDensityContext } from '@dxos/react-ui';
9
- import { DropdownMenu, MenuProvider, type MenuItem } from '@dxos/react-ui-menu';
10
- import { hoverableControlItem, hoverableOpenControlItem, mx } from '@dxos/react-ui-theme';
7
+ import { type Node } from '@dxos/app-graph';
8
+ import { useActionRunner } from '@dxos/plugin-graph';
9
+ import { IconButton, toLocalizedString, useDensityContext, useTranslation } from '@dxos/react-ui';
10
+ import { Menu, type MenuItem } from '@dxos/react-ui-menu';
11
+ import { composable, composableProps, hoverableControlItem, hoverableOpenControlItem } from '@dxos/ui-theme';
11
12
 
12
- import { NAVTREE_PLUGIN } from '../../meta';
13
- import { type ActionProperties } from '../../types';
14
-
15
- export type NavTreeItemActionMenuProps = ActionProperties & {
16
- parent: Node;
17
- caller?: string;
18
- monolithic?: boolean;
19
- menuActions?: Action[];
20
- };
13
+ import { meta } from '#meta';
14
+ import { type ActionProperties } from '#types';
21
15
 
22
16
  const fallbackIcon = 'ph--placeholder--regular';
23
17
 
@@ -25,82 +19,111 @@ const fineActionButtonProps = {
25
19
  size: 4 as const,
26
20
  density: 'fine' as const,
27
21
  };
22
+
28
23
  const coarseActionButtonProps = {
29
24
  size: 5 as const,
30
25
  density: 'coarse' as const,
31
26
  };
32
27
 
33
- export const NavTreeItemActionDropdownMenu = ({
34
- parent,
35
- label,
36
- icon,
37
- testId,
38
- menuActions,
39
- caller,
40
- }: NavTreeItemActionMenuProps) => {
41
- const { t } = useTranslation(NAVTREE_PLUGIN);
42
- const density = useDensityContext();
43
- return (
44
- <MenuProvider>
45
- <DropdownMenu.Root group={parent} items={menuActions as MenuItem[]} caller={caller}>
46
- <DropdownMenu.Trigger asChild>
28
+ export type NavTreeItemActionMenuProps = ActionProperties & {
29
+ parent: Node.Node;
30
+ path?: string[];
31
+ caller?: string;
32
+ monolithic?: boolean;
33
+ menuActions?: Node.Action[];
34
+ };
35
+
36
+ export const NavTreeItemActionDropdownMenu = composable<HTMLButtonElement, NavTreeItemActionMenuProps>(
37
+ ({ parent, path, label, icon, testId, menuActions, caller, ...props }, forwardedRef) => {
38
+ const { t } = useTranslation(meta.id);
39
+ const density = useDensityContext();
40
+ const runAction = useActionRunner();
41
+ const handleAction = useCallback(
42
+ (action: Node.Action, params: Node.InvokeProps = {}) => runAction(action, { ...params, path }),
43
+ [runAction, path],
44
+ );
45
+
46
+ return (
47
+ <Menu.Root caller={caller} onAction={handleAction}>
48
+ <Menu.Trigger asChild>
47
49
  <IconButton
48
50
  {...(density === 'coarse' ? coarseActionButtonProps : fineActionButtonProps)}
49
- classNames={mx('shrink-0 pli-2 pointer-fine:pli-1', hoverableControlItem, hoverableOpenControlItem)}
51
+ {...composableProps(props)}
52
+ classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
50
53
  variant='ghost'
51
54
  icon={icon ?? fallbackIcon}
52
55
  iconOnly
53
56
  label={toLocalizedString(label, t)}
54
57
  data-testid={testId}
58
+ ref={forwardedRef}
55
59
  />
56
- </DropdownMenu.Trigger>
57
- </DropdownMenu.Root>
58
- </MenuProvider>
59
- );
60
- };
60
+ </Menu.Trigger>
61
+ <Menu.Content group={parent} items={menuActions as MenuItem[]} />
62
+ </Menu.Root>
63
+ );
64
+ },
65
+ );
61
66
 
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 }) => {
67
+ NavTreeItemActionDropdownMenu.displayName = 'NavTreeItemActionDropdownMenu';
68
+
69
+ export const NavTreeItemMonolithicAction = (
70
+ props: Node.Action & {
71
+ parent: Node.Node;
72
+ path?: string[];
73
+ onAction?: (action: Node.Action) => void;
74
+ baseLabel: string;
75
+ },
76
+ ) => {
77
+ const {
78
+ parent,
79
+ path,
80
+ properties: { disabled, caller, testId, icon, variant = 'ghost', iconOnly = true } = { label: 'never' },
81
+ baseLabel,
82
+ } = props;
68
83
  const density = useDensityContext();
84
+ const runAction = useActionRunner();
69
85
  return (
70
86
  <IconButton
71
87
  {...(density === 'coarse' ? coarseActionButtonProps : fineActionButtonProps)}
72
88
  variant={variant}
73
- classNames={mx(
89
+ classNames={[
74
90
  'shrink-0',
75
91
  hoverableControlItem,
76
92
  hoverableOpenControlItem,
77
- iconOnly ? 'pli-2 pointer-fine:pli-1' : 'p-2 pointer-fine:p-2 mie-1',
78
- )}
93
+ iconOnly ? 'px-2 pointer-fine:px-1' : 'p-2 pointer-fine:p-2 me-1',
94
+ ]}
79
95
  icon={icon ?? fallbackIcon}
80
96
  iconOnly={iconOnly}
81
97
  label={baseLabel}
82
98
  disabled={disabled}
83
99
  onClick={(event) => {
100
+ event.stopPropagation();
84
101
  if (disabled) {
85
102
  return;
86
103
  }
87
- event.stopPropagation();
88
- void invoke?.(caller ? { parent, caller } : { parent });
104
+
105
+ void runAction(props, caller ? { parent, caller, path } : { parent, path });
89
106
  }}
90
107
  data-testid={testId}
91
108
  />
92
109
  );
93
110
  };
94
111
 
95
- export const NavTreeItemAction = ({ monolithic, menuActions, parent: node, ...props }: NavTreeItemActionMenuProps) => {
96
- const { t } = useTranslation(NAVTREE_PLUGIN);
112
+ export const NavTreeItemAction = ({
113
+ monolithic,
114
+ menuActions,
115
+ menuType,
116
+ parent,
117
+ path,
118
+ ...props
119
+ }: NavTreeItemActionMenuProps) => {
120
+ const { t } = useTranslation(meta.id);
97
121
 
98
122
  const monolithicAction = menuActions?.length === 1 && menuActions[0];
99
123
  const baseLabel = toLocalizedString(monolithicAction ? monolithicAction.properties!.label : props.label, t);
100
-
101
124
  return monolithic && menuActions?.length === 1 ? (
102
- <NavTreeItemMonolithicAction baseLabel={baseLabel} parent={node} {...menuActions[0]} />
125
+ <NavTreeItemMonolithicAction baseLabel={baseLabel} parent={parent} path={path} {...menuActions[0]} />
103
126
  ) : (
104
- <NavTreeItemActionDropdownMenu {...props} label={baseLabel} parent={node} menuActions={menuActions} />
127
+ <NavTreeItemActionDropdownMenu {...props} label={baseLabel} parent={parent} path={path} menuActions={menuActions} />
105
128
  );
106
129
  };
@@ -2,72 +2,104 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, { Fragment, memo } from 'react';
5
+ import React, { Fragment, memo, useMemo } from 'react';
6
6
 
7
- import { isAction } from '@dxos/app-graph';
8
- import { DensityProvider, Popover, toLocalizedString, Treegrid, useTranslation } from '@dxos/react-ui';
7
+ import { Node } from '@dxos/app-graph';
8
+ import { Popover, Treegrid, toLocalizedString, useTranslation } from '@dxos/react-ui';
9
+
10
+ import { useActions } from '#hooks';
11
+ import { meta } from '#meta';
9
12
 
10
- import { NavTreeItemAction } from './NavTreeItemAction';
11
- import { useLoadDescendents } from '../../hooks';
12
- import { NAVTREE_PLUGIN } from '../../meta';
13
13
  import { NAV_TREE_ITEM } from '../NavTree';
14
14
  import { useNavTreeContext } from '../NavTreeContext';
15
- import type { NavTreeItemColumnsProps } from '../types';
15
+ import { type NavTreeItemColumnsProps } from '../types';
16
+ import { NavTreeItemAction } from './NavTreeItemAction';
16
17
 
17
- export const NavTreeItemColumns = memo(({ path, item, open, density = 'fine' }: NavTreeItemColumnsProps) => {
18
- const { t } = useTranslation(NAVTREE_PLUGIN);
19
- const { getActions, renderItemEnd: ItemEnd, popoverAnchorId } = useNavTreeContext();
18
+ export const NavTreeItemColumns = memo(({ path, item, open }: NavTreeItemColumnsProps) => {
19
+ const { t } = useTranslation(meta.id);
20
+ const { renderItemEnd: ItemEnd, popoverAnchorId } = useNavTreeContext();
20
21
 
21
22
  const level = path.length - 2;
22
-
23
- const { actions: _actions, groupedActions } = getActions(item);
24
- const [primaryAction, ...secondaryActions] = _actions.toSorted((a, b) =>
25
- a.properties?.disposition === 'list-item-primary' ? -1 : 1,
23
+ const { actions: actionsProp, groupedActions } = useActions(item);
24
+ const sortedActions = useMemo(
25
+ () =>
26
+ actionsProp.toSorted((actionA, actionB) => {
27
+ const primaryA = actionA.properties?.disposition === 'list-item-primary';
28
+ const primaryB = actionB.properties?.disposition === 'list-item-primary';
29
+ if (primaryA && !primaryB) {
30
+ return -1;
31
+ }
32
+ if (primaryB && !primaryA) {
33
+ return 1;
34
+ }
35
+ return 0;
36
+ }),
37
+ [actionsProp],
26
38
  );
39
+ const [primaryAction, ...secondaryActions] = sortedActions;
27
40
 
28
- const actions = (primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : _actions)
29
- .flatMap((action) => (isAction(action) ? [action] : []))
30
- .filter((a) => ['list-item', 'list-item-primary'].includes(a.properties?.disposition));
41
+ const actions = useMemo(
42
+ () =>
43
+ (primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : sortedActions)
44
+ .flatMap((action) => (Node.isAction(action) ? [action] : []))
45
+ .filter((a) => ['list-item', 'list-item-primary'].includes(a.properties?.disposition)),
46
+ [sortedActions, primaryAction],
47
+ );
31
48
 
32
- const ActionRoot = popoverAnchorId === `dxos.org/ui/${NAV_TREE_ITEM}/${item.id}` ? Popover.Anchor : Fragment;
49
+ const primaryMenuActions = useMemo(
50
+ () =>
51
+ primaryAction
52
+ ? Node.isAction(primaryAction)
53
+ ? [primaryAction]
54
+ : groupedActions[primaryAction?.id ?? '']
55
+ : undefined,
56
+ [primaryAction, groupedActions],
57
+ );
33
58
 
34
- useLoadDescendents(item);
35
- useLoadDescendents(primaryAction && !isAction(primaryAction) ? primaryAction : undefined);
59
+ const ActionRoot = popoverAnchorId === `${NAV_TREE_ITEM}:${item.id}` ? Popover.Anchor : Fragment;
36
60
 
37
61
  return (
38
- <DensityProvider density={density}>
39
- <div role='none' className='contents app-no-drag'>
40
- {primaryAction?.properties?.disposition === 'list-item-primary' && !primaryAction?.properties?.disabled ? (
62
+ <div className='contents dx-app-no-drag'>
63
+ {primaryAction?.properties?.disposition === 'list-item-primary' && !primaryAction?.properties?.disabled ? (
64
+ <Treegrid.Cell classNames='contents'>
41
65
  <NavTreeItemAction
42
66
  testId={primaryAction.properties?.testId}
43
67
  label={toLocalizedString(primaryAction.properties?.label, t)}
44
68
  icon={primaryAction.properties?.icon ?? 'ph--placeholder--regular'}
45
69
  parent={item}
46
- monolithic={isAction(primaryAction)}
47
- menuActions={isAction(primaryAction) ? [primaryAction] : groupedActions[primaryAction.id]}
70
+ path={path}
71
+ monolithic={Node.isAction(primaryAction)}
72
+ menuActions={primaryMenuActions}
48
73
  menuType={primaryAction.properties?.menuType}
49
74
  caller={NAV_TREE_ITEM}
50
75
  />
51
- ) : (
52
- <Treegrid.Cell />
53
- )}
54
- <ActionRoot>
55
- {actions.length > 0 ? (
76
+ </Treegrid.Cell>
77
+ ) : (
78
+ <Treegrid.Cell role='none' />
79
+ )}
80
+ <ActionRoot>
81
+ {actions.length > 0 ? (
82
+ <Treegrid.Cell classNames='contents'>
56
83
  <NavTreeItemAction
57
84
  testId={`navtree.treeItem.actionsLevel${level}`}
58
- label={t('tree item actions label')}
85
+ label={t('tree-item-actions.label')}
59
86
  icon='ph--dots-three-vertical--regular'
60
87
  parent={item}
88
+ path={path}
61
89
  menuActions={actions}
62
90
  menuType='dropdown'
63
91
  caller={NAV_TREE_ITEM}
64
92
  />
65
- ) : (
66
- <Treegrid.Cell />
67
- )}
68
- </ActionRoot>
69
- {ItemEnd && <ItemEnd node={item} open={open} />}
70
- </div>
71
- </DensityProvider>
93
+ </Treegrid.Cell>
94
+ ) : (
95
+ <Treegrid.Cell role='none' />
96
+ )}
97
+ </ActionRoot>
98
+ {ItemEnd && (
99
+ <Treegrid.Cell classNames='contents'>
100
+ <ItemEnd node={item} open={open} />
101
+ </Treegrid.Cell>
102
+ )}
103
+ </div>
72
104
  );
73
105
  });