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