@dxos/plugin-sheet 0.6.12-main.ed7cda7 → 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/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 +4 -4
- 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
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import { type FunctionPluginDefinition } from 'hyperformula';
|
|
6
6
|
import { type ConfigParams } from 'hyperformula/typings/ConfigParams';
|
|
7
|
+
import { type Listeners } from 'hyperformula/typings/Emitter';
|
|
7
8
|
import { type FunctionTranslationsPackage } from 'hyperformula/typings/interpreter';
|
|
9
|
+
import defaultsDeep from 'lodash.defaultsdeep';
|
|
8
10
|
|
|
9
11
|
import { Event } from '@dxos/async';
|
|
10
12
|
import { type SpaceId, type Space, Filter, fullyQualifiedId } from '@dxos/client/echo';
|
|
@@ -15,7 +17,7 @@ import { log } from '@dxos/log';
|
|
|
15
17
|
import { FunctionType } from '@dxos/plugin-script/types';
|
|
16
18
|
import { nonNullable } from '@dxos/util';
|
|
17
19
|
|
|
18
|
-
import { HyperFormula } from '#hyperformula';
|
|
20
|
+
import { ExportedCellChange, HyperFormula } from '#hyperformula';
|
|
19
21
|
import { FunctionContext, type FunctionContextOptions } from './async-function';
|
|
20
22
|
import { ComputeNode } from './compute-node';
|
|
21
23
|
import { EdgeFunctionPlugin, EdgeFunctionPluginTranslations } from './edge-function';
|
|
@@ -43,14 +45,15 @@ export type ComputeGraphOptions = {
|
|
|
43
45
|
|
|
44
46
|
export const defaultOptions: ComputeGraphOptions = {
|
|
45
47
|
licenseKey: 'gpl-v3',
|
|
46
|
-
plugins: [
|
|
47
|
-
{
|
|
48
|
-
plugin: EdgeFunctionPlugin,
|
|
49
|
-
translations: EdgeFunctionPluginTranslations,
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
48
|
};
|
|
53
49
|
|
|
50
|
+
export const defaultPlugins: ComputeGraphPlugin[] = [
|
|
51
|
+
{
|
|
52
|
+
plugin: EdgeFunctionPlugin,
|
|
53
|
+
translations: EdgeFunctionPluginTranslations,
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
54
57
|
/**
|
|
55
58
|
* Marker for sheets that are managed by an ECHO object.
|
|
56
59
|
*/
|
|
@@ -59,34 +62,29 @@ export const createSheetName = (id: string) => `${PREFIX}${id}`;
|
|
|
59
62
|
export const getSheetId = (name: string): string | undefined =>
|
|
60
63
|
name.startsWith(PREFIX) ? name.slice(PREFIX.length) : undefined;
|
|
61
64
|
|
|
62
|
-
/**
|
|
63
|
-
* NOTE: Async imports to decouple hyperformula deps.
|
|
64
|
-
*/
|
|
65
|
-
export const createComputeGraphRegistry = (options: Partial<FunctionContextOptions> = {}) => {
|
|
66
|
-
return new ComputeGraphRegistry({
|
|
67
|
-
...defaultOptions,
|
|
68
|
-
...options,
|
|
69
|
-
});
|
|
70
|
-
};
|
|
71
|
-
|
|
72
65
|
/**
|
|
73
66
|
* Manages a collection of ComputeGraph instances for each space.
|
|
74
67
|
*
|
|
75
68
|
* [ComputePlugin] => [ComputeGraphRegistry] => [ComputeGraph(Space)] => [ComputeNode(Object)]
|
|
69
|
+
*
|
|
70
|
+
* NOTE: The ComputeGraphRegistry manages the hierarchy of resources via its root Context.
|
|
76
71
|
*/
|
|
77
72
|
// TODO(burdon): Move graph into separate plugin; isolate HF deps.
|
|
78
73
|
export class ComputeGraphRegistry extends Resource {
|
|
79
|
-
private readonly
|
|
74
|
+
private readonly _graphs = new Map<SpaceId, ComputeGraph>();
|
|
80
75
|
|
|
81
|
-
|
|
76
|
+
private readonly _options: ComputeGraphOptions;
|
|
77
|
+
|
|
78
|
+
constructor(options: ComputeGraphOptions = { plugins: defaultPlugins }) {
|
|
82
79
|
super();
|
|
80
|
+
this._options = defaultsDeep({}, options, defaultOptions);
|
|
83
81
|
this._options.plugins?.forEach(({ plugin, translations }) => {
|
|
84
82
|
HyperFormula.registerFunctionPlugin(plugin, translations);
|
|
85
83
|
});
|
|
86
84
|
}
|
|
87
85
|
|
|
88
86
|
getGraph(spaceId: SpaceId): ComputeGraph | undefined {
|
|
89
|
-
return this.
|
|
87
|
+
return this._graphs.get(spaceId);
|
|
90
88
|
}
|
|
91
89
|
|
|
92
90
|
async getOrCreateGraph(space: Space): Promise<ComputeGraph> {
|
|
@@ -100,22 +98,29 @@ export class ComputeGraphRegistry extends Resource {
|
|
|
100
98
|
}
|
|
101
99
|
|
|
102
100
|
async createGraph(space: Space): Promise<ComputeGraph> {
|
|
103
|
-
invariant(!this.
|
|
101
|
+
invariant(!this._graphs.has(space.id), `ComputeGraph already exists for space: ${space.id}`);
|
|
104
102
|
const hf = HyperFormula.buildEmpty(this._options);
|
|
105
103
|
const graph = new ComputeGraph(hf, space, this._options);
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
this._graphs.set(space.id, graph);
|
|
105
|
+
await graph.open();
|
|
108
106
|
return graph;
|
|
109
107
|
}
|
|
108
|
+
|
|
109
|
+
protected override async _close() {
|
|
110
|
+
for (const graph of this._graphs.values()) {
|
|
111
|
+
await graph.close();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
110
114
|
}
|
|
111
115
|
|
|
116
|
+
export type ComputeGraphEvent = 'functionsUpdated';
|
|
117
|
+
|
|
112
118
|
/**
|
|
113
119
|
* Per-space compute and dependency graph.
|
|
114
120
|
* Consists of multiple ComputeNode (corresponding to a HyperFormula sheet).
|
|
115
121
|
* Manages the set of custom functions.
|
|
116
122
|
* HyperFormula manages the dependency graph.
|
|
117
123
|
*/
|
|
118
|
-
// TODO(burdon): Tests.
|
|
119
124
|
export class ComputeGraph extends Resource {
|
|
120
125
|
public readonly id = `graph-${PublicKey.random().truncate()}`;
|
|
121
126
|
|
|
@@ -125,13 +130,10 @@ export class ComputeGraph extends Resource {
|
|
|
125
130
|
// Cached function objects.
|
|
126
131
|
private _functions: FunctionType[] = [];
|
|
127
132
|
|
|
128
|
-
|
|
129
|
-
public readonly context = new FunctionContext(this._hf, this._space, this.refresh.bind(this), this._options);
|
|
133
|
+
public readonly update = new Event<{ type: ComputeGraphEvent }>();
|
|
130
134
|
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
// TODO(burdon): Event propagation.
|
|
134
|
-
public readonly update = new Event();
|
|
135
|
+
// The context is passed to all functions.
|
|
136
|
+
public readonly context = new FunctionContext(this._hf, this._space, this._options);
|
|
135
137
|
|
|
136
138
|
constructor(
|
|
137
139
|
private readonly _hf: HyperFormula,
|
|
@@ -139,21 +141,36 @@ export class ComputeGraph extends Resource {
|
|
|
139
141
|
private readonly _options?: Partial<FunctionContextOptions>,
|
|
140
142
|
) {
|
|
141
143
|
super();
|
|
142
|
-
|
|
143
144
|
this._hf.updateConfig({ context: this.context });
|
|
145
|
+
// TODO(burdon): If debounce then aggregate changes.
|
|
146
|
+
const onValuesUpdate: Listeners['valuesUpdated'] = (changes) => {
|
|
147
|
+
for (const change of changes) {
|
|
148
|
+
if (change instanceof ExportedCellChange) {
|
|
149
|
+
const { sheet } = change;
|
|
150
|
+
const node = this._nodes.get(sheet);
|
|
151
|
+
if (node) {
|
|
152
|
+
node.update.emit({ type: 'valuesUpdated', change });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
this._hf.on('valuesUpdated', onValuesUpdate);
|
|
159
|
+
this._ctx.onDispose(() => this._hf.off('valuesUpdated', onValuesUpdate));
|
|
144
160
|
}
|
|
145
161
|
|
|
146
|
-
// TODO(burdon): Remove.
|
|
147
162
|
get hf() {
|
|
148
163
|
return this._hf;
|
|
149
164
|
}
|
|
150
165
|
|
|
151
|
-
refresh() {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
166
|
+
// refresh() {
|
|
167
|
+
// log('refresh', { id: this.id });
|
|
168
|
+
// this.update.emit();
|
|
169
|
+
// }
|
|
155
170
|
|
|
156
|
-
getFunctions(
|
|
171
|
+
getFunctions(
|
|
172
|
+
{ standard, echo }: { standard?: boolean; echo?: boolean } = { standard: true, echo: true },
|
|
173
|
+
): FunctionDefinition[] {
|
|
157
174
|
return [
|
|
158
175
|
...(standard
|
|
159
176
|
? this._hf
|
|
@@ -172,19 +189,19 @@ export class ComputeGraph extends Resource {
|
|
|
172
189
|
// This would enable on-the-fly instantiation of new models when then are referenced.
|
|
173
190
|
// E.g., Cross-object reference would be stored as "ObjectId!A1"
|
|
174
191
|
// The graph would then load the object and create a ComputeNode (model) of the appropriate type.
|
|
175
|
-
getOrCreateNode(name: string): ComputeNode {
|
|
192
|
+
async getOrCreateNode(name: string): Promise<ComputeNode> {
|
|
176
193
|
invariant(name.length);
|
|
177
194
|
if (!this._hf.doesSheetExist(name)) {
|
|
178
195
|
log.info('created node', { space: this._space?.id, name });
|
|
179
196
|
this._hf.addSheet(name);
|
|
180
|
-
this.update.emit();
|
|
197
|
+
// this.update.emit();
|
|
181
198
|
}
|
|
182
199
|
|
|
183
200
|
const sheetId = this._hf.getSheetId(name);
|
|
184
201
|
invariant(sheetId !== undefined);
|
|
185
202
|
|
|
186
|
-
// TODO(burdon): Chain context?
|
|
187
203
|
const node = new ComputeNode(this, sheetId);
|
|
204
|
+
await node.open();
|
|
188
205
|
this._nodes.set(sheetId, node);
|
|
189
206
|
return node;
|
|
190
207
|
}
|
|
@@ -281,10 +298,16 @@ export class ComputeGraph extends Resource {
|
|
|
281
298
|
const query = this._space.db.query(Filter.schema(FunctionType));
|
|
282
299
|
const unsubscribe = query.subscribe(({ objects }) => {
|
|
283
300
|
this._functions = objects.filter(({ binding }) => binding);
|
|
284
|
-
this.update.emit();
|
|
301
|
+
this.update.emit({ type: 'functionsUpdated' });
|
|
285
302
|
});
|
|
286
303
|
|
|
287
304
|
this._ctx.onDispose(unsubscribe);
|
|
288
305
|
}
|
|
289
306
|
}
|
|
307
|
+
|
|
308
|
+
protected override async _close() {
|
|
309
|
+
for (const node of this._nodes.values()) {
|
|
310
|
+
await node.close();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
290
313
|
}
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { type Listeners } from 'hyperformula/typings/Emitter';
|
|
6
|
+
import { type ExportedCellChange } from 'hyperformula/typings/Exporter';
|
|
7
|
+
|
|
5
8
|
import { Event } from '@dxos/async';
|
|
6
9
|
import { Resource } from '@dxos/context';
|
|
7
10
|
|
|
@@ -10,13 +13,17 @@ import { type ComputeGraph } from './compute-graph';
|
|
|
10
13
|
import { type CellAddress } from '../defs';
|
|
11
14
|
import { type CellScalarValue } from '../types';
|
|
12
15
|
|
|
16
|
+
export type ComputeNodeEvent = {
|
|
17
|
+
type: keyof Listeners;
|
|
18
|
+
change?: ExportedCellChange;
|
|
19
|
+
};
|
|
20
|
+
|
|
13
21
|
/**
|
|
14
22
|
* Individual "sheet" (typically corresponds to an ECHO object).
|
|
15
23
|
*/
|
|
16
24
|
// TODO(burdon): Factor out common HF wrapper from from SheetModel.
|
|
17
25
|
export class ComputeNode extends Resource {
|
|
18
|
-
|
|
19
|
-
public readonly update = new Event();
|
|
26
|
+
public readonly update = new Event<ComputeNodeEvent>();
|
|
20
27
|
|
|
21
28
|
constructor(
|
|
22
29
|
private readonly _graph: ComputeGraph,
|
|
@@ -25,13 +32,12 @@ export class ComputeNode extends Resource {
|
|
|
25
32
|
super();
|
|
26
33
|
}
|
|
27
34
|
|
|
28
|
-
// TODO(burdon): Remove?
|
|
29
35
|
get graph() {
|
|
30
36
|
return this._graph;
|
|
31
37
|
}
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
clear() {
|
|
40
|
+
this._graph.hf.clearSheet(this.sheetId);
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
getValue(cell: CellAddress): CellScalarValue {
|
|
@@ -48,4 +54,9 @@ export class ComputeNode extends Resource {
|
|
|
48
54
|
typeof value === 'string' && value.charAt(0) === '=' ? this._graph.mapFormulaToNative(value) : value;
|
|
49
55
|
this._graph.hf.setCellContents({ sheet: this.sheetId, row: cell.row, col: cell.col }, [[mappedValue]]);
|
|
50
56
|
}
|
|
57
|
+
|
|
58
|
+
protected override async _open() {
|
|
59
|
+
// const unsubscribe = this._graph.update.on(this.update.emit);
|
|
60
|
+
// this._ctx.onDispose(unsubscribe);
|
|
61
|
+
}
|
|
51
62
|
}
|
|
@@ -74,8 +74,7 @@ EdgeFunctionPlugin.implementedFunctions = {
|
|
|
74
74
|
// Binding
|
|
75
75
|
{ argumentType: FunctionArgumentType.STRING },
|
|
76
76
|
|
|
77
|
-
// Remote function arguments (currently supporting up to
|
|
78
|
-
{ argumentType: FunctionArgumentType.ANY, optionalArg: true },
|
|
77
|
+
// Remote function arguments (currently supporting up to 8).
|
|
79
78
|
{ argumentType: FunctionArgumentType.ANY, optionalArg: true },
|
|
80
79
|
{ argumentType: FunctionArgumentType.ANY, optionalArg: true },
|
|
81
80
|
{ argumentType: FunctionArgumentType.ANY, optionalArg: true },
|
|
@@ -8,8 +8,9 @@ import { type ProcedureAst } from 'hyperformula/typings/parser';
|
|
|
8
8
|
import { getDeep } from '@dxos/util';
|
|
9
9
|
|
|
10
10
|
import { FunctionArgumentType } from '#hyperformula';
|
|
11
|
-
import { type AsyncFunction, FunctionPluginAsync } from '
|
|
12
|
-
import {
|
|
11
|
+
import { type AsyncFunction, FunctionPluginAsync } from '../async-function';
|
|
12
|
+
import { type ComputeGraphPlugin } from '../compute-graph';
|
|
13
|
+
import { parseNumberString } from '../util';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* https://hyperformula.handsontable.com/guide/custom-functions.html#add-a-simple-custom-function
|
|
@@ -64,3 +65,10 @@ export const CustomPluginTranslations = {
|
|
|
64
65
|
CRYPTO: 'CRYPTO',
|
|
65
66
|
},
|
|
66
67
|
};
|
|
68
|
+
|
|
69
|
+
export const testPlugins: ComputeGraphPlugin[] = [
|
|
70
|
+
{
|
|
71
|
+
plugin: CustomPlugin,
|
|
72
|
+
translations: CustomPluginTranslations,
|
|
73
|
+
},
|
|
74
|
+
];
|
|
@@ -14,14 +14,14 @@ import { withTheme } from '@dxos/storybook-utils';
|
|
|
14
14
|
import { ComputeGraphContextProvider } from '../components';
|
|
15
15
|
import { createSheet } from '../defs';
|
|
16
16
|
import { useComputeGraph, useSheetModel } from '../hooks';
|
|
17
|
-
import {
|
|
17
|
+
import { withComputeGraphDecorator } from '../testing';
|
|
18
18
|
import { SheetType } from '../types';
|
|
19
19
|
|
|
20
20
|
const Story = () => {
|
|
21
21
|
const space = useSpace();
|
|
22
22
|
const graph = useComputeGraph(space);
|
|
23
23
|
const [sheet, setSheet] = useState<SheetType>();
|
|
24
|
-
const model = useSheetModel(
|
|
24
|
+
const model = useSheetModel(graph, sheet);
|
|
25
25
|
useEffect(() => {
|
|
26
26
|
if (space) {
|
|
27
27
|
const sheet = space.db.add(createSheet());
|
|
@@ -41,7 +41,7 @@ export default {
|
|
|
41
41
|
component: ComputeGraphContextProvider,
|
|
42
42
|
decorators: [
|
|
43
43
|
withClientProvider({ types: [SheetType], createIdentity: true, createSpace: true }),
|
|
44
|
-
|
|
44
|
+
withComputeGraphDecorator(),
|
|
45
45
|
withTheme,
|
|
46
46
|
],
|
|
47
47
|
render: (args: any) => <Story {...args} />,
|
|
@@ -16,5 +16,6 @@ import { type ComputeGraph } from '../graph';
|
|
|
16
16
|
*/
|
|
17
17
|
export const useComputeGraph = (space?: Space): ComputeGraph | undefined => {
|
|
18
18
|
const { registry } = useContext(ComputeGraphContext) ?? raise(new Error('Missing ComputeGraphContext'));
|
|
19
|
-
|
|
19
|
+
const [graph] = useAsyncState(async () => space && registry.getOrCreateGraph(space), [space, registry]);
|
|
20
|
+
return graph;
|
|
20
21
|
};
|
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { useEffect, useState } from 'react';
|
|
6
6
|
|
|
7
|
-
import { type
|
|
8
|
-
|
|
9
|
-
import { useComputeGraph } from './useComputeGraph';
|
|
7
|
+
import { type ComputeGraph } from '../graph';
|
|
10
8
|
import { SheetModel } from '../model';
|
|
11
9
|
import { type SheetType } from '../types';
|
|
12
10
|
|
|
@@ -15,14 +13,13 @@ export type UseSheetModelOptions = {
|
|
|
15
13
|
};
|
|
16
14
|
|
|
17
15
|
export const useSheetModel = (
|
|
18
|
-
|
|
16
|
+
graph?: ComputeGraph,
|
|
19
17
|
sheet?: SheetType,
|
|
20
18
|
{ readonly }: UseSheetModelOptions = {},
|
|
21
19
|
): SheetModel | undefined => {
|
|
22
|
-
const graph = useComputeGraph(space);
|
|
23
20
|
const [model, setModel] = useState<SheetModel>();
|
|
24
21
|
useEffect(() => {
|
|
25
|
-
if (!
|
|
22
|
+
if (!graph || !sheet) {
|
|
26
23
|
return;
|
|
27
24
|
}
|
|
28
25
|
|
|
@@ -37,7 +34,7 @@ export const useSheetModel = (
|
|
|
37
34
|
clearTimeout(t);
|
|
38
35
|
void model?.close();
|
|
39
36
|
};
|
|
40
|
-
}, [
|
|
37
|
+
}, [graph, sheet, readonly]);
|
|
41
38
|
|
|
42
39
|
return model;
|
|
43
40
|
};
|
package/src/model/sheet-model.ts
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
MAX_ROWS,
|
|
23
23
|
} from '../defs';
|
|
24
24
|
import { addressFromIndex, addressToIndex, initialize, insertIndices, ReadonlyException } from '../defs';
|
|
25
|
-
import { type ComputeNode, type ComputeGraph, createSheetName } from '../graph';
|
|
25
|
+
import { type ComputeNode, type ComputeGraph, createSheetName, type ComputeNodeEvent } from '../graph';
|
|
26
26
|
import { type CellScalarValue, type CellValue, type SheetType, ValueTypeEnum } from '../types';
|
|
27
27
|
|
|
28
28
|
const typeMap: Record<string, ValueTypeEnum> = {
|
|
@@ -64,9 +64,10 @@ export type SheetModelOptions = {
|
|
|
64
64
|
export class SheetModel extends Resource {
|
|
65
65
|
public readonly id = `model-${PublicKey.random().truncate()}`;
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
// Wraps compute node.
|
|
68
|
+
public readonly update = new Event<ComputeNodeEvent>();
|
|
68
69
|
|
|
69
|
-
private
|
|
70
|
+
private _node?: ComputeNode;
|
|
70
71
|
|
|
71
72
|
constructor(
|
|
72
73
|
private readonly _graph: ComputeGraph,
|
|
@@ -74,9 +75,6 @@ export class SheetModel extends Resource {
|
|
|
74
75
|
private readonly _options: SheetModelOptions = {},
|
|
75
76
|
) {
|
|
76
77
|
super();
|
|
77
|
-
// TODO(burdon): SheetModel should extend ComputeNode and be constructed via the graph.
|
|
78
|
-
this._node = this._graph.getOrCreateNode(createSheetName(this._sheet.id));
|
|
79
|
-
this.reset();
|
|
80
78
|
}
|
|
81
79
|
|
|
82
80
|
get graph() {
|
|
@@ -104,12 +102,15 @@ export class SheetModel extends Resource {
|
|
|
104
102
|
protected override async _open() {
|
|
105
103
|
log('initialize', { id: this.id });
|
|
106
104
|
initialize(this._sheet);
|
|
107
|
-
this.reset();
|
|
108
105
|
|
|
109
|
-
// TODO(burdon):
|
|
106
|
+
// TODO(burdon): SheetModel should extend ComputeNode and be constructed via the graph.
|
|
107
|
+
this._node = await this._graph.getOrCreateNode(createSheetName(this._sheet.id));
|
|
108
|
+
|
|
110
109
|
// Listen for model updates (e.g., async calculations).
|
|
111
|
-
const unsubscribe = this.
|
|
110
|
+
const unsubscribe = this._node.update.on((event) => this.update.emit(event));
|
|
112
111
|
this._ctx.onDispose(unsubscribe);
|
|
112
|
+
|
|
113
|
+
this.reset();
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
/**
|
|
@@ -118,8 +119,10 @@ export class SheetModel extends Resource {
|
|
|
118
119
|
* @deprecated
|
|
119
120
|
*/
|
|
120
121
|
reset() {
|
|
121
|
-
|
|
122
|
+
invariant(this._node);
|
|
123
|
+
this._node.graph.hf.clearSheet(this._node.sheetId);
|
|
122
124
|
Object.entries(this._sheet.cells).forEach(([key, { value }]) => {
|
|
125
|
+
invariant(this._node);
|
|
123
126
|
const { col, row } = addressFromIndex(this._sheet, key);
|
|
124
127
|
if (typeof value === 'string' && value.charAt(0) === '=') {
|
|
125
128
|
value = this._graph.mapFormulaToNative(
|
|
@@ -127,7 +130,7 @@ export class SheetModel extends Resource {
|
|
|
127
130
|
);
|
|
128
131
|
}
|
|
129
132
|
|
|
130
|
-
this._node.hf.setCellContents({ sheet: this._node.sheetId, row, col }, value);
|
|
133
|
+
this._node.graph.hf.setCellContents({ sheet: this._node.sheetId, row, col }, value);
|
|
131
134
|
});
|
|
132
135
|
}
|
|
133
136
|
|
|
@@ -139,7 +142,7 @@ export class SheetModel extends Resource {
|
|
|
139
142
|
*/
|
|
140
143
|
// TODO(burdon): Remove.
|
|
141
144
|
recalculate() {
|
|
142
|
-
this._node.hf.rebuildAndRecalculate();
|
|
145
|
+
this._node?.graph.hf.rebuildAndRecalculate();
|
|
143
146
|
}
|
|
144
147
|
|
|
145
148
|
insertRows(i: number, n = 1) {
|
|
@@ -161,9 +164,10 @@ export class SheetModel extends Resource {
|
|
|
161
164
|
* Clear range of values.
|
|
162
165
|
*/
|
|
163
166
|
clear(range: CellRange) {
|
|
167
|
+
invariant(this._node);
|
|
164
168
|
const topLeft = getTopLeft(range);
|
|
165
169
|
const values = this._iterRange(range, () => null);
|
|
166
|
-
this._node.hf.setCellContents(toSimpleCellAddress(this._node.sheetId, topLeft), values);
|
|
170
|
+
this._node.graph.hf.setCellContents(toSimpleCellAddress(this._node.sheetId, topLeft), values);
|
|
167
171
|
this._iterRange(range, (cell) => {
|
|
168
172
|
const idx = addressToIndex(this._sheet, cell);
|
|
169
173
|
delete this._sheet.cells[idx];
|
|
@@ -171,7 +175,8 @@ export class SheetModel extends Resource {
|
|
|
171
175
|
}
|
|
172
176
|
|
|
173
177
|
cut(range: CellRange) {
|
|
174
|
-
|
|
178
|
+
invariant(this._node);
|
|
179
|
+
this._node.graph.hf.cut(toModelRange(this._node.sheetId, range));
|
|
175
180
|
this._iterRange(range, (cell) => {
|
|
176
181
|
const idx = addressToIndex(this._sheet, cell);
|
|
177
182
|
delete this._sheet.cells[idx];
|
|
@@ -179,12 +184,14 @@ export class SheetModel extends Resource {
|
|
|
179
184
|
}
|
|
180
185
|
|
|
181
186
|
copy(range: CellRange) {
|
|
182
|
-
|
|
187
|
+
invariant(this._node);
|
|
188
|
+
this._node.graph.hf.copy(toModelRange(this._node.sheetId, range));
|
|
183
189
|
}
|
|
184
190
|
|
|
185
191
|
paste(cell: CellAddress) {
|
|
186
|
-
|
|
187
|
-
|
|
192
|
+
invariant(this._node);
|
|
193
|
+
if (!this._node.graph.hf.isClipboardEmpty()) {
|
|
194
|
+
const changes = this._node.graph.hf.paste(toSimpleCellAddress(this._node.sheetId, cell));
|
|
188
195
|
for (const change of changes) {
|
|
189
196
|
if (change instanceof ExportedCellChange) {
|
|
190
197
|
const { address, newValue } = change;
|
|
@@ -197,16 +204,18 @@ export class SheetModel extends Resource {
|
|
|
197
204
|
|
|
198
205
|
// TODO(burdon): Display undo/redo state.
|
|
199
206
|
undo() {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
this.
|
|
207
|
+
invariant(this._node);
|
|
208
|
+
if (this._node.graph.hf.isThereSomethingToUndo()) {
|
|
209
|
+
this._node.graph.hf.undo();
|
|
210
|
+
// this.update.emit();
|
|
203
211
|
}
|
|
204
212
|
}
|
|
205
213
|
|
|
206
214
|
redo() {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
this.
|
|
215
|
+
invariant(this._node);
|
|
216
|
+
if (this._node.graph.hf.isThereSomethingToRedo()) {
|
|
217
|
+
this._node.graph.hf.redo();
|
|
218
|
+
// this.update.emit();
|
|
210
219
|
}
|
|
211
220
|
}
|
|
212
221
|
|
|
@@ -246,7 +255,8 @@ export class SheetModel extends Resource {
|
|
|
246
255
|
*/
|
|
247
256
|
getValue(cell: CellAddress): CellScalarValue {
|
|
248
257
|
// Applies rounding and post-processing.
|
|
249
|
-
|
|
258
|
+
invariant(this._node);
|
|
259
|
+
const value = this._node.graph.hf.getCellValue(toSimpleCellAddress(this._node.sheetId, cell));
|
|
250
260
|
if (value instanceof DetailedCellError) {
|
|
251
261
|
return value.toString();
|
|
252
262
|
}
|
|
@@ -258,8 +268,9 @@ export class SheetModel extends Resource {
|
|
|
258
268
|
* Get value type.
|
|
259
269
|
*/
|
|
260
270
|
getValueType(cell: CellAddress): ValueTypeEnum {
|
|
271
|
+
invariant(this._node);
|
|
261
272
|
const addr = toSimpleCellAddress(this._node.sheetId, cell);
|
|
262
|
-
const type = this._node.hf.getCellValueDetailedType(addr);
|
|
273
|
+
const type = this._node.graph.hf.getCellValueDetailedType(addr);
|
|
263
274
|
return typeMap[type];
|
|
264
275
|
}
|
|
265
276
|
|
|
@@ -267,6 +278,7 @@ export class SheetModel extends Resource {
|
|
|
267
278
|
* Sets the value, updating the sheet and engine.
|
|
268
279
|
*/
|
|
269
280
|
setValue(cell: CellAddress, value: CellScalarValue) {
|
|
281
|
+
invariant(this._node);
|
|
270
282
|
if (this._options.readonly) {
|
|
271
283
|
throw new ReadonlyException();
|
|
272
284
|
}
|
|
@@ -288,7 +300,7 @@ export class SheetModel extends Resource {
|
|
|
288
300
|
}
|
|
289
301
|
|
|
290
302
|
// Insert into engine.
|
|
291
|
-
this._node.hf.setCellContents({ sheet: this._node.sheetId, row: cell.row, col: cell.col }, [
|
|
303
|
+
this._node.graph.hf.setCellContents({ sheet: this._node.sheetId, row: cell.row, col: cell.col }, [
|
|
292
304
|
[typeof value === 'string' && value.charAt(0) === '=' ? this._graph.mapFormulaToNative(value) : value],
|
|
293
305
|
]);
|
|
294
306
|
|
|
@@ -386,14 +398,17 @@ export class SheetModel extends Resource {
|
|
|
386
398
|
}
|
|
387
399
|
|
|
388
400
|
toDateTime(num: number): SimpleDateTime {
|
|
389
|
-
|
|
401
|
+
invariant(this._node);
|
|
402
|
+
return this._node.graph.hf.numberToDateTime(num) as SimpleDateTime;
|
|
390
403
|
}
|
|
391
404
|
|
|
392
405
|
toDate(num: number): SimpleDate {
|
|
393
|
-
|
|
406
|
+
invariant(this._node);
|
|
407
|
+
return this._node.graph.hf.numberToDate(num) as SimpleDate;
|
|
394
408
|
}
|
|
395
409
|
|
|
396
410
|
toTime(num: number): SimpleDate {
|
|
397
|
-
|
|
411
|
+
invariant(this._node);
|
|
412
|
+
return this._node.graph.hf.numberToTime(num) as SimpleDate;
|
|
398
413
|
}
|
|
399
414
|
}
|
package/src/testing/testing.tsx
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import type
|
|
5
|
+
import { type Decorator } from '@storybook/react';
|
|
6
6
|
import React, { useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { type Space } from '@dxos/react-client/echo';
|
|
@@ -10,14 +10,13 @@ import { useAsyncState } from '@dxos/react-hooks';
|
|
|
10
10
|
|
|
11
11
|
import { ComputeGraphContextProvider } from '../components';
|
|
12
12
|
import { createSheet } from '../defs';
|
|
13
|
-
import { type ComputeGraph, ComputeGraphRegistry } from '../graph';
|
|
13
|
+
import { type ComputeGraph, type ComputeGraphOptions, ComputeGraphRegistry } from '../graph';
|
|
14
14
|
import { type CellValue, type CreateSheetOptions } from '../types';
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
const testSheetName = 'test';
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
B1: { value: 'Qty' },
|
|
18
|
+
export const createTestCells = (): Record<string, CellValue> => ({
|
|
19
|
+
B1: { value: 'Qty2' },
|
|
21
20
|
B3: { value: 1 },
|
|
22
21
|
B4: { value: 2 },
|
|
23
22
|
B5: { value: 3 },
|
|
@@ -45,7 +44,7 @@ export const createCells = (): Record<string, CellValue> => ({
|
|
|
45
44
|
});
|
|
46
45
|
|
|
47
46
|
export const useTestSheet = (space?: Space, graph?: ComputeGraph, options?: CreateSheetOptions) => {
|
|
48
|
-
|
|
47
|
+
const [sheet] = useAsyncState(async () => {
|
|
49
48
|
if (!space || !graph) {
|
|
50
49
|
return;
|
|
51
50
|
}
|
|
@@ -54,13 +53,16 @@ export const useTestSheet = (space?: Space, graph?: ComputeGraph, options?: Crea
|
|
|
54
53
|
space.db.add(sheet);
|
|
55
54
|
return sheet;
|
|
56
55
|
}, [space, graph]);
|
|
56
|
+
return sheet;
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
export const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
export const withComputeGraphDecorator =
|
|
60
|
+
(options?: ComputeGraphOptions): Decorator =>
|
|
61
|
+
(Story) => {
|
|
62
|
+
const [registry] = useState(new ComputeGraphRegistry(options));
|
|
63
|
+
return (
|
|
64
|
+
<ComputeGraphContextProvider registry={registry}>
|
|
65
|
+
<Story />
|
|
66
|
+
</ComputeGraphContextProvider>
|
|
67
|
+
);
|
|
68
|
+
};
|
package/src/types.ts
CHANGED
|
@@ -102,7 +102,7 @@ export const RowColumnMeta = S.Struct({
|
|
|
102
102
|
// TODO(wittjosiah): Migrate typename to remove `Type` suffix.
|
|
103
103
|
// TODO(wittjosiah): Rename title to name to align with other schemas.
|
|
104
104
|
export class SheetType extends TypedObject({ typename: 'dxos.org/type/SheetType', version: '0.1.0' })({
|
|
105
|
-
|
|
105
|
+
name: S.optional(S.String),
|
|
106
106
|
|
|
107
107
|
// Sparse map of cells referenced by index.
|
|
108
108
|
cells: S.mutable(S.Record(S.String, S.mutable(CellValue))),
|
|
@@ -132,6 +132,6 @@ export type SheetSize = {
|
|
|
132
132
|
};
|
|
133
133
|
|
|
134
134
|
export type CreateSheetOptions = {
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
name?: string;
|
|
136
|
+
cells?: Record<string, CellValue>;
|
|
137
137
|
} & Partial<SheetSize>;
|