@simplysm/excel 13.0.69 → 13.0.71
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/README.md +19 -516
- package/dist/excel-cell.d.ts +28 -28
- package/dist/excel-cell.d.ts.map +1 -1
- package/dist/excel-cell.js +24 -24
- package/dist/excel-cell.js.map +1 -1
- package/dist/excel-col.d.ts +4 -4
- package/dist/excel-col.d.ts.map +1 -1
- package/dist/excel-col.js +3 -3
- package/dist/excel-row.d.ts +3 -3
- package/dist/excel-row.d.ts.map +1 -1
- package/dist/excel-row.js +2 -2
- package/dist/excel-workbook.d.ts +23 -23
- package/dist/excel-workbook.d.ts.map +1 -1
- package/dist/excel-workbook.js +15 -15
- package/dist/excel-workbook.js.map +1 -1
- package/dist/excel-worksheet.d.ts +32 -32
- package/dist/excel-worksheet.d.ts.map +1 -1
- package/dist/excel-worksheet.js +32 -32
- package/dist/excel-worksheet.js.map +1 -1
- package/dist/excel-wrapper.d.ts +7 -7
- package/dist/excel-wrapper.js +7 -7
- package/dist/excel-wrapper.js.map +1 -1
- package/dist/types.d.ts +16 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/excel-utils.d.ts +23 -23
- package/dist/utils/excel-utils.d.ts.map +1 -1
- package/dist/utils/excel-utils.js +25 -25
- package/dist/utils/excel-utils.js.map +1 -1
- package/dist/utils/zip-cache.d.ts +6 -6
- package/dist/xml/excel-xml-content-type.d.ts +2 -2
- package/dist/xml/excel-xml-drawing.d.ts +2 -2
- package/dist/xml/excel-xml-relationship.d.ts +2 -2
- package/dist/xml/excel-xml-relationship.js +1 -1
- package/dist/xml/excel-xml-relationship.js.map +1 -1
- package/dist/xml/excel-xml-shared-string.d.ts +2 -2
- package/dist/xml/excel-xml-style.d.ts +2 -2
- package/dist/xml/excel-xml-style.js +6 -6
- package/dist/xml/excel-xml-style.js.map +1 -1
- package/dist/xml/excel-xml-unknown.d.ts +2 -2
- package/dist/xml/excel-xml-workbook.d.ts +2 -2
- package/dist/xml/excel-xml-workbook.js +1 -1
- package/dist/xml/excel-xml-workbook.js.map +1 -1
- package/dist/xml/excel-xml-worksheet.d.ts +6 -6
- package/dist/xml/excel-xml-worksheet.d.ts.map +1 -1
- package/dist/xml/excel-xml-worksheet.js +9 -8
- package/dist/xml/excel-xml-worksheet.js.map +1 -1
- package/package.json +6 -5
- package/src/excel-cell.ts +35 -35
- package/src/excel-col.ts +4 -4
- package/src/excel-row.ts +3 -3
- package/src/excel-workbook.ts +30 -30
- package/src/excel-worksheet.ts +47 -47
- package/src/excel-wrapper.ts +18 -18
- package/src/index.ts +3 -3
- package/src/types.ts +15 -17
- package/src/utils/excel-utils.ts +38 -38
- package/src/utils/zip-cache.ts +6 -6
- package/src/xml/excel-xml-content-type.ts +3 -3
- package/src/xml/excel-xml-drawing.ts +2 -2
- package/src/xml/excel-xml-relationship.ts +3 -3
- package/src/xml/excel-xml-shared-string.ts +2 -2
- package/src/xml/excel-xml-style.ts +11 -11
- package/src/xml/excel-xml-unknown.ts +2 -2
- package/src/xml/excel-xml-workbook.ts +5 -5
- package/src/xml/excel-xml-worksheet.ts +44 -43
- package/tests/excel-cell.spec.ts +448 -0
- package/tests/excel-col.spec.ts +112 -0
- package/tests/excel-row.spec.ts +71 -0
- package/tests/excel-workbook.spec.ts +219 -0
- package/tests/excel-worksheet.spec.ts +389 -0
- package/tests/excel-wrapper.spec.ts +296 -0
- package/tests/fixtures/logo.png +0 -0
- package/tests/fixtures//354/264/210/352/270/260/355/231/224.xlsx +0 -0
- package/tests/image-insert.spec.ts +190 -0
- package/tests/utils/excel-utils.spec.ts +242 -0
package/src/utils/excel-utils.ts
CHANGED
|
@@ -2,26 +2,26 @@ import { numParseInt } from "@simplysm/core-common";
|
|
|
2
2
|
import type { ExcelAddressPoint, ExcelAddressRangePoint, ExcelNumberFormat } from "../types";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Collection of Excel utility functions.
|
|
6
|
+
* Provides cell address conversion, date/number conversion, and number format processing.
|
|
7
7
|
*/
|
|
8
8
|
export class ExcelUtils {
|
|
9
|
-
/**
|
|
9
|
+
/** Convert cell coordinates to "A1" format string */
|
|
10
10
|
static stringifyAddr(point: ExcelAddressPoint): string {
|
|
11
11
|
const rowStr = this.stringifyRowAddr(point.r);
|
|
12
12
|
const colStr = this.stringifyColAddr(point.c);
|
|
13
13
|
return `${colStr}${rowStr}`;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
/**
|
|
16
|
+
/** Convert row index (0-based) to row address string (e.g. 0 -> "1") */
|
|
17
17
|
static stringifyRowAddr(r: number): string {
|
|
18
18
|
return (r + 1).toString();
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/**
|
|
21
|
+
/** Convert column index (0-based) to column address string (e.g. 0 -> "A", 26 -> "AA") */
|
|
22
22
|
static stringifyColAddr(c: number): string {
|
|
23
23
|
if (c < 0 || c > 16383) {
|
|
24
|
-
throw new Error(
|
|
24
|
+
throw new Error(`Column index must be in range 0~16383: ${c}`);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
let remained = c;
|
|
@@ -34,17 +34,17 @@ export class ExcelUtils {
|
|
|
34
34
|
return result;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/**
|
|
37
|
+
/** Extract row index from cell address (e.g. "A3" -> 2) */
|
|
38
38
|
static parseRowAddrCode(addrCode: string): number {
|
|
39
39
|
const rowAddrCode = /\d*$/.exec(addrCode)?.[0] ?? "";
|
|
40
40
|
const parsed = numParseInt(rowAddrCode);
|
|
41
41
|
if (parsed == null) {
|
|
42
|
-
throw new Error(
|
|
42
|
+
throw new Error(`Invalid row address code: ${addrCode}`);
|
|
43
43
|
}
|
|
44
44
|
return parsed - 1;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/**
|
|
47
|
+
/** Extract column index from cell address (e.g. "B3" -> 1) */
|
|
48
48
|
static parseColAddrCode(addrCode: string): number {
|
|
49
49
|
const colAddrCode = /^[a-zA-Z]*/.exec(addrCode)?.[0] ?? "";
|
|
50
50
|
|
|
@@ -57,7 +57,7 @@ export class ExcelUtils {
|
|
|
57
57
|
return result;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
/**
|
|
60
|
+
/** Convert cell address to coordinates (e.g. "B3" -> {r: 2, c: 1}) */
|
|
61
61
|
static parseCellAddrCode(addr: string): ExcelAddressPoint {
|
|
62
62
|
return {
|
|
63
63
|
r: ExcelUtils.parseRowAddrCode(addr),
|
|
@@ -65,7 +65,7 @@ export class ExcelUtils {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
/**
|
|
68
|
+
/** Convert range address to coordinates (e.g. "A1:C3" -> {s: {r:0,c:0}, e: {r:2,c:2}}) */
|
|
69
69
|
static parseRangeAddrCode(rangeAddr: string): ExcelAddressRangePoint {
|
|
70
70
|
const parts = rangeAddr.split(":");
|
|
71
71
|
return {
|
|
@@ -74,7 +74,7 @@ export class ExcelUtils {
|
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
/**
|
|
77
|
+
/** Convert range coordinates to address string */
|
|
78
78
|
static stringifyRangeAddr(point: ExcelAddressRangePoint): string {
|
|
79
79
|
const sAddr = this.stringifyAddr(point.s);
|
|
80
80
|
const eAddr = this.stringifyAddr(point.e);
|
|
@@ -87,8 +87,8 @@ export class ExcelUtils {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
|
-
* JavaScript
|
|
91
|
-
* Excel
|
|
90
|
+
* Convert JavaScript timestamp (ms) to Excel date number.
|
|
91
|
+
* Excel counts 1900-01-01 as 1 (1899-12-30 is date 0).
|
|
92
92
|
*/
|
|
93
93
|
static convertTimeTickToNumber(tick: number): number {
|
|
94
94
|
const currDate = new Date(tick);
|
|
@@ -99,8 +99,8 @@ export class ExcelUtils {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
|
-
* Excel
|
|
103
|
-
* Excel
|
|
102
|
+
* Convert Excel date number to JavaScript timestamp (ms).
|
|
103
|
+
* Excel counts 1900-01-01 as 1 (1899-12-30 is date 0).
|
|
104
104
|
*/
|
|
105
105
|
static convertNumberToTimeTick(num: number): number {
|
|
106
106
|
const excelBaseDateNumberUtc = Date.UTC(1899, 11, 31);
|
|
@@ -111,7 +111,7 @@ export class ExcelUtils {
|
|
|
111
111
|
return date.getTime();
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
/**
|
|
114
|
+
/** Convert number format code to format name */
|
|
115
115
|
static convertNumFmtCodeToName(numFmtCode: string): ExcelNumberFormat {
|
|
116
116
|
if (numFmtCode === "General") {
|
|
117
117
|
return "number";
|
|
@@ -121,40 +121,40 @@ export class ExcelUtils {
|
|
|
121
121
|
const hasTime = /hh/i.test(numFmtCode) || /ss/i.test(numFmtCode);
|
|
122
122
|
|
|
123
123
|
if (hasDate && hasTime) {
|
|
124
|
-
return "DateTime"; //
|
|
124
|
+
return "DateTime"; // date+time = DateTime
|
|
125
125
|
} else if (hasDate) {
|
|
126
|
-
return "DateOnly"; //
|
|
126
|
+
return "DateOnly"; // date only = DateOnly
|
|
127
127
|
} else if (hasTime) {
|
|
128
|
-
return "Time"; //
|
|
128
|
+
return "Time"; // time only = Time
|
|
129
129
|
}
|
|
130
|
-
//
|
|
131
|
-
// "[
|
|
130
|
+
// Number format pattern: 0, #, decimal, thousands separator, negative separator, parentheses, currency, space, exponent, percent, etc.
|
|
131
|
+
// In "[conditional format]actual format" structure, only check the actual format part (split("]").at(-1))
|
|
132
132
|
else if (/^[0.#,_;()\-\\$ @*?"E%+]*$/.test(numFmtCode.split("]").at(-1) ?? "")) {
|
|
133
133
|
return "number";
|
|
134
134
|
} else if ((numFmtCode.split("]").at(-1) ?? "").includes("#,0")) {
|
|
135
135
|
return "number";
|
|
136
136
|
} else {
|
|
137
|
-
throw new Error(`[numFmtCode: ${numFmtCode}]
|
|
137
|
+
throw new Error(`Unknown format for [numFmtCode: ${numFmtCode}].`);
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
|
-
*
|
|
142
|
+
* Convert number format ID to format name
|
|
143
143
|
*
|
|
144
144
|
* @remarks
|
|
145
|
-
* Excel
|
|
146
|
-
* - 0~13, 37~40, 48:
|
|
147
|
-
* - 14~17, 27~31, 34~36, 50~58:
|
|
148
|
-
* - 22:
|
|
149
|
-
* - 18~21, 32~33, 45~47:
|
|
150
|
-
* - 49:
|
|
145
|
+
* Excel built-in format ID ranges:
|
|
146
|
+
* - 0~13, 37~40, 48: number/general/currency/percent formats
|
|
147
|
+
* - 14~17, 27~31, 34~36, 50~58: date formats (including localized)
|
|
148
|
+
* - 22: date+time format
|
|
149
|
+
* - 18~21, 32~33, 45~47: time formats
|
|
150
|
+
* - 49: text format
|
|
151
151
|
*/
|
|
152
152
|
static convertNumFmtIdToName(numFmtId: number): ExcelNumberFormat {
|
|
153
|
-
//
|
|
153
|
+
// Number/general/currency/percent formats
|
|
154
154
|
if (numFmtId <= 13 || (numFmtId >= 37 && numFmtId <= 40) || numFmtId === 48) {
|
|
155
155
|
return "number";
|
|
156
156
|
}
|
|
157
|
-
//
|
|
157
|
+
// Date formats (including localized)
|
|
158
158
|
else if (
|
|
159
159
|
(numFmtId >= 14 && numFmtId <= 17) ||
|
|
160
160
|
(numFmtId >= 27 && numFmtId <= 31) ||
|
|
@@ -163,11 +163,11 @@ export class ExcelUtils {
|
|
|
163
163
|
) {
|
|
164
164
|
return "DateOnly";
|
|
165
165
|
}
|
|
166
|
-
//
|
|
166
|
+
// Date+time format
|
|
167
167
|
else if (numFmtId === 22) {
|
|
168
168
|
return "DateTime";
|
|
169
169
|
}
|
|
170
|
-
//
|
|
170
|
+
// Time formats
|
|
171
171
|
else if (
|
|
172
172
|
(numFmtId >= 18 && numFmtId <= 21) ||
|
|
173
173
|
(numFmtId >= 32 && numFmtId <= 33) ||
|
|
@@ -175,15 +175,15 @@ export class ExcelUtils {
|
|
|
175
175
|
) {
|
|
176
176
|
return "Time";
|
|
177
177
|
}
|
|
178
|
-
//
|
|
178
|
+
// Text format
|
|
179
179
|
else if (numFmtId === 49) {
|
|
180
180
|
return "string";
|
|
181
181
|
} else {
|
|
182
|
-
throw new Error(`[numFmtId: ${numFmtId}]
|
|
182
|
+
throw new Error(`Unknown format for [numFmtId: ${numFmtId}].`);
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
/**
|
|
186
|
+
/** Convert number format name to format ID */
|
|
187
187
|
static convertNumFmtNameToId(numFmtName: ExcelNumberFormat): number {
|
|
188
188
|
if (numFmtName === "number") {
|
|
189
189
|
return 0;
|
|
@@ -194,7 +194,7 @@ export class ExcelUtils {
|
|
|
194
194
|
} else if (numFmtName === "Time") {
|
|
195
195
|
return 18;
|
|
196
196
|
} else {
|
|
197
|
-
//
|
|
197
|
+
// Last case: "string" (TypeScript verifies automatically through type narrowing)
|
|
198
198
|
return 49;
|
|
199
199
|
}
|
|
200
200
|
}
|
package/src/utils/zip-cache.ts
CHANGED
|
@@ -20,15 +20,15 @@ import { ExcelXmlWorkbook } from "../xml/excel-xml-workbook";
|
|
|
20
20
|
import { ExcelXmlWorksheet } from "../xml/excel-xml-worksheet";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
* XML
|
|
23
|
+
* Class managing file cache for Excel ZIP archives.
|
|
24
|
+
* XML files are parsed into ExcelXml objects; other files are cached as byte arrays.
|
|
25
25
|
*
|
|
26
26
|
* @remarks
|
|
27
|
-
* ## Lazy Loading
|
|
27
|
+
* ## Lazy Loading Cache Strategy
|
|
28
28
|
*
|
|
29
|
-
* -
|
|
30
|
-
* -
|
|
31
|
-
* -
|
|
29
|
+
* - Files are read and parsed from the ZIP only on first access
|
|
30
|
+
* - Subsequent accesses return the cached object
|
|
31
|
+
* - Loads only the needed parts from large Excel files for memory efficiency
|
|
32
32
|
*/
|
|
33
33
|
export class ZipCache {
|
|
34
34
|
private readonly _cache = new Map<string, ExcelXml | Bytes | undefined>();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ExcelXml, ExcelXmlContentTypeData } from "../types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* [Content_Types].xml
|
|
5
|
-
*
|
|
4
|
+
* Class managing [Content_Types].xml.
|
|
5
|
+
* Manages MIME type information per file.
|
|
6
6
|
*/
|
|
7
7
|
export class ExcelXmlContentType implements ExcelXml {
|
|
8
8
|
data: ExcelXmlContentTypeData;
|
|
@@ -45,7 +45,7 @@ export class ExcelXmlContentType implements ExcelXml {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
add(partName: string, contentType: string): this {
|
|
48
|
-
//
|
|
48
|
+
// Duplicate check
|
|
49
49
|
const exists = this.data.Types.Override.some((item) => item.$.PartName === partName);
|
|
50
50
|
if (exists) {
|
|
51
51
|
return this;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ExcelXml, ExcelXmlDrawingData } from "../types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* xl/drawings/drawing*.xml
|
|
5
|
-
*
|
|
4
|
+
* Class managing xl/drawings/drawing*.xml files.
|
|
5
|
+
* Handles position and reference information for image insertion.
|
|
6
6
|
*/
|
|
7
7
|
export class ExcelXmlDrawing implements ExcelXml {
|
|
8
8
|
data: ExcelXmlDrawingData;
|
|
@@ -3,8 +3,8 @@ import { numParseInt } from "@simplysm/core-common";
|
|
|
3
3
|
import type { ExcelRelationshipData, ExcelXml, ExcelXmlRelationshipData } from "../types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* *.rels
|
|
7
|
-
*
|
|
6
|
+
* Class managing *.rels files.
|
|
7
|
+
* Handles reference relationships between files.
|
|
8
8
|
*/
|
|
9
9
|
export class ExcelXmlRelationship implements ExcelXml {
|
|
10
10
|
data: ExcelXmlRelationshipData;
|
|
@@ -82,7 +82,7 @@ export class ExcelXmlRelationship implements ExcelXml {
|
|
|
82
82
|
private _getRelId(rel: ExcelRelationshipData): number {
|
|
83
83
|
const match = /[0-9]+$/.exec(rel.$.Id);
|
|
84
84
|
if (match == null) {
|
|
85
|
-
throw new Error(
|
|
85
|
+
throw new Error(`Invalid relationship ID format: ${rel.$.Id}`);
|
|
86
86
|
}
|
|
87
87
|
return numParseInt(match[0])!;
|
|
88
88
|
}
|
|
@@ -7,8 +7,8 @@ import type {
|
|
|
7
7
|
import "@simplysm/core-common";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* xl/sharedStrings.xml
|
|
11
|
-
*
|
|
10
|
+
* Class managing xl/sharedStrings.xml.
|
|
11
|
+
* Optimizes file size by preventing string duplication.
|
|
12
12
|
*/
|
|
13
13
|
export class ExcelXmlSharedString implements ExcelXml {
|
|
14
14
|
data: ExcelXmlSharedStringData;
|
|
@@ -21,8 +21,8 @@ export interface ExcelStyle {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* xl/styles.xml
|
|
25
|
-
*
|
|
24
|
+
* Class managing xl/styles.xml.
|
|
25
|
+
* Handles styles such as number formats, background colors, borders, and alignment.
|
|
26
26
|
*/
|
|
27
27
|
export class ExcelXmlStyle implements ExcelXml {
|
|
28
28
|
data: ExcelXmlStyleData;
|
|
@@ -108,11 +108,11 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
108
108
|
addWithClone(id: string, style: ExcelStyle): string {
|
|
109
109
|
const idNum = numParseInt(id);
|
|
110
110
|
if (idNum == null) {
|
|
111
|
-
throw new Error(
|
|
111
|
+
throw new Error(`Invalid style ID: ${id}`);
|
|
112
112
|
}
|
|
113
113
|
const xfArray = this.data.styleSheet.cellXfs[0].xf;
|
|
114
114
|
if (idNum < 0 || idNum >= xfArray.length) {
|
|
115
|
-
throw new Error(
|
|
115
|
+
throw new Error(`Non-existent style ID: ${id} (Range: 0-${xfArray.length - 1})`);
|
|
116
116
|
}
|
|
117
117
|
const prevXf = xfArray[idNum];
|
|
118
118
|
const cloneXf = objClone(prevXf);
|
|
@@ -187,7 +187,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
187
187
|
get(id: string): ExcelStyle {
|
|
188
188
|
const idNum = numParseInt(id);
|
|
189
189
|
if (idNum == null) {
|
|
190
|
-
throw new Error(
|
|
190
|
+
throw new Error(`Invalid style ID: ${id}`);
|
|
191
191
|
}
|
|
192
192
|
const xf = this.data.styleSheet.cellXfs[0].xf[idNum] as ExcelXmlStyleDataXf | undefined;
|
|
193
193
|
|
|
@@ -204,7 +204,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
204
204
|
| undefined;
|
|
205
205
|
if (fill == null) {
|
|
206
206
|
throw new Error(
|
|
207
|
-
|
|
207
|
+
`Non-existent fill ID: ${xf.$.fillId} (Range: 0-${this.data.styleSheet.fills[0].fill.length - 1})`,
|
|
208
208
|
);
|
|
209
209
|
}
|
|
210
210
|
result.background = fill.patternFill[0].fgColor?.[0].$.rgb;
|
|
@@ -214,14 +214,14 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
214
214
|
if (xf.$.borderId !== undefined) {
|
|
215
215
|
const borderIdNum = numParseInt(xf.$.borderId);
|
|
216
216
|
if (borderIdNum == null) {
|
|
217
|
-
throw new Error(
|
|
217
|
+
throw new Error(`Invalid border ID: ${xf.$.borderId}`);
|
|
218
218
|
}
|
|
219
219
|
const border = this.data.styleSheet.borders[0].border[borderIdNum] as
|
|
220
220
|
| ExcelXmlStyleDataBorder
|
|
221
221
|
| undefined;
|
|
222
222
|
if (border == null) {
|
|
223
223
|
throw new Error(
|
|
224
|
-
|
|
224
|
+
`Non-existent border ID: ${xf.$.borderId} (Range: 0-${this.data.styleSheet.borders[0].border.length - 1})`,
|
|
225
225
|
);
|
|
226
226
|
}
|
|
227
227
|
if (
|
|
@@ -262,7 +262,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
262
262
|
cleanup(): void {
|
|
263
263
|
const result = {} as ExcelXmlStyleData["styleSheet"];
|
|
264
264
|
|
|
265
|
-
//
|
|
265
|
+
// Sort order (numFmts first)
|
|
266
266
|
|
|
267
267
|
if (this.data.styleSheet.numFmts != null) {
|
|
268
268
|
result.numFmts = this.data.styleSheet.numFmts;
|
|
@@ -282,7 +282,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
282
282
|
//#region Private Methods
|
|
283
283
|
|
|
284
284
|
private _setNumFmtCode(numFmtCode: string): string {
|
|
285
|
-
//
|
|
285
|
+
// Skip if the code already exists
|
|
286
286
|
const existsNumFmtId = (this.data.styleSheet.numFmts?.[0].numFmt ?? []).single(
|
|
287
287
|
(item) => item.$.formatCode === numFmtCode,
|
|
288
288
|
)?.$.numFmtId;
|
|
@@ -299,7 +299,7 @@ export class ExcelXmlStyle implements ExcelXml {
|
|
|
299
299
|
|
|
300
300
|
this.data.styleSheet.numFmts[0].numFmt = this.data.styleSheet.numFmts[0].numFmt ?? [];
|
|
301
301
|
|
|
302
|
-
// Excel
|
|
302
|
+
// Excel custom number formats start from ID 180+ (0-163: built-in, 164-179: reserved)
|
|
303
303
|
const numFmts = this.data.styleSheet.numFmts[0].numFmt;
|
|
304
304
|
const maxItem =
|
|
305
305
|
numFmts.length > 0
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ExcelXml } from "../types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Class preserving Excel XML data of unknown format.
|
|
5
|
+
* Maintains original data without loss.
|
|
6
6
|
*/
|
|
7
7
|
export class ExcelXmlUnknown implements ExcelXml {
|
|
8
8
|
constructor(public readonly data: Record<string, unknown>) {}
|
|
@@ -3,8 +3,8 @@ import { numParseInt } from "@simplysm/core-common";
|
|
|
3
3
|
import type { ExcelXml, ExcelXmlWorkbookData } from "../types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* xl/workbook.xml
|
|
7
|
-
*
|
|
6
|
+
* Class managing xl/workbook.xml.
|
|
7
|
+
* Handles the worksheet list and relationship IDs.
|
|
8
8
|
*/
|
|
9
9
|
export class ExcelXmlWorkbook implements ExcelXml {
|
|
10
10
|
data: ExcelXmlWorkbookData;
|
|
@@ -55,7 +55,7 @@ export class ExcelXmlWorkbook implements ExcelXml {
|
|
|
55
55
|
cleanup(): void {
|
|
56
56
|
const result = {} as ExcelXmlWorkbookData["workbook"];
|
|
57
57
|
|
|
58
|
-
//
|
|
58
|
+
// Sort order (around "sheets", keep others in original position)
|
|
59
59
|
|
|
60
60
|
const workbookRec = this.data.workbook as Record<string, unknown>;
|
|
61
61
|
const resultRec = result as Record<string, unknown>;
|
|
@@ -99,7 +99,7 @@ export class ExcelXmlWorkbook implements ExcelXml {
|
|
|
99
99
|
setWorksheetNameById(id: number, newName: string): void {
|
|
100
100
|
const sheetData = this._getSheetDataById(id);
|
|
101
101
|
if (sheetData == null) {
|
|
102
|
-
throw new Error(
|
|
102
|
+
throw new Error(`Cannot find worksheet ID ${id}`);
|
|
103
103
|
}
|
|
104
104
|
const replacedName = this._getReplacedName(newName);
|
|
105
105
|
sheetData.$.name = replacedName;
|
|
@@ -112,7 +112,7 @@ export class ExcelXmlWorkbook implements ExcelXml {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
private _getReplacedName(name: string): string {
|
|
115
|
-
//--
|
|
115
|
+
//-- Replace invalid sheet name characters with "_"
|
|
116
116
|
return name.replace(/[:\\/?*\[\]']/g, "_");
|
|
117
117
|
}
|
|
118
118
|
}
|