@dxos/plugin-sheet 0.8.1 → 0.8.2-main.10c050d

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 (188) hide show
  1. package/dist/lib/browser/SheetContainer-GXPG3ZDN.mjs +351 -0
  2. package/dist/lib/browser/SheetContainer-GXPG3ZDN.mjs.map +7 -0
  3. package/dist/lib/browser/anchor-sort-CUTFYIT4.mjs +24 -0
  4. package/dist/lib/browser/anchor-sort-CUTFYIT4.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-ZOKEQL2K.mjs → chunk-5FLX3UGU.mjs} +55 -63
  6. package/dist/lib/browser/chunk-5FLX3UGU.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-AYMJXZFS.mjs +847 -0
  8. package/dist/lib/browser/chunk-AYMJXZFS.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-AT5ZK6JD.mjs → chunk-FJRLDX7Z.mjs} +1 -1
  10. package/dist/lib/browser/chunk-FJRLDX7Z.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-EMSCNWEK.mjs → chunk-IR42IS3F.mjs} +2 -2
  12. package/dist/lib/{node-esm/chunk-76T5X4VP.mjs.map → browser/chunk-IR42IS3F.mjs.map} +1 -1
  13. package/dist/lib/browser/{compute-graph-registry-WEJLJJ6T.mjs → compute-graph-registry-IXGGJJBU.mjs} +3 -3
  14. package/dist/lib/browser/compute-graph-registry-IXGGJJBU.mjs.map +7 -0
  15. package/dist/lib/browser/index.mjs +17 -16
  16. package/dist/lib/browser/index.mjs.map +3 -3
  17. package/dist/lib/browser/{intent-resolver-6OUEM3IG.mjs → intent-resolver-UI4DHURQ.mjs} +2 -2
  18. package/dist/lib/browser/{markdown-DR4RDEEY.mjs → markdown-T4TUP4BF.mjs} +4 -4
  19. package/dist/lib/browser/markdown-T4TUP4BF.mjs.map +7 -0
  20. package/dist/lib/browser/meta.json +1 -1
  21. package/dist/lib/browser/{react-surface-ECKBP3UZ.mjs → react-surface-KI6T5M2X.mjs} +5 -5
  22. package/dist/lib/browser/types/index.mjs +1 -1
  23. package/dist/lib/node/SheetContainer-UUDOHLZR.cjs +351 -0
  24. package/dist/lib/node/SheetContainer-UUDOHLZR.cjs.map +7 -0
  25. package/dist/lib/node/{thread-WP43BC4N.cjs → anchor-sort-LTLYUTUP.cjs} +17 -10
  26. package/dist/lib/node/anchor-sort-LTLYUTUP.cjs.map +7 -0
  27. package/dist/lib/node/{chunk-D4MOMCEU.cjs → chunk-76NESQLB.cjs} +70 -78
  28. package/dist/lib/node/chunk-76NESQLB.cjs.map +7 -0
  29. package/dist/lib/node/chunk-BXBNSNDK.cjs +855 -0
  30. package/dist/lib/node/chunk-BXBNSNDK.cjs.map +7 -0
  31. package/dist/lib/node/{chunk-LEV7OSTK.cjs → chunk-FIM6EZ6M.cjs} +4 -4
  32. package/dist/lib/node/chunk-FIM6EZ6M.cjs.map +7 -0
  33. package/dist/lib/node/{chunk-O2FOEUYB.cjs → chunk-LJWWS53Z.cjs} +5 -5
  34. package/dist/lib/node/{chunk-O2FOEUYB.cjs.map → chunk-LJWWS53Z.cjs.map} +1 -1
  35. package/dist/lib/node/{compute-graph-registry-VVSRJUGS.cjs → compute-graph-registry-ARLDHPFW.cjs} +7 -7
  36. package/dist/lib/node/compute-graph-registry-ARLDHPFW.cjs.map +7 -0
  37. package/dist/lib/node/index.cjs +21 -20
  38. package/dist/lib/node/index.cjs.map +3 -3
  39. package/dist/lib/node/{intent-resolver-ICHNDL6F.cjs → intent-resolver-EVLGL7VZ.cjs} +9 -9
  40. package/dist/lib/node/{markdown-E7OUIMZO.cjs → markdown-DBPOAYI7.cjs} +8 -8
  41. package/dist/lib/node/markdown-DBPOAYI7.cjs.map +7 -0
  42. package/dist/lib/node/meta.json +1 -1
  43. package/dist/lib/node/{react-surface-6PBWE75L.cjs → react-surface-QHAPOAR2.cjs} +14 -14
  44. package/dist/lib/node/types/index.cjs +29 -29
  45. package/dist/lib/node/types/index.cjs.map +1 -1
  46. package/dist/lib/node-esm/SheetContainer-44KHKMPI.mjs +352 -0
  47. package/dist/lib/node-esm/SheetContainer-44KHKMPI.mjs.map +7 -0
  48. package/dist/lib/node-esm/anchor-sort-3E2VGLO6.mjs +25 -0
  49. package/dist/lib/node-esm/anchor-sort-3E2VGLO6.mjs.map +7 -0
  50. package/dist/lib/node-esm/{chunk-6NB67Y6X.mjs → chunk-DIF3IOAB.mjs} +55 -63
  51. package/dist/lib/node-esm/chunk-DIF3IOAB.mjs.map +7 -0
  52. package/dist/lib/node-esm/chunk-GCCM7R45.mjs +848 -0
  53. package/dist/lib/node-esm/chunk-GCCM7R45.mjs.map +7 -0
  54. package/dist/lib/node-esm/{chunk-76T5X4VP.mjs → chunk-IQ76YE6M.mjs} +2 -2
  55. package/dist/lib/{browser/chunk-EMSCNWEK.mjs.map → node-esm/chunk-IQ76YE6M.mjs.map} +1 -1
  56. package/dist/lib/node-esm/{chunk-HXBUY5ET.mjs → chunk-NMCVJWDT.mjs} +1 -1
  57. package/dist/lib/node-esm/chunk-NMCVJWDT.mjs.map +7 -0
  58. package/dist/lib/node-esm/{compute-graph-registry-PBQ52KH6.mjs → compute-graph-registry-7PDWXMHF.mjs} +3 -3
  59. package/dist/lib/node-esm/compute-graph-registry-7PDWXMHF.mjs.map +7 -0
  60. package/dist/lib/node-esm/index.mjs +17 -16
  61. package/dist/lib/node-esm/index.mjs.map +3 -3
  62. package/dist/lib/node-esm/{intent-resolver-2JNQCFCI.mjs → intent-resolver-TPOH5JM5.mjs} +2 -2
  63. package/dist/lib/node-esm/{markdown-BPKS2TNG.mjs → markdown-WWUJ3E5F.mjs} +4 -4
  64. package/dist/lib/node-esm/markdown-WWUJ3E5F.mjs.map +7 -0
  65. package/dist/lib/node-esm/meta.json +1 -1
  66. package/dist/lib/node-esm/{react-surface-UM2Y3ZWZ.mjs → react-surface-XT2J3S67.mjs} +5 -5
  67. package/dist/lib/node-esm/types/index.mjs +1 -1
  68. package/dist/types/src/SheetPlugin.d.ts.map +1 -1
  69. package/dist/types/src/capabilities/anchor-sort.d.ts +6 -0
  70. package/dist/types/src/capabilities/anchor-sort.d.ts.map +1 -0
  71. package/dist/types/src/capabilities/compute-graph-registry.d.ts +2 -2
  72. package/dist/types/src/capabilities/compute-graph-registry.d.ts.map +1 -1
  73. package/dist/types/src/capabilities/index.d.ts +6 -6
  74. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  75. package/dist/types/src/capabilities/markdown.d.ts +2 -2
  76. package/dist/types/src/capabilities/markdown.d.ts.map +1 -1
  77. package/dist/types/src/components/ComputeGraph/ComputeGraphContextProvider.d.ts.map +1 -1
  78. package/dist/types/src/components/GridSheet/GridSheet.d.ts.map +1 -1
  79. package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts.map +1 -1
  80. package/dist/types/src/components/GridSheet/util.d.ts.map +1 -1
  81. package/dist/types/src/components/RangeList/RangeList.d.ts.map +1 -1
  82. package/dist/types/src/components/SheetContainer/SheetContainer.d.ts +3 -2
  83. package/dist/types/src/components/SheetContainer/SheetContainer.d.ts.map +1 -1
  84. package/dist/types/src/components/SheetContainer/SheetContainer.stories.d.ts.map +1 -1
  85. package/dist/types/src/components/SheetContext/SheetContext.d.ts.map +1 -1
  86. package/dist/types/src/components/SheetToolbar/SheetToolbar.d.ts +2 -2
  87. package/dist/types/src/components/SheetToolbar/SheetToolbar.d.ts.map +1 -1
  88. package/dist/types/src/components/SheetToolbar/align.d.ts +4 -1
  89. package/dist/types/src/components/SheetToolbar/align.d.ts.map +1 -1
  90. package/dist/types/src/components/SheetToolbar/style.d.ts +3 -1
  91. package/dist/types/src/components/SheetToolbar/style.d.ts.map +1 -1
  92. package/dist/types/src/components/SheetToolbar/useToolbarState.d.ts +2 -3
  93. package/dist/types/src/components/SheetToolbar/useToolbarState.d.ts.map +1 -1
  94. package/dist/types/src/components/index.d.ts +1 -6
  95. package/dist/types/src/components/index.d.ts.map +1 -1
  96. package/dist/types/src/extensions/compute.d.ts.map +1 -1
  97. package/dist/types/src/extensions/editor/extension.d.ts.map +1 -1
  98. package/dist/types/src/integrations/thread-ranges.d.ts.map +1 -1
  99. package/dist/types/src/model/sheet-model.d.ts.map +1 -1
  100. package/dist/types/src/model/testing.d.ts.map +1 -1
  101. package/dist/types/src/model/useSheetModel.d.ts.map +1 -1
  102. package/dist/types/src/serializer.d.ts.map +1 -1
  103. package/dist/types/src/testing/data.d.ts.map +1 -1
  104. package/dist/types/src/testing/playwright/sheet-manager.d.ts.map +1 -1
  105. package/dist/types/src/testing/testing.d.ts.map +1 -1
  106. package/dist/types/src/translations.d.ts +2 -54
  107. package/dist/types/src/translations.d.ts.map +1 -1
  108. package/dist/types/src/types/schema.d.ts +38 -150
  109. package/dist/types/src/types/schema.d.ts.map +1 -1
  110. package/dist/types/src/types/sheet-range-types.d.ts.map +1 -1
  111. package/dist/types/src/types/types.d.ts +36 -36
  112. package/dist/types/src/types/types.d.ts.map +1 -1
  113. package/dist/types/src/types/util.d.ts.map +1 -1
  114. package/dist/types/tsconfig.tsbuildinfo +1 -1
  115. package/package.json +55 -48
  116. package/src/SheetPlugin.tsx +7 -5
  117. package/src/capabilities/anchor-sort.ts +22 -0
  118. package/src/capabilities/compute-graph-registry.ts +3 -3
  119. package/src/capabilities/index.ts +1 -1
  120. package/src/capabilities/markdown.ts +3 -3
  121. package/src/components/ComputeGraph/compute-graph.stories.tsx +4 -4
  122. package/src/components/FunctionEditor/FunctionEditor.tsx +1 -1
  123. package/src/components/GridSheet/GridSheet.stories.tsx +5 -2
  124. package/src/components/GridSheet/GridSheet.tsx +18 -12
  125. package/src/components/GridSheet/SheetCellEditor.stories.tsx +2 -2
  126. package/src/components/GridSheet/util.ts +17 -11
  127. package/src/components/RangeList/RangeList.tsx +2 -2
  128. package/src/components/SheetContainer/SheetContainer.stories.tsx +3 -6
  129. package/src/components/SheetContainer/SheetContainer.tsx +5 -8
  130. package/src/components/SheetToolbar/SheetToolbar.stories.tsx +2 -2
  131. package/src/components/SheetToolbar/SheetToolbar.tsx +56 -20
  132. package/src/components/SheetToolbar/align.ts +44 -14
  133. package/src/components/SheetToolbar/style.ts +48 -12
  134. package/src/components/SheetToolbar/useToolbarState.ts +3 -4
  135. package/src/extensions/compute.ts +1 -1
  136. package/src/integrations/thread-ranges.ts +14 -17
  137. package/src/model/sheet-model.test.ts +1 -1
  138. package/src/model/sheet-model.ts +17 -17
  139. package/src/sanity.test.ts +3 -3
  140. package/src/serializer.ts +3 -4
  141. package/src/testing/playwright/sheet-manager.ts +9 -9
  142. package/src/types/schema.ts +20 -22
  143. package/src/types/types.ts +25 -25
  144. package/src/types/util.ts +2 -3
  145. package/dist/lib/browser/SheetContainer-B3A5443Z.mjs +0 -370
  146. package/dist/lib/browser/SheetContainer-B3A5443Z.mjs.map +0 -7
  147. package/dist/lib/browser/chunk-AT5ZK6JD.mjs.map +0 -7
  148. package/dist/lib/browser/chunk-CHMPICA6.mjs +0 -815
  149. package/dist/lib/browser/chunk-CHMPICA6.mjs.map +0 -7
  150. package/dist/lib/browser/chunk-ZOKEQL2K.mjs.map +0 -7
  151. package/dist/lib/browser/compute-graph-registry-WEJLJJ6T.mjs.map +0 -7
  152. package/dist/lib/browser/markdown-DR4RDEEY.mjs.map +0 -7
  153. package/dist/lib/browser/thread-76MK2FMV.mjs +0 -17
  154. package/dist/lib/browser/thread-76MK2FMV.mjs.map +0 -7
  155. package/dist/lib/node/SheetContainer-LGRD3TTQ.cjs +0 -364
  156. package/dist/lib/node/SheetContainer-LGRD3TTQ.cjs.map +0 -7
  157. package/dist/lib/node/chunk-AEH3L5QZ.cjs +0 -822
  158. package/dist/lib/node/chunk-AEH3L5QZ.cjs.map +0 -7
  159. package/dist/lib/node/chunk-D4MOMCEU.cjs.map +0 -7
  160. package/dist/lib/node/chunk-LEV7OSTK.cjs.map +0 -7
  161. package/dist/lib/node/compute-graph-registry-VVSRJUGS.cjs.map +0 -7
  162. package/dist/lib/node/markdown-E7OUIMZO.cjs.map +0 -7
  163. package/dist/lib/node/thread-WP43BC4N.cjs.map +0 -7
  164. package/dist/lib/node-esm/SheetContainer-4FNTLG5R.mjs +0 -371
  165. package/dist/lib/node-esm/SheetContainer-4FNTLG5R.mjs.map +0 -7
  166. package/dist/lib/node-esm/chunk-6NB67Y6X.mjs.map +0 -7
  167. package/dist/lib/node-esm/chunk-HXBUY5ET.mjs.map +0 -7
  168. package/dist/lib/node-esm/chunk-WQHYR4WD.mjs +0 -816
  169. package/dist/lib/node-esm/chunk-WQHYR4WD.mjs.map +0 -7
  170. package/dist/lib/node-esm/compute-graph-registry-PBQ52KH6.mjs.map +0 -7
  171. package/dist/lib/node-esm/markdown-BPKS2TNG.mjs.map +0 -7
  172. package/dist/lib/node-esm/thread-4NCPE5FK.mjs +0 -18
  173. package/dist/lib/node-esm/thread-4NCPE5FK.mjs.map +0 -7
  174. package/dist/types/src/capabilities/thread.d.ts +0 -6
  175. package/dist/types/src/capabilities/thread.d.ts.map +0 -1
  176. package/dist/types/src/components/SheetToolbar/comment.d.ts +0 -23
  177. package/dist/types/src/components/SheetToolbar/comment.d.ts.map +0 -1
  178. package/dist/types/src/components/SheetToolbar/useToolbarAction.d.ts +0 -8
  179. package/dist/types/src/components/SheetToolbar/useToolbarAction.d.ts.map +0 -1
  180. package/src/capabilities/thread.ts +0 -14
  181. package/src/components/SheetToolbar/comment.ts +0 -56
  182. package/src/components/SheetToolbar/useToolbarAction.ts +0 -87
  183. /package/dist/lib/browser/{intent-resolver-6OUEM3IG.mjs.map → intent-resolver-UI4DHURQ.mjs.map} +0 -0
  184. /package/dist/lib/browser/{react-surface-ECKBP3UZ.mjs.map → react-surface-KI6T5M2X.mjs.map} +0 -0
  185. /package/dist/lib/node/{intent-resolver-ICHNDL6F.cjs.map → intent-resolver-EVLGL7VZ.cjs.map} +0 -0
  186. /package/dist/lib/node/{react-surface-6PBWE75L.cjs.map → react-surface-QHAPOAR2.cjs.map} +0 -0
  187. /package/dist/lib/node-esm/{intent-resolver-2JNQCFCI.mjs.map → intent-resolver-TPOH5JM5.mjs.map} +0 -0
  188. /package/dist/lib/node-esm/{react-surface-UM2Y3ZWZ.mjs.map → react-surface-XT2J3S67.mjs.map} +0 -0
@@ -2,46 +2,82 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { type PropsWithChildren, useCallback } from 'react';
5
+ import { Rx } from '@effect-rx/rx-react';
6
+ import React, { type PropsWithChildren, useMemo } from 'react';
6
7
 
8
+ import { useAppGraph } from '@dxos/app-framework';
9
+ import { type CompleteCellRange } from '@dxos/compute';
7
10
  import { type ThemedClassName } from '@dxos/react-ui';
8
- import { createGapSeparator, MenuProvider, ToolbarMenu, useMenuActions } from '@dxos/react-ui-menu';
11
+ import {
12
+ type ActionGraphEdges,
13
+ type ActionGraphNodes,
14
+ type ActionGraphProps,
15
+ createGapSeparator,
16
+ MenuProvider,
17
+ rxFromSignal,
18
+ ToolbarMenu,
19
+ useMenuActions,
20
+ } from '@dxos/react-ui-menu';
9
21
 
10
22
  import { createAlign, useAlignState } from './align';
11
- import { createComment, useCommentState } from './comment';
12
23
  import { createStyle, useStyleState } from './style';
13
- import { useToolbarAction } from './useToolbarAction';
14
24
  import { type ToolbarState, useToolbarState } from './useToolbarState';
25
+ import { type SheetModel } from '../../model';
26
+ import { useSheetContext } from '../SheetContext';
15
27
 
16
28
  //
17
29
  // Root
18
30
  //
19
31
 
20
- export type SheetToolbarProps = ThemedClassName<PropsWithChildren<{ attendableId?: string }>>;
21
-
22
- const createToolbarActions = (state: ToolbarState) => {
23
- const align = createAlign(state);
24
- const style = createStyle(state);
25
- const gap = createGapSeparator();
26
- const comment = createComment(state);
27
- return {
28
- nodes: [...align.nodes, ...style.nodes, ...gap.nodes, ...comment.nodes],
29
- edges: [...align.edges, ...style.edges, ...gap.edges, ...comment.edges],
30
- };
32
+ export type SheetToolbarProps = ThemedClassName<PropsWithChildren<{ id: string }>>;
33
+
34
+ const createToolbarActions = (
35
+ model: SheetModel,
36
+ state: ToolbarState,
37
+ cursorFallbackRange?: CompleteCellRange,
38
+ customActions?: Rx.Rx<ActionGraphProps>,
39
+ ) => {
40
+ return Rx.make((get) => {
41
+ const align = get(rxFromSignal(() => createAlign(model, state, cursorFallbackRange)));
42
+ const style = get(rxFromSignal(() => createStyle(model, state, cursorFallbackRange)));
43
+ const gap = createGapSeparator();
44
+ const nodes: ActionGraphNodes = [...align.nodes, ...style.nodes, ...gap.nodes];
45
+ const edges: ActionGraphEdges = [...align.edges, ...style.edges, ...gap.edges];
46
+ if (customActions) {
47
+ const custom = get(customActions);
48
+ nodes.push(...custom.nodes);
49
+ edges.push(...custom.edges);
50
+ }
51
+ return {
52
+ nodes,
53
+ edges,
54
+ };
55
+ });
31
56
  };
32
57
 
33
- export const SheetToolbar = ({ attendableId, classNames }: SheetToolbarProps) => {
58
+ export const SheetToolbar = ({ id, classNames }: SheetToolbarProps) => {
59
+ const { model, cursorFallbackRange } = useSheetContext();
34
60
  const state = useToolbarState({});
35
61
  useAlignState(state);
36
62
  useStyleState(state);
37
- useCommentState(state);
38
63
 
39
- const actionsCreator = useCallback(() => createToolbarActions(state), [state]);
64
+ const { graph } = useAppGraph();
65
+ const customActions = useMemo(() => {
66
+ return Rx.make((get) => {
67
+ const actions = get(graph.actions(id));
68
+ const nodes = actions.filter((action) => action.properties.disposition === 'toolbar');
69
+ return { nodes, edges: nodes.map((node) => ({ source: 'root', target: node.id })) };
70
+ });
71
+ }, [graph]);
72
+
73
+ const actionsCreator = useMemo(
74
+ () => createToolbarActions(model, state, cursorFallbackRange, customActions),
75
+ [model, state, cursorFallbackRange, customActions],
76
+ );
40
77
  const menu = useMenuActions(actionsCreator);
41
- const handleAction = useToolbarAction(state);
42
78
 
43
79
  return (
44
- <MenuProvider {...menu} attendableId={attendableId} onAction={handleAction}>
80
+ <MenuProvider {...menu} attendableId={id}>
45
81
  <ToolbarMenu classNames={classNames} />
46
82
  </MenuProvider>
47
83
  );
@@ -4,11 +4,13 @@
4
4
 
5
5
  import { useEffect } from 'react';
6
6
 
7
- import { inRange } from '@dxos/compute';
7
+ import { type CompleteCellRange, inRange } from '@dxos/compute';
8
8
  import { createMenuAction, createMenuItemGroup, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
9
9
 
10
+ import { type ToolbarState } from './useToolbarState';
10
11
  import { SHEET_PLUGIN } from '../../meta';
11
- import { type AlignKey, alignKey, type AlignValue, rangeFromIndex } from '../../types';
12
+ import { type SheetModel } from '../../model';
13
+ import { type AlignKey, alignKey, type AlignValue, rangeFromIndex, rangeToIndex } from '../../types';
12
14
  import { useSheetContext } from '../SheetContext';
13
15
 
14
16
  export type AlignAction = { key: AlignKey; value: AlignValue };
@@ -43,21 +45,49 @@ const createAlignGroupAction = (value?: AlignValue) =>
43
45
  value: `${alignKey}--${value}`,
44
46
  } as ToolbarMenuActionGroupProperties);
45
47
 
46
- const createAlignActions = (value?: AlignValue) =>
48
+ const createAlignActions = (model: SheetModel, state: ToolbarState, cursorFallbackRange?: CompleteCellRange) =>
47
49
  Object.entries(aligns).map(([alignValue, icon]) => {
48
- return createMenuAction<AlignAction>(`${alignKey}--${alignValue}`, {
49
- key: alignKey,
50
- value: alignValue as AlignValue,
51
- checked: value === alignValue,
52
- label: [`range value ${alignValue} label`, { ns: SHEET_PLUGIN }],
53
- icon,
54
- testId: `grid.toolbar.${alignKey}.${alignValue}`,
55
- });
50
+ return createMenuAction<AlignAction>(
51
+ `${alignKey}--${alignValue}`,
52
+ () => {
53
+ if (!cursorFallbackRange) {
54
+ return;
55
+ }
56
+ const index =
57
+ model.sheet.ranges?.findIndex(
58
+ (range) =>
59
+ range.key === alignKey && inRange(rangeFromIndex(model.sheet, range.range), cursorFallbackRange.from),
60
+ ) ?? -1;
61
+ const nextRangeEntity = {
62
+ range: rangeToIndex(model.sheet, cursorFallbackRange),
63
+ key: alignKey,
64
+ value: alignValue as AlignValue,
65
+ };
66
+ if (index < 0) {
67
+ model.sheet.ranges?.push(nextRangeEntity);
68
+ state[alignKey] = nextRangeEntity.value;
69
+ } else if (model.sheet.ranges![index].value === nextRangeEntity.value) {
70
+ model.sheet.ranges?.splice(index, 1);
71
+ state[alignKey] = undefined;
72
+ } else {
73
+ model.sheet.ranges?.splice(index, 1, nextRangeEntity);
74
+ state[alignKey] = nextRangeEntity.value;
75
+ }
76
+ },
77
+ {
78
+ key: alignKey,
79
+ value: alignValue as AlignValue,
80
+ checked: state[alignKey] === alignValue,
81
+ label: [`range value ${alignValue} label`, { ns: SHEET_PLUGIN }],
82
+ icon,
83
+ testId: `grid.toolbar.${alignKey}.${alignValue}`,
84
+ },
85
+ );
56
86
  });
57
87
 
58
- export const createAlign = ({ [alignKey]: alignValue }: Partial<AlignState>) => {
59
- const alignGroup = createAlignGroupAction(alignValue);
60
- const alignActions = createAlignActions(alignValue);
88
+ export const createAlign = (model: SheetModel, state: ToolbarState, cursorFallbackRange?: CompleteCellRange) => {
89
+ const alignGroup = createAlignGroupAction(state[alignKey]);
90
+ const alignActions = createAlignActions(model, state, cursorFallbackRange);
61
91
  return {
62
92
  nodes: [alignGroup, ...alignActions],
63
93
  edges: [
@@ -4,11 +4,12 @@
4
4
 
5
5
  import { useEffect } from 'react';
6
6
 
7
- import { inRange } from '@dxos/compute';
7
+ import { type CompleteCellRange, inRange } from '@dxos/compute';
8
8
  import { createMenuAction, createMenuItemGroup, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
9
9
 
10
10
  import { SHEET_PLUGIN } from '../../meta';
11
- import { rangeFromIndex, type StyleKey, type StyleValue } from '../../types';
11
+ import { type SheetModel } from '../../model';
12
+ import { rangeFromIndex, rangeToIndex, type StyleKey, type StyleValue } from '../../types';
12
13
  import { useSheetContext } from '../SheetContext';
13
14
 
14
15
  export type StyleState = Partial<Record<StyleValue, boolean>>;
@@ -48,20 +49,55 @@ const createStyleGroup = (state: StyleState) => {
48
49
  } as ToolbarMenuActionGroupProperties);
49
50
  };
50
51
 
51
- const createStyleActions = (state: StyleState) =>
52
+ const createStyleActions = (model: SheetModel, state: StyleState, cursorFallbackRange?: CompleteCellRange) =>
52
53
  Object.entries(styles).map(([styleValue, icon]) => {
53
- return createMenuAction<StyleAction>(`style--${styleValue}`, {
54
- key: 'style',
55
- value: styleValue as StyleValue,
56
- icon,
57
- label: [`range value ${styleValue} label`, { ns: SHEET_PLUGIN }],
58
- checked: !!state[styleValue as StyleValue],
59
- });
54
+ return createMenuAction<StyleAction>(
55
+ `style--${styleValue}`,
56
+ () => {
57
+ if (!cursorFallbackRange) {
58
+ return;
59
+ }
60
+ const index =
61
+ model.sheet.ranges?.findIndex(
62
+ (range) =>
63
+ range.key === 'style' && inRange(rangeFromIndex(model.sheet, range.range), cursorFallbackRange.from),
64
+ ) ?? -1;
65
+ const nextRangeEntity = {
66
+ range: rangeToIndex(model.sheet, cursorFallbackRange),
67
+ key: 'style',
68
+ value: styleValue as StyleValue,
69
+ };
70
+ if (
71
+ model.sheet.ranges
72
+ .filter(
73
+ ({ range, key: rangeKey }) =>
74
+ rangeKey === 'style' && inRange(rangeFromIndex(model.sheet, range), cursorFallbackRange.from),
75
+ )
76
+ .some(({ value: rangeValue }) => rangeValue === styleValue)
77
+ ) {
78
+ // this value should be unset
79
+ if (index >= 0) {
80
+ model.sheet.ranges?.splice(index, 1);
81
+ }
82
+ state[nextRangeEntity.value] = false;
83
+ } else {
84
+ model.sheet.ranges?.push(nextRangeEntity);
85
+ state[nextRangeEntity.value] = true;
86
+ }
87
+ },
88
+ {
89
+ key: 'style',
90
+ value: styleValue as StyleValue,
91
+ icon,
92
+ label: [`range value ${styleValue} label`, { ns: SHEET_PLUGIN }],
93
+ checked: !!state[styleValue as StyleValue],
94
+ },
95
+ );
60
96
  });
61
97
 
62
- export const createStyle = (state: StyleState) => {
98
+ export const createStyle = (model: SheetModel, state: StyleState, cursorFallbackRange?: CompleteCellRange) => {
63
99
  const styleGroupAction = createStyleGroup(state);
64
- const styleActions = createStyleActions(state);
100
+ const styleActions = createStyleActions(model, state, cursorFallbackRange);
65
101
  return {
66
102
  nodes: [styleGroupAction, ...styleActions],
67
103
  edges: [
@@ -4,14 +4,13 @@
4
4
 
5
5
  import { useMemo } from 'react';
6
6
 
7
- import { create } from '@dxos/live-object';
7
+ import { live } from '@dxos/live-object';
8
8
 
9
9
  import { type AlignState } from './align';
10
- import { type CommentState } from './comment';
11
10
  import { type StyleState } from './style';
12
11
 
13
- export type ToolbarState = Partial<StyleState & AlignState & CommentState>;
12
+ export type ToolbarState = Partial<StyleState & AlignState>;
14
13
 
15
14
  export const useToolbarState = (initialState: ToolbarState = {}) => {
16
- return useMemo(() => create<ToolbarState>(initialState), []);
15
+ return useMemo(() => live<ToolbarState>(initialState), []);
17
16
  };
@@ -141,7 +141,7 @@ class ComputeWidget extends WidgetType {
141
141
  super();
142
142
  }
143
143
 
144
- override toDOM(_view: EditorView) {
144
+ override toDOM(_view: EditorView): HTMLDivElement {
145
145
  const div = document.createElement('div');
146
146
  div.setAttribute('title', this.formula);
147
147
  div.innerText = String(this.value);
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { pipe } from 'effect';
5
+ import { Schema, pipe } from 'effect';
6
6
  import { useCallback, useEffect, useMemo } from 'react';
7
7
 
8
8
  import {
@@ -15,11 +15,12 @@ import {
15
15
  } from '@dxos/app-framework';
16
16
  import { debounce } from '@dxos/async';
17
17
  import { type CellAddress, type CompleteCellRange, inRange } from '@dxos/compute';
18
- import { S } from '@dxos/echo-schema';
18
+ import { isInstanceOf, RelationSourceId } from '@dxos/echo-schema';
19
19
  import { ATTENDABLE_PATH_SEPARATOR, DeckAction } from '@dxos/plugin-deck/types';
20
- import { ThreadAction } from '@dxos/plugin-thread/types';
21
- import { fullyQualifiedId } from '@dxos/react-client/echo';
20
+ import { ThreadAction, ThreadType } from '@dxos/plugin-thread/types';
21
+ import { Filter, fullyQualifiedId, getSpace, Query, useQuery } from '@dxos/react-client/echo';
22
22
  import { type DxGridElement, type DxGridPosition, type GridContentProps } from '@dxos/react-ui-grid';
23
+ import { AnchoredTo } from '@dxos/schema';
23
24
 
24
25
  import { useSheetContext } from '../components';
25
26
  import { SHEET_PLUGIN } from '../meta';
@@ -55,7 +56,7 @@ export const useUpdateFocusedCellOnThreadSelection = (grid: DxGridElement | null
55
56
  subject: string;
56
57
  options: { cursor: string; ref: GridContentProps['activeRefs'] };
57
58
  } => {
58
- if (!S.is(LayoutAction.ScrollIntoView.fields.input)(data)) {
59
+ if (!Schema.is(LayoutAction.ScrollIntoView.fields.input)(data)) {
59
60
  return false;
60
61
  }
61
62
 
@@ -78,23 +79,19 @@ export const useSelectThreadOnCellFocus = () => {
78
79
  const { model, cursor } = useSheetContext();
79
80
  const { dispatchPromise: dispatch } = useIntentDispatcher();
80
81
 
81
- const threads = useMemo(
82
- () => model.sheet.threads?.filter((thread): thread is NonNullable<typeof thread> => !!thread) ?? [],
83
- [
84
- // TODO(thure): Surely we can find a better dependency for this…
85
- JSON.stringify(model.sheet.threads),
86
- ],
87
- );
82
+ const space = getSpace(model.sheet);
83
+ const anchors = useQuery(space, Query.select(Filter.ids(model.sheet.id)).targetOf(AnchoredTo));
88
84
 
89
85
  const selectClosestThread = useCallback(
90
86
  (cellAddress: CellAddress) => {
91
- if (!cellAddress || !threads) {
87
+ if (!cellAddress) {
92
88
  return;
93
89
  }
94
90
 
95
- const closestThread = threads?.find((ref) => {
96
- if (ref.target?.anchor) {
97
- const range = parseThreadAnchorAsCellRange(ref.target!.anchor);
91
+ const closestThread = anchors.find((anchor) => {
92
+ const source = anchor[RelationSourceId];
93
+ if (anchor.anchor && isInstanceOf(ThreadType, source)) {
94
+ const range = parseThreadAnchorAsCellRange(anchor.anchor);
98
95
  return range ? inRange(range, cellAddress) : false;
99
96
  } else {
100
97
  return false;
@@ -110,7 +107,7 @@ export const useSelectThreadOnCellFocus = () => {
110
107
  void dispatch(intent);
111
108
  }
112
109
  },
113
- [dispatch, threads],
110
+ [dispatch, anchors],
114
111
  );
115
112
 
116
113
  const debounced = useMemo(() => {
@@ -7,7 +7,7 @@ import { afterEach, beforeEach, describe, expect, onTestFinished, test } from 'v
7
7
  import { Trigger } from '@dxos/async';
8
8
  import { type CellScalarValue, addressFromA1Notation, isFormula } from '@dxos/compute';
9
9
  import { TestBuilder, testFunctionPlugins } from '@dxos/compute/testing';
10
- import { FunctionType } from '@dxos/functions/types';
10
+ import { FunctionType } from '@dxos/functions';
11
11
  import { log } from '@dxos/log';
12
12
 
13
13
  import { SheetModel } from './sheet-model';
@@ -119,7 +119,7 @@ export class SheetModel extends Resource {
119
119
  /**
120
120
  * Initialize sheet and engine.
121
121
  */
122
- protected override async _open() {
122
+ protected override async _open(): Promise<void> {
123
123
  log('initialize', { id: this.id });
124
124
  initialize(this._sheet);
125
125
 
@@ -145,7 +145,7 @@ export class SheetModel extends Resource {
145
145
  * NOTE: This resets the undo history.
146
146
  * @deprecated
147
147
  */
148
- reset() {
148
+ reset(): void {
149
149
  invariant(this._node);
150
150
  this._node.graph.hf.clearSheet(this._node.sheetId);
151
151
  Object.entries(this._sheet.cells).forEach(([key, { value }]) => {
@@ -173,17 +173,17 @@ export class SheetModel extends Resource {
173
173
  * @deprecated
174
174
  */
175
175
  // TODO(burdon): Remove.
176
- recalculate() {
176
+ recalculate(): void {
177
177
  this._node?.graph.hf.rebuildAndRecalculate();
178
178
  }
179
179
 
180
- insertRows(i: number, n = 1) {
180
+ insertRows(i: number, n = 1): string[] {
181
181
  const idx = insertIndices(this._sheet.rows, i, n, MAX_ROWS);
182
182
  this.reset();
183
183
  return idx;
184
184
  }
185
185
 
186
- insertColumns(i: number, n = 1) {
186
+ insertColumns(i: number, n = 1): string[] {
187
187
  const idx = insertIndices(this._sheet.columns, i, n, MAX_COLS);
188
188
  this.reset();
189
189
  return idx;
@@ -217,7 +217,7 @@ export class SheetModel extends Resource {
217
217
  return { axis: 'col', index, axisIndex: colIndex, axisMeta: this._sheet.rowMeta[colIndex], values };
218
218
  }
219
219
 
220
- restoreRow({ index, axisIndex, axisMeta, values }: SheetAction.RestoreAxis) {
220
+ restoreRow({ index, axisIndex, axisMeta, values }: SheetAction.RestoreAxis): void {
221
221
  this._sheet.rows.splice(index, 0, axisIndex);
222
222
  values.forEach((value, col) => {
223
223
  if (value) {
@@ -230,7 +230,7 @@ export class SheetModel extends Resource {
230
230
  this.reset();
231
231
  }
232
232
 
233
- restoreColumn({ index, axisIndex, axisMeta, values }: SheetAction.RestoreAxis) {
233
+ restoreColumn({ index, axisIndex, axisMeta, values }: SheetAction.RestoreAxis): void {
234
234
  this._sheet.columns.splice(index, 0, axisIndex);
235
235
  values.forEach((value, row) => {
236
236
  if (value) {
@@ -251,7 +251,7 @@ export class SheetModel extends Resource {
251
251
  /**
252
252
  * Clear range of values.
253
253
  */
254
- clear(range: CellRange) {
254
+ clear(range: CellRange): void {
255
255
  invariant(this._node);
256
256
  const topLeft = getTopLeft(range);
257
257
  const values = this._iterRange(range, () => null);
@@ -262,7 +262,7 @@ export class SheetModel extends Resource {
262
262
  });
263
263
  }
264
264
 
265
- cut(range: CellRange) {
265
+ cut(range: CellRange): void {
266
266
  invariant(this._node);
267
267
  this._node.graph.hf.cut(toModelRange(this._node.sheetId, range));
268
268
  this._iterRange(range, (cell) => {
@@ -271,12 +271,12 @@ export class SheetModel extends Resource {
271
271
  });
272
272
  }
273
273
 
274
- copy(range: CellRange) {
274
+ copy(range: CellRange): void {
275
275
  invariant(this._node);
276
276
  this._node.graph.hf.copy(toModelRange(this._node.sheetId, range));
277
277
  }
278
278
 
279
- paste(cell: CellAddress) {
279
+ paste(cell: CellAddress): void {
280
280
  invariant(this._node);
281
281
  if (!this._node.graph.hf.isClipboardEmpty()) {
282
282
  const changes = this._node.graph.hf.paste(toSimpleCellAddress(this._node.sheetId, cell));
@@ -291,7 +291,7 @@ export class SheetModel extends Resource {
291
291
  }
292
292
 
293
293
  // TODO(burdon): Display undo/redo state.
294
- undo() {
294
+ undo(): void {
295
295
  invariant(this._node);
296
296
  if (this._node.graph.hf.isThereSomethingToUndo()) {
297
297
  this._node.graph.hf.undo();
@@ -299,7 +299,7 @@ export class SheetModel extends Resource {
299
299
  }
300
300
  }
301
301
 
302
- redo() {
302
+ redo(): void {
303
303
  invariant(this._node);
304
304
  if (this._node.graph.hf.isThereSomethingToRedo()) {
305
305
  this._node.graph.hf.redo();
@@ -368,7 +368,7 @@ export class SheetModel extends Resource {
368
368
  /**
369
369
  * Sets the value, updating the sheet and engine.
370
370
  */
371
- setValue(cell: CellAddress, value: CellScalarValue) {
371
+ setValue(cell: CellAddress, value: CellScalarValue): void {
372
372
  invariant(this._node);
373
373
  if (this._options.readonly) {
374
374
  throw new ReadonlyException();
@@ -411,7 +411,7 @@ export class SheetModel extends Resource {
411
411
  /**
412
412
  * Sets values from a simple map.
413
413
  */
414
- setValues(values: Record<string, CellValue>) {
414
+ setValues(values: Record<string, CellValue>): void {
415
415
  Object.entries(values).forEach(([key, { value }]) => {
416
416
  this.setValue(addressFromA1Notation(key), value);
417
417
  });
@@ -441,12 +441,12 @@ export class SheetModel extends Resource {
441
441
  }
442
442
 
443
443
  // TODO(burdon): Delete index.
444
- private _deleteIndices(indices: string[], i: number, n: number) {
444
+ private _deleteIndices(indices: string[], i: number, n: number): void {
445
445
  throw new Error('Not implemented');
446
446
  }
447
447
 
448
448
  // TODO(burdon): Move. Cannot use fractional without changing. Switch back to using unique IDs?
449
- private _moveIndices(indices: string[], i: number, j: number, n: number) {
449
+ private _moveIndices(indices: string[], i: number, j: number, n: number): void {
450
450
  throw new Error('Not implemented');
451
451
  }
452
452
 
@@ -11,8 +11,8 @@ import { describe, test, expect } from 'vitest';
11
11
  // - throws "process.nextTick is not a function" (if browser)
12
12
 
13
13
  import { Client } from '@dxos/client';
14
- import { create } from '@dxos/client/echo';
15
- import { FunctionType } from '@dxos/functions/types';
14
+ import { live } from '@dxos/client/echo';
15
+ import { FunctionType } from '@dxos/functions';
16
16
 
17
17
  // TODO(burdon): Fix test infrastructure:
18
18
  // - Need docs? esp. needed for config. need pristine example package?
@@ -34,7 +34,7 @@ describe('test', () => {
34
34
  // - ERROR "process.nextTick is not a function"
35
35
  // - ERROR "Identifier 'Buffer' has already been declared" if { nodeExternal: true }
36
36
  const space = await client.spaces.create();
37
- const fn = space.db.add(create(FunctionType, { name: 'test', version: '0.0.1', binding: 'HELLO' }));
37
+ const fn = space.db.add(live(FunctionType, { name: 'test', version: '0.0.1', binding: 'HELLO' }));
38
38
  expect(fn).to.exist;
39
39
  });
40
40
  });
package/src/serializer.ts CHANGED
@@ -3,19 +3,18 @@
3
3
  //
4
4
 
5
5
  import { type TypedObjectSerializer } from '@dxos/plugin-space/types';
6
- import { create, getObjectCore } from '@dxos/react-client/echo';
6
+ import { live, getObjectCore } from '@dxos/react-client/echo';
7
7
 
8
8
  import { SheetType } from './types';
9
9
 
10
10
  export const serializer: TypedObjectSerializer<SheetType> = {
11
11
  serialize: async ({ object }): Promise<string> => {
12
- const { threads: _threads, ...sheet } = object;
13
- return JSON.stringify(sheet, null, 2);
12
+ return JSON.stringify(object, null, 2);
14
13
  },
15
14
 
16
15
  deserialize: async ({ content, newId }) => {
17
16
  const { id, ...parsed } = JSON.parse(content);
18
- const sheet = create(SheetType, parsed);
17
+ const sheet = live(SheetType, parsed);
19
18
 
20
19
  if (!newId) {
21
20
  const core = getObjectCore(sheet);
@@ -25,21 +25,21 @@ export class SheetManager {
25
25
  return this.cellByText('Ready').waitFor({ state: 'visible' });
26
26
  }
27
27
 
28
- async fill(text: string) {
28
+ async fill(text: string): Promise<void> {
29
29
  // TODO(thure): Do these timeouts help with test flakiness?
30
30
  await this.page.waitForTimeout(200);
31
31
  await this.cellEditor().fill(text);
32
32
  await this.page.waitForTimeout(200);
33
33
  }
34
34
 
35
- async press(key: string) {
35
+ async press(key: string): Promise<void> {
36
36
  // TODO(thure): Does these timeouts help with test flakiness?
37
37
  await this.page.waitForTimeout(200);
38
38
  await this.page.keyboard.press(key);
39
39
  await this.page.waitForTimeout(200);
40
40
  }
41
41
 
42
- async commit(key: string) {
42
+ async commit(key: string): Promise<void> {
43
43
  // TODO(thure): Why do we need to wait? Enter is ignored otherwise…
44
44
  await this.page.waitForTimeout(500);
45
45
  await this.press(key);
@@ -49,7 +49,7 @@ export class SheetManager {
49
49
  return this.grid.grid.getByText(text);
50
50
  }
51
51
 
52
- async setFocusedCellValue(text: string, commitKey: string) {
52
+ async setFocusedCellValue(text: string, commitKey: string): Promise<void> {
53
53
  const mode = await this.grid.mode();
54
54
  if (mode === 'browse') {
55
55
  await this.commit('Enter');
@@ -58,7 +58,7 @@ export class SheetManager {
58
58
  await this.commit(commitKey);
59
59
  }
60
60
 
61
- async selectRange(start: DxGridPosition, end: DxGridPosition) {
61
+ async selectRange(start: DxGridPosition, end: DxGridPosition): Promise<void> {
62
62
  const startCell = this.grid.cell(start.col, start.row, start.plane);
63
63
  const endCell = this.grid.cell(end.col, end.row, end.plane);
64
64
  const startBox = await startCell.boundingBox();
@@ -69,7 +69,7 @@ export class SheetManager {
69
69
  });
70
70
  }
71
71
 
72
- async deleteAxis(axis: DxGridAxis, position: number) {
72
+ async deleteAxis(axis: DxGridAxis, position: number): Promise<void> {
73
73
  const col = axis === 'row' ? 0 : position;
74
74
  const row = axis === 'row' ? position : 0;
75
75
  const plane = axis === 'row' ? 'frozenColsStart' : 'frozenRowsStart';
@@ -77,15 +77,15 @@ export class SheetManager {
77
77
  await this.page.getByTestId(`grid.${axis}.drop`).click();
78
78
  }
79
79
 
80
- toolbarAction(key: string, value: string) {
80
+ toolbarAction(key: string, value: string): Locator {
81
81
  return this.page.getByTestId(`grid.toolbar.${key}.${value}`);
82
82
  }
83
83
 
84
- cellEditor() {
84
+ cellEditor(): Locator {
85
85
  return this.page.getByTestId('grid.cell-editor').getByRole('textbox');
86
86
  }
87
87
 
88
- rangeInList(a1Coords: string) {
88
+ rangeInList(a1Coords: string): Locator {
89
89
  return this.page.getByTestId('grid.range-list').getByText(a1Coords);
90
90
  }
91
91
  }