@dxos/plugin-navtree 0.8.4-main.dedc0f3 → 0.8.4-main.e8ec1fe
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-6JDBQ25U.mjs → app-graph-builder-OHSCN3GX.mjs} +11 -10
- 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-ZKVY35IG.mjs → chunk-7D2MPHXV.mjs} +125 -111
- package/dist/lib/browser/chunk-7D2MPHXV.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 +5 -7
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-N35ZIHFD.mjs → intent-resolver-YUVXVPHG.mjs} +6 -6
- package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +7 -0
- package/dist/lib/browser/{keyboard-HMTMA33N.mjs → keyboard-YYTYJW4M.mjs} +4 -4
- package/dist/lib/browser/{keyboard-HMTMA33N.mjs.map → keyboard-YYTYJW4M.mjs.map} +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-5PZ4XJT7.mjs → react-surface-J7MWWAGD.mjs} +10 -10
- package/dist/lib/browser/react-surface-J7MWWAGD.mjs.map +7 -0
- package/dist/lib/browser/{state-P5DGL7KS.mjs → state-EUSVT6BI.mjs} +7 -7
- package/dist/lib/browser/state-EUSVT6BI.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.map +1 -1
- package/dist/types/src/capabilities/capabilities.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/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 +1 -2
- 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 +1 -2
- 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/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts +1 -2
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
- package/dist/types/src/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 +2 -3
- 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 +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 +48 -48
- package/src/NavTreePlugin.tsx +70 -71
- package/src/capabilities/app-graph-builder.ts +8 -7
- package/src/capabilities/capabilities.ts +2 -2
- package/src/capabilities/intent-resolver.ts +1 -1
- package/src/capabilities/react-surface.tsx +4 -4
- package/src/capabilities/state.ts +2 -2
- package/src/components/CommandsDialog/CommandsDialog.tsx +14 -6
- package/src/components/CommandsDialog/CommandsTrigger.tsx +4 -3
- package/src/components/NavTree/NavTree.stories.tsx +63 -49
- package/src/components/NavTree/NavTree.tsx +4 -0
- package/src/components/NavTreeContainer.tsx +51 -21
- package/src/components/NavTreeDocumentTitle.tsx +2 -2
- package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +2 -4
- package/src/components/NavTreeItem/NavTreeItemAction.tsx +5 -4
- package/src/components/NavTreeItem/NavTreeItemColumns.tsx +27 -21
- package/src/components/Sidebar/L0Menu.tsx +54 -53
- package/src/components/Sidebar/L1Panel.tsx +7 -10
- package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +3 -4
- package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +20 -12
- package/src/events.ts +2 -2
- package/src/experimental/Tree.stories.tsx +26 -30
- package/src/experimental/Tree.tsx +6 -23
- package/src/meta.ts +10 -7
- package/src/testing/app-graph-builder.ts +19 -18
- package/src/testing/generator.ts +2 -2
- package/src/types.ts +4 -3
- package/dist/lib/browser/app-graph-builder-6JDBQ25U.mjs.map +0 -7
- package/dist/lib/browser/chunk-ERDGQTH5.mjs +0 -16
- package/dist/lib/browser/chunk-ERDGQTH5.mjs.map +0 -7
- package/dist/lib/browser/chunk-WCARAZ7J.mjs.map +0 -7
- package/dist/lib/browser/chunk-ZKVY35IG.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-N35ZIHFD.mjs.map +0 -7
- package/dist/lib/browser/react-surface-5PZ4XJT7.mjs.map +0 -7
- package/dist/lib/browser/state-P5DGL7KS.mjs.map +0 -7
|
@@ -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}
|
|
@@ -7,15 +7,8 @@ import { type Instruction, extractInstruction } from '@atlaskit/pragmatic-drag-a
|
|
|
7
7
|
import { untracked } from '@preact/signals-core';
|
|
8
8
|
import React, { memo, useCallback, useEffect, useMemo } from 'react';
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
Surface,
|
|
13
|
-
createIntent,
|
|
14
|
-
useAppGraph,
|
|
15
|
-
useCapability,
|
|
16
|
-
useIntentDispatcher,
|
|
17
|
-
useLayout,
|
|
18
|
-
} from '@dxos/app-framework';
|
|
10
|
+
import { LayoutAction, createIntent } from '@dxos/app-framework';
|
|
11
|
+
import { Surface, useAppGraph, useCapability, useIntentDispatcher, useLayout } from '@dxos/app-framework/react';
|
|
19
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
14
|
import { useConnections, useActions as useGraphActions } from '@dxos/plugin-graph';
|
|
@@ -25,7 +18,7 @@ import { mx } from '@dxos/react-ui-theme';
|
|
|
25
18
|
import { arrayMove, byPosition } from '@dxos/util';
|
|
26
19
|
|
|
27
20
|
import { NavTreeCapabilities } from '../capabilities';
|
|
28
|
-
import {
|
|
21
|
+
import { meta } from '../meta';
|
|
29
22
|
import { type FlattenedActions, type NavTreeItemGraphNode } from '../types';
|
|
30
23
|
import { getChildren, getParent, resolveMigrationOperation } from '../util';
|
|
31
24
|
|
|
@@ -98,12 +91,12 @@ export type NavTreeContainerProps = {
|
|
|
98
91
|
} & Pick<NavTreeContextValue, 'tab'>;
|
|
99
92
|
|
|
100
93
|
export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeContainerProps) => {
|
|
101
|
-
const [isLg] = useMediaQuery('lg'
|
|
94
|
+
const [isLg] = useMediaQuery('lg');
|
|
102
95
|
const { dispatchPromise: dispatch } = useIntentDispatcher();
|
|
103
96
|
const { graph } = useAppGraph();
|
|
104
97
|
const { isOpen, isCurrent, isAlternateTree, setItem } = useCapability(NavTreeCapabilities.State);
|
|
105
98
|
const layout = useLayout();
|
|
106
|
-
const { navigationSidebarState } = useSidebars(
|
|
99
|
+
const { navigationSidebarState } = useSidebars(meta.id);
|
|
107
100
|
|
|
108
101
|
const getProps = useCallback(
|
|
109
102
|
(node: Node, path: string[]): TreeItemDataProps => {
|
|
@@ -112,12 +105,13 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
112
105
|
children.length > 0 ? children.map(({ id }) => id) : node.properties.role === 'branch' ? [] : undefined;
|
|
113
106
|
return {
|
|
114
107
|
id: node.id,
|
|
115
|
-
label: node.properties.label ?? node.id,
|
|
116
108
|
parentOf,
|
|
117
|
-
icon: node.properties.icon,
|
|
118
109
|
disabled: node.properties.disabled,
|
|
119
|
-
|
|
110
|
+
label: node.properties.label ?? node.id,
|
|
111
|
+
className: mx(node.properties.className, node.properties.modified && 'italic'), // TODO(burdon): Italic?
|
|
120
112
|
headingClassName: node.properties.headingClassName,
|
|
113
|
+
icon: node.properties.icon,
|
|
114
|
+
iconHue: node.properties.iconHue,
|
|
121
115
|
testId: node.properties.testId,
|
|
122
116
|
};
|
|
123
117
|
},
|
|
@@ -162,13 +156,23 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
162
156
|
}),
|
|
163
157
|
);
|
|
164
158
|
|
|
165
|
-
await dispatch(
|
|
159
|
+
await dispatch(
|
|
160
|
+
createIntent(LayoutAction.SwitchWorkspace, {
|
|
161
|
+
part: 'workspace',
|
|
162
|
+
subject: node.id,
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
166
165
|
|
|
167
166
|
// Open the first item if the workspace is empty.
|
|
168
167
|
if (layout.active.length === 0) {
|
|
169
168
|
const [item] = getItems(graph, node).filter((node) => !isActionLike(node));
|
|
170
169
|
if (item && item.data) {
|
|
171
|
-
await dispatch(
|
|
170
|
+
await dispatch(
|
|
171
|
+
createIntent(LayoutAction.Open, {
|
|
172
|
+
part: 'main',
|
|
173
|
+
subject: [item.id],
|
|
174
|
+
}),
|
|
175
|
+
);
|
|
172
176
|
}
|
|
173
177
|
}
|
|
174
178
|
},
|
|
@@ -179,6 +183,10 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
179
183
|
return target.item.properties.canDrop?.(source) ?? false;
|
|
180
184
|
}, []);
|
|
181
185
|
|
|
186
|
+
const canSelect = useCallback(({ item }: { item: Node }) => {
|
|
187
|
+
return item.properties.selectable ?? true;
|
|
188
|
+
}, []);
|
|
189
|
+
|
|
182
190
|
const handleSelect = useCallback(
|
|
183
191
|
({ item: node, path, option }: { item: Node; path: string[]; option: boolean }) => {
|
|
184
192
|
if (!node.data) {
|
|
@@ -202,10 +210,19 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
202
210
|
);
|
|
203
211
|
} else if (option) {
|
|
204
212
|
void dispatch(
|
|
205
|
-
createIntent(LayoutAction.Close, {
|
|
213
|
+
createIntent(LayoutAction.Close, {
|
|
214
|
+
part: 'main',
|
|
215
|
+
subject: [node.id],
|
|
216
|
+
options: { state: false },
|
|
217
|
+
}),
|
|
206
218
|
);
|
|
207
219
|
} else {
|
|
208
|
-
void dispatch(
|
|
220
|
+
void dispatch(
|
|
221
|
+
createIntent(LayoutAction.ScrollIntoView, {
|
|
222
|
+
part: 'current',
|
|
223
|
+
subject: node.id,
|
|
224
|
+
}),
|
|
225
|
+
);
|
|
209
226
|
}
|
|
210
227
|
|
|
211
228
|
const defaultAction = graph.getActions(node.id).find((action) => action.properties?.disposition === 'default');
|
|
@@ -214,14 +231,25 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
214
231
|
}
|
|
215
232
|
|
|
216
233
|
if (!isLg) {
|
|
217
|
-
void dispatch(
|
|
234
|
+
void dispatch(
|
|
235
|
+
createIntent(LayoutAction.UpdateSidebar, {
|
|
236
|
+
part: 'sidebar',
|
|
237
|
+
options: { state: 'closed' },
|
|
238
|
+
}),
|
|
239
|
+
);
|
|
218
240
|
}
|
|
219
241
|
},
|
|
220
242
|
[graph, dispatch, isCurrent, isLg],
|
|
221
243
|
);
|
|
222
244
|
|
|
223
245
|
const handleBack = useCallback(
|
|
224
|
-
() =>
|
|
246
|
+
() =>
|
|
247
|
+
dispatch(
|
|
248
|
+
createIntent(LayoutAction.RevertWorkspace, {
|
|
249
|
+
part: 'workspace',
|
|
250
|
+
options: { revert: true },
|
|
251
|
+
}),
|
|
252
|
+
),
|
|
225
253
|
[dispatch],
|
|
226
254
|
);
|
|
227
255
|
|
|
@@ -304,6 +332,7 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
304
332
|
isCurrent,
|
|
305
333
|
isOpen,
|
|
306
334
|
canDrop,
|
|
335
|
+
canSelect,
|
|
307
336
|
isAlternateTree,
|
|
308
337
|
setAlternateTree,
|
|
309
338
|
onTabChange: handleTabChange,
|
|
@@ -322,6 +351,7 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
|
|
|
322
351
|
isCurrent,
|
|
323
352
|
isOpen,
|
|
324
353
|
canDrop,
|
|
354
|
+
canSelect,
|
|
325
355
|
isAlternateTree,
|
|
326
356
|
setAlternateTree,
|
|
327
357
|
handleTabChange,
|
|
@@ -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
|
|
|
@@ -42,13 +40,13 @@ const menuActions = faker.helpers.multiple(
|
|
|
42
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
|
},
|
|
@@ -9,7 +9,7 @@ import { IconButton, toLocalizedString, useDensityContext, useTranslation } from
|
|
|
9
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);
|
|
@@ -5,18 +5,18 @@
|
|
|
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
|
-
import {
|
|
11
|
+
import { meta } from '../../meta';
|
|
12
12
|
import { NAV_TREE_ITEM } from '../NavTree';
|
|
13
13
|
import { useNavTreeContext } from '../NavTreeContext';
|
|
14
14
|
import { type NavTreeItemColumnsProps } from '../types';
|
|
15
15
|
|
|
16
16
|
import { NavTreeItemAction } from './NavTreeItemAction';
|
|
17
17
|
|
|
18
|
-
export const NavTreeItemColumns = memo(({ path, item, open
|
|
19
|
-
const { t } = useTranslation(
|
|
18
|
+
export const NavTreeItemColumns = memo(({ path, item, open }: NavTreeItemColumnsProps) => {
|
|
19
|
+
const { t } = useTranslation(meta.id);
|
|
20
20
|
const { useActions, renderItemEnd: ItemEnd, popoverAnchorId } = useNavTreeContext();
|
|
21
21
|
|
|
22
22
|
const level = path.length - 2;
|
|
@@ -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
|
});
|
|
@@ -24,15 +24,25 @@ import React, {
|
|
|
24
24
|
|
|
25
25
|
import { type Node } from '@dxos/app-graph';
|
|
26
26
|
import { invariant } from '@dxos/invariant';
|
|
27
|
-
import {
|
|
27
|
+
import { DxAvatar } from '@dxos/lit-ui/react';
|
|
28
|
+
import {
|
|
29
|
+
Icon,
|
|
30
|
+
ListItem,
|
|
31
|
+
ScrollArea,
|
|
32
|
+
type ThemedClassName,
|
|
33
|
+
Tooltip,
|
|
34
|
+
toLocalizedString,
|
|
35
|
+
useMediaQuery,
|
|
36
|
+
useTranslation,
|
|
37
|
+
} from '@dxos/react-ui';
|
|
28
38
|
import { DropdownMenu, type MenuItem, MenuProvider } from '@dxos/react-ui-menu';
|
|
29
39
|
import type { StackItemRearrangeHandler } from '@dxos/react-ui-stack';
|
|
30
40
|
import { Tabs } from '@dxos/react-ui-tabs';
|
|
31
41
|
import { mx } from '@dxos/react-ui-theme';
|
|
32
|
-
import { arrayMove
|
|
42
|
+
import { arrayMove } from '@dxos/util';
|
|
33
43
|
|
|
34
44
|
import { useLoadDescendents } from '../../hooks';
|
|
35
|
-
import {
|
|
45
|
+
import { meta } from '../../meta';
|
|
36
46
|
import { l0ItemType } from '../../util';
|
|
37
47
|
import { useNavTreeContext } from '../NavTreeContext';
|
|
38
48
|
import { UserAccountAvatar } from '../UserAccountAvatar';
|
|
@@ -62,7 +72,7 @@ type L0ItemProps = L0ItemRootProps & {
|
|
|
62
72
|
|
|
63
73
|
const useL0ItemClick = ({ item, parent, path }: L0ItemProps, type: string) => {
|
|
64
74
|
const { tab, isCurrent, onSelect, onTabChange } = useNavTreeContext();
|
|
65
|
-
const [isLg] = useMediaQuery('lg'
|
|
75
|
+
const [isLg] = useMediaQuery('lg');
|
|
66
76
|
|
|
67
77
|
return useCallback(
|
|
68
78
|
(event: MouseEvent) => {
|
|
@@ -92,7 +102,7 @@ const l0Breakpoints: Record<string, string> = {
|
|
|
92
102
|
};
|
|
93
103
|
|
|
94
104
|
const l0ItemRoot =
|
|
95
|
-
'group/l0item flex
|
|
105
|
+
'group/l0item flex is-full justify-center items-center relative data[type!="collection"]:cursor-pointer app-no-drag dx-focus-ring-group';
|
|
96
106
|
|
|
97
107
|
const l0ItemContent = 'flex justify-center items-center dx-focus-ring-group-indicator transition-colors rounded-sm';
|
|
98
108
|
|
|
@@ -103,55 +113,45 @@ const L0ItemRoot = forwardRef<HTMLElement, PropsWithChildren<L0ItemRootProps>>(
|
|
|
103
113
|
const type = l0ItemType(item);
|
|
104
114
|
const itemPath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
105
115
|
|
|
106
|
-
const { t } = useTranslation(
|
|
116
|
+
const { t } = useTranslation(meta.id);
|
|
107
117
|
const localizedString = toLocalizedString(item.properties.label, t);
|
|
108
118
|
|
|
109
119
|
const handleClick = useL0ItemClick({ item, parent, path: itemPath }, type);
|
|
110
120
|
const rootProps =
|
|
111
121
|
type === 'tab'
|
|
112
|
-
? { value: item.id, tabIndex: 0, onClick: handleClick, 'data-testid': testId, 'data-
|
|
122
|
+
? { value: item.id, tabIndex: 0, onClick: handleClick, 'data-testid': testId, 'data-object-id': id }
|
|
113
123
|
: type !== 'collection'
|
|
114
|
-
? { onClick: handleClick, 'data-testid': testId, 'data-
|
|
115
|
-
: { onClick: handleClick };
|
|
116
|
-
|
|
117
|
-
const Root = type === 'collection' ? 'h2' : type === 'tab' ? Tabs.TabPrimitive : 'button';
|
|
124
|
+
? { onClick: handleClick, 'data-testid': testId, 'data-object-id': id }
|
|
125
|
+
: { onClick: handleClick, role: 'button' };
|
|
118
126
|
|
|
119
127
|
return (
|
|
120
128
|
<Tooltip.Trigger asChild delayDuration={0} side='right' content={localizedString}>
|
|
121
|
-
<
|
|
129
|
+
<Tabs.TabPrimitive
|
|
122
130
|
{...(rootProps as any)}
|
|
123
131
|
data-type={type}
|
|
124
132
|
className={mx(l0ItemRoot, l0Breakpoints[item.properties.l0Breakpoint])}
|
|
125
133
|
ref={forwardedRef}
|
|
126
134
|
>
|
|
127
135
|
{children}
|
|
128
|
-
</
|
|
136
|
+
</Tabs.TabPrimitive>
|
|
129
137
|
</Tooltip.Trigger>
|
|
130
138
|
);
|
|
131
139
|
},
|
|
132
140
|
);
|
|
133
141
|
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
return (
|
|
146
|
-
<span role='img' className='place-self-center text-xl font-light' {...hueFgStyle}>
|
|
147
|
-
{avatarValue}
|
|
148
|
-
</span>
|
|
149
|
-
);
|
|
150
|
-
};
|
|
142
|
+
export const L0ItemActiveTabIndicator = ({ classNames }: ThemedClassName<{}>) => (
|
|
143
|
+
<div
|
|
144
|
+
role='none'
|
|
145
|
+
className={mx(
|
|
146
|
+
'hidden group-aria-selected/l0item:block absolute inline-start-0 inset-block-2 is-1 bg-accentSurface rounded-ie',
|
|
147
|
+
classNames,
|
|
148
|
+
)}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
151
|
|
|
152
152
|
// TODO(burdon): Factor out pinned (non-draggable) items.
|
|
153
153
|
const L0Item = ({ item, parent, path, pinned, onRearrange }: L0ItemProps) => {
|
|
154
|
-
const { t } = useTranslation(
|
|
154
|
+
const { t } = useTranslation(meta.id);
|
|
155
155
|
const itemElement = useRef<HTMLElement | null>(null);
|
|
156
156
|
const [closestEdge, setEdge] = useState<Edge | null>(null);
|
|
157
157
|
const localizedString = toLocalizedString(item.properties.label, t);
|
|
@@ -228,10 +228,7 @@ const L0Item = ({ item, parent, path, pinned, onRearrange }: L0ItemProps) => {
|
|
|
228
228
|
>
|
|
229
229
|
<ItemAvatar item={item} />
|
|
230
230
|
</div>
|
|
231
|
-
<
|
|
232
|
-
role='none'
|
|
233
|
-
className='hidden group-aria-selected/l0item:block absolute inline-start-0 inset-block-2 is-1 bg-accentSurface rounded-ie'
|
|
234
|
-
/>
|
|
231
|
+
<L0ItemActiveTabIndicator />
|
|
235
232
|
<span id={`${item.id}__label`} className='sr-only'>
|
|
236
233
|
{localizedString}
|
|
237
234
|
</span>
|
|
@@ -241,15 +238,18 @@ const L0Item = ({ item, parent, path, pinned, onRearrange }: L0ItemProps) => {
|
|
|
241
238
|
};
|
|
242
239
|
|
|
243
240
|
const ItemAvatar = ({ item }: Pick<L0ItemProps, 'item'>) => {
|
|
241
|
+
const { t } = useTranslation(meta.id);
|
|
244
242
|
const type = l0ItemType(item);
|
|
245
243
|
if (item.properties.icon) {
|
|
246
244
|
const hue = item.properties.hue ?? null;
|
|
247
245
|
const hueFgStyle = hue && { style: { color: `var(--dx-${hue}SurfaceText)` } };
|
|
248
|
-
return <Icon icon={item.properties.icon} size={
|
|
246
|
+
return <Icon icon={item.properties.icon} size={6} {...hueFgStyle} />;
|
|
249
247
|
}
|
|
250
248
|
|
|
251
249
|
if (type === 'tab' && item.properties.disposition !== 'pin-end') {
|
|
252
|
-
|
|
250
|
+
const hue = item.properties.hue ?? null;
|
|
251
|
+
const localizedString = toLocalizedString(item.properties.label, t);
|
|
252
|
+
return <DxAvatar hue={hue} hueVariant='surface' variant='square' size={12} fallback={localizedString} />;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
return null;
|
|
@@ -287,7 +287,7 @@ const L0Collection = ({ item, path }: L0ItemProps) => {
|
|
|
287
287
|
role='group'
|
|
288
288
|
className='contents group/l0c'
|
|
289
289
|
aria-labelledby={`${item.id}__label`}
|
|
290
|
-
data-
|
|
290
|
+
data-object-id={id}
|
|
291
291
|
data-testid={testId}
|
|
292
292
|
>
|
|
293
293
|
{collectionItems.map((collectionItem) => (
|
|
@@ -317,7 +317,7 @@ export type L0MenuProps = {
|
|
|
317
317
|
};
|
|
318
318
|
|
|
319
319
|
export const L0Menu = ({ menuActions, topLevelItems, pinnedItems, userAccountItem, parent, path }: L0MenuProps) => {
|
|
320
|
-
const { t } = useTranslation(
|
|
320
|
+
const { t } = useTranslation(meta.id);
|
|
321
321
|
|
|
322
322
|
return (
|
|
323
323
|
<Tabs.Tablist
|
|
@@ -331,20 +331,22 @@ export const L0Menu = ({ menuActions, topLevelItems, pinnedItems, userAccountIte
|
|
|
331
331
|
<MenuProvider>
|
|
332
332
|
<DropdownMenu.Root group={parent} items={menuActions}>
|
|
333
333
|
<Tooltip.Trigger content={t('app menu label')} side='right' asChild>
|
|
334
|
-
<
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
<div
|
|
339
|
-
role='none'
|
|
340
|
-
className={mx(
|
|
341
|
-
l0ItemContent,
|
|
342
|
-
'is-[--rail-action] bs-[--rail-action] group-hover/l0item:bg-hoverSurface',
|
|
343
|
-
)}
|
|
334
|
+
<Tabs.TabPrimitive value='options' asChild role='button'>
|
|
335
|
+
<DropdownMenu.Trigger
|
|
336
|
+
data-testid='spacePlugin.addSpace'
|
|
337
|
+
className={mx(l0ItemRoot, 'grid place-items-center dx-focus-ring-group')}
|
|
344
338
|
>
|
|
345
|
-
<
|
|
346
|
-
|
|
347
|
-
|
|
339
|
+
<div
|
|
340
|
+
role='none'
|
|
341
|
+
className={mx(
|
|
342
|
+
l0ItemContent,
|
|
343
|
+
'is-[--rail-action] bs-[--rail-action] group-hover/l0item:bg-hoverSurface',
|
|
344
|
+
)}
|
|
345
|
+
>
|
|
346
|
+
<Icon icon='ph--list--regular' size={5} />
|
|
347
|
+
</div>
|
|
348
|
+
</DropdownMenu.Trigger>
|
|
349
|
+
</Tabs.TabPrimitive>
|
|
348
350
|
</Tooltip.Trigger>
|
|
349
351
|
</DropdownMenu.Root>
|
|
350
352
|
</MenuProvider>
|
|
@@ -396,7 +398,6 @@ export const L0Menu = ({ menuActions, topLevelItems, pinnedItems, userAccountIte
|
|
|
396
398
|
</L0ItemRoot>
|
|
397
399
|
</div>
|
|
398
400
|
)}
|
|
399
|
-
|
|
400
401
|
<div
|
|
401
402
|
role='none'
|
|
402
403
|
className='hidden [body[data-platform="darwin"]_&]:block absolute block-start-0 is-[calc(var(--l0-size)-1px)] bs-[calc(40px+0.25rem)]'
|
|
@@ -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;
|
|
@@ -56,22 +55,22 @@ export const L1Panel = ({ open, path, item, currentItemId, onBack }: L1PanelProp
|
|
|
56
55
|
]}
|
|
57
56
|
tabIndex={-1}
|
|
58
57
|
aria-label={title}
|
|
59
|
-
{...(!open && { inert:
|
|
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
|
);
|
|
@@ -128,7 +127,6 @@ const L1PanelHeader = ({ item, path, onBack }: L1PanelProps) => {
|
|
|
128
127
|
variant='ghost'
|
|
129
128
|
icon='ph--arrow-u-down-left--regular'
|
|
130
129
|
iconOnly
|
|
131
|
-
size={5}
|
|
132
130
|
label={t('button back')}
|
|
133
131
|
classNames={[hoverableControlItem, hoverableOpenControlItem, 'pointer-fine:pli-1']}
|
|
134
132
|
onClick={handleBack}
|
|
@@ -140,7 +138,6 @@ const L1PanelHeader = ({ item, path, onBack }: L1PanelProps) => {
|
|
|
140
138
|
variant='ghost'
|
|
141
139
|
icon={alternateTree.properties.icon ?? 'ph--placeholder--regular'}
|
|
142
140
|
iconOnly
|
|
143
|
-
size={5}
|
|
144
141
|
label={toLocalizedString(alternateTree.properties.label ?? alternateTree.id, t)}
|
|
145
142
|
classNames={[hoverableControlItem, hoverableOpenControlItem, 'pointer-fine:pli-1']}
|
|
146
143
|
onClick={handleOpen}
|
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
|
|
9
|
-
import { withTheme } from '@dxos/
|
|
7
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
10
8
|
|
|
11
9
|
import { UserAccountAvatar } from './UserAccountAvatar';
|
|
12
10
|
|
|
13
11
|
const meta = {
|
|
14
12
|
title: 'plugins/plugin-navtree/UserAccountAvatar',
|
|
15
|
-
|
|
13
|
+
|
|
16
14
|
decorators: [withTheme],
|
|
15
|
+
component: UserAccountAvatar,
|
|
17
16
|
} satisfies Meta<typeof UserAccountAvatar>;
|
|
18
17
|
|
|
19
18
|
export default meta;
|