@dxos/plugin-navtree 0.8.4-main.9735255 → 0.8.4-main.9be5663bfe
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +137 -72
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +6 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +7 -5
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/keyboard.d.ts +5 -0
- package/dist/types/src/capabilities/keyboard.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/state.d.ts +6 -0
- package/dist/types/src/capabilities/state.d.ts.map +1 -0
- package/dist/types/src/components/NavTree/NavTree.d.ts +10 -32
- package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts +10 -14
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
- package/dist/types/src/components/{NavTreeContext.d.ts → NavTreeContext/NavTreeContext.d.ts} +1 -1
- package/dist/types/src/components/NavTreeContext/NavTreeContext.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeContext/index.d.ts +2 -0
- package/dist/types/src/components/NavTreeContext/index.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +7 -3
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L0Menu.d.ts +4 -1
- package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Panel.d.ts +3 -6
- package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Tabs.d.ts +3 -2
- package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -3
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/types.d.ts +5 -13
- package/dist/types/src/components/types.d.ts.map +1 -1
- package/dist/types/src/{components/CommandsDialog/CommandsDialog.d.ts → containers/CommandsDialogContent/CommandsDialogContent.d.ts} +1 -1
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -0
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +29 -0
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts.map +1 -0
- package/dist/types/src/containers/CommandsDialogContent/index.d.ts +2 -0
- package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -0
- package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -0
- package/dist/types/src/containers/CommandsTrigger/index.d.ts +2 -0
- package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -0
- package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts +9 -0
- package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -0
- package/dist/types/src/containers/NavTreeContainer/index.d.ts +3 -0
- package/dist/types/src/containers/NavTreeContainer/index.d.ts.map +1 -0
- package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -0
- package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +2 -0
- package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +7 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/experimental/Tree.stories.d.ts +18 -4
- package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +5 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useActions.d.ts +5 -0
- package/dist/types/src/hooks/useActions.d.ts.map +1 -0
- package/dist/types/src/hooks/useLoadDescendents.d.ts +6 -0
- package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -0
- package/dist/types/src/hooks/useNavTreeModel.d.ts +7 -0
- package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -0
- package/dist/types/src/{hooks.d.ts → hooks/useNavTreeState.d.ts} +2 -14
- package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/operations/expose.d.ts +5 -0
- package/dist/types/src/operations/expose.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/testing/generator.d.ts +3 -3
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +9 -10
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/events.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +6 -2
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +54 -43
- package/src/NavTreePlugin.tsx +26 -27
- package/src/capabilities/{app-graph-builder/app-graph-builder.ts → app-graph-builder.ts} +10 -9
- package/src/capabilities/index.ts +11 -5
- package/src/capabilities/{keyboard/keyboard.ts → keyboard.ts} +7 -6
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/{react-surface/react-surface.tsx → react-surface.tsx} +15 -15
- package/src/capabilities/{state/state.ts → state.ts} +43 -15
- package/src/components/NavTree/NavTree.stories.tsx +30 -27
- package/src/components/NavTree/NavTree.tsx +64 -22
- package/src/components/{NavTreeContext.tsx → NavTreeContext/NavTreeContext.tsx} +1 -1
- package/src/components/NavTreeContext/index.ts +5 -0
- package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +7 -9
- package/src/components/NavTreeItem/NavTreeItemAction.tsx +56 -39
- package/src/components/NavTreeItem/NavTreeItemColumns.tsx +43 -17
- package/src/components/Sidebar/L0Menu.tsx +104 -113
- package/src/components/Sidebar/L1Panel.tsx +253 -94
- package/src/components/Sidebar/L1Tabs.tsx +13 -4
- package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +2 -3
- package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +1 -1
- package/src/components/index.ts +0 -3
- package/src/components/types.ts +12 -17
- package/src/containers/CommandsDialogContent/CommandsDialogContent.stories.tsx +45 -0
- package/src/{components/CommandsDialog/CommandsDialog.tsx → containers/CommandsDialogContent/CommandsDialogContent.tsx} +23 -21
- package/src/containers/CommandsDialogContent/index.ts +5 -0
- package/src/containers/CommandsTrigger/CommandsTrigger.tsx +28 -0
- package/src/containers/CommandsTrigger/index.ts +5 -0
- package/src/{components → containers/NavTreeContainer}/NavTreeContainer.tsx +85 -142
- package/src/containers/NavTreeContainer/index.ts +6 -0
- package/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.tsx +2 -2
- package/src/containers/NavTreeDocumentTitle/index.ts +5 -0
- package/src/containers/index.ts +12 -0
- package/src/experimental/Tree.stories.tsx +81 -84
- package/src/experimental/Tree.tsx +3 -3
- package/src/hooks/index.ts +8 -0
- package/src/hooks/useActions.ts +34 -0
- package/src/hooks/useLoadDescendents.ts +23 -0
- package/src/hooks/useNavTreeModel.ts +118 -0
- package/src/{hooks.ts → hooks/useNavTreeState.ts} +3 -34
- package/src/meta.ts +2 -2
- package/src/operations/expose.ts +42 -0
- package/src/operations/index.ts +7 -0
- package/src/testing/app-graph-builder.ts +40 -36
- package/src/testing/generator.ts +25 -25
- package/src/translations.ts +9 -10
- package/src/types/capabilities.ts +2 -2
- package/src/types/events.ts +4 -3
- package/src/util.ts +19 -5
- package/dist/lib/browser/app-graph-builder-RSMA6I22.mjs +0 -43
- package/dist/lib/browser/app-graph-builder-RSMA6I22.mjs.map +0 -7
- package/dist/lib/browser/chunk-6YPQ625I.mjs +0 -1293
- package/dist/lib/browser/chunk-6YPQ625I.mjs.map +0 -7
- package/dist/lib/browser/chunk-AGQDZBPL.mjs +0 -23
- package/dist/lib/browser/chunk-AGQDZBPL.mjs.map +0 -7
- package/dist/lib/browser/chunk-YKTYPINZ.mjs +0 -19
- package/dist/lib/browser/chunk-YKTYPINZ.mjs.map +0 -7
- package/dist/lib/browser/keyboard-32EH3N4K.mjs +0 -52
- package/dist/lib/browser/keyboard-32EH3N4K.mjs.map +0 -7
- package/dist/lib/browser/operation-resolver-IB75DC5O.mjs +0 -52
- package/dist/lib/browser/operation-resolver-IB75DC5O.mjs.map +0 -7
- package/dist/lib/browser/react-surface-F6CVMS3Y.mjs +0 -58
- package/dist/lib/browser/react-surface-F6CVMS3Y.mjs.map +0 -7
- package/dist/lib/browser/state-EVYQOUI3.mjs +0 -120
- package/dist/lib/browser/state-EVYQOUI3.mjs.map +0 -7
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +0 -6
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +0 -1
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts +0 -3
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/keyboard/index.d.ts +0 -3
- package/dist/types/src/capabilities/keyboard/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/keyboard/keyboard.d.ts +0 -5
- package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
- package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
- package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts +0 -5
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
- package/dist/types/src/capabilities/state/index.d.ts +0 -3
- package/dist/types/src/capabilities/state/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/state/state.d.ts +0 -6
- package/dist/types/src/capabilities/state/state.d.ts.map +0 -1
- package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +0 -1
- package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +0 -1
- package/dist/types/src/components/CommandsDialog/index.d.ts +0 -3
- package/dist/types/src/components/CommandsDialog/index.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeContainer.d.ts +0 -16
- package/dist/types/src/components/NavTreeContainer.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeContext.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +0 -1
- package/dist/types/src/hooks.d.ts.map +0 -1
- package/src/capabilities/app-graph-builder/index.ts +0 -7
- package/src/capabilities/keyboard/index.ts +0 -7
- package/src/capabilities/operation-resolver/index.ts +0 -7
- package/src/capabilities/operation-resolver/operation-resolver.ts +0 -41
- package/src/capabilities/react-surface/index.ts +0 -7
- package/src/capabilities/state/index.ts +0 -7
- package/src/components/CommandsDialog/CommandsTrigger.tsx +0 -26
- package/src/components/CommandsDialog/index.ts +0 -6
- /package/dist/types/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.d.ts +0 -0
- /package/dist/types/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.d.ts +0 -0
|
@@ -3,38 +3,16 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Meta } from '@storybook/react-vite';
|
|
6
|
-
import React, {
|
|
6
|
+
import React, { useEffect, useState } from 'react';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import { Icon, IconButton } from '@dxos/react-ui';
|
|
8
|
+
import { random } from '@dxos/random';
|
|
9
|
+
import { Icon, IconButton, Panel, ScrollArea } from '@dxos/react-ui';
|
|
10
10
|
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
11
11
|
import { mx } from '@dxos/ui-theme';
|
|
12
12
|
|
|
13
13
|
import { type ItemMap, Tree, type TreeNodeData, type TreeProps, visitNodes, visitor } from './Tree';
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Space:
|
|
19
|
-
* - Space panels are separated from each other to give them more weight than folders.
|
|
20
|
-
* - Private space isn't different (just first, different icon).
|
|
21
|
-
* - Edit titles in place.
|
|
22
|
-
*
|
|
23
|
-
* Navigation:
|
|
24
|
-
* - up/down across spaces/folder peers
|
|
25
|
-
* - right to enter space/folder children
|
|
26
|
-
* - left to parent
|
|
27
|
-
*/
|
|
28
|
-
const Container = ({ children, sidebar }: PropsWithChildren<{ sidebar: JSX.Element }>) => {
|
|
29
|
-
return (
|
|
30
|
-
<div className='flex'>
|
|
31
|
-
{/* TODO(burdon): Custom thin scrollbar. */}
|
|
32
|
-
{/* TODO(burdon): Horizontal scrolling within navtree? */}
|
|
33
|
-
<div className='flex flex-col overflow-y-auto is-[300px] bg-neutral-100 dark:bg-neutral-950'>{sidebar}</div>
|
|
34
|
-
<div className='flex flex-col grow overflow-hidden'>{children}</div>
|
|
35
|
-
</div>
|
|
36
|
-
);
|
|
37
|
-
};
|
|
15
|
+
random.seed(1234);
|
|
38
16
|
|
|
39
17
|
// TODO(burdon): Generate.
|
|
40
18
|
const data: TreeNodeData[] = [
|
|
@@ -43,34 +21,34 @@ const data: TreeNodeData[] = [
|
|
|
43
21
|
title: 'plugins/Root',
|
|
44
22
|
children: [
|
|
45
23
|
{
|
|
46
|
-
id:
|
|
24
|
+
id: random.string.uuid(),
|
|
47
25
|
title: 'plugins/Personal Space',
|
|
48
26
|
color: 'text-green-400',
|
|
49
27
|
iconName: 'ph--house--regular',
|
|
50
28
|
children: [
|
|
51
29
|
{
|
|
52
|
-
id:
|
|
53
|
-
title:
|
|
30
|
+
id: random.string.uuid(),
|
|
31
|
+
title: random.commerce.productName(),
|
|
54
32
|
},
|
|
55
33
|
{
|
|
56
|
-
id:
|
|
57
|
-
title:
|
|
34
|
+
id: random.string.uuid(),
|
|
35
|
+
title: random.commerce.productName(),
|
|
58
36
|
},
|
|
59
37
|
{
|
|
60
|
-
id:
|
|
61
|
-
title:
|
|
38
|
+
id: random.string.uuid(),
|
|
39
|
+
title: random.commerce.productName(),
|
|
62
40
|
children: [
|
|
63
41
|
{
|
|
64
|
-
id:
|
|
65
|
-
title:
|
|
42
|
+
id: random.string.uuid(),
|
|
43
|
+
title: random.commerce.productName(),
|
|
66
44
|
children: [
|
|
67
45
|
{
|
|
68
|
-
id:
|
|
69
|
-
title:
|
|
46
|
+
id: random.string.uuid(),
|
|
47
|
+
title: random.commerce.productName(),
|
|
70
48
|
},
|
|
71
49
|
{
|
|
72
|
-
id:
|
|
73
|
-
title:
|
|
50
|
+
id: random.string.uuid(),
|
|
51
|
+
title: random.commerce.productName(),
|
|
74
52
|
},
|
|
75
53
|
],
|
|
76
54
|
},
|
|
@@ -79,24 +57,24 @@ const data: TreeNodeData[] = [
|
|
|
79
57
|
],
|
|
80
58
|
},
|
|
81
59
|
{
|
|
82
|
-
id:
|
|
83
|
-
title:
|
|
60
|
+
id: random.string.uuid(),
|
|
61
|
+
title: random.commerce.productName(),
|
|
84
62
|
iconName: 'ph--planet--regular',
|
|
85
63
|
children: [
|
|
86
64
|
{
|
|
87
|
-
id:
|
|
88
|
-
title:
|
|
65
|
+
id: random.string.uuid(),
|
|
66
|
+
title: random.commerce.productName(),
|
|
89
67
|
},
|
|
90
68
|
],
|
|
91
69
|
},
|
|
92
70
|
{
|
|
93
|
-
id:
|
|
94
|
-
title:
|
|
71
|
+
id: random.string.uuid(),
|
|
72
|
+
title: random.commerce.productName(),
|
|
95
73
|
iconName: 'ph--sailboat--regular',
|
|
96
74
|
},
|
|
97
75
|
{
|
|
98
|
-
id:
|
|
99
|
-
title:
|
|
76
|
+
id: random.string.uuid(),
|
|
77
|
+
title: random.commerce.productName(),
|
|
100
78
|
iconName: 'ph--planet--regular',
|
|
101
79
|
},
|
|
102
80
|
],
|
|
@@ -134,7 +112,7 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
|
134
112
|
}
|
|
135
113
|
}, []);
|
|
136
114
|
|
|
137
|
-
const handleCreateItem: TreeProps['onMenuAction'] = (id
|
|
115
|
+
const handleCreateItem: TreeProps['onMenuAction'] = (id) => {
|
|
138
116
|
setItems((items) => {
|
|
139
117
|
let parent: TreeNodeData | undefined;
|
|
140
118
|
visitNodes(root, (node) => {
|
|
@@ -144,8 +122,8 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
|
144
122
|
});
|
|
145
123
|
|
|
146
124
|
(parent!.children ??= []).push({
|
|
147
|
-
id:
|
|
148
|
-
title:
|
|
125
|
+
id: random.string.uuid(),
|
|
126
|
+
title: random.commerce.productName(),
|
|
149
127
|
});
|
|
150
128
|
|
|
151
129
|
setOpenItems((open) => ({ ...open, [parent!.id]: true }));
|
|
@@ -157,9 +135,9 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
|
157
135
|
const handleCreateSpace = () => {
|
|
158
136
|
setItems((items) => {
|
|
159
137
|
items[0].children?.push({
|
|
160
|
-
id:
|
|
138
|
+
id: random.string.uuid(),
|
|
161
139
|
iconName: 'ph--planet--regular',
|
|
162
|
-
title:
|
|
140
|
+
title: random.commerce.productName(),
|
|
163
141
|
});
|
|
164
142
|
|
|
165
143
|
return { ...items };
|
|
@@ -167,49 +145,68 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
|
167
145
|
};
|
|
168
146
|
|
|
169
147
|
return (
|
|
170
|
-
<
|
|
171
|
-
<
|
|
172
|
-
<
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
148
|
+
<Panel.Root>
|
|
149
|
+
<Panel.Content asChild>
|
|
150
|
+
<ScrollArea.Root orientation='vertical' thin>
|
|
151
|
+
<ScrollArea.Viewport>
|
|
152
|
+
<Tree
|
|
153
|
+
className='p-0.5 gap-1'
|
|
154
|
+
node={root}
|
|
155
|
+
open={openItems}
|
|
156
|
+
selected={selectedItems}
|
|
157
|
+
active={activeItems}
|
|
158
|
+
onChangeOpen={(id, open) => setOpenItems((items) => ({ ...items, [id]: open }))}
|
|
159
|
+
onChangeSelected={(id, open) => setSelectedItems((items) => ({ ...items, [id]: open }))}
|
|
160
|
+
onMenuAction={handleCreateItem}
|
|
161
|
+
getSlots={(node, open, depth) => {
|
|
162
|
+
if (depth === 1) {
|
|
163
|
+
return {
|
|
164
|
+
root: 'rounded-sm bg-white dark:bg-neutral-850',
|
|
165
|
+
header: mx('rounded-t bg-neutral-50 dark:bg-neutral-900', !open && 'rounded-b'),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}}
|
|
169
|
+
/>
|
|
170
|
+
</ScrollArea.Viewport>
|
|
171
|
+
</ScrollArea.Root>
|
|
172
|
+
</Panel.Content>
|
|
173
|
+
<Panel.Statusbar>
|
|
174
|
+
<div className='flex items-center my-2 px-2 gap-2'>
|
|
175
|
+
<IconButton icon='ph--plus-circle--regular' iconOnly label='Create space' onClick={handleCreateSpace} />
|
|
176
|
+
<span className='grow text-sm' onClick={handleCreateSpace}>
|
|
177
|
+
New space
|
|
178
|
+
</span>
|
|
179
|
+
<Icon icon='ph--list--regular' />
|
|
180
|
+
</div>
|
|
181
|
+
</Panel.Statusbar>
|
|
182
|
+
</Panel.Root>
|
|
200
183
|
);
|
|
201
184
|
};
|
|
202
185
|
|
|
203
186
|
const meta = {
|
|
204
187
|
title: 'plugins/plugin-navtree/experimental/Tree',
|
|
205
188
|
component: Tree,
|
|
206
|
-
decorators: [withTheme, withLayout({ layout: 'column' })],
|
|
189
|
+
decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'w-[20rem]' })],
|
|
190
|
+
parameters: {
|
|
191
|
+
layout: 'fullscreen',
|
|
192
|
+
},
|
|
207
193
|
} satisfies Meta<typeof Tree>;
|
|
208
194
|
|
|
209
195
|
export default meta;
|
|
210
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Space:
|
|
199
|
+
* - Space panels are separated from each other to give them more weight than folders.
|
|
200
|
+
* - Private space isn't different (just first, different icon).
|
|
201
|
+
* - Edit titles in place.
|
|
202
|
+
*
|
|
203
|
+
* Navigation:
|
|
204
|
+
* - up/down across spaces/folder peers
|
|
205
|
+
* - right to enter space/folder children
|
|
206
|
+
* - left to parent
|
|
207
|
+
*/
|
|
211
208
|
export const Default = () => {
|
|
212
|
-
return <
|
|
209
|
+
return <Sidebar mutate />;
|
|
213
210
|
};
|
|
214
211
|
|
|
215
212
|
export const Visitor = () => {
|
|
@@ -19,7 +19,7 @@ export const IconButton = ({
|
|
|
19
19
|
} & Pick<HTMLAttributes<HTMLDivElement>, 'onClick'>) => {
|
|
20
20
|
// TODO(burdon): Density aware.
|
|
21
21
|
return (
|
|
22
|
-
<div className={mx('flex
|
|
22
|
+
<div className={mx('flex w-6 h-6 items-center justify-center select-none', classNames)} onClick={onClick}>
|
|
23
23
|
<Icon icon={iconName} classNames='cursor-pointer' size={size} />
|
|
24
24
|
</div>
|
|
25
25
|
);
|
|
@@ -193,9 +193,9 @@ export const TreeNodeRow = (props: TreeNodeProps & { className?: string }) => {
|
|
|
193
193
|
<Grid
|
|
194
194
|
style={{ paddingLeft: `${(depth - 1) * 24}px` }}
|
|
195
195
|
className={mx(
|
|
196
|
-
'group
|
|
196
|
+
'group w-full items-center cursor-pointer bg-hover-surface',
|
|
197
197
|
// TODO(burdon): Use data-active.
|
|
198
|
-
selected?.[id] && 'bg-
|
|
198
|
+
selected?.[id] && 'bg-active-surface',
|
|
199
199
|
className,
|
|
200
200
|
)}
|
|
201
201
|
onClick={() => selected && onChangeSelected?.(id, !selected[id])}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
8
|
+
import { Graph, Node, useActions as useGraphActions } from '@dxos/plugin-graph';
|
|
9
|
+
|
|
10
|
+
import { type FlattenedActions } from '#types';
|
|
11
|
+
|
|
12
|
+
/** Returns flattened actions and grouped sub-actions for a given graph node. */
|
|
13
|
+
export const useActions = (node: Node.Node): FlattenedActions => {
|
|
14
|
+
const { graph } = useAppGraph();
|
|
15
|
+
const actions = useGraphActions(graph, node.id);
|
|
16
|
+
|
|
17
|
+
return useMemo(() => {
|
|
18
|
+
return actions.reduce(
|
|
19
|
+
(acc: FlattenedActions, arg) => {
|
|
20
|
+
if (arg.properties.disposition === 'item') {
|
|
21
|
+
return acc;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
acc.actions.push(arg);
|
|
25
|
+
if (!Node.isAction(arg)) {
|
|
26
|
+
const actionGroup = Graph.getActions(graph, arg.id);
|
|
27
|
+
acc.groupedActions[arg.id] = actionGroup;
|
|
28
|
+
}
|
|
29
|
+
return acc;
|
|
30
|
+
},
|
|
31
|
+
{ actions: [], groupedActions: {} },
|
|
32
|
+
);
|
|
33
|
+
}, [actions]);
|
|
34
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useEffect } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Node } from '@dxos/app-graph';
|
|
8
|
+
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
9
|
+
import { Graph } from '@dxos/plugin-graph';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Expands a root node in the app graph.
|
|
13
|
+
*/
|
|
14
|
+
export const useLoadDescendents = (root?: Node.Node) => {
|
|
15
|
+
const { graph } = useAppGraph();
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!root) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Graph.expand(graph, root.id, 'child');
|
|
22
|
+
}, [graph, root]);
|
|
23
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Atom } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Option from 'effect/Option';
|
|
7
|
+
import { useMemo } from 'react';
|
|
8
|
+
|
|
9
|
+
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
10
|
+
import { PLANK_COMPANION_TYPE } from '@dxos/plugin-deck/types';
|
|
11
|
+
import { type Node } from '@dxos/plugin-graph';
|
|
12
|
+
import { Path, type TreeModel } from '@dxos/react-ui-list';
|
|
13
|
+
import { mx } from '@dxos/ui-theme';
|
|
14
|
+
|
|
15
|
+
import { type NavTreeItemGraphNode } from '#types';
|
|
16
|
+
|
|
17
|
+
import { filterItems } from '../util';
|
|
18
|
+
import { useNavTreeState } from './useNavTreeState';
|
|
19
|
+
|
|
20
|
+
// TODO(wittjosiah): Move companion/hidden nodes to their own edge categories so this filter is unnecessary.
|
|
21
|
+
const isVisibleChild = (node: Node.Node): boolean =>
|
|
22
|
+
node.type !== PLANK_COMPANION_TYPE && node.properties.disposition !== 'hidden';
|
|
23
|
+
|
|
24
|
+
/** Create an atom family for item display props keyed by path. */
|
|
25
|
+
const createItemPropsFamily = (graph: ReturnType<typeof useAppGraph>['graph']) =>
|
|
26
|
+
Atom.family((pathKey: string) => {
|
|
27
|
+
const path = Path.parts(pathKey);
|
|
28
|
+
const id = Path.last(pathKey);
|
|
29
|
+
return Atom.make((get) => {
|
|
30
|
+
const nodeOpt = get(graph.node(id));
|
|
31
|
+
const node = Option.getOrElse(nodeOpt, () => undefined);
|
|
32
|
+
if (!node) {
|
|
33
|
+
return { id, label: id };
|
|
34
|
+
}
|
|
35
|
+
const safeChildren = get(graph.connections(node.id, 'child')).filter((child) => !path.includes(child.id));
|
|
36
|
+
const visibleChildren = safeChildren.filter(isVisibleChild);
|
|
37
|
+
const parentOf =
|
|
38
|
+
visibleChildren.length > 0
|
|
39
|
+
? visibleChildren.map((child) => child.id)
|
|
40
|
+
: node.properties.role === 'branch'
|
|
41
|
+
? []
|
|
42
|
+
: undefined;
|
|
43
|
+
const parentId = path.length >= 2 ? path[path.length - 2] : undefined;
|
|
44
|
+
const parentNode = parentId ? Option.getOrElse(get(graph.node(parentId)), () => undefined) : undefined;
|
|
45
|
+
const droppable =
|
|
46
|
+
node.properties.droppable === false || parentNode?.properties.childrenDroppable === false ? false : undefined;
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
id: node.id,
|
|
50
|
+
parentOf,
|
|
51
|
+
disabled: node.properties.disabled,
|
|
52
|
+
draggable: node.properties.draggable,
|
|
53
|
+
droppable,
|
|
54
|
+
label: node.properties.label ?? node.id,
|
|
55
|
+
className: mx(node.properties.className, node.properties.modified && 'italic'),
|
|
56
|
+
headingClassName: node.properties.headingClassName,
|
|
57
|
+
icon: node.properties.icon,
|
|
58
|
+
iconHue: node.properties.iconHue,
|
|
59
|
+
testId: node.properties.testId,
|
|
60
|
+
};
|
|
61
|
+
}).pipe(Atom.keepAlive);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/** Create an atom family for outbound child IDs keyed by parent ID (pre-sorted by position on write). */
|
|
65
|
+
const createChildIdsFamily = (graph: ReturnType<typeof useAppGraph>['graph']) =>
|
|
66
|
+
Atom.family((id: string) =>
|
|
67
|
+
Atom.make((get) => get(graph.connections(id, 'child')).map((child) => child.id)).pipe(Atom.keepAlive),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
/** Create an atom family for item resolution keyed by ID. */
|
|
71
|
+
const createItemFamily = (graph: ReturnType<typeof useAppGraph>['graph']) =>
|
|
72
|
+
Atom.family((id: string) =>
|
|
73
|
+
Atom.make((get) => {
|
|
74
|
+
const node = Option.getOrElse(get(graph.node(id)), () => undefined);
|
|
75
|
+
return node && filterItems(node) ? node : undefined;
|
|
76
|
+
}).pipe(Atom.keepAlive),
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
/** Create an atom family for open state keyed by path. */
|
|
80
|
+
const createItemOpenFamily = (getItemAtom: ReturnType<typeof useNavTreeState>['getItemAtom']) =>
|
|
81
|
+
Atom.family((pathKey: string) => {
|
|
82
|
+
const path = Path.parts(pathKey);
|
|
83
|
+
const stateAtom = getItemAtom(path);
|
|
84
|
+
return Atom.make((get) => get(stateAtom).open).pipe(Atom.keepAlive);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/** Create an atom family for current (selected) state keyed by path. */
|
|
88
|
+
const createItemCurrentFamily = (getItemAtom: ReturnType<typeof useNavTreeState>['getItemAtom']) =>
|
|
89
|
+
Atom.family((pathKey: string) => {
|
|
90
|
+
const path = Path.parts(pathKey);
|
|
91
|
+
const stateAtom = getItemAtom(path);
|
|
92
|
+
return Atom.make((get) => get(stateAtom).current).pipe(Atom.keepAlive);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Creates a TreeModel backed by the app graph and navtree state.
|
|
97
|
+
*/
|
|
98
|
+
export const useNavTreeModel = (rootId: string): TreeModel<NavTreeItemGraphNode> => {
|
|
99
|
+
const { graph } = useAppGraph();
|
|
100
|
+
const { getItemAtom } = useNavTreeState();
|
|
101
|
+
|
|
102
|
+
const itemPropsFamily = useMemo(() => createItemPropsFamily(graph), [graph]);
|
|
103
|
+
const childIdsFamily = useMemo(() => createChildIdsFamily(graph), [graph]);
|
|
104
|
+
const itemFamily = useMemo(() => createItemFamily(graph), [graph]);
|
|
105
|
+
const itemOpenFamily = useMemo(() => createItemOpenFamily(getItemAtom), [getItemAtom]);
|
|
106
|
+
const itemCurrentFamily = useMemo(() => createItemCurrentFamily(getItemAtom), [getItemAtom]);
|
|
107
|
+
|
|
108
|
+
return useMemo(
|
|
109
|
+
() => ({
|
|
110
|
+
item: (id: string) => itemFamily(id),
|
|
111
|
+
itemProps: (path: string[]) => itemPropsFamily(Path.create(...path)),
|
|
112
|
+
itemOpen: (path: string[]) => itemOpenFamily(Path.create(...path)),
|
|
113
|
+
itemCurrent: (path: string[]) => itemCurrentFamily(Path.create(...path)),
|
|
114
|
+
childIds: (parentId?: string) => childIdsFamily(parentId ?? rootId),
|
|
115
|
+
}),
|
|
116
|
+
[itemFamily, itemPropsFamily, itemOpenFamily, itemCurrentFamily, childIdsFamily, rootId],
|
|
117
|
+
);
|
|
118
|
+
};
|
|
@@ -3,25 +3,11 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Atom, useAtomValue } from '@effect-atom/atom-react';
|
|
6
|
-
import {
|
|
6
|
+
import { useMemo } from 'react';
|
|
7
7
|
|
|
8
|
-
import { useCapability } from '@dxos/app-framework/
|
|
9
|
-
import { type Node } from '@dxos/app-graph';
|
|
8
|
+
import { useCapability } from '@dxos/app-framework/ui';
|
|
10
9
|
|
|
11
|
-
import {
|
|
12
|
-
import { NavTreeCapabilities } from './types';
|
|
13
|
-
|
|
14
|
-
export const useLoadDescendents = (root?: Node.Node) => {
|
|
15
|
-
const { loadDescendents } = useNavTreeContext();
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
const frame = requestAnimationFrame(() => {
|
|
18
|
-
if (root) {
|
|
19
|
-
void loadDescendents?.(root);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
return () => cancelAnimationFrame(frame);
|
|
23
|
-
}, [root]);
|
|
24
|
-
};
|
|
10
|
+
import { NavTreeCapabilities } from '#types';
|
|
25
11
|
|
|
26
12
|
export type UseNavTreeStateResult = {
|
|
27
13
|
getItem: (path: string[]) => NavTreeCapabilities.NavTreeItemState;
|
|
@@ -43,28 +29,11 @@ export const useNavTreeState = (): UseNavTreeStateResult => {
|
|
|
43
29
|
*/
|
|
44
30
|
export const useNavTreeItemState = (path: string[]): NavTreeCapabilities.NavTreeItemState => {
|
|
45
31
|
const { getItemAtom } = useCapability(NavTreeCapabilities.State);
|
|
46
|
-
// Memoize the path to avoid creating new atoms on every render.
|
|
47
32
|
const pathKey = useMemo(() => path.join('~'), [path]);
|
|
48
33
|
const atom = useMemo(() => getItemAtom(path), [getItemAtom, pathKey]);
|
|
49
34
|
return useAtomValue(atom);
|
|
50
35
|
};
|
|
51
36
|
|
|
52
|
-
/**
|
|
53
|
-
* Hook that subscribes to the open state for a tree item.
|
|
54
|
-
* Designed to be passed to Tree component as useIsOpen prop.
|
|
55
|
-
*/
|
|
56
|
-
export const useIsOpen = (path: string[], _item?: any): boolean => {
|
|
57
|
-
return useNavTreeItemState(path).open;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Hook that subscribes to the current state for a tree item.
|
|
62
|
-
* Designed to be passed to Tree component as useIsCurrent prop.
|
|
63
|
-
*/
|
|
64
|
-
export const useIsCurrent = (path: string[], _item?: any): boolean => {
|
|
65
|
-
return useNavTreeItemState(path).current;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
37
|
/**
|
|
69
38
|
* Hook that subscribes to the alternateTree state for a tree item.
|
|
70
39
|
*/
|
package/src/meta.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { type Plugin } from '@dxos/app-framework';
|
|
|
6
6
|
import { trim } from '@dxos/util';
|
|
7
7
|
|
|
8
8
|
export const meta: Plugin.Meta = {
|
|
9
|
-
id: 'dxos.
|
|
9
|
+
id: 'org.dxos.plugin.navtree',
|
|
10
10
|
name: 'Navtree',
|
|
11
11
|
description: trim`
|
|
12
12
|
Hierarchical navigation tree for browsing spaces, folders, and objects.
|
|
@@ -17,4 +17,4 @@ export const meta: Plugin.Meta = {
|
|
|
17
17
|
// TODO(wittjosiah): Factor out.
|
|
18
18
|
export const KEY_BINDING = 'KeyBinding';
|
|
19
19
|
// TODO(wittjosiah): Factor out.
|
|
20
|
-
export const COMMANDS_DIALOG = `${meta.id}
|
|
20
|
+
export const COMMANDS_DIALOG = `${meta.id}.commands-dialog`;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import * as Option from 'effect/Option';
|
|
7
|
+
|
|
8
|
+
import { Capability } from '@dxos/app-framework';
|
|
9
|
+
import { AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';
|
|
10
|
+
import { log } from '@dxos/log';
|
|
11
|
+
import { Operation } from '@dxos/operation';
|
|
12
|
+
import { Graph } from '@dxos/plugin-graph';
|
|
13
|
+
import { expandAttendableId } from '@dxos/react-ui-attention';
|
|
14
|
+
|
|
15
|
+
import { NavTreeCapabilities } from '../types';
|
|
16
|
+
|
|
17
|
+
const handler: Operation.WithHandler<typeof LayoutOperation.Expose> = LayoutOperation.Expose.pipe(
|
|
18
|
+
Operation.withHandler(
|
|
19
|
+
Effect.fnUntraced(function* ({ subject }) {
|
|
20
|
+
const { graph } = yield* Capability.get(AppCapabilities.AppGraph);
|
|
21
|
+
const { getItem, setItem } = yield* Capability.get(NavTreeCapabilities.State);
|
|
22
|
+
|
|
23
|
+
const prefixes = expandAttendableId(subject);
|
|
24
|
+
|
|
25
|
+
for (const qualifiedId of prefixes) {
|
|
26
|
+
Graph.expand(graph, qualifiedId, 'child');
|
|
27
|
+
|
|
28
|
+
const treePath = prefixes.slice(0, prefixes.indexOf(qualifiedId) + 1);
|
|
29
|
+
const state = getItem(treePath);
|
|
30
|
+
if (!state.open) {
|
|
31
|
+
setItem(treePath, 'open', true);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (Option.isNone(Graph.getNode(graph, subject))) {
|
|
36
|
+
log('Node not found after expansion', { subject });
|
|
37
|
+
}
|
|
38
|
+
}),
|
|
39
|
+
),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export default handler;
|