@trebco/treb 29.6.2 → 29.8.1
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 +10 -10
- package/dist/treb-spreadsheet.mjs +15 -15
- package/dist/treb.d.ts +93 -87
- 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 +36 -30
- 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/regex-functions.ts +205 -0
- package/treb-charts/src/util.ts +3 -36
- package/treb-data-model/src/sheet.ts +7 -3
- package/treb-embed/src/embedded-spreadsheet.ts +128 -267
- 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 +226 -6
- package/treb-parser/src/parser-types.ts +3 -0
- package/treb-parser/src/parser.ts +86 -2
- package/tsproject.json +1 -1
|
@@ -2225,8 +2225,14 @@ export class Sheet {
|
|
|
2225
2225
|
return this.CompositeStyleForCell(area, true, false, apply_theme);
|
|
2226
2226
|
}
|
|
2227
2227
|
|
|
2228
|
+
// the contract says this should return an array, not a single value.
|
|
2229
|
+
//
|
|
2230
|
+
// I can fix it, but will anyone break? (...) check the indent buttons
|
|
2231
|
+
// (update: looks OK)
|
|
2232
|
+
//
|
|
2233
|
+
|
|
2228
2234
|
if (area.start.row === area.end.row && area.start.column === area.end.column) {
|
|
2229
|
-
return this.CompositeStyleForCell(area.start, true, false, apply_theme);
|
|
2235
|
+
return [[this.CompositeStyleForCell(area.start, true, false, apply_theme)]];
|
|
2230
2236
|
}
|
|
2231
2237
|
|
|
2232
2238
|
const result: CellStyle[][] = [];
|
|
@@ -2234,8 +2240,6 @@ export class Sheet {
|
|
|
2234
2240
|
for (let r = area.start.row; r <= area.end.row; r++) {
|
|
2235
2241
|
const row: CellStyle[] = [];
|
|
2236
2242
|
for (let c = area.start.column; c <= area.end.column; c++) {
|
|
2237
|
-
// const cell = this.CellData({row: r, column: c});
|
|
2238
|
-
// row.push(cell.style || {});
|
|
2239
2243
|
row.push(this.CompositeStyleForCell({row: r, column: c}, true, false, apply_theme));
|
|
2240
2244
|
}
|
|
2241
2245
|
result.push(row);
|
|
@@ -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
|
|
|
@@ -114,60 +114,6 @@ import * as export_worker_script from 'worker:../../treb-export/src/export-worke
|
|
|
114
114
|
|
|
115
115
|
// --- types -------------------------------------------------------------------
|
|
116
116
|
|
|
117
|
-
/**
|
|
118
|
-
* this is a structure for copy/paste data. clipboard data may include
|
|
119
|
-
* relative formauls and resolved styles, so it's suitable for pasting into
|
|
120
|
-
* other areas of the spreadsheet.
|
|
121
|
-
*
|
|
122
|
-
* @privateRemarks
|
|
123
|
-
* work in progress. atm we're not using the system clipboard, although it
|
|
124
|
-
* might be useful to merge this with grid copy/paste routines in the future.
|
|
125
|
-
*
|
|
126
|
-
* if it hits the clipboard this should use mime type `application/x-treb-data`
|
|
127
|
-
*
|
|
128
|
-
*/
|
|
129
|
-
export interface ClipboardDataElement {
|
|
130
|
-
|
|
131
|
-
/** calculated cell value */
|
|
132
|
-
calculated: CellValue,
|
|
133
|
-
|
|
134
|
-
/** the actual cell value or formula */
|
|
135
|
-
value: CellValue,
|
|
136
|
-
|
|
137
|
-
/** cell style. this may include row/column styles from the copy source */
|
|
138
|
-
style?: CellStyle,
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** clipboard data is a 2d array */
|
|
143
|
-
export type ClipboardData = ClipboardDataElement[][];
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* optional paste options. we can paste formulas or values, and we
|
|
147
|
-
* can use the source style, target style, or just use the source
|
|
148
|
-
* number formats.
|
|
149
|
-
*/
|
|
150
|
-
export interface PasteOptions {
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* when clipboard data includes formulas, optionally paste calculated
|
|
154
|
-
* values instead of the original formulas. defaults to false.
|
|
155
|
-
*/
|
|
156
|
-
values?: boolean;
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* when pasting data from the clipboard, we can copy formatting/style
|
|
160
|
-
* from the original data, or we can retain the target range formatting
|
|
161
|
-
* and just paste data. a third option allows pasting source number
|
|
162
|
-
* formats but dropping other style information.
|
|
163
|
-
*
|
|
164
|
-
* defaults to "source", meaning paste source styles.
|
|
165
|
-
*/
|
|
166
|
-
|
|
167
|
-
formatting?: 'source'|'target'|'number-formats'
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
117
|
/**
|
|
172
118
|
* options for saving files. we add the option for JSON formatting.
|
|
173
119
|
*/
|
|
@@ -1000,10 +946,20 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1000
946
|
|
|
1001
947
|
const cached_selection = this.last_selection;
|
|
1002
948
|
|
|
949
|
+
/*
|
|
1003
950
|
((this.calculation === CalculationOptions.automatic) ?
|
|
1004
951
|
this.Recalculate(event) : Promise.resolve()).then(() => {
|
|
1005
952
|
this.DocumentChange(cached_selection);
|
|
1006
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);
|
|
1007
963
|
|
|
1008
964
|
/*
|
|
1009
965
|
if (this.calculation === CalculationOptions.automatic) {
|
|
@@ -1084,10 +1040,19 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1084
1040
|
if (event.rebuild_required) {
|
|
1085
1041
|
this.calculator.Reset();
|
|
1086
1042
|
|
|
1043
|
+
/*
|
|
1087
1044
|
((this.calculation === CalculationOptions.automatic) ?
|
|
1088
1045
|
this.Recalculate(event) : Promise.resolve()).then(() => {
|
|
1089
1046
|
this.DocumentChange(cached_selection);
|
|
1090
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);
|
|
1091
1056
|
|
|
1092
1057
|
}
|
|
1093
1058
|
else {
|
|
@@ -1444,18 +1409,10 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1444
1409
|
|
|
1445
1410
|
/** @internal */
|
|
1446
1411
|
public ConditionalFormatDuplicateValues(range: RangeReference|undefined, options: ConditionalFormatDuplicateValuesOptions): ConditionalFormat {
|
|
1447
|
-
|
|
1448
|
-
if (range === undefined) {
|
|
1449
|
-
const ref = this.GetSelectionReference();
|
|
1450
|
-
if (ref.empty) {
|
|
1451
|
-
throw new Error('invalid range (no selection)');
|
|
1452
|
-
}
|
|
1453
|
-
range = ref.area;
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1412
|
+
|
|
1456
1413
|
return this.AddConditionalFormat({
|
|
1457
1414
|
type: 'duplicate-values',
|
|
1458
|
-
area: this.
|
|
1415
|
+
area: this.RangeOrSelection(range, 'invalid range (no selection)'),
|
|
1459
1416
|
...options,
|
|
1460
1417
|
});
|
|
1461
1418
|
|
|
@@ -1466,15 +1423,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1466
1423
|
*/
|
|
1467
1424
|
public ConditionalFormatGradient(range: RangeReference|undefined, options: ConditionalFormatGradientOptions|StandardGradient): ConditionalFormat {
|
|
1468
1425
|
|
|
1469
|
-
|
|
1470
|
-
const ref = this.GetSelectionReference();
|
|
1471
|
-
if (ref.empty) {
|
|
1472
|
-
throw new Error('invalid range (no selection)');
|
|
1473
|
-
}
|
|
1474
|
-
range = ref.area;
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1426
|
+
const area = this.RangeOrSelection(range, 'invalid range (no selection)');
|
|
1478
1427
|
|
|
1479
1428
|
const format: ConditionalFormatGradient = (typeof options === 'object') ?
|
|
1480
1429
|
{
|
|
@@ -1492,19 +1441,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1492
1441
|
/** @internal */
|
|
1493
1442
|
public ConditionalFormatCellMatch(range: RangeReference|undefined, options: ConditionalFormatCellMatchOptions): ConditionalFormat {
|
|
1494
1443
|
|
|
1495
|
-
if (range === undefined) {
|
|
1496
|
-
const ref = this.GetSelectionReference();
|
|
1497
|
-
if (ref.empty) {
|
|
1498
|
-
throw new Error('invalid range (no selection)');
|
|
1499
|
-
}
|
|
1500
|
-
range = ref.area;
|
|
1501
|
-
}
|
|
1502
|
-
|
|
1503
|
-
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1504
|
-
|
|
1505
1444
|
const format: ConditionalFormatCellMatch = {
|
|
1506
1445
|
type: 'cell-match',
|
|
1507
|
-
area,
|
|
1446
|
+
area: this.RangeOrSelection(range, 'invalid range (no selection)'),
|
|
1508
1447
|
...options,
|
|
1509
1448
|
};
|
|
1510
1449
|
|
|
@@ -1519,19 +1458,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1519
1458
|
*/
|
|
1520
1459
|
public ConditionalFormatExpression(range: RangeReference|undefined, options: CondifionalFormatExpressionOptions): ConditionalFormat {
|
|
1521
1460
|
|
|
1522
|
-
if (range === undefined) {
|
|
1523
|
-
const ref = this.GetSelectionReference();
|
|
1524
|
-
if (ref.empty) {
|
|
1525
|
-
throw new Error('invalid range (no selection)');
|
|
1526
|
-
}
|
|
1527
|
-
range = ref.area;
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
|
-
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1531
|
-
|
|
1532
1461
|
const format: ConditionalFormatExpression = {
|
|
1533
1462
|
type: 'expression',
|
|
1534
|
-
area,
|
|
1463
|
+
area: this.RangeOrSelection(range, 'invalid range (no selection)'),
|
|
1535
1464
|
...options,
|
|
1536
1465
|
};
|
|
1537
1466
|
|
|
@@ -1572,18 +1501,10 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1572
1501
|
* @internal
|
|
1573
1502
|
*/
|
|
1574
1503
|
public RemoveConditionalFormats(range?: RangeReference) {
|
|
1575
|
-
|
|
1576
|
-
if (
|
|
1577
|
-
|
|
1578
|
-
if (ref.empty) {
|
|
1579
|
-
throw new Error('invalid range (no selection)');
|
|
1580
|
-
}
|
|
1581
|
-
range = ref.area;
|
|
1504
|
+
const area = this.RangeOrSelection(range);
|
|
1505
|
+
if (area) {
|
|
1506
|
+
this.grid.RemoveConditionalFormat({ area });
|
|
1582
1507
|
}
|
|
1583
|
-
|
|
1584
|
-
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
1585
|
-
this.grid.RemoveConditionalFormat({ area });
|
|
1586
|
-
|
|
1587
1508
|
}
|
|
1588
1509
|
|
|
1589
1510
|
//////////////////////////////////////////////////////////////////////////////
|
|
@@ -2086,7 +2007,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2086
2007
|
* nested calls, because nothing will happen until the last one is complete.
|
|
2087
2008
|
*
|
|
2088
2009
|
*/
|
|
2089
|
-
public
|
|
2010
|
+
public Batch(func: () => void, paint = false): void {
|
|
2090
2011
|
|
|
2091
2012
|
// API v1 OK
|
|
2092
2013
|
|
|
@@ -2118,7 +2039,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2118
2039
|
}
|
|
2119
2040
|
|
|
2120
2041
|
if (recalc || reset) {
|
|
2121
|
-
|
|
2042
|
+
this.Recalculate();
|
|
2122
2043
|
this.DocumentChange(cached_selection);
|
|
2123
2044
|
}
|
|
2124
2045
|
|
|
@@ -3816,10 +3737,12 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
3816
3737
|
*
|
|
3817
3738
|
* the event parameter should not be used if this is called
|
|
3818
3739
|
* as an API function, remove it from typings
|
|
3819
|
-
*
|
|
3740
|
+
*
|
|
3741
|
+
* why is this async? it doesn't do anything async.
|
|
3742
|
+
*
|
|
3820
3743
|
* @public
|
|
3821
3744
|
*/
|
|
3822
|
-
public
|
|
3745
|
+
public Recalculate(event?: GridEvent) {
|
|
3823
3746
|
|
|
3824
3747
|
// API v1 OK
|
|
3825
3748
|
|
|
@@ -4366,111 +4289,80 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4366
4289
|
|
|
4367
4290
|
}
|
|
4368
4291
|
|
|
4292
|
+
/**
|
|
4293
|
+
* override for paste method omits the data parameter.
|
|
4294
|
+
*/
|
|
4295
|
+
public Paste(target?: RangeReference, options?: PasteOptions): void;
|
|
4296
|
+
|
|
4369
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.
|
|
4370
4310
|
*
|
|
4371
4311
|
* @param target - the target to paste data into. this can be larger
|
|
4372
4312
|
* than the clipboard data, in which case values will be recycled in
|
|
4373
4313
|
* blocks. if the target is smaller than the source data, we will expand
|
|
4374
|
-
* it.
|
|
4314
|
+
* it to fit the data.
|
|
4375
4315
|
*
|
|
4376
4316
|
* @param data - clipboard data to paste.
|
|
4377
4317
|
*
|
|
4378
|
-
* @param style - optional paste style. default is to paste formulas and
|
|
4379
|
-
* source formatting. paste options can be usef to paste values, values
|
|
4380
|
-
* and number formats, or retain the target formatting.
|
|
4381
|
-
*
|
|
4382
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.
|
|
4383
4323
|
*/
|
|
4384
|
-
public
|
|
4324
|
+
public Paste(target?: RangeReference, ...args: unknown[]): void {
|
|
4385
4325
|
|
|
4386
|
-
|
|
4387
|
-
throw new Error('no clipboad data');
|
|
4388
|
-
}
|
|
4326
|
+
// support for the dynamic overload (not sure that's actually a good idea)
|
|
4389
4327
|
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
if (!selection.empty) {
|
|
4393
|
-
target = selection.area;
|
|
4394
|
-
}
|
|
4395
|
-
else {
|
|
4396
|
-
throw new Error('no range and no selection');
|
|
4397
|
-
}
|
|
4398
|
-
}
|
|
4328
|
+
let data = EmbeddedSpreadsheet.clipboard; // default to "global" clipboard
|
|
4329
|
+
let options: PasteOptions = {}; // default
|
|
4399
4330
|
|
|
4400
|
-
|
|
4331
|
+
switch (args.length) {
|
|
4332
|
+
case 1:
|
|
4401
4333
|
|
|
4402
|
-
|
|
4403
|
-
// source data, we write the full data irrespective of size (similar
|
|
4404
|
-
// to "spill"). otherwise, we recycle in blocks.
|
|
4334
|
+
// could be either
|
|
4405
4335
|
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
// start with data length
|
|
4409
|
-
|
|
4410
|
-
const rows = data.length;
|
|
4411
|
-
const columns = data[0]?.length || 0;
|
|
4412
|
-
|
|
4413
|
-
// target -> block size
|
|
4414
|
-
|
|
4415
|
-
resolved.Resize(
|
|
4416
|
-
Math.max(1, Math.floor(resolved.rows / rows)) * rows,
|
|
4417
|
-
Math.max(1, Math.floor(resolved.columns / columns)) * columns );
|
|
4418
|
-
|
|
4419
|
-
const sheet = (resolved.start.sheet_id ? this.model.sheets.Find(resolved.start.sheet_id) : this.grid.active_sheet) || this.grid.active_sheet;
|
|
4420
|
-
|
|
4421
|
-
const values: CellValue[][] = [];
|
|
4422
|
-
|
|
4423
|
-
// optionally collect calculated values, instead of raw values
|
|
4424
|
-
|
|
4425
|
-
if (options.values) {
|
|
4426
|
-
for (const [index, row] of data.entries()) {
|
|
4427
|
-
values[index] = [];
|
|
4428
|
-
for (const cell of row) {
|
|
4429
|
-
values[index].push(cell.calculated);
|
|
4336
|
+
if (Array.isArray(args[0])) {
|
|
4337
|
+
data = args[0] as ClipboardData;
|
|
4430
4338
|
}
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
else {
|
|
4434
|
-
for (const [index, row] of data.entries()) {
|
|
4435
|
-
values[index] = [];
|
|
4436
|
-
for (const cell of row) {
|
|
4437
|
-
values[index].push(cell.value);
|
|
4339
|
+
else if (typeof args[0] === 'object') {
|
|
4340
|
+
options = args[0] as PasteOptions;
|
|
4438
4341
|
}
|
|
4439
|
-
|
|
4440
|
-
}
|
|
4441
|
-
|
|
4442
|
-
// batch to limit events, sync up undo
|
|
4443
|
-
|
|
4444
|
-
return this.Batch(() => {
|
|
4445
|
-
|
|
4446
|
-
this.grid.SetRange(resolved, values, {
|
|
4447
|
-
r1c1: true, recycle: true,
|
|
4448
|
-
});
|
|
4449
|
-
|
|
4450
|
-
if (options.formatting === 'number-formats') {
|
|
4342
|
+
break;
|
|
4451
4343
|
|
|
4452
|
-
|
|
4344
|
+
case 2:
|
|
4453
4345
|
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
const number_format = (data[r][c].style || {}).number_format;
|
|
4458
|
-
sheet.UpdateCellStyle(address, { number_format }, true);
|
|
4346
|
+
// check if it exists, could be undef/null
|
|
4347
|
+
if (args[0] && Array.isArray(args[0])) {
|
|
4348
|
+
data = args[0] as ClipboardData;
|
|
4459
4349
|
}
|
|
4460
|
-
}
|
|
4461
|
-
else if (options.formatting !== 'target') {
|
|
4462
4350
|
|
|
4463
|
-
//
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
const c = (address.column - resolved.start.column) % columns;
|
|
4467
|
-
sheet.UpdateCellStyle(address, data[r][c].style || {}, false);
|
|
4351
|
+
// 1 should exist if len is 2
|
|
4352
|
+
if (args[1] && typeof args[1] === 'object') {
|
|
4353
|
+
options = args[1] as PasteOptions;
|
|
4468
4354
|
}
|
|
4355
|
+
break;
|
|
4356
|
+
}
|
|
4357
|
+
|
|
4358
|
+
if (!data) {
|
|
4359
|
+
throw new Error('no clipboad data');
|
|
4360
|
+
}
|
|
4469
4361
|
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4362
|
+
this.grid.PasteArea(
|
|
4363
|
+
this.RangeOrSelection(target, 'no range and no selection'),
|
|
4364
|
+
data,
|
|
4365
|
+
options);
|
|
4474
4366
|
|
|
4475
4367
|
}
|
|
4476
4368
|
|
|
@@ -4482,7 +4374,10 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4482
4374
|
* @privateRemarks LLM API
|
|
4483
4375
|
*/
|
|
4484
4376
|
public Copy(source?: RangeReference): ClipboardData {
|
|
4485
|
-
|
|
4377
|
+
const area = this.RangeOrSelection(source);
|
|
4378
|
+
const data: ClipboardData = area ? this.grid.CopyArea(area, 'copy') : [];
|
|
4379
|
+
EmbeddedSpreadsheet.clipboard = structuredClone(data);
|
|
4380
|
+
return data;
|
|
4486
4381
|
}
|
|
4487
4382
|
|
|
4488
4383
|
/**
|
|
@@ -4496,7 +4391,10 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4496
4391
|
* @privateRemarks LLM API
|
|
4497
4392
|
*/
|
|
4498
4393
|
public Cut(source?: RangeReference): ClipboardData {
|
|
4499
|
-
|
|
4394
|
+
const area = this.RangeOrSelection(source);
|
|
4395
|
+
const data: ClipboardData = area ? this.grid.CopyArea(area, 'cut') : [];
|
|
4396
|
+
EmbeddedSpreadsheet.clipboard = structuredClone(data);
|
|
4397
|
+
return data;
|
|
4500
4398
|
}
|
|
4501
4399
|
|
|
4502
4400
|
/**
|
|
@@ -4509,14 +4407,11 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4509
4407
|
|
|
4510
4408
|
// API v1 OK
|
|
4511
4409
|
|
|
4512
|
-
|
|
4513
|
-
const selection = this.GetSelectionReference();
|
|
4514
|
-
if (!selection.empty) {
|
|
4515
|
-
range = selection.area;
|
|
4516
|
-
}
|
|
4517
|
-
}
|
|
4410
|
+
const resolved = this.RangeOrSelection(range);
|
|
4518
4411
|
|
|
4519
|
-
if (!
|
|
4412
|
+
if (!resolved) {
|
|
4413
|
+
return undefined;
|
|
4414
|
+
}
|
|
4520
4415
|
|
|
4521
4416
|
// handle the old flags and the precedence rule. type takes precedence.
|
|
4522
4417
|
|
|
@@ -4537,7 +4432,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4537
4432
|
type = 'A1';
|
|
4538
4433
|
}
|
|
4539
4434
|
|
|
4540
|
-
return this.grid.GetRange(
|
|
4435
|
+
return this.grid.GetRange(resolved, type);
|
|
4541
4436
|
|
|
4542
4437
|
}
|
|
4543
4438
|
|
|
@@ -4557,16 +4452,12 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4557
4452
|
|
|
4558
4453
|
// API v1 OK
|
|
4559
4454
|
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
range = selection.area;
|
|
4564
|
-
}
|
|
4455
|
+
const resolved = this.RangeOrSelection(range);
|
|
4456
|
+
if (!resolved) {
|
|
4457
|
+
return undefined;
|
|
4565
4458
|
}
|
|
4566
4459
|
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
return this.grid.GetRangeStyle(this.model.ResolveAddress(range, this.grid.active_sheet), apply_theme);
|
|
4460
|
+
return this.grid.GetRangeStyle(resolved, apply_theme);
|
|
4570
4461
|
|
|
4571
4462
|
}
|
|
4572
4463
|
|
|
@@ -4587,15 +4478,10 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4587
4478
|
|
|
4588
4479
|
// API v1 OK
|
|
4589
4480
|
|
|
4590
|
-
|
|
4591
|
-
const selection = this.GetSelectionReference();
|
|
4592
|
-
if (!selection.empty) {
|
|
4593
|
-
range = selection.area;
|
|
4594
|
-
}
|
|
4595
|
-
}
|
|
4481
|
+
const area = this.RangeOrSelection(range);
|
|
4596
4482
|
|
|
4597
|
-
if (
|
|
4598
|
-
const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
4483
|
+
if (area) {
|
|
4484
|
+
// const area = this.model.ResolveArea(range, this.grid.active_sheet);
|
|
4599
4485
|
|
|
4600
4486
|
if (options.spill && Array.isArray(data)) {
|
|
4601
4487
|
const rows = data.length;
|
|
@@ -4652,65 +4538,40 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4652
4538
|
// --- internal (protected) methods ------------------------------------------
|
|
4653
4539
|
|
|
4654
4540
|
/**
|
|
4655
|
-
*
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
*
|
|
4661
|
-
|
|
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.
|
|
4662
4555
|
*/
|
|
4663
|
-
protected
|
|
4556
|
+
protected RangeOrSelection(source: RangeReference|undefined, error?: string): Area|undefined {
|
|
4664
4557
|
|
|
4665
4558
|
if (!source) {
|
|
4666
4559
|
const selection = this.GetSelectionReference();
|
|
4667
4560
|
if (!selection.empty) {
|
|
4668
|
-
|
|
4561
|
+
return selection.area.Clone(); // Area
|
|
4669
4562
|
}
|
|
4670
4563
|
else {
|
|
4671
|
-
|
|
4564
|
+
if (error) {
|
|
4565
|
+
throw error;
|
|
4566
|
+
}
|
|
4567
|
+
// throw new Error('no range and no selection');
|
|
4568
|
+
return undefined;
|
|
4672
4569
|
}
|
|
4673
4570
|
}
|
|
4674
4571
|
|
|
4675
|
-
|
|
4676
|
-
const resolved = this.model.ResolveArea(source, this.grid.active_sheet);
|
|
4677
|
-
const sheet = (resolved.start.sheet_id ? this.model.sheets.Find(resolved.start.sheet_id) : this.grid.active_sheet) || this.grid.active_sheet;
|
|
4678
|
-
|
|
4679
|
-
// get cell data as R1C1 for copy but A1 for cut.
|
|
4680
|
-
const r1c1 = this.grid.GetRange(resolved, semantics === 'cut' ? 'A1' : 'R1C1') as CellValue[][];
|
|
4681
|
-
|
|
4682
|
-
// get style data, !apply theme but do apply r/c styles
|
|
4683
|
-
const styles = sheet.GetCellStyle(resolved, false);
|
|
4684
|
-
|
|
4685
|
-
// get calculated values
|
|
4686
|
-
let calculated = sheet.cells.GetRange(resolved.start, resolved.end);
|
|
4687
|
-
if (!Array.isArray(calculated)) {
|
|
4688
|
-
calculated = [[calculated]];
|
|
4689
|
-
}
|
|
4690
|
-
|
|
4691
|
-
const data: ClipboardData = [];
|
|
4692
|
-
|
|
4693
|
-
for (const [r, row] of r1c1.entries()) {
|
|
4694
|
-
data[r] = [];
|
|
4695
|
-
for (const [c, value] of row.entries()) {
|
|
4696
|
-
|
|
4697
|
-
data[r][c] = {
|
|
4698
|
-
value,
|
|
4699
|
-
calculated: calculated[r][c],
|
|
4700
|
-
style: styles[r]?.[c],
|
|
4701
|
-
};
|
|
4702
|
-
}
|
|
4703
|
-
}
|
|
4704
|
-
|
|
4705
|
-
EmbeddedSpreadsheet.clipboard = structuredClone(data);
|
|
4572
|
+
return this.model.ResolveArea(source, this.grid.active_sheet);
|
|
4706
4573
|
|
|
4707
|
-
|
|
4708
|
-
this.grid.SetRange(resolved, undefined, { recycle: true }); // clear
|
|
4709
|
-
}
|
|
4710
|
-
|
|
4711
|
-
return data;
|
|
4712
|
-
|
|
4713
|
-
}
|
|
4574
|
+
}
|
|
4714
4575
|
|
|
4715
4576
|
// --- moved from grid/grid base ---------------------------------------------
|
|
4716
4577
|
|
|
@@ -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';
|