@dxos/plugin-navtree 0.6.14-main.2b6a0f3 → 0.6.14-main.f49f251
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.
- package/dist/lib/browser/index.mjs +576 -386
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
- package/dist/types/src/components/NavTree.d.ts +20 -0
- package/dist/types/src/components/NavTree.d.ts.map +1 -0
- package/dist/types/src/components/NavTree.stories.d.ts +7 -0
- package/dist/types/src/components/NavTree.stories.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeContainer.d.ts +8 -8
- package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItemAction.d.ts +23 -0
- package/dist/types/src/components/NavTreeItemAction.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeItemAction.stories.d.ts +45 -0
- package/dist/types/src/components/NavTreeItemAction.stories.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/stories/Graph.stories.d.ts +1 -6
- package/dist/types/src/stories/Graph.stories.d.ts.map +1 -1
- package/dist/types/src/testing/generator.d.ts +86 -0
- package/dist/types/src/testing/generator.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +3 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/visitor.d.ts +18 -0
- package/dist/types/src/testing/visitor.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +5 -10
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +41 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/dist/types/src/util.d.ts +8 -9
- package/dist/types/src/util.d.ts.map +1 -1
- package/package.json +36 -33
- package/src/NavTreePlugin.tsx +88 -33
- package/src/components/NavTree.stories.tsx +104 -0
- package/src/components/NavTree.tsx +127 -0
- package/src/components/NavTreeContainer.tsx +141 -325
- package/src/components/NavTreeItemAction.stories.tsx +57 -0
- package/src/components/NavTreeItemAction.tsx +367 -0
- package/src/components/index.ts +1 -0
- package/src/experimental/Tree.stories.tsx +1 -1
- package/src/stories/Graph.stories.tsx +104 -98
- package/src/testing/generator.ts +199 -0
- package/src/testing/index.ts +6 -0
- package/src/testing/visitor.ts +152 -0
- package/src/translations.ts +3 -3
- package/src/types.ts +50 -0
- package/src/util.ts +52 -63
|
@@ -2,79 +2,59 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
6
|
+
import { extractInstruction, type Instruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
|
|
7
|
+
import React, { useCallback, useEffect } from 'react';
|
|
6
8
|
|
|
7
9
|
import { NavigationAction, Surface, useIntentDispatcher } from '@dxos/app-framework';
|
|
8
|
-
import {
|
|
10
|
+
import { isAction, isActionLike, type Node } from '@dxos/app-graph';
|
|
11
|
+
import { useGraph } from '@dxos/plugin-graph';
|
|
12
|
+
import { isEchoObject, isSpace } from '@dxos/react-client/echo';
|
|
9
13
|
import { ElevationProvider, useMediaQuery, useSidebars } from '@dxos/react-ui';
|
|
10
|
-
import {
|
|
11
|
-
import { type MosaicDropEvent, type MosaicMoveEvent, Mosaic, Path } from '@dxos/react-ui-mosaic';
|
|
12
|
-
import {
|
|
13
|
-
getLevel,
|
|
14
|
-
NavTree,
|
|
15
|
-
type NavTreeActionsNode,
|
|
16
|
-
type NavTreeItemMoveDetails,
|
|
17
|
-
type NavTreeItemNode,
|
|
18
|
-
type NavTreeNode,
|
|
19
|
-
type NavTreeProps,
|
|
20
|
-
} from '@dxos/react-ui-navtree';
|
|
21
|
-
import { ghostHover, mx } from '@dxos/react-ui-theme';
|
|
14
|
+
import { isItem } from '@dxos/react-ui-list';
|
|
22
15
|
import { arrayMove } from '@dxos/util';
|
|
23
16
|
|
|
17
|
+
import { NAV_TREE_ITEM, NavTree, type NavTreeProps } from './NavTree';
|
|
24
18
|
import { NavTreeFooter } from './NavTreeFooter';
|
|
25
19
|
import { NAVTREE_PLUGIN } from '../meta';
|
|
20
|
+
import { type NavTreeItem } from '../types';
|
|
26
21
|
import {
|
|
27
22
|
expandActions,
|
|
28
23
|
expandChildren,
|
|
29
24
|
getChildren,
|
|
30
25
|
getParent,
|
|
31
26
|
resolveMigrationOperation,
|
|
32
|
-
treeItemsFromRootNode,
|
|
33
|
-
type NavTreeItem,
|
|
34
27
|
type NavTreeItemGraphNode,
|
|
35
28
|
} from '../util';
|
|
36
29
|
|
|
37
30
|
// TODO(thure): Is NavTree truly authoritative in this regard?
|
|
38
31
|
export const NODE_TYPE = 'dxos/app-graph/node';
|
|
39
32
|
|
|
40
|
-
const renderPresence = (
|
|
41
|
-
<Surface role='presence--glyph' data={{ object: node.data }} />
|
|
33
|
+
const renderPresence = ({ item }: { item: NavTreeItem }) => (
|
|
34
|
+
<Surface role='presence--glyph' data={{ object: item.node.data, id: item.node.id }} />
|
|
42
35
|
);
|
|
43
36
|
|
|
44
|
-
export
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
onOpenItemIdsChange,
|
|
49
|
-
attended,
|
|
50
|
-
popoverAnchorId,
|
|
51
|
-
}: {
|
|
52
|
-
root: NavTreeItemGraphNode;
|
|
53
|
-
activeIds: Set<string>;
|
|
54
|
-
openItemIds: Record<string, true>;
|
|
55
|
-
onOpenItemIdsChange: (nextOpenItemIds: Record<string, true>) => void;
|
|
56
|
-
// TODO(wittjosiah): Use attention context directly?
|
|
57
|
-
attended: string[];
|
|
37
|
+
export type NavTreeContainerProps = {
|
|
38
|
+
items: NavTreeItem[];
|
|
39
|
+
current: string[];
|
|
40
|
+
open: string[];
|
|
58
41
|
popoverAnchorId?: string;
|
|
59
|
-
}
|
|
42
|
+
} & Pick<NavTreeProps, 'onOpenChange'>;
|
|
43
|
+
|
|
44
|
+
export const NavTreeContainer = ({ items, current, open, popoverAnchorId, ...props }: NavTreeContainerProps) => {
|
|
60
45
|
const { closeNavigationSidebar } = useSidebars(NAVTREE_PLUGIN);
|
|
61
46
|
const [isLg] = useMediaQuery('lg', { ssr: false });
|
|
62
47
|
const dispatch = useIntentDispatcher();
|
|
63
|
-
|
|
64
|
-
const graph = useMemo(() => getGraph(root), [root]);
|
|
65
|
-
const items = treeItemsFromRootNode(graph, root, openItemIds);
|
|
48
|
+
const { graph } = useGraph();
|
|
66
49
|
|
|
67
50
|
const loadDescendents = useCallback(
|
|
68
|
-
(node:
|
|
69
|
-
void expandActions(graph, node
|
|
51
|
+
(node: Node) => {
|
|
52
|
+
void expandActions(graph, node);
|
|
70
53
|
if (!isActionLike(node)) {
|
|
71
|
-
void expandChildren(graph, node
|
|
54
|
+
void expandChildren(graph, node).then(() =>
|
|
72
55
|
// Load one level deeper, which resolves some juddering observed on open/close.
|
|
73
56
|
Promise.all(
|
|
74
|
-
getChildren(graph, node
|
|
75
|
-
expandActions(graph, child),
|
|
76
|
-
expandChildren(graph, child),
|
|
77
|
-
]),
|
|
57
|
+
getChildren(graph, node).flatMap((child) => [expandActions(graph, child), expandChildren(graph, child)]),
|
|
78
58
|
),
|
|
79
59
|
);
|
|
80
60
|
}
|
|
@@ -82,311 +62,147 @@ export const NavTreeContainer = ({
|
|
|
82
62
|
[graph],
|
|
83
63
|
);
|
|
84
64
|
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
data: {
|
|
94
|
-
activeParts: {
|
|
95
|
-
main: [node.id],
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const defaultAction = actions?.find((action) => action.properties?.disposition === 'default');
|
|
101
|
-
if (isAction(defaultAction)) {
|
|
102
|
-
void (defaultAction.data as () => void)();
|
|
65
|
+
const canDrop = useCallback((source: NavTreeItem, target: NavTreeItem) => {
|
|
66
|
+
if (isSpace(target.node.data)) {
|
|
67
|
+
// TODO(wittjosiah): Find a way to only allow space as source for rearranging.
|
|
68
|
+
return isEchoObject(source.node.data) || isSpace(source.node.data);
|
|
69
|
+
} else if (isEchoObject(target.node.data)) {
|
|
70
|
+
return isEchoObject(source.node.data);
|
|
71
|
+
} else {
|
|
72
|
+
return false;
|
|
103
73
|
}
|
|
104
|
-
|
|
105
|
-
closeNavigationSidebar();
|
|
106
|
-
}
|
|
107
|
-
};
|
|
74
|
+
}, []);
|
|
108
75
|
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
} else {
|
|
114
|
-
// TODO(thure): Filter vs single-remove, make setting?
|
|
115
|
-
const { [id]: _, ...nextOpenItemIds } = openItemIds;
|
|
116
|
-
onOpenItemIdsChange(nextOpenItemIds);
|
|
76
|
+
const handleSelect = useCallback(
|
|
77
|
+
({ node, actions, path }: NavTreeItem) => {
|
|
78
|
+
if (!node.data) {
|
|
79
|
+
return;
|
|
117
80
|
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// TODO(wittjosiah): This is a temporary solution to ensure spaces get enabled when they are expanded.
|
|
121
|
-
const defaultAction = actions?.find((action) => action.properties?.disposition === 'default');
|
|
122
|
-
if (isAction(defaultAction)) {
|
|
123
|
-
void (defaultAction.data as () => void)();
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
81
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const nextItems = arrayMove(
|
|
133
|
-
items,
|
|
134
|
-
items.findIndex(({ id }) => id === activeId),
|
|
135
|
-
overPosition,
|
|
136
|
-
);
|
|
137
|
-
const previousItem: NavTreeItem | undefined = nextItems[overPosition - 1];
|
|
138
|
-
const nextItem: NavTreeItem | undefined = nextItems[overPosition + 1];
|
|
139
|
-
return Math.min(
|
|
140
|
-
previousItem ? getLevel(previousItem?.path) + 1 : 1,
|
|
141
|
-
Math.max(nextItem ? getLevel(nextItem?.path) : 1, getLevel(nextItems[overPosition].path) + levelOffset),
|
|
142
|
-
);
|
|
82
|
+
if (isAction(node)) {
|
|
83
|
+
const [parentId] = path.slice(1);
|
|
84
|
+
const parent = items.find(({ id }) => id === parentId)?.node;
|
|
85
|
+
void (parent && node.data({ node: parent, caller: NAV_TREE_ITEM }));
|
|
86
|
+
return;
|
|
143
87
|
}
|
|
144
|
-
},
|
|
145
|
-
[items],
|
|
146
|
-
);
|
|
147
88
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
);
|
|
89
|
+
// TODO(thure): Refactor opening related planks (comments in this case) to a generalized approach.
|
|
90
|
+
void dispatch({
|
|
91
|
+
action: NavigationAction.OPEN,
|
|
92
|
+
data: {
|
|
93
|
+
activeParts: {
|
|
94
|
+
main: [node.id],
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
});
|
|
157
98
|
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (!activeNode || !previousItem || !previousItem.path) {
|
|
162
|
-
// console.log('[reject]', !activeNode, !previousItem, !previousItem.path);
|
|
163
|
-
// log.warn('Top-level rearrange before the first item of the NavTree is unsupported at this time.');
|
|
164
|
-
return { operation: 'reject' as const, details: { levelOffset } };
|
|
99
|
+
const defaultAction = actions?.find((action) => action.properties?.disposition === 'default');
|
|
100
|
+
if (isAction(defaultAction)) {
|
|
101
|
+
void (defaultAction.data as () => void)();
|
|
165
102
|
}
|
|
166
103
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// console.log('[over]', overLevel, previousLevel, levelOffset);
|
|
170
|
-
if (previousLevel === overLevel - 1) {
|
|
171
|
-
if (Path.hasChild(previousItem.id, active.item.id)) {
|
|
172
|
-
// Previous is already parent of Active, rearrange.
|
|
173
|
-
const operation = previousItem.node.properties.onRearrangeChildren
|
|
174
|
-
? ('rearrange' as const)
|
|
175
|
-
: ('reject' as const);
|
|
176
|
-
return { operation, details: { levelOffset } };
|
|
177
|
-
} else {
|
|
178
|
-
// Previous is not yet parent of Active, check transfer or copy.
|
|
179
|
-
const operation = resolveMigrationOperation(graph, activeNode, previousItem.path, previousItem.node);
|
|
180
|
-
// console.log('[migration result]', operation, 'Previous is not yet parent of Active, check transfer or copy.');
|
|
181
|
-
return { operation, details: { levelOffset } };
|
|
182
|
-
}
|
|
183
|
-
} else if (previousLevel === overLevel) {
|
|
184
|
-
const parent = getParent(graph, previousItem.node, previousItem.path);
|
|
185
|
-
const parentPath = previousItem.path.slice(0, previousItem.path.length - 1);
|
|
186
|
-
if (Path.siblings(previousItem.id, active.item.id)) {
|
|
187
|
-
// Previous is already a sibling of Active, rearrange.
|
|
188
|
-
const operation = parent?.properties.onRearrangeChildren ? ('rearrange' as const) : ('reject' as const);
|
|
189
|
-
return { operation, details: { levelOffset } };
|
|
190
|
-
} else {
|
|
191
|
-
// Previous is not yet a sibling of Active, transfer/copy to Previous’s parent.
|
|
192
|
-
const operation = resolveMigrationOperation(graph, activeNode, parentPath, parent);
|
|
193
|
-
// console.log(
|
|
194
|
-
// '[migration result]',
|
|
195
|
-
// operation,
|
|
196
|
-
// 'Previous is not yet a sibling of Active, transfer/copy to Previous’s parent.',
|
|
197
|
-
// );
|
|
198
|
-
return { operation, details: { levelOffset } };
|
|
199
|
-
}
|
|
200
|
-
} else if (nextItem && nextItem.path) {
|
|
201
|
-
const nextLevel = getLevel(nextItem.path);
|
|
202
|
-
if (nextLevel === overLevel) {
|
|
203
|
-
const parent = getParent(graph, nextItem.node, nextItem.path);
|
|
204
|
-
const parentPath = nextItem.path.slice(0, nextItem.path.length - 1);
|
|
205
|
-
if (Path.siblings(nextItem.id, active.item.id)) {
|
|
206
|
-
// Next is already a sibling of Active, rearrange.
|
|
207
|
-
const operation = parent?.properties.onRearrangeChildren ? ('rearrange' as const) : ('reject' as const);
|
|
208
|
-
return { operation, details: { levelOffset } };
|
|
209
|
-
} else {
|
|
210
|
-
// Next is not yet a sibling of Active, transfer to Next’s parent.
|
|
211
|
-
const operation = resolveMigrationOperation(graph, activeNode, parentPath, parent);
|
|
212
|
-
// console.log(
|
|
213
|
-
// '[migration result]',
|
|
214
|
-
// operation,
|
|
215
|
-
// 'Next is not yet a sibling of Active, transfer to Next’s parent.',
|
|
216
|
-
// );
|
|
217
|
-
return { operation, details: { levelOffset } };
|
|
218
|
-
}
|
|
219
|
-
} else {
|
|
220
|
-
// console.log('[migration result]', 'reject');
|
|
221
|
-
return { operation: 'reject' as const, details: { levelOffset } };
|
|
222
|
-
}
|
|
223
|
-
} else {
|
|
224
|
-
// console.log('[migration result]', 'reject');
|
|
225
|
-
return { operation: 'reject' as const, details: { levelOffset } };
|
|
104
|
+
if (!isLg) {
|
|
105
|
+
closeNavigationSidebar();
|
|
226
106
|
}
|
|
227
107
|
},
|
|
228
|
-
[
|
|
108
|
+
[items, dispatch, isLg, closeNavigationSidebar],
|
|
229
109
|
);
|
|
230
110
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
items,
|
|
241
|
-
items.findIndex(({ id }) => id === active.item.id),
|
|
242
|
-
overPosition,
|
|
243
|
-
);
|
|
244
|
-
|
|
245
|
-
const activeNode = 'node' in active.item ? (active.item as NavTreeItem).node : undefined;
|
|
246
|
-
const activeParentId = Path.parent(active.item.id);
|
|
247
|
-
if (!activeNode) {
|
|
248
|
-
return undefined;
|
|
249
|
-
}
|
|
111
|
+
// TODO(wittjosiah): Factor out hook.
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
return monitorForElements({
|
|
114
|
+
canMonitor: ({ source }) => isItem(source.data),
|
|
115
|
+
onDrop: ({ location, source }) => {
|
|
116
|
+
// Didn't drop on anything.
|
|
117
|
+
if (!location.current.dropTargets.length) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
250
120
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (onTransferStart) {
|
|
288
|
-
void onTransferEnd(activeNode, previousItem.node);
|
|
289
|
-
void onTransferStart(activeNode, 0);
|
|
290
|
-
return null;
|
|
121
|
+
const target = location.current.dropTargets[0];
|
|
122
|
+
const instruction: Instruction | null = extractInstruction(target.data);
|
|
123
|
+
if (instruction !== null && instruction.type !== 'instruction-blocked') {
|
|
124
|
+
const sourceNode = source.data.node as NavTreeItemGraphNode;
|
|
125
|
+
const targetNode = target.data.node as NavTreeItemGraphNode;
|
|
126
|
+
|
|
127
|
+
const sourcePath = source.data.path as string[];
|
|
128
|
+
const targetPath = target.data.path as string[];
|
|
129
|
+
|
|
130
|
+
const nextItems = instruction.type.startsWith('reorder')
|
|
131
|
+
? items.filter((item) => item.path.slice(0, -1).join() === targetPath.slice(0, -1).join())
|
|
132
|
+
: items.filter((item) => item.path.slice(0, -1).join() === targetPath.join());
|
|
133
|
+
const operation =
|
|
134
|
+
sourcePath.slice(0, -1).join() === targetPath.slice(0, -1).join() && instruction.type !== 'make-child'
|
|
135
|
+
? 'rearrange'
|
|
136
|
+
: resolveMigrationOperation(graph, sourceNode, targetPath, targetNode);
|
|
137
|
+
|
|
138
|
+
const sourceParent = getParent(graph, sourceNode, sourcePath);
|
|
139
|
+
const targetParent = getParent(graph, targetNode, targetPath);
|
|
140
|
+
|
|
141
|
+
const sourceIndex = nextItems.findIndex(({ id }) => id === sourceNode.id);
|
|
142
|
+
const targetIndex = nextItems.findIndex(({ id }) => id === targetNode.id);
|
|
143
|
+
const migrationIndex =
|
|
144
|
+
instruction.type === 'make-child'
|
|
145
|
+
? nextItems.length > 0
|
|
146
|
+
? nextItems.length - 1
|
|
147
|
+
: 0
|
|
148
|
+
: instruction.type === 'reorder-below'
|
|
149
|
+
? targetIndex + 1
|
|
150
|
+
: targetIndex;
|
|
151
|
+
|
|
152
|
+
switch (operation) {
|
|
153
|
+
case 'rearrange': {
|
|
154
|
+
arrayMove(nextItems, sourceIndex, targetIndex);
|
|
155
|
+
void sourceParent?.properties.onRearrangeChildren?.(nextItems.map((item) => item.node.data));
|
|
156
|
+
break;
|
|
291
157
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
void
|
|
296
|
-
|
|
297
|
-
activeNode,
|
|
298
|
-
getChildren(graph, parent).findIndex(({ id }) => id === previousItem.node.id) + 1,
|
|
299
|
-
);
|
|
300
|
-
return null;
|
|
158
|
+
|
|
159
|
+
case 'copy': {
|
|
160
|
+
const target = instruction.type === 'make-child' ? targetNode : targetParent;
|
|
161
|
+
void target?.properties.onCopy?.(sourceNode, migrationIndex);
|
|
162
|
+
break;
|
|
301
163
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
);
|
|
310
|
-
|
|
164
|
+
|
|
165
|
+
case 'transfer': {
|
|
166
|
+
const target = instruction.type === 'make-child' ? targetNode : targetParent;
|
|
167
|
+
if (!target?.properties.onTransferStart || !sourceParent?.properties.onTransferEnd) {
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
void target?.properties.onTransferStart(sourceNode, migrationIndex);
|
|
172
|
+
void sourceParent?.properties.onTransferEnd?.(sourceNode, target);
|
|
173
|
+
break;
|
|
311
174
|
}
|
|
312
175
|
}
|
|
313
176
|
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
[root, items],
|
|
318
|
-
);
|
|
319
|
-
|
|
320
|
-
const handleDragEnd = useCallback(() => {
|
|
321
|
-
onOpenItemIdsChange({ ...openItemIds });
|
|
322
|
-
}, [onOpenItemIdsChange, openItemIds]);
|
|
323
|
-
|
|
324
|
-
// TODO(burdon): Remove.
|
|
325
|
-
// Debug list.
|
|
326
|
-
const debug = false;
|
|
327
|
-
if (debug) {
|
|
328
|
-
const getLabel = (item: NavTreeItem) => {
|
|
329
|
-
const label = item.node?.properties?.label;
|
|
330
|
-
return typeof label === 'string' ? label : item.id;
|
|
331
|
-
};
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}, [items]);
|
|
332
180
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
181
|
+
return (
|
|
182
|
+
<ElevationProvider elevation='chrome'>
|
|
183
|
+
<div
|
|
184
|
+
role='none'
|
|
185
|
+
className='bs-full overflow-hidden row-span-3 grid grid-cols-1 grid-rows-[min-content_1fr_min-content]'
|
|
186
|
+
>
|
|
187
|
+
<Surface role='search-input' limit={1} />
|
|
188
|
+
|
|
189
|
+
{/* TODO(thure): What gives this an inline `overflow: initial`? */}
|
|
336
190
|
<div role='none' className='!overflow-y-auto'>
|
|
337
|
-
<
|
|
338
|
-
{
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
</List.Root>
|
|
191
|
+
<NavTree
|
|
192
|
+
items={items}
|
|
193
|
+
open={open}
|
|
194
|
+
current={current}
|
|
195
|
+
loadDescendents={loadDescendents}
|
|
196
|
+
renderPresence={renderPresence}
|
|
197
|
+
popoverAnchorId={popoverAnchorId}
|
|
198
|
+
canDrop={canDrop}
|
|
199
|
+
onSelect={handleSelect}
|
|
200
|
+
{...props}
|
|
201
|
+
/>
|
|
350
202
|
</div>
|
|
203
|
+
|
|
351
204
|
<NavTreeFooter />
|
|
352
205
|
</div>
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
return (
|
|
357
|
-
<Mosaic.Root>
|
|
358
|
-
<Mosaic.DragOverlay />
|
|
359
|
-
<ElevationProvider elevation='chrome'>
|
|
360
|
-
<div
|
|
361
|
-
role='none'
|
|
362
|
-
className='bs-full overflow-hidden row-span-3 grid grid-cols-1 grid-rows-[min-content_1fr_min-content]'
|
|
363
|
-
>
|
|
364
|
-
<Surface role='search-input' limit={1} />
|
|
365
|
-
|
|
366
|
-
{/* TODO(thure): what gives this an inline `overflow: initial`? */}
|
|
367
|
-
<div role='none' className='!overflow-y-auto'>
|
|
368
|
-
<NavTree
|
|
369
|
-
id={root.id}
|
|
370
|
-
items={items}
|
|
371
|
-
current={activeIds}
|
|
372
|
-
attended={attended}
|
|
373
|
-
type={NODE_TYPE}
|
|
374
|
-
loadDescendents={loadDescendents}
|
|
375
|
-
popoverAnchorId={popoverAnchorId}
|
|
376
|
-
renderPresence={renderPresence}
|
|
377
|
-
resolveItemLevel={resolveItemLevel}
|
|
378
|
-
open={openItemIds}
|
|
379
|
-
onNavigate={handleNavigate}
|
|
380
|
-
onItemOpenChange={handleItemOpenChange}
|
|
381
|
-
onMove={handleMove}
|
|
382
|
-
onDrop={handleDrop}
|
|
383
|
-
onDragEnd={handleDragEnd}
|
|
384
|
-
/>
|
|
385
|
-
</div>
|
|
386
|
-
|
|
387
|
-
<NavTreeFooter />
|
|
388
|
-
</div>
|
|
389
|
-
</ElevationProvider>
|
|
390
|
-
</Mosaic.Root>
|
|
206
|
+
</ElevationProvider>
|
|
391
207
|
);
|
|
392
208
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxos-theme';
|
|
6
|
+
|
|
7
|
+
import { type NodeArg } from '@dxos/app-graph';
|
|
8
|
+
import { faker } from '@dxos/random';
|
|
9
|
+
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
10
|
+
|
|
11
|
+
import { NavTreeItemAction, type NavTreeItemActionMenuProps } from './NavTreeItemAction';
|
|
12
|
+
|
|
13
|
+
const parent = {
|
|
14
|
+
id: faker.string.uuid(),
|
|
15
|
+
type: 'node',
|
|
16
|
+
data: null,
|
|
17
|
+
properties: {
|
|
18
|
+
label: faker.lorem.words(2),
|
|
19
|
+
icon: 'ph--circle--regular',
|
|
20
|
+
},
|
|
21
|
+
} satisfies NodeArg<any>;
|
|
22
|
+
|
|
23
|
+
// TODO(burdon): Factor out across tests.
|
|
24
|
+
const menuActions = faker.helpers.multiple(
|
|
25
|
+
() =>
|
|
26
|
+
({
|
|
27
|
+
id: faker.string.uuid(),
|
|
28
|
+
type: 'action',
|
|
29
|
+
data: () => {
|
|
30
|
+
console.log('invoke');
|
|
31
|
+
},
|
|
32
|
+
properties: {
|
|
33
|
+
label: faker.lorem.words(2),
|
|
34
|
+
icon: 'ph--circle--regular',
|
|
35
|
+
},
|
|
36
|
+
}) satisfies NodeArg<any>,
|
|
37
|
+
{ count: 20 },
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export default {
|
|
41
|
+
title: 'plugins/plugin-navtree/NavTreeItemAction',
|
|
42
|
+
component: NavTreeItemAction,
|
|
43
|
+
decorators: [withTheme, withLayout({ fullscreen: true, tooltips: true })],
|
|
44
|
+
args: {
|
|
45
|
+
icon: 'ph--list--regular',
|
|
46
|
+
parent,
|
|
47
|
+
menuActions,
|
|
48
|
+
label: 'Select action',
|
|
49
|
+
onAction: (action) => {
|
|
50
|
+
console.log(action);
|
|
51
|
+
},
|
|
52
|
+
} satisfies Partial<NavTreeItemActionMenuProps>,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const Default = {};
|
|
56
|
+
|
|
57
|
+
export const SearchList = { args: { menuType: 'searchList' } };
|