@dxos/plugin-navtree 0.8.4-main.67995b8 → 0.8.4-main.70d3990
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-5J4LRWDX.mjs → app-graph-builder-OHSCN3GX.mjs} +13 -12
- package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs.map +7 -0
- package/dist/lib/browser/{chunk-WCARAZ7J.mjs → chunk-5VLWX4PK.mjs} +4 -4
- package/dist/lib/browser/chunk-5VLWX4PK.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/{chunk-WEMCSQI7.mjs → chunk-UDAINHB6.mjs} +195 -176
- package/dist/lib/browser/chunk-UDAINHB6.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +6 -10
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-3XYN2CBU.mjs → intent-resolver-YUVXVPHG.mjs} +7 -7
- package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +7 -0
- package/dist/lib/browser/{keyboard-QRS6WOQH.mjs → keyboard-YJEK2HTH.mjs} +8 -10
- package/dist/lib/browser/{keyboard-QRS6WOQH.mjs.map → keyboard-YJEK2HTH.mjs.map} +2 -2
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-JSJ5SQX5.mjs → react-surface-V6LS3R4L.mjs} +10 -10
- package/dist/lib/browser/react-surface-V6LS3R4L.mjs.map +7 -0
- package/dist/lib/browser/{state-54DY3DOY.mjs → state-BFD5KJCV.mjs} +7 -7
- package/dist/lib/browser/state-BFD5KJCV.mjs.map +7 -0
- package/dist/types/src/NavTreePlugin.d.ts +1 -1
- package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +18 -15
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +2 -2
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
- package/dist/types/src/capabilities/keyboard.d.ts +1 -1
- package/dist/types/src/capabilities/keyboard.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/state.d.ts +3 -3
- package/dist/types/src/capabilities/state.d.ts.map +1 -1
- package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +1 -1
- package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts +26 -4
- 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/NavTreeItemAction.stories.d.ts +32 -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 +2 -0
- package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
- 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/types.d.ts +3 -3
- package/dist/types/src/components/types.d.ts.map +1 -1
- package/dist/types/src/events.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.d.ts +1 -2
- package/dist/types/src/experimental/Tree.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.stories.d.ts +7 -4
- package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +2 -3
- 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 +2 -2
- 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/src/util.d.ts +1 -2
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +50 -50
- package/src/NavTreePlugin.tsx +75 -76
- package/src/capabilities/app-graph-builder.ts +10 -9
- package/src/capabilities/capabilities.ts +2 -2
- package/src/capabilities/intent-resolver.ts +2 -2
- package/src/capabilities/keyboard.ts +3 -5
- package/src/capabilities/react-surface.tsx +5 -5
- package/src/capabilities/state.ts +5 -5
- package/src/components/CommandsDialog/CommandsDialog.tsx +15 -7
- package/src/components/CommandsDialog/CommandsTrigger.tsx +4 -3
- package/src/components/NavTree/NavTree.stories.tsx +67 -53
- package/src/components/NavTree/NavTree.tsx +4 -0
- package/src/components/NavTreeContainer.tsx +120 -55
- package/src/components/NavTreeDocumentTitle.tsx +2 -2
- package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +4 -6
- package/src/components/NavTreeItem/NavTreeItemAction.tsx +7 -6
- package/src/components/NavTreeItem/NavTreeItemColumns.tsx +31 -24
- package/src/components/Sidebar/L0Menu.tsx +56 -55
- package/src/components/Sidebar/L1Panel.tsx +23 -28
- package/src/components/Sidebar/L1Tabs.tsx +1 -0
- package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +9 -8
- package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +20 -12
- package/src/components/types.ts +4 -3
- package/src/events.ts +2 -2
- package/src/experimental/Tree.stories.tsx +28 -30
- package/src/experimental/Tree.tsx +6 -23
- package/src/meta.ts +10 -7
- package/src/testing/app-graph-builder.ts +20 -19
- package/src/testing/generator.ts +10 -8
- package/src/types.ts +4 -3
- package/src/util.ts +2 -20
- package/dist/lib/browser/app-graph-builder-5J4LRWDX.mjs.map +0 -7
- package/dist/lib/browser/chunk-ERDGQTH5.mjs +0 -16
- package/dist/lib/browser/chunk-ERDGQTH5.mjs.map +0 -7
- package/dist/lib/browser/chunk-WCARAZ7J.mjs.map +0 -7
- package/dist/lib/browser/chunk-WEMCSQI7.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-3XYN2CBU.mjs.map +0 -7
- package/dist/lib/browser/react-surface-JSJ5SQX5.mjs.map +0 -7
- package/dist/lib/browser/state-54DY3DOY.mjs.map +0 -7
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@
|
|
6
|
-
|
|
7
|
-
import { type
|
|
8
|
-
import {
|
|
9
|
-
import React from 'react';
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
import React, { type KeyboardEvent, useCallback, useRef } from 'react';
|
|
8
|
+
import { expect, userEvent, within } from 'storybook/test';
|
|
10
9
|
|
|
11
10
|
import {
|
|
12
11
|
Capabilities,
|
|
@@ -16,22 +15,20 @@ import {
|
|
|
16
15
|
contributes,
|
|
17
16
|
createResolver,
|
|
18
17
|
defineCapability,
|
|
19
|
-
useCapability,
|
|
20
18
|
} from '@dxos/app-framework';
|
|
19
|
+
import { useCapability } from '@dxos/app-framework/react';
|
|
21
20
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
22
21
|
import { live } from '@dxos/live-object';
|
|
23
22
|
import { AttentionPlugin } from '@dxos/plugin-attention';
|
|
24
|
-
import { ClientPlugin } from '@dxos/plugin-client';
|
|
25
23
|
import { GraphPlugin } from '@dxos/plugin-graph';
|
|
26
|
-
import { SpacePlugin } from '@dxos/plugin-space';
|
|
27
24
|
import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
|
|
28
25
|
import { ThemePlugin } from '@dxos/plugin-theme';
|
|
29
26
|
import { faker } from '@dxos/random';
|
|
30
27
|
import { IconButton, Input, Main, Toolbar } from '@dxos/react-ui';
|
|
31
|
-
import {
|
|
28
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
29
|
+
import { useAttention, useAttentionAttributes } from '@dxos/react-ui-attention';
|
|
32
30
|
import { Stack, StackItem } from '@dxos/react-ui-stack';
|
|
33
31
|
import { defaultTx, mx } from '@dxos/react-ui-theme';
|
|
34
|
-
import { withLayout } from '@dxos/storybook-utils';
|
|
35
32
|
|
|
36
33
|
import { NavTreePlugin } from '../../NavTreePlugin';
|
|
37
34
|
import { storybookGraphBuilders } from '../../testing';
|
|
@@ -68,6 +65,15 @@ const StoryPlankHeading = ({ attendableId }: { attendableId: string }) => {
|
|
|
68
65
|
|
|
69
66
|
const StoryPlank = ({ attendableId }: { attendableId: string }) => {
|
|
70
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
|
+
}, []);
|
|
71
77
|
|
|
72
78
|
return (
|
|
73
79
|
<StackItem.Root
|
|
@@ -75,10 +81,12 @@ const StoryPlank = ({ attendableId }: { attendableId: string }) => {
|
|
|
75
81
|
{...attentionAttrs}
|
|
76
82
|
classNames='bg-baseSurface border-ie border-separator'
|
|
77
83
|
size={30}
|
|
84
|
+
onKeyDown={handleKeyDown}
|
|
85
|
+
ref={rootElement}
|
|
78
86
|
>
|
|
79
87
|
<StoryPlankHeading attendableId={attendableId} />
|
|
80
88
|
<StackItem.Content toolbar>
|
|
81
|
-
<Toolbar.Root classNames='border-
|
|
89
|
+
<Toolbar.Root classNames='border-be border-subduedSeparator'>
|
|
82
90
|
<Toolbar.Button>Test</Toolbar.Button>
|
|
83
91
|
</Toolbar.Root>
|
|
84
92
|
|
|
@@ -116,20 +124,21 @@ const DefaultStory = () => {
|
|
|
116
124
|
);
|
|
117
125
|
};
|
|
118
126
|
|
|
119
|
-
const meta
|
|
127
|
+
const meta = {
|
|
120
128
|
title: 'plugins/plugin-navtree/NavTree',
|
|
121
129
|
component: NavTreeContainer,
|
|
122
130
|
render: DefaultStory,
|
|
123
131
|
decorators: [
|
|
132
|
+
withTheme,
|
|
124
133
|
withPluginManager({
|
|
125
134
|
plugins: [
|
|
126
135
|
ThemePlugin({ tx: defaultTx }),
|
|
127
|
-
StorybookLayoutPlugin({ initialState: { sidebarState: 'expanded' } }),
|
|
128
136
|
GraphPlugin(),
|
|
129
137
|
IntentPlugin(),
|
|
130
138
|
SettingsPlugin(),
|
|
131
139
|
AttentionPlugin(),
|
|
132
140
|
NavTreePlugin(),
|
|
141
|
+
StorybookLayoutPlugin({ initialState: { sidebarState: 'expanded' } }),
|
|
133
142
|
],
|
|
134
143
|
capabilities: (context) => [
|
|
135
144
|
contributes(StoryState, live({ tab: 'space-0' })),
|
|
@@ -147,54 +156,59 @@ const meta: Meta<typeof NavTreeContainer> = {
|
|
|
147
156
|
]),
|
|
148
157
|
],
|
|
149
158
|
}),
|
|
150
|
-
withLayout({ fullscreen: true }),
|
|
151
159
|
],
|
|
152
160
|
parameters: {
|
|
161
|
+
layout: 'fullscreen',
|
|
153
162
|
translations,
|
|
154
163
|
},
|
|
155
|
-
}
|
|
164
|
+
} satisfies Meta<typeof NavTreeContainer>;
|
|
156
165
|
|
|
157
166
|
export default meta;
|
|
158
167
|
|
|
159
168
|
type Story = StoryObj<typeof NavTreeContainer>;
|
|
160
169
|
|
|
161
|
-
export const Default: Story = {
|
|
170
|
+
export const Default: Story = {
|
|
171
|
+
play: async ({ canvasElement }) => {
|
|
172
|
+
const canvas = within(canvasElement);
|
|
162
173
|
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
+
}
|
|
180
|
+
|
|
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');
|
|
213
|
+
},
|
|
200
214
|
};
|
|
@@ -47,6 +47,10 @@ export const NavTree = ({ id, root, ...props }: NavTreeProps) => {
|
|
|
47
47
|
const path = useMemo(() => [id], [id]);
|
|
48
48
|
|
|
49
49
|
return (
|
|
50
|
+
// TODO(thure): `Tabs.Root` forces all items that should be able to receive focus to use `Tabs.Tab(Primitive)` since
|
|
51
|
+
// it uses RovingFocus and doesn’t support moving focus to an item that is not a tab. Assess whether this situation
|
|
52
|
+
// should change including whether it should motivate a change in the design/taxonomy, or if this means this should
|
|
53
|
+
// not use `react-ui-tabs` at all.
|
|
50
54
|
<Tabs.Root value={tab} orientation='vertical' verticalVariant='stateless' classNames='relative'>
|
|
51
55
|
<L0Menu
|
|
52
56
|
menuActions={topLevelActions}
|
|
@@ -3,34 +3,28 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
|
6
|
-
import {
|
|
6
|
+
import { type Instruction, extractInstruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
|
|
7
7
|
import { untracked } from '@preact/signals-core';
|
|
8
8
|
import React, { memo, useCallback, useEffect, useMemo } from 'react';
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Surface,
|
|
14
|
-
useAppGraph,
|
|
15
|
-
useCapability,
|
|
16
|
-
useIntentDispatcher,
|
|
17
|
-
useLayout,
|
|
18
|
-
} from '@dxos/app-framework';
|
|
19
|
-
import { isAction, isActionLike, ROOT_ID, type Node, type ReadableGraph } from '@dxos/app-graph';
|
|
10
|
+
import { LayoutAction, createIntent } from '@dxos/app-framework';
|
|
11
|
+
import { Surface, useAppGraph, useCapability, useIntentDispatcher, useLayout } from '@dxos/app-framework/react';
|
|
12
|
+
import { type Node, ROOT_ID, type ReadableGraph, isAction, isActionLike } from '@dxos/app-graph';
|
|
20
13
|
import { PLANK_COMPANION_TYPE } from '@dxos/plugin-deck/types';
|
|
21
|
-
import { useConnections } from '@dxos/plugin-graph';
|
|
14
|
+
import { useConnections, useActions as useGraphActions } from '@dxos/plugin-graph';
|
|
22
15
|
import { useMediaQuery, useSidebars } from '@dxos/react-ui';
|
|
23
|
-
import {
|
|
16
|
+
import { type TreeData, type TreeItemDataProps, isTreeData } from '@dxos/react-ui-list';
|
|
24
17
|
import { mx } from '@dxos/react-ui-theme';
|
|
25
18
|
import { arrayMove, byPosition } from '@dxos/util';
|
|
26
19
|
|
|
20
|
+
import { NavTreeCapabilities } from '../capabilities';
|
|
21
|
+
import { meta } from '../meta';
|
|
22
|
+
import { type FlattenedActions, type NavTreeItemGraphNode } from '../types';
|
|
23
|
+
import { getChildren, getParent, resolveMigrationOperation } from '../util';
|
|
24
|
+
|
|
27
25
|
import { NAV_TREE_ITEM, NavTree } from './NavTree';
|
|
28
26
|
import { NavTreeContext } from './NavTreeContext';
|
|
29
27
|
import { type NavTreeContextValue } from './types';
|
|
30
|
-
import { NavTreeCapabilities } from '../capabilities';
|
|
31
|
-
import { NAVTREE_PLUGIN } from '../meta';
|
|
32
|
-
import { type NavTreeItemGraphNode } from '../types';
|
|
33
|
-
import { getChildren, getParent, getActions as naturalGetActions, resolveMigrationOperation } from '../util';
|
|
34
28
|
|
|
35
29
|
// TODO(thure): Is NavTree truly authoritative in this regard?
|
|
36
30
|
export const NODE_TYPE = 'dxos/app-graph/node';
|
|
@@ -40,7 +34,9 @@ const renderItemEnd = ({ node, open }: { node: Node; open: boolean }) => (
|
|
|
40
34
|
);
|
|
41
35
|
|
|
42
36
|
const getChildrenFilter = (node: Node): node is Node =>
|
|
43
|
-
untracked(
|
|
37
|
+
untracked(
|
|
38
|
+
() => !isActionLike(node) && node.type !== PLANK_COMPANION_TYPE && node.properties.disposition !== 'hidden',
|
|
39
|
+
);
|
|
44
40
|
|
|
45
41
|
const filterItems = (node: Node, disposition?: string) => {
|
|
46
42
|
if (!disposition && (node.properties.disposition === 'hidden' || node.properties.disposition === 'alternate-tree')) {
|
|
@@ -64,20 +60,43 @@ const useItems = (node?: Node, options?: { disposition?: string; sort?: boolean
|
|
|
64
60
|
return options?.sort ? filtered.toSorted((a, b) => byPosition(a.properties, b.properties)) : filtered;
|
|
65
61
|
};
|
|
66
62
|
|
|
63
|
+
const useActions = (node: Node): FlattenedActions => {
|
|
64
|
+
const { graph } = useAppGraph();
|
|
65
|
+
const actions = useGraphActions(graph, node.id);
|
|
66
|
+
|
|
67
|
+
return useMemo(
|
|
68
|
+
() =>
|
|
69
|
+
actions.reduce(
|
|
70
|
+
(acc: FlattenedActions, arg) => {
|
|
71
|
+
if (arg.properties.disposition === 'item') {
|
|
72
|
+
return acc;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
acc.actions.push(arg);
|
|
76
|
+
if (!isAction(arg)) {
|
|
77
|
+
const actionGroup = graph.getActions(arg.id);
|
|
78
|
+
acc.groupedActions[arg.id] = actionGroup;
|
|
79
|
+
}
|
|
80
|
+
return acc;
|
|
81
|
+
},
|
|
82
|
+
{ actions: [], groupedActions: {} },
|
|
83
|
+
),
|
|
84
|
+
[actions],
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
67
88
|
export type NavTreeContainerProps = {
|
|
68
89
|
popoverAnchorId?: string;
|
|
69
90
|
topbar?: boolean;
|
|
70
91
|
} & Pick<NavTreeContextValue, 'tab'>;
|
|
71
92
|
|
|
72
93
|
export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeContainerProps) => {
|
|
73
|
-
const [isLg] = useMediaQuery('lg'
|
|
94
|
+
const [isLg] = useMediaQuery('lg');
|
|
74
95
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
75
96
|
const { graph } = useAppGraph();
|
|
76
97
|
const { isOpen, isCurrent, isAlternateTree, setItem } = useCapability(NavTreeCapabilities.State);
|
|
77
98
|
const layout = useLayout();
|
|
78
|
-
const { navigationSidebarState } = useSidebars(
|
|
79
|
-
|
|
80
|
-
const getActions = useCallback((node: Node) => naturalGetActions(graph, node), [graph]);
|
|
99
|
+
const { navigationSidebarState } = useSidebars(meta.id);
|
|
81
100
|
|
|
82
101
|
const getProps = useCallback(
|
|
83
102
|
(node: Node, path: string[]): TreeItemDataProps => {
|
|
@@ -86,12 +105,13 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
86
105
|
children.length > 0 ? children.map(({ id }) => id) : node.properties.role === 'branch' ? [] : undefined;
|
|
87
106
|
return {
|
|
88
107
|
id: node.id,
|
|
89
|
-
label: node.properties.label ?? node.id,
|
|
90
108
|
parentOf,
|
|
91
|
-
icon: node.properties.icon,
|
|
92
109
|
disabled: node.properties.disabled,
|
|
93
|
-
|
|
110
|
+
label: node.properties.label ?? node.id,
|
|
111
|
+
className: mx(node.properties.className, node.properties.modified && 'italic'), // TODO(burdon): Italic?
|
|
94
112
|
headingClassName: node.properties.headingClassName,
|
|
113
|
+
icon: node.properties.icon,
|
|
114
|
+
iconHue: node.properties.iconHue,
|
|
95
115
|
testId: node.properties.testId,
|
|
96
116
|
};
|
|
97
117
|
},
|
|
@@ -136,23 +156,44 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
136
156
|
}),
|
|
137
157
|
);
|
|
138
158
|
|
|
139
|
-
await dispatch(
|
|
159
|
+
await dispatch(
|
|
160
|
+
createIntent(LayoutAction.SwitchWorkspace, {
|
|
161
|
+
part: 'workspace',
|
|
162
|
+
subject: node.id,
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
140
165
|
|
|
141
166
|
// Open the first item if the workspace is empty.
|
|
142
167
|
if (layout.active.length === 0) {
|
|
143
168
|
const [item] = getItems(graph, node).filter((node) => !isActionLike(node));
|
|
144
169
|
if (item && item.data) {
|
|
145
|
-
await dispatch(
|
|
170
|
+
await dispatch(
|
|
171
|
+
createIntent(LayoutAction.Open, {
|
|
172
|
+
part: 'main',
|
|
173
|
+
subject: [item.id],
|
|
174
|
+
}),
|
|
175
|
+
);
|
|
146
176
|
}
|
|
147
177
|
}
|
|
148
178
|
},
|
|
149
179
|
[dispatch, layout.active, tab, navigationSidebarState, isLg],
|
|
150
180
|
);
|
|
151
181
|
|
|
182
|
+
const blockInstruction = useCallback(
|
|
183
|
+
({ instruction, source, target }: { instruction: Instruction; source: TreeData; target: TreeData }) => {
|
|
184
|
+
return target.item.properties.blockInstruction?.(source, instruction) ?? false;
|
|
185
|
+
},
|
|
186
|
+
[],
|
|
187
|
+
);
|
|
188
|
+
|
|
152
189
|
const canDrop = useCallback(({ source, target }: { source: TreeData; target: TreeData }) => {
|
|
153
190
|
return target.item.properties.canDrop?.(source) ?? false;
|
|
154
191
|
}, []);
|
|
155
192
|
|
|
193
|
+
const canSelect = useCallback(({ item }: { item: Node }) => {
|
|
194
|
+
return item.properties.selectable ?? true;
|
|
195
|
+
}, []);
|
|
196
|
+
|
|
156
197
|
const handleSelect = useCallback(
|
|
157
198
|
({ item: node, path, option }: { item: Node; path: string[]; option: boolean }) => {
|
|
158
199
|
if (!node.data) {
|
|
@@ -176,10 +217,19 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
176
217
|
);
|
|
177
218
|
} else if (option) {
|
|
178
219
|
void dispatch(
|
|
179
|
-
createIntent(LayoutAction.Close, {
|
|
220
|
+
createIntent(LayoutAction.Close, {
|
|
221
|
+
part: 'main',
|
|
222
|
+
subject: [node.id],
|
|
223
|
+
options: { state: false },
|
|
224
|
+
}),
|
|
180
225
|
);
|
|
181
226
|
} else {
|
|
182
|
-
void dispatch(
|
|
227
|
+
void dispatch(
|
|
228
|
+
createIntent(LayoutAction.ScrollIntoView, {
|
|
229
|
+
part: 'current',
|
|
230
|
+
subject: node.id,
|
|
231
|
+
}),
|
|
232
|
+
);
|
|
183
233
|
}
|
|
184
234
|
|
|
185
235
|
const defaultAction = graph.getActions(node.id).find((action) => action.properties?.disposition === 'default');
|
|
@@ -188,14 +238,25 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
188
238
|
}
|
|
189
239
|
|
|
190
240
|
if (!isLg) {
|
|
191
|
-
void dispatch(
|
|
241
|
+
void dispatch(
|
|
242
|
+
createIntent(LayoutAction.UpdateSidebar, {
|
|
243
|
+
part: 'sidebar',
|
|
244
|
+
options: { state: 'closed' },
|
|
245
|
+
}),
|
|
246
|
+
);
|
|
192
247
|
}
|
|
193
248
|
},
|
|
194
249
|
[graph, dispatch, isCurrent, isLg],
|
|
195
250
|
);
|
|
196
251
|
|
|
197
252
|
const handleBack = useCallback(
|
|
198
|
-
() =>
|
|
253
|
+
() =>
|
|
254
|
+
dispatch(
|
|
255
|
+
createIntent(LayoutAction.RevertWorkspace, {
|
|
256
|
+
part: 'workspace',
|
|
257
|
+
options: { revert: true },
|
|
258
|
+
}),
|
|
259
|
+
),
|
|
199
260
|
[dispatch],
|
|
200
261
|
);
|
|
201
262
|
|
|
@@ -267,41 +328,45 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
267
328
|
|
|
268
329
|
const navTreeContextValue = useMemo(
|
|
269
330
|
() => ({
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
loadDescendents,
|
|
274
|
-
renderItemEnd,
|
|
275
|
-
popoverAnchorId,
|
|
276
|
-
topbar,
|
|
331
|
+
blockInstruction,
|
|
332
|
+
canDrop,
|
|
333
|
+
canSelect,
|
|
277
334
|
getProps,
|
|
335
|
+
isAlternateTree,
|
|
278
336
|
isCurrent,
|
|
279
337
|
isOpen,
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
setAlternateTree,
|
|
283
|
-
onTabChange: handleTabChange,
|
|
338
|
+
loadDescendents,
|
|
339
|
+
onBack: handleBack,
|
|
284
340
|
onOpenChange: handleOpenChange,
|
|
285
341
|
onSelect: handleSelect,
|
|
286
|
-
|
|
287
|
-
}),
|
|
288
|
-
[
|
|
289
|
-
tab,
|
|
290
|
-
getActions,
|
|
291
|
-
loadDescendents,
|
|
292
|
-
renderItemEnd,
|
|
342
|
+
onTabChange: handleTabChange,
|
|
293
343
|
popoverAnchorId,
|
|
344
|
+
renderItemEnd,
|
|
345
|
+
setAlternateTree,
|
|
346
|
+
tab,
|
|
294
347
|
topbar,
|
|
348
|
+
useActions,
|
|
349
|
+
useItems,
|
|
350
|
+
}),
|
|
351
|
+
[
|
|
352
|
+
blockInstruction,
|
|
353
|
+
canDrop,
|
|
354
|
+
canSelect,
|
|
295
355
|
getProps,
|
|
356
|
+
handleBack,
|
|
357
|
+
handleOpenChange,
|
|
358
|
+
handleSelect,
|
|
359
|
+
handleTabChange,
|
|
360
|
+
isAlternateTree,
|
|
296
361
|
isCurrent,
|
|
297
362
|
isOpen,
|
|
298
|
-
|
|
299
|
-
|
|
363
|
+
loadDescendents,
|
|
364
|
+
popoverAnchorId,
|
|
365
|
+
renderItemEnd,
|
|
300
366
|
setAlternateTree,
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
handleBack,
|
|
367
|
+
tab,
|
|
368
|
+
topbar,
|
|
369
|
+
useActions,
|
|
305
370
|
],
|
|
306
371
|
);
|
|
307
372
|
|
|
@@ -6,10 +6,10 @@ import { useEffect } from 'react';
|
|
|
6
6
|
import { type Node } from '@dxos/app-graph';
|
|
7
7
|
import { toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { meta } from '../meta';
|
|
10
10
|
|
|
11
11
|
export const NavTreeDocumentTitle = ({ node }: { node?: Node }) => {
|
|
12
|
-
const { t } = useTranslation(
|
|
12
|
+
const { t } = useTranslation(meta.id);
|
|
13
13
|
useEffect(() => {
|
|
14
14
|
document.title = node ? toLocalizedString(node.properties.label, t) : t('current app name', { ns: 'appkit' });
|
|
15
15
|
}, [node?.properties?.label]);
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
|
|
9
7
|
import { type NodeArg } from '@dxos/app-graph';
|
|
10
8
|
import { faker } from '@dxos/random';
|
|
11
|
-
import {
|
|
9
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
12
10
|
|
|
13
11
|
import { NavTreeItemAction, type NavTreeItemActionMenuProps } from './NavTreeItemAction';
|
|
14
12
|
|
|
@@ -39,20 +37,20 @@ const menuActions = faker.helpers.multiple(
|
|
|
39
37
|
{ count: 20 },
|
|
40
38
|
);
|
|
41
39
|
|
|
42
|
-
const meta
|
|
40
|
+
const meta = {
|
|
43
41
|
title: 'plugins/plugin-navtree/NavTreeItemAction',
|
|
44
42
|
component: NavTreeItemAction,
|
|
45
|
-
decorators: [withTheme, withLayout()],
|
|
46
43
|
args: {
|
|
47
44
|
icon: 'ph--list--regular',
|
|
48
45
|
parent,
|
|
49
46
|
menuActions,
|
|
50
47
|
label: 'Select action',
|
|
51
48
|
} satisfies Partial<NavTreeItemActionMenuProps>,
|
|
49
|
+
decorators: [withTheme],
|
|
52
50
|
parameters: {
|
|
53
51
|
layout: 'centered',
|
|
54
52
|
},
|
|
55
|
-
}
|
|
53
|
+
} satisfies Meta<typeof NavTreeItemAction>;
|
|
56
54
|
|
|
57
55
|
export default meta;
|
|
58
56
|
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
7
|
import { type Action, type Node } from '@dxos/app-graph';
|
|
8
|
-
import {
|
|
9
|
-
import { DropdownMenu,
|
|
8
|
+
import { IconButton, toLocalizedString, useDensityContext, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
import { DropdownMenu, type MenuItem, MenuProvider } from '@dxos/react-ui-menu';
|
|
10
10
|
import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/react-ui-theme';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { meta } from '../../meta';
|
|
13
13
|
import { type ActionProperties } from '../../types';
|
|
14
14
|
|
|
15
15
|
export type NavTreeItemActionMenuProps = ActionProperties & {
|
|
@@ -38,7 +38,7 @@ export const NavTreeItemActionDropdownMenu = ({
|
|
|
38
38
|
menuActions,
|
|
39
39
|
caller,
|
|
40
40
|
}: NavTreeItemActionMenuProps) => {
|
|
41
|
-
const { t } = useTranslation(
|
|
41
|
+
const { t } = useTranslation(meta.id);
|
|
42
42
|
const density = useDensityContext();
|
|
43
43
|
return (
|
|
44
44
|
<MenuProvider>
|
|
@@ -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}
|
|
@@ -93,7 +94,7 @@ export const NavTreeItemMonolithicAction = ({
|
|
|
93
94
|
};
|
|
94
95
|
|
|
95
96
|
export const NavTreeItemAction = ({ monolithic, menuActions, parent: node, ...props }: NavTreeItemActionMenuProps) => {
|
|
96
|
-
const { t } = useTranslation(
|
|
97
|
+
const { t } = useTranslation(meta.id);
|
|
97
98
|
|
|
98
99
|
const monolithicAction = menuActions?.length === 1 && menuActions[0];
|
|
99
100
|
const baseLabel = toLocalizedString(monolithicAction ? monolithicAction.properties!.label : props.label, t);
|