@dxos/plugin-navtree 0.6.13 → 0.6.14-main.1366248
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/{chunk-U7MTKDFQ.mjs → chunk-PSVCPL4T.mjs} +1 -1
- package/dist/lib/browser/chunk-PSVCPL4T.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +611 -398
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/meta.mjs +1 -1
- package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
- package/dist/types/src/components/CommandsDialog.d.ts.map +1 -1
- package/dist/types/src/components/HaloButton.stories.d.ts +5 -9
- package/dist/types/src/components/HaloButton.stories.d.ts.map +1 -1
- package/dist/types/src/components/NavBarStart.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/experimental/Tree.stories.d.ts +3 -7
- package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/stories/Graph.stories.d.ts +4 -12
- 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 +39 -34
- package/src/NavTreePlugin.tsx +99 -41
- package/src/components/CommandsDialog.tsx +2 -4
- package/src/components/HaloButton.stories.tsx +8 -6
- package/src/components/NavBarStart.tsx +2 -4
- package/src/components/NavTree.stories.tsx +104 -0
- package/src/components/NavTree.tsx +127 -0
- package/src/components/NavTreeContainer.tsx +132 -302
- package/src/components/NavTreeFooter.tsx +1 -1
- 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 +11 -8
- package/src/experimental/Tree.tsx +2 -2
- package/src/meta.ts +3 -1
- package/src/stories/Graph.stories.tsx +111 -102
- 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
- package/dist/lib/browser/chunk-U7MTKDFQ.mjs.map +0 -7
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { Fragment, useCallback, useEffect, type FC } from 'react';
|
|
6
|
+
|
|
7
|
+
import { isAction, type Node } from '@dxos/app-graph';
|
|
8
|
+
import { Popover, toLocalizedString, Treegrid, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
import { Tree, type TreeProps } from '@dxos/react-ui-list';
|
|
10
|
+
import { type MaybePromise } from '@dxos/util';
|
|
11
|
+
|
|
12
|
+
import { NavTreeItemAction } from './NavTreeItemAction';
|
|
13
|
+
import { NAVTREE_PLUGIN } from '../meta';
|
|
14
|
+
import { type NavTreeItem } from '../types';
|
|
15
|
+
|
|
16
|
+
export const NAV_TREE_ITEM = 'NavTreeItem';
|
|
17
|
+
|
|
18
|
+
export type NavTreeProps = Omit<TreeProps<NavTreeItem>, 'draggable' | 'gridTemplateColumns' | 'renderColumns'> &
|
|
19
|
+
Pick<NavTreeColumnsProps, 'loadDescendents' | 'renderPresence' | 'popoverAnchorId'>;
|
|
20
|
+
|
|
21
|
+
export const NavTree = ({ loadDescendents, renderPresence, popoverAnchorId, ...props }: NavTreeProps) => {
|
|
22
|
+
const renderColumns = useCallback<NonNullable<TreeProps<NavTreeItem>['renderColumns']>>(
|
|
23
|
+
({ item, menuOpen, setMenuOpen }) => {
|
|
24
|
+
return (
|
|
25
|
+
<NavTreeColumns
|
|
26
|
+
item={item}
|
|
27
|
+
menuOpen={menuOpen}
|
|
28
|
+
setMenuOpen={setMenuOpen}
|
|
29
|
+
loadDescendents={loadDescendents}
|
|
30
|
+
renderPresence={renderPresence}
|
|
31
|
+
popoverAnchorId={popoverAnchorId}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
[renderPresence, popoverAnchorId, loadDescendents],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Tree<NavTreeItem>
|
|
40
|
+
{...props}
|
|
41
|
+
draggable
|
|
42
|
+
gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
|
|
43
|
+
classNames='pbs-1'
|
|
44
|
+
renderColumns={renderColumns}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type NavTreeColumnsProps = {
|
|
50
|
+
item: NavTreeItem;
|
|
51
|
+
menuOpen: boolean;
|
|
52
|
+
setMenuOpen: (open: boolean) => void;
|
|
53
|
+
loadDescendents?: (node: Node) => MaybePromise<void>;
|
|
54
|
+
renderPresence?: FC<{ item: NavTreeItem }>;
|
|
55
|
+
popoverAnchorId?: string;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const NavTreeColumns = ({
|
|
59
|
+
item,
|
|
60
|
+
menuOpen,
|
|
61
|
+
setMenuOpen,
|
|
62
|
+
loadDescendents,
|
|
63
|
+
renderPresence: Presence,
|
|
64
|
+
popoverAnchorId,
|
|
65
|
+
}: NavTreeColumnsProps) => {
|
|
66
|
+
const { t } = useTranslation(NAVTREE_PLUGIN);
|
|
67
|
+
|
|
68
|
+
const level = item.path.length - 2;
|
|
69
|
+
|
|
70
|
+
const [primaryAction, ...secondaryActions] = item.actions.toSorted((a, b) =>
|
|
71
|
+
a.properties?.disposition === 'toolbar' ? -1 : 1,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const actions = (primaryAction?.properties?.disposition === 'toolbar' ? secondaryActions : item.actions)
|
|
75
|
+
.flatMap((action) => (isAction(action) ? [action] : []))
|
|
76
|
+
.filter((action) => !action.properties?.hidden);
|
|
77
|
+
|
|
78
|
+
const ActionRoot = popoverAnchorId === `dxos.org/ui/${NAV_TREE_ITEM}/${item.id}` ? Popover.Anchor : Fragment;
|
|
79
|
+
|
|
80
|
+
// TODO(thure): Ideally this should not be necessary.
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
const frame = requestAnimationFrame(() => {
|
|
83
|
+
void loadDescendents?.(item.node);
|
|
84
|
+
if (primaryAction && !isAction(primaryAction)) {
|
|
85
|
+
void loadDescendents?.(primaryAction);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return () => cancelAnimationFrame(frame);
|
|
89
|
+
}, [primaryAction]);
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<>
|
|
93
|
+
{primaryAction?.properties?.disposition === 'toolbar' ? (
|
|
94
|
+
<NavTreeItemAction
|
|
95
|
+
testId={primaryAction.properties?.testId}
|
|
96
|
+
label={toLocalizedString(primaryAction.properties?.label, t)}
|
|
97
|
+
icon={primaryAction.properties?.icon ?? 'ph--placeholder--regular'}
|
|
98
|
+
parent={item.node}
|
|
99
|
+
monolithic={isAction(primaryAction)}
|
|
100
|
+
menuActions={isAction(primaryAction) ? [primaryAction] : item.groupedActions[primaryAction.id]}
|
|
101
|
+
menuType={primaryAction.properties?.menuType}
|
|
102
|
+
caller={NAV_TREE_ITEM}
|
|
103
|
+
/>
|
|
104
|
+
) : (
|
|
105
|
+
<Treegrid.Cell />
|
|
106
|
+
)}
|
|
107
|
+
<ActionRoot>
|
|
108
|
+
{actions.length > 0 ? (
|
|
109
|
+
<NavTreeItemAction
|
|
110
|
+
testId={`navtree.treeItem.actionsLevel${level}`}
|
|
111
|
+
label={t('tree item actions label')}
|
|
112
|
+
icon='ph--dots-three-vertical--regular'
|
|
113
|
+
parent={item.node}
|
|
114
|
+
menuActions={actions}
|
|
115
|
+
menuType='dropdown'
|
|
116
|
+
caller={NAV_TREE_ITEM}
|
|
117
|
+
menuOpen={menuOpen}
|
|
118
|
+
onChangeMenuOpen={setMenuOpen}
|
|
119
|
+
/>
|
|
120
|
+
) : (
|
|
121
|
+
<Treegrid.Cell />
|
|
122
|
+
)}
|
|
123
|
+
</ActionRoot>
|
|
124
|
+
{Presence && <Presence item={item} />}
|
|
125
|
+
</>
|
|
126
|
+
);
|
|
127
|
+
};
|
|
@@ -2,84 +2,59 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
useIntentDispatcher,
|
|
14
|
-
} from '@dxos/app-framework';
|
|
15
|
-
import { getGraph, isAction, isActionLike } from '@dxos/app-graph';
|
|
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';
|
|
8
|
+
|
|
9
|
+
import { NavigationAction, Surface, useIntentDispatcher } from '@dxos/app-framework';
|
|
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';
|
|
16
13
|
import { ElevationProvider, useMediaQuery, useSidebars } from '@dxos/react-ui';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
NavTree,
|
|
20
|
-
type NavTreeItemNode,
|
|
21
|
-
type NavTreeNode,
|
|
22
|
-
getLevel,
|
|
23
|
-
type NavTreeActionsNode,
|
|
24
|
-
type NavTreeProps,
|
|
25
|
-
type NavTreeItemMoveDetails,
|
|
26
|
-
} from '@dxos/react-ui-navtree';
|
|
14
|
+
import { isItem } from '@dxos/react-ui-list';
|
|
27
15
|
import { arrayMove } from '@dxos/util';
|
|
28
16
|
|
|
17
|
+
import { NAV_TREE_ITEM, NavTree, type NavTreeProps } from './NavTree';
|
|
29
18
|
import { NavTreeFooter } from './NavTreeFooter';
|
|
30
19
|
import { NAVTREE_PLUGIN } from '../meta';
|
|
20
|
+
import { type NavTreeItem } from '../types';
|
|
31
21
|
import {
|
|
32
22
|
expandActions,
|
|
33
23
|
expandChildren,
|
|
34
24
|
getChildren,
|
|
35
25
|
getParent,
|
|
36
|
-
type NavTreeItem,
|
|
37
|
-
type NavTreeItemGraphNode,
|
|
38
26
|
resolveMigrationOperation,
|
|
39
|
-
|
|
27
|
+
type NavTreeItemGraphNode,
|
|
40
28
|
} from '../util';
|
|
41
29
|
|
|
42
30
|
// TODO(thure): Is NavTree truly authoritative in this regard?
|
|
43
31
|
export const NODE_TYPE = 'dxos/app-graph/node';
|
|
44
32
|
|
|
45
|
-
const renderPresence = (
|
|
46
|
-
<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 }} />
|
|
47
35
|
);
|
|
48
36
|
|
|
49
|
-
export
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
onOpenItemIdsChange,
|
|
54
|
-
attended,
|
|
55
|
-
popoverAnchorId,
|
|
56
|
-
}: {
|
|
57
|
-
root: NavTreeItemGraphNode;
|
|
58
|
-
activeIds: Set<string>;
|
|
59
|
-
openItemIds: Record<string, true>;
|
|
60
|
-
onOpenItemIdsChange: (nextOpenItemIds: Record<string, true>) => void;
|
|
61
|
-
attended: Set<string>;
|
|
37
|
+
export type NavTreeContainerProps = {
|
|
38
|
+
items: NavTreeItem[];
|
|
39
|
+
current: string[];
|
|
40
|
+
open: string[];
|
|
62
41
|
popoverAnchorId?: string;
|
|
63
|
-
}
|
|
42
|
+
} & Pick<NavTreeProps, 'onOpenChange'>;
|
|
43
|
+
|
|
44
|
+
export const NavTreeContainer = ({ items, current, open, popoverAnchorId, ...props }: NavTreeContainerProps) => {
|
|
64
45
|
const { closeNavigationSidebar } = useSidebars(NAVTREE_PLUGIN);
|
|
65
46
|
const [isLg] = useMediaQuery('lg', { ssr: false });
|
|
66
47
|
const dispatch = useIntentDispatcher();
|
|
67
|
-
|
|
68
|
-
const graph = useMemo(() => getGraph(root), [root]);
|
|
69
|
-
|
|
70
|
-
const items = treeItemsFromRootNode(graph, root, openItemIds);
|
|
48
|
+
const { graph } = useGraph();
|
|
71
49
|
|
|
72
50
|
const loadDescendents = useCallback(
|
|
73
|
-
(node:
|
|
74
|
-
void expandActions(graph, node
|
|
51
|
+
(node: Node) => {
|
|
52
|
+
void expandActions(graph, node);
|
|
75
53
|
if (!isActionLike(node)) {
|
|
76
|
-
void expandChildren(graph, node
|
|
54
|
+
void expandChildren(graph, node).then(() =>
|
|
77
55
|
// Load one level deeper, which resolves some juddering observed on open/close.
|
|
78
56
|
Promise.all(
|
|
79
|
-
getChildren(graph, node
|
|
80
|
-
expandActions(graph, child),
|
|
81
|
-
expandChildren(graph, child),
|
|
82
|
-
]),
|
|
57
|
+
getChildren(graph, node).flatMap((child) => [expandActions(graph, child), expandChildren(graph, child)]),
|
|
83
58
|
),
|
|
84
59
|
);
|
|
85
60
|
}
|
|
@@ -87,262 +62,121 @@ export const NavTreeContainer = ({
|
|
|
87
62
|
[graph],
|
|
88
63
|
);
|
|
89
64
|
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
data: {
|
|
99
|
-
activeParts: node.data?.comments
|
|
100
|
-
? {
|
|
101
|
-
main: [node.id],
|
|
102
|
-
complementary: [`${node.id}${SLUG_PATH_SEPARATOR}comments${SLUG_COLLECTION_INDICATOR}`],
|
|
103
|
-
}
|
|
104
|
-
: {
|
|
105
|
-
main: [node.id],
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
await dispatch({ action: LayoutAction.SCROLL_INTO_VIEW, data: { id: node.id } });
|
|
111
|
-
|
|
112
|
-
const defaultAction = actions?.find((action) => action.properties?.disposition === 'default');
|
|
113
|
-
if (isAction(defaultAction)) {
|
|
114
|
-
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;
|
|
115
73
|
}
|
|
116
|
-
|
|
117
|
-
closeNavigationSidebar();
|
|
118
|
-
}
|
|
119
|
-
};
|
|
74
|
+
}, []);
|
|
120
75
|
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
} else {
|
|
126
|
-
// TODO(thure): Filter vs single-remove, make setting?
|
|
127
|
-
const { [id]: _, ...nextOpenItemIds } = openItemIds;
|
|
128
|
-
onOpenItemIdsChange(nextOpenItemIds);
|
|
76
|
+
const handleSelect = useCallback(
|
|
77
|
+
({ node, actions, path }: NavTreeItem) => {
|
|
78
|
+
if (!node.data) {
|
|
79
|
+
return;
|
|
129
80
|
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// TODO(wittjosiah): This is a temporary solution to ensure spaces get enabled when they are expanded.
|
|
133
|
-
const defaultAction = actions?.find((action) => action.properties?.disposition === 'default');
|
|
134
|
-
if (isAction(defaultAction)) {
|
|
135
|
-
void (defaultAction.data as () => void)();
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
81
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const nextItems = arrayMove(
|
|
145
|
-
items,
|
|
146
|
-
items.findIndex(({ id }) => id === activeId),
|
|
147
|
-
overPosition,
|
|
148
|
-
);
|
|
149
|
-
const previousItem: NavTreeItem | undefined = nextItems[overPosition - 1];
|
|
150
|
-
const nextItem: NavTreeItem | undefined = nextItems[overPosition + 1];
|
|
151
|
-
return Math.min(
|
|
152
|
-
previousItem ? getLevel(previousItem?.path) + 1 : 1,
|
|
153
|
-
Math.max(nextItem ? getLevel(nextItem?.path) : 1, getLevel(nextItems[overPosition].path) + levelOffset),
|
|
154
|
-
);
|
|
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;
|
|
155
87
|
}
|
|
156
|
-
},
|
|
157
|
-
[items],
|
|
158
|
-
);
|
|
159
88
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const activeNode = 'node' in active.item ? (active.item as NavTreeItem).node : undefined;
|
|
175
|
-
|
|
176
|
-
if (!activeNode || !previousItem || !previousItem.path) {
|
|
177
|
-
// console.log('[reject]', !activeNode, !previousItem, !previousItem.path);
|
|
178
|
-
// log.warn('Top-level rearrange before the first item of the NavTree is unsupported at this time.');
|
|
179
|
-
return { operation: 'reject' as const, details: { levelOffset } };
|
|
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
|
+
});
|
|
98
|
+
|
|
99
|
+
const defaultAction = actions?.find((action) => action.properties?.disposition === 'default');
|
|
100
|
+
if (isAction(defaultAction)) {
|
|
101
|
+
void (defaultAction.data as () => void)();
|
|
180
102
|
}
|
|
181
103
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const previousLevel = getLevel(previousItem.path);
|
|
185
|
-
|
|
186
|
-
// console.log('[over]', overLevel, previousLevel, levelOffset);
|
|
187
|
-
|
|
188
|
-
if (previousLevel === overLevel - 1) {
|
|
189
|
-
if (Path.hasChild(previousItem.id, active.item.id)) {
|
|
190
|
-
// Previous is already parent of Active, rearrange.
|
|
191
|
-
const operation = previousItem.node.properties.onRearrangeChildren
|
|
192
|
-
? ('rearrange' as const)
|
|
193
|
-
: ('reject' as const);
|
|
194
|
-
return { operation, details: { levelOffset } };
|
|
195
|
-
} else {
|
|
196
|
-
// Previous is not yet parent of Active, check transfer or copy.
|
|
197
|
-
const operation = resolveMigrationOperation(graph, activeNode, previousItem.path, previousItem.node);
|
|
198
|
-
// console.log('[migration result]', operation, 'Previous is not yet parent of Active, check transfer or copy.');
|
|
199
|
-
return { operation, details: { levelOffset } };
|
|
200
|
-
}
|
|
201
|
-
} else if (previousLevel === overLevel) {
|
|
202
|
-
const parent = getParent(graph, previousItem.node, previousItem.path);
|
|
203
|
-
const parentPath = previousItem.path.slice(0, previousItem.path.length - 1);
|
|
204
|
-
if (Path.siblings(previousItem.id, active.item.id)) {
|
|
205
|
-
// Previous is already a sibling of Active, rearrange.
|
|
206
|
-
const operation = parent?.properties.onRearrangeChildren ? ('rearrange' as const) : ('reject' as const);
|
|
207
|
-
return { operation, details: { levelOffset } };
|
|
208
|
-
} else {
|
|
209
|
-
// Previous is not yet a sibling of Active, transfer/copy to Previous’s parent.
|
|
210
|
-
const operation = resolveMigrationOperation(graph, activeNode, parentPath, parent);
|
|
211
|
-
// console.log(
|
|
212
|
-
// '[migration result]',
|
|
213
|
-
// operation,
|
|
214
|
-
// 'Previous is not yet a sibling of Active, transfer/copy to Previous’s parent.',
|
|
215
|
-
// );
|
|
216
|
-
return { operation, details: { levelOffset } };
|
|
217
|
-
}
|
|
218
|
-
} else if (nextItem && nextItem.path) {
|
|
219
|
-
const nextLevel = getLevel(nextItem.path);
|
|
220
|
-
if (nextLevel === overLevel) {
|
|
221
|
-
const parent = getParent(graph, nextItem.node, nextItem.path);
|
|
222
|
-
const parentPath = nextItem.path.slice(0, nextItem.path.length - 1);
|
|
223
|
-
if (Path.siblings(nextItem.id, active.item.id)) {
|
|
224
|
-
// Next is already a sibling of Active, rearrange.
|
|
225
|
-
const operation = parent?.properties.onRearrangeChildren ? ('rearrange' as const) : ('reject' as const);
|
|
226
|
-
return { operation, details: { levelOffset } };
|
|
227
|
-
} else {
|
|
228
|
-
// Next is not yet a sibling of Active, transfer to Next’s parent.
|
|
229
|
-
const operation = resolveMigrationOperation(graph, activeNode, parentPath, parent);
|
|
230
|
-
// console.log(
|
|
231
|
-
// '[migration result]',
|
|
232
|
-
// operation,
|
|
233
|
-
// 'Next is not yet a sibling of Active, transfer to Next’s parent.',
|
|
234
|
-
// );
|
|
235
|
-
return { operation, details: { levelOffset } };
|
|
236
|
-
}
|
|
237
|
-
} else {
|
|
238
|
-
// console.log('[migration result]', 'reject');
|
|
239
|
-
return { operation: 'reject' as const, details: { levelOffset } };
|
|
240
|
-
}
|
|
241
|
-
} else {
|
|
242
|
-
// console.log('[migration result]', 'reject');
|
|
243
|
-
return { operation: 'reject' as const, details: { levelOffset } };
|
|
104
|
+
if (!isLg) {
|
|
105
|
+
closeNavigationSidebar();
|
|
244
106
|
}
|
|
245
107
|
},
|
|
246
|
-
[
|
|
108
|
+
[items, dispatch, isLg, closeNavigationSidebar],
|
|
247
109
|
);
|
|
248
110
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
items,
|
|
259
|
-
items.findIndex(({ id }) => id === active.item.id),
|
|
260
|
-
overPosition,
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
const activeNode = 'node' in active.item ? (active.item as NavTreeItem).node : undefined;
|
|
264
|
-
const activeParentId = Path.parent(active.item.id);
|
|
265
|
-
|
|
266
|
-
if (!activeNode) {
|
|
267
|
-
return undefined;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const activeParent = getParent(graph, activeNode, (active.item as NavTreeItem).path ?? []);
|
|
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
|
+
}
|
|
271
120
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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;
|
|
157
|
+
}
|
|
280
158
|
|
|
281
|
-
|
|
159
|
+
case 'copy': {
|
|
160
|
+
const target = instruction.type === 'make-child' ? targetNode : targetParent;
|
|
161
|
+
void target?.properties.onCopy?.(sourceNode, migrationIndex);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
282
164
|
|
|
283
|
-
|
|
165
|
+
case 'transfer': {
|
|
166
|
+
const target = instruction.type === 'make-child' ? targetNode : targetParent;
|
|
167
|
+
if (!target?.properties.onTransferStart || !sourceParent?.properties.onTransferEnd) {
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
284
170
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
return null;
|
|
289
|
-
} else if (previousLevel === overLevel) {
|
|
290
|
-
const parent = getParent(graph, previousItem.node, previousItem.path ?? []);
|
|
291
|
-
void parent?.properties.onCopy?.(
|
|
292
|
-
activeNode,
|
|
293
|
-
getChildren(graph, parent).findIndex(({ id }) => id === previousItem.node.id) + 1,
|
|
294
|
-
);
|
|
295
|
-
return null;
|
|
296
|
-
} else if (nextItem && nextItem.path) {
|
|
297
|
-
const parent = getParent(graph, nextItem.node, nextItem.path ?? []);
|
|
298
|
-
void parent?.properties.onCopy?.(
|
|
299
|
-
activeNode,
|
|
300
|
-
getChildren(graph, parent).findIndex(({ id }) => id === nextItem.node.id),
|
|
301
|
-
);
|
|
302
|
-
return null;
|
|
303
|
-
}
|
|
304
|
-
} else if (operation === 'transfer') {
|
|
305
|
-
const onTransferEnd = activeParent?.properties.onTransferEnd;
|
|
306
|
-
if (!onTransferEnd) {
|
|
307
|
-
return undefined;
|
|
308
|
-
} else if (previousLevel === overLevel - 1) {
|
|
309
|
-
const onTransferStart = previousItem.node.properties.onTransferStart;
|
|
310
|
-
if (onTransferStart) {
|
|
311
|
-
void onTransferEnd(activeNode, previousItem.node);
|
|
312
|
-
void onTransferStart(activeNode, 0);
|
|
313
|
-
return null;
|
|
314
|
-
}
|
|
315
|
-
} else if (previousLevel === overLevel && previousItem.path) {
|
|
316
|
-
const parent = getParent(graph, previousItem.node, previousItem.path);
|
|
317
|
-
if (parent?.properties.onTransferStart) {
|
|
318
|
-
void onTransferEnd(activeNode, previousItem.node);
|
|
319
|
-
void parent.properties.onTransferStart(
|
|
320
|
-
activeNode,
|
|
321
|
-
getChildren(graph, parent).findIndex(({ id }) => id === previousItem.node.id) + 1,
|
|
322
|
-
);
|
|
323
|
-
return null;
|
|
324
|
-
}
|
|
325
|
-
} else if (nextItem && nextItem.path) {
|
|
326
|
-
const parent = getParent(graph, nextItem.node, nextItem.path);
|
|
327
|
-
if (parent?.properties.onTransferStart) {
|
|
328
|
-
void onTransferEnd(activeNode, previousItem.node);
|
|
329
|
-
void parent.properties.onTransferStart(
|
|
330
|
-
activeNode,
|
|
331
|
-
getChildren(graph, parent).findIndex(({ id }) => id === nextItem.node.id),
|
|
332
|
-
);
|
|
333
|
-
return null;
|
|
171
|
+
void target?.properties.onTransferStart(sourceNode, migrationIndex);
|
|
172
|
+
void sourceParent?.properties.onTransferEnd?.(sourceNode, target);
|
|
173
|
+
break;
|
|
334
174
|
}
|
|
335
175
|
}
|
|
336
176
|
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
[root, items],
|
|
341
|
-
);
|
|
342
|
-
|
|
343
|
-
const handleDragEnd = useCallback(() => {
|
|
344
|
-
onOpenItemIdsChange({ ...openItemIds });
|
|
345
|
-
}, [onOpenItemIdsChange, openItemIds]);
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}, [items]);
|
|
346
180
|
|
|
347
181
|
return (
|
|
348
182
|
<ElevationProvider elevation='chrome'>
|
|
@@ -351,26 +185,22 @@ export const NavTreeContainer = ({
|
|
|
351
185
|
className='bs-full overflow-hidden row-span-3 grid grid-cols-1 grid-rows-[min-content_1fr_min-content]'
|
|
352
186
|
>
|
|
353
187
|
<Surface role='search-input' limit={1} />
|
|
354
|
-
|
|
188
|
+
|
|
189
|
+
{/* TODO(thure): What gives this an inline `overflow: initial`? */}
|
|
355
190
|
<div role='none' className='!overflow-y-auto'>
|
|
356
191
|
<NavTree
|
|
357
|
-
id={root.id}
|
|
358
192
|
items={items}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
type={NODE_TYPE}
|
|
362
|
-
open={openItemIds}
|
|
363
|
-
onNavigate={handleNavigate}
|
|
364
|
-
onItemOpenChange={handleItemOpenChange}
|
|
365
|
-
onMove={handleMove}
|
|
366
|
-
onDrop={handleDrop}
|
|
367
|
-
onDragEnd={handleDragEnd}
|
|
368
|
-
popoverAnchorId={popoverAnchorId}
|
|
369
|
-
renderPresence={renderPresence}
|
|
370
|
-
resolveItemLevel={resolveItemLevel}
|
|
193
|
+
open={open}
|
|
194
|
+
current={current}
|
|
371
195
|
loadDescendents={loadDescendents}
|
|
196
|
+
renderPresence={renderPresence}
|
|
197
|
+
popoverAnchorId={popoverAnchorId}
|
|
198
|
+
canDrop={canDrop}
|
|
199
|
+
onSelect={handleSelect}
|
|
200
|
+
{...props}
|
|
372
201
|
/>
|
|
373
202
|
</div>
|
|
203
|
+
|
|
374
204
|
<NavTreeFooter />
|
|
375
205
|
</div>
|
|
376
206
|
</ElevationProvider>
|
|
@@ -35,7 +35,7 @@ const buttonStyles = 'pli-1.5 text-xs font-normal';
|
|
|
35
35
|
const repo = 'https://github.com/dxos/dxos';
|
|
36
36
|
|
|
37
37
|
export const NavTreeFooter = (props: { layoutPart?: LayoutPart }) => {
|
|
38
|
-
const layoutPart = useDefaultValue(props.layoutPart, 'sidebar');
|
|
38
|
+
const layoutPart = useDefaultValue(props.layoutPart, () => 'sidebar');
|
|
39
39
|
const config = useConfig();
|
|
40
40
|
const { t } = useTranslation(NAVTREE_PLUGIN);
|
|
41
41
|
const { navigationSidebarOpen } = useSidebars(NAVTREE_PLUGIN);
|