@dxos/plugin-sheet 0.6.14-main.7bd9c89 → 0.6.14-staging.3e2eaca

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/{SheetContainer-AKWROARP.mjs → SheetContainer-P3NF5KEI.mjs} +90 -88
  2. package/dist/lib/browser/SheetContainer-P3NF5KEI.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-FGMFOW6U.mjs → chunk-4LKIURJA.mjs} +917 -817
  4. package/dist/lib/browser/chunk-4LKIURJA.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-BWN5DZWZ.mjs → chunk-BVUN7SHF.mjs} +6 -13
  6. package/dist/lib/browser/chunk-BVUN7SHF.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-D3QTX46O.mjs → chunk-RABELMEQ.mjs} +3 -2
  8. package/dist/lib/browser/{chunk-D3QTX46O.mjs.map → chunk-RABELMEQ.mjs.map} +3 -3
  9. package/dist/lib/browser/{chunk-GSV5QNLD.mjs → chunk-VMSX6Z4X.mjs} +297 -44
  10. package/dist/lib/browser/chunk-VMSX6Z4X.mjs.map +7 -0
  11. package/dist/lib/browser/{graph-M4IQ76QX.mjs → compute-graph-GGWUX644.mjs} +4 -2
  12. package/dist/lib/browser/index.mjs +91 -21
  13. package/dist/lib/browser/index.mjs.map +4 -4
  14. package/dist/lib/browser/meta.json +1 -1
  15. package/dist/lib/browser/meta.mjs +1 -1
  16. package/dist/lib/browser/types.mjs +2 -2
  17. package/dist/lib/node/{SheetContainer-N5IQGEFL.cjs → SheetContainer-MPFKXY26.cjs} +88 -93
  18. package/dist/lib/node/SheetContainer-MPFKXY26.cjs.map +7 -0
  19. package/dist/lib/node/{chunk-QIFIGEKV.cjs → chunk-2ZVZI2KJ.cjs} +6 -5
  20. package/dist/lib/node/{chunk-QIFIGEKV.cjs.map → chunk-2ZVZI2KJ.cjs.map} +3 -3
  21. package/dist/lib/node/{chunk-NZARD7UP.cjs → chunk-AWKOWDMI.cjs} +10 -17
  22. package/dist/lib/node/chunk-AWKOWDMI.cjs.map +7 -0
  23. package/dist/lib/node/{chunk-5XPK2V4A.cjs → chunk-O7XR4R7Y.cjs} +306 -42
  24. package/dist/lib/node/chunk-O7XR4R7Y.cjs.map +7 -0
  25. package/dist/lib/node/{chunk-53BMSUIK.cjs → chunk-OO24XJBV.cjs} +853 -767
  26. package/dist/lib/node/chunk-OO24XJBV.cjs.map +7 -0
  27. package/dist/lib/node/{graph-Q3N2X26H.cjs → compute-graph-KGWA2QLE.cjs} +21 -19
  28. package/dist/lib/node/compute-graph-KGWA2QLE.cjs.map +7 -0
  29. package/dist/lib/node/index.cjs +109 -42
  30. package/dist/lib/node/index.cjs.map +4 -4
  31. package/dist/lib/node/meta.cjs +3 -3
  32. package/dist/lib/node/meta.cjs.map +1 -1
  33. package/dist/lib/node/meta.json +1 -1
  34. package/dist/lib/node/types.cjs +7 -7
  35. package/dist/lib/node/types.cjs.map +1 -1
  36. package/dist/lib/node-esm/{SheetContainer-46PBMF2E.mjs → SheetContainer-22IOAW3B.mjs} +90 -88
  37. package/dist/lib/node-esm/SheetContainer-22IOAW3B.mjs.map +7 -0
  38. package/dist/lib/node-esm/{chunk-VCYJWE3O.mjs → chunk-BM2Q3FFC.mjs} +3 -2
  39. package/dist/lib/node-esm/{chunk-VCYJWE3O.mjs.map → chunk-BM2Q3FFC.mjs.map} +3 -3
  40. package/dist/lib/node-esm/{chunk-T3PRH7QS.mjs → chunk-BW36PM2Y.mjs} +917 -817
  41. package/dist/lib/node-esm/chunk-BW36PM2Y.mjs.map +7 -0
  42. package/dist/lib/node-esm/{chunk-5WPZCXNS.mjs → chunk-CR4K75EL.mjs} +296 -44
  43. package/dist/lib/node-esm/chunk-CR4K75EL.mjs.map +7 -0
  44. package/dist/lib/node-esm/{chunk-WFDTY3IC.mjs → chunk-UIBWRHW7.mjs} +6 -13
  45. package/dist/lib/node-esm/chunk-UIBWRHW7.mjs.map +7 -0
  46. package/dist/lib/node-esm/{graph-SMPUMOV2.mjs → compute-graph-2SCZT7N5.mjs} +4 -2
  47. package/dist/lib/node-esm/index.mjs +91 -21
  48. package/dist/lib/node-esm/index.mjs.map +4 -4
  49. package/dist/lib/node-esm/meta.json +1 -1
  50. package/dist/lib/node-esm/meta.mjs +1 -1
  51. package/dist/lib/node-esm/types.mjs +2 -2
  52. package/dist/types/src/SheetPlugin.d.ts.map +1 -1
  53. package/dist/types/src/components/ComputeGraph/ComputeGraphContextProvider.d.ts +3 -1
  54. package/dist/types/src/components/ComputeGraph/ComputeGraphContextProvider.d.ts.map +1 -1
  55. package/dist/types/src/components/FunctionEditor/FunctionEditor.d.ts.map +1 -1
  56. package/dist/types/src/components/GridSheet/GridSheet.d.ts.map +1 -1
  57. package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts.map +1 -1
  58. package/dist/types/src/components/GridSheet/util.d.ts +2 -2
  59. package/dist/types/src/components/GridSheet/util.d.ts.map +1 -1
  60. package/dist/types/src/components/RangeList/RangeList.d.ts +7 -0
  61. package/dist/types/src/components/RangeList/RangeList.d.ts.map +1 -0
  62. package/dist/types/src/components/RangeList/index.d.ts +2 -0
  63. package/dist/types/src/components/RangeList/index.d.ts.map +1 -0
  64. package/dist/types/src/components/SheetContainer/SheetContainer.d.ts +6 -3
  65. package/dist/types/src/components/SheetContainer/SheetContainer.d.ts.map +1 -1
  66. package/dist/types/src/components/SheetContainer/SheetContainer.stories.d.ts.map +1 -1
  67. package/dist/types/src/components/SheetContext/SheetContext.d.ts +7 -5
  68. package/dist/types/src/components/SheetContext/SheetContext.d.ts.map +1 -1
  69. package/dist/types/src/components/Toolbar/Toolbar.d.ts +6 -3
  70. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  71. package/dist/types/src/components/index.d.ts +5 -2
  72. package/dist/types/src/components/index.d.ts.map +1 -1
  73. package/dist/types/src/compute-graph/compute-graph-registry.d.ts.map +1 -0
  74. package/dist/types/src/{graph → compute-graph}/compute-graph.d.ts +3 -3
  75. package/dist/types/src/compute-graph/compute-graph.d.ts.map +1 -0
  76. package/dist/types/src/compute-graph/compute-graph.stories.d.ts.map +1 -0
  77. package/dist/types/src/compute-graph/compute-graph.test.d.ts.map +1 -0
  78. package/dist/types/src/compute-graph/compute-node.d.ts.map +1 -0
  79. package/dist/types/src/compute-graph/functions/async-function.d.ts.map +1 -0
  80. package/dist/types/src/compute-graph/functions/edge-function.d.ts.map +1 -0
  81. package/dist/types/src/compute-graph/functions/function-defs.d.ts.map +1 -0
  82. package/dist/types/src/compute-graph/functions/index.d.ts.map +1 -0
  83. package/dist/types/src/compute-graph/hyperformula.test.d.ts.map +1 -0
  84. package/dist/types/src/compute-graph/index.d.ts.map +1 -0
  85. package/dist/types/src/compute-graph/testing/index.d.ts.map +1 -0
  86. package/dist/types/src/compute-graph/testing/test-builder.d.ts.map +1 -0
  87. package/dist/types/src/compute-graph/testing/test-plugin.d.ts.map +1 -0
  88. package/dist/types/src/compute-graph/util.d.ts.map +1 -0
  89. package/dist/types/src/defs/index.d.ts +1 -1
  90. package/dist/types/src/defs/index.d.ts.map +1 -1
  91. package/dist/types/src/defs/sheet-range-types.d.ts +2 -2
  92. package/dist/types/src/defs/sheet-range-types.d.ts.map +1 -1
  93. package/dist/types/src/defs/types.d.ts +6 -0
  94. package/dist/types/src/defs/types.d.ts.map +1 -1
  95. package/dist/types/src/defs/util.d.ts +3 -7
  96. package/dist/types/src/defs/util.d.ts.map +1 -1
  97. package/dist/types/src/extensions/compute.d.ts +1 -1
  98. package/dist/types/src/extensions/compute.d.ts.map +1 -1
  99. package/dist/types/src/extensions/editor/extension.d.ts +23 -3
  100. package/dist/types/src/extensions/editor/extension.d.ts.map +1 -1
  101. package/dist/types/src/index.d.ts +1 -1
  102. package/dist/types/src/index.d.ts.map +1 -1
  103. package/dist/types/src/integrations/index.d.ts +2 -0
  104. package/dist/types/src/integrations/index.d.ts.map +1 -0
  105. package/dist/types/src/integrations/thread-ranges.d.ts +7 -0
  106. package/dist/types/src/integrations/thread-ranges.d.ts.map +1 -0
  107. package/dist/types/src/meta.d.ts +1 -0
  108. package/dist/types/src/meta.d.ts.map +1 -1
  109. package/dist/types/src/model/index.d.ts +1 -1
  110. package/dist/types/src/model/index.d.ts.map +1 -1
  111. package/dist/types/src/model/sheet-model.d.ts +13 -6
  112. package/dist/types/src/model/sheet-model.d.ts.map +1 -1
  113. package/dist/types/src/{hooks → model}/useSheetModel.d.ts +1 -1
  114. package/dist/types/src/model/useSheetModel.d.ts.map +1 -0
  115. package/dist/types/src/serializer.d.ts +4 -0
  116. package/dist/types/src/serializer.d.ts.map +1 -0
  117. package/dist/types/src/testing/testing.d.ts +1 -1
  118. package/dist/types/src/testing/testing.d.ts.map +1 -1
  119. package/dist/types/src/translations.d.ts +18 -3
  120. package/dist/types/src/translations.d.ts.map +1 -1
  121. package/dist/types/src/types.d.ts +37 -33
  122. package/dist/types/src/types.d.ts.map +1 -1
  123. package/package.json +44 -50
  124. package/src/SheetPlugin.tsx +45 -12
  125. package/src/components/ComputeGraph/ComputeGraphContextProvider.tsx +19 -2
  126. package/src/components/FunctionEditor/FunctionEditor.tsx +6 -10
  127. package/src/components/GridSheet/GridSheet.stories.tsx +3 -1
  128. package/src/components/GridSheet/GridSheet.tsx +196 -36
  129. package/src/components/GridSheet/SheetCellEditor.stories.tsx +1 -1
  130. package/src/components/GridSheet/util.ts +41 -21
  131. package/src/components/RangeList/RangeList.tsx +53 -0
  132. package/src/components/RangeList/index.ts +5 -0
  133. package/src/components/SheetContainer/SheetContainer.stories.tsx +5 -3
  134. package/src/components/SheetContainer/SheetContainer.tsx +19 -12
  135. package/src/components/SheetContext/SheetContext.tsx +43 -20
  136. package/src/components/Toolbar/Toolbar.tsx +98 -86
  137. package/src/components/index.ts +1 -0
  138. package/src/{graph → compute-graph}/compute-graph.stories.tsx +2 -1
  139. package/src/{graph → compute-graph}/compute-graph.ts +27 -9
  140. package/src/{graph → compute-graph}/compute-node.ts +2 -3
  141. package/src/{graph → compute-graph}/functions/async-function.ts +1 -0
  142. package/src/{graph → compute-graph}/functions/edge-function.ts +5 -3
  143. package/src/defs/index.ts +1 -1
  144. package/src/defs/sheet-range-types.ts +9 -6
  145. package/src/defs/types.ts +6 -1
  146. package/src/defs/util.ts +4 -19
  147. package/src/extensions/compute.stories.tsx +2 -2
  148. package/src/extensions/compute.ts +2 -2
  149. package/src/extensions/editor/extension.test.ts +1 -1
  150. package/src/extensions/editor/extension.ts +48 -23
  151. package/src/index.ts +2 -2
  152. package/src/integrations/index.ts +5 -0
  153. package/src/integrations/thread-ranges.ts +101 -0
  154. package/src/meta.ts +1 -0
  155. package/src/model/index.ts +1 -1
  156. package/src/model/sheet-model.test.ts +6 -4
  157. package/src/model/sheet-model.ts +103 -26
  158. package/src/{hooks → model}/useSheetModel.ts +1 -1
  159. package/src/serializer.ts +27 -0
  160. package/src/testing/testing.tsx +1 -1
  161. package/src/translations.ts +18 -3
  162. package/src/types.ts +24 -2
  163. package/dist/lib/browser/SheetContainer-AKWROARP.mjs.map +0 -7
  164. package/dist/lib/browser/chunk-BWN5DZWZ.mjs.map +0 -7
  165. package/dist/lib/browser/chunk-FGMFOW6U.mjs.map +0 -7
  166. package/dist/lib/browser/chunk-GSV5QNLD.mjs.map +0 -7
  167. package/dist/lib/node/SheetContainer-N5IQGEFL.cjs.map +0 -7
  168. package/dist/lib/node/chunk-53BMSUIK.cjs.map +0 -7
  169. package/dist/lib/node/chunk-5XPK2V4A.cjs.map +0 -7
  170. package/dist/lib/node/chunk-NZARD7UP.cjs.map +0 -7
  171. package/dist/lib/node/graph-Q3N2X26H.cjs.map +0 -7
  172. package/dist/lib/node-esm/SheetContainer-46PBMF2E.mjs.map +0 -7
  173. package/dist/lib/node-esm/chunk-5WPZCXNS.mjs.map +0 -7
  174. package/dist/lib/node-esm/chunk-T3PRH7QS.mjs.map +0 -7
  175. package/dist/lib/node-esm/chunk-WFDTY3IC.mjs.map +0 -7
  176. package/dist/types/src/graph/compute-graph-registry.d.ts.map +0 -1
  177. package/dist/types/src/graph/compute-graph.d.ts.map +0 -1
  178. package/dist/types/src/graph/compute-graph.stories.d.ts.map +0 -1
  179. package/dist/types/src/graph/compute-graph.test.d.ts.map +0 -1
  180. package/dist/types/src/graph/compute-node.d.ts.map +0 -1
  181. package/dist/types/src/graph/functions/async-function.d.ts.map +0 -1
  182. package/dist/types/src/graph/functions/edge-function.d.ts.map +0 -1
  183. package/dist/types/src/graph/functions/function-defs.d.ts.map +0 -1
  184. package/dist/types/src/graph/functions/index.d.ts.map +0 -1
  185. package/dist/types/src/graph/hyperformula.test.d.ts.map +0 -1
  186. package/dist/types/src/graph/index.d.ts.map +0 -1
  187. package/dist/types/src/graph/testing/index.d.ts.map +0 -1
  188. package/dist/types/src/graph/testing/test-builder.d.ts.map +0 -1
  189. package/dist/types/src/graph/testing/test-plugin.d.ts.map +0 -1
  190. package/dist/types/src/graph/util.d.ts.map +0 -1
  191. package/dist/types/src/hooks/hooks.stories.d.ts +0 -6
  192. package/dist/types/src/hooks/hooks.stories.d.ts.map +0 -1
  193. package/dist/types/src/hooks/index.d.ts +0 -4
  194. package/dist/types/src/hooks/index.d.ts.map +0 -1
  195. package/dist/types/src/hooks/threads.d.ts +0 -8
  196. package/dist/types/src/hooks/threads.d.ts.map +0 -1
  197. package/dist/types/src/hooks/useComputeGraph.d.ts +0 -7
  198. package/dist/types/src/hooks/useComputeGraph.d.ts.map +0 -1
  199. package/dist/types/src/hooks/useSheetModel.d.ts.map +0 -1
  200. package/dist/types/src/model/decorations.d.ts +0 -26
  201. package/dist/types/src/model/decorations.d.ts.map +0 -1
  202. package/src/hooks/hooks.stories.tsx +0 -53
  203. package/src/hooks/index.ts +0 -7
  204. package/src/hooks/threads.ts +0 -147
  205. package/src/hooks/useComputeGraph.ts +0 -28
  206. package/src/model/decorations.ts +0 -66
  207. /package/dist/lib/browser/{graph-M4IQ76QX.mjs.map → compute-graph-GGWUX644.mjs.map} +0 -0
  208. /package/dist/lib/node-esm/{graph-SMPUMOV2.mjs.map → compute-graph-2SCZT7N5.mjs.map} +0 -0
  209. /package/dist/types/src/{graph → compute-graph}/compute-graph-registry.d.ts +0 -0
  210. /package/dist/types/src/{graph → compute-graph}/compute-graph.stories.d.ts +0 -0
  211. /package/dist/types/src/{graph → compute-graph}/compute-graph.test.d.ts +0 -0
  212. /package/dist/types/src/{graph → compute-graph}/compute-node.d.ts +0 -0
  213. /package/dist/types/src/{graph → compute-graph}/functions/async-function.d.ts +0 -0
  214. /package/dist/types/src/{graph → compute-graph}/functions/edge-function.d.ts +0 -0
  215. /package/dist/types/src/{graph → compute-graph}/functions/function-defs.d.ts +0 -0
  216. /package/dist/types/src/{graph → compute-graph}/functions/index.d.ts +0 -0
  217. /package/dist/types/src/{graph → compute-graph}/hyperformula.test.d.ts +0 -0
  218. /package/dist/types/src/{graph → compute-graph}/index.d.ts +0 -0
  219. /package/dist/types/src/{graph → compute-graph}/testing/index.d.ts +0 -0
  220. /package/dist/types/src/{graph → compute-graph}/testing/test-builder.d.ts +0 -0
  221. /package/dist/types/src/{graph → compute-graph}/testing/test-plugin.d.ts +0 -0
  222. /package/dist/types/src/{graph → compute-graph}/util.d.ts +0 -0
  223. /package/src/{graph → compute-graph}/compute-graph-registry.ts +0 -0
  224. /package/src/{graph → compute-graph}/compute-graph.test.ts +0 -0
  225. /package/src/{graph → compute-graph}/functions/function-defs.ts +0 -0
  226. /package/src/{graph → compute-graph}/functions/index.ts +0 -0
  227. /package/src/{graph → compute-graph}/hyperformula.test.ts +0 -0
  228. /package/src/{graph → compute-graph}/index.ts +0 -0
  229. /package/src/{graph → compute-graph}/testing/index.ts +0 -0
  230. /package/src/{graph → compute-graph}/testing/test-builder.ts +0 -0
  231. /package/src/{graph → compute-graph}/testing/test-plugin.ts +0 -0
  232. /package/src/{graph → compute-graph}/util.ts +0 -0
@@ -2,16 +2,21 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { type PropsWithChildren, createContext, useContext, useMemo, useState } from 'react';
5
+ import React, { type PropsWithChildren, createContext, useCallback, useContext, useState } from 'react';
6
6
 
7
7
  import { invariant } from '@dxos/invariant';
8
8
  import { fullyQualifiedId } from '@dxos/react-client/echo';
9
- import { Grid, useGridContext, type GridScopedProps, type GridEditing } from '@dxos/react-ui-grid';
10
-
11
- import { type CellAddress, type CellRange } from '../../defs';
12
- import { type ComputeGraph } from '../../graph';
13
- import { useSheetModel, useSelectThreadOnCellFocus, useThreadDecorations } from '../../hooks';
14
- import { type SheetModel, createDecorations } from '../../model';
9
+ import {
10
+ Grid,
11
+ type GridContentProps,
12
+ type GridEditing,
13
+ type GridScopedProps,
14
+ useGridContext,
15
+ } from '@dxos/react-ui-grid';
16
+
17
+ import { type ComputeGraph } from '../../compute-graph';
18
+ import { type CellAddress, type CellRange, type CompleteCellRange } from '../../defs';
19
+ import { type SheetModel, useSheetModel } from '../../model';
15
20
  import { type SheetType } from '../../types';
16
21
 
17
22
  export type SheetContextValue = {
@@ -25,17 +30,19 @@ export type SheetContextValue = {
25
30
  setCursor: (cell: CellAddress | undefined) => void;
26
31
  range?: CellRange;
27
32
  setRange: (range: CellRange | undefined) => void;
33
+ cursorFallbackRange?: CompleteCellRange;
28
34
 
29
35
  // Editing state (undefined if not editing).
30
36
  editing: GridEditing;
31
37
  setEditing: (editing: GridEditing) => void;
32
38
 
39
+ // Active refs
40
+ activeRefs: GridContentProps['activeRefs'];
41
+ setActiveRefs: (activeRefs: GridContentProps['activeRefs']) => void;
42
+
33
43
  // Events.
34
44
  // TODO(burdon): Generalize.
35
45
  onInfo?: () => void;
36
-
37
- // Decorations.
38
- decorations: ReturnType<typeof createDecorations>;
39
46
  };
40
47
 
41
48
  const SheetContext = createContext<SheetContextValue | undefined>(undefined);
@@ -54,15 +61,29 @@ const SheetProviderImpl = ({
54
61
  }: GridScopedProps<PropsWithChildren<Pick<SheetContextValue, 'onInfo' | 'model'>>>) => {
55
62
  const { id, editing, setEditing } = useGridContext('SheetProvider', __gridScope);
56
63
 
57
- // TODO(Zan): Impl. set range and set cursor that scrolls to that cell or range if it is not visible.
58
- const decorations = useMemo(() => createDecorations(), []);
59
-
60
- // TODO(thure): Reconnect these.
61
- const [cursor, setCursor] = useState<CellAddress>();
62
- const [range, setRange] = useState<CellRange>();
63
-
64
- useSelectThreadOnCellFocus(model, cursor);
65
- useThreadDecorations(model, decorations);
64
+ const [cursor, setCursorInternal] = useState<CellAddress>();
65
+ const [range, setRangeInternal] = useState<CellRange>();
66
+ const [cursorFallbackRange, setCursorFallbackRange] = useState<CompleteCellRange>();
67
+ const [activeRefs, setActiveRefs] = useState<GridContentProps['activeRefs']>('');
68
+
69
+ const setCursor = useCallback(
70
+ (nextCursor?: CellAddress) => {
71
+ setCursorInternal(nextCursor);
72
+ setCursorFallbackRange(
73
+ range?.to ? (range as CompleteCellRange) : nextCursor ? { from: nextCursor!, to: nextCursor! } : undefined,
74
+ );
75
+ },
76
+ [range],
77
+ );
78
+ const setRange = useCallback(
79
+ (nextRange?: CellRange) => {
80
+ setRangeInternal(nextRange);
81
+ setCursorFallbackRange(
82
+ nextRange?.to ? (nextRange as CompleteCellRange) : cursor ? { from: cursor!, to: cursor! } : undefined,
83
+ );
84
+ },
85
+ [cursor],
86
+ );
66
87
 
67
88
  return (
68
89
  <SheetContext.Provider
@@ -75,9 +96,11 @@ const SheetProviderImpl = ({
75
96
  setCursor,
76
97
  range,
77
98
  setRange,
99
+ cursorFallbackRange,
100
+ activeRefs,
101
+ setActiveRefs,
78
102
  // TODO(burdon): Change to event.
79
103
  onInfo,
80
- decorations,
81
104
  }}
82
105
  >
83
106
  {children}
@@ -3,32 +3,36 @@
3
3
  //
4
4
 
5
5
  import { createContext } from '@radix-ui/react-context';
6
- import React, { type PropsWithChildren, useCallback, useMemo } from 'react';
6
+ import React, { type PropsWithChildren, useCallback } from 'react';
7
7
 
8
8
  import { useIntentDispatcher } from '@dxos/app-framework';
9
9
  import {
10
10
  Icon,
11
+ type ThemedClassName,
11
12
  Toolbar as NaturalToolbar,
12
- useTranslation,
13
- Tooltip,
14
- type ToolbarToggleGroupItemProps as NaturalToolbarToggleGroupItemProps,
15
13
  type ToolbarButtonProps as NaturalToolbarButtonProps,
14
+ type ToolbarToggleGroupItemProps as NaturalToolbarToggleGroupItemProps,
16
15
  type ToolbarToggleProps as NaturalToolbarToggleProps,
17
- type ThemedClassName,
16
+ Tooltip,
17
+ useTranslation,
18
18
  } from '@dxos/react-ui';
19
19
  import { useAttention } from '@dxos/react-ui-attention';
20
20
  import { nonNullable } from '@dxos/util';
21
21
 
22
22
  import {
23
- addressToIndex,
23
+ alignKey,
24
24
  type AlignKey,
25
25
  type AlignValue,
26
26
  type CommentKey,
27
27
  type CommentValue,
28
28
  inRange,
29
+ rangeFromIndex,
30
+ rangeToIndex,
31
+ styleKey,
29
32
  type StyleKey,
30
33
  type StyleValue,
31
34
  } from '../../defs';
35
+ import { completeCellRangeToThreadCursor } from '../../integrations';
32
36
  import { SHEET_PLUGIN } from '../../meta';
33
37
  import { type SheetType } from '../../types';
34
38
  import { useSheetContext } from '../SheetContext';
@@ -86,10 +90,11 @@ type CommentAction = { key: CommentKey; value: CommentValue; cellContent?: strin
86
90
  type StyleAction = { key: StyleKey; value: StyleValue };
87
91
 
88
92
  export type ToolbarAction = StyleAction | AlignAction | CommentAction;
93
+ export type ToolbarActionAnnotated = ToolbarAction & { unset?: boolean };
89
94
 
90
95
  export type ToolbarActionType = ToolbarAction['key'];
91
96
 
92
- export type ToolbarActionHandler = (action: ToolbarAction) => void;
97
+ export type ToolbarActionHandler = (action: ToolbarActionAnnotated) => void;
93
98
 
94
99
  export type ToolbarProps = ThemedClassName<
95
100
  PropsWithChildren<{
@@ -97,53 +102,57 @@ export type ToolbarProps = ThemedClassName<
97
102
  }>
98
103
  >;
99
104
 
100
- const [ToolbarContextProvider, useToolbarContext] = createContext<{ onAction: (action: ToolbarAction) => void }>(
101
- 'Toolbar',
102
- );
103
-
104
- // TODO(Zan): Factor out, copied this from MarkdownPlugin.
105
- const sectionToolbarLayout =
106
- 'bs-[--rail-action] bg-[--sticky-bg] sticky block-start-0 __-block-start-px transition-opacity';
105
+ const [ToolbarContextProvider, useToolbarContext] = createContext<{
106
+ onAction: (action: ToolbarActionAnnotated) => void;
107
+ }>('Toolbar');
107
108
 
108
109
  type Range = SheetType['ranges'][number];
109
110
 
110
111
  const ToolbarRoot = ({ children, role, classNames }: ToolbarProps) => {
111
- const { id, model, range, cursor } = useSheetContext();
112
+ const { id, model, cursorFallbackRange, cursor } = useSheetContext();
112
113
  const { hasAttention } = useAttention(id);
113
114
  const dispatch = useIntentDispatcher();
114
115
 
115
- // TODO(Zan): Centralise the toolbar action handler. Current implementation in stories.
116
+ // TODO(Zan): Externalize the toolbar action handler. E.g., Toolbar/keys should both fire events.
116
117
  const handleAction = useCallback(
117
- (action: ToolbarAction) => {
118
+ (action: ToolbarActionAnnotated) => {
118
119
  switch (action.key) {
119
- case 'align':
120
- if (cursor) {
121
- const index = model.sheet.ranges?.findIndex(
122
- (range) => range.key === action.key && inRange(range.range, cursor),
123
- );
124
- const nextRange = range ? { from: range.from, to: range.to ?? range.from } : { from: cursor, to: cursor };
120
+ case 'alignment':
121
+ if (cursorFallbackRange) {
122
+ const index =
123
+ model.sheet.ranges?.findIndex(
124
+ (range) =>
125
+ range.key === action.key &&
126
+ inRange(rangeFromIndex(model.sheet, range.range), cursorFallbackRange.from),
127
+ ) ?? -1;
125
128
  const nextRangeEntity = {
126
- range: nextRange as Range['range'],
129
+ range: rangeToIndex(model.sheet, cursorFallbackRange),
127
130
  key: action.key,
128
131
  value: action.value,
129
132
  };
130
133
  if (index < 0) {
131
134
  model.sheet.ranges?.push(nextRangeEntity);
135
+ } else if (model.sheet.ranges![index].value === action.value) {
136
+ model.sheet.ranges?.splice(index, 1);
132
137
  } else {
133
138
  model.sheet.ranges?.splice(index, 1, nextRangeEntity);
134
139
  }
135
140
  }
136
141
  break;
137
142
  case 'style':
138
- if (action.value === 'unset') {
139
- const index = model.sheet.ranges?.findIndex((range) => range.key === action.key);
143
+ if (action.unset) {
144
+ const index = model.sheet.ranges?.findIndex(
145
+ (range) =>
146
+ range.key === action.key &&
147
+ cursorFallbackRange &&
148
+ inRange(rangeFromIndex(model.sheet, range.range), cursorFallbackRange.from),
149
+ );
140
150
  if (index >= 0) {
141
151
  model.sheet.ranges?.splice(index, 1);
142
152
  }
143
- } else if (range || cursor) {
144
- const nextRange = range ? { from: range.from, to: range.to ?? range.from } : { from: cursor, to: cursor };
153
+ } else if (cursorFallbackRange) {
145
154
  model.sheet.ranges?.push({
146
- range: nextRange as Range['range'],
155
+ range: rangeToIndex(model.sheet, cursorFallbackRange),
147
156
  key: action.key,
148
157
  value: action.value,
149
158
  });
@@ -151,30 +160,25 @@ const ToolbarRoot = ({ children, role, classNames }: ToolbarProps) => {
151
160
  break;
152
161
  case 'comment': {
153
162
  // TODO(Zan): We shouldn't hardcode the action ID.
154
- void dispatch({
155
- action: 'dxos.org/plugin/thread/action/create',
156
- data: {
157
- cursor: action.value,
158
- name: action.cellContent,
159
- subject: model.sheet,
160
- },
161
- });
163
+ if (cursorFallbackRange) {
164
+ void dispatch({
165
+ action: 'dxos.org/plugin/thread/action/create',
166
+ data: {
167
+ cursor: completeCellRangeToThreadCursor(cursorFallbackRange),
168
+ name: action.cellContent,
169
+ subject: model.sheet,
170
+ },
171
+ });
172
+ }
162
173
  }
163
174
  }
164
175
  },
165
- [model.sheet, range, cursor, dispatch],
176
+ [model.sheet, cursorFallbackRange, cursor, dispatch],
166
177
  );
167
178
 
168
179
  return (
169
180
  <ToolbarContextProvider onAction={handleAction}>
170
- <NaturalToolbar.Root
171
- classNames={[
172
- ...(role === 'section'
173
- ? ['z-[2] group-focus-within/section:visible', !hasAttention && 'invisible', sectionToolbarLayout]
174
- : ['attention-surface']),
175
- classNames,
176
- ]}
177
- >
181
+ <NaturalToolbar.Root classNames={['pli-0.5', !hasAttention && 'opacity-20', classNames]}>
178
182
  {children}
179
183
  </NaturalToolbar.Root>
180
184
  </ToolbarContextProvider>
@@ -204,48 +208,55 @@ const Alignment = () => {
204
208
  const { onAction } = useToolbarContext('Alignment');
205
209
  const { t } = useTranslation(SHEET_PLUGIN);
206
210
 
207
- const value = useMemo(
208
- () =>
209
- cursor
210
- ? model.sheet.ranges?.find(({ range, key }) => key === 'alignment' && inRange(range, cursor))?.value
211
- : undefined,
212
- [cursor, model.sheet.ranges],
213
- );
211
+ // TODO(thure): Can this O(n) call be memoized?
212
+ const value = cursor
213
+ ? model.sheet.ranges?.findLast(
214
+ ({ range, key }) => key === alignKey && inRange(rangeFromIndex(model.sheet, range), cursor),
215
+ )?.value
216
+ : undefined;
214
217
 
215
218
  return (
216
219
  <NaturalToolbar.ToggleGroup
217
220
  type='single'
218
- value={value}
219
- onValueChange={(value: AlignValue) => onAction?.({ key: 'align', value })}
221
+ value={
222
+ // TODO(thure): providing `undefined` leaves the last item active which was active rather than showing none.
223
+ value ?? 'never'
224
+ }
225
+ onValueChange={(value: AlignValue) => onAction?.({ key: alignKey, value })}
220
226
  >
221
227
  {alignmentOptions.map(({ value, icon }) => (
222
228
  <ToolbarItem itemType='toggleGroupItem' key={value} value={value} icon={icon}>
223
- {t(`toolbar ${value} label`)}
229
+ {t('toolbar action label', {
230
+ key: t(`range key ${alignKey} label`),
231
+ value: t(`range value ${value} label`),
232
+ })}
224
233
  </ToolbarItem>
225
234
  ))}
226
235
  </NaturalToolbar.ToggleGroup>
227
236
  );
228
237
  };
229
238
 
230
- const styleOptions: ButtonProps<StyleValue>[] = [{ value: 'highlight', icon: 'ph--highlighter--regular' }];
239
+ const styleOptions: ButtonProps<StyleValue>[] = [
240
+ { value: 'highlight', icon: 'ph--highlighter--regular' },
241
+ { value: 'softwrap', icon: 'ph--paragraph--regular' },
242
+ ];
231
243
 
232
244
  const Styles = () => {
233
- const { cursor, model } = useSheetContext();
245
+ const { cursorFallbackRange, model } = useSheetContext();
234
246
  const { onAction } = useToolbarContext('Styles');
235
247
  const { t } = useTranslation(SHEET_PLUGIN);
236
248
 
237
- const activeValues = useMemo(
238
- () =>
239
- cursor
240
- ? model.sheet.ranges
241
- ?.filter(({ range, key }) => key === 'style' && inRange(range, cursor))
242
- .reduce((acc, { value }) => {
243
- acc.add(value);
244
- return acc;
245
- }, new Set())
246
- : undefined,
247
- [cursor, model.sheet.ranges],
248
- );
249
+ // TODO(thure): Can this O(n) call be memoized?
250
+ const activeValues = cursorFallbackRange
251
+ ? model.sheet.ranges
252
+ ?.filter(
253
+ ({ range, key }) => key === 'style' && inRange(rangeFromIndex(model.sheet, range), cursorFallbackRange.from),
254
+ )
255
+ .reduce((acc, { value }) => {
256
+ acc.add(value);
257
+ return acc;
258
+ }, new Set())
259
+ : undefined;
249
260
 
250
261
  return (
251
262
  <>
@@ -254,10 +265,15 @@ const Styles = () => {
254
265
  itemType='toggle'
255
266
  key={value}
256
267
  pressed={activeValues?.has(value)}
257
- onPressedChange={(nextPressed: boolean) => onAction?.({ key: 'style', value: nextPressed ? value : 'unset' })}
268
+ onPressedChange={(nextPressed: boolean) => {
269
+ onAction?.({ key: 'style', value, unset: !nextPressed });
270
+ }}
258
271
  icon={icon}
259
272
  >
260
- {t(`toolbar ${value} label`)}
273
+ {t('toolbar action label', {
274
+ key: t(`range key ${styleKey} label`),
275
+ value: t(`range value ${value} label`),
276
+ })}
261
277
  </ToolbarItem>
262
278
  ))}
263
279
  </>
@@ -270,29 +286,25 @@ const Styles = () => {
270
286
 
271
287
  const Actions = () => {
272
288
  const { onAction } = useToolbarContext('Actions');
273
- const { cursor, range, model } = useSheetContext();
289
+ const { cursorFallbackRange, cursor, model } = useSheetContext();
274
290
  const { t } = useTranslation(SHEET_PLUGIN);
275
291
 
292
+ // TODO(thure): Can this O(n) call be memoized?
276
293
  const overlapsCommentAnchor = (model.sheet.threads ?? [])
277
294
  .filter(nonNullable)
278
295
  .filter((thread) => thread.status !== 'resolved')
279
296
  .some((thread) => {
280
- if (!cursor) {
297
+ if (!cursorFallbackRange) {
281
298
  return false;
282
299
  }
283
- return addressToIndex(model.sheet, cursor) === thread.anchor;
300
+ return rangeToIndex(model.sheet, cursorFallbackRange) === thread.anchor;
284
301
  });
285
302
 
286
- const hasCursor = !!cursor;
287
- const cursorOnly = hasCursor && !range && !overlapsCommentAnchor;
288
-
289
- const tooltipLabelKey = !hasCursor
303
+ const tooltipLabelKey = !cursor
290
304
  ? 'no cursor label'
291
305
  : overlapsCommentAnchor
292
306
  ? 'selection overlaps existing comment label'
293
- : range
294
- ? 'comment ranges not supported label'
295
- : 'comment label';
307
+ : 'comment label';
296
308
 
297
309
  return (
298
310
  <ToolbarItem
@@ -301,16 +313,16 @@ const Actions = () => {
301
313
  icon='ph--chat-text--regular'
302
314
  data-testid='editor.toolbar.comment'
303
315
  onClick={() => {
304
- if (!cursor) {
316
+ if (!cursorFallbackRange) {
305
317
  return;
306
318
  }
307
319
  return onAction?.({
308
320
  key: 'comment',
309
- value: addressToIndex(model.sheet, cursor),
310
- cellContent: model.getCellText(cursor),
321
+ value: rangeToIndex(model.sheet, cursorFallbackRange),
322
+ cellContent: model.getCellText(cursorFallbackRange.from),
311
323
  });
312
324
  }}
313
- disabled={!cursorOnly || overlapsCommentAnchor}
325
+ disabled={!cursorFallbackRange || overlapsCommentAnchor}
314
326
  >
315
327
  {t(tooltipLabelKey)}
316
328
  </ToolbarItem>
@@ -6,6 +6,7 @@ import { lazy } from 'react';
6
6
 
7
7
  export * from './ComputeGraph';
8
8
  export * from './GridSheet';
9
+ export * from './RangeList';
9
10
  export * from './SheetContext';
10
11
 
11
12
  export const SheetContainer = lazy(() => import('./SheetContainer'));
@@ -15,8 +15,9 @@ import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
15
15
  import { withTheme } from '@dxos/storybook-utils';
16
16
 
17
17
  import { testFunctionPlugins } from './testing';
18
+ import { useComputeGraph } from '../components';
18
19
  import { createSheet } from '../defs';
19
- import { useComputeGraph, useSheetModel } from '../hooks';
20
+ import { useSheetModel } from '../model';
20
21
  import { withComputeGraphDecorator } from '../testing';
21
22
  import { SheetType } from '../types';
22
23
 
@@ -6,6 +6,7 @@ import { type Listeners } from 'hyperformula/typings/Emitter';
6
6
 
7
7
  import { Event } from '@dxos/async';
8
8
  import { type Space, Filter, fullyQualifiedId } from '@dxos/client/echo';
9
+ import { FQ_ID_LENGTH } from '@dxos/client/echo';
9
10
  import { Resource } from '@dxos/context';
10
11
  import { getTypename } from '@dxos/echo-schema';
11
12
  import { invariant } from '@dxos/invariant';
@@ -24,8 +25,9 @@ import {
24
25
  EDGE_FUNCTION_NAME,
25
26
  } from './functions';
26
27
 
27
- // TODO(wittjosiah): Factor out.
28
- const OBJECT_ID_LENGTH = 60; // 33 (space id) + 1 (separator) + 26 (object id).
28
+ // TODO(burdon): Factor out compute-graph.
29
+
30
+ const UNKNOWN_BINDING = '__UNKNOWN__';
29
31
 
30
32
  // TODO(burdon): Factory.
31
33
  // export type ComputeNodeGenerator = <T>(obj: T) => ComputeNode;
@@ -42,7 +44,7 @@ export const parseSheetName = (name: string): Partial<ObjectRef> => {
42
44
  return id ? { type, id } : { id: type };
43
45
  };
44
46
 
45
- export type ComputeGraphEvent = 'functionsUpdated';
47
+ export type ComputeGraphEvent = 'functionsUpdated' | 'valuesUpdated';
46
48
 
47
49
  /**
48
50
  * Per-space compute and dependency graph.
@@ -62,7 +64,7 @@ export class ComputeGraph extends Resource {
62
64
  public readonly update = new Event<{ type: ComputeGraphEvent }>();
63
65
 
64
66
  // The context is passed to all functions.
65
- public readonly context = new FunctionContext(this._hf, this._space, this._options);
67
+ public readonly context: FunctionContext;
66
68
 
67
69
  constructor(
68
70
  private readonly _hf: HyperFormula,
@@ -70,7 +72,17 @@ export class ComputeGraph extends Resource {
70
72
  private readonly _options?: Partial<FunctionContextOptions>,
71
73
  ) {
72
74
  super();
75
+
76
+ const contextOptions = {
77
+ ...this._options,
78
+ onUpdate: (update) => {
79
+ this._options?.onUpdate?.(update);
80
+ this.update.emit({ type: 'valuesUpdated' });
81
+ },
82
+ } satisfies Partial<FunctionContextOptions>;
83
+ this.context = new FunctionContext(this._hf, this._space, contextOptions);
73
84
  this._hf.updateConfig({ context: this.context });
85
+
74
86
  // TODO(burdon): If debounce then aggregate changes.
75
87
  const onValuesUpdate: Listeners['valuesUpdated'] = (changes) => {
76
88
  for (const change of changes) {
@@ -116,7 +128,7 @@ export class ComputeGraph extends Resource {
116
128
  getOrCreateNode(name: string): ComputeNode {
117
129
  invariant(name.length);
118
130
  if (!this._hf.doesSheetExist(name)) {
119
- log.info('created node', { space: this._space?.id, sheet: name });
131
+ log('created node', { space: this._space?.id, sheet: name });
120
132
  this._hf.addSheet(name);
121
133
  }
122
134
 
@@ -130,7 +142,7 @@ export class ComputeGraph extends Resource {
130
142
 
131
143
  /**
132
144
  * Map bound value to custom function invocation.
133
- * E.g., "HELLO(...args)" => "EDGE("HELLO", ...args)".
145
+ * E.g., "HELLO(...args)" => "DX("HELLO", ...args)".
134
146
  */
135
147
  mapFormulaToNative(formula: string): string {
136
148
  return (
@@ -206,9 +218,9 @@ export class ComputeGraph extends Resource {
206
218
  * E.g., spaceId:objectId() => HELLO()
207
219
  */
208
220
  mapFunctionBindingFromId(formula: string) {
209
- return formula.replace(/(\w+):([a-zA-Z0-9]+)\((.*)\)/g, (match, spaceId, objectId, args) => {
221
+ const binding = formula.replace(/(\w+):([a-zA-Z0-9]+)\((.*)\)/g, (match, spaceId, objectId, args) => {
210
222
  const id = `${spaceId}:${objectId}`;
211
- if (id.length !== OBJECT_ID_LENGTH) {
223
+ if (id.length !== FQ_ID_LENGTH) {
212
224
  return match;
213
225
  }
214
226
 
@@ -216,9 +228,15 @@ export class ComputeGraph extends Resource {
216
228
  if (fn?.binding) {
217
229
  return `${fn.binding}(${args})`;
218
230
  } else {
219
- return match;
231
+ return UNKNOWN_BINDING;
220
232
  }
221
233
  });
234
+
235
+ if (binding.startsWith(`=${UNKNOWN_BINDING}`)) {
236
+ return undefined;
237
+ } else {
238
+ return binding;
239
+ }
222
240
  }
223
241
 
224
242
  protected override async _open() {
@@ -10,7 +10,7 @@ import { Resource } from '@dxos/context';
10
10
 
11
11
  import { DetailedCellError } from '#hyperformula';
12
12
  import { type ComputeGraph } from './compute-graph';
13
- import { type CellAddress } from '../defs';
13
+ import { type CellAddress, isFormula } from '../defs';
14
14
  import { type CellScalarValue } from '../types';
15
15
 
16
16
  export type ComputeNodeEvent = {
@@ -50,8 +50,7 @@ export class ComputeNode extends Resource {
50
50
  }
51
51
 
52
52
  setValue(cell: CellAddress, value: CellScalarValue) {
53
- const mappedValue =
54
- typeof value === 'string' && value.charAt(0) === '=' ? this._graph.mapFormulaToNative(value) : value;
53
+ const mappedValue = isFormula(value) ? this._graph.mapFormulaToNative(value) : value;
55
54
  this._graph.hf.setCellContents({ sheet: this.sheetId, row: cell.row, col: cell.col }, [[mappedValue]]);
56
55
  }
57
56
 
@@ -83,6 +83,7 @@ export class FunctionContext {
83
83
  ) {
84
84
  this._options = defaultsDeep(_options ?? {}, defaultFunctionContextOptions);
85
85
  this._onUpdate = debounce((update) => {
86
+ log('update', update);
86
87
  // TODO(burdon): Better way to trigger recalculation? (NOTE: rebuildAndRecalculate resets the undo history.)
87
88
  this._hf.resumeEvaluation();
88
89
  this._options.onUpdate?.(update);
@@ -44,7 +44,7 @@ export class EdgeFunctionPlugin extends AsyncFunctionPlugin {
44
44
 
45
45
  if (subscribe) {
46
46
  const unsubscribe = effect(() => {
47
- log.info('function changed', { fn });
47
+ log('function changed', { fn });
48
48
  const _ = fn?.version;
49
49
 
50
50
  // TODO(wittjosiah): `ttl` should be 0 to force a recalculation when a new version is deployed.
@@ -56,13 +56,15 @@ export class EdgeFunctionPlugin extends AsyncFunctionPlugin {
56
56
  }
57
57
 
58
58
  const path = getUserFunctionUrlInMetadata(getMeta(fn));
59
- const result = await fetch(`${this.context.remoteFunctionUrl}${path}`, {
59
+ const response = await fetch(`${this.context.remoteFunctionUrl}${path}`, {
60
60
  method: 'POST',
61
61
  headers: { 'Content-Type': 'application/json' },
62
62
  body: JSON.stringify({ args: args.filter(nonNullable) }),
63
63
  });
64
+ const result = await response.text();
65
+ log('function executed', { result });
64
66
 
65
- return await result.text();
67
+ return result;
66
68
  };
67
69
 
68
70
  return this.runAsyncFunction(ast, state, handler(true), { ttl: FUNCTION_TTL });
package/src/defs/index.ts CHANGED
@@ -2,6 +2,6 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ export * from './sheet-range-types';
5
6
  export * from './types';
6
7
  export * from './util';
7
- export * from './sheet-range-types';
@@ -6,7 +6,7 @@ import { type ClassNameValue } from '@dxos/react-ui';
6
6
 
7
7
  import { type SheetType } from '../types';
8
8
 
9
- export const alignKey = 'align';
9
+ export const alignKey = 'alignment';
10
10
  export type AlignKey = typeof alignKey;
11
11
  export type AlignValue = 'start' | 'center' | 'end' | 'unset';
12
12
 
@@ -16,11 +16,12 @@ export type CommentValue = string;
16
16
 
17
17
  export const styleKey = 'style';
18
18
  export type StyleKey = typeof styleKey;
19
- export type StyleValue = 'highlight' | 'unset';
19
+ export type StyleValue = 'highlight' | 'softwrap';
20
20
 
21
+ // TODO(burdon): Reconcile with plugin-table.
21
22
  export const cellClassNameForRange = ({ key, value }: SheetType['ranges'][number]): ClassNameValue => {
22
23
  switch (key) {
23
- case 'align':
24
+ case alignKey:
24
25
  switch (value) {
25
26
  case 'start':
26
27
  return 'text-start';
@@ -31,12 +32,14 @@ export const cellClassNameForRange = ({ key, value }: SheetType['ranges'][number
31
32
  default:
32
33
  return undefined;
33
34
  }
34
- case 'comment':
35
+ case commentKey:
35
36
  return 'bg-gridComment';
36
- case 'style':
37
+ case styleKey:
37
38
  switch (value) {
38
39
  case 'highlight':
39
- return 'bg-gridHighlight';
40
+ return '!bg-gridHighlight';
41
+ case 'softwrap':
42
+ return '!whitespace-normal';
40
43
  default:
41
44
  return undefined;
42
45
  }