@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.
@@ -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.model.ResolveArea(range, this.grid.active_sheet),
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
- if (range === undefined) {
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 (range === undefined) {
1577
- const ref = this.GetSelectionReference();
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 async Batch(func: () => void, paint = false): Promise<void> {
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
- await this.Recalculate();
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 async Recalculate(event?: GridEvent): Promise<void> {
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 async Paste(target?: RangeReference, data = EmbeddedSpreadsheet.clipboard, options: PasteOptions = {}) {
4324
+ public Paste(target?: RangeReference, ...args: unknown[]): void {
4385
4325
 
4386
- if (!data) {
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
- if (!target) {
4391
- const selection = this.GetSelectionReference();
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
- const resolved = this.model.ResolveArea(target, this.grid.active_sheet);
4331
+ switch (args.length) {
4332
+ case 1:
4401
4333
 
4402
- // paste has some special semantics. if the target smaller than the
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
- // the setrange method will recycle, but we also need to recycle styles.
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
- // number format only, and apply delta
4344
+ case 2:
4453
4345
 
4454
- for (const address of resolved) {
4455
- const r = (address.row - resolved.start.row) % rows;
4456
- const c = (address.column - resolved.start.column) % columns;
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
- // use source formatting (default)
4464
- for (const address of resolved) {
4465
- const r = (address.row - resolved.start.row) % rows;
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
- }, true);
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
- return this.CopyInternal(source, 'copy');
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
- return this.CopyInternal(source, 'cut');
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
- if (!range) {
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 (!range) { return undefined; }
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(this.model.ResolveAddress(range, this.grid.active_sheet), type);
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
- if (!range) {
4561
- const selection = this.GetSelectionReference();
4562
- if (!selection.empty) {
4563
- range = selection.area;
4564
- }
4455
+ const resolved = this.RangeOrSelection(range);
4456
+ if (!resolved) {
4457
+ return undefined;
4565
4458
  }
4566
4459
 
4567
- if (!range) { return undefined; }
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
- if (!range) {
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 (range) {
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
- * internal composite for cut/copy. mostly identical except we
4656
- * read data as A1 for cut, so it will retain references. also
4657
- * cut clears the data.
4658
- *
4659
- * FIXME: merge with grid cut/copy/paste routines. we already
4660
- * handle recycling and relative addressing, the only thing missing
4661
- * is alternate formats.
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 CopyInternal(source?: RangeReference, semantics: 'cut'|'copy' = 'copy'): ClipboardData {
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
- source = selection.area;
4561
+ return selection.area.Clone(); // Area
4669
4562
  }
4670
4563
  else {
4671
- throw new Error('no range and no selection');
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
- // resolve range so we can use it later -> Area
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
- if (semantics === 'cut') {
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: any = {}, path: string): any[] {
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: any, elements: string[]): any[] {
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
- if (element === '*') {
215
- root = Object.keys(root).
225
+ root = Object.keys(flat).
216
226
  filter(key => (key !== 'a$' && key !== 't$')).
217
- map(key => root[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
 
@@ -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';