@simplysm/excel 14.0.65 → 14.0.69

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/src/types.ts CHANGED
@@ -128,10 +128,45 @@ export interface ExcelXmlWorksheetData {
128
128
  }[];
129
129
  },
130
130
  ];
131
+ conditionalFormatting?: ExcelXmlConditionalFormattingData[];
131
132
  drawing?: { $: { "r:id": string } }[];
132
133
  };
133
134
  }
134
135
 
136
+ export interface ExcelXmlConditionalFormattingData {
137
+ $: { sqref: string };
138
+ cfRule: ExcelXmlCfRuleData[];
139
+ }
140
+
141
+ export interface ExcelXmlCfRuleData {
142
+ $: {
143
+ type:
144
+ | "cellIs"
145
+ | "containsText"
146
+ | "notContainsText"
147
+ | "beginsWith"
148
+ | "endsWith"
149
+ | "expression";
150
+ operator?:
151
+ | "lessThan"
152
+ | "lessThanOrEqual"
153
+ | "equal"
154
+ | "notEqual"
155
+ | "greaterThanOrEqual"
156
+ | "greaterThan"
157
+ | "between"
158
+ | "notBetween"
159
+ | "containsText"
160
+ | "notContains"
161
+ | "beginsWith"
162
+ | "endsWith";
163
+ priority: string;
164
+ dxfId: string;
165
+ text?: string;
166
+ };
167
+ formula: string[];
168
+ }
169
+
135
170
  export interface ExcelRowData {
136
171
  $: {
137
172
  r: string; // 주소 (1~)
@@ -236,7 +271,7 @@ export interface ExcelXmlStyleData {
236
271
  fonts: [
237
272
  {
238
273
  $: { count: string };
239
- font: {}[];
274
+ font: ExcelXmlStyleDataFont[];
240
275
  },
241
276
  ];
242
277
  fills: [
@@ -257,9 +292,44 @@ export interface ExcelXmlStyleData {
257
292
  xf: ExcelXmlStyleDataXf[];
258
293
  },
259
294
  ];
295
+ dxfs?: [
296
+ {
297
+ $: { count: string };
298
+ dxf: ExcelXmlStyleDataDxf[];
299
+ },
300
+ ];
260
301
  };
261
302
  }
262
303
 
304
+ export interface ExcelXmlStyleDataFont {
305
+ sz?: [{ $: { val: string } }];
306
+ name?: [{ $: { val: string } }];
307
+ b?: [{}];
308
+ i?: [{}];
309
+ u?: [{ $?: { val?: ExcelFontUnderline } }];
310
+ strike?: [{}];
311
+ color?: [{ $: { rgb: string } }];
312
+ }
313
+
314
+ export interface ExcelXmlStyleDataDxf {
315
+ font?: [
316
+ {
317
+ b?: [{ $: { val: "0" | "1" } }];
318
+ color?: [{ $: { rgb: string } }];
319
+ },
320
+ ];
321
+ fill?: [
322
+ {
323
+ patternFill: [
324
+ {
325
+ $: { patternType?: "solid" };
326
+ bgColor?: [{ $: { rgb: string } }];
327
+ },
328
+ ];
329
+ },
330
+ ];
331
+ }
332
+
263
333
  export interface ExcelXmlStyleDataXf {
264
334
  $: {
265
335
  numFmtId?: string;
@@ -364,6 +434,30 @@ export interface ExcelXml {
364
434
  export type ExcelBorderPosition = "left" | "right" | "top" | "bottom";
365
435
  export type ExcelHorizontalAlign = "center" | "left" | "right";
366
436
  export type ExcelVerticalAlign = "center" | "top" | "bottom";
437
+ export type ExcelFontUnderline = "single" | "double" | "singleAccounting" | "doubleAccounting";
438
+
439
+ /**
440
+ * 폰트 속성. cell 단위 override (`ExcelStyleOptions.font`) 와
441
+ * workbook default (`wb.setDefaultStyle({ font })`) 양쪽이 공유한다.
442
+ *
443
+ * 미지정 속성은 OOXML `<font>` 자식 엘리먼트로 emit 되지 않으며, Excel 자체 기본값으로 표시된다.
444
+ */
445
+ export interface ExcelFont {
446
+ /** 폰트 크기 (pt) */
447
+ size?: number;
448
+ /** 폰트명 (예: "맑은 고딕", "Calibri") */
449
+ family?: string;
450
+ /** 굵게 */
451
+ bold?: boolean;
452
+ /** 기울임 */
453
+ italic?: boolean;
454
+ /** 밑줄 — `<u val="..."/>` 의 val 에 그대로 매핑 */
455
+ underline?: ExcelFontUnderline;
456
+ /** 글자색 (ARGB 8자리, 예: "00FF0000") */
457
+ color?: string;
458
+ /** 취소선 */
459
+ strike?: boolean;
460
+ }
367
461
 
368
462
  /**
369
463
  * 셀 스타일 옵션
@@ -397,6 +491,58 @@ export interface ExcelStyleOptions {
397
491
  * `numberFormat`과 동시 지정 시 이 필드가 우선 적용된다.
398
492
  */
399
493
  numberFormatCode?: string;
494
+ /** 폰트 (size/family/bold/italic/underline/color/strike) */
495
+ font?: ExcelFont;
496
+ }
497
+
498
+ //#endregion
499
+
500
+ //#region Conditional Format Types
501
+
502
+ /**
503
+ * 조건부 서식 강조 스타일.
504
+ * 미지정 필드는 base 셀 스타일을 그대로 두고, 지정 필드만 OOXML dxf 로 emit 되어 native CF 오버레이로 합성된다.
505
+ */
506
+ export interface ExcelConditionalRuleStyle {
507
+ /** 배경색 (ARGB 8자리, 예: "00FFFF00") */
508
+ background?: string;
509
+ /** 글자색 (ARGB 8자리) */
510
+ fontColor?: string;
511
+ /** 글자 굵기. "normal" 은 base 가 bold 라도 강제 normal. */
512
+ fontWeight?: "bold" | "normal";
400
513
  }
401
514
 
515
+ /**
516
+ * 조건부 서식 규칙.
517
+ * - `cellIs` 단일 비교(`<`, `>`, `<=`, `>=`, `=`, `<>`): `value` 는 number 또는 string.
518
+ * - `cellIs` 구간(`between`, `notBetween`): `value` 는 [a, b] 튜플(양 끝 inclusive).
519
+ * - `text` 매칭(`contains`, `notContains`, `beginsWith`, `endsWith`): `value` 는 string. SEARCH 기반(대소문자 무시) 고정.
520
+ *
521
+ * `value: number` 는 raw formula(`<formula>4999</formula>`), `value: string` 은 따옴표 둘러싼 리터럴 formula(`<formula>"OK"</formula>`) 로 emit.
522
+ */
523
+ export type ExcelConditionalRule =
524
+ | {
525
+ type: "cellIs";
526
+ op: "<" | ">" | "<=" | ">=" | "=" | "<>";
527
+ value: number | string;
528
+ style: ExcelConditionalRuleStyle;
529
+ }
530
+ | {
531
+ type: "cellIs";
532
+ op: "between" | "notBetween";
533
+ value: [number, number] | [string, string];
534
+ style: ExcelConditionalRuleStyle;
535
+ }
536
+ | {
537
+ type: "text";
538
+ op: "contains" | "notContains" | "beginsWith" | "endsWith";
539
+ value: string;
540
+ style: ExcelConditionalRuleStyle;
541
+ }
542
+ | {
543
+ type: "expression";
544
+ formula: string;
545
+ style: ExcelConditionalRuleStyle;
546
+ };
547
+
402
548
  //#endregion
@@ -0,0 +1,31 @@
1
+ import type { ZipCache } from "./zip-cache";
2
+ import { ExcelXmlContentType } from "../xml/excel-xml-content-type";
3
+ import { ExcelXmlRelationship } from "../xml/excel-xml-relationship";
4
+ import { ExcelXmlStyle } from "../xml/excel-xml-style";
5
+
6
+ /**
7
+ * `xl/styles.xml` 을 가져오거나, 없으면 새로 만들어 ZipCache 에 등록한다.
8
+ * Content_Types / workbook.xml.rels 에도 styles.xml 항목을 추가한다.
9
+ *
10
+ * `ExcelCell.setStyle` 과 `ExcelWorkbook.setDefaultStyle` 양쪽이 공유한다.
11
+ */
12
+ export async function getOrCreateStyleData(zipCache: ZipCache): Promise<ExcelXmlStyle> {
13
+ let styleData = (await zipCache.get("xl/styles.xml")) as ExcelXmlStyle | undefined;
14
+ if (styleData == null) {
15
+ styleData = new ExcelXmlStyle();
16
+ zipCache.set("xl/styles.xml", styleData);
17
+
18
+ const typeData = (await zipCache.get("[Content_Types].xml")) as ExcelXmlContentType;
19
+ typeData.add(
20
+ "/xl/styles.xml",
21
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
22
+ );
23
+
24
+ const wbRelData = (await zipCache.get("xl/_rels/workbook.xml.rels")) as ExcelXmlRelationship;
25
+ wbRelData.add(
26
+ "styles.xml",
27
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
28
+ );
29
+ }
30
+ return styleData;
31
+ }
@@ -1,15 +1,21 @@
1
1
  import type {
2
2
  ExcelBorderPosition,
3
+ ExcelConditionalRuleStyle,
4
+ ExcelFont,
3
5
  ExcelHorizontalAlign,
6
+ ExcelStyleOptions,
4
7
  ExcelVerticalAlign,
5
8
  ExcelXml,
6
9
  ExcelXmlStyleData,
7
10
  ExcelXmlStyleDataBorder,
11
+ ExcelXmlStyleDataDxf,
8
12
  ExcelXmlStyleDataFill,
13
+ ExcelXmlStyleDataFont,
9
14
  ExcelXmlStyleDataXf,
10
15
  } from "../types";
11
16
  import "@simplysm/core-common";
12
17
  import { num, obj } from "@simplysm/core-common";
18
+ import { ExcelUtils } from "../utils/excel-utils";
13
19
 
14
20
  export interface ExcelStyle {
15
21
  numFmtId?: string;
@@ -18,6 +24,50 @@ export interface ExcelStyle {
18
24
  background?: string;
19
25
  verticalAlign?: ExcelVerticalAlign;
20
26
  horizontalAlign?: ExcelHorizontalAlign;
27
+ font?: ExcelFont;
28
+ }
29
+
30
+ /**
31
+ * `ExcelStyleOptions` (사용자 표면) → 내부 `ExcelStyle` 변환.
32
+ * cell.setStyle 과 wb.setDefaultStyle 이 공유한다.
33
+ *
34
+ * - background ARGB 8자리 형식 검증
35
+ * - numberFormatCode 가 numberFormat 보다 우선
36
+ * - font 는 그대로 전달 (구체 검증은 ExcelXmlStyle 내부에서 수행)
37
+ */
38
+ export function convertExcelStyleOptions(opts: ExcelStyleOptions): ExcelStyle {
39
+ const style: ExcelStyle = {};
40
+
41
+ if (opts.background != null) {
42
+ if (!/^[0-9A-F]{8}$/i.test(opts.background)) {
43
+ throw new Error("잘못된 색상 형식입니다. (형식: 00000000: alpha(반전)+rgb)");
44
+ }
45
+ style.background = opts.background;
46
+ }
47
+
48
+ if (opts.border != null) {
49
+ style.border = opts.border;
50
+ }
51
+
52
+ if (opts.horizontalAlign != null) {
53
+ style.horizontalAlign = opts.horizontalAlign;
54
+ }
55
+
56
+ if (opts.verticalAlign != null) {
57
+ style.verticalAlign = opts.verticalAlign;
58
+ }
59
+
60
+ if (opts.numberFormatCode != null) {
61
+ style.numFmtCode = opts.numberFormatCode;
62
+ } else if (opts.numberFormat != null) {
63
+ style.numFmtId = ExcelUtils.convertNumFmtNameToId(opts.numberFormat).toString();
64
+ }
65
+
66
+ if (opts.font != null) {
67
+ style.font = opts.font;
68
+ }
69
+
70
+ return style;
21
71
  }
22
72
 
23
73
  /**
@@ -70,41 +120,21 @@ export class ExcelXmlStyle implements ExcelXml {
70
120
 
71
121
  add(style: ExcelStyle): string {
72
122
  const newXf: ExcelXmlStyleDataXf = { $: {} };
73
-
74
- if (style.numFmtId != null) {
75
- newXf.$.numFmtId = style.numFmtId;
76
- }
77
-
78
- if (style.numFmtCode != null) {
79
- newXf.$.numFmtId = this._setNumFmtCode(style.numFmtCode);
80
- newXf.$.applyNumberFormat = "1";
81
- }
82
-
83
- if (style.background != null) {
84
- const newFill: ExcelXmlStyleDataFill = {
85
- patternFill: [
86
- {
87
- $: { patternType: "solid" },
88
- fgColor: [{ $: { rgb: style.background.toUpperCase() } }],
89
- },
90
- ],
91
- };
92
-
93
- newXf.$.applyFill = "1";
94
- newXf.$.fillId = this._getSameOrCreateFill(newFill);
95
- }
96
-
97
- if (style.border != null) {
98
- const newBorder = this._createBorderFromPositions(style.border);
99
- newXf.$.applyBorder = "1";
100
- newXf.$.borderId = this._getSameOrCreateBorder(newBorder);
101
- }
102
-
103
- this._applyAlignment(newXf, style);
104
-
123
+ this._applyStyleToXf(newXf, style);
105
124
  return this._getSameOrCreateXf(newXf);
106
125
  }
107
126
 
127
+ /**
128
+ * 워크북 default cell style 설정. `cellXfs[0].xf[0]` (OOXML default cell style 자리) 을 새로 빌드해 덮어쓴다.
129
+ * fonts/fills/borders/numFmts 자원은 `_getSameOrCreate*` 로 누적·dedup 되고 인덱스가 cellXfs[0] 에 박힌다.
130
+ * 미호출 시 기존 cellXfs[0] 그대로 보존된다.
131
+ */
132
+ setDefaultStyle(style: ExcelStyle): void {
133
+ const newXf: ExcelXmlStyleDataXf = { $: { numFmtId: "0" } };
134
+ this._applyStyleToXf(newXf, style);
135
+ this.data.styleSheet.cellXfs[0].xf[0] = newXf;
136
+ }
137
+
108
138
  addWithClone(id: string, style: ExcelStyle): string {
109
139
  const idNum = num.parseInt(id);
110
140
  if (idNum == null) {
@@ -179,6 +209,12 @@ export class ExcelXmlStyle implements ExcelXml {
179
209
  }
180
210
  }
181
211
 
212
+ if (style.font != null) {
213
+ this._validateFont(style.font);
214
+ cloneXf.$.applyFont = "1";
215
+ cloneXf.$.fontId = this._getSameOrCreateFont(this._buildFontXml(style.font));
216
+ }
217
+
182
218
  this._applyAlignment(cloneXf, style);
183
219
 
184
220
  return this._getSameOrCreateXf(cloneXf);
@@ -248,11 +284,67 @@ export class ExcelXmlStyle implements ExcelXml {
248
284
 
249
285
  result.verticalAlign = xf.alignment?.[0].$.vertical;
250
286
  result.horizontalAlign = xf.alignment?.[0].$.horizontal;
287
+
288
+ if (xf.$.fontId != null) {
289
+ const fontIdNum = num.parseInt(xf.$.fontId);
290
+ if (fontIdNum != null) {
291
+ const font = this.data.styleSheet.fonts[0].font[fontIdNum] as
292
+ | ExcelXmlStyleDataFont
293
+ | undefined;
294
+ if (font != null) {
295
+ const parsed = this._parseFontXml(font);
296
+ if (Object.keys(parsed).length > 0) {
297
+ result.font = parsed;
298
+ }
299
+ }
300
+ }
301
+ }
251
302
  }
252
303
 
253
304
  return result;
254
305
  }
255
306
 
307
+ addDxf(style: ExcelConditionalRuleStyle): string {
308
+ const dxfItem: ExcelXmlStyleDataDxf = {};
309
+
310
+ if (style.fontColor != null || style.fontWeight != null) {
311
+ const font: NonNullable<ExcelXmlStyleDataDxf["font"]>[number] = {};
312
+ if (style.fontWeight != null) {
313
+ font.b = [{ $: { val: style.fontWeight === "bold" ? "1" : "0" } }];
314
+ }
315
+ if (style.fontColor != null) {
316
+ font.color = [{ $: { rgb: style.fontColor.toUpperCase() } }];
317
+ }
318
+ dxfItem.font = [font];
319
+ }
320
+
321
+ if (style.background != null) {
322
+ dxfItem.fill = [
323
+ {
324
+ patternFill: [
325
+ {
326
+ $: { patternType: "solid" },
327
+ bgColor: [{ $: { rgb: style.background.toUpperCase() } }],
328
+ },
329
+ ],
330
+ },
331
+ ];
332
+ }
333
+
334
+ const dxfs = (this.data.styleSheet.dxfs = this.data.styleSheet.dxfs ?? [
335
+ { $: { count: "0" }, dxf: [] },
336
+ ]);
337
+
338
+ const prevSameDxf = dxfs[0].dxf.single((item) => obj.equal(item, dxfItem));
339
+ if (prevSameDxf != null) {
340
+ return dxfs[0].dxf.indexOf(prevSameDxf).toString();
341
+ }
342
+
343
+ dxfs[0].dxf.push(dxfItem);
344
+ dxfs[0].$.count = dxfs[0].dxf.length.toString();
345
+ return (dxfs[0].dxf.length - 1).toString();
346
+ }
347
+
256
348
  getNumFmtCode(numFmtId: string): string | undefined {
257
349
  return (this.data.styleSheet.numFmts?.[0].numFmt ?? []).single(
258
350
  (item) => item.$.numFmtId === numFmtId,
@@ -320,6 +412,108 @@ export class ExcelXmlStyle implements ExcelXml {
320
412
  return nextNumFmtId;
321
413
  }
322
414
 
415
+ private _applyStyleToXf(xf: ExcelXmlStyleDataXf, style: ExcelStyle): void {
416
+ if (style.numFmtId != null) {
417
+ xf.$.numFmtId = style.numFmtId;
418
+ }
419
+
420
+ if (style.numFmtCode != null) {
421
+ xf.$.numFmtId = this._setNumFmtCode(style.numFmtCode);
422
+ xf.$.applyNumberFormat = "1";
423
+ }
424
+
425
+ if (style.background != null) {
426
+ const newFill: ExcelXmlStyleDataFill = {
427
+ patternFill: [
428
+ {
429
+ $: { patternType: "solid" },
430
+ fgColor: [{ $: { rgb: style.background.toUpperCase() } }],
431
+ },
432
+ ],
433
+ };
434
+ xf.$.applyFill = "1";
435
+ xf.$.fillId = this._getSameOrCreateFill(newFill);
436
+ }
437
+
438
+ if (style.border != null) {
439
+ const newBorder = this._createBorderFromPositions(style.border);
440
+ xf.$.applyBorder = "1";
441
+ xf.$.borderId = this._getSameOrCreateBorder(newBorder);
442
+ }
443
+
444
+ if (style.font != null) {
445
+ this._validateFont(style.font);
446
+ xf.$.applyFont = "1";
447
+ xf.$.fontId = this._getSameOrCreateFont(this._buildFontXml(style.font));
448
+ }
449
+
450
+ this._applyAlignment(xf, style);
451
+ }
452
+
453
+ private _validateFont(font: ExcelFont): void {
454
+ if (font.color != null && !/^[0-9A-F]{8}$/i.test(font.color)) {
455
+ throw new Error("잘못된 폰트 색상 형식입니다. (형식: 00000000: alpha(반전)+rgb)");
456
+ }
457
+ }
458
+
459
+ private _buildFontXml(font: ExcelFont): ExcelXmlStyleDataFont {
460
+ const result: ExcelXmlStyleDataFont = {};
461
+ if (font.size != null) {
462
+ result.sz = [{ $: { val: font.size.toString() } }];
463
+ }
464
+ if (font.family != null) {
465
+ result.name = [{ $: { val: font.family } }];
466
+ }
467
+ if (font.bold === true) {
468
+ result.b = [{}];
469
+ }
470
+ if (font.italic === true) {
471
+ result.i = [{}];
472
+ }
473
+ if (font.underline != null) {
474
+ result.u = [{ $: { val: font.underline } }];
475
+ }
476
+ if (font.strike === true) {
477
+ result.strike = [{}];
478
+ }
479
+ if (font.color != null) {
480
+ result.color = [{ $: { rgb: font.color.toUpperCase() } }];
481
+ }
482
+ return result;
483
+ }
484
+
485
+ private _parseFontXml(item: ExcelXmlStyleDataFont): ExcelFont {
486
+ const result: ExcelFont = {};
487
+ if (item.sz?.[0].$.val != null) {
488
+ const sz = num.parseFloat(item.sz[0].$.val);
489
+ if (sz != null) result.size = sz;
490
+ }
491
+ if (item.name?.[0].$.val != null) {
492
+ result.family = item.name[0].$.val;
493
+ }
494
+ if (item.b != null) result.bold = true;
495
+ if (item.i != null) result.italic = true;
496
+ if (item.u != null) {
497
+ result.underline = item.u[0].$?.val ?? "single";
498
+ }
499
+ if (item.strike != null) result.strike = true;
500
+ if (item.color?.[0].$.rgb != null) {
501
+ result.color = item.color[0].$.rgb;
502
+ }
503
+ return result;
504
+ }
505
+
506
+ private _getSameOrCreateFont(item: ExcelXmlStyleDataFont): string {
507
+ const prevSameFont = this.data.styleSheet.fonts[0].font.single((f) => obj.equal(f, item));
508
+ if (prevSameFont != null) {
509
+ return this.data.styleSheet.fonts[0].font.indexOf(prevSameFont).toString();
510
+ } else {
511
+ this.data.styleSheet.fonts[0].font.push(item);
512
+ this.data.styleSheet.fonts[0].$.count = this.data.styleSheet.fonts[0].font.length.toString();
513
+ return (this.data.styleSheet.fonts[0].font.length - 1).toString();
514
+ }
515
+ }
516
+
323
517
  private _applyAlignment(xf: ExcelXmlStyleDataXf, style: ExcelStyle): void {
324
518
  if (style.verticalAlign != null) {
325
519
  xf.$.applyAlignment = "1";
@@ -4,6 +4,7 @@ import type {
4
4
  ExcelCellType,
5
5
  ExcelRowData,
6
6
  ExcelXml,
7
+ ExcelXmlCfRuleData,
7
8
  ExcelXmlWorksheetData,
8
9
  } from "../types";
9
10
  import { ExcelUtils } from "../utils/excel-utils";
@@ -221,6 +222,41 @@ export class ExcelXmlWorksheet implements ExcelXml {
221
222
  }
222
223
  }
223
224
 
225
+ /**
226
+ * `<conditionalFormatting>` 블록을 시트에 push.
227
+ * priority 는 시트 전역 카운터(기존 cfRule 의 최대 priority + 1)부터 호출 내 rules 순서대로 부여.
228
+ */
229
+ addConditionalFormatting(sqref: string, rules: { dxfId: string; cfRule: Omit<ExcelXmlCfRuleData["$"], "priority" | "dxfId"> & { formula: string[] } }[]): void {
230
+ const cfList = (this.data.worksheet.conditionalFormatting =
231
+ this.data.worksheet.conditionalFormatting ?? []);
232
+
233
+ let nextPriority = 1;
234
+ for (const block of cfList) {
235
+ for (const rule of block.cfRule) {
236
+ const p = num.parseInt(rule.$.priority);
237
+ if (p != null && p >= nextPriority) {
238
+ nextPriority = p + 1;
239
+ }
240
+ }
241
+ }
242
+
243
+ const cfRuleData: ExcelXmlCfRuleData[] = rules.map((r) => ({
244
+ $: {
245
+ type: r.cfRule.type,
246
+ priority: (nextPriority++).toString(),
247
+ dxfId: r.dxfId,
248
+ ...(r.cfRule.operator != null ? { operator: r.cfRule.operator } : {}),
249
+ ...(r.cfRule.text != null ? { text: r.cfRule.text } : {}),
250
+ },
251
+ formula: r.cfRule.formula,
252
+ }));
253
+
254
+ cfList.push({
255
+ $: { sqref },
256
+ cfRule: cfRuleData,
257
+ });
258
+ }
259
+
224
260
  shiftMergeCells(fromRow: number, delta: number): void {
225
261
  const mergeCells = this.data.worksheet.mergeCells;
226
262
  if (mergeCells == null) return;
@@ -445,6 +481,7 @@ export class ExcelXmlWorksheet implements ExcelXml {
445
481
  if (key === "cols") continue;
446
482
  if (key === "sheetViews") continue;
447
483
  if (key === "sheetFormatPr") continue;
484
+ if (key === "conditionalFormatting") continue;
448
485
 
449
486
  if (key === "sheetData") {
450
487
  if (this.data.worksheet.sheetViews != null) {
@@ -461,6 +498,9 @@ export class ExcelXmlWorksheet implements ExcelXml {
461
498
  if (this.data.worksheet.mergeCells != null) {
462
499
  result.mergeCells = this.data.worksheet.mergeCells;
463
500
  }
501
+ if (this.data.worksheet.conditionalFormatting != null) {
502
+ result.conditionalFormatting = this.data.worksheet.conditionalFormatting;
503
+ }
464
504
  } else {
465
505
  const worksheetRec = this.data.worksheet as Record<string, unknown>;
466
506
  const resultRec = result as Record<string, unknown>;