@dxos/plugin-navtree 0.8.4-main.406dc2a → 0.8.4-main.4a85c3132b

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