@trebco/treb 29.3.4 → 29.4.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.
- package/dist/treb-spreadsheet-light.mjs +11 -11
- package/dist/treb-spreadsheet.mjs +11 -11
- package/dist/treb.d.ts +3 -25
- 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/import.ts +2 -2
- 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/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 +61 -0
- package/treb-data-model/src/sheet_types.ts +4 -0
- package/treb-embed/src/embedded-spreadsheet.ts +21 -15
- package/treb-embed/src/progress-dialog.ts +4 -1
- package/treb-export/src/export2.ts +27 -14
- package/treb-export/src/import2.ts +58 -12
- package/treb-grid/src/layout/base_layout.ts +2 -11
- package/treb-grid/src/types/grid.ts +29 -18
- package/treb-grid/src/types/grid_base.ts +83 -16
- package/treb-grid/src/types/grid_command.ts +2 -1
- package/treb-parser/src/parser.ts +22 -4
|
@@ -1936,8 +1936,8 @@ for (const name of Object.getOwnPropertyNames(Math)) {
|
|
|
1936
1936
|
case 'function':
|
|
1937
1937
|
// console.info("MATH FUNC", name);
|
|
1938
1938
|
BaseFunctionLibrary[name] = {
|
|
1939
|
-
//
|
|
1940
|
-
fn: (...args:
|
|
1939
|
+
// description: 'Math function',
|
|
1940
|
+
fn: (...args: unknown[]) => {
|
|
1941
1941
|
return Box(value(...args));
|
|
1942
1942
|
},
|
|
1943
1943
|
category: ['Math Functions'],
|
|
@@ -343,7 +343,37 @@ export const StatisticsFunctionLibrary: FunctionMap = {
|
|
|
343
343
|
return { type: ValueType.number, value: (flat[lo-1] + flat[hi-1]) / 2 };
|
|
344
344
|
|
|
345
345
|
},
|
|
346
|
-
}
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
Rank: {
|
|
349
|
+
arguments: [
|
|
350
|
+
{ name: 'Value', },
|
|
351
|
+
{ name: 'Source', },
|
|
352
|
+
{ name: 'Order', },
|
|
353
|
+
],
|
|
354
|
+
fn: (value: number, source: CellValue[], order = 0) => {
|
|
355
|
+
|
|
356
|
+
if (typeof value !== 'number') {
|
|
357
|
+
return ArgumentError();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const numbers = Utils.FlattenNumbers(source);
|
|
361
|
+
numbers.sort(order ? (a, b) => a - b : (a, b) => b - a);
|
|
362
|
+
|
|
363
|
+
for (let i = 0; i < numbers.length; i++) {
|
|
364
|
+
if (numbers[i] === value) {
|
|
365
|
+
return {
|
|
366
|
+
type: ValueType.number,
|
|
367
|
+
value: i + 1,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return ValueError();
|
|
373
|
+
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
|
|
347
377
|
|
|
348
378
|
};
|
|
349
379
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
import type { IArea, CellValue } from 'treb-base-types';
|
|
3
|
+
|
|
4
|
+
/** @internal */
|
|
5
|
+
export interface DataValidationRange {
|
|
6
|
+
type: 'range';
|
|
7
|
+
area: IArea;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** @internal */
|
|
11
|
+
export interface DataValidationList {
|
|
12
|
+
type: 'list';
|
|
13
|
+
list: CellValue[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @internal */
|
|
17
|
+
export interface DataValidationDate {
|
|
18
|
+
type: 'date';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** @internal */
|
|
22
|
+
export interface DataValidationNumber {
|
|
23
|
+
type: 'number';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** @internal */
|
|
27
|
+
export interface DataValidationBoolean {
|
|
28
|
+
type: 'boolean';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @internal */
|
|
32
|
+
export type DataValidation
|
|
33
|
+
= (DataValidationList
|
|
34
|
+
| DataValidationRange
|
|
35
|
+
| DataValidationNumber
|
|
36
|
+
| DataValidationDate
|
|
37
|
+
| DataValidationBoolean) & {
|
|
38
|
+
|
|
39
|
+
error?: boolean;
|
|
40
|
+
// target: Area[]; // can be multple so we'll default to array
|
|
41
|
+
target: IArea[];
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
@@ -73,7 +73,7 @@ export class DataModel {
|
|
|
73
73
|
public readonly sheets = new SheetCollection();
|
|
74
74
|
|
|
75
75
|
/** new composite collection (TODO: add macro functions too?) */
|
|
76
|
-
public readonly named = new NamedRangeManager();
|
|
76
|
+
public readonly named = new NamedRangeManager(this.parser);
|
|
77
77
|
|
|
78
78
|
/** macro functions are functions written in spreadsheet language */
|
|
79
79
|
public readonly macro_functions: Map<string, MacroFunction> = new Map();
|
|
@@ -152,15 +152,19 @@ export class DataModel {
|
|
|
152
152
|
[ parse_result.expression, parse_result.expression ];
|
|
153
153
|
|
|
154
154
|
if (start.sheet) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (area.start.sheet_id) {
|
|
158
|
-
this.named.SetNamedRange(named.name, area, scope);
|
|
155
|
+
if (/^\[\d+\]/.test(start.sheet)) {
|
|
156
|
+
console.warn('named range refers to an external file');
|
|
159
157
|
}
|
|
160
158
|
else {
|
|
161
|
-
|
|
159
|
+
const area = new Area({...start, sheet_id: this.sheets.ID(start.sheet), }, end);
|
|
160
|
+
|
|
161
|
+
if (area.start.sheet_id) {
|
|
162
|
+
this.named.SetNamedRange(named.name, area, scope);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
console.warn("missing sheet ID?", start);
|
|
166
|
+
}
|
|
162
167
|
}
|
|
163
|
-
|
|
164
168
|
}
|
|
165
169
|
else {
|
|
166
170
|
console.warn("missing sheet name?", start);
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
import { Area } from 'treb-base-types';
|
|
23
23
|
import type { IArea } from 'treb-base-types';
|
|
24
|
-
import type { ExpressionUnit } from 'treb-parser';
|
|
24
|
+
import type { ExpressionUnit, Parser } from 'treb-parser';
|
|
25
25
|
|
|
26
26
|
interface NamedExpression {
|
|
27
27
|
type: 'expression';
|
|
@@ -82,6 +82,8 @@ export class NamedRangeManager {
|
|
|
82
82
|
return this.named.values();
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
constructor(public parser: Parser) {}
|
|
86
|
+
|
|
85
87
|
/** shorthand for setting named expression */
|
|
86
88
|
public SetNamedExpression(name: string, expression: ExpressionUnit, scope?: number) {
|
|
87
89
|
return this.SetName({
|
|
@@ -206,30 +208,53 @@ export class NamedRangeManager {
|
|
|
206
208
|
* named range rules:
|
|
207
209
|
*
|
|
208
210
|
* - legal characters are alphanumeric, underscore and dot.
|
|
211
|
+
*
|
|
209
212
|
* - must start with letter or underscore (not a number or dot).
|
|
213
|
+
* UPDATE: also possibly a backslash? not sure if that's escaping or not.
|
|
214
|
+
*
|
|
210
215
|
* - cannot look like a spreadsheet address, which is 1-3 letters followed by numbers.
|
|
211
|
-
*
|
|
216
|
+
* UPDATE: actually this is legal if the number is "0". that's the only
|
|
217
|
+
* number. I don't think our parser will process this correctly, so until
|
|
218
|
+
* then keep it illegal.
|
|
219
|
+
*
|
|
212
220
|
* - apparently questuon marks are legal, but not in first position. atm
|
|
213
221
|
* our parser will reject.
|
|
214
222
|
*
|
|
223
|
+
* - FIXME: we should block R1C1-looking notation as well
|
|
224
|
+
*
|
|
215
225
|
* returns a normalized name (just caps, atm)
|
|
216
226
|
*/
|
|
217
227
|
public ValidateNamed(name: string): string|false {
|
|
218
|
-
|
|
228
|
+
|
|
229
|
+
// normalize
|
|
230
|
+
name = name.trim().toUpperCase();
|
|
219
231
|
|
|
220
232
|
// can't be empty
|
|
221
233
|
if (!name.length) return false;
|
|
222
234
|
|
|
223
|
-
// can
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
// can only contain legal characters
|
|
227
|
-
if (/[^A-Za-z\d_.?]/.test(name)) return false;
|
|
235
|
+
// can only contain legal characters [FIXME: backslash is legal?]
|
|
236
|
+
if (/[^A-Z\d_.?]/.test(name)) return false;
|
|
228
237
|
|
|
229
238
|
// must start with ascii letter or underscore
|
|
230
|
-
if (/^[^A-
|
|
239
|
+
if (/^[^A-Z_]/.test(name)) return false;
|
|
240
|
+
|
|
241
|
+
// these are not legal for some reason -- they look like R1C1?
|
|
242
|
+
if (name === 'R' || name === 'C') {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// FIXME: should we just use a parser? we're adding more and more regexes.
|
|
247
|
+
|
|
248
|
+
// can't look like a spreadsheet address, unless the row is === 0
|
|
249
|
+
if (/^[A-Z]{1,3}[1-9]\d*$/.test(name)) return false;
|
|
250
|
+
|
|
251
|
+
// can't look like R1C1 either (we already checked for R and C)
|
|
252
|
+
if (/^R[[\]\d]+C/.test(name)) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
231
255
|
|
|
232
|
-
return name
|
|
256
|
+
return name;
|
|
257
|
+
|
|
233
258
|
}
|
|
234
259
|
|
|
235
260
|
/**
|
|
@@ -43,6 +43,7 @@ import type { GridSelection } from './sheet_selection';
|
|
|
43
43
|
import { CreateSelection } from './sheet_selection';
|
|
44
44
|
import { Annotation } from './annotation';
|
|
45
45
|
import type { ConditionalFormatList } from './conditional_format';
|
|
46
|
+
import type { DataValidation } from './data-validation';
|
|
46
47
|
|
|
47
48
|
// --- constants --------------------------------------------------------------
|
|
48
49
|
|
|
@@ -163,6 +164,11 @@ export class Sheet {
|
|
|
163
164
|
*/
|
|
164
165
|
public conditional_formats: ConditionalFormatList = [];
|
|
165
166
|
|
|
167
|
+
/**
|
|
168
|
+
* @internal
|
|
169
|
+
*/
|
|
170
|
+
public data_validation: DataValidation[] = [];
|
|
171
|
+
|
|
166
172
|
/**
|
|
167
173
|
* @internal
|
|
168
174
|
*
|
|
@@ -255,6 +261,7 @@ export class Sheet {
|
|
|
255
261
|
*/
|
|
256
262
|
private conditional_format_checklist: IArea[] = [];
|
|
257
263
|
|
|
264
|
+
|
|
258
265
|
// --- accessors ------------------------------------------------------------
|
|
259
266
|
|
|
260
267
|
// public get column_header_count() { return this.column_header_count_; }
|
|
@@ -383,6 +390,11 @@ export class Sheet {
|
|
|
383
390
|
sheet.conditional_formats = source.conditional_formats;
|
|
384
391
|
}
|
|
385
392
|
|
|
393
|
+
sheet.data_validation = (source.data_validations || []).map(validation => ({
|
|
394
|
+
...validation,
|
|
395
|
+
target: (validation.target||[]).map(target => new Area(target.start, target.end)),
|
|
396
|
+
}));
|
|
397
|
+
|
|
386
398
|
// persist ID, name
|
|
387
399
|
|
|
388
400
|
if (source.id) {
|
|
@@ -740,6 +752,45 @@ export class Sheet {
|
|
|
740
752
|
|
|
741
753
|
}
|
|
742
754
|
|
|
755
|
+
/** add a data validation. */
|
|
756
|
+
public AddValidation(validation: DataValidation) {
|
|
757
|
+
this.data_validation.push({
|
|
758
|
+
...validation,
|
|
759
|
+
target: (validation.target||[]).map(target => new Area(target.start, target.end)), // ensure class instance
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* remove validations from area. must be an exact match (FIXME).
|
|
765
|
+
* if there are multiple areas, only remove the matching area.
|
|
766
|
+
*/
|
|
767
|
+
public RemoveValidations(area: IArea) {
|
|
768
|
+
|
|
769
|
+
const check = new Area(area.start, area.end);
|
|
770
|
+
this.data_validation = this.data_validation.filter(validation => {
|
|
771
|
+
validation.target = validation.target.filter(compare => !check.Equals2(compare));
|
|
772
|
+
return validation.target.length > 0;
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/** return data validation(s) that apply to a given address */
|
|
777
|
+
public GetValidation(address: ICellAddress) {
|
|
778
|
+
|
|
779
|
+
// switch to imperative
|
|
780
|
+
|
|
781
|
+
const list: DataValidation[] = [];
|
|
782
|
+
for (const entry of this.data_validation) {
|
|
783
|
+
for (const area of entry.target) {
|
|
784
|
+
if ((area as Area).Contains(address)) {
|
|
785
|
+
list.push(entry);
|
|
786
|
+
break;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
return list;
|
|
792
|
+
|
|
793
|
+
}
|
|
743
794
|
|
|
744
795
|
public Activate(DOM: DOMContext) {
|
|
745
796
|
|
|
@@ -2665,6 +2716,11 @@ export class Sheet {
|
|
|
2665
2716
|
JSON.parse(JSON.stringify(this.conditional_formats.map(format => ({...format, internal: undefined })))) :
|
|
2666
2717
|
undefined;
|
|
2667
2718
|
|
|
2719
|
+
// yes, here. we should have a serialized type so we know to convert. TODO
|
|
2720
|
+
|
|
2721
|
+
const data_validations = this.data_validation.length ? JSON.parse(JSON.stringify(this.data_validation)) : undefined;
|
|
2722
|
+
|
|
2723
|
+
|
|
2668
2724
|
const result: SerializedSheet = {
|
|
2669
2725
|
|
|
2670
2726
|
// not used atm, but in the event we need to gate
|
|
@@ -2688,6 +2744,7 @@ export class Sheet {
|
|
|
2688
2744
|
column_style,
|
|
2689
2745
|
|
|
2690
2746
|
conditional_formats,
|
|
2747
|
+
data_validations,
|
|
2691
2748
|
|
|
2692
2749
|
row_pattern: row_pattern.length ? row_pattern : undefined,
|
|
2693
2750
|
|
|
@@ -2886,6 +2943,10 @@ export class Sheet {
|
|
|
2886
2943
|
this.conditional_formats.push(format);
|
|
2887
2944
|
}
|
|
2888
2945
|
|
|
2946
|
+
for (const validation of data.data_validations || []) {
|
|
2947
|
+
this.AddValidation(validation);
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2889
2950
|
if (data.hidden) {
|
|
2890
2951
|
this.visible = false;
|
|
2891
2952
|
}
|
|
@@ -23,6 +23,7 @@ import type { IArea, SerializedCellData, CellStyle } from 'treb-base-types';
|
|
|
23
23
|
import type { AnnotationData } from './annotation';
|
|
24
24
|
import type { GridSelection, SerializedGridSelection } from './sheet_selection';
|
|
25
25
|
import type { ConditionalFormatList } from './conditional_format';
|
|
26
|
+
import type { DataValidation } from './data-validation';
|
|
26
27
|
|
|
27
28
|
export interface UpdateHints {
|
|
28
29
|
data?: boolean;
|
|
@@ -72,6 +73,9 @@ export interface SerializedSheet {
|
|
|
72
73
|
/** @internal */
|
|
73
74
|
conditional_formats?: ConditionalFormatList;
|
|
74
75
|
|
|
76
|
+
/** @internal */
|
|
77
|
+
data_validations?: DataValidation[];
|
|
78
|
+
|
|
75
79
|
/**
|
|
76
80
|
* @deprecated use `styles` instead
|
|
77
81
|
*/
|
|
@@ -21,9 +21,6 @@
|
|
|
21
21
|
|
|
22
22
|
// --- imports -----------------------------------------------------------------
|
|
23
23
|
|
|
24
|
-
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
|
|
25
|
-
/// <reference path="./content-types.d.ts" />
|
|
26
|
-
|
|
27
24
|
import type {
|
|
28
25
|
GridEvent,
|
|
29
26
|
SheetChangeEvent, GridOptions,
|
|
@@ -103,6 +100,11 @@ import type { StateLeafVertex } from 'treb-calculator';
|
|
|
103
100
|
|
|
104
101
|
// --- worker ------------------------------------------------------------------
|
|
105
102
|
|
|
103
|
+
/**
|
|
104
|
+
* import type for our worker, plus markup files
|
|
105
|
+
*/
|
|
106
|
+
import './content-types.d.ts';
|
|
107
|
+
|
|
106
108
|
/**
|
|
107
109
|
* import the worker as a script file. tsc will read this on typecheck but
|
|
108
110
|
* that's actually to the good; when we build with esbuild we will inline
|
|
@@ -3005,7 +3007,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
3005
3007
|
|
|
3006
3008
|
this.grid.Reset();
|
|
3007
3009
|
this.ResetInternal();
|
|
3008
|
-
this.calculator.AttachModel();
|
|
3010
|
+
// this.calculator.AttachModel();
|
|
3009
3011
|
this.UpdateAC();
|
|
3010
3012
|
|
|
3011
3013
|
this.Publish({ type: 'reset' });
|
|
@@ -3491,25 +3493,30 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
3491
3493
|
/**
|
|
3492
3494
|
* set or clear cell valiation.
|
|
3493
3495
|
*
|
|
3494
|
-
* @param
|
|
3496
|
+
* @param target - target cell/area
|
|
3495
3497
|
* @param validation - a spreadsheet range, list of data, or undefined. pass
|
|
3496
3498
|
* undefined to remove existing cell validation.
|
|
3497
3499
|
* @param error - setting an invalid value in the target cell is an error (and
|
|
3498
3500
|
* is blocked). defaults to false.
|
|
3499
3501
|
*/
|
|
3500
|
-
public SetValidation(
|
|
3502
|
+
public SetValidation(target: RangeReference, validation?: RangeReference|CellValue[], error?: boolean) {
|
|
3501
3503
|
|
|
3502
|
-
if (typeof
|
|
3503
|
-
const reference = this.model.ResolveAddress(
|
|
3504
|
-
|
|
3504
|
+
if (typeof target === 'string') {
|
|
3505
|
+
const reference = this.model.ResolveAddress(target, this.grid.active_sheet);
|
|
3506
|
+
target = IsArea(reference) ? new Area(reference.start, reference.end) : new Area(reference);
|
|
3507
|
+
// address = IsCellAddress(reference) ? reference : reference.start;
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
if (IsCellAddress(target)) {
|
|
3511
|
+
target = new Area(target);
|
|
3505
3512
|
}
|
|
3506
3513
|
|
|
3507
3514
|
if (typeof validation === 'undefined' || Array.isArray(validation)) {
|
|
3508
|
-
this.grid.SetValidation(
|
|
3515
|
+
this.grid.SetValidation(target, validation, error);
|
|
3509
3516
|
}
|
|
3510
3517
|
else {
|
|
3511
3518
|
const range = this.model.ResolveArea(validation, this.grid.active_sheet);
|
|
3512
|
-
this.grid.SetValidation(
|
|
3519
|
+
this.grid.SetValidation(target, range, error);
|
|
3513
3520
|
}
|
|
3514
3521
|
|
|
3515
3522
|
}
|
|
@@ -4626,7 +4633,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4626
4633
|
|
|
4627
4634
|
// this one _is_ the grid cells
|
|
4628
4635
|
|
|
4629
|
-
this.calculator.AttachModel();
|
|
4636
|
+
// this.calculator.AttachModel();
|
|
4630
4637
|
this.Publish({ type: 'load', source, });
|
|
4631
4638
|
this.UpdateDocumentStyles();
|
|
4632
4639
|
|
|
@@ -5845,9 +5852,8 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
5845
5852
|
|
|
5846
5853
|
this.grid.UpdateSheets(sheets, undefined, override_sheet || data.active_sheet);
|
|
5847
5854
|
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
if (table.area.start.sheet_id) {
|
|
5855
|
+
for (const table of this.model.tables.values()) {
|
|
5856
|
+
if (table.area.start.sheet_id) {
|
|
5851
5857
|
const sheet = model.sheets.Find(table.area.start.sheet_id);
|
|
5852
5858
|
if (sheet) {
|
|
5853
5859
|
for (let row = table.area.start.row; row <= table.area.end.row; row++) {
|
|
@@ -57,7 +57,6 @@ export type ResolutionFunction = () => void;
|
|
|
57
57
|
*/
|
|
58
58
|
export class Dialog {
|
|
59
59
|
|
|
60
|
-
// private model: NodeModel;
|
|
61
60
|
private model: Record<string, HTMLElement> = {};
|
|
62
61
|
|
|
63
62
|
private layout_element: HTMLElement;
|
|
@@ -192,6 +191,10 @@ export class Dialog {
|
|
|
192
191
|
|
|
193
192
|
}
|
|
194
193
|
|
|
194
|
+
public Node(name: string): HTMLElement|undefined {
|
|
195
|
+
return this.model[name];
|
|
196
|
+
}
|
|
197
|
+
|
|
195
198
|
public Update(options: Partial<MessageDialogOptions>, delta = true): void {
|
|
196
199
|
if (delta) {
|
|
197
200
|
options = { ...this.options_, ... options};
|
|
@@ -34,11 +34,11 @@ import { PixelsToColumnWidth } from './column-width';
|
|
|
34
34
|
const XMLDeclaration = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n`;
|
|
35
35
|
|
|
36
36
|
import { template } from './template-2';
|
|
37
|
-
import type { SerializedModel, SerializedSheet } from 'treb-data-model';
|
|
37
|
+
import type { SerializedModel, SerializedSheet, DataValidation } from 'treb-data-model';
|
|
38
38
|
|
|
39
|
-
import type { IArea, ICellAddress, CellValue,
|
|
39
|
+
import type { IArea, ICellAddress, CellValue, CellStyle,
|
|
40
40
|
AnnotationLayout, Corner as LayoutCorner, Cell, Rectangle } from 'treb-base-types';
|
|
41
|
-
import { Area, Cells, ValueType, Style
|
|
41
|
+
import { Area, Cells, ValueType, Style } from 'treb-base-types';
|
|
42
42
|
|
|
43
43
|
// import * as xmlparser from 'fast-xml-parser';
|
|
44
44
|
import type { XmlBuilderOptions} from 'fast-xml-parser';
|
|
@@ -1417,10 +1417,12 @@ export class Exporter {
|
|
|
1417
1417
|
const merges: Area[] = [];
|
|
1418
1418
|
const tables: TableDescription[] = [];
|
|
1419
1419
|
|
|
1420
|
+
/*
|
|
1420
1421
|
const validations: Array<{
|
|
1421
1422
|
address: ICellAddress,
|
|
1422
1423
|
validation: DataValidation,
|
|
1423
1424
|
}> = [];
|
|
1425
|
+
*/
|
|
1424
1426
|
|
|
1425
1427
|
// --
|
|
1426
1428
|
|
|
@@ -1606,12 +1608,14 @@ export class Exporter {
|
|
|
1606
1608
|
});
|
|
1607
1609
|
}
|
|
1608
1610
|
|
|
1611
|
+
/*
|
|
1609
1612
|
if (cell.validation && (cell.validation.type === ValidationType.List || cell.validation.type === ValidationType.Range)) {
|
|
1610
1613
|
validations.push({
|
|
1611
1614
|
address: {row: r, column: c},
|
|
1612
1615
|
validation: cell.validation,
|
|
1613
1616
|
});
|
|
1614
1617
|
}
|
|
1618
|
+
*/
|
|
1615
1619
|
|
|
1616
1620
|
// short-circuit here
|
|
1617
1621
|
if (cell.type === ValueType.formula && /^=?sparkline\./i.test(cell.value as string)) {
|
|
@@ -1929,37 +1933,46 @@ export class Exporter {
|
|
|
1929
1933
|
|
|
1930
1934
|
// --- validation ----------------------------------------------------------
|
|
1931
1935
|
|
|
1932
|
-
if (
|
|
1936
|
+
if (sheet.data_validations?.length) {
|
|
1933
1937
|
|
|
1934
1938
|
dom.worksheet.dataValidations = {
|
|
1935
|
-
a$: { count:
|
|
1936
|
-
dataValidation:
|
|
1939
|
+
a$: { count: sheet.data_validations.length },
|
|
1940
|
+
dataValidation: sheet.data_validations.map(validation => {
|
|
1941
|
+
|
|
1942
|
+
const sqref = validation.target.map(target => {
|
|
1943
|
+
return new Area(target.start, target.end).spreadsheet_label;
|
|
1944
|
+
}).join(' ');
|
|
1945
|
+
|
|
1937
1946
|
const entry: any = {
|
|
1938
1947
|
a$: {
|
|
1939
1948
|
type: 'list',
|
|
1940
1949
|
allowBlank: 1,
|
|
1941
1950
|
showInputMessage: 1,
|
|
1942
1951
|
showErrorMessage: 1,
|
|
1943
|
-
sqref: new Area(validation.address).spreadsheet_label,
|
|
1952
|
+
sqref, // : new Area(validation.address).spreadsheet_label,
|
|
1944
1953
|
},
|
|
1945
1954
|
};
|
|
1946
|
-
if (validation.
|
|
1955
|
+
if (validation.type === 'range') {
|
|
1947
1956
|
|
|
1948
1957
|
const range: UnitRange = {
|
|
1949
1958
|
id: 0,
|
|
1950
1959
|
type: 'range',
|
|
1951
1960
|
label: '', position: 0,
|
|
1952
1961
|
start:
|
|
1953
|
-
{...validation.
|
|
1962
|
+
{...validation.area.start, absolute_column: true, absolute_row: true, id: 0, label: '', position: 0, type: 'address', },
|
|
1954
1963
|
end:
|
|
1955
|
-
{...validation.
|
|
1964
|
+
{...validation.area.end, absolute_column: true, absolute_row: true, id: 0, label: '', position: 0, type: 'address', }
|
|
1956
1965
|
,
|
|
1957
1966
|
}
|
|
1958
1967
|
|
|
1959
|
-
|
|
1960
|
-
|
|
1968
|
+
console.info("AA", {validation});
|
|
1969
|
+
|
|
1970
|
+
if (typeof validation.area.start.sheet_id !== 'undefined') {
|
|
1971
|
+
range.start.sheet = sheet_name_map[validation.area.start.sheet_id];
|
|
1961
1972
|
}
|
|
1962
1973
|
|
|
1974
|
+
console.info("m1");
|
|
1975
|
+
|
|
1963
1976
|
/*
|
|
1964
1977
|
const area = new Area(
|
|
1965
1978
|
{...validation.validation.area.start, absolute_column: true, absolute_row: true},
|
|
@@ -1971,8 +1984,8 @@ export class Exporter {
|
|
|
1971
1984
|
entry.formula1 = this.parser.Render(range);
|
|
1972
1985
|
|
|
1973
1986
|
}
|
|
1974
|
-
else if (validation.
|
|
1975
|
-
entry.formula1 = `"${validation.
|
|
1987
|
+
else if (validation.type === 'list') {
|
|
1988
|
+
entry.formula1 = `"${validation.list.join(',')}"`;
|
|
1976
1989
|
}
|
|
1977
1990
|
return entry;
|
|
1978
1991
|
}),
|