@trebco/treb 29.2.0 → 29.3.2
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 +7 -7
- package/dist/treb-spreadsheet.mjs +15 -15
- package/dist/treb.d.ts +20 -9
- package/package.json +1 -1
- package/treb-base-types/src/area.ts +7 -0
- package/treb-calculator/src/calculator.ts +10 -8
- package/treb-calculator/src/dag/array-vertex.ts +0 -2
- package/treb-calculator/src/dag/graph.ts +88 -2
- package/treb-calculator/src/dag/spreadsheet_vertex.ts +1 -60
- package/treb-calculator/src/expression-calculator.ts +3 -1
- package/treb-calculator/src/functions/base-functions.ts +6 -1
- package/treb-data-model/src/data_model.ts +45 -59
- package/treb-data-model/src/index.ts +1 -1
- package/treb-data-model/src/named.ts +11 -32
- package/treb-data-model/src/sheet.ts +14 -6
- package/treb-export/src/export2.ts +3 -82
- package/treb-export/src/workbook2.ts +2 -2
- package/treb-grid/src/types/grid.ts +3 -3
package/dist/treb.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! API v29.
|
|
1
|
+
/*! API v29.3. 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
|
|
@@ -1596,25 +1596,36 @@ export interface SerializedMacroFunction {
|
|
|
1596
1596
|
argument_names?: string[];
|
|
1597
1597
|
description?: string;
|
|
1598
1598
|
}
|
|
1599
|
+
|
|
1600
|
+
/**
|
|
1601
|
+
* this type is no longer in use, but we retain it to parse old documents
|
|
1602
|
+
* that use it.
|
|
1603
|
+
*
|
|
1604
|
+
* @deprecated
|
|
1605
|
+
*/
|
|
1599
1606
|
export interface SerializedNamedExpression {
|
|
1600
1607
|
name: string;
|
|
1601
1608
|
expression: string;
|
|
1602
1609
|
}
|
|
1603
1610
|
|
|
1604
1611
|
/**
|
|
1605
|
-
* serialized type
|
|
1612
|
+
* serialized type is a composite of expression/range. we determine
|
|
1613
|
+
* what it is when parsing the expression. this simplifies passing these
|
|
1614
|
+
* things around.
|
|
1615
|
+
*
|
|
1616
|
+
* (named expressions and ranges they have slightly different behavior,
|
|
1617
|
+
* which is why we have a distinction at all).
|
|
1618
|
+
*
|
|
1606
1619
|
*/
|
|
1607
1620
|
export interface SerializedNamed {
|
|
1608
1621
|
name: string;
|
|
1609
|
-
|
|
1610
|
-
expression
|
|
1622
|
+
|
|
1623
|
+
/** expression or address/area */
|
|
1624
|
+
expression: string;
|
|
1625
|
+
|
|
1626
|
+
/** scope is a sheet name (not ID) */
|
|
1611
1627
|
scope?: string;
|
|
1612
1628
|
}
|
|
1613
|
-
export type SerializedArea = IArea & {
|
|
1614
|
-
start: ICellAddress & {
|
|
1615
|
-
sheet: string;
|
|
1616
|
-
};
|
|
1617
|
-
};
|
|
1618
1629
|
export interface SerializedSheet {
|
|
1619
1630
|
|
|
1620
1631
|
/** cell data */
|
package/package.json
CHANGED
|
@@ -102,6 +102,9 @@ export interface Dimensions {
|
|
|
102
102
|
* start/end value for row/column/both, so watch out on loops. the
|
|
103
103
|
* sheet class has a method for reducing infinite ranges to actual
|
|
104
104
|
* populated ranges.
|
|
105
|
+
*
|
|
106
|
+
* infinitiy is turning into a headache because it doesn't serialize
|
|
107
|
+
* to json properly. should we switch to a flag, or -1, or something?
|
|
105
108
|
*/
|
|
106
109
|
export class Area implements IArea {
|
|
107
110
|
|
|
@@ -166,6 +169,10 @@ export class Area implements IArea {
|
|
|
166
169
|
|
|
167
170
|
public static CellAddressToLabel(address: ICellAddress, sheet_id = false): string {
|
|
168
171
|
|
|
172
|
+
if (address.row === Infinity || address.column === Infinity) {
|
|
173
|
+
throw new Error('this is going to break something');
|
|
174
|
+
}
|
|
175
|
+
|
|
169
176
|
const prefix = sheet_id ? `${address.sheet_id || 0}!` : '';
|
|
170
177
|
|
|
171
178
|
return prefix
|
|
@@ -1930,12 +1930,15 @@ export class Calculator extends Graph {
|
|
|
1930
1930
|
}
|
|
1931
1931
|
}
|
|
1932
1932
|
|
|
1933
|
+
/*
|
|
1933
1934
|
if (area.count > 1) {
|
|
1934
1935
|
range = Area.CellAddressToLabel(area.start) + ':' + Area.CellAddressToLabel(area.end);
|
|
1935
1936
|
}
|
|
1936
1937
|
else {
|
|
1937
1938
|
range = Area.CellAddressToLabel(area.start);
|
|
1938
1939
|
}
|
|
1940
|
+
*/
|
|
1941
|
+
range = area.spreadsheet_label;
|
|
1939
1942
|
|
|
1940
1943
|
if (!qualified) {
|
|
1941
1944
|
return range;
|
|
@@ -2514,21 +2517,20 @@ export class Calculator extends Graph {
|
|
|
2514
2517
|
const unit = dependencies.ranges[key];
|
|
2515
2518
|
const range = new Area(unit.start, unit.end);
|
|
2516
2519
|
|
|
2517
|
-
|
|
2518
|
-
this.AddLeafVertexEdge(
|
|
2520
|
+
if (range.entire_column || range.entire_row || range.count > 1) {
|
|
2521
|
+
// this.AddLeafVertexEdge(range.start, vertex);
|
|
2522
|
+
this.AddLeafVertexArrayEdge(range, vertex);
|
|
2523
|
+
}
|
|
2524
|
+
else {
|
|
2525
|
+
this.AddLeafVertexEdge(range.start, vertex);
|
|
2519
2526
|
}
|
|
2520
|
-
|
|
2521
|
-
/*
|
|
2522
|
-
range.Iterate((address: ICellAddress) => {
|
|
2523
|
-
this.AddLeafVertexEdge(address, vertex);
|
|
2524
|
-
});
|
|
2525
|
-
*/
|
|
2526
2527
|
|
|
2527
2528
|
/*
|
|
2528
2529
|
for (const address of range) {
|
|
2529
2530
|
this.AddLeafVertexEdge(address, vertex);
|
|
2530
2531
|
}
|
|
2531
2532
|
*/
|
|
2533
|
+
|
|
2532
2534
|
}
|
|
2533
2535
|
|
|
2534
2536
|
for (const key of Object.keys(dependencies.addresses)){
|
|
@@ -101,11 +101,9 @@ export class ArrayVertex extends SpreadsheetVertexBase {
|
|
|
101
101
|
* returns a list of arrays that contain this address
|
|
102
102
|
*/
|
|
103
103
|
public static GetContainingArrays(address: ICellAddress2): ArrayVertex[] {
|
|
104
|
-
// console.info('av2 get arrays:', address.row, address.column);
|
|
105
104
|
const list: ArrayVertex[] = [];
|
|
106
105
|
for (const entry of this.list) {
|
|
107
106
|
if ((entry.area.start.sheet_id === address.sheet_id) && entry.area.Contains(address)) {
|
|
108
|
-
// console.info("match", entry.area.spreadsheet_label);
|
|
109
107
|
list.push(entry);
|
|
110
108
|
}
|
|
111
109
|
}
|
|
@@ -613,9 +613,91 @@ export abstract class Graph implements GraphCallbacks {
|
|
|
613
613
|
/**
|
|
614
614
|
* new array vertices
|
|
615
615
|
*/
|
|
616
|
-
|
|
616
|
+
protected CompositeAddArrayEdge(u: Area, vertex: Vertex): void {
|
|
617
|
+
|
|
618
|
+
if (!u.start.sheet_id) {
|
|
619
|
+
throw new Error('AddArrayEdge called without sheet ID');
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// create or use existing
|
|
623
|
+
const [array_vertex, created] = ArrayVertex.GetVertex(u);
|
|
624
|
+
|
|
625
|
+
// add an edge
|
|
626
|
+
vertex.DependsOn(array_vertex);
|
|
627
|
+
|
|
628
|
+
// force a check on next calculation pass
|
|
629
|
+
this.loop_check_required = true;
|
|
630
|
+
|
|
631
|
+
if (!created) {
|
|
632
|
+
// console.info('reusing, so not adding edges');
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// now add edges from/to nodes THAT ALREADY EXIST
|
|
637
|
+
|
|
638
|
+
// range can't span sheets, so we only need one set to look up
|
|
639
|
+
|
|
640
|
+
const map = this.vertices[u.start.sheet_id];
|
|
641
|
+
|
|
642
|
+
// this might happen on create, we can let it go because the
|
|
643
|
+
// references will be added when the relevant sheet is added
|
|
644
|
+
|
|
645
|
+
if (!map) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// ...
|
|
650
|
+
|
|
651
|
+
if (u.entire_row) {
|
|
652
|
+
// console.group('entire row(s)')
|
|
653
|
+
for (let column = 0; column < map.length; column++) {
|
|
654
|
+
if (map[column]) {
|
|
655
|
+
for (let row = u.start.row; row <= u.end.row; row++ ) {
|
|
656
|
+
const vertex = map[column][row];
|
|
657
|
+
if (vertex) {
|
|
658
|
+
// console.info('add', column, row);
|
|
659
|
+
array_vertex.DependsOn(vertex);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
// console.groupEnd();
|
|
665
|
+
}
|
|
666
|
+
else if (u.entire_column) {
|
|
667
|
+
// console.group('entire column(s)');
|
|
668
|
+
for (let column = u.start.column; column <= u.end.column; column++) {
|
|
669
|
+
if(map[column]) {
|
|
670
|
+
for (const vertex of map[column]) {
|
|
671
|
+
if (vertex?.address) {
|
|
672
|
+
// console.info('add', vertex.address);
|
|
673
|
+
array_vertex.DependsOn(vertex);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
// console.groupEnd();
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
for (let row = u.start.row; row <= u.end.row; row++) {
|
|
682
|
+
for (let column = u.start.column; column <= u.end.column; column++) {
|
|
683
|
+
const vertex = map[column][row];
|
|
684
|
+
if (vertex) {
|
|
685
|
+
array_vertex.DependsOn(vertex);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
617
690
|
|
|
618
|
-
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
public AddLeafVertexArrayEdge(u: Area, vertex: LeafVertex) {
|
|
694
|
+
this.CompositeAddArrayEdge(u, vertex);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* new array vertices
|
|
699
|
+
*/
|
|
700
|
+
public AddArrayEdge(u: Area, v: ICellAddress): void {
|
|
619
701
|
|
|
620
702
|
if (!u.start.sheet_id) {
|
|
621
703
|
throw new Error('AddArrayEdge called without sheet ID');
|
|
@@ -624,6 +706,9 @@ export abstract class Graph implements GraphCallbacks {
|
|
|
624
706
|
// this should have already been added...
|
|
625
707
|
const v_v = this.GetVertex(v, true);
|
|
626
708
|
|
|
709
|
+
this.CompositeAddArrayEdge(u, v_v);
|
|
710
|
+
|
|
711
|
+
/*
|
|
627
712
|
// create or use existing
|
|
628
713
|
const [array_vertex, created] = ArrayVertex.GetVertex(u);
|
|
629
714
|
|
|
@@ -692,6 +777,7 @@ export abstract class Graph implements GraphCallbacks {
|
|
|
692
777
|
}
|
|
693
778
|
}
|
|
694
779
|
}
|
|
780
|
+
*/
|
|
695
781
|
|
|
696
782
|
}
|
|
697
783
|
|
|
@@ -226,71 +226,13 @@ export class SpreadsheetVertex extends SpreadsheetVertexBase {
|
|
|
226
226
|
// handle it properly regardless.
|
|
227
227
|
|
|
228
228
|
const single = (this.result.type === ValueType.array) ? this.result.value[0][0] : this.result;
|
|
229
|
-
/*
|
|
230
|
-
if (single.type === ValueType.object) {
|
|
231
|
-
this.reference.SetCalculationError('OBJECT');
|
|
232
|
-
}
|
|
233
|
-
else */
|
|
234
229
|
|
|
235
230
|
// we are using object type in the returned value for sparklines...
|
|
236
231
|
// so we can't drop it here. we could change rendering though. or
|
|
237
232
|
// whitelist types. or blacklist types. or something.
|
|
238
233
|
|
|
239
|
-
|
|
240
|
-
this.reference.SetCalculatedValue(single.value as CellValue, single.type);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/*
|
|
244
|
-
console.info("T2", t2);
|
|
245
|
-
|
|
246
|
-
// because we let sloppy data filter through, it's possible
|
|
247
|
-
// that we get some random stuff at this point. generally this
|
|
248
|
-
// shoudl not happen but if you use (e.g.) one of the chart
|
|
249
|
-
// functions in a spreadsheet cell, you'll get a 1d array.
|
|
250
|
-
|
|
251
|
-
// so we need to validate that what we have is either a UnionValue
|
|
252
|
-
// or a 2d UnionValue[][] array.
|
|
253
|
-
|
|
254
|
-
// don't know the performance cost of this.
|
|
255
|
-
|
|
256
|
-
// FIXME: don't let sloppy data through.
|
|
257
|
-
|
|
258
|
-
let test: any = this.result;
|
|
259
|
-
|
|
260
|
-
if (Array.isArray(test)) {
|
|
261
|
-
if (test[0] && Array.isArray(test[0])) {
|
|
262
|
-
test = test[0][0];
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
// console.warn('error 1');
|
|
266
|
-
test = undefined;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (!test || typeof (test as any).type === undefined) {
|
|
271
|
-
// console.warn('error 2/3');
|
|
272
|
-
this.reference.SetCalculationError('UNK');
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
this.reference.SetCalculatedValue((test as UnionValue).value, (test as UnionValue).type);
|
|
276
|
-
}
|
|
277
|
-
*/
|
|
278
|
-
|
|
279
|
-
/*
|
|
280
|
-
const single = Array.isArray(this.result) ? this.result[0][0] : this.result;
|
|
281
|
-
|
|
282
|
-
// error is implicit
|
|
283
|
-
this.reference.SetCalculatedValue(single.value, single.type);
|
|
284
|
-
*/
|
|
234
|
+
this.reference.SetCalculatedValue(single.value as CellValue, single.type);
|
|
285
235
|
|
|
286
|
-
/*
|
|
287
|
-
if (typeof this.result === 'object' && this.result.error) {
|
|
288
|
-
this.reference.SetCalculationError(this.result.error);
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
this.reference.SetCalculatedValue(this.result);
|
|
292
|
-
}
|
|
293
|
-
*/
|
|
294
236
|
}
|
|
295
237
|
|
|
296
238
|
}
|
|
@@ -318,7 +260,6 @@ export class SpreadsheetVertex extends SpreadsheetVertexBase {
|
|
|
318
260
|
//
|
|
319
261
|
|
|
320
262
|
for (const edge of this.edges_out as Set<SpreadsheetVertexBase>){
|
|
321
|
-
// (edge as SpreadsheetVertex).Calculate(graph);
|
|
322
263
|
if (edge.dirty) {
|
|
323
264
|
graph.calculation_list.push(edge);
|
|
324
265
|
}
|
|
@@ -502,7 +502,9 @@ export class ExpressionCalculator {
|
|
|
502
502
|
|
|
503
503
|
if (!func) {
|
|
504
504
|
|
|
505
|
-
|
|
505
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
506
|
+
console.info('(dev) missing function', outer.name);
|
|
507
|
+
}
|
|
506
508
|
|
|
507
509
|
return () => NameError();
|
|
508
510
|
}
|
|
@@ -416,7 +416,12 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
416
416
|
case 1: // feb, special
|
|
417
417
|
{
|
|
418
418
|
const year = date.getUTCFullYear();
|
|
419
|
-
|
|
419
|
+
|
|
420
|
+
// it's a leap year if it is divisible by 4, unless it's also
|
|
421
|
+
// divisible by 100 AND NOT divisible by 400. 1900 is grand-
|
|
422
|
+
// fathered in via an error in Lotus.
|
|
423
|
+
|
|
424
|
+
if (year % 4 === 0 && (year === 1900 || (year % 400 === 0) || (year % 100 !== 0))) {
|
|
420
425
|
date.setUTCDate(29);
|
|
421
426
|
}
|
|
422
427
|
else {
|
|
@@ -25,7 +25,7 @@ import type { IArea, ICellAddress, Table, CellStyle } from 'treb-base-types';
|
|
|
25
25
|
import type { SerializedSheet } from './sheet_types';
|
|
26
26
|
import { type ExpressionUnit, type UnitAddress, type UnitStructuredReference, type UnitRange, Parser, QuotedSheetNameRegex, DecimalMarkType, ArgumentSeparatorType } from 'treb-parser';
|
|
27
27
|
import { Area, IsCellAddress, Style } from 'treb-base-types';
|
|
28
|
-
import type {
|
|
28
|
+
import type { SerializedNamed } from './named';
|
|
29
29
|
import { NamedRangeManager } from './named';
|
|
30
30
|
|
|
31
31
|
export interface ConnectedElementType {
|
|
@@ -51,7 +51,7 @@ export interface MacroFunction extends SerializedMacroFunction {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
54
|
+
*
|
|
55
55
|
*/
|
|
56
56
|
export class DataModel {
|
|
57
57
|
|
|
@@ -128,51 +128,46 @@ export class DataModel {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
|
-
*
|
|
132
|
-
* need to check if something is a range -- if so, it will just be
|
|
133
|
-
* a single address or range. in that case store it as a named range.
|
|
131
|
+
*
|
|
134
132
|
*/
|
|
135
|
-
public
|
|
133
|
+
public UnserializeNames(names: SerializedNamed[], active_sheet?: Sheet) {
|
|
136
134
|
|
|
137
135
|
this.parser.Save();
|
|
138
136
|
this.parser.SetLocaleSettings(DecimalMarkType.Period, ArgumentSeparatorType.Comma);
|
|
139
137
|
|
|
140
|
-
const sorted = names.map(named => {
|
|
141
|
-
|
|
142
|
-
if (parse_result.expression) {
|
|
143
|
-
if (parse_result.expression.type === 'address' || parse_result.expression.type === 'range') {
|
|
144
|
-
return {
|
|
145
|
-
...named,
|
|
146
|
-
expression: undefined,
|
|
147
|
-
area: parse_result.expression.type === 'address' ? {
|
|
148
|
-
start: parse_result.expression,
|
|
149
|
-
end: parse_result.expression,
|
|
150
|
-
} : parse_result.expression,
|
|
151
|
-
} as SerializedNamed;
|
|
152
|
-
}
|
|
153
|
-
return {
|
|
154
|
-
...named
|
|
155
|
-
} as SerializedNamed;
|
|
156
|
-
}
|
|
157
|
-
return undefined;
|
|
158
|
-
}).filter((test): test is SerializedNamed => !!test);
|
|
138
|
+
//const sorted = names.map(named => {
|
|
139
|
+
for (const named of names) {
|
|
159
140
|
|
|
160
|
-
|
|
161
|
-
this.UnserializeNames(sorted, active_sheet);
|
|
141
|
+
if (!named.expression) { continue; }
|
|
162
142
|
|
|
163
|
-
|
|
143
|
+
const parse_result = this.parser.Parse(named.expression);
|
|
144
|
+
if (parse_result.expression) {
|
|
164
145
|
|
|
165
|
-
|
|
146
|
+
const scope = (typeof named.scope === 'string') ? this.sheets.ID(named.scope) : undefined;
|
|
166
147
|
|
|
167
|
-
|
|
148
|
+
if (parse_result.expression.type === 'address' || parse_result.expression.type === 'range') {
|
|
168
149
|
|
|
169
|
-
|
|
150
|
+
const [start, end] = parse_result.expression.type === 'range' ?
|
|
151
|
+
[ parse_result.expression.start, parse_result.expression.end, ] :
|
|
152
|
+
[ parse_result.expression, parse_result.expression ];
|
|
170
153
|
|
|
171
|
-
|
|
154
|
+
if (start.sheet) {
|
|
155
|
+
const area = new Area({...start, sheet_id: this.sheets.ID(start.sheet), }, end);
|
|
156
|
+
|
|
157
|
+
if (area.start.sheet_id) {
|
|
158
|
+
this.named.SetNamedRange(named.name, area, scope);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
console.warn("missing sheet ID?", start);
|
|
162
|
+
}
|
|
172
163
|
|
|
173
|
-
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
console.warn("missing sheet name?", start);
|
|
167
|
+
}
|
|
174
168
|
|
|
175
|
-
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
176
171
|
this.parser.Walk(parse_result.expression, unit => {
|
|
177
172
|
if (unit.type === 'address' || unit.type === 'range') {
|
|
178
173
|
if (unit.type === 'range') {
|
|
@@ -191,31 +186,14 @@ export class DataModel {
|
|
|
191
186
|
return true;
|
|
192
187
|
});
|
|
193
188
|
|
|
194
|
-
this.named.SetNamedExpression(
|
|
195
|
-
|
|
189
|
+
this.named.SetNamedExpression(named.name, parse_result.expression, scope);
|
|
196
190
|
}
|
|
197
191
|
|
|
198
192
|
}
|
|
199
|
-
|
|
200
|
-
if (entry.area.start.sheet) {
|
|
201
|
-
|
|
202
|
-
const area = new Area({
|
|
203
|
-
...entry.area.start,
|
|
204
|
-
sheet_id: this.sheets.ID(entry.area.start.sheet),
|
|
205
|
-
}, entry.area.end);
|
|
206
|
-
|
|
207
|
-
if (area.start.sheet_id) {
|
|
208
|
-
this.named.SetNamedRange(entry.name, area, scope);
|
|
209
|
-
}
|
|
210
|
-
else {
|
|
211
|
-
console.warn("missing sheet ID?", entry.area.start.sheet);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
console.warn("missing sheet name?");
|
|
216
|
-
}
|
|
217
|
-
}
|
|
193
|
+
return undefined;
|
|
218
194
|
}
|
|
195
|
+
|
|
196
|
+
this.parser.Restore();
|
|
219
197
|
|
|
220
198
|
}
|
|
221
199
|
|
|
@@ -231,7 +209,7 @@ export class DataModel {
|
|
|
231
209
|
|
|
232
210
|
const named: SerializedNamed = {
|
|
233
211
|
name: entry.name,
|
|
234
|
-
|
|
212
|
+
expression: '',
|
|
235
213
|
};
|
|
236
214
|
|
|
237
215
|
if (entry.scope) {
|
|
@@ -278,11 +256,10 @@ export class DataModel {
|
|
|
278
256
|
|
|
279
257
|
}
|
|
280
258
|
else {
|
|
281
|
-
|
|
259
|
+
|
|
260
|
+
const area = {
|
|
282
261
|
start: {
|
|
283
262
|
...entry.area.start,
|
|
284
|
-
sheet: this.sheets.Name(entry.area.start.sheet_id || 0) || '',
|
|
285
|
-
sheet_id: undefined, // in favor of name
|
|
286
263
|
absolute_column: true,
|
|
287
264
|
absolute_row: true,
|
|
288
265
|
},
|
|
@@ -292,6 +269,9 @@ export class DataModel {
|
|
|
292
269
|
absolute_row: true,
|
|
293
270
|
},
|
|
294
271
|
};
|
|
272
|
+
|
|
273
|
+
named.expression = this.AddressToLabel(area);
|
|
274
|
+
|
|
295
275
|
}
|
|
296
276
|
|
|
297
277
|
list.push(named);
|
|
@@ -570,6 +550,12 @@ export interface ViewModel {
|
|
|
570
550
|
view_index: number;
|
|
571
551
|
}
|
|
572
552
|
|
|
553
|
+
/**
|
|
554
|
+
* this type is no longer in use, but we retain it to parse old documents
|
|
555
|
+
* that use it.
|
|
556
|
+
*
|
|
557
|
+
* @deprecated
|
|
558
|
+
*/
|
|
573
559
|
export interface SerializedNamedExpression {
|
|
574
560
|
name: string;
|
|
575
561
|
expression: string;
|
|
@@ -30,7 +30,7 @@ export type {
|
|
|
30
30
|
SerializedMacroFunction,
|
|
31
31
|
} from './data_model';
|
|
32
32
|
|
|
33
|
-
export type { SerializedNamed
|
|
33
|
+
export type { SerializedNamed } from './named';
|
|
34
34
|
|
|
35
35
|
export { Sheet } from './sheet';
|
|
36
36
|
export type { SerializedSheet, FreezePane, LegacySerializedSheet } from './sheet_types';
|
|
@@ -20,15 +20,15 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { Area } from 'treb-base-types';
|
|
23
|
-
import type {
|
|
23
|
+
import type { IArea } from 'treb-base-types';
|
|
24
24
|
import type { ExpressionUnit } from 'treb-parser';
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
interface NamedExpression {
|
|
27
27
|
type: 'expression';
|
|
28
28
|
expression: ExpressionUnit;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
interface NamedRange {
|
|
32
32
|
type: 'range';
|
|
33
33
|
area: Area;
|
|
34
34
|
}
|
|
@@ -38,44 +38,23 @@ export type Named = (NamedExpression | NamedRange) & {
|
|
|
38
38
|
scope?: number; // scope to sheet by ID
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
/**
|
|
42
|
-
* serialized type
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* for the external type we switch on the presence of the area
|
|
47
|
-
* or the expression. area uses a type that includes sheet names
|
|
48
|
-
* (IArea should allow that?). expression here is a string.
|
|
41
|
+
/**
|
|
42
|
+
* serialized type is a composite of expression/range. we determine
|
|
43
|
+
* what it is when parsing the expression. this simplifies passing these
|
|
44
|
+
* things around.
|
|
49
45
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
46
|
+
* (named expressions and ranges they have slightly different behavior,
|
|
47
|
+
* which is why we have a distinction at all).
|
|
52
48
|
*
|
|
53
|
-
* when serialized, scope is either the sheet name or nothing
|
|
54
|
-
* (implicit global scope).
|
|
55
49
|
*/
|
|
56
50
|
export interface SerializedNamed {
|
|
57
|
-
name: string;
|
|
58
|
-
area?: SerializedArea;
|
|
59
|
-
expression?: string;
|
|
60
|
-
scope?: string;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* this is a type we're using in imports. it consolidates the
|
|
65
|
-
* two types. we should maybe switch as well, at least for
|
|
66
|
-
* serialized representation? something to think about.
|
|
67
|
-
*/
|
|
68
|
-
export interface CompositeNamed {
|
|
69
51
|
|
|
70
52
|
name: string;
|
|
71
53
|
|
|
72
|
-
/**
|
|
73
|
-
* could be a address/range or a function expression. we'll distinguish
|
|
74
|
-
* when we parse it.
|
|
75
|
-
*/
|
|
54
|
+
/** expression or address/area */
|
|
76
55
|
expression: string;
|
|
77
56
|
|
|
78
|
-
/**
|
|
57
|
+
/** scope is a sheet name (not ID) */
|
|
79
58
|
scope?: string;
|
|
80
59
|
|
|
81
60
|
}
|
|
@@ -3282,9 +3282,20 @@ export class Sheet {
|
|
|
3282
3282
|
// stop rule. if you go forwards, you need some sort of indicator
|
|
3283
3283
|
// or flag).
|
|
3284
3284
|
|
|
3285
|
+
const area = JSON.parse(JSON.stringify(format.area));
|
|
3286
|
+
|
|
3287
|
+
if (area.start.row === null || area.end.row === null) {
|
|
3288
|
+
area.start.row = 0;
|
|
3289
|
+
area.end.row = this.cells.rows - 1;
|
|
3290
|
+
}
|
|
3291
|
+
if (area.start.column === null || area.end.column === null) {
|
|
3292
|
+
area.start.column = 0;
|
|
3293
|
+
area.end.column = this.cells.columns - 1;
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3296
|
+
const result = format.internal?.vertex?.result;
|
|
3297
|
+
|
|
3285
3298
|
if (format.type === 'gradient') {
|
|
3286
|
-
const area = JSON.parse(JSON.stringify(format.area));
|
|
3287
|
-
const result = format.internal?.vertex?.result;
|
|
3288
3299
|
|
|
3289
3300
|
if (result && format.internal?.gradient) {
|
|
3290
3301
|
const property: 'fill'|'text' = format.property ?? 'fill';
|
|
@@ -3322,16 +3333,13 @@ export class Sheet {
|
|
|
3322
3333
|
|
|
3323
3334
|
// handle types expression, cell-match and duplicate-values
|
|
3324
3335
|
|
|
3325
|
-
const area = JSON.parse(JSON.stringify(format.area));
|
|
3326
|
-
const result = format.internal?.vertex?.result;
|
|
3327
|
-
|
|
3328
3336
|
if (result) {
|
|
3329
3337
|
|
|
3330
3338
|
if (result.type === ValueType.array) {
|
|
3331
3339
|
for (let row = area.start.row; row <= area.end.row; row++) {
|
|
3332
3340
|
for (let column = area.start.column; column <= area.end.column; column++) {
|
|
3333
3341
|
const value = result.value[column - area.start.column][row - area.start.row];
|
|
3334
|
-
if ((value.type === ValueType.boolean || value.type === ValueType.number) && !!value.value) {
|
|
3342
|
+
if (value && (value.type === ValueType.boolean || value.type === ValueType.number) && !!value.value) {
|
|
3335
3343
|
if (!temp[row]) { temp[row] = []; }
|
|
3336
3344
|
if (!temp[row][column] ) { temp[row][column] = []; }
|
|
3337
3345
|
temp[row][column].push(format.style);
|