@trebco/treb 29.4.1 → 29.5.0
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 +11 -11
- package/dist/treb-spreadsheet.mjs +11 -11
- package/dist/treb.d.ts +34 -17
- package/package.json +1 -1
- package/treb-base-types/src/gradient.ts +2 -2
- package/treb-base-types/src/style.ts +79 -6
- package/treb-base-types/src/theme.ts +24 -15
- package/treb-calculator/src/functions/sparkline.ts +2 -2
- package/treb-data-model/src/sheet.ts +14 -15
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +7 -3
- package/treb-embed/src/embedded-spreadsheet.ts +29 -13
- package/treb-embed/src/types.ts +9 -0
- package/treb-export/src/drawing2/chart2.ts +20 -38
- package/treb-export/src/drawing2/drawing2.ts +2 -107
- package/treb-export/src/export-worker/export-worker.ts +1 -1
- package/treb-export/src/{export2.ts → export.ts} +426 -628
- package/treb-export/src/import2.ts +6 -15
- package/treb-export/src/workbook-style2.ts +16 -14
- package/treb-export/src/workbook2.ts +2 -18
- package/treb-export/src/xml-utils.ts +50 -2
- package/treb-export/src/zip-wrapper.ts +1 -1
- package/treb-grid/src/editors/overlay_editor.ts +3 -3
- package/treb-grid/src/layout/base_layout.ts +3 -3
- package/treb-grid/src/render/tile_renderer.ts +49 -48
- package/treb-grid/src/types/grid.ts +135 -8
- package/treb-grid/src/types/grid_base.ts +10 -1
- package/treb-parser/src/parser-types.ts +10 -0
- package/treb-parser/src/parser.ts +33 -13
|
@@ -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?: '
|
|
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 === '
|
|
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 === '
|
|
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 =
|
|
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
|
-
|
|
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,
|
|
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
|
|
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.
|
|
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
|
-
/**
|
|
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
|
}
|