@dxos/plugin-navtree 0.8.4-main.a4bbb77 → 0.8.4-main.ae835ea
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-BCXDEDN7.mjs → app-graph-builder-D7NLWZLU.mjs} +5 -4
- package/dist/lib/browser/app-graph-builder-D7NLWZLU.mjs.map +7 -0
- package/dist/lib/browser/{chunk-LPAREX6J.mjs → chunk-5VLWX4PK.mjs} +2 -2
- package/dist/lib/browser/{chunk-6AZKY5WZ.mjs → chunk-BNGPXJB7.mjs} +46 -34
- package/dist/lib/browser/chunk-BNGPXJB7.mjs.map +7 -0
- package/dist/lib/browser/chunk-E6MZBCU5.mjs +19 -0
- package/dist/lib/browser/chunk-E6MZBCU5.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +3 -3
- package/dist/lib/browser/{intent-resolver-G6COWRWW.mjs → intent-resolver-YUVXVPHG.mjs} +4 -4
- package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +7 -0
- package/dist/lib/browser/{keyboard-AHSAWKXA.mjs → keyboard-YYTYJW4M.mjs} +2 -2
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-FOCB4Y5R.mjs → react-surface-7OUBD57A.mjs} +4 -4
- package/dist/lib/browser/{state-2Q623V7U.mjs → state-EUSVT6BI.mjs} +3 -3
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +12 -9
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts +0 -1
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.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/L1Panel.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.stories.d.ts +0 -3
- package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/testing/generator.d.ts +1 -1
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +4 -3
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +38 -39
- package/src/capabilities/app-graph-builder.ts +3 -2
- package/src/capabilities/intent-resolver.ts +1 -1
- package/src/components/NavTree/NavTree.stories.tsx +56 -45
- package/src/components/NavTreeContainer.tsx +11 -4
- package/src/components/NavTreeItem/NavTreeItemAction.tsx +2 -1
- package/src/components/NavTreeItem/NavTreeItemColumns.tsx +25 -19
- package/src/components/Sidebar/L0Menu.tsx +1 -1
- package/src/components/Sidebar/L1Panel.tsx +6 -7
- package/src/experimental/Tree.stories.tsx +2 -5
- package/src/meta.ts +5 -0
- package/src/testing/app-graph-builder.ts +9 -8
- package/src/testing/generator.ts +2 -2
- package/src/types.ts +4 -3
- package/dist/lib/browser/app-graph-builder-BCXDEDN7.mjs.map +0 -7
- package/dist/lib/browser/chunk-6AZKY5WZ.mjs.map +0 -7
- package/dist/lib/browser/chunk-ZENLR72O.mjs +0 -14
- package/dist/lib/browser/chunk-ZENLR72O.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-G6COWRWW.mjs.map +0 -7
- /package/dist/lib/browser/{chunk-LPAREX6J.mjs.map → chunk-5VLWX4PK.mjs.map} +0 -0
- /package/dist/lib/browser/{keyboard-AHSAWKXA.mjs.map → keyboard-YYTYJW4M.mjs.map} +0 -0
- /package/dist/lib/browser/{react-surface-FOCB4Y5R.mjs.map → react-surface-7OUBD57A.mjs.map} +0 -0
- /package/dist/lib/browser/{state-2Q623V7U.mjs.map → state-EUSVT6BI.mjs.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-navtree",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.ae835ea",
|
|
4
4
|
"description": "DXOS Surface plugin for a tree view visualization of the graph.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -31,50 +31,49 @@
|
|
|
31
31
|
"@radix-ui/react-dismissable-layer": "1.1.5",
|
|
32
32
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
|
33
33
|
"date-fns": "^3.3.1",
|
|
34
|
-
"@dxos/app-framework": "0.8.4-main.
|
|
35
|
-
"@dxos/app-graph": "0.8.4-main.
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/plugin-
|
|
47
|
-
"@dxos/
|
|
48
|
-
"@dxos/plugin-observability": "0.8.4-main.
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/react-
|
|
51
|
-
"@dxos/react-ui-
|
|
52
|
-
"@dxos/react-ui-
|
|
53
|
-
"@dxos/react-ui-
|
|
54
|
-
"@dxos/react-ui-
|
|
55
|
-
"@dxos/
|
|
56
|
-
"@dxos/react-ui-
|
|
57
|
-
"@dxos/util": "0.8.4-main.a4bbb77"
|
|
34
|
+
"@dxos/app-framework": "0.8.4-main.ae835ea",
|
|
35
|
+
"@dxos/app-graph": "0.8.4-main.ae835ea",
|
|
36
|
+
"@dxos/echo": "0.8.4-main.ae835ea",
|
|
37
|
+
"@dxos/keyboard": "0.8.4-main.ae835ea",
|
|
38
|
+
"@dxos/invariant": "0.8.4-main.ae835ea",
|
|
39
|
+
"@dxos/lit-ui": "0.8.4-main.ae835ea",
|
|
40
|
+
"@dxos/live-object": "0.8.4-main.ae835ea",
|
|
41
|
+
"@dxos/async": "0.8.4-main.ae835ea",
|
|
42
|
+
"@dxos/debug": "0.8.4-main.ae835ea",
|
|
43
|
+
"@dxos/local-storage": "0.8.4-main.ae835ea",
|
|
44
|
+
"@dxos/log": "0.8.4-main.ae835ea",
|
|
45
|
+
"@dxos/plugin-deck": "0.8.4-main.ae835ea",
|
|
46
|
+
"@dxos/plugin-graph": "0.8.4-main.ae835ea",
|
|
47
|
+
"@dxos/protocols": "0.8.4-main.ae835ea",
|
|
48
|
+
"@dxos/plugin-observability": "0.8.4-main.ae835ea",
|
|
49
|
+
"@dxos/react-client": "0.8.4-main.ae835ea",
|
|
50
|
+
"@dxos/react-ui-attention": "0.8.4-main.ae835ea",
|
|
51
|
+
"@dxos/react-ui-list": "0.8.4-main.ae835ea",
|
|
52
|
+
"@dxos/react-ui-menu": "0.8.4-main.ae835ea",
|
|
53
|
+
"@dxos/react-ui-tabs": "0.8.4-main.ae835ea",
|
|
54
|
+
"@dxos/react-ui-stack": "0.8.4-main.ae835ea",
|
|
55
|
+
"@dxos/util": "0.8.4-main.ae835ea",
|
|
56
|
+
"@dxos/react-ui-searchlist": "0.8.4-main.ae835ea"
|
|
58
57
|
},
|
|
59
58
|
"devDependencies": {
|
|
60
59
|
"@effect-rx/rx-react": "0.42.4",
|
|
61
60
|
"@effect/platform": "0.92.1",
|
|
62
|
-
"@types/react": "~19.2.
|
|
63
|
-
"@types/react-dom": "~19.2.
|
|
61
|
+
"@types/react": "~19.2.2",
|
|
62
|
+
"@types/react-dom": "~19.2.2",
|
|
64
63
|
"effect": "3.18.3",
|
|
65
64
|
"react": "~19.2.0",
|
|
66
65
|
"react-dom": "~19.2.0",
|
|
67
66
|
"vite": "7.1.9",
|
|
68
|
-
"@dxos/echo-signals": "0.8.4-main.
|
|
69
|
-
"@dxos/plugin-
|
|
70
|
-
"@dxos/plugin-
|
|
71
|
-
"@dxos/plugin-space": "0.8.4-main.
|
|
72
|
-
"@dxos/plugin-storybook-layout": "0.8.4-main.
|
|
73
|
-
"@dxos/plugin-theme": "0.8.4-main.
|
|
74
|
-
"@dxos/random": "0.8.4-main.
|
|
75
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
76
|
-
"@dxos/react-ui-theme": "0.8.4-main.
|
|
77
|
-
"@dxos/storybook-utils": "0.8.4-main.
|
|
67
|
+
"@dxos/echo-signals": "0.8.4-main.ae835ea",
|
|
68
|
+
"@dxos/plugin-client": "0.8.4-main.ae835ea",
|
|
69
|
+
"@dxos/plugin-attention": "0.8.4-main.ae835ea",
|
|
70
|
+
"@dxos/plugin-space": "0.8.4-main.ae835ea",
|
|
71
|
+
"@dxos/plugin-storybook-layout": "0.8.4-main.ae835ea",
|
|
72
|
+
"@dxos/plugin-theme": "0.8.4-main.ae835ea",
|
|
73
|
+
"@dxos/random": "0.8.4-main.ae835ea",
|
|
74
|
+
"@dxos/react-ui": "0.8.4-main.ae835ea",
|
|
75
|
+
"@dxos/react-ui-theme": "0.8.4-main.ae835ea",
|
|
76
|
+
"@dxos/storybook-utils": "0.8.4-main.ae835ea"
|
|
78
77
|
},
|
|
79
78
|
"peerDependencies": {
|
|
80
79
|
"@effect-rx/rx-react": "^0.34.1",
|
|
@@ -82,8 +81,8 @@
|
|
|
82
81
|
"effect": "^3.13.3",
|
|
83
82
|
"react": "^19.0.0",
|
|
84
83
|
"react-dom": "^19.0.0",
|
|
85
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
86
|
-
"@dxos/react-ui
|
|
84
|
+
"@dxos/react-ui-theme": "0.8.4-main.ae835ea",
|
|
85
|
+
"@dxos/react-ui": "0.8.4-main.ae835ea"
|
|
87
86
|
},
|
|
88
87
|
"publishConfig": {
|
|
89
88
|
"access": "public"
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Rx } from '@effect-rx/rx-react';
|
|
6
|
-
import
|
|
6
|
+
import * as Function from 'effect/Function';
|
|
7
|
+
import * as Option from 'effect/Option';
|
|
7
8
|
|
|
8
9
|
import { Capabilities, LayoutAction, type PluginContext, contributes, createIntent } from '@dxos/app-framework';
|
|
9
10
|
import { ROOT_ID, createExtension } from '@dxos/plugin-graph';
|
|
@@ -17,7 +18,7 @@ export default (context: PluginContext) =>
|
|
|
17
18
|
id: meta.id,
|
|
18
19
|
actions: (node) =>
|
|
19
20
|
Rx.make((get) =>
|
|
20
|
-
pipe(
|
|
21
|
+
Function.pipe(
|
|
21
22
|
get(node),
|
|
22
23
|
Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
|
|
23
24
|
Option.map(() => [
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
-
import
|
|
7
|
-
import React from 'react';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
import React, { type KeyboardEvent, useCallback, useRef } from 'react';
|
|
8
|
+
import { expect, userEvent, within } from 'storybook/test';
|
|
8
9
|
|
|
9
10
|
import {
|
|
10
11
|
Capabilities,
|
|
@@ -19,9 +20,7 @@ import {
|
|
|
19
20
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
20
21
|
import { live } from '@dxos/live-object';
|
|
21
22
|
import { AttentionPlugin } from '@dxos/plugin-attention';
|
|
22
|
-
import { ClientPlugin } from '@dxos/plugin-client';
|
|
23
23
|
import { GraphPlugin } from '@dxos/plugin-graph';
|
|
24
|
-
import { SpacePlugin } from '@dxos/plugin-space';
|
|
25
24
|
import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
|
|
26
25
|
import { ThemePlugin } from '@dxos/plugin-theme';
|
|
27
26
|
import { faker } from '@dxos/random';
|
|
@@ -66,6 +65,15 @@ const StoryPlankHeading = ({ attendableId }: { attendableId: string }) => {
|
|
|
66
65
|
|
|
67
66
|
const StoryPlank = ({ attendableId }: { attendableId: string }) => {
|
|
68
67
|
const attentionAttrs = useAttentionAttributes(attendableId);
|
|
68
|
+
const rootElement = useRef<HTMLDivElement | null>(null);
|
|
69
|
+
|
|
70
|
+
// NOTE(thure): This is the same workaround as in Plank, but that component is out of scope for this story.
|
|
71
|
+
// TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
|
|
72
|
+
const handleKeyDown = useCallback((event: KeyboardEvent) => {
|
|
73
|
+
if (event.target === event.currentTarget && event.key === 'Escape') {
|
|
74
|
+
rootElement.current?.closest('main')?.focus();
|
|
75
|
+
}
|
|
76
|
+
}, []);
|
|
69
77
|
|
|
70
78
|
return (
|
|
71
79
|
<StackItem.Root
|
|
@@ -73,10 +81,12 @@ const StoryPlank = ({ attendableId }: { attendableId: string }) => {
|
|
|
73
81
|
{...attentionAttrs}
|
|
74
82
|
classNames='bg-baseSurface border-ie border-separator'
|
|
75
83
|
size={30}
|
|
84
|
+
onKeyDown={handleKeyDown}
|
|
85
|
+
ref={rootElement}
|
|
76
86
|
>
|
|
77
87
|
<StoryPlankHeading attendableId={attendableId} />
|
|
78
88
|
<StackItem.Content toolbar>
|
|
79
|
-
<Toolbar.Root classNames='border-
|
|
89
|
+
<Toolbar.Root classNames='border-be border-subduedSeparator'>
|
|
80
90
|
<Toolbar.Button>Test</Toolbar.Button>
|
|
81
91
|
</Toolbar.Root>
|
|
82
92
|
|
|
@@ -157,47 +167,48 @@ export default meta;
|
|
|
157
167
|
|
|
158
168
|
type Story = StoryObj<typeof NavTreeContainer>;
|
|
159
169
|
|
|
160
|
-
export const Default: Story = {
|
|
170
|
+
export const Default: Story = {
|
|
171
|
+
play: async ({ canvasElement }) => {
|
|
172
|
+
const canvas = within(canvasElement);
|
|
161
173
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
onClientInitialized: async ({ client }) => {
|
|
169
|
-
await client.halo.createIdentity();
|
|
170
|
-
},
|
|
171
|
-
}),
|
|
172
|
-
SpacePlugin({}),
|
|
173
|
-
GraphPlugin(),
|
|
174
|
-
IntentPlugin(),
|
|
175
|
-
SettingsPlugin(),
|
|
176
|
-
AttentionPlugin(),
|
|
174
|
+
// Find the element with treegrid role and click on its parent
|
|
175
|
+
const treegridElement = await canvas.findByRole('treegrid');
|
|
176
|
+
const treegridParent = treegridElement.parentElement;
|
|
177
|
+
if (treegridParent) {
|
|
178
|
+
await userEvent.click(treegridParent);
|
|
179
|
+
}
|
|
177
180
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
181
|
+
// Press Escape
|
|
182
|
+
await userEvent.keyboard('{Escape}');
|
|
183
|
+
|
|
184
|
+
// Confirm that focus is on an element with attribute data-main-landmark="0"
|
|
185
|
+
await expect(document.activeElement).toHaveAttribute('data-main-landmark', '0');
|
|
186
|
+
|
|
187
|
+
// Press Tab
|
|
188
|
+
await userEvent.keyboard('{Tab}');
|
|
189
|
+
|
|
190
|
+
// Confirm that focus is now on an element with data-main-landmark="1"
|
|
191
|
+
await expect(document.activeElement).toHaveAttribute('data-main-landmark', '1');
|
|
192
|
+
|
|
193
|
+
// Press Tab
|
|
194
|
+
await userEvent.keyboard('{Tab}');
|
|
195
|
+
|
|
196
|
+
// Confirm that focus is now on an element with data-main-landmark="0"
|
|
197
|
+
await expect(document.activeElement).toHaveAttribute('data-main-landmark', '0');
|
|
198
|
+
|
|
199
|
+
// Press Shift-Tab
|
|
200
|
+
await userEvent.keyboard('{Shift>}{Tab}{/Shift}');
|
|
201
|
+
|
|
202
|
+
// Press Enter
|
|
203
|
+
await userEvent.keyboard('{Enter}');
|
|
204
|
+
|
|
205
|
+
// Confirm that focus is now on an element with data-attendable-id="space-0:object-0"
|
|
206
|
+
await expect(document.activeElement).toHaveAttribute('data-attendable-id', 'space-0:object-0');
|
|
207
|
+
|
|
208
|
+
// Press Escape
|
|
209
|
+
await userEvent.keyboard('{Escape}');
|
|
210
|
+
|
|
211
|
+
// Confirm that focus is now on an element with data-main-landmark="1"
|
|
212
|
+
await expect(document.activeElement).toHaveAttribute('data-main-landmark', '1');
|
|
202
213
|
},
|
|
203
214
|
};
|
|
@@ -98,7 +98,7 @@ export type NavTreeContainerProps = {
|
|
|
98
98
|
} & Pick<NavTreeContextValue, 'tab'>;
|
|
99
99
|
|
|
100
100
|
export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeContainerProps) => {
|
|
101
|
-
const [isLg] = useMediaQuery('lg'
|
|
101
|
+
const [isLg] = useMediaQuery('lg');
|
|
102
102
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
103
103
|
const { graph } = useAppGraph();
|
|
104
104
|
const { isOpen, isCurrent, isAlternateTree, setItem } = useCapability(NavTreeCapabilities.State);
|
|
@@ -112,12 +112,13 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
112
112
|
children.length > 0 ? children.map(({ id }) => id) : node.properties.role === 'branch' ? [] : undefined;
|
|
113
113
|
return {
|
|
114
114
|
id: node.id,
|
|
115
|
-
label: node.properties.label ?? node.id,
|
|
116
115
|
parentOf,
|
|
117
|
-
icon: node.properties.icon,
|
|
118
116
|
disabled: node.properties.disabled,
|
|
119
|
-
|
|
117
|
+
label: node.properties.label ?? node.id,
|
|
118
|
+
className: mx(node.properties.className, node.properties.modified && 'italic'), // TODO(burdon): Italic?
|
|
120
119
|
headingClassName: node.properties.headingClassName,
|
|
120
|
+
icon: node.properties.icon,
|
|
121
|
+
iconHue: node.properties.iconHue,
|
|
121
122
|
testId: node.properties.testId,
|
|
122
123
|
};
|
|
123
124
|
},
|
|
@@ -179,6 +180,10 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
179
180
|
return target.item.properties.canDrop?.(source) ?? false;
|
|
180
181
|
}, []);
|
|
181
182
|
|
|
183
|
+
const canSelect = useCallback(({ item }: { item: Node }) => {
|
|
184
|
+
return item.properties.selectable ?? true;
|
|
185
|
+
}, []);
|
|
186
|
+
|
|
182
187
|
const handleSelect = useCallback(
|
|
183
188
|
({ item: node, path, option }: { item: Node; path: string[]; option: boolean }) => {
|
|
184
189
|
if (!node.data) {
|
|
@@ -304,6 +309,7 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
304
309
|
isCurrent,
|
|
305
310
|
isOpen,
|
|
306
311
|
canDrop,
|
|
312
|
+
canSelect,
|
|
307
313
|
isAlternateTree,
|
|
308
314
|
setAlternateTree,
|
|
309
315
|
onTabChange: handleTabChange,
|
|
@@ -322,6 +328,7 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
322
328
|
isCurrent,
|
|
323
329
|
isOpen,
|
|
324
330
|
canDrop,
|
|
331
|
+
canSelect,
|
|
325
332
|
isAlternateTree,
|
|
326
333
|
setAlternateTree,
|
|
327
334
|
handleTabChange,
|
|
@@ -81,10 +81,11 @@ export const NavTreeItemMonolithicAction = ({
|
|
|
81
81
|
label={baseLabel}
|
|
82
82
|
disabled={disabled}
|
|
83
83
|
onClick={(event) => {
|
|
84
|
+
event.stopPropagation();
|
|
84
85
|
if (disabled) {
|
|
85
86
|
return;
|
|
86
87
|
}
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
void invoke?.(caller ? { parent, caller } : { parent });
|
|
89
90
|
}}
|
|
90
91
|
data-testid={testId}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import React, { Fragment, memo } from 'react';
|
|
6
6
|
|
|
7
7
|
import { isAction } from '@dxos/app-graph';
|
|
8
|
-
import {
|
|
8
|
+
import { Popover, Treegrid, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
9
9
|
|
|
10
10
|
import { useLoadDescendents } from '../../hooks';
|
|
11
11
|
import { meta } from '../../meta';
|
|
@@ -15,7 +15,7 @@ import { type NavTreeItemColumnsProps } from '../types';
|
|
|
15
15
|
|
|
16
16
|
import { NavTreeItemAction } from './NavTreeItemAction';
|
|
17
17
|
|
|
18
|
-
export const NavTreeItemColumns = memo(({ path, item, open
|
|
18
|
+
export const NavTreeItemColumns = memo(({ path, item, open }: NavTreeItemColumnsProps) => {
|
|
19
19
|
const { t } = useTranslation(meta.id);
|
|
20
20
|
const { useActions, renderItemEnd: ItemEnd, popoverAnchorId } = useNavTreeContext();
|
|
21
21
|
|
|
@@ -29,15 +29,15 @@ export const NavTreeItemColumns = memo(({ path, item, open, density = 'fine' }:
|
|
|
29
29
|
.flatMap((action) => (isAction(action) ? [action] : []))
|
|
30
30
|
.filter((a) => ['list-item', 'list-item-primary'].includes(a.properties?.disposition));
|
|
31
31
|
|
|
32
|
-
const ActionRoot = popoverAnchorId === `dxos.org/ui/${NAV_TREE_ITEM}/${item.id}` ? Popover.Anchor : Fragment;
|
|
33
|
-
|
|
34
32
|
useLoadDescendents(item);
|
|
35
33
|
useLoadDescendents(primaryAction && !isAction(primaryAction) ? primaryAction : undefined);
|
|
36
34
|
|
|
35
|
+
const ActionRoot = popoverAnchorId === `dxos.org/ui/${NAV_TREE_ITEM}/${item.id}` ? Popover.Anchor : Fragment;
|
|
36
|
+
|
|
37
37
|
return (
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
<div role='none' className='contents app-no-drag'>
|
|
39
|
+
{primaryAction?.properties?.disposition === 'list-item-primary' && !primaryAction?.properties?.disabled ? (
|
|
40
|
+
<Treegrid.Cell classNames='contents'>
|
|
41
41
|
<NavTreeItemAction
|
|
42
42
|
testId={primaryAction.properties?.testId}
|
|
43
43
|
label={toLocalizedString(primaryAction.properties?.label, t)}
|
|
@@ -48,11 +48,13 @@ export const NavTreeItemColumns = memo(({ path, item, open, density = 'fine' }:
|
|
|
48
48
|
menuType={primaryAction.properties?.menuType}
|
|
49
49
|
caller={NAV_TREE_ITEM}
|
|
50
50
|
/>
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
</Treegrid.Cell>
|
|
52
|
+
) : (
|
|
53
|
+
<Treegrid.Cell role='none' />
|
|
54
|
+
)}
|
|
55
|
+
<ActionRoot>
|
|
56
|
+
{actions.length > 0 ? (
|
|
57
|
+
<Treegrid.Cell classNames='contents'>
|
|
56
58
|
<NavTreeItemAction
|
|
57
59
|
testId={`navtree.treeItem.actionsLevel${level}`}
|
|
58
60
|
label={t('tree item actions label')}
|
|
@@ -62,12 +64,16 @@ export const NavTreeItemColumns = memo(({ path, item, open, density = 'fine' }:
|
|
|
62
64
|
menuType='dropdown'
|
|
63
65
|
caller={NAV_TREE_ITEM}
|
|
64
66
|
/>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
</Treegrid.Cell>
|
|
68
|
+
) : (
|
|
69
|
+
<Treegrid.Cell role='none' />
|
|
70
|
+
)}
|
|
71
|
+
</ActionRoot>
|
|
72
|
+
{ItemEnd && (
|
|
73
|
+
<Treegrid.Cell classNames='contents'>
|
|
74
|
+
<ItemEnd node={item} open={open} />
|
|
75
|
+
</Treegrid.Cell>
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
72
78
|
);
|
|
73
79
|
});
|
|
@@ -72,7 +72,7 @@ type L0ItemProps = L0ItemRootProps & {
|
|
|
72
72
|
|
|
73
73
|
const useL0ItemClick = ({ item, parent, path }: L0ItemProps, type: string) => {
|
|
74
74
|
const { tab, isCurrent, onSelect, onTabChange } = useNavTreeContext();
|
|
75
|
-
const [isLg] = useMediaQuery('lg'
|
|
75
|
+
const [isLg] = useMediaQuery('lg');
|
|
76
76
|
|
|
77
77
|
return useCallback(
|
|
78
78
|
(event: MouseEvent) => {
|
|
@@ -5,7 +5,7 @@
|
|
|
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
11
|
import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/react-ui-theme';
|
|
@@ -28,11 +28,10 @@ export type L1PanelProps = {
|
|
|
28
28
|
export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProps) => {
|
|
29
29
|
const { t } = useTranslation(meta.id);
|
|
30
30
|
const { isAlternateTree, ...navTreeContext } = useNavTreeContext();
|
|
31
|
-
// NOTE(burdon): I'm very uneasy about this pattern of declaring hooks as context props.
|
|
32
|
-
const { useItems } = navTreeContext;
|
|
33
31
|
const title = toLocalizedString(item.properties.label, t);
|
|
34
32
|
|
|
35
33
|
// TODO(wittjosiah): Support multiple alternate trees.
|
|
34
|
+
const { useItems } = navTreeContext;
|
|
36
35
|
const alternateTree = useItems(item, { disposition: 'alternate-tree' })[0];
|
|
37
36
|
const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
38
37
|
const isAlternate = isAlternateTree?.(alternatePath, item) ?? false;
|
|
@@ -59,19 +58,19 @@ export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProp
|
|
|
59
58
|
{...(!open && { inert: true })}
|
|
60
59
|
>
|
|
61
60
|
{item.id === currentItemId && (
|
|
62
|
-
|
|
61
|
+
<DensityProvider density='fine'>
|
|
63
62
|
<L1PanelHeader path={path} item={item} currentItemId={currentItemId} onBack={onBack} />
|
|
64
63
|
<div role='none' className='overflow-y-auto'>
|
|
65
64
|
{isAlternate ? (
|
|
66
65
|
<Tree
|
|
67
66
|
{...navTreeContext}
|
|
68
|
-
useItems={useAlternateItems}
|
|
69
67
|
id={alternateTree.id}
|
|
70
68
|
root={alternateTree}
|
|
71
69
|
path={alternatePath}
|
|
72
70
|
levelOffset={5}
|
|
73
71
|
gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
|
|
74
72
|
renderColumns={NavTreeItemColumns}
|
|
73
|
+
useItems={useAlternateItems}
|
|
75
74
|
/>
|
|
76
75
|
) : (
|
|
77
76
|
<Tree
|
|
@@ -80,13 +79,13 @@ export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProp
|
|
|
80
79
|
root={item}
|
|
81
80
|
path={path}
|
|
82
81
|
levelOffset={5}
|
|
83
|
-
draggable
|
|
84
82
|
gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
|
|
85
83
|
renderColumns={NavTreeItemColumns}
|
|
84
|
+
draggable
|
|
86
85
|
/>
|
|
87
86
|
)}
|
|
88
87
|
</div>
|
|
89
|
-
|
|
88
|
+
</DensityProvider>
|
|
90
89
|
)}
|
|
91
90
|
</Tabs.Tabpanel>
|
|
92
91
|
);
|
|
@@ -7,7 +7,7 @@ import React, { type JSX, type PropsWithChildren, useEffect, useState } from 're
|
|
|
7
7
|
|
|
8
8
|
import { faker } from '@dxos/random';
|
|
9
9
|
import { Icon, IconButton } from '@dxos/react-ui';
|
|
10
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
10
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
11
11
|
import { mx } from '@dxos/react-ui-theme';
|
|
12
12
|
|
|
13
13
|
import { type ItemMap, Tree, type TreeNodeData, type TreeProps, visitNodes, visitor } from './Tree';
|
|
@@ -203,10 +203,7 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
|
|
|
203
203
|
const meta = {
|
|
204
204
|
title: 'plugins/plugin-navtree/experimental/Tree',
|
|
205
205
|
component: Tree,
|
|
206
|
-
decorators: [withTheme],
|
|
207
|
-
parameters: {
|
|
208
|
-
layout: 'column',
|
|
209
|
-
},
|
|
206
|
+
decorators: [withTheme, withLayout({ container: 'column' })],
|
|
210
207
|
} satisfies Meta<typeof Tree>;
|
|
211
208
|
|
|
212
209
|
export default meta;
|
package/src/meta.ts
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type PluginMeta } from '@dxos/app-framework';
|
|
6
|
+
import { trim } from '@dxos/util';
|
|
6
7
|
|
|
7
8
|
export const meta: PluginMeta = {
|
|
8
9
|
id: 'dxos.org/plugin/navtree',
|
|
9
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
|
+
`,
|
|
10
15
|
};
|
|
11
16
|
|
|
12
17
|
// TODO(wittjosiah): Factor out.
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Rx } from '@effect-rx/rx-react';
|
|
6
|
-
import
|
|
6
|
+
import * as Function from 'effect/Function';
|
|
7
|
+
import * as Option from 'effect/Option';
|
|
7
8
|
|
|
8
9
|
import { type PluginContext } from '@dxos/app-framework';
|
|
9
10
|
import { log } from '@dxos/log';
|
|
@@ -28,7 +29,7 @@ export const storybookGraphBuilders = (context: PluginContext): BuilderExtension
|
|
|
28
29
|
id: 'app-menu',
|
|
29
30
|
actions: (node) =>
|
|
30
31
|
Rx.make((get) =>
|
|
31
|
-
pipe(
|
|
32
|
+
Function.pipe(
|
|
32
33
|
get(node),
|
|
33
34
|
Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
|
|
34
35
|
Option.map((node) => {
|
|
@@ -55,7 +56,7 @@ export const storybookGraphBuilders = (context: PluginContext): BuilderExtension
|
|
|
55
56
|
id: 'user-account',
|
|
56
57
|
connector: (node) =>
|
|
57
58
|
Rx.make((get) =>
|
|
58
|
-
pipe(
|
|
59
|
+
Function.pipe(
|
|
59
60
|
get(node),
|
|
60
61
|
Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
|
|
61
62
|
Option.map(() => {
|
|
@@ -112,7 +113,7 @@ export const storybookGraphBuilders = (context: PluginContext): BuilderExtension
|
|
|
112
113
|
position: 'hoist',
|
|
113
114
|
connector: (node) =>
|
|
114
115
|
Rx.make((get) =>
|
|
115
|
-
pipe(
|
|
116
|
+
Function.pipe(
|
|
116
117
|
get(node),
|
|
117
118
|
Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
|
|
118
119
|
Option.map(() => {
|
|
@@ -153,7 +154,7 @@ export const storybookGraphBuilders = (context: PluginContext): BuilderExtension
|
|
|
153
154
|
});
|
|
154
155
|
|
|
155
156
|
return Rx.make((get) =>
|
|
156
|
-
pipe(
|
|
157
|
+
Function.pipe(
|
|
157
158
|
get(node),
|
|
158
159
|
Option.flatMap((node) => (node.id === 'spaces-root' ? Option.some(node) : Option.none())),
|
|
159
160
|
Option.map(() => {
|
|
@@ -179,7 +180,7 @@ export const storybookGraphBuilders = (context: PluginContext): BuilderExtension
|
|
|
179
180
|
id: 'space-actions',
|
|
180
181
|
actions: (node) =>
|
|
181
182
|
Rx.make((get) =>
|
|
182
|
-
pipe(
|
|
183
|
+
Function.pipe(
|
|
183
184
|
get(node),
|
|
184
185
|
Option.flatMap((node) => (node.type === 'space' ? Option.some(node) : Option.none())),
|
|
185
186
|
Option.map((node) => {
|
|
@@ -220,7 +221,7 @@ export const storybookGraphBuilders = (context: PluginContext): BuilderExtension
|
|
|
220
221
|
});
|
|
221
222
|
|
|
222
223
|
return Rx.make((get) =>
|
|
223
|
-
pipe(
|
|
224
|
+
Function.pipe(
|
|
224
225
|
get(node),
|
|
225
226
|
Option.flatMap((node) => (node.type === 'space' ? Option.some(node) : Option.none())),
|
|
226
227
|
Option.map((node) => {
|
|
@@ -245,7 +246,7 @@ export const storybookGraphBuilders = (context: PluginContext): BuilderExtension
|
|
|
245
246
|
id: 'object-actions',
|
|
246
247
|
actions: (node) =>
|
|
247
248
|
Rx.make((get) =>
|
|
248
|
-
pipe(
|
|
249
|
+
Function.pipe(
|
|
249
250
|
get(node),
|
|
250
251
|
Option.flatMap((node) => (node.type === 'object' ? Option.some(node) : Option.none())),
|
|
251
252
|
Option.map((node) => {
|
package/src/testing/generator.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
6
|
|
|
7
7
|
import { type NodeArg } from '@dxos/app-graph';
|
|
8
8
|
import { type Live, Obj, Type } from '@dxos/echo';
|
|
9
|
-
import { TypedObject } from '@dxos/echo
|
|
9
|
+
import { TypedObject } from '@dxos/echo/internal';
|
|
10
10
|
import { faker } from '@dxos/random';
|
|
11
11
|
import { range } from '@dxos/util';
|
|
12
12
|
|
package/src/types.ts
CHANGED
|
@@ -28,13 +28,14 @@ export type FlattenedActions = {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
type SharedProperties = {
|
|
31
|
-
|
|
32
|
-
icon?: string;
|
|
31
|
+
testId?: string;
|
|
33
32
|
disabled?: boolean;
|
|
34
33
|
position?: Position;
|
|
35
|
-
|
|
34
|
+
label: Label;
|
|
36
35
|
className?: string;
|
|
37
36
|
headingClassName?: string;
|
|
37
|
+
icon?: string;
|
|
38
|
+
iconHue?: string;
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
export type NodeProperties = SharedProperties & {
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/capabilities/app-graph-builder.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport { Rx } from '@effect-rx/rx-react';\nimport { Option, pipe } from 'effect';\n\nimport { Capabilities, LayoutAction, type PluginContext, contributes, createIntent } from '@dxos/app-framework';\nimport { ROOT_ID, createExtension } from '@dxos/plugin-graph';\n\nimport { COMMANDS_DIALOG, meta } from '../meta';\n\nexport default (context: PluginContext) =>\n contributes(\n Capabilities.AppGraphBuilder,\n createExtension({\n id: meta.id,\n actions: (node) =>\n Rx.make((get) =>\n pipe(\n get(node),\n Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),\n Option.map(() => [\n {\n id: COMMANDS_DIALOG,\n data: async () => {\n const { dispatchPromise: dispatch } = context.getCapability(Capabilities.IntentDispatcher);\n await dispatch(\n createIntent(LayoutAction.UpdateDialog, {\n part: 'dialog',\n subject: COMMANDS_DIALOG,\n options: {\n blockAlign: 'start',\n },\n }),\n );\n },\n properties: {\n label: ['open commands label', { ns: meta.id }],\n icon: 'ph--magnifying-glass--regular',\n keyBinding: {\n macos: 'meta+k',\n windows: 'ctrl+k',\n },\n },\n },\n ]),\n Option.getOrElse(() => []),\n ),\n ),\n }),\n );\n"],
|
|
5
|
-
"mappings": ";;;;;;AAIA,SAASA,UAAU;AACnB,SAASC,QAAQC,YAAY;AAE7B,SAASC,cAAcC,cAAkCC,aAAaC,oBAAoB;AAC1F,SAASC,SAASC,uBAAuB;AAIzC,IAAA,6BAAe,CAACC,YACdC,YACEC,aAAaC,iBACbC,gBAAgB;EACdC,IAAIC,KAAKD;EACTE,SAAS,CAACC,SACRC,GAAGC,KAAK,CAACC,QACPC,KACED,IAAIH,IAAAA,GACJK,OAAOC,QAAQ,CAACN,UAAUA,MAAKH,OAAOU,UAAUF,OAAOG,KAAKR,KAAAA,IAAQK,OAAOI,KAAI,CAAA,GAC/EJ,OAAOK,IAAI,MAAM;IACf;MACEb,IAAIc;MACJC,MAAM,YAAA;AACJ,cAAM,EAAEC,iBAAiBC,SAAQ,IAAKtB,QAAQuB,cAAcrB,aAAasB,gBAAgB;AACzF,cAAMF,SACJG,aAAaC,aAAaC,cAAc;UACtCC,MAAM;UACNC,SAASV;UACTW,SAAS;YACPC,YAAY;UACd;QACF,CAAA,CAAA;MAEJ;MACAC,YAAY;QACVC,OAAO;UAAC;UAAuB;YAAEC,IAAI5B,KAAKD;UAAG;;QAC7C8B,MAAM;QACNC,YAAY;UACVC,OAAO;UACPC,SAAS;QACX;MACF;IACF;GACD,GACDzB,OAAO0B,UAAU,MAAM,CAAA,CAAE,CAAA,CAAA;AAGjC,CAAA,CAAA;",
|
|
6
|
-
"names": ["Rx", "Option", "pipe", "Capabilities", "LayoutAction", "contributes", "createIntent", "ROOT_ID", "createExtension", "context", "contributes", "Capabilities", "AppGraphBuilder", "createExtension", "id", "meta", "actions", "node", "Rx", "make", "get", "pipe", "Option", "flatMap", "ROOT_ID", "some", "none", "map", "COMMANDS_DIALOG", "data", "dispatchPromise", "dispatch", "getCapability", "IntentDispatcher", "createIntent", "LayoutAction", "UpdateDialog", "part", "subject", "options", "blockAlign", "properties", "label", "ns", "icon", "keyBinding", "macos", "windows", "getOrElse"]
|
|
7
|
-
}
|