@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
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import type { Cell, ICellAddress, ICellAddress2, UnionValue,
|
|
23
|
-
ArrayUnion, IArea, CellDataWithAddress} from 'treb-base-types';
|
|
22
|
+
import type { Cell, ICellAddress, ICellAddress2, UnionValue, EvaluateOptions,
|
|
23
|
+
ArrayUnion, IArea, CellDataWithAddress, CellValue} from 'treb-base-types';
|
|
24
24
|
import { Localization, Area, ValueType, IsCellAddress} from 'treb-base-types';
|
|
25
25
|
|
|
26
26
|
import type { ExpressionUnit, DependencyList, UnitRange, UnitAddress, UnitIdentifier } from 'treb-parser';
|
|
27
27
|
import { Parser,
|
|
28
|
-
DecimalMarkType, ArgumentSeparatorType } from 'treb-parser';
|
|
28
|
+
DecimalMarkType, ArgumentSeparatorType, QuotedSheetNameRegex } from 'treb-parser';
|
|
29
29
|
|
|
30
30
|
import { Graph } from './dag/graph';
|
|
31
31
|
import type { SpreadsheetVertex } from './dag/spreadsheet_vertex';
|
|
@@ -49,10 +49,12 @@ import { Variance } from './functions/statistics-functions';
|
|
|
49
49
|
|
|
50
50
|
import * as Primitives from './primitives';
|
|
51
51
|
|
|
52
|
-
import type { DataModel, Annotation, FunctionDescriptor, Sheet } from 'treb-grid';
|
|
53
|
-
import { LeafVertex } from './dag/
|
|
52
|
+
import type { DataModel, Annotation, FunctionDescriptor, Sheet, ConditionalFormat } from 'treb-grid';
|
|
53
|
+
import type { LeafVertex } from './dag/graph';
|
|
54
54
|
|
|
55
55
|
import { ArgumentError, ReferenceError, UnknownError, ValueError, ExpressionError, NAError, DivideByZeroError } from './function-error';
|
|
56
|
+
import { StateLeafVertex } from './dag/state_leaf_vertex';
|
|
57
|
+
import { CalculationLeafVertex } from './dag/calculation_leaf_vertex';
|
|
56
58
|
|
|
57
59
|
/**
|
|
58
60
|
* breaking this out so we can use it for export (TODO)
|
|
@@ -120,27 +122,6 @@ const TranslateSubtotalType = (type: string|number): number => {
|
|
|
120
122
|
|
|
121
123
|
};
|
|
122
124
|
|
|
123
|
-
/**
|
|
124
|
-
* options for the evaluate function
|
|
125
|
-
*/
|
|
126
|
-
export interface EvaluateOptions {
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* argument separator to use when parsing input. set this option to
|
|
130
|
-
* use a consistent argument separator independent of current locale.
|
|
131
|
-
*/
|
|
132
|
-
argument_separator?: ','|';';
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* allow R1C1-style references. the Evaluate function cannot use
|
|
136
|
-
* relative references (e.g. R[-1]C[0]), so those will always fail.
|
|
137
|
-
* however it may be useful to use direct R1C1 references (e.g. R3C4),
|
|
138
|
-
* so we optionally support that behind this flag.
|
|
139
|
-
*/
|
|
140
|
-
r1c1?: boolean;
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
125
|
/**
|
|
145
126
|
* we're providing a runtime option for how to handle complex numbers.
|
|
146
127
|
* we will need to pass that into the calculator when it's created to
|
|
@@ -180,7 +161,11 @@ export class Calculator extends Graph {
|
|
|
180
161
|
|
|
181
162
|
// FIXME: need a way to share/pass parser flags
|
|
182
163
|
|
|
183
|
-
public readonly parser: Parser = new Parser();
|
|
164
|
+
// public readonly parser: Parser = new Parser();
|
|
165
|
+
/** localized parser instance. we're sharing. */
|
|
166
|
+
protected get parser(): Parser {
|
|
167
|
+
return this.model.parser;
|
|
168
|
+
}
|
|
184
169
|
|
|
185
170
|
protected readonly library = new FunctionLibrary();
|
|
186
171
|
|
|
@@ -192,10 +177,14 @@ export class Calculator extends Graph {
|
|
|
192
177
|
// protected graph: Graph = new Graph(); // |null = null;
|
|
193
178
|
// protected status: GraphStatus = GraphStatus.OK;
|
|
194
179
|
|
|
180
|
+
/*
|
|
195
181
|
// FIXME: why is this a separate class? [actually is this a composition issue?]
|
|
196
182
|
protected expression_calculator = new ExpressionCalculator(
|
|
197
183
|
this.library,
|
|
198
184
|
this.parser);
|
|
185
|
+
*/
|
|
186
|
+
|
|
187
|
+
protected expression_calculator: ExpressionCalculator;
|
|
199
188
|
|
|
200
189
|
/** the next calculation must do a full rebuild -- set on reset */
|
|
201
190
|
protected full_rebuild_required = false;
|
|
@@ -204,6 +193,9 @@ export class Calculator extends Graph {
|
|
|
204
193
|
|
|
205
194
|
super();
|
|
206
195
|
|
|
196
|
+
this.expression_calculator = new ExpressionCalculator(this.library, this.parser);
|
|
197
|
+
|
|
198
|
+
|
|
207
199
|
// at the moment options are only used here; in the future
|
|
208
200
|
// we may need to extend handling.
|
|
209
201
|
|
|
@@ -1298,6 +1290,7 @@ export class Calculator extends Graph {
|
|
|
1298
1290
|
if (this.full_rebuild_required) {
|
|
1299
1291
|
subset = undefined;
|
|
1300
1292
|
this.UpdateAnnotations();
|
|
1293
|
+
this.UpdateConditionals();
|
|
1301
1294
|
// this.UpdateNotifiers();
|
|
1302
1295
|
this.full_rebuild_required = false; // unset
|
|
1303
1296
|
}
|
|
@@ -1380,67 +1373,11 @@ export class Calculator extends Graph {
|
|
|
1380
1373
|
return map;
|
|
1381
1374
|
}
|
|
1382
1375
|
|
|
1383
|
-
|
|
1384
|
-
public
|
|
1385
|
-
const resolved = this.ResolveAddress(address, active_sheet);
|
|
1386
|
-
return IsCellAddress(resolved) ? new Area(resolved) : new Area(resolved.start, resolved.end);
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
/**
|
|
1390
|
-
* moved from embedded sheet. also modified to preserve ranges, so it
|
|
1391
|
-
* might return a range (area). if you are expecting the old behavior
|
|
1392
|
-
* you need to check (perhaps we could have a wrapper, or make it optional?)
|
|
1393
|
-
*
|
|
1394
|
-
* Q: why does this not go in grid? or model? (...)
|
|
1395
|
-
* Q: why are we not preserving absoute/relative? (...)
|
|
1396
|
-
*
|
|
1397
|
-
*/
|
|
1398
|
-
public ResolveAddress(address: string|ICellAddress|IArea, active_sheet: Sheet): ICellAddress|IArea {
|
|
1399
|
-
|
|
1400
|
-
if (typeof address === 'string') {
|
|
1401
|
-
const parse_result = this.parser.Parse(address);
|
|
1402
|
-
if (parse_result.expression && parse_result.expression.type === 'address') {
|
|
1403
|
-
this.ResolveSheetID(parse_result.expression, undefined, active_sheet);
|
|
1404
|
-
return {
|
|
1405
|
-
row: parse_result.expression.row,
|
|
1406
|
-
column: parse_result.expression.column,
|
|
1407
|
-
sheet_id: parse_result.expression.sheet_id,
|
|
1408
|
-
};
|
|
1409
|
-
}
|
|
1410
|
-
else if (parse_result.expression && parse_result.expression.type === 'range') {
|
|
1411
|
-
this.ResolveSheetID(parse_result.expression, undefined, active_sheet);
|
|
1412
|
-
return {
|
|
1413
|
-
start: {
|
|
1414
|
-
row: parse_result.expression.start.row,
|
|
1415
|
-
column: parse_result.expression.start.column,
|
|
1416
|
-
sheet_id: parse_result.expression.start.sheet_id,
|
|
1417
|
-
},
|
|
1418
|
-
end: {
|
|
1419
|
-
row: parse_result.expression.end.row,
|
|
1420
|
-
column: parse_result.expression.end.column,
|
|
1421
|
-
}
|
|
1422
|
-
};
|
|
1423
|
-
}
|
|
1424
|
-
else if (parse_result.expression && parse_result.expression.type === 'identifier') {
|
|
1425
|
-
|
|
1426
|
-
// is named range guaranteed to have a sheet ID? (I think yes...)
|
|
1427
|
-
|
|
1428
|
-
const named_range = this.model.named_ranges.Get(parse_result.expression.name);
|
|
1429
|
-
if (named_range) {
|
|
1430
|
-
return named_range;
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
return { row: 0, column: 0 }; // default for string types -- broken
|
|
1435
|
-
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
return address; // already range or address
|
|
1439
|
-
|
|
1440
|
-
}
|
|
1376
|
+
public Evaluate(expression: string, active_sheet?: Sheet, options?: EvaluateOptions, raw_result?: false): CellValue|CellValue[][];
|
|
1377
|
+
public Evaluate(expression: string, active_sheet?: Sheet, options?: EvaluateOptions, raw_result?: true): UnionValue;
|
|
1441
1378
|
|
|
1442
1379
|
/** moved from embedded sheet */
|
|
1443
|
-
public Evaluate(expression: string, active_sheet?: Sheet, options: EvaluateOptions = {}) {
|
|
1380
|
+
public Evaluate(expression: string, active_sheet?: Sheet, options: EvaluateOptions = {}, raw_result = false) {
|
|
1444
1381
|
|
|
1445
1382
|
const current = this.parser.argument_separator;
|
|
1446
1383
|
const r1c1_state = this.parser.flags.r1c1;
|
|
@@ -1487,13 +1424,16 @@ export class Calculator extends Graph {
|
|
|
1487
1424
|
}
|
|
1488
1425
|
}
|
|
1489
1426
|
|
|
1490
|
-
this.ResolveSheetID(unit, undefined, active_sheet);
|
|
1427
|
+
this.model.ResolveSheetID(unit, undefined, active_sheet);
|
|
1491
1428
|
}
|
|
1492
1429
|
return true;
|
|
1493
1430
|
});
|
|
1494
1431
|
|
|
1495
1432
|
// console.info({expression: parse_result.expression})
|
|
1496
1433
|
const result = this.CalculateExpression(parse_result.expression);
|
|
1434
|
+
if (raw_result) {
|
|
1435
|
+
return result;
|
|
1436
|
+
}
|
|
1497
1437
|
|
|
1498
1438
|
if (result.type === ValueType.array) {
|
|
1499
1439
|
return result.value.map(row => row.map(value => value.value));
|
|
@@ -1547,6 +1487,7 @@ export class Calculator extends Graph {
|
|
|
1547
1487
|
// add leaf vertices for annotations
|
|
1548
1488
|
|
|
1549
1489
|
this.UpdateAnnotations(); // all
|
|
1490
|
+
this.UpdateConditionals();
|
|
1550
1491
|
|
|
1551
1492
|
// and notifiers
|
|
1552
1493
|
|
|
@@ -1769,6 +1710,180 @@ export class Calculator extends Graph {
|
|
|
1769
1710
|
}
|
|
1770
1711
|
*/
|
|
1771
1712
|
|
|
1713
|
+
public Unresolve(ref: IArea|ICellAddress, context: Sheet, qualified = true, named = true) {
|
|
1714
|
+
|
|
1715
|
+
let range = '';
|
|
1716
|
+
const area = IsCellAddress(ref) ? new Area(ref) : new Area(ref.start, ref.end);
|
|
1717
|
+
|
|
1718
|
+
if (named) {
|
|
1719
|
+
const named_range = this.model.named_ranges.MatchSelection(area);
|
|
1720
|
+
if (named_range) {
|
|
1721
|
+
return named_range;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
if (area.count > 1) {
|
|
1726
|
+
range = Area.CellAddressToLabel(area.start) + ':' + Area.CellAddressToLabel(area.end);
|
|
1727
|
+
}
|
|
1728
|
+
else {
|
|
1729
|
+
range = Area.CellAddressToLabel(area.start);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
if (!qualified) {
|
|
1733
|
+
return range;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// is there a function to resolve sheet? actually, don't we know that
|
|
1737
|
+
// the active selection must be on the active sheet? (...)
|
|
1738
|
+
|
|
1739
|
+
const sheet_id = area.start.sheet_id || context?.id;
|
|
1740
|
+
const sheet_name = this.ResolveSheetName(sheet_id, true);
|
|
1741
|
+
|
|
1742
|
+
return sheet_name ? sheet_name + '!' + range : range;
|
|
1743
|
+
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
/**
|
|
1747
|
+
*
|
|
1748
|
+
*/
|
|
1749
|
+
public ResolveSheetName(id: number, quote = false): string | undefined {
|
|
1750
|
+
const sheet = this.model.sheets.Find(id);
|
|
1751
|
+
if (sheet) {
|
|
1752
|
+
if (quote && QuotedSheetNameRegex.test(sheet.name)) {
|
|
1753
|
+
return `'${sheet.name}'`;
|
|
1754
|
+
}
|
|
1755
|
+
return sheet.name;
|
|
1756
|
+
}
|
|
1757
|
+
return undefined;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
public RemoveConditional(conditional: ConditionalFormat): void {
|
|
1761
|
+
if (conditional.type === 'expression') {
|
|
1762
|
+
const vertex = conditional.internal?.vertex as LeafVertex;
|
|
1763
|
+
if (vertex) {
|
|
1764
|
+
vertex.Reset();
|
|
1765
|
+
this.RemoveLeafVertex(vertex);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
public UpdateConditionals(list?: ConditionalFormat|ConditionalFormat[], context?: Sheet): void {
|
|
1771
|
+
|
|
1772
|
+
if (!list) {
|
|
1773
|
+
for (const sheet of this.model.sheets.list) {
|
|
1774
|
+
if (sheet.conditional_formats?.length) {
|
|
1775
|
+
this.UpdateConditionals(sheet.conditional_formats, sheet);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
if (!context) {
|
|
1782
|
+
throw new Error('invalid call to update conditionals without context');
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
if (list && !Array.isArray(list)) {
|
|
1786
|
+
list = [list];
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
for (const entry of list) {
|
|
1790
|
+
|
|
1791
|
+
let expression = '';
|
|
1792
|
+
|
|
1793
|
+
switch (entry.type) {
|
|
1794
|
+
case 'cell-match':
|
|
1795
|
+
expression = this.Unresolve(entry.area, context, true, false) + ' ' + entry.expression;
|
|
1796
|
+
break;
|
|
1797
|
+
|
|
1798
|
+
case 'expression':
|
|
1799
|
+
expression = entry.expression;
|
|
1800
|
+
break;
|
|
1801
|
+
|
|
1802
|
+
case 'duplicate-values':
|
|
1803
|
+
expression = `UniqueValues(${
|
|
1804
|
+
this.Unresolve(entry.area, context, true, false)
|
|
1805
|
+
})`;
|
|
1806
|
+
if (!entry.unique) {
|
|
1807
|
+
expression = `NOT(${expression})`;
|
|
1808
|
+
}
|
|
1809
|
+
break;
|
|
1810
|
+
|
|
1811
|
+
case 'gradient':
|
|
1812
|
+
expression = `=Gradient(${
|
|
1813
|
+
[
|
|
1814
|
+
this.Unresolve(entry.area, context, true, false),
|
|
1815
|
+
entry.min ?? '',
|
|
1816
|
+
entry.max ?? '',
|
|
1817
|
+
|
|
1818
|
+
].join(this.parser.argument_separator)
|
|
1819
|
+
})`;
|
|
1820
|
+
break;
|
|
1821
|
+
|
|
1822
|
+
default:
|
|
1823
|
+
continue;
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
if (!expression) {
|
|
1827
|
+
continue; // FIXME: warn?
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
// console.info({type: entry.type, expression});
|
|
1831
|
+
|
|
1832
|
+
if (!entry.internal) {
|
|
1833
|
+
entry.internal = {};
|
|
1834
|
+
}
|
|
1835
|
+
if (!entry.internal.vertex) {
|
|
1836
|
+
entry.internal.vertex = new CalculationLeafVertex();
|
|
1837
|
+
|
|
1838
|
+
let options: EvaluateOptions|undefined;
|
|
1839
|
+
if (entry.type !== 'gradient' && entry.type !== 'duplicate-values') {
|
|
1840
|
+
options = entry.options;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
// first pass, run the calculation
|
|
1844
|
+
const check = this.Evaluate(expression, context, options, true);
|
|
1845
|
+
entry.internal.vertex.result = check;
|
|
1846
|
+
entry.internal.vertex.updated = true;
|
|
1847
|
+
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
const vertex = entry.internal.vertex as LeafVertex;
|
|
1851
|
+
this.AddLeafVertex(vertex);
|
|
1852
|
+
this.UpdateLeafVertex(vertex, expression, context);
|
|
1853
|
+
|
|
1854
|
+
/*
|
|
1855
|
+
if (entry.type === 'cell-match') {
|
|
1856
|
+
if (!entry.internal) {
|
|
1857
|
+
entry.internal = {};
|
|
1858
|
+
}
|
|
1859
|
+
if (!entry.internal.vertex) {
|
|
1860
|
+
entry.internal.vertex = new CalculationLeafVertex();
|
|
1861
|
+
}
|
|
1862
|
+
const vertex = entry.internal.vertex as LeafVertex;
|
|
1863
|
+
this.AddLeafVertex(vertex);
|
|
1864
|
+
this.UpdateLeafVertex(vertex, entry.expression, context);
|
|
1865
|
+
}
|
|
1866
|
+
else if (entry.type === 'expression') {
|
|
1867
|
+
if (!entry.internal) {
|
|
1868
|
+
entry.internal = {};
|
|
1869
|
+
}
|
|
1870
|
+
if (!entry.internal.vertex) {
|
|
1871
|
+
entry.internal.vertex = new CalculationLeafVertex();
|
|
1872
|
+
|
|
1873
|
+
// set initial state based on current state
|
|
1874
|
+
entry.internal.vertex.result = { type: ValueType.boolean, value: !!entry.applied };
|
|
1875
|
+
|
|
1876
|
+
}
|
|
1877
|
+
const vertex = entry.internal.vertex as LeafVertex;
|
|
1878
|
+
this.AddLeafVertex(vertex);
|
|
1879
|
+
this.UpdateLeafVertex(vertex, entry.expression, context);
|
|
1880
|
+
}
|
|
1881
|
+
*/
|
|
1882
|
+
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1772
1887
|
public RemoveAnnotation(annotation: Annotation): void {
|
|
1773
1888
|
const vertex = (annotation.temp.vertex as LeafVertex);
|
|
1774
1889
|
if (!vertex) { return; }
|
|
@@ -1803,7 +1918,7 @@ export class Calculator extends Graph {
|
|
|
1803
1918
|
for (const entry of list) {
|
|
1804
1919
|
if (entry.data.formula) {
|
|
1805
1920
|
if (!entry.temp.vertex) {
|
|
1806
|
-
entry.temp.vertex = new
|
|
1921
|
+
entry.temp.vertex = new StateLeafVertex();
|
|
1807
1922
|
}
|
|
1808
1923
|
const vertex = entry.temp.vertex as LeafVertex;
|
|
1809
1924
|
this.AddLeafVertex(vertex);
|
|
@@ -1813,48 +1928,6 @@ export class Calculator extends Graph {
|
|
|
1813
1928
|
|
|
1814
1929
|
}
|
|
1815
1930
|
|
|
1816
|
-
/**
|
|
1817
|
-
* returns false if the sheet cannot be resolved, which probably
|
|
1818
|
-
* means the name changed (that's the case we are working on with
|
|
1819
|
-
* this fix).
|
|
1820
|
-
*/
|
|
1821
|
-
public ResolveSheetID(expr: UnitAddress|UnitRange, context?: ICellAddress, active_sheet?: Sheet): boolean {
|
|
1822
|
-
|
|
1823
|
-
const target = expr.type === 'address' ? expr : expr.start;
|
|
1824
|
-
|
|
1825
|
-
if (target.sheet_id) {
|
|
1826
|
-
return true;
|
|
1827
|
-
}
|
|
1828
|
-
|
|
1829
|
-
if (target.sheet) {
|
|
1830
|
-
const sheet = this.model.sheets.Find(target.sheet);
|
|
1831
|
-
if (sheet) {
|
|
1832
|
-
target.sheet_id = sheet.id;
|
|
1833
|
-
return true;
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
/*
|
|
1837
|
-
const lc = target.sheet.toLowerCase();
|
|
1838
|
-
for (const sheet of this.model.sheets.list) {
|
|
1839
|
-
if (sheet.name.toLowerCase() === lc) {
|
|
1840
|
-
target.sheet_id = sheet.id;
|
|
1841
|
-
return true;
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1844
|
-
*/
|
|
1845
|
-
}
|
|
1846
|
-
else if (context?.sheet_id) {
|
|
1847
|
-
target.sheet_id = context.sheet_id;
|
|
1848
|
-
return true;
|
|
1849
|
-
}
|
|
1850
|
-
else if (active_sheet?.id) {
|
|
1851
|
-
target.sheet_id = active_sheet.id;
|
|
1852
|
-
return true;
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
|
-
return false; // the error
|
|
1856
|
-
|
|
1857
|
-
}
|
|
1858
1931
|
|
|
1859
1932
|
// --- protected -------------------------------------------------------------
|
|
1860
1933
|
|
|
@@ -1866,13 +1939,13 @@ export class Calculator extends Graph {
|
|
|
1866
1939
|
|
|
1867
1940
|
switch (expr.type) {
|
|
1868
1941
|
case 'address':
|
|
1869
|
-
if (this.ResolveSheetID(expr, context)) {
|
|
1942
|
+
if (this.model.ResolveSheetID(expr, context)) {
|
|
1870
1943
|
return new Area(expr);
|
|
1871
1944
|
}
|
|
1872
1945
|
break;
|
|
1873
1946
|
|
|
1874
1947
|
case 'range':
|
|
1875
|
-
if (this.ResolveSheetID(expr, context)) {
|
|
1948
|
+
if (this.model.ResolveSheetID(expr, context)) {
|
|
1876
1949
|
return new Area(expr.start, expr.end);
|
|
1877
1950
|
}
|
|
1878
1951
|
break;
|
|
@@ -0,0 +1,97 @@
|
|
|
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-2023 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { GraphCallbacks } from './spreadsheet_vertex_base';
|
|
23
|
+
import { SpreadsheetVertex } from './spreadsheet_vertex';
|
|
24
|
+
import type { Vertex} from './vertex';
|
|
25
|
+
import { Color } from './vertex';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* adding a new leaf vertex type that actually does a calculation;
|
|
29
|
+
* but it has no spreadsheet context (address) and by definition it
|
|
30
|
+
* has no dependendents.
|
|
31
|
+
*
|
|
32
|
+
* this is intended for managing conditional formats, if they have
|
|
33
|
+
* an expression. we only want to calculate these when necessary
|
|
34
|
+
* (i.e. dependencies have updated, or they are volatile).
|
|
35
|
+
*
|
|
36
|
+
*/
|
|
37
|
+
export class CalculationLeafVertex extends SpreadsheetVertex {
|
|
38
|
+
|
|
39
|
+
public static type = 'calculation-leaf-vertex';
|
|
40
|
+
|
|
41
|
+
public type = CalculationLeafVertex.type; // for type guard
|
|
42
|
+
|
|
43
|
+
public address = { row: -1, column: -1 }; // fake address
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* flag, to reduce unecessary application. work in progress. this
|
|
47
|
+
* indicates that we reached the calculation step. that means either
|
|
48
|
+
* (1) dependencies changed, or (2) we were marked dirty in some global
|
|
49
|
+
* operation, probably a full-recalc.
|
|
50
|
+
*
|
|
51
|
+
* (2) is a waste but we're still going to save some cycles here. if you
|
|
52
|
+
* want you could add a state check like the other leaf vertex.
|
|
53
|
+
*/
|
|
54
|
+
public updated = false;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* leaf vertex defaults to black (i.e. tested) because leaf nodes cannot have
|
|
58
|
+
* outbound edges. it is still possible to change this, because it's a property
|
|
59
|
+
* and we can't override the set accessor, but making it an accessor in the
|
|
60
|
+
* superclass just for this purpose is not worthwhile since regular vertices
|
|
61
|
+
* should vastly outnumber leaves.
|
|
62
|
+
*/
|
|
63
|
+
public color = Color.black;
|
|
64
|
+
|
|
65
|
+
/** overrides calculate function */
|
|
66
|
+
public Calculate(graph: GraphCallbacks): void {
|
|
67
|
+
|
|
68
|
+
// if we are not dirty, nothing to do
|
|
69
|
+
if (!this.dirty) return;
|
|
70
|
+
|
|
71
|
+
// check deps
|
|
72
|
+
for (const edge of this.edges_in) {
|
|
73
|
+
if ((edge as SpreadsheetVertex).dirty) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ...
|
|
79
|
+
|
|
80
|
+
const result = graph.CalculationCallback.call(graph, this);
|
|
81
|
+
|
|
82
|
+
this.result = result.value;
|
|
83
|
+
this.dirty = false;
|
|
84
|
+
|
|
85
|
+
// set flag
|
|
86
|
+
|
|
87
|
+
this.updated = true;
|
|
88
|
+
|
|
89
|
+
// we are not allowed to have edges out, so nothing to do
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public AddDependent(edge: Vertex): void {
|
|
94
|
+
throw(new Error('leaf vertex cannot have dependents'));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
}
|
|
@@ -24,10 +24,13 @@ import { Color } from './vertex';
|
|
|
24
24
|
import { SpreadsheetVertex } from './spreadsheet_vertex';
|
|
25
25
|
import { ArrayVertex } from './array-vertex';
|
|
26
26
|
import type { SpreadsheetVertexBase, CalculationResult, GraphCallbacks } from './spreadsheet_vertex_base';
|
|
27
|
-
import type {
|
|
27
|
+
import type { StateLeafVertex } from './state_leaf_vertex';
|
|
28
28
|
import type { ICellAddress, ICellAddress2, IArea, UnionValue } from 'treb-base-types';
|
|
29
29
|
import { Area } from 'treb-base-types';
|
|
30
30
|
import type { DataModel } from 'treb-grid';
|
|
31
|
+
import { CalculationLeafVertex } from './calculation_leaf_vertex';
|
|
32
|
+
|
|
33
|
+
export type LeafVertex = StateLeafVertex|CalculationLeafVertex;
|
|
31
34
|
|
|
32
35
|
// FIXME: this is a bad habit if you're testing on falsy for OK.
|
|
33
36
|
|
|
@@ -61,7 +64,8 @@ export abstract class Graph implements GraphCallbacks {
|
|
|
61
64
|
public loop_hint?: string;
|
|
62
65
|
|
|
63
66
|
// special
|
|
64
|
-
public leaf_vertices: LeafVertex[] = [];
|
|
67
|
+
// public leaf_vertices: LeafVertex[] = [];
|
|
68
|
+
public leaf_vertices: Set<LeafVertex> = new Set();
|
|
65
69
|
|
|
66
70
|
/** lock down access */
|
|
67
71
|
private dirty_list: SpreadsheetVertexBase[] = [];
|
|
@@ -102,7 +106,7 @@ export abstract class Graph implements GraphCallbacks {
|
|
|
102
106
|
this.dirty_list = [];
|
|
103
107
|
this.volatile_list = [];
|
|
104
108
|
this.vertices = [[]];
|
|
105
|
-
this.leaf_vertices
|
|
109
|
+
this.leaf_vertices.clear();
|
|
106
110
|
// this.cells_map = {};
|
|
107
111
|
|
|
108
112
|
/** array vertex maintains its own list */
|
|
@@ -812,28 +816,12 @@ export abstract class Graph implements GraphCallbacks {
|
|
|
812
816
|
* managing and maintaining these vertices: we only need references.
|
|
813
817
|
*/
|
|
814
818
|
public AddLeafVertex(vertex: LeafVertex): void {
|
|
815
|
-
|
|
816
|
-
// ... don't add more than once. this is expensive but
|
|
817
|
-
// the list should (generally speaking) be short, so not
|
|
818
|
-
// a serious problem atm
|
|
819
|
-
|
|
820
|
-
/*
|
|
821
|
-
if (this.leaf_vertices.some((test) => test === vertex)) {
|
|
822
|
-
return;
|
|
823
|
-
}
|
|
824
|
-
*/
|
|
825
|
-
for (const test of this.leaf_vertices) {
|
|
826
|
-
if (test === vertex) {
|
|
827
|
-
return;
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
this.leaf_vertices.push(vertex);
|
|
819
|
+
this.leaf_vertices.add(vertex);
|
|
832
820
|
}
|
|
833
821
|
|
|
834
|
-
/** removes vertex
|
|
822
|
+
/** removes vertex */
|
|
835
823
|
public RemoveLeafVertex(vertex: LeafVertex): void {
|
|
836
|
-
this.leaf_vertices
|
|
824
|
+
this.leaf_vertices.delete(vertex);
|
|
837
825
|
}
|
|
838
826
|
|
|
839
827
|
/**
|
|
@@ -40,12 +40,12 @@ import { Color } from './vertex';
|
|
|
40
40
|
* have both leaf- and spreadsheet-vertex extend that.
|
|
41
41
|
*
|
|
42
42
|
*/
|
|
43
|
-
export class
|
|
43
|
+
export class StateLeafVertex extends SpreadsheetVertex {
|
|
44
44
|
|
|
45
|
-
public static type = 'leaf-vertex';
|
|
45
|
+
public static type = 'state-leaf-vertex';
|
|
46
46
|
|
|
47
47
|
public state_id = 0;
|
|
48
|
-
public type =
|
|
48
|
+
public type = StateLeafVertex.type; // for type guard
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* leaf vertex defaults to black (i.e. tested) because leaf nodes cannot have
|
|
@@ -112,10 +112,17 @@ export interface CompositeFunctionDescriptor {
|
|
|
112
112
|
fn: (...args: any[]) => UnionValue; // UnionOrArray; // |UnitAddress|UnitRange;
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
115
|
+
* limited visibility
|
|
116
|
+
*
|
|
117
|
+
* internal functions do not show up in the spreadsheet. we have an
|
|
118
|
+
* annotation value which should be usef in the future but it's not
|
|
119
|
+
* implemented yet.
|
|
120
|
+
*
|
|
121
|
+
* should we allow these functions to be used, and just not tooltip them;
|
|
122
|
+
* or block them entirely? for now we'll do the former as it's helpful to
|
|
123
|
+
* defug.
|
|
117
124
|
*/
|
|
118
|
-
visibility?:
|
|
125
|
+
visibility?: 'internal'|'annotation';
|
|
119
126
|
|
|
120
127
|
/**
|
|
121
128
|
* for the future
|
|
@@ -821,7 +821,7 @@ export class ExpressionCalculator {
|
|
|
821
821
|
const value: UnionValue[][] = [];
|
|
822
822
|
|
|
823
823
|
for (let c = 0; c < columns; c++) {
|
|
824
|
-
const col = [];
|
|
824
|
+
const col: UnionValue[] = [];
|
|
825
825
|
for (let r = 0; r < rows; r++ ) {
|
|
826
826
|
col[r] = fn(left_values[c][r], right_values[c][r]);
|
|
827
827
|
}
|