@dxos/plugin-sheet 0.6.12-main.f9d0246 → 0.6.12-staging.e11e696
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-VISF3VUB.mjs → SheetContainer-LG77O4RM.mjs} +11 -10
- package/dist/lib/browser/SheetContainer-LG77O4RM.mjs.map +7 -0
- package/dist/lib/browser/{chunk-ZLJ2GRE2.mjs → chunk-CHQAW4F4.mjs} +55 -33
- package/dist/lib/browser/chunk-CHQAW4F4.mjs.map +7 -0
- package/dist/lib/browser/{chunk-Z2XOOC2R.mjs → chunk-GSV5QNLD.mjs} +183 -159
- package/dist/lib/browser/chunk-GSV5QNLD.mjs.map +7 -0
- package/dist/lib/browser/graph-M4IQ76QX.mjs +33 -0
- package/dist/lib/browser/index.mjs +37 -15
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/{SheetContainer-2MEALQWW.cjs → SheetContainer-OZ7DHH4L.cjs} +18 -17
- package/dist/lib/node/SheetContainer-OZ7DHH4L.cjs.map +7 -0
- package/dist/lib/node/{chunk-6DQABRGJ.cjs → chunk-5FTFZL5W.cjs} +57 -35
- package/dist/lib/node/chunk-5FTFZL5W.cjs.map +7 -0
- package/dist/lib/node/{chunk-P5QYYEHQ.cjs → chunk-5XPK2V4A.cjs} +186 -158
- package/dist/lib/node/chunk-5XPK2V4A.cjs.map +7 -0
- package/dist/lib/node/graph-Q3N2X26H.cjs +55 -0
- package/dist/lib/node/graph-Q3N2X26H.cjs.map +7 -0
- package/dist/lib/node/index.cjs +38 -18
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/{SheetContainer-RPSUSXWS.mjs → SheetContainer-4XS2G25Z.mjs} +11 -10
- package/dist/lib/node-esm/SheetContainer-4XS2G25Z.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-4MM7THJW.mjs → chunk-5WPZCXNS.mjs} +183 -159
- package/dist/lib/node-esm/chunk-5WPZCXNS.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-5RLTCIE2.mjs → chunk-KK3XL37M.mjs} +55 -33
- package/dist/lib/node-esm/chunk-KK3XL37M.mjs.map +7 -0
- package/dist/lib/node-esm/graph-SMPUMOV2.mjs +34 -0
- package/dist/lib/node-esm/index.mjs +37 -15
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/SheetPlugin.d.ts.map +1 -1
- package/dist/types/src/components/CellEditor/CellEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/CellEditor/extension.d.ts.map +1 -1
- package/dist/types/src/components/Sheet/Sheet.d.ts.map +1 -1
- package/dist/types/src/components/Sheet/Sheet.stories.d.ts.map +1 -1
- package/dist/types/src/components/SheetContainer.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/extensions/compute.d.ts +2 -5
- 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/compute-graph-registry.d.ts +34 -0
- package/dist/types/src/graph/compute-graph-registry.d.ts.map +1 -0
- package/dist/types/src/graph/compute-graph.d.ts +13 -33
- package/dist/types/src/graph/compute-graph.d.ts.map +1 -1
- package/dist/types/src/graph/compute-node.d.ts.map +1 -1
- package/dist/types/src/graph/{async-function.d.ts → functions/async-function.d.ts} +6 -3
- package/dist/types/src/graph/functions/async-function.d.ts.map +1 -0
- package/dist/types/src/graph/functions/edge-function.d.ts +21 -0
- package/dist/types/src/graph/functions/edge-function.d.ts.map +1 -0
- package/dist/types/src/graph/functions/function-defs.d.ts.map +1 -0
- package/dist/types/src/graph/functions/index.d.ts +4 -0
- package/dist/types/src/graph/functions/index.d.ts.map +1 -0
- package/dist/types/src/graph/index.d.ts +2 -1
- package/dist/types/src/graph/index.d.ts.map +1 -1
- package/dist/types/src/graph/testing/index.d.ts +2 -1
- package/dist/types/src/graph/testing/index.d.ts.map +1 -1
- package/dist/types/src/graph/testing/test-builder.d.ts +15 -0
- package/dist/types/src/graph/testing/test-builder.d.ts.map +1 -0
- package/dist/types/src/graph/testing/test-plugin.d.ts +36 -0
- package/dist/types/src/graph/testing/test-plugin.d.ts.map +1 -0
- package/dist/types/src/hooks/useComputeGraph.d.ts.map +1 -1
- package/dist/types/src/model/sheet-model.d.ts.map +1 -1
- package/dist/types/src/model/sheet-model.test.d.ts +2 -0
- package/dist/types/src/model/sheet-model.test.d.ts.map +1 -0
- package/package.json +40 -39
- package/src/SheetPlugin.tsx +12 -10
- package/src/components/CellEditor/CellEditor.stories.tsx +1 -2
- package/src/components/CellEditor/extension.test.ts +0 -1
- package/src/components/CellEditor/extension.ts +4 -3
- package/src/components/Sheet/Sheet.stories.tsx +2 -2
- package/src/components/Sheet/Sheet.tsx +30 -14
- package/src/components/SheetContainer.tsx +11 -13
- package/src/extensions/compute.stories.tsx +9 -11
- package/src/extensions/compute.ts +66 -50
- package/src/graph/compute-graph-registry.ts +90 -0
- package/src/graph/compute-graph.stories.tsx +2 -2
- package/src/graph/compute-graph.test.ts +31 -71
- package/src/graph/compute-graph.ts +45 -116
- package/src/graph/compute-node.ts +1 -0
- package/src/graph/{async-function.ts → functions/async-function.ts} +10 -9
- package/src/graph/{edge-function.ts → functions/edge-function.ts} +13 -11
- package/src/graph/functions/index.ts +7 -0
- package/src/graph/hyperformula.test.ts +1 -2
- package/src/graph/index.ts +2 -1
- package/src/graph/testing/index.ts +2 -1
- package/src/graph/testing/test-builder.ts +54 -0
- package/src/graph/testing/{custom-function.ts → test-plugin.ts} +38 -12
- package/src/hooks/useComputeGraph.ts +8 -1
- package/src/model/sheet-model.test.ts +59 -0
- package/src/model/sheet-model.ts +4 -2
- package/dist/lib/browser/SheetContainer-VISF3VUB.mjs.map +0 -7
- package/dist/lib/browser/chunk-Z2XOOC2R.mjs.map +0 -7
- package/dist/lib/browser/chunk-ZLJ2GRE2.mjs.map +0 -7
- package/dist/lib/browser/graph-4XFKIHRL.mjs +0 -21
- package/dist/lib/node/SheetContainer-2MEALQWW.cjs.map +0 -7
- package/dist/lib/node/chunk-6DQABRGJ.cjs.map +0 -7
- package/dist/lib/node/chunk-P5QYYEHQ.cjs.map +0 -7
- package/dist/lib/node/graph-2LRDUXBZ.cjs +0 -43
- package/dist/lib/node/graph-2LRDUXBZ.cjs.map +0 -7
- package/dist/lib/node-esm/SheetContainer-RPSUSXWS.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-4MM7THJW.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-5RLTCIE2.mjs.map +0 -7
- package/dist/lib/node-esm/graph-WG5EKOMO.mjs +0 -22
- package/dist/types/src/graph/async-function.d.ts.map +0 -1
- package/dist/types/src/graph/edge-function.d.ts +0 -20
- package/dist/types/src/graph/edge-function.d.ts.map +0 -1
- package/dist/types/src/graph/function-defs.d.ts.map +0 -1
- package/dist/types/src/graph/testing/custom-function.d.ts +0 -23
- package/dist/types/src/graph/testing/custom-function.d.ts.map +0 -1
- /package/dist/lib/browser/{graph-4XFKIHRL.mjs.map → graph-M4IQ76QX.mjs.map} +0 -0
- /package/dist/lib/node-esm/{graph-WG5EKOMO.mjs.map → graph-SMPUMOV2.mjs.map} +0 -0
- /package/dist/types/src/graph/{function-defs.d.ts → functions/function-defs.d.ts} +0 -0
- /package/src/graph/{function-defs.ts → functions/function-defs.ts} +0 -0
|
@@ -3,25 +3,25 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
|
-
import React, { useEffect } from 'react';
|
|
6
|
+
import React, { useEffect, useMemo } from 'react';
|
|
7
7
|
|
|
8
|
+
import { PublicKey } from '@dxos/keys';
|
|
8
9
|
import { useSpace } from '@dxos/react-client/echo';
|
|
9
10
|
import { withClientProvider } from '@dxos/react-client/testing';
|
|
10
|
-
import { useAsyncState } from '@dxos/react-hooks';
|
|
11
11
|
import { useThemeContext } from '@dxos/react-ui';
|
|
12
12
|
import {
|
|
13
13
|
createBasicExtensions,
|
|
14
14
|
createMarkdownExtensions,
|
|
15
15
|
createThemeExtensions,
|
|
16
16
|
decorateMarkdown,
|
|
17
|
+
documentId,
|
|
17
18
|
useTextEditor,
|
|
18
19
|
} from '@dxos/react-ui-editor';
|
|
19
20
|
import { withTheme, withLayout } from '@dxos/storybook-utils';
|
|
20
21
|
import { nonNullable } from '@dxos/util';
|
|
21
22
|
|
|
22
|
-
import { compute,
|
|
23
|
+
import { compute, computeGraphFacet } from './compute';
|
|
23
24
|
import { Sheet } from '../components';
|
|
24
|
-
import { type ComputeNode } from '../graph';
|
|
25
25
|
import { useComputeGraph, useSheetModel } from '../hooks';
|
|
26
26
|
import { useTestSheet, withComputeGraphDecorator } from '../testing';
|
|
27
27
|
import { SheetType } from '../types';
|
|
@@ -37,16 +37,13 @@ type EditorProps = {
|
|
|
37
37
|
|
|
38
38
|
// TODO(burdon): Inline Adobe eCharts.
|
|
39
39
|
|
|
40
|
-
const DOC_NAME = 'Test Doc';
|
|
41
40
|
const SHEET_NAME = 'Test Sheet';
|
|
42
41
|
|
|
43
42
|
const Editor = ({ text }: EditorProps) => {
|
|
43
|
+
const id = useMemo(() => PublicKey.random(), []);
|
|
44
44
|
const { themeMode } = useThemeContext();
|
|
45
45
|
const space = useSpace();
|
|
46
|
-
const
|
|
47
|
-
const [computeNode] = useAsyncState<ComputeNode>(async () => {
|
|
48
|
-
return graph ? await graph.getOrCreateNode(DOC_NAME) : undefined;
|
|
49
|
-
}, [graph]);
|
|
46
|
+
const computeGraph = useComputeGraph(space);
|
|
50
47
|
const { parentRef, focusAttributes } = useTextEditor(
|
|
51
48
|
() => ({
|
|
52
49
|
initialValue: text,
|
|
@@ -54,12 +51,13 @@ const Editor = ({ text }: EditorProps) => {
|
|
|
54
51
|
createBasicExtensions(),
|
|
55
52
|
createMarkdownExtensions({ themeMode }),
|
|
56
53
|
createThemeExtensions({ themeMode, syntaxHighlighting: true }),
|
|
57
|
-
|
|
54
|
+
documentId.of(id.toHex()),
|
|
55
|
+
computeGraph && computeGraphFacet.of(computeGraph),
|
|
58
56
|
compute(),
|
|
59
57
|
decorateMarkdown(),
|
|
60
58
|
].filter(nonNullable),
|
|
61
59
|
}),
|
|
62
|
-
[
|
|
60
|
+
[computeGraph, themeMode],
|
|
63
61
|
);
|
|
64
62
|
|
|
65
63
|
return <div className='w-[40rem] overflow-hidden' ref={parentRef} {...focusAttributes} />;
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { syntaxTree } from '@codemirror/language';
|
|
6
|
-
import { Facet } from '@codemirror/state';
|
|
7
6
|
import {
|
|
8
7
|
type EditorState,
|
|
9
8
|
type Extension,
|
|
@@ -15,64 +14,68 @@ import {
|
|
|
15
14
|
} from '@codemirror/state';
|
|
16
15
|
import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view';
|
|
17
16
|
|
|
18
|
-
import { type UnsubscribeCallback } from '@dxos/async';
|
|
19
|
-
import {
|
|
17
|
+
import { type UnsubscribeCallback, debounce } from '@dxos/async';
|
|
18
|
+
import { invariant } from '@dxos/invariant';
|
|
19
|
+
import { documentId, singleValueFacet } from '@dxos/react-ui-editor/state';
|
|
20
20
|
|
|
21
21
|
import { type CellAddress } from '../defs';
|
|
22
|
-
import { type ComputeNode } from '../graph';
|
|
22
|
+
import { type ComputeGraph, type ComputeNode, createSheetName } from '../graph';
|
|
23
23
|
import { type CellScalarValue } from '../types';
|
|
24
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
|
-
});
|
|
33
|
-
|
|
34
25
|
const LANGUAGE_TAG = 'dx';
|
|
35
26
|
|
|
36
27
|
// TODO(burdon): Create marker just for our decorator?
|
|
37
28
|
const updateAllDecorations = StateEffect.define<void>();
|
|
38
29
|
|
|
30
|
+
export const computeGraphFacet = singleValueFacet<ComputeGraph>();
|
|
31
|
+
|
|
39
32
|
export type ComputeOptions = {};
|
|
40
33
|
|
|
41
34
|
export const compute = (options: ComputeOptions = {}): Extension => {
|
|
42
|
-
|
|
35
|
+
let computeNode: ComputeNode | undefined;
|
|
36
|
+
|
|
37
|
+
const update = (state: EditorState, current?: RangeSet<Decoration>) => {
|
|
43
38
|
const builder = new RangeSetBuilder<Decoration>();
|
|
44
|
-
const computeNode = state.facet(computeNodeFacet);
|
|
45
39
|
if (computeNode) {
|
|
46
40
|
computeNode.clear();
|
|
47
41
|
syntaxTree(state).iterate({
|
|
48
42
|
enter: (node) => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
node.from,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
43
|
+
switch (node.name) {
|
|
44
|
+
case 'FencedCode': {
|
|
45
|
+
const cursor = state.selection.main.head;
|
|
46
|
+
if (state.readOnly || cursor < node.from || cursor > node.to) {
|
|
47
|
+
const info = node.node.getChild('CodeInfo');
|
|
48
|
+
if (info) {
|
|
49
|
+
const type = state.sliceDoc(info.from, info.to);
|
|
50
|
+
const text = node.node.getChild('CodeText');
|
|
51
|
+
if (type === LANGUAGE_TAG && text) {
|
|
52
|
+
const formula = state.sliceDoc(text.from, text.to);
|
|
53
|
+
|
|
54
|
+
const iter = current?.iter(node.node.from);
|
|
55
|
+
if (iter?.value && iter?.value.spec.formula === formula) {
|
|
56
|
+
// Add existing widget.
|
|
57
|
+
builder.add(node.from, node.to, iter.value);
|
|
58
|
+
} else {
|
|
59
|
+
// TODO(burdon): Create ordered list of cells on each decoration run.
|
|
60
|
+
const cell: CellAddress = { col: node.node.from, row: 0 };
|
|
61
|
+
invariant(computeNode);
|
|
62
|
+
// NOTE: This triggers re-render (below).
|
|
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 ComputeWidget(formula, value),
|
|
70
|
+
formula,
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
}
|
|
77
|
+
|
|
78
|
+
break;
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
},
|
|
@@ -83,25 +86,37 @@ export const compute = (options: ComputeOptions = {}): Extension => {
|
|
|
83
86
|
};
|
|
84
87
|
|
|
85
88
|
return [
|
|
86
|
-
// Graph subscription.
|
|
87
89
|
ViewPlugin.fromClass(
|
|
88
90
|
class {
|
|
89
|
-
|
|
91
|
+
// Graph subscription.
|
|
92
|
+
private _subscription?: UnsubscribeCallback;
|
|
90
93
|
constructor(view: EditorView) {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
const id = view.state.facet(documentId);
|
|
95
|
+
const computeGraph = view.state.facet(computeGraphFacet);
|
|
96
|
+
if (id && computeGraph) {
|
|
97
|
+
queueMicrotask(async () => {
|
|
98
|
+
computeNode = computeGraph.getOrCreateNode(createSheetName({ type: '', id }));
|
|
99
|
+
await computeNode.open();
|
|
100
|
+
|
|
101
|
+
// Trigger re-render if values updated.
|
|
102
|
+
// TODO(burdon): Trigger only if formula value updated (currently triggered during render).
|
|
103
|
+
this._subscription = computeNode.update.on(
|
|
104
|
+
debounce(({ type, ...rest }) => {
|
|
105
|
+
if (type === 'valuesUpdated') {
|
|
106
|
+
view.dispatch({
|
|
107
|
+
effects: updateAllDecorations.of(),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}, 250),
|
|
111
|
+
);
|
|
99
112
|
});
|
|
100
113
|
}
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
destroy() {
|
|
104
117
|
this._subscription?.();
|
|
118
|
+
void computeNode?.close();
|
|
119
|
+
computeNode = undefined;
|
|
105
120
|
}
|
|
106
121
|
},
|
|
107
122
|
),
|
|
@@ -114,7 +129,8 @@ export const compute = (options: ComputeOptions = {}): Extension => {
|
|
|
114
129
|
];
|
|
115
130
|
};
|
|
116
131
|
|
|
117
|
-
|
|
132
|
+
// TODO(burdon): Click to edit.
|
|
133
|
+
class ComputeWidget extends WidgetType {
|
|
118
134
|
constructor(
|
|
119
135
|
private readonly formula: string,
|
|
120
136
|
private readonly value: CellScalarValue,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type { FunctionPluginDefinition } from 'hyperformula';
|
|
6
|
+
import type { ConfigParams } from 'hyperformula/typings/ConfigParams';
|
|
7
|
+
import type { FunctionTranslationsPackage } from 'hyperformula/typings/interpreter';
|
|
8
|
+
import defaultsDeep from 'lodash.defaultsdeep';
|
|
9
|
+
|
|
10
|
+
import { type SpaceId, type Space } from '@dxos/client/echo';
|
|
11
|
+
import { Resource } from '@dxos/context';
|
|
12
|
+
import { invariant } from '@dxos/invariant';
|
|
13
|
+
import { log } from '@dxos/log';
|
|
14
|
+
|
|
15
|
+
import { HyperFormula } from '#hyperformula';
|
|
16
|
+
import { ComputeGraph } from './compute-graph';
|
|
17
|
+
import { EdgeFunctionPlugin, EdgeFunctionPluginTranslations, type FunctionContextOptions } from './functions';
|
|
18
|
+
|
|
19
|
+
export type ComputeGraphPlugin = {
|
|
20
|
+
plugin: FunctionPluginDefinition;
|
|
21
|
+
translations: FunctionTranslationsPackage;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type ComputeGraphOptions = {
|
|
25
|
+
plugins?: ComputeGraphPlugin[];
|
|
26
|
+
} & Partial<FunctionContextOptions> &
|
|
27
|
+
Partial<ConfigParams>;
|
|
28
|
+
|
|
29
|
+
export const defaultOptions: ComputeGraphOptions = {
|
|
30
|
+
licenseKey: 'gpl-v3',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const defaultPlugins: ComputeGraphPlugin[] = [
|
|
34
|
+
{
|
|
35
|
+
plugin: EdgeFunctionPlugin,
|
|
36
|
+
translations: EdgeFunctionPluginTranslations,
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Manages a collection of ComputeGraph instances for each space.
|
|
42
|
+
*
|
|
43
|
+
* [ComputePlugin] => [ComputeGraphRegistry] => [ComputeGraph(Space)] => [ComputeNode(Object)]
|
|
44
|
+
*
|
|
45
|
+
* NOTE: The ComputeGraphRegistry manages the hierarchy of resources via its root Context.
|
|
46
|
+
* NOTE: The package.json file defines the packaged #hyperformula module.
|
|
47
|
+
*/
|
|
48
|
+
// TODO(burdon): Move graph into separate plugin; isolate HF deps.
|
|
49
|
+
export class ComputeGraphRegistry extends Resource {
|
|
50
|
+
private readonly _graphs = new Map<SpaceId, ComputeGraph>();
|
|
51
|
+
|
|
52
|
+
private readonly _options: ComputeGraphOptions;
|
|
53
|
+
|
|
54
|
+
constructor(options: ComputeGraphOptions = { plugins: defaultPlugins }) {
|
|
55
|
+
super();
|
|
56
|
+
this._options = defaultsDeep({}, options, defaultOptions);
|
|
57
|
+
this._options.plugins?.forEach(({ plugin, translations }) => {
|
|
58
|
+
HyperFormula.registerFunctionPlugin(plugin, translations);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getGraph(spaceId: SpaceId): ComputeGraph | undefined {
|
|
63
|
+
return this._graphs.get(spaceId);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getOrCreateGraph(space: Space): ComputeGraph {
|
|
67
|
+
let graph = this._graphs.get(space.id);
|
|
68
|
+
if (!graph) {
|
|
69
|
+
log('create graph', { space: space.id });
|
|
70
|
+
graph = this.createGraph(space);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return graph;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
createGraph(space: Space): ComputeGraph {
|
|
77
|
+
invariant(!this._graphs.has(space.id), `ComputeGraph already exists for space: ${space.id}`);
|
|
78
|
+
const hf = HyperFormula.buildEmpty(this._options);
|
|
79
|
+
const graph = new ComputeGraph(hf, space, this._options);
|
|
80
|
+
this._graphs.set(space.id, graph);
|
|
81
|
+
return graph;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
protected override async _close() {
|
|
85
|
+
for (const graph of this._graphs.values()) {
|
|
86
|
+
await graph.close();
|
|
87
|
+
}
|
|
88
|
+
this._graphs.clear();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -13,7 +13,7 @@ import { Toolbar, Button, Input } from '@dxos/react-ui';
|
|
|
13
13
|
import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
|
|
14
14
|
import { withTheme } from '@dxos/storybook-utils';
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { testFunctionPlugins } from './testing';
|
|
17
17
|
import { createSheet } from '../defs';
|
|
18
18
|
import { useComputeGraph, useSheetModel } from '../hooks';
|
|
19
19
|
import { withComputeGraphDecorator } from '../testing';
|
|
@@ -84,7 +84,7 @@ export default {
|
|
|
84
84
|
title: 'plugin-sheet/functions',
|
|
85
85
|
decorators: [
|
|
86
86
|
withClientProvider({ types: [FunctionType, SheetType], createIdentity: true, createSpace: true }),
|
|
87
|
-
withComputeGraphDecorator({ plugins:
|
|
87
|
+
withComputeGraphDecorator({ plugins: testFunctionPlugins }),
|
|
88
88
|
withTheme,
|
|
89
89
|
],
|
|
90
90
|
render: (args: any) => <Story {...args} />,
|
|
@@ -6,78 +6,49 @@ import { type CellValue } from 'hyperformula';
|
|
|
6
6
|
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
7
7
|
|
|
8
8
|
import { Trigger } from '@dxos/async';
|
|
9
|
-
import { Client } from '@dxos/client';
|
|
10
9
|
import { create, fullyQualifiedId } from '@dxos/client/echo';
|
|
11
|
-
import { Context } from '@dxos/context';
|
|
12
|
-
import { type S } from '@dxos/echo-schema';
|
|
13
10
|
import { FunctionType } from '@dxos/plugin-script/types';
|
|
14
11
|
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { addressFromA1Notation, createSheet } from '../defs';
|
|
18
|
-
import { SheetModel } from '../model';
|
|
19
|
-
import { type CellScalarValue } from '../types';
|
|
12
|
+
import { DetailedCellError } from '#hyperformula';
|
|
13
|
+
import { TestBuilder } from './testing';
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
ctx = new Context();
|
|
15
|
+
describe('ComputeGraph', () => {
|
|
16
|
+
let testBuilder: TestBuilder;
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
testBuilder = new TestBuilder({ types: [FunctionType] });
|
|
19
|
+
await testBuilder.open();
|
|
28
20
|
});
|
|
29
21
|
afterEach(async () => {
|
|
30
|
-
await
|
|
22
|
+
await testBuilder.close();
|
|
31
23
|
});
|
|
32
24
|
|
|
33
|
-
// TODO(burdon): Replace with builder.
|
|
34
|
-
const createModel = async (types?: S.Schema<any>[]) => {
|
|
35
|
-
const client = new Client();
|
|
36
|
-
if (types) {
|
|
37
|
-
// TODO(burdon): Add to config.
|
|
38
|
-
client.addTypes(types);
|
|
39
|
-
}
|
|
40
|
-
await client.initialize();
|
|
41
|
-
await client.halo.createIdentity();
|
|
42
|
-
const space = await client.spaces.create();
|
|
43
|
-
ctx.onDispose(() => client.destroy());
|
|
44
|
-
|
|
45
|
-
const registry = new ComputeGraphRegistry({ plugins: testPlugins });
|
|
46
|
-
await registry.open();
|
|
47
|
-
ctx.onDispose(() => registry.close());
|
|
48
|
-
|
|
49
|
-
const graph = await registry.createGraph(space);
|
|
50
|
-
|
|
51
|
-
const sheet = createSheet({ rows: 5, columns: 5 });
|
|
52
|
-
const model = new SheetModel(graph, sheet);
|
|
53
|
-
await model.open();
|
|
54
|
-
|
|
55
|
-
return { space, graph, model };
|
|
56
|
-
};
|
|
57
|
-
|
|
58
25
|
test('map functions', async () => {
|
|
59
|
-
const
|
|
26
|
+
const space = await testBuilder.client.spaces.create();
|
|
27
|
+
const graph = testBuilder.registry.createGraph(space);
|
|
28
|
+
await graph.open();
|
|
60
29
|
|
|
61
30
|
// Create script.
|
|
62
31
|
const trigger = new Trigger();
|
|
63
32
|
graph.update.once(() => trigger.wake());
|
|
64
|
-
const
|
|
33
|
+
const functionObject = space.db.add(create(FunctionType, { version: 1, binding: 'TEST' }));
|
|
65
34
|
await trigger.wait();
|
|
66
|
-
|
|
35
|
+
const functions = graph.getFunctions({ echo: true });
|
|
36
|
+
expect(functions).to.toHaveLength(1);
|
|
67
37
|
|
|
68
38
|
const id = graph.mapFunctionBindingToId('TEST()');
|
|
69
|
-
expect(id).to.eq(`${fullyQualifiedId(
|
|
39
|
+
expect(id).to.eq(`${fullyQualifiedId(functionObject)}()`);
|
|
70
40
|
});
|
|
71
41
|
|
|
72
42
|
test('cross-node references', async () => {
|
|
73
|
-
const
|
|
43
|
+
const space = await testBuilder.client.spaces.create();
|
|
44
|
+
const graph = testBuilder.registry.createGraph(space);
|
|
74
45
|
|
|
75
46
|
// Create nodes.
|
|
76
|
-
const node1 = await graph.getOrCreateNode('node-1');
|
|
77
|
-
const node2 = await graph.getOrCreateNode('node-2');
|
|
47
|
+
const node1 = await graph.getOrCreateNode('node-1').open();
|
|
48
|
+
const node2 = await graph.getOrCreateNode('node-2').open();
|
|
49
|
+
expect(graph.hf.getSheetNames()).to.toHaveLength(2);
|
|
78
50
|
|
|
79
51
|
{
|
|
80
|
-
expect(graph.hf.getSheetNames()).to.toHaveLength(3);
|
|
81
52
|
node1.graph.hf.setCellContents({ sheet: node1.sheetId, row: 0, col: 0 }, [[100, 200, 300, '=SUM(A1:C1)']]);
|
|
82
53
|
node2.graph.hf.setCellContents({ sheet: node2.sheetId, row: 0, col: 0 }, "='node-1'!D1");
|
|
83
54
|
const value1 = node1.graph.hf.getCellValue({ sheet: node1.sheetId, col: 3, row: 0 });
|
|
@@ -101,27 +72,16 @@ describe('compute graph', () => {
|
|
|
101
72
|
}
|
|
102
73
|
});
|
|
103
74
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Get initial value (null).
|
|
118
|
-
const v1 = model.getValue(addressFromA1Notation('A1'));
|
|
119
|
-
expect(v1).to.be.null;
|
|
120
|
-
expect(graph.context.info.invocations.TEST).to.eq(undefined);
|
|
121
|
-
|
|
122
|
-
// Wait until async update triggered.
|
|
123
|
-
const v2 = await trigger.wait();
|
|
124
|
-
expect(v2).not.to.be.null;
|
|
125
|
-
expect(graph.context.info.invocations.TEST).to.eq(1);
|
|
75
|
+
// TODO(burdon): Dynamically load node/model based on dependencies.
|
|
76
|
+
// - Create dependency then close model.
|
|
77
|
+
test('dynamic loading', async () => {
|
|
78
|
+
const space = await testBuilder.client.spaces.create();
|
|
79
|
+
const graph = testBuilder.registry.createGraph(space);
|
|
80
|
+
|
|
81
|
+
const node1 = await graph.getOrCreateNode('node-1').open();
|
|
82
|
+
node1.graph.hf.setCellContents({ sheet: node1.sheetId, row: 0, col: 0 }, "='node-2'!A1");
|
|
83
|
+
const value1 = node1.graph.hf.getCellValue({ sheet: node1.sheetId, col: 0, row: 0 });
|
|
84
|
+
expect(value1).to.be.instanceof(DetailedCellError);
|
|
85
|
+
expect((value1 as DetailedCellError).type).to.eq('REF');
|
|
126
86
|
});
|
|
127
87
|
});
|