@dxos/plugin-navtree 0.8.4-main.fd6878d → 0.8.4-staging.60fe92afc8
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/LICENSE +102 -5
- package/PLUGIN.mdl +376 -0
- package/README.md +1 -1
- package/dist/lib/neutral/CommandsDialogContent-UBGIQ5FI.mjs +112 -0
- package/dist/lib/neutral/CommandsDialogContent-UBGIQ5FI.mjs.map +7 -0
- package/dist/lib/neutral/CommandsTrigger-BH5WLMHK.mjs +27 -0
- package/dist/lib/neutral/CommandsTrigger-BH5WLMHK.mjs.map +7 -0
- package/dist/lib/neutral/NavTreeContainer-6AR4BFDD.mjs +12 -0
- package/dist/lib/neutral/NavTreeContainer-6AR4BFDD.mjs.map +7 -0
- package/dist/lib/neutral/NavTreeDocumentTitle-H3E6ZLYE.mjs +22 -0
- package/dist/lib/neutral/NavTreeDocumentTitle-H3E6ZLYE.mjs.map +7 -0
- package/dist/lib/neutral/NavTreePlugin.mjs +68 -0
- package/dist/lib/neutral/NavTreePlugin.mjs.map +7 -0
- package/dist/lib/neutral/NavTreePlugin.node.mjs +18 -0
- package/dist/lib/neutral/NavTreePlugin.node.mjs.map +7 -0
- package/dist/lib/neutral/NavTreePlugin.workerd.mjs +16 -0
- package/dist/lib/neutral/NavTreePlugin.workerd.mjs.map +7 -0
- package/dist/lib/neutral/app-graph-builder-Z347LKSF.mjs +42 -0
- package/dist/lib/neutral/app-graph-builder-Z347LKSF.mjs.map +7 -0
- package/dist/lib/neutral/capabilities/index.mjs +17 -0
- package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
- package/dist/lib/neutral/chunk-5OPDKH6B.mjs +273 -0
- package/dist/lib/neutral/chunk-5OPDKH6B.mjs.map +7 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/neutral/chunk-JOSNCOV4.mjs +79 -0
- package/dist/lib/neutral/chunk-JOSNCOV4.mjs.map +7 -0
- package/dist/lib/neutral/chunk-MDGKDQMZ.mjs +37 -0
- package/dist/lib/neutral/chunk-MDGKDQMZ.mjs.map +7 -0
- package/dist/lib/neutral/chunk-T7QS2EFI.mjs +8 -0
- package/dist/lib/neutral/chunk-T7QS2EFI.mjs.map +7 -0
- package/dist/lib/neutral/chunk-ZSWYB7JW.mjs +27 -0
- package/dist/lib/neutral/chunk-ZSWYB7JW.mjs.map +7 -0
- package/dist/lib/neutral/components/index.mjs +805 -0
- package/dist/lib/neutral/components/index.mjs.map +7 -0
- package/dist/lib/neutral/containers/index.mjs +20 -0
- package/dist/lib/neutral/containers/index.mjs.map +7 -0
- package/dist/lib/neutral/expose-26FV4W5D.mjs +38 -0
- package/dist/lib/neutral/expose-26FV4W5D.mjs.map +7 -0
- package/dist/lib/neutral/hooks/index.mjs +178 -0
- package/dist/lib/neutral/hooks/index.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +34 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/{browser/keyboard-HMTMA33N.mjs → neutral/keyboard-EJ65BVTC.mjs} +25 -20
- package/dist/lib/neutral/keyboard-EJ65BVTC.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/meta.mjs +12 -0
- package/dist/lib/neutral/meta.mjs.map +7 -0
- package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs +13 -0
- package/dist/lib/neutral/operation-handler-ORDOJUL2.mjs.map +7 -0
- package/dist/lib/neutral/operations/index.mjs +8 -0
- package/dist/lib/neutral/operations/index.mjs.map +7 -0
- package/dist/lib/neutral/plugin.mjs +16 -0
- package/dist/lib/neutral/plugin.mjs.map +7 -0
- package/dist/lib/neutral/react-surface-Q5XMAG7Z.mjs +50 -0
- package/dist/lib/neutral/react-surface-Q5XMAG7Z.mjs.map +7 -0
- package/dist/lib/neutral/state-VVJEWUHM.mjs +126 -0
- package/dist/lib/neutral/state-VVJEWUHM.mjs.map +7 -0
- package/dist/lib/neutral/testing/index.mjs +439 -0
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +23 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/lib/neutral/types/index.mjs +10 -0
- package/dist/lib/neutral/types/index.mjs.map +7 -0
- package/dist/types/src/NavTreePlugin.d.ts +3 -1
- package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
- package/dist/types/src/NavTreePlugin.node.d.ts +4 -0
- package/dist/types/src/NavTreePlugin.node.d.ts.map +1 -0
- package/dist/types/src/NavTreePlugin.test.d.ts +2 -0
- package/dist/types/src/NavTreePlugin.test.d.ts.map +1 -0
- package/dist/types/src/NavTreePlugin.workerd.d.ts +4 -0
- package/dist/types/src/NavTreePlugin.workerd.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +4 -2
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +7 -105
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/keyboard.d.ts +3 -2
- package/dist/types/src/capabilities/keyboard.d.ts.map +1 -1
- 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 +3 -2
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/state.d.ts +4 -18
- package/dist/types/src/capabilities/state.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.d.ts +11 -4
- package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts +25 -5
- 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 +13 -9
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +33 -4
- 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 +10 -5
- package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Panel.d.ts +4 -7
- package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Tabs.d.ts +5 -4
- 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 +8 -5
- 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 +8 -17
- package/dist/types/src/components/types.d.ts.map +1 -1
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts +6 -0
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -0
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +28 -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/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.d.ts +1 -1
- 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.d.ts +3 -4
- package/dist/types/src/experimental/Tree.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.stories.d.ts +23 -6
- 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 +11 -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/useNavTreeState.d.ts +18 -0
- package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +1 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +4 -4
- 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/plugin.d.ts +4 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- 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/translations.d.ts +8 -11
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/NavTreeCapabilities.d.ts +13 -0
- package/dist/types/src/types/NavTreeCapabilities.d.ts.map +1 -0
- package/dist/types/src/types/NavTreeEvents.d.ts +3 -0
- package/dist/types/src/types/NavTreeEvents.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} +14 -9
- package/dist/types/src/types/schema.d.ts.map +1 -0
- package/dist/types/src/util.d.ts +12 -8
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +124 -62
- package/src/NavTreePlugin.node.ts +17 -0
- package/src/NavTreePlugin.test.ts +27 -0
- package/src/NavTreePlugin.tsx +56 -87
- package/src/NavTreePlugin.workerd.ts +16 -0
- package/src/capabilities/app-graph-builder.ts +34 -44
- package/src/capabilities/index.ts +10 -8
- package/src/capabilities/keyboard.ts +58 -51
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/react-surface.tsx +46 -38
- package/src/capabilities/state.ts +132 -84
- package/src/components/NavTree/NavTree.stories.tsx +112 -106
- package/src/components/NavTree/NavTree.tsx +69 -33
- 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 +25 -19
- package/src/components/NavTreeItem/NavTreeItemAction.tsx +78 -54
- package/src/components/NavTreeItem/NavTreeItemColumns.tsx +40 -51
- package/src/components/Sidebar/L0Menu.tsx +182 -210
- package/src/components/Sidebar/L1Panel.tsx +185 -110
- package/src/components/Sidebar/L1Tabs.tsx +18 -34
- package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +9 -9
- package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +20 -12
- package/src/components/index.ts +1 -3
- package/src/components/types.ts +13 -19
- package/src/containers/CommandsDialogContent/CommandsDialogContent.stories.tsx +46 -0
- package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +127 -0
- 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/containers/NavTreeContainer/NavTreeContainer.tsx +271 -0
- package/src/containers/NavTreeContainer/index.ts +6 -0
- package/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.tsx +5 -4
- package/src/containers/NavTreeDocumentTitle/index.ts +5 -0
- package/src/containers/index.ts +12 -0
- package/src/experimental/Tree.stories.tsx +105 -110
- package/src/experimental/Tree.tsx +7 -24
- package/src/hooks/index.ts +8 -0
- package/src/hooks/useActions.ts +44 -0
- package/src/hooks/useLoadDescendents.ts +23 -0
- package/src/hooks/useNavTreeModel.ts +120 -0
- package/src/hooks/useNavTreeState.ts +35 -0
- package/src/index.ts +1 -3
- package/src/meta.ts +26 -8
- package/src/operations/expose.ts +42 -0
- package/src/operations/index.ts +7 -0
- package/src/plugin.ts +11 -0
- package/src/testing/app-graph-builder.ts +169 -238
- package/src/testing/generator.ts +36 -39
- package/src/testing/update-state.ts +6 -6
- package/src/translations.ts +8 -10
- package/src/types/NavTreeCapabilities.ts +21 -0
- package/src/types/NavTreeEvents.ts +12 -0
- package/src/types/index.ts +8 -0
- package/src/{types.ts → types/schema.ts} +13 -8
- package/src/util.ts +28 -17
- package/src/vite-env.d.ts +10 -0
- 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-SNGLGBFJ.mjs +0 -1469
- package/dist/lib/browser/chunk-SNGLGBFJ.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/index.mjs +0 -148
- package/dist/lib/browser/index.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/meta.json +0 -1
- package/dist/lib/browser/react-surface-WKWXMM2N.mjs +0 -51
- package/dist/lib/browser/react-surface-WKWXMM2N.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/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/components/CommandsDialog/CommandsDialog.d.ts +0 -5
- 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 -9
- 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/events.d.ts +0 -4
- package/dist/types/src/events.d.ts.map +0 -1
- package/dist/types/src/hooks.d.ts +0 -3
- package/dist/types/src/hooks.d.ts.map +0 -1
- package/dist/types/src/types.d.ts.map +0 -1
- package/src/capabilities/capabilities.ts +0 -20
- package/src/capabilities/intent-resolver.ts +0 -37
- package/src/components/CommandsDialog/CommandsDialog.tsx +0 -118
- package/src/components/CommandsDialog/CommandsTrigger.tsx +0 -35
- package/src/components/CommandsDialog/index.ts +0 -6
- package/src/components/NavTreeContainer.tsx +0 -337
- package/src/events.ts +0 -11
- package/src/hooks.ts +0 -21
- /package/dist/types/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.d.ts +0 -0
|
@@ -2,41 +2,17 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta } from '@storybook/react-vite';
|
|
8
|
-
import React, {
|
|
6
|
+
import React, { useEffect, useState } from 'react';
|
|
9
7
|
|
|
10
|
-
import {
|
|
11
|
-
import { Icon, IconButton } from '@dxos/react-ui';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
8
|
+
import { random } from '@dxos/random';
|
|
9
|
+
import { Icon, IconButton, Panel, ScrollArea } from '@dxos/react-ui';
|
|
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
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Space:
|
|
21
|
-
* - Space panels are separated from each other to give them more weight than folders.
|
|
22
|
-
* - Private space isn't different (just first, different icon).
|
|
23
|
-
* - Edit titles in place.
|
|
24
|
-
*
|
|
25
|
-
* Navigation:
|
|
26
|
-
* - up/down across spaces/folder peers
|
|
27
|
-
* - right to enter space/folder children
|
|
28
|
-
* - left to parent
|
|
29
|
-
*/
|
|
30
|
-
const Container = ({ children, sidebar }: PropsWithChildren<{ sidebar: JSX.Element }>) => {
|
|
31
|
-
return (
|
|
32
|
-
<div className='flex'>
|
|
33
|
-
{/* TODO(burdon): Custom thin scrollbar. */}
|
|
34
|
-
{/* TODO(burdon): Horizontal scrolling within navtree? */}
|
|
35
|
-
<div className='flex flex-col overflow-y-auto w-[300px] bg-neutral-100 dark:bg-neutral-950'>{sidebar}</div>
|
|
36
|
-
<div className='flex flex-col grow overflow-hidden'>{children}</div>
|
|
37
|
-
</div>
|
|
38
|
-
);
|
|
39
|
-
};
|
|
15
|
+
random.seed(1234);
|
|
40
16
|
|
|
41
17
|
// TODO(burdon): Generate.
|
|
42
18
|
const data: TreeNodeData[] = [
|
|
@@ -45,34 +21,34 @@ const data: TreeNodeData[] = [
|
|
|
45
21
|
title: 'plugins/Root',
|
|
46
22
|
children: [
|
|
47
23
|
{
|
|
48
|
-
id:
|
|
24
|
+
id: random.string.uuid(),
|
|
49
25
|
title: 'plugins/Personal Space',
|
|
50
26
|
color: 'text-green-400',
|
|
51
27
|
iconName: 'ph--house--regular',
|
|
52
28
|
children: [
|
|
53
29
|
{
|
|
54
|
-
id:
|
|
55
|
-
title:
|
|
30
|
+
id: random.string.uuid(),
|
|
31
|
+
title: random.commerce.productName(),
|
|
56
32
|
},
|
|
57
33
|
{
|
|
58
|
-
id:
|
|
59
|
-
title:
|
|
34
|
+
id: random.string.uuid(),
|
|
35
|
+
title: random.commerce.productName(),
|
|
60
36
|
},
|
|
61
37
|
{
|
|
62
|
-
id:
|
|
63
|
-
title:
|
|
38
|
+
id: random.string.uuid(),
|
|
39
|
+
title: random.commerce.productName(),
|
|
64
40
|
children: [
|
|
65
41
|
{
|
|
66
|
-
id:
|
|
67
|
-
title:
|
|
42
|
+
id: random.string.uuid(),
|
|
43
|
+
title: random.commerce.productName(),
|
|
68
44
|
children: [
|
|
69
45
|
{
|
|
70
|
-
id:
|
|
71
|
-
title:
|
|
46
|
+
id: random.string.uuid(),
|
|
47
|
+
title: random.commerce.productName(),
|
|
72
48
|
},
|
|
73
49
|
{
|
|
74
|
-
id:
|
|
75
|
-
title:
|
|
50
|
+
id: random.string.uuid(),
|
|
51
|
+
title: random.commerce.productName(),
|
|
76
52
|
},
|
|
77
53
|
],
|
|
78
54
|
},
|
|
@@ -81,51 +57,30 @@ const data: TreeNodeData[] = [
|
|
|
81
57
|
],
|
|
82
58
|
},
|
|
83
59
|
{
|
|
84
|
-
id:
|
|
85
|
-
title:
|
|
60
|
+
id: random.string.uuid(),
|
|
61
|
+
title: random.commerce.productName(),
|
|
86
62
|
iconName: 'ph--planet--regular',
|
|
87
63
|
children: [
|
|
88
64
|
{
|
|
89
|
-
id:
|
|
90
|
-
title:
|
|
65
|
+
id: random.string.uuid(),
|
|
66
|
+
title: random.commerce.productName(),
|
|
91
67
|
},
|
|
92
68
|
],
|
|
93
69
|
},
|
|
94
70
|
{
|
|
95
|
-
id:
|
|
96
|
-
title:
|
|
71
|
+
id: random.string.uuid(),
|
|
72
|
+
title: random.commerce.productName(),
|
|
97
73
|
iconName: 'ph--sailboat--regular',
|
|
98
74
|
},
|
|
99
75
|
{
|
|
100
|
-
id:
|
|
101
|
-
title:
|
|
76
|
+
id: random.string.uuid(),
|
|
77
|
+
title: random.commerce.productName(),
|
|
102
78
|
iconName: 'ph--planet--regular',
|
|
103
79
|
},
|
|
104
80
|
],
|
|
105
81
|
},
|
|
106
82
|
];
|
|
107
83
|
|
|
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
84
|
const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
130
85
|
const [items, setItems] = useState(data);
|
|
131
86
|
const root = items[0];
|
|
@@ -157,7 +112,7 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
|
157
112
|
}
|
|
158
113
|
}, []);
|
|
159
114
|
|
|
160
|
-
const handleCreateItem: TreeProps['onMenuAction'] = (id
|
|
115
|
+
const handleCreateItem: TreeProps['onMenuAction'] = (id) => {
|
|
161
116
|
setItems((items) => {
|
|
162
117
|
let parent: TreeNodeData | undefined;
|
|
163
118
|
visitNodes(root, (node) => {
|
|
@@ -167,8 +122,8 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
|
167
122
|
});
|
|
168
123
|
|
|
169
124
|
(parent!.children ??= []).push({
|
|
170
|
-
id:
|
|
171
|
-
title:
|
|
125
|
+
id: random.string.uuid(),
|
|
126
|
+
title: random.commerce.productName(),
|
|
172
127
|
});
|
|
173
128
|
|
|
174
129
|
setOpenItems((open) => ({ ...open, [parent!.id]: true }));
|
|
@@ -180,9 +135,9 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
|
180
135
|
const handleCreateSpace = () => {
|
|
181
136
|
setItems((items) => {
|
|
182
137
|
items[0].children?.push({
|
|
183
|
-
id:
|
|
138
|
+
id: random.string.uuid(),
|
|
184
139
|
iconName: 'ph--planet--regular',
|
|
185
|
-
title:
|
|
140
|
+
title: random.commerce.productName(),
|
|
186
141
|
});
|
|
187
142
|
|
|
188
143
|
return { ...items };
|
|
@@ -190,43 +145,83 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
|
190
145
|
};
|
|
191
146
|
|
|
192
147
|
return (
|
|
193
|
-
<
|
|
194
|
-
<
|
|
195
|
-
<
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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>
|
|
223
183
|
);
|
|
224
184
|
};
|
|
225
185
|
|
|
226
|
-
const meta
|
|
186
|
+
const meta = {
|
|
227
187
|
title: 'plugins/plugin-navtree/experimental/Tree',
|
|
228
188
|
component: Tree,
|
|
229
|
-
decorators: [withTheme, withLayout({
|
|
230
|
-
|
|
189
|
+
decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'w-[20rem]' })],
|
|
190
|
+
parameters: {
|
|
191
|
+
layout: 'fullscreen',
|
|
192
|
+
},
|
|
193
|
+
} satisfies Meta<typeof Tree>;
|
|
231
194
|
|
|
232
195
|
export default meta;
|
|
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
|
+
*/
|
|
208
|
+
export const Default = () => {
|
|
209
|
+
return <Sidebar mutate />;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export const Visitor = () => {
|
|
213
|
+
const [items] = useState(data);
|
|
214
|
+
const root = items[0];
|
|
215
|
+
const [openItems] = useState<ItemMap>({ [root.children![0].id]: true });
|
|
216
|
+
return (
|
|
217
|
+
<div className='flex flex-col'>
|
|
218
|
+
{Array.from(visitor(root, openItems)).map(({ node: { id, title }, depth }) => (
|
|
219
|
+
<div key={id} className='grid grid-cols-[40px_400px_400px] font-mono'>
|
|
220
|
+
<div>{depth}</div>
|
|
221
|
+
<div>{id}</div>
|
|
222
|
+
<div>{title}</div>
|
|
223
|
+
</div>
|
|
224
|
+
))}
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
};
|
|
@@ -5,25 +5,17 @@
|
|
|
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 (
|
|
@@ -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 w-full items-center cursor-pointer',
|
|
214
|
-
|
|
215
|
-
selected?.[id] &&
|
|
196
|
+
'group w-full items-center cursor-pointer bg-hover-surface',
|
|
197
|
+
// TODO(burdon): Use data-active.
|
|
198
|
+
selected?.[id] && 'bg-current-surface',
|
|
216
199
|
className,
|
|
217
200
|
)}
|
|
218
201
|
onClick={() => selected && onChangeSelected?.(id, !selected[id])}
|
|
@@ -237,7 +220,7 @@ export const TreeNode = (props: TreeNodeProps & { ancestors?: TreeNodeData[] })
|
|
|
237
220
|
const { root, header } = getSlots?.(props.node, open?.[id] ?? false, depth, ancestors) ?? {};
|
|
238
221
|
|
|
239
222
|
return (
|
|
240
|
-
<div
|
|
223
|
+
<div className={mx('flex flex-col', root)}>
|
|
241
224
|
<TreeNodeRow {...props} className={header} />
|
|
242
225
|
{(children?.length ?? 0) > 0 && open?.[id] && <TreeChildNodes {...props} ancestors={ancestors} />}
|
|
243
226
|
</div>
|
|
@@ -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>
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
/**
|
|
13
|
+
* Flattens grouped actions and filters to the list-item dispositions rendered as
|
|
14
|
+
* navtree item actions. Shared by row (`NavTreeItemColumns`) and header (`L1Panel`)
|
|
15
|
+
* so both apply the same normalization rule.
|
|
16
|
+
*/
|
|
17
|
+
export const getListActions = ({ actions, groupedActions }: FlattenedActions): Node.Action[] =>
|
|
18
|
+
actions
|
|
19
|
+
.flatMap((action) => (Node.isAction(action) ? [action] : (groupedActions[action.id] ?? [])))
|
|
20
|
+
.filter((action) => ['list-item', 'list-item-primary'].includes(action.properties?.disposition));
|
|
21
|
+
|
|
22
|
+
/** Returns flattened actions and grouped sub-actions for a given graph node. */
|
|
23
|
+
export const useActions = (node: Node.Node): FlattenedActions => {
|
|
24
|
+
const { graph } = useAppGraph();
|
|
25
|
+
const actions = useGraphActions(graph, node.id);
|
|
26
|
+
|
|
27
|
+
return useMemo(() => {
|
|
28
|
+
return actions.reduce(
|
|
29
|
+
(acc: FlattenedActions, arg) => {
|
|
30
|
+
if (arg.properties.disposition === 'item') {
|
|
31
|
+
return acc;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
acc.actions.push(arg);
|
|
35
|
+
if (!Node.isAction(arg)) {
|
|
36
|
+
const actionGroup = Graph.getActions(graph, arg.id);
|
|
37
|
+
acc.groupedActions[arg.id] = actionGroup;
|
|
38
|
+
}
|
|
39
|
+
return acc;
|
|
40
|
+
},
|
|
41
|
+
{ actions: [], groupedActions: {} },
|
|
42
|
+
);
|
|
43
|
+
}, [actions]);
|
|
44
|
+
};
|
|
@@ -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,120 @@
|
|
|
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';
|
|
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
|
+
count: node.properties.count,
|
|
61
|
+
modifiedCount: node.properties.modifiedCount,
|
|
62
|
+
};
|
|
63
|
+
}).pipe(Atom.keepAlive);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/** Create an atom family for outbound child IDs keyed by parent ID (pre-sorted by position on write). */
|
|
67
|
+
const createChildIdsFamily = (graph: ReturnType<typeof useAppGraph>['graph']) =>
|
|
68
|
+
Atom.family((id: string) =>
|
|
69
|
+
Atom.make((get) => get(graph.connections(id, 'child')).map((child) => child.id)).pipe(Atom.keepAlive),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
/** Create an atom family for item resolution keyed by ID. */
|
|
73
|
+
const createItemFamily = (graph: ReturnType<typeof useAppGraph>['graph']) =>
|
|
74
|
+
Atom.family((id: string) =>
|
|
75
|
+
Atom.make((get) => {
|
|
76
|
+
const node = Option.getOrElse(get(graph.node(id)), () => undefined);
|
|
77
|
+
return node && filterItems(node) ? node : undefined;
|
|
78
|
+
}).pipe(Atom.keepAlive),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
/** Create an atom family for open state keyed by path. */
|
|
82
|
+
const createItemOpenFamily = (getItemAtom: ReturnType<typeof useNavTreeState>['getItemAtom']) =>
|
|
83
|
+
Atom.family((pathKey: string) => {
|
|
84
|
+
const path = Path.parts(pathKey);
|
|
85
|
+
const stateAtom = getItemAtom(path);
|
|
86
|
+
return Atom.make((get) => get(stateAtom).open).pipe(Atom.keepAlive);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
/** Create an atom family for current (selected) state keyed by path. */
|
|
90
|
+
const createItemCurrentFamily = (getItemAtom: ReturnType<typeof useNavTreeState>['getItemAtom']) =>
|
|
91
|
+
Atom.family((pathKey: string) => {
|
|
92
|
+
const path = Path.parts(pathKey);
|
|
93
|
+
const stateAtom = getItemAtom(path);
|
|
94
|
+
return Atom.make((get) => get(stateAtom).current).pipe(Atom.keepAlive);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Creates a TreeModel backed by the app graph and navtree state.
|
|
99
|
+
*/
|
|
100
|
+
export const useNavTreeModel = (rootId: string): TreeModel<NavTreeItemGraphNode> => {
|
|
101
|
+
const { graph } = useAppGraph();
|
|
102
|
+
const { getItemAtom } = useNavTreeState();
|
|
103
|
+
|
|
104
|
+
const itemPropsFamily = useMemo(() => createItemPropsFamily(graph), [graph]);
|
|
105
|
+
const childIdsFamily = useMemo(() => createChildIdsFamily(graph), [graph]);
|
|
106
|
+
const itemFamily = useMemo(() => createItemFamily(graph), [graph]);
|
|
107
|
+
const itemOpenFamily = useMemo(() => createItemOpenFamily(getItemAtom), [getItemAtom]);
|
|
108
|
+
const itemCurrentFamily = useMemo(() => createItemCurrentFamily(getItemAtom), [getItemAtom]);
|
|
109
|
+
|
|
110
|
+
return useMemo(
|
|
111
|
+
() => ({
|
|
112
|
+
item: (id: string) => itemFamily(id),
|
|
113
|
+
itemProps: (path: string[]) => itemPropsFamily(Path.create(...path)),
|
|
114
|
+
itemOpen: (path: string[]) => itemOpenFamily(Path.create(...path)),
|
|
115
|
+
itemCurrent: (path: string[]) => itemCurrentFamily(Path.create(...path)),
|
|
116
|
+
childIds: (parentId?: string) => childIdsFamily(parentId ?? rootId),
|
|
117
|
+
}),
|
|
118
|
+
[itemFamily, itemPropsFamily, itemOpenFamily, itemCurrentFamily, childIdsFamily, rootId],
|
|
119
|
+
);
|
|
120
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Atom, useAtomValue } from '@effect-atom/atom-react';
|
|
6
|
+
import { useMemo } from 'react';
|
|
7
|
+
|
|
8
|
+
import { useCapability } from '@dxos/app-framework/ui';
|
|
9
|
+
|
|
10
|
+
import { NavTreeCapabilities } from '#types';
|
|
11
|
+
|
|
12
|
+
export type UseNavTreeStateResult = {
|
|
13
|
+
getItem: (path: string[]) => NavTreeCapabilities.NavTreeItemState;
|
|
14
|
+
getItemAtom: (path: string[]) => Atom.Atom<NavTreeCapabilities.NavTreeItemState>;
|
|
15
|
+
setItem: (path: string[], key: 'open' | 'current', next: boolean) => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Hook that provides access to navtree state functions.
|
|
20
|
+
* For reactive subscriptions, use `useNavTreeItemState` or `useAtomValue(getItemAtom(path))`.
|
|
21
|
+
*/
|
|
22
|
+
export const useNavTreeState = (): UseNavTreeStateResult => {
|
|
23
|
+
return useCapability(NavTreeCapabilities.State);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Hook that subscribes to a single navtree item's state.
|
|
28
|
+
* Use this for fine-grained reactivity.
|
|
29
|
+
*/
|
|
30
|
+
export const useNavTreeItemState = (path: string[]): NavTreeCapabilities.NavTreeItemState => {
|
|
31
|
+
const { getItemAtom } = useCapability(NavTreeCapabilities.State);
|
|
32
|
+
const pathKey = useMemo(() => path.join('~'), [path]);
|
|
33
|
+
const atom = useMemo(() => getItemAtom(path), [getItemAtom, pathKey]);
|
|
34
|
+
return useAtomValue(atom);
|
|
35
|
+
};
|
package/src/index.ts
CHANGED
package/src/meta.ts
CHANGED
|
@@ -2,16 +2,34 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Plugin } from '@dxos/app-framework';
|
|
6
|
+
import { DXN } from '@dxos/keys';
|
|
7
|
+
import { trim } from '@dxos/util';
|
|
6
8
|
|
|
7
|
-
export const
|
|
9
|
+
export const meta = Plugin.makeMeta({
|
|
10
|
+
key: DXN.make('org.dxos.plugin.navtree'),
|
|
11
|
+
name: 'Navtree',
|
|
12
|
+
author: 'DXOS',
|
|
13
|
+
spec: 'PLUGIN.mdl',
|
|
14
|
+
description: trim`
|
|
15
|
+
NavTreePlugin renders the application graph as a collapsible hierarchical tree in the Composer sidebar.
|
|
16
|
+
It manages workspace tabs at the root level and provides expand/collapse navigation for spaces, folders,
|
|
17
|
+
and objects, with open/current state persisted to localStorage across sessions.
|
|
18
|
+
|
|
19
|
+
The plugin wires drag-and-drop rearrangement and cross-parent transfer of tree items via Pragmatic DnD,
|
|
20
|
+
and registers graph-action keyboard shortcuts (including the ⌘K / Ctrl+K command palette) through a
|
|
21
|
+
global Keyboard singleton that tracks context as the user navigates.
|
|
22
|
+
|
|
23
|
+
An operation handler for LayoutOperation.Expose expands ancestor paths on demand so that any
|
|
24
|
+
programmatically activated item is scrolled into view in the tree.
|
|
25
|
+
|
|
26
|
+
A commands dialog surfaces all discoverable graph actions as a searchable palette, making it the primary
|
|
27
|
+
keyboard-first entry point for power users.
|
|
28
|
+
`,
|
|
29
|
+
tags: ['system'],
|
|
30
|
+
});
|
|
8
31
|
|
|
9
32
|
// TODO(wittjosiah): Factor out.
|
|
10
33
|
export const KEY_BINDING = 'KeyBinding';
|
|
11
34
|
// TODO(wittjosiah): Factor out.
|
|
12
|
-
export const COMMANDS_DIALOG = `${
|
|
13
|
-
|
|
14
|
-
export const meta: PluginMeta = {
|
|
15
|
-
id: NAVTREE_PLUGIN,
|
|
16
|
-
name: 'Navtree',
|
|
17
|
-
};
|
|
35
|
+
export const COMMANDS_DIALOG = DXN.make(`${meta.id}.commandsDialog`);
|