@dxos/plugin-navtree 0.8.2-main.f11618f → 0.8.2-staging.42af850

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 (141) hide show
  1. package/dist/lib/browser/{app-graph-builder-26D3MUCA.mjs → app-graph-builder-P7AY3K72.mjs} +7 -6
  2. package/dist/lib/browser/app-graph-builder-P7AY3K72.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-TLQLMZXU.mjs → chunk-33VLL4IJ.mjs} +2 -2
  4. package/dist/lib/browser/{chunk-TLQLMZXU.mjs.map → chunk-33VLL4IJ.mjs.map} +1 -1
  5. package/dist/lib/browser/chunk-MFAWMFJC.mjs +1418 -0
  6. package/dist/lib/browser/chunk-MFAWMFJC.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +6 -12
  8. package/dist/lib/browser/index.mjs.map +3 -3
  9. package/dist/lib/browser/{intent-resolver-OBGGMBVJ.mjs → intent-resolver-NAYKKAOP.mjs} +7 -7
  10. package/dist/lib/browser/intent-resolver-NAYKKAOP.mjs.map +7 -0
  11. package/dist/lib/browser/keyboard-DDFATRUG.mjs +51 -0
  12. package/dist/lib/browser/keyboard-DDFATRUG.mjs.map +7 -0
  13. package/dist/lib/browser/meta.json +1 -1
  14. package/dist/lib/browser/{react-surface-DWMOF2TM.mjs → react-surface-HGA3TAQU.mjs} +3 -3
  15. package/dist/lib/browser/{state-EW62QYEN.mjs → state-R2UMYAHN.mjs} +3 -3
  16. package/dist/lib/browser/state-R2UMYAHN.mjs.map +7 -0
  17. package/dist/types/src/capabilities/app-graph-builder.d.ts +2 -179
  18. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  19. package/dist/types/src/capabilities/index.d.ts +4 -180
  20. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  21. package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
  22. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  23. package/dist/types/src/capabilities/keyboard.d.ts +2 -2
  24. package/dist/types/src/capabilities/keyboard.d.ts.map +1 -1
  25. package/dist/types/src/capabilities/state.d.ts +2 -2
  26. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  27. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +1 -0
  28. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -0
  29. package/dist/types/src/components/CommandsDialog/index.d.ts +3 -0
  30. package/dist/types/src/components/CommandsDialog/index.d.ts.map +1 -0
  31. package/dist/types/src/components/NavTree/NavTree.d.ts +8 -0
  32. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -0
  33. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +8 -0
  34. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -0
  35. package/dist/types/src/components/NavTree/index.d.ts +2 -0
  36. package/dist/types/src/components/NavTree/index.d.ts.map +1 -0
  37. package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
  38. package/dist/types/src/components/NavTreeContext.d.ts.map +1 -1
  39. package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +1 -1
  40. package/dist/types/src/components/{NavTreeItemAction.d.ts → NavTreeItem/NavTreeItemAction.d.ts} +3 -5
  41. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -0
  42. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +8 -0
  43. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -0
  44. package/dist/types/src/components/{NavTreeItemColumns.d.ts → NavTreeItem/NavTreeItemColumns.d.ts} +1 -1
  45. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -0
  46. package/dist/types/src/components/NavTreeItem/index.d.ts +3 -0
  47. package/dist/types/src/components/NavTreeItem/index.d.ts.map +1 -0
  48. package/dist/types/src/components/Sidebar/L0Menu.d.ts +13 -0
  49. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -0
  50. package/dist/types/src/components/Sidebar/L1Panel.d.ts +15 -0
  51. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -0
  52. package/dist/types/src/components/Sidebar/index.d.ts +3 -0
  53. package/dist/types/src/components/Sidebar/index.d.ts.map +1 -0
  54. package/dist/types/src/components/{UserAccountAvatar.d.ts → UserAccountAvatar/UserAccountAvatar.d.ts} +1 -1
  55. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -0
  56. package/dist/types/src/components/{UserAccountAvatar.stories.d.ts → UserAccountAvatar/UserAccountAvatar.stories.d.ts} +1 -2
  57. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -0
  58. package/dist/types/src/components/UserAccountAvatar/index.d.ts +2 -0
  59. package/dist/types/src/components/UserAccountAvatar/index.d.ts.map +1 -0
  60. package/dist/types/src/components/index.d.ts +0 -1
  61. package/dist/types/src/components/index.d.ts.map +1 -1
  62. package/dist/types/src/components/types.d.ts +17 -15
  63. package/dist/types/src/components/types.d.ts.map +1 -1
  64. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  65. package/dist/types/src/hooks.d.ts.map +1 -1
  66. package/dist/types/src/testing/app-graph-builder.d.ts +3 -0
  67. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -0
  68. package/dist/types/src/testing/generator.d.ts +5 -4
  69. package/dist/types/src/testing/generator.d.ts.map +1 -1
  70. package/dist/types/src/testing/index.d.ts +1 -0
  71. package/dist/types/src/testing/index.d.ts.map +1 -1
  72. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  73. package/dist/types/src/translations.d.ts +1 -1
  74. package/dist/types/src/util.d.ts +7 -10
  75. package/dist/types/src/util.d.ts.map +1 -1
  76. package/dist/types/tsconfig.tsbuildinfo +1 -1
  77. package/package.json +42 -32
  78. package/src/NavTreePlugin.tsx +2 -2
  79. package/src/capabilities/app-graph-builder.ts +38 -28
  80. package/src/capabilities/intent-resolver.ts +8 -7
  81. package/src/capabilities/keyboard.ts +37 -32
  82. package/src/capabilities/state.ts +3 -3
  83. package/src/components/{CommandsDialog.tsx → CommandsDialog/CommandsDialog.tsx} +8 -7
  84. package/src/components/{CommandsTrigger.tsx → CommandsDialog/CommandsTrigger.tsx} +1 -1
  85. package/src/components/CommandsDialog/index.ts +6 -0
  86. package/src/components/NavTree/NavTree.stories.tsx +159 -0
  87. package/src/components/NavTree/NavTree.tsx +62 -0
  88. package/src/components/NavTree/index.ts +5 -0
  89. package/src/components/NavTreeContainer.tsx +76 -72
  90. package/src/components/NavTreeContext.tsx +1 -1
  91. package/src/components/{NavTreeItemAction.stories.tsx → NavTreeItem/NavTreeItemAction.stories.tsx} +12 -6
  92. package/src/components/{NavTreeItemAction.tsx → NavTreeItem/NavTreeItemAction.tsx} +9 -18
  93. package/src/components/{NavTreeItemColumns.tsx → NavTreeItem/NavTreeItemColumns.tsx} +5 -5
  94. package/src/components/NavTreeItem/index.ts +6 -0
  95. package/src/components/{L0Menu.tsx → Sidebar/L0Menu.tsx} +186 -131
  96. package/src/components/{L1Panels.tsx → Sidebar/L1Panel.tsx} +40 -65
  97. package/src/components/Sidebar/index.ts +6 -0
  98. package/src/components/{UserAccountAvatar.stories.tsx → UserAccountAvatar/UserAccountAvatar.stories.tsx} +5 -5
  99. package/src/components/{UserAccountAvatar.tsx → UserAccountAvatar/UserAccountAvatar.tsx} +6 -6
  100. package/src/components/UserAccountAvatar/index.ts +5 -0
  101. package/src/components/index.ts +0 -1
  102. package/src/components/types.ts +17 -16
  103. package/src/testing/app-graph-builder.ts +224 -0
  104. package/src/testing/generator.ts +12 -9
  105. package/src/testing/index.ts +1 -0
  106. package/src/translations.ts +1 -1
  107. package/src/util.ts +10 -32
  108. package/dist/lib/browser/app-graph-builder-26D3MUCA.mjs.map +0 -7
  109. package/dist/lib/browser/chunk-FHOAKMRS.mjs +0 -1309
  110. package/dist/lib/browser/chunk-FHOAKMRS.mjs.map +0 -7
  111. package/dist/lib/browser/intent-resolver-OBGGMBVJ.mjs.map +0 -7
  112. package/dist/lib/browser/keyboard-IQXHKUWM.mjs +0 -46
  113. package/dist/lib/browser/keyboard-IQXHKUWM.mjs.map +0 -7
  114. package/dist/lib/browser/state-EW62QYEN.mjs.map +0 -7
  115. package/dist/types/src/components/CommandsDialog.d.ts.map +0 -1
  116. package/dist/types/src/components/CommandsTrigger.d.ts.map +0 -1
  117. package/dist/types/src/components/L0Menu.d.ts +0 -10
  118. package/dist/types/src/components/L0Menu.d.ts.map +0 -1
  119. package/dist/types/src/components/L1Panels.d.ts +0 -14
  120. package/dist/types/src/components/L1Panels.d.ts.map +0 -1
  121. package/dist/types/src/components/NavTree.d.ts +0 -5
  122. package/dist/types/src/components/NavTree.d.ts.map +0 -1
  123. package/dist/types/src/components/NavTree.stories.d.ts +0 -7
  124. package/dist/types/src/components/NavTree.stories.d.ts.map +0 -1
  125. package/dist/types/src/components/NavTreeItemAction.d.ts.map +0 -1
  126. package/dist/types/src/components/NavTreeItemAction.stories.d.ts +0 -41
  127. package/dist/types/src/components/NavTreeItemAction.stories.d.ts.map +0 -1
  128. package/dist/types/src/components/NavTreeItemColumns.d.ts.map +0 -1
  129. package/dist/types/src/components/UserAccountAvatar.d.ts.map +0 -1
  130. package/dist/types/src/components/UserAccountAvatar.stories.d.ts.map +0 -1
  131. package/dist/types/src/stories/Graph.stories.d.ts +0 -7
  132. package/dist/types/src/stories/Graph.stories.d.ts.map +0 -1
  133. package/dist/types/src/stories/StorybookNavTree.d.ts +0 -4
  134. package/dist/types/src/stories/StorybookNavTree.d.ts.map +0 -1
  135. package/src/components/NavTree.stories.tsx +0 -107
  136. package/src/components/NavTree.tsx +0 -51
  137. package/src/stories/Graph.stories.tsx +0 -130
  138. package/src/stories/StorybookNavTree.tsx +0 -63
  139. /package/dist/lib/browser/{react-surface-DWMOF2TM.mjs.map → react-surface-HGA3TAQU.mjs.map} +0 -0
  140. /package/dist/types/src/components/{CommandsDialog.d.ts → CommandsDialog/CommandsDialog.d.ts} +0 -0
  141. /package/dist/types/src/components/{CommandsTrigger.d.ts → CommandsDialog/CommandsTrigger.d.ts} +0 -0
@@ -0,0 +1,62 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useMemo } from 'react';
6
+
7
+ import { type TreeProps } from '@dxos/react-ui-list';
8
+ import { Tabs } from '@dxos/react-ui-tabs';
9
+
10
+ import { useLoadDescendents } from '../../hooks';
11
+ import { type NavTreeItemGraphNode } from '../../types';
12
+ import { useNavTreeContext } from '../NavTreeContext';
13
+ import { L0Menu, L1Panels, type L1PanelsProps } from '../Sidebar';
14
+
15
+ export const NAV_TREE_ITEM = 'NavTreeItem';
16
+
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
+ );
45
+
46
+ useLoadDescendents(root);
47
+ const path = useMemo(() => [id], [id]);
48
+
49
+ return (
50
+ <Tabs.Root value={tab} orientation='vertical' verticalVariant='stateless' classNames='relative'>
51
+ <L0Menu
52
+ menuActions={topLevelActions}
53
+ topLevelItems={l0Items}
54
+ pinnedItems={pinnedItems}
55
+ userAccountItem={userAccountItem}
56
+ path={path}
57
+ parent={root}
58
+ />
59
+ <L1Panels topLevelItems={topLevelItems} path={path} currentItemId={tab} onBack={onBack} {...props} />
60
+ </Tabs.Root>
61
+ );
62
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './NavTree';
@@ -16,28 +16,22 @@ import {
16
16
  useIntentDispatcher,
17
17
  useLayout,
18
18
  } from '@dxos/app-framework';
19
- import { isAction, isActionLike, type Node } from '@dxos/app-graph';
19
+ import { isAction, isActionLike, type ReadableGraph, ROOT_ID, type Node } from '@dxos/app-graph';
20
20
  import { PLANK_COMPANION_TYPE } from '@dxos/plugin-deck/types';
21
+ import { useConnections } from '@dxos/plugin-graph';
21
22
  import { isEchoObject, isSpace } from '@dxos/react-client/echo';
22
- import { useMediaQuery } from '@dxos/react-ui';
23
+ import { useMediaQuery, useSidebars } from '@dxos/react-ui';
23
24
  import { isTreeData, type TreeData, type PropsFromTreeItem } from '@dxos/react-ui-list';
24
25
  import { mx } from '@dxos/react-ui-theme';
25
- import { arrayMove } from '@dxos/util';
26
+ import { arrayMove, byPosition } from '@dxos/util';
26
27
 
27
28
  import { NAV_TREE_ITEM, NavTree } from './NavTree';
28
29
  import { NavTreeContext } from './NavTreeContext';
29
30
  import { type NavTreeContextValue } from './types';
30
31
  import { NavTreeCapabilities } from '../capabilities';
32
+ import { NAVTREE_PLUGIN } from '../meta';
31
33
  import { type NavTreeItemGraphNode } from '../types';
32
- import {
33
- expandActions,
34
- expandChildren,
35
- getActions as naturalGetActions,
36
- getChildren,
37
- getParent,
38
- resolveMigrationOperation,
39
- expandChildrenAndActions,
40
- } from '../util';
34
+ import { getActions as naturalGetActions, getChildren, getParent, resolveMigrationOperation } from '../util';
41
35
 
42
36
  // TODO(thure): Is NavTree truly authoritative in this regard?
43
37
  export const NODE_TYPE = 'dxos/app-graph/node';
@@ -49,6 +43,29 @@ const renderItemEnd = ({ node, open }: { node: Node; open: boolean }) => (
49
43
  const getChildrenFilter = (node: Node): node is Node =>
50
44
  untracked(() => !isActionLike(node) && node.type !== PLANK_COMPANION_TYPE);
51
45
 
46
+ const filterItems = (node: Node, disposition?: string) => {
47
+ if (!disposition && (node.properties.disposition === 'hidden' || node.properties.disposition === 'alternate-tree')) {
48
+ return false;
49
+ } else if (!disposition) {
50
+ const action = isAction(node);
51
+ return !action || node.properties.disposition === 'item';
52
+ } else {
53
+ return node.properties.disposition === disposition;
54
+ }
55
+ };
56
+
57
+ const getItems = (graph: ReadableGraph, node?: Node, disposition?: string) => {
58
+ return graph.getConnections(node?.id ?? ROOT_ID, 'outbound').filter((node) => filterItems(node, disposition));
59
+ };
60
+
61
+ const useItems = (node?: Node, options?: { disposition?: string; sort?: boolean }) => {
62
+ const { graph } = useAppGraph();
63
+ const connections = useConnections(graph, node?.id ?? ROOT_ID).filter((node) =>
64
+ filterItems(node, options?.disposition),
65
+ );
66
+ return options?.sort ? connections.toSorted((a, b) => byPosition(a.properties, b.properties)) : connections;
67
+ };
68
+
52
69
  export type NavTreeContainerProps = {
53
70
  popoverAnchorId?: string;
54
71
  topbar?: boolean;
@@ -58,37 +75,15 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
58
75
  const [isLg] = useMediaQuery('lg', { ssr: false });
59
76
  const { dispatchPromise: dispatch } = useIntentDispatcher();
60
77
  const { graph } = useAppGraph();
61
- const layout = useLayout();
62
78
  const { isOpen, isCurrent, isAlternateTree, setItem } = useCapability(NavTreeCapabilities.State);
79
+ const layout = useLayout();
80
+ const { navigationSidebarState } = useSidebars(NAVTREE_PLUGIN);
63
81
 
64
82
  const getActions = useCallback((node: Node) => naturalGetActions(graph, node), [graph]);
65
83
 
66
- const getItems = useCallback(
67
- (node?: Node, disposition?: string) => {
68
- return graph.nodes(node ?? graph.root, {
69
- filter: (node): node is Node => {
70
- return untracked(() => {
71
- if (
72
- !disposition &&
73
- (node.properties.disposition === 'hidden' || node.properties.disposition === 'alternate-tree')
74
- ) {
75
- return false;
76
- } else if (!disposition) {
77
- const action = isAction(node);
78
- return !action || node.properties.disposition === 'item';
79
- } else {
80
- return node.properties.disposition === disposition;
81
- }
82
- });
83
- },
84
- });
85
- },
86
- [graph],
87
- );
88
-
89
84
  const getProps = useCallback(
90
85
  (node: Node, path: string[]): PropsFromTreeItem => {
91
- const children = getChildren(graph, node, getChildrenFilter, path);
86
+ const children = getChildren(graph, node, path).filter(getChildrenFilter);
92
87
  const parentOf =
93
88
  children.length > 0 ? children.map(({ id }) => id) : node.properties.role === 'branch' ? [] : undefined;
94
89
  return {
@@ -107,49 +102,59 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
107
102
 
108
103
  const loadDescendents = useCallback(
109
104
  (node: Node) => {
110
- void expandActions(graph, node);
111
- if (!isActionLike(node)) {
112
- void expandChildren(graph, node).then(() =>
113
- // Load one level deeper, which resolves some juddering observed on open/close.
114
- Promise.all(
115
- getChildren(graph, node).flatMap((child) => [expandActions(graph, child), expandChildren(graph, child)]),
116
- ),
117
- );
118
- }
105
+ graph.expand(node.id, 'outbound');
106
+ // Load one level deeper, which resolves some juddering observed on open/close.
107
+ graph.getConnections(node.id, 'outbound').forEach((child) => {
108
+ graph.expand(child.id, 'outbound');
109
+ });
119
110
  },
120
111
  [graph],
121
112
  );
122
113
 
123
- const onOpenChange = useCallback(
114
+ const handleOpenChange = useCallback(
124
115
  ({ item: { id }, path, open }: { item: Node; path: string[]; open: boolean }) => {
125
116
  // TODO(thure): This might become a localstorage leak; openItemIds that no longer exist should be removed from this map.
126
117
  setItem(path, 'open', open);
127
-
128
- if (graph) {
129
- const node = graph.findNode(id);
130
- return node && expandChildrenAndActions(graph, node as NavTreeItemGraphNode);
131
- }
118
+ graph.expand(id, 'outbound');
132
119
  },
133
120
  [graph],
134
121
  );
135
122
 
136
- const onTabChange = useCallback(
123
+ const handleTabChange = useCallback(
137
124
  async (node: NavTreeItemGraphNode) => {
125
+ await dispatch(
126
+ createIntent(LayoutAction.UpdateSidebar, {
127
+ part: 'sidebar',
128
+ options: {
129
+ state:
130
+ node.id === tab
131
+ ? navigationSidebarState === 'expanded'
132
+ ? isLg
133
+ ? 'collapsed'
134
+ : 'closed'
135
+ : 'expanded'
136
+ : 'expanded',
137
+ },
138
+ }),
139
+ );
140
+
138
141
  await dispatch(createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: node.id }));
142
+
139
143
  // Open the first item if the workspace is empty.
140
144
  if (layout.active.length === 0) {
141
- const [item] = getItems(node).filter((node) => !isActionLike(node));
145
+ const [item] = getItems(graph, node).filter((node) => !isActionLike(node));
142
146
  if (item && item.data) {
143
147
  await dispatch(createIntent(LayoutAction.Open, { part: 'main', subject: [item.id] }));
144
148
  }
145
149
  }
146
150
  },
147
- [dispatch, layout],
151
+ [dispatch, layout.active, tab, navigationSidebarState, isLg],
148
152
  );
149
153
 
150
154
  const canDrop = useCallback((source: TreeData, target: TreeData) => {
151
155
  const sourceNode = source.item as Node;
152
156
  const targetNode = target.item as Node;
157
+ // TODO(wittjosiah): This shouldn't depend directly on client, there should be a graph-level abstraction.
153
158
  if (isSpace(targetNode.data)) {
154
159
  // TODO(wittjosiah): Find a way to only allow space as source for rearranging.
155
160
  return isEchoObject(sourceNode.data) || isSpace(sourceNode.data);
@@ -167,8 +172,8 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
167
172
  }
168
173
 
169
174
  if (isAction(node)) {
170
- const [parent] = graph.nodes(node, { relation: 'inbound' });
171
- void (parent && node.data({ node: parent, caller: NAV_TREE_ITEM }));
175
+ const [parent] = graph.getConnections(node.id, 'inbound');
176
+ void (parent && node.data({ parent, caller: NAV_TREE_ITEM }));
172
177
  return;
173
178
  }
174
179
 
@@ -189,7 +194,7 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
189
194
  void dispatch(createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: node.id }));
190
195
  }
191
196
 
192
- const defaultAction = graph.actions(node).find((action) => action.properties?.disposition === 'default');
197
+ const defaultAction = graph.getActions(node.id).find((action) => action.properties?.disposition === 'default');
193
198
  if (isAction(defaultAction)) {
194
199
  void (defaultAction.data as () => void)();
195
200
  }
@@ -201,7 +206,7 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
201
206
  [graph, dispatch, isCurrent, isLg],
202
207
  );
203
208
 
204
- const onBack = useCallback(
209
+ const handleBack = useCallback(
205
210
  () => dispatch(createIntent(LayoutAction.RevertWorkspace, { part: 'workspace', options: { revert: true } })),
206
211
  [dispatch],
207
212
  );
@@ -228,8 +233,8 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
228
233
  : resolveMigrationOperation(graph, sourceNode, targetPath, targetNode);
229
234
  const sourceParent = getParent(graph, sourceNode, sourcePath);
230
235
  const targetParent = getParent(graph, targetNode, targetPath);
231
- const sourceItems = getItems(sourceParent);
232
- const targetItems = getItems(targetParent);
236
+ const sourceItems = getItems(graph, sourceParent);
237
+ const targetItems = getItems(graph, targetParent);
233
238
  const sourceIndex = sourceItems.findIndex(({ id }) => id === sourceNode.id);
234
239
  const targetIndex = targetItems.findIndex(({ id }) => id === targetNode.id);
235
240
  const migrationIndex =
@@ -274,48 +279,47 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
274
279
 
275
280
  const navTreeContextValue = useMemo(
276
281
  () => ({
282
+ useItems,
277
283
  tab,
278
- onTabChange,
279
- onBack,
280
284
  getActions,
281
285
  loadDescendents,
282
286
  renderItemEnd,
283
287
  popoverAnchorId,
284
288
  topbar,
285
- getItems,
286
289
  getProps,
287
290
  isCurrent,
288
291
  isOpen,
289
- onOpenChange,
290
292
  canDrop,
291
- onSelect: handleSelect,
292
293
  isAlternateTree,
293
294
  setAlternateTree,
295
+ onTabChange: handleTabChange,
296
+ onOpenChange: handleOpenChange,
297
+ onSelect: handleSelect,
298
+ onBack: handleBack,
294
299
  }),
295
300
  [
296
301
  tab,
297
- onTabChange,
298
- onBack,
299
302
  getActions,
300
303
  loadDescendents,
301
304
  renderItemEnd,
302
305
  popoverAnchorId,
303
306
  topbar,
304
- getItems,
305
307
  getProps,
306
308
  isCurrent,
307
309
  isOpen,
308
- onOpenChange,
309
310
  canDrop,
310
- handleSelect,
311
311
  isAlternateTree,
312
312
  setAlternateTree,
313
+ handleTabChange,
314
+ handleOpenChange,
315
+ handleSelect,
316
+ handleBack,
313
317
  ],
314
318
  );
315
319
 
316
320
  return (
317
321
  <NavTreeContext.Provider value={navTreeContextValue}>
318
- <NavTree root={graph.root} id={graph.root.id} />
322
+ <NavTree id={ROOT_ID} root={graph.root} open={layout.sidebarOpen} />
319
323
  </NavTreeContext.Provider>
320
324
  );
321
325
  });
@@ -10,4 +10,4 @@ import { type NavTreeContextValue } from './types';
10
10
 
11
11
  export const NavTreeContext = createContext<NavTreeContextValue | null>(null);
12
12
 
13
- export const useNavTreeContext = () => useContext(NavTreeContext) ?? raise(new Error('NavTreeContext not found'));
13
+ export const useNavTreeContext = () => useContext(NavTreeContext) ?? raise(new Error('Missing NavTreeContext'));
@@ -4,6 +4,8 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
+ import { type Meta, type StoryObj } from '@storybook/react';
8
+
7
9
  import { type NodeArg } from '@dxos/app-graph';
8
10
  import { faker } from '@dxos/random';
9
11
  import { withLayout, withTheme } from '@dxos/storybook-utils';
@@ -37,19 +39,23 @@ const menuActions = faker.helpers.multiple(
37
39
  { count: 20 },
38
40
  );
39
41
 
40
- export default {
42
+ const meta: Meta<typeof NavTreeItemAction> = {
41
43
  title: 'plugins/plugin-navtree/NavTreeItemAction',
42
44
  component: NavTreeItemAction,
43
- decorators: [withTheme, withLayout({ fullscreen: true, tooltips: true })],
45
+ decorators: [withTheme, withLayout()],
44
46
  args: {
45
47
  icon: 'ph--list--regular',
46
48
  parent,
47
49
  menuActions,
48
50
  label: 'Select action',
49
- onAction: (action) => {
50
- console.log(action);
51
- },
52
51
  } satisfies Partial<NavTreeItemActionMenuProps>,
52
+ parameters: {
53
+ layout: 'centered',
54
+ },
53
55
  };
54
56
 
55
- export const Default = {};
57
+ export default meta;
58
+
59
+ type Story = StoryObj<typeof meta>;
60
+
61
+ export const Default: Story = {};
@@ -2,23 +2,21 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import React, { type MutableRefObject, useRef } from 'react';
5
+ import React from 'react';
6
6
 
7
7
  import { type Action, type Node } from '@dxos/app-graph';
8
8
  import { useTranslation, toLocalizedString, IconButton, useDensityContext } from '@dxos/react-ui';
9
9
  import { DropdownMenu, MenuProvider, type MenuItem } from '@dxos/react-ui-menu';
10
10
  import { hoverableControlItem, hoverableOpenControlItem, mx } from '@dxos/react-ui-theme';
11
11
 
12
- import { NAVTREE_PLUGIN } from '../meta';
13
- import { type ActionProperties } from '../types';
12
+ import { NAVTREE_PLUGIN } from '../../meta';
13
+ import { type ActionProperties } from '../../types';
14
14
 
15
15
  export type NavTreeItemActionMenuProps = ActionProperties & {
16
16
  parent: Node;
17
17
  caller?: string;
18
18
  monolithic?: boolean;
19
19
  menuActions?: Action[];
20
- suppressNextTooltip?: MutableRefObject<boolean>;
21
- onAction?: (action: Action) => void;
22
20
  };
23
21
 
24
22
  const fallbackIcon = 'ph--placeholder--regular';
@@ -33,18 +31,18 @@ const coarseActionButtonProps = {
33
31
  };
34
32
 
35
33
  export const NavTreeItemActionDropdownMenu = ({
34
+ parent,
36
35
  label,
37
36
  icon,
38
37
  testId,
39
38
  menuActions,
40
- onAction,
39
+ caller,
41
40
  }: NavTreeItemActionMenuProps) => {
42
41
  const { t } = useTranslation(NAVTREE_PLUGIN);
43
- const suppressNextTooltip = useRef<boolean>(false);
44
42
  const density = useDensityContext();
45
43
  return (
46
- <MenuProvider onAction={onAction}>
47
- <DropdownMenu.Root items={menuActions as MenuItem[]} suppressNextTooltip={suppressNextTooltip}>
44
+ <MenuProvider>
45
+ <DropdownMenu.Root group={parent} items={menuActions as MenuItem[]} caller={caller}>
48
46
  <DropdownMenu.Trigger asChild>
49
47
  <IconButton
50
48
  {...(density === 'coarse' ? coarseActionButtonProps : fineActionButtonProps)}
@@ -54,7 +52,6 @@ export const NavTreeItemActionDropdownMenu = ({
54
52
  iconOnly
55
53
  label={toLocalizedString(label, t)}
56
54
  data-testid={testId}
57
- suppressNextTooltip={suppressNextTooltip}
58
55
  />
59
56
  </DropdownMenu.Trigger>
60
57
  </DropdownMenu.Root>
@@ -88,7 +85,7 @@ export const NavTreeItemMonolithicAction = ({
88
85
  return;
89
86
  }
90
87
  event.stopPropagation();
91
- void invoke?.(caller ? { node: parent, caller } : { node: parent });
88
+ void invoke?.(caller ? { parent, caller } : { parent });
92
89
  }}
93
90
  data-testid={testId}
94
91
  />
@@ -104,12 +101,6 @@ export const NavTreeItemAction = ({ monolithic, menuActions, parent: node, ...pr
104
101
  return monolithic && menuActions?.length === 1 ? (
105
102
  <NavTreeItemMonolithicAction baseLabel={baseLabel} parent={node} {...menuActions[0]} />
106
103
  ) : (
107
- <NavTreeItemActionDropdownMenu
108
- {...props}
109
- label={baseLabel}
110
- parent={node}
111
- menuActions={menuActions}
112
- onAction={(action) => action.data?.(props.caller ? { node, caller: props.caller } : { node })}
113
- />
104
+ <NavTreeItemActionDropdownMenu {...props} label={baseLabel} parent={node} menuActions={menuActions} />
114
105
  );
115
106
  };
@@ -7,12 +7,12 @@ import React, { Fragment, memo } from 'react';
7
7
  import { isAction } from '@dxos/app-graph';
8
8
  import { DensityProvider, Popover, toLocalizedString, Treegrid, useTranslation } from '@dxos/react-ui';
9
9
 
10
- import { NAV_TREE_ITEM } from './NavTree';
11
- import { useNavTreeContext } from './NavTreeContext';
12
10
  import { NavTreeItemAction } from './NavTreeItemAction';
13
- import type { NavTreeItemColumnsProps } from './types';
14
- import { useLoadDescendents } from '../hooks';
15
- import { NAVTREE_PLUGIN } from '../meta';
11
+ import { useLoadDescendents } from '../../hooks';
12
+ import { NAVTREE_PLUGIN } from '../../meta';
13
+ import { NAV_TREE_ITEM } from '../NavTree';
14
+ import { useNavTreeContext } from '../NavTreeContext';
15
+ import type { NavTreeItemColumnsProps } from '../types';
16
16
 
17
17
  export const NavTreeItemColumns = memo(({ path, item, open, density = 'fine' }: NavTreeItemColumnsProps) => {
18
18
  const { t } = useTranslation(NAVTREE_PLUGIN);
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './NavTreeItemAction';
6
+ export * from './NavTreeItemColumns';