@dxos/plugin-navtree 0.8.4-main.72ec0f3 → 0.8.4-main.8360d9e660

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 (232) hide show
  1. package/dist/lib/browser/CommandsDialogContent-2DKIOLJL.mjs +113 -0
  2. package/dist/lib/browser/CommandsDialogContent-2DKIOLJL.mjs.map +7 -0
  3. package/dist/lib/browser/CommandsTrigger-ABODVVMP.mjs +33 -0
  4. package/dist/lib/browser/CommandsTrigger-ABODVVMP.mjs.map +7 -0
  5. package/dist/lib/browser/NavTreeContainer-IQZCK4Y4.mjs +12 -0
  6. package/dist/lib/browser/NavTreeContainer-IQZCK4Y4.mjs.map +7 -0
  7. package/dist/lib/browser/NavTreeDocumentTitle-L6NLLWLD.mjs +26 -0
  8. package/dist/lib/browser/NavTreeDocumentTitle-L6NLLWLD.mjs.map +7 -0
  9. package/dist/lib/browser/app-graph-builder-Z43OTNI2.mjs +44 -0
  10. package/dist/lib/browser/app-graph-builder-Z43OTNI2.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-E6MZBCU5.mjs → chunk-A4VDCIJC.mjs} +3 -3
  12. package/dist/lib/browser/chunk-A4VDCIJC.mjs.map +7 -0
  13. package/dist/lib/browser/chunk-DINDC6HE.mjs +14 -0
  14. package/dist/lib/browser/chunk-DINDC6HE.mjs.map +7 -0
  15. package/dist/lib/browser/chunk-KF26WA4O.mjs +23 -0
  16. package/dist/lib/browser/chunk-KF26WA4O.mjs.map +7 -0
  17. package/dist/lib/browser/chunk-OGUYILOW.mjs +1462 -0
  18. package/dist/lib/browser/chunk-OGUYILOW.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-YYTYJW4M.mjs → keyboard-HYGFQO7T.mjs} +25 -19
  22. package/dist/lib/browser/keyboard-HYGFQO7T.mjs.map +7 -0
  23. package/dist/lib/browser/meta.json +1 -1
  24. package/dist/lib/browser/operation-resolver-VDWVUII5.mjs +49 -0
  25. package/dist/lib/browser/operation-resolver-VDWVUII5.mjs.map +7 -0
  26. package/dist/lib/browser/react-surface-LFMKIKNY.mjs +59 -0
  27. package/dist/lib/browser/react-surface-LFMKIKNY.mjs.map +7 -0
  28. package/dist/lib/browser/state-CWMNNF7K.mjs +131 -0
  29. package/dist/lib/browser/state-CWMNNF7K.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/{NavTreeContext.d.ts → NavTreeContext/NavTreeContext.d.ts} +1 -1
  59. package/dist/types/src/components/NavTreeContext/NavTreeContext.d.ts.map +1 -0
  60. package/dist/types/src/components/NavTreeContext/index.d.ts +2 -0
  61. package/dist/types/src/components/NavTreeContext/index.d.ts.map +1 -0
  62. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts +10 -8
  63. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.d.ts.map +1 -1
  64. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts +3 -2
  65. package/dist/types/src/components/NavTreeItem/NavTreeItemAction.stories.d.ts.map +1 -1
  66. package/dist/types/src/components/NavTreeItem/NavTreeItemColumns.d.ts.map +1 -1
  67. package/dist/types/src/components/Sidebar/L0Menu.d.ts +8 -5
  68. package/dist/types/src/components/Sidebar/L0Menu.d.ts.map +1 -1
  69. package/dist/types/src/components/Sidebar/L1Panel.d.ts +4 -7
  70. package/dist/types/src/components/Sidebar/L1Panel.d.ts.map +1 -1
  71. package/dist/types/src/components/Sidebar/L1Tabs.d.ts +5 -4
  72. package/dist/types/src/components/Sidebar/L1Tabs.d.ts.map +1 -1
  73. package/dist/types/src/components/UserAccountAvatar/UserAccountAvatar.stories.d.ts.map +1 -1
  74. package/dist/types/src/components/index.d.ts +0 -3
  75. package/dist/types/src/components/index.d.ts.map +1 -1
  76. package/dist/types/src/components/types.d.ts +8 -16
  77. package/dist/types/src/components/types.d.ts.map +1 -1
  78. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts +6 -0
  79. package/dist/types/src/containers/CommandsDialogContent/CommandsDialogContent.d.ts.map +1 -0
  80. package/dist/types/src/containers/CommandsDialogContent/index.d.ts +3 -0
  81. package/dist/types/src/containers/CommandsDialogContent/index.d.ts.map +1 -0
  82. package/dist/types/src/containers/CommandsTrigger/CommandsTrigger.d.ts.map +1 -0
  83. package/dist/types/src/containers/CommandsTrigger/index.d.ts +3 -0
  84. package/dist/types/src/containers/CommandsTrigger/index.d.ts.map +1 -0
  85. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts +9 -0
  86. package/dist/types/src/containers/NavTreeContainer/NavTreeContainer.d.ts.map +1 -0
  87. package/dist/types/src/containers/NavTreeContainer/index.d.ts +3 -0
  88. package/dist/types/src/containers/NavTreeContainer/index.d.ts.map +1 -0
  89. package/dist/types/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.d.ts +1 -1
  90. package/dist/types/src/containers/NavTreeDocumentTitle/NavTreeDocumentTitle.d.ts.map +1 -0
  91. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts +3 -0
  92. package/dist/types/src/containers/NavTreeDocumentTitle/index.d.ts.map +1 -0
  93. package/dist/types/src/containers/index.d.ts +7 -0
  94. package/dist/types/src/containers/index.d.ts.map +1 -0
  95. package/dist/types/src/experimental/Tree.stories.d.ts +18 -4
  96. package/dist/types/src/experimental/Tree.stories.d.ts.map +1 -1
  97. package/dist/types/src/hooks/index.d.ts +5 -0
  98. package/dist/types/src/hooks/index.d.ts.map +1 -0
  99. package/dist/types/src/hooks/useActions.d.ts +5 -0
  100. package/dist/types/src/hooks/useActions.d.ts.map +1 -0
  101. package/dist/types/src/hooks/useLoadDescendents.d.ts +6 -0
  102. package/dist/types/src/hooks/useLoadDescendents.d.ts.map +1 -0
  103. package/dist/types/src/hooks/useNavTreeModel.d.ts +7 -0
  104. package/dist/types/src/hooks/useNavTreeModel.d.ts.map +1 -0
  105. package/dist/types/src/hooks/useNavTreeState.d.ts +22 -0
  106. package/dist/types/src/hooks/useNavTreeState.d.ts.map +1 -0
  107. package/dist/types/src/index.d.ts +1 -2
  108. package/dist/types/src/index.d.ts.map +1 -1
  109. package/dist/types/src/meta.d.ts +2 -2
  110. package/dist/types/src/meta.d.ts.map +1 -1
  111. package/dist/types/src/testing/app-graph-builder.d.ts +2 -3
  112. package/dist/types/src/testing/app-graph-builder.d.ts.map +1 -1
  113. package/dist/types/src/testing/generator.d.ts +5 -6
  114. package/dist/types/src/testing/generator.d.ts.map +1 -1
  115. package/dist/types/src/testing/update-state.d.ts +2 -2
  116. package/dist/types/src/testing/update-state.d.ts.map +1 -1
  117. package/dist/types/src/types/capabilities.d.ts +16 -0
  118. package/dist/types/src/types/capabilities.d.ts.map +1 -0
  119. package/dist/types/src/types/events.d.ts +5 -0
  120. package/dist/types/src/types/events.d.ts.map +1 -0
  121. package/dist/types/src/types/index.d.ts +4 -0
  122. package/dist/types/src/types/index.d.ts.map +1 -0
  123. package/dist/types/src/{types.d.ts → types/schema.d.ts} +6 -6
  124. package/dist/types/src/types/schema.d.ts.map +1 -0
  125. package/dist/types/src/util.d.ts +11 -7
  126. package/dist/types/src/util.d.ts.map +1 -1
  127. package/dist/types/tsconfig.tsbuildinfo +1 -1
  128. package/package.json +55 -53
  129. package/src/NavTreePlugin.tsx +54 -73
  130. package/src/capabilities/app-graph-builder/app-graph-builder.ts +42 -0
  131. package/src/capabilities/app-graph-builder/index.ts +7 -0
  132. package/src/capabilities/index.ts +5 -9
  133. package/src/capabilities/keyboard/index.ts +7 -0
  134. package/src/capabilities/keyboard/keyboard.ts +66 -0
  135. package/src/capabilities/operation-resolver/index.ts +7 -0
  136. package/src/capabilities/operation-resolver/operation-resolver.ts +45 -0
  137. package/src/capabilities/react-surface/index.ts +7 -0
  138. package/src/capabilities/react-surface/react-surface.tsx +53 -0
  139. package/src/capabilities/state/index.ts +7 -0
  140. package/src/capabilities/state/state.ts +149 -0
  141. package/src/components/NavTree/NavTree.stories.tsx +44 -51
  142. package/src/components/NavTree/NavTree.tsx +63 -32
  143. package/src/components/{NavTreeContext.tsx → NavTreeContext/NavTreeContext.tsx} +1 -1
  144. package/src/components/NavTreeContext/index.ts +5 -0
  145. package/src/components/NavTreeItem/NavTreeItemAction.stories.tsx +18 -8
  146. package/src/components/NavTreeItem/NavTreeItemAction.tsx +55 -32
  147. package/src/components/NavTreeItem/NavTreeItemColumns.tsx +42 -16
  148. package/src/components/Sidebar/L0Menu.tsx +133 -162
  149. package/src/components/Sidebar/L1Panel.tsx +247 -91
  150. package/src/components/Sidebar/L1Tabs.tsx +18 -33
  151. package/src/components/UserAccountAvatar/UserAccountAvatar.stories.tsx +2 -3
  152. package/src/components/UserAccountAvatar/UserAccountAvatar.tsx +1 -1
  153. package/src/components/index.ts +1 -3
  154. package/src/components/types.ts +14 -19
  155. package/src/containers/CommandsDialogContent/CommandsDialogContent.tsx +135 -0
  156. package/src/containers/CommandsDialogContent/index.ts +7 -0
  157. package/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.tsx +5 -15
  158. package/src/containers/CommandsTrigger/index.ts +7 -0
  159. package/src/containers/NavTreeContainer/NavTreeContainer.tsx +271 -0
  160. package/src/containers/NavTreeContainer/index.ts +6 -0
  161. package/src/{components → containers/NavTreeDocumentTitle}/NavTreeDocumentTitle.tsx +4 -3
  162. package/src/containers/NavTreeDocumentTitle/index.ts +7 -0
  163. package/src/containers/index.ts +12 -0
  164. package/src/experimental/Tree.stories.tsx +55 -58
  165. package/src/experimental/Tree.tsx +4 -4
  166. package/src/hooks/index.ts +8 -0
  167. package/src/hooks/useActions.ts +34 -0
  168. package/src/hooks/useLoadDescendents.ts +23 -0
  169. package/src/hooks/useNavTreeModel.ts +118 -0
  170. package/src/hooks/useNavTreeState.ts +42 -0
  171. package/src/index.ts +1 -2
  172. package/src/meta.ts +4 -4
  173. package/src/testing/app-graph-builder.ts +158 -234
  174. package/src/testing/generator.ts +19 -17
  175. package/src/testing/update-state.ts +6 -6
  176. package/src/types/capabilities.ts +21 -0
  177. package/src/types/events.ts +12 -0
  178. package/src/types/index.ts +7 -0
  179. package/src/{types.ts → types/schema.ts} +5 -5
  180. package/src/util.ts +27 -16
  181. package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs +0 -46
  182. package/dist/lib/browser/app-graph-builder-OHSCN3GX.mjs.map +0 -7
  183. package/dist/lib/browser/chunk-5VLWX4PK.mjs +0 -15
  184. package/dist/lib/browser/chunk-5VLWX4PK.mjs.map +0 -7
  185. package/dist/lib/browser/chunk-7D2MPHXV.mjs +0 -1483
  186. package/dist/lib/browser/chunk-7D2MPHXV.mjs.map +0 -7
  187. package/dist/lib/browser/chunk-E6MZBCU5.mjs.map +0 -7
  188. package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs +0 -47
  189. package/dist/lib/browser/intent-resolver-YUVXVPHG.mjs.map +0 -7
  190. package/dist/lib/browser/keyboard-YYTYJW4M.mjs.map +0 -7
  191. package/dist/lib/browser/react-surface-J7MWWAGD.mjs +0 -51
  192. package/dist/lib/browser/react-surface-J7MWWAGD.mjs.map +0 -7
  193. package/dist/lib/browser/state-EUSVT6BI.mjs +0 -117
  194. package/dist/lib/browser/state-EUSVT6BI.mjs.map +0 -7
  195. package/dist/types/src/capabilities/app-graph-builder.d.ts +0 -4
  196. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +0 -1
  197. package/dist/types/src/capabilities/capabilities.d.ts +0 -21
  198. package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
  199. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  200. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  201. package/dist/types/src/capabilities/keyboard.d.ts +0 -4
  202. package/dist/types/src/capabilities/keyboard.d.ts.map +0 -1
  203. package/dist/types/src/capabilities/react-surface.d.ts +0 -4
  204. package/dist/types/src/capabilities/react-surface.d.ts.map +0 -1
  205. package/dist/types/src/capabilities/state.d.ts +0 -20
  206. package/dist/types/src/capabilities/state.d.ts.map +0 -1
  207. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts +0 -5
  208. package/dist/types/src/components/CommandsDialog/CommandsDialog.d.ts.map +0 -1
  209. package/dist/types/src/components/CommandsDialog/CommandsTrigger.d.ts.map +0 -1
  210. package/dist/types/src/components/CommandsDialog/index.d.ts +0 -3
  211. package/dist/types/src/components/CommandsDialog/index.d.ts.map +0 -1
  212. package/dist/types/src/components/NavTreeContainer.d.ts +0 -9
  213. package/dist/types/src/components/NavTreeContainer.d.ts.map +0 -1
  214. package/dist/types/src/components/NavTreeContext.d.ts.map +0 -1
  215. package/dist/types/src/components/NavTreeDocumentTitle.d.ts.map +0 -1
  216. package/dist/types/src/events.d.ts +0 -4
  217. package/dist/types/src/events.d.ts.map +0 -1
  218. package/dist/types/src/hooks.d.ts +0 -3
  219. package/dist/types/src/hooks.d.ts.map +0 -1
  220. package/dist/types/src/types.d.ts.map +0 -1
  221. package/src/capabilities/app-graph-builder.ts +0 -53
  222. package/src/capabilities/capabilities.ts +0 -20
  223. package/src/capabilities/intent-resolver.ts +0 -37
  224. package/src/capabilities/keyboard.ts +0 -59
  225. package/src/capabilities/react-surface.tsx +0 -45
  226. package/src/capabilities/state.ts +0 -105
  227. package/src/components/CommandsDialog/CommandsDialog.tsx +0 -126
  228. package/src/components/CommandsDialog/index.ts +0 -6
  229. package/src/components/NavTreeContainer.tsx +0 -369
  230. package/src/events.ts +0 -11
  231. package/src/hooks.ts +0 -21
  232. /package/dist/types/src/{components/CommandsDialog → containers/CommandsTrigger}/CommandsTrigger.d.ts +0 -0
@@ -0,0 +1,135 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React, { forwardRef, useMemo, useState } from 'react';
6
+
7
+ import { useOperationInvoker } from '@dxos/app-framework/ui';
8
+ import { LayoutOperation } from '@dxos/app-toolkit';
9
+ import { useAppGraph } from '@dxos/app-toolkit/ui';
10
+ import { Keyboard, keySymbols } from '@dxos/keyboard';
11
+ import { Graph, Node, useActionRunner } from '@dxos/plugin-graph';
12
+ import { useActions } from '@dxos/plugin-graph';
13
+ import { Button, Dialog, toLocalizedString, useTranslation } from '@dxos/react-ui';
14
+ import { SearchList, useSearchListResults } from '@dxos/react-ui-searchlist';
15
+ import { osTranslations } from '@dxos/ui-theme';
16
+ import { getHostPlatform } from '@dxos/util';
17
+
18
+ import { KEY_BINDING, meta } from '../../meta';
19
+
20
+ export type CommandsDialogContentProps = {
21
+ selected?: string;
22
+ };
23
+
24
+ // TODO(wittjosiah): This probably deserves its own plugin but for now it lives here w/ other navigation UI.
25
+ export const CommandsDialogContent = forwardRef<HTMLDivElement, CommandsDialogContentProps>(
26
+ ({ selected: initial }, forwardedRef) => {
27
+ const { t } = useTranslation(meta.id);
28
+ const { invokeSync } = useOperationInvoker();
29
+ const runAction = useActionRunner();
30
+ const { graph } = useAppGraph();
31
+ const [selected, setSelected] = useState<string | undefined>(initial);
32
+
33
+ // Traverse graph.
34
+ // TODO(burdon): Factor out commonality with shortcut dialog.
35
+ const allActions = useMemo(() => {
36
+ // TODO(burdon): Get from navtree (not keyboard).
37
+ const current = Keyboard.singleton.getCurrentContext();
38
+ const actionMap = new Set<string>();
39
+ const actions: Node.ActionLike[] = [];
40
+ Graph.traverse(graph, {
41
+ relation: ['child', 'action'],
42
+ visitor: (node, path) => {
43
+ if (
44
+ (Node.isAction(node) || Node.isActionGroup(node)) &&
45
+ !actionMap.has(node.id) &&
46
+ current.startsWith(path.slice(0, -1).join('/'))
47
+ ) {
48
+ actionMap.add(node.id);
49
+ actions.push(node);
50
+ }
51
+ },
52
+ });
53
+
54
+ actions.sort((a, b) => {
55
+ return toLocalizedString(a.properties.label, t)
56
+ ?.toLowerCase()
57
+ .localeCompare(toLocalizedString(b.properties.label, t)?.toLowerCase());
58
+ });
59
+
60
+ return actions;
61
+ }, [graph]);
62
+
63
+ const group = allActions.find(({ id }) => id === selected);
64
+ const groupActions = useActions(graph, group?.id);
65
+ const actions = Node.isActionGroup(group) ? groupActions : allActions;
66
+
67
+ const { results, handleSearch } = useSearchListResults({
68
+ items: actions,
69
+ extract: (action) => toLocalizedString(action.properties.label, t),
70
+ });
71
+
72
+ return (
73
+ <Dialog.Content ref={forwardedRef}>
74
+ <Dialog.Title srOnly>{t('commands dialog title', { ns: meta.id })}</Dialog.Title>
75
+ <Dialog.Body>
76
+ <SearchList.Root onSearch={handleSearch}>
77
+ <SearchList.Content>
78
+ <SearchList.Input placeholder={t('command list input placeholder')} />
79
+ <SearchList.Viewport>
80
+ {results.map((action) => {
81
+ const shortcut =
82
+ typeof action.properties.keyBinding === 'string'
83
+ ? action.properties.keyBinding
84
+ : action.properties.keyBinding?.[getHostPlatform()];
85
+
86
+ return (
87
+ <SearchList.Item
88
+ value={action.id}
89
+ key={action.id}
90
+ label={toLocalizedString(action.properties.label, t)}
91
+ icon={action.properties.icon}
92
+ suffix={shortcut ? keySymbols(shortcut).join('') : undefined}
93
+ onSelect={() => {
94
+ if (action.properties.disabled) {
95
+ return;
96
+ }
97
+
98
+ if (Node.isActionGroup(action)) {
99
+ setSelected(action.id);
100
+ return;
101
+ }
102
+
103
+ invokeSync(LayoutOperation.UpdateDialog, { state: false });
104
+ setTimeout(() => {
105
+ const node = Graph.getConnections(
106
+ graph,
107
+ group?.id ?? action.id,
108
+ Node.childRelation('inbound'),
109
+ )[0];
110
+ if (node && Node.isAction(action)) {
111
+ void runAction(action, { parent: node, caller: KEY_BINDING });
112
+ }
113
+ });
114
+ }}
115
+ classNames='flex items-center gap-2'
116
+ disabled={action.properties.disabled}
117
+ {...(action.properties?.testId && {
118
+ 'data-testid': action.properties.testId,
119
+ })}
120
+ />
121
+ );
122
+ })}
123
+ </SearchList.Viewport>
124
+ </SearchList.Content>
125
+ </SearchList.Root>
126
+ </Dialog.Body>
127
+ <Dialog.ActionBar>
128
+ <Dialog.Close asChild>
129
+ <Button classNames='w-full'>{t('close label', { ns: osTranslations })}</Button>
130
+ </Dialog.Close>
131
+ </Dialog.ActionBar>
132
+ </Dialog.Content>
133
+ );
134
+ },
135
+ );
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { CommandsDialogContent } from './CommandsDialogContent';
6
+
7
+ export default CommandsDialogContent;
@@ -4,30 +4,20 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
- import { LayoutAction, createIntent } from '@dxos/app-framework';
8
- import { useIntentDispatcher } from '@dxos/app-framework/react';
7
+ import { useOperationInvoker } from '@dxos/app-framework/ui';
8
+ import { LayoutOperation } from '@dxos/app-toolkit';
9
9
  import { Button, Icon, useTranslation } from '@dxos/react-ui';
10
10
 
11
11
  import { COMMANDS_DIALOG, meta } from '../../meta';
12
12
 
13
13
  // TODO(thure): Refactor to be handled by a more appropriate plugin.
14
14
  export const CommandsTrigger = () => {
15
- const { dispatchPromise: dispatch } = useIntentDispatcher();
15
+ const { invokeSync } = useOperationInvoker();
16
16
  const { t } = useTranslation(meta.id);
17
17
  return (
18
18
  <Button
19
- classNames='m-1 pli-1 lg:pli-2'
20
- onClick={() =>
21
- dispatch(
22
- createIntent(LayoutAction.UpdateDialog, {
23
- part: 'dialog',
24
- subject: COMMANDS_DIALOG,
25
- options: {
26
- blockAlign: 'start',
27
- },
28
- }),
29
- )
30
- }
19
+ classNames='m-1 px-1 lg:px-2'
20
+ onClick={() => invokeSync(LayoutOperation.UpdateDialog, { subject: COMMANDS_DIALOG, blockAlign: 'start' })}
31
21
  >
32
22
  <span className='text-description font-normal grow text-start'>{t('command list input placeholder')}</span>
33
23
  <Icon icon='ph--magnifying-glass--regular' size={5} />
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { CommandsTrigger } from './CommandsTrigger';
6
+
7
+ export default CommandsTrigger;
@@ -0,0 +1,271 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
6
+ import { type Instruction, extractInstruction } from '@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item';
7
+ import React, { forwardRef, memo, useCallback, useEffect, useMemo, useRef } from 'react';
8
+
9
+ import { Surface, useOperationInvoker } from '@dxos/app-framework/ui';
10
+ import { LayoutOperation } from '@dxos/app-toolkit';
11
+ import { useAppGraph, useLayout } from '@dxos/app-toolkit/ui';
12
+ import { Graph, Node, useActionRunner } from '@dxos/plugin-graph';
13
+ import { useMediaQuery, useSidebars } from '@dxos/react-ui';
14
+ import { type TreeData, isTreeData } from '@dxos/react-ui-list';
15
+ import { arrayMove } from '@dxos/util';
16
+
17
+ import { NAV_TREE_ITEM, NavTree, NavTreeContext } from '../../components';
18
+ import { useNavTreeModel, useNavTreeState } from '../../hooks';
19
+ import { meta } from '../../meta';
20
+ import { type NavTreeItemGraphNode } from '../../types';
21
+ import { filterItems, getParent, resolveMigrationOperation } from '../../util';
22
+
23
+ // TODO(thure): Is NavTree truly authoritative in this regard?
24
+ export const NODE_TYPE = 'dxos/app-graph/node';
25
+
26
+ // TODO(wittjosiah): Avoid using Surface within the navtree, prefer declarative data flow.
27
+ const NavTreeItemEnd = ({ node, open }: { node: Node.Node; open: boolean }) => {
28
+ const data = useMemo(() => ({ id: node.id, subject: node.data, open }), [node.id, node.data, open]);
29
+ return <Surface.Surface role='navtree-item-end' data={data} limit={1} />;
30
+ };
31
+
32
+ const getItems = (graph: Graph.ReadableGraph, node?: Node.Node, disposition?: string) => {
33
+ return Graph.getConnections(graph, node?.id ?? Node.RootId, 'child').filter((node) => filterItems(node, disposition));
34
+ };
35
+
36
+ export type NavTreeContainerProps = {
37
+ popoverAnchorId?: string;
38
+ tab: string;
39
+ };
40
+
41
+ export const NavTreeContainer$ = forwardRef<HTMLDivElement, NavTreeContainerProps>(
42
+ ({ tab, popoverAnchorId }, forwardedRef) => {
43
+ const [isLg] = useMediaQuery('lg');
44
+ const { invokeSync, invokePromise } = useOperationInvoker();
45
+ const runAction = useActionRunner();
46
+ const { graph } = useAppGraph();
47
+ const { getItem, setItem } = useNavTreeState();
48
+ const layout = useLayout();
49
+ const model = useNavTreeModel(Node.RootId);
50
+ const { navigationSidebarState } = useSidebars(meta.id);
51
+ const latestRef = useRef({
52
+ tab,
53
+ activeItems: layout.active,
54
+ navigationSidebarState,
55
+ isLg,
56
+ });
57
+
58
+ useEffect(() => {
59
+ latestRef.current = {
60
+ tab,
61
+ activeItems: layout.active,
62
+ navigationSidebarState,
63
+ isLg,
64
+ };
65
+ }, [tab, layout.active, navigationSidebarState, isLg]);
66
+
67
+ const handleOpenChange = useCallback(
68
+ ({ item: { id }, path, open }: { item: Node.Node; path: string[]; open: boolean }) => {
69
+ // TODO(thure): This might become a localstorage leak; openItemIds that no longer exist should be removed from this map.
70
+ setItem(path, 'open', open);
71
+ Graph.expand(graph, id, 'child');
72
+ },
73
+ [graph, setItem],
74
+ );
75
+
76
+ const handleTabChange = useCallback(
77
+ (node: NavTreeItemGraphNode) => {
78
+ Graph.expand(graph, node.id, 'child');
79
+
80
+ const {
81
+ tab: activeTab,
82
+ activeItems,
83
+ navigationSidebarState: currentSidebarState,
84
+ isLg: latestIsLg,
85
+ } = latestRef.current;
86
+ invokeSync(LayoutOperation.UpdateSidebar, {
87
+ state:
88
+ node.id === activeTab
89
+ ? currentSidebarState === 'expanded'
90
+ ? latestIsLg
91
+ ? 'collapsed'
92
+ : 'closed'
93
+ : 'expanded'
94
+ : 'expanded',
95
+ });
96
+
97
+ invokeSync(LayoutOperation.SwitchWorkspace, { subject: node.id });
98
+
99
+ // Open the first item if the workspace is empty.
100
+ if (activeItems.length === 0) {
101
+ const [item] = getItems(graph, node).filter((node) => !Node.isActionLike(node));
102
+ if (item && item.data) {
103
+ invokeSync(LayoutOperation.Open, { subject: [item.id] });
104
+ }
105
+ }
106
+ },
107
+ [invokeSync, graph],
108
+ );
109
+
110
+ const blockInstruction = useCallback(
111
+ ({ instruction, source, target }: { instruction: Instruction; source: TreeData; target: TreeData }) => {
112
+ return target.item.properties.blockInstruction?.(source, instruction) ?? false;
113
+ },
114
+ [],
115
+ );
116
+
117
+ const canDrop = useCallback(({ source, target }: { source: TreeData; target: TreeData }) => {
118
+ return target.item.properties.canDrop?.(source) ?? false;
119
+ }, []);
120
+
121
+ const canSelect = useCallback(({ item }: { item: Node.Node }) => {
122
+ return item.properties.selectable ?? true;
123
+ }, []);
124
+
125
+ const handleSelect = useCallback(
126
+ ({ item: node, path, option }: { item: Node.Node; path: string[]; option: boolean }) => {
127
+ if (!node.data) {
128
+ return;
129
+ }
130
+
131
+ if (Node.isAction(node)) {
132
+ const [parent] = Graph.getConnections(graph, node.id, Node.childRelation('inbound'));
133
+ if (parent) {
134
+ void runAction(node, { parent, path, caller: NAV_TREE_ITEM });
135
+ }
136
+ return;
137
+ }
138
+
139
+ const current = getItem(path).current;
140
+ if (!current) {
141
+ invokeSync(LayoutOperation.Open, { subject: [node.id], key: node.properties.key });
142
+ } else if (option) {
143
+ invokeSync(LayoutOperation.Close, { subject: [node.id] });
144
+ } else {
145
+ void invokePromise(LayoutOperation.ScrollIntoView, { subject: node.id });
146
+ }
147
+
148
+ const defaultAction = Graph.getActions(graph, node.id).find(
149
+ (action) => action.properties?.disposition === 'default',
150
+ );
151
+ if (Node.isAction(defaultAction)) {
152
+ void runAction(defaultAction);
153
+ }
154
+
155
+ if (!isLg) {
156
+ invokeSync(LayoutOperation.UpdateSidebar, { state: 'closed' });
157
+ }
158
+ },
159
+ [graph, invokeSync, invokePromise, getItem, runAction, isLg],
160
+ );
161
+
162
+ const handleBack = useCallback(() => invokeSync(LayoutOperation.RevertWorkspace), [invokeSync]);
163
+
164
+ // TODO(wittjosiah): Factor out hook.
165
+ useEffect(() => {
166
+ return monitorForElements({
167
+ canMonitor: ({ source }) => isTreeData(source.data),
168
+ onDrop: ({ location, source }) => {
169
+ // Didn't drop on anything.
170
+ if (!location.current.dropTargets.length) {
171
+ return;
172
+ }
173
+ const target = location.current.dropTargets[0];
174
+ const instruction: Instruction | null = extractInstruction(target.data);
175
+ if (instruction !== null && instruction.type !== 'instruction-blocked') {
176
+ const sourceNode = source.data.item as NavTreeItemGraphNode;
177
+ const targetNode = target.data.item as NavTreeItemGraphNode;
178
+ const sourcePath = source.data.path as string[];
179
+ const targetPath = target.data.path as string[];
180
+ const sameParent = sourcePath.slice(0, -1).join() === targetPath.slice(0, -1).join();
181
+ const operation =
182
+ sameParent && instruction.type !== 'make-child'
183
+ ? 'rearrange'
184
+ : resolveMigrationOperation(graph, sourceNode, targetPath, targetNode);
185
+ const sourceParent = getParent(graph, sourceNode, sourcePath);
186
+ const targetParent = getParent(graph, targetNode, targetPath);
187
+ const sourceItems = getItems(graph, sourceParent);
188
+ const targetItems = getItems(graph, targetParent);
189
+ const sourceIndex = sourceItems.findIndex(({ id }) => id === sourceNode.id);
190
+ const targetIndex = targetItems.findIndex(({ id }) => id === targetNode.id);
191
+ const migrationIndex =
192
+ instruction.type === 'make-child'
193
+ ? undefined
194
+ : instruction.type === 'reorder-below'
195
+ ? targetIndex + 1
196
+ : targetIndex;
197
+ switch (operation) {
198
+ case 'rearrange': {
199
+ const nextItems = sourceItems.map(({ data }) => data);
200
+ arrayMove(nextItems, sourceIndex, targetIndex);
201
+ void sourceNode.properties.onRearrange?.(nextItems);
202
+ break;
203
+ }
204
+ case 'copy': {
205
+ const target = instruction.type === 'make-child' ? targetNode : targetParent;
206
+ void target?.properties.onCopy?.(sourceNode, migrationIndex);
207
+ break;
208
+ }
209
+ case 'transfer': {
210
+ const target = instruction.type === 'make-child' ? targetNode : targetParent;
211
+ if (!target?.properties.onTransferStart || target?.id === sourceParent?.id) {
212
+ break;
213
+ }
214
+ void target?.properties.onTransferStart(sourceNode, migrationIndex);
215
+ void sourceParent?.properties.onTransferEnd?.(sourceNode, target);
216
+ break;
217
+ }
218
+ }
219
+ }
220
+ },
221
+ });
222
+ }, [graph]);
223
+
224
+ const setAlternateTree = useCallback(
225
+ (path: string[], open: boolean) => {
226
+ setItem(path, 'alternateTree', open);
227
+ },
228
+ [setItem],
229
+ );
230
+
231
+ const onItemHover = useCallback(({ item }: { item: Node.Node }) => Graph.expand(graph, item.id, 'child'), [graph]);
232
+
233
+ const navTreeContextValue = useMemo(
234
+ () => ({
235
+ model,
236
+ popoverAnchorId,
237
+ renderItemEnd: NavTreeItemEnd,
238
+ blockInstruction,
239
+ canDrop,
240
+ canSelect,
241
+ setAlternateTree,
242
+ onBack: handleBack,
243
+ onOpenChange: handleOpenChange,
244
+ onSelect: handleSelect,
245
+ onTabChange: handleTabChange,
246
+ onItemHover,
247
+ }),
248
+ [
249
+ model,
250
+ popoverAnchorId,
251
+ blockInstruction,
252
+ canDrop,
253
+ canSelect,
254
+ setAlternateTree,
255
+ handleBack,
256
+ handleOpenChange,
257
+ handleSelect,
258
+ handleTabChange,
259
+ onItemHover,
260
+ ],
261
+ );
262
+
263
+ return (
264
+ <NavTreeContext.Provider value={navTreeContextValue}>
265
+ <NavTree id={Node.RootId} root={Graph.getRoot(graph)} tab={tab} open={layout.sidebarOpen} ref={forwardedRef} />
266
+ </NavTreeContext.Provider>
267
+ );
268
+ },
269
+ );
270
+
271
+ export const NavTreeContainer = memo(NavTreeContainer$);
@@ -0,0 +1,6 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export { NavTreeContainer, NODE_TYPE } from './NavTreeContainer';
6
+ export { NavTreeContainer as default } from './NavTreeContainer';
@@ -5,13 +5,14 @@ import { useEffect } from 'react';
5
5
 
6
6
  import { type Node } from '@dxos/app-graph';
7
7
  import { toLocalizedString, useTranslation } from '@dxos/react-ui';
8
+ import { osTranslations } from '@dxos/ui-theme';
8
9
 
9
- import { meta } from '../meta';
10
+ import { meta } from '../../meta';
10
11
 
11
- export const NavTreeDocumentTitle = ({ node }: { node?: Node }) => {
12
+ export const NavTreeDocumentTitle = ({ node }: { node?: Node.Node }) => {
12
13
  const { t } = useTranslation(meta.id);
13
14
  useEffect(() => {
14
- document.title = node ? toLocalizedString(node.properties.label, t) : t('current app name', { ns: 'appkit' });
15
+ document.title = node ? toLocalizedString(node.properties.label, t) : t('current app name', { ns: osTranslations });
15
16
  }, [node?.properties?.label]);
16
17
  return null;
17
18
  };
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { NavTreeDocumentTitle } from './NavTreeDocumentTitle';
6
+
7
+ export default NavTreeDocumentTitle;
@@ -0,0 +1,12 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type ComponentType, lazy } from 'react';
6
+
7
+ export { NODE_TYPE } from './NavTreeContainer';
8
+
9
+ export const CommandsDialogContent: ComponentType<any> = lazy(() => import('./CommandsDialogContent'));
10
+ export const CommandsTrigger: ComponentType<any> = lazy(() => import('./CommandsTrigger'));
11
+ export const NavTreeContainer: ComponentType<any> = lazy(() => import('./NavTreeContainer'));
12
+ export const NavTreeDocumentTitle: ComponentType<any> = lazy(() => import('./NavTreeDocumentTitle'));
@@ -3,39 +3,17 @@
3
3
  //
4
4
 
5
5
  import { type Meta } from '@storybook/react-vite';
6
- import React, { type JSX, type PropsWithChildren, useEffect, useState } from 'react';
6
+ import React, { useEffect, useState } from 'react';
7
7
 
8
8
  import { faker } from '@dxos/random';
9
- import { Icon, IconButton } from '@dxos/react-ui';
9
+ import { Icon, IconButton, Panel, ScrollArea } from '@dxos/react-ui';
10
10
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
- import { mx } from '@dxos/react-ui-theme';
11
+ import { mx } from '@dxos/ui-theme';
12
12
 
13
13
  import { type ItemMap, Tree, type TreeNodeData, type TreeProps, visitNodes, visitor } from './Tree';
14
14
 
15
15
  faker.seed(1234);
16
16
 
17
- /**
18
- * Space:
19
- * - Space panels are separated from each other to give them more weight than folders.
20
- * - Private space isn't different (just first, different icon).
21
- * - Edit titles in place.
22
- *
23
- * Navigation:
24
- * - up/down across spaces/folder peers
25
- * - right to enter space/folder children
26
- * - left to parent
27
- */
28
- const Container = ({ children, sidebar }: PropsWithChildren<{ sidebar: JSX.Element }>) => {
29
- return (
30
- <div className='flex'>
31
- {/* TODO(burdon): Custom thin scrollbar. */}
32
- {/* TODO(burdon): Horizontal scrolling within navtree? */}
33
- <div className='flex flex-col overflow-y-auto is-[300px] bg-neutral-100 dark:bg-neutral-950'>{sidebar}</div>
34
- <div className='flex flex-col grow overflow-hidden'>{children}</div>
35
- </div>
36
- );
37
- };
38
-
39
17
  // TODO(burdon): Generate.
40
18
  const data: TreeNodeData[] = [
41
19
  {
@@ -134,7 +112,7 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
134
112
  }
135
113
  }, []);
136
114
 
137
- const handleCreateItem: TreeProps['onMenuAction'] = (id, action) => {
115
+ const handleCreateItem: TreeProps['onMenuAction'] = (id) => {
138
116
  setItems((items) => {
139
117
  let parent: TreeNodeData | undefined;
140
118
  visitNodes(root, (node) => {
@@ -167,49 +145,68 @@ const Sidebar = ({ mutate }: { mutate?: boolean }) => {
167
145
  };
168
146
 
169
147
  return (
170
- <div className='flex flex-col overflow-hidden'>
171
- <div className='flex flex-col overflow-y-auto'>
172
- <Tree
173
- className='p-0.5 gap-1'
174
- node={root}
175
- open={openItems}
176
- selected={selectedItems}
177
- active={activeItems}
178
- onChangeOpen={(id, open) => setOpenItems((items) => ({ ...items, [id]: open }))}
179
- onChangeSelected={(id, open) => setSelectedItems((items) => ({ ...items, [id]: open }))}
180
- onMenuAction={handleCreateItem}
181
- getSlots={(node, open, depth, ancestors) => {
182
- if (depth === 1) {
183
- return {
184
- root: 'rounded bg-white dark:bg-neutral-850',
185
- header: mx('rounded-t bg-neutral-50 dark:bg-neutral-900', !open && 'rounded-b'),
186
- };
187
- }
188
- }}
189
- />
190
- </div>
191
-
192
- <div className='flex items-center my-2 pli-2 gap-2'>
193
- <IconButton icon='ph--plus-circle--regular' iconOnly label='Create space' onClick={handleCreateSpace} />
194
- <span className='grow text-sm' onClick={handleCreateSpace}>
195
- New space
196
- </span>
197
- <Icon icon='ph--list--regular' />
198
- </div>
199
- </div>
148
+ <Panel.Root>
149
+ <Panel.Content asChild>
150
+ <ScrollArea.Root orientation='vertical' thin>
151
+ <ScrollArea.Viewport>
152
+ <Tree
153
+ className='p-0.5 gap-1'
154
+ node={root}
155
+ open={openItems}
156
+ selected={selectedItems}
157
+ active={activeItems}
158
+ onChangeOpen={(id, open) => setOpenItems((items) => ({ ...items, [id]: open }))}
159
+ onChangeSelected={(id, open) => setSelectedItems((items) => ({ ...items, [id]: open }))}
160
+ onMenuAction={handleCreateItem}
161
+ getSlots={(node, open, depth) => {
162
+ if (depth === 1) {
163
+ return {
164
+ root: 'rounded-sm bg-white dark:bg-neutral-850',
165
+ header: mx('rounded-t bg-neutral-50 dark:bg-neutral-900', !open && 'rounded-b'),
166
+ };
167
+ }
168
+ }}
169
+ />
170
+ </ScrollArea.Viewport>
171
+ </ScrollArea.Root>
172
+ </Panel.Content>
173
+ <Panel.Statusbar>
174
+ <div className='flex items-center my-2 px-2 gap-2'>
175
+ <IconButton icon='ph--plus-circle--regular' iconOnly label='Create space' onClick={handleCreateSpace} />
176
+ <span className='grow text-sm' onClick={handleCreateSpace}>
177
+ New space
178
+ </span>
179
+ <Icon icon='ph--list--regular' />
180
+ </div>
181
+ </Panel.Statusbar>
182
+ </Panel.Root>
200
183
  );
201
184
  };
202
185
 
203
186
  const meta = {
204
187
  title: 'plugins/plugin-navtree/experimental/Tree',
205
188
  component: Tree,
206
- decorators: [withTheme, withLayout({ container: 'column' })],
189
+ decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'w-[20rem]' })],
190
+ parameters: {
191
+ layout: 'fullscreen',
192
+ },
207
193
  } satisfies Meta<typeof Tree>;
208
194
 
209
195
  export default meta;
210
196
 
197
+ /**
198
+ * Space:
199
+ * - Space panels are separated from each other to give them more weight than folders.
200
+ * - Private space isn't different (just first, different icon).
201
+ * - Edit titles in place.
202
+ *
203
+ * Navigation:
204
+ * - up/down across spaces/folder peers
205
+ * - right to enter space/folder children
206
+ * - left to parent
207
+ */
211
208
  export const Default = () => {
212
- return <Container sidebar={<Sidebar mutate />} />;
209
+ return <Sidebar mutate />;
213
210
  };
214
211
 
215
212
  export const Visitor = () => {