@dxos/plugin-sheet 0.6.11 → 0.6.12-main.568932b

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 (277) hide show
  1. package/dist/lib/browser/SheetContainer-T2QWJOFD.mjs +262 -0
  2. package/dist/lib/browser/SheetContainer-T2QWJOFD.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-FUAGSXA4.mjs → chunk-5ZMVZYGB.mjs} +24 -19
  4. package/dist/lib/browser/chunk-5ZMVZYGB.mjs.map +7 -0
  5. package/dist/lib/browser/chunk-GSV5QNLD.mjs +2966 -0
  6. package/dist/lib/browser/chunk-GSV5QNLD.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-JRL5LGCE.mjs → chunk-QILRZNE5.mjs} +2 -5
  8. package/dist/lib/browser/chunk-QILRZNE5.mjs.map +7 -0
  9. package/dist/lib/browser/{SheetContainer-U4H5D34A.mjs → chunk-ZL2V5UJR.mjs} +1182 -249
  10. package/dist/lib/browser/chunk-ZL2V5UJR.mjs.map +7 -0
  11. package/dist/lib/browser/graph-M4IQ76QX.mjs +33 -0
  12. package/dist/lib/browser/graph-M4IQ76QX.mjs.map +7 -0
  13. package/dist/lib/browser/index.mjs +96 -60
  14. package/dist/lib/browser/index.mjs.map +4 -4
  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-PV5ET4UJ.cjs +280 -0
  19. package/dist/lib/node/SheetContainer-PV5ET4UJ.cjs.map +7 -0
  20. package/dist/lib/node/{SheetContainer-AXQV3ZT5.cjs → chunk-2K53Z2TU.cjs} +1212 -287
  21. package/dist/lib/node/chunk-2K53Z2TU.cjs.map +7 -0
  22. package/dist/lib/node/{chunk-5KKJ4NPP.cjs → chunk-5XPK2V4A.cjs} +418 -678
  23. package/dist/lib/node/chunk-5XPK2V4A.cjs.map +7 -0
  24. package/dist/lib/node/{chunk-BJ6ZD7MN.cjs → chunk-BNARJ5GM.cjs} +5 -18
  25. package/dist/lib/node/chunk-BNARJ5GM.cjs.map +7 -0
  26. package/dist/lib/node/{chunk-DSYKOI4E.cjs → chunk-STAVQ2JE.cjs} +28 -24
  27. package/dist/lib/node/chunk-STAVQ2JE.cjs.map +7 -0
  28. package/dist/lib/node/graph-Q3N2X26H.cjs +55 -0
  29. package/dist/lib/node/graph-Q3N2X26H.cjs.map +7 -0
  30. package/dist/lib/node/index.cjs +106 -66
  31. package/dist/lib/node/index.cjs.map +4 -4
  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-FOZD2WLT.mjs +263 -0
  38. package/dist/lib/node-esm/SheetContainer-FOZD2WLT.mjs.map +7 -0
  39. package/dist/lib/node-esm/chunk-2HAM45RC.mjs +88 -0
  40. package/dist/lib/node-esm/chunk-2HAM45RC.mjs.map +7 -0
  41. package/dist/lib/{browser/chunk-D5AGLXJP.mjs → node-esm/chunk-5WPZCXNS.mjs} +411 -678
  42. package/dist/lib/node-esm/chunk-5WPZCXNS.mjs.map +7 -0
  43. package/dist/lib/node-esm/chunk-IU2L277A.mjs +17 -0
  44. package/dist/lib/node-esm/chunk-IU2L277A.mjs.map +7 -0
  45. package/dist/lib/node-esm/chunk-QEUCIHIN.mjs +2706 -0
  46. package/dist/lib/node-esm/chunk-QEUCIHIN.mjs.map +7 -0
  47. package/dist/lib/node-esm/graph-SMPUMOV2.mjs +34 -0
  48. package/dist/lib/node-esm/graph-SMPUMOV2.mjs.map +7 -0
  49. package/dist/lib/node-esm/index.mjs +285 -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 +16 -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 +8 -7
  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 +6 -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/graph/compute-graph-registry.d.ts +34 -0
  109. package/dist/types/src/graph/compute-graph-registry.d.ts.map +1 -0
  110. package/dist/types/src/graph/compute-graph.d.ts +64 -0
  111. package/dist/types/src/graph/compute-graph.d.ts.map +1 -0
  112. package/dist/types/src/graph/compute-graph.stories.d.ts +10 -0
  113. package/dist/types/src/graph/compute-graph.stories.d.ts.map +1 -0
  114. package/dist/types/src/graph/compute-graph.test.d.ts +2 -0
  115. package/dist/types/src/graph/compute-graph.test.d.ts.map +1 -0
  116. package/dist/types/src/graph/compute-node.d.ts +26 -0
  117. package/dist/types/src/graph/compute-node.d.ts.map +1 -0
  118. package/dist/types/src/{components/ComputeGraph → graph/functions}/async-function.d.ts +14 -5
  119. package/dist/types/src/graph/functions/async-function.d.ts.map +1 -0
  120. package/dist/types/src/graph/functions/edge-function.d.ts +21 -0
  121. package/dist/types/src/graph/functions/edge-function.d.ts.map +1 -0
  122. package/dist/types/src/{model/functions.d.ts → graph/functions/function-defs.d.ts} +1 -1
  123. package/dist/types/src/graph/functions/function-defs.d.ts.map +1 -0
  124. package/dist/types/src/graph/functions/index.d.ts +4 -0
  125. package/dist/types/src/graph/functions/index.d.ts.map +1 -0
  126. package/dist/types/src/graph/hyperformula.test.d.ts +2 -0
  127. package/dist/types/src/graph/hyperformula.test.d.ts.map +1 -0
  128. package/dist/types/src/graph/index.d.ts +5 -0
  129. package/dist/types/src/graph/index.d.ts.map +1 -0
  130. package/dist/types/src/graph/testing/index.d.ts +3 -0
  131. package/dist/types/src/graph/testing/index.d.ts.map +1 -0
  132. package/dist/types/src/graph/testing/test-builder.d.ts +15 -0
  133. package/dist/types/src/graph/testing/test-builder.d.ts.map +1 -0
  134. package/dist/types/src/graph/testing/test-plugin.d.ts +36 -0
  135. package/dist/types/src/graph/testing/test-plugin.d.ts.map +1 -0
  136. package/dist/types/src/graph/util.d.ts +2 -0
  137. package/dist/types/src/graph/util.d.ts.map +1 -0
  138. package/dist/types/src/hooks/hooks.stories.d.ts +11 -0
  139. package/dist/types/src/hooks/hooks.stories.d.ts.map +1 -0
  140. package/dist/types/src/hooks/index.d.ts +4 -0
  141. package/dist/types/src/hooks/index.d.ts.map +1 -0
  142. package/dist/types/src/hooks/useComputeGraph.d.ts +7 -0
  143. package/dist/types/src/hooks/useComputeGraph.d.ts.map +1 -0
  144. package/dist/types/src/hooks/useFormattingModel.d.ts +3 -0
  145. package/dist/types/src/hooks/useFormattingModel.d.ts.map +1 -0
  146. package/dist/types/src/hooks/useSheetModel.d.ts +8 -0
  147. package/dist/types/src/hooks/useSheetModel.d.ts.map +1 -0
  148. package/dist/types/src/meta.d.ts +1 -4
  149. package/dist/types/src/meta.d.ts.map +1 -1
  150. package/dist/types/src/model/formatting-model.d.ts +16 -0
  151. package/dist/types/src/model/formatting-model.d.ts.map +1 -0
  152. package/dist/types/src/model/index.d.ts +2 -3
  153. package/dist/types/src/model/index.d.ts.map +1 -1
  154. package/dist/types/src/model/{model.d.ts → sheet-model.d.ts} +10 -65
  155. package/dist/types/src/model/sheet-model.d.ts.map +1 -0
  156. package/dist/types/src/model/sheet-model.test.d.ts +2 -0
  157. package/dist/types/src/model/sheet-model.test.d.ts.map +1 -0
  158. package/dist/types/src/sanity.test.d.ts +2 -0
  159. package/dist/types/src/sanity.test.d.ts.map +1 -0
  160. package/dist/types/src/testing/index.d.ts +2 -0
  161. package/dist/types/src/testing/index.d.ts.map +1 -0
  162. package/dist/types/src/testing/testing.d.ts +8 -0
  163. package/dist/types/src/testing/testing.d.ts.map +1 -0
  164. package/dist/types/src/translations.d.ts +17 -12
  165. package/dist/types/src/translations.d.ts.map +1 -1
  166. package/dist/types/src/types.d.ts +86 -5
  167. package/dist/types/src/types.d.ts.map +1 -1
  168. package/dist/vendor/hyperformula.mjs +37145 -0
  169. package/package.json +55 -47
  170. package/src/SheetPlugin.tsx +50 -73
  171. package/src/components/CellEditor/CellEditor.stories.tsx +6 -6
  172. package/src/components/CellEditor/CellEditor.tsx +59 -9
  173. package/src/components/CellEditor/extension.test.ts +4 -6
  174. package/src/components/CellEditor/extension.ts +5 -6
  175. package/src/components/ComputeGraph/ComputeGraphContextProvider.tsx +20 -0
  176. package/src/components/ComputeGraph/index.ts +1 -3
  177. package/src/components/GridSheet/GridSheet.stories.tsx +36 -0
  178. package/src/components/GridSheet/GridSheet.tsx +171 -0
  179. package/src/components/GridSheet/util.ts +148 -0
  180. package/src/components/Sheet/Sheet.stories.tsx +52 -88
  181. package/src/components/Sheet/Sheet.tsx +87 -32
  182. package/src/components/Sheet/decorations.ts +62 -0
  183. package/src/components/Sheet/grid.ts +3 -3
  184. package/src/components/Sheet/nav.ts +19 -19
  185. package/src/components/Sheet/sheet-context.tsx +18 -80
  186. package/src/components/Sheet/threads.tsx +205 -0
  187. package/src/components/SheetContainer.tsx +68 -16
  188. package/src/components/Toolbar/Toolbar.tsx +53 -12
  189. package/src/components/index.ts +1 -0
  190. package/src/defs/index.ts +6 -0
  191. package/src/{model → defs}/types.test.ts +8 -9
  192. package/src/{model → defs}/types.ts +24 -14
  193. package/src/defs/util.ts +151 -0
  194. package/src/extensions/compute.stories.tsx +151 -0
  195. package/src/extensions/compute.ts +147 -0
  196. package/src/extensions/index.ts +5 -0
  197. package/src/graph/compute-graph-registry.ts +90 -0
  198. package/src/graph/compute-graph.stories.tsx +93 -0
  199. package/src/graph/compute-graph.test.ts +87 -0
  200. package/src/graph/compute-graph.ts +242 -0
  201. package/src/graph/compute-node.ts +63 -0
  202. package/src/{components/ComputeGraph → graph/functions}/async-function.ts +25 -15
  203. package/src/{components/ComputeGraph → graph/functions}/edge-function.ts +16 -14
  204. package/src/graph/functions/index.ts +7 -0
  205. package/src/graph/hyperformula.test.ts +14 -0
  206. package/src/graph/index.ts +8 -0
  207. package/src/graph/testing/index.ts +6 -0
  208. package/src/graph/testing/test-builder.ts +54 -0
  209. package/src/{components/ComputeGraph/custom.ts → graph/testing/test-plugin.ts} +44 -14
  210. package/src/graph/util.ts +8 -0
  211. package/src/hooks/hooks.stories.tsx +50 -0
  212. package/src/hooks/index.ts +7 -0
  213. package/src/hooks/useComputeGraph.ts +28 -0
  214. package/src/hooks/useFormattingModel.ts +11 -0
  215. package/src/hooks/useSheetModel.ts +40 -0
  216. package/src/meta.tsx +1 -5
  217. package/src/{components/Sheet/formatting.ts → model/formatting-model.ts} +20 -13
  218. package/src/model/index.ts +2 -3
  219. package/src/model/sheet-model.test.ts +57 -0
  220. package/src/model/sheet-model.ts +416 -0
  221. package/src/sanity.test.ts +40 -0
  222. package/src/testing/index.ts +5 -0
  223. package/src/testing/testing.tsx +68 -0
  224. package/src/translations.ts +6 -1
  225. package/src/types.ts +35 -10
  226. package/dist/lib/browser/SheetContainer-U4H5D34A.mjs.map +0 -7
  227. package/dist/lib/browser/chunk-APHOLYUB.mjs +0 -175
  228. package/dist/lib/browser/chunk-APHOLYUB.mjs.map +0 -7
  229. package/dist/lib/browser/chunk-D5AGLXJP.mjs.map +0 -7
  230. package/dist/lib/browser/chunk-FUAGSXA4.mjs.map +0 -7
  231. package/dist/lib/browser/chunk-JRL5LGCE.mjs.map +0 -7
  232. package/dist/lib/browser/chunk-NU4PBN33.mjs +0 -8
  233. package/dist/lib/browser/chunk-NU4PBN33.mjs.map +0 -7
  234. package/dist/lib/browser/testing.mjs +0 -92
  235. package/dist/lib/browser/testing.mjs.map +0 -7
  236. package/dist/lib/node/SheetContainer-AXQV3ZT5.cjs.map +0 -7
  237. package/dist/lib/node/chunk-5KKJ4NPP.cjs.map +0 -7
  238. package/dist/lib/node/chunk-BJ6ZD7MN.cjs.map +0 -7
  239. package/dist/lib/node/chunk-CN3RPESU.cjs +0 -202
  240. package/dist/lib/node/chunk-CN3RPESU.cjs.map +0 -7
  241. package/dist/lib/node/chunk-DSYKOI4E.cjs.map +0 -7
  242. package/dist/lib/node/chunk-PYXHNAAK.cjs +0 -40
  243. package/dist/lib/node/chunk-PYXHNAAK.cjs.map +0 -7
  244. package/dist/lib/node/testing.cjs +0 -111
  245. package/dist/lib/node/testing.cjs.map +0 -7
  246. package/dist/types/src/components/ComputeGraph/async-function.d.ts.map +0 -1
  247. package/dist/types/src/components/ComputeGraph/custom.d.ts +0 -21
  248. package/dist/types/src/components/ComputeGraph/custom.d.ts.map +0 -1
  249. package/dist/types/src/components/ComputeGraph/edge-function.d.ts +0 -20
  250. package/dist/types/src/components/ComputeGraph/edge-function.d.ts.map +0 -1
  251. package/dist/types/src/components/ComputeGraph/graph-context.d.ts +0 -12
  252. package/dist/types/src/components/ComputeGraph/graph-context.d.ts.map +0 -1
  253. package/dist/types/src/components/ComputeGraph/graph.browser.test.d.ts +0 -2
  254. package/dist/types/src/components/ComputeGraph/graph.browser.test.d.ts.map +0 -1
  255. package/dist/types/src/components/ComputeGraph/graph.d.ts +0 -26
  256. package/dist/types/src/components/ComputeGraph/graph.d.ts.map +0 -1
  257. package/dist/types/src/components/Sheet/formatting.d.ts +0 -14
  258. package/dist/types/src/components/Sheet/formatting.d.ts.map +0 -1
  259. package/dist/types/src/model/functions.d.ts.map +0 -1
  260. package/dist/types/src/model/model.browser.test.d.ts +0 -2
  261. package/dist/types/src/model/model.browser.test.d.ts.map +0 -1
  262. package/dist/types/src/model/model.d.ts.map +0 -1
  263. package/dist/types/src/model/types.d.ts.map +0 -1
  264. package/dist/types/src/model/types.test.d.ts.map +0 -1
  265. package/dist/types/src/model/util.d.ts +0 -15
  266. package/dist/types/src/model/util.d.ts.map +0 -1
  267. package/dist/types/src/testing.d.ts +0 -9
  268. package/dist/types/src/testing.d.ts.map +0 -1
  269. package/src/components/ComputeGraph/graph-context.tsx +0 -50
  270. package/src/components/ComputeGraph/graph.browser.test.ts +0 -50
  271. package/src/components/ComputeGraph/graph.ts +0 -62
  272. package/src/model/model.browser.test.ts +0 -100
  273. package/src/model/model.ts +0 -550
  274. package/src/model/util.ts +0 -36
  275. package/src/testing.ts +0 -50
  276. /package/dist/types/src/{model → defs}/types.test.d.ts +0 -0
  277. /package/src/{model/functions.ts → graph/functions/function-defs.ts} +0 -0
@@ -25,6 +25,7 @@ import { Resizable, type ResizeCallback, type ResizeStartCallback } from 're-res
25
25
  import React, {
26
26
  type CSSProperties,
27
27
  type DOMAttributes,
28
+ type KeyboardEventHandler,
28
29
  type PropsWithChildren,
29
30
  forwardRef,
30
31
  useEffect,
@@ -40,7 +41,7 @@ import { debounce } from '@dxos/async';
40
41
  import { fullyQualifiedId, createDocAccessor } from '@dxos/client/echo';
41
42
  import { log } from '@dxos/log';
42
43
  import { type ThemedClassName } from '@dxos/react-ui';
43
- import { createAttendableAttributes, useHasAttention } from '@dxos/react-ui-attention';
44
+ import { ATTENABLE_ATTRIBUTE, useAttendableAttributes, useAttention, useAttentionPath } from '@dxos/react-ui-attention';
44
45
  import { mx } from '@dxos/react-ui-theme';
45
46
 
46
47
  import {
@@ -60,6 +61,7 @@ import {
60
61
  } from './grid';
61
62
  import { type GridSize, handleArrowNav, handleNav, useRangeSelect } from './nav';
62
63
  import { type SheetContextProps, SheetContextProvider, useSheetContext } from './sheet-context';
64
+ import { useThreads } from './threads';
63
65
  import { getRectUnion, getRelativeClientRect, scrollIntoView } from './util';
64
66
  import {
65
67
  type CellIndex,
@@ -68,7 +70,9 @@ import {
68
70
  columnLetter,
69
71
  posEquals,
70
72
  rangeToA1Notation,
71
- } from '../../model';
73
+ addressToIndex,
74
+ addressFromIndex,
75
+ } from '../../defs';
72
76
  import {
73
77
  CellEditor,
74
78
  type CellRangeNotifier,
@@ -135,12 +139,16 @@ const SheetRoot = ({ children, ...props }: PropsWithChildren<SheetContextProps>)
135
139
 
136
140
  type SheetMainProps = ThemedClassName<Partial<GridSize>>;
137
141
 
138
- const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numRows, numColumns }, forwardRef) => {
142
+ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numRows, numCols }, forwardRef) => {
139
143
  const { model, cursor, setCursor, setRange, setEditing } = useSheetContext();
140
144
 
141
145
  // Scrolling.
142
146
  const { rowsRef, columnsRef, contentRef } = useScrollHandlers();
143
147
 
148
+ // Threads.
149
+ // TODO(Zan): Move this to an extension once we have an extension model.
150
+ useThreads();
151
+
144
152
  //
145
153
  // Order of Row/columns.
146
154
  //
@@ -170,21 +178,21 @@ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numR
170
178
  }, [rows, columns]);
171
179
 
172
180
  const handleMoveRows: SheetRowsProps['onMove'] = (from, to, num = 1) => {
173
- const cursorIdx = cursor ? model.addressToIndex(cursor) : undefined;
181
+ const cursorIdx = cursor ? addressToIndex(model.sheet, cursor) : undefined;
174
182
  const [rows] = model.sheet.rows.splice(from, num);
175
183
  model.sheet.rows.splice(to, 0, rows);
176
184
  if (cursorIdx) {
177
- setCursor(model.addressFromIndex(cursorIdx));
185
+ setCursor(addressFromIndex(model.sheet, cursorIdx));
178
186
  }
179
187
  setRows([...model.sheet.rows]);
180
188
  };
181
189
 
182
190
  const handleMoveColumns: SheetColumnsProps['onMove'] = (from, to, num = 1) => {
183
- const cursorIdx = cursor ? model.addressToIndex(cursor) : undefined;
191
+ const cursorIdx = cursor ? addressToIndex(model.sheet, cursor) : undefined;
184
192
  const columns = model.sheet.columns.splice(from, num);
185
193
  model.sheet.columns.splice(to, 0, ...columns);
186
194
  if (cursorIdx) {
187
- setCursor(model.addressFromIndex(cursorIdx));
195
+ setCursor(addressFromIndex(model.sheet, cursorIdx));
188
196
  }
189
197
  setColumns([...model.sheet.columns]);
190
198
  };
@@ -256,8 +264,8 @@ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numR
256
264
  ref={columnsRef}
257
265
  columns={columns}
258
266
  sizes={columnSizes}
259
- selected={cursor?.column}
260
- onSelect={(column) => setCursor(cursor?.column === column ? undefined : { row: -1, column })}
267
+ selected={cursor?.col}
268
+ onSelect={(col) => setCursor(cursor?.col === col ? undefined : { row: -1, col })}
261
269
  onResize={handleResizeColumn}
262
270
  onMove={handleMoveColumns}
263
271
  />
@@ -267,13 +275,13 @@ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numR
267
275
  rows={rows}
268
276
  sizes={rowSizes}
269
277
  selected={cursor?.row}
270
- onSelect={(row) => setCursor(cursor?.row === row ? undefined : { row, column: -1 })}
278
+ onSelect={(row) => setCursor(cursor?.row === row ? undefined : { row, col: -1 })}
271
279
  onResize={handleResizeRow}
272
280
  onMove={handleMoveRows}
273
281
  />
274
282
  <SheetGrid
275
283
  ref={contentRef}
276
- size={{ numRows: numRows ?? rows.length, numColumns: numColumns ?? columns.length }}
284
+ size={{ numRows: numRows ?? rows.length, numCols: numCols ?? columns.length }}
277
285
  rows={rows}
278
286
  columns={columns}
279
287
  rowSizes={rowSizes}
@@ -854,10 +862,8 @@ const SheetGrid = forwardRef<HTMLDivElement, SheetGridProps>(
854
862
  columnSizes,
855
863
  });
856
864
 
857
- // TODO(burdon): Prevent scroll if not attended.
858
865
  const id = fullyQualifiedId(model.sheet);
859
- const attendableAttrs = createAttendableAttributes(id);
860
- const hasAttention = useHasAttention(id);
866
+ const { hasAttention } = useAttention(id);
861
867
 
862
868
  return (
863
869
  <div ref={containerRef} role='grid' className='relative flex grow overflow-hidden'>
@@ -865,6 +871,7 @@ const SheetGrid = forwardRef<HTMLDivElement, SheetGridProps>(
865
871
  <div className={mx('z-20 absolute inset-0 border border-gridLine pointer-events-none')} />
866
872
 
867
873
  {/* Grid scroll container. */}
874
+ {/* NOTE: Prevents scroll if not attended. */}
868
875
  <div ref={scrollerRef} className={mx('grow', hasAttention && 'overflow-auto scrollbar-thin')}>
869
876
  {/* Scroll content. */}
870
877
  <div
@@ -878,11 +885,11 @@ const SheetGrid = forwardRef<HTMLDivElement, SheetGridProps>(
878
885
 
879
886
  {/* Grid cells. */}
880
887
  {rowRange.map(({ row, top, height }) => {
881
- return columnRange.map(({ column, left, width }) => {
888
+ return columnRange.map(({ col, left, width }) => {
882
889
  const style: CSSProperties = { position: 'absolute', top, left, width, height };
883
- const cell = { row, column };
890
+ const cell: CellAddress = { row, col };
884
891
  const id = addressToA1Notation(cell);
885
- const idx = model.addressToIndex(cell);
892
+ const idx = addressToIndex(model.sheet, cell);
886
893
  const active = posEquals(cursor, cell);
887
894
  if (active && editing) {
888
895
  const value = initialText.current ?? model.getCellText(cell) ?? '';
@@ -945,21 +952,37 @@ const SheetGrid = forwardRef<HTMLDivElement, SheetGridProps>(
945
952
  </div>
946
953
 
947
954
  {/* Hidden input for key navigation. */}
948
- {createPortal(
949
- <input
950
- ref={inputRef}
951
- autoFocus
952
- className='absolute w-[1px] h-[1px] bg-transparent outline-none border-none caret-transparent'
953
- onKeyDown={handleKeyDown}
954
- {...attendableAttrs}
955
- />,
956
- document.body,
957
- )}
955
+ {createPortal(<SheetInput ref={inputRef} id={id} onKeyDown={handleKeyDown} />, document.body)}
958
956
  </div>
959
957
  );
960
958
  },
961
959
  );
962
960
 
961
+ type SheetInputProps = {
962
+ id: string;
963
+ onKeyDown?: KeyboardEventHandler<HTMLInputElement>;
964
+ };
965
+
966
+ const SheetInput = forwardRef<HTMLInputElement, SheetInputProps>(({ id, onKeyDown }, forwardedRef) => {
967
+ const path = useAttentionPath();
968
+ const attendableAttrs = useAttendableAttributes(id);
969
+
970
+ // TODO(wittjosiah): Consider factoring out as an attention util.
971
+ // Wrap input in attendable divs for each part of the path.
972
+ // This ensures that the sheet stays attended when the input is focused.
973
+ return path.toReversed().reduce(
974
+ (acc, part) => {
975
+ return <div {...{ [ATTENABLE_ATTRIBUTE]: part }}>{acc}</div>;
976
+ },
977
+ <input
978
+ ref={forwardedRef}
979
+ className='absolute w-[1px] h-[1px] bg-transparent outline-none border-none caret-transparent'
980
+ onKeyDown={onKeyDown}
981
+ {...attendableAttrs}
982
+ />,
983
+ );
984
+ });
985
+
963
986
  //
964
987
  // Selection
965
988
  //
@@ -1003,16 +1026,46 @@ type SheetCellProps = {
1003
1026
  };
1004
1027
 
1005
1028
  const SheetCell = ({ id, cell, style, active, onSelect }: SheetCellProps) => {
1006
- const { formatting, editing, setRange } = useSheetContext();
1029
+ const {
1030
+ formatting,
1031
+ editing,
1032
+ setRange,
1033
+ decorations,
1034
+ model: { sheet },
1035
+ } = useSheetContext();
1007
1036
  const { value, classNames } = formatting.getFormatting(cell);
1008
1037
 
1038
+ const decorationsForCell = decorations.getDecorationsForCell(addressToIndex(sheet, cell)) ?? [];
1039
+ const decorationAddedClasses = useMemo(
1040
+ () => decorationsForCell.flatMap((d) => d.classNames ?? []),
1041
+ [decorationsForCell],
1042
+ );
1043
+ const decoratedContent = decorationsForCell.reduce(
1044
+ (children, { decorate }) => {
1045
+ if (!decorate) {
1046
+ return children;
1047
+ }
1048
+ const DecoratorComponent = decorate;
1049
+ return <DecoratorComponent>{children}</DecoratorComponent>;
1050
+ },
1051
+ <div
1052
+ role='none'
1053
+ className={mx(
1054
+ 'flex flex-grow bs-full is-full px-2 items-center truncate cursor-pointer',
1055
+ ...decorationAddedClasses,
1056
+ )}
1057
+ >
1058
+ {value}
1059
+ </div>,
1060
+ );
1061
+
1009
1062
  return (
1010
1063
  <div
1011
1064
  {...{ [`data-${CELL_DATA_KEY}`]: id }}
1012
1065
  role='cell'
1013
1066
  style={style}
1014
1067
  className={mx(
1015
- 'flex w-full h-full px-2 py-1 truncate items-center border border-gridLine cursor-pointer',
1068
+ 'border border-gridLine cursor-pointer',
1016
1069
  fragments.cell,
1017
1070
  active && ['z-20', fragments.cellSelected],
1018
1071
  classNames,
@@ -1026,7 +1079,7 @@ const SheetCell = ({ id, cell, style, active, onSelect }: SheetCellProps) => {
1026
1079
  }}
1027
1080
  onDoubleClick={() => onSelect?.(cell, true)}
1028
1081
  >
1029
- {value}
1082
+ {decoratedContent}
1030
1083
  </div>
1031
1084
  );
1032
1085
  };
@@ -1045,10 +1098,11 @@ const GridCellEditor = ({ style, value, onNav, onClose }: GridCellEditorProps) =
1045
1098
  notifier.current?.(rangeToA1Notation(range));
1046
1099
  }
1047
1100
  }, [range]);
1101
+
1048
1102
  const extension = useMemo(
1049
1103
  () => [
1050
1104
  editorKeys({ onNav, onClose }),
1051
- sheetExtension({ functions: model.functions }),
1105
+ sheetExtension({ functions: model.graph.getFunctions() }),
1052
1106
  rangeExtension((fn) => (notifier.current = fn)),
1053
1107
  ],
1054
1108
  [model],
@@ -1072,12 +1126,13 @@ const GridCellEditor = ({ style, value, onNav, onClose }: GridCellEditorProps) =
1072
1126
 
1073
1127
  const SheetStatusBar = () => {
1074
1128
  const { model, cursor, range } = useSheetContext();
1129
+
1075
1130
  let value;
1076
1131
  let isFormula = false;
1077
1132
  if (cursor) {
1078
1133
  value = model.getCellValue(cursor);
1079
1134
  if (typeof value === 'string' && value.charAt(0) === '=') {
1080
- value = model.mapFormulaBindingFromId(model.mapFormulaIndicesToRefs(value));
1135
+ value = model.graph.mapFunctionBindingFromId(model.mapFormulaIndicesToRefs(value));
1081
1136
  isFormula = true;
1082
1137
  } else if (value != null) {
1083
1138
  value = String(value);
@@ -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';
10
8
 
11
- import { FormattingModel } from './formatting';
12
- import { type CellAddress, type CellRange, defaultFunctions, SheetModel } from '../../model';
9
+ import { createDecorations } from './decorations';
10
+ import { type CellAddress, type CellRange } from '../../defs';
11
+ import { type ComputeGraph } from '../../graph';
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);
@@ -47,89 +45,28 @@ export const useSheetContext = (): SheetContextType => {
47
45
  };
48
46
 
49
47
  export type SheetContextProps = {
48
+ graph: ComputeGraph;
50
49
  sheet: SheetType;
51
- 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,
55
+ graph,
99
56
  sheet,
100
- space,
101
57
  readonly,
102
58
  onInfo,
103
- ...options
104
59
  }: PropsWithChildren<SheetContextProps>) => {
105
- const graph = useComputeGraph(space, options);
60
+ const model = useSheetModel(graph, 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}