@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.
@@ -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.model.ResolveArea(range, this.grid.active_sheet),
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
- if (range === undefined) {
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 (range === undefined) {
1520
- const ref = this.GetSelectionReference();
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
- * @public
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 async Batch(func: () => void, paint = false): Promise<void> {
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
- await this.Recalculate();
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 async Recalculate(event?: GridEvent): Promise<void> {
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
- if (!range) {
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 (!range) { return undefined; }
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(this.model.ResolveAddress(range, this.grid.active_sheet), type);
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
- if (!range) {
4351
- const selection = this.GetSelectionReference();
4352
- if (!selection.empty) {
4353
- range = selection.area;
4354
- }
4455
+ const resolved = this.RangeOrSelection(range);
4456
+ if (!resolved) {
4457
+ return undefined;
4355
4458
  }
4356
4459
 
4357
- if (!range) { return undefined; }
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
- if (!range) {
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 (range) {
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: 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';
@@ -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
+