@trebco/treb 23.6.5 → 25.0.0-rc2
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/.eslintignore +8 -0
- package/.eslintrc.js +164 -0
- package/README-shadow-DOM.md +88 -0
- package/README.md +37 -130
- package/api-config.json +29 -0
- package/api-generator/api-generator-types.ts +82 -0
- package/api-generator/api-generator.ts +1172 -0
- package/api-generator/package.json +3 -0
- package/build/treb-spreadsheet.mjs +14 -0
- package/{treb.d.ts → build/treb.d.ts} +323 -271
- package/esbuild-custom-element.mjs +336 -0
- package/esbuild.js +305 -0
- package/package.json +49 -14
- package/treb-base-types/package.json +5 -0
- package/treb-base-types/src/api_types.ts +36 -0
- package/treb-base-types/src/area.ts +583 -0
- package/treb-base-types/src/basic_types.ts +45 -0
- package/treb-base-types/src/cell.ts +612 -0
- package/treb-base-types/src/cells.ts +1066 -0
- package/treb-base-types/src/color.ts +124 -0
- package/treb-base-types/src/import.ts +71 -0
- package/treb-base-types/src/index-standalone.ts +29 -0
- package/treb-base-types/src/index.ts +42 -0
- package/treb-base-types/src/layout.ts +47 -0
- package/treb-base-types/src/localization.ts +187 -0
- package/treb-base-types/src/rectangle.ts +145 -0
- package/treb-base-types/src/render_text.ts +72 -0
- package/treb-base-types/src/style.ts +545 -0
- package/treb-base-types/src/table.ts +109 -0
- package/treb-base-types/src/text_part.ts +54 -0
- package/treb-base-types/src/theme.ts +608 -0
- package/treb-base-types/src/union.ts +152 -0
- package/treb-base-types/src/value-type.ts +164 -0
- package/treb-base-types/style/resizable.css +59 -0
- package/treb-calculator/modern.tsconfig.json +11 -0
- package/treb-calculator/package.json +5 -0
- package/treb-calculator/src/calculator.ts +2546 -0
- package/treb-calculator/src/complex-math.ts +558 -0
- package/treb-calculator/src/dag/array-vertex.ts +198 -0
- package/treb-calculator/src/dag/graph.ts +951 -0
- package/treb-calculator/src/dag/leaf_vertex.ts +118 -0
- package/treb-calculator/src/dag/spreadsheet_vertex.ts +327 -0
- package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +44 -0
- package/treb-calculator/src/dag/vertex.ts +352 -0
- package/treb-calculator/src/descriptors.ts +162 -0
- package/treb-calculator/src/expression-calculator.ts +1069 -0
- package/treb-calculator/src/function-error.ts +103 -0
- package/treb-calculator/src/function-library.ts +103 -0
- package/treb-calculator/src/functions/base-functions.ts +1214 -0
- package/treb-calculator/src/functions/checkbox.ts +164 -0
- package/treb-calculator/src/functions/complex-functions.ts +253 -0
- package/treb-calculator/src/functions/finance-functions.ts +399 -0
- package/treb-calculator/src/functions/information-functions.ts +102 -0
- package/treb-calculator/src/functions/matrix-functions.ts +182 -0
- package/treb-calculator/src/functions/sparkline.ts +335 -0
- package/treb-calculator/src/functions/statistics-functions.ts +350 -0
- package/treb-calculator/src/functions/text-functions.ts +298 -0
- package/treb-calculator/src/index.ts +27 -0
- package/treb-calculator/src/notifier-types.ts +59 -0
- package/treb-calculator/src/primitives.ts +428 -0
- package/treb-calculator/src/utilities.ts +305 -0
- package/treb-charts/package.json +5 -0
- package/treb-charts/src/chart-functions.ts +156 -0
- package/treb-charts/src/chart-types.ts +230 -0
- package/treb-charts/src/chart.ts +1288 -0
- package/treb-charts/src/index.ts +24 -0
- package/treb-charts/src/main.ts +37 -0
- package/treb-charts/src/rectangle.ts +52 -0
- package/treb-charts/src/renderer.ts +1841 -0
- package/treb-charts/src/util.ts +122 -0
- package/treb-charts/style/charts.scss +221 -0
- package/treb-charts/style/old-charts.scss +250 -0
- package/treb-embed/markup/layout.html +137 -0
- package/treb-embed/markup/toolbar.html +175 -0
- package/treb-embed/modern.tsconfig.json +25 -0
- package/treb-embed/src/custom-element/content-types.d.ts +18 -0
- package/treb-embed/src/custom-element/global.d.ts +11 -0
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +1228 -0
- package/treb-embed/src/custom-element/treb-global.ts +44 -0
- package/treb-embed/src/custom-element/treb-spreadsheet-element.ts +52 -0
- package/treb-embed/src/embedded-spreadsheet.ts +5358 -0
- package/treb-embed/src/index.ts +16 -0
- package/treb-embed/src/language-model.ts +41 -0
- package/treb-embed/src/options.ts +298 -0
- package/treb-embed/src/progress-dialog.ts +228 -0
- package/treb-embed/src/selection-state.ts +16 -0
- package/treb-embed/src/spinner.ts +42 -0
- package/treb-embed/src/toolbar-message.ts +96 -0
- package/treb-embed/src/types.ts +167 -0
- package/treb-embed/style/autocomplete.scss +103 -0
- package/treb-embed/style/dark-theme.scss +114 -0
- package/treb-embed/style/defaults.scss +36 -0
- package/treb-embed/style/dialog.scss +181 -0
- package/treb-embed/style/dropdown-select.scss +101 -0
- package/treb-embed/style/formula-bar.scss +193 -0
- package/treb-embed/style/grid.scss +374 -0
- package/treb-embed/style/layout.scss +424 -0
- package/treb-embed/style/mouse-mask.scss +67 -0
- package/treb-embed/style/note.scss +92 -0
- package/treb-embed/style/overlay-editor.scss +102 -0
- package/treb-embed/style/spinner.scss +92 -0
- package/treb-embed/style/tab-bar.scss +228 -0
- package/treb-embed/style/table.scss +80 -0
- package/treb-embed/style/theme-defaults.scss +444 -0
- package/treb-embed/style/toolbar.scss +416 -0
- package/treb-embed/style/tooltip.scss +68 -0
- package/treb-embed/style/treb-icons.scss +130 -0
- package/treb-embed/style/treb-spreadsheet-element.scss +20 -0
- package/treb-embed/style/z-index.scss +43 -0
- package/treb-export/docs/charts.md +68 -0
- package/treb-export/modern.tsconfig.json +19 -0
- package/treb-export/package.json +4 -0
- package/treb-export/src/address-type.ts +77 -0
- package/treb-export/src/base-template.ts +22 -0
- package/treb-export/src/column-width.ts +85 -0
- package/treb-export/src/drawing2/chart-template-components2.ts +389 -0
- package/treb-export/src/drawing2/chart2.ts +282 -0
- package/treb-export/src/drawing2/column-chart-template2.ts +521 -0
- package/treb-export/src/drawing2/donut-chart-template2.ts +296 -0
- package/treb-export/src/drawing2/drawing2.ts +355 -0
- package/treb-export/src/drawing2/embedded-image.ts +71 -0
- package/treb-export/src/drawing2/scatter-chart-template2.ts +555 -0
- package/treb-export/src/export-worker/export-worker.ts +99 -0
- package/treb-export/src/export-worker/index-modern.ts +22 -0
- package/treb-export/src/export2.ts +2204 -0
- package/treb-export/src/import2.ts +882 -0
- package/treb-export/src/relationship.ts +36 -0
- package/treb-export/src/shared-strings2.ts +128 -0
- package/treb-export/src/template-2.ts +22 -0
- package/treb-export/src/unescape_xml.ts +47 -0
- package/treb-export/src/workbook-sheet2.ts +182 -0
- package/treb-export/src/workbook-style2.ts +1285 -0
- package/treb-export/src/workbook-theme2.ts +88 -0
- package/treb-export/src/workbook2.ts +491 -0
- package/treb-export/src/xml-utils.ts +201 -0
- package/treb-export/template/base/[Content_Types].xml +2 -0
- package/treb-export/template/base/_rels/.rels +2 -0
- package/treb-export/template/base/docProps/app.xml +2 -0
- package/treb-export/template/base/docProps/core.xml +12 -0
- package/treb-export/template/base/xl/_rels/workbook.xml.rels +2 -0
- package/treb-export/template/base/xl/sharedStrings.xml +2 -0
- package/treb-export/template/base/xl/styles.xml +2 -0
- package/treb-export/template/base/xl/theme/theme1.xml +2 -0
- package/treb-export/template/base/xl/workbook.xml +2 -0
- package/treb-export/template/base/xl/worksheets/sheet1.xml +2 -0
- package/treb-export/template/base.xlsx +0 -0
- package/treb-format/package.json +8 -0
- package/treb-format/src/format.test.ts +213 -0
- package/treb-format/src/format.ts +942 -0
- package/treb-format/src/format_cache.ts +199 -0
- package/treb-format/src/format_parser.ts +723 -0
- package/treb-format/src/index.ts +25 -0
- package/treb-format/src/number_format_section.ts +100 -0
- package/treb-format/src/value_parser.ts +337 -0
- package/treb-grid/package.json +5 -0
- package/treb-grid/src/editors/autocomplete.ts +394 -0
- package/treb-grid/src/editors/autocomplete_matcher.ts +260 -0
- package/treb-grid/src/editors/formula_bar.ts +473 -0
- package/treb-grid/src/editors/formula_editor_base.ts +910 -0
- package/treb-grid/src/editors/overlay_editor.ts +511 -0
- package/treb-grid/src/index.ts +37 -0
- package/treb-grid/src/layout/base_layout.ts +2618 -0
- package/treb-grid/src/layout/grid_layout.ts +299 -0
- package/treb-grid/src/layout/rectangle_cache.ts +86 -0
- package/treb-grid/src/render/selection-renderer.ts +414 -0
- package/treb-grid/src/render/svg_header_overlay.ts +93 -0
- package/treb-grid/src/render/svg_selection_block.ts +187 -0
- package/treb-grid/src/render/tile_renderer.ts +2122 -0
- package/treb-grid/src/types/annotation.ts +216 -0
- package/treb-grid/src/types/border_constants.ts +34 -0
- package/treb-grid/src/types/clipboard_data.ts +31 -0
- package/treb-grid/src/types/data_model.ts +334 -0
- package/treb-grid/src/types/drag_mask.ts +81 -0
- package/treb-grid/src/types/grid.ts +7743 -0
- package/treb-grid/src/types/grid_base.ts +3644 -0
- package/treb-grid/src/types/grid_command.ts +470 -0
- package/treb-grid/src/types/grid_events.ts +124 -0
- package/treb-grid/src/types/grid_options.ts +97 -0
- package/treb-grid/src/types/grid_selection.ts +60 -0
- package/treb-grid/src/types/named_range.ts +369 -0
- package/treb-grid/src/types/scale-control.ts +202 -0
- package/treb-grid/src/types/serialize_options.ts +72 -0
- package/treb-grid/src/types/set_range_options.ts +52 -0
- package/treb-grid/src/types/sheet.ts +3099 -0
- package/treb-grid/src/types/sheet_types.ts +95 -0
- package/treb-grid/src/types/tab_bar.ts +464 -0
- package/treb-grid/src/types/tile.ts +59 -0
- package/treb-grid/src/types/update_flags.ts +75 -0
- package/treb-grid/src/util/dom_utilities.ts +44 -0
- package/treb-grid/src/util/fontmetrics2.ts +179 -0
- package/treb-grid/src/util/ua.ts +104 -0
- package/treb-logo.svg +18 -0
- package/treb-parser/package.json +5 -0
- package/treb-parser/src/csv-parser.ts +122 -0
- package/treb-parser/src/index.ts +25 -0
- package/treb-parser/src/md-parser.ts +526 -0
- package/treb-parser/src/parser-types.ts +397 -0
- package/treb-parser/src/parser.test.ts +298 -0
- package/treb-parser/src/parser.ts +2673 -0
- package/treb-utils/package.json +5 -0
- package/treb-utils/src/dispatch.ts +57 -0
- package/treb-utils/src/event_source.ts +147 -0
- package/treb-utils/src/ievent_source.ts +33 -0
- package/treb-utils/src/index.ts +31 -0
- package/treb-utils/src/measurement.ts +174 -0
- package/treb-utils/src/resizable.ts +160 -0
- package/treb-utils/src/scale.ts +137 -0
- package/treb-utils/src/serialize_html.ts +124 -0
- package/treb-utils/src/template.ts +70 -0
- package/treb-utils/src/validate_uri.ts +61 -0
- package/tsconfig.json +10 -0
- package/tsproject.json +30 -0
- package/util/license-plugin-esbuild.js +86 -0
- package/util/list-css-vars.sh +46 -0
- package/README-esm.md +0 -37
- package/treb-bundle.css +0 -2
- package/treb-bundle.mjs +0 -15
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2023 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
//import * as JSZip from 'jszip';
|
|
23
|
+
import JSZip from 'jszip';
|
|
24
|
+
|
|
25
|
+
import { AnchoredChartDescription, ChartType, TableDescription, Workbook } from './workbook2';
|
|
26
|
+
import { Parser, ParseResult } from 'treb-parser';
|
|
27
|
+
import { is_range, RangeType, ShiftRange, InRange, AddressType, is_address, HyperlinkType } from './address-type';
|
|
28
|
+
import { ImportedSheetData, AnchoredAnnotation, CellParseResult, ValueType, AnnotationLayout, Corner as LayoutCorner, ICellAddress, DataValidation, ValidationType, IArea } from 'treb-base-types/src';
|
|
29
|
+
import { Sheet, VisibleState } from './workbook-sheet2';
|
|
30
|
+
import type { CellAnchor } from './drawing2/drawing2';
|
|
31
|
+
import { XMLUtils } from './xml-utils';
|
|
32
|
+
|
|
33
|
+
// import { one_hundred_pixels } from './constants';
|
|
34
|
+
import { ColumnWidthToPixels } from './column-width';
|
|
35
|
+
|
|
36
|
+
interface SharedFormula {
|
|
37
|
+
row: number;
|
|
38
|
+
column: number;
|
|
39
|
+
formula: string;
|
|
40
|
+
parse_result: ParseResult;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface SharedFormulaMap { [index: string]: SharedFormula }
|
|
44
|
+
|
|
45
|
+
export class Importer {
|
|
46
|
+
|
|
47
|
+
// FIXME: need a way to share/pass parser flags
|
|
48
|
+
public parser = new Parser();
|
|
49
|
+
|
|
50
|
+
public workbook?: Workbook;
|
|
51
|
+
|
|
52
|
+
public archive?: JSZip;
|
|
53
|
+
|
|
54
|
+
public async Init(data: string | JSZip): Promise<void> {
|
|
55
|
+
|
|
56
|
+
if (typeof data === 'string') {
|
|
57
|
+
this.archive = await JSZip().loadAsync(data);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.archive = data;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this.archive) {
|
|
64
|
+
this.workbook = new Workbook(this.archive);
|
|
65
|
+
await this.workbook.Init();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** FIXME: accessor */
|
|
71
|
+
public SheetCount(): number {
|
|
72
|
+
return this.workbook?.sheet_count || 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public ParseCell(
|
|
76
|
+
sheet: Sheet,
|
|
77
|
+
element: {
|
|
78
|
+
a$: {
|
|
79
|
+
r?: string;
|
|
80
|
+
t?: string;
|
|
81
|
+
s?: string;
|
|
82
|
+
};
|
|
83
|
+
v?: string|number; // is this never an object? (note: booleans are numbers in Excel)
|
|
84
|
+
f?: string|{
|
|
85
|
+
t$: string;
|
|
86
|
+
a$?: {
|
|
87
|
+
si?: string;
|
|
88
|
+
t?: string;
|
|
89
|
+
ref?: string;
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}, // ElementTree.Element,
|
|
93
|
+
shared_formulae: SharedFormulaMap,
|
|
94
|
+
arrays: RangeType[],
|
|
95
|
+
merges: RangeType[],
|
|
96
|
+
links: HyperlinkType[],
|
|
97
|
+
validations: Array<{ address: ICellAddress, validation: DataValidation }>,
|
|
98
|
+
): CellParseResult | undefined {
|
|
99
|
+
|
|
100
|
+
// must have, at minimum, an address (must be a single cell? FIXME)
|
|
101
|
+
const address_attr = element.a$?.r;
|
|
102
|
+
if (!address_attr) {
|
|
103
|
+
console.warn('cell missing address');
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const address = sheet.TranslateAddress(address_attr);
|
|
108
|
+
if (is_range(address)) {
|
|
109
|
+
console.warn('cell has range address');
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// console.info(element);
|
|
114
|
+
|
|
115
|
+
let value: undefined | number | boolean | string;
|
|
116
|
+
let type: ValueType = ValueType.undefined;
|
|
117
|
+
|
|
118
|
+
let calculated_value: undefined | number | boolean | string;
|
|
119
|
+
let calculated_type: ValueType = ValueType.undefined;
|
|
120
|
+
|
|
121
|
+
// QUESTIONS:
|
|
122
|
+
//
|
|
123
|
+
// 1. is v always a value, or can it be an object?
|
|
124
|
+
// if it is always a value, we can drop some of the
|
|
125
|
+
// casting stuff below
|
|
126
|
+
//
|
|
127
|
+
// 2. can we reframe f so it's always an object, moving the string
|
|
128
|
+
// inside -- to remove the simple case and remove all the testing?
|
|
129
|
+
//
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
// assuming we have single element per tag...
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
const mapped: { [index: string]: ElementTree.Element } = {};
|
|
136
|
+
for (const child of element.getchildren()) {
|
|
137
|
+
if (child.tag) mapped[child.tag.toString()] = child;
|
|
138
|
+
}
|
|
139
|
+
*/
|
|
140
|
+
|
|
141
|
+
// console.info(address, 'e', element, 'm', mapped);
|
|
142
|
+
|
|
143
|
+
if (element.a$?.t && element.a$.t === 's') {
|
|
144
|
+
type = ValueType.string;
|
|
145
|
+
if (typeof element.v !== undefined) {
|
|
146
|
+
const index = Number(element.v);
|
|
147
|
+
if (!isNaN(index) && sheet.shared_strings) {
|
|
148
|
+
value = sheet.shared_strings.Get(index) || '';
|
|
149
|
+
if (value[0] === '=') { value = '\'' + value; }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
if (typeof element.f !== 'undefined') {
|
|
155
|
+
type = ValueType.formula;
|
|
156
|
+
|
|
157
|
+
const formula = (typeof element.f === 'string' ? element.f : element.f.t$) || '';
|
|
158
|
+
|
|
159
|
+
if (formula) {
|
|
160
|
+
|
|
161
|
+
// doing it like this is sloppy (also does not work properly).
|
|
162
|
+
value = '=' + formula.replace(/^_xll\./g, '');
|
|
163
|
+
|
|
164
|
+
const parse_result = this.parser.Parse(formula); // l10n?
|
|
165
|
+
if (parse_result.expression) {
|
|
166
|
+
this.parser.Walk(parse_result.expression, (unit) => {
|
|
167
|
+
if (unit.type === 'call' && /^_xll\./.test(unit.name)) {
|
|
168
|
+
unit.name = unit.name.substr(5);
|
|
169
|
+
}
|
|
170
|
+
return true;
|
|
171
|
+
});
|
|
172
|
+
value = '=' + this.parser.Render(parse_result.expression, { missing: '' });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (typeof element.f !== 'string') {
|
|
176
|
+
if (element.f.a$?.t === 'shared' && element.f.a$.si) {
|
|
177
|
+
shared_formulae[element.f.a$.si] = {
|
|
178
|
+
row: address.row - 1,
|
|
179
|
+
column: address.col - 1,
|
|
180
|
+
formula: value,
|
|
181
|
+
parse_result: this.parser.Parse(value),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
}
|
|
187
|
+
else if ((typeof element.f !== 'string') && element.f.a$?.t === 'shared' && element.f.a$.si) {
|
|
188
|
+
const f = shared_formulae[element.f.a$.si];
|
|
189
|
+
if (f) {
|
|
190
|
+
if (f.parse_result.expression) {
|
|
191
|
+
value = '=' + this.parser.Render(f.parse_result.expression, {
|
|
192
|
+
offset: {
|
|
193
|
+
rows: address.row - 1 - f.row,
|
|
194
|
+
columns: address.col - 1 - f.column,
|
|
195
|
+
},
|
|
196
|
+
missing: ''
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
else value = f.formula;
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// console.info("MISSING SHARED", mapped.f.attrib.si);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (typeof element.f !== 'string' && element.f.a$?.t === 'array') {
|
|
207
|
+
const translated = sheet.TranslateAddress(element.f.a$.ref || '');
|
|
208
|
+
if (is_range(translated)) {
|
|
209
|
+
arrays.push(ShiftRange(translated, -1, -1));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (typeof element.v !== 'undefined') {
|
|
214
|
+
const num = Number(element.v.toString());
|
|
215
|
+
if (!isNaN(num)) {
|
|
216
|
+
calculated_type = ValueType.number;
|
|
217
|
+
calculated_value = num;
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
calculated_type = ValueType.string;
|
|
221
|
+
calculated_value = element.v.toString();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
}
|
|
226
|
+
else if (typeof element.v !== 'undefined') {
|
|
227
|
+
const num = Number(element.v.toString());
|
|
228
|
+
if (!isNaN(num)) {
|
|
229
|
+
type = ValueType.number;
|
|
230
|
+
value = num;
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
type = ValueType.string;
|
|
234
|
+
value = element.v.toString();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const shifted: AddressType = { row: address.row - 1, col: address.col - 1 };
|
|
240
|
+
|
|
241
|
+
// check if we are in an array. we're relying on the fact that
|
|
242
|
+
// the array head is the top-left, which I _think_ is universal,
|
|
243
|
+
// but perhaps we should check that... although at this point we have
|
|
244
|
+
// already added the array so we need to check for root
|
|
245
|
+
|
|
246
|
+
for (const array of arrays) {
|
|
247
|
+
if (InRange(array, shifted) && (shifted.row !== array.from.row || shifted.col !== array.from.col)) {
|
|
248
|
+
calculated_type = type;
|
|
249
|
+
calculated_value = value;
|
|
250
|
+
value = undefined;
|
|
251
|
+
type = ValueType.undefined;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const result: CellParseResult = {
|
|
256
|
+
row: shifted.row, column: shifted.col, value, type,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
if (typeof calculated_value !== 'undefined') {
|
|
260
|
+
result.calculated_type = calculated_type;
|
|
261
|
+
result.calculated = calculated_value;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (element.a$?.s) {
|
|
265
|
+
result.style_ref = Number(element.a$.s);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
for (const link of links) {
|
|
269
|
+
if (link.address.row === address.row && link.address.col === address.col) {
|
|
270
|
+
result.hyperlink = link.reference;
|
|
271
|
+
// FIXME: pop?
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (const validation of validations) {
|
|
276
|
+
if (validation.address.row === shifted.row && validation.address.column === shifted.col) {
|
|
277
|
+
result.validation = validation.validation;
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
for (const range of merges) {
|
|
283
|
+
if (InRange(range, shifted)) {
|
|
284
|
+
result.merge_area = {
|
|
285
|
+
start: {
|
|
286
|
+
row: range.from.row,
|
|
287
|
+
column: range.from.col,
|
|
288
|
+
}, end: {
|
|
289
|
+
row: range.to.row,
|
|
290
|
+
column: range.to.col,
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
for (const range of arrays) {
|
|
297
|
+
if (InRange(range, shifted)) {
|
|
298
|
+
result.area = {
|
|
299
|
+
start: {
|
|
300
|
+
row: range.from.row,
|
|
301
|
+
column: range.from.col,
|
|
302
|
+
}, end: {
|
|
303
|
+
row: range.to.row,
|
|
304
|
+
column: range.to.col,
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return result;
|
|
312
|
+
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
public async GetSheet(index = 0): Promise<ImportedSheetData> {
|
|
316
|
+
|
|
317
|
+
if (!this.workbook) {
|
|
318
|
+
throw new Error('missing workbook');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const sheet = this.workbook.sheets[index];
|
|
322
|
+
// console.info(sheet.sheet_data);
|
|
323
|
+
|
|
324
|
+
// console.info(sheet.options.name);
|
|
325
|
+
|
|
326
|
+
// we want a sparse array
|
|
327
|
+
|
|
328
|
+
const data: CellParseResult[] = [];
|
|
329
|
+
const shared_formulae: {[index: string]: SharedFormula} = {};
|
|
330
|
+
const arrays: RangeType[] = [];
|
|
331
|
+
const merges: RangeType[] = [];
|
|
332
|
+
const links: HyperlinkType[] = [];
|
|
333
|
+
const validations: Array<{
|
|
334
|
+
address: ICellAddress,
|
|
335
|
+
validation: DataValidation,
|
|
336
|
+
}> = [];
|
|
337
|
+
const annotations: AnchoredAnnotation[] = [];
|
|
338
|
+
|
|
339
|
+
const FindAll = XMLUtils.FindAll.bind(XMLUtils, sheet.sheet_data);
|
|
340
|
+
|
|
341
|
+
// merges
|
|
342
|
+
|
|
343
|
+
const merge_cells = FindAll('worksheet/mergeCells/mergeCell');
|
|
344
|
+
|
|
345
|
+
for (const element of merge_cells) {
|
|
346
|
+
if (element.a$.ref) {
|
|
347
|
+
const merge = sheet.TranslateAddress(element.a$.ref);
|
|
348
|
+
if (is_range(merge)) {
|
|
349
|
+
merges.push(ShiftRange(merge, -1, -1));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// validation
|
|
355
|
+
|
|
356
|
+
const validation_entries = FindAll('worksheet/dataValidations/dataValidation');
|
|
357
|
+
for (const entry of validation_entries) {
|
|
358
|
+
const type = entry.a$?.type;
|
|
359
|
+
const ref = entry.a$?.sqref;
|
|
360
|
+
const formula = entry.formula1;
|
|
361
|
+
|
|
362
|
+
if (ref && formula && type === 'list') {
|
|
363
|
+
let address: ICellAddress|undefined;
|
|
364
|
+
let validation: DataValidation|undefined;
|
|
365
|
+
let parse_result = this.parser.Parse(ref);
|
|
366
|
+
|
|
367
|
+
// apparently these are encoded as ranges for merged cells...
|
|
368
|
+
|
|
369
|
+
if (parse_result.expression) {
|
|
370
|
+
if (parse_result.expression.type === 'address') {
|
|
371
|
+
address = parse_result.expression;
|
|
372
|
+
}
|
|
373
|
+
else if (parse_result.expression.type === 'range') {
|
|
374
|
+
address = parse_result.expression.start;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
parse_result = this.parser.Parse(formula);
|
|
379
|
+
if (parse_result.expression) {
|
|
380
|
+
if (parse_result.expression.type === 'range') {
|
|
381
|
+
validation = {
|
|
382
|
+
type: ValidationType.Range,
|
|
383
|
+
area: parse_result.expression,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
else if (parse_result.expression.type === 'literal') {
|
|
387
|
+
validation = {
|
|
388
|
+
type: ValidationType.List,
|
|
389
|
+
list: parse_result.expression.value.toString().split(/,/).map(value => {
|
|
390
|
+
const tmp = this.parser.Parse(value);
|
|
391
|
+
|
|
392
|
+
// if type is "group", that means we saw some spaces. this
|
|
393
|
+
// is (probably) an unquoted string literal. for the time
|
|
394
|
+
// being let's assume that. need a counterexample.
|
|
395
|
+
|
|
396
|
+
if (tmp.expression?.type === 'group' && /\s/.test(value)) {
|
|
397
|
+
return value;
|
|
398
|
+
}
|
|
399
|
+
if (tmp.expression?.type === 'literal') {
|
|
400
|
+
return tmp.expression.value;
|
|
401
|
+
}
|
|
402
|
+
if (tmp.expression?.type === 'identifier') {
|
|
403
|
+
return tmp.expression.name;
|
|
404
|
+
}
|
|
405
|
+
return undefined;
|
|
406
|
+
}),
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (address && validation) {
|
|
412
|
+
validations.push({address, validation});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// links
|
|
420
|
+
|
|
421
|
+
const hyperlinks = FindAll('worksheet/hyperlinks/hyperlink');
|
|
422
|
+
|
|
423
|
+
for (const child of hyperlinks) {
|
|
424
|
+
|
|
425
|
+
let address = sheet.TranslateAddress(child.a$?.ref || '');
|
|
426
|
+
if (is_range(address)) {
|
|
427
|
+
address = address.from;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
let text = '';
|
|
431
|
+
let reference = '';
|
|
432
|
+
|
|
433
|
+
if (child.a$ && child.a$['r:id']) {
|
|
434
|
+
|
|
435
|
+
text = 'remote link';
|
|
436
|
+
const relationship = sheet.rels[child.a$['r:id']];
|
|
437
|
+
if (relationship) {
|
|
438
|
+
reference = relationship.target || '';
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
reference = child.__location || '';
|
|
444
|
+
text = child.__display || '';
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
links.push({ address, reference, text });
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// base
|
|
451
|
+
|
|
452
|
+
let default_row_height = 21;
|
|
453
|
+
let default_column_width = 100; // ?
|
|
454
|
+
|
|
455
|
+
const sheet_format = sheet.sheet_data.worksheet?.sheetFormatPr;
|
|
456
|
+
if (sheet_format) {
|
|
457
|
+
if (sheet_format.a$?.defaultColWidth) {
|
|
458
|
+
const width = Number(sheet_format.a$.defaultColWidth);
|
|
459
|
+
if (!isNaN(width)) {
|
|
460
|
+
// default_column_width = Math.round(width / one_hundred_pixels * 100);
|
|
461
|
+
default_column_width = ColumnWidthToPixels(width);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (sheet_format.a$?.defaultRowHeight) {
|
|
465
|
+
const height = Number(sheet_format.a$.defaultRowHeight);
|
|
466
|
+
if (!isNaN(height)) {
|
|
467
|
+
default_row_height = Math.round(height * 4 / 3); // ??
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// data (and row heights)
|
|
473
|
+
|
|
474
|
+
const row_heights: number[] = [];
|
|
475
|
+
|
|
476
|
+
const rows = FindAll('worksheet/sheetData/row');
|
|
477
|
+
|
|
478
|
+
for (const row of rows) {
|
|
479
|
+
const row_index = row.a$?.r ? Number(row.a$.r) : 1;
|
|
480
|
+
|
|
481
|
+
let height = default_row_height;
|
|
482
|
+
if (row.a$?.ht) {
|
|
483
|
+
const num = Number(row.a$.ht);
|
|
484
|
+
if (!isNaN(num)) {
|
|
485
|
+
height = Math.round(num * 4 / 3); // seems to be the excel unit -> pixel ratio
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// if there's a height which is not === default height, but
|
|
490
|
+
// the customHeight attribute is not set, then it's been auto-sized.
|
|
491
|
+
// not sure that's something we need to care about necessarily...
|
|
492
|
+
|
|
493
|
+
if (height !== default_row_height) {
|
|
494
|
+
row_heights[row_index - 1] = height;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/*
|
|
498
|
+
if (row.a$?.ht && row.a$?.customHeight) {
|
|
499
|
+
const num = Number(row.a$.ht);
|
|
500
|
+
if (!isNaN(num)) {
|
|
501
|
+
row_heights[row_index - 1] = Math.round(num * 4 / 3); // seems to be the excel unit -> pixel ratio
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
*/
|
|
505
|
+
|
|
506
|
+
let cells = row.c || [];
|
|
507
|
+
if (!Array.isArray(cells)) { cells = [cells]; }
|
|
508
|
+
|
|
509
|
+
for (const element of cells) {
|
|
510
|
+
const cell = this.ParseCell(sheet, element, shared_formulae, arrays, merges, links, validations);
|
|
511
|
+
if (cell) {
|
|
512
|
+
data.push(cell);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const column_styles: number[] = [];
|
|
518
|
+
let default_column_style = -1;
|
|
519
|
+
const column_widths: number[] = [];
|
|
520
|
+
|
|
521
|
+
const columns = FindAll('worksheet/cols/col');
|
|
522
|
+
|
|
523
|
+
for (const child of columns) {
|
|
524
|
+
|
|
525
|
+
const min = Number(child.a$?.min);
|
|
526
|
+
const max = Number(child.a$?.max);
|
|
527
|
+
|
|
528
|
+
if (child.a$?.style) {
|
|
529
|
+
|
|
530
|
+
const style = Number(child.a$.style);
|
|
531
|
+
|
|
532
|
+
if (!isNaN(min) && !isNaN(max) && !isNaN(style)) {
|
|
533
|
+
|
|
534
|
+
// this is not the way to do this? for the time being
|
|
535
|
+
// it's OK because style doesn't need to extend past
|
|
536
|
+
// extent (but width does)
|
|
537
|
+
|
|
538
|
+
if (sheet.extent && max >= sheet.extent.to.col || max - min > 100) { // just spitballing on that last one
|
|
539
|
+
default_column_style = style;
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
for (let i = min; i <= max; i++) {
|
|
543
|
+
column_styles[i] = style;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
}
|
|
550
|
+
if (child.a$?.customWidth) {
|
|
551
|
+
|
|
552
|
+
let width = Number(child.a$.width);
|
|
553
|
+
|
|
554
|
+
if (!isNaN(min) && !isNaN(max) && !isNaN(width)) {
|
|
555
|
+
|
|
556
|
+
if (max === 16384) {
|
|
557
|
+
|
|
558
|
+
// ...
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
|
|
562
|
+
// otherwise it will set -> 16384
|
|
563
|
+
// if (sheet.extent) {
|
|
564
|
+
// max = Math.min(max, sheet.extent.to.col + 1);
|
|
565
|
+
// }
|
|
566
|
+
|
|
567
|
+
// width = Math.round(width / one_hundred_pixels * 100);
|
|
568
|
+
width = ColumnWidthToPixels(width);
|
|
569
|
+
|
|
570
|
+
for (let i = min; i <= max; i++) column_widths[i - 1] = width;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// --- import tables -------------------------------------------------------
|
|
578
|
+
|
|
579
|
+
const table_references = FindAll('worksheet/tableParts/tablePart')
|
|
580
|
+
for (const child of table_references) {
|
|
581
|
+
const rel = child.a$ ? child.a$['r:id'] : undefined;
|
|
582
|
+
if (rel) {
|
|
583
|
+
let reference = '';
|
|
584
|
+
|
|
585
|
+
const relationship = sheet.rels[rel];
|
|
586
|
+
if (relationship) {
|
|
587
|
+
reference = relationship.target || '';
|
|
588
|
+
const description = await this.workbook.ReadTable(reference);
|
|
589
|
+
if (description) {
|
|
590
|
+
|
|
591
|
+
// console.info({description});
|
|
592
|
+
|
|
593
|
+
const ref = sheet.TranslateAddress(description.ref);
|
|
594
|
+
const area: IArea = is_address(ref) ? {
|
|
595
|
+
start: { row: ref.row - 1, column: ref.col - 1},
|
|
596
|
+
end: { row: ref.row - 1, column: ref.col - 1},
|
|
597
|
+
} : {
|
|
598
|
+
start: { row: ref.from.row - 1, column: ref.from.col - 1},
|
|
599
|
+
end: { row: ref.to.row - 1, column: ref.to.col - 1},
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
for (const cell of data) {
|
|
603
|
+
if (cell.row === area.start.row && cell.column === area.start.column) {
|
|
604
|
+
cell.table = {
|
|
605
|
+
area,
|
|
606
|
+
name: description.name,
|
|
607
|
+
totals_row: (!!description.totals_row_count),
|
|
608
|
+
|
|
609
|
+
// NOTE: column headers are added on first load, we don't
|
|
610
|
+
// read them from here. not super efficient but we do it
|
|
611
|
+
// that way for regular loads as well
|
|
612
|
+
|
|
613
|
+
};
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// --- import drawings -----------------------------------------------------
|
|
625
|
+
|
|
626
|
+
// wip...
|
|
627
|
+
|
|
628
|
+
const drawings = FindAll('worksheet/drawing');
|
|
629
|
+
const chart_descriptors: AnchoredChartDescription[] = [];
|
|
630
|
+
|
|
631
|
+
for (const child of drawings) {
|
|
632
|
+
const rel = child.a$ ? child.a$['r:id'] : undefined;
|
|
633
|
+
if (rel) {
|
|
634
|
+
|
|
635
|
+
let reference = '';
|
|
636
|
+
|
|
637
|
+
const relationship = sheet.rels[rel];
|
|
638
|
+
if (relationship) {
|
|
639
|
+
reference = relationship.target || '';
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (reference) {
|
|
643
|
+
const drawing = await this.workbook.ReadDrawing(reference);
|
|
644
|
+
if (drawing && drawing.length) {
|
|
645
|
+
chart_descriptors.push(...drawing);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const AnchorToCorner = (anchor: CellAnchor): LayoutCorner => {
|
|
653
|
+
|
|
654
|
+
const result: LayoutCorner = {
|
|
655
|
+
address: {
|
|
656
|
+
row: anchor.row,
|
|
657
|
+
column: anchor.column,
|
|
658
|
+
},
|
|
659
|
+
offset: {
|
|
660
|
+
x: 0, // anchor.column_offset || 0, // FIXME: scale
|
|
661
|
+
y: 0, // anchor.row_offset || 0, // FIXME: scale
|
|
662
|
+
},
|
|
663
|
+
};
|
|
664
|
+
|
|
665
|
+
if (anchor.row_offset) {
|
|
666
|
+
let row_height = row_heights[anchor.row];
|
|
667
|
+
if (row_height === undefined) {
|
|
668
|
+
row_height = default_row_height; // FIXME
|
|
669
|
+
}
|
|
670
|
+
result.offset.y = (anchor.row_offset / 9525) / row_height;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (anchor.column_offset) {
|
|
674
|
+
let column_width = column_widths[anchor.column];
|
|
675
|
+
if (column_width === undefined) {
|
|
676
|
+
column_width = default_column_width;
|
|
677
|
+
}
|
|
678
|
+
result.offset.x = (anchor.column_offset / 9525) / column_width;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
return result;
|
|
682
|
+
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
for (const descriptor of chart_descriptors) {
|
|
686
|
+
if (descriptor && descriptor.chart) {
|
|
687
|
+
|
|
688
|
+
// convert the anchor to the annotation type
|
|
689
|
+
|
|
690
|
+
const layout: AnnotationLayout = {
|
|
691
|
+
tl: AnchorToCorner(descriptor.anchor.from),
|
|
692
|
+
br: AnchorToCorner(descriptor.anchor.to),
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
let type: string|undefined;
|
|
696
|
+
const args: Array<string|undefined> = [];
|
|
697
|
+
let func = '';
|
|
698
|
+
const series = descriptor.chart?.series;
|
|
699
|
+
|
|
700
|
+
switch(descriptor.chart.type) {
|
|
701
|
+
case ChartType.Scatter:
|
|
702
|
+
type = 'treb-chart';
|
|
703
|
+
func = 'Scatter.Line';
|
|
704
|
+
if (series && series.length) {
|
|
705
|
+
args[0] = `Group(${series.map(s => `Series(${s.title || ''},${s.categories||''},${s.values||''})` || '').join(', ')})`;
|
|
706
|
+
}
|
|
707
|
+
args[1] = descriptor.chart.title;
|
|
708
|
+
break;
|
|
709
|
+
|
|
710
|
+
case ChartType.Donut:
|
|
711
|
+
case ChartType.Pie:
|
|
712
|
+
|
|
713
|
+
func = descriptor.chart.type === ChartType.Donut ? 'Donut.Chart' : 'Pie.Chart';
|
|
714
|
+
type = 'treb-chart';
|
|
715
|
+
if (series && series[0]) {
|
|
716
|
+
args[0] = series[0].values;
|
|
717
|
+
args[1] = series[0]?.categories || '';
|
|
718
|
+
}
|
|
719
|
+
args[2] = descriptor.chart.title;
|
|
720
|
+
break;
|
|
721
|
+
|
|
722
|
+
case ChartType.Bar:
|
|
723
|
+
case ChartType.Column:
|
|
724
|
+
case ChartType.Line:
|
|
725
|
+
|
|
726
|
+
args[2] = descriptor.chart.title;
|
|
727
|
+
type = 'treb-chart';
|
|
728
|
+
switch (descriptor.chart.type) {
|
|
729
|
+
case ChartType.Bar:
|
|
730
|
+
func = 'Bar.Chart';
|
|
731
|
+
break;
|
|
732
|
+
case ChartType.Column:
|
|
733
|
+
func = 'Column.Chart';
|
|
734
|
+
break;
|
|
735
|
+
default:
|
|
736
|
+
func = 'Line.Chart';
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (series) {
|
|
740
|
+
if (series.length > 1) {
|
|
741
|
+
args[0] = `Group(${series.map(s => `Series(${s.title || ''},,${s.values||''})` || '').join(', ')})`;
|
|
742
|
+
}
|
|
743
|
+
else if (series.length === 1) {
|
|
744
|
+
if (series[0].title) {
|
|
745
|
+
args[0] = `Series(${series[0].title || ''},,${series[0].values||''})`;
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
args[0] = series[0].values;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
args[1] = series[0]?.categories || '';
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
const formula = `=${func}(${args.join(', ')})`;
|
|
758
|
+
// console.info('f', formula);
|
|
759
|
+
|
|
760
|
+
if (type && formula) {
|
|
761
|
+
annotations.push({
|
|
762
|
+
layout,
|
|
763
|
+
type,
|
|
764
|
+
formula,
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// /wip
|
|
772
|
+
|
|
773
|
+
const ext = FindAll('worksheet/extLst/ext');
|
|
774
|
+
for (const entry of ext) {
|
|
775
|
+
|
|
776
|
+
// find the prefix
|
|
777
|
+
let prefix = '';
|
|
778
|
+
for (const key of Object.keys(entry?.a$ || {})) {
|
|
779
|
+
const match = key.match(/^xmlns:(.*)$/);
|
|
780
|
+
if (match) {
|
|
781
|
+
prefix = match[1];
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
const groups = XMLUtils.FindAll(entry, `${prefix}:sparklineGroups/${prefix}:sparklineGroup`);
|
|
787
|
+
for (const group of groups) {
|
|
788
|
+
let func = 'Sparkline.line';
|
|
789
|
+
let reference = '';
|
|
790
|
+
let source = '';
|
|
791
|
+
|
|
792
|
+
if (group.a$?.type === 'column') {
|
|
793
|
+
func = 'Sparkline.column';
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// TODO: gap optional
|
|
797
|
+
// TODO: colors
|
|
798
|
+
|
|
799
|
+
const sparklines = XMLUtils.FindAll(group, `${prefix}:sparklines/${prefix}:sparkline`);
|
|
800
|
+
for (const sparkline of sparklines) {
|
|
801
|
+
for (const key of Object.keys(sparkline)) {
|
|
802
|
+
if (/:f$/.test(key)) {
|
|
803
|
+
source = sparkline[key];
|
|
804
|
+
}
|
|
805
|
+
else if (/:sqref$/.test(key)) {
|
|
806
|
+
reference = sparkline[key];
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
//
|
|
812
|
+
|
|
813
|
+
if (source && reference) {
|
|
814
|
+
const constructed_function = `=${func}(${source})`;
|
|
815
|
+
|
|
816
|
+
// 1: merges
|
|
817
|
+
// 2: maybe already in the list? need to filter
|
|
818
|
+
|
|
819
|
+
const translated = sheet.TranslateAddress(reference);
|
|
820
|
+
|
|
821
|
+
if (is_address(translated)) {
|
|
822
|
+
|
|
823
|
+
const result = {
|
|
824
|
+
row: translated.row - 1,
|
|
825
|
+
column: translated.col - 1,
|
|
826
|
+
value: constructed_function,
|
|
827
|
+
type: ValueType.formula,
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
let matched = false;
|
|
831
|
+
|
|
832
|
+
for (const element of data) {
|
|
833
|
+
if (element.row === result.row && element.column === result.column) {
|
|
834
|
+
matched = true;
|
|
835
|
+
element.type = ValueType.formula;
|
|
836
|
+
element.value = constructed_function;
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
if (!matched) {
|
|
842
|
+
data.push(result);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
//
|
|
849
|
+
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
const result: ImportedSheetData = {
|
|
855
|
+
name: sheet.options.name,
|
|
856
|
+
cells: data,
|
|
857
|
+
default_column_width,
|
|
858
|
+
column_widths,
|
|
859
|
+
row_heights,
|
|
860
|
+
annotations,
|
|
861
|
+
styles: this.workbook?.style_cache?.CellXfToStyles() || [],
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
if (sheet.visible_state === VisibleState.hidden || sheet.visible_state === VisibleState.very_hidden) {
|
|
865
|
+
result.hidden = true;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (default_column_style >= 0) {
|
|
869
|
+
result.sheet_style = default_column_style;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (column_styles.length) {
|
|
873
|
+
result.column_styles = column_styles;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
return result;
|
|
877
|
+
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
}
|