@trebco/treb 29.4.1 → 29.5.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.
@@ -45,9 +45,10 @@ import {
45
45
  ComplexToString,
46
46
  IsComplex,
47
47
  TextPartFlag,
48
+ IsArea,
48
49
  } from 'treb-base-types';
49
50
 
50
- import type { ExpressionUnit } from 'treb-parser';
51
+ import type { ExpressionUnit, UnitAddress } from 'treb-parser';
51
52
  import {
52
53
  DecimalMarkType,
53
54
  ArgumentSeparatorType,
@@ -1746,16 +1747,80 @@ export class Grid extends GridBase {
1746
1747
 
1747
1748
  }
1748
1749
 
1750
+ /**
1751
+ * render the given data as R1C1. the source address is a base for
1752
+ * rendering relative addresses (is there a case for this method
1753
+ * handling absolute offsets as well? ...)
1754
+ *
1755
+ * NOTE: this method modifies the input data (at least it does if
1756
+ * it's an array). so this method should only be called with scratchpad
1757
+ * data.
1758
+ *
1759
+ */
1760
+ public FormatR1C1(data: CellValue|CellValue[][], source: ICellAddress|IArea) {
1761
+
1762
+ // normalize
1763
+
1764
+ if (IsArea(source)) {
1765
+ source = source.start;
1766
+ }
1767
+
1768
+ if (!Array.isArray(data)) {
1769
+ data = [[data]];
1770
+ }
1771
+
1772
+ const base: UnitAddress = {
1773
+ type: 'address',
1774
+ label: '',
1775
+ row: source.row,
1776
+ column: source.column,
1777
+ sheet: source.sheet_id ? this.model.sheets.Name(source.sheet_id) : this.active_sheet.name,
1778
+ position: 0,
1779
+ id: 0,
1780
+ };
1781
+
1782
+ for (let r = 0; r < data.length; r++) {
1783
+ const row = data[r];
1784
+ for (let c = 0; c < row.length; c++) {
1785
+ const cell = row[c];
1786
+ if (typeof cell === 'string' && cell[0] === '=') {
1787
+
1788
+ const parse_result = this.parser.Parse(cell);
1789
+ if (parse_result.expression) {
1790
+ row[c] = '=' + this.parser.Render(parse_result.expression, {
1791
+ missing: '',
1792
+ r1c1: true,
1793
+ r1c1_base: {
1794
+ ...base,
1795
+ row: source.row + r,
1796
+ column: source.column + c,
1797
+ },
1798
+ });
1799
+ }
1800
+ }
1801
+ }
1802
+ }
1803
+
1804
+ return data;
1805
+
1806
+ }
1807
+
1749
1808
  /**
1750
1809
  * get data in a given range, optionally formulas
1751
1810
  * API method
1752
1811
  */
1753
- public GetRange(range: ICellAddress | IArea, type?: 'formula'|'formatted'): CellValue|CellValue[][]|undefined {
1812
+ public GetRange(range: ICellAddress | IArea, type?: 'formatted'|'A1'|'R1C1'): CellValue|CellValue[][]|undefined {
1754
1813
 
1755
1814
  if (IsCellAddress(range)) {
1756
1815
  const sheet = this.model.sheets.Find(range.sheet_id || this.active_sheet.id);
1757
1816
  if (sheet) {
1758
- if (type === 'formula') { return sheet.cells.RawValue(range); }
1817
+ if (type === 'A1' || type === 'R1C1') {
1818
+ const data = sheet.cells.RawValue(range);
1819
+ if (type === 'R1C1') {
1820
+ return this.FormatR1C1(data, range);
1821
+ }
1822
+ return data;
1823
+ }
1759
1824
  if (type === 'formatted') { return sheet.GetFormattedRange(range); }
1760
1825
  return sheet.cells.GetRange(range);
1761
1826
  }
@@ -1764,7 +1829,13 @@ export class Grid extends GridBase {
1764
1829
 
1765
1830
  const sheet = this.model.sheets.Find(range.start.sheet_id || this.active_sheet.id);
1766
1831
  if (sheet) {
1767
- if (type === 'formula') { return sheet.cells.RawValue(range.start, range.end); }
1832
+ if (type === 'A1' || type === 'R1C1') {
1833
+ const data = sheet.cells.RawValue(range.start, range.end);
1834
+ if (type === 'R1C1') {
1835
+ return this.FormatR1C1(data, range);
1836
+ }
1837
+ return data;
1838
+ }
1768
1839
  if (type === 'formatted') { return sheet.GetFormattedRange(range.start, range.end); }
1769
1840
  return sheet.cells.GetRange(range.start, range.end);
1770
1841
  }
@@ -1889,6 +1960,9 @@ export class Grid extends GridBase {
1889
1960
 
1890
1961
  if (!Is2DArray(data)) {
1891
1962
 
1963
+ // we don't allow this anymore (at least we say we don't)
1964
+
1965
+
1892
1966
  // flat array -- we can recycle. recycling is R style (values, not rows).
1893
1967
  // in any event convert to [][]
1894
1968
 
@@ -1920,6 +1994,56 @@ export class Grid extends GridBase {
1920
1994
  }
1921
1995
 
1922
1996
  }
1997
+ else {
1998
+ if (recycle) {
1999
+
2000
+ // recycle 2D array. we'll do this if the target range is a multiple
2001
+ // (or larger) of the source array. in blocks.
2002
+
2003
+ if (range.rows > data.length) {
2004
+ const recycle_rows = Math.floor(range.rows / data.length);
2005
+ if (recycle_rows > 1) {
2006
+ const source = [...data];
2007
+ for (let i = 0; i < recycle_rows; i++) {
2008
+ const clone = JSON.parse(JSON.stringify(source));
2009
+ data.push(...clone);
2010
+ }
2011
+ }
2012
+ }
2013
+
2014
+ let cols = 0;
2015
+ for (const row of data) {
2016
+ cols = Math.max(cols, row.length);
2017
+ }
2018
+
2019
+ if (range.columns > cols) {
2020
+ const recycle_columns = Math.floor(range.columns / cols);
2021
+ if (recycle_columns > 1) {
2022
+
2023
+ for (const row of data) {
2024
+
2025
+ // pad out all rows first, jic
2026
+ while (row.length < cols) {
2027
+ row.push(undefined);
2028
+ }
2029
+
2030
+ // now recycle
2031
+ const source = [...row];
2032
+ for (let i = 0; i < recycle_columns; i++) {
2033
+ const clone = JSON.parse(JSON.stringify(source));
2034
+ row.push(...clone);
2035
+ }
2036
+
2037
+ }
2038
+
2039
+ // ...
2040
+
2041
+ // console.info({recycle_columns});
2042
+ }
2043
+ }
2044
+
2045
+ }
2046
+ }
1923
2047
 
1924
2048
  if (transpose) { data = this.Transpose(data); }
1925
2049
 
@@ -2224,12 +2348,13 @@ export class Grid extends GridBase {
2224
2348
  // const context = Sheet.measurement_canvas.getContext('2d');
2225
2349
  // if (!context) return;
2226
2350
 
2227
- let width = 12;
2228
- const padding = 4 * 2; // FIXME: parameterize
2351
+ let width = 0;
2352
+ const padding = 12; // 4 * 2; // FIXME: parameterize
2229
2353
 
2230
2354
  if (!allow_shrink) width = sheet.GetColumnWidth(column);
2231
2355
 
2232
2356
  for (let row = 0; row < sheet.cells.rows; row++) {
2357
+
2233
2358
  const cell = sheet.CellData({ row, column });
2234
2359
  let text = cell.formatted || '';
2235
2360
  if (typeof text !== 'string') {
@@ -2246,7 +2371,9 @@ export class Grid extends GridBase {
2246
2371
  }
2247
2372
  }
2248
2373
 
2249
- sheet.SetColumnWidth(column, width);
2374
+ if (width > padding + 2) {
2375
+ sheet.SetColumnWidth(column, width);
2376
+ }
2250
2377
 
2251
2378
  }
2252
2379
 
@@ -7141,7 +7268,7 @@ export class Grid extends GridBase {
7141
7268
 
7142
7269
  if (auto) {
7143
7270
  for (const entry of column) {
7144
- this.AutoSizeColumn(sheet, entry, false);
7271
+ this.AutoSizeColumn(sheet, entry, true);
7145
7272
  }
7146
7273
  }
7147
7274
  else {
@@ -593,7 +593,7 @@ export class GridBase {
593
593
  * @param column column, columns, or undefined means all columns
594
594
  * @param width target width, or undefined means auto-size
595
595
  */
596
- public SetColumnWidth(column?: number | number[], width = 0): void {
596
+ public SetColumnWidth(column?: number | number[], width?: number): void {
597
597
  this.ExecCommand({
598
598
  key: CommandKey.ResizeColumns,
599
599
  column,
@@ -2514,11 +2514,20 @@ export class GridBase {
2514
2514
 
2515
2515
  // translate...
2516
2516
  if (unit.offset_column) {
2517
+ unit.absolute_column = false;
2517
2518
  unit.column = unit.column + address.column;
2518
2519
  }
2520
+ else {
2521
+ unit.absolute_column = true;
2522
+ }
2523
+
2519
2524
  if (unit.offset_row) {
2525
+ unit.absolute_row = false;
2520
2526
  unit.row = unit.row + address.row;
2521
2527
  }
2528
+ else {
2529
+ unit.absolute_row = true;
2530
+ }
2522
2531
 
2523
2532
  }
2524
2533
  return true;
@@ -401,6 +401,16 @@ export type ParserFlags = Partial<OptionalParserFlags> & RequiredParserFlags;
401
401
 
402
402
  export interface RenderOptions {
403
403
  offset: { rows: number; columns: number };
404
+
405
+ /**
406
+ * render in R1C1 format. this will be relative if the R1C1 base
407
+ * address is set; otherwise absolute.
408
+ */
409
+ r1c1?: boolean;
410
+
411
+ /** base for offsetting relative R1C1 addresses */
412
+ r1c1_base?: UnitAddress;
413
+
404
414
  missing: string;
405
415
  convert_decimal: DecimalMarkType;
406
416
  convert_argument_separator: ArgumentSeparatorType;
@@ -489,14 +489,12 @@ export class Parser {
489
489
 
490
490
  switch (unit.type) {
491
491
  case 'address':
492
- return this.AddressLabel(unit, offset);
492
+ return options.r1c1 ? this.R1C1Label(unit, options.r1c1_base) : this.AddressLabel(unit, offset);
493
493
 
494
494
  case 'range':
495
- return (
496
- this.AddressLabel(unit.start, offset) +
497
- ':' +
498
- this.AddressLabel(unit.end, offset)
499
- );
495
+ return options.r1c1 ?
496
+ this.R1C1Label(unit.start, options.r1c1_base) + ':' + this.R1C1Label(unit.end, options.r1c1_base) :
497
+ this.AddressLabel(unit.start, offset) + ':' + this.AddressLabel(unit.end, offset);
500
498
 
501
499
  case 'missing':
502
500
  return missing;
@@ -823,7 +821,35 @@ export class Parser {
823
821
  return s;
824
822
  }
825
823
 
826
- /** generates address label ("C3") from address (0-based) */
824
+ /**
825
+ * generates absolute or relative R1C1 address
826
+ */
827
+ protected R1C1Label(
828
+ address: UnitAddress,
829
+ base?: UnitAddress,
830
+ ): string {
831
+
832
+ let label = '';
833
+
834
+ if (address.sheet) { // && (!base?.sheet || base?.sheet !== address.sheet)) {
835
+ label = (QuotedSheetNameRegex.test(address.sheet) ?
836
+ '\'' + address.sheet + '\'' : address.sheet) + '!';
837
+ }
838
+
839
+ const row = (address.absolute_row || !base) ? (address.row + 1).toString() : `[${address.row - base.row}]`;
840
+ const column = (address.absolute_column || !base) ? (address.column + 1).toString() : `[${address.column - base.column}]`;
841
+
842
+ label += `R${row}C${column}`;
843
+
844
+ return label;
845
+ }
846
+
847
+ /**
848
+ * generates address label ("C3") from address (0-based).
849
+ *
850
+ * @param offset - offset by some number of rows or columns
851
+ * @param r1c1 - if set, return data in R1C1 format.
852
+ */
827
853
  protected AddressLabel(
828
854
  address: UnitAddress,
829
855
  offset: { rows: number; columns: number },
@@ -839,12 +865,6 @@ export class Parser {
839
865
  let label = '';
840
866
  if (address.sheet) {
841
867
 
842
- /*
843
- if (address.sheet === '__REF') {
844
- return '#REF'; // magic
845
- }
846
- */
847
-
848
868
  label = (QuotedSheetNameRegex.test(address.sheet) ?
849
869
  '\'' + address.sheet + '\'' : address.sheet) + '!';
850
870
  }