@dxos/plugin-sheet 0.6.11-staging.e6894a4 → 0.6.12-main.15a606f

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 (261) hide show
  1. package/dist/lib/browser/SheetContainer-V4GCCZTX.mjs +261 -0
  2. package/dist/lib/browser/SheetContainer-V4GCCZTX.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-P7SSL3EG.mjs → chunk-6ZMQVB4Z.mjs} +359 -671
  4. package/dist/lib/browser/chunk-6ZMQVB4Z.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-JRL5LGCE.mjs → chunk-QILRZNE5.mjs} +2 -5
  6. package/dist/lib/browser/chunk-QILRZNE5.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-FUAGSXA4.mjs → chunk-T3NJFTD4.mjs} +8 -15
  8. package/dist/lib/browser/chunk-T3NJFTD4.mjs.map +7 -0
  9. package/dist/lib/browser/{SheetContainer-4XOKHKKZ.mjs → chunk-U2JHW3L6.mjs} +1043 -261
  10. package/dist/lib/browser/chunk-U2JHW3L6.mjs.map +7 -0
  11. package/dist/lib/browser/graph-T27BOBOV.mjs +21 -0
  12. package/dist/lib/browser/graph-T27BOBOV.mjs.map +7 -0
  13. package/dist/lib/browser/index.mjs +80 -66
  14. package/dist/lib/browser/index.mjs.map +3 -3
  15. package/dist/lib/browser/meta.json +1 -1
  16. package/dist/lib/browser/meta.mjs +1 -1
  17. package/dist/lib/browser/types.mjs +4 -6
  18. package/dist/lib/node/SheetContainer-3ZY7MPWJ.cjs +279 -0
  19. package/dist/lib/node/SheetContainer-3ZY7MPWJ.cjs.map +7 -0
  20. package/dist/lib/node/{chunk-BJ6ZD7MN.cjs → chunk-BNARJ5GM.cjs} +5 -18
  21. package/dist/lib/node/chunk-BNARJ5GM.cjs.map +7 -0
  22. package/dist/lib/node/{chunk-SVAIIXWQ.cjs → chunk-DD6FIXWC.cjs} +360 -667
  23. package/dist/lib/node/chunk-DD6FIXWC.cjs.map +7 -0
  24. package/dist/lib/node/{SheetContainer-IQT6TR4Z.cjs → chunk-OTTD7FBK.cjs} +1062 -289
  25. package/dist/lib/node/chunk-OTTD7FBK.cjs.map +7 -0
  26. package/dist/lib/node/{chunk-DSYKOI4E.cjs → chunk-Q3HBHPRL.cjs} +12 -20
  27. package/dist/lib/node/chunk-Q3HBHPRL.cjs.map +7 -0
  28. package/dist/lib/node/graph-SPKGX7W4.cjs +43 -0
  29. package/dist/lib/node/graph-SPKGX7W4.cjs.map +7 -0
  30. package/dist/lib/node/index.cjs +93 -72
  31. package/dist/lib/node/index.cjs.map +3 -3
  32. package/dist/lib/node/meta.cjs +3 -3
  33. package/dist/lib/node/meta.cjs.map +1 -1
  34. package/dist/lib/node/meta.json +1 -1
  35. package/dist/lib/node/types.cjs +10 -12
  36. package/dist/lib/node/types.cjs.map +2 -2
  37. package/dist/lib/node-esm/SheetContainer-PXSJX6XK.mjs +262 -0
  38. package/dist/lib/node-esm/SheetContainer-PXSJX6XK.mjs.map +7 -0
  39. package/dist/lib/node-esm/chunk-7HVSOTGA.mjs +2553 -0
  40. package/dist/lib/node-esm/chunk-7HVSOTGA.mjs.map +7 -0
  41. package/dist/lib/node-esm/chunk-BMNA27EX.mjs +76 -0
  42. package/dist/lib/node-esm/chunk-BMNA27EX.mjs.map +7 -0
  43. package/dist/lib/node-esm/chunk-D6KU5MI7.mjs +2925 -0
  44. package/dist/lib/node-esm/chunk-D6KU5MI7.mjs.map +7 -0
  45. package/dist/lib/node-esm/chunk-IU2L277A.mjs +17 -0
  46. package/dist/lib/node-esm/chunk-IU2L277A.mjs.map +7 -0
  47. package/dist/lib/node-esm/graph-U67IO4UC.mjs +22 -0
  48. package/dist/lib/node-esm/graph-U67IO4UC.mjs.map +7 -0
  49. package/dist/lib/node-esm/index.mjs +261 -0
  50. package/dist/lib/node-esm/index.mjs.map +7 -0
  51. package/dist/lib/node-esm/meta.json +1 -0
  52. package/dist/lib/node-esm/meta.mjs +10 -0
  53. package/dist/lib/node-esm/meta.mjs.map +7 -0
  54. package/dist/lib/node-esm/types.mjs +21 -0
  55. package/dist/lib/node-esm/types.mjs.map +7 -0
  56. package/dist/types/src/SheetPlugin.d.ts.map +1 -1
  57. package/dist/types/src/components/CellEditor/CellEditor.d.ts +23 -3
  58. package/dist/types/src/components/CellEditor/CellEditor.d.ts.map +1 -1
  59. package/dist/types/src/components/CellEditor/CellEditor.stories.d.ts +2 -2
  60. package/dist/types/src/components/CellEditor/CellEditor.stories.d.ts.map +1 -1
  61. package/dist/types/src/components/CellEditor/extension.d.ts +1 -1
  62. package/dist/types/src/components/CellEditor/extension.d.ts.map +1 -1
  63. package/dist/types/src/components/ComputeGraph/ComputeGraphContextProvider.d.ts +11 -0
  64. package/dist/types/src/components/ComputeGraph/ComputeGraphContextProvider.d.ts.map +1 -0
  65. package/dist/types/src/components/ComputeGraph/index.d.ts +1 -3
  66. package/dist/types/src/components/ComputeGraph/index.d.ts.map +1 -1
  67. package/dist/types/src/components/GridSheet/GridSheet.d.ts +10 -0
  68. package/dist/types/src/components/GridSheet/GridSheet.d.ts.map +1 -0
  69. package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts +9 -0
  70. package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts.map +1 -0
  71. package/dist/types/src/components/GridSheet/util.d.ts +6 -0
  72. package/dist/types/src/components/GridSheet/util.d.ts.map +1 -0
  73. package/dist/types/src/components/Sheet/Sheet.d.ts +1 -1
  74. package/dist/types/src/components/Sheet/Sheet.d.ts.map +1 -1
  75. package/dist/types/src/components/Sheet/Sheet.stories.d.ts +5 -6
  76. package/dist/types/src/components/Sheet/Sheet.stories.d.ts.map +1 -1
  77. package/dist/types/src/components/Sheet/decorations.d.ts +24 -0
  78. package/dist/types/src/components/Sheet/decorations.d.ts.map +1 -0
  79. package/dist/types/src/components/Sheet/grid.d.ts +2 -2
  80. package/dist/types/src/components/Sheet/grid.d.ts.map +1 -1
  81. package/dist/types/src/components/Sheet/nav.d.ts +3 -3
  82. package/dist/types/src/components/Sheet/nav.d.ts.map +1 -1
  83. package/dist/types/src/components/Sheet/sheet-context.d.ts +6 -5
  84. package/dist/types/src/components/Sheet/sheet-context.d.ts.map +1 -1
  85. package/dist/types/src/components/Sheet/threads.d.ts +2 -0
  86. package/dist/types/src/components/Sheet/threads.d.ts.map +1 -0
  87. package/dist/types/src/components/SheetContainer.d.ts +2 -3
  88. package/dist/types/src/components/SheetContainer.d.ts.map +1 -1
  89. package/dist/types/src/components/Toolbar/Toolbar.d.ts +19 -3
  90. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  91. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +18 -13
  92. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  93. package/dist/types/src/components/index.d.ts +2 -2
  94. package/dist/types/src/components/index.d.ts.map +1 -1
  95. package/dist/types/src/defs/index.d.ts +3 -0
  96. package/dist/types/src/defs/index.d.ts.map +1 -0
  97. package/dist/types/src/{model → defs}/types.d.ts +8 -3
  98. package/dist/types/src/defs/types.d.ts.map +1 -0
  99. package/dist/types/src/defs/types.test.d.ts.map +1 -0
  100. package/dist/types/src/defs/util.d.ts +43 -0
  101. package/dist/types/src/defs/util.d.ts.map +1 -0
  102. package/dist/types/src/extensions/compute.d.ts +5 -0
  103. package/dist/types/src/extensions/compute.d.ts.map +1 -0
  104. package/dist/types/src/extensions/compute.stories.d.ts +26 -0
  105. package/dist/types/src/extensions/compute.stories.d.ts.map +1 -0
  106. package/dist/types/src/extensions/index.d.ts +2 -0
  107. package/dist/types/src/extensions/index.d.ts.map +1 -0
  108. package/dist/types/src/{components/ComputeGraph → graph}/async-function.d.ts +1 -1
  109. package/dist/types/src/graph/async-function.d.ts.map +1 -0
  110. package/dist/types/src/graph/compute-graph.browser.test.d.ts +2 -0
  111. package/dist/types/src/graph/compute-graph.browser.test.d.ts.map +1 -0
  112. package/dist/types/src/graph/compute-graph.d.ts +81 -0
  113. package/dist/types/src/graph/compute-graph.d.ts.map +1 -0
  114. package/dist/types/src/graph/compute-graph.stories.d.ts +10 -0
  115. package/dist/types/src/graph/compute-graph.stories.d.ts.map +1 -0
  116. package/dist/types/src/graph/compute-node.d.ts +19 -0
  117. package/dist/types/src/graph/compute-node.d.ts.map +1 -0
  118. package/dist/types/src/{components/ComputeGraph/custom.d.ts → graph/custom-function.d.ts} +1 -1
  119. package/dist/types/src/graph/custom-function.d.ts.map +1 -0
  120. package/dist/types/src/graph/edge-function.d.ts.map +1 -0
  121. package/dist/types/src/{model/functions.d.ts → graph/function-defs.d.ts} +1 -1
  122. package/dist/types/src/graph/function-defs.d.ts.map +1 -0
  123. package/dist/types/src/graph/hyperformula.test.d.ts +2 -0
  124. package/dist/types/src/graph/hyperformula.test.d.ts.map +1 -0
  125. package/dist/types/src/graph/index.d.ts +4 -0
  126. package/dist/types/src/graph/index.d.ts.map +1 -0
  127. package/dist/types/src/graph/util.d.ts +2 -0
  128. package/dist/types/src/graph/util.d.ts.map +1 -0
  129. package/dist/types/src/hooks/hooks.stories.d.ts +11 -0
  130. package/dist/types/src/hooks/hooks.stories.d.ts.map +1 -0
  131. package/dist/types/src/hooks/index.d.ts +4 -0
  132. package/dist/types/src/hooks/index.d.ts.map +1 -0
  133. package/dist/types/src/hooks/useComputeGraph.d.ts +7 -0
  134. package/dist/types/src/hooks/useComputeGraph.d.ts.map +1 -0
  135. package/dist/types/src/hooks/useFormattingModel.d.ts +3 -0
  136. package/dist/types/src/hooks/useFormattingModel.d.ts.map +1 -0
  137. package/dist/types/src/hooks/useSheetModel.d.ts +8 -0
  138. package/dist/types/src/hooks/useSheetModel.d.ts.map +1 -0
  139. package/dist/types/src/meta.d.ts +1 -4
  140. package/dist/types/src/meta.d.ts.map +1 -1
  141. package/dist/types/src/model/formatting-model.d.ts +16 -0
  142. package/dist/types/src/model/formatting-model.d.ts.map +1 -0
  143. package/dist/types/src/model/index.d.ts +2 -3
  144. package/dist/types/src/model/index.d.ts.map +1 -1
  145. package/dist/types/src/model/{model.d.ts → sheet-model.d.ts} +9 -64
  146. package/dist/types/src/model/sheet-model.d.ts.map +1 -0
  147. package/dist/types/src/sanity.test.d.ts +2 -0
  148. package/dist/types/src/sanity.test.d.ts.map +1 -0
  149. package/dist/types/src/testing/index.d.ts +2 -0
  150. package/dist/types/src/testing/index.d.ts.map +1 -0
  151. package/dist/types/src/testing/testing.d.ts +9 -0
  152. package/dist/types/src/testing/testing.d.ts.map +1 -0
  153. package/dist/types/src/translations.d.ts +17 -12
  154. package/dist/types/src/translations.d.ts.map +1 -1
  155. package/dist/types/src/types.d.ts +83 -3
  156. package/dist/types/src/types.d.ts.map +1 -1
  157. package/dist/vendor/hyperformula.mjs +37145 -0
  158. package/package.json +48 -41
  159. package/src/SheetPlugin.tsx +51 -72
  160. package/src/components/CellEditor/CellEditor.stories.tsx +4 -3
  161. package/src/components/CellEditor/CellEditor.tsx +59 -9
  162. package/src/components/CellEditor/extension.test.ts +4 -5
  163. package/src/components/CellEditor/extension.ts +1 -3
  164. package/src/components/ComputeGraph/ComputeGraphContextProvider.tsx +20 -0
  165. package/src/components/ComputeGraph/index.ts +1 -3
  166. package/src/components/GridSheet/GridSheet.stories.tsx +35 -0
  167. package/src/components/GridSheet/GridSheet.tsx +153 -0
  168. package/src/components/GridSheet/util.ts +89 -0
  169. package/src/components/Sheet/Sheet.stories.tsx +48 -87
  170. package/src/components/Sheet/Sheet.tsx +69 -39
  171. package/src/components/Sheet/decorations.ts +62 -0
  172. package/src/components/Sheet/grid.ts +3 -3
  173. package/src/components/Sheet/nav.ts +19 -19
  174. package/src/components/Sheet/sheet-context.tsx +16 -78
  175. package/src/components/Sheet/threads.tsx +205 -0
  176. package/src/components/SheetContainer.tsx +73 -21
  177. package/src/components/Toolbar/Toolbar.stories.tsx +5 -10
  178. package/src/components/Toolbar/Toolbar.tsx +53 -12
  179. package/src/components/index.ts +1 -0
  180. package/src/defs/index.ts +6 -0
  181. package/src/{model → defs}/types.test.ts +8 -9
  182. package/src/{model → defs}/types.ts +23 -14
  183. package/src/defs/util.ts +135 -0
  184. package/src/extensions/compute.stories.tsx +151 -0
  185. package/src/extensions/compute.ts +98 -0
  186. package/src/extensions/index.ts +5 -0
  187. package/src/{components/ComputeGraph → graph}/async-function.ts +3 -1
  188. package/src/graph/compute-graph.browser.test.ts +104 -0
  189. package/src/graph/compute-graph.stories.tsx +92 -0
  190. package/src/graph/compute-graph.ts +290 -0
  191. package/src/graph/compute-node.ts +51 -0
  192. package/src/{components/ComputeGraph/custom.ts → graph/custom-function.ts} +2 -6
  193. package/src/{components/ComputeGraph → graph}/edge-function.ts +2 -1
  194. package/src/graph/hyperformula.test.ts +15 -0
  195. package/src/graph/index.ts +7 -0
  196. package/src/graph/util.ts +8 -0
  197. package/src/hooks/hooks.stories.tsx +50 -0
  198. package/src/hooks/index.ts +7 -0
  199. package/src/hooks/useComputeGraph.ts +20 -0
  200. package/src/hooks/useFormattingModel.ts +11 -0
  201. package/src/hooks/useSheetModel.ts +43 -0
  202. package/src/meta.tsx +1 -5
  203. package/src/{components/Sheet/formatting.ts → model/formatting-model.ts} +20 -13
  204. package/src/model/index.ts +2 -3
  205. package/src/model/sheet-model.ts +399 -0
  206. package/src/sanity.test.ts +40 -0
  207. package/src/testing/index.ts +5 -0
  208. package/src/testing/testing.tsx +66 -0
  209. package/src/translations.ts +6 -1
  210. package/src/types.ts +30 -5
  211. package/dist/lib/browser/SheetContainer-4XOKHKKZ.mjs.map +0 -7
  212. package/dist/lib/browser/chunk-FUAGSXA4.mjs.map +0 -7
  213. package/dist/lib/browser/chunk-FWGRE3EG.mjs +0 -175
  214. package/dist/lib/browser/chunk-FWGRE3EG.mjs.map +0 -7
  215. package/dist/lib/browser/chunk-JRL5LGCE.mjs.map +0 -7
  216. package/dist/lib/browser/chunk-P7SSL3EG.mjs.map +0 -7
  217. package/dist/lib/browser/chunk-YPU3R7FA.mjs +0 -8
  218. package/dist/lib/browser/chunk-YPU3R7FA.mjs.map +0 -7
  219. package/dist/lib/browser/testing.mjs +0 -92
  220. package/dist/lib/browser/testing.mjs.map +0 -7
  221. package/dist/lib/node/SheetContainer-IQT6TR4Z.cjs.map +0 -7
  222. package/dist/lib/node/chunk-5EPCDAZC.cjs +0 -40
  223. package/dist/lib/node/chunk-5EPCDAZC.cjs.map +0 -7
  224. package/dist/lib/node/chunk-727C6YNP.cjs +0 -202
  225. package/dist/lib/node/chunk-727C6YNP.cjs.map +0 -7
  226. package/dist/lib/node/chunk-BJ6ZD7MN.cjs.map +0 -7
  227. package/dist/lib/node/chunk-DSYKOI4E.cjs.map +0 -7
  228. package/dist/lib/node/chunk-SVAIIXWQ.cjs.map +0 -7
  229. package/dist/lib/node/testing.cjs +0 -111
  230. package/dist/lib/node/testing.cjs.map +0 -7
  231. package/dist/types/src/components/ComputeGraph/async-function.d.ts.map +0 -1
  232. package/dist/types/src/components/ComputeGraph/custom.d.ts.map +0 -1
  233. package/dist/types/src/components/ComputeGraph/edge-function.d.ts.map +0 -1
  234. package/dist/types/src/components/ComputeGraph/graph-context.d.ts +0 -12
  235. package/dist/types/src/components/ComputeGraph/graph-context.d.ts.map +0 -1
  236. package/dist/types/src/components/ComputeGraph/graph.browser.test.d.ts +0 -2
  237. package/dist/types/src/components/ComputeGraph/graph.browser.test.d.ts.map +0 -1
  238. package/dist/types/src/components/ComputeGraph/graph.d.ts +0 -26
  239. package/dist/types/src/components/ComputeGraph/graph.d.ts.map +0 -1
  240. package/dist/types/src/components/Sheet/formatting.d.ts +0 -14
  241. package/dist/types/src/components/Sheet/formatting.d.ts.map +0 -1
  242. package/dist/types/src/model/functions.d.ts.map +0 -1
  243. package/dist/types/src/model/model.browser.test.d.ts +0 -2
  244. package/dist/types/src/model/model.browser.test.d.ts.map +0 -1
  245. package/dist/types/src/model/model.d.ts.map +0 -1
  246. package/dist/types/src/model/types.d.ts.map +0 -1
  247. package/dist/types/src/model/types.test.d.ts.map +0 -1
  248. package/dist/types/src/model/util.d.ts +0 -15
  249. package/dist/types/src/model/util.d.ts.map +0 -1
  250. package/dist/types/src/testing.d.ts +0 -9
  251. package/dist/types/src/testing.d.ts.map +0 -1
  252. package/src/components/ComputeGraph/graph-context.tsx +0 -50
  253. package/src/components/ComputeGraph/graph.browser.test.ts +0 -50
  254. package/src/components/ComputeGraph/graph.ts +0 -62
  255. package/src/model/model.browser.test.ts +0 -100
  256. package/src/model/model.ts +0 -551
  257. package/src/model/util.ts +0 -36
  258. package/src/testing.ts +0 -50
  259. /package/dist/types/src/{model → defs}/types.test.d.ts +0 -0
  260. /package/dist/types/src/{components/ComputeGraph → graph}/edge-function.d.ts +0 -0
  261. /package/src/{model/functions.ts → graph/function-defs.ts} +0 -0
@@ -40,7 +40,7 @@ import { debounce } from '@dxos/async';
40
40
  import { fullyQualifiedId, createDocAccessor } from '@dxos/client/echo';
41
41
  import { log } from '@dxos/log';
42
42
  import { type ThemedClassName } from '@dxos/react-ui';
43
- import { createAttendableAttributes } from '@dxos/react-ui-attention';
43
+ import { createAttendableAttributes, useHasAttention } from '@dxos/react-ui-attention';
44
44
  import { mx } from '@dxos/react-ui-theme';
45
45
 
46
46
  import {
@@ -60,6 +60,7 @@ import {
60
60
  } from './grid';
61
61
  import { type GridSize, handleArrowNav, handleNav, useRangeSelect } from './nav';
62
62
  import { type SheetContextProps, SheetContextProvider, useSheetContext } from './sheet-context';
63
+ import { useThreads } from './threads';
63
64
  import { getRectUnion, getRelativeClientRect, scrollIntoView } from './util';
64
65
  import {
65
66
  type CellIndex,
@@ -68,7 +69,9 @@ import {
68
69
  columnLetter,
69
70
  posEquals,
70
71
  rangeToA1Notation,
71
- } from '../../model';
72
+ addressToIndex,
73
+ addressFromIndex,
74
+ } from '../../defs';
72
75
  import {
73
76
  CellEditor,
74
77
  type CellRangeNotifier,
@@ -112,13 +115,11 @@ import {
112
115
  * - Update formula ranges by selection.
113
116
  */
114
117
 
115
- // TODO(burdon): Factor out fragments.
116
118
  const fragments = {
117
119
  axis: 'bg-axisSurface text-axisText text-xs select-none',
118
120
  axisSelected: 'bg-attention text-baseText',
119
121
  cell: 'bg-gridCell',
120
122
  cellSelected: 'bg-gridCellSelected text-baseText border !border-accentSurface',
121
- border: 'border-gridLine',
122
123
  };
123
124
 
124
125
  //
@@ -137,12 +138,16 @@ const SheetRoot = ({ children, ...props }: PropsWithChildren<SheetContextProps>)
137
138
 
138
139
  type SheetMainProps = ThemedClassName<Partial<GridSize>>;
139
140
 
140
- const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numRows, numColumns }, forwardRef) => {
141
+ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numRows, numCols }, forwardRef) => {
141
142
  const { model, cursor, setCursor, setRange, setEditing } = useSheetContext();
142
143
 
143
144
  // Scrolling.
144
145
  const { rowsRef, columnsRef, contentRef } = useScrollHandlers();
145
146
 
147
+ // Threads.
148
+ // TODO(Zan): Move this to an extension once we have an extension model.
149
+ useThreads();
150
+
146
151
  //
147
152
  // Order of Row/columns.
148
153
  //
@@ -172,21 +177,21 @@ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numR
172
177
  }, [rows, columns]);
173
178
 
174
179
  const handleMoveRows: SheetRowsProps['onMove'] = (from, to, num = 1) => {
175
- const cursorIdx = cursor ? model.addressToIndex(cursor) : undefined;
180
+ const cursorIdx = cursor ? addressToIndex(model.sheet, cursor) : undefined;
176
181
  const [rows] = model.sheet.rows.splice(from, num);
177
182
  model.sheet.rows.splice(to, 0, rows);
178
183
  if (cursorIdx) {
179
- setCursor(model.addressFromIndex(cursorIdx));
184
+ setCursor(addressFromIndex(model.sheet, cursorIdx));
180
185
  }
181
186
  setRows([...model.sheet.rows]);
182
187
  };
183
188
 
184
189
  const handleMoveColumns: SheetColumnsProps['onMove'] = (from, to, num = 1) => {
185
- const cursorIdx = cursor ? model.addressToIndex(cursor) : undefined;
190
+ const cursorIdx = cursor ? addressToIndex(model.sheet, cursor) : undefined;
186
191
  const columns = model.sheet.columns.splice(from, num);
187
192
  model.sheet.columns.splice(to, 0, ...columns);
188
193
  if (cursorIdx) {
189
- setCursor(model.addressFromIndex(cursorIdx));
194
+ setCursor(addressFromIndex(model.sheet, cursorIdx));
190
195
  }
191
196
  setColumns([...model.sheet.columns]);
192
197
  };
@@ -244,7 +249,6 @@ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numR
244
249
  role='none'
245
250
  className={mx(
246
251
  'grid grid-cols-[calc(var(--rail-size)-2px)_1fr] grid-rows-[32px_1fr_32px] bs-full is-full overflow-hidden',
247
- fragments.border,
248
252
  classNames,
249
253
  )}
250
254
  >
@@ -259,8 +263,8 @@ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numR
259
263
  ref={columnsRef}
260
264
  columns={columns}
261
265
  sizes={columnSizes}
262
- selected={cursor?.column}
263
- onSelect={(column) => setCursor(cursor?.column === column ? undefined : { row: -1, column })}
266
+ selected={cursor?.col}
267
+ onSelect={(col) => setCursor(cursor?.col === col ? undefined : { row: -1, col })}
264
268
  onResize={handleResizeColumn}
265
269
  onMove={handleMoveColumns}
266
270
  />
@@ -270,13 +274,13 @@ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numR
270
274
  rows={rows}
271
275
  sizes={rowSizes}
272
276
  selected={cursor?.row}
273
- onSelect={(row) => setCursor(cursor?.row === row ? undefined : { row, column: -1 })}
277
+ onSelect={(row) => setCursor(cursor?.row === row ? undefined : { row, col: -1 })}
274
278
  onResize={handleResizeRow}
275
279
  onMove={handleMoveRows}
276
280
  />
277
281
  <SheetGrid
278
282
  ref={contentRef}
279
- size={{ numRows: numRows ?? rows.length, numColumns: numColumns ?? columns.length }}
283
+ size={{ numRows: numRows ?? rows.length, numCols: numCols ?? columns.length }}
280
284
  rows={rows}
281
285
  columns={columns}
282
286
  rowSizes={rowSizes}
@@ -425,7 +429,7 @@ const SheetRows = forwardRef<HTMLDivElement, SheetRowsProps>(
425
429
  <div className='relative flex grow overflow-hidden'>
426
430
  {/* Fixed border. */}
427
431
  <div
428
- className={mx('z-20 absolute inset-0 border-y pointer-events-none', fragments.border)}
432
+ className={mx('z-20 absolute inset-0 border-y border-gridLine pointer-events-none')}
429
433
  style={{ width: axisWidth }}
430
434
  />
431
435
 
@@ -520,8 +524,7 @@ const GridRowCell = ({ idx, index, label, size, resize, selected, onSelect, onRe
520
524
  {...listeners}
521
525
  className={mx(
522
526
  'flex h-full items-center justify-center cursor-pointer',
523
- 'border-t focus-visible:outline-none',
524
- fragments.border,
527
+ 'border-t border-gridLine focus-visible:outline-none',
525
528
  fragments.axis,
526
529
  selected && fragments.axisSelected,
527
530
  isDragging && fragments.axisSelected,
@@ -588,7 +591,7 @@ const SheetColumns = forwardRef<HTMLDivElement, SheetColumnsProps>(
588
591
  <div className='relative flex grow overflow-hidden'>
589
592
  {/* Fixed border. */}
590
593
  <div
591
- className={mx('z-20 absolute inset-0 border-x pointer-events-none', fragments.border)}
594
+ className={mx('z-20 absolute inset-0 border-x border-gridLine pointer-events-none')}
592
595
  style={{ height: axisHeight }}
593
596
  />
594
597
 
@@ -684,8 +687,7 @@ const GridColumnCell = ({ idx, index, label, size, resize, selected, onSelect, o
684
687
  {...listeners}
685
688
  className={mx(
686
689
  'flex h-full items-center justify-center cursor-pointer',
687
- 'border-l focus-visible:outline-none',
688
- fragments.border,
690
+ 'border-l border-gridLine focus-visible:outline-none',
689
691
  fragments.axis,
690
692
  selected && fragments.axisSelected,
691
693
  isDragging && fragments.axisSelected,
@@ -860,18 +862,17 @@ const SheetGrid = forwardRef<HTMLDivElement, SheetGridProps>(
860
862
  });
861
863
 
862
864
  // TODO(burdon): Prevent scroll if not attended.
863
- const qualifiedSubjectId = fullyQualifiedId(model.sheet);
864
- const attendableAttrs = createAttendableAttributes(qualifiedSubjectId);
865
- // const attended = useHasAttention(qualifiedSubjectId);
866
- const attended = true;
865
+ const id = fullyQualifiedId(model.sheet);
866
+ const attendableAttrs = createAttendableAttributes(id);
867
+ const hasAttention = useHasAttention(id);
867
868
 
868
869
  return (
869
870
  <div ref={containerRef} role='grid' className='relative flex grow overflow-hidden'>
870
871
  {/* Fixed border. */}
871
- <div className={mx('z-20 absolute inset-0 border pointer-events-none', fragments.border)} />
872
+ <div className={mx('z-20 absolute inset-0 border border-gridLine pointer-events-none')} />
872
873
 
873
874
  {/* Grid scroll container. */}
874
- <div ref={scrollerRef} className={mx('grow', attended && 'overflow-auto scrollbar-thin')}>
875
+ <div ref={scrollerRef} className={mx('grow', hasAttention && 'overflow-auto scrollbar-thin')}>
875
876
  {/* Scroll content. */}
876
877
  <div
877
878
  className='relative select-none'
@@ -884,11 +885,11 @@ const SheetGrid = forwardRef<HTMLDivElement, SheetGridProps>(
884
885
 
885
886
  {/* Grid cells. */}
886
887
  {rowRange.map(({ row, top, height }) => {
887
- return columnRange.map(({ column, left, width }) => {
888
+ return columnRange.map(({ col, left, width }) => {
888
889
  const style: CSSProperties = { position: 'absolute', top, left, width, height };
889
- const cell = { row, column };
890
+ const cell: CellAddress = { row, col };
890
891
  const id = addressToA1Notation(cell);
891
- const idx = model.addressToIndex(cell);
892
+ const idx = addressToIndex(model.sheet, cell);
892
893
  const active = posEquals(cursor, cell);
893
894
  if (active && editing) {
894
895
  const value = initialText.current ?? model.getCellText(cell) ?? '';
@@ -1009,19 +1010,47 @@ type SheetCellProps = {
1009
1010
  };
1010
1011
 
1011
1012
  const SheetCell = ({ id, cell, style, active, onSelect }: SheetCellProps) => {
1012
- const { formatting, editing, setRange } = useSheetContext();
1013
+ const {
1014
+ formatting,
1015
+ editing,
1016
+ setRange,
1017
+ decorations,
1018
+ model: { sheet },
1019
+ } = useSheetContext();
1013
1020
  const { value, classNames } = formatting.getFormatting(cell);
1014
1021
 
1022
+ const decorationsForCell = decorations.getDecorationsForCell(addressToIndex(sheet, cell)) ?? [];
1023
+ const decorationAddedClasses = useMemo(
1024
+ () => decorationsForCell.flatMap((d) => d.classNames ?? []),
1025
+ [decorationsForCell],
1026
+ );
1027
+ const decoratedContent = decorationsForCell.reduce(
1028
+ (children, { decorate }) => {
1029
+ if (!decorate) {
1030
+ return children;
1031
+ }
1032
+ const DecoratorComponent = decorate;
1033
+ return <DecoratorComponent>{children}</DecoratorComponent>;
1034
+ },
1035
+ <div
1036
+ role='none'
1037
+ className={mx(
1038
+ 'flex flex-grow bs-full is-full px-2 items-center truncate cursor-pointer',
1039
+ ...decorationAddedClasses,
1040
+ )}
1041
+ >
1042
+ {value}
1043
+ </div>,
1044
+ );
1045
+
1015
1046
  return (
1016
1047
  <div
1017
1048
  {...{ [`data-${CELL_DATA_KEY}`]: id }}
1018
1049
  role='cell'
1019
1050
  style={style}
1020
1051
  className={mx(
1021
- 'flex w-full h-full truncate items-center border cursor-pointer',
1022
- 'px-2 py-1',
1052
+ 'border border-gridLine cursor-pointer',
1023
1053
  fragments.cell,
1024
- fragments.border,
1025
1054
  active && ['z-20', fragments.cellSelected],
1026
1055
  classNames,
1027
1056
  )}
@@ -1034,7 +1063,7 @@ const SheetCell = ({ id, cell, style, active, onSelect }: SheetCellProps) => {
1034
1063
  }}
1035
1064
  onDoubleClick={() => onSelect?.(cell, true)}
1036
1065
  >
1037
- {value}
1066
+ {decoratedContent}
1038
1067
  </div>
1039
1068
  );
1040
1069
  };
@@ -1053,10 +1082,11 @@ const GridCellEditor = ({ style, value, onNav, onClose }: GridCellEditorProps) =
1053
1082
  notifier.current?.(rangeToA1Notation(range));
1054
1083
  }
1055
1084
  }, [range]);
1085
+
1056
1086
  const extension = useMemo(
1057
1087
  () => [
1058
1088
  editorKeys({ onNav, onClose }),
1059
- sheetExtension({ functions: model.functions }),
1089
+ sheetExtension({ functions: model.graph.getFunctions() }),
1060
1090
  rangeExtension((fn) => (notifier.current = fn)),
1061
1091
  ],
1062
1092
  [model],
@@ -1080,12 +1110,13 @@ const GridCellEditor = ({ style, value, onNav, onClose }: GridCellEditorProps) =
1080
1110
 
1081
1111
  const SheetStatusBar = () => {
1082
1112
  const { model, cursor, range } = useSheetContext();
1113
+
1083
1114
  let value;
1084
1115
  let isFormula = false;
1085
1116
  if (cursor) {
1086
1117
  value = model.getCellValue(cursor);
1087
1118
  if (typeof value === 'string' && value.charAt(0) === '=') {
1088
- value = model.mapFormulaBindingFromId(model.mapFormulaIndicesToRefs(value));
1119
+ value = model.graph.mapFunctionBindingFromId(model.mapFormulaIndicesToRefs(value));
1089
1120
  isFormula = true;
1090
1121
  } else if (value != null) {
1091
1122
  value = String(value);
@@ -1093,7 +1124,7 @@ const SheetStatusBar = () => {
1093
1124
  }
1094
1125
 
1095
1126
  return (
1096
- <div className={mx('flex shrink-0 justify-between items-center px-4 py-1 text-sm border-x', fragments.border)}>
1127
+ <div className={mx('flex shrink-0 justify-between items-center px-4 py-1 text-sm border-x border-gridLine')}>
1097
1128
  <div className='flex gap-4 items-center'>
1098
1129
  <div className='flex w-16 items-center font-mono'>
1099
1130
  {(range && rangeToA1Notation(range)) || (cursor && addressToA1Notation(cursor))}
@@ -1129,8 +1160,7 @@ const SheetDebug = () => {
1129
1160
  <div
1130
1161
  className={mx(
1131
1162
  'z-20 absolute right-0 top-20 bottom-20 w-[30rem] overflow-auto scrollbar-thin',
1132
- 'border text-xs bg-neutral-50 dark:bg-black text-cyan-500 font-mono p-1 opacity-80',
1133
- fragments.border,
1163
+ 'border border-gridLine text-xs bg-neutral-50 dark:bg-black text-cyan-500 font-mono p-1 opacity-80',
1134
1164
  )}
1135
1165
  >
1136
1166
  <pre className='whitespace-pre-wrap'>
@@ -0,0 +1,62 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ import { create } from '@dxos/echo-schema';
6
+
7
+ export type Decoration = {
8
+ type: string;
9
+ /**
10
+ * A wrapping render function to encapsulate cell content. This function is applied between
11
+ * the cell's border and its padding/layout/content, allowing for custom rendering or
12
+ * additional elements to be inserted around the cell's main content.
13
+ */
14
+ decorate?: (props: { children: React.ReactNode }) => React.ReactNode;
15
+ /**
16
+ * An array of CSS class names to be applied to the content of the SheetCell.
17
+ * These classes can be used to style the cell's content independently of its structure.
18
+ */
19
+ classNames?: string[];
20
+ cellIndex: string;
21
+ };
22
+
23
+ export const createDecorations = () => {
24
+ // Reactive object to hold decorations
25
+ // TODO(Zan): Use CELL ID's to key the decoration map.
26
+ // TODO(Zan): Consider maintaining an index of decorations by type.
27
+ const { decorations } = create<{ decorations: Record<string, Decoration[]> }>({ decorations: {} });
28
+
29
+ const addDecoration = (cellIndex: string, decorator: Decoration) => {
30
+ decorations[cellIndex] = [...(decorations[cellIndex] || []), decorator];
31
+ };
32
+
33
+ const removeDecoration = (cellIndex: string, type?: string) => {
34
+ if (type) {
35
+ decorations[cellIndex] = (decorations[cellIndex] || []).filter((d) => d.type !== type);
36
+ } else {
37
+ delete decorations[cellIndex];
38
+ }
39
+ };
40
+
41
+ // TODO(Zan): I should check if returning the a value from a map in a deep signal is a reactive slice.
42
+ const getDecorationsForCell = (cellIndex: string): Decoration[] | undefined => {
43
+ return decorations[cellIndex];
44
+ };
45
+
46
+ const getAllDecorations = (): Decoration[] => {
47
+ const result: Decoration[] = [];
48
+ for (const decoratorArray of Object.values(decorations)) {
49
+ for (const decorator of decoratorArray) {
50
+ result.push(decorator);
51
+ }
52
+ }
53
+ return result;
54
+ };
55
+
56
+ return {
57
+ addDecoration,
58
+ removeDecoration,
59
+ getDecorationsForCell,
60
+ getAllDecorations,
61
+ } as const;
62
+ };
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { type MouseEvent, useEffect, useState } from 'react';
6
6
 
7
- import { type CellAddress, type CellIndex, addressFromA1Notation, addressToA1Notation } from '../../model';
7
+ import { type CellAddress, type CellIndex, addressFromA1Notation, addressToA1Notation } from '../../defs';
8
8
 
9
9
  // export type Bounds = Pick<DOMRect, 'left' | 'top' | 'width' | 'height'>;
10
10
  // export type Dimension = Pick<DOMRect, 'width' | 'height'>;
@@ -12,7 +12,7 @@ import { type CellAddress, type CellIndex, addressFromA1Notation, addressToA1Not
12
12
  export type SizeMap = Record<string, number>;
13
13
 
14
14
  export type RowPosition = { row: number } & Pick<DOMRect, 'top' | 'height'>;
15
- export type ColumnPosition = { column: number } & Pick<DOMRect, 'left' | 'width'>;
15
+ export type ColumnPosition = { col: number } & Pick<DOMRect, 'left' | 'width'>;
16
16
 
17
17
  export const axisWidth = 'calc(var(--rail-size)-2px)';
18
18
  export const axisHeight = 34;
@@ -88,7 +88,7 @@ export const useGridLayout = ({
88
88
  const width = columnSizes?.[idx] ?? defaultWidth;
89
89
  const left = x;
90
90
  x += width - 1;
91
- return { column: i, left, width };
91
+ return { col: i, left, width };
92
92
  }),
93
93
  );
94
94
  }, [columns, columnSizes]);
@@ -5,11 +5,11 @@
5
5
  import { type KeyboardEvent, type MouseEventHandler, useState } from 'react';
6
6
 
7
7
  import { getCellAtPointer } from './grid';
8
- import { type CellAddress, type CellRange, posEquals } from '../../model';
8
+ import { type CellAddress, type CellRange, posEquals } from '../../defs';
9
9
 
10
10
  export type GridSize = {
11
11
  numRows: number;
12
- numColumns: number;
12
+ numCols: number;
13
13
  };
14
14
 
15
15
  /**
@@ -38,14 +38,14 @@ export const handleNav = (
38
38
  break;
39
39
  }
40
40
  case 'ArrowLeft': {
41
- if (opposite.column > 0) {
42
- opposite.column -= 1;
41
+ if (opposite.col > 0) {
42
+ opposite.col -= 1;
43
43
  }
44
44
  break;
45
45
  }
46
46
  case 'ArrowRight': {
47
- if (opposite.column < size.numColumns - 1) {
48
- opposite.column += 1;
47
+ if (opposite.col < size.numCols - 1) {
48
+ opposite.col += 1;
49
49
  }
50
50
  break;
51
51
  }
@@ -64,41 +64,41 @@ export const handleNav = (
64
64
  export const handleArrowNav = (
65
65
  ev: Pick<KeyboardEvent<HTMLInputElement>, 'key' | 'metaKey'>,
66
66
  cursor: CellAddress | undefined,
67
- { numRows, numColumns }: GridSize,
67
+ { numRows, numCols }: GridSize,
68
68
  ): CellAddress | undefined => {
69
69
  switch (ev.key) {
70
70
  case 'ArrowUp':
71
71
  if (cursor === undefined) {
72
- return { row: 0, column: 0 };
72
+ return { row: 0, col: 0 };
73
73
  } else if (cursor.row > 0) {
74
- return { row: ev.metaKey ? 0 : cursor.row - 1, column: cursor.column };
74
+ return { row: ev.metaKey ? 0 : cursor.row - 1, col: cursor.col };
75
75
  }
76
76
  break;
77
77
  case 'ArrowDown':
78
78
  if (cursor === undefined) {
79
- return { row: 0, column: 0 };
79
+ return { row: 0, col: 0 };
80
80
  } else if (cursor.row < numRows - 1) {
81
- return { row: ev.metaKey ? numRows - 1 : cursor.row + 1, column: cursor.column };
81
+ return { row: ev.metaKey ? numRows - 1 : cursor.row + 1, col: cursor.col };
82
82
  }
83
83
  break;
84
84
  case 'ArrowLeft':
85
85
  if (cursor === undefined) {
86
- return { row: 0, column: 0 };
87
- } else if (cursor.column > 0) {
88
- return { row: cursor.row, column: ev.metaKey ? 0 : cursor.column - 1 };
86
+ return { row: 0, col: 0 };
87
+ } else if (cursor.col > 0) {
88
+ return { row: cursor.row, col: ev.metaKey ? 0 : cursor.col - 1 };
89
89
  }
90
90
  break;
91
91
  case 'ArrowRight':
92
92
  if (cursor === undefined) {
93
- return { row: 0, column: 0 };
94
- } else if (cursor.column < numColumns - 1) {
95
- return { row: cursor.row, column: ev.metaKey ? numColumns - 1 : cursor.column + 1 };
93
+ return { row: 0, col: 0 };
94
+ } else if (cursor.col < numCols - 1) {
95
+ return { row: cursor.row, col: ev.metaKey ? numCols - 1 : cursor.col + 1 };
96
96
  }
97
97
  break;
98
98
  case 'Home':
99
- return { row: 0, column: 0 };
99
+ return { row: 0, col: 0 };
100
100
  case 'End':
101
- return { row: numRows - 1, column: numColumns - 1 };
101
+ return { row: numRows - 1, col: numCols - 1 };
102
102
  }
103
103
  };
104
104
 
@@ -2,21 +2,16 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { type PropsWithChildren, createContext, useContext, useState, useEffect } from 'react';
5
+ import React, { type PropsWithChildren, createContext, useContext, useMemo, useState } from 'react';
6
6
 
7
7
  import { invariant } from '@dxos/invariant';
8
- import { type FunctionType } from '@dxos/plugin-script';
9
- import { fullyQualifiedId, type Space } from '@dxos/react-client/echo';
8
+ import { type Space } from '@dxos/react-client/echo';
10
9
 
11
- import { FormattingModel } from './formatting';
12
- import { type CellAddress, type CellRange, defaultFunctions, SheetModel } from '../../model';
10
+ import { createDecorations } from './decorations';
11
+ import { type CellAddress, type CellRange } from '../../defs';
12
+ import { useSheetModel, useFormattingModel } from '../../hooks';
13
+ import { type FormattingModel, type SheetModel } from '../../model';
13
14
  import { type SheetType } from '../../types';
14
- import { type FunctionContextOptions } from '../ComputeGraph';
15
- // TODO(wittjosiah): Refactor. This is not exported from ./components due to depending on ECHO.
16
- import { useComputeGraph } from '../ComputeGraph/graph-context';
17
-
18
- // TODO(wittjosiah): Factor out.
19
- const OBJECT_ID_LENGTH = 60; // 33 (space id) + 26 (object id) + 1 (separator).
20
15
 
21
16
  export type SheetContextType = {
22
17
  model: SheetModel;
@@ -36,6 +31,9 @@ export type SheetContextType = {
36
31
  // Events.
37
32
  // TODO(burdon): Generalize.
38
33
  onInfo?: () => void;
34
+
35
+ // Decorations.
36
+ decorations: ReturnType<typeof createDecorations>;
39
37
  };
40
38
 
41
39
  const SheetContext = createContext<SheetContextType | null>(null);
@@ -50,49 +48,7 @@ export type SheetContextProps = {
50
48
  sheet: SheetType;
51
49
  space: Space;
52
50
  readonly?: boolean;
53
- } & Pick<SheetContextType, 'onInfo'> &
54
- Partial<FunctionContextOptions>;
55
-
56
- /**
57
- * Map from binding to fully qualified ECHO ID.
58
- */
59
- const mapFormulaBindingToId =
60
- (functions: FunctionType[]) =>
61
- (formula: string): string => {
62
- return formula.replace(/([a-zA-Z0-9]+)\((.*)\)/g, (match, binding, args) => {
63
- if (defaultFunctions.find((fn) => fn.name === binding) || binding === 'EDGE') {
64
- return match;
65
- }
66
-
67
- const fn = functions.find((fn) => fn.binding === binding);
68
- if (fn) {
69
- return `${fullyQualifiedId(fn)}(${args})`;
70
- } else {
71
- return match;
72
- }
73
- });
74
- };
75
-
76
- /**
77
- * Map from fully qualified ECHO ID to binding.
78
- */
79
- const mapFormulaBindingFromId =
80
- (functions: FunctionType[]) =>
81
- (formula: string): string => {
82
- return formula.replace(/([a-zA-Z0-9]+):([a-zA-Z0-9]+)\((.*)\)/g, (match, spaceId, objectId, args) => {
83
- const id = `${spaceId}:${objectId}`;
84
- if (id.length !== OBJECT_ID_LENGTH) {
85
- return match;
86
- }
87
-
88
- const fn = functions.find((fn) => fullyQualifiedId(fn) === id);
89
- if (fn?.binding) {
90
- return `${fn.binding}(${args})`;
91
- } else {
92
- return match;
93
- }
94
- });
95
- };
51
+ } & Pick<SheetContextType, 'onInfo'>;
96
52
 
97
53
  export const SheetContextProvider = ({
98
54
  children,
@@ -100,36 +56,17 @@ export const SheetContextProvider = ({
100
56
  space,
101
57
  readonly,
102
58
  onInfo,
103
- ...options
104
59
  }: PropsWithChildren<SheetContextProps>) => {
105
- const graph = useComputeGraph(space, options);
60
+ const model = useSheetModel(space, sheet, { readonly });
61
+ const formatting = useFormattingModel(model);
106
62
 
63
+ // TODO(Zan): Impl. set range and set cursor that scrolls to that cell or range if it is not visible.
107
64
  const [cursor, setCursor] = useState<CellAddress>();
108
65
  const [range, setRange] = useState<CellRange>();
109
66
  const [editing, setEditing] = useState<boolean>(false);
67
+ const decorations = useMemo(() => createDecorations(), []);
110
68
 
111
- const [[model, formatting] = [], setModels] = useState<[SheetModel, FormattingModel] | undefined>(undefined);
112
- useEffect(() => {
113
- let model: SheetModel | undefined;
114
- let formatting;
115
- const t = setTimeout(async () => {
116
- model = new SheetModel(graph, sheet, space, { readonly, mapFormulaBindingToId, mapFormulaBindingFromId });
117
- await model.initialize();
118
- formatting = new FormattingModel(model);
119
- setModels([model, formatting]);
120
- });
121
-
122
- return () => {
123
- clearTimeout(t);
124
- void model?.destroy();
125
- };
126
- }, [graph, readonly]);
127
-
128
- if (!model || !formatting) {
129
- return null;
130
- }
131
-
132
- return (
69
+ return !model || !formatting ? null : (
133
70
  <SheetContext.Provider
134
71
  value={{
135
72
  model,
@@ -142,6 +79,7 @@ export const SheetContextProvider = ({
142
79
  setEditing,
143
80
  // TODO(burdon): Change to event.
144
81
  onInfo,
82
+ decorations,
145
83
  }}
146
84
  >
147
85
  {children}