@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
|
@@ -31,34 +31,48 @@ import type {
|
|
|
31
31
|
GridSelection, CellEvent, FunctionDescriptor,
|
|
32
32
|
AnnotationViewData,
|
|
33
33
|
AnnotationType,
|
|
34
|
-
|
|
34
|
+
ExternalEditorConfig,
|
|
35
|
+
ConditionalFormatDuplicateValuesOptions,
|
|
36
|
+
ConditionalFormatDuplicateValues,
|
|
37
|
+
ConditionalFormatGradientOptions,
|
|
38
|
+
ConditionalFormat,
|
|
39
|
+
ConditionalFormatGradient,
|
|
40
|
+
ConditionalFormatExpression,
|
|
41
|
+
StandardGradient,
|
|
42
|
+
CondifionalFormatExpressionOptions,
|
|
43
|
+
ConditionalFormatCellMatchOptions,
|
|
44
|
+
ConditionalFormatCellMatch,
|
|
35
45
|
} from 'treb-grid';
|
|
36
46
|
|
|
37
47
|
import {
|
|
38
|
-
DataModel, Grid, BorderConstants, Sheet, ErrorCode, UA
|
|
48
|
+
DataModel, Grid, BorderConstants, Sheet, ErrorCode, UA,
|
|
49
|
+
StandardGradientsList,
|
|
39
50
|
} from 'treb-grid';
|
|
40
51
|
|
|
41
52
|
import {
|
|
42
53
|
Parser, DecimalMarkType,
|
|
43
54
|
ArgumentSeparatorType, QuotedSheetNameRegex } from 'treb-parser';
|
|
44
55
|
|
|
45
|
-
import { Calculator, type
|
|
56
|
+
import { Calculator, type LeafVertex } from 'treb-calculator';
|
|
46
57
|
|
|
47
58
|
import type {
|
|
48
59
|
ICellAddress,
|
|
60
|
+
EvaluateOptions,
|
|
49
61
|
IArea, CellValue, Point,
|
|
50
62
|
Complex, ExtendedUnion, IRectangle,
|
|
51
|
-
AddressReference, RangeReference, TableSortOptions, Table, TableTheme,
|
|
63
|
+
AddressReference, RangeReference, TableSortOptions, Table, TableTheme, GradientStop,
|
|
52
64
|
} from 'treb-base-types';
|
|
53
65
|
|
|
54
66
|
import {
|
|
55
67
|
IsArea, ThemeColorTable, ComplexToString, Rectangle, IsComplex, type CellStyle,
|
|
56
|
-
Localization, Style, type Color, ThemeColor2, IsCellAddress, Area, IsFlatData, IsFlatDataArray,
|
|
68
|
+
Localization, Style, type Color, ThemeColor2, IsCellAddress, Area, IsFlatData, IsFlatDataArray, Gradient, ValueType,
|
|
57
69
|
} from 'treb-base-types';
|
|
58
70
|
|
|
59
71
|
import { EventSource, Yield, ValidateURI } from 'treb-utils';
|
|
60
72
|
import { NumberFormatCache, ValueParser, NumberFormat } from 'treb-format';
|
|
61
73
|
|
|
74
|
+
|
|
75
|
+
|
|
62
76
|
// --- local -------------------------------------------------------------------
|
|
63
77
|
|
|
64
78
|
import { Dialog, DialogType } from './progress-dialog';
|
|
@@ -81,6 +95,7 @@ import type { SetRangeOptions } from 'treb-grid';
|
|
|
81
95
|
* the script so we can run it as a worker.
|
|
82
96
|
*/
|
|
83
97
|
import * as export_worker_script from 'worker:../../treb-export/src/export-worker/index.worker';
|
|
98
|
+
import { StateLeafVertex } from 'treb-calculator/src/dag/state_leaf_vertex';
|
|
84
99
|
|
|
85
100
|
// --- types -------------------------------------------------------------------
|
|
86
101
|
|
|
@@ -355,7 +370,7 @@ export class EmbeddedSpreadsheet {
|
|
|
355
370
|
|
|
356
371
|
/** localized parser instance. we're sharing. */
|
|
357
372
|
protected get parser(): Parser {
|
|
358
|
-
return this.
|
|
373
|
+
return this.model.parser;
|
|
359
374
|
}
|
|
360
375
|
|
|
361
376
|
/** for destruction */
|
|
@@ -659,7 +674,7 @@ export class EmbeddedSpreadsheet {
|
|
|
659
674
|
// if we don't have a container. that's distinct (at the moment)
|
|
660
675
|
// from headless, which is a state that can change.
|
|
661
676
|
|
|
662
|
-
this.grid = new Grid(grid_options, this.
|
|
677
|
+
this.grid = new Grid(grid_options, this.model, undefined, !!container);
|
|
663
678
|
|
|
664
679
|
if (this.options.headless) {
|
|
665
680
|
this.grid.headless = true; // FIXME: move into grid options
|
|
@@ -1177,8 +1192,8 @@ export class EmbeddedSpreadsheet {
|
|
|
1177
1192
|
* to support outside tooling by highlighting a list of arguments and
|
|
1178
1193
|
* responding to selection.
|
|
1179
1194
|
*/
|
|
1180
|
-
public ExternalEditor(
|
|
1181
|
-
this.grid.ExternalEditor(
|
|
1195
|
+
public ExternalEditor(config?: Partial<ExternalEditorConfig>) {
|
|
1196
|
+
this.grid.ExternalEditor(config);
|
|
1182
1197
|
}
|
|
1183
1198
|
|
|
1184
1199
|
/**
|
|
@@ -1251,6 +1266,265 @@ export class EmbeddedSpreadsheet {
|
|
|
1251
1266
|
|
|
1252
1267
|
}
|
|
1253
1268
|
|
|
1269
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
1270
|
+
//
|
|
1271
|
+
// conditional formatting API (WIP)
|
|
1272
|
+
//
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* list conditional formats. uses the active sheet by default, or pass a
|
|
1276
|
+
* sheet name or id.
|
|
1277
|
+
*
|
|
1278
|
+
* @internal
|
|
1279
|
+
*/
|
|
1280
|
+
public ListConditionalFormats(sheet?: number|string) {
|
|
1281
|
+
|
|
1282
|
+
const target = (typeof sheet === 'undefined') ?
|
|
1283
|
+
this.grid.active_sheet :
|
|
1284
|
+
this.model.sheets.Find(sheet);
|
|
1285
|
+
|
|
1286
|
+
return target?.conditional_formats || [];
|
|
1287
|
+
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
/** @internal */
|
|
1291
|
+
public ConditionalFormatDuplicateValues(range: RangeReference|undefined, options: ConditionalFormatDuplicateValuesOptions): ConditionalFormat {
|
|
1292
|
+
|
|
1293
|
+
if (range === undefined) {
|
|
1294
|
+
const ref = this.GetSelectionReference();
|
|
1295
|
+
if (ref.empty) {
|
|
1296
|
+
throw new Error('invalid range (no selection)');
|
|
1297
|
+
}
|
|
1298
|
+
range = ref.area;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
return this.AddConditionalFormat({
|
|
1302
|
+
type: 'duplicate-values',
|
|
1303
|
+
area: this.model.ResolveArea(range, this.grid.active_sheet),
|
|
1304
|
+
...options,
|
|
1305
|
+
});
|
|
1306
|
+
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
/**
|
|
1310
|
+
* @internal
|
|
1311
|
+
*/
|
|
1312
|
+
public ConditionalFormatGradient(range: RangeReference|undefined, options: ConditionalFormatGradientOptions|StandardGradient): ConditionalFormat {
|
|
1313
|
+
|
|
1314
|
+
if (range === undefined) {
|
|
1315
|
+
const ref = this.GetSelectionReference();
|
|
1316
|
+
if (ref.empty) {
|
|
1317
|
+
throw new Error('invalid range (no selection)');
|
|
1318
|
+
}
|
|
1319
|
+
range = ref.area;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1323
|
+
|
|
1324
|
+
const format: ConditionalFormatGradient = (typeof options === 'object') ?
|
|
1325
|
+
{
|
|
1326
|
+
type: 'gradient', area, ...options,
|
|
1327
|
+
} :
|
|
1328
|
+
{
|
|
1329
|
+
type: 'gradient', area, ...StandardGradientsList[options]
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
this.AddConditionalFormat(format);
|
|
1333
|
+
return format;
|
|
1334
|
+
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
/** @internal */
|
|
1338
|
+
public ConditionalFormatCellMatch(range: RangeReference|undefined, options: ConditionalFormatCellMatchOptions): ConditionalFormat {
|
|
1339
|
+
|
|
1340
|
+
if (range === undefined) {
|
|
1341
|
+
const ref = this.GetSelectionReference();
|
|
1342
|
+
if (ref.empty) {
|
|
1343
|
+
throw new Error('invalid range (no selection)');
|
|
1344
|
+
}
|
|
1345
|
+
range = ref.area;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1349
|
+
|
|
1350
|
+
const format: ConditionalFormatCellMatch = {
|
|
1351
|
+
type: 'cell-match',
|
|
1352
|
+
area,
|
|
1353
|
+
...options,
|
|
1354
|
+
};
|
|
1355
|
+
|
|
1356
|
+
// we need to calculate the formula once, to get an initial state
|
|
1357
|
+
// update: internal
|
|
1358
|
+
// let result = this.Evaluate(this.Unresolve(area, true, false) + ' ' + options.expression, options.options);
|
|
1359
|
+
|
|
1360
|
+
// ... apply ...
|
|
1361
|
+
|
|
1362
|
+
this.AddConditionalFormat(format);
|
|
1363
|
+
return format;
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* @internal
|
|
1370
|
+
*/
|
|
1371
|
+
public ConditionalFormatExpression(range: RangeReference|undefined, options: CondifionalFormatExpressionOptions): ConditionalFormat {
|
|
1372
|
+
|
|
1373
|
+
if (range === undefined) {
|
|
1374
|
+
const ref = this.GetSelectionReference();
|
|
1375
|
+
if (ref.empty) {
|
|
1376
|
+
throw new Error('invalid range (no selection)');
|
|
1377
|
+
}
|
|
1378
|
+
range = ref.area;
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1382
|
+
|
|
1383
|
+
const format: ConditionalFormatExpression = {
|
|
1384
|
+
type: 'expression',
|
|
1385
|
+
area,
|
|
1386
|
+
...options,
|
|
1387
|
+
};
|
|
1388
|
+
|
|
1389
|
+
/*
|
|
1390
|
+
// we need to calculate the formula once, to get an initial state
|
|
1391
|
+
let result = this.Evaluate(options.expression, options.options);
|
|
1392
|
+
|
|
1393
|
+
if (Array.isArray(result)) {
|
|
1394
|
+
result = result[0][0];
|
|
1395
|
+
}
|
|
1396
|
+
const applied = !!result;
|
|
1397
|
+
|
|
1398
|
+
this.AddConditionalFormat({...format, applied });
|
|
1399
|
+
*/
|
|
1400
|
+
|
|
1401
|
+
this.AddConditionalFormat(format);
|
|
1402
|
+
|
|
1403
|
+
return format;
|
|
1404
|
+
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
/**
|
|
1408
|
+
* add a conditional format
|
|
1409
|
+
*
|
|
1410
|
+
* @internal
|
|
1411
|
+
*/
|
|
1412
|
+
public AddConditionalFormat(format: ConditionalFormat) {
|
|
1413
|
+
|
|
1414
|
+
const sheet = this.model.sheets.Find(format.area.start.sheet_id||0);
|
|
1415
|
+
|
|
1416
|
+
if (!sheet) {
|
|
1417
|
+
throw new Error('invalid reference in format');
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
sheet.conditional_formats.push(format);
|
|
1421
|
+
|
|
1422
|
+
this.calculator.UpdateConditionals(format, sheet);
|
|
1423
|
+
|
|
1424
|
+
// call update if it's the current sheet
|
|
1425
|
+
this.ApplyConditionalFormats(sheet, sheet === this.grid.active_sheet);
|
|
1426
|
+
|
|
1427
|
+
this.PushUndo();
|
|
1428
|
+
|
|
1429
|
+
// fluent
|
|
1430
|
+
return format;
|
|
1431
|
+
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
/**
|
|
1435
|
+
* remove conditional format
|
|
1436
|
+
*
|
|
1437
|
+
* @internal
|
|
1438
|
+
*/
|
|
1439
|
+
public RemoveConditionalFormat(format: ConditionalFormat) {
|
|
1440
|
+
const area = format.area;
|
|
1441
|
+
const sheet = area.start.sheet_id ? this.model.sheets.Find(area.start.sheet_id) : this.grid.active_sheet;
|
|
1442
|
+
|
|
1443
|
+
if (!sheet) {
|
|
1444
|
+
throw new Error('invalid reference in format');
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
let count = 0;
|
|
1448
|
+
sheet.conditional_formats = sheet.conditional_formats.filter(test => {
|
|
1449
|
+
if (test === format) {
|
|
1450
|
+
count++;
|
|
1451
|
+
this.calculator.RemoveConditional(test);
|
|
1452
|
+
return false;
|
|
1453
|
+
}
|
|
1454
|
+
return true;
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
if (count) {
|
|
1458
|
+
sheet.FlushConditionalFormats();
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
// we want to call update if it's the current sheet,
|
|
1462
|
+
// but we want a full repaint
|
|
1463
|
+
|
|
1464
|
+
this.ApplyConditionalFormats(sheet, false);
|
|
1465
|
+
|
|
1466
|
+
if (sheet === this.grid.active_sheet) {
|
|
1467
|
+
this.grid.Update(true);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
if (count) {
|
|
1471
|
+
this.PushUndo();
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
/**
|
|
1477
|
+
* clear conditional formats from the target range (or currently selected
|
|
1478
|
+
* range). we operate on format objects, meaning we'll remove the whole
|
|
1479
|
+
* format object rather than clip the area.
|
|
1480
|
+
*
|
|
1481
|
+
* @internal
|
|
1482
|
+
*/
|
|
1483
|
+
public RemoveConditionalFormats(range?: RangeReference) {
|
|
1484
|
+
|
|
1485
|
+
if (range === undefined) {
|
|
1486
|
+
const ref = this.GetSelectionReference();
|
|
1487
|
+
if (ref.empty) {
|
|
1488
|
+
throw new Error('invalid range (no selection)');
|
|
1489
|
+
}
|
|
1490
|
+
range = ref.area;
|
|
1491
|
+
}
|
|
1492
|
+
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1493
|
+
const sheet = area.start.sheet_id ? this.model.sheets.Find(area.start.sheet_id) : this.grid.active_sheet;
|
|
1494
|
+
|
|
1495
|
+
if (sheet) {
|
|
1496
|
+
let count = 0;
|
|
1497
|
+
|
|
1498
|
+
sheet.conditional_formats = sheet.conditional_formats.filter(test => {
|
|
1499
|
+
const compare = new Area(test.area.start, test.area.end);
|
|
1500
|
+
if (compare.Intersects(area)) {
|
|
1501
|
+
count++;
|
|
1502
|
+
this.calculator.RemoveConditional(test);
|
|
1503
|
+
return false;
|
|
1504
|
+
}
|
|
1505
|
+
return true;
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
if (count) {
|
|
1509
|
+
|
|
1510
|
+
sheet.FlushConditionalFormats();
|
|
1511
|
+
|
|
1512
|
+
this.ApplyConditionalFormats(sheet, false);
|
|
1513
|
+
|
|
1514
|
+
if (sheet === this.grid.active_sheet) {
|
|
1515
|
+
this.grid.Update(true);
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
this.PushUndo();
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
//////////////////////////////////////////////////////////////////////////////
|
|
1527
|
+
|
|
1254
1528
|
/**
|
|
1255
1529
|
* @internal
|
|
1256
1530
|
*/
|
|
@@ -1794,6 +2068,19 @@ export class EmbeddedSpreadsheet {
|
|
|
1794
2068
|
|
|
1795
2069
|
this.UpdateDocumentStyles();
|
|
1796
2070
|
|
|
2071
|
+
// we need to flush conditional formats that use theme colors
|
|
2072
|
+
// (I guess we're just flushing everybody?)
|
|
2073
|
+
|
|
2074
|
+
for (const sheet of this.model.sheets.list) {
|
|
2075
|
+
for (const format of sheet.conditional_formats) {
|
|
2076
|
+
format.internal = undefined;
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
this.calculator.UpdateConditionals();
|
|
2081
|
+
this.ApplyConditionalFormats(this.grid.active_sheet, false);
|
|
2082
|
+
this.grid.Update(true);
|
|
2083
|
+
|
|
1797
2084
|
}
|
|
1798
2085
|
|
|
1799
2086
|
/**
|
|
@@ -1846,7 +2133,7 @@ export class EmbeddedSpreadsheet {
|
|
|
1846
2133
|
* @param reference
|
|
1847
2134
|
*/
|
|
1848
2135
|
public InsertTable(range?: RangeReference, options: InsertTableOptions = {}) {
|
|
1849
|
-
const area = range ? this.
|
|
2136
|
+
const area = range ? this.model.ResolveArea(range, this.grid.active_sheet) : this.GetSelectionReference().area;
|
|
1850
2137
|
|
|
1851
2138
|
let theme = options.theme;
|
|
1852
2139
|
if (typeof theme === 'number') {
|
|
@@ -1928,7 +2215,7 @@ export class EmbeddedSpreadsheet {
|
|
|
1928
2215
|
let target: IRectangle | Partial<Area> | undefined;
|
|
1929
2216
|
|
|
1930
2217
|
if (rect) {
|
|
1931
|
-
target = Rectangle.IsRectangle(rect) ? rect : this.
|
|
2218
|
+
target = Rectangle.IsRectangle(rect) ? rect : this.model.ResolveArea(rect, this.grid.active_sheet);
|
|
1932
2219
|
}
|
|
1933
2220
|
|
|
1934
2221
|
if (argument_separator && argument_separator !== this.parser.argument_separator) {
|
|
@@ -2273,7 +2560,7 @@ export class EmbeddedSpreadsheet {
|
|
|
2273
2560
|
|
|
2274
2561
|
// API v1 OK
|
|
2275
2562
|
|
|
2276
|
-
this.grid.MergeCells(range ? this.
|
|
2563
|
+
this.grid.MergeCells(range ? this.model.ResolveArea(range, this.grid.active_sheet) : undefined);
|
|
2277
2564
|
}
|
|
2278
2565
|
|
|
2279
2566
|
/**
|
|
@@ -2287,7 +2574,7 @@ export class EmbeddedSpreadsheet {
|
|
|
2287
2574
|
|
|
2288
2575
|
// API v1 OK
|
|
2289
2576
|
|
|
2290
|
-
this.grid.UnmergeCells(range ? this.
|
|
2577
|
+
this.grid.UnmergeCells(range ? this.model.ResolveArea(range, this.grid.active_sheet) : undefined);
|
|
2291
2578
|
}
|
|
2292
2579
|
|
|
2293
2580
|
/**
|
|
@@ -2954,11 +3241,11 @@ export class EmbeddedSpreadsheet {
|
|
|
2954
3241
|
// FIXME: optional? parameter? (...)
|
|
2955
3242
|
|
|
2956
3243
|
if (data.rendered_values && !options.recalculate) {
|
|
2957
|
-
this.grid.Update();
|
|
2958
3244
|
this.calculator.RebuildClean(true);
|
|
3245
|
+
this.ApplyConditionalFormats(this.grid.active_sheet, false);
|
|
3246
|
+
this.grid.Update();
|
|
2959
3247
|
}
|
|
2960
3248
|
else {
|
|
2961
|
-
// console.info('load recalc');
|
|
2962
3249
|
this.Recalculate();
|
|
2963
3250
|
}
|
|
2964
3251
|
|
|
@@ -2996,7 +3283,7 @@ export class EmbeddedSpreadsheet {
|
|
|
2996
3283
|
// API v1 OK
|
|
2997
3284
|
|
|
2998
3285
|
if (typeof address === 'string') {
|
|
2999
|
-
const reference = this.
|
|
3286
|
+
const reference = this.model.ResolveAddress(address, this.grid.active_sheet);
|
|
3000
3287
|
address = IsCellAddress(reference) ? reference : reference.start;
|
|
3001
3288
|
}
|
|
3002
3289
|
|
|
@@ -3016,7 +3303,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3016
3303
|
public SetValidation(address: AddressReference, validation?: RangeReference|CellValue[], error?: boolean) {
|
|
3017
3304
|
|
|
3018
3305
|
if (typeof address === 'string') {
|
|
3019
|
-
const reference = this.
|
|
3306
|
+
const reference = this.model.ResolveAddress(address, this.grid.active_sheet);
|
|
3020
3307
|
address = IsCellAddress(reference) ? reference : reference.start;
|
|
3021
3308
|
}
|
|
3022
3309
|
|
|
@@ -3024,7 +3311,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3024
3311
|
this.grid.SetValidation(address, validation, error);
|
|
3025
3312
|
}
|
|
3026
3313
|
else {
|
|
3027
|
-
const range = this.
|
|
3314
|
+
const range = this.model.ResolveArea(validation, this.grid.active_sheet);
|
|
3028
3315
|
this.grid.SetValidation(address, range, error);
|
|
3029
3316
|
}
|
|
3030
3317
|
|
|
@@ -3034,7 +3321,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3034
3321
|
public RemoveValidation(address: AddressReference) {
|
|
3035
3322
|
|
|
3036
3323
|
if (typeof address === 'string') {
|
|
3037
|
-
const reference = this.
|
|
3324
|
+
const reference = this.model.ResolveAddress(address);
|
|
3038
3325
|
address = IsCellAddress(reference) ? reference : reference.start;
|
|
3039
3326
|
}
|
|
3040
3327
|
|
|
@@ -3045,7 +3332,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3045
3332
|
public SetValidationList(address: AddressReference, list: CellValue[]) {
|
|
3046
3333
|
|
|
3047
3334
|
if (typeof address === 'string') {
|
|
3048
|
-
const reference = this.
|
|
3335
|
+
const reference = this.model.ResolveAddress(address);
|
|
3049
3336
|
address = IsCellAddress(reference) ? reference : reference.start;
|
|
3050
3337
|
}
|
|
3051
3338
|
|
|
@@ -3056,11 +3343,11 @@ export class EmbeddedSpreadsheet {
|
|
|
3056
3343
|
public SetValidationRange(address: AddressReference, range: RangeReference) {
|
|
3057
3344
|
|
|
3058
3345
|
if (typeof address === 'string') {
|
|
3059
|
-
const reference = this.
|
|
3346
|
+
const reference = this.model.ResolveAddress(address);
|
|
3060
3347
|
address = IsCellAddress(reference) ? reference : reference.start;
|
|
3061
3348
|
}
|
|
3062
3349
|
|
|
3063
|
-
range = this.
|
|
3350
|
+
range = this.model.ResolveArea(range);
|
|
3064
3351
|
this.grid.SetValidation(address, range);
|
|
3065
3352
|
|
|
3066
3353
|
}
|
|
@@ -3248,6 +3535,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3248
3535
|
}
|
|
3249
3536
|
|
|
3250
3537
|
this.calculator.Calculate(area);
|
|
3538
|
+
this.ApplyConditionalFormats(this.grid.active_sheet, false);
|
|
3251
3539
|
|
|
3252
3540
|
this.grid.Update(true); // , area);
|
|
3253
3541
|
this.UpdateAnnotations();
|
|
@@ -3364,7 +3652,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3364
3652
|
// API v1 OK
|
|
3365
3653
|
|
|
3366
3654
|
if (typeof address === 'string') {
|
|
3367
|
-
const reference = this.
|
|
3655
|
+
const reference = this.model.ResolveAddress(address, this.grid.active_sheet);
|
|
3368
3656
|
address = IsCellAddress(reference) ? reference : reference.start;
|
|
3369
3657
|
}
|
|
3370
3658
|
|
|
@@ -3404,7 +3692,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3404
3692
|
// FIXME: we're using the sheet EnsureAddress method, but that should
|
|
3405
3693
|
// move either in here or into some sort of helper class
|
|
3406
3694
|
|
|
3407
|
-
const result = this.
|
|
3695
|
+
const result = this.model.ResolveAddress(reference, this.grid.active_sheet);
|
|
3408
3696
|
|
|
3409
3697
|
if (IsCellAddress(result)) {
|
|
3410
3698
|
return result.sheet_id ? result : undefined;
|
|
@@ -3432,6 +3720,9 @@ export class EmbeddedSpreadsheet {
|
|
|
3432
3720
|
ref = resolved;
|
|
3433
3721
|
}
|
|
3434
3722
|
|
|
3723
|
+
return this.calculator.Unresolve(ref, this.grid.active_sheet, qualified, named);
|
|
3724
|
+
|
|
3725
|
+
/*
|
|
3435
3726
|
let range = '';
|
|
3436
3727
|
const area = IsCellAddress(ref) ? new Area(ref) : new Area(ref.start, ref.end);
|
|
3437
3728
|
|
|
@@ -3457,10 +3748,11 @@ export class EmbeddedSpreadsheet {
|
|
|
3457
3748
|
// the active selection must be on the active sheet? (...)
|
|
3458
3749
|
|
|
3459
3750
|
const sheet_id = area.start.sheet_id || this.grid.active_sheet.id;
|
|
3460
|
-
const sheet_name = this.ResolveSheetName(sheet_id, true);
|
|
3751
|
+
const sheet_name = this.calculator.ResolveSheetName(sheet_id, true);
|
|
3461
3752
|
|
|
3462
3753
|
return sheet_name ? sheet_name + '!' + range : range;
|
|
3463
|
-
|
|
3754
|
+
*/
|
|
3755
|
+
|
|
3464
3756
|
}
|
|
3465
3757
|
|
|
3466
3758
|
/**
|
|
@@ -3482,7 +3774,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3482
3774
|
|
|
3483
3775
|
return this.calculator.Evaluate(
|
|
3484
3776
|
expression, this.grid.active_sheet, options);
|
|
3485
|
-
|
|
3777
|
+
|
|
3486
3778
|
}
|
|
3487
3779
|
|
|
3488
3780
|
/**
|
|
@@ -3522,7 +3814,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3522
3814
|
// the active selection must be on the active sheet? (...)
|
|
3523
3815
|
|
|
3524
3816
|
const sheet_id = ref.area.start.sheet_id || this.grid.active_sheet.id;
|
|
3525
|
-
const sheet_name = this.ResolveSheetName(sheet_id, true);
|
|
3817
|
+
const sheet_name = this.calculator.ResolveSheetName(sheet_id, true);
|
|
3526
3818
|
|
|
3527
3819
|
return sheet_name ? sheet_name + '!' + range : range;
|
|
3528
3820
|
|
|
@@ -3600,7 +3892,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3600
3892
|
|
|
3601
3893
|
// still for now we can take advantage of that and skip the check.
|
|
3602
3894
|
|
|
3603
|
-
this.grid.ApplyBorders2(range ? this.
|
|
3895
|
+
this.grid.ApplyBorders2(range ? this.model.ResolveArea(range, this.grid.active_sheet) : undefined, borders, {}, width);
|
|
3604
3896
|
|
|
3605
3897
|
}
|
|
3606
3898
|
|
|
@@ -3623,7 +3915,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3623
3915
|
// translate old-style alignment constants (UPDATE: moved to grid)
|
|
3624
3916
|
|
|
3625
3917
|
this.grid.ApplyStyle(
|
|
3626
|
-
range ? this.
|
|
3918
|
+
range ? this.model.ResolveArea(range, this.grid.active_sheet) : undefined, style, delta);
|
|
3627
3919
|
}
|
|
3628
3920
|
|
|
3629
3921
|
/**
|
|
@@ -3687,7 +3979,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3687
3979
|
|
|
3688
3980
|
if (typeof value === 'object') {
|
|
3689
3981
|
if (IsCellAddress(value) || IsArea(value)) {
|
|
3690
|
-
this.grid.SetName(name, this.
|
|
3982
|
+
this.grid.SetName(name, this.model.ResolveArea(value, this.grid.active_sheet));
|
|
3691
3983
|
return;
|
|
3692
3984
|
}
|
|
3693
3985
|
}
|
|
@@ -3704,7 +3996,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3704
3996
|
switch (parse_result.expression.type) {
|
|
3705
3997
|
case 'address':
|
|
3706
3998
|
case 'range':
|
|
3707
|
-
this.grid.SetName(name, this.
|
|
3999
|
+
this.grid.SetName(name, this.model.ResolveArea(parse_result.expression, this.grid.active_sheet));
|
|
3708
4000
|
return;
|
|
3709
4001
|
}
|
|
3710
4002
|
this.grid.SetName(name, undefined, value);
|
|
@@ -3730,7 +4022,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3730
4022
|
|
|
3731
4023
|
// NOTE: AC is handled internally
|
|
3732
4024
|
|
|
3733
|
-
this.grid.SetName(name, this.
|
|
4025
|
+
this.grid.SetName(name, this.model.ResolveArea(range, this.grid.active_sheet));
|
|
3734
4026
|
*/
|
|
3735
4027
|
}
|
|
3736
4028
|
|
|
@@ -3756,7 +4048,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3756
4048
|
// API v1 OK
|
|
3757
4049
|
|
|
3758
4050
|
if (typeof address === 'string') {
|
|
3759
|
-
const reference = this.
|
|
4051
|
+
const reference = this.model.ResolveAddress(address, this.grid.active_sheet);
|
|
3760
4052
|
address = IsCellAddress(reference) ? reference : reference.start;
|
|
3761
4053
|
}
|
|
3762
4054
|
|
|
@@ -3784,7 +4076,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3784
4076
|
let resolved: Area|undefined = undefined;
|
|
3785
4077
|
|
|
3786
4078
|
if (!!range) {
|
|
3787
|
-
resolved = this.
|
|
4079
|
+
resolved = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
3788
4080
|
if (resolved.start.sheet_id) {
|
|
3789
4081
|
if (resolved.start.sheet_id !== this.grid.active_sheet.id) {
|
|
3790
4082
|
this.grid.ActivateSheetID(resolved.start.sheet_id);
|
|
@@ -3826,7 +4118,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3826
4118
|
}
|
|
3827
4119
|
}
|
|
3828
4120
|
|
|
3829
|
-
return this.grid.GetRange(this.
|
|
4121
|
+
return this.grid.GetRange(this.model.ResolveAddress(range, this.grid.active_sheet), options.type);
|
|
3830
4122
|
|
|
3831
4123
|
}
|
|
3832
4124
|
|
|
@@ -3855,7 +4147,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3855
4147
|
|
|
3856
4148
|
if (!range) { return undefined; }
|
|
3857
4149
|
|
|
3858
|
-
return this.grid.GetRangeStyle(this.
|
|
4150
|
+
return this.grid.GetRangeStyle(this.model.ResolveAddress(range, this.grid.active_sheet), apply_theme);
|
|
3859
4151
|
|
|
3860
4152
|
}
|
|
3861
4153
|
|
|
@@ -3884,7 +4176,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3884
4176
|
}
|
|
3885
4177
|
|
|
3886
4178
|
if (range) {
|
|
3887
|
-
const area = this.
|
|
4179
|
+
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
3888
4180
|
|
|
3889
4181
|
if (options.spill && Array.isArray(data)) {
|
|
3890
4182
|
const rows = data.length;
|
|
@@ -3940,6 +4232,47 @@ export class EmbeddedSpreadsheet {
|
|
|
3940
4232
|
|
|
3941
4233
|
// --- internal (protected) methods ------------------------------------------
|
|
3942
4234
|
|
|
4235
|
+
/**
|
|
4236
|
+
*
|
|
4237
|
+
*/
|
|
4238
|
+
protected ApplyConditionalFormats(sheet: Sheet, call_update: boolean) {
|
|
4239
|
+
|
|
4240
|
+
// const sheet = this.grid.active_sheet;
|
|
4241
|
+
const areas: IArea[] = [];
|
|
4242
|
+
|
|
4243
|
+
if (sheet.conditional_formats.length || sheet.flush_conditional_formats) {
|
|
4244
|
+
|
|
4245
|
+
for (const entry of sheet.conditional_formats) {
|
|
4246
|
+
areas.push(entry.area);
|
|
4247
|
+
|
|
4248
|
+
// NOTE: we're (optionally) adding the gradient here, instead
|
|
4249
|
+
// of when it's created, because we might want to flush this
|
|
4250
|
+
// from time to time. specifically, because gradient might use
|
|
4251
|
+
// theme colors, we need to flush it when the theme is updated.
|
|
4252
|
+
|
|
4253
|
+
// so don't move it, even though it is tempting. or rewrite to
|
|
4254
|
+
// update on theme changes.
|
|
4255
|
+
|
|
4256
|
+
if (entry.type === 'gradient') {
|
|
4257
|
+
if (!entry.internal) {
|
|
4258
|
+
entry.internal = {};
|
|
4259
|
+
}
|
|
4260
|
+
if (!entry.internal.gradient) {
|
|
4261
|
+
entry.internal.gradient = new Gradient(entry.stops, this.grid.theme, entry.color_space);
|
|
4262
|
+
}
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
|
|
4266
|
+
sheet.ApplyConditionalFormats();
|
|
4267
|
+
|
|
4268
|
+
}
|
|
4269
|
+
|
|
4270
|
+
if (call_update) {
|
|
4271
|
+
this.grid.Update(true, areas);
|
|
4272
|
+
}
|
|
4273
|
+
|
|
4274
|
+
}
|
|
4275
|
+
|
|
3943
4276
|
protected ResolveTable(reference: RangeReference): Table|undefined {
|
|
3944
4277
|
|
|
3945
4278
|
let table: Table|undefined = undefined;
|
|
@@ -3953,7 +4286,7 @@ export class EmbeddedSpreadsheet {
|
|
|
3953
4286
|
|
|
3954
4287
|
if (!table) {
|
|
3955
4288
|
|
|
3956
|
-
let address = this.
|
|
4289
|
+
let address = this.model.ResolveAddress(reference, this.grid.active_sheet);
|
|
3957
4290
|
|
|
3958
4291
|
if (!IsCellAddress(address)) {
|
|
3959
4292
|
address = address.start;
|
|
@@ -4044,6 +4377,11 @@ export class EmbeddedSpreadsheet {
|
|
|
4044
4377
|
|
|
4045
4378
|
this.InflateAnnotations();
|
|
4046
4379
|
|
|
4380
|
+
// and conditional formats
|
|
4381
|
+
|
|
4382
|
+
this.calculator.UpdateConditionals();
|
|
4383
|
+
this.ApplyConditionalFormats(this.grid.active_sheet, false);
|
|
4384
|
+
|
|
4047
4385
|
}
|
|
4048
4386
|
else {
|
|
4049
4387
|
return reject('unknown error (missing data)');
|
|
@@ -4164,6 +4502,9 @@ export class EmbeddedSpreadsheet {
|
|
|
4164
4502
|
|
|
4165
4503
|
this.UpdateAnnotations();
|
|
4166
4504
|
|
|
4505
|
+
this.calculator.UpdateConditionals();
|
|
4506
|
+
this.ApplyConditionalFormats(event.activate, true);
|
|
4507
|
+
|
|
4167
4508
|
}
|
|
4168
4509
|
|
|
4169
4510
|
protected HandleDrag(event: DragEvent): void {
|
|
@@ -4556,7 +4897,7 @@ export class EmbeddedSpreadsheet {
|
|
|
4556
4897
|
protected UpdateAnnotations(): void {
|
|
4557
4898
|
for (const annotation of this.grid.active_sheet.annotations) {
|
|
4558
4899
|
if (annotation.temp.vertex) {
|
|
4559
|
-
const vertex = annotation.temp.vertex as
|
|
4900
|
+
const vertex = annotation.temp.vertex as StateLeafVertex;
|
|
4560
4901
|
if (vertex.state_id !== annotation.temp.state) {
|
|
4561
4902
|
annotation.temp.state = vertex.state_id;
|
|
4562
4903
|
|
|
@@ -4703,7 +5044,7 @@ export class EmbeddedSpreadsheet {
|
|
|
4703
5044
|
|
|
4704
5045
|
this.parser.Walk(parse_result.expression, (unit) => {
|
|
4705
5046
|
if (unit.type === 'address' || unit.type === 'range') {
|
|
4706
|
-
this.
|
|
5047
|
+
this.model.ResolveSheetID(unit, undefined, this.grid.active_sheet);
|
|
4707
5048
|
}
|
|
4708
5049
|
return true;
|
|
4709
5050
|
});
|
|
@@ -5436,35 +5777,5 @@ export class EmbeddedSpreadsheet {
|
|
|
5436
5777
|
}
|
|
5437
5778
|
}
|
|
5438
5779
|
|
|
5439
|
-
/**
|
|
5440
|
-
* this is only used in one place, can we just inline?
|
|
5441
|
-
* [A: seems like it might be useful, though]
|
|
5442
|
-
*
|
|
5443
|
-
* @param id
|
|
5444
|
-
* @param quote
|
|
5445
|
-
* @returns
|
|
5446
|
-
*/
|
|
5447
|
-
protected ResolveSheetName(id: number, quote = false): string | undefined {
|
|
5448
|
-
const sheet = this.model.sheets.Find(id);
|
|
5449
|
-
if (sheet) {
|
|
5450
|
-
if (quote && QuotedSheetNameRegex.test(sheet.name)) {
|
|
5451
|
-
return `'${sheet.name}'`;
|
|
5452
|
-
}
|
|
5453
|
-
return sheet.name;
|
|
5454
|
-
}
|
|
5455
|
-
|
|
5456
|
-
/*
|
|
5457
|
-
for (const sheet of this.grid.model.sheets) {
|
|
5458
|
-
if (sheet.id === id) {
|
|
5459
|
-
if (quote && QuotedSheetNameRegex.test(sheet.name)) {
|
|
5460
|
-
return `'${sheet.name}'`;
|
|
5461
|
-
}
|
|
5462
|
-
return sheet.name;
|
|
5463
|
-
}
|
|
5464
|
-
}
|
|
5465
|
-
*/
|
|
5466
|
-
|
|
5467
|
-
return undefined;
|
|
5468
|
-
}
|
|
5469
5780
|
|
|
5470
5781
|
}
|