@dxos/plugin-navtree 0.8.4-main.4a85c3132b → 0.8.4-main.51f1e5ca51
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/index.mjs +115 -74
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/translations.mjs +21 -0
- package/dist/lib/browser/translations.mjs.map +7 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +7 -5
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/keyboard.d.ts.map +1 -0
- package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/{state/state.d.ts → state.d.ts} +1 -1
- package/dist/types/src/capabilities/state.d.ts.map +1 -0
- package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts +9 -11
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
- package/dist/types/src/components/{NavTreeContext.d.ts → NavTreeContext/NavTreeContext.d.ts} +1 -1
- package/dist/types/src/components/NavTreeContext/NavTreeContext.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeContext/index.d.ts +2 -0
- package/dist/types/src/components/NavTreeContext/index.d.ts.map +1 -0
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +7 -3
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +1 -1
- package/dist/types/src/components/NavTreeItem/NavTreeItemAction.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/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.map +1 -1
- package/dist/types/src/components/types.d.ts +1 -2
- package/dist/types/src/components/types.d.ts.map +1 -1
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -1
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +28 -0
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts.map +1 -0
- package/dist/types/src/containers/CommandsDialogContent/index.d.ts +1 -2
- package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -1
- package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -1
- package/dist/types/src/containers/CommandsTrigger/index.d.ts +1 -2
- package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -1
- package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -1
- package/dist/types/src/containers/NavTreeContainer/index.d.ts +2 -2
- package/dist/types/src/containers/NavTreeContainer/index.d.ts.map +1 -1
- package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -1
- package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +1 -2
- package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -1
- package/dist/types/src/containers/index.d.ts +1 -1
- package/dist/types/src/containers/index.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.d.ts +2 -2
- package/dist/types/src/experimental/Tree.d.ts.map +1 -1
- package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/useActions.d.ts +2 -4
- package/dist/types/src/hooks/useActions.d.ts.map +1 -1
- package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -1
- package/dist/types/src/hooks/useNavTreeModel.d.ts +1 -1
- package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -1
- package/dist/types/src/hooks/useNavTreeState.d.ts +2 -6
- package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/operations/expose.d.ts +5 -0
- package/dist/types/src/operations/expose.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/testing/update-state.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +8 -11
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/capabilities.d.ts +1 -2
- package/dist/types/src/types/capabilities.d.ts.map +1 -1
- package/dist/types/src/types/events.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +2 -2
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +57 -43
- package/src/NavTreePlugin.tsx +7 -7
- package/src/capabilities/{app-graph-builder/app-graph-builder.ts → app-graph-builder.ts} +7 -7
- package/src/capabilities/index.ts +11 -5
- package/src/capabilities/{keyboard/keyboard.ts → keyboard.ts} +2 -2
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/{react-surface/react-surface.tsx → react-surface.tsx} +8 -8
- package/src/capabilities/{state/state.ts → state.ts} +5 -5
- package/src/components/NavTree/NavTree.stories.tsx +17 -16
- package/src/components/NavTree/NavTree.tsx +3 -2
- package/src/components/{NavTreeContext.tsx → NavTreeContext/NavTreeContext.tsx} +1 -1
- package/src/components/NavTreeContext/index.ts +5 -0
- package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +6 -8
- package/src/components/NavTreeItem/NavTreeItemAction.tsx +62 -45
- package/src/components/NavTreeItem/NavTreeItemColumns.tsx +7 -5
- package/src/components/Sidebar/L0Menu.tsx +49 -74
- package/src/components/Sidebar/L1Panel.tsx +97 -134
- package/src/components/Sidebar/L1Tabs.tsx +0 -1
- package/src/components/types.ts +1 -2
- package/src/containers/CommandsDialogContent/CommandsDialogContent.stories.tsx +46 -0
- package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +52 -58
- package/src/containers/CommandsDialogContent/index.ts +1 -3
- package/src/containers/CommandsTrigger/CommandsTrigger.tsx +7 -5
- package/src/containers/CommandsTrigger/index.ts +1 -3
- package/src/containers/NavTreeContainer/NavTreeContainer.tsx +28 -27
- package/src/containers/NavTreeContainer/index.ts +2 -3
- package/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.tsx +2 -2
- package/src/containers/NavTreeDocumentTitle/index.ts +1 -3
- package/src/containers/index.ts +1 -1
- package/src/experimental/Tree.stories.tsx +27 -27
- package/src/hooks/useActions.ts +18 -22
- package/src/hooks/useNavTreeModel.ts +10 -4
- package/src/hooks/useNavTreeState.ts +2 -9
- package/src/meta.ts +2 -2
- package/src/operations/expose.ts +42 -0
- package/src/operations/index.ts +7 -0
- package/src/testing/app-graph-builder.ts +40 -36
- package/src/testing/generator.ts +22 -22
- package/src/translations.ts +8 -10
- package/src/types/capabilities.ts +4 -4
- package/src/types/events.ts +1 -1
- package/src/util.ts +2 -2
- package/dist/lib/browser/CommandsDialogContent-A27AKTHH.mjs +0 -111
- package/dist/lib/browser/CommandsDialogContent-A27AKTHH.mjs.map +0 -7
- package/dist/lib/browser/CommandsTrigger-HBZYQCGV.mjs +0 -33
- package/dist/lib/browser/CommandsTrigger-HBZYQCGV.mjs.map +0 -7
- package/dist/lib/browser/NavTreeContainer-B5K4FUJS.mjs +0 -12
- package/dist/lib/browser/NavTreeContainer-B5K4FUJS.mjs.map +0 -7
- package/dist/lib/browser/NavTreeDocumentTitle-GEE3URWH.mjs +0 -26
- package/dist/lib/browser/NavTreeDocumentTitle-GEE3URWH.mjs.map +0 -7
- package/dist/lib/browser/app-graph-builder-XGA4GPGL.mjs +0 -44
- package/dist/lib/browser/app-graph-builder-XGA4GPGL.mjs.map +0 -7
- package/dist/lib/browser/chunk-DQCYMXEU.mjs +0 -1426
- package/dist/lib/browser/chunk-DQCYMXEU.mjs.map +0 -7
- package/dist/lib/browser/chunk-H3OJ4WLF.mjs +0 -14
- package/dist/lib/browser/chunk-H3OJ4WLF.mjs.map +0 -7
- package/dist/lib/browser/chunk-X2HDG62A.mjs +0 -23
- package/dist/lib/browser/chunk-X2HDG62A.mjs.map +0 -7
- package/dist/lib/browser/chunk-YKTYPINZ.mjs +0 -19
- package/dist/lib/browser/chunk-YKTYPINZ.mjs.map +0 -7
- package/dist/lib/browser/keyboard-OJZC54PK.mjs +0 -54
- package/dist/lib/browser/keyboard-OJZC54PK.mjs.map +0 -7
- package/dist/lib/browser/operation-resolver-BNAMYOHF.mjs +0 -53
- package/dist/lib/browser/operation-resolver-BNAMYOHF.mjs.map +0 -7
- package/dist/lib/browser/react-surface-AYHOAJ4I.mjs +0 -59
- package/dist/lib/browser/react-surface-AYHOAJ4I.mjs.map +0 -7
- package/dist/lib/browser/state-AC3EETN7.mjs +0 -131
- package/dist/lib/browser/state-AC3EETN7.mjs.map +0 -7
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +0 -1
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts +0 -3
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/keyboard/index.d.ts +0 -3
- package/dist/types/src/capabilities/keyboard/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/keyboard/keyboard.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
- package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
- package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
- package/dist/types/src/capabilities/state/index.d.ts +0 -3
- package/dist/types/src/capabilities/state/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/state/state.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeContext.d.ts.map +0 -1
- package/src/capabilities/app-graph-builder/index.ts +0 -7
- package/src/capabilities/keyboard/index.ts +0 -7
- package/src/capabilities/operation-resolver/index.ts +0 -7
- package/src/capabilities/operation-resolver/operation-resolver.ts +0 -44
- package/src/capabilities/react-surface/index.ts +0 -7
- package/src/capabilities/state/index.ts +0 -7
- /package/dist/types/src/capabilities/{app-graph-builder/app-graph-builder.d.ts → app-graph-builder.d.ts} +0 -0
- /package/dist/types/src/capabilities/{keyboard/keyboard.d.ts → keyboard.d.ts} +0 -0
- /package/dist/types/src/capabilities/{react-surface/react-surface.d.ts → react-surface.d.ts} +0 -0
|
@@ -2,21 +2,22 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import * as Effect from 'effect/Effect';
|
|
6
5
|
import * as Option from 'effect/Option';
|
|
7
6
|
import React, { memo, useCallback, useMemo } from 'react';
|
|
8
7
|
|
|
9
8
|
import { Node } from '@dxos/app-graph';
|
|
9
|
+
import { isPinnedWorkspace } from '@dxos/app-toolkit';
|
|
10
10
|
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
11
|
-
import { Graph, useActionRunner,
|
|
11
|
+
import { Graph, useActionRunner, useEdges } from '@dxos/plugin-graph';
|
|
12
12
|
import { DensityProvider, IconButton, ScrollArea, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
13
13
|
import { Tree } from '@dxos/react-ui-list';
|
|
14
14
|
import { Menu, type MenuItem } from '@dxos/react-ui-menu';
|
|
15
15
|
import { Tabs } from '@dxos/react-ui-tabs';
|
|
16
16
|
import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/ui-theme';
|
|
17
17
|
|
|
18
|
-
import { useActions,
|
|
19
|
-
import { meta } from '
|
|
18
|
+
import { useActions, useLoadDescendents } from '#hooks';
|
|
19
|
+
import { meta } from '#meta';
|
|
20
|
+
|
|
20
21
|
import { NAV_TREE_ITEM } from '../NavTree';
|
|
21
22
|
import { useNavTreeContext } from '../NavTreeContext';
|
|
22
23
|
import { NavTreeItemAction, NavTreeItemColumns } from '../NavTreeItem';
|
|
@@ -39,7 +40,7 @@ const L1Panel$ = ({ open, path, item, isCurrent, onBack }: L1PanelProps) => {
|
|
|
39
40
|
const shouldRenderContent = isCurrent || isActivated;
|
|
40
41
|
|
|
41
42
|
return (
|
|
42
|
-
<Tabs.
|
|
43
|
+
<Tabs.Panel
|
|
43
44
|
key={item.id}
|
|
44
45
|
value={item.id}
|
|
45
46
|
classNames={[
|
|
@@ -54,7 +55,7 @@ const L1Panel$ = ({ open, path, item, isCurrent, onBack }: L1PanelProps) => {
|
|
|
54
55
|
{...(!open && { inert: true })}
|
|
55
56
|
>
|
|
56
57
|
{shouldRenderContent && <L1PanelContent open={open} path={path} item={item} onBack={onBack} />}
|
|
57
|
-
</Tabs.
|
|
58
|
+
</Tabs.Panel>
|
|
58
59
|
);
|
|
59
60
|
};
|
|
60
61
|
|
|
@@ -81,49 +82,27 @@ const useIsActivatedWorkspace = (item: Node.Node): boolean => {
|
|
|
81
82
|
const L1PanelContent = ({ path, item, onBack }: Pick<L1PanelProps, 'open' | 'path' | 'item' | 'onBack'>) => {
|
|
82
83
|
const navTreeContext = useNavTreeContext();
|
|
83
84
|
|
|
84
|
-
const alternateTree = useAlternateTreeItem(item);
|
|
85
|
-
const alternatePath = useMemo(() => [...path, item.id], [item.id, path]);
|
|
86
|
-
const isAlternate = useIsAlternateTree(alternatePath, item);
|
|
87
|
-
|
|
88
85
|
return (
|
|
89
86
|
<DensityProvider density='fine'>
|
|
90
87
|
<L1PanelHeader path={path} item={item} onBack={onBack} />
|
|
91
88
|
<ScrollArea.Root thin orientation='vertical'>
|
|
92
89
|
<ScrollArea.Viewport>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
) : (
|
|
110
|
-
<Tree
|
|
111
|
-
model={navTreeContext.model}
|
|
112
|
-
id={item.id}
|
|
113
|
-
rootId={item.id}
|
|
114
|
-
path={path}
|
|
115
|
-
levelOffset={5}
|
|
116
|
-
gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
|
|
117
|
-
draggable
|
|
118
|
-
renderColumns={NavTreeItemColumns}
|
|
119
|
-
blockInstruction={navTreeContext.blockInstruction}
|
|
120
|
-
canDrop={navTreeContext.canDrop}
|
|
121
|
-
canSelect={navTreeContext.canSelect}
|
|
122
|
-
onOpenChange={navTreeContext.onOpenChange}
|
|
123
|
-
onSelect={navTreeContext.onSelect}
|
|
124
|
-
onItemHover={navTreeContext.onItemHover}
|
|
125
|
-
/>
|
|
126
|
-
)}
|
|
90
|
+
<Tree
|
|
91
|
+
model={navTreeContext.model}
|
|
92
|
+
id={item.id}
|
|
93
|
+
rootId={item.id}
|
|
94
|
+
path={path}
|
|
95
|
+
levelOffset={5}
|
|
96
|
+
gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
|
|
97
|
+
draggable
|
|
98
|
+
renderColumns={NavTreeItemColumns}
|
|
99
|
+
blockInstruction={navTreeContext.blockInstruction}
|
|
100
|
+
canDrop={navTreeContext.canDrop}
|
|
101
|
+
canSelect={navTreeContext.canSelect}
|
|
102
|
+
onOpenChange={navTreeContext.onOpenChange}
|
|
103
|
+
onSelect={navTreeContext.onSelect}
|
|
104
|
+
onItemHover={navTreeContext.onItemHover}
|
|
105
|
+
/>
|
|
127
106
|
</ScrollArea.Viewport>
|
|
128
107
|
</ScrollArea.Root>
|
|
129
108
|
</DensityProvider>
|
|
@@ -137,29 +116,33 @@ const L1PanelHeader = ({ item, path, onBack }: Pick<L1PanelProps, 'item' | 'path
|
|
|
137
116
|
const { t } = useTranslation(meta.id);
|
|
138
117
|
const { renderItemEnd: ItemEnd } = useNavTreeContext();
|
|
139
118
|
const title = toLocalizedString(item.properties.label, t);
|
|
140
|
-
const backCapableWorkspace = item.id
|
|
119
|
+
const backCapableWorkspace = isPinnedWorkspace(item.id);
|
|
141
120
|
|
|
142
121
|
const { primaryAction, groupedActions, menuActions, onAction } = useL1MenuActions({ item, path });
|
|
143
122
|
useLoadDescendents(item);
|
|
144
123
|
|
|
145
124
|
return (
|
|
146
|
-
<div
|
|
125
|
+
<div
|
|
126
|
+
data-tauri-drag-region
|
|
127
|
+
className='flex w-full items-center border-b border-subdued-separator dx-app-drag dx-density-coarse pe-1'
|
|
128
|
+
>
|
|
147
129
|
{backCapableWorkspace ? (
|
|
148
130
|
<IconButton
|
|
149
|
-
size={5}
|
|
150
131
|
density='coarse'
|
|
151
132
|
classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
|
|
152
133
|
variant='ghost'
|
|
153
134
|
icon='ph--caret-left--regular'
|
|
154
135
|
iconOnly
|
|
155
|
-
label={t('button
|
|
136
|
+
label={t('button-back.button')}
|
|
156
137
|
data-testid='treeView.primaryTreeButton'
|
|
157
138
|
onClick={() => onBack?.()}
|
|
158
139
|
/>
|
|
159
140
|
) : (
|
|
160
|
-
<div className='w-6' />
|
|
141
|
+
<div data-tauri-drag-region className='w-6' />
|
|
161
142
|
)}
|
|
162
|
-
<h2 className='flex-1 truncate min-w-0'>
|
|
143
|
+
<h2 data-tauri-drag-region className='flex-1 truncate min-w-0'>
|
|
144
|
+
{title}
|
|
145
|
+
</h2>
|
|
163
146
|
{/* TODO(wittjosiah): Reconcile with NavTreeItemColumns. */}
|
|
164
147
|
<div role='none' className='contents dx-app-no-drag'>
|
|
165
148
|
{primaryAction?.properties?.disposition === 'list-item-primary' && !primaryAction?.properties?.disabled && (
|
|
@@ -168,121 +151,101 @@ const L1PanelHeader = ({ item, path, onBack }: Pick<L1PanelProps, 'item' | 'path
|
|
|
168
151
|
label={toLocalizedString(primaryAction.properties?.label, t)}
|
|
169
152
|
icon={primaryAction.properties?.icon ?? 'ph--placeholder--regular'}
|
|
170
153
|
parent={item}
|
|
154
|
+
path={path}
|
|
171
155
|
monolithic={Node.isAction(primaryAction)}
|
|
172
156
|
menuActions={Node.isAction(primaryAction) ? [primaryAction] : groupedActions[primaryAction?.id ?? '']}
|
|
173
157
|
menuType={primaryAction.properties?.menuType}
|
|
174
158
|
caller={NAV_TREE_ITEM}
|
|
175
159
|
/>
|
|
176
160
|
)}
|
|
177
|
-
{menuActions
|
|
178
|
-
<IconButton
|
|
179
|
-
size={5}
|
|
180
|
-
density='coarse'
|
|
181
|
-
classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
|
|
182
|
-
variant='ghost'
|
|
183
|
-
icon={menuActions[0].properties?.icon ?? 'ph--placeholder--regular'}
|
|
184
|
-
iconOnly
|
|
185
|
-
label={toLocalizedString(menuActions[0].properties?.label, t)}
|
|
186
|
-
data-testid={menuActions[0].properties?.testId}
|
|
187
|
-
onClick={() => onAction(menuActions[0] as Node.Action)}
|
|
188
|
-
/>
|
|
189
|
-
)}
|
|
190
|
-
{menuActions.length > 1 && (
|
|
191
|
-
<Menu.Root caller={NAV_TREE_ITEM} onAction={onAction}>
|
|
192
|
-
<Menu.Trigger asChild>
|
|
193
|
-
<IconButton
|
|
194
|
-
size={5}
|
|
195
|
-
density='coarse'
|
|
196
|
-
classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
|
|
197
|
-
variant='ghost'
|
|
198
|
-
icon='ph--dots-three-vertical--regular'
|
|
199
|
-
iconOnly
|
|
200
|
-
label={t('tree item actions label')}
|
|
201
|
-
data-testid='navtree.treeItem.actionsLevel0'
|
|
202
|
-
/>
|
|
203
|
-
</Menu.Trigger>
|
|
204
|
-
<Menu.Content group={item} items={menuActions as MenuItem[]} />
|
|
205
|
-
</Menu.Root>
|
|
206
|
-
)}
|
|
161
|
+
<MenuActions item={item} menuActions={menuActions} onAction={onAction} />
|
|
207
162
|
{ItemEnd && <ItemEnd node={item} open />}
|
|
208
163
|
</div>
|
|
209
164
|
</div>
|
|
210
165
|
);
|
|
211
166
|
};
|
|
212
167
|
|
|
168
|
+
type L1MenuActions = {
|
|
169
|
+
primaryAction: Node.ActionLike;
|
|
170
|
+
groupedActions: Record<string, Node.Action[]>;
|
|
171
|
+
menuActions: Node.Action[];
|
|
172
|
+
onAction: (action: Node.Action, params?: Node.InvokeProps) => void;
|
|
173
|
+
};
|
|
174
|
+
|
|
213
175
|
/**
|
|
214
|
-
*
|
|
176
|
+
* Header menu actions for an L1 workspace tab. Renders nothing for an empty
|
|
177
|
+
* `menuActions`, a single inline icon button for one action, and a
|
|
178
|
+
* `…`-menu trigger for multiple.
|
|
215
179
|
*/
|
|
216
|
-
const
|
|
180
|
+
const MenuActions = ({
|
|
181
|
+
item,
|
|
182
|
+
menuActions,
|
|
183
|
+
onAction,
|
|
184
|
+
}: {
|
|
185
|
+
item: Node.Node;
|
|
186
|
+
} & Pick<L1MenuActions, 'menuActions' | 'onAction'>) => {
|
|
217
187
|
const { t } = useTranslation(meta.id);
|
|
218
|
-
const { setAlternateTree } = useNavTreeContext();
|
|
219
|
-
const { graph } = useAppGraph();
|
|
220
|
-
const runAction = useActionRunner();
|
|
221
188
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
189
|
+
if (menuActions.length === 0) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (menuActions.length === 1) {
|
|
194
|
+
return (
|
|
195
|
+
<IconButton
|
|
196
|
+
density='coarse'
|
|
197
|
+
classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
|
|
198
|
+
variant='ghost'
|
|
199
|
+
icon={menuActions[0].properties?.icon ?? 'ph--placeholder--regular'}
|
|
200
|
+
iconOnly
|
|
201
|
+
label={toLocalizedString(menuActions[0].properties?.label, t)}
|
|
202
|
+
data-testid={menuActions[0].properties?.testId}
|
|
203
|
+
onClick={() => onAction(menuActions[0] as Node.Action)}
|
|
204
|
+
/>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<Menu.Root caller={NAV_TREE_ITEM} onAction={onAction}>
|
|
210
|
+
<Menu.Trigger asChild>
|
|
211
|
+
<IconButton
|
|
212
|
+
density='coarse'
|
|
213
|
+
classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
|
|
214
|
+
variant='ghost'
|
|
215
|
+
icon='ph--dots-three-vertical--regular'
|
|
216
|
+
iconOnly
|
|
217
|
+
label={t('tree-item-actions.label')}
|
|
218
|
+
data-testid='navtree.treeItem.actionsLevel0'
|
|
219
|
+
/>
|
|
220
|
+
</Menu.Trigger>
|
|
221
|
+
<Menu.Content group={item} items={menuActions as MenuItem[]} />
|
|
222
|
+
</Menu.Root>
|
|
223
|
+
);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Builds the menu actions for the L1 panel header.
|
|
228
|
+
*/
|
|
229
|
+
const useL1MenuActions = ({ item, path }: Pick<L1PanelProps, 'item' | 'path'>): L1MenuActions => {
|
|
230
|
+
const runAction = useActionRunner();
|
|
225
231
|
|
|
226
|
-
// Graph actions.
|
|
227
232
|
const { actions: actionsProp, groupedActions } = useActions(item);
|
|
228
233
|
const [primaryAction, ...secondaryActions] = actionsProp.toSorted((a, _b) =>
|
|
229
234
|
a.properties?.disposition === 'list-item-primary' ? -1 : 1,
|
|
230
235
|
);
|
|
231
236
|
|
|
232
|
-
const
|
|
233
|
-
primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : actionsProp
|
|
234
|
-
)
|
|
237
|
+
const menuActions = (primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : actionsProp)
|
|
235
238
|
.flatMap((action) => (Node.isAction(action) ? [action] : []))
|
|
236
239
|
.filter((a) => ['list-item', 'list-item-primary'].includes(a.properties?.disposition));
|
|
237
240
|
|
|
238
|
-
// Synthetic settings/back action.
|
|
239
|
-
const settingsActionId = `${item.id}~settings`;
|
|
240
|
-
const settingsAction = useMemo((): Node.Action | undefined => {
|
|
241
|
-
if (!alternateTree) {
|
|
242
|
-
return undefined;
|
|
243
|
-
}
|
|
244
|
-
return {
|
|
245
|
-
id: settingsActionId,
|
|
246
|
-
type: Node.ActionType,
|
|
247
|
-
data: () => Effect.void,
|
|
248
|
-
properties: {
|
|
249
|
-
label: isAlternate ? ['button back', { ns: meta.id }] : (alternateTree.properties.label ?? alternateTree.id),
|
|
250
|
-
icon: isAlternate
|
|
251
|
-
? 'ph--arrow-u-down-left--regular'
|
|
252
|
-
: (alternateTree.properties.icon ?? 'ph--placeholder--regular'),
|
|
253
|
-
disposition: 'list-item',
|
|
254
|
-
testId: isAlternate ? 'navtree.backToSpace' : 'navtree.spaceSettings',
|
|
255
|
-
},
|
|
256
|
-
} as Node.Action;
|
|
257
|
-
}, [alternateTree, isAlternate, settingsActionId, t]);
|
|
258
|
-
|
|
259
|
-
const menuActions = useMemo(
|
|
260
|
-
() => [...graphMenuActions, ...(settingsAction ? [settingsAction] : [])],
|
|
261
|
-
[graphMenuActions, settingsAction],
|
|
262
|
-
);
|
|
263
|
-
|
|
264
241
|
const onAction = useCallback(
|
|
265
242
|
(action: Node.Action, params?: Node.InvokeProps) => {
|
|
266
|
-
|
|
267
|
-
if (alternateTree && !isAlternate) {
|
|
268
|
-
Graph.expand(graph, alternateTree.id, 'child');
|
|
269
|
-
}
|
|
270
|
-
setAlternateTree?.(alternatePath, !isAlternate);
|
|
271
|
-
} else {
|
|
272
|
-
void runAction(action, params);
|
|
273
|
-
}
|
|
243
|
+
void runAction(action, { ...params, path });
|
|
274
244
|
},
|
|
275
|
-
[
|
|
245
|
+
[runAction, path],
|
|
276
246
|
);
|
|
277
247
|
|
|
278
248
|
return { primaryAction, groupedActions, menuActions, onAction };
|
|
279
249
|
};
|
|
280
250
|
|
|
281
|
-
/** Finds the first child with disposition 'alternate-tree' using graph connections directly. */
|
|
282
|
-
const useAlternateTreeItem = (item: Node.Node): Node.Node | undefined => {
|
|
283
|
-
const { graph } = useAppGraph();
|
|
284
|
-
const connections = useConnections(graph, item.id, 'child');
|
|
285
|
-
return connections.find((node) => node.properties.disposition === 'alternate-tree');
|
|
286
|
-
};
|
|
287
|
-
|
|
288
251
|
export const L1Panel = memo(L1Panel$);
|
package/src/components/types.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { type Node } from '@dxos/app-graph';
|
|
|
8
8
|
import type { Density } from '@dxos/react-ui';
|
|
9
9
|
import type { TreeModel, TreeProps } from '@dxos/react-ui-list';
|
|
10
10
|
|
|
11
|
-
import type { NavTreeItemGraphNode } from '
|
|
11
|
+
import type { NavTreeItemGraphNode } from '#types';
|
|
12
12
|
|
|
13
13
|
import type { L1PanelProps } from './Sidebar';
|
|
14
14
|
|
|
@@ -16,7 +16,6 @@ export type NavTreeContextValue = {
|
|
|
16
16
|
model: TreeModel<NavTreeItemGraphNode>;
|
|
17
17
|
popoverAnchorId?: string;
|
|
18
18
|
renderItemEnd?: FC<{ node: Node.Node; open: boolean }>;
|
|
19
|
-
setAlternateTree?: (path: string[], open: boolean) => void;
|
|
20
19
|
onTabChange?: (node: NavTreeItemGraphNode) => void;
|
|
21
20
|
} & Pick<
|
|
22
21
|
TreeProps<NavTreeItemGraphNode>,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
9
|
+
import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
|
|
10
|
+
import { Dialog } from '@dxos/react-ui';
|
|
11
|
+
import { withLayout } from '@dxos/react-ui/testing';
|
|
12
|
+
|
|
13
|
+
import { translations } from '#translations';
|
|
14
|
+
|
|
15
|
+
import { NavTreePlugin } from '../../NavTreePlugin';
|
|
16
|
+
import { CommandsDialogContent } from './CommandsDialogContent';
|
|
17
|
+
|
|
18
|
+
const DefaultStory = () => (
|
|
19
|
+
<Dialog.Root defaultOpen>
|
|
20
|
+
<Dialog.Overlay>
|
|
21
|
+
<CommandsDialogContent />
|
|
22
|
+
</Dialog.Overlay>
|
|
23
|
+
</Dialog.Root>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const meta = {
|
|
27
|
+
title: 'plugins/plugin-navtree/containers/CommandsDialogContent',
|
|
28
|
+
component: CommandsDialogContent,
|
|
29
|
+
render: DefaultStory,
|
|
30
|
+
decorators: [
|
|
31
|
+
withLayout({ layout: 'fullscreen' }),
|
|
32
|
+
withPluginManager({
|
|
33
|
+
plugins: [...corePlugins(), StorybookPlugin({}), NavTreePlugin()],
|
|
34
|
+
}),
|
|
35
|
+
],
|
|
36
|
+
parameters: {
|
|
37
|
+
layout: 'fullscreen',
|
|
38
|
+
translations,
|
|
39
|
+
},
|
|
40
|
+
} satisfies Meta<typeof CommandsDialogContent>;
|
|
41
|
+
|
|
42
|
+
export default meta;
|
|
43
|
+
|
|
44
|
+
type Story = StoryObj<typeof meta>;
|
|
45
|
+
|
|
46
|
+
export const Default: Story = {};
|
|
@@ -11,11 +11,11 @@ import { Keyboard, keySymbols } from '@dxos/keyboard';
|
|
|
11
11
|
import { Graph, Node, useActionRunner } from '@dxos/plugin-graph';
|
|
12
12
|
import { useActions } from '@dxos/plugin-graph';
|
|
13
13
|
import { Button, Dialog, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
14
|
-
import { SearchList, useSearchListResults } from '@dxos/react-ui-
|
|
14
|
+
import { SearchList, useSearchListResults } from '@dxos/react-ui-search';
|
|
15
15
|
import { osTranslations } from '@dxos/ui-theme';
|
|
16
16
|
import { getHostPlatform } from '@dxos/util';
|
|
17
17
|
|
|
18
|
-
import { KEY_BINDING, meta } from '
|
|
18
|
+
import { KEY_BINDING, meta } from '#meta';
|
|
19
19
|
|
|
20
20
|
export type CommandsDialogContentProps = {
|
|
21
21
|
selected?: string;
|
|
@@ -25,7 +25,7 @@ export type CommandsDialogContentProps = {
|
|
|
25
25
|
export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogContentProps>(
|
|
26
26
|
({ selected: initial }, forwardedRef) => {
|
|
27
27
|
const { t } = useTranslation(meta.id);
|
|
28
|
-
const {
|
|
28
|
+
const { invokePromise } = useOperationInvoker();
|
|
29
29
|
const runAction = useActionRunner();
|
|
30
30
|
const { graph } = useAppGraph();
|
|
31
31
|
const [selected, setSelected] = useState<string | undefined>(initial);
|
|
@@ -38,13 +38,12 @@ export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogCo
|
|
|
38
38
|
const actionMap = new Set<string>();
|
|
39
39
|
const actions: Node.ActionLike[] = [];
|
|
40
40
|
Graph.traverse(graph, {
|
|
41
|
-
relation: 'child',
|
|
41
|
+
relation: ['child', 'action'],
|
|
42
42
|
visitor: (node, path) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
) {
|
|
43
|
+
const isActionLike = Node.isAction(node) || Node.isActionGroup(node);
|
|
44
|
+
const parentId = path.at(-2) ?? '';
|
|
45
|
+
const matches = current === parentId || current.startsWith(parentId + '/');
|
|
46
|
+
if (isActionLike && !actionMap.has(node.id) && matches) {
|
|
48
47
|
actionMap.add(node.id);
|
|
49
48
|
actions.push(node);
|
|
50
49
|
}
|
|
@@ -71,62 +70,57 @@ export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogCo
|
|
|
71
70
|
|
|
72
71
|
return (
|
|
73
72
|
<Dialog.Content ref={forwardedRef}>
|
|
74
|
-
<Dialog.Title>{t('commands
|
|
73
|
+
<Dialog.Title srOnly>{t('commands-dialog.title', { ns: meta.id })}</Dialog.Title>
|
|
75
74
|
<SearchList.Root onSearch={handleSearch}>
|
|
76
|
-
<SearchList.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
: action.properties.keyBinding?.[getHostPlatform()];
|
|
75
|
+
<SearchList.Input placeholder={t('command-list-input.placeholder')} />
|
|
76
|
+
<SearchList.Viewport>
|
|
77
|
+
{results.map((action) => {
|
|
78
|
+
const shortcut =
|
|
79
|
+
typeof action.properties.keyBinding === 'string'
|
|
80
|
+
? action.properties.keyBinding
|
|
81
|
+
: action.properties.keyBinding?.[getHostPlatform()];
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
83
|
+
return (
|
|
84
|
+
<SearchList.Item
|
|
85
|
+
value={action.id}
|
|
86
|
+
key={action.id}
|
|
87
|
+
label={toLocalizedString(action.properties.label, t)}
|
|
88
|
+
icon={action.properties.icon}
|
|
89
|
+
suffix={shortcut ? keySymbols(shortcut).join('') : undefined}
|
|
90
|
+
onSelect={() => {
|
|
91
|
+
if (action.properties.disabled) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
96
94
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
if (Node.isActionGroup(action)) {
|
|
96
|
+
setSelected(action.id);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
101
99
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
121
|
-
})}
|
|
122
|
-
</SearchList.Viewport>
|
|
123
|
-
</SearchList.Content>
|
|
100
|
+
void invokePromise(LayoutOperation.UpdateDialog, { state: false });
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
const lookupId = group?.id ?? action.id;
|
|
103
|
+
const node = Graph.getConnections(graph, lookupId, Node.actionRelation('inbound'))[0];
|
|
104
|
+
if (node && Node.isAction(action)) {
|
|
105
|
+
void runAction(action, { parent: node, caller: KEY_BINDING });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}}
|
|
109
|
+
classNames='flex items-center gap-2'
|
|
110
|
+
disabled={action.properties.disabled}
|
|
111
|
+
{...(action.properties?.testId && {
|
|
112
|
+
'data-testid': action.properties.testId,
|
|
113
|
+
})}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
})}
|
|
117
|
+
</SearchList.Viewport>
|
|
124
118
|
</SearchList.Root>
|
|
125
|
-
<
|
|
119
|
+
<Dialog.ActionBar>
|
|
126
120
|
<Dialog.Close asChild>
|
|
127
|
-
<Button classNames='w-full'>{t('close
|
|
121
|
+
<Button classNames='w-full'>{t('close.label', { ns: osTranslations })}</Button>
|
|
128
122
|
</Dialog.Close>
|
|
129
|
-
</
|
|
123
|
+
</Dialog.ActionBar>
|
|
130
124
|
</Dialog.Content>
|
|
131
125
|
);
|
|
132
126
|
},
|
|
@@ -8,19 +8,21 @@ import { useOperationInvoker } from '@dxos/app-framework/ui';
|
|
|
8
8
|
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
9
9
|
import { Button, Icon, useTranslation } from '@dxos/react-ui';
|
|
10
10
|
|
|
11
|
-
import { COMMANDS_DIALOG, meta } from '
|
|
11
|
+
import { COMMANDS_DIALOG, meta } from '#meta';
|
|
12
12
|
|
|
13
13
|
// TODO(thure): Refactor to be handled by a more appropriate plugin.
|
|
14
14
|
export const CommandsTrigger = () => {
|
|
15
|
-
const {
|
|
15
|
+
const { invokePromise } = useOperationInvoker();
|
|
16
16
|
const { t } = useTranslation(meta.id);
|
|
17
17
|
return (
|
|
18
18
|
<Button
|
|
19
19
|
classNames='m-1 px-1 lg:px-2'
|
|
20
|
-
onClick={() =>
|
|
20
|
+
onClick={() =>
|
|
21
|
+
void invokePromise(LayoutOperation.UpdateDialog, { subject: COMMANDS_DIALOG, blockAlign: 'start' })
|
|
22
|
+
}
|
|
21
23
|
>
|
|
22
|
-
<span className='text-description font-normal grow text-start'>{t('command
|
|
23
|
-
<Icon icon='ph--magnifying-glass--regular'
|
|
24
|
+
<span className='text-description font-normal grow text-start'>{t('command-list-input.placeholder')}</span>
|
|
25
|
+
<Icon icon='ph--magnifying-glass--regular' />
|
|
24
26
|
</Button>
|
|
25
27
|
);
|
|
26
28
|
};
|