@rowsncolumns/toolkit 24.5.12 → 24.5.14

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.
@@ -1,5 +1,5 @@
1
1
  import { __assign, __read, __spreadArray, __values } from "tslib";
2
- import { cellRangeToAddress, chartPaletteColorAt, sanitizeSheetName, } from "@rowsncolumns/utils";
2
+ import { cellRangeToAddress, cellToAddress, chartPaletteColorAt, sanitizeSheetName, } from "@rowsncolumns/utils";
3
3
  import { XMLBuilder } from "fast-xml-parser";
4
4
  import { area_series, bubble_series, chart_template, column_json, column_series, donut_json, line_series, scatter_json, scatter_series, static_title, } from "./chart-templates";
5
5
  var XML_DECLARATION = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
@@ -136,6 +136,24 @@ var formatSourcesRef = function (sources, sheetNameById) {
136
136
  .filter(function (ref) { return Boolean(ref); });
137
137
  return parts.length ? parts.join(",") : undefined;
138
138
  };
139
+ /**
140
+ * The `<c:tx><c:strRef>` formula of a series whose name is linked to a
141
+ * header cell — `dataLabelSource` (a `SheetRange`), else the address an
142
+ * earlier import captured as `dataLabelRef`.
143
+ */
144
+ var seriesLabelRef = function (entry, sheetNameById) {
145
+ var _a, _b;
146
+ return (_b = (_a = ((entry === null || entry === void 0 ? void 0 : entry.dataLabelSource)
147
+ ? formatCellRef(entry.dataLabelSource, sheetNameById)
148
+ : undefined)) !== null && _a !== void 0 ? _a : entry === null || entry === void 0 ? void 0 : entry.dataLabelRef) !== null && _b !== void 0 ? _b : undefined;
149
+ };
150
+ /** `'Sheet'!$B$1` — the absolute single-cell form Excel writes for a series name. */
151
+ var formatCellRef = function (cell, sheetNameById) {
152
+ var sheetName = sheetNameById.get(cell.sheetId);
153
+ if (!sheetName)
154
+ return undefined;
155
+ return "".concat(sanitizeSheetName(sheetName), "!").concat(cellToAddress({ rowIndex: cell.startRowIndex, columnIndex: cell.startColumnIndex }, true, true));
156
+ };
139
157
  var seriesCategoryRef = function (entry, sheetNameById) {
140
158
  return formatSourcesRef(entry === null || entry === void 0 ? void 0 : entry.categorySources, sheetNameById);
141
159
  };
@@ -806,8 +824,9 @@ var isComboChart = function (series, defaultChartType) {
806
824
  /**
807
825
  * Build a bar/column series node.
808
826
  */
809
- var buildBarSeriesNode = function (index, ref, seriesData, domainRef, sheetName, isFirstInGroup) {
827
+ var buildBarSeriesNode = function (index, ref, seriesData, domainRef, sheetName, isFirstInGroup, sheetNameById) {
810
828
  var _a;
829
+ if (sheetNameById === void 0) { sheetNameById = new Map(); }
811
830
  var seriesNode = cloneTemplate(column_series);
812
831
  seriesNode["c:idx"] = { a$: { val: index.toString() } };
813
832
  seriesNode["c:order"] = { a$: { val: index.toString() } };
@@ -865,7 +884,7 @@ var buildBarSeriesNode = function (index, ref, seriesData, domainRef, sheetName,
865
884
  removeProp(seriesNode, "c:val");
866
885
  seriesNode["c:val"] = buildInlineNumLit(inlineValues);
867
886
  }
868
- var labelNode = buildSeriesLabel((_a = seriesData === null || seriesData === void 0 ? void 0 : seriesData.dataLabel) !== null && _a !== void 0 ? _a : undefined, sheetName, seriesData === null || seriesData === void 0 ? void 0 : seriesData.dataLabelRef);
887
+ var labelNode = buildSeriesLabel((_a = seriesData === null || seriesData === void 0 ? void 0 : seriesData.dataLabel) !== null && _a !== void 0 ? _a : undefined, sheetName, seriesLabelRef(seriesData, sheetNameById));
869
888
  if (labelNode) {
870
889
  seriesNode["c:tx"] = labelNode["c:tx"];
871
890
  }
@@ -880,9 +899,10 @@ var buildBarSeriesNode = function (index, ref, seriesData, domainRef, sheetName,
880
899
  /**
881
900
  * Build a line series node.
882
901
  */
883
- var buildLineSeriesNode = function (index, ref, seriesData, domainRef, sheetName, smooth, showMarkers) {
902
+ var buildLineSeriesNode = function (index, ref, seriesData, domainRef, sheetName, smooth, showMarkers, sheetNameById) {
884
903
  var _a, _b;
885
904
  if (showMarkers === void 0) { showMarkers = false; }
905
+ if (sheetNameById === void 0) { sheetNameById = new Map(); }
886
906
  var seriesNode = cloneTemplate(line_series);
887
907
  seriesNode["c:idx"] = { a$: { val: index.toString() } };
888
908
  seriesNode["c:order"] = { a$: { val: index.toString() } };
@@ -926,7 +946,7 @@ var buildLineSeriesNode = function (index, ref, seriesData, domainRef, sheetName
926
946
  if (smooth) {
927
947
  seriesNode["c:smooth"].a$.val = "1";
928
948
  }
929
- var labelNode = buildSeriesLabel((_b = seriesData === null || seriesData === void 0 ? void 0 : seriesData.dataLabel) !== null && _b !== void 0 ? _b : undefined, sheetName, seriesData === null || seriesData === void 0 ? void 0 : seriesData.dataLabelRef);
949
+ var labelNode = buildSeriesLabel((_b = seriesData === null || seriesData === void 0 ? void 0 : seriesData.dataLabel) !== null && _b !== void 0 ? _b : undefined, sheetName, seriesLabelRef(seriesData, sheetNameById));
930
950
  if (labelNode) {
931
951
  seriesNode["c:tx"] = labelNode["c:tx"];
932
952
  }
@@ -935,8 +955,9 @@ var buildLineSeriesNode = function (index, ref, seriesData, domainRef, sheetName
935
955
  /**
936
956
  * Build an area series node.
937
957
  */
938
- var buildAreaSeriesNode = function (index, ref, seriesData, domainRef, sheetName) {
958
+ var buildAreaSeriesNode = function (index, ref, seriesData, domainRef, sheetName, sheetNameById) {
939
959
  var _a, _b, _c;
960
+ if (sheetNameById === void 0) { sheetNameById = new Map(); }
940
961
  var seriesNode = cloneTemplate(area_series);
941
962
  seriesNode["c:idx"] = { a$: { val: index.toString() } };
942
963
  seriesNode["c:order"] = { a$: { val: index.toString() } };
@@ -966,7 +987,7 @@ var buildAreaSeriesNode = function (index, ref, seriesData, domainRef, sheetName
966
987
  removeProp(seriesNode, "c:cat");
967
988
  }
968
989
  seriesNode["c:val"]["c:numRef"]["c:f"] = ref;
969
- var labelNode = buildSeriesLabel((_c = seriesData === null || seriesData === void 0 ? void 0 : seriesData.dataLabel) !== null && _c !== void 0 ? _c : undefined, sheetName, seriesData === null || seriesData === void 0 ? void 0 : seriesData.dataLabelRef);
990
+ var labelNode = buildSeriesLabel((_c = seriesData === null || seriesData === void 0 ? void 0 : seriesData.dataLabel) !== null && _c !== void 0 ? _c : undefined, sheetName, seriesLabelRef(seriesData, sheetNameById));
970
991
  if (labelNode) {
971
992
  seriesNode["c:tx"] = labelNode["c:tx"];
972
993
  }
@@ -979,9 +1000,10 @@ var buildAreaSeriesNode = function (index, ref, seriesData, domainRef, sheetName
979
1000
  * remaining series, each with its own idx/order, name, category and value
980
1001
  * reference.
981
1002
  */
982
- var appendExtraCircularSeries = function (chartNode, groupKey, seriesRefs, seriesCatRefs, domainRef, series, sheetName) {
1003
+ var appendExtraCircularSeries = function (chartNode, groupKey, seriesRefs, seriesCatRefs, domainRef, series, sheetName, sheetNameById) {
983
1004
  var e_7, _a;
984
- var _b, _c, _d, _e, _f, _g, _h;
1005
+ var _b, _c, _d, _e, _f, _g;
1006
+ if (sheetNameById === void 0) { sheetNameById = new Map(); }
985
1007
  var group = (_b = chartNode["c:plotArea"]) === null || _b === void 0 ? void 0 : _b[groupKey];
986
1008
  if (!group)
987
1009
  return;
@@ -1003,16 +1025,16 @@ var appendExtraCircularSeries = function (chartNode, groupKey, seriesRefs, serie
1003
1025
  }
1004
1026
  try {
1005
1027
  for (var extra_1 = __values(extra), extra_1_1 = extra_1.next(); !extra_1_1.done; extra_1_1 = extra_1.next()) {
1006
- var _j = extra_1_1.value, ref = _j.ref, index = _j.index;
1028
+ var _h = extra_1_1.value, ref = _h.ref, index = _h.index;
1007
1029
  var node = {
1008
1030
  "c:idx": { a$: { val: String(index) } },
1009
1031
  "c:order": { a$: { val: String(index) } },
1010
1032
  };
1011
- var labelNode = buildSeriesLabel((_f = (_e = series[index]) === null || _e === void 0 ? void 0 : _e.dataLabel) !== null && _f !== void 0 ? _f : undefined, sheetName, (_g = series[index]) === null || _g === void 0 ? void 0 : _g.dataLabelRef);
1033
+ var labelNode = buildSeriesLabel((_f = (_e = series[index]) === null || _e === void 0 ? void 0 : _e.dataLabel) !== null && _f !== void 0 ? _f : undefined, sheetName, seriesLabelRef(series[index], sheetNameById));
1012
1034
  if (labelNode) {
1013
1035
  node["c:tx"] = labelNode["c:tx"];
1014
1036
  }
1015
- var catRef = (_h = seriesCatRefs[index]) !== null && _h !== void 0 ? _h : domainRef;
1037
+ var catRef = (_g = seriesCatRefs[index]) !== null && _g !== void 0 ? _g : domainRef;
1016
1038
  if (catRef) {
1017
1039
  node["c:cat"] = { "c:strRef": { "c:f": catRef } };
1018
1040
  }
@@ -1299,7 +1321,7 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1299
1321
  };
1300
1322
  for (var i = 0; i < groupSeries.length; i++) {
1301
1323
  var _14 = groupSeries[i], index = _14.index, ref = _14.ref, data = _14.data;
1302
- var seriesNode = buildBarSeriesNode(index, ref, data, (_q = seriesCatRefs[index]) !== null && _q !== void 0 ? _q : domainRef, sheetName, i === 0);
1324
+ var seriesNode = buildBarSeriesNode(index, ref, data, (_q = seriesCatRefs[index]) !== null && _q !== void 0 ? _q : domainRef, sheetName, i === 0, sheetNameById);
1303
1325
  barChartNode["c:ser"].push(seriesNode);
1304
1326
  }
1305
1327
  comboGroups.push(["c:barChart", barChartNode]);
@@ -1326,7 +1348,7 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1326
1348
  try {
1327
1349
  for (var groupSeries_1 = (e_10 = void 0, __values(groupSeries)), groupSeries_1_1 = groupSeries_1.next(); !groupSeries_1_1.done; groupSeries_1_1 = groupSeries_1.next()) {
1328
1350
  var _15 = groupSeries_1_1.value, index = _15.index, ref = _15.ref, data = _15.data;
1329
- var seriesNode = buildLineSeriesNode(index, ref, data, (_r = seriesCatRefs[index]) !== null && _r !== void 0 ? _r : domainRef, sheetName, smooth, showMarkers);
1351
+ var seriesNode = buildLineSeriesNode(index, ref, data, (_r = seriesCatRefs[index]) !== null && _r !== void 0 ? _r : domainRef, sheetName, smooth, showMarkers, sheetNameById);
1330
1352
  lineChartNode["c:ser"].push(seriesNode);
1331
1353
  }
1332
1354
  }
@@ -1350,7 +1372,7 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1350
1372
  try {
1351
1373
  for (var groupSeries_2 = (e_11 = void 0, __values(groupSeries)), groupSeries_2_1 = groupSeries_2.next(); !groupSeries_2_1.done; groupSeries_2_1 = groupSeries_2.next()) {
1352
1374
  var _16 = groupSeries_2_1.value, index = _16.index, ref = _16.ref, data = _16.data;
1353
- var seriesNode = buildAreaSeriesNode(index, ref, data, (_s = seriesCatRefs[index]) !== null && _s !== void 0 ? _s : domainRef, sheetName);
1375
+ var seriesNode = buildAreaSeriesNode(index, ref, data, (_s = seriesCatRefs[index]) !== null && _s !== void 0 ? _s : domainRef, sheetName, sheetNameById);
1354
1376
  areaChartNode["c:ser"].push(seriesNode);
1355
1377
  }
1356
1378
  }
@@ -1392,7 +1414,7 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1392
1414
  bar["c:barDir"].a$.val = "col";
1393
1415
  var seriesContainer_1 = bar["c:ser"];
1394
1416
  seriesRefs.forEach(function (ref, index) {
1395
- var _a, _b, _c, _d, _e;
1417
+ var _a, _b, _c, _d;
1396
1418
  if (!ref)
1397
1419
  return;
1398
1420
  var seriesNode = cloneTemplate(column_series);
@@ -1417,7 +1439,7 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1417
1439
  };
1418
1440
  }
1419
1441
  seriesNode["c:val"]["c:numRef"]["c:f"] = ref;
1420
- var labelNode = buildSeriesLabel((_d = (_c = series[index]) === null || _c === void 0 ? void 0 : _c.dataLabel) !== null && _d !== void 0 ? _d : undefined, sheetNameById.get(chart.position.sheetId), (_e = series[index]) === null || _e === void 0 ? void 0 : _e.dataLabelRef);
1442
+ var labelNode = buildSeriesLabel((_d = (_c = series[index]) === null || _c === void 0 ? void 0 : _c.dataLabel) !== null && _d !== void 0 ? _d : undefined, sheetNameById.get(chart.position.sheetId), seriesLabelRef(series[index], sheetNameById));
1421
1443
  if (labelNode) {
1422
1444
  seriesNode["c:tx"] = labelNode["c:tx"];
1423
1445
  }
@@ -1495,7 +1517,7 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1495
1517
  var enrichedSeries = index === 0 && !domainRef && (inlineDomainValues_1 === null || inlineDomainValues_1 === void 0 ? void 0 : inlineDomainValues_1.length)
1496
1518
  ? __assign(__assign({}, series[index]), { inlineDomainValues: inlineDomainValues_1 })
1497
1519
  : series[index];
1498
- var seriesNode = buildBarSeriesNode(index, ref !== null && ref !== void 0 ? ref : "", enrichedSeries, (_b = seriesCatRefs[index]) !== null && _b !== void 0 ? _b : domainRef, sheetNameById.get(chart.position.sheetId), index === 0);
1520
+ var seriesNode = buildBarSeriesNode(index, ref !== null && ref !== void 0 ? ref : "", enrichedSeries, (_b = seriesCatRefs[index]) !== null && _b !== void 0 ? _b : domainRef, sheetNameById.get(chart.position.sheetId), index === 0, sheetNameById);
1499
1521
  seriesContainer_2.push(seriesNode);
1500
1522
  });
1501
1523
  chartSpace["c:chartSpace"]["c:chart"] = barChart;
@@ -1512,14 +1534,14 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1512
1534
  ? buildOfPieChart(domainRef, valueRef, chartTitle, specAny, chartSubtitle)
1513
1535
  : buildPieChart(domainRef, valueRef, chartTitle, chartSubtitle);
1514
1536
  if (!isOfPie) {
1515
- appendExtraCircularSeries(pieChart, "c:pieChart", seriesRefs, seriesCatRefs, domainRef, series, sheetNameById.get(chart.position.sheetId));
1537
+ appendExtraCircularSeries(pieChart, "c:pieChart", seriesRefs, seriesCatRefs, domainRef, series, sheetNameById.get(chart.position.sheetId), sheetNameById);
1516
1538
  }
1517
1539
  chartSpace["c:chartSpace"]["c:chart"] = pieChart;
1518
1540
  }
1519
1541
  else if (chartType === "doughnut") {
1520
1542
  var valueRef = seriesRefs.find(Boolean);
1521
1543
  var doughnutChart = buildDoughnutChart(domainRef, valueRef, chartTitle, chartSubtitle);
1522
- appendExtraCircularSeries(doughnutChart, "c:doughnutChart", seriesRefs, seriesCatRefs, domainRef, series, sheetNameById.get(chart.position.sheetId));
1544
+ appendExtraCircularSeries(doughnutChart, "c:doughnutChart", seriesRefs, seriesCatRefs, domainRef, series, sheetNameById.get(chart.position.sheetId), sheetNameById);
1523
1545
  chartSpace["c:chartSpace"]["c:chart"] = doughnutChart;
1524
1546
  }
1525
1547
  else if (chartType === "radar") {
@@ -1562,7 +1584,7 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1562
1584
  replacePlotAreaChartGroups(plotArea, [["c:lineChart", lineChartNode]]);
1563
1585
  var seriesContainer_3 = lineChartNode["c:ser"];
1564
1586
  seriesRefs.forEach(function (ref, index) {
1565
- var _a, _b, _c, _d, _e, _f;
1587
+ var _a, _b, _c, _d, _e;
1566
1588
  if (!ref)
1567
1589
  return;
1568
1590
  var seriesNode = cloneTemplate(line_series);
@@ -1599,7 +1621,7 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1599
1621
  else if (showMarkers_1) {
1600
1622
  removeProp(seriesNode, "c:marker");
1601
1623
  }
1602
- var labelNode = buildSeriesLabel((_e = (_d = series[index]) === null || _d === void 0 ? void 0 : _d.dataLabel) !== null && _e !== void 0 ? _e : undefined, sheetNameById.get(chart.position.sheetId), (_f = series[index]) === null || _f === void 0 ? void 0 : _f.dataLabelRef);
1624
+ var labelNode = buildSeriesLabel((_e = (_d = series[index]) === null || _d === void 0 ? void 0 : _d.dataLabel) !== null && _e !== void 0 ? _e : undefined, sheetNameById.get(chart.position.sheetId), seriesLabelRef(series[index], sheetNameById));
1603
1625
  if (labelNode) {
1604
1626
  seriesNode["c:tx"] = labelNode["c:tx"];
1605
1627
  }
@@ -1630,7 +1652,7 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1630
1652
  replacePlotAreaChartGroups(plotArea, [["c:areaChart", areaChartNode]]);
1631
1653
  var seriesContainer_4 = areaChartNode["c:ser"];
1632
1654
  seriesRefs.forEach(function (ref, index) {
1633
- var _a, _b, _c, _d, _e;
1655
+ var _a, _b, _c, _d;
1634
1656
  if (!ref)
1635
1657
  return;
1636
1658
  var seriesNode = cloneTemplate(area_series);
@@ -1656,7 +1678,7 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1656
1678
  removeProp(seriesNode, "c:cat");
1657
1679
  }
1658
1680
  seriesNode["c:val"]["c:numRef"]["c:f"] = ref;
1659
- var labelNode = buildSeriesLabel((_d = (_c = series[index]) === null || _c === void 0 ? void 0 : _c.dataLabel) !== null && _d !== void 0 ? _d : undefined, sheetNameById.get(chart.position.sheetId), (_e = series[index]) === null || _e === void 0 ? void 0 : _e.dataLabelRef);
1681
+ var labelNode = buildSeriesLabel((_d = (_c = series[index]) === null || _c === void 0 ? void 0 : _c.dataLabel) !== null && _d !== void 0 ? _d : undefined, sheetNameById.get(chart.position.sheetId), seriesLabelRef(series[index], sheetNameById));
1660
1682
  if (labelNode) {
1661
1683
  seriesNode["c:tx"] = labelNode["c:tx"];
1662
1684
  }
@@ -1709,7 +1731,7 @@ export var buildChartXml = function (input, sheetNameById, resolvePivotSourceNam
1709
1731
  else if (smooth) {
1710
1732
  seriesNode["c:smooth"].a$.val = "1";
1711
1733
  }
1712
- var labelNode = buildSeriesLabel((_f = (_d = entry === null || entry === void 0 ? void 0 : entry.dataLabel) !== null && _d !== void 0 ? _d : (_e = bubbleLabelEntries_1[index]) === null || _e === void 0 ? void 0 : _e.dataLabel) !== null && _f !== void 0 ? _f : undefined, sheetNameById.get(chart.position.sheetId), entry === null || entry === void 0 ? void 0 : entry.dataLabelRef);
1734
+ var labelNode = buildSeriesLabel((_f = (_d = entry === null || entry === void 0 ? void 0 : entry.dataLabel) !== null && _d !== void 0 ? _d : (_e = bubbleLabelEntries_1[index]) === null || _e === void 0 ? void 0 : _e.dataLabel) !== null && _f !== void 0 ? _f : undefined, sheetNameById.get(chart.position.sheetId), seriesLabelRef(entry, sheetNameById));
1713
1735
  if (labelNode) {
1714
1736
  seriesNode["c:tx"] = labelNode["c:tx"];
1715
1737
  }
@@ -21,6 +21,8 @@ export declare const CHART_EX_GRAPHIC_URI = "http://schemas.microsoft.com/office
21
21
  * checked). Returns the `cx:chart` node or undefined for non-chartex anchors.
22
22
  */
23
23
  export declare function getChartExReferenceFromAnchor(anchor: any): any;
24
+ /** Resolves an `_xlchart.v1.N` defined name (chartex ranges go through them) to its range. */
25
+ export type ResolveChartExName = (name: string) => SheetRange | undefined;
24
26
  /**
25
27
  * Split a multi-column hierarchy range (Excel lays the treemap hierarchy out
26
28
  * as columns, top level first, leaf last) into `parentLabels` (one entry per
@@ -30,8 +32,17 @@ export declare const splitHierarchyRange: (range: SheetRange) => {
30
32
  parentLabels: SheetRange[];
31
33
  labels: SheetRange;
32
34
  };
33
- /** Parse a `cx:chartSpace` DOM into an `EmbeddedChart` spec (null when unsupported). */
34
- export declare function parseChartExSpec(chartSpaceDom: any, sheetId: number, resolveSheetId?: ResolveSheetId): EmbeddedChart["spec"] | null;
35
+ /**
36
+ * Parse a `cx:chartSpace` DOM into an `EmbeddedChart` spec (null when
37
+ * unsupported). Every `<cx:series>` is a series — its name from `<cx:tx>`
38
+ * (linked to the header cell when it is a reference, #1100), its ranges from
39
+ * the `<cx:data>` block its `<cx:dataId>` names — and the per-type options
40
+ * come from `<cx:layoutPr>`: box & whisker visibility + quartile method,
41
+ * histogram binning, waterfall subtotals, treemap label layout
42
+ * (rowsncolumns/spreadsheet#1107). `resolveName` resolves the
43
+ * `_xlchart.v1.N` defined names Excel writes the ranges through.
44
+ */
45
+ export declare function parseChartExSpec(chartSpaceDom: any, sheetId: number, resolveSheetId?: ResolveSheetId, resolveName?: ResolveChartExName): EmbeddedChart["spec"] | null;
35
46
  /** Resolve + parse one chartex part referenced from a drawing anchor. */
36
- export declare function processChartEx(chartTarget: string, drawingPath: string, anchor: any, sheetId: number, zipParser: StreamingZipParser, xmlParser: any, sheetDimensions?: SheetDimensions, resolveSheetId?: ResolveSheetId): Promise<EmbeddedChart | null>;
47
+ export declare function processChartEx(chartTarget: string, drawingPath: string, anchor: any, sheetId: number, zipParser: StreamingZipParser, xmlParser: any, sheetDimensions?: SheetDimensions, resolveSheetId?: ResolveSheetId, resolveName?: ResolveChartExName): Promise<EmbeddedChart | null>;
37
48
  //# sourceMappingURL=chartex.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chartex.d.ts","sourceRoot":"","sources":["../../../../../excel-parser/plugins/xlsx/chartex.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,aAAa,EACb,UAAU,EACX,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,iBAAiB,mEACoC,CAAC;AACnE,eAAO,MAAM,oBAAoB,6DAC2B,CAAC;AAyB7D;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAoB9D;AA8BD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC9B,OAAO,UAAU,KAChB;IAAE,YAAY,EAAE,UAAU,EAAE,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAalD,CAAC;AAWF,wFAAwF;AACxF,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,GAAG,EAClB,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,cAAc,GAC9B,aAAa,CAAC,MAAM,CAAC,GAAG,IAAI,CA0D9B;AAED,yEAAyE;AACzE,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,kBAAkB,EAC7B,SAAS,EAAE,GAAG,EACd,eAAe,CAAC,EAAE,eAAe,EACjC,cAAc,CAAC,EAAE,cAAc,GAC9B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAsB/B"}
1
+ {"version":3,"file":"chartex.d.ts","sourceRoot":"","sources":["../../../../../excel-parser/plugins/xlsx/chartex.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,aAAa,EACb,UAAU,EACX,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,iBAAiB,mEACoC,CAAC;AACnE,eAAO,MAAM,oBAAoB,6DAC2B,CAAC;AAyB7D;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAoB9D;AA6BD,8FAA8F;AAC9F,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAC;AA0B1E;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC9B,OAAO,UAAU,KAChB;IAAE,YAAY,EAAE,UAAU,EAAE,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAalD,CAAC;AAgFF;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,GAAG,EAClB,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,cAAc,EAC/B,WAAW,CAAC,EAAE,kBAAkB,GAC/B,aAAa,CAAC,MAAM,CAAC,GAAG,IAAI,CAyJ9B;AAED,yEAAyE;AACzE,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,kBAAkB,EAC7B,SAAS,EAAE,GAAG,EACd,eAAe,CAAC,EAAE,eAAe,EACjC,cAAc,CAAC,EAAE,cAAc,EAC/B,WAAW,CAAC,EAAE,kBAAkB,GAC/B,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA2B/B"}
@@ -66,19 +66,38 @@ export function getChartExReferenceFromAnchor(anchor) {
66
66
  }
67
67
  return undefined;
68
68
  }
69
- /** `layoutId` → engine chart type. Unknown layouts are not imported. */
70
- const CHART_TYPE_BY_LAYOUT = {
71
- treemap: "treemap",
72
- sunburst: "sunburst",
73
- waterfall: "waterfall",
74
- funnel: "funnel",
75
- boxWhisker: "boxplot",
76
- // `clusteredColumn` is a histogram / pareto only when it carries `<cx:binning>`.
69
+ /**
70
+ * `layoutId` → engine chart type (+ `sourceChartType`). `clusteredColumn` is
71
+ * a histogram only when it carries `<cx:binning>` (else Excel's plain column
72
+ * fallback); `paretoLine` is the cumulative line of a Pareto histogram, not a
73
+ * chart of its own. Unknown layouts are not imported.
74
+ */
75
+ const chartKindOf = (layoutId, hasBinning) => {
76
+ switch (layoutId) {
77
+ case "treemap":
78
+ case "sunburst":
79
+ case "waterfall":
80
+ case "funnel":
81
+ return { chartType: layoutId };
82
+ case "boxWhisker":
83
+ return { chartType: "boxplot" };
84
+ case "regionMap":
85
+ return { chartType: "regionMap", sourceChartType: "REGION_MAP" };
86
+ case "clusteredColumn":
87
+ return { chartType: hasBinning ? "histogram" : "column" };
88
+ default:
89
+ return undefined;
90
+ }
77
91
  };
78
- const sourcesFromRef = (ref, hostSheetId, resolveSheetId) => {
92
+ const sourcesFromRef = (ref, hostSheetId, resolveSheetId, resolveName) => {
79
93
  if (!ref)
80
94
  return [];
81
- const selections = generateSelectionsFromFormula(`=${ref}`);
95
+ const trimmed = ref.trim();
96
+ if (/^_xlchart/i.test(trimmed)) {
97
+ const range = resolveName?.(trimmed);
98
+ return range ? [range] : [];
99
+ }
100
+ const selections = generateSelectionsFromFormula(`=${trimmed}`);
82
101
  return selections.map((sel) => ({
83
102
  ...sel.range,
84
103
  sheetId: (sel.sheetName ? resolveSheetId?.(sel.sheetName) : undefined) ??
@@ -111,64 +130,259 @@ const dimensionRef = (data, dimTag, type) => {
111
130
  const f = child(dim, "f");
112
131
  return textOf(Array.isArray(f) ? f[0] : f)?.trim() || undefined;
113
132
  };
114
- /** Parse a `cx:chartSpace` DOM into an `EmbeddedChart` spec (null when unsupported). */
115
- export function parseChartExSpec(chartSpaceDom, sheetId, resolveSheetId) {
133
+ const flagOf = (node, name) => {
134
+ const raw = get(node, `a$.${name}`);
135
+ if (raw === undefined || raw === null)
136
+ return undefined;
137
+ return raw === "1" || raw === 1 || raw === "true" || raw === true;
138
+ };
139
+ const numberOf = (raw) => {
140
+ if (raw === undefined || raw === null || raw === "")
141
+ return undefined;
142
+ const n = Number(raw);
143
+ return Number.isFinite(n) ? n : undefined;
144
+ };
145
+ /** `<cx:spPr><a:solidFill><a:srgbClr val>` → `#RRGGBB` (the series or Pareto line fill). */
146
+ const colorOf = (series) => {
147
+ const spPr = child(series, "spPr");
148
+ if (!spPr)
149
+ return undefined;
150
+ const walk = (node) => {
151
+ if (!node || typeof node !== "object")
152
+ return undefined;
153
+ for (const key of Object.keys(node)) {
154
+ const local = key.includes(":") ? key.slice(key.indexOf(":") + 1) : key;
155
+ const value = node[key];
156
+ if (local === "srgbClr") {
157
+ const first = Array.isArray(value) ? value[0] : value;
158
+ const val = get(first, "a$.val");
159
+ if (typeof val === "string")
160
+ return `#${val.toUpperCase()}`;
161
+ }
162
+ if (local !== "a$") {
163
+ const found = walk(Array.isArray(value) ? value[0] : value);
164
+ if (found)
165
+ return found;
166
+ }
167
+ }
168
+ return undefined;
169
+ };
170
+ return walk(spPr);
171
+ };
172
+ const CHART_EX_LABEL_POSITIONS = {
173
+ ctr: "center",
174
+ inBase: "insideBase",
175
+ inEnd: "insideEnd",
176
+ outEnd: "outsideEnd",
177
+ t: "above",
178
+ b: "below",
179
+ l: "left",
180
+ r: "right",
181
+ bestFit: "bestFit",
182
+ };
183
+ /** `<cx:dataLabels>` on a series → its `dataLabels`; every visibility flag off is `{ show: false }`. */
184
+ const dataLabelsOf = (series) => {
185
+ const dl = child(series, "dataLabels");
186
+ if (!dl)
187
+ return undefined;
188
+ const vis = child(dl, "visibility");
189
+ const out = {};
190
+ if (flagOf(vis, "value") ?? true)
191
+ out.showValue = true;
192
+ if (flagOf(vis, "categoryName"))
193
+ out.showCategoryName = true;
194
+ if (flagOf(vis, "seriesName"))
195
+ out.showSeriesName = true;
196
+ const position = CHART_EX_LABEL_POSITIONS[String(get(dl, "a$.pos") ?? "")];
197
+ if (position)
198
+ out.position = position;
199
+ if (!Object.keys(out).length)
200
+ return { show: false };
201
+ return { show: true, ...out };
202
+ };
203
+ const singleCell = (range) => ({
204
+ ...range,
205
+ endRowIndex: range.startRowIndex,
206
+ endColumnIndex: range.startColumnIndex,
207
+ });
208
+ /**
209
+ * Parse a `cx:chartSpace` DOM into an `EmbeddedChart` spec (null when
210
+ * unsupported). Every `<cx:series>` is a series — its name from `<cx:tx>`
211
+ * (linked to the header cell when it is a reference, #1100), its ranges from
212
+ * the `<cx:data>` block its `<cx:dataId>` names — and the per-type options
213
+ * come from `<cx:layoutPr>`: box & whisker visibility + quartile method,
214
+ * histogram binning, waterfall subtotals, treemap label layout
215
+ * (rowsncolumns/spreadsheet#1107). `resolveName` resolves the
216
+ * `_xlchart.v1.N` defined names Excel writes the ranges through.
217
+ */
218
+ export function parseChartExSpec(chartSpaceDom, sheetId, resolveSheetId, resolveName) {
116
219
  const chartSpace = child(chartSpaceDom, "chartSpace") ?? chartSpaceDom;
117
220
  const chart = child(chartSpace, "chart");
118
221
  if (!chart)
119
222
  return null;
120
223
  const region = child(child(chart, "plotArea"), "plotAreaRegion");
121
- const series = asArray(child(region, "series"))[0];
122
- if (!series)
224
+ const allSeries = asArray(child(region, "series"));
225
+ const dataSeries = allSeries.filter((series) => String(get(series, "a$.layoutId") ?? "") !== "paretoLine");
226
+ const paretoLine = allSeries.find((series) => String(get(series, "a$.layoutId") ?? "") === "paretoLine");
227
+ const first = dataSeries[0];
228
+ if (!first)
123
229
  return null;
124
- const layoutId = String(get(series, "a$.layoutId") ?? "");
230
+ const layoutId = String(get(first, "a$.layoutId") ?? "");
125
231
  // Excel nests `<cx:binning>` in `<cx:layoutPr>`; tolerate it directly on the series too.
126
- const binning = child(child(series, "layoutPr"), "binning") ?? child(series, "binning");
127
- const chartType = layoutId === "clusteredColumn" && binning
128
- ? "histogram"
129
- : CHART_TYPE_BY_LAYOUT[layoutId];
130
- if (!chartType)
131
- return null;
132
- const dataId = String(get(child(series, "dataId"), "a$.val") ?? "0");
133
- const data = asArray(child(child(chartSpace, "chartData"), "data")).find((d) => String(get(d, "a$.id")) === dataId) ?? asArray(child(child(chartSpace, "chartData"), "data"))[0];
134
- if (!data)
232
+ const layoutPr = child(first, "layoutPr");
233
+ const binning = child(layoutPr, "binning") ?? child(first, "binning");
234
+ const kind = chartKindOf(layoutId, Boolean(binning));
235
+ if (!kind)
135
236
  return null;
237
+ const { chartType } = kind;
238
+ const hierarchy = chartType === "treemap" || chartType === "sunburst";
239
+ const dataBlocks = asArray(child(child(chartSpace, "chartData"), "data"));
240
+ const blockFor = (series) => {
241
+ const dataId = String(get(child(series, "dataId"), "a$.val") ?? "0");
242
+ return (dataBlocks.find((d) => String(get(d, "a$.id")) === dataId) ??
243
+ dataBlocks[0]);
244
+ };
245
+ const sources = (ref) => sourcesFromRef(ref, sheetId, resolveSheetId, resolveName);
136
246
  const title = textOf(child(child(child(child(chart, "title"), "tx"), "txData"), "v"));
137
- const catRef = dimensionRef(data, "strDim", "cat");
138
- const catSources = sourcesFromRef(catRef, sheetId, resolveSheetId);
139
- if (chartType === "treemap" || chartType === "sunburst") {
140
- const sizeRef = dimensionRef(data, "numDim", "size");
141
- const sizeSources = sourcesFromRef(sizeRef, sheetId, resolveSheetId);
247
+ const spec = { chartType };
248
+ if (kind.sourceChartType)
249
+ spec.sourceChartType = kind.sourceChartType;
250
+ if (title)
251
+ spec.title = title;
252
+ if (hierarchy) {
253
+ const data = blockFor(first);
254
+ if (!data)
255
+ return null;
256
+ const catSources = sources(dimensionRef(data, "strDim", "cat"));
257
+ const sizeSources = sources(dimensionRef(data, "numDim", "size") ?? dimensionRef(data, "numDim", "val"));
142
258
  if (!catSources.length || !sizeSources.length)
143
259
  return null;
144
- const hierarchy = splitHierarchyRange(catSources[0]);
145
- return {
146
- chartType,
147
- ...(title ? { title } : {}),
148
- labels: [chartData([hierarchy.labels])],
149
- parentLabels: hierarchy.parentLabels.map((range) => chartData([range])),
150
- sizeData: [chartData(sizeSources)],
151
- };
152
- }
153
- const valRef = dimensionRef(data, "numDim", "val");
154
- const valSources = sourcesFromRef(valRef, sheetId, resolveSheetId);
155
- if (!valSources.length)
156
- return null;
157
- const spec = {
158
- chartType,
159
- ...(title ? { title } : {}),
160
- series: [chartData(valSources)],
161
- ...(catSources.length ? { domains: [chartData(catSources)] } : {}),
260
+ const hierarchyRanges = splitHierarchyRange(catSources[0]);
261
+ spec.labels = [chartData([hierarchyRanges.labels])];
262
+ spec.parentLabels = hierarchyRanges.parentLabels.map((range) => chartData([range]));
263
+ spec.sizeData = [chartData(sizeSources)];
264
+ spec.useFirstRowAsHeaders = false;
265
+ if (chartType === "treemap") {
266
+ const layout = String(get(child(layoutPr, "parentLabelLayout"), "a$.val") ?? "");
267
+ if (layout === "banner" || layout === "overlapping" || layout === "none") {
268
+ spec.parentLabelLayout = layout;
269
+ }
270
+ }
271
+ }
272
+ else {
273
+ const series = [];
274
+ let domains;
275
+ let chartDataLabels;
276
+ for (const entry of dataSeries) {
277
+ const data = blockFor(entry);
278
+ if (!data)
279
+ continue;
280
+ const valSources = sources(dimensionRef(data, "numDim", "val") ?? dimensionRef(data, "numDim", "size"));
281
+ if (!valSources.length)
282
+ continue;
283
+ const txData = child(child(entry, "tx"), "txData");
284
+ const nameRef = textOf(child(txData, "f"))?.trim();
285
+ const nameSource = nameRef ? sources(nameRef)[0] : undefined;
286
+ const item = {
287
+ dataLabel: textOf(child(txData, "v")) ?? `Series ${series.length + 1}`,
288
+ ...(nameSource ? { dataLabelSource: singleCell(nameSource) } : {}),
289
+ sources: valSources,
290
+ };
291
+ const color = colorOf(entry);
292
+ if (color)
293
+ item.color = color;
294
+ const labels = dataLabelsOf(entry);
295
+ if (labels) {
296
+ item.dataLabels = labels;
297
+ chartDataLabels ??= labels;
298
+ }
299
+ if (!domains) {
300
+ const catSources = sources(dimensionRef(data, "strDim", "cat"));
301
+ if (catSources.length)
302
+ domains = [chartData(catSources)];
303
+ }
304
+ series.push(item);
305
+ }
306
+ if (!series.length)
307
+ return null;
308
+ spec.series = series;
309
+ if (domains)
310
+ spec.domains = domains;
311
+ if (chartDataLabels)
312
+ spec.dataLabels = chartDataLabels;
313
+ }
314
+ if (paretoLine) {
315
+ spec.sourceChartType = "PARETO";
316
+ const color = colorOf(paretoLine);
317
+ if (color)
318
+ spec.paretoLineColor = color;
319
+ }
320
+ if (chartType === "boxplot") {
321
+ const vis = child(layoutPr, "visibility");
322
+ if (flagOf(vis, "outliers") === false)
323
+ spec.showOutliers = false;
324
+ if (flagOf(vis, "meanMarker"))
325
+ spec.showMeanMarker = true;
326
+ if (flagOf(vis, "nonoutliers"))
327
+ spec.showInnerPoints = true;
328
+ if (flagOf(vis, "meanLine"))
329
+ spec.showMeanLine = true;
330
+ const method = String(get(child(layoutPr, "statistics"), "a$.quartileMethod") ?? "");
331
+ if (method === "inclusive" || method === "exclusive")
332
+ spec.quartileMethod = method;
333
+ }
334
+ if (binning) {
335
+ const binSize = numberOf(get(child(binning, "binSize"), "a$.val"));
336
+ if (binSize !== undefined && binSize > 0)
337
+ spec.binSize = binSize;
338
+ const binCount = numberOf(get(child(binning, "binCount"), "a$.val"));
339
+ if (binCount !== undefined && binCount >= 1)
340
+ spec.binCount = Math.round(binCount);
341
+ if (get(binning, "a$.intervalClosed") === "l")
342
+ spec.intervalClosed = "l";
343
+ const overflow = numberOf(get(binning, "a$.overflow"));
344
+ if (overflow !== undefined)
345
+ spec.overflow = overflow;
346
+ const underflow = numberOf(get(binning, "a$.underflow"));
347
+ if (underflow !== undefined)
348
+ spec.underflow = underflow;
349
+ }
350
+ if (chartType === "waterfall") {
351
+ const totals = asArray(child(child(layoutPr, "subtotals"), "idx"))
352
+ .map((idx) => numberOf(get(idx, "a$.val")))
353
+ .filter((n) => n !== undefined);
354
+ if (totals.length)
355
+ spec.totalIndices = totals;
356
+ }
357
+ // chartex writes `<cx:legend pos>` only when the legend is shown.
358
+ const legendPos = String(get(child(chart, "legend"), "a$.pos") ?? "");
359
+ spec.legend = {
360
+ position: { t: "top", b: "bottom", l: "left", r: "right", tr: "topRight" }[legendPos] ?? "none",
162
361
  };
163
- if (chartType === "histogram") {
164
- const binCount = Number(get(child(binning, "binCount"), "a$.val"));
165
- if (Number.isFinite(binCount) && binCount > 0)
166
- spec.binCount = binCount;
362
+ // Axes: the value axis' scale and gridlines, the category axis' gap (a fraction of the bar width).
363
+ for (const axis of asArray(child(child(chart, "plotArea"), "axis"))) {
364
+ const valScaling = child(axis, "valScaling");
365
+ if (valScaling) {
366
+ const options = {};
367
+ const min = numberOf(get(valScaling, "a$.min"));
368
+ const max = numberOf(get(valScaling, "a$.max"));
369
+ if (min !== undefined)
370
+ options.min = min;
371
+ if (max !== undefined)
372
+ options.max = max;
373
+ if (!child(axis, "majorGridlines"))
374
+ options.showMajorGridlines = false;
375
+ if (Object.keys(options).length)
376
+ spec.valueAxisOptions = options;
377
+ }
378
+ const gap = numberOf(get(child(axis, "catScaling"), "a$.gapWidth"));
379
+ if (gap !== undefined)
380
+ spec.gapWidth = Math.round(gap * 100);
167
381
  }
168
382
  return spec;
169
383
  }
170
384
  /** Resolve + parse one chartex part referenced from a drawing anchor. */
171
- export async function processChartEx(chartTarget, drawingPath, anchor, sheetId, zipParser, xmlParser, sheetDimensions, resolveSheetId) {
385
+ export async function processChartEx(chartTarget, drawingPath, anchor, sheetId, zipParser, xmlParser, sheetDimensions, resolveSheetId, resolveName) {
172
386
  let chartPath = chartTarget;
173
387
  const drawingDir = drawingPath.substring(0, drawingPath.lastIndexOf("/"));
174
388
  if (chartPath.startsWith("/")) {
@@ -189,7 +403,7 @@ export async function processChartEx(chartTarget, drawingPath, anchor, sheetId,
189
403
  }
190
404
  if (!content)
191
405
  return null;
192
- const spec = parseChartExSpec(xmlParser.parse(content), sheetId, resolveSheetId);
406
+ const spec = parseChartExSpec(xmlParser.parse(content), sheetId, resolveSheetId, resolveName);
193
407
  if (!spec)
194
408
  return null;
195
409
  const position = extractChartPosition(anchor, sheetId, sheetDimensions);