@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.
- package/dist/lib/browser/{app-graph-builder-26D3MUCA.mjs → app-graph-builder-P7AY3K72.mjs} +7 -6
- package/dist/lib/browser/app-graph-builder-P7AY3K72.mjs.map +7 -0
- package/dist/lib/browser/{chunk-TLQLMZXU.mjs → chunk-33VLL4IJ.mjs} +2 -2
- package/dist/lib/browser/{chunk-TLQLMZXU.mjs.map → chunk-33VLL4IJ.mjs.map} +1 -1
- package/dist/lib/browser/chunk-MFAWMFJC.mjs +1418 -0
- package/dist/lib/browser/chunk-MFAWMFJC.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +6 -12
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-OBGGMBVJ.mjs → intent-resolver-NAYKKAOP.mjs} +7 -7
- package/dist/lib/browser/intent-resolver-NAYKKAOP.mjs.map +7 -0
- package/dist/lib/browser/keyboard-DDFATRUG.mjs +51 -0
- package/dist/lib/browser/keyboard-DDFATRUG.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-DWMOF2TM.mjs → react-surface-HGA3TAQU.mjs} +3 -3
- package/dist/lib/browser/{state-EW62QYEN.mjs → state-R2UMYAHN.mjs} +3 -3
- package/dist/lib/browser/state-R2UMYAHN.mjs.map +7 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +2 -179
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +4 -180
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/keyboard.d.ts +2 -2
- package/dist/types/src/capabilities/keyboard.d.ts.map +1 -1
- package/dist/types/src/capabilities/state.d.ts +2 -2
- package/dist/types/src/capabilities/state.d.ts.map +1 -1
- package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +1 -0
- package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -0
- package/dist/types/src/components/CommandsDialog/index.d.ts +3 -0
- package/dist/types/src/components/CommandsDialog/index.d.ts.map +1 -0
- package/dist/types/src/components/NavTree/NavTree.d.ts +8 -0
- package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -0
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts +8 -0
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -0
- package/dist/types/src/components/NavTree/index.d.ts +2 -0
- package/dist/types/src/components/NavTree/index.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeContext.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +1 -1
- package/dist/types/src/components/{NavTreeItemAction.d.ts → NavTreeItem/NavTreeItemAction.d.ts} +3 -5
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +8 -0
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -0
- package/dist/types/src/components/{NavTreeItemColumns.d.ts → NavTreeItem/NavTreeItemColumns.d.ts} +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeItem/index.d.ts +3 -0
- package/dist/types/src/components/NavTreeItem/index.d.ts.map +1 -0
- package/dist/types/src/components/Sidebar/L0Menu.d.ts +13 -0
- package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -0
- package/dist/types/src/components/Sidebar/L1Panel.d.ts +15 -0
- package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -0
- package/dist/types/src/components/Sidebar/index.d.ts +3 -0
- package/dist/types/src/components/Sidebar/index.d.ts.map +1 -0
- package/dist/types/src/components/{UserAccountAvatar.d.ts → UserAccountAvatar/UserAccountAvatar.d.ts} +1 -1
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -0
- package/dist/types/src/components/{UserAccountAvatar.stories.d.ts → UserAccountAvatar/UserAccountAvatar.stories.d.ts} +1 -2
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -0
- package/dist/types/src/components/UserAccountAvatar/index.d.ts +2 -0
- package/dist/types/src/components/UserAccountAvatar/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +0 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/types.d.ts +17 -15
- package/dist/types/src/components/types.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.d.ts.map +1 -1
- package/dist/types/src/hooks.d.ts.map +1 -1
- package/dist/types/src/testing/app-graph-builder.d.ts +3 -0
- package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/testing/generator.d.ts +5 -4
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/update-state.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +1 -1
- package/dist/types/src/util.d.ts +7 -10
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +42 -32
- package/src/NavTreePlugin.tsx +2 -2
- package/src/capabilities/app-graph-builder.ts +38 -28
- package/src/capabilities/intent-resolver.ts +8 -7
- package/src/capabilities/keyboard.ts +37 -32
- package/src/capabilities/state.ts +3 -3
- package/src/components/{CommandsDialog.tsx → CommandsDialog/CommandsDialog.tsx} +8 -7
- package/src/components/{CommandsTrigger.tsx → CommandsDialog/CommandsTrigger.tsx} +1 -1
- package/src/components/CommandsDialog/index.ts +6 -0
- package/src/components/NavTree/NavTree.stories.tsx +159 -0
- package/src/components/NavTree/NavTree.tsx +62 -0
- package/src/components/NavTree/index.ts +5 -0
- package/src/components/NavTreeContainer.tsx +76 -72
- package/src/components/NavTreeContext.tsx +1 -1
- package/src/components/{NavTreeItemAction.stories.tsx → NavTreeItem/NavTreeItemAction.stories.tsx} +12 -6
- package/src/components/{NavTreeItemAction.tsx → NavTreeItem/NavTreeItemAction.tsx} +9 -18
- package/src/components/{NavTreeItemColumns.tsx → NavTreeItem/NavTreeItemColumns.tsx} +5 -5
- package/src/components/NavTreeItem/index.ts +6 -0
- package/src/components/{L0Menu.tsx → Sidebar/L0Menu.tsx} +186 -131
- package/src/components/{L1Panels.tsx → Sidebar/L1Panel.tsx} +40 -65
- package/src/components/Sidebar/index.ts +6 -0
- package/src/components/{UserAccountAvatar.stories.tsx → UserAccountAvatar/UserAccountAvatar.stories.tsx} +5 -5
- package/src/components/{UserAccountAvatar.tsx → UserAccountAvatar/UserAccountAvatar.tsx} +6 -6
- package/src/components/UserAccountAvatar/index.ts +5 -0
- package/src/components/index.ts +0 -1
- package/src/components/types.ts +17 -16
- package/src/testing/app-graph-builder.ts +224 -0
- package/src/testing/generator.ts +12 -9
- package/src/testing/index.ts +1 -0
- package/src/translations.ts +1 -1
- package/src/util.ts +10 -32
- package/dist/lib/browser/app-graph-builder-26D3MUCA.mjs.map +0 -7
- package/dist/lib/browser/chunk-FHOAKMRS.mjs +0 -1309
- package/dist/lib/browser/chunk-FHOAKMRS.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-OBGGMBVJ.mjs.map +0 -7
- package/dist/lib/browser/keyboard-IQXHKUWM.mjs +0 -46
- package/dist/lib/browser/keyboard-IQXHKUWM.mjs.map +0 -7
- package/dist/lib/browser/state-EW62QYEN.mjs.map +0 -7
- package/dist/types/src/components/CommandsDialog.d.ts.map +0 -1
- package/dist/types/src/components/CommandsTrigger.d.ts.map +0 -1
- package/dist/types/src/components/L0Menu.d.ts +0 -10
- package/dist/types/src/components/L0Menu.d.ts.map +0 -1
- package/dist/types/src/components/L1Panels.d.ts +0 -14
- package/dist/types/src/components/L1Panels.d.ts.map +0 -1
- package/dist/types/src/components/NavTree.d.ts +0 -5
- package/dist/types/src/components/NavTree.d.ts.map +0 -1
- package/dist/types/src/components/NavTree.stories.d.ts +0 -7
- package/dist/types/src/components/NavTree.stories.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeItemAction.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeItemAction.stories.d.ts +0 -41
- package/dist/types/src/components/NavTreeItemAction.stories.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeItemColumns.d.ts.map +0 -1
- package/dist/types/src/components/UserAccountAvatar.d.ts.map +0 -1
- package/dist/types/src/components/UserAccountAvatar.stories.d.ts.map +0 -1
- package/dist/types/src/stories/Graph.stories.d.ts +0 -7
- package/dist/types/src/stories/Graph.stories.d.ts.map +0 -1
- package/dist/types/src/stories/StorybookNavTree.d.ts +0 -4
- package/dist/types/src/stories/StorybookNavTree.d.ts.map +0 -1
- package/src/components/NavTree.stories.tsx +0 -107
- package/src/components/NavTree.tsx +0 -51
- package/src/stories/Graph.stories.tsx +0 -130
- package/src/stories/StorybookNavTree.tsx +0 -63
- /package/dist/lib/browser/{react-surface-DWMOF2TM.mjs.map → react-surface-HGA3TAQU.mjs.map} +0 -0
- /package/dist/types/src/components/{CommandsDialog.d.ts → CommandsDialog/CommandsDialog.d.ts} +0 -0
- /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
|
+
};
|
|
@@ -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,
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
|
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
|
|
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.
|
|
171
|
-
void (parent && node.data({
|
|
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.
|
|
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
|
|
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}
|
|
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
|
|
13
|
+
export const useNavTreeContext = () => useContext(NavTreeContext) ?? raise(new Error('Missing NavTreeContext'));
|
package/src/components/{NavTreeItemAction.stories.tsx → NavTreeItem/NavTreeItemAction.stories.tsx}
RENAMED
|
@@ -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
|
-
|
|
42
|
+
const meta: Meta<typeof NavTreeItemAction> = {
|
|
41
43
|
title: 'plugins/plugin-navtree/NavTreeItemAction',
|
|
42
44
|
component: NavTreeItemAction,
|
|
43
|
-
decorators: [withTheme, withLayout(
|
|
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
|
|
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
|
|
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 '
|
|
13
|
-
import { type ActionProperties } from '
|
|
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
|
-
|
|
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
|
|
47
|
-
<DropdownMenu.Root items={menuActions as MenuItem[]}
|
|
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 ? {
|
|
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
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
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);
|