@dxos/plugin-navtree 0.8.4-main.937b3ca → 0.8.4-main.9be5663bfe
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 +137 -72
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts +6 -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 +5 -0
- 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 +5 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/state.d.ts +6 -0
- package/dist/types/src/capabilities/state.d.ts.map +1 -0
- package/dist/types/src/components/NavTree/NavTree.d.ts +10 -32
- package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
- package/dist/types/src/components/NavTree/NavTree.stories.d.ts +10 -14
- 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 +4 -1
- package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Panel.d.ts +3 -6
- package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
- package/dist/types/src/components/Sidebar/L1Tabs.d.ts +3 -2
- package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
- package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +0 -3
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/components/types.d.ts +5 -13
- package/dist/types/src/components/types.d.ts.map +1 -1
- package/dist/types/src/{components/CommandsDialog/CommandsDialog.d.ts → containers/CommandsDialogContent/CommandsDialogContent.d.ts} +1 -1
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -0
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +29 -0
- package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts.map +1 -0
- package/dist/types/src/containers/CommandsDialogContent/index.d.ts +2 -0
- package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -0
- package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -0
- package/dist/types/src/containers/CommandsTrigger/index.d.ts +2 -0
- package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -0
- package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts +9 -0
- package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -0
- package/dist/types/src/containers/NavTreeContainer/index.d.ts +3 -0
- package/dist/types/src/containers/NavTreeContainer/index.d.ts.map +1 -0
- package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -0
- package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +2 -0
- package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +7 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/experimental/Tree.stories.d.ts +18 -4
- package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +5 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useActions.d.ts +5 -0
- package/dist/types/src/hooks/useActions.d.ts.map +1 -0
- package/dist/types/src/hooks/useLoadDescendents.d.ts +6 -0
- package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -0
- package/dist/types/src/hooks/useNavTreeModel.d.ts +7 -0
- package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -0
- package/dist/types/src/{hooks.d.ts → hooks/useNavTreeState.d.ts} +2 -14
- package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -0
- 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 +3 -3
- package/dist/types/src/testing/generator.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +9 -10
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/events.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +6 -2
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +54 -43
- package/src/NavTreePlugin.tsx +26 -27
- package/src/capabilities/{app-graph-builder/app-graph-builder.ts → app-graph-builder.ts} +10 -9
- package/src/capabilities/index.ts +11 -5
- package/src/capabilities/{keyboard/keyboard.ts → keyboard.ts} +7 -6
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/{react-surface/react-surface.tsx → react-surface.tsx} +15 -15
- package/src/capabilities/{state/state.ts → state.ts} +43 -15
- package/src/components/NavTree/NavTree.stories.tsx +31 -28
- package/src/components/NavTree/NavTree.tsx +64 -22
- 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 +7 -9
- package/src/components/NavTreeItem/NavTreeItemAction.tsx +56 -39
- package/src/components/NavTreeItem/NavTreeItemColumns.tsx +43 -17
- package/src/components/Sidebar/L0Menu.tsx +104 -113
- package/src/components/Sidebar/L1Panel.tsx +253 -94
- package/src/components/Sidebar/L1Tabs.tsx +13 -4
- package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +2 -3
- package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +1 -1
- package/src/components/index.ts +0 -3
- package/src/components/types.ts +12 -17
- package/src/containers/CommandsDialogContent/CommandsDialogContent.stories.tsx +45 -0
- package/src/{components/CommandsDialog/CommandsDialog.tsx → containers/CommandsDialogContent/CommandsDialogContent.tsx} +23 -21
- package/src/containers/CommandsDialogContent/index.ts +5 -0
- package/src/containers/CommandsTrigger/CommandsTrigger.tsx +28 -0
- package/src/containers/CommandsTrigger/index.ts +5 -0
- package/src/{components → containers/NavTreeContainer}/NavTreeContainer.tsx +85 -142
- package/src/containers/NavTreeContainer/index.ts +6 -0
- package/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.tsx +2 -2
- package/src/containers/NavTreeDocumentTitle/index.ts +5 -0
- package/src/containers/index.ts +12 -0
- package/src/experimental/Tree.stories.tsx +81 -84
- package/src/experimental/Tree.tsx +3 -3
- package/src/hooks/index.ts +8 -0
- package/src/hooks/useActions.ts +34 -0
- package/src/hooks/useLoadDescendents.ts +23 -0
- package/src/hooks/useNavTreeModel.ts +118 -0
- package/src/{hooks.ts → hooks/useNavTreeState.ts} +3 -34
- 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 +25 -25
- package/src/translations.ts +9 -10
- package/src/types/capabilities.ts +2 -2
- package/src/types/events.ts +4 -3
- package/src/util.ts +19 -5
- package/dist/lib/browser/app-graph-builder-RSMA6I22.mjs +0 -43
- package/dist/lib/browser/app-graph-builder-RSMA6I22.mjs.map +0 -7
- package/dist/lib/browser/chunk-6YPQ625I.mjs +0 -1293
- package/dist/lib/browser/chunk-6YPQ625I.mjs.map +0 -7
- package/dist/lib/browser/chunk-AGQDZBPL.mjs +0 -23
- package/dist/lib/browser/chunk-AGQDZBPL.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-32EH3N4K.mjs +0 -52
- package/dist/lib/browser/keyboard-32EH3N4K.mjs.map +0 -7
- package/dist/lib/browser/operation-resolver-IB75DC5O.mjs +0 -52
- package/dist/lib/browser/operation-resolver-IB75DC5O.mjs.map +0 -7
- package/dist/lib/browser/react-surface-F6CVMS3Y.mjs +0 -58
- package/dist/lib/browser/react-surface-F6CVMS3Y.mjs.map +0 -7
- package/dist/lib/browser/state-EVYQOUI3.mjs +0 -120
- package/dist/lib/browser/state-EVYQOUI3.mjs.map +0 -7
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +0 -6
- 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 +0 -5
- 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 +0 -5
- 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 +0 -6
- package/dist/types/src/capabilities/state/state.d.ts.map +0 -1
- package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +0 -1
- package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +0 -1
- package/dist/types/src/components/CommandsDialog/index.d.ts +0 -3
- package/dist/types/src/components/CommandsDialog/index.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeContainer.d.ts +0 -16
- package/dist/types/src/components/NavTreeContainer.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeContext.d.ts.map +0 -1
- package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +0 -1
- package/dist/types/src/hooks.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 -41
- package/src/capabilities/react-surface/index.ts +0 -7
- package/src/capabilities/state/index.ts +0 -7
- package/src/components/CommandsDialog/CommandsTrigger.tsx +0 -26
- package/src/components/CommandsDialog/index.ts +0 -6
- /package/dist/types/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.d.ts +0 -0
- /package/dist/types/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.d.ts +0 -0
|
@@ -8,24 +8,26 @@ import * as Effect from 'effect/Effect';
|
|
|
8
8
|
import React, { type KeyboardEvent, useCallback, useRef } from 'react';
|
|
9
9
|
import { expect, userEvent, within } from 'storybook/test';
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
import { useAtomCapability } from '@dxos/app-framework/react';
|
|
11
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
13
12
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
14
|
-
import {
|
|
13
|
+
import { useAtomCapability } from '@dxos/app-framework/ui';
|
|
14
|
+
import { AppCapabilities, LayoutOperation } from '@dxos/app-toolkit';
|
|
15
|
+
import { Operation, OperationHandlerSet } from '@dxos/operation';
|
|
15
16
|
import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
|
|
16
|
-
import {
|
|
17
|
+
import { random } from '@dxos/random';
|
|
17
18
|
import { IconButton, Input, Main, Toolbar } from '@dxos/react-ui';
|
|
18
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
19
19
|
import { useAttention, useAttentionAttributes } from '@dxos/react-ui-attention';
|
|
20
20
|
import { Stack, StackItem } from '@dxos/react-ui-stack';
|
|
21
|
+
import { withLayout } from '@dxos/react-ui/testing';
|
|
21
22
|
import { mx } from '@dxos/ui-theme';
|
|
22
23
|
|
|
24
|
+
import { NavTreeContainer } from '#containers';
|
|
25
|
+
import { storybookGraphBuilders } from '#testing';
|
|
26
|
+
|
|
23
27
|
import { NavTreePlugin } from '../../NavTreePlugin';
|
|
24
|
-
import { storybookGraphBuilders } from '../../testing';
|
|
25
28
|
import { translations } from '../../translations';
|
|
26
|
-
import { NavTreeContainer } from '../NavTreeContainer';
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
random.seed(1234);
|
|
29
31
|
|
|
30
32
|
const StoryState = Capability.make<Atom.Atom<{ tab: string }>>('story-state');
|
|
31
33
|
|
|
@@ -39,14 +41,14 @@ const StoryPlankHeading = ({ attendableId }: { attendableId: string }) => {
|
|
|
39
41
|
const { hasAttention } = useAttention(attendableId);
|
|
40
42
|
console.log('hasAttention', hasAttention);
|
|
41
43
|
return (
|
|
42
|
-
<div className='flex p-1 items-center border-
|
|
44
|
+
<div className='flex p-1 items-center border-b border-separator'>
|
|
43
45
|
<IconButton
|
|
44
46
|
density='coarse'
|
|
45
|
-
icon='ph--
|
|
47
|
+
icon='ph--circle--regular'
|
|
46
48
|
label='Test'
|
|
47
49
|
iconOnly
|
|
48
50
|
variant={hasAttention ? 'primary' : 'ghost'}
|
|
49
|
-
classNames='
|
|
51
|
+
classNames='w-(--dx-rail-action) h-(--dx-rail-action)'
|
|
50
52
|
/>
|
|
51
53
|
<StackItem.ResizeHandle />
|
|
52
54
|
</div>
|
|
@@ -69,22 +71,22 @@ const StoryPlank = ({ attendableId }: { attendableId: string }) => {
|
|
|
69
71
|
<StackItem.Root
|
|
70
72
|
item={{ id: attendableId }}
|
|
71
73
|
{...attentionAttrs}
|
|
72
|
-
classNames='bg-
|
|
74
|
+
classNames='bg-base-surface border-e border-separator'
|
|
73
75
|
size={30}
|
|
74
76
|
onKeyDown={handleKeyDown}
|
|
75
77
|
ref={rootElement}
|
|
76
78
|
>
|
|
77
79
|
<StoryPlankHeading attendableId={attendableId} />
|
|
78
80
|
<StackItem.Content toolbar>
|
|
79
|
-
<Toolbar.Root classNames='border-
|
|
81
|
+
<Toolbar.Root classNames='border-b border-subdued-separator'>
|
|
80
82
|
<Toolbar.Button>Test</Toolbar.Button>
|
|
81
83
|
</Toolbar.Root>
|
|
82
84
|
|
|
83
|
-
<div className={mx(container, 'm-2 bg-
|
|
85
|
+
<div className={mx(container, 'm-2 bg-active-surface')}>
|
|
84
86
|
<Input.Root>
|
|
85
87
|
<Input.Label>Level 1 (group)</Input.Label>
|
|
86
88
|
</Input.Root>
|
|
87
|
-
<div className={mx(container, 'bg-
|
|
89
|
+
<div className={mx(container, 'bg-base-surface')}>
|
|
88
90
|
<Input.Root>
|
|
89
91
|
<Input.Label>Level 2 (base)</Input.Label>
|
|
90
92
|
<Input.TextArea placeholder='Enter text' />
|
|
@@ -100,7 +102,7 @@ const DefaultStory = () => {
|
|
|
100
102
|
const state = useAtomCapability(StoryState);
|
|
101
103
|
|
|
102
104
|
return (
|
|
103
|
-
<Main.Root
|
|
105
|
+
<Main.Root navigationSidebarState='expanded'>
|
|
104
106
|
<Main.NavigationSidebar label='Navigation' classNames='grid'>
|
|
105
107
|
<NavTreeContainer tab={state.tab} />
|
|
106
108
|
</Main.NavigationSidebar>
|
|
@@ -115,11 +117,11 @@ const DefaultStory = () => {
|
|
|
115
117
|
};
|
|
116
118
|
|
|
117
119
|
const meta = {
|
|
118
|
-
title: 'plugins/plugin-navtree/NavTree',
|
|
120
|
+
title: 'plugins/plugin-navtree/components/NavTree',
|
|
119
121
|
component: NavTreeContainer,
|
|
120
122
|
render: DefaultStory,
|
|
121
123
|
decorators: [
|
|
122
|
-
|
|
124
|
+
withLayout({ layout: 'fullscreen' }),
|
|
123
125
|
withPluginManager({
|
|
124
126
|
plugins: [
|
|
125
127
|
...corePlugins(),
|
|
@@ -130,20 +132,21 @@ const meta = {
|
|
|
130
132
|
NavTreePlugin(),
|
|
131
133
|
],
|
|
132
134
|
capabilities: () => {
|
|
133
|
-
const storyStateAtom = Atom.make({ tab: 'space-0' }).pipe(Atom.keepAlive);
|
|
135
|
+
const storyStateAtom = Atom.make({ tab: 'root/space-0' }).pipe(Atom.keepAlive);
|
|
134
136
|
return [
|
|
135
137
|
Capability.contributes(StoryState, storyStateAtom),
|
|
136
|
-
Capability.contributes(
|
|
137
|
-
Capability.contributes(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
Capability.contributes(AppCapabilities.AppGraphBuilder, storybookGraphBuilders()),
|
|
139
|
+
Capability.contributes(
|
|
140
|
+
Capabilities.OperationHandler,
|
|
141
|
+
OperationHandlerSet.make(
|
|
142
|
+
Operation.withHandler(LayoutOperation.SwitchWorkspace, ({ subject }) =>
|
|
141
143
|
Effect.gen(function* () {
|
|
142
|
-
const registry: Registry.Registry = yield* Capability.get(
|
|
144
|
+
const registry: Registry.Registry = yield* Capability.get(Capabilities.AtomRegistry);
|
|
143
145
|
registry.set(storyStateAtom, { tab: subject });
|
|
144
146
|
}),
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
),
|
|
148
|
+
),
|
|
149
|
+
),
|
|
147
150
|
];
|
|
148
151
|
},
|
|
149
152
|
}),
|
|
@@ -159,7 +162,7 @@ export default meta;
|
|
|
159
162
|
type Story = StoryObj<typeof NavTreeContainer>;
|
|
160
163
|
|
|
161
164
|
export const Default: Story = {
|
|
162
|
-
play: async ({ canvasElement }) => {
|
|
165
|
+
play: async ({ canvasElement }: { canvasElement: HTMLElement }) => {
|
|
163
166
|
const canvas = within(canvasElement);
|
|
164
167
|
|
|
165
168
|
// Find the element with treegrid role and click on its parent
|
|
@@ -4,44 +4,34 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { forwardRef, useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { useAppGraph } from '@dxos/app-toolkit/ui';
|
|
8
|
+
import { Node, useConnections, useActions as useGraphActions } from '@dxos/plugin-graph';
|
|
8
9
|
import { Tabs } from '@dxos/react-ui-tabs';
|
|
10
|
+
import { byPosition } from '@dxos/util';
|
|
11
|
+
|
|
12
|
+
import { useLoadDescendents } from '#hooks';
|
|
9
13
|
|
|
10
|
-
import { useLoadDescendents } from '../../hooks';
|
|
11
|
-
import { type NavTreeItemGraphNode } from '../../types';
|
|
12
14
|
import { useNavTreeContext } from '../NavTreeContext';
|
|
13
15
|
import { L0Menu, L1Tabs, type L1TabsProps } from '../Sidebar';
|
|
14
16
|
|
|
15
17
|
export const NAV_TREE_ITEM = 'NavTreeItem';
|
|
16
18
|
|
|
17
|
-
export type NavTreeProps =
|
|
19
|
+
export type NavTreeProps = { id: string; root?: Node.Node; tab: string } & Pick<L1TabsProps, 'open'>;
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const l0Items = topLevelWorkspaces;
|
|
24
|
-
const pinnedItems = useItems(root, { disposition: 'pin-end', sort: true });
|
|
25
|
-
const userAccountItem = useItems(root, { disposition: 'user-account' })[0];
|
|
26
|
-
const topLevelItems = useMemo(
|
|
27
|
-
() => [
|
|
28
|
-
// prettier-ignore
|
|
29
|
-
...l0Items,
|
|
30
|
-
...pinnedItems,
|
|
31
|
-
...(userAccountItem ? [userAccountItem] : []),
|
|
32
|
-
],
|
|
33
|
-
[l0Items, pinnedItems, userAccountItem],
|
|
34
|
-
);
|
|
21
|
+
// TODO(wittjosiah): Refactor to Radix-style.
|
|
22
|
+
export const NavTree = forwardRef<HTMLDivElement, NavTreeProps>(({ id, root, tab, ...props }, forwardedRef) => {
|
|
23
|
+
const { onBack, onItemHover } = useNavTreeContext();
|
|
24
|
+
const { topLevelActions, l0Items, pinnedItems, userAccountItem, topLevelItems } = useTopLevelNavItems(root);
|
|
35
25
|
|
|
36
26
|
useLoadDescendents(root);
|
|
37
27
|
const path = useMemo(() => [id], [id]);
|
|
38
28
|
|
|
39
29
|
return (
|
|
40
30
|
// TODO(thure): `Tabs.Root` forces all items that should be able to receive focus to use `Tabs.Tab(Primitive)` since
|
|
41
|
-
// it uses RovingFocus and doesn
|
|
31
|
+
// it uses RovingFocus and doesn't support moving focus to an item that is not a tab. Assess whether this situation
|
|
42
32
|
// should change including whether it should motivate a change in the design/taxonomy, or if this means this should
|
|
43
33
|
// not use `react-ui-tabs` at all.
|
|
44
|
-
<Tabs.Root value={tab} orientation='vertical'
|
|
34
|
+
<Tabs.Root value={tab} orientation='vertical' classNames='relative' ref={forwardedRef}>
|
|
45
35
|
<L0Menu
|
|
46
36
|
menuActions={topLevelActions}
|
|
47
37
|
topLevelItems={l0Items}
|
|
@@ -49,8 +39,60 @@ export const NavTree = forwardRef<HTMLDivElement, NavTreeProps>(({ id, root, ...
|
|
|
49
39
|
userAccountItem={userAccountItem}
|
|
50
40
|
path={path}
|
|
51
41
|
parent={root}
|
|
42
|
+
onItemHover={onItemHover}
|
|
52
43
|
/>
|
|
53
44
|
<L1Tabs topLevelItems={topLevelItems} path={path} currentItemId={tab} onBack={onBack} {...props} />
|
|
54
45
|
</Tabs.Root>
|
|
55
46
|
);
|
|
56
47
|
});
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Partitions root children into workspaces, pinned items, user-account, and top-level actions.
|
|
51
|
+
*/
|
|
52
|
+
const useTopLevelNavItems = (root?: Node.Node) => {
|
|
53
|
+
const { graph } = useAppGraph();
|
|
54
|
+
const rootId = root?.id ?? Node.RootId;
|
|
55
|
+
const rootOutboundItems = useConnections(graph, rootId, 'child');
|
|
56
|
+
const rootActions = useGraphActions(graph, rootId);
|
|
57
|
+
|
|
58
|
+
const { topLevelActions, l0Items, pinnedItems, userAccountItem } = useMemo(() => {
|
|
59
|
+
const topLevelWorkspaces: Node.Node[] = [];
|
|
60
|
+
const outboundPinnedItems: Node.Node[] = [];
|
|
61
|
+
let userAccountItem: Node.Node | undefined;
|
|
62
|
+
for (const node of rootOutboundItems) {
|
|
63
|
+
switch (node.properties.disposition) {
|
|
64
|
+
case 'workspace':
|
|
65
|
+
topLevelWorkspaces.push(node);
|
|
66
|
+
break;
|
|
67
|
+
case 'pin-end':
|
|
68
|
+
outboundPinnedItems.push(node);
|
|
69
|
+
break;
|
|
70
|
+
case 'user-account':
|
|
71
|
+
userAccountItem ??= node;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const topLevelActions = rootActions
|
|
77
|
+
.filter((action) => action.properties.disposition === 'menu')
|
|
78
|
+
.toSorted((a, b) => byPosition(a.properties, b.properties));
|
|
79
|
+
const pinnedItems = [
|
|
80
|
+
...outboundPinnedItems,
|
|
81
|
+
...rootActions.filter((action) => action.properties.disposition === 'pin-end'),
|
|
82
|
+
].toSorted((a, b) => byPosition(a.properties, b.properties));
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
topLevelActions,
|
|
86
|
+
l0Items: topLevelWorkspaces,
|
|
87
|
+
pinnedItems,
|
|
88
|
+
userAccountItem,
|
|
89
|
+
};
|
|
90
|
+
}, [rootActions, rootOutboundItems]);
|
|
91
|
+
|
|
92
|
+
const topLevelItems = useMemo(
|
|
93
|
+
() => [...l0Items, ...pinnedItems, ...(userAccountItem ? [userAccountItem] : [])],
|
|
94
|
+
[l0Items, pinnedItems, userAccountItem],
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return { topLevelActions, l0Items, pinnedItems, userAccountItem, topLevelItems };
|
|
98
|
+
};
|
|
@@ -6,7 +6,7 @@ import { createContext, useContext } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { raise } from '@dxos/debug';
|
|
8
8
|
|
|
9
|
-
import { type NavTreeContextValue } from '
|
|
9
|
+
import { type NavTreeContextValue } from '../types';
|
|
10
10
|
|
|
11
11
|
export const NavTreeContext = createContext<NavTreeContextValue | null>(null);
|
|
12
12
|
|
|
@@ -9,33 +9,32 @@ import { RuntimePlugin } from '@dxos/app-framework';
|
|
|
9
9
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
10
10
|
import { type Node } from '@dxos/app-graph';
|
|
11
11
|
import { corePlugins } from '@dxos/plugin-testing';
|
|
12
|
-
import {
|
|
13
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
12
|
+
import { random } from '@dxos/random';
|
|
14
13
|
|
|
15
14
|
import { NavTreeItemAction, type NavTreeItemActionMenuProps } from './NavTreeItemAction';
|
|
16
15
|
|
|
17
16
|
const parent = {
|
|
18
|
-
id:
|
|
17
|
+
id: random.string.uuid(),
|
|
19
18
|
type: 'node',
|
|
20
19
|
data: null,
|
|
21
20
|
properties: {
|
|
22
|
-
label:
|
|
21
|
+
label: random.lorem.words(2),
|
|
23
22
|
icon: 'ph--circle--regular',
|
|
24
23
|
},
|
|
25
24
|
} satisfies Node.NodeArg<any>;
|
|
26
25
|
|
|
27
26
|
// TODO(burdon): Factor out across tests.
|
|
28
|
-
const menuActions =
|
|
27
|
+
const menuActions = random.helpers.multiple(
|
|
29
28
|
() =>
|
|
30
29
|
({
|
|
31
|
-
id:
|
|
30
|
+
id: random.string.uuid(),
|
|
32
31
|
type: 'action',
|
|
33
32
|
data: () =>
|
|
34
33
|
Effect.sync(() => {
|
|
35
34
|
console.log('invoke');
|
|
36
35
|
}),
|
|
37
36
|
properties: {
|
|
38
|
-
label:
|
|
37
|
+
label: random.lorem.words(2),
|
|
39
38
|
icon: 'ph--circle--regular',
|
|
40
39
|
},
|
|
41
40
|
}) satisfies Node.NodeArg<any>,
|
|
@@ -43,7 +42,7 @@ const menuActions = faker.helpers.multiple(
|
|
|
43
42
|
);
|
|
44
43
|
|
|
45
44
|
const meta = {
|
|
46
|
-
title: 'plugins/plugin-navtree/NavTreeItemAction',
|
|
45
|
+
title: 'plugins/plugin-navtree/components/NavTreeItemAction',
|
|
47
46
|
component: NavTreeItemAction,
|
|
48
47
|
args: {
|
|
49
48
|
icon: 'ph--list--regular',
|
|
@@ -52,7 +51,6 @@ const meta = {
|
|
|
52
51
|
label: 'Select action',
|
|
53
52
|
} satisfies Partial<NavTreeItemActionMenuProps>,
|
|
54
53
|
decorators: [
|
|
55
|
-
withTheme,
|
|
56
54
|
withPluginManager({
|
|
57
55
|
plugins: [...corePlugins(), RuntimePlugin()],
|
|
58
56
|
}),
|
|
@@ -2,23 +2,16 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React from 'react';
|
|
5
|
+
import React, { useCallback } from 'react';
|
|
6
6
|
|
|
7
7
|
import { type Node } from '@dxos/app-graph';
|
|
8
8
|
import { useActionRunner } from '@dxos/plugin-graph';
|
|
9
9
|
import { IconButton, toLocalizedString, useDensityContext, useTranslation } from '@dxos/react-ui';
|
|
10
|
-
import {
|
|
11
|
-
import { hoverableControlItem, hoverableOpenControlItem } from '@dxos/ui-theme';
|
|
10
|
+
import { Menu, type MenuItem } from '@dxos/react-ui-menu';
|
|
11
|
+
import { composable, composableProps, hoverableControlItem, hoverableOpenControlItem } from '@dxos/ui-theme';
|
|
12
12
|
|
|
13
|
-
import { meta } from '
|
|
14
|
-
import { type ActionProperties } from '
|
|
15
|
-
|
|
16
|
-
export type NavTreeItemActionMenuProps = ActionProperties & {
|
|
17
|
-
parent: Node.Node;
|
|
18
|
-
caller?: string;
|
|
19
|
-
monolithic?: boolean;
|
|
20
|
-
menuActions?: Node.Action[];
|
|
21
|
-
};
|
|
13
|
+
import { meta } from '#meta';
|
|
14
|
+
import { type ActionProperties } from '#types';
|
|
22
15
|
|
|
23
16
|
const fallbackIcon = 'ph--placeholder--regular';
|
|
24
17
|
|
|
@@ -26,47 +19,64 @@ const fineActionButtonProps = {
|
|
|
26
19
|
size: 4 as const,
|
|
27
20
|
density: 'fine' as const,
|
|
28
21
|
};
|
|
22
|
+
|
|
29
23
|
const coarseActionButtonProps = {
|
|
30
24
|
size: 5 as const,
|
|
31
25
|
density: 'coarse' as const,
|
|
32
26
|
};
|
|
33
27
|
|
|
34
|
-
export
|
|
35
|
-
parent
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
menuActions
|
|
40
|
-
|
|
41
|
-
}: NavTreeItemActionMenuProps) => {
|
|
42
|
-
const { t } = useTranslation(meta.id);
|
|
43
|
-
const density = useDensityContext();
|
|
44
|
-
const runAction = useActionRunner();
|
|
28
|
+
export type NavTreeItemActionMenuProps = ActionProperties & {
|
|
29
|
+
parent: Node.Node;
|
|
30
|
+
path?: string[];
|
|
31
|
+
caller?: string;
|
|
32
|
+
monolithic?: boolean;
|
|
33
|
+
menuActions?: Node.Action[];
|
|
34
|
+
};
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
36
|
+
export const NavTreeItemActionDropdownMenu = composable<HTMLButtonElement, NavTreeItemActionMenuProps>(
|
|
37
|
+
({ parent, path, label, icon, testId, menuActions, caller, ...props }, forwardedRef) => {
|
|
38
|
+
const { t } = useTranslation(meta.id);
|
|
39
|
+
const density = useDensityContext();
|
|
40
|
+
const runAction = useActionRunner();
|
|
41
|
+
const handleAction = useCallback(
|
|
42
|
+
(action: Node.Action, params: Node.InvokeProps = {}) => runAction(action, { ...params, path }),
|
|
43
|
+
[runAction, path],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Menu.Root caller={caller} onAction={handleAction}>
|
|
48
|
+
<Menu.Trigger asChild>
|
|
50
49
|
<IconButton
|
|
51
50
|
{...(density === 'coarse' ? coarseActionButtonProps : fineActionButtonProps)}
|
|
52
|
-
|
|
51
|
+
{...composableProps(props)}
|
|
52
|
+
classNames={['shrink-0 px-2 pointer-fine:px-1', hoverableControlItem, hoverableOpenControlItem]}
|
|
53
53
|
variant='ghost'
|
|
54
54
|
icon={icon ?? fallbackIcon}
|
|
55
55
|
iconOnly
|
|
56
56
|
label={toLocalizedString(label, t)}
|
|
57
57
|
data-testid={testId}
|
|
58
|
+
ref={forwardedRef}
|
|
58
59
|
/>
|
|
59
|
-
</
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
60
|
+
</Menu.Trigger>
|
|
61
|
+
<Menu.Content group={parent} items={menuActions as MenuItem[]} />
|
|
62
|
+
</Menu.Root>
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
NavTreeItemActionDropdownMenu.displayName = 'NavTreeItemActionDropdownMenu';
|
|
64
68
|
|
|
65
69
|
export const NavTreeItemMonolithicAction = (
|
|
66
|
-
props: Node.Action & {
|
|
70
|
+
props: Node.Action & {
|
|
71
|
+
parent: Node.Node;
|
|
72
|
+
path?: string[];
|
|
73
|
+
onAction?: (action: Node.Action) => void;
|
|
74
|
+
baseLabel: string;
|
|
75
|
+
},
|
|
67
76
|
) => {
|
|
68
77
|
const {
|
|
69
78
|
parent,
|
|
79
|
+
path,
|
|
70
80
|
properties: { disabled, caller, testId, icon, variant = 'ghost', iconOnly = true } = { label: 'never' },
|
|
71
81
|
baseLabel,
|
|
72
82
|
} = props;
|
|
@@ -80,7 +90,7 @@ export const NavTreeItemMonolithicAction = (
|
|
|
80
90
|
'shrink-0',
|
|
81
91
|
hoverableControlItem,
|
|
82
92
|
hoverableOpenControlItem,
|
|
83
|
-
iconOnly ? '
|
|
93
|
+
iconOnly ? 'px-2 pointer-fine:px-1' : 'p-2 pointer-fine:p-2 me-1',
|
|
84
94
|
]}
|
|
85
95
|
icon={icon ?? fallbackIcon}
|
|
86
96
|
iconOnly={iconOnly}
|
|
@@ -92,22 +102,29 @@ export const NavTreeItemMonolithicAction = (
|
|
|
92
102
|
return;
|
|
93
103
|
}
|
|
94
104
|
|
|
95
|
-
void runAction(props, caller ? { parent, caller } : { parent });
|
|
105
|
+
void runAction(props, caller ? { parent, caller, path } : { parent, path });
|
|
96
106
|
}}
|
|
97
107
|
data-testid={testId}
|
|
98
108
|
/>
|
|
99
109
|
);
|
|
100
110
|
};
|
|
101
111
|
|
|
102
|
-
export const NavTreeItemAction = ({
|
|
112
|
+
export const NavTreeItemAction = ({
|
|
113
|
+
monolithic,
|
|
114
|
+
menuActions,
|
|
115
|
+
menuType,
|
|
116
|
+
parent: node,
|
|
117
|
+
path,
|
|
118
|
+
...props
|
|
119
|
+
}: NavTreeItemActionMenuProps) => {
|
|
103
120
|
const { t } = useTranslation(meta.id);
|
|
104
121
|
|
|
105
122
|
const monolithicAction = menuActions?.length === 1 && menuActions[0];
|
|
106
123
|
const baseLabel = toLocalizedString(monolithicAction ? monolithicAction.properties!.label : props.label, t);
|
|
107
124
|
|
|
108
125
|
return monolithic && menuActions?.length === 1 ? (
|
|
109
|
-
<NavTreeItemMonolithicAction baseLabel={baseLabel} parent={node} {...menuActions[0]} />
|
|
126
|
+
<NavTreeItemMonolithicAction baseLabel={baseLabel} parent={node} path={path} {...menuActions[0]} />
|
|
110
127
|
) : (
|
|
111
|
-
<NavTreeItemActionDropdownMenu {...props} label={baseLabel} parent={node} menuActions={menuActions} />
|
|
128
|
+
<NavTreeItemActionDropdownMenu {...props} label={baseLabel} parent={node} path={path} menuActions={menuActions} />
|
|
112
129
|
);
|
|
113
130
|
};
|
|
@@ -2,40 +2,64 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { Fragment, memo } from 'react';
|
|
5
|
+
import React, { Fragment, memo, useMemo } from 'react';
|
|
6
6
|
|
|
7
7
|
import { Node } from '@dxos/app-graph';
|
|
8
8
|
import { Popover, Treegrid, toLocalizedString, useTranslation } from '@dxos/react-ui';
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import { meta } from '
|
|
10
|
+
import { useActions } from '#hooks';
|
|
11
|
+
import { meta } from '#meta';
|
|
12
|
+
|
|
12
13
|
import { NAV_TREE_ITEM } from '../NavTree';
|
|
13
14
|
import { useNavTreeContext } from '../NavTreeContext';
|
|
14
15
|
import { type NavTreeItemColumnsProps } from '../types';
|
|
15
|
-
|
|
16
16
|
import { NavTreeItemAction } from './NavTreeItemAction';
|
|
17
17
|
|
|
18
18
|
export const NavTreeItemColumns = memo(({ path, item, open }: NavTreeItemColumnsProps) => {
|
|
19
19
|
const { t } = useTranslation(meta.id);
|
|
20
|
-
const {
|
|
20
|
+
const { renderItemEnd: ItemEnd, popoverAnchorId } = useNavTreeContext();
|
|
21
21
|
|
|
22
22
|
const level = path.length - 2;
|
|
23
|
-
const { actions:
|
|
24
|
-
const
|
|
25
|
-
|
|
23
|
+
const { actions: actionsProp, groupedActions } = useActions(item);
|
|
24
|
+
const sortedActions = useMemo(
|
|
25
|
+
() =>
|
|
26
|
+
actionsProp.toSorted((actionA, actionB) => {
|
|
27
|
+
const primaryA = actionA.properties?.disposition === 'list-item-primary';
|
|
28
|
+
const primaryB = actionB.properties?.disposition === 'list-item-primary';
|
|
29
|
+
if (primaryA && !primaryB) {
|
|
30
|
+
return -1;
|
|
31
|
+
}
|
|
32
|
+
if (primaryB && !primaryA) {
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
return 0;
|
|
36
|
+
}),
|
|
37
|
+
[actionsProp],
|
|
26
38
|
);
|
|
39
|
+
const [primaryAction, ...secondaryActions] = sortedActions;
|
|
27
40
|
|
|
28
|
-
const actions = (
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
const actions = useMemo(
|
|
42
|
+
() =>
|
|
43
|
+
(primaryAction?.properties?.disposition === 'list-item-primary' ? secondaryActions : sortedActions)
|
|
44
|
+
.flatMap((action) => (Node.isAction(action) ? [action] : []))
|
|
45
|
+
.filter((a) => ['list-item', 'list-item-primary'].includes(a.properties?.disposition)),
|
|
46
|
+
[sortedActions, primaryAction],
|
|
47
|
+
);
|
|
31
48
|
|
|
32
|
-
|
|
33
|
-
|
|
49
|
+
const primaryMenuActions = useMemo(
|
|
50
|
+
() =>
|
|
51
|
+
primaryAction
|
|
52
|
+
? Node.isAction(primaryAction)
|
|
53
|
+
? [primaryAction]
|
|
54
|
+
: groupedActions[primaryAction?.id ?? '']
|
|
55
|
+
: undefined,
|
|
56
|
+
[primaryAction, groupedActions],
|
|
57
|
+
);
|
|
34
58
|
|
|
35
|
-
const ActionRoot = popoverAnchorId ===
|
|
59
|
+
const ActionRoot = popoverAnchorId === `${NAV_TREE_ITEM}:${item.id}` ? Popover.Anchor : Fragment;
|
|
36
60
|
|
|
37
61
|
return (
|
|
38
|
-
<div role='none' className='contents app-no-drag'>
|
|
62
|
+
<div role='none' className='contents dx-app-no-drag'>
|
|
39
63
|
{primaryAction?.properties?.disposition === 'list-item-primary' && !primaryAction?.properties?.disabled ? (
|
|
40
64
|
<Treegrid.Cell classNames='contents'>
|
|
41
65
|
<NavTreeItemAction
|
|
@@ -43,8 +67,9 @@ export const NavTreeItemColumns = memo(({ path, item, open }: NavTreeItemColumns
|
|
|
43
67
|
label={toLocalizedString(primaryAction.properties?.label, t)}
|
|
44
68
|
icon={primaryAction.properties?.icon ?? 'ph--placeholder--regular'}
|
|
45
69
|
parent={item}
|
|
70
|
+
path={path}
|
|
46
71
|
monolithic={Node.isAction(primaryAction)}
|
|
47
|
-
menuActions={
|
|
72
|
+
menuActions={primaryMenuActions}
|
|
48
73
|
menuType={primaryAction.properties?.menuType}
|
|
49
74
|
caller={NAV_TREE_ITEM}
|
|
50
75
|
/>
|
|
@@ -57,9 +82,10 @@ export const NavTreeItemColumns = memo(({ path, item, open }: NavTreeItemColumns
|
|
|
57
82
|
<Treegrid.Cell classNames='contents'>
|
|
58
83
|
<NavTreeItemAction
|
|
59
84
|
testId={`navtree.treeItem.actionsLevel${level}`}
|
|
60
|
-
label={t('tree
|
|
85
|
+
label={t('tree-item-actions.label')}
|
|
61
86
|
icon='ph--dots-three-vertical--regular'
|
|
62
87
|
parent={item}
|
|
88
|
+
path={path}
|
|
63
89
|
menuActions={actions}
|
|
64
90
|
menuType='dropdown'
|
|
65
91
|
caller={NAV_TREE_ITEM}
|