@extend-ai/react-xlsx 0.14.1 → 0.15.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.
package/dist/index.js CHANGED
@@ -1956,7 +1956,7 @@ function normalizeChartExLegend(raw) {
1956
1956
  return null;
1957
1957
  }
1958
1958
  const legend = raw;
1959
- const position = typeof legend.pos === "string" ? normalizeLegendPosition(String(legend.pos)) : void 0;
1959
+ const position = typeof legend.position === "string" ? normalizeLegendPosition(String(legend.position)) : void 0;
1960
1960
  return {
1961
1961
  overlay: typeof legend.overlay === "boolean" ? legend.overlay : void 0,
1962
1962
  position,
@@ -2077,11 +2077,11 @@ function buildChartExHistogramBins(values, rawSeries, sortByFrequency) {
2077
2077
  return [];
2078
2078
  }
2079
2079
  const rawRecord = rawSeries && typeof rawSeries === "object" ? rawSeries : null;
2080
- const layoutProperties = rawRecord?.layoutPr && typeof rawRecord.layoutPr === "object" ? rawRecord.layoutPr : null;
2080
+ const layoutProperties = rawRecord?.layoutProperties && typeof rawRecord.layoutProperties === "object" ? rawRecord.layoutProperties : null;
2081
2081
  const rawBinning = layoutProperties?.binning && typeof layoutProperties.binning === "object" ? layoutProperties.binning : null;
2082
2082
  const minValue = Math.min(...values);
2083
2083
  const maxValue = Math.max(...values);
2084
- const explicitWidth = typeof rawBinning?.binWidth === "number" && Number.isFinite(rawBinning.binWidth) && rawBinning.binWidth > 0 ? rawBinning.binWidth : typeof rawBinning?.width === "number" && Number.isFinite(rawBinning.width) && rawBinning.width > 0 ? rawBinning.width : void 0;
2084
+ const explicitWidth = typeof rawBinning?.binSize === "number" && Number.isFinite(rawBinning.binSize) && rawBinning.binSize > 0 ? rawBinning.binSize : void 0;
2085
2085
  const explicitCount = typeof rawBinning?.binCount === "number" && Number.isFinite(rawBinning.binCount) && rawBinning.binCount > 0 ? rawBinning.binCount : typeof rawBinning?.count === "number" && Number.isFinite(rawBinning.count) && rawBinning.count > 0 ? rawBinning.count : void 0;
2086
2086
  const closedRight = rawBinning?.intervalClosed === "r" || rawBinning?.intervalClosed === "right";
2087
2087
  const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
@@ -2134,7 +2134,7 @@ function buildChartExHistogramSeries(series, rawSeries, sortByFrequency) {
2134
2134
  const layout = resolveChartExSeriesLayout(rawSeries);
2135
2135
  const rawRecord = rawSeries && typeof rawSeries === "object" ? rawSeries : null;
2136
2136
  const hasBinning = Boolean(
2137
- layout === "clusteredColumn" && rawRecord?.layoutPr && typeof rawRecord.layoutPr === "object" && rawRecord.layoutPr.binning != null
2137
+ layout === "clusteredColumn" && rawRecord?.layoutProperties && typeof rawRecord.layoutProperties === "object" && rawRecord.layoutProperties.binning != null
2138
2138
  );
2139
2139
  if (!hasBinning) {
2140
2140
  return series;
@@ -2346,7 +2346,8 @@ function collapseChartExPointSeries(chartType, series) {
2346
2346
  ];
2347
2347
  }
2348
2348
  function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex, raw, index, themePalette) {
2349
- const chart = raw && typeof raw === "object" ? raw : {};
2349
+ const drawing = raw && typeof raw === "object" ? raw : {};
2350
+ const chart = drawing.chartEx && typeof drawing.chartEx === "object" ? drawing.chartEx : {};
2350
2351
  const plotArea = chart.plotArea && typeof chart.plotArea === "object" ? chart.plotArea : {};
2351
2352
  const rawSeries = Array.isArray(plotArea.series) ? plotArea.series : [];
2352
2353
  const seriesLayouts = rawSeries.map(resolveChartExSeriesLayout);
@@ -2401,7 +2402,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2401
2402
  }
2402
2403
  ] : [];
2403
2404
  const normalizedChart = {
2404
- anchor: normalizeChartAnchor(chart.anchor),
2405
+ anchor: normalizeChartAnchor(drawing.anchor),
2405
2406
  autoTitleDeleted: void 0,
2406
2407
  axes,
2407
2408
  axisLabelColor: void 0,
@@ -2425,7 +2426,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2425
2426
  id: `chart-ex-${workbookSheetIndex}-${index}`,
2426
2427
  is3d: void 0,
2427
2428
  legend: normalizeChartExLegend(chart.legend),
2428
- name: chartTitle,
2429
+ name: typeof drawing.name === "string" ? drawing.name : chartTitle,
2429
2430
  overlap: void 0,
2430
2431
  plotVisibleOnly: void 0,
2431
2432
  raw: chart,
@@ -2453,7 +2454,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2453
2454
  view3d: void 0,
2454
2455
  wireframe: void 0,
2455
2456
  workbookSheetIndex,
2456
- zIndex: index
2457
+ zIndex: Array.isArray(drawing.drawingPath) && typeof drawing.drawingPath[0] === "number" ? drawing.drawingPath[0] + 1 : index + 1
2457
2458
  };
2458
2459
  applyBuiltinChartDefaults(normalizedChart, themePalette);
2459
2460
  return normalizedChart;
@@ -2574,10 +2575,11 @@ function normalizeChartReference(raw) {
2574
2575
  return null;
2575
2576
  }
2576
2577
  const record = raw;
2578
+ const values = Array.isArray(record.numbers) ? record.numbers : Array.isArray(record.strings) ? record.strings : void 0;
2577
2579
  return {
2578
2580
  formula: typeof record.formula === "string" ? record.formula : void 0,
2579
2581
  refType: typeof record.refType === "string" ? record.refType : void 0,
2580
- values: Array.isArray(record.values) ? record.values : void 0
2582
+ values
2581
2583
  };
2582
2584
  }
2583
2585
  function normalizeChartAxis(raw) {
@@ -2597,8 +2599,8 @@ function normalizeChartAxis(raw) {
2597
2599
  logBase: typeof axis.logBase === "number" ? axis.logBase : void 0,
2598
2600
  orientation: typeof axis.orientation === "string" ? axis.orientation : void 0,
2599
2601
  majorUnit: typeof axis.majorUnit === "number" ? axis.majorUnit : void 0,
2600
- max: typeof axis.max === "number" ? axis.max : void 0,
2601
- min: typeof axis.min === "number" ? axis.min : void 0,
2602
+ max: typeof axis.maximum === "number" ? axis.maximum : void 0,
2603
+ min: typeof axis.minimum === "number" ? axis.minimum : void 0,
2602
2604
  majorGridlines: typeof axis.majorGridlines === "boolean" ? axis.majorGridlines : void 0,
2603
2605
  majorTickMark: typeof axis.majorTickMark === "string" ? axis.majorTickMark : void 0,
2604
2606
  minorUnit: typeof axis.minorUnit === "number" ? axis.minorUnit : void 0,
@@ -2721,11 +2723,11 @@ function normalizeChartDataLabels(raw) {
2721
2723
  pointLabels: pointLabels && pointLabels.length > 0 ? pointLabels : void 0,
2722
2724
  raw: labels,
2723
2725
  showBubbleSize: typeof labels.showBubbleSize === "boolean" ? labels.showBubbleSize : void 0,
2724
- showCategoryName: typeof labels.showCategoryName === "boolean" ? labels.showCategoryName : void 0,
2726
+ showCategoryName: typeof (labels.showCategoryName ?? labels.visibilityCategoryName) === "boolean" ? Boolean(labels.showCategoryName ?? labels.visibilityCategoryName) : void 0,
2725
2727
  showLegendKey: typeof labels.showLegendKey === "boolean" ? labels.showLegendKey : void 0,
2726
2728
  showPercent: typeof labels.showPercent === "boolean" ? labels.showPercent : void 0,
2727
- showSeriesName: typeof labels.showSeriesName === "boolean" ? labels.showSeriesName : void 0,
2728
- showValue: typeof labels.showValue === "boolean" ? labels.showValue : void 0
2729
+ showSeriesName: typeof (labels.showSeriesName ?? labels.visibilitySeriesName) === "boolean" ? Boolean(labels.showSeriesName ?? labels.visibilitySeriesName) : void 0,
2730
+ showValue: typeof (labels.showValue ?? labels.visibilityValue) === "boolean" ? Boolean(labels.showValue ?? labels.visibilityValue) : void 0
2729
2731
  };
2730
2732
  }
2731
2733
  function normalizeChartAnchor(raw) {
@@ -2737,19 +2739,52 @@ function normalizeChartAnchor(raw) {
2737
2739
  };
2738
2740
  }
2739
2741
  const anchor = raw;
2740
- const fromCol = typeof anchor.fromCol === "number" ? anchor.fromCol : 0;
2741
- const fromColOffsetEmu = typeof anchor.fromColOffset === "number" ? anchor.fromColOffset : 0;
2742
- const fromRow = typeof anchor.fromRow === "number" ? anchor.fromRow : 0;
2743
- const fromRowOffsetEmu = typeof anchor.fromRowOffset === "number" ? anchor.fromRowOffset : 0;
2744
- const rawToCol = typeof anchor.toCol === "number" ? anchor.toCol : null;
2745
- const rawToColOffsetEmu = typeof anchor.toColOffset === "number" ? anchor.toColOffset : 0;
2746
- const rawToRow = typeof anchor.toRow === "number" ? anchor.toRow : null;
2747
- const rawToRowOffsetEmu = typeof anchor.toRowOffset === "number" ? anchor.toRowOffset : 0;
2748
- const hasExplicitTo = rawToCol !== null && rawToRow !== null;
2749
- const collapsedWidth = hasExplicitTo && (rawToCol < fromCol || rawToCol === fromCol && rawToColOffsetEmu <= fromColOffsetEmu);
2750
- const collapsedHeight = hasExplicitTo && (rawToRow < fromRow || rawToRow === fromRow && rawToRowOffsetEmu <= fromRowOffsetEmu);
2751
- const fallbackToCol = Math.max(fromCol + 8, 8);
2752
- const fallbackToRow = Math.max(fromRow + 15, 15);
2742
+ const from = anchor.from && typeof anchor.from === "object" ? anchor.from : null;
2743
+ if (anchor.type === "oneCell") {
2744
+ return {
2745
+ from: {
2746
+ col: typeof from?.col === "number" ? from.col : 0,
2747
+ colOffsetEmu: typeof from?.colOffsetEmu === "number" ? from.colOffsetEmu : 0,
2748
+ row: typeof from?.row === "number" ? from.row : 0,
2749
+ rowOffsetEmu: typeof from?.rowOffsetEmu === "number" ? from.rowOffsetEmu : 0
2750
+ },
2751
+ kind: "one-cell",
2752
+ sizeEmu: {
2753
+ cx: typeof anchor.widthEmu === "number" ? anchor.widthEmu : 0,
2754
+ cy: typeof anchor.heightEmu === "number" ? anchor.heightEmu : 0
2755
+ }
2756
+ };
2757
+ }
2758
+ if (anchor.type === "absolute") {
2759
+ return {
2760
+ kind: "absolute",
2761
+ positionEmu: {
2762
+ x: typeof anchor.xEmu === "number" ? anchor.xEmu : 0,
2763
+ y: typeof anchor.yEmu === "number" ? anchor.yEmu : 0
2764
+ },
2765
+ sizeEmu: {
2766
+ cx: typeof anchor.widthEmu === "number" ? anchor.widthEmu : 0,
2767
+ cy: typeof anchor.heightEmu === "number" ? anchor.heightEmu : 0
2768
+ }
2769
+ };
2770
+ }
2771
+ const to = anchor.to && typeof anchor.to === "object" ? anchor.to : null;
2772
+ const fromColValue = from?.col;
2773
+ const fromColOffsetValue = from?.colOffsetEmu;
2774
+ const fromRowValue = from?.row;
2775
+ const fromRowOffsetValue = from?.rowOffsetEmu;
2776
+ const toColValue = to?.col;
2777
+ const toColOffsetValue = to?.colOffsetEmu;
2778
+ const toRowValue = to?.row;
2779
+ const toRowOffsetValue = to?.rowOffsetEmu;
2780
+ const fromCol = typeof fromColValue === "number" ? fromColValue : 0;
2781
+ const fromColOffsetEmu = typeof fromColOffsetValue === "number" ? fromColOffsetValue : 0;
2782
+ const fromRow = typeof fromRowValue === "number" ? fromRowValue : 0;
2783
+ const fromRowOffsetEmu = typeof fromRowOffsetValue === "number" ? fromRowOffsetValue : 0;
2784
+ const rawToCol = typeof toColValue === "number" ? toColValue : 0;
2785
+ const rawToColOffsetEmu = typeof toColOffsetValue === "number" ? toColOffsetValue : 0;
2786
+ const rawToRow = typeof toRowValue === "number" ? toRowValue : 0;
2787
+ const rawToRowOffsetEmu = typeof toRowOffsetValue === "number" ? toRowOffsetValue : 0;
2753
2788
  return {
2754
2789
  kind: "two-cell",
2755
2790
  from: {
@@ -2759,10 +2794,10 @@ function normalizeChartAnchor(raw) {
2759
2794
  rowOffsetEmu: fromRowOffsetEmu
2760
2795
  },
2761
2796
  to: {
2762
- col: !hasExplicitTo || collapsedWidth ? fallbackToCol : rawToCol,
2763
- colOffsetEmu: !hasExplicitTo || collapsedWidth ? 0 : rawToColOffsetEmu,
2764
- row: !hasExplicitTo || collapsedHeight ? fallbackToRow : rawToRow,
2765
- rowOffsetEmu: !hasExplicitTo || collapsedHeight ? 0 : rawToRowOffsetEmu
2797
+ col: rawToCol,
2798
+ colOffsetEmu: rawToColOffsetEmu,
2799
+ row: rawToRow,
2800
+ rowOffsetEmu: rawToRowOffsetEmu
2766
2801
  }
2767
2802
  };
2768
2803
  }
@@ -2876,7 +2911,7 @@ function normalizeChartTypeGroup(workbook, workbookSheetIndex, chartId, raw, ind
2876
2911
  chartType: typeof group.chartType === "string" ? group.chartType : "ColumnClustered",
2877
2912
  dataLabels: normalizeChartDataLabels(group.dataLabels),
2878
2913
  gapWidth: typeof group.gapWidth === "number" && Number.isFinite(group.gapWidth) ? group.gapWidth : void 0,
2879
- is3d: typeof group.is3d === "boolean" ? group.is3d : void 0,
2914
+ is3d: typeof group.is3D === "boolean" ? group.is3D : void 0,
2880
2915
  overlap: typeof group.overlap === "number" && Number.isFinite(group.overlap) ? group.overlap : void 0,
2881
2916
  raw: group,
2882
2917
  series: rawSeries.map((entry, seriesIndex) => normalizeChartSeries(workbook, workbookSheetIndex, `${chartId}-group-${index}`, entry, seriesIndex)),
@@ -3030,20 +3065,25 @@ function applyChartOrigins(chartsByWorkbookSheetIndex, chartOriginsById, archive
3030
3065
  }
3031
3066
  }
3032
3067
  function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkbookSheetIndex, showHiddenSheets = false) {
3068
+ const excludedChartIds = /* @__PURE__ */ new Set();
3033
3069
  const chartsByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
3034
3070
  const worksheet = workbook.getSheet(workbookSheetIndex);
3035
- const rawCharts = Array.isArray(worksheet.charts) ? worksheet.charts : [];
3036
- const rawChartsEx = Array.isArray(worksheet.chartsEx) ? worksheet.chartsEx : [];
3071
+ const rawCharts = worksheet.charts;
3072
+ const rawChartsEx = worksheet.chartsEx;
3037
3073
  const visibleSheetIndex = visibleSheetIndexByWorkbookSheetIndex.get(workbookSheetIndex) ?? workbookSheetIndex;
3038
3074
  const classicCharts = rawCharts.map((rawChart, chartIndex) => {
3039
3075
  const chartId = `chart-${workbookSheetIndex}-${chartIndex}`;
3040
- const chart = rawChart && typeof rawChart === "object" ? rawChart : {};
3041
- const rawView3d = chart.view3d && typeof chart.view3d === "object" ? chart.view3d : null;
3076
+ if (rawChart.hidden || !rawChart.anchor) {
3077
+ excludedChartIds.add(chartId);
3078
+ }
3079
+ const drawing = rawChart && typeof rawChart === "object" ? rawChart : {};
3080
+ const chart = drawing.chart && typeof drawing.chart === "object" ? drawing.chart : {};
3081
+ const rawView3d = chart.view3D && typeof chart.view3D === "object" ? chart.view3D : null;
3042
3082
  const rawSeries = Array.isArray(chart.series) ? chart.series : [];
3043
3083
  const chartLevelDataLabels = normalizeChartDataLabels(chart.dataLabels);
3044
3084
  const firstSeriesDataLabels = rawSeries.length > 0 && rawSeries[0] && typeof rawSeries[0] === "object" ? normalizeChartDataLabels(rawSeries[0].dataLabels) : null;
3045
3085
  return {
3046
- anchor: normalizeChartAnchor(chart.anchor),
3086
+ anchor: normalizeChartAnchor(drawing.anchor),
3047
3087
  autoTitleDeleted: typeof chart.autoTitleDeleted === "boolean" ? chart.autoTitleDeleted : void 0,
3048
3088
  axes: Array.isArray(chart.axes) ? chart.axes.map(normalizeChartAxis).filter((value) => Boolean(value)) : [],
3049
3089
  axisLabelColor: void 0,
@@ -3064,12 +3104,12 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3064
3104
  gapWidth: typeof chart.gapWidth === "number" ? chart.gapWidth : void 0,
3065
3105
  holeSize: typeof chart.holeSize === "number" ? chart.holeSize : void 0,
3066
3106
  id: chartId,
3067
- is3d: typeof chart.is3d === "boolean" ? chart.is3d : void 0,
3107
+ is3d: typeof chart.is3D === "boolean" ? chart.is3D : void 0,
3068
3108
  legend: normalizeLegend(chart.legend) ? {
3069
3109
  ...normalizeLegend(chart.legend),
3070
3110
  position: normalizeLegendPosition(normalizeLegend(chart.legend)?.position)
3071
3111
  } : null,
3072
- name: typeof chart.name === "string" ? chart.name : void 0,
3112
+ name: typeof drawing.name === "string" ? drawing.name : void 0,
3073
3113
  overlap: typeof chart.overlap === "number" ? chart.overlap : void 0,
3074
3114
  plotVisibleOnly: typeof chart.plotVisibleOnly === "boolean" ? chart.plotVisibleOnly : void 0,
3075
3115
  raw: chart,
@@ -3103,17 +3143,23 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3103
3143
  } : void 0,
3104
3144
  wireframe: typeof chart.wireframe === "boolean" ? chart.wireframe : void 0,
3105
3145
  workbookSheetIndex,
3106
- zIndex: 200 + chartIndex
3146
+ zIndex: Array.isArray(drawing.drawingPath) && typeof drawing.drawingPath[0] === "number" ? drawing.drawingPath[0] + 1 : chartIndex + 1
3107
3147
  };
3108
3148
  });
3109
- const modernCharts = rawChartsEx.map((rawChartEx, chartExIndex) => normalizeChartExChart(
3110
- workbook,
3111
- workbookSheetIndex,
3112
- visibleSheetIndex,
3113
- rawChartEx,
3114
- chartExIndex,
3115
- imageAssets?.themePalette ?? null
3116
- ));
3149
+ const modernCharts = rawChartsEx.map((rawChartEx, chartExIndex) => {
3150
+ const chartId = `chart-ex-${workbookSheetIndex}-${chartExIndex}`;
3151
+ if (rawChartEx.hidden || !rawChartEx.anchor) {
3152
+ excludedChartIds.add(chartId);
3153
+ }
3154
+ return normalizeChartExChart(
3155
+ workbook,
3156
+ workbookSheetIndex,
3157
+ visibleSheetIndex,
3158
+ rawChartEx,
3159
+ chartExIndex,
3160
+ imageAssets?.themePalette ?? null
3161
+ );
3162
+ });
3117
3163
  return [...classicCharts, ...modernCharts];
3118
3164
  });
3119
3165
  const chartsheets = Array.isArray(workbook.chartsheets) ? workbook.chartsheets.map((entry, index) => normalizeChartsheet(entry, index)) : [];
@@ -3121,6 +3167,12 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3121
3167
  const chartOriginsById = /* @__PURE__ */ new Map();
3122
3168
  if (imageAssets) {
3123
3169
  applyChartOrigins(chartsByWorkbookSheetIndex, chartOriginsById, imageAssets.archive, imageAssets.sheetOrigins);
3170
+ for (let index = 0; index < chartsByWorkbookSheetIndex.length; index += 1) {
3171
+ chartsByWorkbookSheetIndex[index] = (chartsByWorkbookSheetIndex[index] ?? []).filter((chart) => !excludedChartIds.has(chart.id));
3172
+ }
3173
+ for (const id of excludedChartIds) {
3174
+ chartOriginsById.delete(id);
3175
+ }
3124
3176
  for (const charts of chartsByWorkbookSheetIndex) {
3125
3177
  for (const chart of charts) {
3126
3178
  applyChartStyleFromXml(chart, chart.chartPath, imageAssets.archive, imageAssets.themePalette);
@@ -3128,6 +3180,9 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3128
3180
  }
3129
3181
  }
3130
3182
  } else {
3183
+ for (let index = 0; index < chartsByWorkbookSheetIndex.length; index += 1) {
3184
+ chartsByWorkbookSheetIndex[index] = (chartsByWorkbookSheetIndex[index] ?? []).filter((chart) => !excludedChartIds.has(chart.id));
3185
+ }
3131
3186
  for (const charts of chartsByWorkbookSheetIndex) {
3132
3187
  for (const chart of charts) {
3133
3188
  applyBuiltinChartDefaults(chart, null);
@@ -3360,6 +3415,7 @@ function updateWorkbookChartAnchor(imageAssets, chartAssets, chartId, anchor) {
3360
3415
  }
3361
3416
  updateAnchorNode(anchorNode, anchor);
3362
3417
  imageAssets.archive[normalizeArchivePath(origin.drawingPath)] = strToU8(serializeXml(drawingDocument));
3418
+ imageAssets.dirtyArchivePaths.add(normalizeArchivePath(origin.drawingPath));
3363
3419
  return true;
3364
3420
  }
3365
3421
  function updateWorkbookChartDefinition(imageAssets, chartAssets, chartId, patch) {
@@ -3414,6 +3470,7 @@ function updateWorkbookChartDefinition(imageAssets, chartAssets, chartId, patch)
3414
3470
  );
3415
3471
  updateAxisNode(getLocalChildren(plotAreaNode, "valAx")[0] ?? null, patch.valueAxis);
3416
3472
  imageAssets.archive[normalizeArchivePath(origin.chartPath)] = strToU8(serializeXml(chartDocument));
3473
+ imageAssets.dirtyArchivePaths.add(normalizeArchivePath(origin.chartPath));
3417
3474
  return true;
3418
3475
  }
3419
3476
 
@@ -3591,7 +3648,6 @@ var PKG_REL_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
3591
3648
  var SPREADSHEET_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
3592
3649
  var CONTENT_TYPES_NS = "http://schemas.openxmlformats.org/package/2006/content-types";
3593
3650
  var DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing";
3594
- var VML_DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing";
3595
3651
  var IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
3596
3652
  var DRAWING_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawing+xml";
3597
3653
  var EMU_PER_PIXEL2 = 9525;
@@ -5018,71 +5074,174 @@ function parseMarker(node) {
5018
5074
  rowOffsetEmu
5019
5075
  };
5020
5076
  }
5021
- function parseSpreadsheetBooleanValue(value) {
5022
- if (value === null || value === void 0) {
5077
+ function dukeDrawingAnchorToXlsxAnchor(anchor) {
5078
+ if (anchor.type === "absolute") {
5079
+ return {
5080
+ kind: "absolute",
5081
+ positionEmu: { x: anchor.xEmu, y: anchor.yEmu },
5082
+ sizeEmu: { cx: anchor.widthEmu, cy: anchor.heightEmu }
5083
+ };
5084
+ }
5085
+ const from = {
5086
+ col: anchor.from.col,
5087
+ colOffsetEmu: anchor.from.colOffsetEmu ?? 0,
5088
+ row: anchor.from.row,
5089
+ rowOffsetEmu: anchor.from.rowOffsetEmu ?? 0
5090
+ };
5091
+ if (anchor.type === "oneCell") {
5092
+ return {
5093
+ from,
5094
+ kind: "one-cell",
5095
+ sizeEmu: { cx: anchor.widthEmu, cy: anchor.heightEmu }
5096
+ };
5097
+ }
5098
+ return {
5099
+ from,
5100
+ kind: "two-cell",
5101
+ to: {
5102
+ col: anchor.to.col,
5103
+ colOffsetEmu: anchor.to.colOffsetEmu ?? 0,
5104
+ row: anchor.to.row,
5105
+ rowOffsetEmu: anchor.to.rowOffsetEmu ?? 0
5106
+ }
5107
+ };
5108
+ }
5109
+ function xlsxAnchorToDukeDrawingAnchor(anchor, editAs) {
5110
+ if (anchor.kind === "absolute") {
5111
+ return {
5112
+ heightEmu: anchor.sizeEmu.cy,
5113
+ type: "absolute",
5114
+ widthEmu: anchor.sizeEmu.cx,
5115
+ xEmu: anchor.positionEmu.x,
5116
+ yEmu: anchor.positionEmu.y
5117
+ };
5118
+ }
5119
+ const from = {
5120
+ col: anchor.from.col,
5121
+ colOffsetEmu: anchor.from.colOffsetEmu,
5122
+ row: anchor.from.row,
5123
+ rowOffsetEmu: anchor.from.rowOffsetEmu
5124
+ };
5125
+ if (anchor.kind === "one-cell") {
5126
+ return {
5127
+ from,
5128
+ heightEmu: anchor.sizeEmu.cy,
5129
+ type: "oneCell",
5130
+ widthEmu: anchor.sizeEmu.cx
5131
+ };
5132
+ }
5133
+ return {
5134
+ editAs,
5135
+ from,
5136
+ to: {
5137
+ col: anchor.to.col,
5138
+ colOffsetEmu: anchor.to.colOffsetEmu,
5139
+ row: anchor.to.row,
5140
+ rowOffsetEmu: anchor.to.rowOffsetEmu
5141
+ },
5142
+ type: "twoCell"
5143
+ };
5144
+ }
5145
+ function mapDukeDrawingColor(color) {
5146
+ if (!color) {
5023
5147
  return void 0;
5024
5148
  }
5025
- const normalized = value.trim().toLowerCase();
5026
- if (!normalized) {
5027
- return true;
5149
+ switch (color.colorType) {
5150
+ case "auto":
5151
+ return { colorType: "auto" };
5152
+ case "rgb":
5153
+ return { b: color.b, colorType: "rgb", g: color.g, r: color.r };
5154
+ case "argb":
5155
+ return { a: color.a, b: color.b, colorType: "argb", g: color.g, r: color.r };
5156
+ case "theme":
5157
+ return { colorType: "theme", themeIndex: color.index, tint: color.tint };
5158
+ case "indexed":
5159
+ return { colorType: "indexed", paletteIndex: color.index };
5160
+ }
5161
+ }
5162
+ function mapDukeDrawingFont(font) {
5163
+ if (!font) {
5164
+ return void 0;
5028
5165
  }
5029
- return !["0", "false", "none", "off", "unchecked"].includes(normalized);
5166
+ return {
5167
+ bold: font.bold,
5168
+ charset: font.charset,
5169
+ color: mapDukeDrawingColor(font.color),
5170
+ family: font.family,
5171
+ italic: font.italic,
5172
+ name: font.name,
5173
+ scheme: font.scheme,
5174
+ size: font.size,
5175
+ strikethrough: font.strikethrough,
5176
+ underline: font.underline,
5177
+ verticalAlign: font.verticalAlign
5178
+ };
5030
5179
  }
5031
- function parseSpreadsheetBooleanNode(node) {
5032
- if (!node) {
5180
+ function mapDukeDrawingText(text) {
5181
+ return {
5182
+ horizontalAlignment: text.horizontalAlignment,
5183
+ runs: text.runs.map((run) => ({ font: mapDukeDrawingFont(run.font), text: run.text })),
5184
+ verticalAlignment: text.verticalAlignment
5185
+ };
5186
+ }
5187
+ function resolveDukeDrawingColor(color, themePalette) {
5188
+ if (!color) {
5033
5189
  return void 0;
5034
5190
  }
5035
- return parseSpreadsheetBooleanValue(node.getAttribute("val") ?? node.textContent);
5191
+ switch (color.colorType) {
5192
+ case "rgb":
5193
+ return resolveWorkbookColor({ rgb: [color.r, color.g, color.b].map((value) => value.toString(16).padStart(2, "0")).join("") }) ?? void 0;
5194
+ case "argb":
5195
+ return resolveWorkbookColor({ argb: [color.a, color.r, color.g, color.b].map((value) => value.toString(16).padStart(2, "0")).join("") }) ?? void 0;
5196
+ case "theme":
5197
+ return resolveWorkbookColor({ theme: color.index, tint: color.tint }, themePalette) ?? void 0;
5198
+ default:
5199
+ return void 0;
5200
+ }
5036
5201
  }
5037
- function mapDukeFormControlAnchor(anchor) {
5202
+ function resolveDukeDrawingText(text, themePalette) {
5203
+ const caption = mapDukeDrawingText(text);
5204
+ const label = normalizeControlLabel(text.runs.map((run) => run.text).join(""));
5205
+ const firstStyledRun = text.runs.find((run) => run.font)?.font;
5206
+ const horizontalAlignment = text.horizontalAlignment;
5038
5207
  return {
5039
- from: {
5040
- col: anchor.fromCol,
5041
- colOffsetEmu: anchor.fromColOffset,
5042
- row: anchor.fromRow,
5043
- rowOffsetEmu: anchor.fromRowOffset
5044
- },
5045
- kind: "two-cell",
5046
- to: {
5047
- col: anchor.toCol,
5048
- colOffsetEmu: anchor.toColOffset,
5049
- row: anchor.toRow,
5050
- rowOffsetEmu: anchor.toRowOffset
5051
- }
5208
+ caption,
5209
+ fontFamily: firstStyledRun?.name,
5210
+ fontSizePt: firstStyledRun?.size,
5211
+ label,
5212
+ textAlign: horizontalAlignment === "left" || horizontalAlignment === "center" || horizontalAlignment === "right" ? horizontalAlignment : void 0,
5213
+ textColor: resolveDukeDrawingColor(firstStyledRun?.color, themePalette)
5052
5214
  };
5053
5215
  }
5054
- function mapDukeFormControlKind(kind) {
5216
+ function mapDukeFormControlKind(kind, themePalette) {
5055
5217
  switch (kind.kind) {
5056
5218
  case "button":
5057
- return { caption: kind.caption, kind: "button", label: normalizeControlLabel(kind.caption) };
5219
+ return { ...resolveDukeDrawingText(kind.caption, themePalette), kind: "button" };
5058
5220
  case "checkbox":
5059
5221
  return {
5060
- caption: kind.caption,
5222
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5061
5223
  checked: kind.state === "checked",
5062
5224
  kind: "checkbox",
5063
- label: normalizeControlLabel(kind.caption),
5064
5225
  linkedCell: kind.cellLink,
5065
5226
  no3D: kind.no3D,
5066
5227
  state: kind.state
5067
5228
  };
5068
5229
  case "optionButton":
5069
5230
  return {
5070
- caption: kind.caption,
5231
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5071
5232
  checked: kind.state === "checked",
5072
5233
  firstInGroup: kind.firstInGroup,
5073
5234
  kind: "radio",
5074
- label: normalizeControlLabel(kind.caption),
5075
5235
  linkedCell: kind.cellLink,
5076
5236
  no3D: kind.no3D,
5077
5237
  state: kind.state
5078
5238
  };
5079
5239
  case "label":
5080
- return { caption: kind.caption, kind: "label", label: normalizeControlLabel(kind.caption) };
5240
+ return { ...resolveDukeDrawingText(kind.caption, themePalette), kind: "label" };
5081
5241
  case "groupBox":
5082
5242
  return {
5083
- caption: kind.caption,
5243
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5084
5244
  kind: "group-box",
5085
- label: normalizeControlLabel(kind.caption),
5086
5245
  no3D: kind.no3D
5087
5246
  };
5088
5247
  case "listBox":
@@ -5123,92 +5282,47 @@ function mapDukeFormControlKind(kind) {
5123
5282
  min: kind.min,
5124
5283
  value: kind.value
5125
5284
  };
5285
+ case "unknown":
5286
+ return {
5287
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5288
+ kind: kind.objectType.toLowerCase() === "editbox" ? "editbox" : "unknown",
5289
+ legacyObjectType: kind.legacyObjectType,
5290
+ objectType: kind.objectType,
5291
+ rawObj: kind.rawObj,
5292
+ rawProperties: kind.rawProperties
5293
+ };
5126
5294
  }
5127
5295
  }
5128
- function mapDukeFormControl(control, controlIndex, workbookSheetIndex) {
5296
+ function mapDukeFormControl(control, controlIndex, workbookSheetIndex, themePalette) {
5129
5297
  return {
5130
- anchor: mapDukeFormControlAnchor(control.anchor),
5298
+ altText: control.altText,
5299
+ anchor: dukeDrawingAnchorToXlsxAnchor(control.anchor),
5131
5300
  controlIndex,
5132
- dukeAnchor: { ...control.anchor },
5133
- editAs: control.anchor.editAs,
5134
- hidden: false,
5301
+ editAs: control.anchor.type === "twoCell" ? control.anchor.editAs : void 0,
5302
+ hidden: control.hidden,
5135
5303
  id: `form-control-${workbookSheetIndex}-${controlIndex}`,
5136
5304
  locked: control.locked,
5305
+ macroName: control.formControl.macroName,
5137
5306
  name: control.name,
5138
5307
  printable: control.printable,
5308
+ rawClientData: control.formControl.rawClientData,
5139
5309
  sheetIndex: workbookSheetIndex,
5310
+ title: control.title,
5140
5311
  workbookSheetIndex,
5141
- zIndex: controlIndex + 1,
5142
- ...mapDukeFormControlKind(control.kind)
5312
+ zIndex: (control.drawingPath[0] ?? controlIndex) + 1,
5313
+ ...mapDukeFormControlKind(control.formControl.kind, themePalette)
5143
5314
  };
5144
5315
  }
5145
- function collectWorkbookFormControls(workbook) {
5316
+ function collectWorkbookFormControls(workbook, themePalette) {
5146
5317
  return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
5147
5318
  try {
5148
5319
  const controls = workbook.getSheet(workbookSheetIndex).formControls;
5149
- return Array.isArray(controls) ? controls.map((control, controlIndex) => mapDukeFormControl(control, controlIndex, workbookSheetIndex)) : [];
5320
+ return Array.isArray(controls) ? controls.flatMap((control, controlIndex) => control.anchor ? [mapDukeFormControl(control, controlIndex, workbookSheetIndex, themePalette)] : []) : [];
5150
5321
  } catch {
5151
5322
  return [];
5152
5323
  }
5153
5324
  });
5154
5325
  }
5155
- function parseFormControlKind(rawType) {
5156
- const normalized = (rawType ?? "").trim().toLowerCase();
5157
- switch (normalized) {
5158
- case "button":
5159
- return "button";
5160
- case "checkbox":
5161
- return "checkbox";
5162
- case "drop":
5163
- return "dropdown";
5164
- case "editbox":
5165
- return "editbox";
5166
- case "gbox":
5167
- return "group-box";
5168
- case "label":
5169
- return "label";
5170
- case "list":
5171
- return "listbox";
5172
- case "radio":
5173
- return "radio";
5174
- case "scroll":
5175
- return "scrollbar";
5176
- case "spin":
5177
- return "spinner";
5178
- default:
5179
- return "unknown";
5180
- }
5181
- }
5182
- function parseFormControlShapeId(value) {
5183
- const match = (value ?? "").match(/(\d+)(?!.*\d)/);
5184
- if (!match) {
5185
- return null;
5186
- }
5187
- const parsed = Number(match[1]);
5188
- return Number.isFinite(parsed) ? parsed : null;
5189
- }
5190
- function parseCssDeclarationValue(styleText, property) {
5191
- if (!styleText) {
5192
- return null;
5193
- }
5194
- const pattern = new RegExp(`${property}\\s*:\\s*([^;]+)`, "i");
5195
- const match = pattern.exec(styleText);
5196
- return match?.[1]?.trim() ?? null;
5197
- }
5198
- function parseControlTextAlign(styleText) {
5199
- const value = parseCssDeclarationValue(styleText, "text-align")?.toLowerCase();
5200
- if (value === "center" || value === "right") {
5201
- return value;
5202
- }
5203
- return value === "left" ? "left" : void 0;
5204
- }
5205
- function parseVmlFontSizePt(value) {
5206
- const parsed = Number(value ?? Number.NaN);
5207
- if (!Number.isFinite(parsed) || parsed <= 0) {
5208
- return void 0;
5209
- }
5210
- return parsed > 40 ? parsed / 20 : parsed;
5211
- }
5212
5326
  function normalizeControlLabel(label) {
5213
5327
  if (!label) {
5214
5328
  return void 0;
@@ -5216,16 +5330,6 @@ function normalizeControlLabel(label) {
5216
5330
  const normalized = label.replace(/\u00a0/g, " ").replace(/\s+/g, " ").trim();
5217
5331
  return normalized.length > 0 ? normalized : void 0;
5218
5332
  }
5219
- function isPlaceholderFormControlName(name) {
5220
- const normalized = normalizeControlLabel(name)?.toLowerCase();
5221
- if (!normalized) {
5222
- return false;
5223
- }
5224
- return /^(option button|group box|check box|drop down|dropdown|list box|edit box|scroll bar|spinner|spin button|button)\s+\d+$/.test(normalized);
5225
- }
5226
- function resolveNamedFormControlLabel(name) {
5227
- return isPlaceholderFormControlName(name) ? void 0 : normalizeControlLabel(name);
5228
- }
5229
5333
  function parseAnchor(anchorNode) {
5230
5334
  if (anchorNode.localName === "anchor") {
5231
5335
  const from = parseMarker(getFirstChild(anchorNode, "from"));
@@ -5906,260 +6010,8 @@ function parseDrawingObjects(archive, contentTypes, drawingPath, objectUrls, wor
5906
6010
  shapes
5907
6011
  };
5908
6012
  }
5909
- function parseSheetFormControlNodes(archive, sheetPath) {
5910
- const sheetXml = readArchiveText2(archive, sheetPath);
5911
- if (!sheetXml) {
5912
- return [];
5913
- }
5914
- const sheetDocument = parseXml2(sheetXml);
5915
- if (!sheetDocument) {
5916
- return [];
5917
- }
5918
- return getLocalElements(sheetDocument, "control").map((controlNode) => ({
5919
- anchor: parseAnchor(getFirstDescendant(controlNode, "anchor") ?? controlNode),
5920
- name: controlNode.getAttribute("name") ?? void 0,
5921
- shapeId: parseFormControlShapeId(controlNode.getAttribute("shapeId"))
5922
- }));
5923
- }
5924
- function parseVmlFormControls(archive, vmlDrawingPath) {
5925
- const xml = readArchiveText2(archive, vmlDrawingPath);
5926
- if (!xml) {
5927
- return /* @__PURE__ */ new Map();
5928
- }
5929
- const document2 = parseXml2(xml);
5930
- if (!document2) {
5931
- return /* @__PURE__ */ new Map();
5932
- }
5933
- const controls = /* @__PURE__ */ new Map();
5934
- for (const shapeNode of getLocalElements(document2, "shape")) {
5935
- const clientDataNode = getFirstChild(shapeNode, "ClientData");
5936
- if (!clientDataNode) {
5937
- continue;
5938
- }
5939
- const shapeId = parseFormControlShapeId(
5940
- shapeNode.getAttributeNS("urn:schemas-microsoft-com:office:office", "spid") ?? shapeNode.getAttribute("o:spid") ?? shapeNode.getAttribute("spid") ?? shapeNode.getAttribute("id")
5941
- );
5942
- if (shapeId === null) {
5943
- continue;
5944
- }
5945
- const styleText = shapeNode.getAttribute("style");
5946
- const textboxNode = getFirstChild(shapeNode, "textbox");
5947
- const fontNode = textboxNode ? getFirstDescendant(textboxNode, "font") : null;
5948
- const textContainerNode = textboxNode ? getFirstDescendant(textboxNode, "div") : null;
5949
- const label = normalizeControlLabel(textboxNode?.textContent);
5950
- const zIndex = Number(parseCssDeclarationValue(styleText, "z-index") ?? Number.NaN);
5951
- controls.set(shapeId, {
5952
- checked: parseSpreadsheetBooleanNode(getFirstChild(clientDataNode, "Checked")),
5953
- fontFamily: fontNode?.getAttribute("face") ?? void 0,
5954
- fontSizePt: parseVmlFontSizePt(fontNode?.getAttribute("size")),
5955
- hidden: (parseCssDeclarationValue(styleText, "visibility") ?? "").toLowerCase() === "hidden",
5956
- label,
5957
- linkedCell: normalizeControlLabel(getFirstChild(clientDataNode, "FmlaLink")?.textContent),
5958
- objectType: clientDataNode.getAttribute("ObjectType") ?? void 0,
5959
- shapeId,
5960
- textAlign: parseControlTextAlign(textContainerNode?.getAttribute("style")),
5961
- textColor: fontNode?.getAttribute("color") ?? void 0,
5962
- zIndex: Number.isFinite(zIndex) ? zIndex : controls.size + 1
5963
- });
5964
- }
5965
- return controls;
5966
- }
5967
- function parseSheetFormControls(archive, sheetPath, sheetRelationships, workbookSheetIndex, zIndexBase) {
5968
- const controlNodes = parseSheetFormControlNodes(archive, sheetPath);
5969
- if (controlNodes.length === 0) {
5970
- return [];
5971
- }
5972
- const legacyDrawingRelationship = [...sheetRelationships.values()].find(
5973
- (relationship) => relationship.type === VML_DRAWING_REL_TYPE
5974
- );
5975
- const vmlControlsByShapeId = legacyDrawingRelationship ? parseVmlFormControls(archive, legacyDrawingRelationship.target) : /* @__PURE__ */ new Map();
5976
- const parsedControls = [];
5977
- controlNodes.forEach((controlNode, index) => {
5978
- if (!controlNode.anchor) {
5979
- return;
5980
- }
5981
- const vmlControl = controlNode.shapeId !== null ? vmlControlsByShapeId.get(controlNode.shapeId) ?? null : null;
5982
- const kind = parseFormControlKind(vmlControl?.objectType);
5983
- parsedControls.push({
5984
- anchor: controlNode.anchor,
5985
- checked: vmlControl?.checked,
5986
- fontFamily: vmlControl?.fontFamily,
5987
- fontSizePt: vmlControl?.fontSizePt,
5988
- hidden: vmlControl?.hidden ?? false,
5989
- id: `form-control-${workbookSheetIndex}-${index}`,
5990
- kind,
5991
- label: vmlControl?.label,
5992
- linkedCell: vmlControl?.linkedCell,
5993
- name: controlNode.name,
5994
- sheetIndex: workbookSheetIndex,
5995
- textAlign: vmlControl?.textAlign,
5996
- textColor: vmlControl?.textColor,
5997
- workbookSheetIndex,
5998
- zIndex: zIndexBase + (vmlControl?.zIndex ?? index + 1)
5999
- });
6000
- });
6001
- return parsedControls.sort((left, right) => left.zIndex - right.zIndex);
6002
- }
6003
- function flattenShapeText(shape) {
6004
- const text = shape.paragraphs.flatMap((paragraph) => paragraph.runs.map((run) => run.text)).join(" ");
6005
- return normalizeControlLabel(text);
6006
- }
6007
- function rectArea(rect) {
6008
- return Math.max(0, rect.cx) * Math.max(0, rect.cy);
6009
- }
6010
- function rectIntersectionArea(left, right) {
6011
- const overlapX = Math.max(0, Math.min(left.x + left.cx, right.x + right.cx) - Math.max(left.x, right.x));
6012
- const overlapY = Math.max(0, Math.min(left.y + left.cy, right.y + right.cy) - Math.max(left.y, right.y));
6013
- return overlapX * overlapY;
6014
- }
6015
- function rectCenterDistance(left, right) {
6016
- const leftCenterX = left.x + left.cx / 2;
6017
- const leftCenterY = left.y + left.cy / 2;
6018
- const rightCenterX = right.x + right.cx / 2;
6019
- const rightCenterY = right.y + right.cy / 2;
6020
- return Math.hypot(leftCenterX - rightCenterX, leftCenterY - rightCenterY);
6021
- }
6022
- function findHiddenShapeControlMatch(control, shapes, sheetState) {
6023
- const controlRect = anchorToAbsoluteRect(control.anchor, sheetState);
6024
- const controlArea = Math.max(1, rectArea(controlRect));
6025
- const placeholderName = isPlaceholderFormControlName(control.name);
6026
- let bestMatch = null;
6027
- let bestScore = Number.NEGATIVE_INFINITY;
6028
- shapes.forEach((shape) => {
6029
- if (!shape.hidden) {
6030
- return;
6031
- }
6032
- const shapeRect = anchorToAbsoluteRect(shape.anchor, sheetState);
6033
- const shapeArea = Math.max(1, rectArea(shapeRect));
6034
- const intersectionArea = rectIntersectionArea(controlRect, shapeRect);
6035
- const overlapScore = intersectionArea / Math.min(controlArea, shapeArea);
6036
- const distance = rectCenterDistance(controlRect, shapeRect);
6037
- const maxDimension = Math.max(controlRect.cx, controlRect.cy, shapeRect.cx, shapeRect.cy, 1);
6038
- const distanceScore = distance / maxDimension;
6039
- const textLabel = flattenShapeText(shape);
6040
- const sameName = normalizeControlLabel(shape.name)?.toLowerCase() === normalizeControlLabel(control.name)?.toLowerCase();
6041
- let score = overlapScore * 4 - distanceScore;
6042
- if (sameName) {
6043
- score += 1;
6044
- }
6045
- if (control.kind === "group-box") {
6046
- score += textLabel ? -3 : 0.5;
6047
- score += shape.stroke?.none ? -1.5 : 0.75;
6048
- } else {
6049
- score += textLabel ? 2 : -1;
6050
- score += shape.stroke?.none ? 0.2 : -0.5;
6051
- }
6052
- if (!placeholderName && textLabel && textLabel === resolveNamedFormControlLabel(control.name)) {
6053
- score += 0.5;
6054
- }
6055
- if (score > bestScore) {
6056
- bestMatch = shape;
6057
- bestScore = score;
6058
- }
6059
- });
6060
- return bestScore >= 0.25 ? bestMatch : null;
6061
- }
6062
- function enrichFormControlsWithHiddenShapes(formControls, shapes, sheetState) {
6063
- return formControls.map((control) => {
6064
- const matchedShape = findHiddenShapeControlMatch(control, shapes, sheetState);
6065
- const matchedLabel = matchedShape ? flattenShapeText(matchedShape) : void 0;
6066
- const fallbackLabel = resolveNamedFormControlLabel(control.name);
6067
- let resolvedAnchor = control.anchor;
6068
- if (matchedShape && (control.kind === "group-box" || matchedLabel || isPlaceholderFormControlName(control.name))) {
6069
- resolvedAnchor = matchedShape.anchor;
6070
- }
6071
- return {
6072
- ...control,
6073
- anchor: resolvedAnchor,
6074
- label: control.label ?? matchedLabel ?? fallbackLabel
6075
- };
6076
- });
6077
- }
6078
- function getFormControlAnchorStart(anchor) {
6079
- return anchor.kind === "absolute" ? null : { col: anchor.from.col, row: anchor.from.row };
6080
- }
6081
- function scoreParsedFormControlMatch(dukeControl, parsedControl, parsedIndex) {
6082
- if (dukeControl.kind !== parsedControl.kind && parsedControl.kind !== "unknown") {
6083
- return Number.NEGATIVE_INFINITY;
6084
- }
6085
- let score = 10;
6086
- const dukeName = normalizeControlLabel(dukeControl.name)?.toLowerCase();
6087
- const parsedName = normalizeControlLabel(parsedControl.name)?.toLowerCase();
6088
- if (dukeName && parsedName) {
6089
- if (dukeName !== parsedName) {
6090
- return Number.NEGATIVE_INFINITY;
6091
- }
6092
- score += 100;
6093
- }
6094
- const dukeStart = getFormControlAnchorStart(dukeControl.anchor);
6095
- const parsedStart = getFormControlAnchorStart(parsedControl.anchor);
6096
- if (dukeStart && parsedStart && dukeStart.col === parsedStart.col && dukeStart.row === parsedStart.row) {
6097
- score += 40;
6098
- }
6099
- if (dukeControl.controlIndex === parsedIndex) {
6100
- score += 5;
6101
- }
6102
- return score;
6103
- }
6104
- function areDukeFormControlAnchorsEqual(left, right) {
6105
- if (!left || !right) {
6106
- return true;
6107
- }
6108
- return left.editAs === right.editAs && left.fromCol === right.fromCol && left.fromColOffset === right.fromColOffset && left.fromRow === right.fromRow && left.fromRowOffset === right.fromRowOffset && left.toCol === right.toCol && left.toColOffset === right.toColOffset && left.toRow === right.toRow && left.toRowOffset === right.toRowOffset;
6109
- }
6110
- function mergeDukeAndParsedFormControls(dukeControls, parsedControls, keepUnmatchedParsedControl = () => true) {
6111
- const usedParsedIndexes = /* @__PURE__ */ new Set();
6112
- const mergedControls = dukeControls.map((dukeControl) => {
6113
- let bestIndex = -1;
6114
- let bestScore = Number.NEGATIVE_INFINITY;
6115
- parsedControls.forEach((parsedControl2, parsedIndex) => {
6116
- if (usedParsedIndexes.has(parsedIndex)) {
6117
- return;
6118
- }
6119
- const score = scoreParsedFormControlMatch(dukeControl, parsedControl2, parsedIndex);
6120
- if (score > bestScore) {
6121
- bestIndex = parsedIndex;
6122
- bestScore = score;
6123
- }
6124
- });
6125
- const parsedControl = bestIndex >= 0 && bestScore >= 10 ? parsedControls[bestIndex] : null;
6126
- if (!parsedControl) {
6127
- return dukeControl;
6128
- }
6129
- usedParsedIndexes.add(bestIndex);
6130
- const dukeAnchorChanged = !areDukeFormControlAnchorsEqual(
6131
- parsedControl.dukeAnchor,
6132
- dukeControl.dukeAnchor
6133
- );
6134
- return {
6135
- ...parsedControl,
6136
- ...dukeControl,
6137
- anchor: dukeAnchorChanged ? dukeControl.anchor : parsedControl.anchor,
6138
- fontFamily: parsedControl.fontFamily,
6139
- fontSizePt: parsedControl.fontSizePt,
6140
- hidden: parsedControl.hidden,
6141
- id: parsedControl.id,
6142
- label: dukeControl.label ?? parsedControl.label,
6143
- name: dukeControl.name ?? parsedControl.name,
6144
- textAlign: parsedControl.textAlign,
6145
- textColor: parsedControl.textColor,
6146
- zIndex: parsedControl.zIndex
6147
- };
6148
- });
6149
- parsedControls.forEach((parsedControl, parsedIndex) => {
6150
- if (!usedParsedIndexes.has(parsedIndex) && keepUnmatchedParsedControl(parsedControl)) {
6151
- mergedControls.push(parsedControl);
6152
- }
6153
- });
6154
- return mergedControls.sort((left, right) => left.zIndex - right.zIndex);
6155
- }
6156
- function refreshWorkbookFormControls(workbook, currentControlsByWorkbookSheetIndex) {
6157
- const dukeControlsByWorkbookSheetIndex = collectWorkbookFormControls(workbook);
6158
- return dukeControlsByWorkbookSheetIndex.map((dukeControls, workbookSheetIndex) => mergeDukeAndParsedFormControls(
6159
- dukeControls,
6160
- currentControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6161
- (control) => control.controlIndex === void 0
6162
- ));
6013
+ function refreshWorkbookFormControls(workbook, _currentControlsByWorkbookSheetIndex, themePalette) {
6014
+ return collectWorkbookFormControls(workbook, themePalette);
6163
6015
  }
6164
6016
  function revokeWorkbookImageAssets(assets) {
6165
6017
  if (!assets) {
@@ -6193,7 +6045,7 @@ function parseWorkbookStructureAssetsFromArchive(archive, options) {
6193
6045
  workbookSheets
6194
6046
  };
6195
6047
  }
6196
- function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6048
+ function parseWorkbookImageAssets(bytes, workbook) {
6197
6049
  const archive = unzipSync(bytes);
6198
6050
  const {
6199
6051
  contentTypes,
@@ -6212,7 +6064,7 @@ function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6212
6064
  const shapesByWorkbookSheetIndex = [];
6213
6065
  const sheetOrigins = [];
6214
6066
  const imageOriginsById = /* @__PURE__ */ new Map();
6215
- const dukeFormControlsByWorkbookSheetIndex = Array.isArray(workbookOrFormControls) ? workbookOrFormControls : collectWorkbookFormControls(workbookOrFormControls);
6067
+ const formControls = workbook ? collectWorkbookFormControls(workbook, themePalette) : [];
6216
6068
  workbookSheets.forEach((sheet, workbookSheetIndex) => {
6217
6069
  const sheetRelationships = parseRelationships(archive, relsPathForDocument(sheet.path), sheet.path);
6218
6070
  const attachments = [];
@@ -6245,23 +6097,8 @@ function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6245
6097
  mediaPaths: drawingImages.mediaPaths
6246
6098
  });
6247
6099
  }
6248
- const formControlList = parseSheetFormControls(
6249
- archive,
6250
- sheet.path,
6251
- sheetRelationships,
6252
- workbookSheetIndex,
6253
- zIndexBase
6254
- );
6255
6100
  const visibleShapeList = shapeList.filter((shape) => !shape.hidden);
6256
- const enrichedFormControlList = enrichFormControlsWithHiddenShapes(
6257
- formControlList,
6258
- shapeList,
6259
- sheetStatesByWorkbookSheetIndex[workbookSheetIndex] ?? null
6260
- );
6261
- formControlsByWorkbookSheetIndex[workbookSheetIndex] = mergeDukeAndParsedFormControls(
6262
- dukeFormControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6263
- enrichedFormControlList
6264
- );
6101
+ formControlsByWorkbookSheetIndex[workbookSheetIndex] = formControls[workbookSheetIndex] ?? [];
6265
6102
  imagesByWorkbookSheetIndex[workbookSheetIndex] = imageList;
6266
6103
  shapesByWorkbookSheetIndex[workbookSheetIndex] = visibleShapeList;
6267
6104
  sheetOrigins[workbookSheetIndex] = attachments.length > 0 ? {
@@ -6271,6 +6108,7 @@ function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6271
6108
  });
6272
6109
  return {
6273
6110
  archive,
6111
+ dirtyArchivePaths: /* @__PURE__ */ new Set(),
6274
6112
  formControlsByWorkbookSheetIndex,
6275
6113
  imageOriginsById,
6276
6114
  imagesByWorkbookSheetIndex,
@@ -6333,6 +6171,7 @@ function updateWorkbookImageAnchor(assets, imageId, anchor) {
6333
6171
  }
6334
6172
  updateAnchorNode2(anchorNode, anchor);
6335
6173
  assets.archive[attachment.drawingPath] = strToU82(serializeXml2(drawingDocument));
6174
+ assets.dirtyArchivePaths.add(normalizeArchivePath2(attachment.drawingPath));
6336
6175
  const imageList = assets.imagesByWorkbookSheetIndex[origin.workbookSheetIndex] ?? [];
6337
6176
  const imageIndex = imageList.findIndex((image) => image.id === imageId);
6338
6177
  if (imageIndex >= 0) {
@@ -6449,7 +6288,7 @@ function appendSheetDrawingReference(sheetDocument, relationshipId) {
6449
6288
  worksheet.appendChild(drawingNode);
6450
6289
  }
6451
6290
  function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6452
- if (!sourceAssets || sheetOrigins.every((origin) => !origin?.attachments.length)) {
6291
+ if (!sourceAssets || sourceAssets.dirtyArchivePaths.size === 0) {
6453
6292
  return cloneBytes(savedBytes);
6454
6293
  }
6455
6294
  try {
@@ -6461,7 +6300,8 @@ function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6461
6300
  return cloneBytes(savedBytes);
6462
6301
  }
6463
6302
  sheetOrigins.forEach((origin, workbookSheetIndex) => {
6464
- if (!origin?.attachments.length) {
6303
+ const hasDirtyAttachment = origin?.attachments.some((attachment) => sourceAssets.dirtyArchivePaths.has(normalizeArchivePath2(attachment.drawingPath)) || attachment.drawingRelsPath && sourceAssets.dirtyArchivePaths.has(normalizeArchivePath2(attachment.drawingRelsPath)) || attachment.mediaPaths.some((path) => sourceAssets.dirtyArchivePaths.has(normalizeArchivePath2(path))));
6304
+ if (!origin?.attachments.length || !hasDirtyAttachment) {
6465
6305
  return;
6466
6306
  }
6467
6307
  const currentSheet = workbookSheets[workbookSheetIndex];
@@ -6513,6 +6353,14 @@ function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6513
6353
  archive[relsPath] = strToU82(serializeXml2(relDocument));
6514
6354
  mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, relsPath);
6515
6355
  });
6356
+ for (const dirtyPath of sourceAssets.dirtyArchivePaths) {
6357
+ const bytes = sourceAssets.archive[dirtyPath];
6358
+ if (!bytes) {
6359
+ continue;
6360
+ }
6361
+ archive[dirtyPath] = cloneBytes(bytes);
6362
+ mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, dirtyPath);
6363
+ }
6516
6364
  const hasDrawingOverride = getLocalElements(targetContentTypesDocument, "Override").some(
6517
6365
  (node) => node.getAttribute("ContentType") === DRAWING_CONTENT_TYPE
6518
6366
  );
@@ -6902,6 +6750,204 @@ function cloneArrayBufferForTransfer(buffer) {
6902
6750
  return buffer.slice(0);
6903
6751
  }
6904
6752
 
6753
+ // src/zip-entry-names.ts
6754
+ var BACKSLASH_BYTE = 92;
6755
+ var FORWARD_SLASH_BYTE = 47;
6756
+ var END_OF_CENTRAL_DIRECTORY_SIGNATURE = 101010256;
6757
+ var CENTRAL_FILE_HEADER_SIGNATURE = 33639248;
6758
+ var LOCAL_FILE_HEADER_SIGNATURE = 67324752;
6759
+ var END_OF_CENTRAL_DIRECTORY_LENGTH = 22;
6760
+ var CENTRAL_FILE_HEADER_LENGTH = 46;
6761
+ var LOCAL_FILE_HEADER_LENGTH = 30;
6762
+ var MAX_ZIP_COMMENT_LENGTH = 65535;
6763
+ var ZIP16_SENTINEL = 65535;
6764
+ var ZIP32_SENTINEL = 4294967295;
6765
+ function findEndOfCentralDirectoryOffset(view) {
6766
+ const firstPossibleOffset = Math.max(
6767
+ 0,
6768
+ view.byteLength - END_OF_CENTRAL_DIRECTORY_LENGTH - MAX_ZIP_COMMENT_LENGTH
6769
+ );
6770
+ for (let offset = view.byteLength - END_OF_CENTRAL_DIRECTORY_LENGTH; offset >= firstPossibleOffset; offset -= 1) {
6771
+ if (view.getUint32(offset, true) !== END_OF_CENTRAL_DIRECTORY_SIGNATURE) {
6772
+ continue;
6773
+ }
6774
+ const commentLength = view.getUint16(offset + 20, true);
6775
+ if (offset + END_OF_CENTRAL_DIRECTORY_LENGTH + commentLength === view.byteLength) {
6776
+ return offset;
6777
+ }
6778
+ }
6779
+ return null;
6780
+ }
6781
+ function readCentralDirectoryInfo(view) {
6782
+ if (view.byteLength < END_OF_CENTRAL_DIRECTORY_LENGTH) {
6783
+ return null;
6784
+ }
6785
+ const endOfCentralDirectoryOffset = findEndOfCentralDirectoryOffset(view);
6786
+ if (endOfCentralDirectoryOffset === null) {
6787
+ return null;
6788
+ }
6789
+ const diskNumber = view.getUint16(endOfCentralDirectoryOffset + 4, true);
6790
+ const centralDirectoryDiskNumber = view.getUint16(endOfCentralDirectoryOffset + 6, true);
6791
+ const entriesOnDisk = view.getUint16(endOfCentralDirectoryOffset + 8, true);
6792
+ const entryCount = view.getUint16(endOfCentralDirectoryOffset + 10, true);
6793
+ const centralDirectorySize = view.getUint32(endOfCentralDirectoryOffset + 12, true);
6794
+ const centralDirectoryOffset = view.getUint32(endOfCentralDirectoryOffset + 16, true);
6795
+ if (diskNumber !== 0 || centralDirectoryDiskNumber !== 0 || entriesOnDisk !== entryCount || entryCount === ZIP16_SENTINEL || centralDirectorySize === ZIP32_SENTINEL || centralDirectoryOffset === ZIP32_SENTINEL) {
6796
+ return null;
6797
+ }
6798
+ const centralDirectoryEndOffset = centralDirectoryOffset + centralDirectorySize;
6799
+ if (centralDirectoryOffset > endOfCentralDirectoryOffset || centralDirectoryEndOffset > endOfCentralDirectoryOffset || centralDirectoryEndOffset > view.byteLength) {
6800
+ return null;
6801
+ }
6802
+ return {
6803
+ endOffset: centralDirectoryEndOffset,
6804
+ entryCount,
6805
+ offset: centralDirectoryOffset
6806
+ };
6807
+ }
6808
+ function readCentralDirectoryEntry(view, centralDirectoryEndOffset, offset) {
6809
+ if (offset < 0 || offset + CENTRAL_FILE_HEADER_LENGTH > centralDirectoryEndOffset || offset + CENTRAL_FILE_HEADER_LENGTH > view.byteLength || view.getUint32(offset, true) !== CENTRAL_FILE_HEADER_SIGNATURE) {
6810
+ return null;
6811
+ }
6812
+ const nameLength = view.getUint16(offset + 28, true);
6813
+ const extraLength = view.getUint16(offset + 30, true);
6814
+ const commentLength = view.getUint16(offset + 32, true);
6815
+ const nameStart = offset + CENTRAL_FILE_HEADER_LENGTH;
6816
+ const nameEnd = nameStart + nameLength;
6817
+ const nextOffset = nameEnd + extraLength + commentLength;
6818
+ if (nameEnd > centralDirectoryEndOffset || nextOffset > centralDirectoryEndOffset) {
6819
+ return null;
6820
+ }
6821
+ return {
6822
+ diskNumberStart: view.getUint16(offset + 34, true),
6823
+ localHeaderOffset: view.getUint32(offset + 42, true),
6824
+ nameEnd,
6825
+ nameStart,
6826
+ nextOffset
6827
+ };
6828
+ }
6829
+ function entryNameUsesBackslash(bytes, entry) {
6830
+ for (let cursor = entry.nameStart; cursor < entry.nameEnd; cursor += 1) {
6831
+ if (bytes[cursor] === BACKSLASH_BYTE) {
6832
+ return true;
6833
+ }
6834
+ }
6835
+ return false;
6836
+ }
6837
+ function centralDirectoryUsesBackslashSeparators(bytes, view, centralDirectory) {
6838
+ let offset = centralDirectory.offset;
6839
+ for (let index = 0; index < centralDirectory.entryCount; index += 1) {
6840
+ const entry = readCentralDirectoryEntry(view, centralDirectory.endOffset, offset);
6841
+ if (!entry) {
6842
+ return null;
6843
+ }
6844
+ if (entryNameUsesBackslash(bytes, entry)) {
6845
+ return true;
6846
+ }
6847
+ offset = entry.nextOffset;
6848
+ }
6849
+ return false;
6850
+ }
6851
+ function normalizedEntryNameKey(bytes, entry) {
6852
+ let key = "";
6853
+ for (let cursor = entry.nameStart; cursor < entry.nameEnd; cursor += 1) {
6854
+ const byte = bytes[cursor] === BACKSLASH_BYTE ? FORWARD_SLASH_BYTE : bytes[cursor];
6855
+ key += String.fromCharCode(byte);
6856
+ }
6857
+ return key;
6858
+ }
6859
+ function validateEntryNamePatches(bytes, view, centralDirectory) {
6860
+ const normalizedNames = /* @__PURE__ */ new Set();
6861
+ const localHeaderOffsets = /* @__PURE__ */ new Set();
6862
+ let offset = centralDirectory.offset;
6863
+ for (let index = 0; index < centralDirectory.entryCount; index += 1) {
6864
+ const entry = readCentralDirectoryEntry(view, centralDirectory.endOffset, offset);
6865
+ if (!entry || entry.diskNumberStart !== 0 || entry.localHeaderOffset === ZIP32_SENTINEL || entry.localHeaderOffset + LOCAL_FILE_HEADER_LENGTH > centralDirectory.offset || localHeaderOffsets.has(entry.localHeaderOffset)) {
6866
+ return false;
6867
+ }
6868
+ localHeaderOffsets.add(entry.localHeaderOffset);
6869
+ const normalizedName = normalizedEntryNameKey(bytes, entry);
6870
+ if (normalizedNames.has(normalizedName)) {
6871
+ return false;
6872
+ }
6873
+ normalizedNames.add(normalizedName);
6874
+ if (!entryNameUsesBackslash(bytes, entry)) {
6875
+ offset = entry.nextOffset;
6876
+ continue;
6877
+ }
6878
+ if (view.getUint32(entry.localHeaderOffset, true) !== LOCAL_FILE_HEADER_SIGNATURE) {
6879
+ return false;
6880
+ }
6881
+ const localNameLength = view.getUint16(entry.localHeaderOffset + 26, true);
6882
+ const localNameStart = entry.localHeaderOffset + LOCAL_FILE_HEADER_LENGTH;
6883
+ const localNameEnd = localNameStart + localNameLength;
6884
+ if (localNameLength !== entry.nameEnd - entry.nameStart || localNameEnd > centralDirectory.offset) {
6885
+ return false;
6886
+ }
6887
+ for (let nameIndex = 0; nameIndex < localNameLength; nameIndex += 1) {
6888
+ const centralNameOffset = entry.nameStart + nameIndex;
6889
+ const localNameOffset = localNameStart + nameIndex;
6890
+ if (bytes[centralNameOffset] !== bytes[localNameOffset]) {
6891
+ return false;
6892
+ }
6893
+ }
6894
+ offset = entry.nextOffset;
6895
+ }
6896
+ return true;
6897
+ }
6898
+ function patchEntryNameSeparators(bytes, view, centralDirectory) {
6899
+ let offset = centralDirectory.offset;
6900
+ for (let index = 0; index < centralDirectory.entryCount; index += 1) {
6901
+ const entry = readCentralDirectoryEntry(view, centralDirectory.endOffset, offset);
6902
+ if (!entry) {
6903
+ return;
6904
+ }
6905
+ if (entryNameUsesBackslash(bytes, entry)) {
6906
+ const localNameStart = entry.localHeaderOffset + LOCAL_FILE_HEADER_LENGTH;
6907
+ for (let nameIndex = 0; nameIndex < entry.nameEnd - entry.nameStart; nameIndex += 1) {
6908
+ const centralNameOffset = entry.nameStart + nameIndex;
6909
+ if (bytes[centralNameOffset] === BACKSLASH_BYTE) {
6910
+ bytes[centralNameOffset] = FORWARD_SLASH_BYTE;
6911
+ bytes[localNameStart + nameIndex] = FORWARD_SLASH_BYTE;
6912
+ }
6913
+ }
6914
+ }
6915
+ offset = entry.nextOffset;
6916
+ }
6917
+ }
6918
+ function normalizeZipEntryNames(bytes) {
6919
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
6920
+ const centralDirectory = readCentralDirectoryInfo(view);
6921
+ if (!centralDirectory || centralDirectoryUsesBackslashSeparators(bytes, view, centralDirectory) !== true) {
6922
+ return bytes;
6923
+ }
6924
+ if (!validateEntryNamePatches(bytes, view, centralDirectory)) {
6925
+ return bytes;
6926
+ }
6927
+ const normalizedBytes = new Uint8Array(bytes.byteLength);
6928
+ normalizedBytes.set(bytes);
6929
+ const normalizedView = new DataView(
6930
+ normalizedBytes.buffer,
6931
+ normalizedBytes.byteOffset,
6932
+ normalizedBytes.byteLength
6933
+ );
6934
+ patchEntryNameSeparators(normalizedBytes, normalizedView, centralDirectory);
6935
+ return normalizedBytes;
6936
+ }
6937
+ function normalizeWorkbookArrayBuffer(buffer) {
6938
+ const bytes = new Uint8Array(buffer);
6939
+ const normalizedBytes = normalizeZipEntryNames(bytes);
6940
+ if (normalizedBytes === bytes) {
6941
+ return buffer;
6942
+ }
6943
+ if (normalizedBytes.byteOffset === 0 && normalizedBytes.byteLength === normalizedBytes.buffer.byteLength && normalizedBytes.buffer instanceof ArrayBuffer) {
6944
+ return normalizedBytes.buffer;
6945
+ }
6946
+ const normalizedBuffer = new ArrayBuffer(normalizedBytes.byteLength);
6947
+ new Uint8Array(normalizedBuffer).set(normalizedBytes);
6948
+ return normalizedBuffer;
6949
+ }
6950
+
6905
6951
  // src/controller.tsx
6906
6952
  var FORMULA_COUNT_THRESHOLD = 1e3;
6907
6953
  var DEFAULT_ROW_HEIGHT = 24;
@@ -6918,8 +6964,6 @@ var DEFAULT_MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
6918
6964
  var MAX_INTERACTIVE_WORKSHEET_XML_BYTES = 200 * 1024 * 1024;
6919
6965
  var MAX_INTERACTIVE_SHARED_STRINGS_BYTES = 50 * 1024 * 1024;
6920
6966
  var MAX_INTERACTIVE_TOTAL_XML_BYTES = 256 * 1024 * 1024;
6921
- var EMU_PER_PIXEL3 = 9525;
6922
- var IMAGE_BATCH_ROW_COUNT = 256;
6923
6967
  var DEFAULT_ZOOM_SCALE = 100;
6924
6968
  var MIN_ZOOM_SCALE = 10;
6925
6969
  var MAX_ZOOM_SCALE = 400;
@@ -7374,104 +7418,175 @@ function resolveWorkbookReference(workbook, defaultWorkbookSheetIndex, rawRefere
7374
7418
  return null;
7375
7419
  }
7376
7420
  }
7377
- function formControlInputFromDukeControl(control) {
7378
- return {
7379
- anchor: { ...control.anchor },
7380
- kind: { ...control.kind },
7381
- locked: control.locked,
7382
- name: control.name,
7383
- printable: control.printable
7384
- };
7385
- }
7386
- function getFormControlCellLink(kind) {
7387
- return "cellLink" in kind ? kind.cellLink : void 0;
7421
+ function parseColorHex(hex) {
7422
+ const normalized = hex?.replace(/^#/, "");
7423
+ return normalized && /^[0-9a-f]{6}$/i.test(normalized) ? [0, 2, 4].map((offset) => Number.parseInt(normalized.slice(offset, offset + 2), 16)) : null;
7388
7424
  }
7389
- function anchorContainsControl(group, control) {
7390
- const groupAnchor = group.anchor;
7391
- const controlAnchor = control.anchor;
7392
- return controlAnchor.fromCol >= groupAnchor.fromCol && controlAnchor.fromCol <= groupAnchor.toCol && controlAnchor.fromRow >= groupAnchor.fromRow && controlAnchor.fromRow <= groupAnchor.toRow;
7393
- }
7394
- function resolveOptionButtonGroupIndexes(controls, targetIndex) {
7395
- const target = controls[targetIndex];
7396
- if (!target || target.kind.kind !== "optionButton") {
7397
- return [];
7398
- }
7399
- if (target.kind.cellLink) {
7400
- const targetCellLink = target.kind.cellLink;
7401
- return controls.flatMap((control, index) => control.kind.kind === "optionButton" && control.kind.cellLink === targetCellLink ? [index] : []);
7402
- }
7403
- const containingGroups = controls.map((control, index) => ({ control, index })).filter(({ control }) => control.kind.kind === "groupBox" && anchorContainsControl(control, target)).sort((left, right) => {
7404
- const leftArea = (left.control.anchor.toCol - left.control.anchor.fromCol) * (left.control.anchor.toRow - left.control.anchor.fromRow);
7405
- const rightArea = (right.control.anchor.toCol - right.control.anchor.fromCol) * (right.control.anchor.toRow - right.control.anchor.fromRow);
7406
- return leftArea - rightArea;
7407
- });
7408
- const containingGroup = containingGroups[0]?.control;
7409
- if (containingGroup) {
7410
- return controls.flatMap((control, index) => control.kind.kind === "optionButton" && anchorContainsControl(containingGroup, control) ? [index] : []);
7411
- }
7412
- const optionIndexes = controls.flatMap((control, index) => control.kind.kind === "optionButton" ? [index] : []);
7413
- const targetPosition = optionIndexes.indexOf(targetIndex);
7414
- if (targetPosition < 0) {
7415
- return [];
7425
+ function xlsxColorToDukeColor(color) {
7426
+ if (!color) {
7427
+ return void 0;
7416
7428
  }
7417
- let startPosition = targetPosition;
7418
- while (startPosition > 0) {
7419
- const control = controls[optionIndexes[startPosition] ?? -1];
7420
- if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7421
- break;
7429
+ const normalizedHex = color.hex?.replace(/^#/, "");
7430
+ const colorType = color.colorType ?? (normalizedHex?.length === 8 ? "argb" : normalizedHex?.length === 6 ? "rgb" : void 0) ?? (color.r !== void 0 && color.g !== void 0 && color.b !== void 0 ? color.a !== void 0 ? "argb" : "rgb" : void 0) ?? (color.themeIndex !== void 0 ? "theme" : color.paletteIndex !== void 0 ? "indexed" : void 0);
7431
+ switch (colorType) {
7432
+ case "auto":
7433
+ return { colorType: "auto" };
7434
+ case "rgb": {
7435
+ const channels = color.r !== void 0 && color.g !== void 0 && color.b !== void 0 ? [color.r, color.g, color.b] : parseColorHex(color.hex);
7436
+ return channels ? { b: channels[2], colorType: "rgb", g: channels[1], r: channels[0] } : void 0;
7422
7437
  }
7423
- startPosition -= 1;
7424
- }
7425
- let endPosition = targetPosition + 1;
7426
- while (endPosition < optionIndexes.length) {
7427
- const control = controls[optionIndexes[endPosition] ?? -1];
7428
- if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7429
- break;
7438
+ case "argb": {
7439
+ const normalized = color.hex?.replace(/^#/, "");
7440
+ const channels = normalized && /^[0-9a-f]{8}$/i.test(normalized) ? [0, 2, 4, 6].map((offset) => Number.parseInt(normalized.slice(offset, offset + 2), 16)) : color.a !== void 0 && color.r !== void 0 && color.g !== void 0 && color.b !== void 0 ? [color.a, color.r, color.g, color.b] : null;
7441
+ return channels ? { a: channels[0], b: channels[3], colorType: "argb", g: channels[2], r: channels[1] } : void 0;
7430
7442
  }
7431
- endPosition += 1;
7443
+ case "theme":
7444
+ return color.themeIndex === void 0 ? void 0 : { colorType: "theme", index: color.themeIndex, tint: color.tint ?? 0 };
7445
+ case "indexed":
7446
+ return color.paletteIndex === void 0 ? void 0 : { colorType: "indexed", index: color.paletteIndex };
7432
7447
  }
7433
- return optionIndexes.slice(startPosition, endPosition);
7434
7448
  }
7435
- function resolveLinkedCellValue(controls, controlIndex) {
7436
- const control = controls[controlIndex];
7437
- if (!control) {
7438
- return void 0;
7449
+ function xlsxFontToDukeFont(font) {
7450
+ return {
7451
+ bold: font.bold,
7452
+ charset: font.charset,
7453
+ color: xlsxColorToDukeColor(font.color),
7454
+ family: font.family,
7455
+ italic: font.italic,
7456
+ name: font.name,
7457
+ scheme: font.scheme,
7458
+ size: font.size,
7459
+ strikethrough: font.strikethrough,
7460
+ underline: font.underline,
7461
+ verticalAlign: font.verticalAlign
7462
+ };
7463
+ }
7464
+ function xlsxCaptionToDukeText(caption) {
7465
+ if (typeof caption === "string") {
7466
+ return { runs: [{ text: caption }] };
7439
7467
  }
7440
- switch (control.kind.kind) {
7468
+ return {
7469
+ horizontalAlignment: caption.horizontalAlignment,
7470
+ runs: caption.runs.map((run) => ({
7471
+ font: run.font ? xlsxFontToDukeFont(run.font) : void 0,
7472
+ text: run.text
7473
+ })),
7474
+ verticalAlignment: caption.verticalAlignment
7475
+ };
7476
+ }
7477
+ function xlsxFormControlKindToDukeKind(kind) {
7478
+ switch (kind.kind) {
7479
+ case "button":
7480
+ case "label":
7481
+ return { caption: xlsxCaptionToDukeText(kind.caption), kind: kind.kind };
7441
7482
  case "checkbox":
7442
- return control.kind.state === "mixed" ? "#N/A" : control.kind.state === "checked";
7443
- case "optionButton": {
7444
- const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
7445
- const checkedIndex = groupIndexes.find((index) => controls[index]?.kind.kind === "optionButton" && controls[index]?.kind.state === "checked");
7446
- const groupPosition = checkedIndex === void 0 ? -1 : groupIndexes.indexOf(checkedIndex);
7447
- return groupPosition >= 0 ? groupPosition + 1 : "";
7448
- }
7449
- case "dropdown":
7450
- return control.kind.selected === void 0 ? "" : control.kind.selected + 1;
7451
- case "listBox":
7452
- return control.kind.selection === "single" && control.kind.selected.length === 1 ? (control.kind.selected[0] ?? -1) + 1 : "";
7453
- case "scrollbar":
7454
- case "spinner":
7455
- return control.kind.value;
7483
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7484
+ case "optionButton":
7485
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7486
+ case "groupBox":
7487
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7488
+ case "editbox":
7489
+ return {
7490
+ caption: xlsxCaptionToDukeText(kind.caption),
7491
+ kind: "unknown",
7492
+ legacyObjectType: kind.legacyObjectType,
7493
+ objectType: "EditBox",
7494
+ rawObj: kind.rawObj,
7495
+ rawProperties: kind.rawProperties
7496
+ };
7497
+ case "unknown":
7498
+ return {
7499
+ caption: kind.caption ? xlsxCaptionToDukeText(kind.caption) : void 0,
7500
+ kind: "unknown",
7501
+ legacyObjectType: kind.legacyObjectType,
7502
+ objectType: kind.objectType,
7503
+ rawObj: kind.rawObj,
7504
+ rawProperties: kind.rawProperties
7505
+ };
7456
7506
  default:
7457
- return void 0;
7507
+ return { ...kind };
7458
7508
  }
7459
7509
  }
7460
- function syncFormControlLinkedCell(workbook, workbookSheetIndex, controls, controlIndex) {
7461
- const control = controls[controlIndex];
7462
- if (!control) {
7463
- return;
7510
+ function dukeFormControlKindToInput(kind) {
7511
+ if (kind.kind === "optionButton") {
7512
+ return { ...kind, state: kind.state === "checked" ? "checked" : "unchecked" };
7464
7513
  }
7465
- const cellLink = getFormControlCellLink(control.kind);
7466
- const value = resolveLinkedCellValue(controls, controlIndex);
7467
- if (!cellLink || value === void 0) {
7468
- return;
7514
+ if (kind.kind === "unknown") {
7515
+ return {
7516
+ caption: kind.caption,
7517
+ kind: "unknown",
7518
+ legacyObjectType: kind.legacyObjectType,
7519
+ objectType: kind.objectType,
7520
+ rawObj: kind.rawObj,
7521
+ rawProperties: kind.rawProperties
7522
+ };
7469
7523
  }
7470
- const target = resolveWorkbookReference(workbook, workbookSheetIndex, cellLink);
7471
- if (!target || target.range.start.row !== target.range.end.row || target.range.start.col !== target.range.end.col) {
7472
- return;
7524
+ return { ...kind };
7525
+ }
7526
+ function formControlInputFromDukeControl(control) {
7527
+ return {
7528
+ altText: control.altText,
7529
+ ...control.anchor ? { anchor: control.anchor } : { transform: control.transform },
7530
+ formControl: {
7531
+ kind: dukeFormControlKindToInput(control.formControl.kind),
7532
+ macroName: control.formControl.macroName,
7533
+ rawClientData: control.formControl.rawClientData
7534
+ },
7535
+ hidden: control.hidden,
7536
+ kind: "formControl",
7537
+ locked: control.locked,
7538
+ name: control.name,
7539
+ printable: control.printable,
7540
+ title: control.title
7541
+ };
7542
+ }
7543
+ function xlsxFormControlInputToDukeDrawing(input) {
7544
+ return {
7545
+ altText: input.altText,
7546
+ anchor: xlsxAnchorToDukeDrawingAnchor(input.anchor, input.editAs),
7547
+ formControl: {
7548
+ kind: xlsxFormControlKindToDukeKind(input.kind),
7549
+ macroName: input.macroName,
7550
+ rawClientData: input.rawClientData
7551
+ },
7552
+ hidden: input.hidden,
7553
+ kind: "formControl",
7554
+ locked: input.locked,
7555
+ name: input.name,
7556
+ printable: input.printable,
7557
+ title: input.title
7558
+ };
7559
+ }
7560
+ function reconcileCheckedOptionButtons(worksheet) {
7561
+ const checkedPaths = worksheet.formControls.flatMap((control) => control.formControl.kind.kind === "optionButton" && control.formControl.kind.state === "checked" ? [control.drawingPath] : []);
7562
+ for (const path of checkedPaths) {
7563
+ const current = worksheet.formControls.find((control) => control.drawingPath.length === path.length && control.drawingPath.every((segment, index) => segment === path[index]));
7564
+ if (current?.formControl.kind.kind === "optionButton" && current.formControl.kind.state === "checked") {
7565
+ worksheet.setFormControlCheckState(current.drawingPath, "checked");
7566
+ }
7567
+ }
7568
+ }
7569
+ function formControlInteractionChanged(current, next) {
7570
+ if (current.kind !== next.kind) {
7571
+ return true;
7572
+ }
7573
+ if ("cellLink" in current && "cellLink" in next && current.cellLink !== next.cellLink) {
7574
+ return true;
7575
+ }
7576
+ switch (current.kind) {
7577
+ case "checkbox":
7578
+ case "optionButton":
7579
+ return next.kind === current.kind && current.state !== next.state;
7580
+ case "dropdown":
7581
+ return next.kind === "dropdown" && current.selected !== next.selected;
7582
+ case "listBox":
7583
+ return next.kind === "listBox" && (current.selection !== next.selection || current.selected.length !== next.selected.length || current.selected.some((value, index) => value !== next.selected[index]));
7584
+ case "scrollbar":
7585
+ case "spinner":
7586
+ return next.kind === current.kind && current.value !== next.value;
7587
+ default:
7588
+ return false;
7473
7589
  }
7474
- target.worksheet.setCell(cellAddressToA1(target.range.start), value);
7475
7590
  }
7476
7591
  function parseWorksheetFreezePanes(worksheet) {
7477
7592
  const rawFreezePanes = worksheet.freezePanes;
@@ -7805,9 +7920,6 @@ function scheduleLowPriorityTask(task) {
7805
7920
  const timeoutHandle = window.setTimeout(task, 0);
7806
7921
  return () => window.clearTimeout(timeoutHandle);
7807
7922
  }
7808
- function asFiniteNumber(value) {
7809
- return typeof value === "number" && Number.isFinite(value) ? value : null;
7810
- }
7811
7923
  function inferImageMimeType(source) {
7812
7924
  if (source.startsWith("data:")) {
7813
7925
  const separatorIndex = source.indexOf(";");
@@ -7831,18 +7943,27 @@ function inferImageMimeType(source) {
7831
7943
  return "image/png";
7832
7944
  }
7833
7945
  function inferWorksheetDirectImageMimeType(info) {
7834
- const format = typeof info.format === "string" ? info.format.trim().toLowerCase() : "";
7946
+ const format = info.format;
7835
7947
  if (format === "gif") {
7836
7948
  return "image/gif";
7837
7949
  }
7838
- if (format === "jpg" || format === "jpeg") {
7950
+ if (format === "jpeg") {
7839
7951
  return "image/jpeg";
7840
7952
  }
7841
7953
  if (format === "svg") {
7842
7954
  return "image/svg+xml";
7843
7955
  }
7844
- if (format === "webp") {
7845
- return "image/webp";
7956
+ if (format === "bmp") {
7957
+ return "image/bmp";
7958
+ }
7959
+ if (format === "tiff") {
7960
+ return "image/tiff";
7961
+ }
7962
+ if (format === "emf") {
7963
+ return "image/emf";
7964
+ }
7965
+ if (format === "wmf") {
7966
+ return "image/wmf";
7846
7967
  }
7847
7968
  if (format === "png") {
7848
7969
  return "image/png";
@@ -7864,323 +7985,108 @@ function createWorksheetDirectImageSource(data, mimeType, objectUrls) {
7864
7985
  objectUrls.push(objectUrl);
7865
7986
  return objectUrl;
7866
7987
  }
7867
- function buildWorksheetDirectImageAnchor(rawAnchor, widthEmu, heightEmu) {
7868
- const anchor = rawAnchor && typeof rawAnchor === "object" ? rawAnchor : {};
7869
- const fromCol = asFiniteNumber(anchor.fromCol) ?? 0;
7870
- const fromRow = asFiniteNumber(anchor.fromRow) ?? 0;
7871
- const fromColOffset = asFiniteNumber(anchor.fromColOffset) ?? 0;
7872
- const fromRowOffset = asFiniteNumber(anchor.fromRowOffset) ?? 0;
7873
- const toCol = asFiniteNumber(anchor.toCol);
7874
- const toRow = asFiniteNumber(anchor.toRow);
7875
- const toColOffset = asFiniteNumber(anchor.toColOffset) ?? 0;
7876
- const toRowOffset = asFiniteNumber(anchor.toRowOffset) ?? 0;
7877
- if (toCol !== null && toRow !== null) {
7878
- return {
7879
- from: {
7880
- col: Math.max(0, Math.round(fromCol)),
7881
- colOffsetEmu: Math.max(0, Math.round(fromColOffset)),
7882
- row: Math.max(0, Math.round(fromRow)),
7883
- rowOffsetEmu: Math.max(0, Math.round(fromRowOffset))
7884
- },
7885
- kind: "two-cell",
7886
- to: {
7887
- col: Math.max(0, Math.round(toCol)),
7888
- colOffsetEmu: Math.max(0, Math.round(toColOffset)),
7889
- row: Math.max(0, Math.round(toRow)),
7890
- rowOffsetEmu: Math.max(0, Math.round(toRowOffset))
7891
- }
7892
- };
7893
- }
7894
- return {
7895
- from: {
7896
- col: Math.max(0, Math.round(fromCol)),
7897
- colOffsetEmu: Math.max(0, Math.round(fromColOffset)),
7898
- row: Math.max(0, Math.round(fromRow)),
7899
- rowOffsetEmu: Math.max(0, Math.round(fromRowOffset))
7900
- },
7901
- kind: "one-cell",
7902
- sizeEmu: {
7903
- cx: Math.max(EMU_PER_PIXEL3, Math.round(widthEmu)),
7904
- cy: Math.max(EMU_PER_PIXEL3, Math.round(heightEmu))
7905
- }
7906
- };
7907
- }
7908
- function normalizeWorksheetDirectShapeParagraphs(rawParagraphs, fallbackText) {
7909
- const normalizedParagraphs = [];
7910
- if (Array.isArray(rawParagraphs)) {
7911
- for (const entry of rawParagraphs) {
7912
- const paragraph = entry && typeof entry === "object" ? entry : {};
7913
- const runs = [];
7914
- if (Array.isArray(paragraph.runs)) {
7915
- for (const runEntry of paragraph.runs) {
7916
- const run = runEntry && typeof runEntry === "object" ? runEntry : {};
7917
- const text2 = typeof run.text === "string" ? run.text : "";
7918
- if (!text2) {
7919
- continue;
7920
- }
7921
- runs.push({
7922
- bold: typeof run.bold === "boolean" ? run.bold : void 0,
7923
- color: typeof run.color === "string" && run.color.trim() ? run.color : void 0,
7924
- fontFamily: typeof run.fontFamily === "string" && run.fontFamily.trim() ? run.fontFamily : void 0,
7925
- fontSizePt: asFiniteNumber(run.fontSizePt) ?? void 0,
7926
- italic: typeof run.italic === "boolean" ? run.italic : void 0,
7927
- text: text2,
7928
- underline: typeof run.underline === "boolean" ? run.underline : void 0
7929
- });
7930
- }
7931
- }
7932
- if (runs.length === 0) {
7933
- continue;
7934
- }
7935
- const align = paragraph.align;
7936
- normalizedParagraphs.push({
7937
- align: align === "center" || align === "justify" || align === "left" || align === "right" ? align : void 0,
7938
- runs
7939
- });
7988
+ function buildWorksheetDirectApiImage(worksheet, workbookSheetIndex, drawing, objectUrls) {
7989
+ const hasSvgCompanion = Boolean(drawing.image.svgMediaPath);
7990
+ let mimeType = inferWorksheetDirectImageMimeType(drawing.image);
7991
+ let data;
7992
+ try {
7993
+ const svgData = drawing.image.format === "svg" || hasSvgCompanion ? worksheet.drawingSvgData(drawing.drawingPath) : void 0;
7994
+ if (svgData) {
7995
+ data = svgData;
7996
+ mimeType = "image/svg+xml";
7997
+ } else {
7998
+ data = worksheet.drawingImageData(drawing.drawingPath);
7940
7999
  }
7941
- }
7942
- if (normalizedParagraphs.length > 0) {
7943
- return normalizedParagraphs;
7944
- }
7945
- const text = typeof fallbackText === "string" ? fallbackText : "";
7946
- return text ? [{ runs: [{ text }] }] : [];
7947
- }
7948
- function buildWorksheetDirectApiShape(workbookSheetIndex, info, zIndex) {
7949
- const fill = info.fill && typeof info.fill === "object" ? {
7950
- color: typeof info.fill.color === "string" && info.fill.color.trim() ? info.fill.color : void 0,
7951
- none: typeof info.fill.none === "boolean" ? info.fill.none : void 0,
7952
- opacity: asFiniteNumber(info.fill.opacity) ?? void 0
7953
- } : void 0;
7954
- const stroke = info.stroke && typeof info.stroke === "object" ? {
7955
- color: typeof info.stroke.color === "string" && info.stroke.color.trim() ? info.stroke.color : void 0,
7956
- dash: typeof info.stroke.dash === "string" && info.stroke.dash.trim() ? info.stroke.dash : void 0,
7957
- headEndType: typeof info.stroke.headEndType === "string" && info.stroke.headEndType.trim() ? info.stroke.headEndType : void 0,
7958
- none: typeof info.stroke.none === "boolean" ? info.stroke.none : void 0,
7959
- opacity: asFiniteNumber(info.stroke.opacity) ?? void 0,
7960
- tailEndType: typeof info.stroke.tailEndType === "string" && info.stroke.tailEndType.trim() ? info.stroke.tailEndType : void 0,
7961
- widthPx: asFiniteNumber(info.stroke.widthPx) ?? void 0
7962
- } : void 0;
7963
- const rawSvgViewBox = info.svgViewBox && typeof info.svgViewBox === "object" ? info.svgViewBox : null;
7964
- const rawTextBox = info.textBox && typeof info.textBox === "object" ? info.textBox : null;
7965
- const rawInset = rawTextBox?.insetPx && typeof rawTextBox.insetPx === "object" ? rawTextBox.insetPx : null;
7966
- return {
7967
- anchor: buildWorksheetDirectImageAnchor(
7968
- info.anchor,
7969
- DEFAULT_COL_WIDTH * EMU_PER_PIXEL3,
7970
- DEFAULT_ROW_HEIGHT * EMU_PER_PIXEL3
7971
- ),
7972
- description: typeof info.description === "string" && info.description.trim() ? info.description : void 0,
7973
- fill,
7974
- flipH: typeof info.flipH === "boolean" ? info.flipH : void 0,
7975
- flipV: typeof info.flipV === "boolean" ? info.flipV : void 0,
7976
- geometry: typeof info.geometry === "string" && info.geometry.trim() ? info.geometry : "rect",
7977
- geometryAdjustments: info.geometryAdjustments && typeof info.geometryAdjustments === "object" ? Object.fromEntries(
7978
- Object.entries(info.geometryAdjustments).map(([key, value]) => [key, asFiniteNumber(value)]).filter((entry) => typeof entry[1] === "number")
7979
- ) : void 0,
7980
- hyperlink: typeof info.hyperlink === "string" && info.hyperlink.trim() ? info.hyperlink : void 0,
7981
- id: `shape-${workbookSheetIndex}-${String(info.id ?? zIndex)}`,
7982
- name: typeof info.name === "string" && info.name.trim() ? info.name : void 0,
7983
- paragraphs: normalizeWorksheetDirectShapeParagraphs(info.paragraphs, info.text),
7984
- rotationDeg: asFiniteNumber(info.rotationDeg) ?? void 0,
7985
- scaleX: asFiniteNumber(info.scaleX) ?? void 0,
7986
- scaleY: asFiniteNumber(info.scaleY) ?? void 0,
7987
- sheetIndex: workbookSheetIndex,
7988
- svgPath: typeof info.svgPath === "string" && info.svgPath.trim() ? info.svgPath : void 0,
7989
- svgViewBox: rawSvgViewBox && asFiniteNumber(rawSvgViewBox.width) !== null && asFiniteNumber(rawSvgViewBox.height) !== null ? {
7990
- height: asFiniteNumber(rawSvgViewBox.height) ?? 0,
7991
- width: asFiniteNumber(rawSvgViewBox.width) ?? 0
7992
- } : void 0,
7993
- stroke,
7994
- textBox: rawTextBox ? {
7995
- horizontalAlign: rawTextBox.horizontalAlign === "center" || rawTextBox.horizontalAlign === "left" ? rawTextBox.horizontalAlign : void 0,
7996
- insetPx: rawInset ? {
7997
- bottom: asFiniteNumber(rawInset.bottom) ?? 0,
7998
- left: asFiniteNumber(rawInset.left) ?? 0,
7999
- right: asFiniteNumber(rawInset.right) ?? 0,
8000
- top: asFiniteNumber(rawInset.top) ?? 0
8001
- } : void 0,
8002
- verticalAlign: rawTextBox.verticalAlign === "bottom" || rawTextBox.verticalAlign === "middle" || rawTextBox.verticalAlign === "top" ? rawTextBox.verticalAlign : void 0
8003
- } : void 0,
8004
- workbookSheetIndex,
8005
- zIndex
8006
- };
8007
- }
8008
- function buildWorksheetApiImage(workbookSheetIndex, row, col, info, zIndex) {
8009
- if (typeof info.source !== "string" || !info.source) {
8000
+ } catch {
8010
8001
  return null;
8011
8002
  }
8012
- const width = Math.max(1, Math.round(asFiniteNumber(info.width) ?? DEFAULT_COL_WIDTH));
8013
- const height = Math.max(1, Math.round(asFiniteNumber(info.height) ?? DEFAULT_ROW_HEIGHT));
8014
- const description = typeof info.altText === "string" && info.altText.trim() ? info.altText : void 0;
8015
- return {
8016
- anchor: {
8017
- from: {
8018
- col,
8019
- colOffsetEmu: 0,
8020
- row,
8021
- rowOffsetEmu: 0
8022
- },
8023
- kind: "one-cell",
8024
- sizeEmu: {
8025
- cx: width * EMU_PER_PIXEL3,
8026
- cy: height * EMU_PER_PIXEL3
8027
- }
8028
- },
8029
- description,
8030
- editable: false,
8031
- id: `worksheet-image-${workbookSheetIndex}-${row}-${col}-${zIndex}`,
8032
- mimeType: inferImageMimeType(info.source),
8033
- sheetIndex: workbookSheetIndex,
8034
- src: info.source,
8035
- workbookSheetIndex,
8036
- zIndex
8037
- };
8038
- }
8039
- function buildWorksheetDirectApiImage(workbookSheetIndex, info, zIndex, objectUrls) {
8040
- const mimeType = inferWorksheetDirectImageMimeType(info);
8041
- const src = createWorksheetDirectImageSource(info.data, mimeType, objectUrls);
8003
+ const src = createWorksheetDirectImageSource(data, mimeType, objectUrls);
8042
8004
  if (!src) {
8043
8005
  return null;
8044
8006
  }
8045
- const widthEmu = Math.max(EMU_PER_PIXEL3, Math.round(asFiniteNumber(info.widthEmu) ?? DEFAULT_COL_WIDTH * EMU_PER_PIXEL3));
8046
- const heightEmu = Math.max(EMU_PER_PIXEL3, Math.round(asFiniteNumber(info.heightEmu) ?? DEFAULT_ROW_HEIGHT * EMU_PER_PIXEL3));
8007
+ const zIndex = (drawing.drawingPath[0] ?? 0) + 1;
8047
8008
  return {
8048
- anchor: buildWorksheetDirectImageAnchor(info.anchor, widthEmu, heightEmu),
8009
+ anchor: dukeDrawingAnchorToXlsxAnchor(drawing.anchor),
8010
+ description: drawing.altText,
8049
8011
  editable: false,
8050
- id: `worksheet-image-${workbookSheetIndex}-${String(info.id ?? zIndex)}`,
8051
- mediaPath: typeof info.mediaPath === "string" && info.mediaPath.trim() ? info.mediaPath : void 0,
8012
+ id: `worksheet-image-${workbookSheetIndex}-${drawing.drawingPath.join("-")}`,
8013
+ mediaPath: drawing.image.mediaPath || void 0,
8052
8014
  mimeType,
8053
- name: typeof info.name === "string" && info.name.trim() ? info.name : void 0,
8015
+ name: drawing.name,
8054
8016
  sheetIndex: workbookSheetIndex,
8055
8017
  src,
8056
8018
  workbookSheetIndex,
8057
8019
  zIndex
8058
8020
  };
8059
8021
  }
8060
- function collectWorksheetBatchImages(workbook) {
8061
- const imagesByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, () => []);
8062
- for (let workbookSheetIndex = 0; workbookSheetIndex < workbook.sheetCount; workbookSheetIndex += 1) {
8063
- const worksheet = workbook.getSheet(workbookSheetIndex);
8064
- if (typeof worksheet.getRowsBatch !== "function") {
8065
- continue;
8066
- }
8067
- const usedRange = worksheet.usedRange();
8068
- const maxRow = usedRange?.[2] ?? -1;
8069
- if (maxRow < 0) {
8070
- continue;
8071
- }
8072
- let zIndex = 1;
8073
- let sheetFailed = false;
8074
- for (let startRow = 0; startRow <= maxRow; startRow += IMAGE_BATCH_ROW_COUNT) {
8075
- let rows;
8076
- try {
8077
- rows = worksheet.getRowsBatch(startRow, IMAGE_BATCH_ROW_COUNT, { includeImages: true });
8078
- } catch {
8079
- sheetFailed = true;
8080
- break;
8081
- }
8082
- if (!Array.isArray(rows)) {
8083
- continue;
8084
- }
8085
- for (const rowEntry of rows) {
8086
- const row = typeof rowEntry.index === "number" ? rowEntry.index : null;
8087
- if (row === null || !Array.isArray(rowEntry.cells)) {
8088
- continue;
8089
- }
8090
- for (const cellEntry of rowEntry.cells) {
8091
- const col = typeof cellEntry.col === "number" ? cellEntry.col : null;
8092
- if (col === null || !cellEntry.image || typeof cellEntry.image !== "object") {
8093
- continue;
8094
- }
8095
- const image = buildWorksheetApiImage(workbookSheetIndex, row, col, cellEntry.image, zIndex);
8096
- if (!image) {
8097
- continue;
8098
- }
8099
- imagesByWorkbookSheetIndex[workbookSheetIndex].push(image);
8100
- zIndex += 1;
8101
- }
8102
- }
8103
- }
8104
- if (sheetFailed) {
8105
- imagesByWorkbookSheetIndex[workbookSheetIndex] = [];
8106
- }
8107
- }
8108
- return imagesByWorkbookSheetIndex;
8109
- }
8110
8022
  function collectWorksheetApiImages(workbook, objectUrls) {
8111
8023
  const directImagesByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, () => []);
8112
- let didUseDirectImages = false;
8113
8024
  for (let workbookSheetIndex = 0; workbookSheetIndex < workbook.sheetCount; workbookSheetIndex += 1) {
8114
8025
  const worksheet = workbook.getSheet(workbookSheetIndex);
8115
- const rawImages = Array.isArray(worksheet.images) ? worksheet.images : [];
8026
+ const rawImages = worksheet.images;
8116
8027
  if (rawImages.length === 0) {
8117
8028
  continue;
8118
8029
  }
8119
- const nextImages = rawImages.map((info, index) => buildWorksheetDirectApiImage(workbookSheetIndex, info, index + 1, objectUrls)).filter((image) => Boolean(image));
8030
+ const nextImages = rawImages.filter((drawing) => !drawing.hidden && Boolean(drawing.anchor)).map((drawing) => buildWorksheetDirectApiImage(worksheet, workbookSheetIndex, drawing, objectUrls)).filter((image) => Boolean(image));
8120
8031
  if (nextImages.length > 0) {
8121
8032
  directImagesByWorkbookSheetIndex[workbookSheetIndex] = nextImages;
8122
- didUseDirectImages = true;
8123
8033
  }
8124
8034
  }
8125
- if (didUseDirectImages) {
8126
- return directImagesByWorkbookSheetIndex;
8127
- }
8128
- return collectWorksheetBatchImages(workbook);
8129
- }
8130
- function collectWorksheetApiShapes(workbook) {
8131
- return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
8132
- const worksheet = workbook.getSheet(workbookSheetIndex);
8133
- const rawShapes = Array.isArray(worksheet.shapes) ? worksheet.shapes : [];
8134
- return rawShapes.map((shape, index) => buildWorksheetDirectApiShape(workbookSheetIndex, shape, index + 1));
8135
- });
8035
+ return directImagesByWorkbookSheetIndex;
8136
8036
  }
8137
- function mergeParsedAndApiImages(parsedImages, apiImages) {
8138
- if (parsedImages.length === 0) {
8139
- return apiImages;
8140
- }
8141
- if (apiImages.length === 0) {
8142
- return parsedImages;
8037
+ function imageAnchorKey(anchor) {
8038
+ if (anchor.kind === "absolute") {
8039
+ return [
8040
+ "absolute",
8041
+ Math.round(anchor.positionEmu.x),
8042
+ Math.round(anchor.positionEmu.y),
8043
+ Math.round(anchor.sizeEmu.cx),
8044
+ Math.round(anchor.sizeEmu.cy)
8045
+ ].join(":");
8143
8046
  }
8144
- const normalizeTextKey = (value) => value?.trim().toLowerCase() ?? "";
8145
- const anchorKey = (anchor) => {
8146
- if (anchor.kind === "absolute") {
8147
- return [
8148
- "absolute",
8149
- Math.round(anchor.positionEmu.x),
8150
- Math.round(anchor.positionEmu.y),
8151
- Math.round(anchor.sizeEmu.cx),
8152
- Math.round(anchor.sizeEmu.cy)
8153
- ].join(":");
8154
- }
8155
- if (anchor.kind === "one-cell") {
8156
- return [
8157
- "one",
8158
- anchor.from.col,
8159
- anchor.from.row,
8160
- Math.round(anchor.from.colOffsetEmu),
8161
- Math.round(anchor.from.rowOffsetEmu),
8162
- Math.round(anchor.sizeEmu.cx),
8163
- Math.round(anchor.sizeEmu.cy)
8164
- ].join(":");
8165
- }
8047
+ if (anchor.kind === "one-cell") {
8166
8048
  return [
8167
- "two",
8049
+ "one",
8168
8050
  anchor.from.col,
8169
8051
  anchor.from.row,
8170
8052
  Math.round(anchor.from.colOffsetEmu),
8171
8053
  Math.round(anchor.from.rowOffsetEmu),
8172
- anchor.to.col,
8173
- anchor.to.row,
8174
- Math.round(anchor.to.colOffsetEmu),
8175
- Math.round(anchor.to.rowOffsetEmu)
8054
+ Math.round(anchor.sizeEmu.cx),
8055
+ Math.round(anchor.sizeEmu.cy)
8176
8056
  ].join(":");
8177
- };
8057
+ }
8058
+ return [
8059
+ "two",
8060
+ anchor.from.col,
8061
+ anchor.from.row,
8062
+ Math.round(anchor.from.colOffsetEmu),
8063
+ Math.round(anchor.from.rowOffsetEmu),
8064
+ anchor.to.col,
8065
+ anchor.to.row,
8066
+ Math.round(anchor.to.colOffsetEmu),
8067
+ Math.round(anchor.to.rowOffsetEmu)
8068
+ ].join(":");
8069
+ }
8070
+ function collectHiddenWorksheetImageAnchors(workbook) {
8071
+ return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => new Set(
8072
+ workbook.getSheet(workbookSheetIndex).images.flatMap((drawing) => drawing.hidden && drawing.anchor ? [imageAnchorKey(dukeDrawingAnchorToXlsxAnchor(drawing.anchor))] : [])
8073
+ ));
8074
+ }
8075
+ function mergeParsedAndApiImages(parsedImages, apiImages, hiddenAnchorKeys) {
8076
+ const visibleParsedImages = parsedImages.filter((image) => !hiddenAnchorKeys.has(imageAnchorKey(image.anchor)));
8077
+ if (visibleParsedImages.length === 0) {
8078
+ return apiImages;
8079
+ }
8080
+ if (apiImages.length === 0) {
8081
+ return visibleParsedImages;
8082
+ }
8083
+ const normalizeTextKey = (value) => value?.trim().toLowerCase() ?? "";
8178
8084
  const imageKeys = (image) => {
8179
8085
  const keys = [
8180
- `${normalizeTextKey(image.mediaPath)}|${normalizeTextKey(image.name)}|${anchorKey(image.anchor)}`,
8181
- `${normalizeTextKey(image.mediaPath)}|${anchorKey(image.anchor)}`,
8182
- `${normalizeTextKey(image.name)}|${anchorKey(image.anchor)}`,
8183
- `${anchorKey(image.anchor)}`
8086
+ `${normalizeTextKey(image.mediaPath)}|${normalizeTextKey(image.name)}|${imageAnchorKey(image.anchor)}`,
8087
+ `${normalizeTextKey(image.mediaPath)}|${imageAnchorKey(image.anchor)}`,
8088
+ `${normalizeTextKey(image.name)}|${imageAnchorKey(image.anchor)}`,
8089
+ `${imageAnchorKey(image.anchor)}`
8184
8090
  ];
8185
8091
  return keys.filter((key, index) => key && keys.indexOf(key) === index);
8186
8092
  };
@@ -8210,7 +8116,7 @@ function mergeParsedAndApiImages(parsedImages, apiImages) {
8210
8116
  }
8211
8117
  return null;
8212
8118
  };
8213
- const merged = parsedImages.map((image) => {
8119
+ const merged = visibleParsedImages.map((image) => {
8214
8120
  const apiImage = takeApiMatch(image);
8215
8121
  if (!apiImage) {
8216
8122
  return image;
@@ -8221,7 +8127,8 @@ function mergeParsedAndApiImages(parsedImages, apiImages) {
8221
8127
  mediaPath: apiImage.mediaPath ?? image.mediaPath,
8222
8128
  mimeType: apiImage.mimeType,
8223
8129
  name: apiImage.name ?? image.name,
8224
- src: apiImage.src
8130
+ src: apiImage.src,
8131
+ zIndex: apiImage.zIndex
8225
8132
  };
8226
8133
  });
8227
8134
  return merged;
@@ -8239,12 +8146,13 @@ function createBasicWorkbookAssets(workbook) {
8239
8146
  const objectUrls = [];
8240
8147
  return {
8241
8148
  archive: {},
8149
+ dirtyArchivePaths: /* @__PURE__ */ new Set(),
8242
8150
  formControlsByWorkbookSheetIndex: collectWorkbookFormControls(workbook),
8243
8151
  imageOriginsById: /* @__PURE__ */ new Map(),
8244
8152
  imagesByWorkbookSheetIndex: collectWorksheetApiImages(workbook, objectUrls),
8245
8153
  namedCellStyleByName: {},
8246
8154
  objectUrls,
8247
- shapesByWorkbookSheetIndex: collectWorksheetApiShapes(workbook),
8155
+ shapesByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => []),
8248
8156
  sheetOrigins: Array.from({ length: workbook.sheetCount }, () => null),
8249
8157
  sheetStatesByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => null),
8250
8158
  styleById: {},
@@ -8259,12 +8167,17 @@ function loadWorkbookImageAssets(bytes, workbook, skipXmlParsing = false) {
8259
8167
  }
8260
8168
  const parsedAssets = parseWorkbookImageAssets(bytes, workbook);
8261
8169
  const apiImagesByWorkbookSheetIndex = collectWorksheetApiImages(workbook, parsedAssets.objectUrls);
8170
+ const hiddenImageAnchorsByWorkbookSheetIndex = collectHiddenWorksheetImageAnchors(workbook);
8262
8171
  const imagesByWorkbookSheetIndex = Array.from(
8263
8172
  { length: Math.max(workbook.sheetCount, parsedAssets.imagesByWorkbookSheetIndex.length, apiImagesByWorkbookSheetIndex.length) },
8264
8173
  (_, index) => {
8265
8174
  const parsedImages = parsedAssets.imagesByWorkbookSheetIndex[index] ?? [];
8266
8175
  const apiImages = apiImagesByWorkbookSheetIndex[index] ?? [];
8267
- return mergeParsedAndApiImages(parsedImages, apiImages);
8176
+ return mergeParsedAndApiImages(
8177
+ parsedImages,
8178
+ apiImages,
8179
+ hiddenImageAnchorsByWorkbookSheetIndex[index] ?? /* @__PURE__ */ new Set()
8180
+ );
8268
8181
  }
8269
8182
  );
8270
8183
  return {
@@ -8575,7 +8488,11 @@ function useXlsxViewerController(options) {
8575
8488
  }, [skipXmlParsing]);
8576
8489
  const refreshWorkbookState = React.useCallback((targetWorkbook) => {
8577
8490
  const currentFormControls = imageAssetsRef.current?.formControlsByWorkbookSheetIndex ?? [];
8578
- const nextFormControls = refreshWorkbookFormControls(targetWorkbook, currentFormControls);
8491
+ const nextFormControls = refreshWorkbookFormControls(
8492
+ targetWorkbook,
8493
+ currentFormControls,
8494
+ imageAssetsRef.current?.themePalette
8495
+ );
8579
8496
  if (imageAssetsRef.current) {
8580
8497
  imageAssetsRef.current.formControlsByWorkbookSheetIndex = nextFormControls;
8581
8498
  }
@@ -8674,6 +8591,7 @@ function useXlsxViewerController(options) {
8674
8591
  if (maxFileSizeBytes > 0 && buffer.byteLength > maxFileSizeBytes) {
8675
8592
  throw new XlsxFileSizeLimitExceededError(buffer.byteLength, maxFileSizeBytes);
8676
8593
  }
8594
+ buffer = normalizeWorkbookArrayBuffer(buffer);
8677
8595
  const preflight = preflightWorkbookBuffer(buffer);
8678
8596
  if (preflight?.tooLarge) {
8679
8597
  throw createWorkbookTooLargeError(preflight);
@@ -8701,24 +8619,19 @@ function useXlsxViewerController(options) {
8701
8619
  if (!effectiveSkipXmlParsing && hasIncompleteWorkerChartSnapshot(snapshot)) {
8702
8620
  throw new Error("Worker chart payload incomplete");
8703
8621
  }
8704
- const workerImageAssets = effectiveSkipXmlParsing ? null : parseWorkbookImageAssets(
8705
- new Uint8Array(buffer),
8706
- snapshot.formControlsByWorkbookSheetIndex
8707
- );
8622
+ const workerImageAssets = effectiveSkipXmlParsing ? null : parseWorkbookImageAssets(new Uint8Array(buffer));
8708
8623
  if (!isCurrent || abortController.signal.aborted) {
8709
8624
  revokeWorkbookImageAssets(workerImageAssets);
8710
8625
  return;
8711
8626
  }
8712
8627
  setImageAssets(workerImageAssets);
8628
+ setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8713
8629
  setWorkbook(null);
8714
8630
  setSheets(snapshot.sheets);
8715
8631
  setChartsByWorkbookSheetIndex(snapshot.chartsByWorkbookSheetIndex);
8716
8632
  setChartsheets(snapshot.chartsheets);
8717
8633
  setTabs(snapshot.tabs);
8718
8634
  chartAssetsRef.current = null;
8719
- if (!workerImageAssets) {
8720
- setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8721
- }
8722
8635
  setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);
8723
8636
  setShouldAutoCalculate(false);
8724
8637
  setIsWorkerBacked(true);
@@ -9224,7 +9137,8 @@ function useXlsxViewerController(options) {
9224
9137
  return control2?.items?.slice() ?? [];
9225
9138
  }
9226
9139
  const control = target.worksheet.formControls[controlIndex];
9227
- const inputRange = control?.kind.kind === "listBox" || control?.kind.kind === "dropdown" ? control.kind.inputRange : void 0;
9140
+ const kind = control?.formControl.kind;
9141
+ const inputRange = kind?.kind === "listBox" || kind?.kind === "dropdown" ? kind.inputRange : void 0;
9228
9142
  if (!inputRange) {
9229
9143
  return [];
9230
9144
  }
@@ -9643,30 +9557,17 @@ function useXlsxViewerController(options) {
9643
9557
  return null;
9644
9558
  }
9645
9559
  recordHistoryBeforeMutation();
9646
- const controlIndex = target.worksheet.addFormControl(input);
9647
- const controls = target.worksheet.formControls;
9648
- const addedControl = controls[controlIndex];
9649
- if (addedControl?.kind.kind === "optionButton" && addedControl.kind.state === "checked") {
9650
- const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
9651
- for (const siblingIndex of groupIndexes) {
9652
- if (siblingIndex === controlIndex) {
9653
- continue;
9654
- }
9655
- const sibling = target.worksheet.formControls[siblingIndex];
9656
- if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9657
- target.worksheet.setFormControl(siblingIndex, {
9658
- ...formControlInputFromDukeControl(sibling),
9659
- kind: { ...sibling.kind, state: "unchecked" }
9660
- });
9661
- }
9662
- }
9560
+ target.worksheet.addDrawing(xlsxFormControlInputToDukeDrawing(input));
9561
+ const controlIndex = target.worksheet.formControlCount - 1;
9562
+ const addedControl = target.worksheet.formControls[controlIndex];
9563
+ const addedKind = addedControl?.formControl.kind;
9564
+ if (addedControl && (addedKind?.kind === "checkbox" || addedKind?.kind === "optionButton")) {
9565
+ target.worksheet.setFormControlCheckState(addedControl.drawingPath, addedKind.state);
9566
+ } else if (addedKind?.kind === "groupBox") {
9567
+ reconcileCheckedOptionButtons(target.worksheet);
9568
+ } else if (addedKind && "cellLink" in addedKind && addedKind.cellLink) {
9569
+ workbook.syncFormControls();
9663
9570
  }
9664
- syncFormControlLinkedCell(
9665
- workbook,
9666
- target.workbookSheetIndex,
9667
- target.worksheet.formControls,
9668
- controlIndex
9669
- );
9670
9571
  maybeRecalculateWorkbook(workbook);
9671
9572
  refreshWorkbookState(workbook);
9672
9573
  return controlIndex;
@@ -9678,34 +9579,51 @@ function useXlsxViewerController(options) {
9678
9579
  return false;
9679
9580
  }
9680
9581
  recordHistoryBeforeMutation();
9582
+ const currentInput = formControlInputFromDukeControl(currentControl);
9583
+ if (currentInput.kind !== "formControl" || !currentControl.anchor) {
9584
+ return false;
9585
+ }
9586
+ const nextAnchor = patch.anchor ? xlsxAnchorToDukeDrawingAnchor(
9587
+ patch.anchor,
9588
+ patch.editAs ?? (currentControl.anchor.type === "twoCell" ? currentControl.anchor.editAs : void 0)
9589
+ ) : patch.editAs !== void 0 && currentControl.anchor.type === "twoCell" ? { ...currentControl.anchor, editAs: patch.editAs } : currentControl.anchor;
9590
+ let nextKind = patch.kind ? xlsxFormControlKindToDukeKind(patch.kind) : dukeFormControlKindToInput(currentControl.formControl.kind);
9591
+ if (currentControl.formControl.kind.kind === "unknown" && nextKind.kind === "unknown") {
9592
+ nextKind = {
9593
+ ...nextKind,
9594
+ legacyObjectType: nextKind.legacyObjectType ?? currentControl.formControl.kind.legacyObjectType,
9595
+ rawObj: nextKind.rawObj ?? currentControl.formControl.kind.rawObj,
9596
+ rawProperties: nextKind.rawProperties ?? currentControl.formControl.kind.rawProperties
9597
+ };
9598
+ }
9681
9599
  const nextInput = {
9682
- ...formControlInputFromDukeControl(currentControl),
9683
- ...patch,
9684
- anchor: patch.anchor ? { ...patch.anchor } : { ...currentControl.anchor },
9685
- kind: patch.kind ? { ...patch.kind } : { ...currentControl.kind }
9600
+ altText: "altText" in patch ? patch.altText : currentControl.altText,
9601
+ anchor: nextAnchor,
9602
+ formControl: {
9603
+ ...currentInput.formControl,
9604
+ kind: nextKind,
9605
+ macroName: "macroName" in patch ? patch.macroName : currentControl.formControl.macroName,
9606
+ rawClientData: "rawClientData" in patch ? patch.rawClientData : currentControl.formControl.rawClientData
9607
+ },
9608
+ hidden: "hidden" in patch ? patch.hidden : currentControl.hidden,
9609
+ kind: "formControl",
9610
+ locked: "locked" in patch ? patch.locked : currentControl.locked,
9611
+ name: "name" in patch ? patch.name : currentControl.name,
9612
+ printable: "printable" in patch ? patch.printable : currentControl.printable,
9613
+ title: "title" in patch ? patch.title : currentControl.title
9686
9614
  };
9687
- if (nextInput.kind.kind === "optionButton" && nextInput.kind.state === "checked") {
9688
- const groupIndexes = resolveOptionButtonGroupIndexes(target.worksheet.formControls, controlIndex);
9689
- for (const siblingIndex of groupIndexes) {
9690
- if (siblingIndex === controlIndex) {
9691
- continue;
9692
- }
9693
- const sibling = target.worksheet.formControls[siblingIndex];
9694
- if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9695
- target.worksheet.setFormControl(siblingIndex, {
9696
- ...formControlInputFromDukeControl(sibling),
9697
- kind: { ...sibling.kind, state: "unchecked" }
9698
- });
9699
- }
9700
- }
9615
+ target.worksheet.setDrawing(currentControl.drawingPath, nextInput);
9616
+ const updatedControl = target.worksheet.formControls[controlIndex];
9617
+ const updatedKind = updatedControl?.formControl.kind;
9618
+ const interactionChanged = updatedKind ? formControlInteractionChanged(currentControl.formControl.kind, updatedKind) : false;
9619
+ const groupTopologyChanged = patch.anchor !== void 0 || updatedKind !== void 0 && updatedKind.kind !== currentControl.formControl.kind.kind && (updatedKind.kind === "groupBox" || currentControl.formControl.kind.kind === "groupBox");
9620
+ if (interactionChanged && updatedControl && (updatedKind?.kind === "checkbox" || updatedKind?.kind === "optionButton")) {
9621
+ target.worksheet.setFormControlCheckState(updatedControl.drawingPath, updatedKind.state);
9622
+ } else if (groupTopologyChanged) {
9623
+ reconcileCheckedOptionButtons(target.worksheet);
9624
+ } else if (interactionChanged && updatedKind && "cellLink" in updatedKind && updatedKind.cellLink) {
9625
+ workbook.syncFormControls();
9701
9626
  }
9702
- target.worksheet.setFormControl(controlIndex, nextInput);
9703
- syncFormControlLinkedCell(
9704
- workbook,
9705
- target.workbookSheetIndex,
9706
- target.worksheet.formControls,
9707
- controlIndex
9708
- );
9709
9627
  maybeRecalculateWorkbook(workbook);
9710
9628
  refreshWorkbookState(workbook);
9711
9629
  return true;
@@ -9716,10 +9634,16 @@ function useXlsxViewerController(options) {
9716
9634
  return false;
9717
9635
  }
9718
9636
  recordHistoryBeforeMutation();
9719
- target.worksheet.removeFormControl(controlIndex);
9637
+ const removedControl = target.worksheet.formControls[controlIndex];
9638
+ const removedKind = removedControl.formControl.kind.kind;
9639
+ target.worksheet.removeDrawing(removedControl.drawingPath);
9640
+ if (removedKind === "groupBox" || removedKind === "optionButton") {
9641
+ reconcileCheckedOptionButtons(target.worksheet);
9642
+ maybeRecalculateWorkbook(workbook);
9643
+ }
9720
9644
  refreshWorkbookState(workbook);
9721
9645
  return true;
9722
- }, [activeSheetIndex, getFormControlWorksheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9646
+ }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9723
9647
  const recordCellEditHistory = React.useCallback((cell, before, after) => {
9724
9648
  if (!activeSheet || isApplyingHistoryRef.current) {
9725
9649
  return;
@@ -17914,7 +17838,7 @@ var THUMBNAIL_MAX_COLS = 80;
17914
17838
  var THUMBNAIL_MAX_SOURCE_HEIGHT_PX = 900;
17915
17839
  var THUMBNAIL_MAX_SOURCE_WIDTH_PX = 1440;
17916
17840
  var LIVE_ZOOM_COMMIT_IDLE_MS = 48;
17917
- var WHEEL_ZOOM_SENSITIVITY = 25e-5;
17841
+ var WHEEL_ZOOM_SENSITIVITY = 25e-4;
17918
17842
  var WHEEL_LINE_DELTA_PX = 16;
17919
17843
  var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669", "#ea580c", "#db2777"];
17920
17844
  var CHART_SOURCE_HIGHLIGHT_COLOR_BY_ROLE = {
@@ -18957,6 +18881,7 @@ function resolveAxisStartOffset(actualIndex, actualIndices, sizes, indexByActual
18957
18881
  function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWidths, options) {
18958
18882
  const headerHeight = options?.headerHeight ?? HEADER_HEIGHT;
18959
18883
  const rowHeaderWidth = options?.rowHeaderWidth ?? ROW_HEADER_WIDTH;
18884
+ const anchorZoomFactor = options?.zoomFactor ?? 1;
18960
18885
  const resolveMarkerLeft = (col, colOffsetEmu) => rowHeaderWidth + resolveAxisStartOffset(
18961
18886
  col,
18962
18887
  visibleCols,
@@ -18964,7 +18889,7 @@ function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWi
18964
18889
  options?.colIndexByActual,
18965
18890
  options?.colPrefixSums,
18966
18891
  options?.actualColPrefixSums
18967
- ) + emuToPixels(colOffsetEmu);
18892
+ ) + emuToPixels(colOffsetEmu) * anchorZoomFactor;
18968
18893
  const resolveMarkerTop = (row, rowOffsetEmu) => headerHeight + resolveAxisStartOffset(
18969
18894
  row,
18970
18895
  visibleRows,
@@ -18972,21 +18897,21 @@ function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWi
18972
18897
  options?.rowIndexByActual,
18973
18898
  options?.rowPrefixSums,
18974
18899
  options?.actualRowPrefixSums
18975
- ) + emuToPixels(rowOffsetEmu);
18900
+ ) + emuToPixels(rowOffsetEmu) * anchorZoomFactor;
18976
18901
  if (anchor.kind === "absolute") {
18977
18902
  return {
18978
- height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18979
- left: rowHeaderWidth + emuToPixels(anchor.positionEmu.x),
18980
- top: headerHeight + emuToPixels(anchor.positionEmu.y),
18981
- width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18903
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy) * anchorZoomFactor),
18904
+ left: rowHeaderWidth + emuToPixels(anchor.positionEmu.x) * anchorZoomFactor,
18905
+ top: headerHeight + emuToPixels(anchor.positionEmu.y) * anchorZoomFactor,
18906
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx) * anchorZoomFactor)
18982
18907
  };
18983
18908
  }
18984
18909
  if (anchor.kind === "one-cell") {
18985
18910
  return {
18986
- height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18911
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy) * anchorZoomFactor),
18987
18912
  left: resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu),
18988
18913
  top: resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu),
18989
- width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18914
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx) * anchorZoomFactor)
18990
18915
  };
18991
18916
  }
18992
18917
  const left = resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu);
@@ -22146,7 +22071,6 @@ function buildFormControlKindInput(control) {
22146
22071
  return {
22147
22072
  caption: control.caption ?? control.label ?? "",
22148
22073
  cellLink: control.linkedCell,
22149
- firstInGroup: control.firstInGroup,
22150
22074
  kind: "optionButton",
22151
22075
  no3D: control.no3D ?? false,
22152
22076
  state: control.state === "checked" ? "checked" : "unchecked"
@@ -23192,6 +23116,7 @@ function XlsxGrid({
23192
23116
  const canvasCornerOverlayContentRef = React4.useRef(null);
23193
23117
  const canvasImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
23194
23118
  const selectionOverlayRef = React4.useRef(null);
23119
+ const canvasSelectionLayerRef = React4.useRef(null);
23195
23120
  const activeValidationOverlayRef = React4.useRef(null);
23196
23121
  const fillHandleRef = React4.useRef(null);
23197
23122
  const tableMenuRef = React4.useRef(null);
@@ -23217,9 +23142,11 @@ function XlsxGrid({
23217
23142
  const liveGestureZoomRef = React4.useRef(null);
23218
23143
  const pendingLiveGestureZoomStateRef = React4.useRef(null);
23219
23144
  const liveGestureZoomFrameRef = React4.useRef(null);
23145
+ const liveGestureZoomTransformFrameRef = React4.useRef(null);
23220
23146
  const pendingZoomAnchorRef = React4.useRef(null);
23221
23147
  const liveZoomCommitTimerRef = React4.useRef(null);
23222
23148
  const pendingLiveZoomCommitRef = React4.useRef(null);
23149
+ const gestureScrollerRectRef = React4.useRef(null);
23223
23150
  const touchPinchStateRef = React4.useRef(null);
23224
23151
  const safariPinchStartZoomRef = React4.useRef(null);
23225
23152
  const canvasTransformStyleCacheRef = React4.useRef(/* @__PURE__ */ new WeakMap());
@@ -23303,7 +23230,10 @@ function XlsxGrid({
23303
23230
  const setInteractionMode = React4.useCallback((mode) => {
23304
23231
  interactionModeRef.current = mode;
23305
23232
  }, []);
23306
- const worksheet = getActiveWorksheet();
23233
+ const worksheet = React4.useMemo(
23234
+ () => getActiveWorksheet(),
23235
+ [activeSheetIndex, getActiveWorksheet, revision]
23236
+ );
23307
23237
  const mergedRegions = React4.useMemo(() => {
23308
23238
  const regions = [];
23309
23239
  const rawMergedRegions = Array.isArray(worksheet?.mergedRegions) ? worksheet.mergedRegions : [];
@@ -23383,8 +23313,31 @@ function XlsxGrid({
23383
23313
  const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
23384
23314
  const scrollDeltaX = paintedViewport.left - nextLiveViewport.left;
23385
23315
  const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
23386
- const liveZoomTranslateX2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.x * (1 - liveZoomScale2) : 0;
23387
- const liveZoomTranslateY2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.y * (1 - liveZoomScale2) : 0;
23316
+ const resolveClampedLiveZoomAnchor = (scrollOffset, viewportSize, scrollExtent, gestureAnchor) => {
23317
+ if (!isLiveZooming2 || liveZoomScale2 === 1) {
23318
+ return gestureAnchor;
23319
+ }
23320
+ const targetScrollExtent = Math.max(viewportSize, scrollExtent * liveZoomScale2);
23321
+ const targetScrollOffset = clampValue(
23322
+ (scrollOffset + gestureAnchor) * liveZoomScale2 - gestureAnchor,
23323
+ 0,
23324
+ Math.max(0, targetScrollExtent - viewportSize)
23325
+ );
23326
+ return (scrollOffset * liveZoomScale2 - targetScrollOffset) / (1 - liveZoomScale2);
23327
+ };
23328
+ const scroller = scrollRef.current;
23329
+ const liveZoomAnchorX = resolveClampedLiveZoomAnchor(
23330
+ nextLiveViewport.left,
23331
+ nextLiveViewport.width,
23332
+ scroller?.scrollWidth ?? nextLiveViewport.width,
23333
+ currentLiveGestureZoom?.anchor.x ?? 0
23334
+ );
23335
+ const liveZoomAnchorY = resolveClampedLiveZoomAnchor(
23336
+ nextLiveViewport.top,
23337
+ nextLiveViewport.height,
23338
+ scroller?.scrollHeight ?? nextLiveViewport.height,
23339
+ currentLiveGestureZoom?.anchor.y ?? 0
23340
+ );
23388
23341
  const applyElementTransform = (element, transform, willChange, transformOrigin = null) => {
23389
23342
  if (!element) {
23390
23343
  return;
@@ -23405,7 +23358,10 @@ function XlsxGrid({
23405
23358
  willChange
23406
23359
  });
23407
23360
  };
23408
- const applyCanvasTransform = (canvas, translateX, translateY) => {
23361
+ const resolveLiveTranslation = (baseTranslation, anchor, origin) => baseTranslation * liveZoomScale2 + (anchor - origin) * (1 - liveZoomScale2);
23362
+ const applyCanvasTransform = (canvas, baseTranslateX, baseTranslateY, originX, originY, anchorX, anchorY) => {
23363
+ const translateX = resolveLiveTranslation(baseTranslateX, anchorX, originX);
23364
+ const translateY = resolveLiveTranslation(baseTranslateY, anchorY, originY);
23409
23365
  const shouldTransform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1;
23410
23366
  applyElementTransform(
23411
23367
  canvas,
@@ -23413,85 +23369,169 @@ function XlsxGrid({
23413
23369
  shouldTransform ? "transform" : ""
23414
23370
  );
23415
23371
  };
23416
- const applyOverlayTransform = (element, translateX, translateY) => {
23372
+ const applyOverlayTransform = (element, baseTranslateX, baseTranslateY, paneLeft, paneTop, anchorX, anchorY) => {
23373
+ const translateX = resolveLiveTranslation(baseTranslateX, anchorX, paneLeft);
23374
+ const translateY = resolveLiveTranslation(baseTranslateY, anchorY, paneTop);
23417
23375
  applyElementTransform(
23418
23376
  element,
23419
- liveZoomScale2 !== 1 ? `translate3d(${translateX + liveZoomTranslateX2}px, ${translateY + liveZoomTranslateY2}px, 0) scale(${liveZoomScale2})` : `translate3d(${translateX}px, ${translateY}px, 0)`,
23377
+ liveZoomScale2 !== 1 ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : `translate3d(${baseTranslateX}px, ${baseTranslateY}px, 0)`,
23420
23378
  "transform",
23421
23379
  "0 0"
23422
23380
  );
23423
23381
  };
23382
+ const liveScrollBodyViewportWidth = Math.max(0, nextLiveViewport.width - liveFrozenPaneRight);
23383
+ const liveScrollBodyViewportHeight = Math.max(0, nextLiveViewport.height - liveFrozenPaneBottom);
23384
+ const liveScrollBodyCanvasLeft = liveFrozenPaneRight - Math.min(CANVAS_SCROLL_BUFFER_PX, liveScrollBodyViewportWidth);
23385
+ const liveScrollBodyCanvasTop = liveFrozenPaneBottom - Math.min(CANVAS_SCROLL_BUFFER_PX, liveScrollBodyViewportHeight);
23386
+ const selectionLayerTranslateX = (nextLiveViewport.left + liveZoomAnchorX) * (1 - liveZoomScale2);
23387
+ const selectionLayerTranslateY = (nextLiveViewport.top + liveZoomAnchorY) * (1 - liveZoomScale2);
23388
+ const shouldTransformSelectionLayer = experimentalCanvas && isLiveZooming2 && liveZoomScale2 !== 1;
23389
+ applyElementTransform(
23390
+ canvasSelectionLayerRef.current,
23391
+ shouldTransformSelectionLayer ? `translate3d(${selectionLayerTranslateX}px, ${selectionLayerTranslateY}px, 0) scale(${liveZoomScale2})` : "",
23392
+ shouldTransformSelectionLayer ? "transform" : "",
23393
+ "0 0"
23394
+ );
23424
23395
  applyCanvasTransform(
23425
23396
  scrollBodyCanvasRef.current,
23426
- scrollDeltaX + liveZoomTranslateX2,
23427
- scrollDeltaY + liveZoomTranslateY2
23397
+ scrollDeltaX,
23398
+ scrollDeltaY,
23399
+ liveScrollBodyCanvasLeft,
23400
+ liveScrollBodyCanvasTop,
23401
+ liveZoomAnchorX,
23402
+ liveZoomAnchorY
23428
23403
  );
23429
23404
  applyCanvasTransform(
23430
23405
  topBodyCanvasRef.current,
23431
- scrollDeltaX + liveZoomTranslateX2,
23432
- liveZoomTranslateY2
23406
+ scrollDeltaX,
23407
+ 0,
23408
+ liveScrollBodyCanvasLeft,
23409
+ liveDisplayHeaderHeight,
23410
+ liveZoomAnchorX,
23411
+ 0
23433
23412
  );
23434
23413
  applyCanvasTransform(
23435
23414
  leftBodyCanvasRef.current,
23436
- liveZoomTranslateX2,
23437
- scrollDeltaY + liveZoomTranslateY2
23415
+ 0,
23416
+ scrollDeltaY,
23417
+ liveDisplayRowHeaderWidth,
23418
+ liveScrollBodyCanvasTop,
23419
+ 0,
23420
+ liveZoomAnchorY
23438
23421
  );
23439
23422
  applyCanvasTransform(
23440
23423
  cornerBodyCanvasRef.current,
23441
- liveZoomTranslateX2,
23442
- liveZoomTranslateY2
23424
+ 0,
23425
+ 0,
23426
+ liveDisplayRowHeaderWidth,
23427
+ liveDisplayHeaderHeight,
23428
+ 0,
23429
+ 0
23443
23430
  );
23444
23431
  applyCanvasTransform(
23445
23432
  topScrollHeaderCanvasRef.current,
23446
- scrollDeltaX + liveZoomTranslateX2,
23447
- liveZoomTranslateY2
23433
+ scrollDeltaX,
23434
+ 0,
23435
+ liveScrollBodyCanvasLeft,
23436
+ 0,
23437
+ liveZoomAnchorX,
23438
+ 0
23448
23439
  );
23449
23440
  applyCanvasTransform(
23450
23441
  topFrozenHeaderCanvasRef.current,
23451
- liveZoomTranslateX2,
23452
- liveZoomTranslateY2
23442
+ 0,
23443
+ 0,
23444
+ liveDisplayRowHeaderWidth,
23445
+ 0,
23446
+ 0,
23447
+ 0
23453
23448
  );
23454
23449
  applyCanvasTransform(
23455
23450
  leftScrollHeaderCanvasRef.current,
23456
- liveZoomTranslateX2,
23457
- scrollDeltaY + liveZoomTranslateY2
23451
+ 0,
23452
+ scrollDeltaY,
23453
+ 0,
23454
+ liveScrollBodyCanvasTop,
23455
+ 0,
23456
+ liveZoomAnchorY
23458
23457
  );
23459
23458
  applyCanvasTransform(
23460
23459
  leftFrozenHeaderCanvasRef.current,
23461
- liveZoomTranslateX2,
23462
- liveZoomTranslateY2
23460
+ 0,
23461
+ 0,
23462
+ 0,
23463
+ liveDisplayHeaderHeight,
23464
+ 0,
23465
+ 0
23463
23466
  );
23464
23467
  applyCanvasTransform(
23465
23468
  cornerHeaderCanvasRef.current,
23466
- liveZoomTranslateX2,
23467
- liveZoomTranslateY2
23469
+ 0,
23470
+ 0,
23471
+ 0,
23472
+ 0,
23473
+ 0,
23474
+ 0
23468
23475
  );
23469
23476
  applyOverlayTransform(
23470
23477
  canvasScrollOverlayContentRef.current,
23471
23478
  -nextLiveViewport.left - liveFrozenPaneRight,
23472
- -nextLiveViewport.top - liveFrozenPaneBottom
23479
+ -nextLiveViewport.top - liveFrozenPaneBottom,
23480
+ liveFrozenPaneRight,
23481
+ liveFrozenPaneBottom,
23482
+ liveZoomAnchorX,
23483
+ liveZoomAnchorY
23473
23484
  );
23474
23485
  applyOverlayTransform(
23475
23486
  canvasTopOverlayContentRef.current,
23476
23487
  -nextLiveViewport.left - liveFrozenPaneRight,
23477
- -liveDisplayHeaderHeight
23488
+ -liveDisplayHeaderHeight,
23489
+ liveFrozenPaneRight,
23490
+ liveDisplayHeaderHeight,
23491
+ liveZoomAnchorX,
23492
+ 0
23478
23493
  );
23479
23494
  applyOverlayTransform(
23480
23495
  canvasLeftOverlayContentRef.current,
23481
23496
  -liveDisplayRowHeaderWidth,
23482
- -nextLiveViewport.top - liveFrozenPaneBottom
23497
+ -nextLiveViewport.top - liveFrozenPaneBottom,
23498
+ liveDisplayRowHeaderWidth,
23499
+ liveFrozenPaneBottom,
23500
+ 0,
23501
+ liveZoomAnchorY
23483
23502
  );
23484
23503
  applyOverlayTransform(
23485
23504
  canvasCornerOverlayContentRef.current,
23486
23505
  -liveDisplayRowHeaderWidth,
23487
- -liveDisplayHeaderHeight
23506
+ -liveDisplayHeaderHeight,
23507
+ liveDisplayRowHeaderWidth,
23508
+ liveDisplayHeaderHeight,
23509
+ 0,
23510
+ 0
23488
23511
  );
23489
- }, [zoomScale]);
23512
+ }, [experimentalCanvas, zoomScale]);
23490
23513
  const updateLiveGestureZoomState = React4.useCallback((nextState) => {
23491
- const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
23514
+ const previousState = liveGestureZoomRef.current;
23515
+ const resolvedState = typeof nextState === "function" ? nextState(previousState) : nextState;
23492
23516
  liveGestureZoomRef.current = resolvedState;
23517
+ const shouldApplyTransformImmediately = !experimentalCanvas || previousState === null || resolvedState === null;
23518
+ if (shouldApplyTransformImmediately) {
23519
+ if (liveGestureZoomTransformFrameRef.current !== null) {
23520
+ window.cancelAnimationFrame(liveGestureZoomTransformFrameRef.current);
23521
+ liveGestureZoomTransformFrameRef.current = null;
23522
+ }
23523
+ applyCanvasViewportCompensation();
23524
+ } else if (liveGestureZoomTransformFrameRef.current === null) {
23525
+ liveGestureZoomTransformFrameRef.current = window.requestAnimationFrame(() => {
23526
+ liveGestureZoomTransformFrameRef.current = null;
23527
+ applyCanvasViewportCompensation();
23528
+ });
23529
+ }
23530
+ const shouldSyncReactState = !experimentalCanvas || previousState === null !== (resolvedState === null);
23531
+ if (!shouldSyncReactState) {
23532
+ return;
23533
+ }
23493
23534
  pendingLiveGestureZoomStateRef.current = resolvedState;
23494
- applyCanvasViewportCompensation();
23495
23535
  if (liveGestureZoomFrameRef.current !== null) {
23496
23536
  return;
23497
23537
  }
@@ -23501,7 +23541,7 @@ function XlsxGrid({
23501
23541
  pendingLiveGestureZoomStateRef.current = null;
23502
23542
  setLiveGestureZoom(pendingState);
23503
23543
  });
23504
- }, [applyCanvasViewportCompensation]);
23544
+ }, [applyCanvasViewportCompensation, experimentalCanvas]);
23505
23545
  const clearLiveZoomCommitTimer = React4.useCallback(() => {
23506
23546
  if (liveZoomCommitTimerRef.current !== null) {
23507
23547
  window.clearTimeout(liveZoomCommitTimerRef.current);
@@ -23514,6 +23554,7 @@ function XlsxGrid({
23514
23554
  pendingZoomAnchorRef.current = null;
23515
23555
  touchPinchStateRef.current = null;
23516
23556
  safariPinchStartZoomRef.current = null;
23557
+ gestureScrollerRectRef.current = null;
23517
23558
  gestureZoomScaleRef.current = committedZoomScaleRef.current;
23518
23559
  updateLiveGestureZoomState(null);
23519
23560
  }, [clearLiveZoomCommitTimer, updateLiveGestureZoomState]);
@@ -24128,6 +24169,10 @@ function XlsxGrid({
24128
24169
  window.cancelAnimationFrame(liveGestureZoomFrameRef.current);
24129
24170
  liveGestureZoomFrameRef.current = null;
24130
24171
  }
24172
+ if (liveGestureZoomTransformFrameRef.current !== null) {
24173
+ window.cancelAnimationFrame(liveGestureZoomTransformFrameRef.current);
24174
+ liveGestureZoomTransformFrameRef.current = null;
24175
+ }
24131
24176
  if (drawingViewportFrameRef.current !== null) {
24132
24177
  window.cancelAnimationFrame(drawingViewportFrameRef.current);
24133
24178
  drawingViewportFrameRef.current = null;
@@ -24391,6 +24436,7 @@ function XlsxGrid({
24391
24436
  const handleScrollerScroll = React4.useCallback((event) => {
24392
24437
  const currentScroller = event.currentTarget;
24393
24438
  cachedScrollerRectRef.current = null;
24439
+ gestureScrollerRectRef.current = null;
24394
24440
  const paintedViewport = paintedDrawingViewportRef.current;
24395
24441
  const shouldSyncDrawingViewportImmediately = Math.abs(currentScroller.scrollLeft - paintedViewport.left) > CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX || Math.abs(currentScroller.scrollTop - paintedViewport.top) > CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX;
24396
24442
  syncDrawingViewport(currentScroller, { immediate: shouldSyncDrawingViewportImmediately });
@@ -24425,7 +24471,9 @@ function XlsxGrid({
24425
24471
  return;
24426
24472
  }
24427
24473
  event.preventDefault();
24428
- const anchor = resolveEventAnchor(event.clientX, event.clientY, scroller.getBoundingClientRect());
24474
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24475
+ gestureScrollerRectRef.current = scrollerRect;
24476
+ const anchor = resolveEventAnchor(event.clientX, event.clientY, scrollerRect);
24429
24477
  updateLiveGestureZoomTarget(
24430
24478
  gestureZoomScaleRef.current * Math.exp(-delta * WHEEL_ZOOM_SENSITIVITY),
24431
24479
  anchor
@@ -24445,10 +24493,12 @@ function XlsxGrid({
24445
24493
  return;
24446
24494
  }
24447
24495
  event.preventDefault();
24496
+ const scrollerRect = scroller.getBoundingClientRect();
24497
+ gestureScrollerRectRef.current = scrollerRect;
24448
24498
  const anchor = resolveEventAnchor(
24449
24499
  (firstTouch.clientX + secondTouch.clientX) / 2,
24450
24500
  (firstTouch.clientY + secondTouch.clientY) / 2,
24451
- scroller.getBoundingClientRect()
24501
+ scrollerRect
24452
24502
  );
24453
24503
  clearLiveZoomCommitTimer();
24454
24504
  updateLiveGestureZoomState({
@@ -24476,10 +24526,12 @@ function XlsxGrid({
24476
24526
  return;
24477
24527
  }
24478
24528
  event.preventDefault();
24529
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24530
+ gestureScrollerRectRef.current = scrollerRect;
24479
24531
  const anchor = resolveEventAnchor(
24480
24532
  (firstTouch.clientX + secondTouch.clientX) / 2,
24481
24533
  (firstTouch.clientY + secondTouch.clientY) / 2,
24482
- scroller.getBoundingClientRect()
24534
+ scrollerRect
24483
24535
  );
24484
24536
  updateLiveGestureZoomTarget(
24485
24537
  pinchState.startZoomScale * distance / pinchState.startDistance,
@@ -24500,10 +24552,12 @@ function XlsxGrid({
24500
24552
  const handleGestureStart = (event) => {
24501
24553
  const gestureEvent = event;
24502
24554
  event.preventDefault();
24555
+ const scrollerRect = scroller.getBoundingClientRect();
24556
+ gestureScrollerRectRef.current = scrollerRect;
24503
24557
  const anchor = resolveEventAnchor(
24504
24558
  gestureEvent.clientX,
24505
24559
  gestureEvent.clientY,
24506
- scroller.getBoundingClientRect()
24560
+ scrollerRect
24507
24561
  );
24508
24562
  clearLiveZoomCommitTimer();
24509
24563
  safariPinchStartZoomRef.current = committedZoomScaleRef.current;
@@ -24521,10 +24575,12 @@ function XlsxGrid({
24521
24575
  return;
24522
24576
  }
24523
24577
  event.preventDefault();
24578
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24579
+ gestureScrollerRectRef.current = scrollerRect;
24524
24580
  const anchor = resolveEventAnchor(
24525
24581
  gestureEvent.clientX,
24526
24582
  gestureEvent.clientY,
24527
- scroller.getBoundingClientRect()
24583
+ scrollerRect
24528
24584
  );
24529
24585
  updateLiveGestureZoomTarget(startZoomScale * gestureEvent.scale, anchor);
24530
24586
  };
@@ -25518,7 +25574,8 @@ function XlsxGrid({
25518
25574
  headerHeight: displayHeaderHeight,
25519
25575
  rowIndexByActual,
25520
25576
  rowHeaderWidth: displayRowHeaderWidth,
25521
- rowPrefixSums
25577
+ rowPrefixSums,
25578
+ zoomFactor
25522
25579
  })
25523
25580
  })) : [],
25524
25581
  [
@@ -25536,7 +25593,8 @@ function XlsxGrid({
25536
25593
  rowPrefixSums,
25537
25594
  showImages,
25538
25595
  visibleCols,
25539
- visibleRows
25596
+ visibleRows,
25597
+ zoomFactor
25540
25598
  ]
25541
25599
  );
25542
25600
  const shapeRects = React4.useMemo(
@@ -25549,7 +25607,8 @@ function XlsxGrid({
25549
25607
  headerHeight: displayHeaderHeight,
25550
25608
  rowIndexByActual,
25551
25609
  rowHeaderWidth: displayRowHeaderWidth,
25552
- rowPrefixSums
25610
+ rowPrefixSums,
25611
+ zoomFactor
25553
25612
  }),
25554
25613
  shape
25555
25614
  })) : [],
@@ -25567,7 +25626,8 @@ function XlsxGrid({
25567
25626
  shapes,
25568
25627
  showImages,
25569
25628
  visibleCols,
25570
- visibleRows
25629
+ visibleRows,
25630
+ zoomFactor
25571
25631
  ]
25572
25632
  );
25573
25633
  const formControlRects = React4.useMemo(
@@ -25581,7 +25641,8 @@ function XlsxGrid({
25581
25641
  headerHeight: displayHeaderHeight,
25582
25642
  rowIndexByActual,
25583
25643
  rowHeaderWidth: displayRowHeaderWidth,
25584
- rowPrefixSums
25644
+ rowPrefixSums,
25645
+ zoomFactor
25585
25646
  })
25586
25647
  })) : [],
25587
25648
  [
@@ -25598,7 +25659,8 @@ function XlsxGrid({
25598
25659
  rowPrefixSums,
25599
25660
  showImages,
25600
25661
  visibleCols,
25601
- visibleRows
25662
+ visibleRows,
25663
+ zoomFactor
25602
25664
  ]
25603
25665
  );
25604
25666
  const chartRects = React4.useMemo(
@@ -25612,7 +25674,8 @@ function XlsxGrid({
25612
25674
  headerHeight: displayHeaderHeight,
25613
25675
  rowIndexByActual,
25614
25676
  rowHeaderWidth: displayRowHeaderWidth,
25615
- rowPrefixSums
25677
+ rowPrefixSums,
25678
+ zoomFactor
25616
25679
  })
25617
25680
  })) : [],
25618
25681
  [
@@ -25630,7 +25693,8 @@ function XlsxGrid({
25630
25693
  rowPrefixSums,
25631
25694
  showImages,
25632
25695
  visibleCols,
25633
- visibleRows
25696
+ visibleRows,
25697
+ zoomFactor
25634
25698
  ]
25635
25699
  );
25636
25700
  const shouldBakeCanvasStaticDrawings = Boolean(experimentalCanvas && readOnly && showImages);
@@ -27086,6 +27150,8 @@ function XlsxGrid({
27086
27150
  palette,
27087
27151
  rowHeaderWidth,
27088
27152
  rowSignature: buildRenderedAxisSignature(canvasVisibleRowItems, (item) => item.index),
27153
+ viewportLeft: drawingViewport.left,
27154
+ viewportTop: drawingViewport.top,
27089
27155
  zoomFactor
27090
27156
  };
27091
27157
  const nextHeaderCanvasSignature = {
@@ -27105,7 +27171,7 @@ function XlsxGrid({
27105
27171
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
27106
27172
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
27107
27173
  const previousPaintedViewport = paintedDrawingViewportRef.current;
27108
- const shouldRepaintBody = !(previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.colSignature === nextBodyCanvasSignature.colSignature && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.rowSignature === nextBodyCanvasSignature.rowSignature && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor);
27174
+ const shouldRepaintBody = !(previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.colSignature === nextBodyCanvasSignature.colSignature && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.rowSignature === nextBodyCanvasSignature.rowSignature && previousBodyCanvasSignature.viewportLeft === nextBodyCanvasSignature.viewportLeft && previousBodyCanvasSignature.viewportTop === nextBodyCanvasSignature.viewportTop && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor);
27109
27175
  const shouldRepaintHeaders = !(previousHeaderCanvasSignature && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth && previousHeaderCanvasSignature.colSignature === nextHeaderCanvasSignature.colSignature && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth && previousHeaderCanvasSignature.rowSignature === nextHeaderCanvasSignature.rowSignature && previousHeaderCanvasSignature.viewportLeft === nextHeaderCanvasSignature.viewportLeft && previousHeaderCanvasSignature.viewportTop === nextHeaderCanvasSignature.viewportTop && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor);
27110
27176
  const canBlitBody = Boolean(
27111
27177
  shouldRepaintBody && previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor
@@ -28332,7 +28398,8 @@ function XlsxGrid({
28332
28398
  const headerLabelLiveScale = isLiveZooming ? liveZoomScale : 1;
28333
28399
  const selectionBorderWidth = Math.max(1, zoomFactor);
28334
28400
  const shouldAnimateCanvasSelection = experimentalCanvas && enableCanvasSelectionAnimation;
28335
- const canvasSelectionTransition = shouldAnimateCanvasSelection ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1), width 120ms cubic-bezier(0.22, 1, 0.36, 1), height 120ms cubic-bezier(0.22, 1, 0.36, 1), opacity 100ms linear" : "none";
28401
+ const shouldAnimateCanvasSelectionGeometry = shouldAnimateCanvasSelection && liveGestureZoom === null;
28402
+ const canvasSelectionTransition = shouldAnimateCanvasSelectionGeometry ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1), width 120ms cubic-bezier(0.22, 1, 0.36, 1), height 120ms cubic-bezier(0.22, 1, 0.36, 1), opacity 100ms linear" : "none";
28336
28403
  const rowColSpan = renderedCols.length + 1 + (leadingColumnSpacerWidth > 0 ? 1 : 0) + (trailingColumnSpacerWidth > 0 ? 1 : 0);
28337
28404
  const gutterSeparatorShadow = `inset -1px 0 0 ${palette.border}, inset 0 -1px 0 ${palette.border}`;
28338
28405
  const maxDrawingOverlayZIndex = Math.max(
@@ -30451,81 +30518,101 @@ function XlsxGrid({
30451
30518
  ]
30452
30519
  }
30453
30520
  ),
30454
- /* @__PURE__ */ jsx3(
30521
+ /* @__PURE__ */ jsxs3(
30455
30522
  "div",
30456
30523
  {
30457
- ref: selectionOverlayRef,
30524
+ ref: canvasSelectionLayerRef,
30458
30525
  style: {
30459
- backgroundColor: selectionFill,
30460
- boxSizing: "border-box",
30461
- boxShadow: `inset 0 0 0 ${selectionBorderWidth}px ${selectionStroke}`,
30462
- contain: "layout paint",
30463
- height: resolvedSelectionOverlay?.height ?? 0,
30464
- left: resolvedSelectionOverlay?.left ?? 0,
30465
- opacity: resolvedSelectionOverlay ? 1 : 0,
30526
+ height: 0,
30527
+ left: 0,
30528
+ overflow: "visible",
30466
30529
  pointerEvents: "none",
30467
30530
  position: "absolute",
30468
- top: resolvedSelectionOverlay?.top ?? 0,
30469
- transition: canvasSelectionTransition,
30470
- visibility: resolvedSelectionOverlay ? "visible" : "hidden",
30471
- willChange: shouldAnimateCanvasSelection ? "left, top, width, height" : void 0,
30472
- width: resolvedSelectionOverlay?.width ?? 0,
30531
+ top: 0,
30532
+ transformOrigin: "0 0",
30533
+ transition: "none",
30534
+ width: 0,
30473
30535
  zIndex: 24
30474
- }
30475
- }
30476
- ),
30477
- /* @__PURE__ */ jsx3(
30478
- "div",
30479
- {
30480
- ref: activeValidationOverlayRef,
30481
- "aria-hidden": "true",
30482
- style: {
30483
- alignItems: "center",
30484
- color: palette.mutedText,
30485
- display: "inline-flex",
30486
- fontSize: 10 * zoomFactor,
30487
- fontWeight: 700,
30488
- height: 16 * zoomFactor,
30489
- justifyContent: "center",
30490
- opacity: 0,
30491
- pointerEvents: "none",
30492
- position: "absolute",
30493
- transform: "translateY(-50%)",
30494
- visibility: "hidden",
30495
- width: 12 * zoomFactor,
30496
- zIndex: 26
30497
- },
30498
- children: "\u25BE"
30499
- }
30500
- ),
30501
- /* @__PURE__ */ jsx3(
30502
- "div",
30503
- {
30504
- ref: fillHandleRef,
30505
- onPointerDown: (event) => {
30506
- if (readOnly || event.button !== 0 || !normalizedSelection || !resolvedSelectionOverlay) {
30507
- return;
30508
- }
30509
- event.preventDefault();
30510
- event.stopPropagation();
30511
- startFillDrag(event.pointerId, normalizedSelection);
30512
30536
  },
30513
- style: {
30514
- backgroundColor: selectionStroke,
30515
- border: `${Math.max(1, zoomFactor)}px solid ${palette.surface}`,
30516
- contain: "layout paint",
30517
- cursor: "crosshair",
30518
- display: !readOnly && resolvedSelectionOverlay ? "block" : "none",
30519
- height: 8 * zoomFactor,
30520
- left: resolvedSelectionOverlay ? resolvedSelectionOverlay.left + resolvedSelectionOverlay.width - 4 * zoomFactor : 0,
30521
- pointerEvents: "auto",
30522
- position: "absolute",
30523
- top: resolvedSelectionOverlay ? resolvedSelectionOverlay.top + resolvedSelectionOverlay.height - 4 * zoomFactor : 0,
30524
- transition: shouldAnimateCanvasSelection ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1)" : "none",
30525
- willChange: shouldAnimateCanvasSelection ? "left, top" : void 0,
30526
- width: 8 * zoomFactor,
30527
- zIndex: 25
30528
- }
30537
+ children: [
30538
+ /* @__PURE__ */ jsx3(
30539
+ "div",
30540
+ {
30541
+ ref: selectionOverlayRef,
30542
+ style: {
30543
+ backgroundColor: selectionFill,
30544
+ boxSizing: "border-box",
30545
+ boxShadow: `inset 0 0 0 ${selectionBorderWidth}px ${selectionStroke}`,
30546
+ contain: "layout paint",
30547
+ height: resolvedSelectionOverlay?.height ?? 0,
30548
+ left: resolvedSelectionOverlay?.left ?? 0,
30549
+ opacity: resolvedSelectionOverlay ? 1 : 0,
30550
+ pointerEvents: "none",
30551
+ position: "absolute",
30552
+ top: resolvedSelectionOverlay?.top ?? 0,
30553
+ transition: canvasSelectionTransition,
30554
+ visibility: resolvedSelectionOverlay ? "visible" : "hidden",
30555
+ willChange: shouldAnimateCanvasSelectionGeometry ? "left, top, width, height" : void 0,
30556
+ width: resolvedSelectionOverlay?.width ?? 0,
30557
+ zIndex: 24
30558
+ }
30559
+ }
30560
+ ),
30561
+ /* @__PURE__ */ jsx3(
30562
+ "div",
30563
+ {
30564
+ ref: activeValidationOverlayRef,
30565
+ "aria-hidden": "true",
30566
+ style: {
30567
+ alignItems: "center",
30568
+ color: palette.mutedText,
30569
+ display: "inline-flex",
30570
+ fontSize: 10 * zoomFactor,
30571
+ fontWeight: 700,
30572
+ height: 16 * zoomFactor,
30573
+ justifyContent: "center",
30574
+ opacity: 0,
30575
+ pointerEvents: "none",
30576
+ position: "absolute",
30577
+ transform: "translateY(-50%)",
30578
+ visibility: "hidden",
30579
+ width: 12 * zoomFactor,
30580
+ zIndex: 26
30581
+ },
30582
+ children: "\u25BE"
30583
+ }
30584
+ ),
30585
+ /* @__PURE__ */ jsx3(
30586
+ "div",
30587
+ {
30588
+ ref: fillHandleRef,
30589
+ onPointerDown: (event) => {
30590
+ if (readOnly || event.button !== 0 || !normalizedSelection || !resolvedSelectionOverlay) {
30591
+ return;
30592
+ }
30593
+ event.preventDefault();
30594
+ event.stopPropagation();
30595
+ startFillDrag(event.pointerId, normalizedSelection);
30596
+ },
30597
+ style: {
30598
+ backgroundColor: selectionStroke,
30599
+ border: `${Math.max(1, zoomFactor)}px solid ${palette.surface}`,
30600
+ contain: "layout paint",
30601
+ cursor: "crosshair",
30602
+ display: !readOnly && resolvedSelectionOverlay ? "block" : "none",
30603
+ height: 8 * zoomFactor,
30604
+ left: resolvedSelectionOverlay ? resolvedSelectionOverlay.left + resolvedSelectionOverlay.width - 4 * zoomFactor : 0,
30605
+ pointerEvents: "auto",
30606
+ position: "absolute",
30607
+ top: resolvedSelectionOverlay ? resolvedSelectionOverlay.top + resolvedSelectionOverlay.height - 4 * zoomFactor : 0,
30608
+ transition: shouldAnimateCanvasSelectionGeometry ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1)" : "none",
30609
+ willChange: shouldAnimateCanvasSelectionGeometry ? "left, top" : void 0,
30610
+ width: 8 * zoomFactor,
30611
+ zIndex: 25
30612
+ }
30613
+ }
30614
+ )
30615
+ ]
30529
30616
  }
30530
30617
  ),
30531
30618
  resizeGuide ? /* @__PURE__ */ jsx3(