@dxos/plugin-sheet 0.6.12-main.c974201 → 0.6.12-main.f9d0246
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-V4GCCZTX.mjs → SheetContainer-VISF3VUB.mjs} +6 -6
- package/dist/lib/browser/{SheetContainer-V4GCCZTX.mjs.map → SheetContainer-VISF3VUB.mjs.map} +3 -3
- package/dist/lib/browser/{chunk-T3NJFTD4.mjs → chunk-WZMOZKQZ.mjs} +2 -2
- package/dist/lib/browser/{chunk-T3NJFTD4.mjs.map → chunk-WZMOZKQZ.mjs.map} +3 -3
- package/dist/lib/browser/{chunk-6ZMQVB4Z.mjs → chunk-Z2XOOC2R.mjs} +81 -62
- package/dist/lib/browser/chunk-Z2XOOC2R.mjs.map +7 -0
- package/dist/lib/browser/{chunk-U2JHW3L6.mjs → chunk-ZLJ2GRE2.mjs} +173 -42
- package/dist/lib/browser/chunk-ZLJ2GRE2.mjs.map +7 -0
- package/dist/lib/browser/{graph-T27BOBOV.mjs → graph-4XFKIHRL.mjs} +4 -4
- package/dist/lib/browser/index.mjs +15 -13
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types.mjs +1 -1
- package/dist/lib/node/{SheetContainer-3ZY7MPWJ.cjs → SheetContainer-2MEALQWW.cjs} +14 -14
- package/dist/lib/node/{SheetContainer-3ZY7MPWJ.cjs.map → SheetContainer-2MEALQWW.cjs.map} +3 -3
- package/dist/lib/node/{chunk-OTTD7FBK.cjs → chunk-6DQABRGJ.cjs} +192 -60
- package/dist/lib/node/chunk-6DQABRGJ.cjs.map +7 -0
- package/dist/lib/node/{chunk-Q3HBHPRL.cjs → chunk-AOP42UAA.cjs} +5 -5
- package/dist/lib/node/{chunk-Q3HBHPRL.cjs.map → chunk-AOP42UAA.cjs.map} +3 -3
- package/dist/lib/node/{chunk-DD6FIXWC.cjs → chunk-P5QYYEHQ.cjs} +86 -67
- package/dist/lib/node/chunk-P5QYYEHQ.cjs.map +7 -0
- package/dist/lib/node/{graph-SPKGX7W4.cjs → graph-2LRDUXBZ.cjs} +14 -14
- package/dist/lib/node/graph-2LRDUXBZ.cjs.map +7 -0
- package/dist/lib/node/index.cjs +25 -24
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/types.cjs +8 -8
- package/dist/lib/node/types.cjs.map +1 -1
- package/dist/lib/node-esm/{SheetContainer-PXSJX6XK.mjs → SheetContainer-RPSUSXWS.mjs} +6 -6
- package/dist/lib/node-esm/{SheetContainer-PXSJX6XK.mjs.map → SheetContainer-RPSUSXWS.mjs.map} +3 -3
- package/dist/lib/node-esm/{chunk-D6KU5MI7.mjs → chunk-4MM7THJW.mjs} +81 -62
- package/dist/lib/node-esm/chunk-4MM7THJW.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-7HVSOTGA.mjs → chunk-5RLTCIE2.mjs} +173 -42
- package/dist/lib/node-esm/chunk-5RLTCIE2.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-BMNA27EX.mjs → chunk-RR2AO4SM.mjs} +2 -2
- package/dist/lib/node-esm/{chunk-BMNA27EX.mjs.map → chunk-RR2AO4SM.mjs.map} +3 -3
- package/dist/lib/node-esm/{graph-U67IO4UC.mjs → graph-WG5EKOMO.mjs} +4 -4
- package/dist/lib/node-esm/index.mjs +15 -13
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types.mjs +1 -1
- package/dist/types/src/SheetPlugin.d.ts.map +1 -1
- package/dist/types/src/components/GridSheet/GridSheet.d.ts +3 -3
- package/dist/types/src/components/GridSheet/GridSheet.d.ts.map +1 -1
- package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts +1 -1
- package/dist/types/src/components/GridSheet/GridSheet.stories.d.ts.map +1 -1
- package/dist/types/src/components/GridSheet/util.d.ts +3 -2
- package/dist/types/src/components/GridSheet/util.d.ts.map +1 -1
- package/dist/types/src/components/Sheet/Sheet.stories.d.ts.map +1 -1
- package/dist/types/src/components/Sheet/sheet-context.d.ts +3 -3
- package/dist/types/src/components/Sheet/sheet-context.d.ts.map +1 -1
- package/dist/types/src/components/SheetContainer.d.ts +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/defs/types.d.ts.map +1 -1
- package/dist/types/src/defs/util.d.ts +1 -1
- package/dist/types/src/defs/util.d.ts.map +1 -1
- package/dist/types/src/extensions/compute.d.ts +5 -1
- package/dist/types/src/extensions/compute.d.ts.map +1 -1
- package/dist/types/src/extensions/compute.stories.d.ts.map +1 -1
- package/dist/types/src/graph/async-function.d.ts +7 -1
- package/dist/types/src/graph/async-function.d.ts.map +1 -1
- package/dist/types/src/graph/compute-graph.d.ts +12 -9
- package/dist/types/src/graph/compute-graph.d.ts.map +1 -1
- package/dist/types/src/graph/compute-graph.stories.d.ts.map +1 -1
- package/dist/types/src/graph/compute-graph.test.d.ts +2 -0
- package/dist/types/src/graph/compute-graph.test.d.ts.map +1 -0
- package/dist/types/src/graph/compute-node.d.ts +9 -2
- package/dist/types/src/graph/compute-node.d.ts.map +1 -1
- package/dist/types/src/graph/edge-function.d.ts.map +1 -1
- package/dist/types/src/graph/{custom-function.d.ts → testing/custom-function.d.ts} +3 -1
- package/dist/types/src/graph/testing/custom-function.d.ts.map +1 -0
- package/dist/types/src/graph/testing/index.d.ts +2 -0
- package/dist/types/src/graph/testing/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useComputeGraph.d.ts.map +1 -1
- package/dist/types/src/hooks/useSheetModel.d.ts +2 -2
- package/dist/types/src/hooks/useSheetModel.d.ts.map +1 -1
- package/dist/types/src/model/sheet-model.d.ts +3 -3
- package/dist/types/src/model/sheet-model.d.ts.map +1 -1
- package/dist/types/src/testing/testing.d.ts +4 -5
- package/dist/types/src/testing/testing.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +4 -3
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +33 -33
- package/src/SheetPlugin.tsx +9 -7
- package/src/components/CellEditor/CellEditor.stories.tsx +1 -1
- package/src/components/GridSheet/GridSheet.stories.tsx +5 -4
- package/src/components/GridSheet/GridSheet.tsx +6 -6
- package/src/components/GridSheet/util.ts +46 -27
- package/src/components/Sheet/Sheet.stories.tsx +21 -20
- package/src/components/Sheet/sheet-context.tsx +4 -4
- package/src/components/SheetContainer.tsx +2 -2
- package/src/defs/types.ts +1 -0
- package/src/defs/util.ts +19 -3
- package/src/extensions/compute.stories.tsx +18 -16
- package/src/extensions/compute.ts +72 -39
- package/src/graph/async-function.ts +13 -6
- package/src/graph/compute-graph.stories.tsx +4 -3
- package/src/graph/compute-graph.test.ts +127 -0
- package/src/graph/compute-graph.ts +64 -41
- package/src/graph/compute-node.ts +16 -5
- package/src/graph/edge-function.ts +1 -2
- package/src/graph/{custom-function.ts → testing/custom-function.ts} +10 -2
- package/src/graph/testing/index.ts +5 -0
- package/src/hooks/hooks.stories.tsx +3 -3
- package/src/hooks/useComputeGraph.ts +2 -1
- package/src/hooks/useSheetModel.ts +4 -7
- package/src/model/sheet-model.ts +44 -29
- package/src/testing/testing.tsx +17 -15
- package/src/types.ts +3 -3
- package/dist/lib/browser/chunk-6ZMQVB4Z.mjs.map +0 -7
- package/dist/lib/browser/chunk-U2JHW3L6.mjs.map +0 -7
- package/dist/lib/node/chunk-DD6FIXWC.cjs.map +0 -7
- package/dist/lib/node/chunk-OTTD7FBK.cjs.map +0 -7
- package/dist/lib/node/graph-SPKGX7W4.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-7HVSOTGA.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-D6KU5MI7.mjs.map +0 -7
- package/dist/types/src/graph/compute-graph.browser.test.d.ts +0 -2
- package/dist/types/src/graph/compute-graph.browser.test.d.ts.map +0 -1
- package/dist/types/src/graph/custom-function.d.ts.map +0 -1
- package/src/graph/compute-graph.browser.test.ts +0 -104
- /package/dist/lib/browser/{graph-T27BOBOV.mjs.map → graph-4XFKIHRL.mjs.map} +0 -0
- /package/dist/lib/node-esm/{graph-U67IO4UC.mjs.map → graph-WG5EKOMO.mjs.map} +0 -0
|
@@ -2,13 +2,20 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { useEffect, useState } from 'react';
|
|
5
|
+
import { type MutableRefObject, useEffect, useLayoutEffect, useState } from 'react';
|
|
6
6
|
|
|
7
7
|
import { createDocAccessor } from '@dxos/react-client/echo';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
type GridEditing,
|
|
10
|
+
type GridContentProps,
|
|
11
|
+
type DxGridElement,
|
|
12
|
+
type DxGridCells,
|
|
13
|
+
type DxGridAxisMeta,
|
|
14
|
+
type DxGridRange,
|
|
15
|
+
} from '@dxos/react-ui-grid';
|
|
9
16
|
import { mx } from '@dxos/react-ui-theme';
|
|
10
17
|
|
|
11
|
-
import {
|
|
18
|
+
import { type CellAddress } from '../../defs';
|
|
12
19
|
import { type SheetModel, type FormattingModel } from '../../model';
|
|
13
20
|
|
|
14
21
|
export const dxGridCellIndexToSheetCellAddress = (gridEditing: GridEditing): CellAddress | null => {
|
|
@@ -22,19 +29,8 @@ export const dxGridCellIndexToSheetCellAddress = (gridEditing: GridEditing): Cel
|
|
|
22
29
|
};
|
|
23
30
|
};
|
|
24
31
|
|
|
25
|
-
const
|
|
26
|
-
return
|
|
27
|
-
const address = addressFromIndex(model.sheet, sheetCellIndex);
|
|
28
|
-
const cell = formatting.getFormatting(address);
|
|
29
|
-
if (cell.value) {
|
|
30
|
-
acc[`${address.col},${address.row}`] = { value: cell.value, className: mx(cell.classNames) };
|
|
31
|
-
}
|
|
32
|
-
return acc;
|
|
33
|
-
}, {});
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const createDxGridColumns = (model: SheetModel): GridContentProps['columns'] => {
|
|
37
|
-
return model.sheet.columns.reduce((acc: NonNullable<GridContentProps['columns']>, columnId, numericIndex) => {
|
|
32
|
+
const createDxGridColumns = (model: SheetModel): DxGridAxisMeta => {
|
|
33
|
+
return model.sheet.columns.reduce((acc: DxGridAxisMeta, columnId, numericIndex) => {
|
|
38
34
|
if (model.sheet.columnMeta[columnId] && model.sheet.columnMeta[columnId].size) {
|
|
39
35
|
acc[numericIndex] = { size: model.sheet.columnMeta[columnId].size, resizeable: true };
|
|
40
36
|
}
|
|
@@ -42,8 +38,8 @@ const createDxGridColumns = (model: SheetModel): GridContentProps['columns'] =>
|
|
|
42
38
|
}, {});
|
|
43
39
|
};
|
|
44
40
|
|
|
45
|
-
const createDxGridRows = (model: SheetModel):
|
|
46
|
-
return model.sheet.rows.reduce((acc:
|
|
41
|
+
const createDxGridRows = (model: SheetModel): DxGridAxisMeta => {
|
|
42
|
+
return model.sheet.rows.reduce((acc: DxGridAxisMeta, rowId, numericIndex) => {
|
|
47
43
|
if (model.sheet.rowMeta[rowId] && model.sheet.rowMeta[rowId].size) {
|
|
48
44
|
acc[numericIndex] = { size: model.sheet.rowMeta[rowId].size, resizeable: true };
|
|
49
45
|
}
|
|
@@ -51,18 +47,41 @@ const createDxGridRows = (model: SheetModel): GridContentProps['rows'] => {
|
|
|
51
47
|
}, {});
|
|
52
48
|
};
|
|
53
49
|
|
|
50
|
+
const cellGetter = (model: SheetModel, formatting: FormattingModel) => {
|
|
51
|
+
// TODO(thure): Actually use the cache.
|
|
52
|
+
let _cachedBounds: DxGridRange | null = null;
|
|
53
|
+
const cachedCells: DxGridCells = {};
|
|
54
|
+
return (nextBounds: DxGridRange): DxGridCells => {
|
|
55
|
+
[...Array(nextBounds.end.col - nextBounds.start.col)].forEach((_, c0) => {
|
|
56
|
+
return [...Array(nextBounds.end.row - nextBounds.start.row)].forEach((_, r0) => {
|
|
57
|
+
const col = nextBounds.start.col + c0;
|
|
58
|
+
const row = nextBounds.start.row + r0;
|
|
59
|
+
const cell = formatting.getFormatting({ col, row });
|
|
60
|
+
if (cell.value) {
|
|
61
|
+
cachedCells[`${col},${row}`] = { value: cell.value, className: mx(cell.classNames) };
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
_cachedBounds = nextBounds;
|
|
66
|
+
return cachedCells;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
|
|
54
70
|
export const useSheetModelDxGridProps = (
|
|
71
|
+
dxGridRef: MutableRefObject<DxGridElement | null>,
|
|
55
72
|
model: SheetModel,
|
|
56
73
|
formatting: FormattingModel,
|
|
57
|
-
): Pick<GridContentProps, '
|
|
58
|
-
const [
|
|
59
|
-
const [
|
|
60
|
-
const [dxGridRows, setDxGridRows] = useState<GridContentProps['rows']>(createDxGridColumns(model));
|
|
74
|
+
): Pick<GridContentProps, 'columns' | 'rows'> => {
|
|
75
|
+
const [columns, setColumns] = useState<DxGridAxisMeta>(createDxGridColumns(model));
|
|
76
|
+
const [rows, setRows] = useState<DxGridAxisMeta>(createDxGridColumns(model));
|
|
61
77
|
|
|
62
|
-
|
|
78
|
+
useLayoutEffect(() => {
|
|
63
79
|
const cellsAccessor = createDocAccessor(model.sheet, ['cells']);
|
|
80
|
+
if (dxGridRef.current) {
|
|
81
|
+
dxGridRef.current.getCells = cellGetter(model, formatting);
|
|
82
|
+
}
|
|
64
83
|
const handleCellsUpdate = () => {
|
|
65
|
-
|
|
84
|
+
dxGridRef.current?.requestUpdate('initialCells');
|
|
66
85
|
};
|
|
67
86
|
cellsAccessor.handle.addListener('change', handleCellsUpdate);
|
|
68
87
|
return () => cellsAccessor.handle.removeListener('change', handleCellsUpdate);
|
|
@@ -72,10 +91,10 @@ export const useSheetModelDxGridProps = (
|
|
|
72
91
|
const columnMetaAccessor = createDocAccessor(model.sheet, ['columnMeta']);
|
|
73
92
|
const rowMetaAccessor = createDocAccessor(model.sheet, ['rowMeta']);
|
|
74
93
|
const handleColumnMetaUpdate = () => {
|
|
75
|
-
|
|
94
|
+
setColumns(createDxGridColumns(model));
|
|
76
95
|
};
|
|
77
96
|
const handleRowMetaUpdate = () => {
|
|
78
|
-
|
|
97
|
+
setRows(createDxGridRows(model));
|
|
79
98
|
};
|
|
80
99
|
columnMetaAccessor.handle.addListener('change', handleColumnMetaUpdate);
|
|
81
100
|
rowMetaAccessor.handle.addListener('change', handleRowMetaUpdate);
|
|
@@ -85,5 +104,5 @@ export const useSheetModelDxGridProps = (
|
|
|
85
104
|
};
|
|
86
105
|
}, [model]);
|
|
87
106
|
|
|
88
|
-
return {
|
|
107
|
+
return { columns, rows };
|
|
89
108
|
};
|
|
@@ -7,7 +7,7 @@ import '@dxos-theme';
|
|
|
7
7
|
import React, { useState } from 'react';
|
|
8
8
|
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
|
-
import {
|
|
10
|
+
import { useSpace } from '@dxos/react-client/echo';
|
|
11
11
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
12
12
|
import { Button } from '@dxos/react-ui';
|
|
13
13
|
import { mx } from '@dxos/react-ui-theme';
|
|
@@ -17,18 +17,19 @@ import { Sheet } from './Sheet';
|
|
|
17
17
|
import { type SizeMap } from './grid';
|
|
18
18
|
import { useSheetContext } from './sheet-context';
|
|
19
19
|
import { addressToIndex, rangeToIndex } from '../../defs';
|
|
20
|
+
import { type ComputeGraph } from '../../graph';
|
|
21
|
+
import { testPlugins } from '../../graph/testing';
|
|
20
22
|
import { useComputeGraph } from '../../hooks';
|
|
21
|
-
import { useTestSheet,
|
|
23
|
+
import { createTestCells, useTestSheet, withComputeGraphDecorator } from '../../testing';
|
|
22
24
|
import { SheetType, ValueTypeEnum } from '../../types';
|
|
23
25
|
import { Toolbar, type ToolbarActionHandler } from '../Toolbar';
|
|
24
26
|
|
|
25
27
|
// TODO(burdon): Allow toolbar to access sheet context; provide state for current cursor/range.
|
|
26
|
-
const SheetWithToolbar = ({
|
|
28
|
+
const SheetWithToolbar = ({ graph, debug }: { graph: ComputeGraph; debug?: boolean }) => {
|
|
27
29
|
const { model, cursor, range } = useSheetContext();
|
|
28
30
|
|
|
29
|
-
const graph = useComputeGraph(space);
|
|
30
31
|
const handleRefresh = () => {
|
|
31
|
-
graph?.refresh();
|
|
32
|
+
// graph?.refresh(); // TODO(burdon): ???
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
// TODO(burdon): Factor out.
|
|
@@ -104,7 +105,7 @@ export default {
|
|
|
104
105
|
component: Sheet,
|
|
105
106
|
decorators: [
|
|
106
107
|
withClientProvider({ types: [SheetType], createIdentity: true }),
|
|
107
|
-
|
|
108
|
+
withComputeGraphDecorator({ plugins: testPlugins }),
|
|
108
109
|
withTheme,
|
|
109
110
|
withLayout({ fullscreen: true, tooltips: true, classNames: 'inset-4' }),
|
|
110
111
|
],
|
|
@@ -114,14 +115,14 @@ export const Default = () => {
|
|
|
114
115
|
const [debug, setDebug] = useState(false);
|
|
115
116
|
const space = useSpace();
|
|
116
117
|
const graph = useComputeGraph(space);
|
|
117
|
-
const sheet = useTestSheet(space, graph);
|
|
118
|
-
if (!
|
|
118
|
+
const sheet = useTestSheet(space, graph, { cells: createTestCells() });
|
|
119
|
+
if (!graph || !sheet) {
|
|
119
120
|
return null;
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
return (
|
|
123
|
-
<Sheet.Root
|
|
124
|
-
<SheetWithToolbar
|
|
124
|
+
<Sheet.Root graph={graph} sheet={sheet} onInfo={() => setDebug((debug) => !debug)}>
|
|
125
|
+
<SheetWithToolbar graph={graph} debug={debug} />
|
|
125
126
|
</Sheet.Root>
|
|
126
127
|
);
|
|
127
128
|
};
|
|
@@ -129,13 +130,13 @@ export const Default = () => {
|
|
|
129
130
|
export const Debug = () => {
|
|
130
131
|
const space = useSpace();
|
|
131
132
|
const graph = useComputeGraph(space);
|
|
132
|
-
const sheet = useTestSheet(space, graph);
|
|
133
|
-
if (!
|
|
133
|
+
const sheet = useTestSheet(space, graph, { cells: createTestCells() });
|
|
134
|
+
if (!graph || !sheet) {
|
|
134
135
|
return null;
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
return (
|
|
138
|
-
<Sheet.Root
|
|
139
|
+
<Sheet.Root graph={graph} sheet={sheet}>
|
|
139
140
|
<Sheet.Main />
|
|
140
141
|
<Sheet.Debug />
|
|
141
142
|
</Sheet.Root>
|
|
@@ -147,12 +148,12 @@ export const Rows = () => {
|
|
|
147
148
|
const space = useSpace();
|
|
148
149
|
const graph = useComputeGraph(space);
|
|
149
150
|
const sheet = useTestSheet(space, graph);
|
|
150
|
-
if (!
|
|
151
|
+
if (!graph || !sheet) {
|
|
151
152
|
return null;
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
return (
|
|
155
|
-
<Sheet.Root
|
|
156
|
+
<Sheet.Root graph={graph} sheet={sheet}>
|
|
156
157
|
<Sheet.Rows
|
|
157
158
|
rows={sheet.rows}
|
|
158
159
|
sizes={rowSizes}
|
|
@@ -167,12 +168,12 @@ export const Columns = () => {
|
|
|
167
168
|
const space = useSpace();
|
|
168
169
|
const graph = useComputeGraph(space);
|
|
169
170
|
const sheet = useTestSheet(space, graph);
|
|
170
|
-
if (!
|
|
171
|
+
if (!graph || !sheet) {
|
|
171
172
|
return null;
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
return (
|
|
175
|
-
<Sheet.Root
|
|
176
|
+
<Sheet.Root graph={graph} sheet={sheet}>
|
|
176
177
|
<Sheet.Columns
|
|
177
178
|
columns={sheet.columns}
|
|
178
179
|
sizes={columnSizes}
|
|
@@ -185,13 +186,13 @@ export const Columns = () => {
|
|
|
185
186
|
export const Main = () => {
|
|
186
187
|
const space = useSpace();
|
|
187
188
|
const graph = useComputeGraph(space);
|
|
188
|
-
const sheet = useTestSheet(space, graph);
|
|
189
|
-
if (!
|
|
189
|
+
const sheet = useTestSheet(space, graph, { cells: createTestCells() });
|
|
190
|
+
if (!graph || !sheet) {
|
|
190
191
|
return null;
|
|
191
192
|
}
|
|
192
193
|
|
|
193
194
|
return (
|
|
194
|
-
<Sheet.Root
|
|
195
|
+
<Sheet.Root graph={graph} sheet={sheet}>
|
|
195
196
|
<Sheet.Grid
|
|
196
197
|
size={{
|
|
197
198
|
numRows: 50,
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
import React, { type PropsWithChildren, createContext, useContext, useMemo, useState } from 'react';
|
|
6
6
|
|
|
7
7
|
import { invariant } from '@dxos/invariant';
|
|
8
|
-
import { type Space } from '@dxos/react-client/echo';
|
|
9
8
|
|
|
10
9
|
import { createDecorations } from './decorations';
|
|
11
10
|
import { type CellAddress, type CellRange } from '../../defs';
|
|
11
|
+
import { type ComputeGraph } from '../../graph';
|
|
12
12
|
import { useSheetModel, useFormattingModel } from '../../hooks';
|
|
13
13
|
import { type FormattingModel, type SheetModel } from '../../model';
|
|
14
14
|
import { type SheetType } from '../../types';
|
|
@@ -45,19 +45,19 @@ export const useSheetContext = (): SheetContextType => {
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
export type SheetContextProps = {
|
|
48
|
+
graph: ComputeGraph;
|
|
48
49
|
sheet: SheetType;
|
|
49
|
-
space: Space;
|
|
50
50
|
readonly?: boolean;
|
|
51
51
|
} & Pick<SheetContextType, 'onInfo'>;
|
|
52
52
|
|
|
53
53
|
export const SheetContextProvider = ({
|
|
54
54
|
children,
|
|
55
|
+
graph,
|
|
55
56
|
sheet,
|
|
56
|
-
space,
|
|
57
57
|
readonly,
|
|
58
58
|
onInfo,
|
|
59
59
|
}: PropsWithChildren<SheetContextProps>) => {
|
|
60
|
-
const model = useSheetModel(
|
|
60
|
+
const model = useSheetModel(graph, sheet, { readonly });
|
|
61
61
|
const formatting = useFormattingModel(model);
|
|
62
62
|
|
|
63
63
|
// TODO(Zan): Impl. set range and set cursor that scrolls to that cell or range if it is not visible.
|
|
@@ -22,7 +22,7 @@ const attentionFragment = mx(
|
|
|
22
22
|
export const sectionToolbarLayout =
|
|
23
23
|
'bs-[--rail-action] bg-[--sticky-bg] sticky block-start-0 __-block-start-px transition-opacity';
|
|
24
24
|
|
|
25
|
-
const SheetContainer = ({
|
|
25
|
+
const SheetContainer = ({ graph, sheet, role }: SheetRootProps & { role?: string }) => {
|
|
26
26
|
const dispatch = useIntentDispatcher();
|
|
27
27
|
|
|
28
28
|
const id = fullyQualifiedId(sheet);
|
|
@@ -50,7 +50,7 @@ const SheetContainer = ({ sheet, space, role }: SheetRootProps & { role?: string
|
|
|
50
50
|
|
|
51
51
|
return (
|
|
52
52
|
<div role='none' className={role === 'article' ? 'row-span-2 grid grid-rows-subgrid' : undefined}>
|
|
53
|
-
<Sheet.Root
|
|
53
|
+
<Sheet.Root graph={graph} sheet={sheet}>
|
|
54
54
|
<div role='none' className={mx('flex flex-0 justify-center overflow-x-auto')}>
|
|
55
55
|
<Toolbar.Root
|
|
56
56
|
onAction={handleAction}
|
package/src/defs/types.ts
CHANGED
|
@@ -34,6 +34,7 @@ export const addressToA1Notation = ({ col, row }: CellAddress): string => {
|
|
|
34
34
|
return `${columnLetter(col)}${row + 1}`;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
// TODO(burdon): See simpleCellAddressFromString
|
|
37
38
|
export const addressFromA1Notation = (ref: string): CellAddress => {
|
|
38
39
|
const match = ref.match(/([A-Z]+)(\d+)/);
|
|
39
40
|
invariant(match, `Invalid notation: ${ref}`);
|
package/src/defs/util.ts
CHANGED
|
@@ -5,7 +5,15 @@
|
|
|
5
5
|
import { randomBytes } from '@dxos/crypto';
|
|
6
6
|
import { create } from '@dxos/echo-schema';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
addressFromA1Notation,
|
|
10
|
+
type CellAddress,
|
|
11
|
+
type CellRange,
|
|
12
|
+
DEFAULT_COLUMNS,
|
|
13
|
+
DEFAULT_ROWS,
|
|
14
|
+
MAX_COLUMNS,
|
|
15
|
+
MAX_ROWS,
|
|
16
|
+
} from './types';
|
|
9
17
|
import { type CreateSheetOptions, type SheetSize, SheetType } from '../types';
|
|
10
18
|
|
|
11
19
|
// TODO(burdon): Factor out from dxos/protocols to new common package.
|
|
@@ -52,9 +60,9 @@ export const initialize = (
|
|
|
52
60
|
}
|
|
53
61
|
};
|
|
54
62
|
|
|
55
|
-
export const createSheet = ({
|
|
63
|
+
export const createSheet = ({ name, cells, ...size }: CreateSheetOptions = {}): SheetType => {
|
|
56
64
|
const sheet = create(SheetType, {
|
|
57
|
-
|
|
65
|
+
name,
|
|
58
66
|
cells: {},
|
|
59
67
|
rows: [],
|
|
60
68
|
columns: [],
|
|
@@ -64,6 +72,14 @@ export const createSheet = ({ title, ...size }: CreateSheetOptions = {}): SheetT
|
|
|
64
72
|
});
|
|
65
73
|
|
|
66
74
|
initialize(sheet, size);
|
|
75
|
+
|
|
76
|
+
if (cells) {
|
|
77
|
+
Object.entries(cells).forEach(([key, { value }]) => {
|
|
78
|
+
const idx = addressToIndex(sheet, addressFromA1Notation(key));
|
|
79
|
+
sheet.cells[idx] = { value };
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
67
83
|
return sheet;
|
|
68
84
|
};
|
|
69
85
|
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
|
-
import React, { useEffect
|
|
6
|
+
import React, { useEffect } from 'react';
|
|
7
7
|
|
|
8
8
|
import { useSpace } from '@dxos/react-client/echo';
|
|
9
9
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
10
|
+
import { useAsyncState } from '@dxos/react-hooks';
|
|
10
11
|
import { useThemeContext } from '@dxos/react-ui';
|
|
11
12
|
import {
|
|
12
13
|
createBasicExtensions,
|
|
@@ -18,11 +19,11 @@ import {
|
|
|
18
19
|
import { withTheme, withLayout } from '@dxos/storybook-utils';
|
|
19
20
|
import { nonNullable } from '@dxos/util';
|
|
20
21
|
|
|
21
|
-
import { compute } from './compute';
|
|
22
|
+
import { compute, computeNodeFacet } from './compute';
|
|
22
23
|
import { Sheet } from '../components';
|
|
23
24
|
import { type ComputeNode } from '../graph';
|
|
24
25
|
import { useComputeGraph, useSheetModel } from '../hooks';
|
|
25
|
-
import { useTestSheet,
|
|
26
|
+
import { useTestSheet, withComputeGraphDecorator } from '../testing';
|
|
26
27
|
import { SheetType } from '../types';
|
|
27
28
|
|
|
28
29
|
const str = (...lines: string[]) => lines.join('\n');
|
|
@@ -34,6 +35,8 @@ type EditorProps = {
|
|
|
34
35
|
// TODO(burdon): Implement named expressions.
|
|
35
36
|
// https://hyperformula.handsontable.com/guide/cell-references.html
|
|
36
37
|
|
|
38
|
+
// TODO(burdon): Inline Adobe eCharts.
|
|
39
|
+
|
|
37
40
|
const DOC_NAME = 'Test Doc';
|
|
38
41
|
const SHEET_NAME = 'Test Sheet';
|
|
39
42
|
|
|
@@ -41,12 +44,8 @@ const Editor = ({ text }: EditorProps) => {
|
|
|
41
44
|
const { themeMode } = useThemeContext();
|
|
42
45
|
const space = useSpace();
|
|
43
46
|
const graph = useComputeGraph(space);
|
|
44
|
-
const [
|
|
45
|
-
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (graph) {
|
|
48
|
-
setNode(graph.getOrCreateNode(DOC_NAME));
|
|
49
|
-
}
|
|
47
|
+
const [computeNode] = useAsyncState<ComputeNode>(async () => {
|
|
48
|
+
return graph ? await graph.getOrCreateNode(DOC_NAME) : undefined;
|
|
50
49
|
}, [graph]);
|
|
51
50
|
const { parentRef, focusAttributes } = useTextEditor(
|
|
52
51
|
() => ({
|
|
@@ -55,11 +54,12 @@ const Editor = ({ text }: EditorProps) => {
|
|
|
55
54
|
createBasicExtensions(),
|
|
56
55
|
createMarkdownExtensions({ themeMode }),
|
|
57
56
|
createThemeExtensions({ themeMode, syntaxHighlighting: true }),
|
|
58
|
-
|
|
57
|
+
computeNode && computeNodeFacet.of(computeNode),
|
|
58
|
+
compute(),
|
|
59
59
|
decorateMarkdown(),
|
|
60
60
|
].filter(nonNullable),
|
|
61
61
|
}),
|
|
62
|
-
[
|
|
62
|
+
[computeNode, themeMode],
|
|
63
63
|
);
|
|
64
64
|
|
|
65
65
|
return <div className='w-[40rem] overflow-hidden' ref={parentRef} {...focusAttributes} />;
|
|
@@ -68,21 +68,21 @@ const Editor = ({ text }: EditorProps) => {
|
|
|
68
68
|
const Grid = () => {
|
|
69
69
|
const space = useSpace();
|
|
70
70
|
const graph = useComputeGraph(space);
|
|
71
|
-
const sheet = useTestSheet(space, graph, {
|
|
72
|
-
const model = useSheetModel(
|
|
71
|
+
const sheet = useTestSheet(space, graph, { name: SHEET_NAME });
|
|
72
|
+
const model = useSheetModel(graph, sheet);
|
|
73
73
|
useEffect(() => {
|
|
74
74
|
if (model) {
|
|
75
75
|
model.setValues({ A1: { value: 100 }, A2: { value: 200 }, A3: { value: 300 }, A5: { value: '=SUM(A1:A3)' } });
|
|
76
76
|
}
|
|
77
77
|
}, [model]);
|
|
78
78
|
|
|
79
|
-
if (!
|
|
79
|
+
if (!graph || !sheet) {
|
|
80
80
|
return null;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
return (
|
|
84
84
|
<div className='flex w-[40rem] overflow-hidden'>
|
|
85
|
-
<Sheet.Root
|
|
85
|
+
<Sheet.Root graph={graph} sheet={sheet}>
|
|
86
86
|
<Sheet.Main classNames='border border-separator' />
|
|
87
87
|
</Sheet.Root>
|
|
88
88
|
</div>
|
|
@@ -102,13 +102,14 @@ export default {
|
|
|
102
102
|
title: 'plugin-sheet/extensions',
|
|
103
103
|
decorators: [
|
|
104
104
|
withClientProvider({ types: [SheetType], createIdentity: true, createSpace: true }),
|
|
105
|
-
|
|
105
|
+
withComputeGraphDecorator(),
|
|
106
106
|
withTheme,
|
|
107
107
|
withLayout({ fullscreen: true, classNames: 'justify-center' }),
|
|
108
108
|
],
|
|
109
109
|
parameters: { layout: 'fullscreen' },
|
|
110
110
|
};
|
|
111
111
|
|
|
112
|
+
// TODO(burdon): Inline formulae.
|
|
112
113
|
export const Default = {
|
|
113
114
|
render: Editor,
|
|
114
115
|
args: {
|
|
@@ -146,6 +147,7 @@ export const Graph = {
|
|
|
146
147
|
`="${SHEET_NAME}"!A5`,
|
|
147
148
|
'```',
|
|
148
149
|
'',
|
|
150
|
+
'',
|
|
149
151
|
),
|
|
150
152
|
},
|
|
151
153
|
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { syntaxTree } from '@codemirror/language';
|
|
6
|
+
import { Facet } from '@codemirror/state';
|
|
6
7
|
import {
|
|
7
8
|
type EditorState,
|
|
8
9
|
type Extension,
|
|
@@ -14,7 +15,21 @@ import {
|
|
|
14
15
|
} from '@codemirror/state';
|
|
15
16
|
import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view';
|
|
16
17
|
|
|
18
|
+
import { type UnsubscribeCallback } from '@dxos/async';
|
|
19
|
+
import { type Space } from '@dxos/client/echo';
|
|
20
|
+
|
|
21
|
+
import { type CellAddress } from '../defs';
|
|
17
22
|
import { type ComputeNode } from '../graph';
|
|
23
|
+
import { type CellScalarValue } from '../types';
|
|
24
|
+
|
|
25
|
+
export const spaceFacet = Facet.define<Space, Space>({
|
|
26
|
+
combine: (values) => values[0],
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// TODO(burdon): Create on demand?
|
|
30
|
+
export const computeNodeFacet = Facet.define<ComputeNode, ComputeNode>({
|
|
31
|
+
combine: (values) => values[0],
|
|
32
|
+
});
|
|
18
33
|
|
|
19
34
|
const LANGUAGE_TAG = 'dx';
|
|
20
35
|
|
|
@@ -23,36 +38,46 @@ const updateAllDecorations = StateEffect.define<void>();
|
|
|
23
38
|
|
|
24
39
|
export type ComputeOptions = {};
|
|
25
40
|
|
|
26
|
-
export const compute = (
|
|
27
|
-
const update = (state: EditorState) => {
|
|
28
|
-
const builder = new RangeSetBuilder();
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
export const compute = (options: ComputeOptions = {}): Extension => {
|
|
42
|
+
const update = (state: EditorState, rangeSet?: RangeSet<Decoration>) => {
|
|
43
|
+
const builder = new RangeSetBuilder<Decoration>();
|
|
44
|
+
const computeNode = state.facet(computeNodeFacet);
|
|
45
|
+
if (computeNode) {
|
|
46
|
+
computeNode.clear();
|
|
47
|
+
syntaxTree(state).iterate({
|
|
48
|
+
enter: (node) => {
|
|
49
|
+
if (node.name === 'FencedCode') {
|
|
50
|
+
const cursor = state.selection.main.head;
|
|
51
|
+
if (state.readOnly || cursor < node.from || cursor > node.to) {
|
|
52
|
+
const info = node.node.getChild('CodeInfo');
|
|
53
|
+
if (info) {
|
|
54
|
+
const type = state.sliceDoc(info.from, info.to);
|
|
55
|
+
const text = node.node.getChild('CodeText');
|
|
56
|
+
if (type === LANGUAGE_TAG && text) {
|
|
57
|
+
const formula = state.sliceDoc(text.from, text.to);
|
|
58
|
+
const iter = rangeSet?.iter(node.node.from);
|
|
59
|
+
if (iter?.value && iter?.value.spec.formula === formula) {
|
|
60
|
+
builder.add(node.from, node.to, iter.value);
|
|
61
|
+
} else {
|
|
62
|
+
const cell: CellAddress = { col: node.node.from, row: 0 };
|
|
63
|
+
computeNode.setValue(cell, formula);
|
|
64
|
+
const value = computeNode.getValue(cell);
|
|
65
|
+
builder.add(
|
|
66
|
+
node.from,
|
|
67
|
+
node.to,
|
|
68
|
+
Decoration.replace({
|
|
69
|
+
widget: new DxWidget(formula, value),
|
|
70
|
+
formula,
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
50
75
|
}
|
|
51
76
|
}
|
|
52
77
|
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
56
81
|
|
|
57
82
|
return builder.finish();
|
|
58
83
|
};
|
|
@@ -61,38 +86,46 @@ export const compute = (computeNode: ComputeNode, options: ComputeOptions = {}):
|
|
|
61
86
|
// Graph subscription.
|
|
62
87
|
ViewPlugin.fromClass(
|
|
63
88
|
class {
|
|
64
|
-
private readonly _subscription
|
|
89
|
+
private readonly _subscription?: UnsubscribeCallback;
|
|
65
90
|
constructor(view: EditorView) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
91
|
+
const computeNode = view.state.facet(computeNodeFacet);
|
|
92
|
+
if (computeNode) {
|
|
93
|
+
this._subscription = computeNode.update.on(({ type }) => {
|
|
94
|
+
if (type === 'valuesUpdated') {
|
|
95
|
+
view.dispatch({
|
|
96
|
+
effects: updateAllDecorations.of(),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
69
99
|
});
|
|
70
|
-
}
|
|
100
|
+
}
|
|
71
101
|
}
|
|
72
102
|
|
|
73
103
|
destroy() {
|
|
74
|
-
this._subscription();
|
|
104
|
+
this._subscription?.();
|
|
75
105
|
}
|
|
76
106
|
},
|
|
77
107
|
),
|
|
78
108
|
|
|
79
|
-
|
|
80
|
-
StateField.define<RangeSet<any>>({
|
|
109
|
+
StateField.define<RangeSet<Decoration>>({
|
|
81
110
|
create: (state) => update(state),
|
|
82
|
-
update: (
|
|
111
|
+
update: (rangeSet: RangeSet<Decoration>, tr: Transaction) => update(tr.state, rangeSet),
|
|
83
112
|
provide: (field) => EditorView.decorations.from(field),
|
|
84
113
|
}),
|
|
85
114
|
];
|
|
86
115
|
};
|
|
87
116
|
|
|
88
117
|
class DxWidget extends WidgetType {
|
|
89
|
-
constructor(
|
|
118
|
+
constructor(
|
|
119
|
+
private readonly formula: string,
|
|
120
|
+
private readonly value: CellScalarValue,
|
|
121
|
+
) {
|
|
90
122
|
super();
|
|
91
123
|
}
|
|
92
124
|
|
|
93
|
-
override toDOM(
|
|
125
|
+
override toDOM(_view: EditorView) {
|
|
94
126
|
const div = document.createElement('div');
|
|
95
|
-
div.
|
|
127
|
+
div.setAttribute('title', this.formula);
|
|
128
|
+
div.innerText = String(this.value);
|
|
96
129
|
return div;
|
|
97
130
|
}
|
|
98
131
|
}
|