@trebco/treb 27.5.3 → 27.9.0
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/treb-spreadsheet.mjs +14 -14
- package/dist/treb.d.ts +37 -23
- package/notes/conditional-fomratring.md +29 -0
- package/package.json +3 -3
- package/treb-base-types/src/area.ts +181 -0
- package/treb-base-types/src/evaluate-options.ts +21 -0
- package/treb-base-types/src/gradient.ts +97 -0
- package/treb-base-types/src/import.ts +2 -1
- package/treb-base-types/src/index.ts +2 -0
- package/treb-calculator/src/calculator.ts +205 -132
- package/treb-calculator/src/dag/calculation_leaf_vertex.ts +97 -0
- package/treb-calculator/src/dag/graph.ts +10 -22
- package/treb-calculator/src/dag/{leaf_vertex.ts → state_leaf_vertex.ts} +3 -3
- package/treb-calculator/src/descriptors.ts +10 -3
- package/treb-calculator/src/expression-calculator.ts +1 -1
- package/treb-calculator/src/function-library.ts +25 -22
- package/treb-calculator/src/functions/base-functions.ts +166 -5
- package/treb-calculator/src/index.ts +6 -6
- package/treb-calculator/src/notifier-types.ts +1 -1
- package/treb-calculator/src/utilities.ts +2 -2
- package/treb-charts/src/util.ts +2 -2
- package/treb-embed/src/embedded-spreadsheet.ts +382 -71
- package/treb-embed/style/formula-bar.scss +2 -0
- package/treb-embed/style/theme-defaults.scss +46 -15
- package/treb-export/src/export-worker/export-worker.ts +0 -13
- package/treb-export/src/export2.ts +187 -2
- package/treb-export/src/import2.ts +169 -4
- package/treb-export/src/workbook-style2.ts +56 -8
- package/treb-export/src/workbook2.ts +10 -1
- package/treb-grid/src/editors/editor.ts +1276 -0
- package/treb-grid/src/editors/external_editor.ts +113 -0
- package/treb-grid/src/editors/formula_bar.ts +450 -474
- package/treb-grid/src/editors/overlay_editor.ts +437 -512
- package/treb-grid/src/index.ts +2 -1
- package/treb-grid/src/layout/base_layout.ts +24 -16
- package/treb-grid/src/render/tile_renderer.ts +2 -1
- package/treb-grid/src/types/conditional_format.ts +168 -0
- package/treb-grid/src/types/data_model.ts +130 -3
- package/treb-grid/src/types/external_editor_config.ts +47 -0
- package/treb-grid/src/types/grid.ts +96 -45
- package/treb-grid/src/types/grid_base.ts +187 -35
- package/treb-grid/src/types/scale-control.ts +1 -1
- package/treb-grid/src/types/sheet.ts +330 -26
- package/treb-grid/src/types/sheet_types.ts +4 -0
- package/treb-grid/src/util/dom_utilities.ts +58 -25
- package/treb-grid/src/editors/formula_editor_base.ts +0 -912
- package/treb-grid/src/types/external_editor.ts +0 -27
- /package/{README-shadow-DOM.md → notes/shadow-DOM.md} +0 -0
package/treb-grid/src/index.ts
CHANGED
|
@@ -39,4 +39,5 @@ export type { FunctionDescriptor, ArgumentDescriptor } from './editors/autocompl
|
|
|
39
39
|
export { UA } from './util/ua';
|
|
40
40
|
export type { SetRangeOptions } from './types/set_range_options';
|
|
41
41
|
export type { AnnotationData, AnnotationType } from './types/annotation';
|
|
42
|
-
export type {
|
|
42
|
+
export type { ExternalEditorConfig, DependencyList, ExternalEditorCallback } from './types/external_editor_config';
|
|
43
|
+
export * from './types/conditional_format';
|
|
@@ -23,7 +23,7 @@ import { DOMUtilities } from '../util/dom_utilities';
|
|
|
23
23
|
import type { DataModel, ViewModel } from '../types/data_model';
|
|
24
24
|
|
|
25
25
|
import type { Tile } from '../types/tile';
|
|
26
|
-
import type { Theme, Point, Extent, Size, Position, ICellAddress, Table } from 'treb-base-types';
|
|
26
|
+
import type { Theme, Point, Extent, Size, Position, ICellAddress, Table, IArea } from 'treb-base-types';
|
|
27
27
|
import { Style, Area, Rectangle, ThemeColor } from 'treb-base-types';
|
|
28
28
|
|
|
29
29
|
import { MouseDrag } from '../types/drag_mask';
|
|
@@ -357,7 +357,7 @@ export abstract class BaseLayout {
|
|
|
357
357
|
this.note_node = DOMUtilities.CreateDiv('treb-note');
|
|
358
358
|
this.title_node = DOMUtilities.CreateDiv('treb-hover-title');
|
|
359
359
|
|
|
360
|
-
this.sort_button = DOMUtilities.Create
|
|
360
|
+
this.sort_button = DOMUtilities.Create(
|
|
361
361
|
'button',
|
|
362
362
|
'treb-sort-button', undefined, undefined, { title: 'Sort table', tabindex: '-1'});
|
|
363
363
|
|
|
@@ -2160,25 +2160,33 @@ export abstract class BaseLayout {
|
|
|
2160
2160
|
}
|
|
2161
2161
|
}
|
|
2162
2162
|
|
|
2163
|
-
public DirtyArea(
|
|
2163
|
+
public DirtyArea(areas: IArea|IArea[]): void {
|
|
2164
2164
|
|
|
2165
2165
|
if (!this.initialized) return;
|
|
2166
2166
|
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
if (area.start.column !== Infinity) {
|
|
2171
|
-
start.column = end.column = this.TileIndexForColumn(area.start.column);
|
|
2172
|
-
if (area.end.column !== area.start.column) end.column = this.TileIndexForColumn(area.end.column);
|
|
2173
|
-
}
|
|
2174
|
-
if (area.start.row !== Infinity) {
|
|
2175
|
-
start.row = end.row = this.TileIndexForRow(area.start.row);
|
|
2176
|
-
if (area.end.row !== area.start.row) end.row = this.TileIndexForRow(area.end.row);
|
|
2167
|
+
if (!Array.isArray(areas)) {
|
|
2168
|
+
areas = [areas];
|
|
2177
2169
|
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2170
|
+
|
|
2171
|
+
for (const area of areas) {
|
|
2172
|
+
|
|
2173
|
+
const start = { row: 0, column: 0 };
|
|
2174
|
+
const end = { row: this.grid_tiles[0].length - 1, column: this.grid_tiles.length - 1 };
|
|
2175
|
+
|
|
2176
|
+
if (area.start.column !== Infinity) {
|
|
2177
|
+
start.column = end.column = this.TileIndexForColumn(area.start.column);
|
|
2178
|
+
if (area.end.column !== area.start.column) end.column = this.TileIndexForColumn(area.end.column);
|
|
2181
2179
|
}
|
|
2180
|
+
if (area.start.row !== Infinity) {
|
|
2181
|
+
start.row = end.row = this.TileIndexForRow(area.start.row);
|
|
2182
|
+
if (area.end.row !== area.start.row) end.row = this.TileIndexForRow(area.end.row);
|
|
2183
|
+
}
|
|
2184
|
+
for (let column = start.column; column <= end.column; column++) {
|
|
2185
|
+
for (let row = start.row; row <= end.row; row++) {
|
|
2186
|
+
this.grid_tiles[column][row].dirty = true;
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2182
2190
|
}
|
|
2183
2191
|
|
|
2184
2192
|
}
|
|
@@ -239,7 +239,7 @@ export class TileRenderer {
|
|
|
239
239
|
*/
|
|
240
240
|
public OverflowDirty(full_tile = false): void {
|
|
241
241
|
|
|
242
|
-
const mutated = [];
|
|
242
|
+
const mutated: OverflowRecord[] = [];
|
|
243
243
|
|
|
244
244
|
for (const overflow of this.overflow_areas) {
|
|
245
245
|
const row = overflow.area.start.row;
|
|
@@ -1341,6 +1341,7 @@ export class TileRenderer {
|
|
|
1341
1341
|
else {
|
|
1342
1342
|
|
|
1343
1343
|
const fill = ThemeColor2(this.theme, style.fill);
|
|
1344
|
+
|
|
1344
1345
|
if (fill) {
|
|
1345
1346
|
context.fillStyle = fill;
|
|
1346
1347
|
context.fillRect(0, 0, width - 1, height - 1);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
|
|
2
|
+
import type { CellStyle, EvaluateOptions, IArea, Color, Gradient, GradientStop, UnionValue } from 'treb-base-types';
|
|
3
|
+
|
|
4
|
+
interface VertexPlaceholder {
|
|
5
|
+
result: UnionValue;
|
|
6
|
+
updated: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface CondifionalFormatExpressionOptions {
|
|
10
|
+
style: CellStyle;
|
|
11
|
+
expression: string;
|
|
12
|
+
options?: EvaluateOptions;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* conditional format predicated on an expression. if the expression
|
|
17
|
+
* evaluates to true, we apply the style. otherwise no.
|
|
18
|
+
*/
|
|
19
|
+
export interface ConditionalFormatExpression extends CondifionalFormatExpressionOptions {
|
|
20
|
+
|
|
21
|
+
type: 'expression';
|
|
22
|
+
area: IArea;
|
|
23
|
+
|
|
24
|
+
/** @internal */
|
|
25
|
+
internal?: {
|
|
26
|
+
vertex?: VertexPlaceholder;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ConditionalFormatGradientOptions {
|
|
32
|
+
|
|
33
|
+
/** property defaults to fill */
|
|
34
|
+
property?: 'fill'|'text';
|
|
35
|
+
|
|
36
|
+
/** defaults to HSL */
|
|
37
|
+
color_space?: 'HSL'|'RGB';
|
|
38
|
+
|
|
39
|
+
/** gradient stops, required */
|
|
40
|
+
stops: Array<{ value: number, color: Color }>;
|
|
41
|
+
|
|
42
|
+
/** min and max are optional. if not provided, we use the min/max of the range of data. */
|
|
43
|
+
min?: number;
|
|
44
|
+
|
|
45
|
+
/** min and max are optional. if not provided, we use the min/max of the range of data. */
|
|
46
|
+
max?: number;
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
export const StandardGradientsList = {
|
|
52
|
+
'red-green': {
|
|
53
|
+
color_space: 'RGB',
|
|
54
|
+
stops: [
|
|
55
|
+
{ value: 0, color: { theme: 5, tint: .5 }},
|
|
56
|
+
{ value: 1, color: { theme: 9, tint: .5 }},
|
|
57
|
+
] as GradientStop[],
|
|
58
|
+
},
|
|
59
|
+
'red-yellow-green': {
|
|
60
|
+
color_space: 'RGB',
|
|
61
|
+
stops: [
|
|
62
|
+
{ value: 0, color: { theme: 5, tint: .5 }},
|
|
63
|
+
{ value: 0.5, color: { theme: 7, tint: .5 }},
|
|
64
|
+
{ value: 1, color: { theme: 9, tint: .5 }},
|
|
65
|
+
] as GradientStop[],
|
|
66
|
+
},
|
|
67
|
+
'green-red': {
|
|
68
|
+
color_space: 'RGB',
|
|
69
|
+
stops: [
|
|
70
|
+
{ value: 0, color: { theme: 9, tint: .5 }},
|
|
71
|
+
{ value: 1, color: { theme: 5, tint: .5 }},
|
|
72
|
+
] as GradientStop[],
|
|
73
|
+
},
|
|
74
|
+
'green-yellow-red': {
|
|
75
|
+
color_space: 'RGB',
|
|
76
|
+
stops: [
|
|
77
|
+
{ value: 0, color: { theme: 9, tint: .5 }},
|
|
78
|
+
{ value: 0.5, color: { theme: 7, tint: .5 }},
|
|
79
|
+
{ value: 1, color: { theme: 5, tint: .5 }},
|
|
80
|
+
] as GradientStop[],
|
|
81
|
+
},
|
|
82
|
+
} as const;
|
|
83
|
+
export type StandardGradient = keyof typeof StandardGradientsList;
|
|
84
|
+
|
|
85
|
+
export interface ConditionalFormatGradient extends ConditionalFormatGradientOptions {
|
|
86
|
+
type: 'gradient';
|
|
87
|
+
area: IArea;
|
|
88
|
+
|
|
89
|
+
/** @internal */
|
|
90
|
+
internal?: {
|
|
91
|
+
gradient?: Gradient;
|
|
92
|
+
vertex?: VertexPlaceholder;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface ConditionalFormatCellMatchOptions {
|
|
97
|
+
style: CellStyle;
|
|
98
|
+
expression: string;
|
|
99
|
+
options?: EvaluateOptions;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface ConditionalFormatCellMatch extends ConditionalFormatCellMatchOptions {
|
|
103
|
+
type: 'cell-match';
|
|
104
|
+
area: IArea;
|
|
105
|
+
|
|
106
|
+
/** @internal */
|
|
107
|
+
internal?: {
|
|
108
|
+
vertex?: VertexPlaceholder;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ConditionalFormatCellMatchOptions {
|
|
113
|
+
style: CellStyle;
|
|
114
|
+
expression: string;
|
|
115
|
+
options?: EvaluateOptions;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface ConditionalFormatCellMatch extends ConditionalFormatCellMatchOptions {
|
|
119
|
+
type: 'cell-match';
|
|
120
|
+
area: IArea;
|
|
121
|
+
|
|
122
|
+
/** @internal */
|
|
123
|
+
internal?: {
|
|
124
|
+
vertex?: VertexPlaceholder;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface ConditionalFormatDuplicateValuesOptions {
|
|
129
|
+
style: CellStyle;
|
|
130
|
+
|
|
131
|
+
/** true to highlight unique cells, false to highlight duplicates. defaults to false. */
|
|
132
|
+
unique?: boolean;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface ConditionalFormatDuplicateValues extends ConditionalFormatDuplicateValuesOptions {
|
|
136
|
+
|
|
137
|
+
type: 'duplicate-values';
|
|
138
|
+
area: IArea;
|
|
139
|
+
|
|
140
|
+
/** @internal */
|
|
141
|
+
internal?: {
|
|
142
|
+
vertex?: VertexPlaceholder;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* union, plus we're adding a state used to track application.
|
|
149
|
+
* that state is serialized if it's true.
|
|
150
|
+
* we also add an internal field that will be type-specific, and not serialized.
|
|
151
|
+
*
|
|
152
|
+
* ...everybody has a vertex now, we could standardize it
|
|
153
|
+
*
|
|
154
|
+
*/
|
|
155
|
+
export type ConditionalFormat = { internal?: unknown } & (
|
|
156
|
+
ConditionalFormatDuplicateValues |
|
|
157
|
+
ConditionalFormatExpression |
|
|
158
|
+
ConditionalFormatCellMatch |
|
|
159
|
+
ConditionalFormatGradient
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* the list of formats, in reverse order of precedence. as a starting
|
|
164
|
+
* point we're using the naive approach, just applying everything in
|
|
165
|
+
* order. that may change.
|
|
166
|
+
*/
|
|
167
|
+
export type ConditionalFormatList = ConditionalFormat[];
|
|
168
|
+
|
|
@@ -23,8 +23,8 @@ import type { Sheet } from './sheet';
|
|
|
23
23
|
import type { IArea, ICellAddress, Table, CellStyle } from 'treb-base-types';
|
|
24
24
|
import type { SerializedSheet } from './sheet_types';
|
|
25
25
|
import { NamedRangeCollection } from './named_range';
|
|
26
|
-
import type
|
|
27
|
-
import { Style } from 'treb-base-types';
|
|
26
|
+
import { type ExpressionUnit, type UnitAddress, type UnitStructuredReference, type UnitRange, Parser, QuotedSheetNameRegex } from 'treb-parser';
|
|
27
|
+
import { Area, IsCellAddress, Style } from 'treb-base-types';
|
|
28
28
|
|
|
29
29
|
export interface SerializedMacroFunction {
|
|
30
30
|
name: string;
|
|
@@ -159,6 +159,8 @@ export class SheetCollection {
|
|
|
159
159
|
*/
|
|
160
160
|
export class DataModel {
|
|
161
161
|
|
|
162
|
+
public readonly parser: Parser = new Parser();
|
|
163
|
+
|
|
162
164
|
/** document metadata */
|
|
163
165
|
public document_name?: string;
|
|
164
166
|
|
|
@@ -317,7 +319,132 @@ export class DataModel {
|
|
|
317
319
|
|
|
318
320
|
return undefined;
|
|
319
321
|
}
|
|
320
|
-
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* return an address label for this address (single cell or range)
|
|
325
|
+
*
|
|
326
|
+
* @param address
|
|
327
|
+
* @param active_sheet
|
|
328
|
+
*/
|
|
329
|
+
public AddressToLabel(address: ICellAddress|IArea, active_sheet?: Sheet) {
|
|
330
|
+
|
|
331
|
+
const start = IsCellAddress(address) ? address : address.start;
|
|
332
|
+
const parts = IsCellAddress(address) ?
|
|
333
|
+
[Area.CellAddressToLabel(address)] :
|
|
334
|
+
[Area.CellAddressToLabel(address.start), Area.CellAddressToLabel(address.end)];
|
|
335
|
+
|
|
336
|
+
const sheet = this.sheets.Find(start.sheet_id || 0);
|
|
337
|
+
const name = (sheet?.name) ?
|
|
338
|
+
(QuotedSheetNameRegex.test(sheet.name) ? `'${sheet.name}'` : sheet.name) : '';
|
|
339
|
+
|
|
340
|
+
return name + (name ? '!' : '') + (parts[0] === parts[1] ? parts[0] : parts.join(':'));
|
|
341
|
+
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// --- resolution api, moved from calculator ---------------------------------
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* returns false if the sheet cannot be resolved, which probably
|
|
348
|
+
* means the name changed (that's the case we are working on with
|
|
349
|
+
* this fix).
|
|
350
|
+
*/
|
|
351
|
+
public ResolveSheetID(expr: UnitAddress|UnitRange, context?: ICellAddress, active_sheet?: Sheet): boolean {
|
|
352
|
+
|
|
353
|
+
const target = expr.type === 'address' ? expr : expr.start;
|
|
354
|
+
|
|
355
|
+
if (target.sheet_id) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (target.sheet) {
|
|
360
|
+
const sheet = this.sheets.Find(target.sheet);
|
|
361
|
+
if (sheet) {
|
|
362
|
+
target.sheet_id = sheet.id;
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/*
|
|
367
|
+
const lc = target.sheet.toLowerCase();
|
|
368
|
+
for (const sheet of this.model.sheets.list) {
|
|
369
|
+
if (sheet.name.toLowerCase() === lc) {
|
|
370
|
+
target.sheet_id = sheet.id;
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
*/
|
|
375
|
+
}
|
|
376
|
+
else if (context?.sheet_id) {
|
|
377
|
+
target.sheet_id = context.sheet_id;
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
else if (active_sheet?.id) {
|
|
381
|
+
target.sheet_id = active_sheet.id;
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return false; // the error
|
|
386
|
+
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/** wrapper method ensures it always returns an Area (instance, not interface) */
|
|
390
|
+
public ResolveArea(address: string|ICellAddress|IArea, active_sheet: Sheet): Area {
|
|
391
|
+
const resolved = this.ResolveAddress(address, active_sheet);
|
|
392
|
+
return IsCellAddress(resolved) ? new Area(resolved) : new Area(resolved.start, resolved.end);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* moved from embedded sheet. also modified to preserve ranges, so it
|
|
397
|
+
* might return a range (area). if you are expecting the old behavior
|
|
398
|
+
* you need to check (perhaps we could have a wrapper, or make it optional?)
|
|
399
|
+
*
|
|
400
|
+
* Q: why does this not go in grid? or model? (...)
|
|
401
|
+
* Q: why are we not preserving absoute/relative? (...)
|
|
402
|
+
*
|
|
403
|
+
*/
|
|
404
|
+
public ResolveAddress(address: string|ICellAddress|IArea, active_sheet: Sheet): ICellAddress|IArea {
|
|
405
|
+
|
|
406
|
+
if (typeof address === 'string') {
|
|
407
|
+
const parse_result = this.parser.Parse(address);
|
|
408
|
+
if (parse_result.expression && parse_result.expression.type === 'address') {
|
|
409
|
+
this.ResolveSheetID(parse_result.expression, undefined, active_sheet);
|
|
410
|
+
return {
|
|
411
|
+
row: parse_result.expression.row,
|
|
412
|
+
column: parse_result.expression.column,
|
|
413
|
+
sheet_id: parse_result.expression.sheet_id,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
else if (parse_result.expression && parse_result.expression.type === 'range') {
|
|
417
|
+
this.ResolveSheetID(parse_result.expression, undefined, active_sheet);
|
|
418
|
+
return {
|
|
419
|
+
start: {
|
|
420
|
+
row: parse_result.expression.start.row,
|
|
421
|
+
column: parse_result.expression.start.column,
|
|
422
|
+
sheet_id: parse_result.expression.start.sheet_id,
|
|
423
|
+
},
|
|
424
|
+
end: {
|
|
425
|
+
row: parse_result.expression.end.row,
|
|
426
|
+
column: parse_result.expression.end.column,
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
else if (parse_result.expression && parse_result.expression.type === 'identifier') {
|
|
431
|
+
|
|
432
|
+
// is named range guaranteed to have a sheet ID? (I think yes...)
|
|
433
|
+
|
|
434
|
+
const named_range = this.named_ranges.Get(parse_result.expression.name);
|
|
435
|
+
if (named_range) {
|
|
436
|
+
return named_range;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return { row: 0, column: 0 }; // default for string types -- broken
|
|
441
|
+
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return address; // already range or address
|
|
445
|
+
|
|
446
|
+
}
|
|
447
|
+
|
|
321
448
|
}
|
|
322
449
|
|
|
323
450
|
export interface ViewModel {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
import type { IArea, ICellAddress } from 'treb-base-types';
|
|
3
|
+
|
|
4
|
+
export type DependencyList = Array<IArea|ICellAddress|undefined>;
|
|
5
|
+
export type ExternalEditorCallback = (selection?: string) => DependencyList|undefined;
|
|
6
|
+
|
|
7
|
+
// FIXME: if you want to keep the old interface, split into to separate types
|
|
8
|
+
|
|
9
|
+
export interface ExternalEditorConfig {
|
|
10
|
+
|
|
11
|
+
// --- old interface ---------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* list of dependencies to highlight. we support undefined entries in
|
|
15
|
+
* this list so you can use the result of `EmbeddedSpreadsheet.Resolve`,
|
|
16
|
+
* which may return undefined.
|
|
17
|
+
*/
|
|
18
|
+
dependencies: DependencyList;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* this callback will be called when the selection changes in the
|
|
22
|
+
* spreadsheet and this external editor is active. return an updated
|
|
23
|
+
* list of dependencies to highlight.
|
|
24
|
+
*
|
|
25
|
+
* NOTE: this is currently synchronous, but don't rely on that. it
|
|
26
|
+
* might switch to async in the future depending on how it works in
|
|
27
|
+
* practice.
|
|
28
|
+
*/
|
|
29
|
+
update: ExternalEditorCallback;
|
|
30
|
+
|
|
31
|
+
// --- new interface ---------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* a list of nodes that will serve as editors. when you attach, we will do
|
|
35
|
+
* an initial pass of context highlighting. we highlight on text changes
|
|
36
|
+
* and insert references if you make a selection in the spreadsheet while
|
|
37
|
+
* an editor is focused.
|
|
38
|
+
*/
|
|
39
|
+
nodes: HTMLElement[];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* assume that we're editing a formula. does not require leading `=`.
|
|
43
|
+
* defaults to `true` for historical reasons.
|
|
44
|
+
*/
|
|
45
|
+
assume_formula?: boolean;
|
|
46
|
+
|
|
47
|
+
}
|