@rowsncolumns/toolkit 24.4.2 → 24.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/excel-parser/cfb-decryptor.d.ts +6 -0
- package/dist/cjs/excel-parser/cfb-decryptor.d.ts.map +1 -1
- package/dist/cjs/excel-parser/cfb-decryptor.js +6 -0
- package/dist/cjs/excel-parser/helpers.d.ts +9 -1
- package/dist/cjs/excel-parser/helpers.d.ts.map +1 -1
- package/dist/cjs/excel-parser/helpers.js +37 -0
- package/dist/cjs/excel-parser/interface.d.ts +15 -2
- package/dist/cjs/excel-parser/interface.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/base/index.d.ts +2 -0
- package/dist/cjs/excel-parser/plugins/base/index.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/base/index.js +3 -0
- package/dist/cjs/excel-parser/plugins/ods/index.d.ts +19 -0
- package/dist/cjs/excel-parser/plugins/ods/index.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/ods/index.js +676 -304
- package/dist/cjs/excel-parser/plugins/ods/styles.d.ts +43 -1
- package/dist/cjs/excel-parser/plugins/ods/styles.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/ods/styles.js +343 -45
- package/dist/cjs/excel-parser/plugins/xls/index.d.ts +55 -1
- package/dist/cjs/excel-parser/plugins/xls/index.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xls/index.js +518 -6
- package/dist/cjs/excel-parser/plugins/xlsb/index.d.ts +4 -0
- package/dist/cjs/excel-parser/plugins/xlsb/index.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsb/index.js +33 -14
- package/dist/cjs/excel-parser/plugins/xlsx/chartex.d.ts +37 -0
- package/dist/cjs/excel-parser/plugins/xlsx/chartex.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/chartex.js +244 -0
- package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts +10 -0
- package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/charts.js +68 -9
- package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts +28 -1
- package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/drawing.js +155 -20
- package/dist/cjs/excel-parser/plugins/xlsx/form-controls.d.ts +70 -0
- package/dist/cjs/excel-parser/plugins/xlsx/form-controls.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/form-controls.js +295 -0
- package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts +35 -2
- package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/index.js +737 -257
- package/dist/cjs/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/pivots.js +125 -37
- package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.d.ts +3 -0
- package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.js +4 -1
- package/dist/cjs/excel-parser/plugins/xlsx/styles.d.ts +15 -1
- package/dist/cjs/excel-parser/plugins/xlsx/styles.d.ts.map +1 -1
- package/dist/cjs/excel-parser/plugins/xlsx/styles.js +102 -6
- package/dist/cjs/excel-parser/registry.d.ts +7 -0
- package/dist/cjs/excel-parser/registry.d.ts.map +1 -1
- package/dist/cjs/excel-parser/registry.js +29 -0
- package/dist/cjs/excel-parser/types.d.ts +17 -2
- package/dist/cjs/excel-parser/types.d.ts.map +1 -1
- package/dist/cjs/excel-parser/workbook.d.ts +3 -0
- package/dist/cjs/excel-parser/workbook.d.ts.map +1 -1
- package/dist/cjs/excel-parser/workbook.js +24 -14
- package/dist/cjs/excel-parser/worker.js +131 -131
- package/dist/cjs/exporter/ods/ods-export.d.ts.map +1 -1
- package/dist/cjs/exporter/ods/ods-export.js +432 -175
- package/dist/cjs/exporter/xlsx/chart-builder.d.ts.map +1 -1
- package/dist/cjs/exporter/xlsx/chart-builder.js +215 -39
- package/dist/cjs/exporter/xlsx/chartex-builder.d.ts +36 -0
- package/dist/cjs/exporter/xlsx/chartex-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/chartex-builder.js +109 -0
- package/dist/cjs/exporter/xlsx/form-controls-builder.d.ts +61 -0
- package/dist/cjs/exporter/xlsx/form-controls-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/form-controls-builder.js +197 -0
- package/dist/cjs/exporter/xlsx/pivot-builder.d.ts +8 -0
- package/dist/cjs/exporter/xlsx/pivot-builder.d.ts.map +1 -1
- package/dist/cjs/exporter/xlsx/pivot-builder.js +141 -42
- package/dist/cjs/exporter/xlsx/style-builder.js +9 -3
- package/dist/cjs/exporter/xlsx/threaded-comments-builder.d.ts +7 -1
- package/dist/cjs/exporter/xlsx/threaded-comments-builder.d.ts.map +1 -1
- package/dist/cjs/exporter/xlsx/threaded-comments-builder.js +15 -3
- package/dist/cjs/exporter/xlsx/xlsx-export.d.ts +11 -2
- package/dist/cjs/exporter/xlsx/xlsx-export.d.ts.map +1 -1
- package/dist/cjs/exporter/xlsx/xlsx-export.js +991 -297
- package/dist/cjs/helpers/implicit-intersection.d.ts +16 -0
- package/dist/cjs/helpers/implicit-intersection.d.ts.map +1 -0
- package/dist/cjs/helpers/implicit-intersection.js +197 -0
- package/dist/cjs/print-settings.d.ts +105 -0
- package/dist/cjs/print-settings.d.ts.map +1 -0
- package/dist/cjs/print-settings.js +494 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/excel-parser/cfb-decryptor.d.ts +6 -0
- package/dist/esm/excel-parser/cfb-decryptor.d.ts.map +1 -1
- package/dist/esm/excel-parser/cfb-decryptor.js +6 -0
- package/dist/esm/excel-parser/helpers.d.ts +9 -1
- package/dist/esm/excel-parser/helpers.d.ts.map +1 -1
- package/dist/esm/excel-parser/helpers.js +34 -0
- package/dist/esm/excel-parser/interface.d.ts +15 -2
- package/dist/esm/excel-parser/interface.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/base/index.d.ts +2 -0
- package/dist/esm/excel-parser/plugins/base/index.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/base/index.js +3 -0
- package/dist/esm/excel-parser/plugins/ods/index.d.ts +19 -0
- package/dist/esm/excel-parser/plugins/ods/index.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/ods/index.js +347 -33
- package/dist/esm/excel-parser/plugins/ods/styles.d.ts +43 -1
- package/dist/esm/excel-parser/plugins/ods/styles.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/ods/styles.js +276 -2
- package/dist/esm/excel-parser/plugins/xls/index.d.ts +55 -1
- package/dist/esm/excel-parser/plugins/xls/index.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xls/index.js +441 -3
- package/dist/esm/excel-parser/plugins/xlsb/index.d.ts +4 -0
- package/dist/esm/excel-parser/plugins/xlsb/index.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsb/index.js +30 -9
- package/dist/esm/excel-parser/plugins/xlsx/chartex.d.ts +37 -0
- package/dist/esm/excel-parser/plugins/xlsx/chartex.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/chartex.js +199 -0
- package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts +10 -0
- package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/charts.js +69 -6
- package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts +28 -1
- package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/drawing.js +99 -0
- package/dist/esm/excel-parser/plugins/xlsx/form-controls.d.ts +70 -0
- package/dist/esm/excel-parser/plugins/xlsx/form-controls.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/form-controls.js +246 -0
- package/dist/esm/excel-parser/plugins/xlsx/index.d.ts +35 -2
- package/dist/esm/excel-parser/plugins/xlsx/index.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/index.js +454 -32
- package/dist/esm/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/pivots.js +86 -6
- package/dist/esm/excel-parser/plugins/xlsx/sheet-views.d.ts +3 -0
- package/dist/esm/excel-parser/plugins/xlsx/sheet-views.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/sheet-views.js +2 -0
- package/dist/esm/excel-parser/plugins/xlsx/styles.d.ts +15 -1
- package/dist/esm/excel-parser/plugins/xlsx/styles.d.ts.map +1 -1
- package/dist/esm/excel-parser/plugins/xlsx/styles.js +92 -2
- package/dist/esm/excel-parser/registry.d.ts +7 -0
- package/dist/esm/excel-parser/registry.d.ts.map +1 -1
- package/dist/esm/excel-parser/registry.js +19 -0
- package/dist/esm/excel-parser/types.d.ts +17 -2
- package/dist/esm/excel-parser/types.d.ts.map +1 -1
- package/dist/esm/excel-parser/workbook.d.ts +3 -0
- package/dist/esm/excel-parser/workbook.d.ts.map +1 -1
- package/dist/esm/excel-parser/workbook.js +19 -10
- package/dist/esm/excel-parser/worker.js +12 -18
- package/dist/esm/exporter/ods/ods-export.d.ts.map +1 -1
- package/dist/esm/exporter/ods/ods-export.js +306 -35
- package/dist/esm/exporter/xlsx/chart-builder.d.ts.map +1 -1
- package/dist/esm/exporter/xlsx/chart-builder.js +161 -28
- package/dist/esm/exporter/xlsx/chartex-builder.d.ts +36 -0
- package/dist/esm/exporter/xlsx/chartex-builder.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/chartex-builder.js +97 -0
- package/dist/esm/exporter/xlsx/form-controls-builder.d.ts +61 -0
- package/dist/esm/exporter/xlsx/form-controls-builder.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/form-controls-builder.js +173 -0
- package/dist/esm/exporter/xlsx/pivot-builder.d.ts +8 -0
- package/dist/esm/exporter/xlsx/pivot-builder.d.ts.map +1 -1
- package/dist/esm/exporter/xlsx/pivot-builder.js +104 -19
- package/dist/esm/exporter/xlsx/style-builder.js +9 -3
- package/dist/esm/exporter/xlsx/threaded-comments-builder.d.ts +7 -1
- package/dist/esm/exporter/xlsx/threaded-comments-builder.d.ts.map +1 -1
- package/dist/esm/exporter/xlsx/threaded-comments-builder.js +15 -3
- package/dist/esm/exporter/xlsx/xlsx-export.d.ts +11 -2
- package/dist/esm/exporter/xlsx/xlsx-export.d.ts.map +1 -1
- package/dist/esm/exporter/xlsx/xlsx-export.js +675 -64
- package/dist/esm/helpers/implicit-intersection.d.ts +16 -0
- package/dist/esm/helpers/implicit-intersection.d.ts.map +1 -0
- package/dist/esm/helpers/implicit-intersection.js +184 -0
- package/dist/esm/print-settings.d.ts +105 -0
- package/dist/esm/print-settings.d.ts.map +1 -0
- package/dist/esm/print-settings.js +422 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/xlsx.d.ts +14 -0
|
@@ -1456,34 +1456,6 @@ export const buildChartXml = (chart, sheetNameById) => {
|
|
|
1456
1456
|
targetChart["c:ser"] = seriesContainer;
|
|
1457
1457
|
chartSpace["c:chartSpace"]["c:chart"] = chartDoc;
|
|
1458
1458
|
}
|
|
1459
|
-
else if (chartType === "treemap" || chartType === "sunburst") {
|
|
1460
|
-
const treeSpec = chart.spec;
|
|
1461
|
-
const labelRef = formatRangeRef(getPrimarySource(treeSpec.labels?.[0]), sheetNameById);
|
|
1462
|
-
const sizeRef = formatRangeRef(getPrimarySource(treeSpec.sizeData?.[0]), sheetNameById);
|
|
1463
|
-
if (!labelRef || !sizeRef)
|
|
1464
|
-
return undefined;
|
|
1465
|
-
if (chartType === "sunburst") {
|
|
1466
|
-
const pieChart = buildPieChart(labelRef, sizeRef, chartTitle, chartSubtitle);
|
|
1467
|
-
chartSpace["c:chartSpace"]["c:chart"] = pieChart;
|
|
1468
|
-
}
|
|
1469
|
-
else {
|
|
1470
|
-
const columnChart = cloneTemplate(column_json);
|
|
1471
|
-
applyChartTitle(columnChart, chartTitle, chartTitleFmt, chartSubtitle);
|
|
1472
|
-
const bar = columnChart["c:plotArea"]["c:barChart"];
|
|
1473
|
-
const seriesContainer = bar["c:ser"];
|
|
1474
|
-
const seriesNode = cloneTemplate(column_series);
|
|
1475
|
-
seriesNode["c:idx"] = { a$: { val: "0" } };
|
|
1476
|
-
seriesNode["c:order"] = { a$: { val: "0" } };
|
|
1477
|
-
seriesNode["c:cat"] = {
|
|
1478
|
-
"c:strRef": {
|
|
1479
|
-
"c:f": labelRef,
|
|
1480
|
-
},
|
|
1481
|
-
};
|
|
1482
|
-
seriesNode["c:val"]["c:numRef"]["c:f"] = sizeRef;
|
|
1483
|
-
seriesContainer.push(seriesNode);
|
|
1484
|
-
chartSpace["c:chartSpace"]["c:chart"] = columnChart;
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
1459
|
else {
|
|
1488
1460
|
return undefined;
|
|
1489
1461
|
}
|
|
@@ -1492,6 +1464,12 @@ export const buildChartXml = (chart, sheetNameById) => {
|
|
|
1492
1464
|
// chartType-agnostic and intentionally run last so per-type branches
|
|
1493
1465
|
// don't have to know about them.
|
|
1494
1466
|
applyChartFormatting(chartSpace, chart.spec, sheetNameById);
|
|
1467
|
+
// 3-D / surface variants (sourceChartType BAR_3D, LINE_3D, PIE_3D,
|
|
1468
|
+
// AREA_3D, SURFACE, SURFACE_3D) — swap the 2-D chart group for its
|
|
1469
|
+
// OOXML 3-D element and add `<c:view3D>`. Runs after the formatting
|
|
1470
|
+
// pass so data labels / overlays land in the container first and are
|
|
1471
|
+
// carried across by the rename.
|
|
1472
|
+
applyThreeDChartType(chartSpace["c:chartSpace"]["c:chart"], chart.spec.sourceChartType);
|
|
1495
1473
|
// `<c:roundedCorners val="1"/>` lives at the chartSpace root (sibling
|
|
1496
1474
|
// of `<c:chart>`). Default per OOXML §21.2.2.183 is `false`; only
|
|
1497
1475
|
// emit when the spec opted in so files imported without the element
|
|
@@ -1502,6 +1480,161 @@ export const buildChartXml = (chart, sheetNameById) => {
|
|
|
1502
1480
|
const xml = chartBuilder.build(chartSpace);
|
|
1503
1481
|
return `${XML_DECLARATION}${xml}`;
|
|
1504
1482
|
};
|
|
1483
|
+
// -------------------------- 3-D / surface --------------------------
|
|
1484
|
+
/** Series-axis id for the 3-D chart groups that take a third axis. */
|
|
1485
|
+
const SER_AX_ID = "1789284465";
|
|
1486
|
+
/**
|
|
1487
|
+
* How each 3-D class maps onto its 2-D chart group: which container it
|
|
1488
|
+
* replaces, the element it becomes, and how many `<c:axId>` refs the
|
|
1489
|
+
* schema wants (0 = no axes, 3 = adds a series axis).
|
|
1490
|
+
*/
|
|
1491
|
+
const THREE_D_CHART_GROUPS = {
|
|
1492
|
+
BAR_3D: { from: "c:barChart", to: "c:bar3DChart", axes: 2 },
|
|
1493
|
+
LINE_3D: { from: "c:lineChart", to: "c:line3DChart", axes: 3 },
|
|
1494
|
+
PIE_3D: { from: "c:pieChart", to: "c:pie3DChart", axes: 0 },
|
|
1495
|
+
AREA_3D: { from: "c:areaChart", to: "c:area3DChart", axes: 2 },
|
|
1496
|
+
SURFACE: { from: "c:areaChart", to: "c:surfaceChart", axes: 2 },
|
|
1497
|
+
SURFACE_3D: { from: "c:areaChart", to: "c:surface3DChart", axes: 3 },
|
|
1498
|
+
};
|
|
1499
|
+
const normalizeThreeDToken = (value) => {
|
|
1500
|
+
if (typeof value !== "string")
|
|
1501
|
+
return undefined;
|
|
1502
|
+
const token = value.trim().toUpperCase();
|
|
1503
|
+
return token in THREE_D_CHART_GROUPS ? token : undefined;
|
|
1504
|
+
};
|
|
1505
|
+
/** Copy `keys` (in order) from `source` into `target`, skipping absent ones. */
|
|
1506
|
+
const pickKeys = (target, source, keys) => {
|
|
1507
|
+
for (const key of keys) {
|
|
1508
|
+
if (source[key] !== undefined)
|
|
1509
|
+
target[key] = source[key];
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
/**
|
|
1513
|
+
* Rebuild the 2-D chart group as its 3-D element with the CT_*3DChart
|
|
1514
|
+
* child sequence. Element order matters to Excel, so each shape is
|
|
1515
|
+
* written out explicitly rather than renaming the key in place:
|
|
1516
|
+
* bar3DChart: barDir, grouping, varyColors, ser, dLbls, gapWidth, shape, axId
|
|
1517
|
+
* line3DChart: grouping, varyColors, ser, dLbls, axId×3
|
|
1518
|
+
* pie3DChart: varyColors, ser, dLbls
|
|
1519
|
+
* area3DChart: grouping, varyColors, ser, dLbls, axId
|
|
1520
|
+
* surfaceChart: wireframe, ser, axId (surface3DChart: axId×3)
|
|
1521
|
+
*/
|
|
1522
|
+
const buildThreeDChartGroup = (token, group, axIds) => {
|
|
1523
|
+
const node = {};
|
|
1524
|
+
switch (token) {
|
|
1525
|
+
case "BAR_3D":
|
|
1526
|
+
pickKeys(node, group, ["c:barDir", "c:grouping", "c:varyColors"]);
|
|
1527
|
+
pickKeys(node, group, ["c:ser", "c:dLbls", "c:gapWidth"]);
|
|
1528
|
+
node["c:shape"] = { a$: { val: "box" } };
|
|
1529
|
+
node["c:axId"] = axIds;
|
|
1530
|
+
return node;
|
|
1531
|
+
case "LINE_3D":
|
|
1532
|
+
case "AREA_3D":
|
|
1533
|
+
pickKeys(node, group, ["c:grouping", "c:varyColors", "c:ser", "c:dLbls"]);
|
|
1534
|
+
node["c:axId"] = axIds;
|
|
1535
|
+
return node;
|
|
1536
|
+
case "PIE_3D":
|
|
1537
|
+
pickKeys(node, group, ["c:varyColors", "c:ser", "c:dLbls"]);
|
|
1538
|
+
return node;
|
|
1539
|
+
case "SURFACE":
|
|
1540
|
+
case "SURFACE_3D":
|
|
1541
|
+
node["c:wireframe"] = { a$: { val: "0" } };
|
|
1542
|
+
pickKeys(node, group, ["c:ser"]);
|
|
1543
|
+
node["c:axId"] = axIds;
|
|
1544
|
+
return node;
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
/** `<c:serAx>` for the 3-D groups whose schema takes a third (depth) axis. */
|
|
1548
|
+
const buildSerAxNode = (valAxId) => ({
|
|
1549
|
+
"c:axId": { a$: { val: SER_AX_ID } },
|
|
1550
|
+
"c:scaling": { "c:orientation": { a$: { val: "minMax" } } },
|
|
1551
|
+
"c:delete": { a$: { val: "0" } },
|
|
1552
|
+
"c:axPos": { a$: { val: "b" } },
|
|
1553
|
+
"c:majorTickMark": { a$: { val: "out" } },
|
|
1554
|
+
"c:minorTickMark": { a$: { val: "none" } },
|
|
1555
|
+
"c:tickLblPos": { a$: { val: "nextTo" } },
|
|
1556
|
+
"c:crossAx": { a$: { val: valAxId } },
|
|
1557
|
+
"c:crosses": { a$: { val: "autoZero" } },
|
|
1558
|
+
});
|
|
1559
|
+
/** `<c:view3D>` — Excel's default camera for each class (CT_View3D order). */
|
|
1560
|
+
const buildView3DNode = (token) => {
|
|
1561
|
+
if (token === "PIE_3D") {
|
|
1562
|
+
return {
|
|
1563
|
+
"c:rotX": { a$: { val: "30" } },
|
|
1564
|
+
"c:rotY": { a$: { val: "0" } },
|
|
1565
|
+
"c:rAngAx": { a$: { val: "0" } },
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
if (token === "SURFACE" || token === "SURFACE_3D") {
|
|
1569
|
+
// Right-angle axes are not allowed on surfaces; use a perspective view.
|
|
1570
|
+
return {
|
|
1571
|
+
"c:rotX": { a$: { val: "15" } },
|
|
1572
|
+
"c:rotY": { a$: { val: "20" } },
|
|
1573
|
+
"c:rAngAx": { a$: { val: "0" } },
|
|
1574
|
+
"c:perspective": { a$: { val: "30" } },
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
return {
|
|
1578
|
+
"c:rotX": { a$: { val: "15" } },
|
|
1579
|
+
"c:rotY": { a$: { val: "20" } },
|
|
1580
|
+
"c:rAngAx": { a$: { val: "1" } },
|
|
1581
|
+
};
|
|
1582
|
+
};
|
|
1583
|
+
/**
|
|
1584
|
+
* Turn an assembled 2-D `<c:chart>` into its 3-D variant when the spec
|
|
1585
|
+
* carries a 3-D `sourceChartType`: the chart group is swapped for the
|
|
1586
|
+
* matching `c:*3DChart` / `c:surfaceChart` element, a `<c:serAx>` is
|
|
1587
|
+
* added where the schema takes three axes, and `<c:view3D>` is inserted
|
|
1588
|
+
* before `<c:plotArea>`. No-op for 2-D specs or when the token doesn't
|
|
1589
|
+
* match the emitted chart group (the 2-D chart is left untouched).
|
|
1590
|
+
*/
|
|
1591
|
+
const applyThreeDChartType = (chartNode, sourceChartType) => {
|
|
1592
|
+
const token = normalizeThreeDToken(sourceChartType);
|
|
1593
|
+
if (!token || !chartNode)
|
|
1594
|
+
return;
|
|
1595
|
+
const plotArea = chartNode["c:plotArea"];
|
|
1596
|
+
if (!plotArea)
|
|
1597
|
+
return;
|
|
1598
|
+
const { from, to, axes } = THREE_D_CHART_GROUPS[token];
|
|
1599
|
+
const group = plotArea[from];
|
|
1600
|
+
if (!group || Array.isArray(group))
|
|
1601
|
+
return;
|
|
1602
|
+
const existingAxIds = Array.isArray(group["c:axId"])
|
|
1603
|
+
? group["c:axId"]
|
|
1604
|
+
: group["c:axId"]
|
|
1605
|
+
? [group["c:axId"]]
|
|
1606
|
+
: [];
|
|
1607
|
+
const valAxId = existingAxIds[1]?.a$?.val ?? existingAxIds[0]?.a$?.val ?? "0";
|
|
1608
|
+
const axIds = axes === 3
|
|
1609
|
+
? [...existingAxIds.slice(0, 2), { a$: { val: SER_AX_ID } }]
|
|
1610
|
+
: existingAxIds.slice(0, 2);
|
|
1611
|
+
// Rebuild plotArea preserving element order, swapping the chart group
|
|
1612
|
+
// and appending the series axis after the value axis when needed.
|
|
1613
|
+
const rebuiltPlotArea = {};
|
|
1614
|
+
for (const [key, value] of Object.entries(plotArea)) {
|
|
1615
|
+
if (key === from) {
|
|
1616
|
+
rebuiltPlotArea[to] = buildThreeDChartGroup(token, group, axIds);
|
|
1617
|
+
continue;
|
|
1618
|
+
}
|
|
1619
|
+
rebuiltPlotArea[key] = value;
|
|
1620
|
+
if (key === "c:valAx" && axes === 3) {
|
|
1621
|
+
rebuiltPlotArea["c:serAx"] = buildSerAxNode(valAxId);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
// Rebuild the chart node so `<c:view3D>` lands before `<c:plotArea>`.
|
|
1625
|
+
const rebuiltChart = {};
|
|
1626
|
+
for (const [key, value] of Object.entries(chartNode)) {
|
|
1627
|
+
if (key === "c:plotArea") {
|
|
1628
|
+
rebuiltChart["c:view3D"] = buildView3DNode(token);
|
|
1629
|
+
rebuiltChart[key] = rebuiltPlotArea;
|
|
1630
|
+
continue;
|
|
1631
|
+
}
|
|
1632
|
+
rebuiltChart[key] = value;
|
|
1633
|
+
}
|
|
1634
|
+
for (const key of Object.keys(chartNode))
|
|
1635
|
+
delete chartNode[key];
|
|
1636
|
+
Object.assign(chartNode, rebuiltChart);
|
|
1637
|
+
};
|
|
1505
1638
|
// -------------------------- Phase 5 helpers --------------------------
|
|
1506
1639
|
const LEGEND_POS = {
|
|
1507
1640
|
top: "t",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { EmbeddedChart, SheetRange, TreemapChart } from "@rowsncolumns/spreadsheet";
|
|
2
|
+
/**
|
|
3
|
+
* Excel 2016+ "chartex" writer (`xl/charts/chartEx{N}.xml`) — the inverse
|
|
4
|
+
* of `excel-parser/plugins/xlsx/chartex.ts`. Treemap and sunburst have no
|
|
5
|
+
* `c:chartSpace` representation; Excel stores them as a `cx:chartSpace`
|
|
6
|
+
* whose series `layoutId` names the chart and whose data is a hierarchy
|
|
7
|
+
* `strDim type="cat"` (parent columns first, leaf last) plus a
|
|
8
|
+
* `numDim type="size"`. Anchored through `mc:AlternateContent` requiring the
|
|
9
|
+
* `cx1` namespace.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CHART_EX_REL_TYPE = "http://schemas.microsoft.com/office/2014/relationships/chartEx";
|
|
12
|
+
export declare const CHART_EX_CONTENT_TYPE = "application/vnd.ms-office.chartex+xml";
|
|
13
|
+
export declare const CHART_EX_NS = "http://schemas.microsoft.com/office/drawing/2014/chartex";
|
|
14
|
+
/** Chart types that live in a chartex part rather than a `c:chartSpace`. */
|
|
15
|
+
export declare const isChartExType: (chartType: string | undefined | null) => chartType is "treemap" | "sunburst";
|
|
16
|
+
/**
|
|
17
|
+
* The hierarchy reference: parent columns + the leaf column merged into one
|
|
18
|
+
* contiguous range when they sit side by side on the same sheet over the same
|
|
19
|
+
* rows (how Excel lays a treemap out); otherwise the leaf labels alone.
|
|
20
|
+
*/
|
|
21
|
+
export declare const hierarchyRange: (spec: TreemapChart) => SheetRange | undefined;
|
|
22
|
+
/** `xl/charts/chartEx{N}.xml` for a treemap / sunburst; undefined when the spec has no data. */
|
|
23
|
+
export declare const buildChartExXml: (chart: EmbeddedChart, sheetNameById: Map<number, string>) => string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* The drawing anchor body for a chartex part: Excel's `mc:AlternateContent`
|
|
26
|
+
* with a `cx1`-requiring choice holding the graphic frame. No `mc:Fallback`
|
|
27
|
+
* is written — Excel opens the file without one, and a fallback text box
|
|
28
|
+
* would import as a stray shape in consumers that do not know chartex.
|
|
29
|
+
*/
|
|
30
|
+
export declare const buildChartExGraphicFrameXml: ({ relId, frameId, widthEmu, heightEmu, }: {
|
|
31
|
+
relId: string;
|
|
32
|
+
frameId: number;
|
|
33
|
+
widthEmu: number;
|
|
34
|
+
heightEmu: number;
|
|
35
|
+
}) => string;
|
|
36
|
+
//# sourceMappingURL=chartex-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chartex-builder.d.ts","sourceRoot":"","sources":["../../../../exporter/xlsx/chartex-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,aAAa,EACb,UAAU,EACV,YAAY,EACb,MAAM,2BAA2B,CAAC;AAGnC;;;;;;;;GAQG;AAEH,eAAO,MAAM,iBAAiB,mEACoC,CAAC;AACnE,eAAO,MAAM,qBAAqB,0CAA0C,CAAC;AAC7E,eAAO,MAAM,WAAW,6DAA6D,CAAC;AAyBtF,4EAA4E;AAC5E,eAAO,MAAM,aAAa,GAAI,WAAW,MAAM,GAAG,SAAS,GAAG,IAAI,wCACb,CAAC;AAItD;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,YAAY,KAAG,UAAU,GAAG,SAsBhE,CAAC;AAEF,gGAAgG;AAChG,eAAO,MAAM,eAAe,GAC1B,OAAO,aAAa,EACpB,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KACjC,MAAM,GAAG,SAkCX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GAAI,0CAKzC;IACD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,WACkhB,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { cellRangeToAddress } from "@rowsncolumns/utils";
|
|
2
|
+
/**
|
|
3
|
+
* Excel 2016+ "chartex" writer (`xl/charts/chartEx{N}.xml`) — the inverse
|
|
4
|
+
* of `excel-parser/plugins/xlsx/chartex.ts`. Treemap and sunburst have no
|
|
5
|
+
* `c:chartSpace` representation; Excel stores them as a `cx:chartSpace`
|
|
6
|
+
* whose series `layoutId` names the chart and whose data is a hierarchy
|
|
7
|
+
* `strDim type="cat"` (parent columns first, leaf last) plus a
|
|
8
|
+
* `numDim type="size"`. Anchored through `mc:AlternateContent` requiring the
|
|
9
|
+
* `cx1` namespace.
|
|
10
|
+
*/
|
|
11
|
+
export const CHART_EX_REL_TYPE = "http://schemas.microsoft.com/office/2014/relationships/chartEx";
|
|
12
|
+
export const CHART_EX_CONTENT_TYPE = "application/vnd.ms-office.chartex+xml";
|
|
13
|
+
export const CHART_EX_NS = "http://schemas.microsoft.com/office/drawing/2014/chartex";
|
|
14
|
+
const CHART_EX_1_NS = "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex";
|
|
15
|
+
const A_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
|
|
16
|
+
const R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
17
|
+
const MC_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006";
|
|
18
|
+
const XML_DECLARATION = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n`;
|
|
19
|
+
/** `{XXXXXXXX-XXXX-4XXX-XXXX-XXXXXXXXXXXX}` — the series `uniqueId` Excel expects. */
|
|
20
|
+
const guid = () => {
|
|
21
|
+
const hex = () => Math.floor(Math.random() * 16).toString(16).toUpperCase();
|
|
22
|
+
const block = (n) => Array.from({ length: n }, hex).join("");
|
|
23
|
+
return `{${block(8)}-${block(4)}-4${block(3)}-${block(4)}-${block(12)}}`;
|
|
24
|
+
};
|
|
25
|
+
/** `'Sheet'!A2:B9` → `'Sheet'!$A$2:$B$9` — Excel writes chart data references absolute. */
|
|
26
|
+
const absoluteRef = (ref) => {
|
|
27
|
+
const bang = ref.lastIndexOf("!");
|
|
28
|
+
const sheetPart = bang >= 0 ? ref.slice(0, bang + 1) : "";
|
|
29
|
+
const cells = bang >= 0 ? ref.slice(bang + 1) : ref;
|
|
30
|
+
return `${sheetPart}${cells.replace(/\$?([A-Z]+)\$?(\d+)/g, "$$$1$$$2")}`;
|
|
31
|
+
};
|
|
32
|
+
const escapeText = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
33
|
+
/** Chart types that live in a chartex part rather than a `c:chartSpace`. */
|
|
34
|
+
export const isChartExType = (chartType) => chartType === "treemap" || chartType === "sunburst";
|
|
35
|
+
const primarySource = (entry) => entry?.sources?.[0];
|
|
36
|
+
/**
|
|
37
|
+
* The hierarchy reference: parent columns + the leaf column merged into one
|
|
38
|
+
* contiguous range when they sit side by side on the same sheet over the same
|
|
39
|
+
* rows (how Excel lays a treemap out); otherwise the leaf labels alone.
|
|
40
|
+
*/
|
|
41
|
+
export const hierarchyRange = (spec) => {
|
|
42
|
+
const leaf = primarySource(spec.labels?.[0]);
|
|
43
|
+
if (!leaf)
|
|
44
|
+
return undefined;
|
|
45
|
+
const parents = (spec.parentLabels ?? [])
|
|
46
|
+
.map((entry) => primarySource(entry))
|
|
47
|
+
.filter((range) => Boolean(range));
|
|
48
|
+
if (!parents.length)
|
|
49
|
+
return leaf;
|
|
50
|
+
const sameRows = parents.every((p) => p.sheetId === leaf.sheetId &&
|
|
51
|
+
p.startRowIndex === leaf.startRowIndex &&
|
|
52
|
+
p.endRowIndex === leaf.endRowIndex);
|
|
53
|
+
if (!sameRows)
|
|
54
|
+
return leaf;
|
|
55
|
+
const columns = [...parents, leaf].map((r) => r.startColumnIndex).sort((a, b) => a - b);
|
|
56
|
+
const contiguous = columns.every((c, i) => i === 0 || c === columns[i - 1] + 1);
|
|
57
|
+
if (!contiguous || columns[columns.length - 1] !== leaf.startColumnIndex) {
|
|
58
|
+
return leaf;
|
|
59
|
+
}
|
|
60
|
+
return { ...leaf, startColumnIndex: columns[0], endColumnIndex: leaf.endColumnIndex };
|
|
61
|
+
};
|
|
62
|
+
/** `xl/charts/chartEx{N}.xml` for a treemap / sunburst; undefined when the spec has no data. */
|
|
63
|
+
export const buildChartExXml = (chart, sheetNameById) => {
|
|
64
|
+
const spec = chart.spec;
|
|
65
|
+
if (!isChartExType(spec.chartType))
|
|
66
|
+
return undefined;
|
|
67
|
+
const cat = hierarchyRange(spec);
|
|
68
|
+
const size = primarySource(spec.sizeData?.[0]);
|
|
69
|
+
if (!cat || !size)
|
|
70
|
+
return undefined;
|
|
71
|
+
const catAddress = cellRangeToAddress(sheetNameById.get(cat.sheetId), cat);
|
|
72
|
+
const sizeAddress = cellRangeToAddress(sheetNameById.get(size.sheetId), size);
|
|
73
|
+
if (!catAddress || !sizeAddress)
|
|
74
|
+
return undefined;
|
|
75
|
+
const catRef = absoluteRef(catAddress);
|
|
76
|
+
const sizeRef = absoluteRef(sizeAddress);
|
|
77
|
+
const title = chart.spec.title;
|
|
78
|
+
const titleXml = title
|
|
79
|
+
? `<cx:title pos="t" align="ctr" overlay="0"><cx:tx><cx:txData><cx:v>${escapeText(title)}</cx:v></cx:txData></cx:tx></cx:title>`
|
|
80
|
+
: "";
|
|
81
|
+
const isTreemap = spec.chartType === "treemap";
|
|
82
|
+
const dataLabels = isTreemap
|
|
83
|
+
? `<cx:dataLabels pos="inEnd"><cx:visibility seriesName="0" categoryName="1" value="0"/></cx:dataLabels>`
|
|
84
|
+
: `<cx:dataLabels><cx:visibility seriesName="0" categoryName="1" value="0"/></cx:dataLabels>`;
|
|
85
|
+
const layoutPr = isTreemap
|
|
86
|
+
? `<cx:layoutPr><cx:parentLabelLayout val="banner"/></cx:layoutPr>`
|
|
87
|
+
: "";
|
|
88
|
+
const legend = isTreemap ? `<cx:legend pos="t" align="ctr" overlay="0"/>` : "";
|
|
89
|
+
return `${XML_DECLARATION}<cx:chartSpace xmlns:a="${A_NS}" xmlns:r="${R_NS}" xmlns:cx="${CHART_EX_NS}"><cx:chartData><cx:data id="0"><cx:strDim type="cat"><cx:f>${escapeText(catRef)}</cx:f></cx:strDim><cx:numDim type="size"><cx:f>${escapeText(sizeRef)}</cx:f></cx:numDim></cx:data></cx:chartData><cx:chart>${titleXml}<cx:plotArea><cx:plotAreaRegion><cx:series layoutId="${spec.chartType}" uniqueId="${guid()}">${dataLabels}<cx:dataId val="0"/>${layoutPr}</cx:series></cx:plotAreaRegion></cx:plotArea>${legend}</cx:chart></cx:chartSpace>`;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* The drawing anchor body for a chartex part: Excel's `mc:AlternateContent`
|
|
93
|
+
* with a `cx1`-requiring choice holding the graphic frame. No `mc:Fallback`
|
|
94
|
+
* is written — Excel opens the file without one, and a fallback text box
|
|
95
|
+
* would import as a stray shape in consumers that do not know chartex.
|
|
96
|
+
*/
|
|
97
|
+
export const buildChartExGraphicFrameXml = ({ relId, frameId, widthEmu, heightEmu, }) => `<mc:AlternateContent xmlns:mc="${MC_NS}"><mc:Choice xmlns:cx1="${CHART_EX_1_NS}" Requires="cx1"><xdr:graphicFrame macro=""><xdr:nvGraphicFramePr><xdr:cNvPr id="${frameId}" name="Chart ${frameId}"/><xdr:cNvGraphicFramePr/></xdr:nvGraphicFramePr><xdr:xfrm><a:off x="0" y="0"/><a:ext cx="${widthEmu}" cy="${heightEmu}"/></xdr:xfrm><a:graphic><a:graphicData uri="${CHART_EX_NS}"><cx:chart xmlns:cx="${CHART_EX_NS}" xmlns:r="${R_NS}" r:id="${relId}"/></a:graphicData></a:graphic></xdr:graphicFrame></mc:Choice></mc:AlternateContent>`;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { EmbedFormControlSpec } from "@rowsncolumns/spreadsheet";
|
|
2
|
+
/**
|
|
3
|
+
* Excel form-control writers — the inverse of
|
|
4
|
+
* `excel-parser/plugins/xlsx/form-controls.ts`. A control is three parts:
|
|
5
|
+
* the worksheet `<controls><control>` entry (anchor + `<controlPr>`), its
|
|
6
|
+
* `xl/ctrlProps/ctrlPropN.xml` (`<formControlPr>`), and the legacy VML shape
|
|
7
|
+
* Excel actually draws (`<v:shape type="#_x0000_t201"><x:ClientData
|
|
8
|
+
* ObjectType="Checkbox">`). All three are written; Excel drops a control
|
|
9
|
+
* whose VML shape is missing.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CTRL_PROP_CONTENT_TYPE = "application/vnd.ms-excel.controlproperties+xml";
|
|
12
|
+
export declare const CTRL_PROP_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp";
|
|
13
|
+
export declare const FORM_CONTROL_NS = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main";
|
|
14
|
+
/** `xl/ctrlProps/ctrlPropN.xml` body. */
|
|
15
|
+
export declare const buildCtrlPropXml: (spec: EmbedFormControlSpec) => string;
|
|
16
|
+
/** A cell anchor in OOXML units: 0-based cell + EMU offsets. */
|
|
17
|
+
export type ControlCellAnchor = {
|
|
18
|
+
col: number;
|
|
19
|
+
colOff: number;
|
|
20
|
+
row: number;
|
|
21
|
+
rowOff: number;
|
|
22
|
+
};
|
|
23
|
+
/** One `<control>` element (inside the `<controls>` wrapper). */
|
|
24
|
+
export declare const buildControlElementXml: ({ spec, relId, shapeId, from, to, }: {
|
|
25
|
+
spec: EmbedFormControlSpec;
|
|
26
|
+
relId: string;
|
|
27
|
+
shapeId: number;
|
|
28
|
+
from: ControlCellAnchor;
|
|
29
|
+
to: ControlCellAnchor;
|
|
30
|
+
}) => string;
|
|
31
|
+
/**
|
|
32
|
+
* The worksheet `<controls>` element. Excel wraps controls in an
|
|
33
|
+
* `mc:AlternateContent` whose `Choice` requires the `x14` namespace; the
|
|
34
|
+
* namespaces the wrapper and the `xdr:` anchor cells need are declared on
|
|
35
|
+
* the element so the worksheet root stays untouched.
|
|
36
|
+
*/
|
|
37
|
+
export declare const buildControlsXml: (controlElements: string[]) => string;
|
|
38
|
+
/** VML `<v:shapetype>` every form control references (`#_x0000_t201`). */
|
|
39
|
+
export declare 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>";
|
|
40
|
+
/** Pixel geometry the VML shape needs (absolute position + cell-span anchor). */
|
|
41
|
+
export type FormControlVmlGeometry = {
|
|
42
|
+
leftPx: number;
|
|
43
|
+
topPx: number;
|
|
44
|
+
widthPx: number;
|
|
45
|
+
heightPx: number;
|
|
46
|
+
/** `x:Anchor` — from col, from col px offset, from row, from row px offset, to … */
|
|
47
|
+
anchor: [number, number, number, number, number, number, number, number];
|
|
48
|
+
};
|
|
49
|
+
/** One `<v:shape>` drawing a form control. */
|
|
50
|
+
export declare const buildFormControlVmlShape: ({ spec, shapeId, zIndex, geometry, }: {
|
|
51
|
+
spec: EmbedFormControlSpec;
|
|
52
|
+
shapeId: number;
|
|
53
|
+
zIndex: number;
|
|
54
|
+
geometry: FormControlVmlGeometry;
|
|
55
|
+
}) => string;
|
|
56
|
+
/** Wrap note + control shapes into one `vmlDrawing` part. */
|
|
57
|
+
export declare const buildVmlDrawingXml: ({ shapeTypes, shapes, }: {
|
|
58
|
+
shapeTypes: string[];
|
|
59
|
+
shapes: string[];
|
|
60
|
+
}) => string | undefined;
|
|
61
|
+
//# sourceMappingURL=form-controls-builder.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Excel form-control writers — the inverse of
|
|
3
|
+
* `excel-parser/plugins/xlsx/form-controls.ts`. A control is three parts:
|
|
4
|
+
* the worksheet `<controls><control>` entry (anchor + `<controlPr>`), its
|
|
5
|
+
* `xl/ctrlProps/ctrlPropN.xml` (`<formControlPr>`), and the legacy VML shape
|
|
6
|
+
* Excel actually draws (`<v:shape type="#_x0000_t201"><x:ClientData
|
|
7
|
+
* ObjectType="Checkbox">`). All three are written; Excel drops a control
|
|
8
|
+
* whose VML shape is missing.
|
|
9
|
+
*/
|
|
10
|
+
export const CTRL_PROP_CONTENT_TYPE = "application/vnd.ms-excel.controlproperties+xml";
|
|
11
|
+
export const CTRL_PROP_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp";
|
|
12
|
+
export const FORM_CONTROL_NS = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main";
|
|
13
|
+
const MC_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006";
|
|
14
|
+
const XDR_NS = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing";
|
|
15
|
+
const XML_DECLARATION = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n`;
|
|
16
|
+
const escapeAttr = (value) => value
|
|
17
|
+
.replace(/&/g, "&")
|
|
18
|
+
.replace(/</g, "<")
|
|
19
|
+
.replace(/>/g, ">")
|
|
20
|
+
.replace(/"/g, """);
|
|
21
|
+
const escapeText = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
22
|
+
const attrsXml = (attrs) => Object.entries(attrs ?? {})
|
|
23
|
+
.filter(([key, value]) => key && value != null)
|
|
24
|
+
.map(([key, value]) => ` ${key}="${escapeAttr(String(value))}"`)
|
|
25
|
+
.join("");
|
|
26
|
+
/** `xl/ctrlProps/ctrlPropN.xml` body. */
|
|
27
|
+
export const buildCtrlPropXml = (spec) => {
|
|
28
|
+
const { objectType: _ignored, ...rest } = spec.properties ?? {};
|
|
29
|
+
const items = (spec.listItems ?? [])
|
|
30
|
+
.map((item) => `<item val="${escapeAttr(item)}"/>`)
|
|
31
|
+
.join("");
|
|
32
|
+
const body = items ? `<itemLst>${items}</itemLst>` : "";
|
|
33
|
+
const open = `<formControlPr xmlns="${FORM_CONTROL_NS}" objectType="${escapeAttr(spec.objectType)}"${attrsXml(rest)}`;
|
|
34
|
+
return `${XML_DECLARATION}${body ? `${open}>${body}</formControlPr>` : `${open}/>`}`;
|
|
35
|
+
};
|
|
36
|
+
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
|
+
/** One `<control>` element (inside the `<controls>` wrapper). */
|
|
38
|
+
export const buildControlElementXml = ({ spec, relId, shapeId, from, to, }) => {
|
|
39
|
+
const name = spec.name ?? `${spec.objectType} ${shapeId}`;
|
|
40
|
+
const anchorAttrs = (spec.moveWithCells ? ' moveWithCells="1"' : "") +
|
|
41
|
+
(spec.sizeWithCells ? ' sizeWithCells="1"' : "");
|
|
42
|
+
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
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* The worksheet `<controls>` element. Excel wraps controls in an
|
|
46
|
+
* `mc:AlternateContent` whose `Choice` requires the `x14` namespace; the
|
|
47
|
+
* namespaces the wrapper and the `xdr:` anchor cells need are declared on
|
|
48
|
+
* the element so the worksheet root stays untouched.
|
|
49
|
+
*/
|
|
50
|
+
export const buildControlsXml = (controlElements) => {
|
|
51
|
+
if (!controlElements.length)
|
|
52
|
+
return "";
|
|
53
|
+
return `<controls xmlns:mc="${MC_NS}" xmlns:xdr="${XDR_NS}" xmlns:x14="${FORM_CONTROL_NS}"><mc:AlternateContent><mc:Choice Requires="x14">${controlElements.join("")}</mc:Choice></mc:AlternateContent></controls>`;
|
|
54
|
+
};
|
|
55
|
+
/** `formControlPr/@objectType` → VML `x:ClientData/@ObjectType`. */
|
|
56
|
+
const VML_OBJECT_TYPE = {
|
|
57
|
+
CheckBox: "Checkbox",
|
|
58
|
+
EditBox: "Edit",
|
|
59
|
+
};
|
|
60
|
+
const truthy = (value) => value === "1" || value === "true";
|
|
61
|
+
/**
|
|
62
|
+
* `x:ClientData` children derived from the `formControlPr` / `controlPr`
|
|
63
|
+
* attributes — Excel reads the control's state (checked, linked cell, list
|
|
64
|
+
* range, selection, scroll values) from the VML, the ctrlProp part is the
|
|
65
|
+
* x14 mirror.
|
|
66
|
+
*/
|
|
67
|
+
const clientDataChildrenXml = (spec) => {
|
|
68
|
+
const p = spec.properties ?? {};
|
|
69
|
+
const c = spec.controlProperties ?? {};
|
|
70
|
+
const out = [];
|
|
71
|
+
const push = (tag, value) => {
|
|
72
|
+
if (value === undefined)
|
|
73
|
+
out.push(`<x:${tag}/>`);
|
|
74
|
+
else
|
|
75
|
+
out.push(`<x:${tag}>${escapeText(value)}</x:${tag}>`);
|
|
76
|
+
};
|
|
77
|
+
if (spec.moveWithCells)
|
|
78
|
+
push("MoveWithCells");
|
|
79
|
+
if (spec.sizeWithCells)
|
|
80
|
+
push("SizeWithCells");
|
|
81
|
+
// Anchor is inserted by the caller (needs sheet geometry).
|
|
82
|
+
out.push("__ANCHOR__");
|
|
83
|
+
if (c.locked === "0")
|
|
84
|
+
push("Locked", "False");
|
|
85
|
+
if (c.defaultSize === "0")
|
|
86
|
+
push("DefaultSize", "False");
|
|
87
|
+
if (c.print === "0")
|
|
88
|
+
push("PrintObject", "False");
|
|
89
|
+
if (c.autoFill === "0")
|
|
90
|
+
push("AutoFill", "False");
|
|
91
|
+
if (c.autoLine === "0")
|
|
92
|
+
push("AutoLine", "False");
|
|
93
|
+
if (c.autoPict === "0")
|
|
94
|
+
push("AutoPict", "False");
|
|
95
|
+
if (c.macro)
|
|
96
|
+
push("FmlaMacro", c.macro);
|
|
97
|
+
if (p.textHAlign)
|
|
98
|
+
push("TextHAlign", p.textHAlign);
|
|
99
|
+
if (p.textVAlign)
|
|
100
|
+
push("TextVAlign", p.textVAlign);
|
|
101
|
+
if (truthy(p.lockText))
|
|
102
|
+
push("LockText");
|
|
103
|
+
if (truthy(p.justLastX))
|
|
104
|
+
push("JustLastX");
|
|
105
|
+
if (truthy(p.passwordEdit))
|
|
106
|
+
push("SecretEdit");
|
|
107
|
+
if (truthy(p.multiLine))
|
|
108
|
+
push("MultiLine");
|
|
109
|
+
if (truthy(p.verticalBar))
|
|
110
|
+
push("VScroll");
|
|
111
|
+
if (p.fmlaRange)
|
|
112
|
+
push("FmlaRange", p.fmlaRange);
|
|
113
|
+
if (p.widthMin)
|
|
114
|
+
push("WidthMin", p.widthMin);
|
|
115
|
+
if (p.sel)
|
|
116
|
+
push("Sel", p.sel);
|
|
117
|
+
if (p.seltype)
|
|
118
|
+
push("SelType", p.seltype);
|
|
119
|
+
if (p.multiSel)
|
|
120
|
+
push("MultiSel", p.multiSel);
|
|
121
|
+
if (p.dropStyle)
|
|
122
|
+
push("DropStyle", p.dropStyle);
|
|
123
|
+
if (truthy(p.colored))
|
|
124
|
+
push("Colored");
|
|
125
|
+
if (p.dropLines)
|
|
126
|
+
push("DropLines", p.dropLines);
|
|
127
|
+
if (p.checked === "Checked")
|
|
128
|
+
push("Checked", "1");
|
|
129
|
+
else if (p.checked === "Mixed")
|
|
130
|
+
push("Checked", "2");
|
|
131
|
+
if (p.fmlaLink)
|
|
132
|
+
push("FmlaLink", p.fmlaLink);
|
|
133
|
+
if (truthy(p.noThreeD))
|
|
134
|
+
push("NoThreeD");
|
|
135
|
+
if (truthy(p.firstButton))
|
|
136
|
+
push("FirstButton");
|
|
137
|
+
if (p.fmlaGroup)
|
|
138
|
+
push("FmlaGroup", p.fmlaGroup);
|
|
139
|
+
if (p.val)
|
|
140
|
+
push("Val", p.val);
|
|
141
|
+
if (p.min)
|
|
142
|
+
push("Min", p.min);
|
|
143
|
+
if (p.max)
|
|
144
|
+
push("Max", p.max);
|
|
145
|
+
if (p.inc)
|
|
146
|
+
push("Inc", p.inc);
|
|
147
|
+
if (p.page)
|
|
148
|
+
push("Page", p.page);
|
|
149
|
+
if (truthy(p.horiz))
|
|
150
|
+
push("Horiz");
|
|
151
|
+
if (p.dx)
|
|
152
|
+
push("Dx", p.dx);
|
|
153
|
+
return out.join("");
|
|
154
|
+
};
|
|
155
|
+
/** VML `<v:shapetype>` every form control references (`#_x0000_t201`). */
|
|
156
|
+
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
|
+
const toPt = (px) => `${(px * 0.75).toFixed(2).replace(/\.?0+$/, "")}pt`;
|
|
158
|
+
/** One `<v:shape>` drawing a form control. */
|
|
159
|
+
export const buildFormControlVmlShape = ({ spec, shapeId, zIndex, geometry, }) => {
|
|
160
|
+
const objectType = VML_OBJECT_TYPE[spec.objectType] ?? spec.objectType;
|
|
161
|
+
const caption = spec.text
|
|
162
|
+
? `<div style="text-align:left"><font face="Tahoma" size="160" color="auto">${escapeText(spec.text)}</font></div>`
|
|
163
|
+
: "";
|
|
164
|
+
const clientData = clientDataChildrenXml(spec).replace("__ANCHOR__", `<x:Anchor>${geometry.anchor.join(", ")}</x:Anchor>`);
|
|
165
|
+
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
|
+
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
|
+
};
|
|
168
|
+
/** Wrap note + control shapes into one `vmlDrawing` part. */
|
|
169
|
+
export const buildVmlDrawingXml = ({ shapeTypes, shapes, }) => {
|
|
170
|
+
if (!shapes.length)
|
|
171
|
+
return undefined;
|
|
172
|
+
return `<xml xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel"><o:shapelayout v:ext="edit"><o:idmap v:ext="edit" data="1"/></o:shapelayout>${shapeTypes.join("")}${shapes.join("")}</xml>`;
|
|
173
|
+
};
|
|
@@ -26,6 +26,14 @@
|
|
|
26
26
|
* + ~100 lines in xlsx-export.ts) is the closest reference template.
|
|
27
27
|
*/
|
|
28
28
|
import type { PivotTable } from "@rowsncolumns/spreadsheet";
|
|
29
|
+
/**
|
|
30
|
+
* Distinct source fields a calculated-field formula references —
|
|
31
|
+
* single-quoted names (`'Unit Price'`, `''` escapes a quote) or bare
|
|
32
|
+
* identifiers; numeric literals (incl. `1e5`) and operators are skipped.
|
|
33
|
+
* Mirrors `@rowsncolumns/pivot`'s tokenizer without pulling in the
|
|
34
|
+
* DuckDB-backed pivot package.
|
|
35
|
+
*/
|
|
36
|
+
export declare const calculatedFieldReferences: (formula: string) => string[];
|
|
29
37
|
/**
|
|
30
38
|
* Build pivotCacheDefinition XML for a pivot. The cacheSource
|
|
31
39
|
* points at the live worksheet range; `refreshOnLoad="1"` makes
|
|
@@ -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,
|
|
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"}
|