@dxos/plugin-navtree 0.8.4-main.1da679c → 0.8.4-main.21d9917
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-RSMA6I22.mjs +43 -0
- package/dist/lib/browser/app-graph-builder-RSMA6I22.mjs.map +7 -0
- package/dist/lib/browser/chunk-6YPQ625I.mjs +1293 -0
- package/dist/lib/browser/chunk-6YPQ625I.mjs.map +7 -0
- package/dist/lib/browser/chunk-AGQDZBPL.mjs +23 -0
- package/dist/lib/browser/chunk-AGQDZBPL.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 +82 -93
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/{keyboard-R2APW7UM.mjs → keyboard-32EH3N4K.mjs} +20 -19
- package/dist/lib/browser/keyboard-32EH3N4K.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/operation-resolver-IB75DC5O.mjs +52 -0
- package/dist/lib/browser/operation-resolver-IB75DC5O.mjs.map +7 -0
- package/dist/lib/browser/react-surface-F6CVMS3Y.mjs +58 -0
- package/dist/lib/browser/react-surface-F6CVMS3Y.mjs.map +7 -0
- package/dist/lib/browser/state-EVYQOUI3.mjs +120 -0
- package/dist/lib/browser/state-EVYQOUI3.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 +1 -1
- 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 +2 -3
- 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 +54 -53
- package/src/NavTreePlugin.tsx +65 -83
- package/src/capabilities/app-graph-builder/app-graph-builder.ts +41 -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 +65 -0
- package/src/capabilities/operation-resolver/index.ts +7 -0
- package/src/capabilities/operation-resolver/operation-resolver.ts +41 -0
- package/src/capabilities/react-surface/index.ts +7 -0
- package/src/capabilities/react-surface/react-surface.tsx +54 -0
- package/src/capabilities/state/index.ts +7 -0
- package/src/capabilities/state/state.ts +121 -0
- package/src/components/CommandsDialog/CommandsDialog.tsx +109 -98
- package/src/components/CommandsDialog/CommandsTrigger.tsx +6 -15
- package/src/components/NavTree/NavTree.stories.tsx +93 -88
- package/src/components/NavTree/NavTree.tsx +9 -15
- package/src/components/NavTreeContainer.tsx +254 -256
- 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 +112 -137
- package/src/components/Sidebar/L1Panel.tsx +16 -17
- 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 +26 -30
- 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 +11 -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-DU57YHO5.mjs +0 -45
- package/dist/lib/browser/app-graph-builder-DU57YHO5.mjs.map +0 -7
- package/dist/lib/browser/chunk-EMIN6D7Q.mjs +0 -1469
- package/dist/lib/browser/chunk-EMIN6D7Q.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/intent-resolver-RPZ5O7AJ.mjs +0 -47
- package/dist/lib/browser/intent-resolver-RPZ5O7AJ.mjs.map +0 -7
- package/dist/lib/browser/keyboard-R2APW7UM.mjs.map +0 -7
- package/dist/lib/browser/react-surface-DF7FNA34.mjs +0 -51
- package/dist/lib/browser/react-surface-DF7FNA34.mjs.map +0 -7
- package/dist/lib/browser/state-W5JDWIJL.mjs +0 -117
- package/dist/lib/browser/state-W5JDWIJL.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
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
import React, { useCallback, useMemo } from 'react';
|
|
6
6
|
|
|
7
7
|
import { type Node } from '@dxos/app-graph';
|
|
8
|
-
import { IconButton, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
8
|
+
import { DensityProvider, IconButton, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
9
9
|
import { Tree } from '@dxos/react-ui-list';
|
|
10
10
|
import { Tabs } from '@dxos/react-ui-tabs';
|
|
11
|
-
import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/
|
|
11
|
+
import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/ui-theme';
|
|
12
12
|
|
|
13
|
+
import { useIsAlternateTree } from '../../hooks';
|
|
13
14
|
import { meta } from '../../meta';
|
|
14
15
|
import { useNavTreeContext } from '../NavTreeContext';
|
|
15
16
|
import { NavTreeItemColumns } from '../NavTreeItem';
|
|
@@ -17,7 +18,7 @@ import { NavTreeItemColumns } from '../NavTreeItem';
|
|
|
17
18
|
export type L1PanelProps = {
|
|
18
19
|
open?: boolean;
|
|
19
20
|
path: string[];
|
|
20
|
-
item: Node
|
|
21
|
+
item: Node.Node;
|
|
21
22
|
currentItemId: string;
|
|
22
23
|
onBack?: () => void;
|
|
23
24
|
};
|
|
@@ -27,17 +28,16 @@ export type L1PanelProps = {
|
|
|
27
28
|
*/
|
|
28
29
|
export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProps) => {
|
|
29
30
|
const { t } = useTranslation(meta.id);
|
|
30
|
-
const
|
|
31
|
-
// NOTE(burdon): I'm very uneasy about this pattern of declaring hooks as context props.
|
|
32
|
-
const { useItems } = navTreeContext;
|
|
31
|
+
const navTreeContext = useNavTreeContext();
|
|
33
32
|
const title = toLocalizedString(item.properties.label, t);
|
|
34
33
|
|
|
35
34
|
// TODO(wittjosiah): Support multiple alternate trees.
|
|
35
|
+
const { useItems } = navTreeContext;
|
|
36
36
|
const alternateTree = useItems(item, { disposition: 'alternate-tree' })[0];
|
|
37
37
|
const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
38
|
-
const isAlternate =
|
|
38
|
+
const isAlternate = useIsAlternateTree(alternatePath, item);
|
|
39
39
|
const useAlternateItems = useCallback(
|
|
40
|
-
(node?: Node, { disposition }: { disposition?: string } = {}) => {
|
|
40
|
+
(node?: Node.Node, { disposition }: { disposition?: string } = {}) => {
|
|
41
41
|
// TODO(wittjosiah): Sorting is expensive, limit to necessary items for now.
|
|
42
42
|
return useItems(node, { disposition, sort: node?.id === alternateTree.id });
|
|
43
43
|
},
|
|
@@ -56,22 +56,22 @@ export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProp
|
|
|
56
56
|
]}
|
|
57
57
|
tabIndex={-1}
|
|
58
58
|
aria-label={title}
|
|
59
|
-
{...(!open && { inert:
|
|
59
|
+
{...(!open && { inert: true })}
|
|
60
60
|
>
|
|
61
61
|
{item.id === currentItemId && (
|
|
62
|
-
|
|
62
|
+
<DensityProvider density='fine'>
|
|
63
63
|
<L1PanelHeader path={path} item={item} currentItemId={currentItemId} onBack={onBack} />
|
|
64
64
|
<div role='none' className='overflow-y-auto'>
|
|
65
65
|
{isAlternate ? (
|
|
66
66
|
<Tree
|
|
67
67
|
{...navTreeContext}
|
|
68
|
-
useItems={useAlternateItems}
|
|
69
68
|
id={alternateTree.id}
|
|
70
69
|
root={alternateTree}
|
|
71
70
|
path={alternatePath}
|
|
72
71
|
levelOffset={5}
|
|
73
72
|
gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
|
|
74
73
|
renderColumns={NavTreeItemColumns}
|
|
74
|
+
useItems={useAlternateItems}
|
|
75
75
|
/>
|
|
76
76
|
) : (
|
|
77
77
|
<Tree
|
|
@@ -80,13 +80,13 @@ export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProp
|
|
|
80
80
|
root={item}
|
|
81
81
|
path={path}
|
|
82
82
|
levelOffset={5}
|
|
83
|
-
draggable
|
|
84
83
|
gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
|
|
85
84
|
renderColumns={NavTreeItemColumns}
|
|
85
|
+
draggable
|
|
86
86
|
/>
|
|
87
87
|
)}
|
|
88
88
|
</div>
|
|
89
|
-
|
|
89
|
+
</DensityProvider>
|
|
90
90
|
)}
|
|
91
91
|
</Tabs.Tabpanel>
|
|
92
92
|
);
|
|
@@ -97,13 +97,13 @@ export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProp
|
|
|
97
97
|
*/
|
|
98
98
|
const L1PanelHeader = ({ item, path, onBack }: L1PanelProps) => {
|
|
99
99
|
const { t } = useTranslation(meta.id);
|
|
100
|
-
const {
|
|
100
|
+
const { setAlternateTree, ...navTreeContext } = useNavTreeContext();
|
|
101
101
|
const title = toLocalizedString(item.properties.label, t);
|
|
102
102
|
|
|
103
103
|
// TODO(wittjosiah): Support multiple alternate trees.
|
|
104
104
|
const alternateTree = navTreeContext.useItems(item, { disposition: 'alternate-tree' })[0];
|
|
105
105
|
const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
106
|
-
const isAlternate =
|
|
106
|
+
const isAlternate = useIsAlternateTree(alternatePath, item);
|
|
107
107
|
const backCapable = item.id.startsWith('!') || isAlternate;
|
|
108
108
|
|
|
109
109
|
const handleOpen = useCallback(() => {
|
|
@@ -125,10 +125,10 @@ const L1PanelHeader = ({ item, path, onBack }: L1PanelProps) => {
|
|
|
125
125
|
<h2 className='flex-1 truncate min-is-0'>{title}</h2>
|
|
126
126
|
{(backCapable && (
|
|
127
127
|
<IconButton
|
|
128
|
+
data-testid='treeView.primaryTreeButton'
|
|
128
129
|
variant='ghost'
|
|
129
130
|
icon='ph--arrow-u-down-left--regular'
|
|
130
131
|
iconOnly
|
|
131
|
-
size={5}
|
|
132
132
|
label={t('button back')}
|
|
133
133
|
classNames={[hoverableControlItem, hoverableOpenControlItem, 'pointer-fine:pli-1']}
|
|
134
134
|
onClick={handleBack}
|
|
@@ -140,7 +140,6 @@ const L1PanelHeader = ({ item, path, onBack }: L1PanelProps) => {
|
|
|
140
140
|
variant='ghost'
|
|
141
141
|
icon={alternateTree.properties.icon ?? 'ph--placeholder--regular'}
|
|
142
142
|
iconOnly
|
|
143
|
-
size={5}
|
|
144
143
|
label={toLocalizedString(alternateTree.properties.label ?? alternateTree.id, t)}
|
|
145
144
|
classNames={[hoverableControlItem, hoverableOpenControlItem, 'pointer-fine:pli-1']}
|
|
146
145
|
onClick={handleOpen}
|
|
@@ -2,57 +2,32 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React
|
|
5
|
+
import React from 'react';
|
|
6
6
|
|
|
7
7
|
import { type Node } from '@dxos/app-graph';
|
|
8
8
|
|
|
9
|
-
import { useLoadDescendents } from '../../hooks';
|
|
10
9
|
import { l0ItemType } from '../../util';
|
|
11
|
-
import { useNavTreeContext } from '../NavTreeContext';
|
|
12
10
|
|
|
13
11
|
import { L1Panel, type L1PanelProps } from './L1Panel';
|
|
14
12
|
|
|
15
13
|
export type L1TabsProps = Pick<L1PanelProps, 'open' | 'currentItemId' | 'onBack'> & {
|
|
16
14
|
path: string[];
|
|
17
|
-
topLevelItems: Node
|
|
15
|
+
topLevelItems: Node.Node[];
|
|
18
16
|
};
|
|
19
17
|
|
|
20
18
|
/**
|
|
21
|
-
* Each
|
|
19
|
+
* Each workspace is an L1 tab.
|
|
22
20
|
*/
|
|
23
21
|
export const L1Tabs = ({ topLevelItems, onBack, ...props }: L1TabsProps) => {
|
|
24
22
|
return (
|
|
25
23
|
<>
|
|
26
24
|
{topLevelItems.map((item) => {
|
|
27
25
|
const type = l0ItemType(item);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return <L1Panel key={item.id} item={item} {...props} onBack={onBack} />;
|
|
31
|
-
case 'collection':
|
|
32
|
-
return <L1PanelCollection key={item.id} item={item} {...props} />;
|
|
33
|
-
default:
|
|
34
|
-
return null;
|
|
26
|
+
if (type === 'tab') {
|
|
27
|
+
return <L1Panel key={item.id} item={item} {...props} onBack={onBack} />;
|
|
35
28
|
}
|
|
29
|
+
return null;
|
|
36
30
|
})}
|
|
37
31
|
</>
|
|
38
32
|
);
|
|
39
33
|
};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
*
|
|
43
|
-
*/
|
|
44
|
-
const L1PanelCollection = ({ item, path, ...props }: L1PanelProps) => {
|
|
45
|
-
const { useItems } = useNavTreeContext();
|
|
46
|
-
useLoadDescendents(item);
|
|
47
|
-
const collectionItems = useItems(item);
|
|
48
|
-
const groupPath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
49
|
-
return (
|
|
50
|
-
<>
|
|
51
|
-
{collectionItems
|
|
52
|
-
.filter((item) => l0ItemType(item) === 'tab')
|
|
53
|
-
.map((item) => (
|
|
54
|
-
<L1Panel key={item.id} item={item} path={groupPath} {...props} />
|
|
55
|
-
))}
|
|
56
|
-
</>
|
|
57
|
-
);
|
|
58
|
-
};
|
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
|
|
9
|
-
import { withTheme } from '@dxos/
|
|
7
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
10
8
|
|
|
11
9
|
import { UserAccountAvatar } from './UserAccountAvatar';
|
|
12
10
|
|
|
13
11
|
const meta = {
|
|
14
12
|
title: 'plugins/plugin-navtree/UserAccountAvatar',
|
|
15
|
-
|
|
13
|
+
|
|
16
14
|
decorators: [withTheme],
|
|
15
|
+
component: UserAccountAvatar,
|
|
17
16
|
} satisfies Meta<typeof UserAccountAvatar>;
|
|
18
17
|
|
|
19
18
|
export default meta;
|
|
@@ -7,6 +7,8 @@ import React from 'react';
|
|
|
7
7
|
import { Avatar, type AvatarStatus, type Size } from '@dxos/react-ui';
|
|
8
8
|
import { hexToFallback } from '@dxos/util';
|
|
9
9
|
|
|
10
|
+
import { L0ItemActiveTabIndicator } from '../Sidebar';
|
|
11
|
+
|
|
10
12
|
export type UserAccountAvatarProps = {
|
|
11
13
|
size?: Size;
|
|
12
14
|
userId?: string;
|
|
@@ -19,17 +21,23 @@ export const UserAccountAvatar = ({ size, userId, hue, emoji, status }: UserAcco
|
|
|
19
21
|
const fallbackValue = hexToFallback(userId ?? '0');
|
|
20
22
|
|
|
21
23
|
return (
|
|
22
|
-
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
<>
|
|
25
|
+
<L0ItemActiveTabIndicator classNames='inset-block-6' />
|
|
26
|
+
<div
|
|
27
|
+
className='grid place-items-center dx-focus-ring-group-indicator rounded-full'
|
|
28
|
+
data-joyride='welcome/account'
|
|
29
|
+
>
|
|
30
|
+
<Avatar.Root>
|
|
31
|
+
<Avatar.Content
|
|
32
|
+
variant='circle'
|
|
33
|
+
size={size ?? 12}
|
|
34
|
+
status={status ?? 'active'}
|
|
35
|
+
hue={hue || fallbackValue.hue}
|
|
36
|
+
fallback={emoji || fallbackValue.emoji}
|
|
37
|
+
data-testid='treeView.userAccount'
|
|
38
|
+
/>
|
|
39
|
+
</Avatar.Root>
|
|
40
|
+
</div>
|
|
41
|
+
</>
|
|
34
42
|
);
|
|
35
43
|
};
|
package/src/components/index.ts
CHANGED
package/src/components/types.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type { FC } from 'react';
|
|
6
6
|
|
|
7
|
-
import type
|
|
7
|
+
import { type Node } from '@dxos/app-graph';
|
|
8
8
|
import type { Density } from '@dxos/react-ui';
|
|
9
9
|
import type { TreeProps } from '@dxos/react-ui-list';
|
|
10
10
|
import type { MaybePromise } from '@dxos/util';
|
|
@@ -15,23 +15,23 @@ import type { L1PanelProps } from './Sidebar';
|
|
|
15
15
|
|
|
16
16
|
export type NavTreeContextValue = Pick<
|
|
17
17
|
TreeProps<NavTreeItemGraphNode, { disposition?: string; sort?: boolean }>,
|
|
18
|
-
'useItems' | 'getProps' | '
|
|
18
|
+
'useItems' | 'getProps' | 'useIsCurrent' | 'useIsOpen' | 'blockInstruction' | 'canDrop' | 'onOpenChange' | 'onSelect'
|
|
19
19
|
> &
|
|
20
20
|
Pick<L1PanelProps, 'onBack'> & {
|
|
21
21
|
tab: string;
|
|
22
22
|
topbar?: boolean;
|
|
23
23
|
popoverAnchorId?: string;
|
|
24
|
-
renderItemEnd?: FC<{ node: Node; open: boolean }>;
|
|
25
|
-
useActions: (node: Node) => FlattenedActions;
|
|
26
|
-
loadDescendents?: (node: Node) => MaybePromise<void>;
|
|
27
|
-
|
|
24
|
+
renderItemEnd?: FC<{ node: Node.Node; open: boolean }>;
|
|
25
|
+
useActions: (node: Node.Node) => FlattenedActions;
|
|
26
|
+
loadDescendents?: (node: Node.Node) => MaybePromise<void>;
|
|
27
|
+
useIsAlternateTree?: (path: string[], item: NavTreeItemGraphNode) => boolean;
|
|
28
28
|
setAlternateTree?: (path: string[], open: boolean) => void;
|
|
29
29
|
onTabChange?: (node: NavTreeItemGraphNode) => void;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
export type NavTreeItemColumnsProps = {
|
|
33
33
|
path: string[];
|
|
34
|
-
item: Node;
|
|
34
|
+
item: Node.Node;
|
|
35
35
|
open: boolean;
|
|
36
36
|
density?: Density;
|
|
37
37
|
};
|
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
7
|
-
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
5
|
+
import { type Meta } from '@storybook/react-vite';
|
|
8
6
|
import React, { type JSX, type PropsWithChildren, useEffect, useState } from 'react';
|
|
9
7
|
|
|
10
8
|
import { faker } from '@dxos/random';
|
|
11
9
|
import { Icon, IconButton } from '@dxos/react-ui';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
10
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
11
|
+
import { mx } from '@dxos/ui-theme';
|
|
14
12
|
|
|
15
13
|
import { type ItemMap, Tree, type TreeNodeData, type TreeProps, visitNodes, visitor } from './Tree';
|
|
16
14
|
|
|
@@ -32,7 +30,7 @@ const Container = ({ children, sidebar }: PropsWithChildren<{ sidebar: JSX.Eleme
|
|
|
32
30
|
<div className='flex'>
|
|
33
31
|
{/* TODO(burdon): Custom thin scrollbar. */}
|
|
34
32
|
{/* TODO(burdon): Horizontal scrolling within navtree? */}
|
|
35
|
-
<div className='flex flex-col overflow-y-auto
|
|
33
|
+
<div className='flex flex-col overflow-y-auto is-[300px] bg-neutral-100 dark:bg-neutral-950'>{sidebar}</div>
|
|
36
34
|
<div className='flex flex-col grow overflow-hidden'>{children}</div>
|
|
37
35
|
</div>
|
|
38
36
|
);
|
|
@@ -105,27 +103,6 @@ const data: TreeNodeData[] = [
|
|
|
105
103
|
},
|
|
106
104
|
];
|
|
107
105
|
|
|
108
|
-
export const Default = () => {
|
|
109
|
-
return <Container sidebar={<Sidebar mutate />} />;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
export const Visitor = () => {
|
|
113
|
-
const [items] = useState(data);
|
|
114
|
-
const root = items[0];
|
|
115
|
-
const [openItems] = useState<ItemMap>({ [root.children![0].id]: true });
|
|
116
|
-
return (
|
|
117
|
-
<div className='flex flex-col'>
|
|
118
|
-
{Array.from(visitor(root, openItems)).map(({ node: { id, title }, depth }) => (
|
|
119
|
-
<div key={id} className='grid grid-cols-[40px_400px_400px] font-mono'>
|
|
120
|
-
<div>{depth}</div>
|
|
121
|
-
<div>{id}</div>
|
|
122
|
-
<div>{title}</div>
|
|
123
|
-
</div>
|
|
124
|
-
))}
|
|
125
|
-
</div>
|
|
126
|
-
);
|
|
127
|
-
};
|
|
128
|
-
|
|
129
106
|
const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
130
107
|
const [items, setItems] = useState(data);
|
|
131
108
|
const root = items[0];
|
|
@@ -212,7 +189,7 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
|
212
189
|
/>
|
|
213
190
|
</div>
|
|
214
191
|
|
|
215
|
-
<div className='flex items-center my-2
|
|
192
|
+
<div className='flex items-center my-2 pli-2 gap-2'>
|
|
216
193
|
<IconButton icon='ph--plus-circle--regular' iconOnly label='Create space' onClick={handleCreateSpace} />
|
|
217
194
|
<span className='grow text-sm' onClick={handleCreateSpace}>
|
|
218
195
|
New space
|
|
@@ -226,9 +203,28 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
|
226
203
|
const meta = {
|
|
227
204
|
title: 'plugins/plugin-navtree/experimental/Tree',
|
|
228
205
|
component: Tree,
|
|
229
|
-
decorators: [withTheme, withLayout({
|
|
206
|
+
decorators: [withTheme, withLayout({ layout: 'column' })],
|
|
230
207
|
} satisfies Meta<typeof Tree>;
|
|
231
208
|
|
|
232
209
|
export default meta;
|
|
233
210
|
|
|
234
|
-
|
|
211
|
+
export const Default = () => {
|
|
212
|
+
return <Container sidebar={<Sidebar mutate />} />;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
export const Visitor = () => {
|
|
216
|
+
const [items] = useState(data);
|
|
217
|
+
const root = items[0];
|
|
218
|
+
const [openItems] = useState<ItemMap>({ [root.children![0].id]: true });
|
|
219
|
+
return (
|
|
220
|
+
<div className='flex flex-col'>
|
|
221
|
+
{Array.from(visitor(root, openItems)).map(({ node: { id, title }, depth }) => (
|
|
222
|
+
<div key={id} className='grid grid-cols-[40px_400px_400px] font-mono'>
|
|
223
|
+
<div>{depth}</div>
|
|
224
|
+
<div>{id}</div>
|
|
225
|
+
<div>{title}</div>
|
|
226
|
+
</div>
|
|
227
|
+
))}
|
|
228
|
+
</div>
|
|
229
|
+
);
|
|
230
|
+
};
|
|
@@ -5,29 +5,21 @@
|
|
|
5
5
|
import React, { type HTMLAttributes, type PropsWithChildren } from 'react';
|
|
6
6
|
|
|
7
7
|
import { type ClassNameValue, Icon, type Size } from '@dxos/react-ui';
|
|
8
|
-
import { mx } from '@dxos/
|
|
9
|
-
|
|
10
|
-
// TODO(burdon): Rounded border if first/last.
|
|
11
|
-
const styles = {
|
|
12
|
-
hover: 'hover:bg-slate-400 dark:hover:bg-slate-800',
|
|
13
|
-
selected: '!bg-slate-300 dark:!bg-slate-700',
|
|
14
|
-
};
|
|
8
|
+
import { mx } from '@dxos/ui-theme';
|
|
15
9
|
|
|
16
10
|
export const IconButton = ({
|
|
17
11
|
iconName,
|
|
18
12
|
classNames,
|
|
19
13
|
size = 4,
|
|
20
14
|
onClick,
|
|
21
|
-
weight = 'regular',
|
|
22
15
|
}: {
|
|
23
16
|
iconName: string;
|
|
24
17
|
classNames?: ClassNameValue;
|
|
25
18
|
size?: Size;
|
|
26
|
-
weight?: 'regular' | 'duotone' | 'fill';
|
|
27
19
|
} & Pick<HTMLAttributes<HTMLDivElement>, 'onClick'>) => {
|
|
28
20
|
// TODO(burdon): Density aware.
|
|
29
21
|
return (
|
|
30
|
-
<div className={mx('flex
|
|
22
|
+
<div className={mx('flex is-6 bs-6 items-center justify-center select-none', classNames)} onClick={onClick}>
|
|
31
23
|
<Icon icon={iconName} classNames='cursor-pointer' size={size} />
|
|
32
24
|
</div>
|
|
33
25
|
);
|
|
@@ -124,7 +116,6 @@ const StateIcon = ({ node, open, selected, active }: TreeNodeProps) => {
|
|
|
124
116
|
<IconButton
|
|
125
117
|
iconName={isActive ? 'ph--user-circle--regular' : 'ph--circle--regular'}
|
|
126
118
|
size={4}
|
|
127
|
-
weight={selected?.[id] ? 'fill' : 'duotone'}
|
|
128
119
|
classNames={mx(
|
|
129
120
|
'text-slate-500',
|
|
130
121
|
!isChildActive && 'opacity-0 transition duration-500',
|
|
@@ -158,15 +149,7 @@ const OpenIcon = ({
|
|
|
158
149
|
const ItemIcon = ({
|
|
159
150
|
node: { children, iconName = children?.length ? 'ph--folder--regular' : 'ph--file--regular', color },
|
|
160
151
|
}: Pick<TreeNodeProps, 'node'>) => {
|
|
161
|
-
return (
|
|
162
|
-
(iconName && (
|
|
163
|
-
<IconButton
|
|
164
|
-
iconName={iconName}
|
|
165
|
-
classNames={color ?? 'text-neutral-700 dark:text-neutral-300'}
|
|
166
|
-
weight={color ? 'duotone' : 'regular'}
|
|
167
|
-
/>
|
|
168
|
-
)) || <div />
|
|
169
|
-
);
|
|
152
|
+
return (iconName && <IconButton iconName={iconName} classNames={color ?? 'text-subdued'} />) || <div />;
|
|
170
153
|
};
|
|
171
154
|
|
|
172
155
|
const MenuItem = ({ node: { id }, onMenuAction }: Pick<TreeNodeProps, 'node' | 'onMenuAction'>) => {
|
|
@@ -210,9 +193,9 @@ export const TreeNodeRow = (props: TreeNodeProps & { className?: string }) => {
|
|
|
210
193
|
<Grid
|
|
211
194
|
style={{ paddingLeft: `${(depth - 1) * 24}px` }}
|
|
212
195
|
className={mx(
|
|
213
|
-
'group
|
|
214
|
-
|
|
215
|
-
selected?.[id] &&
|
|
196
|
+
'group is-full items-center cursor-pointer bg-hoverSurface',
|
|
197
|
+
// TODO(burdon): Use data-active.
|
|
198
|
+
selected?.[id] && 'bg-activeSurface',
|
|
216
199
|
className,
|
|
217
200
|
)}
|
|
218
201
|
onClick={() => selected && onChangeSelected?.(id, !selected[id])}
|
|
@@ -255,7 +238,7 @@ export const TreeChildNodes = ({
|
|
|
255
238
|
} = props;
|
|
256
239
|
return (
|
|
257
240
|
<div className={mx('flex flex-col mt-0.5 gap-0.5', className)}>
|
|
258
|
-
{children?.map((child
|
|
241
|
+
{children?.map((child) => (
|
|
259
242
|
<TreeNode key={child.id} {...props} depth={depth + 1} node={child} ancestors={[...ancestors, props.node]} />
|
|
260
243
|
))}
|
|
261
244
|
</div>
|
package/src/hooks.ts
CHANGED
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { type Atom, useAtomValue } from '@effect-atom/atom-react';
|
|
6
|
+
import { useEffect, useMemo } from 'react';
|
|
6
7
|
|
|
8
|
+
import { useCapability } from '@dxos/app-framework/react';
|
|
7
9
|
import { type Node } from '@dxos/app-graph';
|
|
8
10
|
|
|
9
11
|
import { useNavTreeContext } from './components';
|
|
12
|
+
import { NavTreeCapabilities } from './types';
|
|
10
13
|
|
|
11
|
-
export const useLoadDescendents = (root?: Node) => {
|
|
14
|
+
export const useLoadDescendents = (root?: Node.Node) => {
|
|
12
15
|
const { loadDescendents } = useNavTreeContext();
|
|
13
16
|
useEffect(() => {
|
|
14
17
|
const frame = requestAnimationFrame(() => {
|
|
@@ -19,3 +22,52 @@ export const useLoadDescendents = (root?: Node) => {
|
|
|
19
22
|
return () => cancelAnimationFrame(frame);
|
|
20
23
|
}, [root]);
|
|
21
24
|
};
|
|
25
|
+
|
|
26
|
+
export type UseNavTreeStateResult = {
|
|
27
|
+
getItem: (path: string[]) => NavTreeCapabilities.NavTreeItemState;
|
|
28
|
+
getItemAtom: (path: string[]) => Atom.Atom<NavTreeCapabilities.NavTreeItemState>;
|
|
29
|
+
setItem: (path: string[], key: 'open' | 'current' | 'alternateTree', next: boolean) => void;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Hook that provides access to navtree state functions.
|
|
34
|
+
* For reactive subscriptions, use `useNavTreeItemState` or `useAtomValue(getItemAtom(path))`.
|
|
35
|
+
*/
|
|
36
|
+
export const useNavTreeState = (): UseNavTreeStateResult => {
|
|
37
|
+
return useCapability(NavTreeCapabilities.State);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Hook that subscribes to a single navtree item's state.
|
|
42
|
+
* Use this for fine-grained reactivity.
|
|
43
|
+
*/
|
|
44
|
+
export const useNavTreeItemState = (path: string[]): NavTreeCapabilities.NavTreeItemState => {
|
|
45
|
+
const { getItemAtom } = useCapability(NavTreeCapabilities.State);
|
|
46
|
+
// Memoize the path to avoid creating new atoms on every render.
|
|
47
|
+
const pathKey = useMemo(() => path.join('~'), [path]);
|
|
48
|
+
const atom = useMemo(() => getItemAtom(path), [getItemAtom, pathKey]);
|
|
49
|
+
return useAtomValue(atom);
|
|
50
|
+
};
|
|
51
|
+
|
|
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
|
+
/**
|
|
69
|
+
* Hook that subscribes to the alternateTree state for a tree item.
|
|
70
|
+
*/
|
|
71
|
+
export const useIsAlternateTree = (path: string[], _item?: any): boolean => {
|
|
72
|
+
return useNavTreeItemState(path).alternateTree ?? false;
|
|
73
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
export { NavTreeCapabilities } from './
|
|
6
|
-
export { NavTreeEvents } from './events';
|
|
5
|
+
export { NavTreeCapabilities, NavTreeEvents } from './types';
|
|
7
6
|
export * from './NavTreePlugin';
|
|
8
7
|
export * from './meta';
|
|
9
8
|
export * from './util';
|
package/src/meta.ts
CHANGED
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type
|
|
5
|
+
import { type Plugin } from '@dxos/app-framework';
|
|
6
|
+
import { trim } from '@dxos/util';
|
|
6
7
|
|
|
7
|
-
export const
|
|
8
|
+
export const meta: Plugin.Meta = {
|
|
9
|
+
id: 'dxos.org/plugin/navtree',
|
|
10
|
+
name: 'Navtree',
|
|
11
|
+
description: trim`
|
|
12
|
+
Hierarchical navigation tree for browsing spaces, folders, and objects.
|
|
13
|
+
Provides sidebar navigation with collapsible sections and quick access to workspace content.
|
|
14
|
+
`,
|
|
15
|
+
};
|
|
8
16
|
|
|
9
17
|
// TODO(wittjosiah): Factor out.
|
|
10
18
|
export const KEY_BINDING = 'KeyBinding';
|
|
11
19
|
// TODO(wittjosiah): Factor out.
|
|
12
|
-
export const COMMANDS_DIALOG = `${
|
|
13
|
-
|
|
14
|
-
export const meta: PluginMeta = {
|
|
15
|
-
id: NAVTREE_PLUGIN,
|
|
16
|
-
name: 'Navtree',
|
|
17
|
-
};
|
|
20
|
+
export const COMMANDS_DIALOG = `${meta.id}/CommandsDialog`;
|