@dxos/plugin-sheet 0.6.14-main.7bd9c89 → 0.6.14-staging.3e2eaca
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/{SheetContainer-AKWROARP.mjs → SheetContainer-P3NF5KEI.mjs} +90 -88
- package/dist/lib/browser/SheetContainer-P3NF5KEI.mjs.map +7 -0
- package/dist/lib/browser/{chunk-FGMFOW6U.mjs → chunk-4LKIURJA.mjs} +917 -817
- package/dist/lib/browser/chunk-4LKIURJA.mjs.map +7 -0
- package/dist/lib/browser/{chunk-BWN5DZWZ.mjs → chunk-BVUN7SHF.mjs} +6 -13
- package/dist/lib/browser/chunk-BVUN7SHF.mjs.map +7 -0
- package/dist/lib/browser/{chunk-D3QTX46O.mjs → chunk-RABELMEQ.mjs} +3 -2
- package/dist/lib/browser/{chunk-D3QTX46O.mjs.map → chunk-RABELMEQ.mjs.map} +3 -3
- package/dist/lib/browser/{chunk-GSV5QNLD.mjs → chunk-VMSX6Z4X.mjs} +297 -44
- package/dist/lib/browser/chunk-VMSX6Z4X.mjs.map +7 -0
- package/dist/lib/browser/{graph-M4IQ76QX.mjs → compute-graph-GGWUX644.mjs} +4 -2
- package/dist/lib/browser/index.mjs +91 -21
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/meta.mjs +1 -1
- package/dist/lib/browser/types.mjs +2 -2
- package/dist/lib/node/{SheetContainer-N5IQGEFL.cjs → SheetContainer-MPFKXY26.cjs} +88 -93
- package/dist/lib/node/SheetContainer-MPFKXY26.cjs.map +7 -0
- package/dist/lib/node/{chunk-QIFIGEKV.cjs → chunk-2ZVZI2KJ.cjs} +6 -5
- package/dist/lib/node/{chunk-QIFIGEKV.cjs.map → chunk-2ZVZI2KJ.cjs.map} +3 -3
- package/dist/lib/node/{chunk-NZARD7UP.cjs → chunk-AWKOWDMI.cjs} +10 -17
- package/dist/lib/node/chunk-AWKOWDMI.cjs.map +7 -0
- package/dist/lib/node/{chunk-5XPK2V4A.cjs → chunk-O7XR4R7Y.cjs} +306 -42
- package/dist/lib/node/chunk-O7XR4R7Y.cjs.map +7 -0
- package/dist/lib/node/{chunk-53BMSUIK.cjs → chunk-OO24XJBV.cjs} +853 -767
- package/dist/lib/node/chunk-OO24XJBV.cjs.map +7 -0
- package/dist/lib/node/{graph-Q3N2X26H.cjs → compute-graph-KGWA2QLE.cjs} +21 -19
- package/dist/lib/node/compute-graph-KGWA2QLE.cjs.map +7 -0
- package/dist/lib/node/index.cjs +109 -42
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.cjs +3 -3
- package/dist/lib/node/meta.cjs.map +1 -1
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types.cjs +7 -7
- package/dist/lib/node/types.cjs.map +1 -1
- package/dist/lib/node-esm/{SheetContainer-46PBMF2E.mjs → SheetContainer-22IOAW3B.mjs} +90 -88
- package/dist/lib/node-esm/SheetContainer-22IOAW3B.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-VCYJWE3O.mjs → chunk-BM2Q3FFC.mjs} +3 -2
- package/dist/lib/node-esm/{chunk-VCYJWE3O.mjs.map → chunk-BM2Q3FFC.mjs.map} +3 -3
- package/dist/lib/node-esm/{chunk-T3PRH7QS.mjs → chunk-BW36PM2Y.mjs} +917 -817
- package/dist/lib/node-esm/chunk-BW36PM2Y.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-5WPZCXNS.mjs → chunk-CR4K75EL.mjs} +296 -44
- package/dist/lib/node-esm/chunk-CR4K75EL.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-WFDTY3IC.mjs → chunk-UIBWRHW7.mjs} +6 -13
- package/dist/lib/node-esm/chunk-UIBWRHW7.mjs.map +7 -0
- package/dist/lib/node-esm/{graph-SMPUMOV2.mjs → compute-graph-2SCZT7N5.mjs} +4 -2
- package/dist/lib/node-esm/index.mjs +91 -21
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/meta.mjs +1 -1
- package/dist/lib/node-esm/types.mjs +2 -2
- package/dist/types/src/SheetPlugin.d.ts.map +1 -1
- package/dist/types/src/components/ComputeGraph/ComputeGraphContextProvider.d.ts +3 -1
- package/dist/types/src/components/ComputeGraph/ComputeGraphContextProvider.d.ts.map +1 -1
- package/dist/types/src/components/FunctionEditor/FunctionEditor.d.ts.map +1 -1
- package/dist/types/src/components/GridSheet/GridSheet.d.ts.map +1 -1
- package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts.map +1 -1
- package/dist/types/src/components/GridSheet/util.d.ts +2 -2
- package/dist/types/src/components/GridSheet/util.d.ts.map +1 -1
- package/dist/types/src/components/RangeList/RangeList.d.ts +7 -0
- package/dist/types/src/components/RangeList/RangeList.d.ts.map +1 -0
- package/dist/types/src/components/RangeList/index.d.ts +2 -0
- package/dist/types/src/components/RangeList/index.d.ts.map +1 -0
- package/dist/types/src/components/SheetContainer/SheetContainer.d.ts +6 -3
- package/dist/types/src/components/SheetContainer/SheetContainer.d.ts.map +1 -1
- package/dist/types/src/components/SheetContainer/SheetContainer.stories.d.ts.map +1 -1
- package/dist/types/src/components/SheetContext/SheetContext.d.ts +7 -5
- package/dist/types/src/components/SheetContext/SheetContext.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +6 -3
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +5 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/compute-graph/compute-graph-registry.d.ts.map +1 -0
- package/dist/types/src/{graph → compute-graph}/compute-graph.d.ts +3 -3
- package/dist/types/src/compute-graph/compute-graph.d.ts.map +1 -0
- package/dist/types/src/compute-graph/compute-graph.stories.d.ts.map +1 -0
- package/dist/types/src/compute-graph/compute-graph.test.d.ts.map +1 -0
- package/dist/types/src/compute-graph/compute-node.d.ts.map +1 -0
- package/dist/types/src/compute-graph/functions/async-function.d.ts.map +1 -0
- package/dist/types/src/compute-graph/functions/edge-function.d.ts.map +1 -0
- package/dist/types/src/compute-graph/functions/function-defs.d.ts.map +1 -0
- package/dist/types/src/compute-graph/functions/index.d.ts.map +1 -0
- package/dist/types/src/compute-graph/hyperformula.test.d.ts.map +1 -0
- package/dist/types/src/compute-graph/index.d.ts.map +1 -0
- package/dist/types/src/compute-graph/testing/index.d.ts.map +1 -0
- package/dist/types/src/compute-graph/testing/test-builder.d.ts.map +1 -0
- package/dist/types/src/compute-graph/testing/test-plugin.d.ts.map +1 -0
- package/dist/types/src/compute-graph/util.d.ts.map +1 -0
- package/dist/types/src/defs/index.d.ts +1 -1
- package/dist/types/src/defs/index.d.ts.map +1 -1
- package/dist/types/src/defs/sheet-range-types.d.ts +2 -2
- package/dist/types/src/defs/sheet-range-types.d.ts.map +1 -1
- package/dist/types/src/defs/types.d.ts +6 -0
- package/dist/types/src/defs/types.d.ts.map +1 -1
- package/dist/types/src/defs/util.d.ts +3 -7
- package/dist/types/src/defs/util.d.ts.map +1 -1
- package/dist/types/src/extensions/compute.d.ts +1 -1
- package/dist/types/src/extensions/compute.d.ts.map +1 -1
- package/dist/types/src/extensions/editor/extension.d.ts +23 -3
- package/dist/types/src/extensions/editor/extension.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/integrations/index.d.ts +2 -0
- package/dist/types/src/integrations/index.d.ts.map +1 -0
- package/dist/types/src/integrations/thread-ranges.d.ts +7 -0
- package/dist/types/src/integrations/thread-ranges.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +1 -0
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/model/index.d.ts +1 -1
- package/dist/types/src/model/index.d.ts.map +1 -1
- package/dist/types/src/model/sheet-model.d.ts +13 -6
- package/dist/types/src/model/sheet-model.d.ts.map +1 -1
- package/dist/types/src/{hooks → model}/useSheetModel.d.ts +1 -1
- package/dist/types/src/model/useSheetModel.d.ts.map +1 -0
- package/dist/types/src/serializer.d.ts +4 -0
- package/dist/types/src/serializer.d.ts.map +1 -0
- package/dist/types/src/testing/testing.d.ts +1 -1
- package/dist/types/src/testing/testing.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +18 -3
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +37 -33
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +44 -50
- package/src/SheetPlugin.tsx +45 -12
- package/src/components/ComputeGraph/ComputeGraphContextProvider.tsx +19 -2
- package/src/components/FunctionEditor/FunctionEditor.tsx +6 -10
- package/src/components/GridSheet/GridSheet.stories.tsx +3 -1
- package/src/components/GridSheet/GridSheet.tsx +196 -36
- package/src/components/GridSheet/SheetCellEditor.stories.tsx +1 -1
- package/src/components/GridSheet/util.ts +41 -21
- package/src/components/RangeList/RangeList.tsx +53 -0
- package/src/components/RangeList/index.ts +5 -0
- package/src/components/SheetContainer/SheetContainer.stories.tsx +5 -3
- package/src/components/SheetContainer/SheetContainer.tsx +19 -12
- package/src/components/SheetContext/SheetContext.tsx +43 -20
- package/src/components/Toolbar/Toolbar.tsx +98 -86
- package/src/components/index.ts +1 -0
- package/src/{graph → compute-graph}/compute-graph.stories.tsx +2 -1
- package/src/{graph → compute-graph}/compute-graph.ts +27 -9
- package/src/{graph → compute-graph}/compute-node.ts +2 -3
- package/src/{graph → compute-graph}/functions/async-function.ts +1 -0
- package/src/{graph → compute-graph}/functions/edge-function.ts +5 -3
- package/src/defs/index.ts +1 -1
- package/src/defs/sheet-range-types.ts +9 -6
- package/src/defs/types.ts +6 -1
- package/src/defs/util.ts +4 -19
- package/src/extensions/compute.stories.tsx +2 -2
- package/src/extensions/compute.ts +2 -2
- package/src/extensions/editor/extension.test.ts +1 -1
- package/src/extensions/editor/extension.ts +48 -23
- package/src/index.ts +2 -2
- package/src/integrations/index.ts +5 -0
- package/src/integrations/thread-ranges.ts +101 -0
- package/src/meta.ts +1 -0
- package/src/model/index.ts +1 -1
- package/src/model/sheet-model.test.ts +6 -4
- package/src/model/sheet-model.ts +103 -26
- package/src/{hooks → model}/useSheetModel.ts +1 -1
- package/src/serializer.ts +27 -0
- package/src/testing/testing.tsx +1 -1
- package/src/translations.ts +18 -3
- package/src/types.ts +24 -2
- package/dist/lib/browser/SheetContainer-AKWROARP.mjs.map +0 -7
- package/dist/lib/browser/chunk-BWN5DZWZ.mjs.map +0 -7
- package/dist/lib/browser/chunk-FGMFOW6U.mjs.map +0 -7
- package/dist/lib/browser/chunk-GSV5QNLD.mjs.map +0 -7
- package/dist/lib/node/SheetContainer-N5IQGEFL.cjs.map +0 -7
- package/dist/lib/node/chunk-53BMSUIK.cjs.map +0 -7
- package/dist/lib/node/chunk-5XPK2V4A.cjs.map +0 -7
- package/dist/lib/node/chunk-NZARD7UP.cjs.map +0 -7
- package/dist/lib/node/graph-Q3N2X26H.cjs.map +0 -7
- package/dist/lib/node-esm/SheetContainer-46PBMF2E.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-5WPZCXNS.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-T3PRH7QS.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-WFDTY3IC.mjs.map +0 -7
- package/dist/types/src/graph/compute-graph-registry.d.ts.map +0 -1
- package/dist/types/src/graph/compute-graph.d.ts.map +0 -1
- package/dist/types/src/graph/compute-graph.stories.d.ts.map +0 -1
- package/dist/types/src/graph/compute-graph.test.d.ts.map +0 -1
- package/dist/types/src/graph/compute-node.d.ts.map +0 -1
- package/dist/types/src/graph/functions/async-function.d.ts.map +0 -1
- package/dist/types/src/graph/functions/edge-function.d.ts.map +0 -1
- package/dist/types/src/graph/functions/function-defs.d.ts.map +0 -1
- package/dist/types/src/graph/functions/index.d.ts.map +0 -1
- package/dist/types/src/graph/hyperformula.test.d.ts.map +0 -1
- package/dist/types/src/graph/index.d.ts.map +0 -1
- package/dist/types/src/graph/testing/index.d.ts.map +0 -1
- package/dist/types/src/graph/testing/test-builder.d.ts.map +0 -1
- package/dist/types/src/graph/testing/test-plugin.d.ts.map +0 -1
- package/dist/types/src/graph/util.d.ts.map +0 -1
- package/dist/types/src/hooks/hooks.stories.d.ts +0 -6
- package/dist/types/src/hooks/hooks.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/index.d.ts +0 -4
- package/dist/types/src/hooks/index.d.ts.map +0 -1
- package/dist/types/src/hooks/threads.d.ts +0 -8
- package/dist/types/src/hooks/threads.d.ts.map +0 -1
- package/dist/types/src/hooks/useComputeGraph.d.ts +0 -7
- package/dist/types/src/hooks/useComputeGraph.d.ts.map +0 -1
- package/dist/types/src/hooks/useSheetModel.d.ts.map +0 -1
- package/dist/types/src/model/decorations.d.ts +0 -26
- package/dist/types/src/model/decorations.d.ts.map +0 -1
- package/src/hooks/hooks.stories.tsx +0 -53
- package/src/hooks/index.ts +0 -7
- package/src/hooks/threads.ts +0 -147
- package/src/hooks/useComputeGraph.ts +0 -28
- package/src/model/decorations.ts +0 -66
- /package/dist/lib/browser/{graph-M4IQ76QX.mjs.map → compute-graph-GGWUX644.mjs.map} +0 -0
- /package/dist/lib/node-esm/{graph-SMPUMOV2.mjs.map → compute-graph-2SCZT7N5.mjs.map} +0 -0
- /package/dist/types/src/{graph → compute-graph}/compute-graph-registry.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/compute-graph.stories.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/compute-graph.test.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/compute-node.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/functions/async-function.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/functions/edge-function.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/functions/function-defs.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/functions/index.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/hyperformula.test.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/index.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/testing/index.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/testing/test-builder.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/testing/test-plugin.d.ts +0 -0
- /package/dist/types/src/{graph → compute-graph}/util.d.ts +0 -0
- /package/src/{graph → compute-graph}/compute-graph-registry.ts +0 -0
- /package/src/{graph → compute-graph}/compute-graph.test.ts +0 -0
- /package/src/{graph → compute-graph}/functions/function-defs.ts +0 -0
- /package/src/{graph → compute-graph}/functions/index.ts +0 -0
- /package/src/{graph → compute-graph}/hyperformula.test.ts +0 -0
- /package/src/{graph → compute-graph}/index.ts +0 -0
- /package/src/{graph → compute-graph}/testing/index.ts +0 -0
- /package/src/{graph → compute-graph}/testing/test-builder.ts +0 -0
- /package/src/{graph → compute-graph}/testing/test-plugin.ts +0 -0
- /package/src/{graph → compute-graph}/util.ts +0 -0
package/src/defs/types.ts
CHANGED
|
@@ -13,11 +13,16 @@ export const MAX_COLUMNS = 26 * 2;
|
|
|
13
13
|
export type CellAddress = { col: number; row: number };
|
|
14
14
|
|
|
15
15
|
export type CellRange = { from: CellAddress; to?: CellAddress };
|
|
16
|
+
export type CompleteCellRange = { from: CellAddress; to: CellAddress };
|
|
16
17
|
|
|
17
18
|
export type CellIndex = string;
|
|
18
19
|
|
|
19
20
|
export type CellContentValue = number | string | boolean | null;
|
|
20
21
|
|
|
22
|
+
export const RANGE_NOTATION = /^[A-Z]+[0-9]+(:[A-Z]+[0-9]+)?$/;
|
|
23
|
+
|
|
24
|
+
export const isFormula = (value: any): value is string => typeof value === 'string' && value.charAt(0) === '=';
|
|
25
|
+
|
|
21
26
|
export const posEquals = (a: CellAddress | undefined, b: CellAddress | undefined) => {
|
|
22
27
|
return a?.col === b?.col && a?.row === b?.row;
|
|
23
28
|
};
|
|
@@ -34,7 +39,7 @@ export const addressToA1Notation = ({ col, row }: CellAddress): string => {
|
|
|
34
39
|
return `${columnLetter(col)}${row + 1}`;
|
|
35
40
|
};
|
|
36
41
|
|
|
37
|
-
// TODO(burdon): See simpleCellAddressFromString
|
|
42
|
+
// TODO(burdon): See (HF) simpleCellAddressFromString.
|
|
38
43
|
export const addressFromA1Notation = (ref: string): CellAddress => {
|
|
39
44
|
const match = ref.match(/([A-Z]+)(\d+)/);
|
|
40
45
|
invariant(match, `Invalid notation: ${ref}`);
|
package/src/defs/util.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
addressFromA1Notation,
|
|
10
10
|
type CellAddress,
|
|
11
11
|
type CellRange,
|
|
12
|
+
type CompleteCellRange,
|
|
12
13
|
DEFAULT_COLUMNS,
|
|
13
14
|
DEFAULT_ROWS,
|
|
14
15
|
MAX_COLUMNS,
|
|
@@ -46,6 +47,7 @@ export const insertIndices = (indices: string[], i: number, n: number, max: numb
|
|
|
46
47
|
|
|
47
48
|
const idx = createIndices(n);
|
|
48
49
|
indices.splice(i, 0, ...idx);
|
|
50
|
+
return idx;
|
|
49
51
|
};
|
|
50
52
|
|
|
51
53
|
export const initialize = (
|
|
@@ -69,6 +71,7 @@ export const createSheet = ({ name, cells, ...size }: CreateSheetOptions = {}):
|
|
|
69
71
|
rowMeta: {},
|
|
70
72
|
columnMeta: {},
|
|
71
73
|
ranges: [],
|
|
74
|
+
threads: [],
|
|
72
75
|
});
|
|
73
76
|
|
|
74
77
|
initialize(sheet, size);
|
|
@@ -111,29 +114,11 @@ export const rangeToIndex = (sheet: SheetType, range: CellRange): string => {
|
|
|
111
114
|
/**
|
|
112
115
|
* E.g., "CA2@CB3:CC4@CD5" => "A1:B2".
|
|
113
116
|
*/
|
|
114
|
-
export const rangeFromIndex = (sheet: SheetType, idx: string):
|
|
117
|
+
export const rangeFromIndex = (sheet: SheetType, idx: string): CompleteCellRange => {
|
|
115
118
|
const [from, to] = idx.split(':').map((index) => addressFromIndex(sheet, index));
|
|
116
119
|
return { from, to };
|
|
117
120
|
};
|
|
118
121
|
|
|
119
|
-
/**
|
|
120
|
-
* Find closest cell to cursor.
|
|
121
|
-
*/
|
|
122
|
-
export const closest = (cursor: CellAddress, cells: CellAddress[]): CellAddress | undefined => {
|
|
123
|
-
let closestCell: CellAddress | undefined;
|
|
124
|
-
let closestDistance = Number.MAX_SAFE_INTEGER;
|
|
125
|
-
|
|
126
|
-
for (const cell of cells) {
|
|
127
|
-
const distance = Math.abs(cell.row - cursor.row) + Math.abs(cell.col - cursor.col);
|
|
128
|
-
if (distance < closestDistance) {
|
|
129
|
-
closestCell = cell;
|
|
130
|
-
closestDistance = distance;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return closestCell;
|
|
135
|
-
};
|
|
136
|
-
|
|
137
122
|
/**
|
|
138
123
|
* Compares the positions of two cell indexes in a sheet.
|
|
139
124
|
* Sorts primarily by row, then by column if rows are equal.
|
|
@@ -23,8 +23,8 @@ import { withTheme, withLayout } from '@dxos/storybook-utils';
|
|
|
23
23
|
import { nonNullable } from '@dxos/util';
|
|
24
24
|
|
|
25
25
|
import { compute, computeGraphFacet } from './compute';
|
|
26
|
-
import { GridSheet, SheetProvider } from '../components';
|
|
27
|
-
import {
|
|
26
|
+
import { GridSheet, SheetProvider, useComputeGraph } from '../components';
|
|
27
|
+
import { useSheetModel } from '../model';
|
|
28
28
|
import { useTestSheet, withComputeGraphDecorator } from '../testing';
|
|
29
29
|
import { SheetType } from '../types';
|
|
30
30
|
|
|
@@ -16,10 +16,10 @@ import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view
|
|
|
16
16
|
|
|
17
17
|
import { type UnsubscribeCallback, debounce } from '@dxos/async';
|
|
18
18
|
import { invariant } from '@dxos/invariant';
|
|
19
|
-
import { documentId, singleValueFacet } from '@dxos/react-ui-editor
|
|
19
|
+
import { documentId, singleValueFacet } from '@dxos/react-ui-editor';
|
|
20
20
|
|
|
21
|
+
import { type ComputeGraph, type ComputeNode, createSheetName } from '../compute-graph';
|
|
21
22
|
import { type CellAddress } from '../defs';
|
|
22
|
-
import { type ComputeGraph, type ComputeNode, createSheetName } from '../graph';
|
|
23
23
|
import { type CellScalarValue } from '../types';
|
|
24
24
|
|
|
25
25
|
const LANGUAGE_TAG = 'dx';
|
|
@@ -9,7 +9,7 @@ import { spreadsheet } from 'codemirror-lang-spreadsheet';
|
|
|
9
9
|
import { describe, expect, test } from 'vitest';
|
|
10
10
|
|
|
11
11
|
import { sheetExtension } from './extension';
|
|
12
|
-
import { defaultFunctions } from '../../graph';
|
|
12
|
+
import { defaultFunctions } from '../../compute-graph';
|
|
13
13
|
|
|
14
14
|
describe('formula parser', () => {
|
|
15
15
|
const {
|
|
@@ -18,10 +18,11 @@ import { type SyntaxNode } from '@lezer/common';
|
|
|
18
18
|
import { tags } from '@lezer/highlight';
|
|
19
19
|
import { spreadsheet } from 'codemirror-lang-spreadsheet';
|
|
20
20
|
|
|
21
|
-
import { singleValueFacet } from '@dxos/react-ui-editor
|
|
21
|
+
import { singleValueFacet } from '@dxos/react-ui-editor';
|
|
22
22
|
import { mx } from '@dxos/react-ui-theme';
|
|
23
23
|
|
|
24
|
-
import { type FunctionDefinition } from '../../graph';
|
|
24
|
+
import { type FunctionDefinition } from '../../compute-graph';
|
|
25
|
+
import { RANGE_NOTATION } from '../../defs';
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* https://codemirror.net/examples/styling
|
|
@@ -195,34 +196,53 @@ export const sheetExtension = ({ functions = [] }: SheetExtensionOptions): Exten
|
|
|
195
196
|
];
|
|
196
197
|
};
|
|
197
198
|
|
|
198
|
-
export type
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
export type SelectionRange = { from: number; to: number };
|
|
200
|
+
|
|
201
|
+
export interface RangeController {
|
|
202
|
+
setRange(range: string): void;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export type RangeExtensionOptions = {
|
|
206
|
+
/**
|
|
207
|
+
* Provides controller callback when extension is initialized.
|
|
208
|
+
*/
|
|
209
|
+
onInit?: (controller: RangeController) => void;
|
|
210
|
+
/**
|
|
211
|
+
* Called when the active range changes.
|
|
212
|
+
* @param state The current state.
|
|
213
|
+
* @param state.activeRange undefined if no range is active, otherwise a possibly partially defined range.
|
|
214
|
+
*/
|
|
215
|
+
onStateChange?: (state: { activeRange: string | undefined }) => void;
|
|
216
|
+
};
|
|
201
217
|
|
|
202
218
|
/**
|
|
203
219
|
* Tracks the currently active cell within a formula and provides a callback to modify it.
|
|
204
220
|
*/
|
|
205
|
-
export const rangeExtension = (onInit
|
|
221
|
+
export const rangeExtension = ({ onInit, onStateChange }: RangeExtensionOptions): Extension => {
|
|
206
222
|
let view: EditorView;
|
|
207
|
-
let activeRange:
|
|
208
|
-
const provider: CellRangeNotifier = (range: string) => {
|
|
209
|
-
if (activeRange) {
|
|
210
|
-
view.dispatch(
|
|
211
|
-
view.state.update({
|
|
212
|
-
changes: { ...activeRange, insert: range.toString() },
|
|
213
|
-
selection: { anchor: activeRange.from + range.length },
|
|
214
|
-
}),
|
|
215
|
-
);
|
|
216
|
-
}
|
|
223
|
+
let activeRange: SelectionRange | undefined;
|
|
217
224
|
|
|
218
|
-
|
|
225
|
+
// Called externally to provide current range.
|
|
226
|
+
const notifier: RangeController = {
|
|
227
|
+
setRange: (range: string) => {
|
|
228
|
+
if (activeRange) {
|
|
229
|
+
view.dispatch(
|
|
230
|
+
view.state.update({
|
|
231
|
+
changes: { ...activeRange, insert: range.toString() },
|
|
232
|
+
selection: { anchor: activeRange.from + range.length },
|
|
233
|
+
}),
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
view.focus();
|
|
238
|
+
},
|
|
219
239
|
};
|
|
220
240
|
|
|
221
241
|
return ViewPlugin.fromClass(
|
|
222
242
|
class {
|
|
223
243
|
constructor(_view: EditorView) {
|
|
224
244
|
view = _view;
|
|
225
|
-
onInit(
|
|
245
|
+
onInit?.(notifier);
|
|
226
246
|
}
|
|
227
247
|
|
|
228
248
|
update(view: ViewUpdate) {
|
|
@@ -235,17 +255,15 @@ export const rangeExtension = (onInit: (notifier: CellRangeNotifier) => void): E
|
|
|
235
255
|
visitTree(topNode, ({ type, from, to }) => {
|
|
236
256
|
if (from <= anchor && to >= anchor) {
|
|
237
257
|
switch (type.name) {
|
|
238
|
-
case 'Function':
|
|
258
|
+
case 'Function':
|
|
239
259
|
// Mark but keep looking.
|
|
240
260
|
activeRange = { from: to, to };
|
|
241
261
|
break;
|
|
242
|
-
}
|
|
243
262
|
|
|
244
|
-
case 'CloseParen':
|
|
263
|
+
case 'CloseParen':
|
|
245
264
|
// Mark but keep looking.
|
|
246
265
|
activeRange = { from, to: from };
|
|
247
266
|
break;
|
|
248
|
-
}
|
|
249
267
|
|
|
250
268
|
case 'RangeToken':
|
|
251
269
|
case 'CellToken':
|
|
@@ -259,8 +277,15 @@ export const rangeExtension = (onInit: (notifier: CellRangeNotifier) => void): E
|
|
|
259
277
|
|
|
260
278
|
// Allow start of formula.
|
|
261
279
|
if (!activeRange && view.state.doc.toString()[0] === '=') {
|
|
262
|
-
|
|
280
|
+
const str = view.state.doc.sliceString(1);
|
|
281
|
+
if (RANGE_NOTATION.test(str)) {
|
|
282
|
+
activeRange = { from: 1, to: str.length + 1 };
|
|
283
|
+
}
|
|
263
284
|
}
|
|
285
|
+
|
|
286
|
+
onStateChange?.({
|
|
287
|
+
activeRange: activeRange ? view.state.doc.sliceString(activeRange.from, activeRange.to) : undefined,
|
|
288
|
+
});
|
|
264
289
|
}
|
|
265
290
|
},
|
|
266
291
|
);
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useCallback, useEffect, useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type IntentResolver, LayoutAction, useIntentDispatcher, useIntentResolver } from '@dxos/app-framework';
|
|
8
|
+
import { debounce } from '@dxos/async';
|
|
9
|
+
import { fullyQualifiedId } from '@dxos/react-client/echo';
|
|
10
|
+
import { type DxGridElement, type DxGridPosition } from '@dxos/react-ui-grid';
|
|
11
|
+
|
|
12
|
+
import { useSheetContext } from '../components';
|
|
13
|
+
import { type CellAddress, type CompleteCellRange, inRange } from '../defs';
|
|
14
|
+
import { SHEET_PLUGIN } from '../meta';
|
|
15
|
+
|
|
16
|
+
export const completeCellRangeToThreadCursor = (range: CompleteCellRange): string => {
|
|
17
|
+
return `${range.from.col},${range.from.row},${range.to.col},${range.to.row}`;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const parseThreadAnchorAsCellRange = (cursor: string): CompleteCellRange | null => {
|
|
21
|
+
const coords = cursor.split(',');
|
|
22
|
+
if (coords.length !== 4) {
|
|
23
|
+
return null;
|
|
24
|
+
} else {
|
|
25
|
+
const [fromCol, fromRow, toCol, toRow] = coords;
|
|
26
|
+
return {
|
|
27
|
+
from: { col: parseInt(fromCol), row: parseInt(fromRow) },
|
|
28
|
+
to: { col: parseInt(toCol), row: parseInt(toRow) },
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const useUpdateFocusedCellOnThreadSelection = (grid: DxGridElement | null) => {
|
|
34
|
+
const { model, setActiveRefs } = useSheetContext();
|
|
35
|
+
const handleScrollIntoView: IntentResolver = useCallback(
|
|
36
|
+
({ action, data }) => {
|
|
37
|
+
switch (action) {
|
|
38
|
+
case LayoutAction.SCROLL_INTO_VIEW: {
|
|
39
|
+
if (!data?.id || data?.cursor === undefined || data?.id !== fullyQualifiedId(model.sheet)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
setActiveRefs(data.thread);
|
|
43
|
+
// TODO(Zan): Everywhere we refer to the cursor in a thread context should change to `anchor`.
|
|
44
|
+
const range = parseThreadAnchorAsCellRange(data.cursor);
|
|
45
|
+
range && grid?.setFocus({ ...range.to, plane: 'grid' }, true);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
[model.sheet, setActiveRefs],
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
useIntentResolver(SHEET_PLUGIN, handleScrollIntoView);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const useSelectThreadOnCellFocus = () => {
|
|
56
|
+
const { model, cursor } = useSheetContext();
|
|
57
|
+
const dispatch = useIntentDispatcher();
|
|
58
|
+
|
|
59
|
+
const threads = useMemo(
|
|
60
|
+
() => model.sheet.threads?.filter((thread): thread is NonNullable<typeof thread> => !!thread) ?? [],
|
|
61
|
+
[
|
|
62
|
+
// TODO(thure): Surely we can find a better dependency for this…
|
|
63
|
+
JSON.stringify(model.sheet.threads),
|
|
64
|
+
],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const selectClosestThread = useCallback(
|
|
68
|
+
(cellAddress: CellAddress) => {
|
|
69
|
+
if (!cellAddress || !threads) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const closestThread = threads?.find(({ anchor }) => {
|
|
74
|
+
if (anchor) {
|
|
75
|
+
const range = parseThreadAnchorAsCellRange(anchor);
|
|
76
|
+
return range ? inRange(range, cellAddress) : false;
|
|
77
|
+
} else {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (closestThread) {
|
|
83
|
+
void dispatch([
|
|
84
|
+
{ action: 'dxos.org/plugin/thread/action/select', data: { current: fullyQualifiedId(closestThread) } },
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
[dispatch, threads],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const debounced = useMemo(() => {
|
|
92
|
+
return debounce((cellCoords: DxGridPosition) => requestAnimationFrame(() => selectClosestThread(cellCoords)), 50);
|
|
93
|
+
}, [selectClosestThread]);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!cursor) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
debounced(cursor);
|
|
100
|
+
}, [cursor, debounced]);
|
|
101
|
+
};
|
package/src/meta.ts
CHANGED
package/src/model/index.ts
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, onTestFinished, test } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { Trigger } from '@dxos/async';
|
|
8
8
|
import { FunctionType } from '@dxos/plugin-script/types';
|
|
9
9
|
|
|
10
10
|
import { SheetModel } from './sheet-model';
|
|
11
|
+
import { TestBuilder, testFunctionPlugins } from '../compute-graph/testing';
|
|
11
12
|
import { addressFromA1Notation, createSheet } from '../defs';
|
|
12
|
-
import { TestBuilder, testFunctionPlugins } from '../graph/testing';
|
|
13
13
|
import { type CellScalarValue } from '../types';
|
|
14
14
|
|
|
15
15
|
describe('SheetModel', () => {
|
|
@@ -36,13 +36,15 @@ describe('SheetModel', () => {
|
|
|
36
36
|
// Trigger waits for function invocation.
|
|
37
37
|
const trigger = new Trigger<CellScalarValue>();
|
|
38
38
|
model.setValue(addressFromA1Notation('A1'), '=TEST(100)');
|
|
39
|
-
|
|
39
|
+
// TODO(wittjosiah): Currently this fires twice, once for the binding loading & once for the function invocation.
|
|
40
|
+
const unsubscribe = model.update.on((update) => {
|
|
40
41
|
const { type } = update;
|
|
41
42
|
if (type === 'valuesUpdated') {
|
|
42
43
|
const value = model.getValue(addressFromA1Notation('A1'));
|
|
43
|
-
trigger.wake(value);
|
|
44
|
+
value && trigger.wake(value);
|
|
44
45
|
}
|
|
45
46
|
});
|
|
47
|
+
onTestFinished(() => unsubscribe());
|
|
46
48
|
|
|
47
49
|
// Initial value will be null.
|
|
48
50
|
const v1 = model.getValue(addressFromA1Notation('A1'));
|
package/src/model/sheet-model.ts
CHANGED
|
@@ -8,34 +8,41 @@ import { type SimpleDate, type SimpleDateTime } from 'hyperformula/typings/DateT
|
|
|
8
8
|
|
|
9
9
|
import { Event } from '@dxos/async';
|
|
10
10
|
import { Resource } from '@dxos/context';
|
|
11
|
-
import { getTypename } from '@dxos/echo-schema';
|
|
11
|
+
import { getTypename, FormatEnum, TypeEnum } from '@dxos/echo-schema';
|
|
12
12
|
import { invariant } from '@dxos/invariant';
|
|
13
13
|
import { PublicKey } from '@dxos/keys';
|
|
14
14
|
import { log } from '@dxos/log';
|
|
15
|
-
import { FieldValueType } from '@dxos/schema';
|
|
16
15
|
|
|
17
16
|
import { DetailedCellError, ExportedCellChange } from '#hyperformula';
|
|
17
|
+
import { type ComputeGraph, type ComputeNode, type ComputeNodeEvent, createSheetName } from '../compute-graph';
|
|
18
18
|
import {
|
|
19
19
|
addressFromA1Notation,
|
|
20
|
+
addressFromIndex,
|
|
20
21
|
addressToA1Notation,
|
|
22
|
+
addressToIndex,
|
|
23
|
+
initialize,
|
|
24
|
+
insertIndices,
|
|
25
|
+
isFormula,
|
|
21
26
|
type CellAddress,
|
|
22
27
|
type CellRange,
|
|
28
|
+
ReadonlyException,
|
|
23
29
|
MAX_COLUMNS,
|
|
24
30
|
MAX_ROWS,
|
|
25
31
|
} from '../defs';
|
|
26
|
-
import {
|
|
27
|
-
import { type ComputeNode, type ComputeGraph, createSheetName, type ComputeNodeEvent } from '../graph';
|
|
28
|
-
import { type CellScalarValue, type CellValue, type SheetType } from '../types';
|
|
32
|
+
import { type CellScalarValue, type CellValue, type SheetType, type RestoreAxis } from '../types';
|
|
29
33
|
|
|
30
34
|
// Map sheet types to system types.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
// https://hyperformula.handsontable.com/guide/types-of-values.html
|
|
36
|
+
// - https://github.com/handsontable/hyperformula/blob/master/src/Cell.ts (CellValueType)
|
|
37
|
+
// - https://github.com/handsontable/hyperformula/blob/master/src/interpreter/InterpreterValue.ts (NumberType)
|
|
38
|
+
const typeMap: Record<string, { type: TypeEnum; format?: FormatEnum }> = {
|
|
39
|
+
BOOLEAN: { type: TypeEnum.Boolean },
|
|
40
|
+
NUMBER_RAW: { type: TypeEnum.Number },
|
|
41
|
+
NUMBER_PERCENT: { type: TypeEnum.Number, format: FormatEnum.Percent },
|
|
42
|
+
NUMBER_CURRENCY: { type: TypeEnum.Number, format: FormatEnum.Currency },
|
|
43
|
+
NUMBER_DATETIME: { type: TypeEnum.String, format: FormatEnum.DateTime },
|
|
44
|
+
NUMBER_DATE: { type: TypeEnum.String, format: FormatEnum.Date },
|
|
45
|
+
NUMBER_TIME: { type: TypeEnum.String, format: FormatEnum.Time },
|
|
39
46
|
};
|
|
40
47
|
|
|
41
48
|
const getTopLeft = (range: CellRange): CellAddress => {
|
|
@@ -106,6 +113,12 @@ export class SheetModel extends Resource {
|
|
|
106
113
|
log('initialize', { id: this.id });
|
|
107
114
|
initialize(this._sheet);
|
|
108
115
|
|
|
116
|
+
this._graph.update.on((event) => {
|
|
117
|
+
if (event.type === 'functionsUpdated') {
|
|
118
|
+
this.reset();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
109
122
|
// TODO(burdon): SheetModel should extend ComputeNode and be constructed via the graph.
|
|
110
123
|
this._node = this._graph.getOrCreateNode(createSheetName({ type: getTypename(this._sheet)!, id: this._sheet.id }));
|
|
111
124
|
await this._node.open();
|
|
@@ -128,10 +141,15 @@ export class SheetModel extends Resource {
|
|
|
128
141
|
Object.entries(this._sheet.cells).forEach(([key, { value }]) => {
|
|
129
142
|
invariant(this._node);
|
|
130
143
|
const { col, row } = addressFromIndex(this._sheet, key);
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
144
|
+
if (isFormula(value)) {
|
|
145
|
+
const binding = this._graph.mapFunctionBindingFromId(this.mapFormulaIndicesToRefs(value));
|
|
146
|
+
if (binding) {
|
|
147
|
+
value = this._graph.mapFormulaToNative(binding);
|
|
148
|
+
} else {
|
|
149
|
+
// If binding is not found, render the cell as empty.
|
|
150
|
+
// This prevents the cell from momentarily rendering an error while the binding is being loaded.
|
|
151
|
+
value = '';
|
|
152
|
+
}
|
|
135
153
|
}
|
|
136
154
|
|
|
137
155
|
this._node.graph.hf.setCellContents({ sheet: this._node.sheetId, row, col }, value);
|
|
@@ -150,12 +168,68 @@ export class SheetModel extends Resource {
|
|
|
150
168
|
}
|
|
151
169
|
|
|
152
170
|
insertRows(i: number, n = 1) {
|
|
153
|
-
insertIndices(this._sheet.rows, i, n, MAX_ROWS);
|
|
171
|
+
const idx = insertIndices(this._sheet.rows, i, n, MAX_ROWS);
|
|
154
172
|
this.reset();
|
|
173
|
+
return idx;
|
|
155
174
|
}
|
|
156
175
|
|
|
157
176
|
insertColumns(i: number, n = 1) {
|
|
158
|
-
insertIndices(this._sheet.columns, i, n, MAX_COLUMNS);
|
|
177
|
+
const idx = insertIndices(this._sheet.columns, i, n, MAX_COLUMNS);
|
|
178
|
+
this.reset();
|
|
179
|
+
return idx;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
dropRow(rowIndex: string): RestoreAxis {
|
|
183
|
+
const range = {
|
|
184
|
+
from: addressFromIndex(this._sheet, `${this._sheet.columns[0]}@${rowIndex}`),
|
|
185
|
+
to: addressFromIndex(this._sheet, `${this._sheet.columns[this._sheet.columns.length - 1]}@${rowIndex}`),
|
|
186
|
+
};
|
|
187
|
+
const values = this.getCellValues(range).flat();
|
|
188
|
+
const index = this._sheet.rows.indexOf(rowIndex);
|
|
189
|
+
this.clear(range);
|
|
190
|
+
this._sheet.rows.splice(index, 1);
|
|
191
|
+
delete this._sheet.rowMeta[rowIndex];
|
|
192
|
+
this.reset();
|
|
193
|
+
return { axis: 'row', index, axisIndex: rowIndex, axisMeta: this._sheet.rowMeta[rowIndex], values };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
dropColumn(colIndex: string): RestoreAxis {
|
|
197
|
+
const range = {
|
|
198
|
+
from: addressFromIndex(this._sheet, `${colIndex}@${this._sheet.rows[0]}`),
|
|
199
|
+
to: addressFromIndex(this._sheet, `${colIndex}@${this._sheet.rows[this._sheet.rows.length - 1]}`),
|
|
200
|
+
};
|
|
201
|
+
const values = this.getCellValues(range).flat();
|
|
202
|
+
const index = this._sheet.columns.indexOf(colIndex);
|
|
203
|
+
this.clear(range);
|
|
204
|
+
this._sheet.columns.splice(index, 1);
|
|
205
|
+
delete this._sheet.columnMeta[colIndex];
|
|
206
|
+
this.reset();
|
|
207
|
+
return { axis: 'col', index, axisIndex: colIndex, axisMeta: this._sheet.rowMeta[colIndex], values };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
restoreRow({ index, axisIndex, axisMeta, values }: RestoreAxis) {
|
|
211
|
+
this._sheet.rows.splice(index, 0, axisIndex);
|
|
212
|
+
values.forEach((value, col) => {
|
|
213
|
+
if (value) {
|
|
214
|
+
this._sheet.cells[`${this._sheet.columns[col]}@${axisIndex}`] = { value };
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
if (axisMeta) {
|
|
218
|
+
this._sheet.rowMeta[axisIndex] = axisMeta;
|
|
219
|
+
}
|
|
220
|
+
this.reset();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
restoreColumn({ index, axisIndex, axisMeta, values }: RestoreAxis) {
|
|
224
|
+
this._sheet.columns.splice(index, 0, axisIndex);
|
|
225
|
+
values.forEach((value, row) => {
|
|
226
|
+
if (value) {
|
|
227
|
+
this._sheet.cells[`${axisIndex}@${this._sheet.rows[row]}`] = { value };
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
if (axisMeta) {
|
|
231
|
+
this._sheet.columnMeta[axisIndex] = axisMeta;
|
|
232
|
+
}
|
|
159
233
|
this.reset();
|
|
160
234
|
}
|
|
161
235
|
|
|
@@ -240,7 +314,7 @@ export class SheetModel extends Resource {
|
|
|
240
314
|
return undefined;
|
|
241
315
|
}
|
|
242
316
|
|
|
243
|
-
if (
|
|
317
|
+
if (isFormula(value)) {
|
|
244
318
|
return this._graph.mapFunctionBindingFromId(this.mapFormulaIndicesToRefs(value));
|
|
245
319
|
} else {
|
|
246
320
|
return String(value);
|
|
@@ -260,8 +334,11 @@ export class SheetModel extends Resource {
|
|
|
260
334
|
getValue(cell: CellAddress): CellScalarValue {
|
|
261
335
|
// Applies rounding and post-processing.
|
|
262
336
|
invariant(this._node);
|
|
263
|
-
const
|
|
337
|
+
const address = toSimpleCellAddress(this._node.sheetId, cell);
|
|
338
|
+
const value = this._node.graph.hf.getCellValue(address);
|
|
264
339
|
if (value instanceof DetailedCellError) {
|
|
340
|
+
// TODO(wittjosiah): Error details should be shown in cell `title`.
|
|
341
|
+
log.info('cell error', { cell, error: value });
|
|
265
342
|
return value.toString();
|
|
266
343
|
}
|
|
267
344
|
|
|
@@ -271,7 +348,7 @@ export class SheetModel extends Resource {
|
|
|
271
348
|
/**
|
|
272
349
|
* Get value type.
|
|
273
350
|
*/
|
|
274
|
-
|
|
351
|
+
getValueDescription(cell: CellAddress): { type: TypeEnum; format?: FormatEnum } | undefined {
|
|
275
352
|
invariant(this._node);
|
|
276
353
|
const addr = toSimpleCellAddress(this._node.sheetId, cell);
|
|
277
354
|
const type = this._node.graph.hf.getCellValueDetailedType(addr);
|
|
@@ -305,7 +382,7 @@ export class SheetModel extends Resource {
|
|
|
305
382
|
|
|
306
383
|
// Insert into engine.
|
|
307
384
|
this._node.graph.hf.setCellContents({ sheet: this._node.sheetId, row: cell.row, col: cell.col }, [
|
|
308
|
-
[
|
|
385
|
+
[isFormula(value) ? this._graph.mapFormulaToNative(value) : value],
|
|
309
386
|
]);
|
|
310
387
|
|
|
311
388
|
// Insert into sheet.
|
|
@@ -313,7 +390,7 @@ export class SheetModel extends Resource {
|
|
|
313
390
|
if (value === undefined || value === null) {
|
|
314
391
|
delete this._sheet.cells[idx];
|
|
315
392
|
} else {
|
|
316
|
-
if (
|
|
393
|
+
if (isFormula(value)) {
|
|
317
394
|
value = this._graph.mapFunctionBindingToId(this.mapFormulaRefsToIndices(value));
|
|
318
395
|
}
|
|
319
396
|
|
|
@@ -371,7 +448,7 @@ export class SheetModel extends Resource {
|
|
|
371
448
|
* Map from A1 notation to indices.
|
|
372
449
|
*/
|
|
373
450
|
mapFormulaRefsToIndices(formula: string): string {
|
|
374
|
-
invariant(formula
|
|
451
|
+
invariant(isFormula(formula));
|
|
375
452
|
return formula.replace(/([a-zA-Z]+)([0-9]+)/g, (match) => {
|
|
376
453
|
return addressToIndex(this._sheet, addressFromA1Notation(match));
|
|
377
454
|
});
|
|
@@ -381,7 +458,7 @@ export class SheetModel extends Resource {
|
|
|
381
458
|
* Map from indices to A1 notation.
|
|
382
459
|
*/
|
|
383
460
|
mapFormulaIndicesToRefs(formula: string): string {
|
|
384
|
-
invariant(formula
|
|
461
|
+
invariant(isFormula(formula));
|
|
385
462
|
return formula.replace(/([a-zA-Z0-9]+)@([a-zA-Z0-9]+)/g, (idx) => {
|
|
386
463
|
return addressToA1Notation(addressFromIndex(this._sheet, idx));
|
|
387
464
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type TypedObjectSerializer } from '@dxos/plugin-space/types';
|
|
6
|
+
import { create, getObjectCore } from '@dxos/react-client/echo';
|
|
7
|
+
|
|
8
|
+
import { SheetType } from './types';
|
|
9
|
+
|
|
10
|
+
export const serializer: TypedObjectSerializer<SheetType> = {
|
|
11
|
+
serialize: async ({ object }): Promise<string> => {
|
|
12
|
+
const { threads: _threads, ...sheet } = object;
|
|
13
|
+
return JSON.stringify(sheet, null, 2);
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
deserialize: async ({ content, newId }) => {
|
|
17
|
+
const { id, ...parsed } = JSON.parse(content);
|
|
18
|
+
const sheet = create(SheetType, parsed);
|
|
19
|
+
|
|
20
|
+
if (!newId) {
|
|
21
|
+
const core = getObjectCore(sheet);
|
|
22
|
+
core.id = id;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return sheet;
|
|
26
|
+
},
|
|
27
|
+
};
|