@trebco/treb 32.5.0 → 32.6.4
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.mjs +12 -12
- package/package.json +1 -1
- package/treb-base-types/src/import.ts +14 -10
- package/treb-calculator/src/calculator.ts +5 -0
- package/treb-charts/src/chart-types.ts +2 -0
- package/treb-charts/src/chart-utils.ts +45 -4
- package/treb-charts/src/default-chart-renderer.ts +84 -24
- package/treb-data-model/src/sheet.ts +9 -0
- package/treb-export/src/import.ts +147 -13
- package/treb-export/src/metadata.ts +187 -0
- package/treb-export/src/workbook.ts +151 -5
package/package.json
CHANGED
|
@@ -27,18 +27,22 @@ import type { Table } from './table';
|
|
|
27
27
|
import type { DataValidation, AnnotationType, ConditionalFormat } from 'treb-data-model';
|
|
28
28
|
|
|
29
29
|
export interface CellParseResult {
|
|
30
|
-
row: number
|
|
31
|
-
column: number
|
|
30
|
+
row: number;
|
|
31
|
+
column: number;
|
|
32
32
|
type: SerializedValueType; // ValueType,
|
|
33
|
-
value: number|string|undefined|boolean
|
|
34
|
-
calculated?: number|string|undefined|boolean
|
|
33
|
+
value: number|string|undefined|boolean;
|
|
34
|
+
calculated?: number|string|undefined|boolean;
|
|
35
35
|
calculated_type?: SerializedValueType; // ValueType,
|
|
36
|
-
style_ref?: number
|
|
37
|
-
hyperlink?: string
|
|
38
|
-
validation?: DataValidation
|
|
39
|
-
merge_area?: IArea
|
|
40
|
-
area?: IArea
|
|
41
|
-
|
|
36
|
+
style_ref?: number;
|
|
37
|
+
hyperlink?: string;
|
|
38
|
+
validation?: DataValidation;
|
|
39
|
+
merge_area?: IArea;
|
|
40
|
+
area?: IArea;
|
|
41
|
+
|
|
42
|
+
/** dynamic arrays */
|
|
43
|
+
spill?: IArea;
|
|
44
|
+
|
|
45
|
+
table?: Table;
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
export interface AnchoredAnnotation {
|
|
@@ -1221,11 +1221,16 @@ export class Calculator extends Graph {
|
|
|
1221
1221
|
public AttachSpillData(area: Area, cells?: Cells) {
|
|
1222
1222
|
|
|
1223
1223
|
if (!cells) {
|
|
1224
|
+
|
|
1225
|
+
// can we assume active sheet here? actually I guess not, we'll
|
|
1226
|
+
// need to set that...
|
|
1227
|
+
|
|
1224
1228
|
const sheet = area.start.sheet_id ? this.model.sheets.Find(area.start.sheet_id) : undefined;
|
|
1225
1229
|
cells = sheet?.cells;
|
|
1226
1230
|
}
|
|
1227
1231
|
|
|
1228
1232
|
if (!cells) {
|
|
1233
|
+
console.info({area, cells});
|
|
1229
1234
|
throw new Error('invalid sheet ID in attach spill data');
|
|
1230
1235
|
}
|
|
1231
1236
|
|
|
@@ -207,6 +207,7 @@ export interface HistogramData extends ColumnDataBaseType {
|
|
|
207
207
|
export interface LineBaseData extends ChartDataBaseType {
|
|
208
208
|
series?: NumberOrUndefinedArray[];
|
|
209
209
|
series2?: SeriesType[];
|
|
210
|
+
stacked_series?: SeriesType[];
|
|
210
211
|
scale: RangeScale;
|
|
211
212
|
x_scale?: RangeScale;
|
|
212
213
|
titles?: string[];
|
|
@@ -262,6 +263,7 @@ export interface BarData extends LineBaseData {
|
|
|
262
263
|
type: 'bar';
|
|
263
264
|
round?: boolean;
|
|
264
265
|
space?: number;
|
|
266
|
+
stacked?: boolean;
|
|
265
267
|
}
|
|
266
268
|
|
|
267
269
|
export interface DonutDataBaseType extends ChartDataBaseType {
|
|
@@ -1001,8 +1001,11 @@ export const CreateColumnChart = (
|
|
|
1001
1001
|
|
|
1002
1002
|
const [data, labels, args_title, args_options] = args;
|
|
1003
1003
|
|
|
1004
|
-
const
|
|
1005
|
-
const
|
|
1004
|
+
const options = args_options?.toString() || undefined;
|
|
1005
|
+
const stacked = /stacked/i.test(options || '');
|
|
1006
|
+
|
|
1007
|
+
let series: SeriesType[] = TransformSeriesData(data);
|
|
1008
|
+
let common = CommonData(series);
|
|
1006
1009
|
|
|
1007
1010
|
let category_labels: string[] | undefined;
|
|
1008
1011
|
|
|
@@ -1041,15 +1044,50 @@ export const CreateColumnChart = (
|
|
|
1041
1044
|
|
|
1042
1045
|
}
|
|
1043
1046
|
|
|
1047
|
+
let stacked_series: SeriesType[]|undefined = undefined;
|
|
1048
|
+
const legend = common.legend; // in case we munge it
|
|
1049
|
+
|
|
1050
|
+
if (stacked) {
|
|
1051
|
+
stacked_series = series;
|
|
1052
|
+
|
|
1053
|
+
const map: Map<number, number> = new Map();
|
|
1054
|
+
// const label_map: Map<number, string> = new Map();
|
|
1055
|
+
|
|
1056
|
+
for (const entry of series) {
|
|
1057
|
+
for (const [index, key] of entry.x.data.entries()) {
|
|
1058
|
+
if (key !== undefined) {
|
|
1059
|
+
const value = entry.y.data[index] || 0;
|
|
1060
|
+
map.set(key, value + (map.get(key) || 0));
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
const x_data = Array.from(map.keys()).sort((a, b) => a - b);
|
|
1066
|
+
const y_data = x_data.map(key => map.get(key) || 0);
|
|
1067
|
+
|
|
1068
|
+
// console.info({stacked_series, map, x_data, y_data});
|
|
1069
|
+
|
|
1070
|
+
series = [{
|
|
1071
|
+
x: { data: x_data, format: stacked_series[0]?.x?.format },
|
|
1072
|
+
y: { data: y_data, format: stacked_series[0]?.y?.format },
|
|
1073
|
+
}];
|
|
1074
|
+
|
|
1075
|
+
series[0].x.range = ArrayMinMax(x_data);
|
|
1076
|
+
series[0].y.range = ArrayMinMax(y_data);
|
|
1077
|
+
|
|
1078
|
+
common = CommonData(series, Math.min(0, ...y_data));
|
|
1079
|
+
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1044
1082
|
const title = args_title?.toString() || undefined;
|
|
1045
|
-
const options = args_options?.toString() || undefined;
|
|
1046
1083
|
|
|
1047
1084
|
const chart_data = {
|
|
1048
1085
|
type,
|
|
1049
|
-
legend: common.legend,
|
|
1086
|
+
legend, // legend: common.legend,
|
|
1050
1087
|
// legend_position: LegendPosition.right,
|
|
1051
1088
|
legend_style: LegendStyle.marker,
|
|
1052
1089
|
series2: series,
|
|
1090
|
+
stacked_series,
|
|
1053
1091
|
scale: common.y.scale,
|
|
1054
1092
|
title,
|
|
1055
1093
|
y_labels: type === 'bar' ? category_labels : common.y.labels, // swapped
|
|
@@ -1057,6 +1095,9 @@ export const CreateColumnChart = (
|
|
|
1057
1095
|
};
|
|
1058
1096
|
|
|
1059
1097
|
if (options) {
|
|
1098
|
+
|
|
1099
|
+
(chart_data as BarData).stacked = stacked;
|
|
1100
|
+
|
|
1060
1101
|
(chart_data as BarData).round = /round/i.test(options);
|
|
1061
1102
|
(chart_data as ChartDataBaseType).data_labels = /labels/i.test(options);
|
|
1062
1103
|
|
|
@@ -599,23 +599,33 @@ export class DefaultChartRenderer implements ChartRendererType {
|
|
|
599
599
|
corners = [half_width, half_width, 0, 0];
|
|
600
600
|
}
|
|
601
601
|
|
|
602
|
-
|
|
603
|
-
const series = chart_data.series2[s];
|
|
604
|
-
const color_index = typeof series.index === 'number' ? series.index : s + 1;
|
|
602
|
+
if (chart_data.stacked_series) {
|
|
605
603
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
// const format = NumberFormatCache.Get(series.y.format || '0.00');
|
|
604
|
+
// step by group (stacked series)
|
|
605
|
+
for (let s = 0; s < chart_data.stacked_series[0].y.data.length; s++ ){
|
|
609
606
|
|
|
610
|
-
|
|
607
|
+
const x = (area.left + s * column_width + space) ; // + s * width;
|
|
608
|
+
let y = 0;
|
|
609
|
+
|
|
610
|
+
// now check each series, grab s-th value
|
|
611
|
+
for (let t = 0; t < chart_data.stacked_series.length; t++) {
|
|
612
|
+
|
|
613
|
+
const series = chart_data.stacked_series[t];
|
|
614
|
+
const color_index = typeof series.index === 'number' ? series.index : t + 1;
|
|
611
615
|
|
|
612
|
-
// const x = Math.round(area.left + i * column_width + space) + s * width;
|
|
613
|
-
const x = (area.left + i * column_width + space) + s * width;
|
|
614
|
-
|
|
615
616
|
let height = 0;
|
|
616
|
-
let y = 0;
|
|
617
617
|
// let negative = false;
|
|
618
618
|
|
|
619
|
+
const value = (series.y.data[s] || 0);
|
|
620
|
+
|
|
621
|
+
if (t === 0) {
|
|
622
|
+
y = Math.min(chart_data.scale.min || 0, value);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
height = Util.ApplyScale(value, area.height, chart_data.scale);
|
|
626
|
+
const base = area.bottom - height - Util.ApplyScale(y, area.height, chart_data.scale);
|
|
627
|
+
|
|
628
|
+
/*
|
|
619
629
|
if (zero) {
|
|
620
630
|
if (value > 0) {
|
|
621
631
|
height = Util.ApplyScale(value + chart_data.scale.min, area.height, chart_data.scale);
|
|
@@ -631,27 +641,77 @@ export class DefaultChartRenderer implements ChartRendererType {
|
|
|
631
641
|
height = Util.ApplyScale(value, area.height, chart_data.scale);
|
|
632
642
|
y = area.bottom - height;
|
|
633
643
|
}
|
|
644
|
+
*/
|
|
634
645
|
|
|
635
|
-
|
|
636
|
-
|
|
646
|
+
let bar_title = undefined;
|
|
647
|
+
let label = undefined;
|
|
648
|
+
let label_point = undefined;
|
|
637
649
|
|
|
638
|
-
|
|
650
|
+
this.renderer.RenderRectangle(new Area(
|
|
651
|
+
x, base, x + width, base + height,
|
|
652
|
+
), corners, ['chart-column', `series-${color_index}`], bar_title || undefined, label, label_point);
|
|
639
653
|
|
|
640
|
-
|
|
641
|
-
const label_point = {
|
|
642
|
-
x: Math.round(x + width / 2),
|
|
643
|
-
y: Math.round(y - 10),
|
|
644
|
-
};
|
|
654
|
+
y += value;
|
|
645
655
|
|
|
646
|
-
this.renderer.RenderRectangle(new Area(
|
|
647
|
-
x, y, x + width, y + height,
|
|
648
|
-
), corners, ['chart-column', `series-${color_index}`], bar_title || undefined, label, label_point);
|
|
649
|
-
}
|
|
650
656
|
}
|
|
657
|
+
|
|
658
|
+
|
|
651
659
|
}
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
for (let s = 0; s < series_count; s++) {
|
|
663
|
+
const series = chart_data.series2[s];
|
|
664
|
+
const color_index = typeof series.index === 'number' ? series.index : s + 1;
|
|
665
|
+
|
|
666
|
+
for (let i = 0; i < series.y.data.length; i++ ){
|
|
667
|
+
const value = series.y.data[i];
|
|
668
|
+
|
|
669
|
+
if (typeof value === 'number') {
|
|
670
|
+
|
|
671
|
+
// const x = Math.round(area.left + i * column_width + space) + s * width;
|
|
672
|
+
const x = (area.left + i * column_width + space) + s * width;
|
|
673
|
+
|
|
674
|
+
let height = 0;
|
|
675
|
+
let y = 0;
|
|
676
|
+
// let negative = false;
|
|
677
|
+
|
|
678
|
+
if (zero) {
|
|
679
|
+
if (value > 0) {
|
|
680
|
+
height = Util.ApplyScale(value + chart_data.scale.min, area.height, chart_data.scale);
|
|
681
|
+
y = area.bottom - height - zero;
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
height = Util.ApplyScale(chart_data.scale.min - value, area.height, chart_data.scale);
|
|
685
|
+
y = area.bottom - zero; // // area.bottom - height - zero;
|
|
686
|
+
// negative = true;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
height = Util.ApplyScale(value, area.height, chart_data.scale);
|
|
691
|
+
y = area.bottom - height;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// const bar_title = chart_data.titles ? chart_data.titles[i] : undefined;
|
|
695
|
+
const bar_title = undefined;
|
|
696
|
+
|
|
697
|
+
if (height) {
|
|
652
698
|
|
|
699
|
+
const label = (chart_data.data_labels && !!series.y.labels) ? series.y.labels[i] : '';
|
|
700
|
+
const label_point = {
|
|
701
|
+
x: Math.round(x + width / 2),
|
|
702
|
+
y: Math.round(y - 10),
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
this.renderer.RenderRectangle(new Area(
|
|
706
|
+
x, y, x + width, y + height,
|
|
707
|
+
), corners, ['chart-column', `series-${color_index}`], bar_title || undefined, label, label_point);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
}
|
|
653
713
|
}
|
|
654
|
-
|
|
714
|
+
|
|
655
715
|
}
|
|
656
716
|
|
|
657
717
|
}
|
|
@@ -3011,6 +3011,15 @@ export class Sheet {
|
|
|
3011
3011
|
this.name = data.name || ''; // wtf is this?
|
|
3012
3012
|
}
|
|
3013
3013
|
|
|
3014
|
+
// patching from import
|
|
3015
|
+
for (const cell of this.cells.Iterate()) {
|
|
3016
|
+
if (cell.spill) {
|
|
3017
|
+
if (!cell.spill.start.sheet_id) {
|
|
3018
|
+
cell.spill.SetSheetID(this.id);
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
|
|
3014
3023
|
if (data.tab_color) {
|
|
3015
3024
|
this.tab_color = data.tab_color;
|
|
3016
3025
|
}
|
|
@@ -26,7 +26,7 @@ import Base64JS from 'base64-js';
|
|
|
26
26
|
|
|
27
27
|
import type { AnchoredChartDescription, AnchoredImageDescription, AnchoredTextBoxDescription} from './workbook';
|
|
28
28
|
import { ChartType, ConditionalFormatOperators, Workbook } from './workbook';
|
|
29
|
-
import type { ParseResult } from 'treb-parser';
|
|
29
|
+
import type { ExpressionUnit, ParseResult, UnitCall } from 'treb-parser';
|
|
30
30
|
import { DecimalMarkType, Parser } from 'treb-parser';
|
|
31
31
|
import type { RangeType, AddressType, HyperlinkType } from './address-type';
|
|
32
32
|
import { is_range, ShiftRange, InRange, is_address } from './address-type';
|
|
@@ -42,6 +42,7 @@ import { ColumnWidthToPixels } from './column-width';
|
|
|
42
42
|
import type { DataValidation, AnnotationType } from 'treb-data-model';
|
|
43
43
|
import { ZipWrapper } from './zip-wrapper';
|
|
44
44
|
import type { ConditionalFormat } from 'treb-data-model';
|
|
45
|
+
import { LookupMetadata, type MetadataFlags } from './metadata';
|
|
45
46
|
|
|
46
47
|
interface SharedFormula {
|
|
47
48
|
row: number;
|
|
@@ -57,6 +58,7 @@ interface CellElementType {
|
|
|
57
58
|
r?: string;
|
|
58
59
|
t?: string;
|
|
59
60
|
s?: string;
|
|
61
|
+
cm?: string;
|
|
60
62
|
};
|
|
61
63
|
v?: string|number|{
|
|
62
64
|
t$: string;
|
|
@@ -147,6 +149,7 @@ export class Importer {
|
|
|
147
149
|
element: CellElementType,
|
|
148
150
|
shared_formulae: SharedFormulaMap,
|
|
149
151
|
arrays: RangeType[],
|
|
152
|
+
dynamic_arrays: RangeType[],
|
|
150
153
|
merges: RangeType[],
|
|
151
154
|
links: HyperlinkType[],
|
|
152
155
|
// validations: Array<{ address: ICellAddress, validation: DataValidation }>,
|
|
@@ -165,6 +168,15 @@ export class Importer {
|
|
|
165
168
|
return undefined;
|
|
166
169
|
}
|
|
167
170
|
|
|
171
|
+
// new (to us) metadata
|
|
172
|
+
let metadata_flags: MetadataFlags = {};
|
|
173
|
+
if (element.a$?.cm) {
|
|
174
|
+
const cm_index = Number(element.a$?.cm);
|
|
175
|
+
if (this.workbook?.metadata) {
|
|
176
|
+
metadata_flags = LookupMetadata(this.workbook.metadata, 'cell', cm_index).flags;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
168
180
|
// console.info(element);
|
|
169
181
|
|
|
170
182
|
let value: undefined | number | boolean | string;
|
|
@@ -213,6 +225,8 @@ export class Importer {
|
|
|
213
225
|
|
|
214
226
|
if (formula) {
|
|
215
227
|
|
|
228
|
+
// console.info("F", formula);
|
|
229
|
+
|
|
216
230
|
// doing it like this is sloppy (also does not work properly).
|
|
217
231
|
value = '=' + formula.replace(/^_xll\./g, '');
|
|
218
232
|
|
|
@@ -221,10 +235,66 @@ export class Importer {
|
|
|
221
235
|
value = formula;
|
|
222
236
|
}
|
|
223
237
|
else {
|
|
238
|
+
|
|
224
239
|
const parse_result = this.parser.Parse(formula); // l10n?
|
|
225
240
|
if (parse_result.expression) {
|
|
226
|
-
|
|
241
|
+
|
|
242
|
+
const TrimPrefixes = (name: string) => {
|
|
243
|
+
|
|
244
|
+
if (/^_xll\./.test(name)) {
|
|
245
|
+
name = name.substring(5);
|
|
246
|
+
}
|
|
247
|
+
if (/^_xlfn\./.test(name)) {
|
|
248
|
+
console.info("xlfn:", name);
|
|
249
|
+
name = name.substring(6);
|
|
250
|
+
}
|
|
251
|
+
if (/^_xlws\./.test(name)) {
|
|
252
|
+
console.info("xlws:", name);
|
|
253
|
+
name = name.substring(6);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return name;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const TreeWalker = (unit: ExpressionUnit) => {
|
|
260
|
+
|
|
227
261
|
if (unit.type === 'call') {
|
|
262
|
+
|
|
263
|
+
// if we see _xlfn.SINGLE, translate that into
|
|
264
|
+
// @ + the name of the first parameter...
|
|
265
|
+
//
|
|
266
|
+
// this is solving the case where single appears, but it
|
|
267
|
+
// doesn't solve the case where it does not appear -- they
|
|
268
|
+
// may be using the array flag of the cell as an indicator?
|
|
269
|
+
// but how then do they know it _should_ be an array? not
|
|
270
|
+
// sure, and I don't see any other indication.
|
|
271
|
+
|
|
272
|
+
if (/^_xlfn\.single/i.test(unit.name)) {
|
|
273
|
+
|
|
274
|
+
const first = unit.args[0];
|
|
275
|
+
if (first.type === 'call') {
|
|
276
|
+
|
|
277
|
+
// we could do this in place, we don't need to copy...
|
|
278
|
+
// although it seems like a good idea. also watch out,
|
|
279
|
+
// these SINGLEs could be nested.
|
|
280
|
+
|
|
281
|
+
const replacement: UnitCall = JSON.parse(JSON.stringify(first));
|
|
282
|
+
replacement.name = '@' + TrimPrefixes(replacement.name);
|
|
283
|
+
|
|
284
|
+
for (let i = 0; i < replacement.args.length; i++) {
|
|
285
|
+
replacement.args[i] = this.parser.Walk2(replacement.args[i], TreeWalker);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return replacement;
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
console.info("_xlfn.SINGLE unexpected argument", unit.args[0]);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
unit.name = TrimPrefixes(unit.name);
|
|
296
|
+
|
|
297
|
+
/*
|
|
228
298
|
if (/^_xll\./.test(unit.name)) {
|
|
229
299
|
unit.name = unit.name.substring(5);
|
|
230
300
|
}
|
|
@@ -236,9 +306,15 @@ export class Importer {
|
|
|
236
306
|
console.info("xlws:", unit.name);
|
|
237
307
|
unit.name = unit.name.substring(6);
|
|
238
308
|
}
|
|
309
|
+
*/
|
|
310
|
+
|
|
239
311
|
}
|
|
240
312
|
return true;
|
|
241
|
-
|
|
313
|
+
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
parse_result.expression = this.parser.Walk2(parse_result.expression, TreeWalker);
|
|
317
|
+
|
|
242
318
|
value = '=' + this.parser.Render(parse_result.expression, { missing: '' });
|
|
243
319
|
}
|
|
244
320
|
}
|
|
@@ -274,10 +350,38 @@ export class Importer {
|
|
|
274
350
|
}
|
|
275
351
|
}
|
|
276
352
|
|
|
353
|
+
//
|
|
354
|
+
// arrays and spill/dynamic arrays
|
|
355
|
+
//
|
|
356
|
+
|
|
277
357
|
if (typeof element.f !== 'string' && element.f.a$?.t === 'array') {
|
|
278
358
|
const translated = sheet.TranslateAddress(element.f.a$.ref || '');
|
|
279
|
-
|
|
280
|
-
|
|
359
|
+
|
|
360
|
+
// why are we checking "is_range" here? this should be valid
|
|
361
|
+
// even if the ref attribute is one cell, if it explicitly
|
|
362
|
+
// says t="array"
|
|
363
|
+
|
|
364
|
+
// we will need to adjust it, though? yes, because the lists
|
|
365
|
+
// only accept ranges. note that range type has a superfluous
|
|
366
|
+
// sheet parameter? ...
|
|
367
|
+
|
|
368
|
+
let range = translated;
|
|
369
|
+
if (!is_range(range)) {
|
|
370
|
+
range = {
|
|
371
|
+
to: { ...range },
|
|
372
|
+
from: { ...range },
|
|
373
|
+
sheet: range.sheet,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// if (is_range(translated))
|
|
378
|
+
{
|
|
379
|
+
if (metadata_flags['dynamic-array']) {
|
|
380
|
+
dynamic_arrays.push(ShiftRange(range, -1, -1));
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
arrays.push(ShiftRange(range, -1, -1));
|
|
384
|
+
}
|
|
281
385
|
}
|
|
282
386
|
}
|
|
283
387
|
|
|
@@ -317,12 +421,14 @@ export class Importer {
|
|
|
317
421
|
// but perhaps we should check that... although at this point we have
|
|
318
422
|
// already added the array so we need to check for root
|
|
319
423
|
|
|
320
|
-
for (const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
424
|
+
for (const set of [arrays, dynamic_arrays]) {
|
|
425
|
+
for (const array of set) {
|
|
426
|
+
if (InRange(array, shifted) && (shifted.row !== array.from.row || shifted.col !== array.from.col)) {
|
|
427
|
+
calculated_type = type;
|
|
428
|
+
calculated_value = value;
|
|
429
|
+
value = undefined;
|
|
430
|
+
type = 'undefined'; // ValueType.undefined;
|
|
431
|
+
}
|
|
326
432
|
}
|
|
327
433
|
}
|
|
328
434
|
|
|
@@ -369,6 +475,20 @@ export class Importer {
|
|
|
369
475
|
}
|
|
370
476
|
}
|
|
371
477
|
|
|
478
|
+
for (const range of dynamic_arrays) {
|
|
479
|
+
if (InRange(range, shifted)) {
|
|
480
|
+
result.spill = {
|
|
481
|
+
start: {
|
|
482
|
+
row: range.from.row,
|
|
483
|
+
column: range.from.col,
|
|
484
|
+
}, end: {
|
|
485
|
+
row: range.to.row,
|
|
486
|
+
column: range.to.col,
|
|
487
|
+
},
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
372
492
|
for (const range of arrays) {
|
|
373
493
|
if (InRange(range, shifted)) {
|
|
374
494
|
result.area = {
|
|
@@ -675,6 +795,7 @@ export class Importer {
|
|
|
675
795
|
const data: CellParseResult[] = [];
|
|
676
796
|
const shared_formulae: {[index: string]: SharedFormula} = {};
|
|
677
797
|
const arrays: RangeType[] = [];
|
|
798
|
+
const dynamic_arrays: RangeType[] = [];
|
|
678
799
|
const merges: RangeType[] = [];
|
|
679
800
|
const conditional_formats: ConditionalFormat[] = [];
|
|
680
801
|
const links: HyperlinkType[] = [];
|
|
@@ -967,7 +1088,7 @@ export class Importer {
|
|
|
967
1088
|
const cells = row.c ? Array.isArray(row.c) ? row.c : [row.c] : [];
|
|
968
1089
|
|
|
969
1090
|
for (const element of cells) {
|
|
970
|
-
const cell = this.ParseCell(sheet, element as unknown as CellElementType, shared_formulae, arrays, merges, links); // , validations);
|
|
1091
|
+
const cell = this.ParseCell(sheet, element as unknown as CellElementType, shared_formulae, arrays, dynamic_arrays, merges, links); // , validations);
|
|
971
1092
|
if (cell) {
|
|
972
1093
|
data.push(cell);
|
|
973
1094
|
}
|
|
@@ -1254,12 +1375,21 @@ export class Importer {
|
|
|
1254
1375
|
|
|
1255
1376
|
break;
|
|
1256
1377
|
|
|
1378
|
+
case ChartType.Histogram:
|
|
1379
|
+
type = 'treb-chart';
|
|
1380
|
+
func = 'Histogram.Plot';
|
|
1381
|
+
if (series?.length) {
|
|
1382
|
+
// ...
|
|
1383
|
+
}
|
|
1384
|
+
args[1] = descriptor.chart.title;
|
|
1385
|
+
break;
|
|
1386
|
+
|
|
1257
1387
|
case ChartType.Box:
|
|
1258
1388
|
type = 'treb-chart';
|
|
1259
1389
|
func = 'Box.Plot';
|
|
1260
1390
|
if (series?.length) {
|
|
1261
1391
|
args[0] = `Group(${series.map(s => `Series(${s.title || ''},,${s.values||''})`).join(', ')})`;
|
|
1262
|
-
console.info("S?", {series}, args[0])
|
|
1392
|
+
// console.info("S?", {series}, args[0])
|
|
1263
1393
|
}
|
|
1264
1394
|
args[1] = descriptor.chart.title;
|
|
1265
1395
|
break;
|
|
@@ -1317,6 +1447,10 @@ export class Importer {
|
|
|
1317
1447
|
args[1] = series[0]?.categories || '';
|
|
1318
1448
|
}
|
|
1319
1449
|
|
|
1450
|
+
if (descriptor.chart.type === ChartType.Column && descriptor.chart.flags?.includes('stacked')) {
|
|
1451
|
+
args[3] = '"stacked"';
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1320
1454
|
break;
|
|
1321
1455
|
}
|
|
1322
1456
|
|