@dxos/plugin-navtree 0.7.5-labs.e27f9b9 → 0.7.5-main.2567c87

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-BL7LRXCM.mjs → app-graph-builder-OF4MMBLH.mjs} +5 -7
  2. package/dist/lib/browser/{app-graph-builder-BL7LRXCM.mjs.map → app-graph-builder-OF4MMBLH.mjs.map} +3 -3
  3. package/dist/lib/browser/chunk-IMGT2LAL.mjs +658 -0
  4. package/dist/lib/browser/chunk-IMGT2LAL.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-AOAGODQB.mjs → chunk-OI7AAA6Q.mjs} +1 -1
  6. package/dist/lib/browser/chunk-OI7AAA6Q.mjs.map +7 -0
  7. package/dist/lib/browser/index.mjs +26 -21
  8. package/dist/lib/browser/index.mjs.map +4 -4
  9. package/dist/lib/browser/intent-resolver-74JPWYBP.mjs +42 -0
  10. package/dist/lib/browser/intent-resolver-74JPWYBP.mjs.map +7 -0
  11. package/dist/lib/browser/meta.json +1 -1
  12. package/dist/lib/browser/react-surface-6QWXI53A.mjs +189 -0
  13. package/dist/lib/browser/react-surface-6QWXI53A.mjs.map +7 -0
  14. package/dist/lib/browser/{state-65TQKYX7.mjs → state-HT65TAYL.mjs} +9 -6
  15. package/dist/lib/browser/state-HT65TAYL.mjs.map +7 -0
  16. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  17. package/dist/types/src/capabilities/app-graph-builder.d.ts +22 -22
  18. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  19. package/dist/types/src/capabilities/capabilities.d.ts +2 -3
  20. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  21. package/dist/types/src/capabilities/index.d.ts +32 -88
  22. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  23. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  24. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  25. package/dist/types/src/capabilities/state.d.ts +3 -3
  26. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  27. package/dist/types/src/components/CommandsDialog.d.ts.map +1 -1
  28. package/dist/types/src/components/CommandsTrigger.d.ts.map +1 -1
  29. package/dist/types/src/components/NavTree.d.ts +20 -3
  30. package/dist/types/src/components/NavTree.d.ts.map +1 -1
  31. package/dist/types/src/components/NavTree.stories.d.ts +2 -2
  32. package/dist/types/src/components/NavTree.stories.d.ts.map +1 -1
  33. package/dist/types/src/components/NavTreeContainer.d.ts +3 -4
  34. package/dist/types/src/components/NavTreeContainer.d.ts.map +1 -1
  35. package/dist/types/src/components/NavTreeFooter.d.ts +6 -0
  36. package/dist/types/src/components/NavTreeFooter.d.ts.map +1 -0
  37. package/dist/types/src/components/NavTreeItemAction.d.ts +1 -1
  38. package/dist/types/src/components/NavTreeItemAction.d.ts.map +1 -1
  39. package/dist/types/src/components/index.d.ts +0 -2
  40. package/dist/types/src/components/index.d.ts.map +1 -1
  41. package/dist/types/src/stories/Graph.stories.d.ts +2 -2
  42. package/dist/types/src/stories/Graph.stories.d.ts.map +1 -1
  43. package/dist/types/src/translations.d.ts +6 -0
  44. package/dist/types/src/translations.d.ts.map +1 -1
  45. package/dist/types/src/types.d.ts +1 -2
  46. package/dist/types/src/types.d.ts.map +1 -1
  47. package/dist/types/src/util.d.ts +0 -9
  48. package/dist/types/src/util.d.ts.map +1 -1
  49. package/package.json +31 -33
  50. package/src/NavTreePlugin.tsx +10 -8
  51. package/src/capabilities/app-graph-builder.ts +4 -6
  52. package/src/capabilities/capabilities.ts +2 -3
  53. package/src/capabilities/intent-resolver.ts +16 -22
  54. package/src/capabilities/react-surface.tsx +41 -15
  55. package/src/capabilities/state.ts +8 -5
  56. package/src/components/CommandsDialog.tsx +4 -3
  57. package/src/components/CommandsTrigger.tsx +5 -7
  58. package/src/components/NavTree.stories.tsx +5 -5
  59. package/src/components/NavTree.tsx +116 -26
  60. package/src/components/NavTreeContainer.tsx +32 -106
  61. package/src/components/NavTreeFooter.tsx +161 -0
  62. package/src/components/NavTreeItemAction.tsx +4 -12
  63. package/src/components/NotchStart.tsx +2 -2
  64. package/src/components/index.ts +0 -2
  65. package/src/stories/Graph.stories.tsx +4 -5
  66. package/src/translations.ts +6 -0
  67. package/src/types.ts +1 -2
  68. package/src/util.ts +0 -54
  69. package/dist/lib/browser/chunk-54MPRXMH.mjs +0 -1109
  70. package/dist/lib/browser/chunk-54MPRXMH.mjs.map +0 -7
  71. package/dist/lib/browser/chunk-AOAGODQB.mjs.map +0 -7
  72. package/dist/lib/browser/intent-resolver-U25VBFH3.mjs +0 -47
  73. package/dist/lib/browser/intent-resolver-U25VBFH3.mjs.map +0 -7
  74. package/dist/lib/browser/react-surface-LNRTNSM6.mjs +0 -57
  75. package/dist/lib/browser/react-surface-LNRTNSM6.mjs.map +0 -7
  76. package/dist/lib/browser/state-65TQKYX7.mjs.map +0 -7
  77. package/dist/types/src/components/L0Menu.d.ts +0 -9
  78. package/dist/types/src/components/L0Menu.d.ts.map +0 -1
  79. package/dist/types/src/components/L1Panels.d.ts +0 -8
  80. package/dist/types/src/components/L1Panels.d.ts.map +0 -1
  81. package/dist/types/src/components/NavTreeContext.d.ts +0 -4
  82. package/dist/types/src/components/NavTreeContext.d.ts.map +0 -1
  83. package/dist/types/src/components/NavTreeItemColumns.d.ts +0 -4
  84. package/dist/types/src/components/NavTreeItemColumns.d.ts.map +0 -1
  85. package/dist/types/src/components/types.d.ts +0 -25
  86. package/dist/types/src/components/types.d.ts.map +0 -1
  87. package/dist/types/src/hooks.d.ts +0 -3
  88. package/dist/types/src/hooks.d.ts.map +0 -1
  89. package/dist/types/src/stories/StorybookNavTree.d.ts +0 -4
  90. package/dist/types/src/stories/StorybookNavTree.d.ts.map +0 -1
  91. package/src/components/L0Menu.tsx +0 -210
  92. package/src/components/L1Panels.tsx +0 -100
  93. package/src/components/NavTreeContext.tsx +0 -13
  94. package/src/components/NavTreeItemColumns.tsx +0 -71
  95. package/src/components/types.ts +0 -33
  96. package/src/hooks.ts +0 -21
  97. package/src/stories/StorybookNavTree.tsx +0 -63
@@ -2,40 +2,130 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useMemo } from 'react';
5
+ import React, { Fragment, useCallback, useEffect, type FC } from 'react';
6
6
 
7
- import { Tabs } from '@dxos/react-ui-tabs';
8
- import { byPosition } from '@dxos/util';
7
+ import { isAction, type Node } from '@dxos/app-graph';
8
+ import { Popover, toLocalizedString, Treegrid, useTranslation } from '@dxos/react-ui';
9
+ import { Tree, type TreeProps } from '@dxos/react-ui-list';
10
+ import { type MaybePromise } from '@dxos/util';
9
11
 
10
- import { L0Menu } from './L0Menu';
11
- import { L1Panels } from './L1Panels';
12
- import { useNavTreeContext } from './NavTreeContext';
13
- import { type NavTreeProps } from './types';
14
- import { useLoadDescendents } from '../hooks';
12
+ import { NavTreeItemAction } from './NavTreeItemAction';
13
+ import { NAVTREE_PLUGIN } from '../meta';
14
+ import { type FlattenedActions, type NavTreeItemGraphNode } from '../types';
15
15
 
16
16
  export const NAV_TREE_ITEM = 'NavTreeItem';
17
17
 
18
- export const NavTree = ({ id, root }: NavTreeProps) => {
19
- const { getItems, tab } = useNavTreeContext();
20
- const topLevelActions = getItems(root, 'item');
21
- const topLevelCollections = getItems(root, 'collection');
22
- const topLevelWorkspaces = getItems(root, 'workspace');
23
- const topLevelNavigation = getItems(root, 'navigation');
24
- const l0Items = useMemo(
25
- () => [...topLevelActions, ...topLevelCollections, ...topLevelWorkspaces, ...topLevelNavigation],
26
- [topLevelActions, topLevelCollections, topLevelWorkspaces, topLevelNavigation],
18
+ export type NavTreeProps = Omit<
19
+ TreeProps<NavTreeItemGraphNode>,
20
+ 'draggable' | 'gridTemplateColumns' | 'renderColumns'
21
+ > &
22
+ Pick<NavTreeColumnsProps, 'getActions' | 'loadDescendents' | 'renderItemEnd' | 'popoverAnchorId'>;
23
+
24
+ export const NavTree = ({ getActions, loadDescendents, renderItemEnd, popoverAnchorId, ...props }: NavTreeProps) => {
25
+ const renderColumns = useCallback<NonNullable<TreeProps<NavTreeItemGraphNode>['renderColumns']>>(
26
+ ({ item, path, open }) => {
27
+ return (
28
+ <NavTreeColumns
29
+ path={path}
30
+ node={item}
31
+ getActions={getActions}
32
+ open={open}
33
+ loadDescendents={loadDescendents}
34
+ renderItemEnd={renderItemEnd}
35
+ popoverAnchorId={popoverAnchorId}
36
+ />
37
+ );
38
+ },
39
+ [renderItemEnd, popoverAnchorId, loadDescendents],
40
+ );
41
+
42
+ return (
43
+ <Tree
44
+ {...props}
45
+ draggable
46
+ gridTemplateColumns='[tree-row-start] 1fr min-content min-content min-content [tree-row-end]'
47
+ renderColumns={renderColumns}
48
+ />
27
49
  );
28
- const pinnedItems = getItems(root, 'pin-end').toSorted((a, b) => byPosition(a.properties, b.properties));
29
- const topLevelItems = useMemo(() => [...l0Items, ...pinnedItems], [l0Items, pinnedItems]);
50
+ };
51
+
52
+ type NavTreeColumnsProps = {
53
+ path: string[];
54
+ node: Node;
55
+ getActions: (node: Node) => FlattenedActions;
56
+ open: boolean;
57
+ loadDescendents?: (node: Node) => MaybePromise<void>;
58
+ renderItemEnd?: FC<{ node: Node; open: boolean }>;
59
+ popoverAnchorId?: string;
60
+ };
61
+
62
+ const NavTreeColumns = ({
63
+ path,
64
+ node,
65
+ getActions,
66
+ open,
67
+ loadDescendents,
68
+ renderItemEnd: ItemEnd,
69
+ popoverAnchorId,
70
+ }: NavTreeColumnsProps) => {
71
+ const { t } = useTranslation(NAVTREE_PLUGIN);
72
+
73
+ const level = path.length - 2;
74
+
75
+ const { actions: _actions, groupedActions } = getActions(node);
76
+ const [primaryAction, ...secondaryActions] = _actions.toSorted((a, b) =>
77
+ a.properties?.disposition === 'toolbar' ? -1 : 1,
78
+ );
79
+
80
+ const actions = (primaryAction?.properties?.disposition === 'toolbar' ? secondaryActions : _actions)
81
+ .flatMap((action) => (isAction(action) ? [action] : []))
82
+ .filter((action) => !action.properties?.hidden);
83
+
84
+ const ActionRoot = popoverAnchorId === `dxos.org/ui/${NAV_TREE_ITEM}/${node.id}` ? Popover.Anchor : Fragment;
30
85
 
31
- useLoadDescendents(root);
32
- const path = useMemo(() => [id], [id]);
86
+ // TODO(thure): Ideally this should not be necessary.
87
+ useEffect(() => {
88
+ const frame = requestAnimationFrame(() => {
89
+ void loadDescendents?.(node);
90
+ if (primaryAction && !isAction(primaryAction)) {
91
+ void loadDescendents?.(primaryAction);
92
+ }
93
+ });
94
+ return () => cancelAnimationFrame(frame);
95
+ }, [primaryAction]);
33
96
 
34
97
  return (
35
- // NOTE(thure): 74px (rather than rem) is intentional in order to match the size of macOS windowing controls
36
- <Tabs.Root value={tab} orientation='vertical' verticalVariant='stateless' classNames='relative'>
37
- <L0Menu topLevelItems={l0Items} pinnedItems={pinnedItems} path={path} parent={root} />
38
- <L1Panels topLevelItems={topLevelItems} path={path} currentItemId={tab} />
39
- </Tabs.Root>
98
+ <>
99
+ {primaryAction?.properties?.disposition === 'toolbar' ? (
100
+ <NavTreeItemAction
101
+ testId={primaryAction.properties?.testId}
102
+ label={toLocalizedString(primaryAction.properties?.label, t)}
103
+ icon={primaryAction.properties?.icon ?? 'ph--placeholder--regular'}
104
+ parent={node}
105
+ monolithic={isAction(primaryAction)}
106
+ menuActions={isAction(primaryAction) ? [primaryAction] : groupedActions[primaryAction.id]}
107
+ menuType={primaryAction.properties?.menuType}
108
+ caller={NAV_TREE_ITEM}
109
+ />
110
+ ) : (
111
+ <Treegrid.Cell />
112
+ )}
113
+ <ActionRoot>
114
+ {actions.length > 0 ? (
115
+ <NavTreeItemAction
116
+ testId={`navtree.treeItem.actionsLevel${level}`}
117
+ label={t('tree item actions label')}
118
+ icon='ph--dots-three-vertical--regular'
119
+ parent={node}
120
+ menuActions={actions}
121
+ menuType='dropdown'
122
+ caller={NAV_TREE_ITEM}
123
+ />
124
+ ) : (
125
+ <Treegrid.Cell />
126
+ )}
127
+ </ActionRoot>
128
+ {ItemEnd && <ItemEnd node={node} open={open} />}
129
+ </>
40
130
  );
41
131
  };
@@ -5,28 +5,19 @@
5
5
  import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
6
6
  import { extractInstruction, type Instruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
7
7
  import { untracked } from '@preact/signals-core';
8
- import React, { memo, useCallback, useEffect, useMemo } from 'react';
8
+ import React, { memo, useCallback, useEffect } from 'react';
9
9
 
10
- import {
11
- createIntent,
12
- LayoutAction,
13
- Surface,
14
- useAppGraph,
15
- useCapability,
16
- useIntentDispatcher,
17
- useLayout,
18
- } from '@dxos/app-framework';
10
+ import { createIntent, LayoutAction, NavigationAction, useIntentDispatcher, Surface } from '@dxos/app-framework';
19
11
  import { isAction, isActionLike, type Node } from '@dxos/app-graph';
12
+ import { useGraph } from '@dxos/plugin-graph';
20
13
  import { isEchoObject, isSpace } from '@dxos/react-client/echo';
21
- import { useMediaQuery } from '@dxos/react-ui';
14
+ import { useMediaQuery, useSidebars } from '@dxos/react-ui';
22
15
  import { isTreeData, type TreeData, type PropsFromTreeItem } from '@dxos/react-ui-list';
23
16
  import { mx } from '@dxos/react-ui-theme';
24
17
  import { arrayMove } from '@dxos/util';
25
18
 
26
- import { NAV_TREE_ITEM, NavTree } from './NavTree';
27
- import { NavTreeContext } from './NavTreeContext';
28
- import { type NavTreeContextValue } from './types';
29
- import { NavTreeCapabilities } from '../capabilities';
19
+ import { NAV_TREE_ITEM, NavTree, type NavTreeProps } from './NavTree';
20
+ import { NAVTREE_PLUGIN } from '../meta';
30
21
  import { type NavTreeItemGraphNode } from '../types';
31
22
  import {
32
23
  expandActions,
@@ -35,7 +26,6 @@ import {
35
26
  getChildren,
36
27
  getParent,
37
28
  resolveMigrationOperation,
38
- expandChildrenAndActions,
39
29
  } from '../util';
40
30
 
41
31
  // TODO(thure): Is NavTree truly authoritative in this regard?
@@ -47,29 +37,24 @@ const renderItemEnd = ({ node, open }: { node: Node; open: boolean }) => (
47
37
 
48
38
  export type NavTreeContainerProps = {
49
39
  popoverAnchorId?: string;
50
- topbar?: boolean;
51
- } & Pick<NavTreeContextValue, 'tab'>;
40
+ } & Pick<NavTreeProps, 'isOpen' | 'isCurrent' | 'onOpenChange'>;
52
41
 
53
- export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeContainerProps) => {
42
+ export const NavTreeContainer = memo(({ isCurrent, popoverAnchorId, ...props }: NavTreeContainerProps) => {
43
+ const { closeNavigationSidebar } = useSidebars(NAVTREE_PLUGIN);
54
44
  const [isLg] = useMediaQuery('lg', { ssr: false });
55
45
  const { dispatchPromise: dispatch } = useIntentDispatcher();
56
- const { graph } = useAppGraph();
57
- const layout = useLayout();
58
- const { isOpen, isCurrent, setItem } = useCapability(NavTreeCapabilities.State);
46
+ const { graph } = useGraph();
59
47
 
60
48
  const getActions = useCallback((node: Node) => naturalGetActions(graph, node), [graph]);
61
49
 
62
50
  const getItems = useCallback(
63
- (node?: Node, disposition?: string) => {
51
+ (node?: Node) => {
64
52
  return graph.nodes(node ?? graph.root, {
65
53
  filter: (node): node is Node => {
66
54
  return untracked(() => {
67
55
  const action = isActionLike(node);
68
- if (!disposition) {
69
- return !action || node.properties.disposition === 'item';
70
- }
71
-
72
- return node.properties.disposition === disposition;
56
+ const disposition = node.properties.disposition;
57
+ return !action || (action && disposition === 'item');
73
58
  });
74
59
  },
75
60
  });
@@ -111,33 +96,6 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
111
96
  [graph],
112
97
  );
113
98
 
114
- const onOpenChange = useCallback(
115
- ({ item: { id }, path, open }: { item: Node; path: string[]; open: boolean }) => {
116
- // TODO(thure): This might become a localstorage leak; openItemIds that no longer exist should be removed from this map.
117
- setItem(path, 'open', open);
118
-
119
- if (graph) {
120
- const node = graph.findNode(id);
121
- return node && expandChildrenAndActions(graph, node as NavTreeItemGraphNode);
122
- }
123
- },
124
- [graph],
125
- );
126
-
127
- const onTabChange = useCallback(
128
- async (node: NavTreeItemGraphNode) => {
129
- await dispatch(createIntent(LayoutAction.SwitchWorkspace, { part: 'workspace', subject: node.id }));
130
- // Open the first item if the workspace is empty.
131
- if (layout.active.length === 0) {
132
- const [item] = getItems(node).filter((node) => !isActionLike(node));
133
- if (item) {
134
- await dispatch(createIntent(LayoutAction.Open, { part: 'main', subject: [item.id] }));
135
- }
136
- }
137
- },
138
- [dispatch, layout],
139
- );
140
-
141
99
  const canDrop = useCallback((source: TreeData, target: TreeData) => {
142
100
  const sourceNode = source.item as Node;
143
101
  const targetNode = target.item as Node;
@@ -165,19 +123,11 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
165
123
 
166
124
  const current = isCurrent(path, node);
167
125
  if (!current) {
168
- void dispatch(
169
- createIntent(LayoutAction.Open, {
170
- part: 'main',
171
- subject: [node.id],
172
- options: { key: node.properties.key },
173
- }),
174
- );
126
+ void dispatch(createIntent(NavigationAction.Open, { activeParts: { main: [node.id] } }));
175
127
  } else if (option) {
176
- void dispatch(
177
- createIntent(LayoutAction.Close, { part: 'main', subject: [node.id], options: { state: false } }),
178
- );
128
+ void dispatch(createIntent(NavigationAction.Close, { activeParts: { main: [node.id] } }));
179
129
  } else {
180
- void dispatch(createIntent(LayoutAction.ScrollIntoView, { part: 'current', subject: node.id }));
130
+ void dispatch(createIntent(LayoutAction.ScrollIntoView, { id: node.id }));
181
131
  }
182
132
 
183
133
  const defaultAction = graph.actions(node).find((action) => action.properties?.disposition === 'default');
@@ -186,10 +136,10 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
186
136
  }
187
137
 
188
138
  if (!isLg) {
189
- void dispatch(createIntent(LayoutAction.UpdateSidebar, { part: 'sidebar', options: { state: 'closed' } }));
139
+ closeNavigationSidebar();
190
140
  }
191
141
  },
192
- [graph, dispatch, isCurrent, isLg],
142
+ [graph, dispatch, isCurrent, isLg, closeNavigationSidebar],
193
143
  );
194
144
 
195
145
  // TODO(wittjosiah): Factor out hook.
@@ -251,44 +201,20 @@ export const NavTreeContainer = memo(({ tab, popoverAnchorId, topbar }: NavTreeC
251
201
  });
252
202
  }, [graph]);
253
203
 
254
- const navTreeContextValue = useMemo(
255
- () => ({
256
- tab,
257
- onTabChange,
258
- getActions,
259
- loadDescendents,
260
- renderItemEnd,
261
- popoverAnchorId,
262
- topbar,
263
- getItems,
264
- getProps,
265
- isCurrent,
266
- isOpen,
267
- onOpenChange,
268
- canDrop,
269
- onSelect: handleSelect,
270
- }),
271
- [
272
- tab,
273
- onTabChange,
274
- getActions,
275
- loadDescendents,
276
- renderItemEnd,
277
- popoverAnchorId,
278
- topbar,
279
- getItems,
280
- getProps,
281
- isCurrent,
282
- isOpen,
283
- onOpenChange,
284
- canDrop,
285
- handleSelect,
286
- ],
287
- );
288
-
204
+ // TODO(thure): What gives this an inline `overflow: initial`?
289
205
  return (
290
- <NavTreeContext.Provider value={navTreeContextValue}>
291
- <NavTree root={graph.root} id={graph.root.id} />
292
- </NavTreeContext.Provider>
206
+ <NavTree
207
+ id={graph.root.id}
208
+ getActions={getActions}
209
+ getItems={getItems}
210
+ getProps={getProps}
211
+ isCurrent={isCurrent}
212
+ loadDescendents={loadDescendents}
213
+ renderItemEnd={renderItemEnd}
214
+ popoverAnchorId={popoverAnchorId}
215
+ canDrop={canDrop}
216
+ onSelect={handleSelect}
217
+ {...props}
218
+ />
293
219
  );
294
220
  });
@@ -0,0 +1,161 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { formatDistance } from 'date-fns/formatDistance';
6
+ import React from 'react';
7
+
8
+ import {
9
+ type LayoutPart,
10
+ SettingsAction,
11
+ useIntentDispatcher,
12
+ createIntent,
13
+ usePluginManager,
14
+ } from '@dxos/app-framework';
15
+ import { useConfig } from '@dxos/react-client';
16
+ import {
17
+ Button,
18
+ Link,
19
+ Message,
20
+ Popover,
21
+ Tooltip,
22
+ Trans,
23
+ useDefaultValue,
24
+ useSidebars,
25
+ useTranslation,
26
+ Icon,
27
+ IconButton,
28
+ } from '@dxos/react-ui';
29
+ import { LayoutControls } from '@dxos/react-ui-stack';
30
+ import { mx } from '@dxos/react-ui-theme';
31
+
32
+ import { NAVTREE_PLUGIN } from '../meta';
33
+
34
+ const buttonStyles = '!pli-1.5 text-xs font-normal';
35
+
36
+ const repo = 'https://github.com/dxos/dxos';
37
+
38
+ const VERSION_REGEX = /([\d.]+)/;
39
+
40
+ export const NavTreeFooter = (props: { layoutPart?: LayoutPart }) => {
41
+ const layoutPart = useDefaultValue(props.layoutPart, () => 'sidebar');
42
+ const config = useConfig();
43
+ const { t } = useTranslation(NAVTREE_PLUGIN);
44
+ const { navigationSidebarOpen } = useSidebars(NAVTREE_PLUGIN);
45
+ const { dispatchPromise: dispatch } = useIntentDispatcher();
46
+ const { version, timestamp, commitHash } = config.values.runtime?.app?.build ?? {};
47
+ const manager = usePluginManager();
48
+ const [_, v] = version?.match(VERSION_REGEX) ?? [];
49
+
50
+ const releaseUrl =
51
+ config.values.runtime?.app?.env?.DX_ENVIRONMENT === 'production'
52
+ ? `${repo}/releases/tag/v${version}`
53
+ : `${repo}/commit/${commitHash}`;
54
+
55
+ const previewUrl = 'https://docs.dxos.org/composer#technology-preview';
56
+
57
+ return (
58
+ <div
59
+ role='none'
60
+ className={mx(
61
+ 'flex pbe-[env(safe-area-inset-bottom)] box-content justify-end',
62
+ layoutPart === 'complementary' && 'md:justify-end flex-row-reverse',
63
+ )}
64
+ >
65
+ <Popover.Root>
66
+ <Popover.Trigger asChild>
67
+ <Button variant='ghost' classNames={buttonStyles} {...(!navigationSidebarOpen && { tabIndex: -1 })}>
68
+ v{v}
69
+ </Button>
70
+ </Popover.Trigger>
71
+ <Popover.Portal>
72
+ <Popover.Content
73
+ side='top'
74
+ role='message'
75
+ classNames='z-[12] bg-warning-500 max-is-[min(calc(100vw-16px),40ch)]'
76
+ >
77
+ <Message.Root valence='warning' className='rounded-be-none p-5'>
78
+ <Message.Title>
79
+ <Icon icon='ph--warning--duotone' size={6} classNames='inline mie-2' />
80
+ <span>{t('warning title')}</span>
81
+ </Message.Title>
82
+ <Message.Body>
83
+ {t('technology preview message')}
84
+ <br />
85
+ <Link href={previewUrl} target='_blank' rel='noreferrer' variant='neutral'>
86
+ {t('learn more label')}
87
+ <Icon icon='ph--arrow-square-out--bold' classNames='inline mis-1' />
88
+ </Link>
89
+ </Message.Body>
90
+ </Message.Root>
91
+ <div role='none' className='plb-4 pli-5 space-b-2 text-baseText'>
92
+ {timestamp && (
93
+ <p>
94
+ {t('released message', {
95
+ released: formatDistance(new Date(timestamp), new Date(), { addSuffix: true }),
96
+ })}
97
+ <br />
98
+ <Link href={releaseUrl} target='_blank' rel='noreferrer' variant='neutral'>
99
+ {t('see release label')}
100
+ <Icon icon='ph--arrow-square-out--bold' classNames='inline mis-1' />
101
+ </Link>
102
+ </p>
103
+ )}
104
+ <p>
105
+ <Trans
106
+ {...{
107
+ t,
108
+ i18nKey: 'powered by dxos message',
109
+ components: {
110
+ dxos: <Link href='https://dxos.org' target='_blank' rel='noreferrer' variant='neutral' />,
111
+ },
112
+ }}
113
+ />
114
+ </p>
115
+ </div>
116
+ <Popover.Arrow />
117
+ </Popover.Content>
118
+ </Popover.Portal>
119
+ </Popover.Root>
120
+
121
+ <Tooltip.Root>
122
+ <Tooltip.Trigger asChild>
123
+ {/* TODO(burdon): Reconcile with action created by LayoutPlugin. */}
124
+ <IconButton
125
+ icon='ph--gear-six--regular'
126
+ iconOnly
127
+ label={t('open settings label')}
128
+ size={4}
129
+ variant='ghost'
130
+ classNames={buttonStyles}
131
+ data-testid='treeView.openSettings'
132
+ data-joyride='welcome/settings'
133
+ {...(!navigationSidebarOpen && { tabIndex: -1 })}
134
+ iconClassNames='rotate-90'
135
+ onClick={() => dispatch(createIntent(SettingsAction.Open))}
136
+ />
137
+ </Tooltip.Trigger>
138
+ <Tooltip.Portal>
139
+ <Tooltip.Content classNames='z-[12]'>
140
+ {t('open settings label')}
141
+ <Tooltip.Arrow />
142
+ </Tooltip.Content>
143
+ </Tooltip.Portal>
144
+ </Tooltip.Root>
145
+
146
+ {/* NOTE(thure): Unpinning from the NavTree’s default position in Deck is temporarily disabled. */}
147
+ {manager.enabled.includes('dxos.org/plugin/deck') && (
148
+ <LayoutControls
149
+ variant='hide-disabled'
150
+ capabilities={{
151
+ incrementStart: false,
152
+ incrementEnd: false,
153
+ }}
154
+ onClick={(_type) => {
155
+ // TODO(Zan): If we inmplement pinning again, we should dispatch here.
156
+ }}
157
+ />
158
+ )}
159
+ </div>
160
+ );
161
+ };
@@ -24,8 +24,11 @@ export type NavTreeItemActionMenuProps = ActionProperties & {
24
24
  const fallbackIcon = 'ph--placeholder--regular';
25
25
 
26
26
  const actionButtonProps = {
27
+ iconOnly: true,
27
28
  size: 4 as const,
29
+ variant: 'ghost' as const,
28
30
  density: 'fine' as const,
31
+ classNames: mx('shrink-0 !pli-2 pointer-fine:!pli-1', hoverableControlItem, hoverableOpenControlItem),
29
32
  };
30
33
 
31
34
  export const NavTreeItemActionDropdownMenu = ({
@@ -43,10 +46,7 @@ export const NavTreeItemActionDropdownMenu = ({
43
46
  <DropdownMenu.Trigger asChild>
44
47
  <IconButton
45
48
  {...actionButtonProps}
46
- classNames={mx('shrink-0 pli-2 pointer-fine:pli-1', hoverableControlItem, hoverableOpenControlItem)}
47
- variant='ghost'
48
49
  icon={icon ?? fallbackIcon}
49
- iconOnly
50
50
  label={toLocalizedString(label, t)}
51
51
  data-testid={testId}
52
52
  suppressNextTooltip={suppressNextTooltip}
@@ -59,22 +59,14 @@ export const NavTreeItemActionDropdownMenu = ({
59
59
 
60
60
  export const NavTreeItemMonolithicAction = ({
61
61
  parent,
62
- properties: { disabled, caller, testId, icon, variant = 'ghost', iconOnly = true } = { label: 'never' },
62
+ properties: { disabled, caller, testId, icon } = { label: 'never' },
63
63
  data: invoke,
64
64
  baseLabel,
65
65
  }: Action & { parent: Node; onAction?: (action: Action) => void; baseLabel: string }) => {
66
66
  return (
67
67
  <IconButton
68
68
  {...actionButtonProps}
69
- variant={variant}
70
- classNames={mx(
71
- 'shrink-0',
72
- hoverableControlItem,
73
- hoverableOpenControlItem,
74
- iconOnly ? 'pli-2 pointer-fine:pli-1' : 'p-2 pointer-fine:p-2 mie-1',
75
- )}
76
69
  icon={icon ?? fallbackIcon}
77
- iconOnly={iconOnly}
78
70
  label={baseLabel}
79
71
  disabled={disabled}
80
72
  onClick={(event) => {
@@ -4,7 +4,7 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { createIntent, useCapabilities, useIntentDispatcher } from '@dxos/app-framework';
7
+ import { createIntent, useIntentDispatcher, useCapabilities } from '@dxos/app-framework';
8
8
  import { ClientAction } from '@dxos/plugin-client/types';
9
9
  import { ObservabilityCapabilities } from '@dxos/plugin-observability';
10
10
  import { useIdentity } from '@dxos/react-client/halo';
@@ -19,7 +19,7 @@ export const NotchStart = () => {
19
19
  const { dispatchPromise: dispatch } = useIntentDispatcher();
20
20
  return (
21
21
  <HaloButton
22
- size={12}
22
+ size={8}
23
23
  identityKey={identity?.identityKey.toHex()}
24
24
  hue={identity?.profile?.data?.hue}
25
25
  emoji={identity?.profile?.data?.emoji}
@@ -6,7 +6,5 @@ export * from './CommandsDialog';
6
6
  export * from './CommandsTrigger';
7
7
  export * from './NavTree';
8
8
  export * from './NavTreeContainer';
9
- export * from './NavTreeContext';
10
9
  export * from './NavTreeDocumentTitle';
11
10
  export * from './NotchStart';
12
- export * from './types';
@@ -18,7 +18,7 @@ import { isTreeData, type PropsFromTreeItem } from '@dxos/react-ui-list';
18
18
  import { Path } from '@dxos/react-ui-mosaic';
19
19
  import { withLayout, withTheme } from '@dxos/storybook-utils';
20
20
 
21
- import { StorybookNavTree } from './StorybookNavTree';
21
+ import { NavTree } from '../components';
22
22
  import { getActions, getChildren } from '../util';
23
23
 
24
24
  faker.seed(3);
@@ -53,9 +53,9 @@ const state = new Map<string, ReactiveObject<{ open: boolean; current: boolean }
53
53
 
54
54
  export const Default = {};
55
55
 
56
- const meta: Meta<typeof StorybookNavTree> = {
56
+ const meta: Meta<typeof NavTree> = {
57
57
  title: 'plugins/plugin-navtree/Graph',
58
- component: StorybookNavTree,
58
+ component: NavTree,
59
59
  decorators: [withTheme, withLayout({ tooltips: true })],
60
60
  render: (args) => {
61
61
  useEffect(() => {
@@ -78,10 +78,9 @@ const meta: Meta<typeof StorybookNavTree> = {
78
78
  });
79
79
  }, []);
80
80
 
81
- return <StorybookNavTree {...args} />;
81
+ return <NavTree {...args} />;
82
82
  },
83
83
  args: {
84
- root: graph.root,
85
84
  id: graph.root.id,
86
85
  getActions: (node: Node) => getActions(graph, node),
87
86
  getItems: (node?: Node) => graph.nodes(node ?? graph.root),
@@ -13,6 +13,12 @@ export default [
13
13
  'commands dialog title': 'Commands',
14
14
  'command list input placeholder': 'Search for commands…',
15
15
  'node actions menu invoker label': 'More options',
16
+ 'warning title': 'WARNING',
17
+ 'technology preview message': 'Composer is still in technology preview.',
18
+ 'learn more label': 'Learn more',
19
+ 'released message': 'This version released {{released}}.',
20
+ 'see release label': 'See release on GitHub',
21
+ 'powered by dxos message': 'Powered by <dxos>DXOS</dxos>',
16
22
  'tree item actions label': 'More actions',
17
23
  'empty branch message': ' ',
18
24
  },
package/src/types.ts CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { type Action, type ActionLike, type Node } from '@dxos/app-graph';
6
6
  import { type Label } from '@dxos/react-ui';
7
- import { type MaybePromise, type Position } from '@dxos/util';
7
+ import { type MaybePromise } from '@dxos/util';
8
8
 
9
9
  export type NavTreeItemGraphNode = Node<
10
10
  any,
@@ -31,7 +31,6 @@ type SharedProperties = {
31
31
  label: Label;
32
32
  icon?: string;
33
33
  disabled?: boolean;
34
- position?: Position;
35
34
  testId?: string;
36
35
  className?: string;
37
36
  headingClassName?: string;