@dxos/plugin-sheet 0.6.14-main.7bd9c89 → 0.6.14-main.8b352a0

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 (217) hide show
  1. package/dist/lib/browser/{SheetContainer-AKWROARP.mjs → SheetContainer-R65IDJHN.mjs} +54 -58
  2. package/dist/lib/browser/SheetContainer-R65IDJHN.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-BWN5DZWZ.mjs → chunk-5KVQ5IPW.mjs} +3 -12
  4. package/dist/lib/browser/chunk-5KVQ5IPW.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-FGMFOW6U.mjs → chunk-I2DKJ72A.mjs} +570 -669
  6. package/dist/lib/browser/chunk-I2DKJ72A.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-GSV5QNLD.mjs → chunk-KCYJSOFB.mjs} +252 -33
  8. package/dist/lib/browser/chunk-KCYJSOFB.mjs.map +7 -0
  9. package/dist/lib/browser/{graph-M4IQ76QX.mjs → compute-graph-SJT67236.mjs} +4 -2
  10. package/dist/lib/browser/index.mjs +62 -20
  11. package/dist/lib/browser/index.mjs.map +4 -4
  12. package/dist/lib/browser/meta.json +1 -1
  13. package/dist/lib/browser/types.mjs +1 -1
  14. package/dist/lib/node/{SheetContainer-N5IQGEFL.cjs → SheetContainer-6BO4C5X2.cjs} +52 -62
  15. package/dist/lib/node/SheetContainer-6BO4C5X2.cjs.map +7 -0
  16. package/dist/lib/node/{chunk-NZARD7UP.cjs → chunk-2XJ5I4UF.cjs} +6 -15
  17. package/dist/lib/node/chunk-2XJ5I4UF.cjs.map +7 -0
  18. package/dist/lib/node/{chunk-53BMSUIK.cjs → chunk-DEPJHN47.cjs} +534 -646
  19. package/dist/lib/node/chunk-DEPJHN47.cjs.map +7 -0
  20. package/dist/lib/node/{chunk-5XPK2V4A.cjs → chunk-JF5XNTF3.cjs} +257 -28
  21. package/dist/lib/node/chunk-JF5XNTF3.cjs.map +7 -0
  22. package/dist/lib/node/{graph-Q3N2X26H.cjs → compute-graph-AQBDL7HO.cjs} +21 -19
  23. package/dist/lib/node/compute-graph-AQBDL7HO.cjs.map +7 -0
  24. package/dist/lib/node/index.cjs +70 -31
  25. package/dist/lib/node/index.cjs.map +4 -4
  26. package/dist/lib/node/meta.json +1 -1
  27. package/dist/lib/node/types.cjs +6 -6
  28. package/dist/lib/node/types.cjs.map +1 -1
  29. package/dist/lib/node-esm/{SheetContainer-46PBMF2E.mjs → SheetContainer-MJXC5E3P.mjs} +54 -58
  30. package/dist/lib/node-esm/SheetContainer-MJXC5E3P.mjs.map +7 -0
  31. package/dist/lib/node-esm/{chunk-T3PRH7QS.mjs → chunk-25V7WY4R.mjs} +570 -669
  32. package/dist/lib/node-esm/chunk-25V7WY4R.mjs.map +7 -0
  33. package/dist/lib/node-esm/{chunk-WFDTY3IC.mjs → chunk-5TXLF6PL.mjs} +3 -12
  34. package/dist/lib/node-esm/chunk-5TXLF6PL.mjs.map +7 -0
  35. package/dist/lib/node-esm/{chunk-5WPZCXNS.mjs → chunk-XBEHKYO7.mjs} +251 -33
  36. package/dist/lib/node-esm/chunk-XBEHKYO7.mjs.map +7 -0
  37. package/dist/lib/node-esm/{graph-SMPUMOV2.mjs → compute-graph-FRCKXEYK.mjs} +4 -2
  38. package/dist/lib/node-esm/index.mjs +62 -20
  39. package/dist/lib/node-esm/index.mjs.map +4 -4
  40. package/dist/lib/node-esm/meta.json +1 -1
  41. package/dist/lib/node-esm/types.mjs +1 -1
  42. package/dist/types/src/SheetPlugin.d.ts.map +1 -1
  43. package/dist/types/src/components/ComputeGraph/ComputeGraphContextProvider.d.ts +3 -1
  44. package/dist/types/src/components/ComputeGraph/ComputeGraphContextProvider.d.ts.map +1 -1
  45. package/dist/types/src/components/GridSheet/GridSheet.d.ts.map +1 -1
  46. package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts.map +1 -1
  47. package/dist/types/src/components/GridSheet/util.d.ts +1 -2
  48. package/dist/types/src/components/GridSheet/util.d.ts.map +1 -1
  49. package/dist/types/src/components/RangeList/RangeList.d.ts +7 -0
  50. package/dist/types/src/components/RangeList/RangeList.d.ts.map +1 -0
  51. package/dist/types/src/components/RangeList/index.d.ts +2 -0
  52. package/dist/types/src/components/RangeList/index.d.ts.map +1 -0
  53. package/dist/types/src/components/SheetContainer/SheetContainer.d.ts +6 -3
  54. package/dist/types/src/components/SheetContainer/SheetContainer.d.ts.map +1 -1
  55. package/dist/types/src/components/SheetContext/SheetContext.d.ts +7 -5
  56. package/dist/types/src/components/SheetContext/SheetContext.d.ts.map +1 -1
  57. package/dist/types/src/components/Toolbar/Toolbar.d.ts +1 -1
  58. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  59. package/dist/types/src/components/index.d.ts +5 -2
  60. package/dist/types/src/components/index.d.ts.map +1 -1
  61. package/dist/types/src/compute-graph/compute-graph-registry.d.ts.map +1 -0
  62. package/dist/types/src/{graph → compute-graph}/compute-graph.d.ts +1 -1
  63. package/dist/types/src/compute-graph/compute-graph.d.ts.map +1 -0
  64. package/dist/types/src/compute-graph/compute-graph.stories.d.ts.map +1 -0
  65. package/dist/types/src/compute-graph/compute-graph.test.d.ts.map +1 -0
  66. package/dist/types/src/compute-graph/compute-node.d.ts.map +1 -0
  67. package/dist/types/src/compute-graph/functions/async-function.d.ts.map +1 -0
  68. package/dist/types/src/compute-graph/functions/edge-function.d.ts.map +1 -0
  69. package/dist/types/src/compute-graph/functions/function-defs.d.ts.map +1 -0
  70. package/dist/types/src/compute-graph/functions/index.d.ts.map +1 -0
  71. package/dist/types/src/compute-graph/hyperformula.test.d.ts.map +1 -0
  72. package/dist/types/src/compute-graph/index.d.ts.map +1 -0
  73. package/dist/types/src/compute-graph/testing/index.d.ts.map +1 -0
  74. package/dist/types/src/compute-graph/testing/test-builder.d.ts.map +1 -0
  75. package/dist/types/src/compute-graph/testing/test-plugin.d.ts.map +1 -0
  76. package/dist/types/src/compute-graph/util.d.ts.map +1 -0
  77. package/dist/types/src/defs/index.d.ts +1 -1
  78. package/dist/types/src/defs/index.d.ts.map +1 -1
  79. package/dist/types/src/defs/sheet-range-types.d.ts +1 -1
  80. package/dist/types/src/defs/sheet-range-types.d.ts.map +1 -1
  81. package/dist/types/src/defs/types.d.ts +6 -0
  82. package/dist/types/src/defs/types.d.ts.map +1 -1
  83. package/dist/types/src/defs/util.d.ts +2 -6
  84. package/dist/types/src/defs/util.d.ts.map +1 -1
  85. package/dist/types/src/extensions/compute.d.ts +1 -1
  86. package/dist/types/src/extensions/compute.d.ts.map +1 -1
  87. package/dist/types/src/extensions/editor/extension.d.ts +23 -3
  88. package/dist/types/src/extensions/editor/extension.d.ts.map +1 -1
  89. package/dist/types/src/index.d.ts +1 -1
  90. package/dist/types/src/index.d.ts.map +1 -1
  91. package/dist/types/src/integrations/index.d.ts +2 -0
  92. package/dist/types/src/integrations/index.d.ts.map +1 -0
  93. package/dist/types/src/integrations/thread-ranges.d.ts +7 -0
  94. package/dist/types/src/integrations/thread-ranges.d.ts.map +1 -0
  95. package/dist/types/src/model/index.d.ts +1 -1
  96. package/dist/types/src/model/index.d.ts.map +1 -1
  97. package/dist/types/src/model/sheet-model.d.ts +1 -1
  98. package/dist/types/src/model/sheet-model.d.ts.map +1 -1
  99. package/dist/types/src/{hooks → model}/useSheetModel.d.ts +1 -1
  100. package/dist/types/src/model/useSheetModel.d.ts.map +1 -0
  101. package/dist/types/src/serializer.d.ts +4 -0
  102. package/dist/types/src/serializer.d.ts.map +1 -0
  103. package/dist/types/src/testing/testing.d.ts +1 -1
  104. package/dist/types/src/testing/testing.d.ts.map +1 -1
  105. package/dist/types/src/translations.d.ts +13 -3
  106. package/dist/types/src/translations.d.ts.map +1 -1
  107. package/dist/types/src/types.d.ts +4 -30
  108. package/dist/types/src/types.d.ts.map +1 -1
  109. package/package.json +43 -50
  110. package/src/SheetPlugin.tsx +25 -12
  111. package/src/components/ComputeGraph/ComputeGraphContextProvider.tsx +19 -2
  112. package/src/components/FunctionEditor/FunctionEditor.tsx +5 -5
  113. package/src/components/GridSheet/GridSheet.stories.tsx +3 -1
  114. package/src/components/GridSheet/GridSheet.tsx +118 -22
  115. package/src/components/GridSheet/SheetCellEditor.stories.tsx +1 -1
  116. package/src/components/GridSheet/util.ts +24 -14
  117. package/src/components/RangeList/RangeList.tsx +53 -0
  118. package/src/components/RangeList/index.ts +5 -0
  119. package/src/components/SheetContainer/SheetContainer.stories.tsx +3 -3
  120. package/src/components/SheetContainer/SheetContainer.tsx +10 -4
  121. package/src/components/SheetContext/SheetContext.tsx +43 -20
  122. package/src/components/Toolbar/Toolbar.tsx +44 -41
  123. package/src/components/index.ts +1 -0
  124. package/src/{graph → compute-graph}/compute-graph.stories.tsx +2 -1
  125. package/src/{graph → compute-graph}/compute-graph.ts +5 -2
  126. package/src/{graph → compute-graph}/compute-node.ts +2 -3
  127. package/src/defs/index.ts +1 -1
  128. package/src/defs/sheet-range-types.ts +5 -4
  129. package/src/defs/types.ts +6 -1
  130. package/src/defs/util.ts +3 -19
  131. package/src/extensions/compute.stories.tsx +2 -2
  132. package/src/extensions/compute.ts +2 -2
  133. package/src/extensions/editor/extension.test.ts +1 -1
  134. package/src/extensions/editor/extension.ts +48 -23
  135. package/src/index.ts +2 -2
  136. package/src/integrations/index.ts +5 -0
  137. package/src/integrations/thread-ranges.ts +101 -0
  138. package/src/model/index.ts +1 -1
  139. package/src/model/sheet-model.test.ts +1 -1
  140. package/src/model/sheet-model.ts +13 -8
  141. package/src/{hooks → model}/useSheetModel.ts +1 -1
  142. package/src/serializer.ts +27 -0
  143. package/src/testing/testing.tsx +1 -1
  144. package/src/translations.ts +13 -3
  145. package/src/types.ts +4 -2
  146. package/dist/lib/browser/SheetContainer-AKWROARP.mjs.map +0 -7
  147. package/dist/lib/browser/chunk-BWN5DZWZ.mjs.map +0 -7
  148. package/dist/lib/browser/chunk-FGMFOW6U.mjs.map +0 -7
  149. package/dist/lib/browser/chunk-GSV5QNLD.mjs.map +0 -7
  150. package/dist/lib/node/SheetContainer-N5IQGEFL.cjs.map +0 -7
  151. package/dist/lib/node/chunk-53BMSUIK.cjs.map +0 -7
  152. package/dist/lib/node/chunk-5XPK2V4A.cjs.map +0 -7
  153. package/dist/lib/node/chunk-NZARD7UP.cjs.map +0 -7
  154. package/dist/lib/node/graph-Q3N2X26H.cjs.map +0 -7
  155. package/dist/lib/node-esm/SheetContainer-46PBMF2E.mjs.map +0 -7
  156. package/dist/lib/node-esm/chunk-5WPZCXNS.mjs.map +0 -7
  157. package/dist/lib/node-esm/chunk-T3PRH7QS.mjs.map +0 -7
  158. package/dist/lib/node-esm/chunk-WFDTY3IC.mjs.map +0 -7
  159. package/dist/types/src/graph/compute-graph-registry.d.ts.map +0 -1
  160. package/dist/types/src/graph/compute-graph.d.ts.map +0 -1
  161. package/dist/types/src/graph/compute-graph.stories.d.ts.map +0 -1
  162. package/dist/types/src/graph/compute-graph.test.d.ts.map +0 -1
  163. package/dist/types/src/graph/compute-node.d.ts.map +0 -1
  164. package/dist/types/src/graph/functions/async-function.d.ts.map +0 -1
  165. package/dist/types/src/graph/functions/edge-function.d.ts.map +0 -1
  166. package/dist/types/src/graph/functions/function-defs.d.ts.map +0 -1
  167. package/dist/types/src/graph/functions/index.d.ts.map +0 -1
  168. package/dist/types/src/graph/hyperformula.test.d.ts.map +0 -1
  169. package/dist/types/src/graph/index.d.ts.map +0 -1
  170. package/dist/types/src/graph/testing/index.d.ts.map +0 -1
  171. package/dist/types/src/graph/testing/test-builder.d.ts.map +0 -1
  172. package/dist/types/src/graph/testing/test-plugin.d.ts.map +0 -1
  173. package/dist/types/src/graph/util.d.ts.map +0 -1
  174. package/dist/types/src/hooks/hooks.stories.d.ts +0 -6
  175. package/dist/types/src/hooks/hooks.stories.d.ts.map +0 -1
  176. package/dist/types/src/hooks/index.d.ts +0 -4
  177. package/dist/types/src/hooks/index.d.ts.map +0 -1
  178. package/dist/types/src/hooks/threads.d.ts +0 -8
  179. package/dist/types/src/hooks/threads.d.ts.map +0 -1
  180. package/dist/types/src/hooks/useComputeGraph.d.ts +0 -7
  181. package/dist/types/src/hooks/useComputeGraph.d.ts.map +0 -1
  182. package/dist/types/src/hooks/useSheetModel.d.ts.map +0 -1
  183. package/dist/types/src/model/decorations.d.ts +0 -26
  184. package/dist/types/src/model/decorations.d.ts.map +0 -1
  185. package/src/hooks/hooks.stories.tsx +0 -53
  186. package/src/hooks/index.ts +0 -7
  187. package/src/hooks/threads.ts +0 -147
  188. package/src/hooks/useComputeGraph.ts +0 -28
  189. package/src/model/decorations.ts +0 -66
  190. /package/dist/lib/browser/{graph-M4IQ76QX.mjs.map → compute-graph-SJT67236.mjs.map} +0 -0
  191. /package/dist/lib/node-esm/{graph-SMPUMOV2.mjs.map → compute-graph-FRCKXEYK.mjs.map} +0 -0
  192. /package/dist/types/src/{graph → compute-graph}/compute-graph-registry.d.ts +0 -0
  193. /package/dist/types/src/{graph → compute-graph}/compute-graph.stories.d.ts +0 -0
  194. /package/dist/types/src/{graph → compute-graph}/compute-graph.test.d.ts +0 -0
  195. /package/dist/types/src/{graph → compute-graph}/compute-node.d.ts +0 -0
  196. /package/dist/types/src/{graph → compute-graph}/functions/async-function.d.ts +0 -0
  197. /package/dist/types/src/{graph → compute-graph}/functions/edge-function.d.ts +0 -0
  198. /package/dist/types/src/{graph → compute-graph}/functions/function-defs.d.ts +0 -0
  199. /package/dist/types/src/{graph → compute-graph}/functions/index.d.ts +0 -0
  200. /package/dist/types/src/{graph → compute-graph}/hyperformula.test.d.ts +0 -0
  201. /package/dist/types/src/{graph → compute-graph}/index.d.ts +0 -0
  202. /package/dist/types/src/{graph → compute-graph}/testing/index.d.ts +0 -0
  203. /package/dist/types/src/{graph → compute-graph}/testing/test-builder.d.ts +0 -0
  204. /package/dist/types/src/{graph → compute-graph}/testing/test-plugin.d.ts +0 -0
  205. /package/dist/types/src/{graph → compute-graph}/util.d.ts +0 -0
  206. /package/src/{graph → compute-graph}/compute-graph-registry.ts +0 -0
  207. /package/src/{graph → compute-graph}/compute-graph.test.ts +0 -0
  208. /package/src/{graph → compute-graph}/functions/async-function.ts +0 -0
  209. /package/src/{graph → compute-graph}/functions/edge-function.ts +0 -0
  210. /package/src/{graph → compute-graph}/functions/function-defs.ts +0 -0
  211. /package/src/{graph → compute-graph}/functions/index.ts +0 -0
  212. /package/src/{graph → compute-graph}/hyperformula.test.ts +0 -0
  213. /package/src/{graph → compute-graph}/index.ts +0 -0
  214. /package/src/{graph → compute-graph}/testing/index.ts +0 -0
  215. /package/src/{graph → compute-graph}/testing/test-builder.ts +0 -0
  216. /package/src/{graph → compute-graph}/testing/test-plugin.ts +0 -0
  217. /package/src/{graph → compute-graph}/util.ts +0 -0
@@ -13,12 +13,12 @@ import { SpaceAction } from '@dxos/plugin-space';
13
13
  import { getSpace, isEchoObject } from '@dxos/react-client/echo';
14
14
  import { Icon } from '@dxos/react-ui';
15
15
 
16
- import { ComputeGraphContextProvider, SheetContainer } from './components';
16
+ import { ComputeGraphContextProvider, RangeList, SheetContainer } from './components';
17
+ import { type ComputeGraphRegistry } from './compute-graph';
17
18
  import { compareIndexPositions, createSheet } from './defs';
18
19
  import { computeGraphFacet } from './extensions';
19
- import { type ComputeGraphRegistry } from './graph';
20
- import { useComputeGraph } from './hooks';
21
20
  import meta, { SHEET_PLUGIN } from './meta';
21
+ import { serializer } from './serializer';
22
22
  import translations from './translations';
23
23
  import { SheetAction, SheetType, type SheetPluginProvides } from './types';
24
24
 
@@ -38,8 +38,8 @@ export const SheetPlugin = (): PluginDefinition<SheetPluginProvides> => {
38
38
  }
39
39
 
40
40
  // Async import removes direct dependency on hyperformula.
41
- const { ComputeGraphRegistry } = await import('./graph');
42
- computeGraphRegistry = new ComputeGraphRegistry({ remoteFunctionUrl });
41
+ const { defaultPlugins, ComputeGraphRegistry } = await import('./compute-graph');
42
+ computeGraphRegistry = new ComputeGraphRegistry({ plugins: defaultPlugins, remoteFunctionUrl });
43
43
  },
44
44
  provides: {
45
45
  context: ({ children }) => {
@@ -52,6 +52,7 @@ export const SheetPlugin = (): PluginDefinition<SheetPluginProvides> => {
52
52
  label: (object: any) => (object instanceof SheetType ? object.name : undefined),
53
53
  placeholder: ['sheet title placeholder', { ns: SHEET_PLUGIN }],
54
54
  icon: 'ph--grid-nine--regular',
55
+ serializer,
55
56
  },
56
57
  },
57
58
  },
@@ -143,14 +144,26 @@ export const SheetPlugin = (): PluginDefinition<SheetPluginProvides> => {
143
144
  surface: {
144
145
  component: ({ data, role }) => {
145
146
  const space = isEchoObject(data.object) ? getSpace(data.object) : undefined;
146
- const graph = useComputeGraph(space);
147
- if (graph && data.object instanceof SheetType) {
148
- switch (role) {
149
- case 'article':
150
- case 'section': {
151
- return <SheetContainer graph={graph} sheet={data.object} role={role} />;
147
+ if (!space) {
148
+ return null;
149
+ }
150
+
151
+ switch (role) {
152
+ case 'article':
153
+ case 'section':
154
+ if (data.object instanceof SheetType) {
155
+ return <SheetContainer space={space} sheet={data.object} role={role} />;
152
156
  }
153
- }
157
+
158
+ return null;
159
+ case 'complementary--settings':
160
+ if (data.subject instanceof SheetType) {
161
+ return {
162
+ node: <RangeList sheet={data.subject} />,
163
+ };
164
+ }
165
+
166
+ return null;
154
167
  }
155
168
 
156
169
  return null;
@@ -2,9 +2,13 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { createContext, type PropsWithChildren } from 'react';
5
+ import React, { createContext, type PropsWithChildren, useContext } from 'react';
6
6
 
7
- import { type ComputeGraphRegistry } from '../../graph';
7
+ import { raise } from '@dxos/debug';
8
+ import { type Space } from '@dxos/react-client/echo';
9
+ import { useAsyncState } from '@dxos/react-hooks';
10
+
11
+ import { type ComputeGraph, type ComputeGraphRegistry } from '../../compute-graph';
8
12
 
9
13
  export type ComputeGraphContextType = {
10
14
  registry: ComputeGraphRegistry;
@@ -18,3 +22,16 @@ export const ComputeGraphContext = createContext<ComputeGraphContextType | undef
18
22
  export const ComputeGraphContextProvider = ({ registry, children }: PropsWithChildren<ComputeGraphContextType>) => {
19
23
  return <ComputeGraphContext.Provider value={{ registry }}>{children}</ComputeGraphContext.Provider>;
20
24
  };
25
+
26
+ export const useComputeGraph = (space?: Space): ComputeGraph | undefined => {
27
+ const { registry } = useContext(ComputeGraphContext) ?? raise(new Error('Missing ComputeGraphContext'));
28
+ const [graph] = useAsyncState(async () => {
29
+ if (space) {
30
+ const graph = registry.getOrCreateGraph(space);
31
+ await graph.open();
32
+ return graph;
33
+ }
34
+ }, [space, registry]);
35
+
36
+ return graph;
37
+ };
@@ -7,19 +7,19 @@ import React from 'react';
7
7
  import { Icon } from '@dxos/react-ui';
8
8
  import { mx } from '@dxos/react-ui-theme';
9
9
 
10
- import { addressToA1Notation, rangeToA1Notation } from '../../defs';
10
+ import { addressToA1Notation, isFormula, rangeToA1Notation } from '../../defs';
11
11
  import { useSheetContext } from '../SheetContext';
12
12
 
13
13
  export const FunctionEditor = () => {
14
14
  const { model, cursor, range } = useSheetContext();
15
15
 
16
16
  let value;
17
- let isFormula = false;
17
+ let formula = false;
18
18
  if (cursor) {
19
19
  value = model.getCellValue(cursor);
20
- if (typeof value === 'string' && value.charAt(0) === '=') {
20
+ if (isFormula(value)) {
21
21
  value = model.graph.mapFunctionBindingFromId(model.mapFormulaIndicesToRefs(value));
22
- isFormula = true;
22
+ formula = true;
23
23
  } else if (value != null) {
24
24
  value = String(value);
25
25
  }
@@ -36,7 +36,7 @@ export const FunctionEditor = () => {
36
36
  {(range && rangeToA1Notation(range)) || (cursor && addressToA1Notation(cursor))}
37
37
  </div>
38
38
  <div className='flex gap-2 items-center'>
39
- <Icon icon='ph--function--regular' classNames={['text-greenText', isFormula ? 'visible' : 'invisible']} />
39
+ <Icon icon='ph--function--regular' classNames={['text-greenText', formula ? 'visible' : 'invisible']} />
40
40
  <span className='font-mono'>{value}</span>
41
41
  </div>
42
42
  </div>
@@ -10,9 +10,10 @@ import { withClientProvider } from '@dxos/react-client/testing';
10
10
  import { withTheme, withLayout } from '@dxos/storybook-utils';
11
11
 
12
12
  import { GridSheet } from './GridSheet';
13
- import { useComputeGraph } from '../../hooks';
14
13
  import { createTestCells, useTestSheet, withComputeGraphDecorator } from '../../testing';
14
+ import translations from '../../translations';
15
15
  import { SheetType } from '../../types';
16
+ import { useComputeGraph } from '../ComputeGraph';
16
17
  import { SheetProvider } from '../SheetContext';
17
18
 
18
19
  export const Basic = () => {
@@ -39,6 +40,7 @@ const meta: Meta = {
39
40
  withTheme,
40
41
  withLayout({ fullscreen: true, tooltips: true }),
41
42
  ],
43
+ parameters: { translations },
42
44
  };
43
45
 
44
46
  export default meta;
@@ -2,25 +2,39 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useMemo, useRef, type FocusEvent, type WheelEvent, type KeyboardEvent } from 'react';
5
+ import React, {
6
+ useCallback,
7
+ useMemo,
8
+ useRef,
9
+ type FocusEvent,
10
+ type KeyboardEvent,
11
+ type WheelEvent,
12
+ type MouseEvent,
13
+ useState,
14
+ } from 'react';
6
15
 
16
+ import { DropdownMenu, Icon, useTranslation } from '@dxos/react-ui';
7
17
  import { useAttention } from '@dxos/react-ui-attention';
8
18
  import {
9
- type DxGridElement,
10
- Grid,
11
- type GridContentProps,
19
+ closestCell,
12
20
  editorKeys,
13
- type EditorKeysProps,
21
+ Grid,
14
22
  GridCellEditor,
15
- closestCell,
23
+ type DxGridElement,
24
+ type EditorKeysProps,
25
+ type GridContentProps,
26
+ type DxGridPosition,
16
27
  } from '@dxos/react-ui-grid';
17
28
 
18
29
  import { colLabelCell, dxGridCellIndexToSheetCellAddress, rowLabelCell, useSheetModelDxGridProps } from './util';
19
- import { rangeToA1Notation, type CellRange, DEFAULT_COLUMNS, DEFAULT_ROWS } from '../../defs';
20
- import { rangeExtension, sheetExtension, type CellRangeNotifier } from '../../extensions';
21
- import { useUpdateFocusedCellOnThreadSelection } from '../../hooks';
30
+ import { DEFAULT_COLUMNS, DEFAULT_ROWS, rangeToA1Notation, type CellRange } from '../../defs';
31
+ import { rangeExtension, sheetExtension, type RangeController } from '../../extensions';
32
+ import { useSelectThreadOnCellFocus, useUpdateFocusedCellOnThreadSelection } from '../../integrations';
33
+ import { SHEET_PLUGIN } from '../../meta';
22
34
  import { useSheetContext } from '../SheetContext';
23
35
 
36
+ const inertPosition: DxGridPosition = { plane: 'grid', col: 0, row: 0 };
37
+
24
38
  const initialCells = {
25
39
  grid: {},
26
40
  frozenColsStart: [...Array(64)].reduce((acc, _, i) => {
@@ -42,9 +56,12 @@ const sheetRowDefault = { frozenRowsStart: { size: 32, readonly: true }, grid: {
42
56
  const sheetColDefault = { frozenColsStart: { size: 48, readonly: true }, grid: { size: 180, resizeable: true } };
43
57
 
44
58
  export const GridSheet = () => {
45
- const { id, model, editing, setEditing, setCursor, setRange, range, cursor } = useSheetContext();
46
- const dxGrid = useRef<DxGridElement | null>(null);
47
- const rangeNotifier = useRef<CellRangeNotifier>();
59
+ const { t } = useTranslation(SHEET_PLUGIN);
60
+ const { id, model, editing, setEditing, setCursor, setRange, cursor, cursorFallbackRange, activeRefs } =
61
+ useSheetContext();
62
+ // NOTE(thure): using `useState` instead of `useRef` works with refs provided by `@lit/react` and gives us a reliable dependency for `useEffect` whereas `useLayoutEffect` does not guarantee the element will be defined.
63
+ const [dxGrid, setDxGrid] = useState<DxGridElement | null>(null);
64
+ const rangeController = useRef<RangeController>();
48
65
  const { hasAttention } = useAttention(id);
49
66
 
50
67
  const handleFocus = useCallback(
@@ -72,9 +89,19 @@ export const GridSheet = () => {
72
89
  ? 'col'
73
90
  : undefined;
74
91
  const delta = key.startsWith('Arrow') ? (['ArrowUp', 'ArrowLeft'].includes(key) ? -1 : 1) : shift ? -1 : 1;
75
- dxGrid.current?.refocus(axis, delta);
92
+ dxGrid?.refocus(axis, delta);
76
93
  },
77
- [model, editing, setEditing],
94
+ [model, editing, dxGrid],
95
+ );
96
+
97
+ const handleBlur = useCallback(
98
+ (value?: string) => {
99
+ if (value !== undefined) {
100
+ model.setValue(dxGridCellIndexToSheetCellAddress(editing!.index), value);
101
+ }
102
+ setEditing(null);
103
+ },
104
+ [model, editing],
78
105
  );
79
106
 
80
107
  const handleAxisResize = useCallback<NonNullable<GridContentProps['onAxisResize']>>(
@@ -100,7 +127,7 @@ export const GridSheet = () => {
100
127
  }
101
128
  if (editing) {
102
129
  // Update range selection in formula.
103
- rangeNotifier.current?.(rangeToA1Notation(range));
130
+ rangeController.current?.setRange(rangeToA1Notation(range));
104
131
  } else {
105
132
  // Setting range while editing causes focus to move to null, avoid doing so.
106
133
  setRange(range.to ? range : undefined);
@@ -108,7 +135,6 @@ export const GridSheet = () => {
108
135
  },
109
136
  [editing],
110
137
  );
111
-
112
138
  const handleWheel = useCallback(
113
139
  (event: WheelEvent) => {
114
140
  if (!hasAttention) {
@@ -120,7 +146,6 @@ export const GridSheet = () => {
120
146
 
121
147
  const handleKeyDown = useCallback(
122
148
  (event: KeyboardEvent) => {
123
- const cursorFallbackRange: CellRange | null = range ?? cursor ? { from: cursor!, to: cursor! } : null;
124
149
  switch (event.key) {
125
150
  case 'Backspace':
126
151
  case 'Delete':
@@ -151,7 +176,37 @@ export const GridSheet = () => {
151
176
  }
152
177
  }
153
178
  },
154
- [range, model, cursor],
179
+ [cursorFallbackRange, model, cursor],
180
+ );
181
+
182
+ const contextMenuAnchorRef = useRef<HTMLButtonElement | null>(null);
183
+ const [contextMenuOpen, setContextMenuOpen] = useState<DxGridPosition | null>(null);
184
+ const contextMenuAxis = contextMenuOpen?.plane.startsWith('frozenRows') ? 'col' : 'row';
185
+
186
+ const handleContextMenu = useCallback((event: MouseEvent) => {
187
+ const cell = closestCell(event.target);
188
+ if (cell && cell.plane.startsWith('frozen')) {
189
+ event.preventDefault();
190
+ contextMenuAnchorRef.current = event.target as HTMLButtonElement;
191
+ setContextMenuOpen(cell);
192
+ }
193
+ }, []);
194
+
195
+ const handleAxisMenuAction = useCallback(
196
+ (operation: 'add-before' | 'add-after' | 'remove') => {
197
+ switch (operation) {
198
+ case 'add-before':
199
+ case 'add-after':
200
+ model[contextMenuAxis === 'col' ? 'insertColumns' : 'insertRows'](
201
+ contextMenuOpen![contextMenuAxis] + (operation === 'add-before' ? 0 : 1),
202
+ 1,
203
+ );
204
+ break;
205
+ case 'remove':
206
+ // console.warn('[model does not implement]');
207
+ }
208
+ },
209
+ [contextMenuAxis, contextMenuOpen, model],
155
210
  );
156
211
 
157
212
  const { columns, rows } = useSheetModelDxGridProps(dxGrid, model);
@@ -160,7 +215,15 @@ export const GridSheet = () => {
160
215
  () => [
161
216
  editorKeys({ onClose: handleClose, ...(editing?.initialContent && { onNav: handleClose }) }),
162
217
  sheetExtension({ functions: model.graph.getFunctions() }),
163
- rangeExtension((fn) => (rangeNotifier.current = fn)),
218
+ rangeExtension({
219
+ onInit: (fn) => (rangeController.current = fn),
220
+ onStateChange: (state) => {
221
+ if (dxGrid) {
222
+ // This can’t dispatch a setState in this component, otherwise the cell editor remounts and loses focus.
223
+ dxGrid.mode = typeof state.activeRange === 'undefined' ? 'edit' : 'edit-select';
224
+ }
225
+ },
226
+ }),
164
227
  ],
165
228
  [model, handleClose, editing],
166
229
  );
@@ -173,11 +236,12 @@ export const GridSheet = () => {
173
236
  [model],
174
237
  );
175
238
 
176
- useUpdateFocusedCellOnThreadSelection(model, dxGrid);
239
+ useUpdateFocusedCellOnThreadSelection(dxGrid);
240
+ useSelectThreadOnCellFocus();
177
241
 
178
242
  return (
179
243
  <>
180
- <GridCellEditor getCellContent={getCellContent} extension={extension} />
244
+ <GridCellEditor getCellContent={getCellContent} extension={extension} onBlur={handleBlur} />
181
245
  <Grid.Content
182
246
  initialCells={initialCells}
183
247
  limitColumns={DEFAULT_COLUMNS}
@@ -192,10 +256,42 @@ export const GridSheet = () => {
192
256
  onFocus={handleFocus}
193
257
  onWheelCapture={handleWheel}
194
258
  onKeyDown={handleKeyDown}
259
+ onContextMenu={handleContextMenu}
195
260
  overscroll='inline'
196
261
  className='[--dx-grid-base:var(--surface-bg)]'
197
- ref={dxGrid}
262
+ activeRefs={activeRefs}
263
+ ref={setDxGrid}
198
264
  />
265
+ <DropdownMenu.Root
266
+ modal={false}
267
+ open={!!contextMenuOpen}
268
+ onOpenChange={(nextOpen) => setContextMenuOpen(nextOpen ? inertPosition : null)}
269
+ >
270
+ <DropdownMenu.VirtualTrigger virtualRef={contextMenuAnchorRef} />
271
+ <DropdownMenu.Content side={contextMenuAxis === 'col' ? 'bottom' : 'right'} sideOffset={4} collisionPadding={8}>
272
+ <DropdownMenu.Viewport>
273
+ <DropdownMenu.Item onClick={() => handleAxisMenuAction('add-before')}>
274
+ <Icon
275
+ size={5}
276
+ icon={contextMenuAxis === 'col' ? 'ph--columns-plus-left--regular' : 'ph--rows-plus-top--regular'}
277
+ />
278
+ <span>{t(`add ${contextMenuAxis} before label`)}</span>
279
+ </DropdownMenu.Item>
280
+ <DropdownMenu.Item onClick={() => handleAxisMenuAction('add-after')}>
281
+ <Icon
282
+ size={5}
283
+ icon={contextMenuAxis === 'col' ? 'ph--columns-plus-right--regular' : 'ph--rows-plus-bottom--regular'}
284
+ />
285
+ <span>{t(`add ${contextMenuAxis} after label`)}</span>
286
+ </DropdownMenu.Item>
287
+ <DropdownMenu.Item disabled onClick={() => handleAxisMenuAction('remove')}>
288
+ <Icon size={5} icon='ph--backspace--regular' />
289
+ <span>{t(`delete ${contextMenuAxis} label`)}</span>
290
+ </DropdownMenu.Item>
291
+ </DropdownMenu.Viewport>
292
+ <DropdownMenu.Arrow />
293
+ </DropdownMenu.Content>
294
+ </DropdownMenu.Root>
199
295
  </>
200
296
  );
201
297
  };
@@ -14,9 +14,9 @@ import { CellEditor, type CellEditorProps } from '@dxos/react-ui-grid';
14
14
  import { withTheme } from '@dxos/storybook-utils';
15
15
 
16
16
  import { HyperFormula } from '#hyperformula';
17
+ import { defaultFunctions } from '../../compute-graph';
17
18
  import { createSheet } from '../../defs';
18
19
  import { sheetExtension } from '../../extensions';
19
- import { defaultFunctions } from '../../graph';
20
20
  import { SheetType } from '../../types';
21
21
 
22
22
  type StoryProps = CellEditorProps;
@@ -2,9 +2,9 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { type MutableRefObject, useEffect, useLayoutEffect, useState } from 'react';
5
+ import { useEffect, useState } from 'react';
6
6
 
7
- import { createDocAccessor } from '@dxos/react-client/echo';
7
+ import { createDocAccessor, fullyQualifiedId } from '@dxos/react-client/echo';
8
8
  import { parseValue, cellClassesForFieldType } from '@dxos/react-ui-data';
9
9
  import {
10
10
  type GridContentProps,
@@ -16,10 +16,12 @@ import {
16
16
  type DxGridCellValue,
17
17
  colToA1Notation,
18
18
  rowToA1Notation,
19
+ commentedClassName,
19
20
  } from '@dxos/react-ui-grid';
20
21
  import { mx } from '@dxos/react-ui-theme';
21
22
 
22
- import { type CellAddress, inRange, cellClassNameForRange } from '../../defs';
23
+ import { type CellAddress, inRange, cellClassNameForRange, rangeFromIndex } from '../../defs';
24
+ import { parseThreadAnchorAsCellRange } from '../../integrations';
23
25
  import { type SheetModel } from '../../model';
24
26
 
25
27
  export const dxGridCellIndexToSheetCellAddress = (index: string): CellAddress => {
@@ -57,14 +59,22 @@ const createDxGridRows = (model: SheetModel): DxGridAxisMeta => {
57
59
  const projectCellProps = (model: SheetModel, col: number, row: number): DxGridCellValue => {
58
60
  const address = { col, row };
59
61
  const rawValue = model.getValue(address);
60
- if (rawValue === undefined || rawValue === null) {
61
- return { value: '' };
62
- }
63
- const ranges = model.sheet.ranges?.filter(({ range }) => inRange(range, address));
62
+ const ranges = model.sheet.ranges?.filter(({ range }) => inRange(rangeFromIndex(model.sheet, range), address));
63
+ const threadRefs = model.sheet.threads
64
+ ?.filter((thread) => {
65
+ const range = thread?.anchor && parseThreadAnchorAsCellRange(thread!.anchor);
66
+ return thread && range ? inRange(range, address) : false;
67
+ })
68
+ .map((thread) => fullyQualifiedId(thread!))
69
+ .join(' ');
64
70
  const type = model.getValueType(address);
65
71
  const classNames = ranges?.map(cellClassNameForRange).reverse();
66
72
 
67
- return { value: parseValue(type, rawValue), className: mx(cellClassesForFieldType(type), classNames) };
73
+ return {
74
+ value: parseValue(type, rawValue),
75
+ className: mx(cellClassesForFieldType(type), threadRefs && commentedClassName, classNames),
76
+ dataRefs: threadRefs,
77
+ };
68
78
  };
69
79
 
70
80
  const gridCellGetter = (model: SheetModel) => {
@@ -115,23 +125,23 @@ const cellGetter = (model: SheetModel) => {
115
125
  };
116
126
 
117
127
  export const useSheetModelDxGridProps = (
118
- dxGridRef: MutableRefObject<DxGridElement | null>,
128
+ dxGrid: DxGridElement | null,
119
129
  model: SheetModel,
120
130
  ): Pick<GridContentProps, 'columns' | 'rows'> => {
121
131
  const [columns, setColumns] = useState<DxGridAxisMeta>(createDxGridColumns(model));
122
132
  const [rows, setRows] = useState<DxGridAxisMeta>(createDxGridColumns(model));
123
133
 
124
- useLayoutEffect(() => {
134
+ useEffect(() => {
125
135
  const cellsAccessor = createDocAccessor(model.sheet, ['cells']);
126
- if (dxGridRef.current) {
127
- dxGridRef.current.getCells = cellGetter(model);
136
+ if (dxGrid) {
137
+ dxGrid.getCells = cellGetter(model);
128
138
  }
129
139
  const handleCellsUpdate = () => {
130
- dxGridRef.current?.requestUpdate('initialCells');
140
+ dxGrid?.requestUpdate('initialCells');
131
141
  };
132
142
  cellsAccessor.handle.addListener('change', handleCellsUpdate);
133
143
  return () => cellsAccessor.handle.removeListener('change', handleCellsUpdate);
134
- }, [model]);
144
+ }, [model, dxGrid]);
135
145
 
136
146
  useEffect(() => {
137
147
  const columnMetaAccessor = createDocAccessor(model.sheet, ['columnMeta']);
@@ -0,0 +1,53 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback } from 'react';
6
+
7
+ import { S } from '@dxos/echo-schema';
8
+ import { useTranslation } from '@dxos/react-ui';
9
+ import { List } from '@dxos/react-ui-list';
10
+ import { ghostHover } from '@dxos/react-ui-theme';
11
+
12
+ import { rangeFromIndex, rangeToA1Notation } from '../../defs';
13
+ import { SHEET_PLUGIN } from '../../meta';
14
+ import { Range, type SheetType } from '../../types';
15
+
16
+ export type RangeListProps = {
17
+ sheet: SheetType;
18
+ };
19
+
20
+ export const RangeList = ({ sheet }: RangeListProps) => {
21
+ const { t } = useTranslation(SHEET_PLUGIN);
22
+ // TODO(thure): Implement similar to comments, #8121
23
+ const handleSelectRange = (range: Range) => {};
24
+ const handleDeleteRange = useCallback(
25
+ (range: Range) => {
26
+ const index = sheet.ranges.findIndex((sheetRange) => sheetRange === range);
27
+ sheet.ranges.splice(index, 1);
28
+ },
29
+ [sheet],
30
+ );
31
+ return (
32
+ <>
33
+ <h2 className='p-2 text-sm font-semibold'>{t('range list heading')}</h2>
34
+ <List.Root<Range> items={sheet.ranges} isItem={S.is(Range)}>
35
+ {({ items: ranges }) =>
36
+ ranges.map((range, i) => (
37
+ <List.Item key={i} item={range} classNames={['p-2', ghostHover]}>
38
+ <List.ItemDragHandle />
39
+ <List.ItemTitle onClick={() => handleSelectRange(range)}>
40
+ {t('range title', {
41
+ position: rangeToA1Notation(rangeFromIndex(sheet, range.range)),
42
+ key: t(`range key ${range.key} label`),
43
+ value: t(`range value ${range.value} label`),
44
+ })}
45
+ </List.ItemTitle>
46
+ <List.ItemDeleteButton onClick={() => handleDeleteRange(range)} />
47
+ </List.Item>
48
+ ))
49
+ }
50
+ </List.Root>
51
+ </>
52
+ );
53
+ };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './RangeList';
@@ -10,19 +10,19 @@ import { withClientProvider } from '@dxos/react-client/testing';
10
10
  import { withTheme, withLayout } from '@dxos/storybook-utils';
11
11
 
12
12
  import { SheetContainer } from './SheetContainer';
13
- import { useComputeGraph } from '../../hooks';
14
13
  import { createTestCells, useTestSheet, withComputeGraphDecorator } from '../../testing';
15
14
  import { SheetType } from '../../types';
15
+ import { useComputeGraph } from '../ComputeGraph';
16
16
 
17
17
  export const Basic = () => {
18
18
  const space = useSpace();
19
19
  const graph = useComputeGraph(space);
20
20
  const sheet = useTestSheet(space, graph, { cells: createTestCells() });
21
- if (!sheet || !graph) {
21
+ if (!sheet || !space) {
22
22
  return null;
23
23
  }
24
24
 
25
- return <SheetContainer graph={graph} sheet={sheet} role='article' />;
25
+ return <SheetContainer space={space} sheet={sheet} role='article' />;
26
26
  };
27
27
 
28
28
  const meta: Meta = {
@@ -4,13 +4,19 @@
4
4
 
5
5
  import React from 'react';
6
6
 
7
+ import { type Space } from '@dxos/react-client/echo';
8
+
9
+ import { type SheetType } from '../../types';
10
+ import { useComputeGraph } from '../ComputeGraph';
7
11
  import { FunctionEditor } from '../FunctionEditor';
8
12
  import { GridSheet } from '../GridSheet';
9
- import { SheetProvider, type SheetProviderProps } from '../SheetContext';
13
+ import { SheetProvider } from '../SheetContext';
10
14
  import { Toolbar } from '../Toolbar';
11
15
 
12
- export const SheetContainer = ({ graph, sheet, role }: SheetProviderProps & { role?: string }) => {
13
- return (
16
+ export const SheetContainer = ({ space, sheet, role }: { space: Space; sheet: SheetType; role?: string }) => {
17
+ const graph = useComputeGraph(space);
18
+
19
+ return graph ? (
14
20
  <SheetProvider sheet={sheet} graph={graph}>
15
21
  <Toolbar.Root role={role}>
16
22
  <Toolbar.Styles />
@@ -23,5 +29,5 @@ export const SheetContainer = ({ graph, sheet, role }: SheetProviderProps & { ro
23
29
  <FunctionEditor />
24
30
  </div>
25
31
  </SheetProvider>
26
- );
32
+ ) : null;
27
33
  };