@dxos/plugin-navtree 0.8.4-main.f9ba587 → 0.8.4-main.fcfe5033a5

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 (221) hide show
  1. package/dist/lib/browser/index.mjs +168 -115
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/NavTreePlugin.d.ts +2 -1
  5. package/dist/types/src/NavTreePlugin.d.ts.map +1 -1
  6. package/dist/types/src/capabilities/app-graph-builder.d.ts +4 -2
  7. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  8. package/dist/types/src/capabilities/index.d.ts +7 -105
  9. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  10. package/dist/types/src/capabilities/keyboard.d.ts +3 -2
  11. package/dist/types/src/capabilities/keyboard.d.ts.map +1 -1
  12. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  13. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  14. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  15. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  16. package/dist/types/src/capabilities/state.d.ts +4 -18
  17. package/dist/types/src/capabilities/state.d.ts.map +1 -1
  18. package/dist/types/src/components/NavTree/NavTree.d.ts +12 -5
  19. package/dist/types/src/components/NavTree/NavTree.d.ts.map +1 -1
  20. package/dist/types/src/components/NavTree/NavTree.stories.d.ts +26 -5
  21. package/dist/types/src/components/NavTree/NavTree.stories.d.ts.map +1 -1
  22. package/dist/types/src/components/{NavTreeContext.d.ts → NavTreeContext/NavTreeContext.d.ts} +1 -1
  23. package/dist/types/src/components/NavTreeContext/NavTreeContext.d.ts.map +1 -0
  24. package/dist/types/src/components/NavTreeContext/index.d.ts +2 -0
  25. package/dist/types/src/components/NavTreeContext/index.d.ts.map +1 -0
  26. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +13 -9
  27. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  28. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +33 -4
  29. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  30. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts +2 -2
  31. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  32. package/dist/types/src/components/Sidebar/L0Menu.d.ts +10 -5
  33. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  34. package/dist/types/src/components/Sidebar/L1Panel.d.ts +4 -8
  35. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  36. package/dist/types/src/components/Sidebar/L1Tabs.d.ts +13 -0
  37. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -0
  38. package/dist/types/src/components/Sidebar/index.d.ts +1 -0
  39. package/dist/types/src/components/Sidebar/index.d.ts.map +1 -1
  40. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.d.ts.map +1 -1
  41. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts +8 -5
  42. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  43. package/dist/types/src/components/index.d.ts +1 -4
  44. package/dist/types/src/components/index.d.ts.map +1 -1
  45. package/dist/types/src/components/types.d.ts +8 -16
  46. package/dist/types/src/components/types.d.ts.map +1 -1
  47. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts +6 -0
  48. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -0
  49. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts +29 -0
  50. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.stories.d.ts.map +1 -0
  51. package/dist/types/src/containers/CommandsDialogContent/index.d.ts +2 -0
  52. package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -0
  53. package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -0
  54. package/dist/types/src/containers/CommandsTrigger/index.d.ts +2 -0
  55. package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -0
  56. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts +9 -0
  57. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -0
  58. package/dist/types/src/containers/NavTreeContainer/index.d.ts +3 -0
  59. package/dist/types/src/containers/NavTreeContainer/index.d.ts.map +1 -0
  60. package/dist/types/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.d.ts +1 -1
  61. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -0
  62. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +2 -0
  63. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -0
  64. package/dist/types/src/containers/index.d.ts +7 -0
  65. package/dist/types/src/containers/index.d.ts.map +1 -0
  66. package/dist/types/src/experimental/Tree.d.ts +4 -5
  67. package/dist/types/src/experimental/Tree.d.ts.map +1 -1
  68. package/dist/types/src/experimental/Tree.stories.d.ts +23 -6
  69. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  70. package/dist/types/src/hooks/index.d.ts +5 -0
  71. package/dist/types/src/hooks/index.d.ts.map +1 -0
  72. package/dist/types/src/hooks/useActions.d.ts +5 -0
  73. package/dist/types/src/hooks/useActions.d.ts.map +1 -0
  74. package/dist/types/src/hooks/useLoadDescendents.d.ts +6 -0
  75. package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -0
  76. package/dist/types/src/hooks/useNavTreeModel.d.ts +7 -0
  77. package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -0
  78. package/dist/types/src/hooks/useNavTreeState.d.ts +22 -0
  79. package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -0
  80. package/dist/types/src/index.d.ts +1 -2
  81. package/dist/types/src/index.d.ts.map +1 -1
  82. package/dist/types/src/meta.d.ts +3 -4
  83. package/dist/types/src/meta.d.ts.map +1 -1
  84. package/dist/types/src/operations/expose.d.ts +5 -0
  85. package/dist/types/src/operations/expose.d.ts.map +1 -0
  86. package/dist/types/src/operations/index.d.ts +3 -0
  87. package/dist/types/src/operations/index.d.ts.map +1 -0
  88. package/dist/types/src/testing/app-graph-builder.d.ts +2 -3
  89. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  90. package/dist/types/src/testing/generator.d.ts +5 -6
  91. package/dist/types/src/testing/generator.d.ts.map +1 -1
  92. package/dist/types/src/testing/update-state.d.ts +2 -2
  93. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  94. package/dist/types/src/translations.d.ts +9 -9
  95. package/dist/types/src/translations.d.ts.map +1 -1
  96. package/dist/types/src/types/capabilities.d.ts +16 -0
  97. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  98. package/dist/types/src/types/events.d.ts +5 -0
  99. package/dist/types/src/types/events.d.ts.map +1 -0
  100. package/dist/types/src/types/index.d.ts +4 -0
  101. package/dist/types/src/types/index.d.ts.map +1 -0
  102. package/dist/types/src/{types.d.ts → types/schema.d.ts} +10 -9
  103. package/dist/types/src/types/schema.d.ts.map +1 -0
  104. package/dist/types/src/util.d.ts +12 -9
  105. package/dist/types/src/util.d.ts.map +1 -1
  106. package/dist/types/tsconfig.tsbuildinfo +1 -1
  107. package/package.json +67 -56
  108. package/src/NavTreePlugin.tsx +66 -85
  109. package/src/capabilities/app-graph-builder.ts +34 -44
  110. package/src/capabilities/index.ts +10 -8
  111. package/src/capabilities/keyboard.ts +58 -51
  112. package/src/capabilities/operation-handler.ts +16 -0
  113. package/src/capabilities/react-surface.tsx +46 -38
  114. package/src/capabilities/state.ts +129 -84
  115. package/src/components/NavTree/NavTree.stories.tsx +110 -102
  116. package/src/components/NavTree/NavTree.tsx +71 -35
  117. package/src/components/{NavTreeContext.tsx → NavTreeContext/NavTreeContext.tsx} +1 -1
  118. package/src/components/NavTreeContext/index.ts +5 -0
  119. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +25 -19
  120. package/src/components/NavTreeItem/NavTreeItemAction.tsx +73 -49
  121. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +70 -38
  122. package/src/components/Sidebar/L0Menu.tsx +176 -210
  123. package/src/components/Sidebar/L1Panel.tsx +271 -135
  124. package/src/components/Sidebar/L1Tabs.tsx +42 -0
  125. package/src/components/Sidebar/index.ts +1 -0
  126. package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +9 -9
  127. package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +20 -12
  128. package/src/components/index.ts +2 -4
  129. package/src/components/types.ts +15 -19
  130. package/src/containers/CommandsDialogContent/CommandsDialogContent.stories.tsx +45 -0
  131. package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +127 -0
  132. package/src/containers/CommandsDialogContent/index.ts +5 -0
  133. package/src/containers/CommandsTrigger/CommandsTrigger.tsx +28 -0
  134. package/src/containers/CommandsTrigger/index.ts +5 -0
  135. package/src/containers/NavTreeContainer/NavTreeContainer.tsx +280 -0
  136. package/src/containers/NavTreeContainer/index.ts +6 -0
  137. package/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.tsx +5 -4
  138. package/src/containers/NavTreeDocumentTitle/index.ts +5 -0
  139. package/src/containers/index.ts +12 -0
  140. package/src/experimental/Tree.stories.tsx +110 -115
  141. package/src/experimental/Tree.tsx +16 -34
  142. package/src/hooks/index.ts +8 -0
  143. package/src/hooks/useActions.ts +34 -0
  144. package/src/hooks/useLoadDescendents.ts +23 -0
  145. package/src/hooks/useNavTreeModel.ts +118 -0
  146. package/src/hooks/useNavTreeState.ts +42 -0
  147. package/src/index.ts +1 -2
  148. package/src/meta.ts +11 -8
  149. package/src/operations/expose.ts +42 -0
  150. package/src/operations/index.ts +7 -0
  151. package/src/testing/app-graph-builder.ts +167 -238
  152. package/src/testing/generator.ts +41 -39
  153. package/src/testing/update-state.ts +6 -6
  154. package/src/translations.ts +9 -9
  155. package/src/types/capabilities.ts +21 -0
  156. package/src/types/events.ts +12 -0
  157. package/src/types/index.ts +7 -0
  158. package/src/{types.ts → types/schema.ts} +9 -8
  159. package/src/util.ts +26 -33
  160. package/dist/lib/browser/app-graph-builder-5J4LRWDX.mjs +0 -45
  161. package/dist/lib/browser/app-graph-builder-5J4LRWDX.mjs.map +0 -7
  162. package/dist/lib/browser/chunk-ERDGQTH5.mjs +0 -16
  163. package/dist/lib/browser/chunk-ERDGQTH5.mjs.map +0 -7
  164. package/dist/lib/browser/chunk-TZA65AKW.mjs +0 -1428
  165. package/dist/lib/browser/chunk-TZA65AKW.mjs.map +0 -7
  166. package/dist/lib/browser/chunk-WCARAZ7J.mjs +0 -15
  167. package/dist/lib/browser/chunk-WCARAZ7J.mjs.map +0 -7
  168. package/dist/lib/browser/intent-resolver-3XYN2CBU.mjs +0 -47
  169. package/dist/lib/browser/intent-resolver-3XYN2CBU.mjs.map +0 -7
  170. package/dist/lib/browser/keyboard-QRS6WOQH.mjs +0 -51
  171. package/dist/lib/browser/keyboard-QRS6WOQH.mjs.map +0 -7
  172. package/dist/lib/browser/react-surface-N2HPMYCU.mjs +0 -51
  173. package/dist/lib/browser/react-surface-N2HPMYCU.mjs.map +0 -7
  174. package/dist/lib/browser/state-54DY3DOY.mjs +0 -117
  175. package/dist/lib/browser/state-54DY3DOY.mjs.map +0 -7
  176. package/dist/lib/node-esm/app-graph-builder-GY7GCTGU.mjs +0 -46
  177. package/dist/lib/node-esm/app-graph-builder-GY7GCTGU.mjs.map +0 -7
  178. package/dist/lib/node-esm/chunk-CY7QWR4R.mjs +0 -1429
  179. package/dist/lib/node-esm/chunk-CY7QWR4R.mjs.map +0 -7
  180. package/dist/lib/node-esm/chunk-P2JGW3WR.mjs +0 -18
  181. package/dist/lib/node-esm/chunk-P2JGW3WR.mjs.map +0 -7
  182. package/dist/lib/node-esm/chunk-V3QXFDDP.mjs +0 -16
  183. package/dist/lib/node-esm/chunk-V3QXFDDP.mjs.map +0 -7
  184. package/dist/lib/node-esm/index.mjs +0 -150
  185. package/dist/lib/node-esm/index.mjs.map +0 -7
  186. package/dist/lib/node-esm/intent-resolver-R7UNPSZY.mjs +0 -48
  187. package/dist/lib/node-esm/intent-resolver-R7UNPSZY.mjs.map +0 -7
  188. package/dist/lib/node-esm/keyboard-MOZWHFUM.mjs +0 -52
  189. package/dist/lib/node-esm/keyboard-MOZWHFUM.mjs.map +0 -7
  190. package/dist/lib/node-esm/meta.json +0 -1
  191. package/dist/lib/node-esm/react-surface-EW5FDMW4.mjs +0 -52
  192. package/dist/lib/node-esm/react-surface-EW5FDMW4.mjs.map +0 -7
  193. package/dist/lib/node-esm/state-Z3OTRC2I.mjs +0 -118
  194. package/dist/lib/node-esm/state-Z3OTRC2I.mjs.map +0 -7
  195. package/dist/types/src/capabilities/capabilities.d.ts +0 -21
  196. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  197. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  198. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  199. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts +0 -5
  200. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +0 -1
  201. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +0 -1
  202. package/dist/types/src/components/CommandsDialog/index.d.ts +0 -3
  203. package/dist/types/src/components/CommandsDialog/index.d.ts.map +0 -1
  204. package/dist/types/src/components/NavTreeContainer.d.ts +0 -9
  205. package/dist/types/src/components/NavTreeContainer.d.ts.map +0 -1
  206. package/dist/types/src/components/NavTreeContext.d.ts.map +0 -1
  207. package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +0 -1
  208. package/dist/types/src/events.d.ts +0 -4
  209. package/dist/types/src/events.d.ts.map +0 -1
  210. package/dist/types/src/hooks.d.ts +0 -3
  211. package/dist/types/src/hooks.d.ts.map +0 -1
  212. package/dist/types/src/types.d.ts.map +0 -1
  213. package/src/capabilities/capabilities.ts +0 -20
  214. package/src/capabilities/intent-resolver.ts +0 -37
  215. package/src/components/CommandsDialog/CommandsDialog.tsx +0 -118
  216. package/src/components/CommandsDialog/CommandsTrigger.tsx +0 -37
  217. package/src/components/CommandsDialog/index.ts +0 -6
  218. package/src/components/NavTreeContainer.tsx +0 -314
  219. package/src/events.ts +0 -11
  220. package/src/hooks.ts +0 -21
  221. /package/dist/types/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.d.ts +0 -0
@@ -2,19 +2,21 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
6
- import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
7
- import { preserveOffsetOnSource } from '@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source';
8
- import { scrollJustEnoughIntoView } from '@atlaskit/pragmatic-drag-and-drop/element/scroll-just-enough-into-view';
9
5
  import {
10
6
  type Edge,
11
7
  attachClosestEdge,
12
8
  extractClosestEdge,
13
9
  } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
10
+ import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
11
+ import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
12
+ import { preserveOffsetOnSource } from '@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source';
13
+ import { scrollJustEnoughIntoView } from '@atlaskit/pragmatic-drag-and-drop/element/scroll-just-enough-into-view';
14
+ import { useAtomValue } from '@effect-atom/atom-react';
14
15
  import React, {
15
- forwardRef,
16
16
  type MouseEvent,
17
17
  type PropsWithChildren,
18
+ forwardRef,
19
+ memo,
18
20
  useCallback,
19
21
  useLayoutEffect,
20
22
  useMemo,
@@ -23,16 +25,28 @@ import React, {
23
25
  } from 'react';
24
26
 
25
27
  import { type Node } from '@dxos/app-graph';
26
- import { invariant } from '@dxos/invariant';
27
- import { Icon, ListItem, ScrollArea, Tooltip, toLocalizedString, useMediaQuery, useTranslation } from '@dxos/react-ui';
28
- import { MenuProvider, DropdownMenu, type MenuItem } from '@dxos/react-ui-menu';
28
+ import { DxAvatar } from '@dxos/lit-ui/react';
29
+ import { useActionRunner } from '@dxos/plugin-graph';
30
+ import {
31
+ Icon,
32
+ IconButton,
33
+ ListItem,
34
+ ScrollArea,
35
+ type ThemedClassName,
36
+ Tooltip,
37
+ toLocalizedString,
38
+ useMediaQuery,
39
+ useTranslation,
40
+ } from '@dxos/react-ui';
41
+ import { Menu, type MenuItem } from '@dxos/react-ui-menu';
29
42
  import type { StackItemRearrangeHandler } from '@dxos/react-ui-stack';
30
43
  import { Tabs } from '@dxos/react-ui-tabs';
31
- import { mx } from '@dxos/react-ui-theme';
32
- import { arrayMove, getFirstTwoRenderableChars } from '@dxos/util';
44
+ import { mx } from '@dxos/ui-theme';
45
+ import { arrayMove } from '@dxos/util';
46
+
47
+ import { useNavTreeState } from '#hooks';
48
+ import { meta } from '#meta';
33
49
 
34
- import { useLoadDescendents } from '../../hooks';
35
- import { NAVTREE_PLUGIN } from '../../meta';
36
50
  import { l0ItemType } from '../../util';
37
51
  import { useNavTreeContext } from '../NavTreeContext';
38
52
  import { UserAccountAvatar } from '../UserAccountAvatar';
@@ -47,43 +61,41 @@ type L0ItemData = {
47
61
  };
48
62
 
49
63
  type L0ItemRootProps = {
50
- item: Node<any>;
51
- parent?: Node<any>;
64
+ item: Node.Node;
65
+ parent?: Node.Node;
52
66
  path: string[];
67
+ onMouseEnter?: () => void;
53
68
  };
54
69
 
55
70
  type L0ItemProps = L0ItemRootProps & {
56
- item: Node<any>;
57
- parent?: Node<any>;
71
+ item: Node.Node;
72
+ parent?: Node.Node;
58
73
  path: string[];
59
74
  pinned?: boolean;
60
75
  onRearrange?: StackItemRearrangeHandler<L0ItemData>;
76
+ onItemHover?: (params: { item: Node.Node }) => void;
61
77
  };
62
78
 
63
79
  const useL0ItemClick = ({ item, parent, path }: L0ItemProps, type: string) => {
64
- const { tab, isCurrent, onSelect, onTabChange } = useNavTreeContext();
65
- const [isLg] = useMediaQuery('lg', { ssr: false });
80
+ const { onSelect, onTabChange } = useNavTreeContext();
81
+ const { getItem } = useNavTreeState();
82
+ const [isLg] = useMediaQuery('lg');
83
+ const runAction = useActionRunner();
66
84
 
67
85
  return useCallback(
68
86
  (event: MouseEvent) => {
69
87
  switch (type) {
70
88
  case 'action': {
71
- const {
72
- data: invoke,
73
- properties: { caller },
74
- } = item;
75
-
76
- return invoke?.(caller ? { node: parent, caller } : { node: parent });
89
+ const { properties: { caller } = {} } = item;
90
+ return void runAction(item as Node.Action, caller ? { parent, path, caller } : { parent, path });
77
91
  }
78
-
79
92
  case 'tab':
80
93
  return onTabChange?.(item);
81
-
82
94
  case 'link':
83
- return onSelect?.({ item, path, current: !isCurrent(path, item), option: event.altKey });
95
+ return onSelect?.({ item, path, current: !getItem(path).current, option: event.altKey });
84
96
  }
85
97
  },
86
- [item, parent, type, tab, isCurrent, onSelect, onTabChange, isLg],
98
+ [item, parent, type, getItem, onSelect, onTabChange, isLg, runAction],
87
99
  );
88
100
  };
89
101
 
@@ -91,74 +103,62 @@ const l0Breakpoints: Record<string, string> = {
91
103
  lg: 'hidden lg:grid',
92
104
  };
93
105
 
94
- const l0ItemRoot =
95
- 'group/l0item flex w-full justify-center items-center relative data[type!="collection"]:cursor-pointer app-no-drag dx-focus-ring-group';
96
-
97
- const l0ItemContent = 'flex justify-center items-center dx-focus-ring-group-indicator transition-colors rounded-sm';
98
-
99
- const L0ItemRoot = forwardRef<HTMLElement, PropsWithChildren<L0ItemRootProps>>(
100
- ({ item, parent, path, children }, forwardedRef) => {
101
- const { getProps } = useNavTreeContext();
102
- const { id, testId } = getProps?.(item, path) ?? {};
103
- const type = l0ItemType(item);
104
- const itemPath = useMemo(() => [...path, item.id], [item.id, path]);
105
-
106
- const { t } = useTranslation(NAVTREE_PLUGIN);
107
- const localizedString = toLocalizedString(item.properties.label, t);
108
-
109
- const handleClick = useL0ItemClick({ item, parent, path: itemPath }, type);
110
- const rootProps =
111
- type === 'tab'
112
- ? { value: item.id, tabIndex: 0, onClick: handleClick, 'data-testid': testId, 'data-itemid': id }
113
- : type !== 'collection'
114
- ? { onClick: handleClick, 'data-testid': testId, 'data-itemid': id }
115
- : { onClick: handleClick };
116
-
117
- const Root = type === 'collection' ? 'h2' : type === 'tab' ? Tabs.TabPrimitive : 'button';
118
-
119
- return (
120
- <Tooltip.Trigger asChild delayDuration={0} side='right' content={localizedString}>
121
- <Root
122
- {...(rootProps as any)}
123
- data-type={type}
124
- className={mx(l0ItemRoot, l0Breakpoints[item.properties.l0Breakpoint])}
125
- ref={forwardedRef}
126
- >
127
- {children}
128
- </Root>
129
- </Tooltip.Trigger>
130
- );
131
- },
106
+ const L0ItemRoot = memo(
107
+ forwardRef<HTMLButtonElement, PropsWithChildren<L0ItemRootProps>>(
108
+ ({ item, parent, path, onMouseEnter, children }, forwardedRef) => {
109
+ const { t } = useTranslation(meta.id);
110
+ const { model } = useNavTreeContext();
111
+ const itemPath = useMemo(() => [...path, item.id], [item.id, path]);
112
+ const { id, testId } = useAtomValue(model.itemProps(itemPath));
113
+ const localizedString = toLocalizedString(item.properties.label, t);
114
+
115
+ const type = l0ItemType(item);
116
+ const handleClick = useL0ItemClick({ item, parent, path: itemPath }, type);
117
+
118
+ return (
119
+ <Tooltip.Trigger asChild delayDuration={0} side='right' content={localizedString}>
120
+ <Tabs.TabPrimitive
121
+ className={mx(
122
+ 'group/l0item flex w-full justify-center items-center relative',
123
+ 'dx-app-no-drag dx-focus-ring-group data[type!="collection"]:cursor-pointer',
124
+ l0Breakpoints[item.properties.l0Breakpoint],
125
+ )}
126
+ tabIndex={type === 'tab' ? 0 : undefined}
127
+ data-type={type}
128
+ data-testid={testId}
129
+ data-object-id={id}
130
+ value={item.id}
131
+ onClick={handleClick}
132
+ onMouseEnter={onMouseEnter}
133
+ ref={forwardedRef}
134
+ >
135
+ {children}
136
+ </Tabs.TabPrimitive>
137
+ </Tooltip.Trigger>
138
+ );
139
+ },
140
+ ),
132
141
  );
133
142
 
134
- const L0Avatar = ({ item }: Pick<L0ItemProps, 'item'>) => {
135
- const { t } = useTranslation(NAVTREE_PLUGIN);
136
- const type = l0ItemType(item);
137
- const hue = item.properties.hue ?? null;
138
- const hueFgStyle = hue && { style: { color: `var(--dx-${hue}SurfaceText)` } };
139
- const localizedString = toLocalizedString(item.properties.label, t);
140
- const avatarValue = useMemo(
141
- () => (type === 'tab' ? getFirstTwoRenderableChars(localizedString).join('').toUpperCase() : []),
142
- [type, item.properties.label, t],
143
- );
144
-
145
- return (
146
- <span role='img' className='place-self-center text-xl font-light' {...hueFgStyle}>
147
- {avatarValue}
148
- </span>
149
- );
150
- };
143
+ export const L0ItemActiveTabIndicator = ({ classNames }: ThemedClassName<{}>) => (
144
+ <div
145
+ role='none'
146
+ className={mx(
147
+ 'hidden group-aria-selected/l0item:block absolute start-0 h-6 w-1.5 bg-accent-surface rounded-sm',
148
+ classNames,
149
+ )}
150
+ />
151
+ );
151
152
 
152
153
  // TODO(burdon): Factor out pinned (non-draggable) items.
153
- const L0Item = ({ item, parent, path, pinned, onRearrange }: L0ItemProps) => {
154
- const { t } = useTranslation(NAVTREE_PLUGIN);
155
- const itemElement = useRef<HTMLElement | null>(null);
154
+ const L0Item = memo(({ item, parent, path, pinned, onRearrange, onItemHover }: L0ItemProps) => {
155
+ const { t } = useTranslation(meta.id);
156
+ const itemElement = useRef<HTMLButtonElement | null>(null);
156
157
  const [closestEdge, setEdge] = useState<Edge | null>(null);
157
158
  const localizedString = toLocalizedString(item.properties.label, t);
158
159
  const hue = item.properties.hue ?? null;
159
160
 
160
161
  useLayoutEffect(() => {
161
- // NOTE(thure): This is copied from StackItem, perhaps this should be DRYed out.
162
162
  if (!itemElement.current || !onRearrange) {
163
163
  return;
164
164
  }
@@ -213,178 +213,153 @@ const L0Item = ({ item, parent, path, pinned, onRearrange }: L0ItemProps) => {
213
213
  );
214
214
  }, [item, onRearrange]);
215
215
 
216
+ const handleMouseEnter = useCallback(() => onItemHover?.({ item }), [item, onItemHover]);
217
+
216
218
  return (
217
- <L0ItemRoot ref={itemElement} item={item} parent={parent} path={path}>
219
+ <L0ItemRoot ref={itemElement} item={item} parent={parent} path={path} onMouseEnter={handleMouseEnter}>
218
220
  <div
219
221
  role='none'
220
222
  data-frame={true}
221
- {...(hue && { style: { background: `var(--dx-${hue}Surface)` } })}
223
+ {...(hue && { style: { background: `var(--color-${hue}-surface)` } })}
222
224
  className={mx(
223
- l0ItemContent,
225
+ 'flex justify-center items-center dx-focus-ring-group-indicator transition-colors rounded-sm',
224
226
  pinned
225
- ? 'p-2 group-hover/l0item:bg-activeSurface'
226
- : 'is-[--l0-avatar-size] bs-[--l0-avatar-size] bg-activeSurface',
227
+ ? 'p-2 group-hover/l0item:bg-active-surface'
228
+ : 'w-(--dx-l0-avatar-size) h-(--dx-l0-avatar-size) bg-active-surface',
227
229
  )}
228
230
  >
229
231
  <ItemAvatar item={item} />
230
232
  </div>
231
- <div
232
- role='none'
233
- className='hidden group-aria-selected/l0item:block absolute inline-start-0 inset-block-2 is-1 bg-accentSurface rounded-ie'
234
- />
233
+ <L0ItemActiveTabIndicator />
235
234
  <span id={`${item.id}__label`} className='sr-only'>
236
235
  {localizedString}
237
236
  </span>
238
237
  {closestEdge && <ListItem.DropIndicator edge={closestEdge} />}
239
238
  </L0ItemRoot>
240
239
  );
241
- };
240
+ });
242
241
 
243
242
  const ItemAvatar = ({ item }: Pick<L0ItemProps, 'item'>) => {
244
- const type = l0ItemType(item);
243
+ const { t } = useTranslation(meta.id);
244
+
245
+ // Actions.
245
246
  if (item.properties.icon) {
246
247
  const hue = item.properties.hue ?? null;
247
- const hueFgStyle = hue && { style: { color: `var(--dx-${hue}SurfaceText)` } };
248
- return <Icon icon={item.properties.icon} size={5} {...hueFgStyle} />;
248
+ const hueFgStyle = hue && { style: { color: `var(--color-${hue}-surface-text)` } };
249
+ return <Icon icon={item.properties.icon} size={6} {...hueFgStyle} />;
249
250
  }
250
251
 
252
+ const type = l0ItemType(item);
251
253
  if (type === 'tab' && item.properties.disposition !== 'pin-end') {
252
- return <L0Avatar item={item} />;
254
+ const hue = item.properties.hue ?? null;
255
+ const localizedString = toLocalizedString(item.properties.label, t);
256
+ return <DxAvatar hue={hue} hueVariant='surface' variant='square' size={12} fallback={localizedString} />;
253
257
  }
254
258
 
255
259
  return null;
256
260
  };
257
261
 
258
262
  //
259
- // L0Collection
263
+ // L0Menu
260
264
  //
261
265
 
262
- const L0Collection = ({ item, path }: L0ItemProps) => {
263
- const navTreeContext = useNavTreeContext();
264
- useLoadDescendents(item);
265
- const collectionItems = navTreeContext.useItems(item);
266
- const groupPath = useMemo(() => [...path, item.id], [item.id, path]);
267
- const { id, testId } = navTreeContext.getProps?.(item, path) ?? {};
266
+ export type L0MenuProps = {
267
+ menuActions: MenuItem[];
268
+ topLevelItems: Node.Node[];
269
+ pinnedItems: Node.Node[];
270
+ userAccountItem?: Node.Node;
271
+ parent?: Node.Node;
272
+ path: string[];
273
+ onItemHover?: (params: { item: Node.Node }) => void;
274
+ };
275
+
276
+ export const L0Menu = ({
277
+ menuActions,
278
+ topLevelItems,
279
+ pinnedItems,
280
+ userAccountItem,
281
+ parent,
282
+ path,
283
+ onItemHover,
284
+ }: L0MenuProps) => {
285
+ const { t } = useTranslation(meta.id);
286
+ const runAction = useActionRunner();
287
+ const handleAction = useCallback(
288
+ (action: Node.Action, params: Node.InvokeProps) => {
289
+ void runAction(action, params);
290
+ },
291
+ [runAction],
292
+ );
268
293
 
269
294
  const handleRearrange = useCallback<StackItemRearrangeHandler<L0ItemData>>(
270
295
  (source, target, closestEdge) => {
271
- invariant(item.properties.onRearrangeChildren);
272
- const sourceIndex = collectionItems.findIndex((i) => i.id === source.id);
273
- const targetIndex = collectionItems.findIndex((i) => i.id === target.id);
296
+ const sourceItem = topLevelItems.find((i) => i.id === source.id);
297
+ if (!sourceItem?.properties.onRearrange) {
298
+ return;
299
+ }
300
+
301
+ const sourceIndex = topLevelItems.findIndex((i) => i.id === source.id);
302
+ const targetIndex = topLevelItems.findIndex((i) => i.id === target.id);
274
303
  const insertIndex =
275
304
  source.id === target.id
276
305
  ? sourceIndex
277
306
  : targetIndex +
278
307
  (sourceIndex < targetIndex ? (closestEdge === 'top' ? -1 : 0) : closestEdge === 'bottom' ? 1 : 0);
279
- const nextOrder = arrayMove([...collectionItems], sourceIndex, insertIndex);
280
- return item.properties.onRearrangeChildren(nextOrder);
308
+ const nextOrder = arrayMove([...topLevelItems], sourceIndex, insertIndex);
309
+ return sourceItem.properties.onRearrange(nextOrder.map((item) => item.data));
281
310
  },
282
- [collectionItems, item.properties.onRearrangeChildren],
311
+ [topLevelItems],
283
312
  );
284
313
 
285
- return (
286
- <div
287
- role='group'
288
- className='contents group/l0c'
289
- aria-labelledby={`${item.id}__label`}
290
- data-itemid={id}
291
- data-testid={testId}
292
- >
293
- {collectionItems.map((collectionItem) => (
294
- <L0Item
295
- key={collectionItem.id}
296
- item={collectionItem}
297
- parent={item}
298
- path={groupPath}
299
- {...(item.properties.onRearrangeChildren && { onRearrange: handleRearrange })}
300
- />
301
- ))}
302
- </div>
303
- );
304
- };
305
-
306
- //
307
- // L0Menu
308
- //
309
-
310
- export type L0MenuProps = {
311
- menuActions: MenuItem[];
312
- topLevelItems: Node<any>[];
313
- pinnedItems: Node<any>[];
314
- userAccountItem?: Node<any>;
315
- parent?: Node<any>;
316
- path: string[];
317
- };
318
-
319
- export const L0Menu = ({ menuActions, topLevelItems, pinnedItems, userAccountItem, parent, path }: L0MenuProps) => {
320
- const { t } = useTranslation(NAVTREE_PLUGIN);
314
+ // Check if any items have onRearrange to enable drag-and-drop.
315
+ const hasRearrangeableItems = topLevelItems.some((item) => item.properties.onRearrange);
321
316
 
322
317
  return (
323
318
  <Tabs.Tablist
319
+ data-tauri-drag-region
324
320
  classNames={[
325
- 'group/l0 absolute z-[1] inset-block-0 inline-start-0 rounded-is',
326
- 'grid grid-cols-[var(--l0-size)] grid-rows-[var(--rail-size)_1fr_min-content_var(--l0-size)] contain-layout',
327
- '!is-[--l0-size] bg-baseSurface border-ie border-subduedSeparator app-drag pbe-[env(safe-area-inset-bottom)]',
321
+ 'group/l0 absolute z-[1] inset-y-0 start-0 rounded-is',
322
+ 'grid grid-cols-[var(--dx-l0-size)] grid-rows-[var(--dx-rail-size)_1fr_min-content_var(--dx-l0-size)] dx-contain-layout',
323
+ 'w-(--dx-l0-size) bg-toolbar-surface dx-app-drag pb-[env(safe-area-inset-bottom)]',
324
+ '[body[data-platform="macos"]_&]:pt-[30px]',
325
+ '[body[data-platform="ios"]_&]:pt-[max(env(safe-area-inset-top),0.25rem)]',
328
326
  ]}
329
327
  >
330
328
  {/* TODO(wittjosiah): Use L0Item trigger. */}
331
- <MenuProvider>
332
- <DropdownMenu.Root group={parent} items={menuActions}>
333
- <Tooltip.Trigger content={t('app menu label')} side='right' asChild>
334
- <DropdownMenu.Trigger
335
- data-testid='spacePlugin.addSpace'
336
- className={mx(l0ItemRoot, 'grid place-items-center')}
337
- >
338
- <div
339
- role='none'
340
- className={mx(
341
- l0ItemContent,
342
- 'is-[--rail-action] bs-[--rail-action] group-hover/l0item:bg-hoverSurface',
343
- )}
344
- >
345
- <Icon icon='ph--list--regular' size={5} />
346
- </div>
347
- </DropdownMenu.Trigger>
348
- </Tooltip.Trigger>
349
- </DropdownMenu.Root>
350
- </MenuProvider>
329
+ <Menu.Root onAction={handleAction}>
330
+ <Menu.Trigger asChild data-testid='spacePlugin.addSpace'>
331
+ <div role='none' className='grid place-items-center'>
332
+ <IconButton variant='ghost' icon='ph--list--regular' iconOnly label={t('app-menu.label')} />
333
+ </div>
334
+ </Menu.Trigger>
335
+ <Menu.Content group={parent} items={menuActions} />
336
+ </Menu.Root>
351
337
 
352
338
  {/* Space list. */}
353
- <ScrollArea.Root>
354
- <ScrollArea.Viewport>
355
- <div
356
- role='none'
357
- className={mx([
358
- 'flex flex-col gap-1 pbs-1',
359
- '[body[data-platform="darwin"]_&]:pbs-[calc(30px+0.25rem)]',
360
- '[body[data-platform="ios"]_&]:pbs-[max(env(safe-area-inset-top),0.25rem)]',
361
- ])}
362
- >
363
- {topLevelItems.map((item) => {
364
- if (l0ItemType(item) === 'collection') {
365
- return <L0Collection key={item.id} item={item} parent={parent} path={path} />;
366
- } else {
367
- return <L0Item key={item.id} item={item} parent={parent} path={path} />;
368
- }
369
- })}
370
- </div>
371
- <ScrollArea.Scrollbar orientation='vertical'>
372
- <ScrollArea.Thumb />
373
- </ScrollArea.Scrollbar>
339
+ <ScrollArea.Root centered thin orientation='vertical'>
340
+ <ScrollArea.Viewport classNames='flex flex-col gap-2 py-1'>
341
+ {topLevelItems.map((item) => (
342
+ <L0Item
343
+ key={item.id}
344
+ item={item}
345
+ parent={parent}
346
+ path={path}
347
+ onItemHover={onItemHover}
348
+ {...(hasRearrangeableItems && { onRearrange: handleRearrange })}
349
+ />
350
+ ))}
374
351
  </ScrollArea.Viewport>
375
352
  </ScrollArea.Root>
376
353
 
377
354
  {/* Actions. */}
378
- <div role='none' className='grid grid-cols-1 auto-rows-[--rail-action] pbs-2'>
379
- {pinnedItems
380
- .filter((item) => l0ItemType(item) !== 'collection')
381
- .map((item) => (
382
- <L0Item key={item.id} item={item} parent={parent} path={path} pinned />
383
- ))}
355
+ <div role='none' className='grid grid-cols-1 auto-rows-(--dx-rail-action) pt-2'>
356
+ {pinnedItems.map((item) => (
357
+ <L0Item key={item.id} item={item} parent={parent} path={path} pinned />
358
+ ))}
384
359
  </div>
385
360
 
386
361
  {userAccountItem && (
387
- <div role='none' className='grid app-no-drag'>
362
+ <div role='none' className='grid dx-app-no-drag'>
388
363
  <L0ItemRoot key={userAccountItem.id} item={userAccountItem} parent={parent} path={path}>
389
364
  <UserAccountAvatar
390
365
  userId={userAccountItem.properties.userId}
@@ -396,15 +371,6 @@ export const L0Menu = ({ menuActions, topLevelItems, pinnedItems, userAccountIte
396
371
  </L0ItemRoot>
397
372
  </div>
398
373
  )}
399
-
400
- <div
401
- role='none'
402
- className='hidden [body[data-platform="darwin"]_&]:block absolute block-start-0 is-[calc(var(--l0-size)-1px)] bs-[calc(40px+0.25rem)]'
403
- style={{
404
- background:
405
- 'linear-gradient(to bottom, var(--dx-sidebarSurface) 0%, var(--dx-sidebarSurface) 70%, transparent 100%)',
406
- }}
407
- />
408
374
  </Tabs.Tablist>
409
375
  );
410
376
  };