@dxos/plugin-navtree 0.8.4-main.5ea62a8 → 0.8.4-main.66e292d

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.
Files changed (97) hide show
  1. package/dist/lib/browser/{app-graph-builder-6JDBQ25U.mjs → app-graph-builder-OHSCN3GX.mjs} +11 -10
  2. package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-WCARAZ7J.mjs → chunk-5VLWX4PK.mjs} +4 -4
  4. package/dist/lib/browser/chunk-5VLWX4PK.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-E6MZBCU5.mjs +19 -0
  6. package/dist/lib/browser/chunk-E6MZBCU5.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-ZKVY35IG.mjs → chunk-UDAINHB6.mjs} +153 -134
  8. package/dist/lib/browser/chunk-UDAINHB6.mjs.map +7 -0
  9. package/dist/lib/browser/index.mjs +5 -7
  10. package/dist/lib/browser/index.mjs.map +3 -3
  11. package/dist/lib/browser/{intent-resolver-N35ZIHFD.mjs → intent-resolver-YUVXVPHG.mjs} +6 -6
  12. package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +7 -0
  13. package/dist/lib/browser/{keyboard-HMTMA33N.mjs → keyboard-YJEK2HTH.mjs} +8 -10
  14. package/dist/lib/browser/{keyboard-HMTMA33N.mjs.map → keyboard-YJEK2HTH.mjs.map} +2 -2
  15. package/dist/lib/browser/meta.json +1 -1
  16. package/dist/lib/browser/{react-surface-5PZ4XJT7.mjs → react-surface-V6LS3R4L.mjs} +10 -10
  17. package/dist/lib/browser/react-surface-V6LS3R4L.mjs.map +7 -0
  18. package/dist/lib/browser/{state-P5DGL7KS.mjs → state-BFD5KJCV.mjs} +7 -7
  19. package/dist/lib/browser/state-BFD5KJCV.mjs.map +7 -0
  20. package/dist/types/src/NavTreePlugin.d.ts +1 -1
  21. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  22. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  23. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  24. package/dist/types/src/capabilities/index.d.ts +12 -9
  25. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  26. package/dist/types/src/capabilities/keyboard.d.ts.map +1 -1
  27. package/dist/types/src/capabilities/state.d.ts +1 -1
  28. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  29. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +1 -1
  30. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +1 -1
  31. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  32. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +1 -2
  33. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  34. package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
  35. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  36. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +1 -2
  37. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts +1 -1
  39. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  40. package/dist/types/src/components/Sidebar/L0Menu.d.ts +2 -0
  41. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  42. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  43. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
  44. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts +1 -2
  45. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  46. package/dist/types/src/components/types.d.ts +1 -1
  47. package/dist/types/src/components/types.d.ts.map +1 -1
  48. package/dist/types/src/events.d.ts.map +1 -1
  49. package/dist/types/src/experimental/Tree.d.ts +1 -2
  50. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  51. package/dist/types/src/experimental/Tree.stories.d.ts +2 -3
  52. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  53. package/dist/types/src/meta.d.ts +2 -3
  54. package/dist/types/src/meta.d.ts.map +1 -1
  55. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  56. package/dist/types/src/testing/generator.d.ts +2 -2
  57. package/dist/types/src/testing/generator.d.ts.map +1 -1
  58. package/dist/types/src/types.d.ts +4 -3
  59. package/dist/types/src/types.d.ts.map +1 -1
  60. package/dist/types/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +48 -48
  62. package/src/NavTreePlugin.tsx +70 -71
  63. package/src/capabilities/app-graph-builder.ts +8 -7
  64. package/src/capabilities/capabilities.ts +2 -2
  65. package/src/capabilities/intent-resolver.ts +1 -1
  66. package/src/capabilities/keyboard.ts +1 -3
  67. package/src/capabilities/react-surface.tsx +4 -4
  68. package/src/capabilities/state.ts +3 -4
  69. package/src/components/CommandsDialog/CommandsDialog.tsx +14 -6
  70. package/src/components/CommandsDialog/CommandsTrigger.tsx +4 -3
  71. package/src/components/NavTree/NavTree.stories.tsx +63 -49
  72. package/src/components/NavTree/NavTree.tsx +4 -0
  73. package/src/components/NavTreeContainer.tsx +84 -45
  74. package/src/components/NavTreeDocumentTitle.tsx +2 -2
  75. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +2 -4
  76. package/src/components/NavTreeItem/NavTreeItemAction.tsx +5 -4
  77. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +27 -21
  78. package/src/components/Sidebar/L0Menu.tsx +54 -53
  79. package/src/components/Sidebar/L1Panel.tsx +7 -10
  80. package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +3 -4
  81. package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +20 -12
  82. package/src/components/types.ts +1 -1
  83. package/src/events.ts +2 -2
  84. package/src/experimental/Tree.stories.tsx +26 -30
  85. package/src/experimental/Tree.tsx +6 -23
  86. package/src/meta.ts +10 -7
  87. package/src/testing/app-graph-builder.ts +19 -18
  88. package/src/testing/generator.ts +10 -8
  89. package/src/types.ts +4 -3
  90. package/dist/lib/browser/app-graph-builder-6JDBQ25U.mjs.map +0 -7
  91. package/dist/lib/browser/chunk-ERDGQTH5.mjs +0 -16
  92. package/dist/lib/browser/chunk-ERDGQTH5.mjs.map +0 -7
  93. package/dist/lib/browser/chunk-WCARAZ7J.mjs.map +0 -7
  94. package/dist/lib/browser/chunk-ZKVY35IG.mjs.map +0 -7
  95. package/dist/lib/browser/intent-resolver-N35ZIHFD.mjs.map +0 -7
  96. package/dist/lib/browser/react-surface-5PZ4XJT7.mjs.map +0 -7
  97. package/dist/lib/browser/state-P5DGL7KS.mjs.map +0 -7
@@ -2,11 +2,10 @@
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
- import { Schema } from 'effect';
9
- import React from 'react';
6
+ import * as Schema from 'effect/Schema';
7
+ import React, { type KeyboardEvent, useCallback, useRef } from 'react';
8
+ import { expect, userEvent, within } from 'storybook/test';
10
9
 
11
10
  import {
12
11
  Capabilities,
@@ -16,22 +15,20 @@ import {
16
15
  contributes,
17
16
  createResolver,
18
17
  defineCapability,
19
- useCapability,
20
18
  } from '@dxos/app-framework';
19
+ import { useCapability } from '@dxos/app-framework/react';
21
20
  import { withPluginManager } from '@dxos/app-framework/testing';
22
21
  import { live } from '@dxos/live-object';
23
22
  import { AttentionPlugin } from '@dxos/plugin-attention';
24
- import { ClientPlugin } from '@dxos/plugin-client';
25
23
  import { GraphPlugin } from '@dxos/plugin-graph';
26
- import { SpacePlugin } from '@dxos/plugin-space';
27
24
  import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
28
25
  import { ThemePlugin } from '@dxos/plugin-theme';
29
26
  import { faker } from '@dxos/random';
30
27
  import { IconButton, Input, Main, Toolbar } from '@dxos/react-ui';
28
+ import { withTheme } from '@dxos/react-ui/testing';
31
29
  import { useAttention, useAttentionAttributes } from '@dxos/react-ui-attention';
32
30
  import { Stack, StackItem } from '@dxos/react-ui-stack';
33
31
  import { defaultTx, mx } from '@dxos/react-ui-theme';
34
- import { withLayout } from '@dxos/storybook-utils';
35
32
 
36
33
  import { NavTreePlugin } from '../../NavTreePlugin';
37
34
  import { storybookGraphBuilders } from '../../testing';
@@ -68,6 +65,15 @@ const StoryPlankHeading = ({ attendableId }: { attendableId: string }) => {
68
65
 
69
66
  const StoryPlank = ({ attendableId }: { attendableId: string }) => {
70
67
  const attentionAttrs = useAttentionAttributes(attendableId);
68
+ const rootElement = useRef<HTMLDivElement | null>(null);
69
+
70
+ // NOTE(thure): This is the same workaround as in Plank, but that component is out of scope for this story.
71
+ // TODO(thure): Tabster’s focus group should handle moving focus to Main, but something is blocking it.
72
+ const handleKeyDown = useCallback((event: KeyboardEvent) => {
73
+ if (event.target === event.currentTarget && event.key === 'Escape') {
74
+ rootElement.current?.closest('main')?.focus();
75
+ }
76
+ }, []);
71
77
 
72
78
  return (
73
79
  <StackItem.Root
@@ -75,10 +81,12 @@ const StoryPlank = ({ attendableId }: { attendableId: string }) => {
75
81
  {...attentionAttrs}
76
82
  classNames='bg-baseSurface border-ie border-separator'
77
83
  size={30}
84
+ onKeyDown={handleKeyDown}
85
+ ref={rootElement}
78
86
  >
79
87
  <StoryPlankHeading attendableId={attendableId} />
80
88
  <StackItem.Content toolbar>
81
- <Toolbar.Root classNames='border-b border-subduedSeparator'>
89
+ <Toolbar.Root classNames='border-be border-subduedSeparator'>
82
90
  <Toolbar.Button>Test</Toolbar.Button>
83
91
  </Toolbar.Root>
84
92
 
@@ -121,15 +129,16 @@ const meta = {
121
129
  component: NavTreeContainer,
122
130
  render: DefaultStory,
123
131
  decorators: [
132
+ withTheme,
124
133
  withPluginManager({
125
134
  plugins: [
126
135
  ThemePlugin({ tx: defaultTx }),
127
- StorybookLayoutPlugin({ initialState: { sidebarState: 'expanded' } }),
128
136
  GraphPlugin(),
129
137
  IntentPlugin(),
130
138
  SettingsPlugin(),
131
139
  AttentionPlugin(),
132
140
  NavTreePlugin(),
141
+ StorybookLayoutPlugin({ initialState: { sidebarState: 'expanded' } }),
133
142
  ],
134
143
  capabilities: (context) => [
135
144
  contributes(StoryState, live({ tab: 'space-0' })),
@@ -147,9 +156,9 @@ const meta = {
147
156
  ]),
148
157
  ],
149
158
  }),
150
- withLayout({ fullscreen: true }),
151
159
  ],
152
160
  parameters: {
161
+ layout: 'fullscreen',
153
162
  translations,
154
163
  },
155
164
  } satisfies Meta<typeof NavTreeContainer>;
@@ -158,43 +167,48 @@ export default meta;
158
167
 
159
168
  type Story = StoryObj<typeof NavTreeContainer>;
160
169
 
161
- export const Default: Story = {};
170
+ export const Default: Story = {
171
+ play: async ({ canvasElement }) => {
172
+ const canvas = within(canvasElement);
162
173
 
163
- // TODO(wittjosiah): Deduplicate plugins/capabilities with default story.
164
- export const WithClient: Story = {
165
- decorators: [
166
- withPluginManager({
167
- plugins: [
168
- ThemePlugin({ tx: defaultTx }),
169
- GraphPlugin(),
170
- IntentPlugin(),
171
- SettingsPlugin(),
172
- AttentionPlugin(),
173
- NavTreePlugin(),
174
- ClientPlugin({
175
- onClientInitialized: async ({ client }) => {
176
- await client.halo.createIdentity();
177
- },
178
- }),
179
- SpacePlugin(),
180
- // Needs to be last so that the dialog and popovers have access to all contexts.
181
- StorybookLayoutPlugin({ initialState: { sidebarState: 'expanded' } }),
182
- ],
183
- capabilities: (context) => [
184
- contributes(StoryState, live({ tab: 'space-0' })),
185
- contributes(Capabilities.IntentResolver, [
186
- createResolver({
187
- intent: LayoutAction.UpdateLayout,
188
- filter: (data): data is Schema.Schema.Type<typeof LayoutAction.SwitchWorkspace.fields.input> =>
189
- Schema.is(LayoutAction.SwitchWorkspace.fields.input)(data),
190
- resolve: ({ subject }) => {
191
- const state = context.getCapability(StoryState);
192
- state.tab = subject;
193
- },
194
- }),
195
- ]),
196
- ],
197
- }),
198
- withLayout({ fullscreen: true }),
199
- ],
174
+ // Find the element with treegrid role and click on its parent
175
+ const treegridElement = await canvas.findByRole('treegrid');
176
+ const treegridParent = treegridElement.parentElement;
177
+ if (treegridParent) {
178
+ await userEvent.click(treegridParent);
179
+ }
180
+
181
+ // Press Escape
182
+ await userEvent.keyboard('{Escape}');
183
+
184
+ // Confirm that focus is on an element with attribute data-main-landmark="0"
185
+ await expect(document.activeElement).toHaveAttribute('data-main-landmark', '0');
186
+
187
+ // Press Tab
188
+ await userEvent.keyboard('{Tab}');
189
+
190
+ // Confirm that focus is now on an element with data-main-landmark="1"
191
+ await expect(document.activeElement).toHaveAttribute('data-main-landmark', '1');
192
+
193
+ // Press Tab
194
+ await userEvent.keyboard('{Tab}');
195
+
196
+ // Confirm that focus is now on an element with data-main-landmark="0"
197
+ await expect(document.activeElement).toHaveAttribute('data-main-landmark', '0');
198
+
199
+ // Press Shift-Tab
200
+ await userEvent.keyboard('{Shift>}{Tab}{/Shift}');
201
+
202
+ // Press Enter
203
+ await userEvent.keyboard('{Enter}');
204
+
205
+ // Confirm that focus is now on an element with data-attendable-id="space-0:object-0"
206
+ await expect(document.activeElement).toHaveAttribute('data-attendable-id', 'space-0:object-0');
207
+
208
+ // Press Escape
209
+ await userEvent.keyboard('{Escape}');
210
+
211
+ // Confirm that focus is now on an element with data-main-landmark="1"
212
+ await expect(document.activeElement).toHaveAttribute('data-main-landmark', '1');
213
+ },
200
214
  };
@@ -47,6 +47,10 @@ export const NavTree = ({ id, root, ...props }: NavTreeProps) => {
47
47
  const path = useMemo(() => [id], [id]);
48
48
 
49
49
  return (
50
+ // TODO(thure): `Tabs.Root` forces all items that should be able to receive focus to use `Tabs.Tab(Primitive)` since
51
+ // it uses RovingFocus and doesn’t support moving focus to an item that is not a tab. Assess whether this situation
52
+ // should change including whether it should motivate a change in the design/taxonomy, or if this means this should
53
+ // not use `react-ui-tabs` at all.
50
54
  <Tabs.Root value={tab} orientation='vertical' verticalVariant='stateless' classNames='relative'>
51
55
  <L0Menu
52
56
  menuActions={topLevelActions}
@@ -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
- LayoutAction,
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 { NAVTREE_PLUGIN } from '../meta';
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', { ssr: false });
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(NAVTREE_PLUGIN);
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
- className: mx(node.properties.modified && 'italic', node.properties.className),
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,23 +156,44 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
162
156
  }),
163
157
  );
164
158
 
165
- await dispatch(createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: node.id }));
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(createIntent(LayoutAction.Open, { part: 'main', subject: [item.id] }));
170
+ await dispatch(
171
+ createIntent(LayoutAction.Open, {
172
+ part: 'main',
173
+ subject: [item.id],
174
+ }),
175
+ );
172
176
  }
173
177
  }
174
178
  },
175
179
  [dispatch, layout.active, tab, navigationSidebarState, isLg],
176
180
  );
177
181
 
182
+ const blockInstruction = useCallback(
183
+ ({ instruction, source, target }: { instruction: Instruction; source: TreeData; target: TreeData }) => {
184
+ return target.item.properties.blockInstruction?.(source, instruction) ?? false;
185
+ },
186
+ [],
187
+ );
188
+
178
189
  const canDrop = useCallback(({ source, target }: { source: TreeData; target: TreeData }) => {
179
190
  return target.item.properties.canDrop?.(source) ?? false;
180
191
  }, []);
181
192
 
193
+ const canSelect = useCallback(({ item }: { item: Node }) => {
194
+ return item.properties.selectable ?? true;
195
+ }, []);
196
+
182
197
  const handleSelect = useCallback(
183
198
  ({ item: node, path, option }: { item: Node; path: string[]; option: boolean }) => {
184
199
  if (!node.data) {
@@ -202,10 +217,19 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
202
217
  );
203
218
  } else if (option) {
204
219
  void dispatch(
205
- createIntent(LayoutAction.Close, { part: 'main', subject: [node.id], options: { state: false } }),
220
+ createIntent(LayoutAction.Close, {
221
+ part: 'main',
222
+ subject: [node.id],
223
+ options: { state: false },
224
+ }),
206
225
  );
207
226
  } else {
208
- void dispatch(createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: node.id }));
227
+ void dispatch(
228
+ createIntent(LayoutAction.ScrollIntoView, {
229
+ part: 'current',
230
+ subject: node.id,
231
+ }),
232
+ );
209
233
  }
210
234
 
211
235
  const defaultAction = graph.getActions(node.id).find((action) => action.properties?.disposition === 'default');
@@ -214,14 +238,25 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
214
238
  }
215
239
 
216
240
  if (!isLg) {
217
- void dispatch(createIntent(LayoutAction.UpdateSidebar, { part: 'sidebar', options: { state: 'closed' } }));
241
+ void dispatch(
242
+ createIntent(LayoutAction.UpdateSidebar, {
243
+ part: 'sidebar',
244
+ options: { state: 'closed' },
245
+ }),
246
+ );
218
247
  }
219
248
  },
220
249
  [graph, dispatch, isCurrent, isLg],
221
250
  );
222
251
 
223
252
  const handleBack = useCallback(
224
- () => dispatch(createIntent(LayoutAction.RevertWorkspace, { part: 'workspace', options: { revert: true } })),
253
+ () =>
254
+ dispatch(
255
+ createIntent(LayoutAction.RevertWorkspace, {
256
+ part: 'workspace',
257
+ options: { revert: true },
258
+ }),
259
+ ),
225
260
  [dispatch],
226
261
  );
227
262
 
@@ -293,41 +328,45 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
293
328
 
294
329
  const navTreeContextValue = useMemo(
295
330
  () => ({
296
- useItems,
297
- tab,
298
- useActions,
299
- loadDescendents,
300
- renderItemEnd,
301
- popoverAnchorId,
302
- topbar,
331
+ blockInstruction,
332
+ canDrop,
333
+ canSelect,
303
334
  getProps,
335
+ isAlternateTree,
304
336
  isCurrent,
305
337
  isOpen,
306
- canDrop,
307
- isAlternateTree,
308
- setAlternateTree,
309
- onTabChange: handleTabChange,
338
+ loadDescendents,
339
+ onBack: handleBack,
310
340
  onOpenChange: handleOpenChange,
311
341
  onSelect: handleSelect,
312
- onBack: handleBack,
313
- }),
314
- [
315
- tab,
316
- useActions,
317
- loadDescendents,
318
- renderItemEnd,
342
+ onTabChange: handleTabChange,
319
343
  popoverAnchorId,
344
+ renderItemEnd,
345
+ setAlternateTree,
346
+ tab,
320
347
  topbar,
348
+ useActions,
349
+ useItems,
350
+ }),
351
+ [
352
+ blockInstruction,
353
+ canDrop,
354
+ canSelect,
321
355
  getProps,
356
+ handleBack,
357
+ handleOpenChange,
358
+ handleSelect,
359
+ handleTabChange,
360
+ isAlternateTree,
322
361
  isCurrent,
323
362
  isOpen,
324
- canDrop,
325
- isAlternateTree,
363
+ loadDescendents,
364
+ popoverAnchorId,
365
+ renderItemEnd,
326
366
  setAlternateTree,
327
- handleTabChange,
328
- handleOpenChange,
329
- handleSelect,
330
- handleBack,
367
+ tab,
368
+ topbar,
369
+ useActions,
331
370
  ],
332
371
  );
333
372
 
@@ -6,10 +6,10 @@ import { useEffect } from 'react';
6
6
  import { type Node } from '@dxos/app-graph';
7
7
  import { toLocalizedString, useTranslation } from '@dxos/react-ui';
8
8
 
9
- import { NAVTREE_PLUGIN } from '../meta';
9
+ import { meta } from '../meta';
10
10
 
11
11
  export const NavTreeDocumentTitle = ({ node }: { node?: Node }) => {
12
- const { t } = useTranslation(NAVTREE_PLUGIN);
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 { withLayout, withTheme } from '@dxos/storybook-utils';
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 { NAVTREE_PLUGIN } from '../../meta';
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(NAVTREE_PLUGIN);
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
- event.stopPropagation();
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(NAVTREE_PLUGIN);
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 { DensityProvider, Popover, Treegrid, toLocalizedString, useTranslation } from '@dxos/react-ui';
8
+ import { Popover, Treegrid, toLocalizedString, useTranslation } from '@dxos/react-ui';
9
9
 
10
10
  import { useLoadDescendents } from '../../hooks';
11
- import { NAVTREE_PLUGIN } from '../../meta';
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, density = 'fine' }: NavTreeItemColumnsProps) => {
19
- const { t } = useTranslation(NAVTREE_PLUGIN);
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
- <DensityProvider density={density}>
39
- <div role='none' className='contents app-no-drag'>
40
- {primaryAction?.properties?.disposition === 'list-item-primary' && !primaryAction?.properties?.disabled ? (
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
- <Treegrid.Cell />
53
- )}
54
- <ActionRoot>
55
- {actions.length > 0 ? (
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
- <Treegrid.Cell />
67
- )}
68
- </ActionRoot>
69
- {ItemEnd && <ItemEnd node={item} open={open} />}
70
- </div>
71
- </DensityProvider>
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
  });