@dxos/plugin-navtree 0.9.0 → 0.9.1-staging.ee54ba693a
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/neutral/{CommandsDialogContent-UBGIQ5FI.mjs → CommandsDialogContent-TEKBCQ3U.mjs} +3 -3
- package/dist/lib/neutral/CommandsDialogContent-TEKBCQ3U.mjs.map +7 -0
- package/dist/lib/neutral/{CommandsTrigger-BH5WLMHK.mjs → CommandsTrigger-HGCI22SP.mjs} +2 -2
- package/dist/lib/neutral/{CommandsTrigger-BH5WLMHK.mjs.map → CommandsTrigger-HGCI22SP.mjs.map} +3 -3
- package/dist/lib/neutral/{NavTreeContainer-6AR4BFDD.mjs → NavTreeContainer-ICDKC2GI.mjs} +2 -2
- package/dist/lib/neutral/{NavTreeDocumentTitle-H3E6ZLYE.mjs → NavTreeDocumentTitle-RMERXZGK.mjs} +2 -2
- package/dist/lib/neutral/{NavTreeDocumentTitle-H3E6ZLYE.mjs.map → NavTreeDocumentTitle-RMERXZGK.mjs.map} +3 -3
- package/dist/lib/neutral/NavTreePlugin.mjs +2 -2
- package/dist/lib/neutral/NavTreePlugin.mjs.map +4 -4
- package/dist/lib/neutral/{app-graph-builder-Z347LKSF.mjs → app-graph-builder-2DYAIROU.mjs} +2 -2
- package/dist/lib/neutral/{app-graph-builder-Z347LKSF.mjs.map → app-graph-builder-2DYAIROU.mjs.map} +3 -3
- package/dist/lib/neutral/capabilities/index.mjs +4 -4
- package/dist/lib/neutral/{chunk-5OPDKH6B.mjs → chunk-4RN6TH36.mjs} +23 -4
- package/dist/lib/neutral/chunk-4RN6TH36.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-ZSWYB7JW.mjs → chunk-BAVI2K7A.mjs} +3 -3
- package/dist/lib/neutral/{chunk-ZSWYB7JW.mjs.map → chunk-BAVI2K7A.mjs.map} +3 -3
- package/dist/lib/neutral/{chunk-T7QS2EFI.mjs → chunk-EAN4K6OL.mjs} +2 -2
- package/dist/lib/neutral/chunk-IRQMJLRY.mjs +45 -0
- package/dist/lib/neutral/chunk-IRQMJLRY.mjs.map +7 -0
- package/dist/lib/neutral/components/index.mjs +52 -43
- package/dist/lib/neutral/components/index.mjs.map +3 -3
- package/dist/lib/neutral/containers/index.mjs +5 -5
- package/dist/lib/neutral/{expose-26FV4W5D.mjs → expose-HIBA2NQG.mjs} +2 -2
- package/dist/lib/neutral/hooks/index.mjs +7 -4
- package/dist/lib/neutral/hooks/index.mjs.map +3 -3
- package/dist/lib/neutral/index.mjs +2 -2
- package/dist/lib/neutral/{keyboard-EJ65BVTC.mjs → keyboard-JM2HCF5N.mjs} +5 -3
- package/dist/lib/neutral/{keyboard-EJ65BVTC.mjs.map → keyboard-JM2HCF5N.mjs.map} +3 -3
- package/dist/lib/neutral/meta.json +1 -1
- package/dist/lib/neutral/meta.mjs +1 -1
- package/dist/lib/neutral/operations/index.mjs +1 -1
- package/dist/lib/neutral/plugin.mjs +2 -2
- package/dist/lib/neutral/{react-surface-Q5XMAG7Z.mjs → react-surface-QPMUEDZX.mjs} +11 -13
- package/dist/lib/neutral/react-surface-QPMUEDZX.mjs.map +7 -0
- package/dist/lib/neutral/{state-VVJEWUHM.mjs → state-PDZU4PIP.mjs} +2 -2
- package/dist/lib/neutral/state-PDZU4PIP.mjs.map +7 -0
- package/dist/lib/neutral/testing/index.mjs +2 -2
- package/dist/lib/neutral/testing/index.mjs.map +3 -3
- package/dist/lib/neutral/translations.mjs +1 -1
- package/dist/lib/neutral/translations.mjs.map +3 -3
- package/dist/lib/neutral/types/index.mjs +1 -1
- package/dist/types/dx.config.d.ts +28 -0
- package/dist/types/dx.config.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -2
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +4 -4
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/keyboard.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +2 -2
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/state.d.ts +1 -2
- package/dist/types/src/capabilities/state.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
- package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -1
- package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +32 -2
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/types/NavTreeCapabilities.d.ts.map +1 -1
- package/dist/types/src/types/NavTreeEvents.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +1 -1
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/dx.config.ts +31 -0
- package/package.json +41 -41
- package/src/NavTreePlugin.test.ts +1 -1
- package/src/NavTreePlugin.tsx +1 -1
- package/src/capabilities/app-graph-builder.ts +1 -1
- package/src/capabilities/keyboard.ts +5 -2
- package/src/capabilities/react-surface.tsx +8 -14
- package/src/capabilities/state.ts +1 -1
- package/src/components/NavTree/NavTree.stories.tsx +43 -36
- package/src/components/NavTree/NavTree.tsx +5 -4
- package/src/components/NavTreeItem/NavTreeItemAction.tsx +2 -2
- package/src/components/NavTreeItem/NavTreeItemColumns.tsx +7 -4
- package/src/components/Sidebar/L0Menu.tsx +14 -7
- package/src/components/Sidebar/L1Panel.tsx +5 -5
- package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +2 -2
- package/src/containers/CommandsTrigger/CommandsTrigger.tsx +1 -1
- package/src/containers/NavTreeContainer/NavTreeContainer.tsx +17 -3
- package/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.tsx +1 -1
- package/src/hooks/useNavTreeModel.ts +8 -4
- package/src/meta.ts +3 -23
- package/src/testing/generator.ts +8 -6
- package/src/translations.ts +1 -1
- package/src/types/NavTreeCapabilities.ts +1 -1
- package/src/types/NavTreeEvents.ts +1 -1
- package/src/types/schema.ts +2 -2
- package/dist/lib/neutral/CommandsDialogContent-UBGIQ5FI.mjs.map +0 -7
- package/dist/lib/neutral/chunk-5OPDKH6B.mjs.map +0 -7
- package/dist/lib/neutral/chunk-MDGKDQMZ.mjs +0 -37
- package/dist/lib/neutral/chunk-MDGKDQMZ.mjs.map +0 -7
- package/dist/lib/neutral/react-surface-Q5XMAG7Z.mjs.map +0 -7
- package/dist/lib/neutral/state-VVJEWUHM.mjs.map +0 -7
- /package/dist/lib/neutral/{NavTreeContainer-6AR4BFDD.mjs.map → NavTreeContainer-ICDKC2GI.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-T7QS2EFI.mjs.map → chunk-EAN4K6OL.mjs.map} +0 -0
- /package/dist/lib/neutral/{expose-26FV4W5D.mjs.map → expose-HIBA2NQG.mjs.map} +0 -0
package/dx.config.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Config2 } from '@dxos/app-framework/config';
|
|
6
|
+
import { trim } from '@dxos/util';
|
|
7
|
+
|
|
8
|
+
export default Config2.make({
|
|
9
|
+
plugin: {
|
|
10
|
+
key: 'org.dxos.plugin.navtree',
|
|
11
|
+
name: 'Navtree',
|
|
12
|
+
author: 'DXOS',
|
|
13
|
+
description: trim`
|
|
14
|
+
NavTreePlugin renders the application graph as a collapsible hierarchical tree in the Composer sidebar.
|
|
15
|
+
It manages workspace tabs at the root level and provides expand/collapse navigation for spaces, folders,
|
|
16
|
+
and objects, with open/current state persisted to localStorage across sessions.
|
|
17
|
+
|
|
18
|
+
The plugin wires drag-and-drop rearrangement and cross-parent transfer of tree items via Pragmatic DnD,
|
|
19
|
+
and registers graph-action keyboard shortcuts (including the ⌘K / Ctrl+K command palette) through a
|
|
20
|
+
global Keyboard singleton that tracks context as the user navigates.
|
|
21
|
+
|
|
22
|
+
An operation handler for LayoutOperation.Expose expands ancestor paths on demand so that any
|
|
23
|
+
programmatically activated item is scrolled into view in the tree.
|
|
24
|
+
|
|
25
|
+
A commands dialog surfaces all discoverable graph actions as a searchable palette, making it the primary
|
|
26
|
+
keyboard-first entry point for power users.
|
|
27
|
+
`,
|
|
28
|
+
spec: 'PLUGIN.mdl',
|
|
29
|
+
tags: ['system'],
|
|
30
|
+
},
|
|
31
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-navtree",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1-staging.ee54ba693a",
|
|
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",
|
|
@@ -91,60 +91,60 @@
|
|
|
91
91
|
"types": "dist/types/src/index.d.ts",
|
|
92
92
|
"files": [
|
|
93
93
|
"dist",
|
|
94
|
+
"dx.config.ts",
|
|
94
95
|
"src",
|
|
95
96
|
"PLUGIN.mdl"
|
|
96
97
|
],
|
|
97
98
|
"dependencies": {
|
|
98
99
|
"@atlaskit/pragmatic-drag-and-drop": "1.7.7",
|
|
99
100
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "1.1.0",
|
|
100
|
-
"@dxos/app-
|
|
101
|
-
"@dxos/app-
|
|
102
|
-
"@dxos/
|
|
103
|
-
"@dxos/
|
|
104
|
-
"@dxos/
|
|
105
|
-
"@dxos/
|
|
106
|
-
"@dxos/echo": "0.9.
|
|
107
|
-
"@dxos/keyboard": "0.9.
|
|
108
|
-
"@dxos/keys": "0.9.
|
|
109
|
-
"@dxos/lit-ui": "0.9.
|
|
110
|
-
"@dxos/log": "0.9.
|
|
111
|
-
"@dxos/plugin-deck": "0.9.
|
|
112
|
-
"@dxos/plugin-graph": "0.9.
|
|
113
|
-
"@dxos/random": "0.9.
|
|
114
|
-
"@dxos/react-ui-
|
|
115
|
-
"@dxos/react-ui-
|
|
116
|
-
"@dxos/react-ui-menu": "0.9.
|
|
117
|
-
"@dxos/react-ui-search": "0.9.
|
|
118
|
-
"@dxos/react-ui-tabs": "0.9.
|
|
119
|
-
"@dxos/
|
|
120
|
-
"@dxos/util": "0.9.0"
|
|
101
|
+
"@dxos/app-framework": "0.9.1-staging.ee54ba693a",
|
|
102
|
+
"@dxos/app-graph": "0.9.1-staging.ee54ba693a",
|
|
103
|
+
"@dxos/compute": "0.9.1-staging.ee54ba693a",
|
|
104
|
+
"@dxos/debug": "0.9.1-staging.ee54ba693a",
|
|
105
|
+
"@dxos/app-toolkit": "0.9.1-staging.ee54ba693a",
|
|
106
|
+
"@dxos/async": "0.9.1-staging.ee54ba693a",
|
|
107
|
+
"@dxos/echo": "0.9.1-staging.ee54ba693a",
|
|
108
|
+
"@dxos/keyboard": "0.9.1-staging.ee54ba693a",
|
|
109
|
+
"@dxos/keys": "0.9.1-staging.ee54ba693a",
|
|
110
|
+
"@dxos/lit-ui": "0.9.1-staging.ee54ba693a",
|
|
111
|
+
"@dxos/log": "0.9.1-staging.ee54ba693a",
|
|
112
|
+
"@dxos/plugin-deck": "0.9.1-staging.ee54ba693a",
|
|
113
|
+
"@dxos/plugin-graph": "0.9.1-staging.ee54ba693a",
|
|
114
|
+
"@dxos/random": "0.9.1-staging.ee54ba693a",
|
|
115
|
+
"@dxos/react-ui-list": "0.9.1-staging.ee54ba693a",
|
|
116
|
+
"@dxos/react-ui-attention": "0.9.1-staging.ee54ba693a",
|
|
117
|
+
"@dxos/react-ui-menu": "0.9.1-staging.ee54ba693a",
|
|
118
|
+
"@dxos/react-ui-search": "0.9.1-staging.ee54ba693a",
|
|
119
|
+
"@dxos/react-ui-tabs": "0.9.1-staging.ee54ba693a",
|
|
120
|
+
"@dxos/util": "0.9.1-staging.ee54ba693a"
|
|
121
121
|
},
|
|
122
122
|
"devDependencies": {
|
|
123
123
|
"@effect-atom/atom-react": "^0.5.0",
|
|
124
|
-
"@effect/platform": "0.96.
|
|
125
|
-
"@types/react": "~19.2.
|
|
124
|
+
"@effect/platform": "0.96.2",
|
|
125
|
+
"@types/react": "~19.2.17",
|
|
126
126
|
"@types/react-dom": "~19.2.3",
|
|
127
|
-
"effect": "3.21.
|
|
128
|
-
"react": "~19.2.
|
|
129
|
-
"react-dom": "~19.2.
|
|
127
|
+
"effect": "3.21.4",
|
|
128
|
+
"react": "~19.2.7",
|
|
129
|
+
"react-dom": "~19.2.7",
|
|
130
130
|
"vite": "^8.0.16",
|
|
131
|
-
"@dxos/plugin-attention": "0.9.
|
|
132
|
-
"@dxos/plugin-client": "0.9.
|
|
133
|
-
"@dxos/plugin-space": "0.9.
|
|
134
|
-
"@dxos/
|
|
135
|
-
"@dxos/react-ui": "0.9.
|
|
136
|
-
"@dxos/
|
|
137
|
-
"@dxos/
|
|
138
|
-
"@dxos/
|
|
131
|
+
"@dxos/plugin-attention": "0.9.1-staging.ee54ba693a",
|
|
132
|
+
"@dxos/plugin-client": "0.9.1-staging.ee54ba693a",
|
|
133
|
+
"@dxos/plugin-space": "0.9.1-staging.ee54ba693a",
|
|
134
|
+
"@dxos/storybook-utils": "0.9.1-staging.ee54ba693a",
|
|
135
|
+
"@dxos/react-ui": "0.9.1-staging.ee54ba693a",
|
|
136
|
+
"@dxos/ui-theme": "0.9.1-staging.ee54ba693a",
|
|
137
|
+
"@dxos/plugin-theme": "0.9.1-staging.ee54ba693a",
|
|
138
|
+
"@dxos/plugin-testing": "0.9.1-staging.ee54ba693a"
|
|
139
139
|
},
|
|
140
140
|
"peerDependencies": {
|
|
141
141
|
"@effect-atom/atom-react": "^0.5.0",
|
|
142
|
-
"@effect/platform": "0.96.
|
|
143
|
-
"effect": "3.21.
|
|
144
|
-
"react": "~19.2.
|
|
145
|
-
"react-dom": "~19.2.
|
|
146
|
-
"@dxos/
|
|
147
|
-
"@dxos/ui
|
|
142
|
+
"@effect/platform": "0.96.2",
|
|
143
|
+
"effect": "3.21.4",
|
|
144
|
+
"react": "~19.2.7",
|
|
145
|
+
"react-dom": "~19.2.7",
|
|
146
|
+
"@dxos/ui-theme": "0.9.1-staging.ee54ba693a",
|
|
147
|
+
"@dxos/react-ui": "0.9.1-staging.ee54ba693a"
|
|
148
148
|
},
|
|
149
149
|
"publishConfig": {
|
|
150
150
|
"access": "public"
|
|
@@ -11,7 +11,7 @@ import { NavTreePlugin } from '#plugin';
|
|
|
11
11
|
|
|
12
12
|
import { meta } from './meta';
|
|
13
13
|
|
|
14
|
-
const moduleId = (name: string) => `${meta.
|
|
14
|
+
const moduleId = (name: string) => `${meta.profile.key}.module.${name}`;
|
|
15
15
|
|
|
16
16
|
describe('NavTreePlugin', () => {
|
|
17
17
|
test('modules activate on the expected events', async ({ expect }) => {
|
package/src/NavTreePlugin.tsx
CHANGED
|
@@ -56,7 +56,7 @@ export const NavTreePlugin = Plugin.define(meta).pipe(
|
|
|
56
56
|
activate: Keyboard,
|
|
57
57
|
}),
|
|
58
58
|
AppPlugin.addPluginAssetModule({
|
|
59
|
-
asset: { pluginId: meta.
|
|
59
|
+
asset: { pluginId: meta.profile.key, path: 'PLUGIN.mdl', content: pluginSpec, mimeType: 'application/x-mdl' },
|
|
60
60
|
}),
|
|
61
61
|
Plugin.make,
|
|
62
62
|
);
|
|
@@ -26,7 +26,7 @@ export default Capability.makeModule(
|
|
|
26
26
|
blockAlign: 'start',
|
|
27
27
|
}),
|
|
28
28
|
properties: {
|
|
29
|
-
label: ['open-commands.label', { ns: meta.
|
|
29
|
+
label: ['open-commands.label', { ns: meta.profile.key }],
|
|
30
30
|
icon: 'ph--magnifying-glass--regular',
|
|
31
31
|
keyBinding: {
|
|
32
32
|
macos: 'meta+k',
|
|
@@ -46,11 +46,14 @@ export default Capability.makeModule(
|
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
const
|
|
49
|
+
const syncBindings = () => {
|
|
50
50
|
Graph.traverse(graph, { relation: ['child', 'action'], visitor });
|
|
51
|
-
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const eventHandler = debounce(syncBindings, 500);
|
|
52
54
|
|
|
53
55
|
const unsubscribe = graph.onNodeChanged.on(eventHandler);
|
|
56
|
+
syncBindings();
|
|
54
57
|
|
|
55
58
|
// TODO(burdon): Create context and plugin.
|
|
56
59
|
Keyboard.singleton.initialize();
|
|
@@ -9,6 +9,7 @@ import { Capabilities, Capability } from '@dxos/app-framework';
|
|
|
9
9
|
import { Surface } from '@dxos/app-framework/ui';
|
|
10
10
|
import { AppSurface } from '@dxos/app-toolkit/ui';
|
|
11
11
|
import { Node } from '@dxos/plugin-graph';
|
|
12
|
+
import { Position } from '@dxos/util';
|
|
12
13
|
|
|
13
14
|
import { CommandsDialogContent, CommandsTrigger, NavTreeContainer, NavTreeDocumentTitle } from '#containers';
|
|
14
15
|
import { COMMANDS_DIALOG } from '#meta';
|
|
@@ -23,29 +24,22 @@ export default Capability.makeModule(() =>
|
|
|
23
24
|
}),
|
|
24
25
|
Surface.create({
|
|
25
26
|
id: 'navigation',
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<NavTreeContainer
|
|
31
|
-
tab={data.current}
|
|
32
|
-
popoverAnchorId={data.popoverAnchorId as string | undefined}
|
|
33
|
-
ref={ref}
|
|
34
|
-
/>
|
|
35
|
-
);
|
|
36
|
-
},
|
|
27
|
+
filter: Surface.makeFilter(AppSurface.Navigation),
|
|
28
|
+
component: ({ data, ref }) => (
|
|
29
|
+
<NavTreeContainer tab={data.current} popoverAnchorId={data.popoverAnchorId} ref={ref} />
|
|
30
|
+
),
|
|
37
31
|
}),
|
|
38
32
|
Surface.create({
|
|
39
33
|
id: 'documentTitle',
|
|
40
|
-
|
|
34
|
+
filter: Surface.makeFilter(AppSurface.DocumentTitle),
|
|
41
35
|
component: ({ data }) => (
|
|
42
36
|
<NavTreeDocumentTitle node={Node.isGraphNode(data.subject) ? data.subject : undefined} />
|
|
43
37
|
),
|
|
44
38
|
}),
|
|
45
39
|
Surface.create({
|
|
46
40
|
id: 'searchInput',
|
|
47
|
-
|
|
48
|
-
position:
|
|
41
|
+
filter: Surface.makeFilter(AppSurface.SearchInput),
|
|
42
|
+
position: Position.last,
|
|
49
43
|
component: () => <CommandsTrigger />,
|
|
50
44
|
}),
|
|
51
45
|
]),
|
|
@@ -13,7 +13,7 @@ import { Path } from '@dxos/react-ui-list';
|
|
|
13
13
|
import { meta } from '#meta';
|
|
14
14
|
import { NavTreeCapabilities } from '#types';
|
|
15
15
|
|
|
16
|
-
const KEY = `${meta.
|
|
16
|
+
const KEY = `${meta.profile.key}.state.v1`;
|
|
17
17
|
|
|
18
18
|
/** Default item state for new entries. */
|
|
19
19
|
const defaultItemState: NavTreeCapabilities.NavTreeItemState = { open: false, current: false };
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { Atom, type Registry } from '@effect-atom/atom-react';
|
|
6
6
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
7
7
|
import * as Effect from 'effect/Effect';
|
|
8
|
-
import React, {
|
|
8
|
+
import React, { useEffect, useRef } from 'react';
|
|
9
9
|
import { expect, userEvent, within } from 'storybook/test';
|
|
10
10
|
|
|
11
11
|
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
@@ -15,9 +15,8 @@ import { AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';
|
|
|
15
15
|
import { Operation, OperationHandlerSet } from '@dxos/compute';
|
|
16
16
|
import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
|
|
17
17
|
import { random } from '@dxos/random';
|
|
18
|
-
import { IconButton, Input, Main, Toolbar } from '@dxos/react-ui';
|
|
18
|
+
import { Focus, IconButton, Input, Main, Panel, Toolbar } from '@dxos/react-ui';
|
|
19
19
|
import { useAttention, useAttentionAttributes } from '@dxos/react-ui-attention';
|
|
20
|
-
import { Stack, StackItem } from '@dxos/react-ui-stack';
|
|
21
20
|
import { withLayout } from '@dxos/react-ui/testing';
|
|
22
21
|
import { mx } from '@dxos/ui-theme';
|
|
23
22
|
|
|
@@ -35,9 +34,8 @@ const container = 'flex flex-col grow gap-2 p-4 rounded-md';
|
|
|
35
34
|
|
|
36
35
|
const StoryPlankHeading = ({ attendableId }: { attendableId: string }) => {
|
|
37
36
|
const { hasAttention } = useAttention(attendableId);
|
|
38
|
-
console.log('hasAttention', hasAttention);
|
|
39
37
|
return (
|
|
40
|
-
<
|
|
38
|
+
<Panel.Toolbar classNames='border-b border-separator'>
|
|
41
39
|
<IconButton
|
|
42
40
|
density='lg'
|
|
43
41
|
icon='ph--circle--regular'
|
|
@@ -46,8 +44,7 @@ const StoryPlankHeading = ({ attendableId }: { attendableId: string }) => {
|
|
|
46
44
|
variant={hasAttention ? 'primary' : 'ghost'}
|
|
47
45
|
classNames='w-(--dx-rail-action) h-(--dx-rail-action)'
|
|
48
46
|
/>
|
|
49
|
-
|
|
50
|
-
</div>
|
|
47
|
+
</Panel.Toolbar>
|
|
51
48
|
);
|
|
52
49
|
};
|
|
53
50
|
|
|
@@ -57,40 +54,50 @@ const StoryPlank = ({ attendableId }: { attendableId: string }) => {
|
|
|
57
54
|
|
|
58
55
|
// NOTE(thure): This is the same workaround as in Plank, but that component is out of scope for this story.
|
|
59
56
|
// TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
// Attached imperatively because `Focus.Item`/`Panel.Root` expose a narrow (slottable) prop surface.
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const element = rootElement.current;
|
|
60
|
+
if (!element) {
|
|
61
|
+
return;
|
|
63
62
|
}
|
|
63
|
+
|
|
64
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
65
|
+
if (event.target === element && event.key === 'Escape') {
|
|
66
|
+
element.closest('main')?.focus();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
element.addEventListener('keydown', handleKeyDown);
|
|
71
|
+
return () => element.removeEventListener('keydown', handleKeyDown);
|
|
64
72
|
}, []);
|
|
65
73
|
|
|
66
74
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<div className={mx(container, 'm-2 bg-current-surface')}>
|
|
82
|
-
<Input.Root>
|
|
83
|
-
<Input.Label>Level 1 (group)</Input.Label>
|
|
84
|
-
</Input.Root>
|
|
85
|
-
<div className={mx(container, 'bg-base-surface')}>
|
|
75
|
+
<Focus.Item asChild ref={rootElement}>
|
|
76
|
+
<Panel.Root
|
|
77
|
+
{...attentionAttrs}
|
|
78
|
+
role='article'
|
|
79
|
+
classNames='is-[30rem] shrink-0 bs-full bg-base-surface border-e border-separator'
|
|
80
|
+
>
|
|
81
|
+
<StoryPlankHeading attendableId={attendableId} />
|
|
82
|
+
<Panel.Content classNames='grid'>
|
|
83
|
+
<Toolbar.Root classNames='border-b border-subdued-separator'>
|
|
84
|
+
<Toolbar.Button>Test</Toolbar.Button>
|
|
85
|
+
</Toolbar.Root>
|
|
86
|
+
|
|
87
|
+
<div className={mx(container, 'm-2 bg-current-surface')}>
|
|
86
88
|
<Input.Root>
|
|
87
|
-
<Input.Label>Level
|
|
88
|
-
<Input.TextArea placeholder='Enter text' />
|
|
89
|
+
<Input.Label>Level 1 (group)</Input.Label>
|
|
89
90
|
</Input.Root>
|
|
91
|
+
<div className={mx(container, 'bg-base-surface')}>
|
|
92
|
+
<Input.Root>
|
|
93
|
+
<Input.Label>Level 2 (base)</Input.Label>
|
|
94
|
+
<Input.TextArea placeholder='Enter text' />
|
|
95
|
+
</Input.Root>
|
|
96
|
+
</div>
|
|
90
97
|
</div>
|
|
91
|
-
</
|
|
92
|
-
</
|
|
93
|
-
</
|
|
98
|
+
</Panel.Content>
|
|
99
|
+
</Panel.Root>
|
|
100
|
+
</Focus.Item>
|
|
94
101
|
);
|
|
95
102
|
};
|
|
96
103
|
|
|
@@ -103,10 +110,10 @@ const DefaultStory = () => {
|
|
|
103
110
|
<NavTreeContainer tab={state.tab} />
|
|
104
111
|
</Main.NavigationSidebar>
|
|
105
112
|
<Main.Content bounce handlesFocus>
|
|
106
|
-
<
|
|
113
|
+
<div role='none' className='flex grow overflow-x-auto'>
|
|
107
114
|
<StoryPlank attendableId='space-0:object-0' />
|
|
108
115
|
<StoryPlank attendableId='space-0:object-1' />
|
|
109
|
-
</
|
|
116
|
+
</div>
|
|
110
117
|
</Main.Content>
|
|
111
118
|
</Main.Root>
|
|
112
119
|
);
|
|
@@ -6,8 +6,9 @@ import React, { forwardRef, useMemo } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
8
8
|
import { Node, useConnections, useActions as useGraphActions } from '@dxos/plugin-graph';
|
|
9
|
+
import { type MenuItem } from '@dxos/react-ui-menu';
|
|
9
10
|
import { Tabs } from '@dxos/react-ui-tabs';
|
|
10
|
-
import {
|
|
11
|
+
import { Position } from '@dxos/util';
|
|
11
12
|
|
|
12
13
|
import { useLoadDescendents } from '#hooks';
|
|
13
14
|
|
|
@@ -33,7 +34,7 @@ export const NavTree = forwardRef<HTMLDivElement, NavTreeProps>(({ id, root, tab
|
|
|
33
34
|
// not use `react-ui-tabs` at all.
|
|
34
35
|
<Tabs.Root value={tab} orientation='vertical' classNames='relative' ref={forwardedRef}>
|
|
35
36
|
<L0Menu
|
|
36
|
-
menuActions={topLevelActions}
|
|
37
|
+
menuActions={topLevelActions as MenuItem[]}
|
|
37
38
|
topLevelItems={l0Items}
|
|
38
39
|
pinnedItems={pinnedItems}
|
|
39
40
|
userAccountItem={userAccountItem}
|
|
@@ -75,11 +76,11 @@ const useTopLevelNavItems = (root?: Node.Node) => {
|
|
|
75
76
|
|
|
76
77
|
const topLevelActions = rootActions
|
|
77
78
|
.filter((action) => action.properties.disposition === 'menu')
|
|
78
|
-
.toSorted((a, b) =>
|
|
79
|
+
.toSorted((a, b) => Position.compare(a.properties, b.properties));
|
|
79
80
|
const pinnedItems = [
|
|
80
81
|
...outboundPinnedItems,
|
|
81
82
|
...rootActions.filter((action) => action.properties.disposition === 'pin-end'),
|
|
82
|
-
].toSorted((a, b) =>
|
|
83
|
+
].toSorted((a, b) => Position.compare(a.properties, b.properties));
|
|
83
84
|
|
|
84
85
|
return {
|
|
85
86
|
pinnedItems,
|
|
@@ -36,7 +36,7 @@ export type NavTreeItemActionMenuProps = ActionProperties & {
|
|
|
36
36
|
|
|
37
37
|
export const NavTreeItemActionDropdownMenu = composable<HTMLButtonElement, NavTreeItemActionMenuProps>(
|
|
38
38
|
({ parent, path, label, icon, testId, menuActions, caller, ...props }, forwardedRef) => {
|
|
39
|
-
const { t } = useTranslation(meta.
|
|
39
|
+
const { t } = useTranslation(meta.profile.key);
|
|
40
40
|
const density = useDensityContext();
|
|
41
41
|
const runAction = useActionRunner();
|
|
42
42
|
const handleAction = useCallback(
|
|
@@ -118,7 +118,7 @@ export const NavTreeItemAction = ({
|
|
|
118
118
|
path,
|
|
119
119
|
...props
|
|
120
120
|
}: NavTreeItemActionMenuProps) => {
|
|
121
|
-
const { t } = useTranslation(meta.
|
|
121
|
+
const { t } = useTranslation(meta.profile.key);
|
|
122
122
|
|
|
123
123
|
const monolithicAction = menuActions?.length === 1 && menuActions[0];
|
|
124
124
|
const baseLabel = toLocalizedString(monolithicAction ? monolithicAction.properties!.label : props.label, t);
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { Fragment, memo, useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import { Popover,
|
|
7
|
+
import { Popover, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
8
|
+
import { Treegrid } from '@dxos/react-ui-list';
|
|
8
9
|
|
|
9
10
|
import { getListActions, useActions } from '#hooks';
|
|
10
11
|
import { meta } from '#meta';
|
|
@@ -15,17 +16,19 @@ import { type NavTreeItemColumnsProps } from '../types';
|
|
|
15
16
|
import { NavTreeItemActionDropdownMenu, NavTreeItemMonolithicAction } from './NavTreeItemAction';
|
|
16
17
|
|
|
17
18
|
export const NavTreeItemColumns = memo(({ path, item, open }: NavTreeItemColumnsProps) => {
|
|
18
|
-
const { t } = useTranslation(meta.
|
|
19
|
+
const { t } = useTranslation(meta.profile.key);
|
|
19
20
|
const { renderItemEnd: ItemEnd, popoverAnchorId } = useNavTreeContext();
|
|
20
21
|
|
|
21
22
|
const level = path.length - 2;
|
|
22
23
|
const flattenedActions = useActions(item);
|
|
23
24
|
const allActions = useMemo(() => getListActions(flattenedActions), [flattenedActions]);
|
|
24
25
|
|
|
25
|
-
const
|
|
26
|
+
const anchored = popoverAnchorId === `${NAV_TREE_ITEM}:${item.id}`;
|
|
27
|
+
const ActionRoot = anchored ? Popover.Anchor : Fragment;
|
|
26
28
|
|
|
27
29
|
return (
|
|
28
|
-
|
|
30
|
+
// `data-popover-anchor` lets the enclosing row highlight itself while a popover (e.g. rename) is open on it.
|
|
31
|
+
<div className='contents dx-app-no-drag' {...(anchored && { 'data-popover-anchor': '' })}>
|
|
29
32
|
<ActionRoot>
|
|
30
33
|
{allActions.length === 1 ? (
|
|
31
34
|
<Treegrid.Cell classNames='contents'>
|
|
@@ -30,7 +30,6 @@ import { useActionRunner } from '@dxos/plugin-graph';
|
|
|
30
30
|
import {
|
|
31
31
|
Icon,
|
|
32
32
|
IconButton,
|
|
33
|
-
ListItem,
|
|
34
33
|
ScrollArea,
|
|
35
34
|
type ThemedClassName,
|
|
36
35
|
Tooltip,
|
|
@@ -38,8 +37,8 @@ import {
|
|
|
38
37
|
useMediaQuery,
|
|
39
38
|
useTranslation,
|
|
40
39
|
} from '@dxos/react-ui';
|
|
40
|
+
import { DropIndicator } from '@dxos/react-ui-list';
|
|
41
41
|
import { Menu, type MenuItem } from '@dxos/react-ui-menu';
|
|
42
|
-
import type { StackItemRearrangeHandler } from '@dxos/react-ui-stack';
|
|
43
42
|
import { Tabs } from '@dxos/react-ui-tabs';
|
|
44
43
|
import { mx } from '@dxos/ui-theme';
|
|
45
44
|
import { arrayMove } from '@dxos/util';
|
|
@@ -60,6 +59,14 @@ type L0ItemData = {
|
|
|
60
59
|
type: 'l0Item';
|
|
61
60
|
};
|
|
62
61
|
|
|
62
|
+
// Local rearrange-handler callback shape; drag-and-drop is wired directly via pragmatic-dnd in `L0Item`,
|
|
63
|
+
// so only the callback signature is needed here.
|
|
64
|
+
type StackItemRearrangeHandler<Data extends { id: string } = { id: string }> = (
|
|
65
|
+
source: Data,
|
|
66
|
+
target: Data,
|
|
67
|
+
closestEdge: Edge | null,
|
|
68
|
+
) => void;
|
|
69
|
+
|
|
63
70
|
type L0ItemRootProps = {
|
|
64
71
|
item: Node.Node;
|
|
65
72
|
parent?: Node.Node;
|
|
@@ -106,7 +113,7 @@ const l0Breakpoints: Record<string, string> = {
|
|
|
106
113
|
const L0ItemRoot = memo(
|
|
107
114
|
forwardRef<HTMLButtonElement, PropsWithChildren<L0ItemRootProps>>(
|
|
108
115
|
({ item, parent, path, onMouseEnter, children }, forwardedRef) => {
|
|
109
|
-
const { t } = useTranslation(meta.
|
|
116
|
+
const { t } = useTranslation(meta.profile.key);
|
|
110
117
|
const { model } = useNavTreeContext();
|
|
111
118
|
const itemPath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
112
119
|
const { id, testId } = useAtomValue(model.itemProps(itemPath));
|
|
@@ -151,7 +158,7 @@ export const L0ItemActiveTabIndicator = ({ classNames }: ThemedClassName<{}>) =>
|
|
|
151
158
|
|
|
152
159
|
// TODO(burdon): Factor out pinned (non-draggable) items.
|
|
153
160
|
const L0Item = memo(({ item, parent, path, pinned, onRearrange, onItemHover }: L0ItemProps) => {
|
|
154
|
-
const { t } = useTranslation(meta.
|
|
161
|
+
const { t } = useTranslation(meta.profile.key);
|
|
155
162
|
const itemElement = useRef<HTMLButtonElement | null>(null);
|
|
156
163
|
const [closestEdge, setEdge] = useState<Edge | null>(null);
|
|
157
164
|
const localizedString = toLocalizedString(item.properties.label, t);
|
|
@@ -232,13 +239,13 @@ const L0Item = memo(({ item, parent, path, pinned, onRearrange, onItemHover }: L
|
|
|
232
239
|
<span id={`${item.id}__label`} className='sr-only'>
|
|
233
240
|
{localizedString}
|
|
234
241
|
</span>
|
|
235
|
-
{closestEdge && <
|
|
242
|
+
{closestEdge && <DropIndicator edge={closestEdge} />}
|
|
236
243
|
</L0ItemRoot>
|
|
237
244
|
);
|
|
238
245
|
});
|
|
239
246
|
|
|
240
247
|
const ItemAvatar = ({ item }: Pick<L0ItemProps, 'item'>) => {
|
|
241
|
-
const { t } = useTranslation(meta.
|
|
248
|
+
const { t } = useTranslation(meta.profile.key);
|
|
242
249
|
|
|
243
250
|
// Actions.
|
|
244
251
|
if (item.properties.icon) {
|
|
@@ -280,7 +287,7 @@ export const L0Menu = ({
|
|
|
280
287
|
path,
|
|
281
288
|
onItemHover,
|
|
282
289
|
}: L0MenuProps) => {
|
|
283
|
-
const { t } = useTranslation(meta.
|
|
290
|
+
const { t } = useTranslation(meta.profile.key);
|
|
284
291
|
const runAction = useActionRunner();
|
|
285
292
|
const handleAction = useCallback(
|
|
286
293
|
(action: Node.Action, params: Node.InvokeProps) => {
|
|
@@ -6,7 +6,7 @@ import * as Option from 'effect/Option';
|
|
|
6
6
|
import React, { memo, useCallback, useMemo } from 'react';
|
|
7
7
|
|
|
8
8
|
import { Node } from '@dxos/app-graph';
|
|
9
|
-
import {
|
|
9
|
+
import { Paths } from '@dxos/app-toolkit';
|
|
10
10
|
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
11
11
|
import { Graph, useActionRunner, useEdges } from '@dxos/plugin-graph';
|
|
12
12
|
import { DensityProvider, IconButton, ScrollArea, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
@@ -34,7 +34,7 @@ export type L1PanelProps = {
|
|
|
34
34
|
* Space or settings panel.
|
|
35
35
|
*/
|
|
36
36
|
const L1Panel$ = ({ open, path, item, isCurrent, onBack }: L1PanelProps) => {
|
|
37
|
-
const { t } = useTranslation(meta.
|
|
37
|
+
const { t } = useTranslation(meta.profile.key);
|
|
38
38
|
const title = toLocalizedString(item.properties.label, t);
|
|
39
39
|
const isActivated = useIsActivatedWorkspace(item);
|
|
40
40
|
const shouldRenderContent = isCurrent || isActivated;
|
|
@@ -114,10 +114,10 @@ const L1PanelContent = ({ path, item, onBack }: Pick<L1PanelProps, 'open' | 'pat
|
|
|
114
114
|
* Header row.
|
|
115
115
|
*/
|
|
116
116
|
const L1PanelHeader = ({ item, path, onBack }: Pick<L1PanelProps, 'item' | 'path' | 'onBack'>) => {
|
|
117
|
-
const { t } = useTranslation(meta.
|
|
117
|
+
const { t } = useTranslation(meta.profile.key);
|
|
118
118
|
const { renderItemEnd: ItemEnd } = useNavTreeContext();
|
|
119
119
|
const title = toLocalizedString(item.properties.label, t);
|
|
120
|
-
const backCapableWorkspace = isPinnedWorkspace(item.id);
|
|
120
|
+
const backCapableWorkspace = Paths.isPinnedWorkspace(item.id);
|
|
121
121
|
|
|
122
122
|
const { menuActions, onAction } = useL1MenuActions({ item, path });
|
|
123
123
|
useLoadDescendents(item);
|
|
@@ -169,7 +169,7 @@ const MenuActions = ({
|
|
|
169
169
|
}: {
|
|
170
170
|
item: Node.Node;
|
|
171
171
|
} & Pick<L1MenuActions, 'menuActions' | 'onAction'>) => {
|
|
172
|
-
const { t } = useTranslation(meta.
|
|
172
|
+
const { t } = useTranslation(meta.profile.key);
|
|
173
173
|
|
|
174
174
|
if (menuActions.length === 0) {
|
|
175
175
|
return null;
|
|
@@ -24,7 +24,7 @@ export type CommandsDialogContentProps = {
|
|
|
24
24
|
// TODO(wittjosiah): This probably deserves its own plugin but for now it lives here w/ other navigation UI.
|
|
25
25
|
export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogContentProps>(
|
|
26
26
|
({ selected: initial }, forwardedRef) => {
|
|
27
|
-
const { t } = useTranslation(meta.
|
|
27
|
+
const { t } = useTranslation(meta.profile.key);
|
|
28
28
|
const { invokePromise } = useOperationInvoker();
|
|
29
29
|
const runAction = useActionRunner();
|
|
30
30
|
const { graph } = useAppGraph();
|
|
@@ -70,7 +70,7 @@ export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogCo
|
|
|
70
70
|
|
|
71
71
|
return (
|
|
72
72
|
<Dialog.Content ref={forwardedRef}>
|
|
73
|
-
<Dialog.Title srOnly>{t('commands-dialog.title', { ns: meta.
|
|
73
|
+
<Dialog.Title srOnly>{t('commands-dialog.title', { ns: meta.profile.key })}</Dialog.Title>
|
|
74
74
|
<SearchList.Root onSearch={handleSearch}>
|
|
75
75
|
<SearchList.Input placeholder={t('command-list-input.placeholder')} />
|
|
76
76
|
<SearchList.Viewport>
|
|
@@ -13,7 +13,7 @@ import { COMMANDS_DIALOG, meta } from '#meta';
|
|
|
13
13
|
// TODO(thure): Refactor to be handled by a more appropriate plugin.
|
|
14
14
|
export const CommandsTrigger = () => {
|
|
15
15
|
const { invokePromise } = useOperationInvoker();
|
|
16
|
-
const { t } = useTranslation(meta.
|
|
16
|
+
const { t } = useTranslation(meta.profile.key);
|
|
17
17
|
return (
|
|
18
18
|
<Button
|
|
19
19
|
classNames='m-1 px-1 lg:px-2'
|