@dxos/plugin-navtree 0.8.4-main.7ace549 → 0.8.4-main.937b3ca

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 (166) hide show
  1. package/dist/lib/browser/app-graph-builder-RSMA6I22.mjs +43 -0
  2. package/dist/lib/browser/app-graph-builder-RSMA6I22.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-6YPQ625I.mjs +1293 -0
  4. package/dist/lib/browser/chunk-6YPQ625I.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-AGQDZBPL.mjs +23 -0
  6. package/dist/lib/browser/chunk-AGQDZBPL.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-E6MZBCU5.mjs → chunk-YKTYPINZ.mjs} +1 -1
  8. package/dist/lib/browser/chunk-YKTYPINZ.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +82 -91
  10. package/dist/lib/browser/index.mjs.map +4 -4
  11. package/dist/lib/browser/{keyboard-YJEK2HTH.mjs → keyboard-32EH3N4K.mjs} +17 -14
  12. package/dist/lib/browser/keyboard-32EH3N4K.mjs.map +7 -0
  13. package/dist/lib/browser/meta.json +1 -1
  14. package/dist/lib/browser/operation-resolver-IB75DC5O.mjs +52 -0
  15. package/dist/lib/browser/operation-resolver-IB75DC5O.mjs.map +7 -0
  16. package/dist/lib/browser/react-surface-F6CVMS3Y.mjs +58 -0
  17. package/dist/lib/browser/react-surface-F6CVMS3Y.mjs.map +7 -0
  18. package/dist/lib/browser/state-EVYQOUI3.mjs +120 -0
  19. package/dist/lib/browser/state-EVYQOUI3.mjs.map +7 -0
  20. package/dist/types/src/NavTreePlugin.d.ts +2 -1
  21. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  22. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +6 -0
  23. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +1 -0
  24. package/dist/types/src/capabilities/app-graph-builder/index.d.ts +3 -0
  25. package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +1 -0
  26. package/dist/types/src/capabilities/index.d.ts +5 -108
  27. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  28. package/dist/types/src/capabilities/keyboard/index.d.ts +3 -0
  29. package/dist/types/src/capabilities/keyboard/index.d.ts.map +1 -0
  30. package/dist/types/src/capabilities/keyboard/keyboard.d.ts +5 -0
  31. package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +1 -0
  32. package/dist/types/src/capabilities/operation-resolver/index.d.ts +3 -0
  33. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +1 -0
  34. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +5 -0
  35. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -0
  36. package/dist/types/src/capabilities/react-surface/index.d.ts +3 -0
  37. package/dist/types/src/capabilities/react-surface/index.d.ts.map +1 -0
  38. package/dist/types/src/capabilities/react-surface/react-surface.d.ts +5 -0
  39. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -0
  40. package/dist/types/src/capabilities/state/index.d.ts +3 -0
  41. package/dist/types/src/capabilities/state/index.d.ts.map +1 -0
  42. package/dist/types/src/capabilities/state/state.d.ts +6 -0
  43. package/dist/types/src/capabilities/state/state.d.ts.map +1 -0
  44. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts +3 -2
  45. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +1 -1
  46. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -1
  47. package/dist/types/src/components/NavTree/NavTree.d.ts +30 -1
  48. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  49. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +4 -1
  50. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  51. package/dist/types/src/components/NavTreeContainer.d.ts +8 -1
  52. package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
  53. package/dist/types/src/components/NavTreeDocumentTitle.d.ts +1 -1
  54. package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +1 -1
  55. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +6 -6
  56. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  57. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +2 -1
  58. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  59. package/dist/types/src/components/Sidebar/L0Menu.d.ts +4 -4
  60. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  61. package/dist/types/src/components/Sidebar/L1Panel.d.ts +1 -1
  62. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  63. package/dist/types/src/components/Sidebar/L1Tabs.d.ts +2 -2
  64. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  65. package/dist/types/src/components/index.d.ts.map +1 -1
  66. package/dist/types/src/components/types.d.ts +7 -7
  67. package/dist/types/src/components/types.d.ts.map +1 -1
  68. package/dist/types/src/hooks.d.ts +32 -1
  69. package/dist/types/src/hooks.d.ts.map +1 -1
  70. package/dist/types/src/index.d.ts +1 -2
  71. package/dist/types/src/index.d.ts.map +1 -1
  72. package/dist/types/src/meta.d.ts +2 -2
  73. package/dist/types/src/meta.d.ts.map +1 -1
  74. package/dist/types/src/testing/app-graph-builder.d.ts +2 -3
  75. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  76. package/dist/types/src/testing/generator.d.ts +5 -6
  77. package/dist/types/src/testing/generator.d.ts.map +1 -1
  78. package/dist/types/src/testing/update-state.d.ts +2 -2
  79. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  80. package/dist/types/src/types/capabilities.d.ts +16 -0
  81. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  82. package/dist/types/src/types/events.d.ts +5 -0
  83. package/dist/types/src/types/events.d.ts.map +1 -0
  84. package/dist/types/src/types/index.d.ts +4 -0
  85. package/dist/types/src/types/index.d.ts.map +1 -0
  86. package/dist/types/src/{types.d.ts → types/schema.d.ts} +6 -6
  87. package/dist/types/src/types/schema.d.ts.map +1 -0
  88. package/dist/types/src/util.d.ts +7 -7
  89. package/dist/types/src/util.d.ts.map +1 -1
  90. package/dist/types/tsconfig.tsbuildinfo +1 -1
  91. package/package.json +53 -52
  92. package/src/NavTreePlugin.tsx +53 -70
  93. package/src/capabilities/app-graph-builder/app-graph-builder.ts +41 -0
  94. package/src/capabilities/app-graph-builder/index.ts +7 -0
  95. package/src/capabilities/index.ts +5 -9
  96. package/src/capabilities/keyboard/index.ts +7 -0
  97. package/src/capabilities/keyboard/keyboard.ts +65 -0
  98. package/src/capabilities/operation-resolver/index.ts +7 -0
  99. package/src/capabilities/operation-resolver/operation-resolver.ts +41 -0
  100. package/src/capabilities/react-surface/index.ts +7 -0
  101. package/src/capabilities/react-surface/react-surface.tsx +54 -0
  102. package/src/capabilities/state/index.ts +7 -0
  103. package/src/capabilities/state/state.ts +121 -0
  104. package/src/components/CommandsDialog/CommandsDialog.tsx +107 -104
  105. package/src/components/CommandsDialog/CommandsTrigger.tsx +4 -14
  106. package/src/components/NavTree/NavTree.stories.tsx +32 -41
  107. package/src/components/NavTree/NavTree.tsx +5 -15
  108. package/src/components/NavTreeContainer.tsx +251 -292
  109. package/src/components/NavTreeDocumentTitle.tsx +3 -2
  110. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +17 -7
  111. package/src/components/NavTreeItem/NavTreeItemAction.tsx +18 -12
  112. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +5 -5
  113. package/src/components/Sidebar/L0Menu.tsx +65 -91
  114. package/src/components/Sidebar/L1Panel.tsx +9 -7
  115. package/src/components/Sidebar/L1Tabs.tsx +6 -31
  116. package/src/components/index.ts +1 -0
  117. package/src/components/types.ts +7 -7
  118. package/src/experimental/Tree.stories.tsx +2 -2
  119. package/src/experimental/Tree.tsx +1 -1
  120. package/src/hooks.ts +54 -2
  121. package/src/index.ts +1 -2
  122. package/src/meta.ts +2 -2
  123. package/src/testing/app-graph-builder.ts +158 -234
  124. package/src/testing/generator.ts +12 -12
  125. package/src/testing/update-state.ts +6 -6
  126. package/src/types/capabilities.ts +21 -0
  127. package/src/types/events.ts +11 -0
  128. package/src/types/index.ts +7 -0
  129. package/src/{types.ts → types/schema.ts} +5 -5
  130. package/src/util.ts +13 -16
  131. package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs +0 -46
  132. package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs.map +0 -7
  133. package/dist/lib/browser/chunk-5VLWX4PK.mjs +0 -15
  134. package/dist/lib/browser/chunk-5VLWX4PK.mjs.map +0 -7
  135. package/dist/lib/browser/chunk-E6MZBCU5.mjs.map +0 -7
  136. package/dist/lib/browser/chunk-UDAINHB6.mjs +0 -1488
  137. package/dist/lib/browser/chunk-UDAINHB6.mjs.map +0 -7
  138. package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs +0 -47
  139. package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +0 -7
  140. package/dist/lib/browser/keyboard-YJEK2HTH.mjs.map +0 -7
  141. package/dist/lib/browser/react-surface-V6LS3R4L.mjs +0 -51
  142. package/dist/lib/browser/react-surface-V6LS3R4L.mjs.map +0 -7
  143. package/dist/lib/browser/state-BFD5KJCV.mjs +0 -117
  144. package/dist/lib/browser/state-BFD5KJCV.mjs.map +0 -7
  145. package/dist/types/src/capabilities/app-graph-builder.d.ts +0 -4
  146. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +0 -1
  147. package/dist/types/src/capabilities/capabilities.d.ts +0 -21
  148. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  149. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  150. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  151. package/dist/types/src/capabilities/keyboard.d.ts +0 -4
  152. package/dist/types/src/capabilities/keyboard.d.ts.map +0 -1
  153. package/dist/types/src/capabilities/react-surface.d.ts +0 -4
  154. package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
  155. package/dist/types/src/capabilities/state.d.ts +0 -20
  156. package/dist/types/src/capabilities/state.d.ts.map +0 -1
  157. package/dist/types/src/events.d.ts +0 -4
  158. package/dist/types/src/events.d.ts.map +0 -1
  159. package/dist/types/src/types.d.ts.map +0 -1
  160. package/src/capabilities/app-graph-builder.ts +0 -53
  161. package/src/capabilities/capabilities.ts +0 -20
  162. package/src/capabilities/intent-resolver.ts +0 -37
  163. package/src/capabilities/keyboard.ts +0 -57
  164. package/src/capabilities/react-surface.tsx +0 -45
  165. package/src/capabilities/state.ts +0 -104
  166. package/src/events.ts +0 -11
@@ -4,20 +4,19 @@
4
4
 
5
5
  import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
6
6
  import { type Instruction, extractInstruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
7
- import { untracked } from '@preact/signals-core';
8
- import React, { memo, useCallback, useEffect, useMemo } from 'react';
7
+ import React, { forwardRef, memo, useCallback, useEffect, useMemo } from 'react';
9
8
 
10
- import { LayoutAction, createIntent } from '@dxos/app-framework';
11
- import { Surface, useAppGraph, useCapability, useIntentDispatcher, useLayout } from '@dxos/app-framework/react';
12
- import { type Node, ROOT_ID, type ReadableGraph, isAction, isActionLike } from '@dxos/app-graph';
9
+ import { Common } from '@dxos/app-framework';
10
+ import { Surface, useAppGraph, useLayout, useOperationInvoker } from '@dxos/app-framework/react';
13
11
  import { PLANK_COMPANION_TYPE } from '@dxos/plugin-deck/types';
12
+ import { Graph, Node, useActionRunner } from '@dxos/plugin-graph';
14
13
  import { useConnections, useActions as useGraphActions } from '@dxos/plugin-graph';
15
14
  import { useMediaQuery, useSidebars } from '@dxos/react-ui';
16
15
  import { type TreeData, type TreeItemDataProps, isTreeData } from '@dxos/react-ui-list';
17
- import { mx } from '@dxos/react-ui-theme';
16
+ import { mx } from '@dxos/ui-theme';
18
17
  import { arrayMove, byPosition } from '@dxos/util';
19
18
 
20
- import { NavTreeCapabilities } from '../capabilities';
19
+ import { useIsAlternateTree, useIsCurrent, useIsOpen, useNavTreeState } from '../hooks';
21
20
  import { meta } from '../meta';
22
21
  import { type FlattenedActions, type NavTreeItemGraphNode } from '../types';
23
22
  import { getChildren, getParent, resolveMigrationOperation } from '../util';
@@ -29,38 +28,38 @@ import { type NavTreeContextValue } from './types';
29
28
  // TODO(thure): Is NavTree truly authoritative in this regard?
30
29
  export const NODE_TYPE = 'dxos/app-graph/node';
31
30
 
32
- const renderItemEnd = ({ node, open }: { node: Node; open: boolean }) => (
31
+ const renderItemEnd = ({ node, open }: { node: Node.Node; open: boolean }) => (
33
32
  <Surface role='navtree-item-end' data={{ id: node.id, subject: node.data, open }} limit={1} />
34
33
  );
35
34
 
36
- const getChildrenFilter = (node: Node): node is Node =>
37
- untracked(
38
- () => !isActionLike(node) && node.type !== PLANK_COMPANION_TYPE && node.properties.disposition !== 'hidden',
39
- );
35
+ const getChildrenFilter = (node: Node.Node): node is Node.Node =>
36
+ !Node.isActionLike(node) && node.type !== PLANK_COMPANION_TYPE && node.properties.disposition !== 'hidden';
40
37
 
41
- const filterItems = (node: Node, disposition?: string) => {
38
+ const filterItems = (node: Node.Node, disposition?: string) => {
42
39
  if (!disposition && (node.properties.disposition === 'hidden' || node.properties.disposition === 'alternate-tree')) {
43
40
  return false;
44
41
  } else if (!disposition) {
45
- const action = isAction(node);
42
+ const action = Node.isAction(node);
46
43
  return !action || node.properties.disposition === 'item';
47
44
  } else {
48
45
  return node.properties.disposition === disposition;
49
46
  }
50
47
  };
51
48
 
52
- const getItems = (graph: ReadableGraph, node?: Node, disposition?: string) => {
53
- return graph.getConnections(node?.id ?? ROOT_ID, 'outbound').filter((node) => filterItems(node, disposition));
49
+ const getItems = (graph: Graph.ReadableGraph, node?: Node.Node, disposition?: string) => {
50
+ return Graph.getConnections(graph, node?.id ?? Node.RootId, 'outbound').filter((node) =>
51
+ filterItems(node, disposition),
52
+ );
54
53
  };
55
54
 
56
- const useItems = (node?: Node, options?: { disposition?: string; sort?: boolean }) => {
55
+ const useItems = (node?: Node.Node, options?: { disposition?: string; sort?: boolean }) => {
57
56
  const { graph } = useAppGraph();
58
- const connections = useConnections(graph, node?.id ?? ROOT_ID);
57
+ const connections = useConnections(graph, node?.id ?? Node.RootId);
59
58
  const filtered = connections.filter((node) => filterItems(node, options?.disposition));
60
59
  return options?.sort ? filtered.toSorted((a, b) => byPosition(a.properties, b.properties)) : filtered;
61
60
  };
62
61
 
63
- const useActions = (node: Node): FlattenedActions => {
62
+ const useActions = (node: Node.Node): FlattenedActions => {
64
63
  const { graph } = useAppGraph();
65
64
  const actions = useGraphActions(graph, node.id);
66
65
 
@@ -73,8 +72,8 @@ const useActions = (node: Node): FlattenedActions => {
73
72
  }
74
73
 
75
74
  acc.actions.push(arg);
76
- if (!isAction(arg)) {
77
- const actionGroup = graph.getActions(arg.id);
75
+ if (!Node.isAction(arg)) {
76
+ const actionGroup = Graph.getActions(graph, arg.id);
78
77
  acc.groupedActions[arg.id] = actionGroup;
79
78
  }
80
79
  return acc;
@@ -90,289 +89,249 @@ export type NavTreeContainerProps = {
90
89
  topbar?: boolean;
91
90
  } & Pick<NavTreeContextValue, 'tab'>;
92
91
 
93
- export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeContainerProps) => {
94
- const [isLg] = useMediaQuery('lg');
95
- const { dispatchPromise: dispatch } = useIntentDispatcher();
96
- const { graph } = useAppGraph();
97
- const { isOpen, isCurrent, isAlternateTree, setItem } = useCapability(NavTreeCapabilities.State);
98
- const layout = useLayout();
99
- const { navigationSidebarState } = useSidebars(meta.id);
100
-
101
- const getProps = useCallback(
102
- (node: Node, path: string[]): TreeItemDataProps => {
103
- const children = getChildren(graph, node, path).filter(getChildrenFilter);
104
- const parentOf =
105
- children.length > 0 ? children.map(({ id }) => id) : node.properties.role === 'branch' ? [] : undefined;
106
- return {
107
- id: node.id,
108
- parentOf,
109
- disabled: node.properties.disabled,
110
- label: node.properties.label ?? node.id,
111
- className: mx(node.properties.className, node.properties.modified && 'italic'), // TODO(burdon): Italic?
112
- headingClassName: node.properties.headingClassName,
113
- icon: node.properties.icon,
114
- iconHue: node.properties.iconHue,
115
- testId: node.properties.testId,
116
- };
117
- },
118
- [graph],
119
- );
92
+ export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProps>(
93
+ ({ tab, popoverAnchorId, topbar }, forwardedRef) => {
94
+ const [isLg] = useMediaQuery('lg');
95
+ const { invokeSync, invokePromise } = useOperationInvoker();
96
+ const runAction = useActionRunner();
97
+ const { graph } = useAppGraph();
98
+ const { getItem, setItem } = useNavTreeState();
99
+ const layout = useLayout();
100
+ const { navigationSidebarState } = useSidebars(meta.id);
101
+
102
+ const getProps = useCallback(
103
+ (node: Node.Node, path: string[]): TreeItemDataProps => {
104
+ const children = getChildren(graph, node, path).filter(getChildrenFilter);
105
+ const parentOf =
106
+ children.length > 0 ? children.map(({ id }) => id) : node.properties.role === 'branch' ? [] : undefined;
107
+ return {
108
+ id: node.id,
109
+ parentOf,
110
+ disabled: node.properties.disabled,
111
+ label: node.properties.label ?? node.id,
112
+ className: mx(node.properties.className, node.properties.modified && 'italic'), // TODO(burdon): Italic?
113
+ headingClassName: node.properties.headingClassName,
114
+ icon: node.properties.icon,
115
+ iconHue: node.properties.iconHue,
116
+ testId: node.properties.testId,
117
+ };
118
+ },
119
+ [graph],
120
+ );
121
+
122
+ const loadDescendents = useCallback(
123
+ (node: Node.Node) => {
124
+ Graph.expand(graph, node.id, 'outbound');
125
+ // Load one level deeper, which resolves some juddering observed on open/close.
126
+ Graph.getConnections(graph, node.id, 'outbound').forEach((child) => {
127
+ Graph.expand(graph, child.id, 'outbound');
128
+ });
129
+ },
130
+ [graph],
131
+ );
132
+
133
+ const handleOpenChange = useCallback(
134
+ ({ item: { id }, path, open }: { item: Node.Node; path: string[]; open: boolean }) => {
135
+ // TODO(thure): This might become a localstorage leak; openItemIds that no longer exist should be removed from this map.
136
+ setItem(path, 'open', open);
137
+ Graph.expand(graph, id, 'outbound');
138
+ },
139
+ [graph],
140
+ );
141
+
142
+ const handleTabChange = useCallback(
143
+ (node: NavTreeItemGraphNode) => {
144
+ invokeSync(Common.LayoutOperation.UpdateSidebar, {
145
+ state:
146
+ node.id === tab
147
+ ? navigationSidebarState === 'expanded'
148
+ ? isLg
149
+ ? 'collapsed'
150
+ : 'closed'
151
+ : 'expanded'
152
+ : 'expanded',
153
+ });
154
+
155
+ invokeSync(Common.LayoutOperation.SwitchWorkspace, { subject: node.id });
156
+
157
+ // Open the first item if the workspace is empty.
158
+ if (layout.active.length === 0) {
159
+ const [item] = getItems(graph, node).filter((node) => !Node.isActionLike(node));
160
+ if (item && item.data) {
161
+ invokeSync(Common.LayoutOperation.Open, { subject: [item.id] });
162
+ }
163
+ }
164
+ },
165
+ [invokeSync, layout.active, tab, navigationSidebarState, isLg],
166
+ );
120
167
 
121
- const loadDescendents = useCallback(
122
- (node: Node) => {
123
- graph.expand(node.id, 'outbound');
124
- // Load one level deeper, which resolves some juddering observed on open/close.
125
- graph.getConnections(node.id, 'outbound').forEach((child) => {
126
- graph.expand(child.id, 'outbound');
127
- });
128
- },
129
- [graph],
130
- );
168
+ const blockInstruction = useCallback(
169
+ ({ instruction, source, target }: { instruction: Instruction; source: TreeData; target: TreeData }) => {
170
+ return target.item.properties.blockInstruction?.(source, instruction) ?? false;
171
+ },
172
+ [],
173
+ );
131
174
 
132
- const handleOpenChange = useCallback(
133
- ({ item: { id }, path, open }: { item: Node; path: string[]; open: boolean }) => {
134
- // TODO(thure): This might become a localstorage leak; openItemIds that no longer exist should be removed from this map.
135
- setItem(path, 'open', open);
136
- graph.expand(id, 'outbound');
137
- },
138
- [graph],
139
- );
175
+ const canDrop = useCallback(({ source, target }: { source: TreeData; target: TreeData }) => {
176
+ return target.item.properties.canDrop?.(source) ?? false;
177
+ }, []);
178
+
179
+ const canSelect = useCallback(({ item }: { item: Node.Node }) => {
180
+ return item.properties.selectable ?? true;
181
+ }, []);
140
182
 
141
- const handleTabChange = useCallback(
142
- async (node: NavTreeItemGraphNode) => {
143
- await dispatch(
144
- createIntent(LayoutAction.UpdateSidebar, {
145
- part: 'sidebar',
146
- options: {
147
- state:
148
- node.id === tab
149
- ? navigationSidebarState === 'expanded'
150
- ? isLg
151
- ? 'collapsed'
152
- : 'closed'
153
- : 'expanded'
154
- : 'expanded',
155
- },
156
- }),
157
- );
158
-
159
- await dispatch(
160
- createIntent(LayoutAction.SwitchWorkspace, {
161
- part: 'workspace',
162
- subject: node.id,
163
- }),
164
- );
165
-
166
- // Open the first item if the workspace is empty.
167
- if (layout.active.length === 0) {
168
- const [item] = getItems(graph, node).filter((node) => !isActionLike(node));
169
- if (item && item.data) {
170
- await dispatch(
171
- createIntent(LayoutAction.Open, {
172
- part: 'main',
173
- subject: [item.id],
174
- }),
175
- );
183
+ const handleSelect = useCallback(
184
+ ({ item: node, path, option }: { item: Node.Node; path: string[]; option: boolean }) => {
185
+ if (!node.data) {
186
+ return;
176
187
  }
177
- }
178
- },
179
- [dispatch, layout.active, tab, navigationSidebarState, isLg],
180
- );
181
188
 
182
- const blockInstruction = useCallback(
183
- ({ instruction, source, target }: { instruction: Instruction; source: TreeData; target: TreeData }) => {
184
- return target.item.properties.blockInstruction?.(source, instruction) ?? false;
185
- },
186
- [],
187
- );
189
+ if (Node.isAction(node)) {
190
+ const [parent] = Graph.getConnections(graph, node.id, 'inbound');
191
+ if (parent) {
192
+ void runAction(node, { parent, caller: NAV_TREE_ITEM });
193
+ }
194
+ return;
195
+ }
188
196
 
189
- const canDrop = useCallback(({ source, target }: { source: TreeData; target: TreeData }) => {
190
- return target.item.properties.canDrop?.(source) ?? false;
191
- }, []);
192
-
193
- const canSelect = useCallback(({ item }: { item: Node }) => {
194
- return item.properties.selectable ?? true;
195
- }, []);
196
-
197
- const handleSelect = useCallback(
198
- ({ item: node, path, option }: { item: Node; path: string[]; option: boolean }) => {
199
- if (!node.data) {
200
- return;
201
- }
202
-
203
- if (isAction(node)) {
204
- const [parent] = graph.getConnections(node.id, 'inbound');
205
- void (parent && node.data({ parent, caller: NAV_TREE_ITEM }));
206
- return;
207
- }
208
-
209
- const current = isCurrent(path, node);
210
- if (!current) {
211
- void dispatch(
212
- createIntent(LayoutAction.Open, {
213
- part: 'main',
214
- subject: [node.id],
215
- options: { key: node.properties.key },
216
- }),
217
- );
218
- } else if (option) {
219
- void dispatch(
220
- createIntent(LayoutAction.Close, {
221
- part: 'main',
222
- subject: [node.id],
223
- options: { state: false },
224
- }),
225
- );
226
- } else {
227
- void dispatch(
228
- createIntent(LayoutAction.ScrollIntoView, {
229
- part: 'current',
230
- subject: node.id,
231
- }),
232
- );
233
- }
234
-
235
- const defaultAction = graph.getActions(node.id).find((action) => action.properties?.disposition === 'default');
236
- if (isAction(defaultAction)) {
237
- void (defaultAction.data as () => void)();
238
- }
239
-
240
- if (!isLg) {
241
- void dispatch(
242
- createIntent(LayoutAction.UpdateSidebar, {
243
- part: 'sidebar',
244
- options: { state: 'closed' },
245
- }),
246
- );
247
- }
248
- },
249
- [graph, dispatch, isCurrent, isLg],
250
- );
197
+ const current = getItem(path).current;
198
+ if (!current) {
199
+ invokeSync(Common.LayoutOperation.Open, { subject: [node.id], key: node.properties.key });
200
+ } else if (option) {
201
+ invokeSync(Common.LayoutOperation.Close, { subject: [node.id] });
202
+ } else {
203
+ void invokePromise(Common.LayoutOperation.ScrollIntoView, { subject: node.id });
204
+ }
251
205
 
252
- const handleBack = useCallback(
253
- () =>
254
- dispatch(
255
- createIntent(LayoutAction.RevertWorkspace, {
256
- part: 'workspace',
257
- options: { revert: true },
258
- }),
259
- ),
260
- [dispatch],
261
- );
206
+ const defaultAction = Graph.getActions(graph, node.id).find(
207
+ (action) => action.properties?.disposition === 'default',
208
+ );
209
+ if (Node.isAction(defaultAction)) {
210
+ void runAction(defaultAction);
211
+ }
262
212
 
263
- // TODO(wittjosiah): Factor out hook.
264
- useEffect(() => {
265
- return monitorForElements({
266
- canMonitor: ({ source }) => isTreeData(source.data),
267
- onDrop: ({ location, source }) => {
268
- // Didn't drop on anything.
269
- if (!location.current.dropTargets.length) {
270
- return;
213
+ if (!isLg) {
214
+ invokeSync(Common.LayoutOperation.UpdateSidebar, { state: 'closed' });
271
215
  }
272
- const target = location.current.dropTargets[0];
273
- const instruction: Instruction | null = extractInstruction(target.data);
274
- if (instruction !== null && instruction.type !== 'instruction-blocked') {
275
- const sourceNode = source.data.item as NavTreeItemGraphNode;
276
- const targetNode = target.data.item as NavTreeItemGraphNode;
277
- const sourcePath = source.data.path as string[];
278
- const targetPath = target.data.path as string[];
279
- const operation =
280
- sourcePath.slice(0, -1).join() === targetPath.slice(0, -1).join() && instruction.type !== 'make-child'
281
- ? 'rearrange'
282
- : resolveMigrationOperation(graph, sourceNode, targetPath, targetNode);
283
- const sourceParent = getParent(graph, sourceNode, sourcePath);
284
- const targetParent = getParent(graph, targetNode, targetPath);
285
- const sourceItems = getItems(graph, sourceParent);
286
- const targetItems = getItems(graph, targetParent);
287
- const sourceIndex = sourceItems.findIndex(({ id }) => id === sourceNode.id);
288
- const targetIndex = targetItems.findIndex(({ id }) => id === targetNode.id);
289
- const migrationIndex =
290
- instruction.type === 'make-child'
291
- ? undefined
292
- : instruction.type === 'reorder-below'
293
- ? targetIndex + 1
294
- : targetIndex;
295
- switch (operation) {
296
- case 'rearrange': {
297
- const nextItems = sourceItems.map(({ data }) => data);
298
- arrayMove(nextItems, sourceIndex, targetIndex);
299
- void sourceParent?.properties.onRearrangeChildren?.(nextItems);
300
- break;
301
- }
302
- case 'copy': {
303
- const target = instruction.type === 'make-child' ? targetNode : targetParent;
304
- void target?.properties.onCopy?.(sourceNode, migrationIndex);
305
- break;
306
- }
307
- case 'transfer': {
308
- const target = instruction.type === 'make-child' ? targetNode : targetParent;
309
- if (!target?.properties.onTransferStart || !sourceParent?.properties.onTransferEnd) {
216
+ },
217
+ [graph, invokeSync, invokePromise, getItem, isLg, runAction],
218
+ );
219
+
220
+ const handleBack = useCallback(() => invokeSync(Common.LayoutOperation.RevertWorkspace), [invokeSync]);
221
+
222
+ // TODO(wittjosiah): Factor out hook.
223
+ useEffect(() => {
224
+ return monitorForElements({
225
+ canMonitor: ({ source }) => isTreeData(source.data),
226
+ onDrop: ({ location, source }) => {
227
+ // Didn't drop on anything.
228
+ if (!location.current.dropTargets.length) {
229
+ return;
230
+ }
231
+ const target = location.current.dropTargets[0];
232
+ const instruction: Instruction | null = extractInstruction(target.data);
233
+ if (instruction !== null && instruction.type !== 'instruction-blocked') {
234
+ const sourceNode = source.data.item as NavTreeItemGraphNode;
235
+ const targetNode = target.data.item as NavTreeItemGraphNode;
236
+ const sourcePath = source.data.path as string[];
237
+ const targetPath = target.data.path as string[];
238
+ const operation =
239
+ sourcePath.slice(0, -1).join() === targetPath.slice(0, -1).join() && instruction.type !== 'make-child'
240
+ ? 'rearrange'
241
+ : resolveMigrationOperation(graph, sourceNode, targetPath, targetNode);
242
+ const sourceParent = getParent(graph, sourceNode, sourcePath);
243
+ const targetParent = getParent(graph, targetNode, targetPath);
244
+ const sourceItems = getItems(graph, sourceParent);
245
+ const targetItems = getItems(graph, targetParent);
246
+ const sourceIndex = sourceItems.findIndex(({ id }) => id === sourceNode.id);
247
+ const targetIndex = targetItems.findIndex(({ id }) => id === targetNode.id);
248
+ const migrationIndex =
249
+ instruction.type === 'make-child'
250
+ ? undefined
251
+ : instruction.type === 'reorder-below'
252
+ ? targetIndex + 1
253
+ : targetIndex;
254
+ switch (operation) {
255
+ case 'rearrange': {
256
+ const nextItems = sourceItems.map(({ data }) => data);
257
+ arrayMove(nextItems, sourceIndex, targetIndex);
258
+ void sourceNode.properties.onRearrange?.(nextItems);
259
+ break;
260
+ }
261
+ case 'copy': {
262
+ const target = instruction.type === 'make-child' ? targetNode : targetParent;
263
+ void target?.properties.onCopy?.(sourceNode, migrationIndex);
264
+ break;
265
+ }
266
+ case 'transfer': {
267
+ const target = instruction.type === 'make-child' ? targetNode : targetParent;
268
+ if (!target?.properties.onTransferStart || !sourceParent?.properties.onTransferEnd) {
269
+ break;
270
+ }
271
+ void target?.properties.onTransferStart(sourceNode, migrationIndex);
272
+ void sourceParent?.properties.onTransferEnd?.(sourceNode, target);
310
273
  break;
311
274
  }
312
- void target?.properties.onTransferStart(sourceNode, migrationIndex);
313
- void sourceParent?.properties.onTransferEnd?.(sourceNode, target);
314
- break;
315
275
  }
316
276
  }
317
- }
318
- },
319
- });
320
- }, [graph]);
321
-
322
- const setAlternateTree = useCallback(
323
- (path: string[], open: boolean) => {
324
- setItem(path, 'alternateTree', open);
325
- },
326
- [setItem],
327
- );
277
+ },
278
+ });
279
+ }, [graph]);
328
280
 
329
- const navTreeContextValue = useMemo(
330
- () => ({
331
- blockInstruction,
332
- canDrop,
333
- canSelect,
334
- getProps,
335
- isAlternateTree,
336
- isCurrent,
337
- isOpen,
338
- loadDescendents,
339
- onBack: handleBack,
340
- onOpenChange: handleOpenChange,
341
- onSelect: handleSelect,
342
- onTabChange: handleTabChange,
343
- popoverAnchorId,
344
- renderItemEnd,
345
- setAlternateTree,
346
- tab,
347
- topbar,
348
- useActions,
349
- useItems,
350
- }),
351
- [
352
- blockInstruction,
353
- canDrop,
354
- canSelect,
355
- getProps,
356
- handleBack,
357
- handleOpenChange,
358
- handleSelect,
359
- handleTabChange,
360
- isAlternateTree,
361
- isCurrent,
362
- isOpen,
363
- loadDescendents,
364
- popoverAnchorId,
365
- renderItemEnd,
366
- setAlternateTree,
367
- tab,
368
- topbar,
369
- useActions,
370
- ],
371
- );
281
+ const setAlternateTree = useCallback(
282
+ (path: string[], open: boolean) => {
283
+ setItem(path, 'alternateTree', open);
284
+ },
285
+ [setItem],
286
+ );
287
+
288
+ const navTreeContextValue = useMemo(
289
+ () => ({
290
+ blockInstruction,
291
+ canDrop,
292
+ canSelect,
293
+ getProps,
294
+ useIsAlternateTree,
295
+ useIsCurrent,
296
+ useIsOpen,
297
+ loadDescendents,
298
+ onBack: handleBack,
299
+ onOpenChange: handleOpenChange,
300
+ onSelect: handleSelect,
301
+ onTabChange: handleTabChange,
302
+ popoverAnchorId,
303
+ renderItemEnd,
304
+ setAlternateTree,
305
+ tab,
306
+ topbar,
307
+ useActions,
308
+ useItems,
309
+ }),
310
+ [
311
+ blockInstruction,
312
+ canDrop,
313
+ canSelect,
314
+ getProps,
315
+ handleBack,
316
+ handleOpenChange,
317
+ handleSelect,
318
+ handleTabChange,
319
+ loadDescendents,
320
+ popoverAnchorId,
321
+ renderItemEnd,
322
+ setAlternateTree,
323
+ tab,
324
+ topbar,
325
+ useActions,
326
+ ],
327
+ );
328
+
329
+ return (
330
+ <NavTreeContext.Provider value={navTreeContextValue}>
331
+ <NavTree id={Node.RootId} root={Graph.getRoot(graph)} open={layout.sidebarOpen} ref={forwardedRef} />
332
+ </NavTreeContext.Provider>
333
+ );
334
+ },
335
+ );
372
336
 
373
- return (
374
- <NavTreeContext.Provider value={navTreeContextValue}>
375
- <NavTree id={ROOT_ID} root={graph.root} open={layout.sidebarOpen} />
376
- </NavTreeContext.Provider>
377
- );
378
- });
337
+ export const NavTreeContainer = memo(NavTreeContainer$);
@@ -5,13 +5,14 @@ import { useEffect } from 'react';
5
5
 
6
6
  import { type Node } from '@dxos/app-graph';
7
7
  import { toLocalizedString, useTranslation } from '@dxos/react-ui';
8
+ import { osTranslations } from '@dxos/ui-theme';
8
9
 
9
10
  import { meta } from '../meta';
10
11
 
11
- export const NavTreeDocumentTitle = ({ node }: { node?: Node }) => {
12
+ export const NavTreeDocumentTitle = ({ node }: { node?: Node.Node }) => {
12
13
  const { t } = useTranslation(meta.id);
13
14
  useEffect(() => {
14
- document.title = node ? toLocalizedString(node.properties.label, t) : t('current app name', { ns: 'appkit' });
15
+ document.title = node ? toLocalizedString(node.properties.label, t) : t('current app name', { ns: osTranslations });
15
16
  }, [node?.properties?.label]);
16
17
  return null;
17
18
  };