@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
@@ -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}
@@ -8,27 +8,30 @@ import React, { type PropsWithChildren, useCallback, useMemo } from 'react';
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,
29
31
  type StyleKey,
30
32
  type StyleValue,
31
33
  } from '../../defs';
34
+ import { completeCellRangeToThreadCursor } from '../../integrations';
32
35
  import { SHEET_PLUGIN } from '../../meta';
33
36
  import { type SheetType } from '../../types';
34
37
  import { useSheetContext } from '../SheetContext';
@@ -108,22 +111,21 @@ const sectionToolbarLayout =
108
111
  type Range = SheetType['ranges'][number];
109
112
 
110
113
  const ToolbarRoot = ({ children, role, classNames }: ToolbarProps) => {
111
- const { id, model, range, cursor } = useSheetContext();
114
+ const { id, model, cursorFallbackRange, cursor } = useSheetContext();
112
115
  const { hasAttention } = useAttention(id);
113
116
  const dispatch = useIntentDispatcher();
114
117
 
115
- // TODO(Zan): Centralise the toolbar action handler. Current implementation in stories.
118
+ // TODO(Zan): Externalize the toolbar action handler. E.g., Toolbar/keys should both fire events.
116
119
  const handleAction = useCallback(
117
120
  (action: ToolbarAction) => {
118
121
  switch (action.key) {
119
- case 'align':
120
- if (cursor) {
122
+ case 'alignment':
123
+ if (cursor && cursorFallbackRange) {
121
124
  const index = model.sheet.ranges?.findIndex(
122
- (range) => range.key === action.key && inRange(range.range, cursor),
125
+ (range) => range.key === action.key && inRange(rangeFromIndex(model.sheet, range.range), cursor),
123
126
  );
124
- const nextRange = range ? { from: range.from, to: range.to ?? range.from } : { from: cursor, to: cursor };
125
127
  const nextRangeEntity = {
126
- range: nextRange as Range['range'],
128
+ range: rangeToIndex(model.sheet, cursorFallbackRange),
127
129
  key: action.key,
128
130
  value: action.value,
129
131
  };
@@ -140,10 +142,9 @@ const ToolbarRoot = ({ children, role, classNames }: ToolbarProps) => {
140
142
  if (index >= 0) {
141
143
  model.sheet.ranges?.splice(index, 1);
142
144
  }
143
- } else if (range || cursor) {
144
- const nextRange = range ? { from: range.from, to: range.to ?? range.from } : { from: cursor, to: cursor };
145
+ } else if (cursorFallbackRange) {
145
146
  model.sheet.ranges?.push({
146
- range: nextRange as Range['range'],
147
+ range: rangeToIndex(model.sheet, cursorFallbackRange),
147
148
  key: action.key,
148
149
  value: action.value,
149
150
  });
@@ -151,18 +152,20 @@ const ToolbarRoot = ({ children, role, classNames }: ToolbarProps) => {
151
152
  break;
152
153
  case 'comment': {
153
154
  // 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
- });
155
+ if (cursorFallbackRange) {
156
+ void dispatch({
157
+ action: 'dxos.org/plugin/thread/action/create',
158
+ data: {
159
+ cursor: completeCellRangeToThreadCursor(cursorFallbackRange),
160
+ name: action.cellContent,
161
+ subject: model.sheet,
162
+ },
163
+ });
164
+ }
162
165
  }
163
166
  }
164
167
  },
165
- [model.sheet, range, cursor, dispatch],
168
+ [model.sheet, cursorFallbackRange, cursor, dispatch],
166
169
  );
167
170
 
168
171
  return (
@@ -207,7 +210,9 @@ const Alignment = () => {
207
210
  const value = useMemo(
208
211
  () =>
209
212
  cursor
210
- ? model.sheet.ranges?.find(({ range, key }) => key === 'alignment' && inRange(range, cursor))?.value
213
+ ? model.sheet.ranges?.find(
214
+ ({ range, key }) => key === alignKey && inRange(rangeFromIndex(model.sheet, range), cursor),
215
+ )?.value
211
216
  : undefined,
212
217
  [cursor, model.sheet.ranges],
213
218
  );
@@ -216,11 +221,14 @@ const Alignment = () => {
216
221
  <NaturalToolbar.ToggleGroup
217
222
  type='single'
218
223
  value={value}
219
- onValueChange={(value: AlignValue) => onAction?.({ key: 'align', value })}
224
+ onValueChange={(value: AlignValue) => onAction?.({ key: alignKey, value })}
220
225
  >
221
226
  {alignmentOptions.map(({ value, icon }) => (
222
227
  <ToolbarItem itemType='toggleGroupItem' key={value} value={value} icon={icon}>
223
- {t(`toolbar ${value} label`)}
228
+ {t('toolbar action label', {
229
+ key: t(`range key ${alignKey} label`),
230
+ value: t(`range value ${value} label`),
231
+ })}
224
232
  </ToolbarItem>
225
233
  ))}
226
234
  </NaturalToolbar.ToggleGroup>
@@ -238,7 +246,7 @@ const Styles = () => {
238
246
  () =>
239
247
  cursor
240
248
  ? model.sheet.ranges
241
- ?.filter(({ range, key }) => key === 'style' && inRange(range, cursor))
249
+ ?.filter(({ range, key }) => key === 'style' && inRange(rangeFromIndex(model.sheet, range), cursor))
242
250
  .reduce((acc, { value }) => {
243
251
  acc.add(value);
244
252
  return acc;
@@ -270,29 +278,24 @@ const Styles = () => {
270
278
 
271
279
  const Actions = () => {
272
280
  const { onAction } = useToolbarContext('Actions');
273
- const { cursor, range, model } = useSheetContext();
281
+ const { cursorFallbackRange, cursor, model } = useSheetContext();
274
282
  const { t } = useTranslation(SHEET_PLUGIN);
275
283
 
276
284
  const overlapsCommentAnchor = (model.sheet.threads ?? [])
277
285
  .filter(nonNullable)
278
286
  .filter((thread) => thread.status !== 'resolved')
279
287
  .some((thread) => {
280
- if (!cursor) {
288
+ if (!cursorFallbackRange) {
281
289
  return false;
282
290
  }
283
- return addressToIndex(model.sheet, cursor) === thread.anchor;
291
+ return rangeToIndex(model.sheet, cursorFallbackRange) === thread.anchor;
284
292
  });
285
293
 
286
- const hasCursor = !!cursor;
287
- const cursorOnly = hasCursor && !range && !overlapsCommentAnchor;
288
-
289
- const tooltipLabelKey = !hasCursor
294
+ const tooltipLabelKey = !cursor
290
295
  ? 'no cursor label'
291
296
  : overlapsCommentAnchor
292
297
  ? 'selection overlaps existing comment label'
293
- : range
294
- ? 'comment ranges not supported label'
295
- : 'comment label';
298
+ : 'comment label';
296
299
 
297
300
  return (
298
301
  <ToolbarItem
@@ -301,16 +304,16 @@ const Actions = () => {
301
304
  icon='ph--chat-text--regular'
302
305
  data-testid='editor.toolbar.comment'
303
306
  onClick={() => {
304
- if (!cursor) {
307
+ if (!(cursorFallbackRange && cursor)) {
305
308
  return;
306
309
  }
307
310
  return onAction?.({
308
311
  key: 'comment',
309
- value: addressToIndex(model.sheet, cursor),
312
+ value: rangeToIndex(model.sheet, cursorFallbackRange),
310
313
  cellContent: model.getCellText(cursor),
311
314
  });
312
315
  }}
313
- disabled={!cursorOnly || overlapsCommentAnchor}
316
+ disabled={!cursor || overlapsCommentAnchor}
314
317
  >
315
318
  {t(tooltipLabelKey)}
316
319
  </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
 
@@ -24,6 +24,8 @@ import {
24
24
  EDGE_FUNCTION_NAME,
25
25
  } from './functions';
26
26
 
27
+ // TODO(burdon): Factor out compute-graph.
28
+
27
29
  // TODO(wittjosiah): Factor out.
28
30
  const OBJECT_ID_LENGTH = 60; // 33 (space id) + 1 (separator) + 26 (object id).
29
31
 
@@ -71,6 +73,7 @@ export class ComputeGraph extends Resource {
71
73
  ) {
72
74
  super();
73
75
  this._hf.updateConfig({ context: this.context });
76
+
74
77
  // TODO(burdon): If debounce then aggregate changes.
75
78
  const onValuesUpdate: Listeners['valuesUpdated'] = (changes) => {
76
79
  for (const change of changes) {
@@ -116,7 +119,7 @@ export class ComputeGraph extends Resource {
116
119
  getOrCreateNode(name: string): ComputeNode {
117
120
  invariant(name.length);
118
121
  if (!this._hf.doesSheetExist(name)) {
119
- log.info('created node', { space: this._space?.id, sheet: name });
122
+ log('created node', { space: this._space?.id, sheet: name });
120
123
  this._hf.addSheet(name);
121
124
  }
122
125
 
@@ -130,7 +133,7 @@ export class ComputeGraph extends Resource {
130
133
 
131
134
  /**
132
135
  * Map bound value to custom function invocation.
133
- * E.g., "HELLO(...args)" => "EDGE("HELLO", ...args)".
136
+ * E.g., "HELLO(...args)" => "DX("HELLO", ...args)".
134
137
  */
135
138
  mapFormulaToNative(formula: string): string {
136
139
  return (
@@ -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
 
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
 
@@ -18,9 +18,10 @@ export const styleKey = 'style';
18
18
  export type StyleKey = typeof styleKey;
19
19
  export type StyleValue = 'highlight' | 'unset';
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,9 +32,9 @@ 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
40
  return 'bg-gridHighlight';
package/src/defs/types.ts CHANGED
@@ -13,11 +13,16 @@ export const MAX_COLUMNS = 26 * 2;
13
13
  export type CellAddress = { col: number; row: number };
14
14
 
15
15
  export type CellRange = { from: CellAddress; to?: CellAddress };
16
+ export type CompleteCellRange = { from: CellAddress; to: CellAddress };
16
17
 
17
18
  export type CellIndex = string;
18
19
 
19
20
  export type CellContentValue = number | string | boolean | null;
20
21
 
22
+ export const RANGE_NOTATION = /^[A-Z]+[0-9]+(:[A-Z]+[0-9]+)?$/;
23
+
24
+ export const isFormula = (value: any): value is string => typeof value === 'string' && value.charAt(0) === '=';
25
+
21
26
  export const posEquals = (a: CellAddress | undefined, b: CellAddress | undefined) => {
22
27
  return a?.col === b?.col && a?.row === b?.row;
23
28
  };
@@ -34,7 +39,7 @@ export const addressToA1Notation = ({ col, row }: CellAddress): string => {
34
39
  return `${columnLetter(col)}${row + 1}`;
35
40
  };
36
41
 
37
- // TODO(burdon): See simpleCellAddressFromString
42
+ // TODO(burdon): See (HF) simpleCellAddressFromString.
38
43
  export const addressFromA1Notation = (ref: string): CellAddress => {
39
44
  const match = ref.match(/([A-Z]+)(\d+)/);
40
45
  invariant(match, `Invalid notation: ${ref}`);
package/src/defs/util.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  addressFromA1Notation,
10
10
  type CellAddress,
11
11
  type CellRange,
12
+ type CompleteCellRange,
12
13
  DEFAULT_COLUMNS,
13
14
  DEFAULT_ROWS,
14
15
  MAX_COLUMNS,
@@ -69,6 +70,7 @@ export const createSheet = ({ name, cells, ...size }: CreateSheetOptions = {}):
69
70
  rowMeta: {},
70
71
  columnMeta: {},
71
72
  ranges: [],
73
+ threads: [],
72
74
  });
73
75
 
74
76
  initialize(sheet, size);
@@ -111,29 +113,11 @@ export const rangeToIndex = (sheet: SheetType, range: CellRange): string => {
111
113
  /**
112
114
  * E.g., "CA2@CB3:CC4@CD5" => "A1:B2".
113
115
  */
114
- export const rangeFromIndex = (sheet: SheetType, idx: string): CellRange => {
116
+ export const rangeFromIndex = (sheet: SheetType, idx: string): CompleteCellRange => {
115
117
  const [from, to] = idx.split(':').map((index) => addressFromIndex(sheet, index));
116
118
  return { from, to };
117
119
  };
118
120
 
119
- /**
120
- * Find closest cell to cursor.
121
- */
122
- export const closest = (cursor: CellAddress, cells: CellAddress[]): CellAddress | undefined => {
123
- let closestCell: CellAddress | undefined;
124
- let closestDistance = Number.MAX_SAFE_INTEGER;
125
-
126
- for (const cell of cells) {
127
- const distance = Math.abs(cell.row - cursor.row) + Math.abs(cell.col - cursor.col);
128
- if (distance < closestDistance) {
129
- closestCell = cell;
130
- closestDistance = distance;
131
- }
132
- }
133
-
134
- return closestCell;
135
- };
136
-
137
121
  /**
138
122
  * Compares the positions of two cell indexes in a sheet.
139
123
  * Sorts primarily by row, then by column if rows are equal.
@@ -23,8 +23,8 @@ import { withTheme, withLayout } from '@dxos/storybook-utils';
23
23
  import { nonNullable } from '@dxos/util';
24
24
 
25
25
  import { compute, computeGraphFacet } from './compute';
26
- import { GridSheet, SheetProvider } from '../components';
27
- import { useComputeGraph, useSheetModel } from '../hooks';
26
+ import { GridSheet, SheetProvider, useComputeGraph } from '../components';
27
+ import { useSheetModel } from '../model';
28
28
  import { useTestSheet, withComputeGraphDecorator } from '../testing';
29
29
  import { SheetType } from '../types';
30
30
 
@@ -16,10 +16,10 @@ import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view
16
16
 
17
17
  import { type UnsubscribeCallback, debounce } from '@dxos/async';
18
18
  import { invariant } from '@dxos/invariant';
19
- import { documentId, singleValueFacet } from '@dxos/react-ui-editor/state';
19
+ import { documentId, singleValueFacet } from '@dxos/react-ui-editor';
20
20
 
21
+ import { type ComputeGraph, type ComputeNode, createSheetName } from '../compute-graph';
21
22
  import { type CellAddress } from '../defs';
22
- import { type ComputeGraph, type ComputeNode, createSheetName } from '../graph';
23
23
  import { type CellScalarValue } from '../types';
24
24
 
25
25
  const LANGUAGE_TAG = 'dx';
@@ -9,7 +9,7 @@ import { spreadsheet } from 'codemirror-lang-spreadsheet';
9
9
  import { describe, expect, test } from 'vitest';
10
10
 
11
11
  import { sheetExtension } from './extension';
12
- import { defaultFunctions } from '../../graph';
12
+ import { defaultFunctions } from '../../compute-graph';
13
13
 
14
14
  describe('formula parser', () => {
15
15
  const {
@@ -18,10 +18,11 @@ import { type SyntaxNode } from '@lezer/common';
18
18
  import { tags } from '@lezer/highlight';
19
19
  import { spreadsheet } from 'codemirror-lang-spreadsheet';
20
20
 
21
- import { singleValueFacet } from '@dxos/react-ui-editor/state';
21
+ import { singleValueFacet } from '@dxos/react-ui-editor';
22
22
  import { mx } from '@dxos/react-ui-theme';
23
23
 
24
- import { type FunctionDefinition } from '../../graph';
24
+ import { type FunctionDefinition } from '../../compute-graph';
25
+ import { RANGE_NOTATION } from '../../defs';
25
26
 
26
27
  /**
27
28
  * https://codemirror.net/examples/styling
@@ -195,34 +196,53 @@ export const sheetExtension = ({ functions = [] }: SheetExtensionOptions): Exten
195
196
  ];
196
197
  };
197
198
 
198
- export type CellRangeNotifier = (range: string) => void;
199
-
200
- type Range = { from: number; to: number };
199
+ export type SelectionRange = { from: number; to: number };
200
+
201
+ export interface RangeController {
202
+ setRange(range: string): void;
203
+ }
204
+
205
+ export type RangeExtensionOptions = {
206
+ /**
207
+ * Provides controller callback when extension is initialized.
208
+ */
209
+ onInit?: (controller: RangeController) => void;
210
+ /**
211
+ * Called when the active range changes.
212
+ * @param state The current state.
213
+ * @param state.activeRange undefined if no range is active, otherwise a possibly partially defined range.
214
+ */
215
+ onStateChange?: (state: { activeRange: string | undefined }) => void;
216
+ };
201
217
 
202
218
  /**
203
219
  * Tracks the currently active cell within a formula and provides a callback to modify it.
204
220
  */
205
- export const rangeExtension = (onInit: (notifier: CellRangeNotifier) => void): Extension => {
221
+ export const rangeExtension = ({ onInit, onStateChange }: RangeExtensionOptions): Extension => {
206
222
  let view: EditorView;
207
- let activeRange: Range | undefined;
208
- const provider: CellRangeNotifier = (range: string) => {
209
- if (activeRange) {
210
- view.dispatch(
211
- view.state.update({
212
- changes: { ...activeRange, insert: range.toString() },
213
- selection: { anchor: activeRange.from + range.length },
214
- }),
215
- );
216
- }
223
+ let activeRange: SelectionRange | undefined;
217
224
 
218
- view.focus();
225
+ // Called externally to provide current range.
226
+ const notifier: RangeController = {
227
+ setRange: (range: string) => {
228
+ if (activeRange) {
229
+ view.dispatch(
230
+ view.state.update({
231
+ changes: { ...activeRange, insert: range.toString() },
232
+ selection: { anchor: activeRange.from + range.length },
233
+ }),
234
+ );
235
+ }
236
+
237
+ view.focus();
238
+ },
219
239
  };
220
240
 
221
241
  return ViewPlugin.fromClass(
222
242
  class {
223
243
  constructor(_view: EditorView) {
224
244
  view = _view;
225
- onInit(provider);
245
+ onInit?.(notifier);
226
246
  }
227
247
 
228
248
  update(view: ViewUpdate) {
@@ -235,17 +255,15 @@ export const rangeExtension = (onInit: (notifier: CellRangeNotifier) => void): E
235
255
  visitTree(topNode, ({ type, from, to }) => {
236
256
  if (from <= anchor && to >= anchor) {
237
257
  switch (type.name) {
238
- case 'Function': {
258
+ case 'Function':
239
259
  // Mark but keep looking.
240
260
  activeRange = { from: to, to };
241
261
  break;
242
- }
243
262
 
244
- case 'CloseParen': {
263
+ case 'CloseParen':
245
264
  // Mark but keep looking.
246
265
  activeRange = { from, to: from };
247
266
  break;
248
- }
249
267
 
250
268
  case 'RangeToken':
251
269
  case 'CellToken':
@@ -259,8 +277,15 @@ export const rangeExtension = (onInit: (notifier: CellRangeNotifier) => void): E
259
277
 
260
278
  // Allow start of formula.
261
279
  if (!activeRange && view.state.doc.toString()[0] === '=') {
262
- activeRange = { from: 1, to: view.state.doc.toString().length };
280
+ const str = view.state.doc.sliceString(1);
281
+ if (RANGE_NOTATION.test(str)) {
282
+ activeRange = { from: 1, to: str.length + 1 };
283
+ }
263
284
  }
285
+
286
+ onStateChange?.({
287
+ activeRange: activeRange ? view.state.doc.sliceString(activeRange.from, activeRange.to) : undefined,
288
+ });
264
289
  }
265
290
  },
266
291
  );
package/src/index.ts CHANGED
@@ -4,6 +4,6 @@
4
4
 
5
5
  import { SheetPlugin } from './SheetPlugin';
6
6
 
7
- export default SheetPlugin;
8
-
9
7
  export * from './SheetPlugin';
8
+
9
+ export default SheetPlugin;
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './thread-ranges';