@trebco/treb 29.8.3 → 30.1.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-light.mjs +11 -11
- package/dist/treb-spreadsheet.mjs +15 -15
- package/dist/treb.d.ts +11 -1
- package/eslint.config.js +9 -0
- package/package.json +1 -1
- package/treb-base-types/src/area-utils.ts +60 -0
- package/treb-base-types/src/area.ts +11 -0
- package/treb-base-types/src/cell.ts +6 -1
- package/treb-base-types/src/cells.ts +38 -7
- package/treb-base-types/src/index.ts +2 -0
- package/treb-calculator/src/calculator.ts +274 -4
- package/treb-calculator/src/dag/array-vertex.ts +0 -10
- package/treb-calculator/src/dag/graph.ts +118 -77
- package/treb-calculator/src/dag/spreadsheet_vertex.ts +38 -9
- package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +1 -0
- package/treb-calculator/src/expression-calculator.ts +7 -2
- package/treb-calculator/src/function-error.ts +6 -0
- package/treb-charts/src/chart-functions.ts +39 -5
- package/treb-charts/src/chart-types.ts +23 -0
- package/treb-charts/src/chart-utils.ts +165 -2
- package/treb-charts/src/chart.ts +6 -1
- package/treb-charts/src/default-chart-renderer.ts +70 -1
- package/treb-charts/src/index.ts +1 -0
- package/treb-charts/src/renderer.ts +95 -2
- package/treb-charts/style/charts.scss +41 -0
- package/treb-embed/src/embedded-spreadsheet.ts +11 -4
- package/treb-embed/src/options.ts +8 -0
- package/treb-embed/style/dark-theme.scss +4 -0
- package/treb-embed/style/grid.scss +15 -0
- package/treb-embed/style/z-index.scss +3 -0
- package/treb-export/src/import2.ts +9 -0
- package/treb-export/src/workbook2.ts +67 -3
- package/treb-grid/src/editors/editor.ts +12 -5
- package/treb-grid/src/layout/base_layout.ts +41 -0
- package/treb-grid/src/types/grid.ts +72 -28
- package/treb-parser/src/parser-types.ts +3 -0
- package/treb-parser/src/parser.ts +21 -2
- package/treb-utils/src/serialize_html.ts +35 -10
package/dist/treb.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! API
|
|
1
|
+
/*! API v30.1. Copyright 2018-2024 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* add our tag to the map
|
|
@@ -276,6 +276,12 @@ export interface EmbeddedSpreadsheetOptions {
|
|
|
276
276
|
* indent/outdent buttons; default false
|
|
277
277
|
*/
|
|
278
278
|
indent_buttons?: boolean;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* enable spill arrays and spill references. this is on by default
|
|
282
|
+
* starting in 30.1.0. set to false to disable.
|
|
283
|
+
*/
|
|
284
|
+
spill?: boolean;
|
|
279
285
|
}
|
|
280
286
|
|
|
281
287
|
/**
|
|
@@ -291,6 +297,9 @@ export interface ICellAddress {
|
|
|
291
297
|
absolute_row?: boolean;
|
|
292
298
|
absolute_column?: boolean;
|
|
293
299
|
sheet_id?: number;
|
|
300
|
+
|
|
301
|
+
/** spill reference */
|
|
302
|
+
spill?: boolean;
|
|
294
303
|
}
|
|
295
304
|
|
|
296
305
|
/**
|
|
@@ -1794,6 +1803,7 @@ export interface BaseCellData {
|
|
|
1794
1803
|
hyperlink?: string;
|
|
1795
1804
|
type?: SerializedValueType;
|
|
1796
1805
|
sheet_id?: number;
|
|
1806
|
+
spill?: IArea;
|
|
1797
1807
|
}
|
|
1798
1808
|
|
|
1799
1809
|
/**
|
package/eslint.config.js
CHANGED
|
@@ -9,6 +9,15 @@ export default tseslint.config(
|
|
|
9
9
|
{
|
|
10
10
|
rules: {
|
|
11
11
|
|
|
12
|
+
// allow destructuring to use let if some vars are changed
|
|
13
|
+
|
|
14
|
+
"prefer-const": [
|
|
15
|
+
"error", {
|
|
16
|
+
"destructuring": "all",
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
|
|
20
|
+
|
|
12
21
|
// allow destructuring to use garbage variables. prefix name with
|
|
13
22
|
// underscore (or just use underscore).
|
|
14
23
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2024 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { IArea } from './area';
|
|
23
|
+
import type { ICellAddress } from './area';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* area being a class is a mistake, but it will take a while
|
|
27
|
+
* to undo that (if we do it at all). for now we'll start creating
|
|
28
|
+
* utilities that can operate on the interface type, and maybe over
|
|
29
|
+
* time the class will wither.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export function* Iterate(area: IArea): Generator<ICellAddress> {
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* this doesn't serialize. perhaps we should switch to -1,
|
|
36
|
+
* which is obviously invalid. although I think we may from
|
|
37
|
+
* time to time use that as a flag. which is bad, obviously.
|
|
38
|
+
* if we do that we'll need to define a new interface. which
|
|
39
|
+
* might be good.
|
|
40
|
+
*/
|
|
41
|
+
if (area.start.row === Infinity || area.end.row === Infinity) {
|
|
42
|
+
throw new Error(`don't iterate infinite area`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/*
|
|
46
|
+
if (area.entire_row || area.entire_column) {
|
|
47
|
+
throw new Error(`don't iterate infinite area`);
|
|
48
|
+
}
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
const sheet_id = area.start.sheet_id;
|
|
52
|
+
|
|
53
|
+
for (let row = area.start.row; row <= area.end.row; row++) {
|
|
54
|
+
for (let column = area.start.column; column <= area.end.column; column++) {
|
|
55
|
+
yield { row, column, sheet_id };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
|
|
@@ -33,6 +33,10 @@ export interface ICellAddress {
|
|
|
33
33
|
absolute_row?: boolean;
|
|
34
34
|
absolute_column?: boolean;
|
|
35
35
|
sheet_id?: number;
|
|
36
|
+
|
|
37
|
+
/** spill reference */
|
|
38
|
+
spill?: boolean;
|
|
39
|
+
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
/**
|
|
@@ -659,6 +663,13 @@ export class Area implements IArea {
|
|
|
659
663
|
}
|
|
660
664
|
}
|
|
661
665
|
|
|
666
|
+
/** Resizes range in place to be the requested shape */
|
|
667
|
+
public Reshape(rows: number, columns: number): Area {
|
|
668
|
+
this.end_.row = this.start_.row + rows - 1;
|
|
669
|
+
this.end_.column = this.start_.column + columns - 1;
|
|
670
|
+
return this; // fluent
|
|
671
|
+
}
|
|
672
|
+
|
|
662
673
|
/** Resizes range in place so that it includes the given area (merge) */
|
|
663
674
|
public ConsumeArea(area: IArea): void {
|
|
664
675
|
this.ConsumeAddress(area.start);
|
|
@@ -236,6 +236,9 @@ export class Cell {
|
|
|
236
236
|
/** if this cell is part of an array, pointer to the area. */
|
|
237
237
|
public area?: Area;
|
|
238
238
|
|
|
239
|
+
/** testing spill */
|
|
240
|
+
public spill?: Area;
|
|
241
|
+
|
|
239
242
|
/**
|
|
240
243
|
* if this cell is merged, pointer to the area
|
|
241
244
|
*/
|
|
@@ -354,6 +357,7 @@ export class Cell {
|
|
|
354
357
|
public Reset(): void{
|
|
355
358
|
this.type = ValueType.undefined;
|
|
356
359
|
this.value
|
|
360
|
+
= this.spill
|
|
357
361
|
= this.note
|
|
358
362
|
= this.hyperlink
|
|
359
363
|
= this.formatted
|
|
@@ -372,7 +376,8 @@ export class Cell {
|
|
|
372
376
|
public Set(value: CellValue, type = GetValueType(value)): void {
|
|
373
377
|
this.value = value;
|
|
374
378
|
this.type = type;
|
|
375
|
-
this.
|
|
379
|
+
this.spill = // added
|
|
380
|
+
this.formatted =
|
|
376
381
|
this.rendered_type =
|
|
377
382
|
this.style =
|
|
378
383
|
this.calculated =
|
|
@@ -87,6 +87,7 @@ export interface BaseCellData {
|
|
|
87
87
|
hyperlink?: string;
|
|
88
88
|
type?: SerializedValueType; // ValueType;
|
|
89
89
|
sheet_id?: number;
|
|
90
|
+
spill?: IArea;
|
|
90
91
|
// locked?: boolean;
|
|
91
92
|
}
|
|
92
93
|
|
|
@@ -507,6 +508,9 @@ export class Cells {
|
|
|
507
508
|
// cell.calculated = obj.calculated;
|
|
508
509
|
// cell.calculated_type = obj.calculated_type;
|
|
509
510
|
cell.SetCalculatedValue(obj.calculated, this.SerializedTypeToValueType(obj.calculated_type));
|
|
511
|
+
if (obj.spill) {
|
|
512
|
+
cell.spill = new Area(obj.spill.start, obj.spill.end);
|
|
513
|
+
}
|
|
510
514
|
}
|
|
511
515
|
|
|
512
516
|
if (style_refs) {
|
|
@@ -704,6 +708,9 @@ export class Cells {
|
|
|
704
708
|
if (options.calculated_value &&
|
|
705
709
|
typeof cell.calculated !== 'undefined') { // && cell.calculated_type !== ValueType.error) {
|
|
706
710
|
obj.calculated = cell.calculated;
|
|
711
|
+
if (cell.spill) {
|
|
712
|
+
obj.spill = cell.spill;
|
|
713
|
+
}
|
|
707
714
|
|
|
708
715
|
// always preserve error type, because we can't infer
|
|
709
716
|
if (options.preserve_type || cell.calculated_type === ValueType.error) {
|
|
@@ -1216,16 +1223,40 @@ export class Cells {
|
|
|
1216
1223
|
*
|
|
1217
1224
|
* UPDATE: adding area parameter; not shrinking it (don't call w/ infinities)
|
|
1218
1225
|
*/
|
|
1219
|
-
public *IterateRC(area?: IArea) {
|
|
1226
|
+
public *IterateRC(area?: IArea, create_missing_cells = false) {
|
|
1227
|
+
|
|
1228
|
+
if (!area && create_missing_cells) {
|
|
1229
|
+
area = new Area({
|
|
1230
|
+
row: 0,
|
|
1231
|
+
column: 0,
|
|
1232
|
+
}, {
|
|
1233
|
+
row: this.rows_ - 1,
|
|
1234
|
+
column: this.columns - 1,
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1220
1237
|
|
|
1221
1238
|
if (area) {
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1239
|
+
if (create_missing_cells) {
|
|
1240
|
+
for (let row = area.start.row; row <= area.end.row; row++) {
|
|
1241
|
+
if (!this.data[row]) this.data[row] = [];
|
|
1242
|
+
const block = this.data[row];
|
|
1225
1243
|
for (let column = area.start.column; column <= area.end.column; column++) {
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1244
|
+
if (!block[column]) {
|
|
1245
|
+
block[column] = new Cell();
|
|
1246
|
+
}
|
|
1247
|
+
yield { cell: block[column], row, column };
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
else {
|
|
1252
|
+
for (let row = area.start.row; row <= area.end.row; row++) {
|
|
1253
|
+
const block = this.data[row];
|
|
1254
|
+
if (block) {
|
|
1255
|
+
for (let column = area.start.column; column <= area.end.column; column++) {
|
|
1256
|
+
const cell = block[column];
|
|
1257
|
+
if (cell) {
|
|
1258
|
+
yield { cell, row, column };
|
|
1259
|
+
}
|
|
1229
1260
|
}
|
|
1230
1261
|
}
|
|
1231
1262
|
}
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import type { Cell, ICellAddress, ICellAddress2, UnionValue, EvaluateOptions,
|
|
23
|
-
ArrayUnion, IArea, CellDataWithAddress, CellValue
|
|
23
|
+
ArrayUnion, IArea, CellDataWithAddress, CellValue,
|
|
24
|
+
Cells} from 'treb-base-types';
|
|
24
25
|
import { Localization, Area, ValueType, IsCellAddress} from 'treb-base-types';
|
|
25
26
|
|
|
26
27
|
import type { ExpressionUnit, DependencyList, UnitRange, UnitAddress, UnitIdentifier, ParseResult } from 'treb-parser';
|
|
@@ -140,10 +141,14 @@ export interface CalculatorOptions {
|
|
|
140
141
|
*/
|
|
141
142
|
complex_numbers: 'on'|'off';
|
|
142
143
|
|
|
144
|
+
/** enable spill arrays */
|
|
145
|
+
spill?: boolean;
|
|
146
|
+
|
|
143
147
|
}
|
|
144
148
|
|
|
145
149
|
const default_calculator_options: CalculatorOptions = {
|
|
146
150
|
complex_numbers: 'off',
|
|
151
|
+
spill: false,
|
|
147
152
|
};
|
|
148
153
|
|
|
149
154
|
/**
|
|
@@ -181,6 +186,15 @@ export class Calculator extends Graph {
|
|
|
181
186
|
/** the next calculation must do a full rebuild -- set on reset */
|
|
182
187
|
protected full_rebuild_required = false;
|
|
183
188
|
|
|
189
|
+
protected options: CalculatorOptions;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* this is a flag we're using to communicate back to the embedded
|
|
193
|
+
* sheet, when the grid has expanded as a result of a calculation
|
|
194
|
+
* (because of a spill).
|
|
195
|
+
*/
|
|
196
|
+
public grid_expanded = false;
|
|
197
|
+
|
|
184
198
|
constructor(protected readonly model: DataModel, calculator_options: Partial<CalculatorOptions> = {}) {
|
|
185
199
|
|
|
186
200
|
super();
|
|
@@ -191,12 +205,12 @@ export class Calculator extends Graph {
|
|
|
191
205
|
// at the moment options are only used here; in the future
|
|
192
206
|
// we may need to extend handling.
|
|
193
207
|
|
|
194
|
-
|
|
208
|
+
this.options = {
|
|
195
209
|
...default_calculator_options,
|
|
196
210
|
...calculator_options,
|
|
197
211
|
};
|
|
198
212
|
|
|
199
|
-
if (options.complex_numbers === 'on') {
|
|
213
|
+
if (this.options.complex_numbers === 'on') {
|
|
200
214
|
|
|
201
215
|
// complex number handling: we need to change SQRT, POWER and ^
|
|
202
216
|
|
|
@@ -1065,6 +1079,244 @@ export class Calculator extends Graph {
|
|
|
1065
1079
|
}
|
|
1066
1080
|
}
|
|
1067
1081
|
|
|
1082
|
+
public AttachSpillData(area: Area, cells?: Cells) {
|
|
1083
|
+
|
|
1084
|
+
if (!cells) {
|
|
1085
|
+
const sheet = area.start.sheet_id ? this.model.sheets.Find(area.start.sheet_id) : undefined;
|
|
1086
|
+
cells = sheet?.cells;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
if (!cells) {
|
|
1090
|
+
throw new Error('invalid sheet ID in attach spill data');
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const vertex = new StateLeafVertex();
|
|
1094
|
+
let counter = 0;
|
|
1095
|
+
let error = false;
|
|
1096
|
+
|
|
1097
|
+
for (const {cell, row, column} of cells.IterateRC(area, true)) {
|
|
1098
|
+
if (counter++ && (cell.type !== ValueType.undefined || cell.area || cell.merge_area || cell.table)) {
|
|
1099
|
+
error = true; // spill error.
|
|
1100
|
+
}
|
|
1101
|
+
this.AddLeafVertexEdge({row, column, sheet_id: area.start.sheet_id}, vertex);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// console.info("storing spill data");
|
|
1105
|
+
this.spill_data.push({area, vertex});
|
|
1106
|
+
|
|
1107
|
+
return { vertex, area, error };
|
|
1108
|
+
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
public SpillCallback(vertex: SpreadsheetVertex, result: ArrayUnion): SpreadsheetVertex[] {
|
|
1112
|
+
|
|
1113
|
+
const { reference, address } = vertex;
|
|
1114
|
+
const { value } = result;
|
|
1115
|
+
|
|
1116
|
+
const recalculate_list: SpreadsheetVertex[] = [];
|
|
1117
|
+
|
|
1118
|
+
if (!reference) {
|
|
1119
|
+
// should throw but this is new and I don't want to break stuff rn
|
|
1120
|
+
console.error("invalid reference in spill callback");
|
|
1121
|
+
return recalculate_list;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
if (!address || !address.sheet_id) {
|
|
1125
|
+
// should throw but this is new and I don't want to break stuff rn
|
|
1126
|
+
console.error("invalid address in spill callback");
|
|
1127
|
+
return recalculate_list;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// I guess we could do the one-cell version here
|
|
1131
|
+
|
|
1132
|
+
if (value.length === 1 && value[0].length === 1) {
|
|
1133
|
+
reference.SetCalculatedValue(value[0][0].value as CellValue);
|
|
1134
|
+
return recalculate_list;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
if (!this.options.spill) {
|
|
1138
|
+
reference.SetCalculatedValue(value[0][0].value as CellValue);
|
|
1139
|
+
return recalculate_list;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// console.info("SPILLING");
|
|
1143
|
+
|
|
1144
|
+
const sheet = this.model.sheets.Find(address.sheet_id);
|
|
1145
|
+
const cells = sheet?.cells;
|
|
1146
|
+
|
|
1147
|
+
if (cells) {
|
|
1148
|
+
|
|
1149
|
+
// first thing we do is check for empty. if !empty, that's a
|
|
1150
|
+
// spill error and we can stop. also check for area, spill and
|
|
1151
|
+
// merge (and table).
|
|
1152
|
+
|
|
1153
|
+
const columns = result.value.length;
|
|
1154
|
+
const rows = result.value[0].length;
|
|
1155
|
+
const area = new Area(address).Reshape(rows, columns);
|
|
1156
|
+
|
|
1157
|
+
|
|
1158
|
+
/*
|
|
1159
|
+
let counter = 0;
|
|
1160
|
+
let error = false;
|
|
1161
|
+
const leaf = new StateLeafVertex();
|
|
1162
|
+
|
|
1163
|
+
for (const {cell, row, column} of cells.IterateRC(area, true)) {
|
|
1164
|
+
if (counter++ && (cell.type !== ValueType.undefined || cell.area || cell.merge_area || cell.table)) {
|
|
1165
|
+
error = true; // spill error.
|
|
1166
|
+
}
|
|
1167
|
+
this.AddLeafVertexEdge({row, column, sheet_id: area.start.sheet_id}, leaf);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
console.info("storing spill data");
|
|
1171
|
+
|
|
1172
|
+
// this.spills.push(new Area(area.start, area.end));
|
|
1173
|
+
this.spill_data.push({area, vertex: leaf});
|
|
1174
|
+
*/
|
|
1175
|
+
|
|
1176
|
+
const { error } = this.AttachSpillData(area, cells);
|
|
1177
|
+
|
|
1178
|
+
if (error) {
|
|
1179
|
+
// console.info("returning error");
|
|
1180
|
+
reference.SetCalculationError('SPILL');
|
|
1181
|
+
return recalculate_list;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// expand the sheet, if necessary (+1)
|
|
1185
|
+
|
|
1186
|
+
if (sheet.rows < area.end.row + 1) {
|
|
1187
|
+
sheet.cells.EnsureRow(area.end.row + 1);
|
|
1188
|
+
this.grid_expanded = true;
|
|
1189
|
+
}
|
|
1190
|
+
if (sheet.columns < area.end.column + 1) {
|
|
1191
|
+
sheet.cells.EnsureColumn(area.end.column + 1);
|
|
1192
|
+
this.grid_expanded = true;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// hmmm... we need the grid to update... how can we ensure that?
|
|
1196
|
+
// we could use a flag that the embedded sheet checks after
|
|
1197
|
+
// calculation... which is kind of sloppy but I don't have a better
|
|
1198
|
+
// idea
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
const sheet_id = address.sheet_id;
|
|
1203
|
+
// let dirty = false;
|
|
1204
|
+
|
|
1205
|
+
for (const {row, column} of cells.IterateRC(area)) {
|
|
1206
|
+
if (row === address.row && column === address.column) { continue; }
|
|
1207
|
+
|
|
1208
|
+
const vertex = this.GetVertex({sheet_id, row, column}, false) as SpreadsheetVertex;
|
|
1209
|
+
|
|
1210
|
+
if (vertex) {
|
|
1211
|
+
// onsole.info("Have vertex @", row, column, "dirty?", vertex.dirty);
|
|
1212
|
+
|
|
1213
|
+
if (!(vertex as SpreadsheetVertex).dirty) {
|
|
1214
|
+
recalculate_list.push(vertex);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// do we need these edges? if so, what for? (...)
|
|
1219
|
+
// I guess to propagate dirty if there's a dependent?
|
|
1220
|
+
// apparently not, although I'm not sure why...
|
|
1221
|
+
|
|
1222
|
+
|
|
1223
|
+
// this.AddEdge(address, {sheet_id, row, column});
|
|
1224
|
+
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
/*
|
|
1228
|
+
// ok, now we can go on: copying a little from dynamic dependencies,
|
|
1229
|
+
// we're going to add vertices and check for dirty:
|
|
1230
|
+
|
|
1231
|
+
const sheet_id = address.sheet_id;
|
|
1232
|
+
let dirty = false;
|
|
1233
|
+
|
|
1234
|
+
for (const {row, column} of cells.IterateRC(area)) {
|
|
1235
|
+
|
|
1236
|
+
if (row === address.row && column === address.column) { continue; }
|
|
1237
|
+
|
|
1238
|
+
const vertex = this.GetVertex({sheet_id, row, column}, true);
|
|
1239
|
+
if (vertex && vertex.dirty) {
|
|
1240
|
+
|
|
1241
|
+
console.info(`Adding edge from ${{row: address.row, column: address.column}} -> ${{row, column}}`)
|
|
1242
|
+
|
|
1243
|
+
// see comments in DynamicDependencies()
|
|
1244
|
+
|
|
1245
|
+
this.AddEdge(address, {row, column, sheet_id});
|
|
1246
|
+
dirty = true;
|
|
1247
|
+
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
console.info("DIRTY?", dirty);
|
|
1253
|
+
|
|
1254
|
+
if (dirty) {
|
|
1255
|
+
const current_vertex = this.GetVertex(address, true) as SpreadsheetVertex;
|
|
1256
|
+
current_vertex.short_circuit = true;
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
*/
|
|
1260
|
+
|
|
1261
|
+
//
|
|
1262
|
+
|
|
1263
|
+
|
|
1264
|
+
// maybe we could use a vertex here?
|
|
1265
|
+
// actually we also need to do a loop check
|
|
1266
|
+
|
|
1267
|
+
// so I think the approach is
|
|
1268
|
+
//
|
|
1269
|
+
// 1 - create a vertex (spill -- array vertex?)
|
|
1270
|
+
// 2 - check for loops
|
|
1271
|
+
// 3 - if no loop, check for empty
|
|
1272
|
+
// 4 - if empty, fill in values
|
|
1273
|
+
//
|
|
1274
|
+
|
|
1275
|
+
// and then we need to flush spill vertices at
|
|
1276
|
+
// some point, either always on recalc, or on
|
|
1277
|
+
// recalc if something is dirty. flushing spill
|
|
1278
|
+
// vertices implies removing all spilled values
|
|
1279
|
+
// so they will be empty if something changes
|
|
1280
|
+
|
|
1281
|
+
// PLAN: start by flushing all spill vertices on
|
|
1282
|
+
// every recalc, and then we can trim it back
|
|
1283
|
+
|
|
1284
|
+
// spill ok, set values
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
for (let {cell, row, column} of cells.IterateRC(area)) {
|
|
1288
|
+
|
|
1289
|
+
cell.spill = area;
|
|
1290
|
+
|
|
1291
|
+
row -= address.row;
|
|
1292
|
+
column -= address.column;
|
|
1293
|
+
|
|
1294
|
+
const v = result.value[column][row];
|
|
1295
|
+
|
|
1296
|
+
switch (v.type) {
|
|
1297
|
+
case ValueType.object:
|
|
1298
|
+
case ValueType.array:
|
|
1299
|
+
break;
|
|
1300
|
+
default:
|
|
1301
|
+
cell.SetCalculatedValue(v.value, v.type);
|
|
1302
|
+
break;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
return recalculate_list;
|
|
1308
|
+
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
//
|
|
1312
|
+
|
|
1313
|
+
console.error("invalid cell reference in spill callback");
|
|
1314
|
+
reference.SetCalculationError('SPILL');
|
|
1315
|
+
|
|
1316
|
+
return [];
|
|
1317
|
+
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1068
1320
|
/**
|
|
1069
1321
|
* this is a mess [not as bad as it used to be]
|
|
1070
1322
|
*/
|
|
@@ -1507,6 +1759,8 @@ export class Calculator extends Graph {
|
|
|
1507
1759
|
|
|
1508
1760
|
this.RebuildGraph(subset);
|
|
1509
1761
|
|
|
1762
|
+
this.grid_expanded = false; // unset
|
|
1763
|
+
|
|
1510
1764
|
try {
|
|
1511
1765
|
this.Recalculate();
|
|
1512
1766
|
}
|
|
@@ -2567,7 +2821,23 @@ export class Calculator extends Graph {
|
|
|
2567
2821
|
*/
|
|
2568
2822
|
protected RebuildGraphCell(cell: Cell, address: ICellAddress2): void {
|
|
2569
2823
|
|
|
2570
|
-
//
|
|
2824
|
+
// FIXME/TODO: if spill is not enabled, we'll need to clean up
|
|
2825
|
+
// rendered values from the spill here
|
|
2826
|
+
|
|
2827
|
+
if (cell.spill) {
|
|
2828
|
+
if (this.options.spill) {
|
|
2829
|
+
if (cell.spill.start.row === address.row && cell.spill.start.column === address.column) {
|
|
2830
|
+
|
|
2831
|
+
// this.spills.push(new Area(cell.spill.start, cell.spill.end));
|
|
2832
|
+
this.AttachSpillData(new Area(cell.spill.start, cell.spill.end));
|
|
2833
|
+
|
|
2834
|
+
}
|
|
2835
|
+
else {
|
|
2836
|
+
// ...
|
|
2837
|
+
this.AddEdge(cell.spill.start, address);
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2571
2841
|
|
|
2572
2842
|
// array head
|
|
2573
2843
|
if (cell.area && cell.area.start.column === address.column && cell.area.start.row === address.row) {
|
|
@@ -66,16 +66,6 @@ export class ArrayVertex extends SpreadsheetVertexBase {
|
|
|
66
66
|
/** the target area */
|
|
67
67
|
public area: Area;
|
|
68
68
|
|
|
69
|
-
/* * temporary method, we should clean up explicitly * /
|
|
70
|
-
public static CheckOutbound(): void {
|
|
71
|
-
for (const vertex of this.list) {
|
|
72
|
-
if (vertex.edges_out.length === 0) {
|
|
73
|
-
console.info('no outbound edges', vertex);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
*/
|
|
78
|
-
|
|
79
69
|
/**
|
|
80
70
|
* factory/lookup method: creates a vertex if it does not exist, or
|
|
81
71
|
* returns existing vertex. returns the vertex and a flag indicating
|