@dxos/plugin-navtree 0.8.4-main.bc674ce → 0.8.4-main.bcb3aa67d6

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 (187) hide show
  1. package/dist/lib/browser/index.mjs +137 -72
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  5. package/dist/types/src/capabilities/app-graph-builder.d.ts +6 -0
  6. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  7. package/dist/types/src/capabilities/index.d.ts +7 -5
  8. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  9. package/dist/types/src/capabilities/keyboard.d.ts +5 -0
  10. package/dist/types/src/capabilities/keyboard.d.ts.map +1 -0
  11. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  12. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  13. package/dist/types/src/capabilities/react-surface.d.ts +5 -0
  14. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  15. package/dist/types/src/capabilities/state.d.ts +6 -0
  16. package/dist/types/src/capabilities/state.d.ts.map +1 -0
  17. package/dist/types/src/components/NavTree/NavTree.d.ts +10 -32
  18. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  19. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +10 -14
  20. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  21. package/dist/types/src/components/{NavTreeContext.d.ts → NavTreeContext/NavTreeContext.d.ts} +1 -1
  22. package/dist/types/src/components/NavTreeContext/NavTreeContext.d.ts.map +1 -0
  23. package/dist/types/src/components/NavTreeContext/index.d.ts +2 -0
  24. package/dist/types/src/components/NavTreeContext/index.d.ts.map +1 -0
  25. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +11 -3
  26. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  27. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +1 -1
  28. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  29. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  30. package/dist/types/src/components/Sidebar/L0Menu.d.ts +4 -1
  31. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  32. package/dist/types/src/components/Sidebar/L1Panel.d.ts +3 -6
  33. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  34. package/dist/types/src/components/Sidebar/L1Tabs.d.ts +3 -2
  35. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  36. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/index.d.ts +0 -3
  38. package/dist/types/src/components/index.d.ts.map +1 -1
  39. package/dist/types/src/components/types.d.ts +5 -13
  40. package/dist/types/src/components/types.d.ts.map +1 -1
  41. package/dist/types/src/{components/CommandsDialog/CommandsDialog.d.ts → containers/CommandsDialogContent/CommandsDialogContent.d.ts} +1 -1
  42. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -0
  43. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +29 -0
  44. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts.map +1 -0
  45. package/dist/types/src/containers/CommandsDialogContent/index.d.ts +3 -0
  46. package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -0
  47. package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -0
  48. package/dist/types/src/containers/CommandsTrigger/index.d.ts +3 -0
  49. package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -0
  50. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts +9 -0
  51. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -0
  52. package/dist/types/src/containers/NavTreeContainer/index.d.ts +3 -0
  53. package/dist/types/src/containers/NavTreeContainer/index.d.ts.map +1 -0
  54. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -0
  55. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +3 -0
  56. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -0
  57. package/dist/types/src/containers/index.d.ts +7 -0
  58. package/dist/types/src/containers/index.d.ts.map +1 -0
  59. package/dist/types/src/experimental/Tree.stories.d.ts +18 -4
  60. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  61. package/dist/types/src/hooks/index.d.ts +5 -0
  62. package/dist/types/src/hooks/index.d.ts.map +1 -0
  63. package/dist/types/src/hooks/useActions.d.ts +5 -0
  64. package/dist/types/src/hooks/useActions.d.ts.map +1 -0
  65. package/dist/types/src/hooks/useLoadDescendents.d.ts +6 -0
  66. package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -0
  67. package/dist/types/src/hooks/useNavTreeModel.d.ts +7 -0
  68. package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -0
  69. package/dist/types/src/{hooks.d.ts → hooks/useNavTreeState.d.ts} +2 -14
  70. package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -0
  71. package/dist/types/src/meta.d.ts.map +1 -1
  72. package/dist/types/src/operations/expose.d.ts +5 -0
  73. package/dist/types/src/operations/expose.d.ts.map +1 -0
  74. package/dist/types/src/operations/index.d.ts +3 -0
  75. package/dist/types/src/operations/index.d.ts.map +1 -0
  76. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  77. package/dist/types/src/testing/generator.d.ts +3 -3
  78. package/dist/types/src/testing/generator.d.ts.map +1 -1
  79. package/dist/types/src/translations.d.ts +9 -10
  80. package/dist/types/src/translations.d.ts.map +1 -1
  81. package/dist/types/src/types/events.d.ts.map +1 -1
  82. package/dist/types/src/util.d.ts +6 -2
  83. package/dist/types/src/util.d.ts.map +1 -1
  84. package/dist/types/tsconfig.tsbuildinfo +1 -1
  85. package/package.json +54 -43
  86. package/src/NavTreePlugin.tsx +26 -27
  87. package/src/capabilities/{app-graph-builder/app-graph-builder.ts → app-graph-builder.ts} +6 -5
  88. package/src/capabilities/index.ts +11 -5
  89. package/src/capabilities/{keyboard/keyboard.ts → keyboard.ts} +7 -6
  90. package/src/capabilities/operation-handler.ts +16 -0
  91. package/src/capabilities/{react-surface/react-surface.tsx → react-surface.tsx} +13 -14
  92. package/src/capabilities/{state/state.ts → state.ts} +43 -15
  93. package/src/components/NavTree/NavTree.stories.tsx +26 -24
  94. package/src/components/NavTree/NavTree.tsx +63 -22
  95. package/src/components/{NavTreeContext.tsx → NavTreeContext/NavTreeContext.tsx} +1 -1
  96. package/src/components/NavTreeContext/index.ts +5 -0
  97. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +1 -3
  98. package/src/components/NavTreeItem/NavTreeItemAction.tsx +56 -39
  99. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +42 -16
  100. package/src/components/Sidebar/L0Menu.tsx +99 -109
  101. package/src/components/Sidebar/L1Panel.tsx +252 -94
  102. package/src/components/Sidebar/L1Tabs.tsx +13 -3
  103. package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +2 -3
  104. package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +1 -1
  105. package/src/components/index.ts +0 -3
  106. package/src/components/types.ts +12 -17
  107. package/src/containers/CommandsDialogContent/CommandsDialogContent.stories.tsx +46 -0
  108. package/src/{components/CommandsDialog/CommandsDialog.tsx → containers/CommandsDialogContent/CommandsDialogContent.tsx} +23 -21
  109. package/src/containers/CommandsDialogContent/index.ts +7 -0
  110. package/src/containers/CommandsTrigger/CommandsTrigger.tsx +28 -0
  111. package/src/containers/CommandsTrigger/index.ts +7 -0
  112. package/src/{components → containers/NavTreeContainer}/NavTreeContainer.tsx +76 -142
  113. package/src/containers/NavTreeContainer/index.ts +6 -0
  114. package/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.tsx +2 -2
  115. package/src/containers/NavTreeDocumentTitle/index.ts +7 -0
  116. package/src/containers/index.ts +12 -0
  117. package/src/experimental/Tree.stories.tsx +54 -57
  118. package/src/experimental/Tree.tsx +3 -3
  119. package/src/hooks/index.ts +8 -0
  120. package/src/hooks/useActions.ts +34 -0
  121. package/src/hooks/useLoadDescendents.ts +23 -0
  122. package/src/hooks/useNavTreeModel.ts +118 -0
  123. package/src/{hooks.ts → hooks/useNavTreeState.ts} +3 -34
  124. package/src/meta.ts +2 -2
  125. package/src/operations/expose.ts +42 -0
  126. package/src/operations/index.ts +7 -0
  127. package/src/testing/app-graph-builder.ts +34 -30
  128. package/src/testing/generator.ts +4 -4
  129. package/src/translations.ts +9 -10
  130. package/src/types/capabilities.ts +2 -2
  131. package/src/types/events.ts +4 -3
  132. package/src/util.ts +19 -5
  133. package/dist/lib/browser/app-graph-builder-RSMA6I22.mjs +0 -43
  134. package/dist/lib/browser/app-graph-builder-RSMA6I22.mjs.map +0 -7
  135. package/dist/lib/browser/chunk-6YPQ625I.mjs +0 -1293
  136. package/dist/lib/browser/chunk-6YPQ625I.mjs.map +0 -7
  137. package/dist/lib/browser/chunk-AGQDZBPL.mjs +0 -23
  138. package/dist/lib/browser/chunk-AGQDZBPL.mjs.map +0 -7
  139. package/dist/lib/browser/chunk-YKTYPINZ.mjs +0 -19
  140. package/dist/lib/browser/chunk-YKTYPINZ.mjs.map +0 -7
  141. package/dist/lib/browser/keyboard-32EH3N4K.mjs +0 -52
  142. package/dist/lib/browser/keyboard-32EH3N4K.mjs.map +0 -7
  143. package/dist/lib/browser/operation-resolver-IB75DC5O.mjs +0 -52
  144. package/dist/lib/browser/operation-resolver-IB75DC5O.mjs.map +0 -7
  145. package/dist/lib/browser/react-surface-F6CVMS3Y.mjs +0 -58
  146. package/dist/lib/browser/react-surface-F6CVMS3Y.mjs.map +0 -7
  147. package/dist/lib/browser/state-EVYQOUI3.mjs +0 -120
  148. package/dist/lib/browser/state-EVYQOUI3.mjs.map +0 -7
  149. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +0 -6
  150. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +0 -1
  151. package/dist/types/src/capabilities/app-graph-builder/index.d.ts +0 -3
  152. package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +0 -1
  153. package/dist/types/src/capabilities/keyboard/index.d.ts +0 -3
  154. package/dist/types/src/capabilities/keyboard/index.d.ts.map +0 -1
  155. package/dist/types/src/capabilities/keyboard/keyboard.d.ts +0 -5
  156. package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +0 -1
  157. package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
  158. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
  159. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
  160. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
  161. package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
  162. package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
  163. package/dist/types/src/capabilities/react-surface/react-surface.d.ts +0 -5
  164. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
  165. package/dist/types/src/capabilities/state/index.d.ts +0 -3
  166. package/dist/types/src/capabilities/state/index.d.ts.map +0 -1
  167. package/dist/types/src/capabilities/state/state.d.ts +0 -6
  168. package/dist/types/src/capabilities/state/state.d.ts.map +0 -1
  169. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +0 -1
  170. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +0 -1
  171. package/dist/types/src/components/CommandsDialog/index.d.ts +0 -3
  172. package/dist/types/src/components/CommandsDialog/index.d.ts.map +0 -1
  173. package/dist/types/src/components/NavTreeContainer.d.ts +0 -16
  174. package/dist/types/src/components/NavTreeContainer.d.ts.map +0 -1
  175. package/dist/types/src/components/NavTreeContext.d.ts.map +0 -1
  176. package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +0 -1
  177. package/dist/types/src/hooks.d.ts.map +0 -1
  178. package/src/capabilities/app-graph-builder/index.ts +0 -7
  179. package/src/capabilities/keyboard/index.ts +0 -7
  180. package/src/capabilities/operation-resolver/index.ts +0 -7
  181. package/src/capabilities/operation-resolver/operation-resolver.ts +0 -41
  182. package/src/capabilities/react-surface/index.ts +0 -7
  183. package/src/capabilities/state/index.ts +0 -7
  184. package/src/components/CommandsDialog/CommandsTrigger.tsx +0 -26
  185. package/src/components/CommandsDialog/index.ts +0 -6
  186. /package/dist/types/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.d.ts +0 -0
  187. /package/dist/types/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.d.ts +0 -0
@@ -0,0 +1,23 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useEffect } from 'react';
6
+
7
+ import { type Node } from '@dxos/app-graph';
8
+ import { useAppGraph } from '@dxos/app-toolkit/ui';
9
+ import { Graph } from '@dxos/plugin-graph';
10
+
11
+ /**
12
+ * Expands a root node in the app graph.
13
+ */
14
+ export const useLoadDescendents = (root?: Node.Node) => {
15
+ const { graph } = useAppGraph();
16
+ useEffect(() => {
17
+ if (!root) {
18
+ return;
19
+ }
20
+
21
+ Graph.expand(graph, root.id, 'child');
22
+ }, [graph, root]);
23
+ };
@@ -0,0 +1,118 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Atom } from '@effect-atom/atom-react';
6
+ import * as Option from 'effect/Option';
7
+ import { useMemo } from 'react';
8
+
9
+ import { useAppGraph } from '@dxos/app-toolkit/ui';
10
+ import { PLANK_COMPANION_TYPE } from '@dxos/plugin-deck/types';
11
+ import { type Node } from '@dxos/plugin-graph';
12
+ import { Path, type TreeModel } from '@dxos/react-ui-list';
13
+ import { mx } from '@dxos/ui-theme';
14
+
15
+ import { type NavTreeItemGraphNode } from '#types';
16
+ import { filterItems } from '../util';
17
+
18
+ import { useNavTreeState } from './useNavTreeState';
19
+
20
+ // TODO(wittjosiah): Move companion/hidden nodes to their own edge categories so this filter is unnecessary.
21
+ const isVisibleChild = (node: Node.Node): boolean =>
22
+ node.type !== PLANK_COMPANION_TYPE && node.properties.disposition !== 'hidden';
23
+
24
+ /** Create an atom family for item display props keyed by path. */
25
+ const createItemPropsFamily = (graph: ReturnType<typeof useAppGraph>['graph']) =>
26
+ Atom.family((pathKey: string) => {
27
+ const path = Path.parts(pathKey);
28
+ const id = Path.last(pathKey);
29
+ return Atom.make((get) => {
30
+ const nodeOpt = get(graph.node(id));
31
+ const node = Option.getOrElse(nodeOpt, () => undefined);
32
+ if (!node) {
33
+ return { id, label: id };
34
+ }
35
+ const safeChildren = get(graph.connections(node.id, 'child')).filter((child) => !path.includes(child.id));
36
+ const visibleChildren = safeChildren.filter(isVisibleChild);
37
+ const parentOf =
38
+ visibleChildren.length > 0
39
+ ? visibleChildren.map((child) => child.id)
40
+ : node.properties.role === 'branch'
41
+ ? []
42
+ : undefined;
43
+ const parentId = path.length >= 2 ? path[path.length - 2] : undefined;
44
+ const parentNode = parentId ? Option.getOrElse(get(graph.node(parentId)), () => undefined) : undefined;
45
+ const droppable =
46
+ node.properties.droppable === false || parentNode?.properties.childrenDroppable === false ? false : undefined;
47
+
48
+ return {
49
+ id: node.id,
50
+ parentOf,
51
+ disabled: node.properties.disabled,
52
+ draggable: node.properties.draggable,
53
+ droppable,
54
+ label: node.properties.label ?? node.id,
55
+ className: mx(node.properties.className, node.properties.modified && 'italic'),
56
+ headingClassName: node.properties.headingClassName,
57
+ icon: node.properties.icon,
58
+ iconHue: node.properties.iconHue,
59
+ testId: node.properties.testId,
60
+ };
61
+ }).pipe(Atom.keepAlive);
62
+ });
63
+
64
+ /** Create an atom family for outbound child IDs keyed by parent ID (pre-sorted by position on write). */
65
+ const createChildIdsFamily = (graph: ReturnType<typeof useAppGraph>['graph']) =>
66
+ Atom.family((id: string) =>
67
+ Atom.make((get) => get(graph.connections(id, 'child')).map((child) => child.id)).pipe(Atom.keepAlive),
68
+ );
69
+
70
+ /** Create an atom family for item resolution keyed by ID. */
71
+ const createItemFamily = (graph: ReturnType<typeof useAppGraph>['graph']) =>
72
+ Atom.family((id: string) =>
73
+ Atom.make((get) => {
74
+ const node = Option.getOrElse(get(graph.node(id)), () => undefined);
75
+ return node && filterItems(node) ? node : undefined;
76
+ }).pipe(Atom.keepAlive),
77
+ );
78
+
79
+ /** Create an atom family for open state keyed by path. */
80
+ const createItemOpenFamily = (getItemAtom: ReturnType<typeof useNavTreeState>['getItemAtom']) =>
81
+ Atom.family((pathKey: string) => {
82
+ const path = Path.parts(pathKey);
83
+ const stateAtom = getItemAtom(path);
84
+ return Atom.make((get) => get(stateAtom).open).pipe(Atom.keepAlive);
85
+ });
86
+
87
+ /** Create an atom family for current (selected) state keyed by path. */
88
+ const createItemCurrentFamily = (getItemAtom: ReturnType<typeof useNavTreeState>['getItemAtom']) =>
89
+ Atom.family((pathKey: string) => {
90
+ const path = Path.parts(pathKey);
91
+ const stateAtom = getItemAtom(path);
92
+ return Atom.make((get) => get(stateAtom).current).pipe(Atom.keepAlive);
93
+ });
94
+
95
+ /**
96
+ * Creates a TreeModel backed by the app graph and navtree state.
97
+ */
98
+ export const useNavTreeModel = (rootId: string): TreeModel<NavTreeItemGraphNode> => {
99
+ const { graph } = useAppGraph();
100
+ const { getItemAtom } = useNavTreeState();
101
+
102
+ const itemPropsFamily = useMemo(() => createItemPropsFamily(graph), [graph]);
103
+ const childIdsFamily = useMemo(() => createChildIdsFamily(graph), [graph]);
104
+ const itemFamily = useMemo(() => createItemFamily(graph), [graph]);
105
+ const itemOpenFamily = useMemo(() => createItemOpenFamily(getItemAtom), [getItemAtom]);
106
+ const itemCurrentFamily = useMemo(() => createItemCurrentFamily(getItemAtom), [getItemAtom]);
107
+
108
+ return useMemo(
109
+ () => ({
110
+ item: (id: string) => itemFamily(id),
111
+ itemProps: (path: string[]) => itemPropsFamily(Path.create(...path)),
112
+ itemOpen: (path: string[]) => itemOpenFamily(Path.create(...path)),
113
+ itemCurrent: (path: string[]) => itemCurrentFamily(Path.create(...path)),
114
+ childIds: (parentId?: string) => childIdsFamily(parentId ?? rootId),
115
+ }),
116
+ [itemFamily, itemPropsFamily, itemOpenFamily, itemCurrentFamily, childIdsFamily, rootId],
117
+ );
118
+ };
@@ -3,25 +3,11 @@
3
3
  //
4
4
 
5
5
  import { type Atom, useAtomValue } from '@effect-atom/atom-react';
6
- import { useEffect, useMemo } from 'react';
6
+ import { useMemo } from 'react';
7
7
 
8
- import { useCapability } from '@dxos/app-framework/react';
9
- import { type Node } from '@dxos/app-graph';
8
+ import { useCapability } from '@dxos/app-framework/ui';
10
9
 
11
- import { useNavTreeContext } from './components';
12
- import { NavTreeCapabilities } from './types';
13
-
14
- export const useLoadDescendents = (root?: Node.Node) => {
15
- const { loadDescendents } = useNavTreeContext();
16
- useEffect(() => {
17
- const frame = requestAnimationFrame(() => {
18
- if (root) {
19
- void loadDescendents?.(root);
20
- }
21
- });
22
- return () => cancelAnimationFrame(frame);
23
- }, [root]);
24
- };
10
+ import { NavTreeCapabilities } from '#types';
25
11
 
26
12
  export type UseNavTreeStateResult = {
27
13
  getItem: (path: string[]) => NavTreeCapabilities.NavTreeItemState;
@@ -43,28 +29,11 @@ export const useNavTreeState = (): UseNavTreeStateResult => {
43
29
  */
44
30
  export const useNavTreeItemState = (path: string[]): NavTreeCapabilities.NavTreeItemState => {
45
31
  const { getItemAtom } = useCapability(NavTreeCapabilities.State);
46
- // Memoize the path to avoid creating new atoms on every render.
47
32
  const pathKey = useMemo(() => path.join('~'), [path]);
48
33
  const atom = useMemo(() => getItemAtom(path), [getItemAtom, pathKey]);
49
34
  return useAtomValue(atom);
50
35
  };
51
36
 
52
- /**
53
- * Hook that subscribes to the open state for a tree item.
54
- * Designed to be passed to Tree component as useIsOpen prop.
55
- */
56
- export const useIsOpen = (path: string[], _item?: any): boolean => {
57
- return useNavTreeItemState(path).open;
58
- };
59
-
60
- /**
61
- * Hook that subscribes to the current state for a tree item.
62
- * Designed to be passed to Tree component as useIsCurrent prop.
63
- */
64
- export const useIsCurrent = (path: string[], _item?: any): boolean => {
65
- return useNavTreeItemState(path).current;
66
- };
67
-
68
37
  /**
69
38
  * Hook that subscribes to the alternateTree state for a tree item.
70
39
  */
package/src/meta.ts CHANGED
@@ -6,7 +6,7 @@ import { type Plugin } from '@dxos/app-framework';
6
6
  import { trim } from '@dxos/util';
7
7
 
8
8
  export const meta: Plugin.Meta = {
9
- id: 'dxos.org/plugin/navtree',
9
+ id: 'org.dxos.plugin.navtree',
10
10
  name: 'Navtree',
11
11
  description: trim`
12
12
  Hierarchical navigation tree for browsing spaces, folders, and objects.
@@ -17,4 +17,4 @@ export const meta: Plugin.Meta = {
17
17
  // TODO(wittjosiah): Factor out.
18
18
  export const KEY_BINDING = 'KeyBinding';
19
19
  // TODO(wittjosiah): Factor out.
20
- export const COMMANDS_DIALOG = `${meta.id}/CommandsDialog`;
20
+ export const COMMANDS_DIALOG = `${meta.id}.commands-dialog`;
@@ -0,0 +1,42 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Effect from 'effect/Effect';
6
+ import * as Option from 'effect/Option';
7
+
8
+ import { Capability } from '@dxos/app-framework';
9
+ import { AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';
10
+ import { log } from '@dxos/log';
11
+ import { Operation } from '@dxos/operation';
12
+ import { Graph } from '@dxos/plugin-graph';
13
+ import { expandAttendableId } from '@dxos/react-ui-attention';
14
+
15
+ import { NavTreeCapabilities } from '../types';
16
+
17
+ const handler: Operation.WithHandler<typeof LayoutOperation.Expose> = LayoutOperation.Expose.pipe(
18
+ Operation.withHandler(
19
+ Effect.fnUntraced(function* ({ subject }) {
20
+ const { graph } = yield* Capability.get(AppCapabilities.AppGraph);
21
+ const { getItem, setItem } = yield* Capability.get(NavTreeCapabilities.State);
22
+
23
+ const prefixes = expandAttendableId(subject);
24
+
25
+ for (const qualifiedId of prefixes) {
26
+ Graph.expand(graph, qualifiedId, 'child');
27
+
28
+ const treePath = prefixes.slice(0, prefixes.indexOf(qualifiedId) + 1);
29
+ const state = getItem(treePath);
30
+ if (!state.open) {
31
+ setItem(treePath, 'open', true);
32
+ }
33
+ }
34
+
35
+ if (Option.isNone(Graph.getNode(graph, subject))) {
36
+ log('Node not found after expansion', { subject });
37
+ }
38
+ }),
39
+ ),
40
+ );
41
+
42
+ export default handler;
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { OperationHandlerSet } from '@dxos/operation';
6
+
7
+ export const NavTreeOperationHandlerSet = OperationHandlerSet.lazy(() => import('./expose'));
@@ -7,7 +7,7 @@ import * as Effect from 'effect/Effect';
7
7
 
8
8
  import { type BuilderExtensions } from '@dxos/app-graph';
9
9
  import { log } from '@dxos/log';
10
- import { GraphBuilder, NodeMatcher } from '@dxos/plugin-graph';
10
+ import { GraphBuilder, Node, NodeMatcher } from '@dxos/plugin-graph';
11
11
  import { faker } from '@dxos/random';
12
12
 
13
13
  export const storybookGraphBuilders = (): BuilderExtensions => {
@@ -31,7 +31,7 @@ export const storybookGraphBuilders = (): BuilderExtensions => {
31
31
  actions: () =>
32
32
  Effect.succeed(
33
33
  Array.from({ length: 5 }, (_, i) => ({
34
- id: `app-menu/action-${i}`,
34
+ id: `action-${i}`,
35
35
  data: Effect.fnUntraced(function* () {
36
36
  log.info('action', { id: 'app-menu', index: i });
37
37
  }),
@@ -49,7 +49,7 @@ export const storybookGraphBuilders = (): BuilderExtensions => {
49
49
  match: NodeMatcher.whenRoot,
50
50
  connector: () =>
51
51
  Effect.succeed([
52
- {
52
+ Node.make({
53
53
  id: 'user-account',
54
54
  type: 'user-account',
55
55
  properties: {
@@ -62,32 +62,32 @@ export const storybookGraphBuilders = (): BuilderExtensions => {
62
62
  status: 'active',
63
63
  },
64
64
  nodes: [
65
- {
65
+ Node.make({
66
66
  id: 'profile',
67
67
  type: 'profile',
68
68
  properties: {
69
69
  label: 'Profile',
70
70
  icon: 'ph--user--regular',
71
71
  },
72
- },
73
- {
72
+ }),
73
+ Node.make({
74
74
  id: 'devices',
75
75
  type: 'devices',
76
76
  properties: {
77
77
  label: 'Devices',
78
78
  icon: 'ph--devices--regular',
79
79
  },
80
- },
81
- {
80
+ }),
81
+ Node.make({
82
82
  id: 'security',
83
83
  type: 'security',
84
84
  properties: {
85
85
  label: 'Security',
86
86
  icon: 'ph--key--regular',
87
87
  },
88
- },
88
+ }),
89
89
  ],
90
- },
90
+ }),
91
91
  ]),
92
92
  }),
93
93
  // Create space (workspace) nodes directly under root.
@@ -111,16 +111,18 @@ export const storybookGraphBuilders = (): BuilderExtensions => {
111
111
  return value;
112
112
  });
113
113
 
114
- return Array.from({ length: get(count) }, (_, i) => ({
115
- id: `space-${i}`,
116
- type: 'space',
117
- properties: getProperties(`space-${i}`, {
118
- label: `Space ${i}`,
119
- icon: faker.properties.icon(),
120
- hue: faker.properties.hue(),
121
- disposition: 'workspace',
114
+ return Array.from({ length: get(count) }, (_, i) =>
115
+ Node.make({
116
+ id: `space-${i}`,
117
+ type: 'space',
118
+ properties: getProperties(`space-${i}`, {
119
+ label: `Space ${i}`,
120
+ icon: faker.properties.icon(),
121
+ hue: faker.properties.hue(),
122
+ disposition: 'workspace',
123
+ }),
122
124
  }),
123
- }));
125
+ );
124
126
  }),
125
127
  }),
126
128
  // Create space actions.
@@ -130,11 +132,11 @@ export const storybookGraphBuilders = (): BuilderExtensions => {
130
132
  actions: () =>
131
133
  Effect.succeed(
132
134
  Array.from({ length: 5 }, (_, i) => ({
133
- id: `space-actions/action-${i}`,
135
+ id: `action-${i}`,
134
136
  data: Effect.fnUntraced(function* () {
135
137
  log.info('action', { id: 'space-actions', index: i });
136
138
  }),
137
- properties: getProperties(`space-actions/action-${i}`, {
139
+ properties: getProperties(`action-${i}`, {
138
140
  label: `Action ${i}`,
139
141
  icon: faker.properties.icon(),
140
142
  }),
@@ -162,14 +164,16 @@ export const storybookGraphBuilders = (): BuilderExtensions => {
162
164
  return value;
163
165
  });
164
166
 
165
- return Array.from({ length: get(count) }, (_, i) => ({
166
- id: `object-${i}`,
167
- type: 'object',
168
- properties: getProperties(`object-${i}`, {
169
- label: `Object ${i}`,
170
- icon: faker.properties.icon(),
167
+ return Array.from({ length: get(count) }, (_, i) =>
168
+ Node.make({
169
+ id: `object-${i}`,
170
+ type: 'object',
171
+ properties: getProperties(`object-${i}`, {
172
+ label: `Object ${i}`,
173
+ icon: faker.properties.icon(),
174
+ }),
171
175
  }),
172
- }));
176
+ );
173
177
  }),
174
178
  }),
175
179
  // Create object actions.
@@ -179,11 +183,11 @@ export const storybookGraphBuilders = (): BuilderExtensions => {
179
183
  actions: () =>
180
184
  Effect.succeed(
181
185
  Array.from({ length: 5 }, (_, i) => ({
182
- id: `object-actions/action-${i}`,
186
+ id: `action-${i}`,
183
187
  data: Effect.fnUntraced(function* () {
184
188
  log.info('action', { id: 'object-actions', index: i });
185
189
  }),
186
- properties: getProperties(`object-actions/action-${i}`, {
190
+ properties: getProperties(`action-${i}`, {
187
191
  label: `Action ${i}`,
188
192
  icon: faker.properties.icon(),
189
193
  }),
@@ -17,12 +17,12 @@ import { range } from '@dxos/util';
17
17
  export type TestItem = { id: string; type: string } & Record<string, any>;
18
18
 
19
19
  type ObjectDataGenerator = {
20
- createSchema?: () => Type.Obj.Any;
20
+ createSchema?: () => Type.AnyObj;
21
21
  createData: () => any;
22
22
  };
23
23
 
24
24
  type ObjectFactory<T> = {
25
- schema?: Type.Obj.Any; // TODO(burdon): Support both typed and expando schema.
25
+ schema?: Type.AnyObj; // TODO(burdon): Support both typed and expando schema.
26
26
  createObject: () => T;
27
27
  };
28
28
 
@@ -65,7 +65,7 @@ export const defaultGenerators: { [type: string]: ObjectDataGenerator } = {
65
65
  priority: Schema.Number,
66
66
  }).pipe(
67
67
  Type.object({
68
- typename: 'example.com/type/Project',
68
+ typename: 'com.example.type.project',
69
69
  version: '0.1.0',
70
70
  }),
71
71
  ),
@@ -94,7 +94,7 @@ export class TestObjectGenerator {
94
94
  }, {});
95
95
  }
96
96
 
97
- get schema(): Type.Obj.Any[] {
97
+ get schema(): Type.AnyObj[] {
98
98
  return Object.values(this.factories).map((f) => f.schema!);
99
99
  }
100
100
 
@@ -4,21 +4,20 @@
4
4
 
5
5
  import { type Resource } from '@dxos/react-ui';
6
6
 
7
- import { meta } from './meta';
7
+ import { meta } from '#meta';
8
8
 
9
9
  export const translations = [
10
10
  {
11
11
  'en-US': {
12
12
  [meta.id]: {
13
- 'app menu label': 'App menu',
14
- 'open settings label': 'Settings',
15
- 'open commands label': 'Search commands',
16
- 'commands dialog title': 'Commands',
17
- 'command list input placeholder': 'Search for commands…',
18
- 'node actions menu invoker label': 'More options',
19
- 'tree item actions label': 'More actions',
20
- 'empty branch message': ' ',
21
- 'button back': 'Back to Space',
13
+ 'app-menu.label': 'App menu',
14
+ 'open-settings.label': 'Settings',
15
+ 'open-commands.label': 'Search commands',
16
+ 'commands-dialog.title': 'Commands',
17
+ 'command-list-input.placeholder': 'Search for commands…',
18
+ 'node-actions-menu-invoker.label': 'More options',
19
+ 'tree-item-actions.label': 'More actions',
20
+ 'button-back.button': 'Back to Space',
22
21
  },
23
22
  },
24
23
  },
@@ -6,7 +6,7 @@ import { type Atom } from '@effect-atom/atom-react';
6
6
 
7
7
  import { Capability } from '@dxos/app-framework';
8
8
 
9
- import { meta } from '../meta';
9
+ import { meta } from '#meta';
10
10
 
11
11
  export namespace NavTreeCapabilities {
12
12
  export type NavTreeItemState = { open: boolean; current: boolean; alternateTree?: boolean };
@@ -17,5 +17,5 @@ export namespace NavTreeCapabilities {
17
17
  setItem: (path: string[], key: 'open' | 'current' | 'alternateTree', next: boolean) => void;
18
18
  };
19
19
 
20
- export const State = Capability.make<NavTreeStateStore>(`${meta.id}/capability/state`);
20
+ export const State = Capability.make<NavTreeStateStore>(`${meta.id}.capability.state`);
21
21
  }
@@ -2,10 +2,11 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { type ActivationEvent, Common } from '@dxos/app-framework';
5
+ import { type ActivationEvent } from '@dxos/app-framework';
6
+ import { AppActivationEvents } from '@dxos/app-toolkit';
6
7
 
7
- import { meta } from '../meta';
8
+ import { meta } from '#meta';
8
9
 
9
10
  export namespace NavTreeEvents {
10
- export const StateReady: ActivationEvent.ActivationEvent = Common.ActivationEvent.createStateEvent(meta.id);
11
+ export const StateReady: ActivationEvent.ActivationEvent = AppActivationEvents.createStateEvent(meta.id);
11
12
  }
package/src/util.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  import { Graph, Node } from '@dxos/plugin-graph';
6
6
  import { isNonNullable } from '@dxos/util';
7
7
 
8
- import { type NavTreeItemGraphNode } from './types';
8
+ import { type NavTreeItemGraphNode } from '#types';
9
9
 
10
10
  export const getParent = (
11
11
  graph: Graph.ReadableGraph,
@@ -13,9 +13,9 @@ export const getParent = (
13
13
  path: string[],
14
14
  ): NavTreeItemGraphNode | undefined => {
15
15
  const parentId = path[path.length - 2];
16
- return Graph.getConnections(graph, node.id, 'inbound').find((n: Node.Node) => n.id === parentId) as
17
- | NavTreeItemGraphNode
18
- | undefined;
16
+ return Graph.getConnections(graph, node.id, Node.childRelation('inbound')).find(
17
+ (n: Node.Node) => n.id === parentId,
18
+ ) as NavTreeItemGraphNode | undefined;
19
19
  };
20
20
 
21
21
  export const getPersistenceParent = (
@@ -80,7 +80,7 @@ export const getChildren = (
80
80
  node: NavTreeItemGraphNode,
81
81
  path: readonly string[] = [],
82
82
  ): NavTreeItemGraphNode[] => {
83
- return Graph.getConnections(graph, node.id, 'outbound')
83
+ return Graph.getConnections(graph, node.id, 'child')
84
84
  .map((n: Node.Node) => {
85
85
  // Break cycles.
86
86
  const nextPath = [...path, node.id];
@@ -89,6 +89,20 @@ export const getChildren = (
89
89
  .filter(isNonNullable) as NavTreeItemGraphNode[];
90
90
  };
91
91
 
92
+ /**
93
+ * Determines whether a node should be visible based on its disposition.
94
+ */
95
+ export const filterItems = (node: Node.Node, disposition?: string) => {
96
+ if (!disposition && (node.properties.disposition === 'hidden' || node.properties.disposition === 'alternate-tree')) {
97
+ return false;
98
+ } else if (!disposition) {
99
+ const action = Node.isAction(node);
100
+ return !action || node.properties.disposition === 'item';
101
+ } else {
102
+ return node.properties.disposition === disposition;
103
+ }
104
+ };
105
+
92
106
  export const l0ItemType = (item: Node.Node) => {
93
107
  if (Node.isActionLike(item)) {
94
108
  return 'action';
@@ -1,43 +0,0 @@
1
- import {
2
- COMMANDS_DIALOG,
3
- meta
4
- } from "./chunk-YKTYPINZ.mjs";
5
-
6
- // src/capabilities/app-graph-builder/app-graph-builder.ts
7
- import * as Effect from "effect/Effect";
8
- import { Capability, Common } from "@dxos/app-framework";
9
- import { Operation } from "@dxos/operation";
10
- import { GraphBuilder, NodeMatcher } from "@dxos/plugin-graph";
11
- var app_graph_builder_default = Capability.makeModule(Effect.fnUntraced(function* () {
12
- const extensions = yield* GraphBuilder.createExtension({
13
- id: meta.id,
14
- match: NodeMatcher.whenRoot,
15
- actions: () => Effect.succeed([
16
- {
17
- id: COMMANDS_DIALOG,
18
- data: () => Operation.invoke(Common.LayoutOperation.UpdateDialog, {
19
- subject: COMMANDS_DIALOG,
20
- blockAlign: "start"
21
- }),
22
- properties: {
23
- label: [
24
- "open commands label",
25
- {
26
- ns: meta.id
27
- }
28
- ],
29
- icon: "ph--magnifying-glass--regular",
30
- keyBinding: {
31
- macos: "meta+k",
32
- windows: "ctrl+k"
33
- }
34
- }
35
- }
36
- ])
37
- });
38
- return Capability.contributes(Common.Capability.AppGraphBuilder, extensions);
39
- }));
40
- export {
41
- app_graph_builder_default as default
42
- };
43
- //# sourceMappingURL=app-graph-builder-RSMA6I22.mjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/capabilities/app-graph-builder/app-graph-builder.ts"],
4
- "sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Capability, Common } from '@dxos/app-framework';\nimport { Operation } from '@dxos/operation';\nimport { GraphBuilder, NodeMatcher } from '@dxos/plugin-graph';\n\nimport { COMMANDS_DIALOG, meta } from '../../meta';\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n const extensions = yield* GraphBuilder.createExtension({\n id: meta.id,\n match: NodeMatcher.whenRoot,\n actions: () =>\n Effect.succeed([\n {\n id: COMMANDS_DIALOG,\n data: () =>\n Operation.invoke(Common.LayoutOperation.UpdateDialog, {\n subject: COMMANDS_DIALOG,\n blockAlign: 'start',\n }),\n properties: {\n label: ['open commands label', { ns: meta.id }],\n icon: 'ph--magnifying-glass--regular',\n keyBinding: {\n macos: 'meta+k',\n windows: 'ctrl+k',\n },\n },\n },\n ]),\n });\n\n return Capability.contributes(Common.Capability.AppGraphBuilder, extensions);\n }),\n);\n"],
5
- "mappings": ";;;;;;AAIA,YAAYA,YAAY;AAExB,SAASC,YAAYC,cAAc;AACnC,SAASC,iBAAiB;AAC1B,SAASC,cAAcC,mBAAmB;AAI1C,IAAA,4BAAeC,WAAWC,WACjBC,kBAAW,aAAA;AAChB,QAAMC,aAAa,OAAOC,aAAaC,gBAAgB;IACrDC,IAAIC,KAAKD;IACTE,OAAOC,YAAYC;IACnBC,SAAS,MACAC,eAAQ;MACb;QACEN,IAAIO;QACJC,MAAM,MACJC,UAAUC,OAAOC,OAAOC,gBAAgBC,cAAc;UACpDC,SAASP;UACTQ,YAAY;QACd,CAAA;QACFC,YAAY;UACVC,OAAO;YAAC;YAAuB;cAAEC,IAAIjB,KAAKD;YAAG;;UAC7CmB,MAAM;UACNC,YAAY;YACVC,OAAO;YACPC,SAAS;UACX;QACF;MACF;KACD;EACL,CAAA;AAEA,SAAO5B,WAAW6B,YAAYZ,OAAOjB,WAAW8B,iBAAiB3B,UAAAA;AACnE,CAAA,CAAA;",
6
- "names": ["Effect", "Capability", "Common", "Operation", "GraphBuilder", "NodeMatcher", "Capability", "makeModule", "fnUntraced", "extensions", "GraphBuilder", "createExtension", "id", "meta", "match", "NodeMatcher", "whenRoot", "actions", "succeed", "COMMANDS_DIALOG", "data", "Operation", "invoke", "Common", "LayoutOperation", "UpdateDialog", "subject", "blockAlign", "properties", "label", "ns", "icon", "keyBinding", "macos", "windows", "contributes", "AppGraphBuilder"]
7
- }