@trebco/treb 29.5.4 → 29.7.5
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/api-generator/api-generator.ts +10 -1
- package/dist/treb-spreadsheet-light.mjs +10 -10
- package/dist/treb-spreadsheet.mjs +10 -10
- package/dist/treb.d.ts +102 -22
- package/package.json +1 -1
- package/treb-base-types/src/cells.ts +34 -0
- package/treb-base-types/src/evaluate-options.ts +22 -1
- package/treb-base-types/src/union.ts +0 -11
- package/treb-base-types/src/value-type.ts +3 -3
- package/treb-calculator/src/calculator.ts +34 -29
- package/treb-calculator/src/descriptors.ts +2 -0
- package/treb-calculator/src/expression-calculator.ts +31 -173
- package/treb-calculator/src/function-error.ts +16 -21
- package/treb-calculator/src/functions/matrix-functions.ts +1 -1
- package/treb-charts/src/util.ts +3 -36
- package/treb-data-model/src/data_model.ts +2 -63
- package/treb-data-model/src/index.ts +2 -10
- package/treb-data-model/src/sheet.ts +7 -3
- package/treb-data-model/src/types.ts +71 -0
- package/treb-embed/src/embedded-spreadsheet.ts +215 -83
- package/treb-export/src/xml-utils.ts +20 -8
- package/treb-grid/src/index.ts +1 -0
- package/treb-grid/src/types/clipboard_data2.ts +85 -0
- package/treb-grid/src/types/grid.ts +252 -13
- package/treb-grid/src/types/grid_base.ts +14 -2
- package/treb-parser/src/parser-types.ts +3 -0
- package/treb-parser/src/parser.ts +86 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
|
|
2
|
+
import type { SerializedSheet } from './sheet_types';
|
|
3
|
+
import type { Sheet } from './sheet';
|
|
4
|
+
import type { IArea } from 'treb-base-types';
|
|
5
|
+
import type { SerializedNamed } from './named';
|
|
6
|
+
import type { Table } from 'treb-base-types';
|
|
7
|
+
import type { ExpressionUnit } from 'treb-parser';
|
|
8
|
+
|
|
9
|
+
export interface ConnectedElementType {
|
|
10
|
+
formula: string;
|
|
11
|
+
update?: (instance: ConnectedElementType) => void;
|
|
12
|
+
internal?: unknown; // opaque type to prevent circular dependencies
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface SerializedMacroFunction {
|
|
16
|
+
name: string;
|
|
17
|
+
function_def: string;
|
|
18
|
+
argument_names?: string[];
|
|
19
|
+
description?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* we define this as extending the serialized version, rather
|
|
24
|
+
* than taking out the parameter, so we can make that def public
|
|
25
|
+
* in the API types.
|
|
26
|
+
*/
|
|
27
|
+
export interface MacroFunction extends SerializedMacroFunction {
|
|
28
|
+
expression?: ExpressionUnit;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
export interface ViewModel {
|
|
35
|
+
active_sheet: Sheet;
|
|
36
|
+
view_index: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* this type is no longer in use, but we retain it to parse old documents
|
|
41
|
+
* that use it.
|
|
42
|
+
*
|
|
43
|
+
* @deprecated
|
|
44
|
+
*/
|
|
45
|
+
export interface SerializedNamedExpression {
|
|
46
|
+
name: string;
|
|
47
|
+
expression: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SerializedModel {
|
|
51
|
+
sheet_data: SerializedSheet[];
|
|
52
|
+
active_sheet: number;
|
|
53
|
+
|
|
54
|
+
/** @deprecated */
|
|
55
|
+
named_ranges?: Record<string, IArea>;
|
|
56
|
+
|
|
57
|
+
/** @deprecated */
|
|
58
|
+
named_expressions?: SerializedNamedExpression[];
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* new type for consolidated named ranges & expressions. the old
|
|
62
|
+
* types are retained for backwards compatibility on import but we won't
|
|
63
|
+
* export them anymore.
|
|
64
|
+
*/
|
|
65
|
+
named?: SerializedNamed[];
|
|
66
|
+
|
|
67
|
+
macro_functions?: MacroFunction[];
|
|
68
|
+
tables?: Table[];
|
|
69
|
+
decimal_mark?: ','|'.';
|
|
70
|
+
}
|
|
71
|
+
|
|
@@ -73,7 +73,7 @@ import type {
|
|
|
73
73
|
|
|
74
74
|
import {
|
|
75
75
|
IsArea, ThemeColorTable, ComplexToString, Rectangle, IsComplex, type CellStyle,
|
|
76
|
-
Localization, Style, type Color, ResolveThemeColor, IsCellAddress, Area, IsFlatData, IsFlatDataArray, Gradient, DOMContext
|
|
76
|
+
Localization, Style, type Color, ResolveThemeColor, IsCellAddress, Area, IsFlatData, IsFlatDataArray, Gradient, DOMContext
|
|
77
77
|
} from 'treb-base-types';
|
|
78
78
|
|
|
79
79
|
import { EventSource, ValidateURI } from 'treb-utils';
|
|
@@ -93,7 +93,7 @@ import type { SelectionState } from './selection-state';
|
|
|
93
93
|
import type { BorderToolbarMessage, ToolbarMessage } from './toolbar-message';
|
|
94
94
|
|
|
95
95
|
import { Chart, ChartFunctions } from 'treb-charts';
|
|
96
|
-
import type { SetRangeOptions } from 'treb-grid';
|
|
96
|
+
import type { SetRangeOptions, ClipboardData, PasteOptions } from 'treb-grid';
|
|
97
97
|
|
|
98
98
|
import type { StateLeafVertex } from 'treb-calculator';
|
|
99
99
|
|
|
@@ -264,6 +264,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
264
264
|
/** @internal */
|
|
265
265
|
public static one_time_warnings: Record<string, boolean> = {};
|
|
266
266
|
|
|
267
|
+
/** @internal */
|
|
268
|
+
protected static clipboard?: ClipboardData;
|
|
269
|
+
|
|
267
270
|
protected DOM = DOMContext.GetInstance(); // default
|
|
268
271
|
|
|
269
272
|
/**
|
|
@@ -943,10 +946,20 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
943
946
|
|
|
944
947
|
const cached_selection = this.last_selection;
|
|
945
948
|
|
|
949
|
+
/*
|
|
946
950
|
((this.calculation === CalculationOptions.automatic) ?
|
|
947
951
|
this.Recalculate(event) : Promise.resolve()).then(() => {
|
|
948
952
|
this.DocumentChange(cached_selection);
|
|
949
953
|
});
|
|
954
|
+
*/
|
|
955
|
+
|
|
956
|
+
// recalc is no longer async
|
|
957
|
+
|
|
958
|
+
if (this.calculation === CalculationOptions.automatic) {
|
|
959
|
+
this.Recalculate(event);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
this.DocumentChange(cached_selection);
|
|
950
963
|
|
|
951
964
|
/*
|
|
952
965
|
if (this.calculation === CalculationOptions.automatic) {
|
|
@@ -1027,10 +1040,19 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1027
1040
|
if (event.rebuild_required) {
|
|
1028
1041
|
this.calculator.Reset();
|
|
1029
1042
|
|
|
1043
|
+
/*
|
|
1030
1044
|
((this.calculation === CalculationOptions.automatic) ?
|
|
1031
1045
|
this.Recalculate(event) : Promise.resolve()).then(() => {
|
|
1032
1046
|
this.DocumentChange(cached_selection);
|
|
1033
1047
|
});
|
|
1048
|
+
*/
|
|
1049
|
+
|
|
1050
|
+
// recalculate is no longer async
|
|
1051
|
+
|
|
1052
|
+
if (this.calculation === CalculationOptions.automatic) {
|
|
1053
|
+
this.Recalculate(event);
|
|
1054
|
+
}
|
|
1055
|
+
this.DocumentChange(cached_selection);
|
|
1034
1056
|
|
|
1035
1057
|
}
|
|
1036
1058
|
else {
|
|
@@ -1387,18 +1409,10 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1387
1409
|
|
|
1388
1410
|
/** @internal */
|
|
1389
1411
|
public ConditionalFormatDuplicateValues(range: RangeReference|undefined, options: ConditionalFormatDuplicateValuesOptions): ConditionalFormat {
|
|
1390
|
-
|
|
1391
|
-
if (range === undefined) {
|
|
1392
|
-
const ref = this.GetSelectionReference();
|
|
1393
|
-
if (ref.empty) {
|
|
1394
|
-
throw new Error('invalid range (no selection)');
|
|
1395
|
-
}
|
|
1396
|
-
range = ref.area;
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1412
|
+
|
|
1399
1413
|
return this.AddConditionalFormat({
|
|
1400
1414
|
type: 'duplicate-values',
|
|
1401
|
-
area: this.
|
|
1415
|
+
area: this.RangeOrSelection(range, 'invalid range (no selection)'),
|
|
1402
1416
|
...options,
|
|
1403
1417
|
});
|
|
1404
1418
|
|
|
@@ -1409,15 +1423,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1409
1423
|
*/
|
|
1410
1424
|
public ConditionalFormatGradient(range: RangeReference|undefined, options: ConditionalFormatGradientOptions|StandardGradient): ConditionalFormat {
|
|
1411
1425
|
|
|
1412
|
-
|
|
1413
|
-
const ref = this.GetSelectionReference();
|
|
1414
|
-
if (ref.empty) {
|
|
1415
|
-
throw new Error('invalid range (no selection)');
|
|
1416
|
-
}
|
|
1417
|
-
range = ref.area;
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1426
|
+
const area = this.RangeOrSelection(range, 'invalid range (no selection)');
|
|
1421
1427
|
|
|
1422
1428
|
const format: ConditionalFormatGradient = (typeof options === 'object') ?
|
|
1423
1429
|
{
|
|
@@ -1435,19 +1441,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1435
1441
|
/** @internal */
|
|
1436
1442
|
public ConditionalFormatCellMatch(range: RangeReference|undefined, options: ConditionalFormatCellMatchOptions): ConditionalFormat {
|
|
1437
1443
|
|
|
1438
|
-
if (range === undefined) {
|
|
1439
|
-
const ref = this.GetSelectionReference();
|
|
1440
|
-
if (ref.empty) {
|
|
1441
|
-
throw new Error('invalid range (no selection)');
|
|
1442
|
-
}
|
|
1443
|
-
range = ref.area;
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1447
|
-
|
|
1448
1444
|
const format: ConditionalFormatCellMatch = {
|
|
1449
1445
|
type: 'cell-match',
|
|
1450
|
-
area,
|
|
1446
|
+
area: this.RangeOrSelection(range, 'invalid range (no selection)'),
|
|
1451
1447
|
...options,
|
|
1452
1448
|
};
|
|
1453
1449
|
|
|
@@ -1462,19 +1458,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1462
1458
|
*/
|
|
1463
1459
|
public ConditionalFormatExpression(range: RangeReference|undefined, options: CondifionalFormatExpressionOptions): ConditionalFormat {
|
|
1464
1460
|
|
|
1465
|
-
if (range === undefined) {
|
|
1466
|
-
const ref = this.GetSelectionReference();
|
|
1467
|
-
if (ref.empty) {
|
|
1468
|
-
throw new Error('invalid range (no selection)');
|
|
1469
|
-
}
|
|
1470
|
-
range = ref.area;
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1474
|
-
|
|
1475
1461
|
const format: ConditionalFormatExpression = {
|
|
1476
1462
|
type: 'expression',
|
|
1477
|
-
area,
|
|
1463
|
+
area: this.RangeOrSelection(range, 'invalid range (no selection)'),
|
|
1478
1464
|
...options,
|
|
1479
1465
|
};
|
|
1480
1466
|
|
|
@@ -1515,18 +1501,10 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1515
1501
|
* @internal
|
|
1516
1502
|
*/
|
|
1517
1503
|
public RemoveConditionalFormats(range?: RangeReference) {
|
|
1518
|
-
|
|
1519
|
-
if (
|
|
1520
|
-
|
|
1521
|
-
if (ref.empty) {
|
|
1522
|
-
throw new Error('invalid range (no selection)');
|
|
1523
|
-
}
|
|
1524
|
-
range = ref.area;
|
|
1504
|
+
const area = this.RangeOrSelection(range);
|
|
1505
|
+
if (area) {
|
|
1506
|
+
this.grid.RemoveConditionalFormat({ area });
|
|
1525
1507
|
}
|
|
1526
|
-
|
|
1527
|
-
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1528
|
-
this.grid.RemoveConditionalFormat({ area });
|
|
1529
|
-
|
|
1530
1508
|
}
|
|
1531
1509
|
|
|
1532
1510
|
//////////////////////////////////////////////////////////////////////////////
|
|
@@ -2009,9 +1987,27 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2009
1987
|
* grid stops broadcasting events for the duration of the function call,
|
|
2010
1988
|
* and collects them instead. After the function call we update as necessary.
|
|
2011
1989
|
*
|
|
2012
|
-
* @
|
|
1990
|
+
* @privateRemarks
|
|
1991
|
+
*
|
|
1992
|
+
* FIXME: we need to consider the case where this is nested, since we now
|
|
1993
|
+
* call it from the Paste method. that might be batched by a caller. we
|
|
1994
|
+
* need a batch stack, and we need to consolidate any options (paint is the
|
|
1995
|
+
* only option) and keep the top-of-stack last selection.
|
|
1996
|
+
*
|
|
1997
|
+
* Q: why does this work the way it does, anyway? why not just rely
|
|
1998
|
+
* on the actual events? if grid published them after a batch, wouldn't
|
|
1999
|
+
* everything just work? or is the problem that we normally handle events
|
|
2000
|
+
* serially? maybe we need to rethink how we handle events coming from
|
|
2001
|
+
* the grid.
|
|
2002
|
+
*
|
|
2003
|
+
* ---
|
|
2004
|
+
*
|
|
2005
|
+
* OK so now, grid will handle nested batching. it won't return anything
|
|
2006
|
+
* until the last batch is complete. so this should work in the case of
|
|
2007
|
+
* nested calls, because nothing will happen until the last one is complete.
|
|
2008
|
+
*
|
|
2013
2009
|
*/
|
|
2014
|
-
public
|
|
2010
|
+
public Batch(func: () => void, paint = false): void {
|
|
2015
2011
|
|
|
2016
2012
|
// API v1 OK
|
|
2017
2013
|
|
|
@@ -2021,6 +2017,8 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2021
2017
|
let recalc = false;
|
|
2022
2018
|
let reset = false;
|
|
2023
2019
|
|
|
2020
|
+
// FIXME (2024): are these FIXMEs below still a thing? (...)
|
|
2021
|
+
|
|
2024
2022
|
// FIXME: annotation events
|
|
2025
2023
|
// TODO: annotation events
|
|
2026
2024
|
|
|
@@ -2041,7 +2039,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2041
2039
|
}
|
|
2042
2040
|
|
|
2043
2041
|
if (recalc || reset) {
|
|
2044
|
-
|
|
2042
|
+
this.Recalculate();
|
|
2045
2043
|
this.DocumentChange(cached_selection);
|
|
2046
2044
|
}
|
|
2047
2045
|
|
|
@@ -3739,10 +3737,12 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
3739
3737
|
*
|
|
3740
3738
|
* the event parameter should not be used if this is called
|
|
3741
3739
|
* as an API function, remove it from typings
|
|
3742
|
-
*
|
|
3740
|
+
*
|
|
3741
|
+
* why is this async? it doesn't do anything async.
|
|
3742
|
+
*
|
|
3743
3743
|
* @public
|
|
3744
3744
|
*/
|
|
3745
|
-
public
|
|
3745
|
+
public Recalculate(event?: GridEvent) {
|
|
3746
3746
|
|
|
3747
3747
|
// API v1 OK
|
|
3748
3748
|
|
|
@@ -4289,6 +4289,114 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4289
4289
|
|
|
4290
4290
|
}
|
|
4291
4291
|
|
|
4292
|
+
/**
|
|
4293
|
+
* override for paste method omits the data parameter.
|
|
4294
|
+
*/
|
|
4295
|
+
public Paste(target?: RangeReference, options?: PasteOptions): void;
|
|
4296
|
+
|
|
4297
|
+
/**
|
|
4298
|
+
* standard paste method accepts data argument
|
|
4299
|
+
*
|
|
4300
|
+
* @param target
|
|
4301
|
+
* @param data
|
|
4302
|
+
* @param options
|
|
4303
|
+
*/
|
|
4304
|
+
public Paste(target?: RangeReference, data?: ClipboardData, options?: PasteOptions): void;
|
|
4305
|
+
|
|
4306
|
+
/**
|
|
4307
|
+
* paste clipboard data into a target range. this method does not use
|
|
4308
|
+
* the system clipboard; pass in clipboard data returned from the Cut or
|
|
4309
|
+
* Copy method.
|
|
4310
|
+
*
|
|
4311
|
+
* @param target - the target to paste data into. this can be larger
|
|
4312
|
+
* than the clipboard data, in which case values will be recycled in
|
|
4313
|
+
* blocks. if the target is smaller than the source data, we will expand
|
|
4314
|
+
* it to fit the data.
|
|
4315
|
+
*
|
|
4316
|
+
* @param data - clipboard data to paste.
|
|
4317
|
+
*
|
|
4318
|
+
* @privateRemarks LLM API
|
|
4319
|
+
*
|
|
4320
|
+
* @privateRemarks this was async when we were thinking of using the
|
|
4321
|
+
* system clipboard, but that's pretty broken so we're not going to
|
|
4322
|
+
* bother atm.
|
|
4323
|
+
*/
|
|
4324
|
+
public Paste(target?: RangeReference, ...args: unknown[]): void {
|
|
4325
|
+
|
|
4326
|
+
// support for the dynamic overload (not sure that's actually a good idea)
|
|
4327
|
+
|
|
4328
|
+
let data = EmbeddedSpreadsheet.clipboard; // default to "global" clipboard
|
|
4329
|
+
let options: PasteOptions = {}; // default
|
|
4330
|
+
|
|
4331
|
+
switch (args.length) {
|
|
4332
|
+
case 1:
|
|
4333
|
+
|
|
4334
|
+
// could be either
|
|
4335
|
+
|
|
4336
|
+
if (Array.isArray(args[0])) {
|
|
4337
|
+
data = args[0] as ClipboardData;
|
|
4338
|
+
}
|
|
4339
|
+
else if (typeof args[0] === 'object') {
|
|
4340
|
+
options = args[0] as PasteOptions;
|
|
4341
|
+
}
|
|
4342
|
+
break;
|
|
4343
|
+
|
|
4344
|
+
case 2:
|
|
4345
|
+
|
|
4346
|
+
// check if it exists, could be undef/null
|
|
4347
|
+
if (args[0] && Array.isArray(args[0])) {
|
|
4348
|
+
data = args[0] as ClipboardData;
|
|
4349
|
+
}
|
|
4350
|
+
|
|
4351
|
+
// 1 should exist if len is 2
|
|
4352
|
+
if (args[1] && typeof args[1] === 'object') {
|
|
4353
|
+
options = args[1] as PasteOptions;
|
|
4354
|
+
}
|
|
4355
|
+
break;
|
|
4356
|
+
}
|
|
4357
|
+
|
|
4358
|
+
if (!data) {
|
|
4359
|
+
throw new Error('no clipboad data');
|
|
4360
|
+
}
|
|
4361
|
+
|
|
4362
|
+
this.grid.PasteArea(
|
|
4363
|
+
this.RangeOrSelection(target, 'no range and no selection'),
|
|
4364
|
+
data,
|
|
4365
|
+
options);
|
|
4366
|
+
|
|
4367
|
+
}
|
|
4368
|
+
|
|
4369
|
+
/**
|
|
4370
|
+
* copy data. this method returns the copied data. it does not put it on
|
|
4371
|
+
* the system clipboard. this is for API access when the system clipboard
|
|
4372
|
+
* might not be available.
|
|
4373
|
+
*
|
|
4374
|
+
* @privateRemarks LLM API
|
|
4375
|
+
*/
|
|
4376
|
+
public Copy(source?: RangeReference): ClipboardData {
|
|
4377
|
+
const area = this.RangeOrSelection(source);
|
|
4378
|
+
const data: ClipboardData = area ? this.grid.CopyArea(area, 'copy') : [];
|
|
4379
|
+
EmbeddedSpreadsheet.clipboard = structuredClone(data);
|
|
4380
|
+
return data;
|
|
4381
|
+
}
|
|
4382
|
+
|
|
4383
|
+
/**
|
|
4384
|
+
* cut data. this method returns the cut data. it does not put it on the
|
|
4385
|
+
* system clipboard. this method is similar to the Copy method, with
|
|
4386
|
+
* two differences: (1) we remove the source data, effectively clearing
|
|
4387
|
+
* the source range; and (2) the clipboard data retains references, meaning
|
|
4388
|
+
* if you paste the data in a different location it will refer to the same
|
|
4389
|
+
* cells.
|
|
4390
|
+
*
|
|
4391
|
+
* @privateRemarks LLM API
|
|
4392
|
+
*/
|
|
4393
|
+
public Cut(source?: RangeReference): ClipboardData {
|
|
4394
|
+
const area = this.RangeOrSelection(source);
|
|
4395
|
+
const data: ClipboardData = area ? this.grid.CopyArea(area, 'cut') : [];
|
|
4396
|
+
EmbeddedSpreadsheet.clipboard = structuredClone(data);
|
|
4397
|
+
return data;
|
|
4398
|
+
}
|
|
4399
|
+
|
|
4292
4400
|
/**
|
|
4293
4401
|
*
|
|
4294
4402
|
* @param range target range. leave undefined to use current selection.
|
|
@@ -4299,14 +4407,11 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4299
4407
|
|
|
4300
4408
|
// API v1 OK
|
|
4301
4409
|
|
|
4302
|
-
|
|
4303
|
-
const selection = this.GetSelectionReference();
|
|
4304
|
-
if (!selection.empty) {
|
|
4305
|
-
range = selection.area;
|
|
4306
|
-
}
|
|
4307
|
-
}
|
|
4410
|
+
const resolved = this.RangeOrSelection(range);
|
|
4308
4411
|
|
|
4309
|
-
if (!
|
|
4412
|
+
if (!resolved) {
|
|
4413
|
+
return undefined;
|
|
4414
|
+
}
|
|
4310
4415
|
|
|
4311
4416
|
// handle the old flags and the precedence rule. type takes precedence.
|
|
4312
4417
|
|
|
@@ -4327,7 +4432,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4327
4432
|
type = 'A1';
|
|
4328
4433
|
}
|
|
4329
4434
|
|
|
4330
|
-
return this.grid.GetRange(
|
|
4435
|
+
return this.grid.GetRange(resolved, type);
|
|
4331
4436
|
|
|
4332
4437
|
}
|
|
4333
4438
|
|
|
@@ -4347,16 +4452,12 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4347
4452
|
|
|
4348
4453
|
// API v1 OK
|
|
4349
4454
|
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
range = selection.area;
|
|
4354
|
-
}
|
|
4455
|
+
const resolved = this.RangeOrSelection(range);
|
|
4456
|
+
if (!resolved) {
|
|
4457
|
+
return undefined;
|
|
4355
4458
|
}
|
|
4356
4459
|
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
return this.grid.GetRangeStyle(this.model.ResolveAddress(range, this.grid.active_sheet), apply_theme);
|
|
4460
|
+
return this.grid.GetRangeStyle(resolved, apply_theme);
|
|
4360
4461
|
|
|
4361
4462
|
}
|
|
4362
4463
|
|
|
@@ -4377,15 +4478,10 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4377
4478
|
|
|
4378
4479
|
// API v1 OK
|
|
4379
4480
|
|
|
4380
|
-
|
|
4381
|
-
const selection = this.GetSelectionReference();
|
|
4382
|
-
if (!selection.empty) {
|
|
4383
|
-
range = selection.area;
|
|
4384
|
-
}
|
|
4385
|
-
}
|
|
4481
|
+
const area = this.RangeOrSelection(range);
|
|
4386
4482
|
|
|
4387
|
-
if (
|
|
4388
|
-
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
4483
|
+
if (area) {
|
|
4484
|
+
// const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
4389
4485
|
|
|
4390
4486
|
if (options.spill && Array.isArray(data)) {
|
|
4391
4487
|
const rows = data.length;
|
|
@@ -4441,6 +4537,42 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4441
4537
|
|
|
4442
4538
|
// --- internal (protected) methods ------------------------------------------
|
|
4443
4539
|
|
|
4540
|
+
/**
|
|
4541
|
+
* overload returns undefined if no range and no selection
|
|
4542
|
+
*/
|
|
4543
|
+
protected RangeOrSelection(source: RangeReference|undefined): Area|undefined;
|
|
4544
|
+
|
|
4545
|
+
/**
|
|
4546
|
+
* overload throws if no range and no selection (pass the error to throw)
|
|
4547
|
+
*/
|
|
4548
|
+
protected RangeOrSelection(source: RangeReference|undefined, error: string): Area;
|
|
4549
|
+
|
|
4550
|
+
/**
|
|
4551
|
+
* this is a common pattern, start with a range reference; if it's
|
|
4552
|
+
* undefined, check selection; then resolve. if there's no selection
|
|
4553
|
+
* and no range passed in, returns undefined or if the error parameter
|
|
4554
|
+
* is set, throws.
|
|
4555
|
+
*/
|
|
4556
|
+
protected RangeOrSelection(source: RangeReference|undefined, error?: string): Area|undefined {
|
|
4557
|
+
|
|
4558
|
+
if (!source) {
|
|
4559
|
+
const selection = this.GetSelectionReference();
|
|
4560
|
+
if (!selection.empty) {
|
|
4561
|
+
return selection.area.Clone(); // Area
|
|
4562
|
+
}
|
|
4563
|
+
else {
|
|
4564
|
+
if (error) {
|
|
4565
|
+
throw error;
|
|
4566
|
+
}
|
|
4567
|
+
// throw new Error('no range and no selection');
|
|
4568
|
+
return undefined;
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
|
|
4572
|
+
return this.model.ResolveArea(source, this.grid.active_sheet);
|
|
4573
|
+
|
|
4574
|
+
}
|
|
4575
|
+
|
|
4444
4576
|
// --- moved from grid/grid base ---------------------------------------------
|
|
4445
4577
|
|
|
4446
4578
|
|
|
@@ -158,7 +158,7 @@ export class XMLUtils {
|
|
|
158
158
|
*
|
|
159
159
|
* in either case we want both "c" elements.
|
|
160
160
|
*/
|
|
161
|
-
public static FindAll(root:
|
|
161
|
+
public static FindAll(root: DOMContent = {}, path: string): any[] {
|
|
162
162
|
|
|
163
163
|
const components = path.split('/');
|
|
164
164
|
|
|
@@ -190,12 +190,12 @@ export class XMLUtils {
|
|
|
190
190
|
* basically if you see a wildcard, just treat every element as a
|
|
191
191
|
* match -- right?
|
|
192
192
|
*/
|
|
193
|
-
public static FindAllTail(root:
|
|
193
|
+
public static FindAllTail(root: DOMContent|DOMContent[], elements: string[]): DOMContent[] {
|
|
194
194
|
|
|
195
195
|
if (Array.isArray(root)) {
|
|
196
196
|
return root.reduce((composite, element) => {
|
|
197
197
|
return composite.concat(this.FindAllTail(element, elements));
|
|
198
|
-
}, []);
|
|
198
|
+
}, [] as DOMContent[]);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
for (let i = 0; i < elements.length; i++) {
|
|
@@ -209,15 +209,27 @@ export class XMLUtils {
|
|
|
209
209
|
// maps attributes and text differently... hopefully they won't use
|
|
210
210
|
// wildcards
|
|
211
211
|
|
|
212
|
+
if (element === '*') {
|
|
213
|
+
|
|
214
|
+
const pairs = Object.entries(root) as Array<[string, DOMContent]>;
|
|
215
|
+
root = pairs.reduce((result, [key, value]) => {
|
|
216
|
+
if (key !== 'a$' && key !== 't$') {
|
|
217
|
+
result.push(value);
|
|
218
|
+
}
|
|
219
|
+
return result;
|
|
220
|
+
}, [] as DOMContent[]);
|
|
221
|
+
|
|
222
|
+
/*
|
|
212
223
|
// two loops, really? come on
|
|
213
224
|
|
|
214
|
-
|
|
215
|
-
root = Object.keys(root).
|
|
225
|
+
root = Object.keys(flat).
|
|
216
226
|
filter(key => (key !== 'a$' && key !== 't$')).
|
|
217
|
-
map(key =>
|
|
227
|
+
map(key => flat[key]) as DOMContent[];
|
|
228
|
+
*/
|
|
229
|
+
|
|
218
230
|
}
|
|
219
231
|
else {
|
|
220
|
-
root = root[element];
|
|
232
|
+
root = root[element] as DOMContent;
|
|
221
233
|
}
|
|
222
234
|
|
|
223
235
|
if (!root) {
|
|
@@ -237,7 +249,7 @@ export class XMLUtils {
|
|
|
237
249
|
const step = elements.slice(1);
|
|
238
250
|
return root.reduce((composite, element) => {
|
|
239
251
|
return composite.concat(this.FindAllTail(element, step));
|
|
240
|
-
}, []);
|
|
252
|
+
}, [] as DOMContent[]);
|
|
241
253
|
|
|
242
254
|
}
|
|
243
255
|
|
package/treb-grid/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
export { Grid } from './types/grid';
|
|
23
23
|
export { GridBase } from './types/grid_base';
|
|
24
24
|
export * from './types/grid_events';
|
|
25
|
+
export * from './types/clipboard_data2';
|
|
25
26
|
export type { GridOptions } from './types/grid_options';
|
|
26
27
|
export { CommandKey } from './types/grid_command';
|
|
27
28
|
export type { Command } from './types/grid_command';
|
|
@@ -0,0 +1,85 @@
|
|
|
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 { CellValue, IArea, CellStyle } from 'treb-base-types';
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* this is a structure for copy/paste data. clipboard data may include
|
|
27
|
+
* relative formauls and resolved styles, so it's suitable for pasting into
|
|
28
|
+
* other areas of the spreadsheet.
|
|
29
|
+
*
|
|
30
|
+
* @privateRemarks
|
|
31
|
+
* work in progress. atm we're not using the system clipboard, although it
|
|
32
|
+
* might be useful to merge this with grid copy/paste routines in the future.
|
|
33
|
+
*
|
|
34
|
+
* if it hits the clipboard this should use mime type `application/x-treb-data`
|
|
35
|
+
*
|
|
36
|
+
*/
|
|
37
|
+
export interface ClipboardDataElement {
|
|
38
|
+
|
|
39
|
+
/** calculated cell value */
|
|
40
|
+
calculated: CellValue,
|
|
41
|
+
|
|
42
|
+
/** the actual cell value or formula */
|
|
43
|
+
value: CellValue,
|
|
44
|
+
|
|
45
|
+
/** cell style. this may include row/column styles from the copy source */
|
|
46
|
+
style?: CellStyle,
|
|
47
|
+
|
|
48
|
+
/** area. if this cell is part of an array, this is the array range */
|
|
49
|
+
area?: IArea,
|
|
50
|
+
|
|
51
|
+
/* TODO: merge, like area */
|
|
52
|
+
|
|
53
|
+
/* TODO: table */
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** clipboard data is a 2d array */
|
|
58
|
+
export type ClipboardData = ClipboardDataElement[][];
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* optional paste options. we can paste formulas or values, and we
|
|
62
|
+
* can use the source style, target style, or just use the source
|
|
63
|
+
* number formats.
|
|
64
|
+
*/
|
|
65
|
+
export interface PasteOptions {
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* when clipboard data includes formulas, optionally paste calculated
|
|
69
|
+
* values instead of the original formulas. defaults to false.
|
|
70
|
+
*/
|
|
71
|
+
values?: boolean;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* when pasting data from the clipboard, we can copy formatting/style
|
|
75
|
+
* from the original data, or we can retain the target range formatting
|
|
76
|
+
* and just paste data. a third option allows pasting source number
|
|
77
|
+
* formats but dropping other style information.
|
|
78
|
+
*
|
|
79
|
+
* defaults to "source", meaning paste source styles.
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
formatting?: 'source'|'target'|'number-formats'
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|