@dxos/plugin-sheet 0.6.12-main.78ddbdf → 0.6.12-main.89e9959
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-LG77O4RM.mjs} +14 -13
- package/dist/lib/browser/SheetContainer-LG77O4RM.mjs.map +7 -0
- package/dist/lib/browser/{chunk-U2JHW3L6.mjs → chunk-CHQAW4F4.mjs} +206 -53
- package/dist/lib/browser/chunk-CHQAW4F4.mjs.map +7 -0
- package/dist/lib/browser/{chunk-6ZMQVB4Z.mjs → chunk-GSV5QNLD.mjs} +220 -177
- package/dist/lib/browser/chunk-GSV5QNLD.mjs.map +7 -0
- 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/graph-M4IQ76QX.mjs +33 -0
- package/dist/lib/browser/index.mjs +45 -21
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/types.mjs +1 -1
- package/dist/lib/node/{SheetContainer-3ZY7MPWJ.cjs → SheetContainer-OZ7DHH4L.cjs} +21 -20
- package/dist/lib/node/SheetContainer-OZ7DHH4L.cjs.map +7 -0
- package/dist/lib/node/{chunk-OTTD7FBK.cjs → chunk-5FTFZL5W.cjs} +224 -70
- package/dist/lib/node/chunk-5FTFZL5W.cjs.map +7 -0
- package/dist/lib/node/{chunk-DD6FIXWC.cjs → chunk-5XPK2V4A.cjs} +222 -175
- package/dist/lib/node/chunk-5XPK2V4A.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/graph-Q3N2X26H.cjs +55 -0
- package/dist/lib/node/graph-Q3N2X26H.cjs.map +7 -0
- package/dist/lib/node/index.cjs +51 -30
- package/dist/lib/node/index.cjs.map +4 -4
- 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-4XS2G25Z.mjs} +14 -13
- package/dist/lib/node-esm/SheetContainer-4XS2G25Z.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-D6KU5MI7.mjs → chunk-5WPZCXNS.mjs} +220 -177
- package/dist/lib/node-esm/chunk-5WPZCXNS.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-7HVSOTGA.mjs → chunk-KK3XL37M.mjs} +206 -53
- package/dist/lib/node-esm/chunk-KK3XL37M.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-SMPUMOV2.mjs +34 -0
- package/dist/lib/node-esm/index.mjs +45 -21
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/types.mjs +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/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/util.d.ts +9 -0
- package/dist/types/src/components/GridSheet/util.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/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/SheetContainer.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +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 +3 -2
- 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 +17 -34
- 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/{async-function.d.ts → functions/async-function.d.ts} +13 -4
- 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 +3 -0
- package/dist/types/src/graph/testing/index.d.ts.map +1 -0
- 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/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/model/sheet-model.test.d.ts +2 -0
- package/dist/types/src/model/sheet-model.test.d.ts.map +1 -0
- 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 +40 -39
- package/src/SheetPlugin.tsx +19 -15
- package/src/components/CellEditor/CellEditor.stories.tsx +2 -3
- package/src/components/CellEditor/extension.test.ts +0 -1
- package/src/components/CellEditor/extension.ts +4 -3
- package/src/components/GridSheet/GridSheet.stories.tsx +3 -3
- package/src/components/GridSheet/GridSheet.tsx +26 -8
- package/src/components/GridSheet/util.ts +61 -21
- package/src/components/Sheet/Sheet.stories.tsx +21 -20
- package/src/components/Sheet/Sheet.tsx +30 -14
- package/src/components/Sheet/sheet-context.tsx +4 -4
- package/src/components/SheetContainer.tsx +13 -15
- package/src/defs/types.ts +1 -0
- package/src/defs/util.ts +19 -3
- package/src/extensions/compute.stories.tsx +20 -20
- package/src/extensions/compute.ts +91 -42
- package/src/graph/compute-graph-registry.ts +90 -0
- package/src/graph/compute-graph.stories.tsx +4 -3
- package/src/graph/compute-graph.test.ts +87 -0
- package/src/graph/compute-graph.ts +73 -121
- package/src/graph/compute-node.ts +17 -5
- package/src/graph/{async-function.ts → functions/async-function.ts} +23 -15
- package/src/graph/{edge-function.ts → functions/edge-function.ts} +14 -13
- 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 +6 -0
- package/src/graph/testing/test-builder.ts +54 -0
- package/src/graph/{custom-function.ts → testing/test-plugin.ts} +43 -9
- package/src/hooks/hooks.stories.tsx +3 -3
- package/src/hooks/useComputeGraph.ts +9 -1
- package/src/hooks/useSheetModel.ts +4 -7
- package/src/model/sheet-model.test.ts +59 -0
- package/src/model/sheet-model.ts +47 -30
- package/src/testing/testing.tsx +17 -15
- package/src/types.ts +3 -3
- package/dist/lib/browser/SheetContainer-V4GCCZTX.mjs.map +0 -7
- package/dist/lib/browser/chunk-6ZMQVB4Z.mjs.map +0 -7
- package/dist/lib/browser/chunk-U2JHW3L6.mjs.map +0 -7
- package/dist/lib/browser/graph-T27BOBOV.mjs +0 -21
- package/dist/lib/node/SheetContainer-3ZY7MPWJ.cjs.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 +0 -43
- package/dist/lib/node/graph-SPKGX7W4.cjs.map +0 -7
- package/dist/lib/node-esm/SheetContainer-PXSJX6XK.mjs.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/lib/node-esm/graph-U67IO4UC.mjs +0 -22
- package/dist/types/src/graph/async-function.d.ts.map +0 -1
- 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 +0 -21
- package/dist/types/src/graph/custom-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/src/graph/compute-graph.browser.test.ts +0 -104
- /package/dist/lib/browser/{graph-T27BOBOV.mjs.map → graph-M4IQ76QX.mjs.map} +0 -0
- /package/dist/lib/node-esm/{graph-U67IO4UC.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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Client, type ClientOptions } from '@dxos/client';
|
|
6
|
+
import { type Context, Resource } from '@dxos/context';
|
|
7
|
+
import { invariant } from '@dxos/invariant';
|
|
8
|
+
|
|
9
|
+
import { type ComputeGraphOptions, ComputeGraphRegistry } from '../compute-graph-registry';
|
|
10
|
+
|
|
11
|
+
export type TestBuilderOptions = ClientOptions & ComputeGraphOptions;
|
|
12
|
+
|
|
13
|
+
// TODO(burdon): Reconcile with @dxos/client/testing.
|
|
14
|
+
export class TestBuilder extends Resource {
|
|
15
|
+
private _client?: Client;
|
|
16
|
+
private _registry?: ComputeGraphRegistry;
|
|
17
|
+
|
|
18
|
+
constructor(private readonly _options: TestBuilderOptions = {}) {
|
|
19
|
+
super();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get ctx(): Context {
|
|
23
|
+
return this._ctx;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get client(): Client {
|
|
27
|
+
invariant(this._client);
|
|
28
|
+
return this._client;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get registry(): ComputeGraphRegistry {
|
|
32
|
+
invariant(this._registry);
|
|
33
|
+
return this._registry;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
override async _open() {
|
|
37
|
+
const client = new Client(this._options);
|
|
38
|
+
await client.initialize();
|
|
39
|
+
await client.halo.createIdentity();
|
|
40
|
+
this._client = client;
|
|
41
|
+
this._ctx.onDispose(async () => {
|
|
42
|
+
await client.destroy();
|
|
43
|
+
this._client = undefined;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const registry = new ComputeGraphRegistry(this._options);
|
|
47
|
+
await registry.open();
|
|
48
|
+
this._registry = registry;
|
|
49
|
+
this._ctx.onDispose(async () => {
|
|
50
|
+
await registry.close();
|
|
51
|
+
this._registry = undefined;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -8,14 +8,30 @@ 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
|
|
12
|
-
import {
|
|
11
|
+
import { type ComputeGraphPlugin } from '../compute-graph-registry';
|
|
12
|
+
import { type AsyncFunction, AsyncFunctionPlugin } from '../functions';
|
|
13
|
+
import { parseNumberString } from '../util';
|
|
13
14
|
|
|
14
15
|
/**
|
|
16
|
+
* Testing functions run locally (not run via EDGE).
|
|
15
17
|
* https://hyperformula.handsontable.com/guide/custom-functions.html#add-a-simple-custom-function
|
|
16
18
|
*/
|
|
17
|
-
export class
|
|
19
|
+
export class TestPlugin extends AsyncFunctionPlugin {
|
|
20
|
+
/**
|
|
21
|
+
* Simple local function returns input value.
|
|
22
|
+
*/
|
|
18
23
|
test(ast: ProcedureAst, state: InterpreterState) {
|
|
24
|
+
const handler: AsyncFunction = async (_value) => {
|
|
25
|
+
return _value;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return this.runAsyncFunction(ast, state, handler);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Simple local function returns random number.
|
|
33
|
+
*/
|
|
34
|
+
random(ast: ProcedureAst, state: InterpreterState) {
|
|
19
35
|
const handler: AsyncFunction = async () => {
|
|
20
36
|
return Math.random();
|
|
21
37
|
};
|
|
@@ -23,6 +39,9 @@ export class CustomPlugin extends FunctionPluginAsync {
|
|
|
23
39
|
return this.runAsyncFunction(ast, state, handler);
|
|
24
40
|
}
|
|
25
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Async HTTP function.
|
|
44
|
+
*/
|
|
26
45
|
crypto(ast: ProcedureAst, state: InterpreterState) {
|
|
27
46
|
const handler: AsyncFunction = async (_currency) => {
|
|
28
47
|
const currency = (_currency || 'USD').toUpperCase();
|
|
@@ -40,9 +59,15 @@ export class CustomPlugin extends FunctionPluginAsync {
|
|
|
40
59
|
}
|
|
41
60
|
}
|
|
42
61
|
|
|
43
|
-
|
|
62
|
+
TestPlugin.implementedFunctions = {
|
|
44
63
|
TEST: {
|
|
45
64
|
method: 'test',
|
|
65
|
+
parameters: [{ argumentType: FunctionArgumentType.NUMBER, optionalArg: false }],
|
|
66
|
+
isVolatile: true,
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
RANDOM: {
|
|
70
|
+
method: 'random',
|
|
46
71
|
parameters: [],
|
|
47
72
|
isVolatile: true,
|
|
48
73
|
},
|
|
@@ -54,13 +79,22 @@ CustomPlugin.implementedFunctions = {
|
|
|
54
79
|
},
|
|
55
80
|
};
|
|
56
81
|
|
|
57
|
-
export const
|
|
82
|
+
export const TestPluginTranslations = {
|
|
58
83
|
enGB: {
|
|
59
|
-
TEST: '
|
|
60
|
-
|
|
84
|
+
TEST: 'Returns input value',
|
|
85
|
+
RANDOM: 'Random number',
|
|
86
|
+
CRYPTO: 'Crypto token value',
|
|
61
87
|
},
|
|
62
88
|
enUS: {
|
|
63
|
-
TEST: '
|
|
64
|
-
|
|
89
|
+
TEST: 'Returns input value',
|
|
90
|
+
RANDOM: 'Random number',
|
|
91
|
+
CRYPTO: 'Crypto token value',
|
|
65
92
|
},
|
|
66
93
|
};
|
|
94
|
+
|
|
95
|
+
export const testFunctionPlugins: ComputeGraphPlugin[] = [
|
|
96
|
+
{
|
|
97
|
+
plugin: TestPlugin,
|
|
98
|
+
translations: TestPluginTranslations,
|
|
99
|
+
},
|
|
100
|
+
];
|
|
@@ -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,13 @@ 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 () => {
|
|
20
|
+
if (space) {
|
|
21
|
+
const graph = registry.getOrCreateGraph(space);
|
|
22
|
+
await graph.open();
|
|
23
|
+
return graph;
|
|
24
|
+
}
|
|
25
|
+
}, [space, registry]);
|
|
26
|
+
|
|
27
|
+
return graph;
|
|
20
28
|
};
|
|
@@ -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
|
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { Trigger } from '@dxos/async';
|
|
8
|
+
import { FunctionType } from '@dxos/plugin-script/types';
|
|
9
|
+
|
|
10
|
+
import { SheetModel } from './sheet-model';
|
|
11
|
+
import { createSheet, addressFromA1Notation } from '../defs';
|
|
12
|
+
import { TestBuilder, testFunctionPlugins } from '../graph/testing';
|
|
13
|
+
import { type CellScalarValue } from '../types';
|
|
14
|
+
|
|
15
|
+
// TODO(burdon): GPT("prompt", inputs); e.g., with large text cells.
|
|
16
|
+
|
|
17
|
+
describe('SheetModel', () => {
|
|
18
|
+
let testBuilder: TestBuilder;
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
testBuilder = new TestBuilder({ types: [FunctionType], plugins: testFunctionPlugins });
|
|
21
|
+
await testBuilder.open();
|
|
22
|
+
});
|
|
23
|
+
afterEach(async () => {
|
|
24
|
+
await testBuilder.close();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('async function', async () => {
|
|
28
|
+
const space = await testBuilder.client.spaces.create();
|
|
29
|
+
const graph = testBuilder.registry.createGraph(space);
|
|
30
|
+
await graph.open();
|
|
31
|
+
|
|
32
|
+
// TODO(burdon): Create via factory.
|
|
33
|
+
const sheet = createSheet({ rows: 5, columns: 5 });
|
|
34
|
+
const model = new SheetModel(graph, sheet);
|
|
35
|
+
await model.open();
|
|
36
|
+
testBuilder.ctx.onDispose(() => model.close());
|
|
37
|
+
|
|
38
|
+
// Trigger waits for function invocation.
|
|
39
|
+
const trigger = new Trigger<CellScalarValue>();
|
|
40
|
+
model.setValue(addressFromA1Notation('A1'), '=TEST(100)');
|
|
41
|
+
model.update.once((update) => {
|
|
42
|
+
const { type } = update;
|
|
43
|
+
if (type === 'valuesUpdated') {
|
|
44
|
+
const value = model.getValue(addressFromA1Notation('A1'));
|
|
45
|
+
trigger.wake(value);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Initial value will be null.
|
|
50
|
+
const v1 = model.getValue(addressFromA1Notation('A1'));
|
|
51
|
+
expect(v1).to.be.null;
|
|
52
|
+
expect(graph.context.info.invocations.TEST).not.to.exist;
|
|
53
|
+
|
|
54
|
+
// Wait until async update triggered.
|
|
55
|
+
const v2 = await trigger.wait();
|
|
56
|
+
expect(v2).to.eq(100);
|
|
57
|
+
expect(graph.context.info.invocations.TEST).to.eq(1);
|
|
58
|
+
});
|
|
59
|
+
});
|
package/src/model/sheet-model.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { type SimpleDate, type SimpleDateTime } from 'hyperformula/typings/DateT
|
|
|
8
8
|
|
|
9
9
|
import { Event } from '@dxos/async';
|
|
10
10
|
import { Resource } from '@dxos/context';
|
|
11
|
+
import { getTypename } from '@dxos/echo-schema';
|
|
11
12
|
import { invariant } from '@dxos/invariant';
|
|
12
13
|
import { PublicKey } from '@dxos/keys';
|
|
13
14
|
import { log } from '@dxos/log';
|
|
@@ -22,7 +23,7 @@ import {
|
|
|
22
23
|
MAX_ROWS,
|
|
23
24
|
} from '../defs';
|
|
24
25
|
import { addressFromIndex, addressToIndex, initialize, insertIndices, ReadonlyException } from '../defs';
|
|
25
|
-
import { type ComputeNode, type ComputeGraph, createSheetName } from '../graph';
|
|
26
|
+
import { type ComputeNode, type ComputeGraph, createSheetName, type ComputeNodeEvent } from '../graph';
|
|
26
27
|
import { type CellScalarValue, type CellValue, type SheetType, ValueTypeEnum } from '../types';
|
|
27
28
|
|
|
28
29
|
const typeMap: Record<string, ValueTypeEnum> = {
|
|
@@ -60,13 +61,14 @@ export type SheetModelOptions = {
|
|
|
60
61
|
*
|
|
61
62
|
* [ComputeGraphContext] > [SheetContext]:[SheetModel] > [Sheet.Root]
|
|
62
63
|
*/
|
|
63
|
-
// TODO(burdon): Factor out commonality with ComputeNode.
|
|
64
|
+
// TODO(burdon): Factor out commonality with ComputeNode. Factor out HF.
|
|
64
65
|
export class SheetModel extends Resource {
|
|
65
66
|
public readonly id = `model-${PublicKey.random().truncate()}`;
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
// Wraps compute node.
|
|
69
|
+
public readonly update = new Event<ComputeNodeEvent>();
|
|
68
70
|
|
|
69
|
-
private
|
|
71
|
+
private _node?: ComputeNode;
|
|
70
72
|
|
|
71
73
|
constructor(
|
|
72
74
|
private readonly _graph: ComputeGraph,
|
|
@@ -74,9 +76,6 @@ export class SheetModel extends Resource {
|
|
|
74
76
|
private readonly _options: SheetModelOptions = {},
|
|
75
77
|
) {
|
|
76
78
|
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
79
|
}
|
|
81
80
|
|
|
82
81
|
get graph() {
|
|
@@ -104,12 +103,16 @@ export class SheetModel extends Resource {
|
|
|
104
103
|
protected override async _open() {
|
|
105
104
|
log('initialize', { id: this.id });
|
|
106
105
|
initialize(this._sheet);
|
|
107
|
-
this.reset();
|
|
108
106
|
|
|
109
|
-
// TODO(burdon):
|
|
107
|
+
// TODO(burdon): SheetModel should extend ComputeNode and be constructed via the graph.
|
|
108
|
+
this._node = this._graph.getOrCreateNode(createSheetName({ type: getTypename(this._sheet)!, id: this._sheet.id }));
|
|
109
|
+
await this._node.open();
|
|
110
|
+
|
|
110
111
|
// Listen for model updates (e.g., async calculations).
|
|
111
|
-
const unsubscribe = this.
|
|
112
|
+
const unsubscribe = this._node.update.on((event) => this.update.emit(event));
|
|
112
113
|
this._ctx.onDispose(unsubscribe);
|
|
114
|
+
|
|
115
|
+
this.reset();
|
|
113
116
|
}
|
|
114
117
|
|
|
115
118
|
/**
|
|
@@ -118,8 +121,10 @@ export class SheetModel extends Resource {
|
|
|
118
121
|
* @deprecated
|
|
119
122
|
*/
|
|
120
123
|
reset() {
|
|
121
|
-
|
|
124
|
+
invariant(this._node);
|
|
125
|
+
this._node.graph.hf.clearSheet(this._node.sheetId);
|
|
122
126
|
Object.entries(this._sheet.cells).forEach(([key, { value }]) => {
|
|
127
|
+
invariant(this._node);
|
|
123
128
|
const { col, row } = addressFromIndex(this._sheet, key);
|
|
124
129
|
if (typeof value === 'string' && value.charAt(0) === '=') {
|
|
125
130
|
value = this._graph.mapFormulaToNative(
|
|
@@ -127,7 +132,7 @@ export class SheetModel extends Resource {
|
|
|
127
132
|
);
|
|
128
133
|
}
|
|
129
134
|
|
|
130
|
-
this._node.hf.setCellContents({ sheet: this._node.sheetId, row, col }, value);
|
|
135
|
+
this._node.graph.hf.setCellContents({ sheet: this._node.sheetId, row, col }, value);
|
|
131
136
|
});
|
|
132
137
|
}
|
|
133
138
|
|
|
@@ -139,7 +144,7 @@ export class SheetModel extends Resource {
|
|
|
139
144
|
*/
|
|
140
145
|
// TODO(burdon): Remove.
|
|
141
146
|
recalculate() {
|
|
142
|
-
this._node.hf.rebuildAndRecalculate();
|
|
147
|
+
this._node?.graph.hf.rebuildAndRecalculate();
|
|
143
148
|
}
|
|
144
149
|
|
|
145
150
|
insertRows(i: number, n = 1) {
|
|
@@ -161,9 +166,10 @@ export class SheetModel extends Resource {
|
|
|
161
166
|
* Clear range of values.
|
|
162
167
|
*/
|
|
163
168
|
clear(range: CellRange) {
|
|
169
|
+
invariant(this._node);
|
|
164
170
|
const topLeft = getTopLeft(range);
|
|
165
171
|
const values = this._iterRange(range, () => null);
|
|
166
|
-
this._node.hf.setCellContents(toSimpleCellAddress(this._node.sheetId, topLeft), values);
|
|
172
|
+
this._node.graph.hf.setCellContents(toSimpleCellAddress(this._node.sheetId, topLeft), values);
|
|
167
173
|
this._iterRange(range, (cell) => {
|
|
168
174
|
const idx = addressToIndex(this._sheet, cell);
|
|
169
175
|
delete this._sheet.cells[idx];
|
|
@@ -171,7 +177,8 @@ export class SheetModel extends Resource {
|
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
cut(range: CellRange) {
|
|
174
|
-
|
|
180
|
+
invariant(this._node);
|
|
181
|
+
this._node.graph.hf.cut(toModelRange(this._node.sheetId, range));
|
|
175
182
|
this._iterRange(range, (cell) => {
|
|
176
183
|
const idx = addressToIndex(this._sheet, cell);
|
|
177
184
|
delete this._sheet.cells[idx];
|
|
@@ -179,12 +186,14 @@ export class SheetModel extends Resource {
|
|
|
179
186
|
}
|
|
180
187
|
|
|
181
188
|
copy(range: CellRange) {
|
|
182
|
-
|
|
189
|
+
invariant(this._node);
|
|
190
|
+
this._node.graph.hf.copy(toModelRange(this._node.sheetId, range));
|
|
183
191
|
}
|
|
184
192
|
|
|
185
193
|
paste(cell: CellAddress) {
|
|
186
|
-
|
|
187
|
-
|
|
194
|
+
invariant(this._node);
|
|
195
|
+
if (!this._node.graph.hf.isClipboardEmpty()) {
|
|
196
|
+
const changes = this._node.graph.hf.paste(toSimpleCellAddress(this._node.sheetId, cell));
|
|
188
197
|
for (const change of changes) {
|
|
189
198
|
if (change instanceof ExportedCellChange) {
|
|
190
199
|
const { address, newValue } = change;
|
|
@@ -197,16 +206,18 @@ export class SheetModel extends Resource {
|
|
|
197
206
|
|
|
198
207
|
// TODO(burdon): Display undo/redo state.
|
|
199
208
|
undo() {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
this.
|
|
209
|
+
invariant(this._node);
|
|
210
|
+
if (this._node.graph.hf.isThereSomethingToUndo()) {
|
|
211
|
+
this._node.graph.hf.undo();
|
|
212
|
+
// this.update.emit();
|
|
203
213
|
}
|
|
204
214
|
}
|
|
205
215
|
|
|
206
216
|
redo() {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
this.
|
|
217
|
+
invariant(this._node);
|
|
218
|
+
if (this._node.graph.hf.isThereSomethingToRedo()) {
|
|
219
|
+
this._node.graph.hf.redo();
|
|
220
|
+
// this.update.emit();
|
|
210
221
|
}
|
|
211
222
|
}
|
|
212
223
|
|
|
@@ -246,7 +257,8 @@ export class SheetModel extends Resource {
|
|
|
246
257
|
*/
|
|
247
258
|
getValue(cell: CellAddress): CellScalarValue {
|
|
248
259
|
// Applies rounding and post-processing.
|
|
249
|
-
|
|
260
|
+
invariant(this._node);
|
|
261
|
+
const value = this._node.graph.hf.getCellValue(toSimpleCellAddress(this._node.sheetId, cell));
|
|
250
262
|
if (value instanceof DetailedCellError) {
|
|
251
263
|
return value.toString();
|
|
252
264
|
}
|
|
@@ -258,8 +270,9 @@ export class SheetModel extends Resource {
|
|
|
258
270
|
* Get value type.
|
|
259
271
|
*/
|
|
260
272
|
getValueType(cell: CellAddress): ValueTypeEnum {
|
|
273
|
+
invariant(this._node);
|
|
261
274
|
const addr = toSimpleCellAddress(this._node.sheetId, cell);
|
|
262
|
-
const type = this._node.hf.getCellValueDetailedType(addr);
|
|
275
|
+
const type = this._node.graph.hf.getCellValueDetailedType(addr);
|
|
263
276
|
return typeMap[type];
|
|
264
277
|
}
|
|
265
278
|
|
|
@@ -267,6 +280,7 @@ export class SheetModel extends Resource {
|
|
|
267
280
|
* Sets the value, updating the sheet and engine.
|
|
268
281
|
*/
|
|
269
282
|
setValue(cell: CellAddress, value: CellScalarValue) {
|
|
283
|
+
invariant(this._node);
|
|
270
284
|
if (this._options.readonly) {
|
|
271
285
|
throw new ReadonlyException();
|
|
272
286
|
}
|
|
@@ -288,7 +302,7 @@ export class SheetModel extends Resource {
|
|
|
288
302
|
}
|
|
289
303
|
|
|
290
304
|
// Insert into engine.
|
|
291
|
-
this._node.hf.setCellContents({ sheet: this._node.sheetId, row: cell.row, col: cell.col }, [
|
|
305
|
+
this._node.graph.hf.setCellContents({ sheet: this._node.sheetId, row: cell.row, col: cell.col }, [
|
|
292
306
|
[typeof value === 'string' && value.charAt(0) === '=' ? this._graph.mapFormulaToNative(value) : value],
|
|
293
307
|
]);
|
|
294
308
|
|
|
@@ -386,14 +400,17 @@ export class SheetModel extends Resource {
|
|
|
386
400
|
}
|
|
387
401
|
|
|
388
402
|
toDateTime(num: number): SimpleDateTime {
|
|
389
|
-
|
|
403
|
+
invariant(this._node);
|
|
404
|
+
return this._node.graph.hf.numberToDateTime(num) as SimpleDateTime;
|
|
390
405
|
}
|
|
391
406
|
|
|
392
407
|
toDate(num: number): SimpleDate {
|
|
393
|
-
|
|
408
|
+
invariant(this._node);
|
|
409
|
+
return this._node.graph.hf.numberToDate(num) as SimpleDate;
|
|
394
410
|
}
|
|
395
411
|
|
|
396
412
|
toTime(num: number): SimpleDate {
|
|
397
|
-
|
|
413
|
+
invariant(this._node);
|
|
414
|
+
return this._node.graph.hf.numberToTime(num) as SimpleDate;
|
|
398
415
|
}
|
|
399
416
|
}
|
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>;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/components/SheetContainer.tsx", "../../../src/components/Toolbar/Toolbar.tsx", "../../../src/components/Toolbar/common.tsx"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport React, { useCallback } from 'react';\n\nimport { useIntentDispatcher } from '@dxos/app-framework';\nimport { fullyQualifiedId } from '@dxos/react-client/echo';\nimport { useIsDirectlyAttended } from '@dxos/react-ui-attention';\nimport { focusRing, mx } from '@dxos/react-ui-theme';\n\nimport { Sheet, type SheetRootProps } from './Sheet';\nimport { Toolbar, type ToolbarAction } from './Toolbar';\n\n// TODO(Zan): Factor out, copied this from MarkdownPlugin.\nconst attentionFragment = mx(\n 'group-focus-within/editor:attention-surface group-[[aria-current]]/editor:attention-surface',\n 'group-focus-within/editor:border-separator',\n);\n\n// TODO(Zan): Factor out, copied this from MarkdownPlugin.\nexport const sectionToolbarLayout =\n 'bs-[--rail-action] bg-[--sticky-bg] sticky block-start-0 __-block-start-px transition-opacity';\n\nconst SheetContainer = ({ sheet, space, role }: SheetRootProps & { role?: string }) => {\n const dispatch = useIntentDispatcher();\n\n const id = fullyQualifiedId(sheet);\n const isDirectlyAttended = useIsDirectlyAttended(id);\n\n // TODO(Zan): Centralise the toolbar action handler. Current implementation in stories.\n const handleAction = useCallback(\n (action: ToolbarAction) => {\n switch (action.type) {\n case 'comment': {\n // TODO(Zan): We shouldn't hardcode the action ID.\n void dispatch({\n action: 'dxos.org/plugin/thread/action/create',\n data: {\n cursor: action.anchor,\n name: action.cellContent,\n subject: sheet,\n },\n });\n }\n }\n },\n [sheet, dispatch],\n );\n\n return (\n <div role='none' className={role === 'article' ? 'row-span-2 grid grid-rows-subgrid' : undefined}>\n <Sheet.Root space={space} sheet={sheet}>\n <div role='none' className={mx('flex flex-0 justify-center overflow-x-auto')}>\n <Toolbar.Root\n onAction={handleAction}\n classNames={mx(\n role === 'section'\n ? ['z-[2] group-focus-within/section:visible', !isDirectlyAttended && 'invisible', sectionToolbarLayout]\n : 'group-focus-within/editor:border-separator group-[[aria-current]]/editor:border-separator',\n )}\n >\n {/* TODO(Zan): Restore some of this functionality */}\n {/* <Toolbar.Styles /> */}\n {/* <Toolbar.Format /> */}\n {/* <Toolbar.Alignment /> */}\n <Toolbar.Separator />\n <Toolbar.Actions />\n </Toolbar.Root>\n </div>\n <div\n role='none'\n className={mx(\n role === 'section' && 'aspect-square border-is border-bs border-be border-separator',\n role === 'article' &&\n 'flex is-full overflow-hidden focus-visible:ring-inset row-span-1 data-[toolbar=disabled]:pbs-2 data-[toolbar=disabled]:row-span-2 border-bs border-separator',\n focusRing,\n attentionFragment,\n )}\n >\n <Sheet.Main />\n </div>\n </Sheet.Root>\n </div>\n );\n};\n\nexport default SheetContainer;\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport {\n type Icon,\n Calendar,\n ChatText,\n CurrencyDollar,\n Eraser,\n HighlighterCircle,\n TextAlignCenter,\n TextAlignLeft,\n TextAlignRight,\n} from '@phosphor-icons/react';\nimport { createContext } from '@radix-ui/react-context';\nimport React, { type PropsWithChildren } from 'react';\n\nimport {\n DensityProvider,\n ElevationProvider,\n Toolbar as NaturalToolbar,\n type ThemedClassName,\n useTranslation,\n} from '@dxos/react-ui';\nimport { nonNullable } from '@dxos/util';\n\nimport { ToolbarButton, ToolbarSeparator, ToolbarToggleButton } from './common';\nimport { addressToIndex } from '../../defs';\nimport { SHEET_PLUGIN } from '../../meta';\nimport { type Formatting } from '../../types';\nimport { useSheetContext } from '../Sheet/sheet-context';\n\n//\n// Root\n//\n\nexport type ToolbarAction =\n | { type: 'clear' }\n | { type: 'highlight' }\n | { type: 'left' }\n | { type: 'center' }\n | { type: 'right' }\n | { type: 'date' }\n | { type: 'currency' }\n | { type: 'comment'; anchor: string; cellContent?: string };\n\nexport type ToolbarActionType = ToolbarAction['type'];\n\nexport type ToolbarActionHandler = (action: ToolbarAction) => void;\n\nexport type ToolbarProps = ThemedClassName<\n PropsWithChildren<{\n onAction?: ToolbarActionHandler;\n }>\n>;\n\nconst [ToolbarContextProvider, useToolbarContext] = createContext<ToolbarProps>('Toolbar');\n\nconst ToolbarRoot = ({ children, onAction, classNames }: ToolbarProps) => {\n return (\n <ToolbarContextProvider onAction={onAction}>\n <DensityProvider density='fine'>\n <ElevationProvider elevation='chrome'>\n <NaturalToolbar.Root classNames={['is-full shrink-0 overflow-x-auto overflow-y-hidden p-1', classNames]}>\n {children}\n </NaturalToolbar.Root>\n </ElevationProvider>\n </DensityProvider>\n </ToolbarContextProvider>\n );\n};\n\n// TODO(burdon): Generalize.\n// TODO(burdon): Detect and display current state.\ntype ButtonProps = {\n type: ToolbarActionType;\n Icon: Icon;\n getState: (state: Formatting) => boolean;\n disabled?: (state: Formatting) => boolean;\n};\n\n//\n// Alignment\n//\n\nconst formatOptions: ButtonProps[] = [\n { type: 'date', Icon: Calendar, getState: (state) => false },\n { type: 'currency', Icon: CurrencyDollar, getState: (state) => false },\n];\n\nconst Format = () => {\n const { onAction } = useToolbarContext('Format');\n const { t } = useTranslation(SHEET_PLUGIN);\n\n return (\n <NaturalToolbar.ToggleGroup\n type='single'\n // value={cellStyles.filter(({ getState }) => state && getState(state)).map(({ type }) => type)}\n >\n {formatOptions.map(({ type, getState, Icon }) => (\n <ToolbarToggleButton\n key={type}\n value={type}\n Icon={Icon}\n // disabled={state?.blockType === 'codeblock'}\n // onClick={state ? () => onAction?.({ type, data: !getState(state) }) : undefined}\n onClick={() => onAction?.({ type: type as Exclude<typeof type, 'comment'> })}\n >\n {t(`toolbar ${type} label`)}\n </ToolbarToggleButton>\n ))}\n </NaturalToolbar.ToggleGroup>\n );\n};\n\nconst alignmentOptions: ButtonProps[] = [\n { type: 'left', Icon: TextAlignLeft, getState: (state) => false },\n { type: 'center', Icon: TextAlignCenter, getState: (state) => false },\n { type: 'right', Icon: TextAlignRight, getState: (state) => false },\n];\n\nconst Alignment = () => {\n const { onAction } = useToolbarContext('Alignment');\n const { t } = useTranslation(SHEET_PLUGIN);\n\n return (\n <NaturalToolbar.ToggleGroup\n type='single'\n // value={cellStyles.filter(({ getState }) => state && getState(state)).map(({ type }) => type)}\n >\n {alignmentOptions.map(({ type, getState, Icon }) => (\n <ToolbarToggleButton\n key={type}\n value={type}\n Icon={Icon}\n // disabled={state?.blockType === 'codeblock'}\n // onClick={state ? () => onAction?.({ type, data: !getState(state) }) : undefined}\n onClick={() => onAction?.({ type: type as Exclude<typeof type, 'comment'> })}\n >\n {t(`toolbar ${type} label`)}\n </ToolbarToggleButton>\n ))}\n </NaturalToolbar.ToggleGroup>\n );\n};\n\nconst styleOptions: ButtonProps[] = [\n { type: 'clear', Icon: Eraser, getState: (state) => false },\n { type: 'highlight', Icon: HighlighterCircle, getState: (state) => false },\n];\n\nconst Styles = () => {\n const { onAction } = useToolbarContext('Alignment');\n const { t } = useTranslation(SHEET_PLUGIN);\n\n return (\n <NaturalToolbar.ToggleGroup\n type='single'\n // value={cellStyles.filter(({ getState }) => state && getState(state)).map(({ type }) => type)}\n >\n {styleOptions.map(({ type, getState, Icon }) => (\n <ToolbarToggleButton\n key={type}\n value={type}\n Icon={Icon}\n // disabled={state?.blockType === 'codeblock'}\n // onClick={state ? () => onAction?.({ type, data: !getState(state) }) : undefined}\n onClick={() => onAction?.({ type: type as Exclude<typeof type, 'comment'> })}\n >\n {t(`toolbar ${type} label`)}\n </ToolbarToggleButton>\n ))}\n </NaturalToolbar.ToggleGroup>\n );\n};\n\n//\n// Actions\n//\n\nconst Actions = () => {\n const { onAction } = useToolbarContext('Actions');\n const { cursor, range, model } = useSheetContext();\n const { t } = useTranslation(SHEET_PLUGIN);\n\n const overlapsCommentAnchor = (model.sheet.threads ?? [])\n .filter(nonNullable)\n .filter((thread) => thread.status !== 'resolved')\n .some((thread) => {\n if (!cursor) {\n return false;\n }\n return addressToIndex(model.sheet, cursor) === thread.anchor;\n });\n\n const hasCursor = !!cursor;\n const cursorOnly = hasCursor && !range && !overlapsCommentAnchor;\n\n const tooltipLabelKey = !hasCursor\n ? 'no cursor label'\n : overlapsCommentAnchor\n ? 'selection overlaps existing comment label'\n : range\n ? 'comment ranges not supported label'\n : 'comment label';\n\n return (\n <ToolbarButton\n value='comment'\n Icon={ChatText}\n data-testid='editor.toolbar.comment'\n onClick={() => {\n if (!cursor) {\n return;\n }\n return onAction?.({\n type: 'comment',\n anchor: addressToIndex(model.sheet, cursor),\n cellContent: model.getCellText(cursor),\n });\n }}\n disabled={!cursorOnly || overlapsCommentAnchor}\n >\n {t(tooltipLabelKey)}\n </ToolbarButton>\n );\n};\n\nexport const Toolbar = {\n Root: ToolbarRoot,\n Separator: ToolbarSeparator,\n Alignment,\n Format,\n Styles,\n Actions,\n};\n\nexport { useToolbarContext };\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Icon } from '@phosphor-icons/react';\nimport React from 'react';\n\nimport {\n Toolbar as NaturalToolbar,\n Tooltip,\n type ToolbarButtonProps as NaturalToolbarButtonProps,\n type ToolbarToggleGroupItemProps as NaturalToolbarToggleGroupItemProps,\n} from '@dxos/react-ui';\nimport { getSize } from '@dxos/react-ui-theme';\n\n// TODO(burdon): Factor out in common with react-ui-editor.\n\nexport const iconStyles = getSize(5);\nexport const buttonStyles = 'min-bs-0 p-2';\nexport const tooltipProps = { side: 'top' as const, classNames: 'z-10' };\n\nexport const ToolbarSeparator = () => <div role='separator' className='grow' />;\n\n//\n// ToolbarButton\n//\n\ntype ToolbarButtonProps = NaturalToolbarButtonProps & { Icon: Icon };\n\nexport const ToolbarButton = ({ Icon, children, ...props }: ToolbarButtonProps) => {\n return (\n <Tooltip.Root>\n <Tooltip.Trigger asChild>\n <NaturalToolbar.Button variant='ghost' {...props} classNames={buttonStyles}>\n <Icon className={iconStyles} />\n <span className='sr-only'>{children}</span>\n </NaturalToolbar.Button>\n </Tooltip.Trigger>\n <Tooltip.Portal>\n <Tooltip.Content {...tooltipProps}>\n {children}\n <Tooltip.Arrow />\n </Tooltip.Content>\n </Tooltip.Portal>\n </Tooltip.Root>\n );\n};\n\n//\n// ToolbarToggleButton\n//\n\nexport type ToolbarToggleButtonProps = NaturalToolbarToggleGroupItemProps & { Icon: Icon };\n\nexport const ToolbarToggleButton = ({ Icon, children, ...props }: ToolbarToggleButtonProps) => {\n return (\n <Tooltip.Root>\n <Tooltip.Trigger asChild>\n <NaturalToolbar.ToggleGroupItem variant='ghost' {...props} classNames={buttonStyles}>\n <Icon className={iconStyles} />\n <span className='sr-only'>{children}</span>\n </NaturalToolbar.ToggleGroupItem>\n </Tooltip.Trigger>\n <Tooltip.Portal>\n <Tooltip.Content {...tooltipProps}>\n {children}\n <Tooltip.Arrow />\n </Tooltip.Content>\n </Tooltip.Portal>\n </Tooltip.Root>\n );\n};\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["React", "useCallback", "useIntentDispatcher", "fullyQualifiedId", "useIsDirectlyAttended", "focusRing", "mx", "Calendar", "ChatText", "CurrencyDollar", "Eraser", "HighlighterCircle", "TextAlignCenter", "TextAlignLeft", "TextAlignRight", "createContext", "React", "DensityProvider", "ElevationProvider", "Toolbar", "NaturalToolbar", "useTranslation", "nonNullable", "React", "Toolbar", "NaturalToolbar", "Tooltip", "getSize", "iconStyles", "getSize", "buttonStyles", "tooltipProps", "side", "classNames", "ToolbarSeparator", "div", "role", "className", "ToolbarButton", "Icon", "children", "props", "Tooltip", "Root", "Trigger", "asChild", "NaturalToolbar", "Button", "variant", "span", "Portal", "Content", "Arrow", "ToolbarToggleButton", "ToggleGroupItem", "ToolbarContextProvider", "useToolbarContext", "createContext", "ToolbarRoot", "children", "onAction", "classNames", "React", "DensityProvider", "density", "ElevationProvider", "elevation", "NaturalToolbar", "Root", "formatOptions", "type", "Icon", "Calendar", "getState", "state", "CurrencyDollar", "Format", "t", "useTranslation", "SHEET_PLUGIN", "ToggleGroup", "map", "ToolbarToggleButton", "key", "value", "onClick", "alignmentOptions", "TextAlignLeft", "TextAlignCenter", "TextAlignRight", "Alignment", "styleOptions", "Eraser", "HighlighterCircle", "Styles", "Actions", "cursor", "range", "model", "useSheetContext", "overlapsCommentAnchor", "sheet", "threads", "filter", "nonNullable", "thread", "status", "some", "addressToIndex", "anchor", "hasCursor", "cursorOnly", "tooltipLabelKey", "ToolbarButton", "ChatText", "data-testid", "cellContent", "getCellText", "disabled", "Toolbar", "Separator", "ToolbarSeparator", "attentionFragment", "mx", "sectionToolbarLayout", "SheetContainer", "sheet", "space", "role", "dispatch", "useIntentDispatcher", "id", "fullyQualifiedId", "isDirectlyAttended", "useIsDirectlyAttended", "handleAction", "useCallback", "action", "type", "data", "cursor", "anchor", "name", "cellContent", "subject", "React", "div", "className", "undefined", "Sheet", "Root", "Toolbar", "onAction", "classNames", "Separator", "Actions", "focusRing", "Main"]
|
|
7
|
-
}
|