@trebco/treb 29.3.4 → 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 +12 -12
- package/dist/treb-spreadsheet.mjs +12 -12
- package/dist/treb.d.ts +36 -41
- package/package.json +1 -1
- package/treb-base-types/src/area.ts +7 -0
- package/treb-base-types/src/cell.ts +2 -46
- package/treb-base-types/src/cells.ts +14 -8
- package/treb-base-types/src/gradient.ts +2 -2
- package/treb-base-types/src/import.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/calculator.ts +22 -12
- package/treb-calculator/src/dag/graph.ts +12 -3
- package/treb-calculator/src/expression-calculator.ts +66 -74
- package/treb-calculator/src/functions/base-functions.ts +2 -2
- package/treb-calculator/src/functions/sparkline.ts +2 -2
- package/treb-calculator/src/functions/statistics-functions.ts +31 -1
- package/treb-data-model/src/data-validation.ts +44 -0
- package/treb-data-model/src/data_model.ts +11 -7
- package/treb-data-model/src/index.ts +1 -1
- package/treb-data-model/src/named.ts +35 -10
- package/treb-data-model/src/sheet.ts +75 -15
- package/treb-data-model/src/sheet_types.ts +4 -0
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +7 -3
- package/treb-embed/src/embedded-spreadsheet.ts +50 -28
- package/treb-embed/src/progress-dialog.ts +4 -1
- 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} +439 -628
- package/treb-export/src/import2.ts +63 -26
- 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 +5 -14
- package/treb-grid/src/render/tile_renderer.ts +49 -48
- package/treb-grid/src/types/grid.ts +164 -26
- package/treb-grid/src/types/grid_base.ts +93 -17
- package/treb-grid/src/types/grid_command.ts +2 -1
- package/treb-parser/src/parser-types.ts +10 -0
- package/treb-parser/src/parser.ts +55 -17
|
@@ -37,11 +37,12 @@
|
|
|
37
37
|
|
|
38
38
|
import { EventSource } from 'treb-utils';
|
|
39
39
|
import type { DataModel, ConditionalFormat, // MacroFunction, SerializedModel, SerializedNamedExpression,
|
|
40
|
-
ViewModel
|
|
40
|
+
ViewModel,
|
|
41
|
+
DataValidation} from 'treb-data-model';
|
|
41
42
|
|
|
42
43
|
import type { Parser, UnitAddress} from 'treb-parser';
|
|
43
44
|
import { type ExpressionUnit, IllegalSheetNameRegex, ParseCSV, DecimalMarkType } from 'treb-parser';
|
|
44
|
-
import { Area, IsCellAddress,
|
|
45
|
+
import { Area, IsCellAddress, ValueType, DefaultTableSortOptions } from 'treb-base-types';
|
|
45
46
|
import type { ICellAddress, IArea, Cell, CellValue, CellStyle, Table, TableSortOptions, TableTheme, Complex, PatchOptions as PatchAreaOptions } from 'treb-base-types';
|
|
46
47
|
|
|
47
48
|
import { Sheet, type SerializeOptions, type Annotation } from 'treb-data-model';
|
|
@@ -592,7 +593,7 @@ export class GridBase {
|
|
|
592
593
|
* @param column column, columns, or undefined means all columns
|
|
593
594
|
* @param width target width, or undefined means auto-size
|
|
594
595
|
*/
|
|
595
|
-
public SetColumnWidth(column?: number | number[], width
|
|
596
|
+
public SetColumnWidth(column?: number | number[], width?: number): void {
|
|
596
597
|
this.ExecCommand({
|
|
597
598
|
key: CommandKey.ResizeColumns,
|
|
598
599
|
column,
|
|
@@ -922,34 +923,54 @@ export class GridBase {
|
|
|
922
923
|
|
|
923
924
|
protected SetValidationInternal(command: DataValidationCommand): void {
|
|
924
925
|
|
|
925
|
-
let cell: Cell|undefined;
|
|
926
|
-
|
|
927
926
|
const sheet = this.FindSheet(command.area);
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
cell = sheet.cells.GetCell(command.area, true);
|
|
927
|
+
if (!sheet) {
|
|
928
|
+
throw new Error('invalid sheet in set validation');
|
|
931
929
|
}
|
|
932
930
|
|
|
933
|
-
|
|
934
|
-
throw new Error('invalid cell in set validation');
|
|
935
|
-
}
|
|
931
|
+
const target = {start: command.area.start, end: command.area.end };
|
|
936
932
|
|
|
937
933
|
if (command.range) {
|
|
934
|
+
|
|
935
|
+
sheet.AddValidation({
|
|
936
|
+
type: 'range',
|
|
937
|
+
error: !!command.error,
|
|
938
|
+
area: command.range,
|
|
939
|
+
target: [target],
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
/*
|
|
938
943
|
cell.validation = {
|
|
939
944
|
type: ValidationType.Range,
|
|
940
945
|
area: command.range,
|
|
941
946
|
error: !!command.error,
|
|
942
947
|
};
|
|
948
|
+
*/
|
|
949
|
+
|
|
943
950
|
}
|
|
944
951
|
else if (command.list) {
|
|
952
|
+
|
|
953
|
+
sheet.AddValidation({
|
|
954
|
+
type: 'list',
|
|
955
|
+
error: !!command.error,
|
|
956
|
+
list: JSON.parse(JSON.stringify(command.list)),
|
|
957
|
+
target: [target],
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
/*
|
|
945
961
|
cell.validation = {
|
|
946
962
|
type: ValidationType.List,
|
|
947
963
|
list: JSON.parse(JSON.stringify(command.list)),
|
|
948
964
|
error: !!command.error,
|
|
949
965
|
}
|
|
966
|
+
*/
|
|
967
|
+
|
|
950
968
|
}
|
|
951
969
|
else {
|
|
952
|
-
cell.validation = undefined;
|
|
970
|
+
// cell.validation = undefined;
|
|
971
|
+
|
|
972
|
+
sheet.RemoveValidations(target);
|
|
973
|
+
|
|
953
974
|
}
|
|
954
975
|
|
|
955
976
|
}
|
|
@@ -2493,11 +2514,20 @@ export class GridBase {
|
|
|
2493
2514
|
|
|
2494
2515
|
// translate...
|
|
2495
2516
|
if (unit.offset_column) {
|
|
2517
|
+
unit.absolute_column = false;
|
|
2496
2518
|
unit.column = unit.column + address.column;
|
|
2497
2519
|
}
|
|
2520
|
+
else {
|
|
2521
|
+
unit.absolute_column = true;
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2498
2524
|
if (unit.offset_row) {
|
|
2525
|
+
unit.absolute_row = false;
|
|
2499
2526
|
unit.row = unit.row + address.row;
|
|
2500
2527
|
}
|
|
2528
|
+
else {
|
|
2529
|
+
unit.absolute_row = true;
|
|
2530
|
+
}
|
|
2501
2531
|
|
|
2502
2532
|
}
|
|
2503
2533
|
return true;
|
|
@@ -2794,8 +2824,54 @@ export class GridBase {
|
|
|
2794
2824
|
/**
|
|
2795
2825
|
* patch sheet conditionals for insert/delete row/column operations.
|
|
2796
2826
|
* some of them may be deleted.
|
|
2827
|
+
*
|
|
2828
|
+
* UPDATE: using this routine to also patch data validations
|
|
2797
2829
|
*/
|
|
2798
|
-
protected
|
|
2830
|
+
protected PatchConditionalsAndValidations(options: PatchOptions) {
|
|
2831
|
+
|
|
2832
|
+
if (options.sheet.data_validation.length) {
|
|
2833
|
+
|
|
2834
|
+
const delete_list: Set<DataValidation> = new Set();
|
|
2835
|
+
for (const validation of options.sheet.data_validation) {
|
|
2836
|
+
|
|
2837
|
+
const targets: IArea[] = [];
|
|
2838
|
+
for (const area of validation.target) {
|
|
2839
|
+
const updated = Area.PatchArea(area, options);
|
|
2840
|
+
if (updated) {
|
|
2841
|
+
targets.push(updated);
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
if (targets.length > 0) {
|
|
2846
|
+
|
|
2847
|
+
validation.target = targets;
|
|
2848
|
+
|
|
2849
|
+
// format the range, if necessary
|
|
2850
|
+
|
|
2851
|
+
if (validation.type === 'range') {
|
|
2852
|
+
if (validation.area.start.sheet_id === options.sheet.id) {
|
|
2853
|
+
const updated = Area.PatchArea(validation.area, options);
|
|
2854
|
+
if (updated) {
|
|
2855
|
+
validation.area = updated;
|
|
2856
|
+
}
|
|
2857
|
+
else {
|
|
2858
|
+
delete_list.add(validation);
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2863
|
+
}
|
|
2864
|
+
else {
|
|
2865
|
+
delete_list.add(validation);
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
if (delete_list.size) {
|
|
2871
|
+
options.sheet.data_validation = options.sheet.data_validation.filter(test => !delete_list.has(test));
|
|
2872
|
+
}
|
|
2873
|
+
|
|
2874
|
+
}
|
|
2799
2875
|
|
|
2800
2876
|
if (options.sheet.conditional_formats?.length) {
|
|
2801
2877
|
|
|
@@ -2869,7 +2945,7 @@ export class GridBase {
|
|
|
2869
2945
|
}
|
|
2870
2946
|
|
|
2871
2947
|
// conditionals
|
|
2872
|
-
this.
|
|
2948
|
+
this.PatchConditionalsAndValidations({
|
|
2873
2949
|
sheet: target_sheet,
|
|
2874
2950
|
before_column: 0,
|
|
2875
2951
|
column_count: 0,
|
|
@@ -3217,7 +3293,7 @@ export class GridBase {
|
|
|
3217
3293
|
}
|
|
3218
3294
|
|
|
3219
3295
|
// conditionals
|
|
3220
|
-
this.
|
|
3296
|
+
this.PatchConditionalsAndValidations({
|
|
3221
3297
|
sheet: target_sheet,
|
|
3222
3298
|
before_column: command.before_column,
|
|
3223
3299
|
column_count: command.count,
|
|
@@ -4029,8 +4105,8 @@ export class GridBase {
|
|
|
4029
4105
|
// COEDITING: ok
|
|
4030
4106
|
|
|
4031
4107
|
this.SetValidationInternal(command);
|
|
4032
|
-
if (!command.area.sheet_id || command.area.sheet_id === this.active_sheet.id) {
|
|
4033
|
-
flags.render_area = Area.Join(new Area(command.area), flags.render_area);
|
|
4108
|
+
if (!command.area.start.sheet_id || command.area.start.sheet_id === this.active_sheet.id) {
|
|
4109
|
+
flags.render_area = Area.Join(new Area(command.area.start, command.area.end), flags.render_area);
|
|
4034
4110
|
}
|
|
4035
4111
|
break;
|
|
4036
4112
|
|
|
@@ -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;
|
|
@@ -409,7 +409,7 @@ export class Parser {
|
|
|
409
409
|
for (let i = 0; i < n; i++) {
|
|
410
410
|
transposed[i] = [];
|
|
411
411
|
for (let j = 0; j < m; j++) {
|
|
412
|
-
transposed[i][j] = arr[j][i];
|
|
412
|
+
transposed[i][j] = arr[j] ? arr[j][i] : undefined;
|
|
413
413
|
}
|
|
414
414
|
}
|
|
415
415
|
|
|
@@ -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
|
}
|
|
@@ -2415,8 +2435,11 @@ export class Parser {
|
|
|
2415
2435
|
if (!c) return null;
|
|
2416
2436
|
position = c.position;
|
|
2417
2437
|
|
|
2438
|
+
// things that look like an address but have row 0 are legal
|
|
2439
|
+
// as names. so this should be a token if r === 0.
|
|
2440
|
+
|
|
2418
2441
|
const r = this.ConsumeAddressRow(position);
|
|
2419
|
-
if (!r) return null;
|
|
2442
|
+
if (!r) return null;
|
|
2420
2443
|
position = r.position;
|
|
2421
2444
|
|
|
2422
2445
|
const label = sheet ?
|
|
@@ -2460,7 +2483,11 @@ export class Parser {
|
|
|
2460
2483
|
|
|
2461
2484
|
/**
|
|
2462
2485
|
* consumes a row, possibly absolute ($). returns the numeric row
|
|
2463
|
-
* (0-based) and metadata
|
|
2486
|
+
* (0-based) and metadata.
|
|
2487
|
+
*
|
|
2488
|
+
* note that something like "X0" is a legal token, because 0 is not
|
|
2489
|
+
* a valid row. but at the same time it can't have a $ in it. although
|
|
2490
|
+
* maybe "X$0" is a token but not a valid name? dunno
|
|
2464
2491
|
*/
|
|
2465
2492
|
protected ConsumeAddressRow(position: number):
|
|
2466
2493
|
{
|
|
@@ -2484,7 +2511,18 @@ export class Parser {
|
|
|
2484
2511
|
else break;
|
|
2485
2512
|
}
|
|
2486
2513
|
|
|
2487
|
-
if (start === position)
|
|
2514
|
+
if (start === position) {
|
|
2515
|
+
return false;
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
// handle token X0. should ~maybe~ handle this only if !absolute
|
|
2519
|
+
// temp leaving this separate from the above test just so it's clear
|
|
2520
|
+
// what we are doing
|
|
2521
|
+
|
|
2522
|
+
if (value === 0) {
|
|
2523
|
+
return false;
|
|
2524
|
+
}
|
|
2525
|
+
|
|
2488
2526
|
return { absolute, row: value - 1, position };
|
|
2489
2527
|
}
|
|
2490
2528
|
|