reamkit 1.12.0 → 1.14.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/esm/core/document-model/types.d.ts +3 -0
- package/dist/esm/core/ir/sheet.d.ts +49 -3
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +80 -1
- package/dist/esm/excel/comments-parser.d.ts +4 -0
- package/dist/esm/excel/comments-parser.js +104 -0
- package/dist/esm/excel/conditional-format.d.ts +2 -2
- package/dist/esm/excel/conditional-format.js +113 -11
- package/dist/esm/excel/form-control-parser.d.ts +6 -0
- package/dist/esm/excel/form-control-parser.js +29 -0
- package/dist/esm/excel/header-footer.d.ts +2 -0
- package/dist/esm/excel/header-footer.js +127 -0
- package/dist/esm/excel/index.d.ts +1 -1
- package/dist/esm/excel/print-model.d.ts +7 -2
- package/dist/esm/excel/print-model.js +218 -19
- package/dist/esm/excel/shared-strings-parser.d.ts +6 -1
- package/dist/esm/excel/shared-strings-parser.js +91 -12
- package/dist/esm/excel/sheet-drawing.d.ts +13 -1
- package/dist/esm/excel/sheet-drawing.js +47 -14
- package/dist/esm/excel/sheet-shape-parser.d.ts +4 -0
- package/dist/esm/excel/sheet-shape-parser.js +96 -0
- package/dist/esm/excel/sheet-to-flow.js +130 -3
- package/dist/esm/excel/slicer-parser.d.ts +15 -0
- package/dist/esm/excel/slicer-parser.js +76 -0
- package/dist/esm/excel/styles-parser.js +37 -0
- package/dist/esm/excel/table-parser.d.ts +10 -0
- package/dist/esm/excel/table-parser.js +36 -4
- package/dist/esm/excel/worksheet-parser.js +181 -1
- package/dist/esm/excel/xlsx-reader.js +203 -17
- package/dist/esm/excel/xlsx-writer.js +19 -3
- package/dist/esm/html/html-writer.js +4 -0
- package/dist/esm/layout/styled-layout.js +72 -1
- package/dist/esm/pptx/slide-parser.d.ts +4 -2
- package/dist/esm/pptx/slide-parser.js +1 -1
- package/package.json +1 -1
|
@@ -235,6 +235,8 @@ export interface CellBorders {
|
|
|
235
235
|
readonly left?: Border;
|
|
236
236
|
readonly insideH?: Border;
|
|
237
237
|
readonly insideV?: Border;
|
|
238
|
+
readonly diagonalDown?: Border;
|
|
239
|
+
readonly diagonalUp?: Border;
|
|
238
240
|
}
|
|
239
241
|
export interface CellMargins {
|
|
240
242
|
readonly top?: Pt;
|
|
@@ -275,6 +277,7 @@ export interface CellProperties {
|
|
|
275
277
|
readonly dataBar?: CellDataBar;
|
|
276
278
|
readonly icon?: CellIcon;
|
|
277
279
|
readonly sparkline?: CellSparkline;
|
|
280
|
+
readonly dropdown?: boolean;
|
|
278
281
|
}
|
|
279
282
|
export interface RowProperties {
|
|
280
283
|
readonly height?: Pt;
|
|
@@ -1,15 +1,55 @@
|
|
|
1
|
-
import { Chart, DocumentInfo } from '../document-model/index.js';
|
|
2
|
-
import { DefinedName, ParsedWorksheet, XlsxStyles } from '../spreadsheet-model/index.js';
|
|
3
|
-
import { ResourceStore } from './resources.js';
|
|
1
|
+
import { Chart, DocumentInfo, ShapeBlock } from '../document-model/index.js';
|
|
2
|
+
import { DefinedName, MergedRange, ParsedWorksheet, SheetRichRun, XlsxStyles } from '../spreadsheet-model/index.js';
|
|
3
|
+
import { ResourceId, ResourceStore } from './resources.js';
|
|
4
4
|
export interface SheetChartRef {
|
|
5
5
|
readonly chartPartPath: string;
|
|
6
6
|
readonly widthPt: number;
|
|
7
7
|
readonly heightPt: number;
|
|
8
8
|
}
|
|
9
|
+
export interface SheetImageRef {
|
|
10
|
+
readonly resourceId: ResourceId;
|
|
11
|
+
readonly widthPt: number;
|
|
12
|
+
readonly heightPt: number;
|
|
13
|
+
}
|
|
14
|
+
export interface SheetSlicerItem {
|
|
15
|
+
readonly label: string;
|
|
16
|
+
readonly selected: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface SheetSlicer {
|
|
19
|
+
readonly caption: string;
|
|
20
|
+
readonly columnCount: number;
|
|
21
|
+
readonly items: ReadonlyArray<SheetSlicerItem>;
|
|
22
|
+
readonly headerHex?: string;
|
|
23
|
+
readonly selectedHex?: string;
|
|
24
|
+
readonly headerTextHex?: string;
|
|
25
|
+
readonly selectedTextHex?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface SheetHyperlink {
|
|
28
|
+
readonly ref: MergedRange;
|
|
29
|
+
readonly url: string;
|
|
30
|
+
}
|
|
31
|
+
export interface SheetComment {
|
|
32
|
+
readonly ref: string;
|
|
33
|
+
readonly author?: string;
|
|
34
|
+
readonly text: string;
|
|
35
|
+
readonly threaded: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface SheetFormControl {
|
|
38
|
+
readonly name?: string;
|
|
39
|
+
readonly objectType?: string;
|
|
40
|
+
readonly checked?: boolean;
|
|
41
|
+
readonly value?: number;
|
|
42
|
+
}
|
|
9
43
|
export interface Sheet {
|
|
10
44
|
readonly name: string;
|
|
11
45
|
readonly grid: ParsedWorksheet;
|
|
12
46
|
readonly charts?: ReadonlyArray<SheetChartRef>;
|
|
47
|
+
readonly images?: ReadonlyArray<SheetImageRef>;
|
|
48
|
+
readonly shapes?: ReadonlyArray<ShapeBlock>;
|
|
49
|
+
readonly hyperlinks?: ReadonlyArray<SheetHyperlink>;
|
|
50
|
+
readonly slicers?: ReadonlyArray<SheetSlicer>;
|
|
51
|
+
readonly comments?: ReadonlyArray<SheetComment>;
|
|
52
|
+
readonly formControls?: ReadonlyArray<SheetFormControl>;
|
|
13
53
|
}
|
|
14
54
|
export interface SheetDoc {
|
|
15
55
|
readonly kind: 'sheet';
|
|
@@ -18,6 +58,12 @@ export interface SheetDoc {
|
|
|
18
58
|
readonly styles: XlsxStyles;
|
|
19
59
|
/** §18.4 shared string table; a cell with type 's' indexes into it. */
|
|
20
60
|
readonly sharedStrings: ReadonlyArray<string>;
|
|
61
|
+
/**
|
|
62
|
+
* §18.4.4 per-index rich-text runs (E-SHEET W6), parallel to sharedStrings —
|
|
63
|
+
* `[i]` is defined only when shared string i carries per-run formatting.
|
|
64
|
+
* Render-only: the writer flattens to plain text, so it is absent on round-trip.
|
|
65
|
+
*/
|
|
66
|
+
readonly sharedStringRuns?: ReadonlyArray<ReadonlyArray<SheetRichRun> | undefined>;
|
|
21
67
|
/** §18.2.5 workbook defined names (print areas/titles, named ranges). */
|
|
22
68
|
readonly definedNames: ReadonlyArray<DefinedName>;
|
|
23
69
|
/** §18.2.28 1904 date system (serial-to-date epoch). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { CellType, WorksheetCell, ColumnWidth, MergedRange, RowHeight, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, ParsedWorksheet, XlsxFont, XlsxFill, XlsxBorderStyleName, XlsxBorderEdge, XlsxBorder, XlsxHorizontalAlign, XlsxVerticalAlign, XlsxCellAlignment, XlsxCellXf, XlsxStyles, DefinedName, Dxf, CfOperator, CfRuleCellIs, CfvoType, Cfvo, CfRuleColorScale, CfRuleDataBar, CfRuleIconSet, CfRule, ConditionalFormat, SparklineKind, ParsedSparkline, ExcelTable, PivotTable, SheetPane, } from './types.js';
|
|
1
|
+
export type { CellType, WorksheetCell, ColumnWidth, MergedRange, RowHeight, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, ParsedWorksheet, XlsxFont, XlsxFill, XlsxBorderStyleName, XlsxBorderEdge, XlsxBorder, XlsxHorizontalAlign, XlsxVerticalAlign, XlsxCellAlignment, XlsxCellXf, XlsxStyles, DefinedName, Dxf, CfOperator, CfRuleCellIs, CfvoType, Cfvo, CfRuleColorScale, CfRuleDataBar, CfRuleIconSet, CfRuleTop10, CfRuleAboveAverage, CfRuleDupUnique, CfRuleText, CfRule, ConditionalFormat, DataValidationType, DataValidation, HyperlinkRef, HeaderFooter, FormControlRef, SheetRichRun, SparklineKind, ParsedSparkline, ExcelTable, PivotTable, SheetPane, } from './types.js';
|
|
@@ -58,6 +58,10 @@ export interface ParsedWorksheet {
|
|
|
58
58
|
readonly colBreaks?: ReadonlyArray<number>;
|
|
59
59
|
readonly drawingRelId?: string;
|
|
60
60
|
readonly conditionalFormats?: ReadonlyArray<ConditionalFormat>;
|
|
61
|
+
readonly dataValidations?: ReadonlyArray<DataValidation>;
|
|
62
|
+
readonly hyperlinks?: ReadonlyArray<HyperlinkRef>;
|
|
63
|
+
readonly headerFooter?: HeaderFooter;
|
|
64
|
+
readonly formControls?: ReadonlyArray<FormControlRef>;
|
|
61
65
|
readonly sparklines?: ReadonlyArray<ParsedSparkline>;
|
|
62
66
|
readonly tablePartRelIds?: ReadonlyArray<string>;
|
|
63
67
|
readonly tables?: ReadonlyArray<ExcelTable>;
|
|
@@ -127,6 +131,9 @@ export interface XlsxBorder {
|
|
|
127
131
|
readonly right?: XlsxBorderEdge;
|
|
128
132
|
readonly bottom?: XlsxBorderEdge;
|
|
129
133
|
readonly left?: XlsxBorderEdge;
|
|
134
|
+
readonly diagonal?: XlsxBorderEdge;
|
|
135
|
+
readonly diagonalUp?: boolean;
|
|
136
|
+
readonly diagonalDown?: boolean;
|
|
130
137
|
}
|
|
131
138
|
export type XlsxHorizontalAlign = 'left' | 'center' | 'right' | 'fill' | 'justify' | 'centerContinuous' | 'distributed';
|
|
132
139
|
export type XlsxVerticalAlign = 'top' | 'center' | 'bottom' | 'justify' | 'distributed';
|
|
@@ -134,6 +141,9 @@ export interface XlsxCellAlignment {
|
|
|
134
141
|
readonly horizontal?: XlsxHorizontalAlign;
|
|
135
142
|
readonly vertical?: XlsxVerticalAlign;
|
|
136
143
|
readonly wrapText?: boolean;
|
|
144
|
+
readonly indent?: number;
|
|
145
|
+
readonly textRotation?: number;
|
|
146
|
+
readonly shrinkToFit?: boolean;
|
|
137
147
|
}
|
|
138
148
|
export interface XlsxCellXf {
|
|
139
149
|
readonly numFmtId: number;
|
|
@@ -198,8 +208,77 @@ export interface CfRuleIconSet {
|
|
|
198
208
|
readonly cfvos: ReadonlyArray<Cfvo>;
|
|
199
209
|
readonly reverse?: boolean;
|
|
200
210
|
}
|
|
201
|
-
export
|
|
211
|
+
export interface CfRuleTop10 {
|
|
212
|
+
readonly type: 'top10';
|
|
213
|
+
readonly priority: number;
|
|
214
|
+
readonly rank: number;
|
|
215
|
+
readonly percent: boolean;
|
|
216
|
+
readonly bottom: boolean;
|
|
217
|
+
readonly dxfId: number;
|
|
218
|
+
}
|
|
219
|
+
export interface CfRuleAboveAverage {
|
|
220
|
+
readonly type: 'aboveAverage';
|
|
221
|
+
readonly priority: number;
|
|
222
|
+
readonly aboveAverage: boolean;
|
|
223
|
+
readonly equalAverage: boolean;
|
|
224
|
+
readonly stdDev?: number;
|
|
225
|
+
readonly dxfId: number;
|
|
226
|
+
}
|
|
227
|
+
export interface CfRuleDupUnique {
|
|
228
|
+
readonly type: 'duplicateValues' | 'uniqueValues';
|
|
229
|
+
readonly priority: number;
|
|
230
|
+
readonly dxfId: number;
|
|
231
|
+
}
|
|
232
|
+
export interface CfRuleText {
|
|
233
|
+
readonly type: 'containsText' | 'notContainsText' | 'beginsWith' | 'endsWith';
|
|
234
|
+
readonly priority: number;
|
|
235
|
+
readonly text: string;
|
|
236
|
+
readonly dxfId: number;
|
|
237
|
+
readonly formula?: string;
|
|
238
|
+
}
|
|
239
|
+
export type CfRule = CfRuleCellIs | CfRuleColorScale | CfRuleDataBar | CfRuleIconSet | CfRuleTop10 | CfRuleAboveAverage | CfRuleDupUnique | CfRuleText;
|
|
202
240
|
export interface ConditionalFormat {
|
|
203
241
|
readonly ranges: ReadonlyArray<MergedRange>;
|
|
204
242
|
readonly rules: ReadonlyArray<CfRule>;
|
|
205
243
|
}
|
|
244
|
+
export interface HyperlinkRef {
|
|
245
|
+
readonly ref: string;
|
|
246
|
+
readonly relId?: string;
|
|
247
|
+
readonly location?: string;
|
|
248
|
+
readonly display?: string;
|
|
249
|
+
readonly tooltip?: string;
|
|
250
|
+
}
|
|
251
|
+
export interface HeaderFooter {
|
|
252
|
+
readonly oddHeader?: string;
|
|
253
|
+
readonly oddFooter?: string;
|
|
254
|
+
}
|
|
255
|
+
export interface FormControlRef {
|
|
256
|
+
readonly name?: string;
|
|
257
|
+
readonly relId: string;
|
|
258
|
+
}
|
|
259
|
+
export interface SheetRichRun {
|
|
260
|
+
readonly text: string;
|
|
261
|
+
readonly bold?: boolean;
|
|
262
|
+
readonly italic?: boolean;
|
|
263
|
+
readonly underline?: boolean;
|
|
264
|
+
readonly colorHex?: string;
|
|
265
|
+
readonly sizePt?: number;
|
|
266
|
+
readonly vertAlign?: 'superscript' | 'subscript';
|
|
267
|
+
}
|
|
268
|
+
export type DataValidationType = 'none' | 'whole' | 'decimal' | 'list' | 'date' | 'time' | 'textLength' | 'custom';
|
|
269
|
+
export interface DataValidation {
|
|
270
|
+
readonly type: DataValidationType;
|
|
271
|
+
readonly ranges: ReadonlyArray<MergedRange>;
|
|
272
|
+
readonly operator?: string;
|
|
273
|
+
readonly allowBlank?: boolean;
|
|
274
|
+
readonly showDropDown?: boolean;
|
|
275
|
+
readonly showInputMessage?: boolean;
|
|
276
|
+
readonly showErrorMessage?: boolean;
|
|
277
|
+
readonly errorStyle?: string;
|
|
278
|
+
readonly formula1?: string;
|
|
279
|
+
readonly formula2?: string;
|
|
280
|
+
readonly promptTitle?: string;
|
|
281
|
+
readonly prompt?: string;
|
|
282
|
+
readonly errorTitle?: string;
|
|
283
|
+
readonly error?: string;
|
|
284
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { SheetComment } from '../core/ir/sheet.js';
|
|
2
|
+
export declare function parseLegacyComments(data: Uint8Array): Array<SheetComment>;
|
|
3
|
+
export declare function parsePersons(data: Uint8Array): Map<string, string>;
|
|
4
|
+
export declare function parseThreadedComments(data: Uint8Array, persons: ReadonlyMap<string, string>): Array<SheetComment>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { XMLParser } from "fast-xml-parser";
|
|
2
|
+
//#region src/excel/comments-parser.ts
|
|
3
|
+
var decoder = new TextDecoder("utf-8");
|
|
4
|
+
var parser = new XMLParser({
|
|
5
|
+
ignoreAttributes: false,
|
|
6
|
+
attributeNamePrefix: "@_",
|
|
7
|
+
textNodeName: "#text",
|
|
8
|
+
parseAttributeValue: false,
|
|
9
|
+
parseTagValue: false,
|
|
10
|
+
trimValues: false,
|
|
11
|
+
removeNSPrefix: true
|
|
12
|
+
});
|
|
13
|
+
function parseLegacyComments(data) {
|
|
14
|
+
const root = asObject(parser.parse(decoder.decode(data))["comments"]);
|
|
15
|
+
if (!root) return [];
|
|
16
|
+
const authors = toArray(asObject(root["authors"])?.["author"]).map((a) => textOf(a));
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const c of toArray(asObject(root["commentList"])?.["comment"])) {
|
|
19
|
+
const obj = asObject(c);
|
|
20
|
+
if (!obj) continue;
|
|
21
|
+
const ref = strAttr(obj, "ref");
|
|
22
|
+
if (!ref) continue;
|
|
23
|
+
const authorIdx = Number(strAttr(obj, "authorId"));
|
|
24
|
+
const author = Number.isInteger(authorIdx) ? authors[authorIdx] : void 0;
|
|
25
|
+
const text = stripAuthorPrefix(richText(obj["text"]), author);
|
|
26
|
+
if (text.length === 0 && !author) continue;
|
|
27
|
+
out.push({
|
|
28
|
+
ref,
|
|
29
|
+
...author ? { author } : {},
|
|
30
|
+
text,
|
|
31
|
+
threaded: false
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
function parsePersons(data) {
|
|
37
|
+
const root = asObject(parser.parse(decoder.decode(data))["personList"]);
|
|
38
|
+
const map = /* @__PURE__ */ new Map();
|
|
39
|
+
if (!root) return map;
|
|
40
|
+
for (const p of toArray(root["person"])) {
|
|
41
|
+
const obj = asObject(p);
|
|
42
|
+
const id = obj ? strAttr(obj, "id") : void 0;
|
|
43
|
+
const name = obj ? strAttr(obj, "displayName") : void 0;
|
|
44
|
+
if (id && name) map.set(id, name);
|
|
45
|
+
}
|
|
46
|
+
return map;
|
|
47
|
+
}
|
|
48
|
+
function parseThreadedComments(data, persons) {
|
|
49
|
+
const root = asObject(parser.parse(decoder.decode(data))["ThreadedComments"]);
|
|
50
|
+
if (!root) return [];
|
|
51
|
+
const out = [];
|
|
52
|
+
for (const c of toArray(root["threadedComment"])) {
|
|
53
|
+
const obj = asObject(c);
|
|
54
|
+
if (!obj) continue;
|
|
55
|
+
const ref = strAttr(obj, "ref");
|
|
56
|
+
if (!ref) continue;
|
|
57
|
+
const personId = strAttr(obj, "personId");
|
|
58
|
+
const author = personId ? persons.get(personId) : void 0;
|
|
59
|
+
const text = textOf(obj["text"]);
|
|
60
|
+
if (text.length === 0) continue;
|
|
61
|
+
out.push({
|
|
62
|
+
ref,
|
|
63
|
+
...author ? { author } : {},
|
|
64
|
+
text,
|
|
65
|
+
threaded: true
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
function richText(node) {
|
|
71
|
+
const obj = asObject(node);
|
|
72
|
+
if (!obj) return textOf(node);
|
|
73
|
+
const direct = textOf(obj["t"]);
|
|
74
|
+
if (direct) return direct;
|
|
75
|
+
return toArray(obj["r"]).map((r) => textOf(asObject(r)?.["t"])).join("");
|
|
76
|
+
}
|
|
77
|
+
function stripAuthorPrefix(text, author) {
|
|
78
|
+
if (!author) return text;
|
|
79
|
+
const prefix = `${author}:`;
|
|
80
|
+
if (text.startsWith(prefix)) return text.slice(prefix.length).replace(/^\s+/, "");
|
|
81
|
+
return text;
|
|
82
|
+
}
|
|
83
|
+
function asObject(v) {
|
|
84
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
85
|
+
}
|
|
86
|
+
function toArray(v) {
|
|
87
|
+
return Array.isArray(v) ? v : v !== void 0 ? [v] : [];
|
|
88
|
+
}
|
|
89
|
+
function strAttr(obj, key) {
|
|
90
|
+
const v = obj[`@_${key}`];
|
|
91
|
+
return typeof v === "string" ? v : void 0;
|
|
92
|
+
}
|
|
93
|
+
function textOf(node) {
|
|
94
|
+
if (typeof node === "string") return node;
|
|
95
|
+
if (typeof node === "number") return String(node);
|
|
96
|
+
const obj = asObject(node);
|
|
97
|
+
if (!obj) return "";
|
|
98
|
+
const inner = obj["#text"];
|
|
99
|
+
if (typeof inner === "string") return inner;
|
|
100
|
+
if (typeof inner === "number") return String(inner);
|
|
101
|
+
return "";
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
export { parseLegacyComments, parsePersons, parseThreadedComments };
|
|
@@ -12,5 +12,5 @@ export interface CfOverride {
|
|
|
12
12
|
};
|
|
13
13
|
readonly icon?: CellIcon;
|
|
14
14
|
}
|
|
15
|
-
export type CellConditionalFormatter = (row: number, col: number, numericValue: number | undefined) => CfOverride | undefined;
|
|
16
|
-
export declare function buildConditionalFormatter(conditionalFormats: ReadonlyArray<ConditionalFormat> | undefined, styles: XlsxStyles, cells: ReadonlyArray<WorksheetCell
|
|
15
|
+
export type CellConditionalFormatter = (row: number, col: number, numericValue: number | undefined, text: string | undefined) => CfOverride | undefined;
|
|
16
|
+
export declare function buildConditionalFormatter(conditionalFormats: ReadonlyArray<ConditionalFormat> | undefined, styles: XlsxStyles, cells: ReadonlyArray<WorksheetCell>, resolveText?: (cell: WorksheetCell) => string): CellConditionalFormatter | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/excel/conditional-format.ts
|
|
2
|
-
function buildConditionalFormatter(conditionalFormats, styles, cells) {
|
|
2
|
+
function buildConditionalFormatter(conditionalFormats, styles, cells, resolveText) {
|
|
3
3
|
if (!conditionalFormats || conditionalFormats.length === 0) return void 0;
|
|
4
4
|
const dxfs = styles.dxfs ?? [];
|
|
5
5
|
const flat = [];
|
|
@@ -18,41 +18,72 @@ function buildConditionalFormatter(conditionalFormats, styles, cells) {
|
|
|
18
18
|
rule,
|
|
19
19
|
iconThresholds: resolveIconThresholds(rule, cf.ranges, cells)
|
|
20
20
|
});
|
|
21
|
+
else if (rule.type === "top10") flat.push({
|
|
22
|
+
ranges: cf.ranges,
|
|
23
|
+
rule,
|
|
24
|
+
threshold: resolveTop10(rule, cf.ranges, cells)
|
|
25
|
+
});
|
|
26
|
+
else if (rule.type === "aboveAverage") flat.push({
|
|
27
|
+
ranges: cf.ranges,
|
|
28
|
+
rule,
|
|
29
|
+
threshold: resolveAboveAverage(rule, cf.ranges, cells)
|
|
30
|
+
});
|
|
31
|
+
else if (rule.type === "duplicateValues" || rule.type === "uniqueValues") flat.push({
|
|
32
|
+
ranges: cf.ranges,
|
|
33
|
+
rule,
|
|
34
|
+
dupKeys: resolveDupUnique(rule, cf.ranges, cells, resolveText)
|
|
35
|
+
});
|
|
21
36
|
else flat.push({
|
|
22
37
|
ranges: cf.ranges,
|
|
23
38
|
rule
|
|
24
39
|
});
|
|
25
40
|
flat.sort((a, b) => a.rule.priority - b.rule.priority);
|
|
26
|
-
return (row, col, value) => {
|
|
27
|
-
if (value === void 0) return void 0;
|
|
41
|
+
return (row, col, value, text) => {
|
|
42
|
+
if (value === void 0 && (text === void 0 || text.length === 0)) return void 0;
|
|
43
|
+
const dupKey = dupKeyOf(value, text);
|
|
28
44
|
let textFmt;
|
|
29
45
|
let textClaimed = false;
|
|
30
46
|
let bar;
|
|
31
47
|
let icon;
|
|
32
|
-
|
|
48
|
+
const claim = (dxfId) => {
|
|
49
|
+
textFmt = dxfToOverride(dxfs[dxfId]);
|
|
50
|
+
textClaimed = true;
|
|
51
|
+
};
|
|
52
|
+
for (const { ranges, rule, scale, bar: resolved, iconThresholds, threshold, dupKeys } of flat) {
|
|
33
53
|
if (!coversCell(ranges, row, col)) continue;
|
|
34
54
|
switch (rule.type) {
|
|
35
55
|
case "cellIs":
|
|
36
|
-
if (!textClaimed && cellIsMatches(rule.operator, value, rule.formulas))
|
|
37
|
-
textFmt = dxfToOverride(dxfs[rule.dxfId]);
|
|
38
|
-
textClaimed = true;
|
|
39
|
-
}
|
|
56
|
+
if (!textClaimed && value !== void 0 && cellIsMatches(rule.operator, value, rule.formulas)) claim(rule.dxfId);
|
|
40
57
|
break;
|
|
41
58
|
case "colorScale":
|
|
42
|
-
if (!textClaimed && scale) {
|
|
59
|
+
if (!textClaimed && value !== void 0 && scale) {
|
|
43
60
|
textFmt = { fillHex: rgbHex(colorScaleColor(scale, value)) };
|
|
44
61
|
textClaimed = true;
|
|
45
62
|
}
|
|
46
63
|
break;
|
|
47
64
|
case "dataBar":
|
|
48
|
-
if (!bar && resolved) bar = dataBarBar(resolved, value);
|
|
65
|
+
if (!bar && value !== void 0 && resolved) bar = dataBarBar(resolved, value);
|
|
49
66
|
break;
|
|
50
67
|
case "iconSet":
|
|
51
|
-
if (!icon && iconThresholds) {
|
|
68
|
+
if (!icon && value !== void 0 && iconThresholds) {
|
|
52
69
|
const bucket = iconBucket(iconThresholds, value);
|
|
53
70
|
icon = iconToCell(rule.iconSet, iconThresholds.length, bucket, rule.reverse ?? false);
|
|
54
71
|
}
|
|
55
72
|
break;
|
|
73
|
+
case "top10":
|
|
74
|
+
case "aboveAverage":
|
|
75
|
+
if (!textClaimed && value !== void 0 && threshold && thresholdMatches(threshold, value)) claim(rule.dxfId);
|
|
76
|
+
break;
|
|
77
|
+
case "duplicateValues":
|
|
78
|
+
case "uniqueValues":
|
|
79
|
+
if (!textClaimed && dupKey !== void 0 && dupKeys?.has(dupKey)) claim(rule.dxfId);
|
|
80
|
+
break;
|
|
81
|
+
case "containsText":
|
|
82
|
+
case "notContainsText":
|
|
83
|
+
case "beginsWith":
|
|
84
|
+
case "endsWith":
|
|
85
|
+
if (!textClaimed && text !== void 0 && textRuleMatches(rule, text)) claim(rule.dxfId);
|
|
86
|
+
break;
|
|
56
87
|
}
|
|
57
88
|
}
|
|
58
89
|
if (!bar && !icon) return textFmt;
|
|
@@ -63,6 +94,26 @@ function buildConditionalFormatter(conditionalFormats, styles, cells) {
|
|
|
63
94
|
};
|
|
64
95
|
};
|
|
65
96
|
}
|
|
97
|
+
function dupKeyOf(value, text) {
|
|
98
|
+
if (value !== void 0) return `n:${value}`;
|
|
99
|
+
if (text === void 0) return void 0;
|
|
100
|
+
const k = text.trim().toLowerCase();
|
|
101
|
+
return k.length > 0 ? `s:${k}` : void 0;
|
|
102
|
+
}
|
|
103
|
+
function textRuleMatches(rule, text) {
|
|
104
|
+
const hay = text.toLowerCase();
|
|
105
|
+
const needle = rule.text.toLowerCase();
|
|
106
|
+
switch (rule.type) {
|
|
107
|
+
case "containsText": return hay.includes(needle);
|
|
108
|
+
case "notContainsText": return !hay.includes(needle);
|
|
109
|
+
case "beginsWith": return hay.startsWith(needle);
|
|
110
|
+
case "endsWith": return hay.endsWith(needle);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function thresholdMatches(t, value) {
|
|
114
|
+
if (t.below) return t.orEqual ? value <= t.threshold : value < t.threshold;
|
|
115
|
+
return t.orEqual ? value >= t.threshold : value > t.threshold;
|
|
116
|
+
}
|
|
66
117
|
function coversCell(ranges, row, col) {
|
|
67
118
|
for (const r of ranges) if (row >= r.startRow && row <= r.endRow && col >= r.startColumn && col <= r.endColumn) return true;
|
|
68
119
|
return false;
|
|
@@ -323,5 +374,56 @@ function iconColor(count, e) {
|
|
|
323
374
|
const ramp = ICON_RAMPS[count] ?? ICON_RAMPS[3];
|
|
324
375
|
return ramp[Math.max(0, Math.min(ramp.length - 1, e))];
|
|
325
376
|
}
|
|
377
|
+
function resolveTop10(rule, ranges, cells) {
|
|
378
|
+
const vals = collectRangeValues(cells, ranges);
|
|
379
|
+
if (vals.length === 0) return void 0;
|
|
380
|
+
const n = vals.length;
|
|
381
|
+
const raw = rule.percent ? Math.floor(n * rule.rank / 100) : rule.rank;
|
|
382
|
+
const count = Math.max(1, Math.min(n, raw));
|
|
383
|
+
return {
|
|
384
|
+
threshold: [...vals].sort((a, b) => rule.bottom ? a - b : b - a)[count - 1],
|
|
385
|
+
below: rule.bottom,
|
|
386
|
+
orEqual: true
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
function resolveAboveAverage(rule, ranges, cells) {
|
|
390
|
+
const vals = collectRangeValues(cells, ranges);
|
|
391
|
+
if (vals.length === 0) return void 0;
|
|
392
|
+
const mean = vals.reduce((s, v) => s + v, 0) / vals.length;
|
|
393
|
+
let threshold = mean;
|
|
394
|
+
if (rule.stdDev !== void 0 && rule.stdDev > 0) {
|
|
395
|
+
const variance = vals.reduce((s, v) => s + (v - mean) * (v - mean), 0) / vals.length;
|
|
396
|
+
const sd = Math.sqrt(variance);
|
|
397
|
+
threshold = rule.aboveAverage ? mean + rule.stdDev * sd : mean - rule.stdDev * sd;
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
threshold,
|
|
401
|
+
below: !rule.aboveAverage,
|
|
402
|
+
orEqual: rule.equalAverage
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
function resolveDupUnique(rule, ranges, cells, resolveText) {
|
|
406
|
+
const counts = /* @__PURE__ */ new Map();
|
|
407
|
+
for (const cell of cells) {
|
|
408
|
+
if (!coversCell(ranges, cell.row, cell.column)) continue;
|
|
409
|
+
const key = cellDupKey(cell, resolveText);
|
|
410
|
+
if (key === void 0) continue;
|
|
411
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
412
|
+
}
|
|
413
|
+
if (counts.size === 0) return void 0;
|
|
414
|
+
const qualifies = rule.type === "duplicateValues" ? (c) => c >= 2 : (c) => c === 1;
|
|
415
|
+
const out = /* @__PURE__ */ new Set();
|
|
416
|
+
for (const [k, c] of counts) if (qualifies(c)) out.add(k);
|
|
417
|
+
return out;
|
|
418
|
+
}
|
|
419
|
+
function cellDupKey(cell, resolveText) {
|
|
420
|
+
if (cell.type === "n") {
|
|
421
|
+
const n = Number(cell.rawValue);
|
|
422
|
+
return Number.isFinite(n) ? `n:${n}` : void 0;
|
|
423
|
+
}
|
|
424
|
+
if (!resolveText) return void 0;
|
|
425
|
+
const k = resolveText(cell).trim().toLowerCase();
|
|
426
|
+
return k.length > 0 ? `s:${k}` : void 0;
|
|
427
|
+
}
|
|
326
428
|
//#endregion
|
|
327
429
|
export { buildConditionalFormatter };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { XMLParser } from "fast-xml-parser";
|
|
2
|
+
//#region src/excel/form-control-parser.ts
|
|
3
|
+
var decoder = new TextDecoder("utf-8");
|
|
4
|
+
var parser = new XMLParser({
|
|
5
|
+
ignoreAttributes: false,
|
|
6
|
+
attributeNamePrefix: "@_",
|
|
7
|
+
parseAttributeValue: false,
|
|
8
|
+
removeNSPrefix: true
|
|
9
|
+
});
|
|
10
|
+
function parseFormControlProps(data) {
|
|
11
|
+
const pr = parser.parse(decoder.decode(data))["formControlPr"];
|
|
12
|
+
const obj = pr && typeof pr === "object" ? pr : void 0;
|
|
13
|
+
if (!obj) return {};
|
|
14
|
+
const objectType = strAttr(obj, "objectType");
|
|
15
|
+
const checkedRaw = strAttr(obj, "checked");
|
|
16
|
+
const valRaw = strAttr(obj, "val");
|
|
17
|
+
const value = valRaw !== void 0 && /^-?\d+$/.test(valRaw) ? Number(valRaw) : void 0;
|
|
18
|
+
return {
|
|
19
|
+
...objectType ? { objectType } : {},
|
|
20
|
+
...checkedRaw !== void 0 ? { checked: checkedRaw === "Checked" || checkedRaw === "1" } : {},
|
|
21
|
+
...value !== void 0 ? { value } : {}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function strAttr(obj, key) {
|
|
25
|
+
const v = obj[`@_${key}`];
|
|
26
|
+
return typeof v === "string" ? v : void 0;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { parseFormControlProps };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
//#region src/excel/header-footer.ts
|
|
2
|
+
function buildHeaderFooterContent(formatString, sheetName) {
|
|
3
|
+
const regions = parseHeaderFooterString(formatString, sheetName);
|
|
4
|
+
const out = [];
|
|
5
|
+
const para = (runs, alignment) => {
|
|
6
|
+
if (runs.length > 0) out.push({
|
|
7
|
+
kind: "paragraph",
|
|
8
|
+
paragraph: {
|
|
9
|
+
properties: { alignment },
|
|
10
|
+
runs: [...runs]
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
para(regions.left, "left");
|
|
15
|
+
para(regions.center, "center");
|
|
16
|
+
para(regions.right, "right");
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
function parseHeaderFooterString(s, sheetName) {
|
|
20
|
+
const regions = {
|
|
21
|
+
left: [],
|
|
22
|
+
center: [],
|
|
23
|
+
right: []
|
|
24
|
+
};
|
|
25
|
+
let current = regions.center;
|
|
26
|
+
let bold = false;
|
|
27
|
+
let italic = false;
|
|
28
|
+
let buf = "";
|
|
29
|
+
const runProps = () => ({
|
|
30
|
+
...bold ? { bold: true } : {},
|
|
31
|
+
...italic ? { italic: true } : {}
|
|
32
|
+
});
|
|
33
|
+
const flush = () => {
|
|
34
|
+
if (buf.length > 0) {
|
|
35
|
+
current.push({
|
|
36
|
+
text: buf,
|
|
37
|
+
properties: runProps()
|
|
38
|
+
});
|
|
39
|
+
buf = "";
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const pushField = (field) => {
|
|
43
|
+
flush();
|
|
44
|
+
current.push({
|
|
45
|
+
text: "1",
|
|
46
|
+
properties: runProps(),
|
|
47
|
+
field
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
let i = 0;
|
|
51
|
+
while (i < s.length) {
|
|
52
|
+
const ch = s[i];
|
|
53
|
+
if (ch !== "&") {
|
|
54
|
+
buf += ch;
|
|
55
|
+
i++;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const next = s[i + 1];
|
|
59
|
+
if (next === void 0) {
|
|
60
|
+
buf += "&";
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
if (next === "&") {
|
|
64
|
+
buf += "&";
|
|
65
|
+
i += 2;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (next === "L" || next === "C" || next === "R") {
|
|
69
|
+
flush();
|
|
70
|
+
current = next === "L" ? regions.left : next === "R" ? regions.right : regions.center;
|
|
71
|
+
i += 2;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (next === "P") {
|
|
75
|
+
pushField("PAGE");
|
|
76
|
+
i += 2;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (next === "N") {
|
|
80
|
+
pushField("NUMPAGES");
|
|
81
|
+
i += 2;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (next === "A") {
|
|
85
|
+
buf += sheetName;
|
|
86
|
+
i += 2;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (next === "B") {
|
|
90
|
+
flush();
|
|
91
|
+
bold = !bold;
|
|
92
|
+
i += 2;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (next === "I") {
|
|
96
|
+
flush();
|
|
97
|
+
italic = !italic;
|
|
98
|
+
i += 2;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (next === "K") {
|
|
102
|
+
i += 2;
|
|
103
|
+
let n = 0;
|
|
104
|
+
while (n < 6 && i < s.length && /[0-9A-Fa-f]/.test(s[i])) {
|
|
105
|
+
i++;
|
|
106
|
+
n++;
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (next === "\"") {
|
|
111
|
+
i += 2;
|
|
112
|
+
while (i < s.length && s[i] !== "\"") i++;
|
|
113
|
+
if (i < s.length) i++;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (next >= "0" && next <= "9") {
|
|
117
|
+
i += 1;
|
|
118
|
+
while (i < s.length && s[i] >= "0" && s[i] <= "9") i++;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
i += 2;
|
|
122
|
+
}
|
|
123
|
+
flush();
|
|
124
|
+
return regions;
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
export { buildHeaderFooterContent };
|
|
@@ -7,7 +7,7 @@ export { parseWorkbook } from './workbook-parser.js';
|
|
|
7
7
|
export type { ParsedWorkbook, SheetReference } from './workbook-parser.js';
|
|
8
8
|
export type { DefinedName } from '../core/spreadsheet-model/index.js';
|
|
9
9
|
export { parseWorksheet } from './worksheet-parser.js';
|
|
10
|
-
export type { CellType, ColumnWidth, ExcelTable, MergedRange, ParsedWorksheet, RowHeight, WorksheetCell, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, } from '../core/spreadsheet-model/index.js';
|
|
10
|
+
export type { CellType, ColumnWidth, DataValidation, DataValidationType, HeaderFooter, HyperlinkRef, ExcelTable, MergedRange, ParsedWorksheet, RowHeight, SheetRichRun, WorksheetCell, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, } from '../core/spreadsheet-model/index.js';
|
|
11
11
|
export { parseXlsxStyles, EMPTY_XLSX_STYLES } from './styles-parser.js';
|
|
12
12
|
export type { XlsxBorder, XlsxBorderEdge, XlsxBorderStyleName, XlsxCellAlignment, XlsxCellXf, XlsxFill, XlsxFont, XlsxHorizontalAlign, XlsxStyles, XlsxVerticalAlign, } from '../core/spreadsheet-model/index.js';
|
|
13
13
|
export { applyNumberFormat } from './number-format.js';
|