@rowsncolumns/toolkit 24.4.5 → 24.5.1

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.
Files changed (113) hide show
  1. package/dist/cjs/autofilter-condition.d.ts +45 -1
  2. package/dist/cjs/autofilter-condition.d.ts.map +1 -1
  3. package/dist/cjs/autofilter-condition.js +135 -12
  4. package/dist/cjs/excel-parser/helpers.d.ts +15 -1
  5. package/dist/cjs/excel-parser/helpers.d.ts.map +1 -1
  6. package/dist/cjs/excel-parser/helpers.js +119 -5
  7. package/dist/cjs/excel-parser/interface.d.ts +3 -1
  8. package/dist/cjs/excel-parser/interface.d.ts.map +1 -1
  9. package/dist/cjs/excel-parser/plugins/ods/index.d.ts.map +1 -1
  10. package/dist/cjs/excel-parser/plugins/ods/index.js +186 -97
  11. package/dist/cjs/excel-parser/plugins/ods/styles.d.ts +15 -0
  12. package/dist/cjs/excel-parser/plugins/ods/styles.d.ts.map +1 -1
  13. package/dist/cjs/excel-parser/plugins/ods/styles.js +80 -6
  14. package/dist/cjs/excel-parser/plugins/xlsb/index.d.ts.map +1 -1
  15. package/dist/cjs/excel-parser/plugins/xlsb/index.js +107 -103
  16. package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts +77 -1
  17. package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts.map +1 -1
  18. package/dist/cjs/excel-parser/plugins/xlsx/charts.js +920 -122
  19. package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts +7 -1
  20. package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -1
  21. package/dist/cjs/excel-parser/plugins/xlsx/drawing.js +24 -9
  22. package/dist/cjs/excel-parser/plugins/xlsx/form-controls.d.ts +24 -3
  23. package/dist/cjs/excel-parser/plugins/xlsx/form-controls.d.ts.map +1 -1
  24. package/dist/cjs/excel-parser/plugins/xlsx/form-controls.js +25 -3
  25. package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts +24 -1
  26. package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts.map +1 -1
  27. package/dist/cjs/excel-parser/plugins/xlsx/index.js +561 -394
  28. package/dist/cjs/excel-parser/plugins/xlsx/pivots.d.ts +8 -2
  29. package/dist/cjs/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -1
  30. package/dist/cjs/excel-parser/plugins/xlsx/pivots.js +312 -21
  31. package/dist/cjs/excel-parser/plugins/xlsx/relationships.d.ts +6 -0
  32. package/dist/cjs/excel-parser/plugins/xlsx/relationships.d.ts.map +1 -1
  33. package/dist/cjs/excel-parser/plugins/xlsx/relationships.js +9 -0
  34. package/dist/cjs/excel-parser/plugins/xlsx/theme.d.ts +1 -1
  35. package/dist/cjs/excel-parser/types.d.ts +9 -1
  36. package/dist/cjs/excel-parser/types.d.ts.map +1 -1
  37. package/dist/cjs/excel-parser/workbook.d.ts +1 -0
  38. package/dist/cjs/excel-parser/workbook.d.ts.map +1 -1
  39. package/dist/cjs/excel-parser/workbook.js +5 -0
  40. package/dist/cjs/excel-parser/worker.js +2 -0
  41. package/dist/cjs/exporter/ods/ods-export.d.ts.map +1 -1
  42. package/dist/cjs/exporter/ods/ods-export.js +255 -80
  43. package/dist/cjs/exporter/xlsx/chart-builder.d.ts +7 -1
  44. package/dist/cjs/exporter/xlsx/chart-builder.d.ts.map +1 -1
  45. package/dist/cjs/exporter/xlsx/chart-builder.js +902 -119
  46. package/dist/cjs/exporter/xlsx/form-controls-builder.d.ts +16 -3
  47. package/dist/cjs/exporter/xlsx/form-controls-builder.d.ts.map +1 -1
  48. package/dist/cjs/exporter/xlsx/form-controls-builder.js +30 -8
  49. package/dist/cjs/exporter/xlsx/pivot-builder.d.ts.map +1 -1
  50. package/dist/cjs/exporter/xlsx/pivot-builder.js +206 -162
  51. package/dist/cjs/exporter/xlsx/style-builder.d.ts +8 -2
  52. package/dist/cjs/exporter/xlsx/style-builder.d.ts.map +1 -1
  53. package/dist/cjs/exporter/xlsx/style-builder.js +25 -0
  54. package/dist/cjs/exporter/xlsx/xlsx-export.d.ts +2 -2
  55. package/dist/cjs/exporter/xlsx/xlsx-export.d.ts.map +1 -1
  56. package/dist/cjs/exporter/xlsx/xlsx-export.js +884 -470
  57. package/dist/esm/autofilter-condition.d.ts +45 -1
  58. package/dist/esm/autofilter-condition.d.ts.map +1 -1
  59. package/dist/esm/autofilter-condition.js +131 -11
  60. package/dist/esm/excel-parser/helpers.d.ts +15 -1
  61. package/dist/esm/excel-parser/helpers.d.ts.map +1 -1
  62. package/dist/esm/excel-parser/helpers.js +94 -4
  63. package/dist/esm/excel-parser/interface.d.ts +3 -1
  64. package/dist/esm/excel-parser/interface.d.ts.map +1 -1
  65. package/dist/esm/excel-parser/plugins/ods/index.d.ts.map +1 -1
  66. package/dist/esm/excel-parser/plugins/ods/index.js +105 -22
  67. package/dist/esm/excel-parser/plugins/ods/styles.d.ts +15 -0
  68. package/dist/esm/excel-parser/plugins/ods/styles.d.ts.map +1 -1
  69. package/dist/esm/excel-parser/plugins/ods/styles.js +79 -5
  70. package/dist/esm/excel-parser/plugins/xlsb/index.d.ts.map +1 -1
  71. package/dist/esm/excel-parser/plugins/xlsb/index.js +15 -7
  72. package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts +77 -1
  73. package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts.map +1 -1
  74. package/dist/esm/excel-parser/plugins/xlsx/charts.js +708 -16
  75. package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts +7 -1
  76. package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -1
  77. package/dist/esm/excel-parser/plugins/xlsx/drawing.js +23 -0
  78. package/dist/esm/excel-parser/plugins/xlsx/form-controls.d.ts +24 -3
  79. package/dist/esm/excel-parser/plugins/xlsx/form-controls.d.ts.map +1 -1
  80. package/dist/esm/excel-parser/plugins/xlsx/form-controls.js +21 -3
  81. package/dist/esm/excel-parser/plugins/xlsx/index.d.ts +24 -1
  82. package/dist/esm/excel-parser/plugins/xlsx/index.d.ts.map +1 -1
  83. package/dist/esm/excel-parser/plugins/xlsx/index.js +192 -54
  84. package/dist/esm/excel-parser/plugins/xlsx/pivots.d.ts +8 -2
  85. package/dist/esm/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -1
  86. package/dist/esm/excel-parser/plugins/xlsx/pivots.js +220 -4
  87. package/dist/esm/excel-parser/plugins/xlsx/relationships.d.ts +6 -0
  88. package/dist/esm/excel-parser/plugins/xlsx/relationships.d.ts.map +1 -1
  89. package/dist/esm/excel-parser/plugins/xlsx/relationships.js +9 -0
  90. package/dist/esm/excel-parser/plugins/xlsx/theme.d.ts +1 -1
  91. package/dist/esm/excel-parser/types.d.ts +9 -1
  92. package/dist/esm/excel-parser/types.d.ts.map +1 -1
  93. package/dist/esm/excel-parser/workbook.d.ts +1 -0
  94. package/dist/esm/excel-parser/workbook.d.ts.map +1 -1
  95. package/dist/esm/excel-parser/workbook.js +4 -0
  96. package/dist/esm/excel-parser/worker.js +2 -0
  97. package/dist/esm/exporter/ods/ods-export.d.ts.map +1 -1
  98. package/dist/esm/exporter/ods/ods-export.js +238 -72
  99. package/dist/esm/exporter/xlsx/chart-builder.d.ts +7 -1
  100. package/dist/esm/exporter/xlsx/chart-builder.d.ts.map +1 -1
  101. package/dist/esm/exporter/xlsx/chart-builder.js +674 -58
  102. package/dist/esm/exporter/xlsx/form-controls-builder.d.ts +16 -3
  103. package/dist/esm/exporter/xlsx/form-controls-builder.d.ts.map +1 -1
  104. package/dist/esm/exporter/xlsx/form-controls-builder.js +29 -8
  105. package/dist/esm/exporter/xlsx/pivot-builder.d.ts.map +1 -1
  106. package/dist/esm/exporter/xlsx/pivot-builder.js +187 -139
  107. package/dist/esm/exporter/xlsx/style-builder.d.ts +8 -2
  108. package/dist/esm/exporter/xlsx/style-builder.d.ts.map +1 -1
  109. package/dist/esm/exporter/xlsx/style-builder.js +28 -0
  110. package/dist/esm/exporter/xlsx/xlsx-export.d.ts +2 -2
  111. package/dist/esm/exporter/xlsx/xlsx-export.d.ts.map +1 -1
  112. package/dist/esm/exporter/xlsx/xlsx-export.js +600 -206
  113. package/package.json +10 -5
@@ -1,4 +1,4 @@
1
- import type { EmbedFormControlSpec } from "@rowsncolumns/spreadsheet";
1
+ import type { EmbedFormControlSpec, OverlayPosition } from "@rowsncolumns/spreadsheet";
2
2
  /**
3
3
  * Excel form-control writers — the inverse of
4
4
  * `excel-parser/plugins/xlsx/form-controls.ts`. A control is three parts:
@@ -20,13 +20,25 @@ export type ControlCellAnchor = {
20
20
  row: number;
21
21
  rowOff: number;
22
22
  };
23
+ /**
24
+ * The `<anchor moveWithCells sizeWithCells>` flags to write: the control's
25
+ * placement mode when its position carries one (Move and size = both, Move
26
+ * but don't size = `moveWithCells`, Don't move or size = neither) — a
27
+ * persisted second anchor is a "Move and size" object too — else the flags
28
+ * the spec arrived with (#668 C-46).
29
+ */
30
+ export declare const controlAnchorFlags: (spec: Pick<EmbedFormControlSpec, "moveWithCells" | "sizeWithCells">, overlay?: Pick<OverlayPosition, "placement" | "toCell"> | null) => {
31
+ moveWithCells: boolean;
32
+ sizeWithCells: boolean;
33
+ };
23
34
  /** One `<control>` element (inside the `<controls>` wrapper). */
24
- export declare const buildControlElementXml: ({ spec, relId, shapeId, from, to, }: {
35
+ export declare const buildControlElementXml: ({ spec, relId, shapeId, from, to, overlay, }: {
25
36
  spec: EmbedFormControlSpec;
26
37
  relId: string;
27
38
  shapeId: number;
28
39
  from: ControlCellAnchor;
29
40
  to: ControlCellAnchor;
41
+ overlay?: Pick<OverlayPosition, "placement" | "toCell"> | null;
30
42
  }) => string;
31
43
  /**
32
44
  * The worksheet `<controls>` element. Excel wraps controls in an
@@ -47,11 +59,12 @@ export type FormControlVmlGeometry = {
47
59
  anchor: [number, number, number, number, number, number, number, number];
48
60
  };
49
61
  /** One `<v:shape>` drawing a form control. */
50
- export declare const buildFormControlVmlShape: ({ spec, shapeId, zIndex, geometry, }: {
62
+ export declare const buildFormControlVmlShape: ({ spec, shapeId, zIndex, geometry, overlay, }: {
51
63
  spec: EmbedFormControlSpec;
52
64
  shapeId: number;
53
65
  zIndex: number;
54
66
  geometry: FormControlVmlGeometry;
67
+ overlay?: Pick<OverlayPosition, "placement" | "toCell"> | null;
55
68
  }) => string;
56
69
  /** Wrap note + control shapes into one `vmlDrawing` part. */
57
70
  export declare const buildVmlDrawingXml: ({ shapeTypes, shapes, }: {
@@ -1 +1 @@
1
- {"version":3,"file":"form-controls-builder.d.ts","sourceRoot":"","sources":["../../../../exporter/xlsx/form-controls-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE;;;;;;;;GAQG;AAEH,eAAO,MAAM,sBAAsB,mDACe,CAAC;AACnD,eAAO,MAAM,kBAAkB,iFACiD,CAAC;AACjF,eAAO,MAAM,eAAe,kEACqC,CAAC;AAuBlE,yCAAyC;AACzC,eAAO,MAAM,gBAAgB,GAAI,MAAM,oBAAoB,KAAG,MAU7D,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AASF,iEAAiE;AACjE,eAAO,MAAM,sBAAsB,GAAI,qCAMpC;IACD,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,iBAAiB,CAAC;IACxB,EAAE,EAAE,iBAAiB,CAAC;CACvB,KAAG,MAaH,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,iBAAiB,MAAM,EAAE,KAAG,MAK5D,CAAC;AAmEF,0EAA0E;AAC1E,eAAO,MAAM,0BAA0B,oSAA4Q,CAAC;AAEpT,iFAAiF;AACjF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1E,CAAC;AAIF,8CAA8C;AAC9C,eAAO,MAAM,wBAAwB,GAAI,sCAKtC;IACD,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,sBAAsB,CAAC;CAClC,KAAG,MAmBH,CAAC;AAEF,6DAA6D;AAC7D,eAAO,MAAM,kBAAkB,GAAI,yBAGhC;IACD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,KAAG,MAAM,GAAG,SAKZ,CAAC"}
1
+ {"version":3,"file":"form-controls-builder.d.ts","sourceRoot":"","sources":["../../../../exporter/xlsx/form-controls-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EAChB,MAAM,2BAA2B,CAAC;AAEnC;;;;;;;;GAQG;AAEH,eAAO,MAAM,sBAAsB,mDACe,CAAC;AACnD,eAAO,MAAM,kBAAkB,iFACiD,CAAC;AACjF,eAAO,MAAM,eAAe,kEACqC,CAAC;AAuBlE,yCAAyC;AACzC,eAAO,MAAM,gBAAgB,GAAI,MAAM,oBAAoB,KAAG,MAU7D,CAAC;AAEF,gEAAgE;AAChE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,IAAI,CAAC,oBAAoB,EAAE,eAAe,GAAG,eAAe,CAAC,EACnE,UAAU,IAAI,CAAC,eAAe,EAAE,WAAW,GAAG,QAAQ,CAAC,GAAG,IAAI,KAC7D;IAAE,aAAa,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,OAAO,CAAA;CAalD,CAAC;AASF,iEAAiE;AACjE,eAAO,MAAM,sBAAsB,GAAI,8CAOpC;IACD,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,iBAAiB,CAAC;IACxB,EAAE,EAAE,iBAAiB,CAAC;IACtB,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC;CAChE,KAAG,MAcH,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,iBAAiB,MAAM,EAAE,KAAG,MAK5D,CAAC;AAsEF,0EAA0E;AAC1E,eAAO,MAAM,0BAA0B,oSAA4Q,CAAC;AAEpT,iFAAiF;AACjF,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1E,CAAC;AAIF,8CAA8C;AAC9C,eAAO,MAAM,wBAAwB,GAAI,+CAMtC;IACD,IAAI,EAAE,oBAAoB,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,sBAAsB,CAAC;IACjC,OAAO,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,GAAG,QAAQ,CAAC,GAAG,IAAI,CAAC;CAChE,KAAG,MAsBH,CAAC;AAEF,6DAA6D;AAC7D,eAAO,MAAM,kBAAkB,GAAI,yBAGhC;IACD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,KAAG,MAAM,GAAG,SAKZ,CAAC"}
@@ -33,12 +33,33 @@ export const buildCtrlPropXml = (spec) => {
33
33
  const open = `<formControlPr xmlns="${FORM_CONTROL_NS}" objectType="${escapeAttr(spec.objectType)}"${attrsXml(rest)}`;
34
34
  return `${XML_DECLARATION}${body ? `${open}>${body}</formControlPr>` : `${open}/>`}`;
35
35
  };
36
+ /**
37
+ * The `<anchor moveWithCells sizeWithCells>` flags to write: the control's
38
+ * placement mode when its position carries one (Move and size = both, Move
39
+ * but don't size = `moveWithCells`, Don't move or size = neither) — a
40
+ * persisted second anchor is a "Move and size" object too — else the flags
41
+ * the spec arrived with (#668 C-46).
42
+ */
43
+ export const controlAnchorFlags = (spec, overlay) => {
44
+ const placement = overlay?.placement ?? (overlay?.toCell ? "moveAndSize" : undefined);
45
+ if (placement) {
46
+ return {
47
+ moveWithCells: placement !== "absolute",
48
+ sizeWithCells: placement === "moveAndSize",
49
+ };
50
+ }
51
+ return {
52
+ moveWithCells: spec.moveWithCells === true,
53
+ sizeWithCells: spec.sizeWithCells === true,
54
+ };
55
+ };
36
56
  const cellAnchorXml = (tag, anchor) => `<${tag}><xdr:col>${anchor.col}</xdr:col><xdr:colOff>${Math.round(anchor.colOff)}</xdr:colOff><xdr:row>${anchor.row}</xdr:row><xdr:rowOff>${Math.round(anchor.rowOff)}</xdr:rowOff></${tag}>`;
37
57
  /** One `<control>` element (inside the `<controls>` wrapper). */
38
- export const buildControlElementXml = ({ spec, relId, shapeId, from, to, }) => {
58
+ export const buildControlElementXml = ({ spec, relId, shapeId, from, to, overlay, }) => {
39
59
  const name = spec.name ?? `${spec.objectType} ${shapeId}`;
40
- const anchorAttrs = (spec.moveWithCells ? ' moveWithCells="1"' : "") +
41
- (spec.sizeWithCells ? ' sizeWithCells="1"' : "");
60
+ const flags = controlAnchorFlags(spec, overlay);
61
+ const anchorAttrs = (flags.moveWithCells ? ' moveWithCells="1"' : "") +
62
+ (flags.sizeWithCells ? ' sizeWithCells="1"' : "");
42
63
  return `<control shapeId="${shapeId}" r:id="${relId}" name="${escapeAttr(name)}"><controlPr${attrsXml(spec.controlProperties)}><anchor${anchorAttrs}>${cellAnchorXml("from", from)}${cellAnchorXml("to", to)}</anchor></controlPr></control>`;
43
64
  };
44
65
  /**
@@ -64,7 +85,7 @@ const truthy = (value) => value === "1" || value === "true";
64
85
  * range, selection, scroll values) from the VML, the ctrlProp part is the
65
86
  * x14 mirror.
66
87
  */
67
- const clientDataChildrenXml = (spec) => {
88
+ const clientDataChildrenXml = (spec, flags) => {
68
89
  const p = spec.properties ?? {};
69
90
  const c = spec.controlProperties ?? {};
70
91
  const out = [];
@@ -74,9 +95,9 @@ const clientDataChildrenXml = (spec) => {
74
95
  else
75
96
  out.push(`<x:${tag}>${escapeText(value)}</x:${tag}>`);
76
97
  };
77
- if (spec.moveWithCells)
98
+ if (flags.moveWithCells)
78
99
  push("MoveWithCells");
79
- if (spec.sizeWithCells)
100
+ if (flags.sizeWithCells)
80
101
  push("SizeWithCells");
81
102
  // Anchor is inserted by the caller (needs sheet geometry).
82
103
  out.push("__ANCHOR__");
@@ -156,12 +177,12 @@ const clientDataChildrenXml = (spec) => {
156
177
  export const FORM_CONTROL_VML_SHAPETYPE = `<v:shapetype id="_x0000_t201" coordsize="21600,21600" o:spt="201" path="m,l,21600r21600,l21600,xe"><v:stroke joinstyle="miter"/><v:path shadowok="f" o:extrusionok="f" strokeok="f" fillok="f" o:connecttype="rect"/><o:lock v:ext="edit" shapetype="t"/></v:shapetype>`;
157
178
  const toPt = (px) => `${(px * 0.75).toFixed(2).replace(/\.?0+$/, "")}pt`;
158
179
  /** One `<v:shape>` drawing a form control. */
159
- export const buildFormControlVmlShape = ({ spec, shapeId, zIndex, geometry, }) => {
180
+ export const buildFormControlVmlShape = ({ spec, shapeId, zIndex, geometry, overlay, }) => {
160
181
  const objectType = VML_OBJECT_TYPE[spec.objectType] ?? spec.objectType;
161
182
  const caption = spec.text
162
183
  ? `<div style="text-align:left"><font face="Tahoma" size="160" color="auto">${escapeText(spec.text)}</font></div>`
163
184
  : "";
164
- const clientData = clientDataChildrenXml(spec).replace("__ANCHOR__", `<x:Anchor>${geometry.anchor.join(", ")}</x:Anchor>`);
185
+ const clientData = clientDataChildrenXml(spec, controlAnchorFlags(spec, overlay)).replace("__ANCHOR__", `<x:Anchor>${geometry.anchor.join(", ")}</x:Anchor>`);
165
186
  const style = `position:absolute;margin-left:${toPt(geometry.leftPx)};margin-top:${toPt(geometry.topPx)};width:${toPt(geometry.widthPx)};height:${toPt(geometry.heightPx)};z-index:${zIndex};mso-wrap-style:tight`;
166
187
  return `<v:shape id="_x0000_s${shapeId}" type="#_x0000_t201" style="${style}" filled="f" fillcolor="window [65]" stroked="f" strokecolor="windowText [64]" o:insetmode="auto"><v:path shadowok="f" o:extrusionok="f" strokeok="f" fillok="f" o:connecttype="rect"/><v:textbox style="mso-direction-alt:auto" o:singleclick="f">${caption}</v:textbox><x:ClientData ObjectType="${escapeAttr(objectType)}">${clientData}</x:ClientData></v:shape>`;
167
188
  };
@@ -1 +1 @@
1
- {"version":3,"file":"pivot-builder.d.ts","sourceRoot":"","sources":["../../../../exporter/xlsx/pivot-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,UAAU,EAAc,MAAM,2BAA2B,CAAC;AAqBxE;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,MAAM,KAAG,MAAM,EAYjE,CAAC;AA2DF;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GACvC,OAAO,UAAU,EACjB,WAAW,MAAM,EACjB,YAAY,MAAM,EAAE,KACnB,MAAM,GAAG,IAuCX,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,UAAU,EACjB,SAAS,MAAM,EACf,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAChC,MAAM,GAAG,IA2JX,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GACjC,UAAU,MAAM,EAChB,aAAa,MAAM,KAClB,MAcF,CAAC"}
1
+ {"version":3,"file":"pivot-builder.d.ts","sourceRoot":"","sources":["../../../../exporter/xlsx/pivot-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAKV,UAAU,EAEX,MAAM,2BAA2B,CAAC;AAqBnC;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,MAAM,KAAG,MAAM,EAYjE,CAAC;AA6HF;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GACvC,OAAO,UAAU,EACjB,WAAW,MAAM,EACjB,YAAY,MAAM,EAAE,KACnB,MAAM,GAAG,IA2CX,CAAC;AAsDF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,UAAU,EACjB,SAAS,MAAM,EACf,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KAChC,MAAM,GAAG,IAkKX,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GACjC,UAAU,MAAM,EAChB,aAAa,MAAM,KAClB,MAUF,CAAC"}
@@ -40,10 +40,26 @@ export const calculatedFieldReferences = (formula) => {
40
40
  };
41
41
  const xmlBuilder = new XMLBuilder({
42
42
  ignoreAttributes: false,
43
- attributeNamePrefix: "@_",
43
+ preserveOrder: true,
44
+ attributeNamePrefix: "",
45
+ suppressEmptyNode: true,
44
46
  format: true,
45
47
  indentBy: " ",
46
48
  });
49
+ /** An element with `attributes` (undefined values dropped) and ordered `children`. */
50
+ const el = (tag, attributes = {}, children = []) => {
51
+ const attrs = {};
52
+ for (const [key, value] of Object.entries(attributes)) {
53
+ if (value !== undefined)
54
+ attrs[key] = value;
55
+ }
56
+ return Object.keys(attrs).length ? { [tag]: children, ":@": attrs } : { [tag]: children };
57
+ };
58
+ const XML_DECLARATION = {
59
+ "?xml": [{ "#text": "" }],
60
+ ":@": { version: "1.0", encoding: "UTF-8", standalone: "yes" },
61
+ };
62
+ const buildDocument = (root) => xmlBuilder.build([XML_DECLARATION, root]);
47
63
  /**
48
64
  * Convert numeric col/row to A1 notation. 1-indexed.
49
65
  * e.g. (1, 1) → "A1", (28, 5) → "AB5".
@@ -80,6 +96,23 @@ const aggToSubtotal = (agg) => {
80
96
  };
81
97
  return map[agg] ?? "sum";
82
98
  };
99
+ /** Preserved XLSX cache items by cache-field name (`PivotTable.cacheFields`). */
100
+ const preservedCacheFields = (pivot) => new Map((pivot.cacheFields ?? []).map((field) => [field.name, field]));
101
+ /**
102
+ * Preserved `<sharedItems …>`: its attributes, `count` only with an item list
103
+ * (Excel's form — a numeric field keeps just its type attributes), children
104
+ * in document order (`<s v/>`, `<n v/>`, `<m/>`… with any further attributes).
105
+ */
106
+ const buildSharedItems = (shared) => el("sharedItems", {
107
+ ...(shared.attributes ?? {}),
108
+ ...(shared.items.length ? { count: String(shared.items.length) } : {}),
109
+ }, shared.items.map((item) => el(item.type, { v: item.value, ...(item.attributes ?? {}) })));
110
+ /** Preserved `<pivotField><items>`: `x`, `t`, the rest verbatim, in Excel's order. */
111
+ const buildFieldItems = (items) => el("items", { count: String(items.length) }, items.map((item) => el("item", {
112
+ ...(item.x !== undefined ? { x: String(item.x) } : {}),
113
+ ...(item.t !== undefined ? { t: item.t } : {}),
114
+ ...(item.attributes ?? {}),
115
+ })));
83
116
  /**
84
117
  * Build pivotCacheDefinition XML for a pivot. The cacheSource
85
118
  * points at the live worksheet range; `refreshOnLoad="1"` makes
@@ -96,35 +129,64 @@ export const buildPivotCacheDefinitionXml = (pivot, sheetName, fieldNames) => {
96
129
  // Calculated fields are cache fields too (`databaseField="0"` +
97
130
  // `formula`), listed after the source columns like Excel does.
98
131
  const calculatedByName = new Map((pivot.calculatedFields ?? []).map((c) => [c.name, c.formula]));
132
+ const preserved = preservedCacheFields(pivot);
133
+ let preservedAny = false;
99
134
  const cacheFields = fieldNames.map((name) => {
100
135
  const formula = calculatedByName.get(name);
101
- return formula !== undefined
102
- ? { "@_name": name, "@_formula": formula, "@_databaseField": "0" }
103
- : { "@_name": name, sharedItems: { "@_count": "0" } };
136
+ if (formula !== undefined) {
137
+ return el("cacheField", { name, formula, databaseField: "0" });
138
+ }
139
+ const shared = preserved.get(name)?.sharedItems;
140
+ if (!shared)
141
+ return el("cacheField", { name }, [el("sharedItems", { count: "0" })]);
142
+ preservedAny = true;
143
+ return el("cacheField", { name }, [buildSharedItems(shared)]);
104
144
  });
105
- const doc = {
106
- "?xml": { "@_version": "1.0", "@_encoding": "UTF-8", "@_standalone": "yes" },
107
- pivotCacheDefinition: {
108
- "@_xmlns": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
109
- "@_xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
110
- "@_invalid": "1",
111
- "@_refreshOnLoad": "1",
112
- "@_refreshedBy": "Rows n Columns",
113
- "@_recordCount": "0",
114
- cacheSource: {
115
- "@_type": "worksheet",
116
- worksheetSource: {
117
- "@_ref": sourceRef,
118
- "@_sheet": sheetName,
119
- },
120
- },
121
- cacheFields: {
122
- "@_count": String(cacheFields.length),
123
- cacheField: cacheFields,
124
- },
125
- },
126
- };
127
- return xmlBuilder.build(doc);
145
+ return buildDocument(el("pivotCacheDefinition", {
146
+ xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
147
+ "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
148
+ invalid: "1",
149
+ refreshOnLoad: "1",
150
+ refreshedBy: "Rows n Columns",
151
+ recordCount: "0",
152
+ // Preserved item lists with no records part: Excel's own "don't save
153
+ // source data" shape (`saveData="0"`), rebuilt on open by refreshOnLoad.
154
+ ...(preservedAny ? { saveData: "0" } : {}),
155
+ }, [
156
+ el("cacheSource", { type: "worksheet" }, [
157
+ el("worksheetSource", { ref: sourceRef, sheet: sheetName }),
158
+ ]),
159
+ el("cacheFields", { count: String(cacheFields.length) }, cacheFields),
160
+ ]));
161
+ };
162
+ /** `PivotArea` → `<pivotArea …><references count><reference … count><x v/>…` (Excel's `<x/>` order). */
163
+ const buildPivotArea = (area) => {
164
+ const references = area.references ?? [];
165
+ return el("pivotArea", area.attributes ?? {}, references.length
166
+ ? [
167
+ el("references", { count: String(references.length) }, references.map((ref) => {
168
+ const items = ref.items ?? [];
169
+ return el("reference", { ...(ref.attributes ?? {}), count: String(items.length) }, items.map((v) => el("x", { v: String(v) })));
170
+ })),
171
+ ]
172
+ : []);
173
+ };
174
+ /**
175
+ * `PivotTable.chartFormats` → `<chartFormats count><chartFormat chart
176
+ * format series><pivotArea>…`: the pivot areas a PivotChart's
177
+ * `<c:pivotFmt>` entries (`format`) format, keyed by the chart's
178
+ * `<c:fmtId>` (`chart`). Nothing is written without formats — Excel
179
+ * itself keeps `<c:pivotFmt>` entries with no `<chartFormat>`.
180
+ */
181
+ const buildChartFormats = (pivot) => {
182
+ const formats = pivot.chartFormats ?? [];
183
+ if (!formats.length)
184
+ return undefined;
185
+ return el("chartFormats", { count: String(formats.length) }, formats.map((format) => el("chartFormat", {
186
+ chart: String(format.chart),
187
+ format: String(format.format),
188
+ ...(format.series ? { series: "1" } : {}),
189
+ }, [buildPivotArea(format.pivotArea ?? {})])));
128
190
  };
129
191
  /**
130
192
  * Build the pivotTable layout XML. Excel determines whether a cell
@@ -165,131 +227,122 @@ export const buildPivotTableXml = (pivot, cacheId, fieldIndices) => {
165
227
  dataFieldIndices.add(idx);
166
228
  }
167
229
  const calculatedNames = new Set((pivot.calculatedFields ?? []).map((c) => c.name));
230
+ const preserved = preservedCacheFields(pivot);
168
231
  // Need to emit one <pivotField> per cacheField in order, even
169
232
  // for unused fields (Excel requires the array shapes match).
170
233
  // Caller passes us the full field set via fieldIndices.
171
234
  const sortedFieldIndices = [...fieldIndices.entries()].sort((a, b) => a[1] - b[1]);
172
235
  for (const [name, idx] of sortedFieldIndices) {
173
236
  const axis = fieldAxis.get(idx);
174
- const entry = {};
175
- if (axis)
176
- entry["@_axis"] = axis;
177
- if (dataFieldIndices.has(idx))
178
- entry["@_dataField"] = "1";
237
+ const attributes = {
238
+ axis,
239
+ dataField: dataFieldIndices.has(idx) ? "1" : undefined,
240
+ };
179
241
  if (calculatedNames.has(name)) {
180
242
  // Excel pins calculated fields to the values area.
181
- entry["@_dragToRow"] = "0";
182
- entry["@_dragToCol"] = "0";
183
- entry["@_dragToPage"] = "0";
184
- entry["@_showAll"] = "0";
185
- entry["@_defaultSubtotal"] = "0";
243
+ pivotFields.push(el("pivotField", {
244
+ ...attributes,
245
+ dragToRow: "0",
246
+ dragToCol: "0",
247
+ dragToPage: "0",
248
+ showAll: "0",
249
+ defaultSubtotal: "0",
250
+ }));
251
+ continue;
186
252
  }
187
- else {
188
- entry["@_compact"] = "0";
189
- if (axis)
190
- entry.items = {};
191
- }
192
- pivotFields.push(entry);
253
+ // Preserved `<items>` (C-84) keep Excel's item order — the indices the
254
+ // pivot's `<chartFormats>` pivot areas reference.
255
+ const items = preserved.get(name)?.items;
256
+ const children = items?.length ? [buildFieldItems(items)] : axis ? [el("items")] : [];
257
+ pivotFields.push(el("pivotField", { ...attributes, compact: "0" }, children));
193
258
  }
194
- const rowFields = (pivot.rows ?? [])
259
+ const rowFieldIndices = (pivot.rows ?? [])
195
260
  .map((r) => fieldIndices.get(r.field))
196
- .filter((i) => i != null)
197
- .map((i) => ({ "@_x": String(i) }));
198
- const colFields = (pivot.columns ?? [])
261
+ .filter((i) => i != null);
262
+ const colFieldIndices = (pivot.columns ?? [])
199
263
  .map((c) => fieldIndices.get(c.field))
200
- .filter((i) => i != null)
201
- .map((i) => ({ "@_x": String(i) }));
264
+ .filter((i) => i != null);
265
+ const axisFields = (indices) => indices.map((i) => el("field", { x: String(i) }));
202
266
  // Running totals need a base field: Excel defaults to the first row
203
267
  // field (falls back to the first column field / field 0).
204
- const runTotalBase = rowFields[0]?.["@_x"] ?? colFields[0]?.["@_x"] ?? "0";
268
+ const runTotalBase = String(rowFieldIndices[0] ?? colFieldIndices[0] ?? 0);
205
269
  const dataFields = (pivot.values ?? [])
206
270
  .map((v) => {
207
271
  const idx = fieldIndices.get(v.field);
208
272
  if (idx == null)
209
273
  return null;
210
- const entry = {
211
- "@_name": v.displayName ?? `${v.aggFunc} of ${v.field}`,
212
- "@_fld": String(idx),
213
- "@_subtotal": calculatedNames.has(v.field)
214
- ? "sum"
215
- : aggToSubtotal(v.aggFunc),
216
- };
217
274
  const showDataAs = v.showAs ? SHOW_DATA_AS[v.showAs] : undefined;
218
- if (showDataAs) {
219
- entry["@_showDataAs"] = showDataAs;
220
- if (showDataAs === "runTotal")
221
- entry["@_baseField"] = runTotalBase;
222
- }
223
275
  const pivotShowAs = v.showAs ? X14_PIVOT_SHOW_AS[v.showAs] : undefined;
224
- if (pivotShowAs) {
225
- entry.extLst = {
226
- ext: {
227
- "@_uri": X14_DATA_FIELD_EXT_URI,
228
- "@_xmlns:x14": X14_NS,
229
- "x14:dataField": { "@_pivotShowAs": pivotShowAs },
230
- },
231
- };
232
- }
233
- return entry;
276
+ return el("dataField", {
277
+ name: v.displayName ?? `${v.aggFunc} of ${v.field}`,
278
+ fld: String(idx),
279
+ subtotal: calculatedNames.has(v.field) ? "sum" : aggToSubtotal(v.aggFunc),
280
+ showDataAs,
281
+ baseField: showDataAs === "runTotal" ? runTotalBase : undefined,
282
+ }, pivotShowAs
283
+ ? [
284
+ el("extLst", {}, [
285
+ el("ext", { uri: X14_DATA_FIELD_EXT_URI, "xmlns:x14": X14_NS }, [
286
+ el("x14:dataField", { pivotShowAs }),
287
+ ]),
288
+ ]),
289
+ ]
290
+ : []);
234
291
  })
235
292
  .filter((d) => d !== null);
236
293
  const styleInfo = pivot.styleInfo ?? {};
237
294
  const styleFlag = (value, fallback) => value === undefined ? fallback : value ? "1" : "0";
238
- const doc = {
239
- "?xml": { "@_version": "1.0", "@_encoding": "UTF-8", "@_standalone": "yes" },
240
- pivotTableDefinition: {
241
- "@_xmlns": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
242
- "@_name": pivot.name ?? `PivotTable${pivot.pivotId}`,
243
- "@_cacheId": String(cacheId),
244
- "@_dataOnRows": "0",
245
- "@_applyNumberFormats": "0",
246
- "@_applyBorderFormats": "0",
247
- "@_applyFontFormats": "0",
248
- "@_applyPatternFormats": "0",
249
- "@_applyAlignmentFormats": "0",
250
- "@_applyWidthHeightFormats": "1",
251
- "@_dataCaption": "Values",
252
- "@_updatedVersion": "8",
253
- "@_minRefreshableVersion": "3",
254
- "@_showCalcMbrs": "0",
255
- "@_useAutoFormatting": "1",
256
- "@_itemPrintTitles": "1",
257
- "@_createdVersion": "8",
258
- "@_indent": "0",
259
- "@_outline": "1",
260
- "@_outlineData": "1",
261
- "@_multipleFieldFilters": "0",
262
- "@_rowGrandTotals": pivot.showRowGrandTotals === false ? "0" : "1",
263
- "@_colGrandTotals": pivot.showColumnGrandTotals === false ? "0" : "1",
264
- location: {
265
- "@_ref": ref,
266
- "@_firstHeaderRow": "1",
267
- "@_firstDataRow": "1",
268
- "@_firstDataCol": "1",
269
- },
270
- pivotFields: pivotFields.length
271
- ? { "@_count": String(pivotFields.length), pivotField: pivotFields }
272
- : undefined,
273
- rowFields: rowFields.length
274
- ? { "@_count": String(rowFields.length), field: rowFields }
275
- : undefined,
276
- colFields: colFields.length
277
- ? { "@_count": String(colFields.length), field: colFields }
278
- : undefined,
279
- dataFields: dataFields.length
280
- ? { "@_count": String(dataFields.length), dataField: dataFields }
281
- : undefined,
282
- pivotTableStyleInfo: {
283
- "@_name": styleInfo.name || "PivotStyleLight16",
284
- "@_showRowHeaders": styleFlag(styleInfo.showRowHeaders, "1"),
285
- "@_showColHeaders": styleFlag(styleInfo.showColHeaders, "1"),
286
- "@_showRowStripes": styleFlag(styleInfo.showRowStripes, "0"),
287
- "@_showColStripes": styleFlag(styleInfo.showColStripes, "0"),
288
- "@_showLastColumn": styleFlag(styleInfo.showLastColumn, "1"),
289
- },
290
- },
291
- };
292
- return xmlBuilder.build(doc);
295
+ const chartFormats = buildChartFormats(pivot);
296
+ return buildDocument(el("pivotTableDefinition", {
297
+ xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
298
+ name: pivot.name ?? `PivotTable${pivot.pivotId}`,
299
+ cacheId: String(cacheId),
300
+ dataOnRows: "0",
301
+ applyNumberFormats: "0",
302
+ applyBorderFormats: "0",
303
+ applyFontFormats: "0",
304
+ applyPatternFormats: "0",
305
+ applyAlignmentFormats: "0",
306
+ applyWidthHeightFormats: "1",
307
+ dataCaption: "Values",
308
+ updatedVersion: "8",
309
+ minRefreshableVersion: "3",
310
+ showCalcMbrs: "0",
311
+ useAutoFormatting: "1",
312
+ itemPrintTitles: "1",
313
+ createdVersion: "8",
314
+ indent: "0",
315
+ outline: "1",
316
+ outlineData: "1",
317
+ multipleFieldFilters: "0",
318
+ rowGrandTotals: pivot.showRowGrandTotals === false ? "0" : "1",
319
+ colGrandTotals: pivot.showColumnGrandTotals === false ? "0" : "1",
320
+ }, [
321
+ el("location", { ref, firstHeaderRow: "1", firstDataRow: "1", firstDataCol: "1" }),
322
+ ...(pivotFields.length
323
+ ? [el("pivotFields", { count: String(pivotFields.length) }, pivotFields)]
324
+ : []),
325
+ ...(rowFieldIndices.length
326
+ ? [el("rowFields", { count: String(rowFieldIndices.length) }, axisFields(rowFieldIndices))]
327
+ : []),
328
+ ...(colFieldIndices.length
329
+ ? [el("colFields", { count: String(colFieldIndices.length) }, axisFields(colFieldIndices))]
330
+ : []),
331
+ ...(dataFields.length
332
+ ? [el("dataFields", { count: String(dataFields.length) }, dataFields)]
333
+ : []),
334
+ // CT_pivotTableDefinition order: chartFormats follows dataFields (and
335
+ // formats / conditionalFormats) and precedes pivotTableStyleInfo.
336
+ ...(chartFormats ? [chartFormats] : []),
337
+ el("pivotTableStyleInfo", {
338
+ name: styleInfo.name || "PivotStyleLight16",
339
+ showRowHeaders: styleFlag(styleInfo.showRowHeaders, "1"),
340
+ showColHeaders: styleFlag(styleInfo.showColHeaders, "1"),
341
+ showRowStripes: styleFlag(styleInfo.showRowStripes, "0"),
342
+ showColStripes: styleFlag(styleInfo.showColStripes, "0"),
343
+ showLastColumn: styleFlag(styleInfo.showLastColumn, "1"),
344
+ }),
345
+ ]));
293
346
  };
294
347
  /**
295
348
  * Build the pivotTable.xml.rels file (links the pivot to its
@@ -300,16 +353,11 @@ export const buildPivotTableXml = (pivot, cacheId, fieldIndices) => {
300
353
  * "../pivotCache/pivotCacheDefinition1.xml"
301
354
  */
302
355
  export const buildPivotTableRelsXml = (cacheRid, cacheTarget) => {
303
- const doc = {
304
- "?xml": { "@_version": "1.0", "@_encoding": "UTF-8", "@_standalone": "yes" },
305
- Relationships: {
306
- "@_xmlns": "http://schemas.openxmlformats.org/package/2006/relationships",
307
- Relationship: {
308
- "@_Id": cacheRid,
309
- "@_Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition",
310
- "@_Target": cacheTarget,
311
- },
312
- },
313
- };
314
- return xmlBuilder.build(doc);
356
+ return buildDocument(el("Relationships", { xmlns: "http://schemas.openxmlformats.org/package/2006/relationships" }, [
357
+ el("Relationship", {
358
+ Id: cacheRid,
359
+ Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition",
360
+ Target: cacheTarget,
361
+ }),
362
+ ]));
315
363
  };
@@ -1,4 +1,4 @@
1
- import type { CellData, CellFormat, Color, TextFormat } from "@rowsncolumns/spreadsheet";
1
+ import type { CellData, CellFormat, Color, TextFormat, BandedDefinition } from "@rowsncolumns/spreadsheet";
2
2
  import { type CellXfs } from "@rowsncolumns/spreadsheet-state";
3
3
  type NamedCellStyleRecord = {
4
4
  name: string;
@@ -8,7 +8,7 @@ type NamedCellStyleRecord = {
8
8
  hidden?: boolean;
9
9
  customBuiltin?: boolean;
10
10
  };
11
- type CustomTableStyleRecord = {
11
+ export type CustomTableStyleRecord = {
12
12
  name: string;
13
13
  pivot?: boolean;
14
14
  elements: Array<{
@@ -20,6 +20,12 @@ type CustomTableStyleRecord = {
20
20
  italic?: boolean;
21
21
  }>;
22
22
  };
23
+ /**
24
+ * A `CustomTableStyle`'s banding as `<tableStyleElement>` records — the inverse of the parser's
25
+ * `getCustomTableStyleBanding` (header → `headerRow`, stripes, totals row with its bold, first /
26
+ * last column, column stripes). Elements without a colour or font are dropped (#668 T-103).
27
+ */
28
+ export declare const tableStyleElementsFromBanding: (bandedRange: BandedDefinition) => CustomTableStyleRecord["elements"];
23
29
  export declare class StyleBuilder {
24
30
  private numberFormats;
25
31
  private fonts;
@@ -1 +1 @@
1
- {"version":3,"file":"style-builder.d.ts","sourceRoot":"","sources":["../../../../exporter/xlsx/style-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,QAAQ,EACR,UAAU,EACV,KAAK,EAEL,UAAU,EACX,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAwF/D,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAKF,KAAK,sBAAsB,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,eAAe,CAAC,EAAE,KAAK,CAAC;QACxB,SAAS,CAAC,EAAE,KAAK,CAAC;QAClB,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC,CAAC;CACJ,CAAC;AAEF,qBAAa,YAAY;IACvB,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,KAAK,CAKX;IACF,OAAO,CAAC,KAAK,CAA+D;IAC5E,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,eAAe,CAErB;IACF,OAAO,CAAC,iBAAiB,CAAgC;IAEzD;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,EAAE;IAUlD;;;;;;;OAOG;IACH,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,EAAE;IAOtD,OAAO,CAAC,OAAO,CAOb;IAEF,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,MAAM,CAA6B;;IAU3C;;;;;;;OAOG;IACH,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI;IASzC,YAAY,CAAC,CAAC,SAAS,QAAQ,EAC7B,IAAI,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAC1B,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS;IACnC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,UAAU,CAAC,cAAc,CAAC,GAC/C,MAAM;IAsET,0BAA0B,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI;IA0BrD,OAAO,CAAC,iBAAiB;IAoCzB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,SAAS;IA2DjB,OAAO,CAAC,WAAW;IA2BnB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,eAAe;IA8BvB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,YAAY;IAqCpB,OAAO,CAAC,oBAAoB;IAyB5B,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,YAAY;IAMpB,KAAK;CAuJN"}
1
+ {"version":3,"file":"style-builder.d.ts","sourceRoot":"","sources":["../../../../exporter/xlsx/style-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,QAAQ,EACR,UAAU,EACV,KAAK,EAEL,UAAU,EACV,gBAAgB,EAEjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAwF/D,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAKF,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,eAAe,CAAC,EAAE,KAAK,CAAC;QACxB,SAAS,CAAC,EAAE,KAAK,CAAC;QAClB,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC,CAAC;CACJ,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GACxC,aAAa,gBAAgB,KAC5B,sBAAsB,CAAC,UAAU,CAqBnC,CAAC;AAEF,qBAAa,YAAY;IACvB,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,KAAK,CAKX;IACF,OAAO,CAAC,KAAK,CAA+D;IAC5E,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,eAAe,CAErB;IACF,OAAO,CAAC,iBAAiB,CAAgC;IAEzD;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,EAAE;IAUlD;;;;;;;OAOG;IACH,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,EAAE;IAOtD,OAAO,CAAC,OAAO,CAOb;IAEF,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,MAAM,CAA6B;;IAU3C;;;;;;;OAOG;IACH,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI;IASzC,YAAY,CAAC,CAAC,SAAS,QAAQ,EAC7B,IAAI,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,EAC1B,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS;IACnC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,UAAU,CAAC,cAAc,CAAC,GAC/C,MAAM;IAsET,0BAA0B,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,IAAI;IA0BrD,OAAO,CAAC,iBAAiB;IAoCzB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,SAAS;IA2DjB,OAAO,CAAC,WAAW;IA2BnB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,eAAe;IA8BvB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,YAAY;IAqCpB,OAAO,CAAC,oBAAoB;IAyB5B,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,YAAY;IAMpB,KAAK;CAuJN"}
@@ -7,6 +7,34 @@ const isStyleReference = (value) => !!value && typeof value === "object" && "sid
7
7
  // numFmtId 47 per ECMA-376). Re-aliased for backwards compat with the
8
8
  // previous local name in this file.
9
9
  const BUILT_IN_NUMFMT_BY_PATTERN = BUILTIN_NUMFMT_BY_PATTERN;
10
+ /**
11
+ * A `CustomTableStyle`'s banding as `<tableStyleElement>` records — the inverse of the parser's
12
+ * `getCustomTableStyleBanding` (header → `headerRow`, stripes, totals row with its bold, first /
13
+ * last column, column stripes). Elements without a colour or font are dropped (#668 T-103).
14
+ */
15
+ export const tableStyleElementsFromBanding = (bandedRange) => {
16
+ const rows = bandedRange.rowProperties ?? {};
17
+ const cols = bandedRange.columnProperties ?? {};
18
+ const elements = [];
19
+ const push = (type, backgroundColor, bold) => {
20
+ if (backgroundColor == null && !bold)
21
+ return;
22
+ elements.push({
23
+ type,
24
+ ...(backgroundColor != null ? { backgroundColor: backgroundColor } : {}),
25
+ ...(bold ? { bold: true } : {}),
26
+ });
27
+ };
28
+ push("headerRow", rows.headerColor);
29
+ push("firstRowStripe", rows.firstBandColor);
30
+ push("secondRowStripe", rows.secondBandColor);
31
+ push("totalRow", rows.totalRowColor, rows.totalRowBold === true);
32
+ push("firstColumn", rows.firstColumnColor);
33
+ push("lastColumn", rows.lastColumnColor);
34
+ push("firstColumnStripe", cols.firstBandColor);
35
+ push("secondColumnStripe", cols.secondBandColor);
36
+ return elements;
37
+ };
10
38
  export class StyleBuilder {
11
39
  numberFormats = [];
12
40
  fonts = [
@@ -1,5 +1,5 @@
1
1
  import type { CellData, Citation, CommentThread, DataValidationRuleRecord, IterativeCalculationOptions, SheetData, TextFormat } from "@rowsncolumns/common-types";
2
- import type { Color, ConditionalFormatRule, EmbeddedChart, EmbeddedObject, NamedRange, PivotTable, ProtectedRange, Sheet, Slicer, SpreadsheetTheme, TableView } from "@rowsncolumns/spreadsheet";
2
+ import type { Color, ConditionalFormatRule, EmbeddedChart, EmbeddedObject, NamedRange, PivotTable, ProtectedRange, Sheet, Slicer, SpreadsheetTheme, TableView, CustomTableStyle } from "@rowsncolumns/spreadsheet";
3
3
  import type { CellXfs, SharedStrings as SharedStringsType } from "@rowsncolumns/spreadsheet-state";
4
4
  import type { PowerQueryParts } from "../../power-query";
5
5
  import type { ExternalLink, WorkbookProperties } from "../../excel-parser/types";
@@ -70,7 +70,7 @@ export type ExportExcelProps<S extends Sheet, T extends CellData, C extends Cita
70
70
  * are emitted regardless — Excel needs them as the fallback when a
71
71
  * table doesn't pick a custom style.
72
72
  */
73
- customTableStyles?: Array<{
73
+ customTableStyles?: Array<CustomTableStyle | {
74
74
  name: string;
75
75
  pivot?: boolean;
76
76
  elements: Array<{