@dxos/plugin-sheet 0.6.11-staging.e6894a4 → 0.6.12-main.5cc132e
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-4XOKHKKZ.mjs → SheetContainer-Y7ZMFBAP.mjs} +582 -121
- package/dist/lib/browser/SheetContainer-Y7ZMFBAP.mjs.map +7 -0
- package/dist/lib/browser/{chunk-P7SSL3EG.mjs → chunk-GNNVBNCX.mjs} +61 -53
- package/dist/lib/browser/chunk-GNNVBNCX.mjs.map +7 -0
- package/dist/lib/browser/{chunk-FWGRE3EG.mjs → chunk-PGKZPKUD.mjs} +2 -2
- package/dist/lib/browser/chunk-VBF7YENS.mjs +8 -0
- package/dist/lib/browser/{chunk-FUAGSXA4.mjs → chunk-WUPTZUTX.mjs} +6 -3
- package/dist/lib/browser/chunk-WUPTZUTX.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +29 -18
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing.mjs +3 -3
- package/dist/lib/browser/types.mjs +1 -1
- package/dist/lib/node/{SheetContainer-IQT6TR4Z.cjs → SheetContainer-KEOKUKAQ.cjs} +528 -79
- package/dist/lib/node/SheetContainer-KEOKUKAQ.cjs.map +7 -0
- package/dist/lib/node/{chunk-5EPCDAZC.cjs → chunk-57PB2HPY.cjs} +5 -5
- package/dist/lib/node/{chunk-727C6YNP.cjs → chunk-6LWBQAQZ.cjs} +9 -9
- package/dist/lib/node/{chunk-DSYKOI4E.cjs → chunk-VJU3NPUJ.cjs} +8 -5
- package/dist/lib/node/chunk-VJU3NPUJ.cjs.map +7 -0
- package/dist/lib/node/{chunk-SVAIIXWQ.cjs → chunk-ZRQZFV5T.cjs} +76 -63
- package/dist/lib/node/chunk-ZRQZFV5T.cjs.map +7 -0
- package/dist/lib/node/index.cjs +43 -33
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing.cjs +6 -6
- package/dist/lib/node/types.cjs +9 -9
- package/dist/lib/node/types.cjs.map +1 -1
- package/dist/lib/node-esm/SheetContainer-Y7ZMFBAP.mjs +2231 -0
- package/dist/lib/node-esm/SheetContainer-Y7ZMFBAP.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-GNNVBNCX.mjs +3243 -0
- package/dist/lib/node-esm/chunk-GNNVBNCX.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-JRL5LGCE.mjs +18 -0
- package/dist/lib/node-esm/chunk-JRL5LGCE.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-PGKZPKUD.mjs +175 -0
- package/dist/lib/node-esm/chunk-PGKZPKUD.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-VBF7YENS.mjs +8 -0
- package/dist/lib/node-esm/chunk-VBF7YENS.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-WUPTZUTX.mjs +85 -0
- package/dist/lib/node-esm/chunk-WUPTZUTX.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +257 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/meta.mjs +9 -0
- package/dist/lib/node-esm/meta.mjs.map +7 -0
- package/dist/lib/node-esm/testing.mjs +92 -0
- package/dist/lib/node-esm/testing.mjs.map +7 -0
- package/dist/lib/node-esm/types.mjs +22 -0
- package/dist/lib/node-esm/types.mjs.map +7 -0
- package/dist/types/src/SheetPlugin.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/decorations.d.ts +24 -0
- package/dist/types/src/components/Sheet/decorations.d.ts.map +1 -0
- package/dist/types/src/components/Sheet/formatting.d.ts.map +1 -1
- package/dist/types/src/components/Sheet/sheet-context.d.ts +2 -0
- package/dist/types/src/components/Sheet/sheet-context.d.ts.map +1 -1
- package/dist/types/src/components/Sheet/threads.d.ts +2 -0
- package/dist/types/src/components/Sheet/threads.d.ts.map +1 -0
- package/dist/types/src/components/SheetContainer.d.ts +2 -3
- package/dist/types/src/components/SheetContainer.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +19 -3
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +17 -12
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/model/index.d.ts +1 -0
- package/dist/types/src/model/index.d.ts.map +1 -1
- package/dist/types/src/model/model.d.ts +0 -16
- package/dist/types/src/model/model.d.ts.map +1 -1
- package/dist/types/src/model/util.d.ts +24 -0
- package/dist/types/src/model/util.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +17 -12
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +72 -2
- package/dist/types/src/types.d.ts.map +1 -1
- package/package.json +36 -32
- package/src/SheetPlugin.tsx +19 -20
- package/src/components/CellEditor/extension.test.ts +1 -2
- package/src/components/ComputeGraph/graph.browser.test.ts +1 -2
- package/src/components/Sheet/Sheet.stories.tsx +11 -9
- package/src/components/Sheet/Sheet.tsx +57 -29
- package/src/components/Sheet/decorations.ts +62 -0
- package/src/components/Sheet/formatting.ts +3 -3
- package/src/components/Sheet/sheet-context.tsx +9 -1
- package/src/components/Sheet/threads.tsx +201 -0
- package/src/components/SheetContainer.tsx +72 -20
- package/src/components/Toolbar/Toolbar.stories.tsx +5 -10
- package/src/components/Toolbar/Toolbar.tsx +54 -12
- package/src/model/index.ts +1 -0
- package/src/model/model.browser.test.ts +1 -2
- package/src/model/model.ts +11 -46
- package/src/model/types.test.ts +1 -2
- package/src/model/util.ts +67 -0
- package/src/translations.ts +6 -1
- package/src/types.ts +26 -3
- package/dist/lib/browser/SheetContainer-4XOKHKKZ.mjs.map +0 -7
- package/dist/lib/browser/chunk-FUAGSXA4.mjs.map +0 -7
- package/dist/lib/browser/chunk-P7SSL3EG.mjs.map +0 -7
- package/dist/lib/browser/chunk-YPU3R7FA.mjs +0 -8
- package/dist/lib/node/SheetContainer-IQT6TR4Z.cjs.map +0 -7
- package/dist/lib/node/chunk-DSYKOI4E.cjs.map +0 -7
- package/dist/lib/node/chunk-SVAIIXWQ.cjs.map +0 -7
- /package/dist/lib/browser/{chunk-FWGRE3EG.mjs.map → chunk-PGKZPKUD.mjs.map} +0 -0
- /package/dist/lib/browser/{chunk-YPU3R7FA.mjs.map → chunk-VBF7YENS.mjs.map} +0 -0
- /package/dist/lib/node/{chunk-5EPCDAZC.cjs.map → chunk-57PB2HPY.cjs.map} +0 -0
- /package/dist/lib/node/{chunk-727C6YNP.cjs.map → chunk-6LWBQAQZ.cjs.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-sheet",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.12-main.5cc132e",
|
|
4
4
|
"description": "Braneframe sketch plugin",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -10,14 +10,16 @@
|
|
|
10
10
|
".": {
|
|
11
11
|
"browser": "./dist/lib/browser/index.mjs",
|
|
12
12
|
"node": {
|
|
13
|
-
"
|
|
13
|
+
"require": "./dist/lib/node/index.cjs",
|
|
14
|
+
"default": "./dist/lib/node-esm/index.mjs"
|
|
14
15
|
},
|
|
15
16
|
"types": "./dist/types/src/index.d.ts"
|
|
16
17
|
},
|
|
17
18
|
"./meta": {
|
|
18
19
|
"browser": "./dist/lib/browser/meta.mjs",
|
|
19
20
|
"node": {
|
|
20
|
-
"
|
|
21
|
+
"require": "./dist/lib/node/meta.cjs",
|
|
22
|
+
"default": "./dist/lib/node-esm/meta.mjs"
|
|
21
23
|
},
|
|
22
24
|
"types": "./dist/types/src/meta.d.ts"
|
|
23
25
|
},
|
|
@@ -31,7 +33,8 @@
|
|
|
31
33
|
"./types": {
|
|
32
34
|
"browser": "./dist/lib/browser/types.mjs",
|
|
33
35
|
"node": {
|
|
34
|
-
"
|
|
36
|
+
"require": "./dist/lib/node/types.cjs",
|
|
37
|
+
"default": "./dist/lib/node-esm/types.mjs"
|
|
35
38
|
},
|
|
36
39
|
"types": "./dist/types/src/types.d.ts"
|
|
37
40
|
}
|
|
@@ -76,25 +79,26 @@
|
|
|
76
79
|
"re-resizable": "^6.9.17",
|
|
77
80
|
"react-markdown": "^8.0.5",
|
|
78
81
|
"react-resize-detector": "^11.0.1",
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/
|
|
81
|
-
"@dxos/
|
|
82
|
-
"@dxos/crypto": "0.6.
|
|
83
|
-
"@dxos/
|
|
84
|
-
"@dxos/
|
|
85
|
-
"@dxos/
|
|
86
|
-
"@dxos/
|
|
87
|
-
"@dxos/
|
|
88
|
-
"@dxos/
|
|
89
|
-
"@dxos/plugin-
|
|
90
|
-
"@dxos/plugin-
|
|
91
|
-
"@dxos/plugin-
|
|
92
|
-
"@dxos/plugin-
|
|
93
|
-
"@dxos/
|
|
94
|
-
"@dxos/react-
|
|
95
|
-
"@dxos/
|
|
96
|
-
"@dxos/
|
|
97
|
-
"@dxos/react-ui-editor": "0.6.
|
|
82
|
+
"@dxos/app-framework": "0.6.12-main.5cc132e",
|
|
83
|
+
"@dxos/async": "0.6.12-main.5cc132e",
|
|
84
|
+
"@dxos/client": "0.6.12-main.5cc132e",
|
|
85
|
+
"@dxos/crypto": "0.6.12-main.5cc132e",
|
|
86
|
+
"@dxos/context": "0.6.12-main.5cc132e",
|
|
87
|
+
"@dxos/echo-schema": "0.6.12-main.5cc132e",
|
|
88
|
+
"@dxos/debug": "0.6.12-main.5cc132e",
|
|
89
|
+
"@dxos/invariant": "0.6.12-main.5cc132e",
|
|
90
|
+
"@dxos/keys": "0.6.12-main.5cc132e",
|
|
91
|
+
"@dxos/log": "0.6.12-main.5cc132e",
|
|
92
|
+
"@dxos/plugin-attention": "0.6.12-main.5cc132e",
|
|
93
|
+
"@dxos/plugin-client": "0.6.12-main.5cc132e",
|
|
94
|
+
"@dxos/plugin-graph": "0.6.12-main.5cc132e",
|
|
95
|
+
"@dxos/plugin-script": "0.6.12-main.5cc132e",
|
|
96
|
+
"@dxos/plugin-space": "0.6.12-main.5cc132e",
|
|
97
|
+
"@dxos/react-client": "0.6.12-main.5cc132e",
|
|
98
|
+
"@dxos/react-ui-attention": "0.6.12-main.5cc132e",
|
|
99
|
+
"@dxos/plugin-stack": "0.6.12-main.5cc132e",
|
|
100
|
+
"@dxos/react-ui-editor": "0.6.12-main.5cc132e",
|
|
101
|
+
"@dxos/util": "0.6.12-main.5cc132e"
|
|
98
102
|
},
|
|
99
103
|
"devDependencies": {
|
|
100
104
|
"@lezer/generator": "^1.7.1",
|
|
@@ -106,20 +110,20 @@
|
|
|
106
110
|
"@types/react-window": "^1.8.8",
|
|
107
111
|
"react": "~18.2.0",
|
|
108
112
|
"react-dom": "~18.2.0",
|
|
109
|
-
"vite": "
|
|
110
|
-
"@dxos/echo-generator": "0.6.
|
|
111
|
-
"@dxos/random": "0.6.
|
|
112
|
-
"@dxos/react-ui": "0.6.
|
|
113
|
-
"@dxos/
|
|
114
|
-
"@dxos/react-ui-theme": "0.6.
|
|
115
|
-
"@dxos/
|
|
113
|
+
"vite": "5.4.7",
|
|
114
|
+
"@dxos/echo-generator": "0.6.12-main.5cc132e",
|
|
115
|
+
"@dxos/random": "0.6.12-main.5cc132e",
|
|
116
|
+
"@dxos/react-ui": "0.6.12-main.5cc132e",
|
|
117
|
+
"@dxos/react-ui-types": "0.6.12-main.5cc132e",
|
|
118
|
+
"@dxos/react-ui-theme": "0.6.12-main.5cc132e",
|
|
119
|
+
"@dxos/storybook-utils": "0.6.12-main.5cc132e"
|
|
116
120
|
},
|
|
117
121
|
"optionalDependencies": {
|
|
118
122
|
"@phosphor-icons/react": "^2.1.5",
|
|
119
123
|
"react": "^18.0.0",
|
|
120
124
|
"react-dom": "^18.0.0",
|
|
121
|
-
"@dxos/react-ui": "0.6.
|
|
122
|
-
"@dxos/react-ui-theme": "0.6.
|
|
125
|
+
"@dxos/react-ui": "0.6.12-main.5cc132e",
|
|
126
|
+
"@dxos/react-ui-theme": "0.6.12-main.5cc132e"
|
|
123
127
|
},
|
|
124
128
|
"publishConfig": {
|
|
125
129
|
"access": "public"
|
package/src/SheetPlugin.tsx
CHANGED
|
@@ -5,13 +5,7 @@
|
|
|
5
5
|
import { type IconProps, GridNine } from '@phosphor-icons/react';
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
NavigationAction,
|
|
10
|
-
parseIntentPlugin,
|
|
11
|
-
resolvePlugin,
|
|
12
|
-
type PluginDefinition,
|
|
13
|
-
type LayoutCoordinate,
|
|
14
|
-
} from '@dxos/app-framework';
|
|
8
|
+
import { NavigationAction, parseIntentPlugin, resolvePlugin, type PluginDefinition } from '@dxos/app-framework';
|
|
15
9
|
import { create } from '@dxos/echo-schema';
|
|
16
10
|
import { parseClientPlugin } from '@dxos/plugin-client';
|
|
17
11
|
import { type ActionGroup, createExtension, isActionGroup } from '@dxos/plugin-graph';
|
|
@@ -30,7 +24,7 @@ import {
|
|
|
30
24
|
import { EdgeFunctionPlugin, EdgeFunctionPluginTranslations } from './components/ComputeGraph/edge-function';
|
|
31
25
|
import { ComputeGraphContextProvider } from './components/ComputeGraph/graph-context';
|
|
32
26
|
import meta, { SHEET_PLUGIN } from './meta';
|
|
33
|
-
import { SheetModel } from './model';
|
|
27
|
+
import { compareIndexPositions, SheetModel } from './model';
|
|
34
28
|
import translations from './translations';
|
|
35
29
|
import { createSheet, SheetAction, type SheetPluginProvides, SheetType } from './types';
|
|
36
30
|
|
|
@@ -138,22 +132,27 @@ export const SheetPlugin = (): PluginDefinition<SheetPluginProvides> => {
|
|
|
138
132
|
},
|
|
139
133
|
],
|
|
140
134
|
},
|
|
135
|
+
thread: {
|
|
136
|
+
predicate: (data) => data instanceof SheetType,
|
|
137
|
+
createSort: (sheet) => (anchorA, anchorB) =>
|
|
138
|
+
!anchorA || !anchorB ? 0 : compareIndexPositions(sheet, anchorA, anchorB),
|
|
139
|
+
},
|
|
141
140
|
surface: {
|
|
142
141
|
component: ({ data, role = 'never' }) => {
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
// TODO(burdon): Standardize wrapper (with room for toolbar).
|
|
143
|
+
const space = isEchoObject(data.object) && getSpace(data.object);
|
|
144
|
+
if (space && data.object instanceof SheetType) {
|
|
145
|
+
switch (role) {
|
|
146
|
+
case 'article':
|
|
147
|
+
case 'section': {
|
|
148
|
+
return (
|
|
149
|
+
<SheetContainer sheet={data.object} space={space} role={role} remoteFunctionUrl={remoteFunctionUrl} />
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
145
153
|
}
|
|
146
154
|
|
|
147
|
-
|
|
148
|
-
return space && data.object instanceof SheetType ? (
|
|
149
|
-
<SheetContainer
|
|
150
|
-
sheet={data.object}
|
|
151
|
-
space={space}
|
|
152
|
-
role={role}
|
|
153
|
-
coordinate={data.coordinate as LayoutCoordinate}
|
|
154
|
-
remoteFunctionUrl={remoteFunctionUrl}
|
|
155
|
-
/>
|
|
156
|
-
) : null;
|
|
155
|
+
return null;
|
|
157
156
|
},
|
|
158
157
|
},
|
|
159
158
|
intent: {
|
|
@@ -6,9 +6,8 @@ import { CompletionContext, type CompletionSource } from '@codemirror/autocomple
|
|
|
6
6
|
import { EditorState } from '@codemirror/state';
|
|
7
7
|
// @ts-ignore
|
|
8
8
|
import { testTree } from '@lezer/generator/test';
|
|
9
|
-
import { expect } from 'chai';
|
|
10
9
|
import { spreadsheet } from 'codemirror-lang-spreadsheet';
|
|
11
|
-
import { describe, test } from 'vitest';
|
|
10
|
+
import { describe, expect, test } from 'vitest';
|
|
12
11
|
|
|
13
12
|
import { sheetExtension } from './extension';
|
|
14
13
|
import { defaultFunctions } from '../../model/functions';
|
|
@@ -11,13 +11,14 @@ import { Client } from '@dxos/client';
|
|
|
11
11
|
import { type EchoReactiveObject } from '@dxos/echo-schema';
|
|
12
12
|
import { log } from '@dxos/log';
|
|
13
13
|
import { getSpace, type Space } from '@dxos/react-client/echo';
|
|
14
|
-
import { Button
|
|
14
|
+
import { Button } from '@dxos/react-ui';
|
|
15
15
|
import { mx } from '@dxos/react-ui-theme';
|
|
16
|
-
import { withTheme,
|
|
16
|
+
import { withTheme, withLayout } from '@dxos/storybook-utils';
|
|
17
17
|
|
|
18
18
|
import { Sheet } from './Sheet';
|
|
19
19
|
import { type SizeMap } from './grid';
|
|
20
20
|
import { useSheetContext } from './sheet-context';
|
|
21
|
+
import { addressToIndex, rangeToIndex } from '../../model';
|
|
21
22
|
import { createTestSheet, testSheetName } from '../../testing';
|
|
22
23
|
import { ValueTypeEnum, SheetType } from '../../types';
|
|
23
24
|
import { type ComputeGraph, createComputeGraph } from '../ComputeGraph';
|
|
@@ -36,7 +37,7 @@ const SheetWithToolbar = ({ debug, space }: { debug?: boolean; space: Space }) =
|
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
const idx = range ? model.
|
|
40
|
+
const idx = range ? rangeToIndex(model.sheet, range) : addressToIndex(model.sheet, cursor);
|
|
40
41
|
model.sheet.formatting[idx] ??= {};
|
|
41
42
|
const format = model.sheet.formatting[idx];
|
|
42
43
|
|
|
@@ -75,6 +76,9 @@ const SheetWithToolbar = ({ debug, space }: { debug?: boolean; space: Space }) =
|
|
|
75
76
|
format.precision = 2;
|
|
76
77
|
break;
|
|
77
78
|
}
|
|
79
|
+
case 'comment': {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
78
82
|
}
|
|
79
83
|
};
|
|
80
84
|
|
|
@@ -122,7 +126,7 @@ const withGraphDecorator: Decorator = (Story) => {
|
|
|
122
126
|
export default {
|
|
123
127
|
title: 'plugin-sheet/Sheet',
|
|
124
128
|
component: Sheet,
|
|
125
|
-
decorators: [
|
|
129
|
+
decorators: [withTheme, withLayout({ fullscreen: true, tooltips: true, classNames: 'inset-4' }), withGraphDecorator],
|
|
126
130
|
};
|
|
127
131
|
|
|
128
132
|
export const Default = () => {
|
|
@@ -134,11 +138,9 @@ export const Default = () => {
|
|
|
134
138
|
}
|
|
135
139
|
|
|
136
140
|
return (
|
|
137
|
-
<
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
</Sheet.Root>
|
|
141
|
-
</Tooltip.Provider>
|
|
141
|
+
<Sheet.Root sheet={sheet} space={space} onInfo={() => setDebug((debug) => !debug)}>
|
|
142
|
+
<SheetWithToolbar debug={debug} space={space} />
|
|
143
|
+
</Sheet.Root>
|
|
142
144
|
);
|
|
143
145
|
};
|
|
144
146
|
|
|
@@ -40,7 +40,7 @@ import { debounce } from '@dxos/async';
|
|
|
40
40
|
import { fullyQualifiedId, createDocAccessor } from '@dxos/client/echo';
|
|
41
41
|
import { log } from '@dxos/log';
|
|
42
42
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
43
|
-
import { createAttendableAttributes } from '@dxos/react-ui-attention';
|
|
43
|
+
import { createAttendableAttributes, useHasAttention } from '@dxos/react-ui-attention';
|
|
44
44
|
import { mx } from '@dxos/react-ui-theme';
|
|
45
45
|
|
|
46
46
|
import {
|
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
} from './grid';
|
|
61
61
|
import { type GridSize, handleArrowNav, handleNav, useRangeSelect } from './nav';
|
|
62
62
|
import { type SheetContextProps, SheetContextProvider, useSheetContext } from './sheet-context';
|
|
63
|
+
import { useThreads } from './threads';
|
|
63
64
|
import { getRectUnion, getRelativeClientRect, scrollIntoView } from './util';
|
|
64
65
|
import {
|
|
65
66
|
type CellIndex,
|
|
@@ -68,6 +69,8 @@ import {
|
|
|
68
69
|
columnLetter,
|
|
69
70
|
posEquals,
|
|
70
71
|
rangeToA1Notation,
|
|
72
|
+
addressToIndex,
|
|
73
|
+
addressFromIndex,
|
|
71
74
|
} from '../../model';
|
|
72
75
|
import {
|
|
73
76
|
CellEditor,
|
|
@@ -112,13 +115,11 @@ import {
|
|
|
112
115
|
* - Update formula ranges by selection.
|
|
113
116
|
*/
|
|
114
117
|
|
|
115
|
-
// TODO(burdon): Factor out fragments.
|
|
116
118
|
const fragments = {
|
|
117
119
|
axis: 'bg-axisSurface text-axisText text-xs select-none',
|
|
118
120
|
axisSelected: 'bg-attention text-baseText',
|
|
119
121
|
cell: 'bg-gridCell',
|
|
120
122
|
cellSelected: 'bg-gridCellSelected text-baseText border !border-accentSurface',
|
|
121
|
-
border: 'border-gridLine',
|
|
122
123
|
};
|
|
123
124
|
|
|
124
125
|
//
|
|
@@ -143,6 +144,10 @@ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numR
|
|
|
143
144
|
// Scrolling.
|
|
144
145
|
const { rowsRef, columnsRef, contentRef } = useScrollHandlers();
|
|
145
146
|
|
|
147
|
+
// Threads.
|
|
148
|
+
// TODO(Zan): Move this to an extension once we have an extension model.
|
|
149
|
+
useThreads();
|
|
150
|
+
|
|
146
151
|
//
|
|
147
152
|
// Order of Row/columns.
|
|
148
153
|
//
|
|
@@ -172,21 +177,21 @@ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numR
|
|
|
172
177
|
}, [rows, columns]);
|
|
173
178
|
|
|
174
179
|
const handleMoveRows: SheetRowsProps['onMove'] = (from, to, num = 1) => {
|
|
175
|
-
const cursorIdx = cursor ? model.
|
|
180
|
+
const cursorIdx = cursor ? addressToIndex(model.sheet, cursor) : undefined;
|
|
176
181
|
const [rows] = model.sheet.rows.splice(from, num);
|
|
177
182
|
model.sheet.rows.splice(to, 0, rows);
|
|
178
183
|
if (cursorIdx) {
|
|
179
|
-
setCursor(model.
|
|
184
|
+
setCursor(addressFromIndex(model.sheet, cursorIdx));
|
|
180
185
|
}
|
|
181
186
|
setRows([...model.sheet.rows]);
|
|
182
187
|
};
|
|
183
188
|
|
|
184
189
|
const handleMoveColumns: SheetColumnsProps['onMove'] = (from, to, num = 1) => {
|
|
185
|
-
const cursorIdx = cursor ? model.
|
|
190
|
+
const cursorIdx = cursor ? addressToIndex(model.sheet, cursor) : undefined;
|
|
186
191
|
const columns = model.sheet.columns.splice(from, num);
|
|
187
192
|
model.sheet.columns.splice(to, 0, ...columns);
|
|
188
193
|
if (cursorIdx) {
|
|
189
|
-
setCursor(model.
|
|
194
|
+
setCursor(addressFromIndex(model.sheet, cursorIdx));
|
|
190
195
|
}
|
|
191
196
|
setColumns([...model.sheet.columns]);
|
|
192
197
|
};
|
|
@@ -244,7 +249,6 @@ const SheetMain = forwardRef<HTMLDivElement, SheetMainProps>(({ classNames, numR
|
|
|
244
249
|
role='none'
|
|
245
250
|
className={mx(
|
|
246
251
|
'grid grid-cols-[calc(var(--rail-size)-2px)_1fr] grid-rows-[32px_1fr_32px] bs-full is-full overflow-hidden',
|
|
247
|
-
fragments.border,
|
|
248
252
|
classNames,
|
|
249
253
|
)}
|
|
250
254
|
>
|
|
@@ -425,7 +429,7 @@ const SheetRows = forwardRef<HTMLDivElement, SheetRowsProps>(
|
|
|
425
429
|
<div className='relative flex grow overflow-hidden'>
|
|
426
430
|
{/* Fixed border. */}
|
|
427
431
|
<div
|
|
428
|
-
className={mx('z-20 absolute inset-0 border-y pointer-events-none'
|
|
432
|
+
className={mx('z-20 absolute inset-0 border-y border-gridLine pointer-events-none')}
|
|
429
433
|
style={{ width: axisWidth }}
|
|
430
434
|
/>
|
|
431
435
|
|
|
@@ -520,8 +524,7 @@ const GridRowCell = ({ idx, index, label, size, resize, selected, onSelect, onRe
|
|
|
520
524
|
{...listeners}
|
|
521
525
|
className={mx(
|
|
522
526
|
'flex h-full items-center justify-center cursor-pointer',
|
|
523
|
-
'border-t focus-visible:outline-none',
|
|
524
|
-
fragments.border,
|
|
527
|
+
'border-t border-gridLine focus-visible:outline-none',
|
|
525
528
|
fragments.axis,
|
|
526
529
|
selected && fragments.axisSelected,
|
|
527
530
|
isDragging && fragments.axisSelected,
|
|
@@ -588,7 +591,7 @@ const SheetColumns = forwardRef<HTMLDivElement, SheetColumnsProps>(
|
|
|
588
591
|
<div className='relative flex grow overflow-hidden'>
|
|
589
592
|
{/* Fixed border. */}
|
|
590
593
|
<div
|
|
591
|
-
className={mx('z-20 absolute inset-0 border-x pointer-events-none'
|
|
594
|
+
className={mx('z-20 absolute inset-0 border-x border-gridLine pointer-events-none')}
|
|
592
595
|
style={{ height: axisHeight }}
|
|
593
596
|
/>
|
|
594
597
|
|
|
@@ -684,8 +687,7 @@ const GridColumnCell = ({ idx, index, label, size, resize, selected, onSelect, o
|
|
|
684
687
|
{...listeners}
|
|
685
688
|
className={mx(
|
|
686
689
|
'flex h-full items-center justify-center cursor-pointer',
|
|
687
|
-
'border-l focus-visible:outline-none',
|
|
688
|
-
fragments.border,
|
|
690
|
+
'border-l border-gridLine focus-visible:outline-none',
|
|
689
691
|
fragments.axis,
|
|
690
692
|
selected && fragments.axisSelected,
|
|
691
693
|
isDragging && fragments.axisSelected,
|
|
@@ -860,18 +862,17 @@ const SheetGrid = forwardRef<HTMLDivElement, SheetGridProps>(
|
|
|
860
862
|
});
|
|
861
863
|
|
|
862
864
|
// TODO(burdon): Prevent scroll if not attended.
|
|
863
|
-
const
|
|
864
|
-
const attendableAttrs = createAttendableAttributes(
|
|
865
|
-
|
|
866
|
-
const attended = true;
|
|
865
|
+
const id = fullyQualifiedId(model.sheet);
|
|
866
|
+
const attendableAttrs = createAttendableAttributes(id);
|
|
867
|
+
const hasAttention = useHasAttention(id);
|
|
867
868
|
|
|
868
869
|
return (
|
|
869
870
|
<div ref={containerRef} role='grid' className='relative flex grow overflow-hidden'>
|
|
870
871
|
{/* Fixed border. */}
|
|
871
|
-
<div className={mx('z-20 absolute inset-0 border pointer-events-none'
|
|
872
|
+
<div className={mx('z-20 absolute inset-0 border border-gridLine pointer-events-none')} />
|
|
872
873
|
|
|
873
874
|
{/* Grid scroll container. */}
|
|
874
|
-
<div ref={scrollerRef} className={mx('grow',
|
|
875
|
+
<div ref={scrollerRef} className={mx('grow', hasAttention && 'overflow-auto scrollbar-thin')}>
|
|
875
876
|
{/* Scroll content. */}
|
|
876
877
|
<div
|
|
877
878
|
className='relative select-none'
|
|
@@ -888,7 +889,7 @@ const SheetGrid = forwardRef<HTMLDivElement, SheetGridProps>(
|
|
|
888
889
|
const style: CSSProperties = { position: 'absolute', top, left, width, height };
|
|
889
890
|
const cell = { row, column };
|
|
890
891
|
const id = addressToA1Notation(cell);
|
|
891
|
-
const idx = model.
|
|
892
|
+
const idx = addressToIndex(model.sheet, cell);
|
|
892
893
|
const active = posEquals(cursor, cell);
|
|
893
894
|
if (active && editing) {
|
|
894
895
|
const value = initialText.current ?? model.getCellText(cell) ?? '';
|
|
@@ -1009,19 +1010,47 @@ type SheetCellProps = {
|
|
|
1009
1010
|
};
|
|
1010
1011
|
|
|
1011
1012
|
const SheetCell = ({ id, cell, style, active, onSelect }: SheetCellProps) => {
|
|
1012
|
-
const {
|
|
1013
|
+
const {
|
|
1014
|
+
formatting,
|
|
1015
|
+
editing,
|
|
1016
|
+
setRange,
|
|
1017
|
+
decorations,
|
|
1018
|
+
model: { sheet },
|
|
1019
|
+
} = useSheetContext();
|
|
1013
1020
|
const { value, classNames } = formatting.getFormatting(cell);
|
|
1014
1021
|
|
|
1022
|
+
const decorationsForCell = decorations.getDecorationsForCell(addressToIndex(sheet, cell)) ?? [];
|
|
1023
|
+
const decorationAddedClasses = useMemo(
|
|
1024
|
+
() => decorationsForCell.flatMap((d) => d.classNames ?? []),
|
|
1025
|
+
[decorationsForCell],
|
|
1026
|
+
);
|
|
1027
|
+
const decoratedContent = decorationsForCell.reduce(
|
|
1028
|
+
(children, { decorate }) => {
|
|
1029
|
+
if (!decorate) {
|
|
1030
|
+
return children;
|
|
1031
|
+
}
|
|
1032
|
+
const DecoratorComponent = decorate;
|
|
1033
|
+
return <DecoratorComponent>{children}</DecoratorComponent>;
|
|
1034
|
+
},
|
|
1035
|
+
<div
|
|
1036
|
+
role='none'
|
|
1037
|
+
className={mx(
|
|
1038
|
+
'flex flex-grow bs-full is-full px-2 items-center truncate cursor-pointer',
|
|
1039
|
+
...decorationAddedClasses,
|
|
1040
|
+
)}
|
|
1041
|
+
>
|
|
1042
|
+
{value}
|
|
1043
|
+
</div>,
|
|
1044
|
+
);
|
|
1045
|
+
|
|
1015
1046
|
return (
|
|
1016
1047
|
<div
|
|
1017
1048
|
{...{ [`data-${CELL_DATA_KEY}`]: id }}
|
|
1018
1049
|
role='cell'
|
|
1019
1050
|
style={style}
|
|
1020
1051
|
className={mx(
|
|
1021
|
-
'
|
|
1022
|
-
'px-2 py-1',
|
|
1052
|
+
'border border-gridLine cursor-pointer',
|
|
1023
1053
|
fragments.cell,
|
|
1024
|
-
fragments.border,
|
|
1025
1054
|
active && ['z-20', fragments.cellSelected],
|
|
1026
1055
|
classNames,
|
|
1027
1056
|
)}
|
|
@@ -1034,7 +1063,7 @@ const SheetCell = ({ id, cell, style, active, onSelect }: SheetCellProps) => {
|
|
|
1034
1063
|
}}
|
|
1035
1064
|
onDoubleClick={() => onSelect?.(cell, true)}
|
|
1036
1065
|
>
|
|
1037
|
-
{
|
|
1066
|
+
{decoratedContent}
|
|
1038
1067
|
</div>
|
|
1039
1068
|
);
|
|
1040
1069
|
};
|
|
@@ -1093,7 +1122,7 @@ const SheetStatusBar = () => {
|
|
|
1093
1122
|
}
|
|
1094
1123
|
|
|
1095
1124
|
return (
|
|
1096
|
-
<div className={mx('flex shrink-0 justify-between items-center px-4 py-1 text-sm border-x
|
|
1125
|
+
<div className={mx('flex shrink-0 justify-between items-center px-4 py-1 text-sm border-x border-gridLine')}>
|
|
1097
1126
|
<div className='flex gap-4 items-center'>
|
|
1098
1127
|
<div className='flex w-16 items-center font-mono'>
|
|
1099
1128
|
{(range && rangeToA1Notation(range)) || (cursor && addressToA1Notation(cursor))}
|
|
@@ -1129,8 +1158,7 @@ const SheetDebug = () => {
|
|
|
1129
1158
|
<div
|
|
1130
1159
|
className={mx(
|
|
1131
1160
|
'z-20 absolute right-0 top-20 bottom-20 w-[30rem] overflow-auto scrollbar-thin',
|
|
1132
|
-
'border text-xs bg-neutral-50 dark:bg-black text-cyan-500 font-mono p-1 opacity-80',
|
|
1133
|
-
fragments.border,
|
|
1161
|
+
'border border-gridLine text-xs bg-neutral-50 dark:bg-black text-cyan-500 font-mono p-1 opacity-80',
|
|
1134
1162
|
)}
|
|
1135
1163
|
>
|
|
1136
1164
|
<pre className='whitespace-pre-wrap'>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { create } from '@dxos/echo-schema';
|
|
6
|
+
|
|
7
|
+
export type Decoration = {
|
|
8
|
+
type: string;
|
|
9
|
+
/**
|
|
10
|
+
* A wrapping render function to encapsulate cell content. This function is applied between
|
|
11
|
+
* the cell's border and its padding/layout/content, allowing for custom rendering or
|
|
12
|
+
* additional elements to be inserted around the cell's main content.
|
|
13
|
+
*/
|
|
14
|
+
decorate?: (props: { children: React.ReactNode }) => React.ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* An array of CSS class names to be applied to the content of the SheetCell.
|
|
17
|
+
* These classes can be used to style the cell's content independently of its structure.
|
|
18
|
+
*/
|
|
19
|
+
classNames?: string[];
|
|
20
|
+
cellIndex: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const createDecorations = () => {
|
|
24
|
+
// Reactive object to hold decorations
|
|
25
|
+
// TODO(Zan): Use CELL ID's to key the decoration map.
|
|
26
|
+
// TODO(Zan): Consider maintaining an index of decorations by type.
|
|
27
|
+
const { decorations } = create<{ decorations: Record<string, Decoration[]> }>({ decorations: {} });
|
|
28
|
+
|
|
29
|
+
const addDecoration = (cellIndex: string, decorator: Decoration) => {
|
|
30
|
+
decorations[cellIndex] = [...(decorations[cellIndex] || []), decorator];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const removeDecoration = (cellIndex: string, type?: string) => {
|
|
34
|
+
if (type) {
|
|
35
|
+
decorations[cellIndex] = (decorations[cellIndex] || []).filter((d) => d.type !== type);
|
|
36
|
+
} else {
|
|
37
|
+
delete decorations[cellIndex];
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// TODO(Zan): I should check if returning the a value from a map in a deep signal is a reactive slice.
|
|
42
|
+
const getDecorationsForCell = (cellIndex: string): Decoration[] | undefined => {
|
|
43
|
+
return decorations[cellIndex];
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const getAllDecorations = (): Decoration[] => {
|
|
47
|
+
const result: Decoration[] = [];
|
|
48
|
+
for (const decoratorArray of Object.values(decorations)) {
|
|
49
|
+
for (const decorator of decoratorArray) {
|
|
50
|
+
result.push(decorator);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
addDecoration,
|
|
58
|
+
removeDecoration,
|
|
59
|
+
getDecorationsForCell,
|
|
60
|
+
getAllDecorations,
|
|
61
|
+
} as const;
|
|
62
|
+
};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { type ClassNameValue } from '@dxos/react-ui-types';
|
|
6
6
|
|
|
7
|
-
import { type SheetModel, type CellAddress, inRange } from '../../model';
|
|
7
|
+
import { type SheetModel, type CellAddress, inRange, addressToIndex, rangeFromIndex } from '../../model';
|
|
8
8
|
import { ValueTypeEnum } from '../../types';
|
|
9
9
|
|
|
10
10
|
export class FormattingModel {
|
|
@@ -23,7 +23,7 @@ export class FormattingModel {
|
|
|
23
23
|
const locales = undefined;
|
|
24
24
|
|
|
25
25
|
// Cell-specific formatting.
|
|
26
|
-
const idx = this.model.
|
|
26
|
+
const idx = addressToIndex(this.model.sheet, cell);
|
|
27
27
|
let formatting = this.model.sheet.formatting?.[idx] ?? {};
|
|
28
28
|
const classNames = [...(formatting?.classNames ?? [])];
|
|
29
29
|
|
|
@@ -31,7 +31,7 @@ export class FormattingModel {
|
|
|
31
31
|
// TODO(burdon): NOTE: D0 means the D column.
|
|
32
32
|
// TODO(burdon): Cache model formatting (e.g., for ranges). Create class out of this function.
|
|
33
33
|
for (const [idx, _formatting] of Object.entries(this.model.sheet.formatting)) {
|
|
34
|
-
const range = this.model.
|
|
34
|
+
const range = rangeFromIndex(this.model.sheet, idx);
|
|
35
35
|
if (inRange(range, cell)) {
|
|
36
36
|
if (_formatting.classNames) {
|
|
37
37
|
classNames.push(..._formatting.classNames);
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import React, { type PropsWithChildren, createContext, useContext, useState, useEffect } from 'react';
|
|
5
|
+
import React, { type PropsWithChildren, createContext, useContext, useState, useEffect, useMemo } from 'react';
|
|
6
6
|
|
|
7
7
|
import { invariant } from '@dxos/invariant';
|
|
8
8
|
import { type FunctionType } from '@dxos/plugin-script';
|
|
9
9
|
import { fullyQualifiedId, type Space } from '@dxos/react-client/echo';
|
|
10
10
|
|
|
11
|
+
import { createDecorations } from './decorations';
|
|
11
12
|
import { FormattingModel } from './formatting';
|
|
12
13
|
import { type CellAddress, type CellRange, defaultFunctions, SheetModel } from '../../model';
|
|
13
14
|
import { type SheetType } from '../../types';
|
|
@@ -36,6 +37,9 @@ export type SheetContextType = {
|
|
|
36
37
|
// Events.
|
|
37
38
|
// TODO(burdon): Generalize.
|
|
38
39
|
onInfo?: () => void;
|
|
40
|
+
|
|
41
|
+
// Decorations.
|
|
42
|
+
decorations: ReturnType<typeof createDecorations>;
|
|
39
43
|
};
|
|
40
44
|
|
|
41
45
|
const SheetContext = createContext<SheetContextType | null>(null);
|
|
@@ -104,9 +108,12 @@ export const SheetContextProvider = ({
|
|
|
104
108
|
}: PropsWithChildren<SheetContextProps>) => {
|
|
105
109
|
const graph = useComputeGraph(space, options);
|
|
106
110
|
|
|
111
|
+
// TODO(Zan): We should offer a version of set range and set cursor that scrolls to
|
|
112
|
+
// that cell or range if it is not visible.
|
|
107
113
|
const [cursor, setCursor] = useState<CellAddress>();
|
|
108
114
|
const [range, setRange] = useState<CellRange>();
|
|
109
115
|
const [editing, setEditing] = useState<boolean>(false);
|
|
116
|
+
const decorations = useMemo(() => createDecorations(), []);
|
|
110
117
|
|
|
111
118
|
const [[model, formatting] = [], setModels] = useState<[SheetModel, FormattingModel] | undefined>(undefined);
|
|
112
119
|
useEffect(() => {
|
|
@@ -142,6 +149,7 @@ export const SheetContextProvider = ({
|
|
|
142
149
|
setEditing,
|
|
143
150
|
// TODO(burdon): Change to event.
|
|
144
151
|
onInfo,
|
|
152
|
+
decorations,
|
|
145
153
|
}}
|
|
146
154
|
>
|
|
147
155
|
{children}
|