@extend-ai/react-xlsx 0.14.1 → 0.15.0

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.cjs CHANGED
@@ -2006,7 +2006,7 @@ function normalizeChartExLegend(raw) {
2006
2006
  return null;
2007
2007
  }
2008
2008
  const legend = raw;
2009
- const position = typeof legend.pos === "string" ? normalizeLegendPosition(String(legend.pos)) : void 0;
2009
+ const position = typeof legend.position === "string" ? normalizeLegendPosition(String(legend.position)) : void 0;
2010
2010
  return {
2011
2011
  overlay: typeof legend.overlay === "boolean" ? legend.overlay : void 0,
2012
2012
  position,
@@ -2127,11 +2127,11 @@ function buildChartExHistogramBins(values, rawSeries, sortByFrequency) {
2127
2127
  return [];
2128
2128
  }
2129
2129
  const rawRecord = rawSeries && typeof rawSeries === "object" ? rawSeries : null;
2130
- const layoutProperties = rawRecord?.layoutPr && typeof rawRecord.layoutPr === "object" ? rawRecord.layoutPr : null;
2130
+ const layoutProperties = rawRecord?.layoutProperties && typeof rawRecord.layoutProperties === "object" ? rawRecord.layoutProperties : null;
2131
2131
  const rawBinning = layoutProperties?.binning && typeof layoutProperties.binning === "object" ? layoutProperties.binning : null;
2132
2132
  const minValue = Math.min(...values);
2133
2133
  const maxValue = Math.max(...values);
2134
- 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;
2134
+ const explicitWidth = typeof rawBinning?.binSize === "number" && Number.isFinite(rawBinning.binSize) && rawBinning.binSize > 0 ? rawBinning.binSize : void 0;
2135
2135
  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;
2136
2136
  const closedRight = rawBinning?.intervalClosed === "r" || rawBinning?.intervalClosed === "right";
2137
2137
  const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
@@ -2184,7 +2184,7 @@ function buildChartExHistogramSeries(series, rawSeries, sortByFrequency) {
2184
2184
  const layout = resolveChartExSeriesLayout(rawSeries);
2185
2185
  const rawRecord = rawSeries && typeof rawSeries === "object" ? rawSeries : null;
2186
2186
  const hasBinning = Boolean(
2187
- layout === "clusteredColumn" && rawRecord?.layoutPr && typeof rawRecord.layoutPr === "object" && rawRecord.layoutPr.binning != null
2187
+ layout === "clusteredColumn" && rawRecord?.layoutProperties && typeof rawRecord.layoutProperties === "object" && rawRecord.layoutProperties.binning != null
2188
2188
  );
2189
2189
  if (!hasBinning) {
2190
2190
  return series;
@@ -2396,7 +2396,8 @@ function collapseChartExPointSeries(chartType, series) {
2396
2396
  ];
2397
2397
  }
2398
2398
  function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex, raw, index, themePalette) {
2399
- const chart = raw && typeof raw === "object" ? raw : {};
2399
+ const drawing = raw && typeof raw === "object" ? raw : {};
2400
+ const chart = drawing.chartEx && typeof drawing.chartEx === "object" ? drawing.chartEx : {};
2400
2401
  const plotArea = chart.plotArea && typeof chart.plotArea === "object" ? chart.plotArea : {};
2401
2402
  const rawSeries = Array.isArray(plotArea.series) ? plotArea.series : [];
2402
2403
  const seriesLayouts = rawSeries.map(resolveChartExSeriesLayout);
@@ -2451,7 +2452,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2451
2452
  }
2452
2453
  ] : [];
2453
2454
  const normalizedChart = {
2454
- anchor: normalizeChartAnchor(chart.anchor),
2455
+ anchor: normalizeChartAnchor(drawing.anchor),
2455
2456
  autoTitleDeleted: void 0,
2456
2457
  axes,
2457
2458
  axisLabelColor: void 0,
@@ -2475,7 +2476,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2475
2476
  id: `chart-ex-${workbookSheetIndex}-${index}`,
2476
2477
  is3d: void 0,
2477
2478
  legend: normalizeChartExLegend(chart.legend),
2478
- name: chartTitle,
2479
+ name: typeof drawing.name === "string" ? drawing.name : chartTitle,
2479
2480
  overlap: void 0,
2480
2481
  plotVisibleOnly: void 0,
2481
2482
  raw: chart,
@@ -2503,7 +2504,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2503
2504
  view3d: void 0,
2504
2505
  wireframe: void 0,
2505
2506
  workbookSheetIndex,
2506
- zIndex: index
2507
+ zIndex: Array.isArray(drawing.drawingPath) && typeof drawing.drawingPath[0] === "number" ? drawing.drawingPath[0] + 1 : index + 1
2507
2508
  };
2508
2509
  applyBuiltinChartDefaults(normalizedChart, themePalette);
2509
2510
  return normalizedChart;
@@ -2624,10 +2625,11 @@ function normalizeChartReference(raw) {
2624
2625
  return null;
2625
2626
  }
2626
2627
  const record = raw;
2628
+ const values = Array.isArray(record.numbers) ? record.numbers : Array.isArray(record.strings) ? record.strings : void 0;
2627
2629
  return {
2628
2630
  formula: typeof record.formula === "string" ? record.formula : void 0,
2629
2631
  refType: typeof record.refType === "string" ? record.refType : void 0,
2630
- values: Array.isArray(record.values) ? record.values : void 0
2632
+ values
2631
2633
  };
2632
2634
  }
2633
2635
  function normalizeChartAxis(raw) {
@@ -2647,8 +2649,8 @@ function normalizeChartAxis(raw) {
2647
2649
  logBase: typeof axis.logBase === "number" ? axis.logBase : void 0,
2648
2650
  orientation: typeof axis.orientation === "string" ? axis.orientation : void 0,
2649
2651
  majorUnit: typeof axis.majorUnit === "number" ? axis.majorUnit : void 0,
2650
- max: typeof axis.max === "number" ? axis.max : void 0,
2651
- min: typeof axis.min === "number" ? axis.min : void 0,
2652
+ max: typeof axis.maximum === "number" ? axis.maximum : void 0,
2653
+ min: typeof axis.minimum === "number" ? axis.minimum : void 0,
2652
2654
  majorGridlines: typeof axis.majorGridlines === "boolean" ? axis.majorGridlines : void 0,
2653
2655
  majorTickMark: typeof axis.majorTickMark === "string" ? axis.majorTickMark : void 0,
2654
2656
  minorUnit: typeof axis.minorUnit === "number" ? axis.minorUnit : void 0,
@@ -2771,11 +2773,11 @@ function normalizeChartDataLabels(raw) {
2771
2773
  pointLabels: pointLabels && pointLabels.length > 0 ? pointLabels : void 0,
2772
2774
  raw: labels,
2773
2775
  showBubbleSize: typeof labels.showBubbleSize === "boolean" ? labels.showBubbleSize : void 0,
2774
- showCategoryName: typeof labels.showCategoryName === "boolean" ? labels.showCategoryName : void 0,
2776
+ showCategoryName: typeof (labels.showCategoryName ?? labels.visibilityCategoryName) === "boolean" ? Boolean(labels.showCategoryName ?? labels.visibilityCategoryName) : void 0,
2775
2777
  showLegendKey: typeof labels.showLegendKey === "boolean" ? labels.showLegendKey : void 0,
2776
2778
  showPercent: typeof labels.showPercent === "boolean" ? labels.showPercent : void 0,
2777
- showSeriesName: typeof labels.showSeriesName === "boolean" ? labels.showSeriesName : void 0,
2778
- showValue: typeof labels.showValue === "boolean" ? labels.showValue : void 0
2779
+ showSeriesName: typeof (labels.showSeriesName ?? labels.visibilitySeriesName) === "boolean" ? Boolean(labels.showSeriesName ?? labels.visibilitySeriesName) : void 0,
2780
+ showValue: typeof (labels.showValue ?? labels.visibilityValue) === "boolean" ? Boolean(labels.showValue ?? labels.visibilityValue) : void 0
2779
2781
  };
2780
2782
  }
2781
2783
  function normalizeChartAnchor(raw) {
@@ -2787,19 +2789,52 @@ function normalizeChartAnchor(raw) {
2787
2789
  };
2788
2790
  }
2789
2791
  const anchor = raw;
2790
- const fromCol = typeof anchor.fromCol === "number" ? anchor.fromCol : 0;
2791
- const fromColOffsetEmu = typeof anchor.fromColOffset === "number" ? anchor.fromColOffset : 0;
2792
- const fromRow = typeof anchor.fromRow === "number" ? anchor.fromRow : 0;
2793
- const fromRowOffsetEmu = typeof anchor.fromRowOffset === "number" ? anchor.fromRowOffset : 0;
2794
- const rawToCol = typeof anchor.toCol === "number" ? anchor.toCol : null;
2795
- const rawToColOffsetEmu = typeof anchor.toColOffset === "number" ? anchor.toColOffset : 0;
2796
- const rawToRow = typeof anchor.toRow === "number" ? anchor.toRow : null;
2797
- const rawToRowOffsetEmu = typeof anchor.toRowOffset === "number" ? anchor.toRowOffset : 0;
2798
- const hasExplicitTo = rawToCol !== null && rawToRow !== null;
2799
- const collapsedWidth = hasExplicitTo && (rawToCol < fromCol || rawToCol === fromCol && rawToColOffsetEmu <= fromColOffsetEmu);
2800
- const collapsedHeight = hasExplicitTo && (rawToRow < fromRow || rawToRow === fromRow && rawToRowOffsetEmu <= fromRowOffsetEmu);
2801
- const fallbackToCol = Math.max(fromCol + 8, 8);
2802
- const fallbackToRow = Math.max(fromRow + 15, 15);
2792
+ const from = anchor.from && typeof anchor.from === "object" ? anchor.from : null;
2793
+ if (anchor.type === "oneCell") {
2794
+ return {
2795
+ from: {
2796
+ col: typeof from?.col === "number" ? from.col : 0,
2797
+ colOffsetEmu: typeof from?.colOffsetEmu === "number" ? from.colOffsetEmu : 0,
2798
+ row: typeof from?.row === "number" ? from.row : 0,
2799
+ rowOffsetEmu: typeof from?.rowOffsetEmu === "number" ? from.rowOffsetEmu : 0
2800
+ },
2801
+ kind: "one-cell",
2802
+ sizeEmu: {
2803
+ cx: typeof anchor.widthEmu === "number" ? anchor.widthEmu : 0,
2804
+ cy: typeof anchor.heightEmu === "number" ? anchor.heightEmu : 0
2805
+ }
2806
+ };
2807
+ }
2808
+ if (anchor.type === "absolute") {
2809
+ return {
2810
+ kind: "absolute",
2811
+ positionEmu: {
2812
+ x: typeof anchor.xEmu === "number" ? anchor.xEmu : 0,
2813
+ y: typeof anchor.yEmu === "number" ? anchor.yEmu : 0
2814
+ },
2815
+ sizeEmu: {
2816
+ cx: typeof anchor.widthEmu === "number" ? anchor.widthEmu : 0,
2817
+ cy: typeof anchor.heightEmu === "number" ? anchor.heightEmu : 0
2818
+ }
2819
+ };
2820
+ }
2821
+ const to = anchor.to && typeof anchor.to === "object" ? anchor.to : null;
2822
+ const fromColValue = from?.col;
2823
+ const fromColOffsetValue = from?.colOffsetEmu;
2824
+ const fromRowValue = from?.row;
2825
+ const fromRowOffsetValue = from?.rowOffsetEmu;
2826
+ const toColValue = to?.col;
2827
+ const toColOffsetValue = to?.colOffsetEmu;
2828
+ const toRowValue = to?.row;
2829
+ const toRowOffsetValue = to?.rowOffsetEmu;
2830
+ const fromCol = typeof fromColValue === "number" ? fromColValue : 0;
2831
+ const fromColOffsetEmu = typeof fromColOffsetValue === "number" ? fromColOffsetValue : 0;
2832
+ const fromRow = typeof fromRowValue === "number" ? fromRowValue : 0;
2833
+ const fromRowOffsetEmu = typeof fromRowOffsetValue === "number" ? fromRowOffsetValue : 0;
2834
+ const rawToCol = typeof toColValue === "number" ? toColValue : 0;
2835
+ const rawToColOffsetEmu = typeof toColOffsetValue === "number" ? toColOffsetValue : 0;
2836
+ const rawToRow = typeof toRowValue === "number" ? toRowValue : 0;
2837
+ const rawToRowOffsetEmu = typeof toRowOffsetValue === "number" ? toRowOffsetValue : 0;
2803
2838
  return {
2804
2839
  kind: "two-cell",
2805
2840
  from: {
@@ -2809,10 +2844,10 @@ function normalizeChartAnchor(raw) {
2809
2844
  rowOffsetEmu: fromRowOffsetEmu
2810
2845
  },
2811
2846
  to: {
2812
- col: !hasExplicitTo || collapsedWidth ? fallbackToCol : rawToCol,
2813
- colOffsetEmu: !hasExplicitTo || collapsedWidth ? 0 : rawToColOffsetEmu,
2814
- row: !hasExplicitTo || collapsedHeight ? fallbackToRow : rawToRow,
2815
- rowOffsetEmu: !hasExplicitTo || collapsedHeight ? 0 : rawToRowOffsetEmu
2847
+ col: rawToCol,
2848
+ colOffsetEmu: rawToColOffsetEmu,
2849
+ row: rawToRow,
2850
+ rowOffsetEmu: rawToRowOffsetEmu
2816
2851
  }
2817
2852
  };
2818
2853
  }
@@ -2926,7 +2961,7 @@ function normalizeChartTypeGroup(workbook, workbookSheetIndex, chartId, raw, ind
2926
2961
  chartType: typeof group.chartType === "string" ? group.chartType : "ColumnClustered",
2927
2962
  dataLabels: normalizeChartDataLabels(group.dataLabels),
2928
2963
  gapWidth: typeof group.gapWidth === "number" && Number.isFinite(group.gapWidth) ? group.gapWidth : void 0,
2929
- is3d: typeof group.is3d === "boolean" ? group.is3d : void 0,
2964
+ is3d: typeof group.is3D === "boolean" ? group.is3D : void 0,
2930
2965
  overlap: typeof group.overlap === "number" && Number.isFinite(group.overlap) ? group.overlap : void 0,
2931
2966
  raw: group,
2932
2967
  series: rawSeries.map((entry, seriesIndex) => normalizeChartSeries(workbook, workbookSheetIndex, `${chartId}-group-${index}`, entry, seriesIndex)),
@@ -3080,20 +3115,25 @@ function applyChartOrigins(chartsByWorkbookSheetIndex, chartOriginsById, archive
3080
3115
  }
3081
3116
  }
3082
3117
  function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkbookSheetIndex, showHiddenSheets = false) {
3118
+ const excludedChartIds = /* @__PURE__ */ new Set();
3083
3119
  const chartsByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
3084
3120
  const worksheet = workbook.getSheet(workbookSheetIndex);
3085
- const rawCharts = Array.isArray(worksheet.charts) ? worksheet.charts : [];
3086
- const rawChartsEx = Array.isArray(worksheet.chartsEx) ? worksheet.chartsEx : [];
3121
+ const rawCharts = worksheet.charts;
3122
+ const rawChartsEx = worksheet.chartsEx;
3087
3123
  const visibleSheetIndex = visibleSheetIndexByWorkbookSheetIndex.get(workbookSheetIndex) ?? workbookSheetIndex;
3088
3124
  const classicCharts = rawCharts.map((rawChart, chartIndex) => {
3089
3125
  const chartId = `chart-${workbookSheetIndex}-${chartIndex}`;
3090
- const chart = rawChart && typeof rawChart === "object" ? rawChart : {};
3091
- const rawView3d = chart.view3d && typeof chart.view3d === "object" ? chart.view3d : null;
3126
+ if (rawChart.hidden || !rawChart.anchor) {
3127
+ excludedChartIds.add(chartId);
3128
+ }
3129
+ const drawing = rawChart && typeof rawChart === "object" ? rawChart : {};
3130
+ const chart = drawing.chart && typeof drawing.chart === "object" ? drawing.chart : {};
3131
+ const rawView3d = chart.view3D && typeof chart.view3D === "object" ? chart.view3D : null;
3092
3132
  const rawSeries = Array.isArray(chart.series) ? chart.series : [];
3093
3133
  const chartLevelDataLabels = normalizeChartDataLabels(chart.dataLabels);
3094
3134
  const firstSeriesDataLabels = rawSeries.length > 0 && rawSeries[0] && typeof rawSeries[0] === "object" ? normalizeChartDataLabels(rawSeries[0].dataLabels) : null;
3095
3135
  return {
3096
- anchor: normalizeChartAnchor(chart.anchor),
3136
+ anchor: normalizeChartAnchor(drawing.anchor),
3097
3137
  autoTitleDeleted: typeof chart.autoTitleDeleted === "boolean" ? chart.autoTitleDeleted : void 0,
3098
3138
  axes: Array.isArray(chart.axes) ? chart.axes.map(normalizeChartAxis).filter((value) => Boolean(value)) : [],
3099
3139
  axisLabelColor: void 0,
@@ -3114,12 +3154,12 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3114
3154
  gapWidth: typeof chart.gapWidth === "number" ? chart.gapWidth : void 0,
3115
3155
  holeSize: typeof chart.holeSize === "number" ? chart.holeSize : void 0,
3116
3156
  id: chartId,
3117
- is3d: typeof chart.is3d === "boolean" ? chart.is3d : void 0,
3157
+ is3d: typeof chart.is3D === "boolean" ? chart.is3D : void 0,
3118
3158
  legend: normalizeLegend(chart.legend) ? {
3119
3159
  ...normalizeLegend(chart.legend),
3120
3160
  position: normalizeLegendPosition(normalizeLegend(chart.legend)?.position)
3121
3161
  } : null,
3122
- name: typeof chart.name === "string" ? chart.name : void 0,
3162
+ name: typeof drawing.name === "string" ? drawing.name : void 0,
3123
3163
  overlap: typeof chart.overlap === "number" ? chart.overlap : void 0,
3124
3164
  plotVisibleOnly: typeof chart.plotVisibleOnly === "boolean" ? chart.plotVisibleOnly : void 0,
3125
3165
  raw: chart,
@@ -3153,17 +3193,23 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3153
3193
  } : void 0,
3154
3194
  wireframe: typeof chart.wireframe === "boolean" ? chart.wireframe : void 0,
3155
3195
  workbookSheetIndex,
3156
- zIndex: 200 + chartIndex
3196
+ zIndex: Array.isArray(drawing.drawingPath) && typeof drawing.drawingPath[0] === "number" ? drawing.drawingPath[0] + 1 : chartIndex + 1
3157
3197
  };
3158
3198
  });
3159
- const modernCharts = rawChartsEx.map((rawChartEx, chartExIndex) => normalizeChartExChart(
3160
- workbook,
3161
- workbookSheetIndex,
3162
- visibleSheetIndex,
3163
- rawChartEx,
3164
- chartExIndex,
3165
- imageAssets?.themePalette ?? null
3166
- ));
3199
+ const modernCharts = rawChartsEx.map((rawChartEx, chartExIndex) => {
3200
+ const chartId = `chart-ex-${workbookSheetIndex}-${chartExIndex}`;
3201
+ if (rawChartEx.hidden || !rawChartEx.anchor) {
3202
+ excludedChartIds.add(chartId);
3203
+ }
3204
+ return normalizeChartExChart(
3205
+ workbook,
3206
+ workbookSheetIndex,
3207
+ visibleSheetIndex,
3208
+ rawChartEx,
3209
+ chartExIndex,
3210
+ imageAssets?.themePalette ?? null
3211
+ );
3212
+ });
3167
3213
  return [...classicCharts, ...modernCharts];
3168
3214
  });
3169
3215
  const chartsheets = Array.isArray(workbook.chartsheets) ? workbook.chartsheets.map((entry, index) => normalizeChartsheet(entry, index)) : [];
@@ -3171,6 +3217,12 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3171
3217
  const chartOriginsById = /* @__PURE__ */ new Map();
3172
3218
  if (imageAssets) {
3173
3219
  applyChartOrigins(chartsByWorkbookSheetIndex, chartOriginsById, imageAssets.archive, imageAssets.sheetOrigins);
3220
+ for (let index = 0; index < chartsByWorkbookSheetIndex.length; index += 1) {
3221
+ chartsByWorkbookSheetIndex[index] = (chartsByWorkbookSheetIndex[index] ?? []).filter((chart) => !excludedChartIds.has(chart.id));
3222
+ }
3223
+ for (const id of excludedChartIds) {
3224
+ chartOriginsById.delete(id);
3225
+ }
3174
3226
  for (const charts of chartsByWorkbookSheetIndex) {
3175
3227
  for (const chart of charts) {
3176
3228
  applyChartStyleFromXml(chart, chart.chartPath, imageAssets.archive, imageAssets.themePalette);
@@ -3178,6 +3230,9 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3178
3230
  }
3179
3231
  }
3180
3232
  } else {
3233
+ for (let index = 0; index < chartsByWorkbookSheetIndex.length; index += 1) {
3234
+ chartsByWorkbookSheetIndex[index] = (chartsByWorkbookSheetIndex[index] ?? []).filter((chart) => !excludedChartIds.has(chart.id));
3235
+ }
3181
3236
  for (const charts of chartsByWorkbookSheetIndex) {
3182
3237
  for (const chart of charts) {
3183
3238
  applyBuiltinChartDefaults(chart, null);
@@ -3410,6 +3465,7 @@ function updateWorkbookChartAnchor(imageAssets, chartAssets, chartId, anchor) {
3410
3465
  }
3411
3466
  updateAnchorNode(anchorNode, anchor);
3412
3467
  imageAssets.archive[normalizeArchivePath(origin.drawingPath)] = (0, import_fflate.strToU8)(serializeXml(drawingDocument));
3468
+ imageAssets.dirtyArchivePaths.add(normalizeArchivePath(origin.drawingPath));
3413
3469
  return true;
3414
3470
  }
3415
3471
  function updateWorkbookChartDefinition(imageAssets, chartAssets, chartId, patch) {
@@ -3464,6 +3520,7 @@ function updateWorkbookChartDefinition(imageAssets, chartAssets, chartId, patch)
3464
3520
  );
3465
3521
  updateAxisNode(getLocalChildren(plotAreaNode, "valAx")[0] ?? null, patch.valueAxis);
3466
3522
  imageAssets.archive[normalizeArchivePath(origin.chartPath)] = (0, import_fflate.strToU8)(serializeXml(chartDocument));
3523
+ imageAssets.dirtyArchivePaths.add(normalizeArchivePath(origin.chartPath));
3467
3524
  return true;
3468
3525
  }
3469
3526
 
@@ -3641,7 +3698,6 @@ var PKG_REL_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
3641
3698
  var SPREADSHEET_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
3642
3699
  var CONTENT_TYPES_NS = "http://schemas.openxmlformats.org/package/2006/content-types";
3643
3700
  var DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing";
3644
- var VML_DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing";
3645
3701
  var IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
3646
3702
  var DRAWING_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawing+xml";
3647
3703
  var EMU_PER_PIXEL2 = 9525;
@@ -5068,71 +5124,174 @@ function parseMarker(node) {
5068
5124
  rowOffsetEmu
5069
5125
  };
5070
5126
  }
5071
- function parseSpreadsheetBooleanValue(value) {
5072
- if (value === null || value === void 0) {
5127
+ function dukeDrawingAnchorToXlsxAnchor(anchor) {
5128
+ if (anchor.type === "absolute") {
5129
+ return {
5130
+ kind: "absolute",
5131
+ positionEmu: { x: anchor.xEmu, y: anchor.yEmu },
5132
+ sizeEmu: { cx: anchor.widthEmu, cy: anchor.heightEmu }
5133
+ };
5134
+ }
5135
+ const from = {
5136
+ col: anchor.from.col,
5137
+ colOffsetEmu: anchor.from.colOffsetEmu ?? 0,
5138
+ row: anchor.from.row,
5139
+ rowOffsetEmu: anchor.from.rowOffsetEmu ?? 0
5140
+ };
5141
+ if (anchor.type === "oneCell") {
5142
+ return {
5143
+ from,
5144
+ kind: "one-cell",
5145
+ sizeEmu: { cx: anchor.widthEmu, cy: anchor.heightEmu }
5146
+ };
5147
+ }
5148
+ return {
5149
+ from,
5150
+ kind: "two-cell",
5151
+ to: {
5152
+ col: anchor.to.col,
5153
+ colOffsetEmu: anchor.to.colOffsetEmu ?? 0,
5154
+ row: anchor.to.row,
5155
+ rowOffsetEmu: anchor.to.rowOffsetEmu ?? 0
5156
+ }
5157
+ };
5158
+ }
5159
+ function xlsxAnchorToDukeDrawingAnchor(anchor, editAs) {
5160
+ if (anchor.kind === "absolute") {
5161
+ return {
5162
+ heightEmu: anchor.sizeEmu.cy,
5163
+ type: "absolute",
5164
+ widthEmu: anchor.sizeEmu.cx,
5165
+ xEmu: anchor.positionEmu.x,
5166
+ yEmu: anchor.positionEmu.y
5167
+ };
5168
+ }
5169
+ const from = {
5170
+ col: anchor.from.col,
5171
+ colOffsetEmu: anchor.from.colOffsetEmu,
5172
+ row: anchor.from.row,
5173
+ rowOffsetEmu: anchor.from.rowOffsetEmu
5174
+ };
5175
+ if (anchor.kind === "one-cell") {
5176
+ return {
5177
+ from,
5178
+ heightEmu: anchor.sizeEmu.cy,
5179
+ type: "oneCell",
5180
+ widthEmu: anchor.sizeEmu.cx
5181
+ };
5182
+ }
5183
+ return {
5184
+ editAs,
5185
+ from,
5186
+ to: {
5187
+ col: anchor.to.col,
5188
+ colOffsetEmu: anchor.to.colOffsetEmu,
5189
+ row: anchor.to.row,
5190
+ rowOffsetEmu: anchor.to.rowOffsetEmu
5191
+ },
5192
+ type: "twoCell"
5193
+ };
5194
+ }
5195
+ function mapDukeDrawingColor(color) {
5196
+ if (!color) {
5073
5197
  return void 0;
5074
5198
  }
5075
- const normalized = value.trim().toLowerCase();
5076
- if (!normalized) {
5077
- return true;
5199
+ switch (color.colorType) {
5200
+ case "auto":
5201
+ return { colorType: "auto" };
5202
+ case "rgb":
5203
+ return { b: color.b, colorType: "rgb", g: color.g, r: color.r };
5204
+ case "argb":
5205
+ return { a: color.a, b: color.b, colorType: "argb", g: color.g, r: color.r };
5206
+ case "theme":
5207
+ return { colorType: "theme", themeIndex: color.index, tint: color.tint };
5208
+ case "indexed":
5209
+ return { colorType: "indexed", paletteIndex: color.index };
5210
+ }
5211
+ }
5212
+ function mapDukeDrawingFont(font) {
5213
+ if (!font) {
5214
+ return void 0;
5078
5215
  }
5079
- return !["0", "false", "none", "off", "unchecked"].includes(normalized);
5216
+ return {
5217
+ bold: font.bold,
5218
+ charset: font.charset,
5219
+ color: mapDukeDrawingColor(font.color),
5220
+ family: font.family,
5221
+ italic: font.italic,
5222
+ name: font.name,
5223
+ scheme: font.scheme,
5224
+ size: font.size,
5225
+ strikethrough: font.strikethrough,
5226
+ underline: font.underline,
5227
+ verticalAlign: font.verticalAlign
5228
+ };
5080
5229
  }
5081
- function parseSpreadsheetBooleanNode(node) {
5082
- if (!node) {
5230
+ function mapDukeDrawingText(text) {
5231
+ return {
5232
+ horizontalAlignment: text.horizontalAlignment,
5233
+ runs: text.runs.map((run) => ({ font: mapDukeDrawingFont(run.font), text: run.text })),
5234
+ verticalAlignment: text.verticalAlignment
5235
+ };
5236
+ }
5237
+ function resolveDukeDrawingColor(color, themePalette) {
5238
+ if (!color) {
5083
5239
  return void 0;
5084
5240
  }
5085
- return parseSpreadsheetBooleanValue(node.getAttribute("val") ?? node.textContent);
5241
+ switch (color.colorType) {
5242
+ case "rgb":
5243
+ return resolveWorkbookColor({ rgb: [color.r, color.g, color.b].map((value) => value.toString(16).padStart(2, "0")).join("") }) ?? void 0;
5244
+ case "argb":
5245
+ return resolveWorkbookColor({ argb: [color.a, color.r, color.g, color.b].map((value) => value.toString(16).padStart(2, "0")).join("") }) ?? void 0;
5246
+ case "theme":
5247
+ return resolveWorkbookColor({ theme: color.index, tint: color.tint }, themePalette) ?? void 0;
5248
+ default:
5249
+ return void 0;
5250
+ }
5086
5251
  }
5087
- function mapDukeFormControlAnchor(anchor) {
5252
+ function resolveDukeDrawingText(text, themePalette) {
5253
+ const caption = mapDukeDrawingText(text);
5254
+ const label = normalizeControlLabel(text.runs.map((run) => run.text).join(""));
5255
+ const firstStyledRun = text.runs.find((run) => run.font)?.font;
5256
+ const horizontalAlignment = text.horizontalAlignment;
5088
5257
  return {
5089
- from: {
5090
- col: anchor.fromCol,
5091
- colOffsetEmu: anchor.fromColOffset,
5092
- row: anchor.fromRow,
5093
- rowOffsetEmu: anchor.fromRowOffset
5094
- },
5095
- kind: "two-cell",
5096
- to: {
5097
- col: anchor.toCol,
5098
- colOffsetEmu: anchor.toColOffset,
5099
- row: anchor.toRow,
5100
- rowOffsetEmu: anchor.toRowOffset
5101
- }
5258
+ caption,
5259
+ fontFamily: firstStyledRun?.name,
5260
+ fontSizePt: firstStyledRun?.size,
5261
+ label,
5262
+ textAlign: horizontalAlignment === "left" || horizontalAlignment === "center" || horizontalAlignment === "right" ? horizontalAlignment : void 0,
5263
+ textColor: resolveDukeDrawingColor(firstStyledRun?.color, themePalette)
5102
5264
  };
5103
5265
  }
5104
- function mapDukeFormControlKind(kind) {
5266
+ function mapDukeFormControlKind(kind, themePalette) {
5105
5267
  switch (kind.kind) {
5106
5268
  case "button":
5107
- return { caption: kind.caption, kind: "button", label: normalizeControlLabel(kind.caption) };
5269
+ return { ...resolveDukeDrawingText(kind.caption, themePalette), kind: "button" };
5108
5270
  case "checkbox":
5109
5271
  return {
5110
- caption: kind.caption,
5272
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5111
5273
  checked: kind.state === "checked",
5112
5274
  kind: "checkbox",
5113
- label: normalizeControlLabel(kind.caption),
5114
5275
  linkedCell: kind.cellLink,
5115
5276
  no3D: kind.no3D,
5116
5277
  state: kind.state
5117
5278
  };
5118
5279
  case "optionButton":
5119
5280
  return {
5120
- caption: kind.caption,
5281
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5121
5282
  checked: kind.state === "checked",
5122
5283
  firstInGroup: kind.firstInGroup,
5123
5284
  kind: "radio",
5124
- label: normalizeControlLabel(kind.caption),
5125
5285
  linkedCell: kind.cellLink,
5126
5286
  no3D: kind.no3D,
5127
5287
  state: kind.state
5128
5288
  };
5129
5289
  case "label":
5130
- return { caption: kind.caption, kind: "label", label: normalizeControlLabel(kind.caption) };
5290
+ return { ...resolveDukeDrawingText(kind.caption, themePalette), kind: "label" };
5131
5291
  case "groupBox":
5132
5292
  return {
5133
- caption: kind.caption,
5293
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5134
5294
  kind: "group-box",
5135
- label: normalizeControlLabel(kind.caption),
5136
5295
  no3D: kind.no3D
5137
5296
  };
5138
5297
  case "listBox":
@@ -5173,92 +5332,47 @@ function mapDukeFormControlKind(kind) {
5173
5332
  min: kind.min,
5174
5333
  value: kind.value
5175
5334
  };
5335
+ case "unknown":
5336
+ return {
5337
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5338
+ kind: kind.objectType.toLowerCase() === "editbox" ? "editbox" : "unknown",
5339
+ legacyObjectType: kind.legacyObjectType,
5340
+ objectType: kind.objectType,
5341
+ rawObj: kind.rawObj,
5342
+ rawProperties: kind.rawProperties
5343
+ };
5176
5344
  }
5177
5345
  }
5178
- function mapDukeFormControl(control, controlIndex, workbookSheetIndex) {
5346
+ function mapDukeFormControl(control, controlIndex, workbookSheetIndex, themePalette) {
5179
5347
  return {
5180
- anchor: mapDukeFormControlAnchor(control.anchor),
5348
+ altText: control.altText,
5349
+ anchor: dukeDrawingAnchorToXlsxAnchor(control.anchor),
5181
5350
  controlIndex,
5182
- dukeAnchor: { ...control.anchor },
5183
- editAs: control.anchor.editAs,
5184
- hidden: false,
5351
+ editAs: control.anchor.type === "twoCell" ? control.anchor.editAs : void 0,
5352
+ hidden: control.hidden,
5185
5353
  id: `form-control-${workbookSheetIndex}-${controlIndex}`,
5186
5354
  locked: control.locked,
5355
+ macroName: control.formControl.macroName,
5187
5356
  name: control.name,
5188
5357
  printable: control.printable,
5358
+ rawClientData: control.formControl.rawClientData,
5189
5359
  sheetIndex: workbookSheetIndex,
5360
+ title: control.title,
5190
5361
  workbookSheetIndex,
5191
- zIndex: controlIndex + 1,
5192
- ...mapDukeFormControlKind(control.kind)
5362
+ zIndex: (control.drawingPath[0] ?? controlIndex) + 1,
5363
+ ...mapDukeFormControlKind(control.formControl.kind, themePalette)
5193
5364
  };
5194
5365
  }
5195
- function collectWorkbookFormControls(workbook) {
5366
+ function collectWorkbookFormControls(workbook, themePalette) {
5196
5367
  return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
5197
5368
  try {
5198
5369
  const controls = workbook.getSheet(workbookSheetIndex).formControls;
5199
- return Array.isArray(controls) ? controls.map((control, controlIndex) => mapDukeFormControl(control, controlIndex, workbookSheetIndex)) : [];
5370
+ return Array.isArray(controls) ? controls.flatMap((control, controlIndex) => control.anchor ? [mapDukeFormControl(control, controlIndex, workbookSheetIndex, themePalette)] : []) : [];
5200
5371
  } catch {
5201
5372
  return [];
5202
5373
  }
5203
5374
  });
5204
5375
  }
5205
- function parseFormControlKind(rawType) {
5206
- const normalized = (rawType ?? "").trim().toLowerCase();
5207
- switch (normalized) {
5208
- case "button":
5209
- return "button";
5210
- case "checkbox":
5211
- return "checkbox";
5212
- case "drop":
5213
- return "dropdown";
5214
- case "editbox":
5215
- return "editbox";
5216
- case "gbox":
5217
- return "group-box";
5218
- case "label":
5219
- return "label";
5220
- case "list":
5221
- return "listbox";
5222
- case "radio":
5223
- return "radio";
5224
- case "scroll":
5225
- return "scrollbar";
5226
- case "spin":
5227
- return "spinner";
5228
- default:
5229
- return "unknown";
5230
- }
5231
- }
5232
- function parseFormControlShapeId(value) {
5233
- const match = (value ?? "").match(/(\d+)(?!.*\d)/);
5234
- if (!match) {
5235
- return null;
5236
- }
5237
- const parsed = Number(match[1]);
5238
- return Number.isFinite(parsed) ? parsed : null;
5239
- }
5240
- function parseCssDeclarationValue(styleText, property) {
5241
- if (!styleText) {
5242
- return null;
5243
- }
5244
- const pattern = new RegExp(`${property}\\s*:\\s*([^;]+)`, "i");
5245
- const match = pattern.exec(styleText);
5246
- return match?.[1]?.trim() ?? null;
5247
- }
5248
- function parseControlTextAlign(styleText) {
5249
- const value = parseCssDeclarationValue(styleText, "text-align")?.toLowerCase();
5250
- if (value === "center" || value === "right") {
5251
- return value;
5252
- }
5253
- return value === "left" ? "left" : void 0;
5254
- }
5255
- function parseVmlFontSizePt(value) {
5256
- const parsed = Number(value ?? Number.NaN);
5257
- if (!Number.isFinite(parsed) || parsed <= 0) {
5258
- return void 0;
5259
- }
5260
- return parsed > 40 ? parsed / 20 : parsed;
5261
- }
5262
5376
  function normalizeControlLabel(label) {
5263
5377
  if (!label) {
5264
5378
  return void 0;
@@ -5266,16 +5380,6 @@ function normalizeControlLabel(label) {
5266
5380
  const normalized = label.replace(/\u00a0/g, " ").replace(/\s+/g, " ").trim();
5267
5381
  return normalized.length > 0 ? normalized : void 0;
5268
5382
  }
5269
- function isPlaceholderFormControlName(name) {
5270
- const normalized = normalizeControlLabel(name)?.toLowerCase();
5271
- if (!normalized) {
5272
- return false;
5273
- }
5274
- return /^(option button|group box|check box|drop down|dropdown|list box|edit box|scroll bar|spinner|spin button|button)\s+\d+$/.test(normalized);
5275
- }
5276
- function resolveNamedFormControlLabel(name) {
5277
- return isPlaceholderFormControlName(name) ? void 0 : normalizeControlLabel(name);
5278
- }
5279
5383
  function parseAnchor(anchorNode) {
5280
5384
  if (anchorNode.localName === "anchor") {
5281
5385
  const from = parseMarker(getFirstChild(anchorNode, "from"));
@@ -5956,260 +6060,8 @@ function parseDrawingObjects(archive, contentTypes, drawingPath, objectUrls, wor
5956
6060
  shapes
5957
6061
  };
5958
6062
  }
5959
- function parseSheetFormControlNodes(archive, sheetPath) {
5960
- const sheetXml = readArchiveText2(archive, sheetPath);
5961
- if (!sheetXml) {
5962
- return [];
5963
- }
5964
- const sheetDocument = parseXml2(sheetXml);
5965
- if (!sheetDocument) {
5966
- return [];
5967
- }
5968
- return getLocalElements(sheetDocument, "control").map((controlNode) => ({
5969
- anchor: parseAnchor(getFirstDescendant(controlNode, "anchor") ?? controlNode),
5970
- name: controlNode.getAttribute("name") ?? void 0,
5971
- shapeId: parseFormControlShapeId(controlNode.getAttribute("shapeId"))
5972
- }));
5973
- }
5974
- function parseVmlFormControls(archive, vmlDrawingPath) {
5975
- const xml = readArchiveText2(archive, vmlDrawingPath);
5976
- if (!xml) {
5977
- return /* @__PURE__ */ new Map();
5978
- }
5979
- const document2 = parseXml2(xml);
5980
- if (!document2) {
5981
- return /* @__PURE__ */ new Map();
5982
- }
5983
- const controls = /* @__PURE__ */ new Map();
5984
- for (const shapeNode of getLocalElements(document2, "shape")) {
5985
- const clientDataNode = getFirstChild(shapeNode, "ClientData");
5986
- if (!clientDataNode) {
5987
- continue;
5988
- }
5989
- const shapeId = parseFormControlShapeId(
5990
- shapeNode.getAttributeNS("urn:schemas-microsoft-com:office:office", "spid") ?? shapeNode.getAttribute("o:spid") ?? shapeNode.getAttribute("spid") ?? shapeNode.getAttribute("id")
5991
- );
5992
- if (shapeId === null) {
5993
- continue;
5994
- }
5995
- const styleText = shapeNode.getAttribute("style");
5996
- const textboxNode = getFirstChild(shapeNode, "textbox");
5997
- const fontNode = textboxNode ? getFirstDescendant(textboxNode, "font") : null;
5998
- const textContainerNode = textboxNode ? getFirstDescendant(textboxNode, "div") : null;
5999
- const label = normalizeControlLabel(textboxNode?.textContent);
6000
- const zIndex = Number(parseCssDeclarationValue(styleText, "z-index") ?? Number.NaN);
6001
- controls.set(shapeId, {
6002
- checked: parseSpreadsheetBooleanNode(getFirstChild(clientDataNode, "Checked")),
6003
- fontFamily: fontNode?.getAttribute("face") ?? void 0,
6004
- fontSizePt: parseVmlFontSizePt(fontNode?.getAttribute("size")),
6005
- hidden: (parseCssDeclarationValue(styleText, "visibility") ?? "").toLowerCase() === "hidden",
6006
- label,
6007
- linkedCell: normalizeControlLabel(getFirstChild(clientDataNode, "FmlaLink")?.textContent),
6008
- objectType: clientDataNode.getAttribute("ObjectType") ?? void 0,
6009
- shapeId,
6010
- textAlign: parseControlTextAlign(textContainerNode?.getAttribute("style")),
6011
- textColor: fontNode?.getAttribute("color") ?? void 0,
6012
- zIndex: Number.isFinite(zIndex) ? zIndex : controls.size + 1
6013
- });
6014
- }
6015
- return controls;
6016
- }
6017
- function parseSheetFormControls(archive, sheetPath, sheetRelationships, workbookSheetIndex, zIndexBase) {
6018
- const controlNodes = parseSheetFormControlNodes(archive, sheetPath);
6019
- if (controlNodes.length === 0) {
6020
- return [];
6021
- }
6022
- const legacyDrawingRelationship = [...sheetRelationships.values()].find(
6023
- (relationship) => relationship.type === VML_DRAWING_REL_TYPE
6024
- );
6025
- const vmlControlsByShapeId = legacyDrawingRelationship ? parseVmlFormControls(archive, legacyDrawingRelationship.target) : /* @__PURE__ */ new Map();
6026
- const parsedControls = [];
6027
- controlNodes.forEach((controlNode, index) => {
6028
- if (!controlNode.anchor) {
6029
- return;
6030
- }
6031
- const vmlControl = controlNode.shapeId !== null ? vmlControlsByShapeId.get(controlNode.shapeId) ?? null : null;
6032
- const kind = parseFormControlKind(vmlControl?.objectType);
6033
- parsedControls.push({
6034
- anchor: controlNode.anchor,
6035
- checked: vmlControl?.checked,
6036
- fontFamily: vmlControl?.fontFamily,
6037
- fontSizePt: vmlControl?.fontSizePt,
6038
- hidden: vmlControl?.hidden ?? false,
6039
- id: `form-control-${workbookSheetIndex}-${index}`,
6040
- kind,
6041
- label: vmlControl?.label,
6042
- linkedCell: vmlControl?.linkedCell,
6043
- name: controlNode.name,
6044
- sheetIndex: workbookSheetIndex,
6045
- textAlign: vmlControl?.textAlign,
6046
- textColor: vmlControl?.textColor,
6047
- workbookSheetIndex,
6048
- zIndex: zIndexBase + (vmlControl?.zIndex ?? index + 1)
6049
- });
6050
- });
6051
- return parsedControls.sort((left, right) => left.zIndex - right.zIndex);
6052
- }
6053
- function flattenShapeText(shape) {
6054
- const text = shape.paragraphs.flatMap((paragraph) => paragraph.runs.map((run) => run.text)).join(" ");
6055
- return normalizeControlLabel(text);
6056
- }
6057
- function rectArea(rect) {
6058
- return Math.max(0, rect.cx) * Math.max(0, rect.cy);
6059
- }
6060
- function rectIntersectionArea(left, right) {
6061
- const overlapX = Math.max(0, Math.min(left.x + left.cx, right.x + right.cx) - Math.max(left.x, right.x));
6062
- const overlapY = Math.max(0, Math.min(left.y + left.cy, right.y + right.cy) - Math.max(left.y, right.y));
6063
- return overlapX * overlapY;
6064
- }
6065
- function rectCenterDistance(left, right) {
6066
- const leftCenterX = left.x + left.cx / 2;
6067
- const leftCenterY = left.y + left.cy / 2;
6068
- const rightCenterX = right.x + right.cx / 2;
6069
- const rightCenterY = right.y + right.cy / 2;
6070
- return Math.hypot(leftCenterX - rightCenterX, leftCenterY - rightCenterY);
6071
- }
6072
- function findHiddenShapeControlMatch(control, shapes, sheetState) {
6073
- const controlRect = anchorToAbsoluteRect(control.anchor, sheetState);
6074
- const controlArea = Math.max(1, rectArea(controlRect));
6075
- const placeholderName = isPlaceholderFormControlName(control.name);
6076
- let bestMatch = null;
6077
- let bestScore = Number.NEGATIVE_INFINITY;
6078
- shapes.forEach((shape) => {
6079
- if (!shape.hidden) {
6080
- return;
6081
- }
6082
- const shapeRect = anchorToAbsoluteRect(shape.anchor, sheetState);
6083
- const shapeArea = Math.max(1, rectArea(shapeRect));
6084
- const intersectionArea = rectIntersectionArea(controlRect, shapeRect);
6085
- const overlapScore = intersectionArea / Math.min(controlArea, shapeArea);
6086
- const distance = rectCenterDistance(controlRect, shapeRect);
6087
- const maxDimension = Math.max(controlRect.cx, controlRect.cy, shapeRect.cx, shapeRect.cy, 1);
6088
- const distanceScore = distance / maxDimension;
6089
- const textLabel = flattenShapeText(shape);
6090
- const sameName = normalizeControlLabel(shape.name)?.toLowerCase() === normalizeControlLabel(control.name)?.toLowerCase();
6091
- let score = overlapScore * 4 - distanceScore;
6092
- if (sameName) {
6093
- score += 1;
6094
- }
6095
- if (control.kind === "group-box") {
6096
- score += textLabel ? -3 : 0.5;
6097
- score += shape.stroke?.none ? -1.5 : 0.75;
6098
- } else {
6099
- score += textLabel ? 2 : -1;
6100
- score += shape.stroke?.none ? 0.2 : -0.5;
6101
- }
6102
- if (!placeholderName && textLabel && textLabel === resolveNamedFormControlLabel(control.name)) {
6103
- score += 0.5;
6104
- }
6105
- if (score > bestScore) {
6106
- bestMatch = shape;
6107
- bestScore = score;
6108
- }
6109
- });
6110
- return bestScore >= 0.25 ? bestMatch : null;
6111
- }
6112
- function enrichFormControlsWithHiddenShapes(formControls, shapes, sheetState) {
6113
- return formControls.map((control) => {
6114
- const matchedShape = findHiddenShapeControlMatch(control, shapes, sheetState);
6115
- const matchedLabel = matchedShape ? flattenShapeText(matchedShape) : void 0;
6116
- const fallbackLabel = resolveNamedFormControlLabel(control.name);
6117
- let resolvedAnchor = control.anchor;
6118
- if (matchedShape && (control.kind === "group-box" || matchedLabel || isPlaceholderFormControlName(control.name))) {
6119
- resolvedAnchor = matchedShape.anchor;
6120
- }
6121
- return {
6122
- ...control,
6123
- anchor: resolvedAnchor,
6124
- label: control.label ?? matchedLabel ?? fallbackLabel
6125
- };
6126
- });
6127
- }
6128
- function getFormControlAnchorStart(anchor) {
6129
- return anchor.kind === "absolute" ? null : { col: anchor.from.col, row: anchor.from.row };
6130
- }
6131
- function scoreParsedFormControlMatch(dukeControl, parsedControl, parsedIndex) {
6132
- if (dukeControl.kind !== parsedControl.kind && parsedControl.kind !== "unknown") {
6133
- return Number.NEGATIVE_INFINITY;
6134
- }
6135
- let score = 10;
6136
- const dukeName = normalizeControlLabel(dukeControl.name)?.toLowerCase();
6137
- const parsedName = normalizeControlLabel(parsedControl.name)?.toLowerCase();
6138
- if (dukeName && parsedName) {
6139
- if (dukeName !== parsedName) {
6140
- return Number.NEGATIVE_INFINITY;
6141
- }
6142
- score += 100;
6143
- }
6144
- const dukeStart = getFormControlAnchorStart(dukeControl.anchor);
6145
- const parsedStart = getFormControlAnchorStart(parsedControl.anchor);
6146
- if (dukeStart && parsedStart && dukeStart.col === parsedStart.col && dukeStart.row === parsedStart.row) {
6147
- score += 40;
6148
- }
6149
- if (dukeControl.controlIndex === parsedIndex) {
6150
- score += 5;
6151
- }
6152
- return score;
6153
- }
6154
- function areDukeFormControlAnchorsEqual(left, right) {
6155
- if (!left || !right) {
6156
- return true;
6157
- }
6158
- 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;
6159
- }
6160
- function mergeDukeAndParsedFormControls(dukeControls, parsedControls, keepUnmatchedParsedControl = () => true) {
6161
- const usedParsedIndexes = /* @__PURE__ */ new Set();
6162
- const mergedControls = dukeControls.map((dukeControl) => {
6163
- let bestIndex = -1;
6164
- let bestScore = Number.NEGATIVE_INFINITY;
6165
- parsedControls.forEach((parsedControl2, parsedIndex) => {
6166
- if (usedParsedIndexes.has(parsedIndex)) {
6167
- return;
6168
- }
6169
- const score = scoreParsedFormControlMatch(dukeControl, parsedControl2, parsedIndex);
6170
- if (score > bestScore) {
6171
- bestIndex = parsedIndex;
6172
- bestScore = score;
6173
- }
6174
- });
6175
- const parsedControl = bestIndex >= 0 && bestScore >= 10 ? parsedControls[bestIndex] : null;
6176
- if (!parsedControl) {
6177
- return dukeControl;
6178
- }
6179
- usedParsedIndexes.add(bestIndex);
6180
- const dukeAnchorChanged = !areDukeFormControlAnchorsEqual(
6181
- parsedControl.dukeAnchor,
6182
- dukeControl.dukeAnchor
6183
- );
6184
- return {
6185
- ...parsedControl,
6186
- ...dukeControl,
6187
- anchor: dukeAnchorChanged ? dukeControl.anchor : parsedControl.anchor,
6188
- fontFamily: parsedControl.fontFamily,
6189
- fontSizePt: parsedControl.fontSizePt,
6190
- hidden: parsedControl.hidden,
6191
- id: parsedControl.id,
6192
- label: dukeControl.label ?? parsedControl.label,
6193
- name: dukeControl.name ?? parsedControl.name,
6194
- textAlign: parsedControl.textAlign,
6195
- textColor: parsedControl.textColor,
6196
- zIndex: parsedControl.zIndex
6197
- };
6198
- });
6199
- parsedControls.forEach((parsedControl, parsedIndex) => {
6200
- if (!usedParsedIndexes.has(parsedIndex) && keepUnmatchedParsedControl(parsedControl)) {
6201
- mergedControls.push(parsedControl);
6202
- }
6203
- });
6204
- return mergedControls.sort((left, right) => left.zIndex - right.zIndex);
6205
- }
6206
- function refreshWorkbookFormControls(workbook, currentControlsByWorkbookSheetIndex) {
6207
- const dukeControlsByWorkbookSheetIndex = collectWorkbookFormControls(workbook);
6208
- return dukeControlsByWorkbookSheetIndex.map((dukeControls, workbookSheetIndex) => mergeDukeAndParsedFormControls(
6209
- dukeControls,
6210
- currentControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6211
- (control) => control.controlIndex === void 0
6212
- ));
6063
+ function refreshWorkbookFormControls(workbook, _currentControlsByWorkbookSheetIndex, themePalette) {
6064
+ return collectWorkbookFormControls(workbook, themePalette);
6213
6065
  }
6214
6066
  function revokeWorkbookImageAssets(assets) {
6215
6067
  if (!assets) {
@@ -6243,7 +6095,7 @@ function parseWorkbookStructureAssetsFromArchive(archive, options) {
6243
6095
  workbookSheets
6244
6096
  };
6245
6097
  }
6246
- function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6098
+ function parseWorkbookImageAssets(bytes, workbook) {
6247
6099
  const archive = (0, import_fflate2.unzipSync)(bytes);
6248
6100
  const {
6249
6101
  contentTypes,
@@ -6262,7 +6114,7 @@ function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6262
6114
  const shapesByWorkbookSheetIndex = [];
6263
6115
  const sheetOrigins = [];
6264
6116
  const imageOriginsById = /* @__PURE__ */ new Map();
6265
- const dukeFormControlsByWorkbookSheetIndex = Array.isArray(workbookOrFormControls) ? workbookOrFormControls : collectWorkbookFormControls(workbookOrFormControls);
6117
+ const formControls = workbook ? collectWorkbookFormControls(workbook, themePalette) : [];
6266
6118
  workbookSheets.forEach((sheet, workbookSheetIndex) => {
6267
6119
  const sheetRelationships = parseRelationships(archive, relsPathForDocument(sheet.path), sheet.path);
6268
6120
  const attachments = [];
@@ -6295,23 +6147,8 @@ function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6295
6147
  mediaPaths: drawingImages.mediaPaths
6296
6148
  });
6297
6149
  }
6298
- const formControlList = parseSheetFormControls(
6299
- archive,
6300
- sheet.path,
6301
- sheetRelationships,
6302
- workbookSheetIndex,
6303
- zIndexBase
6304
- );
6305
6150
  const visibleShapeList = shapeList.filter((shape) => !shape.hidden);
6306
- const enrichedFormControlList = enrichFormControlsWithHiddenShapes(
6307
- formControlList,
6308
- shapeList,
6309
- sheetStatesByWorkbookSheetIndex[workbookSheetIndex] ?? null
6310
- );
6311
- formControlsByWorkbookSheetIndex[workbookSheetIndex] = mergeDukeAndParsedFormControls(
6312
- dukeFormControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6313
- enrichedFormControlList
6314
- );
6151
+ formControlsByWorkbookSheetIndex[workbookSheetIndex] = formControls[workbookSheetIndex] ?? [];
6315
6152
  imagesByWorkbookSheetIndex[workbookSheetIndex] = imageList;
6316
6153
  shapesByWorkbookSheetIndex[workbookSheetIndex] = visibleShapeList;
6317
6154
  sheetOrigins[workbookSheetIndex] = attachments.length > 0 ? {
@@ -6321,6 +6158,7 @@ function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6321
6158
  });
6322
6159
  return {
6323
6160
  archive,
6161
+ dirtyArchivePaths: /* @__PURE__ */ new Set(),
6324
6162
  formControlsByWorkbookSheetIndex,
6325
6163
  imageOriginsById,
6326
6164
  imagesByWorkbookSheetIndex,
@@ -6383,6 +6221,7 @@ function updateWorkbookImageAnchor(assets, imageId, anchor) {
6383
6221
  }
6384
6222
  updateAnchorNode2(anchorNode, anchor);
6385
6223
  assets.archive[attachment.drawingPath] = (0, import_fflate2.strToU8)(serializeXml2(drawingDocument));
6224
+ assets.dirtyArchivePaths.add(normalizeArchivePath2(attachment.drawingPath));
6386
6225
  const imageList = assets.imagesByWorkbookSheetIndex[origin.workbookSheetIndex] ?? [];
6387
6226
  const imageIndex = imageList.findIndex((image) => image.id === imageId);
6388
6227
  if (imageIndex >= 0) {
@@ -6499,7 +6338,7 @@ function appendSheetDrawingReference(sheetDocument, relationshipId) {
6499
6338
  worksheet.appendChild(drawingNode);
6500
6339
  }
6501
6340
  function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6502
- if (!sourceAssets || sheetOrigins.every((origin) => !origin?.attachments.length)) {
6341
+ if (!sourceAssets || sourceAssets.dirtyArchivePaths.size === 0) {
6503
6342
  return cloneBytes(savedBytes);
6504
6343
  }
6505
6344
  try {
@@ -6511,7 +6350,8 @@ function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6511
6350
  return cloneBytes(savedBytes);
6512
6351
  }
6513
6352
  sheetOrigins.forEach((origin, workbookSheetIndex) => {
6514
- if (!origin?.attachments.length) {
6353
+ 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))));
6354
+ if (!origin?.attachments.length || !hasDirtyAttachment) {
6515
6355
  return;
6516
6356
  }
6517
6357
  const currentSheet = workbookSheets[workbookSheetIndex];
@@ -6563,6 +6403,14 @@ function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6563
6403
  archive[relsPath] = (0, import_fflate2.strToU8)(serializeXml2(relDocument));
6564
6404
  mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, relsPath);
6565
6405
  });
6406
+ for (const dirtyPath of sourceAssets.dirtyArchivePaths) {
6407
+ const bytes = sourceAssets.archive[dirtyPath];
6408
+ if (!bytes) {
6409
+ continue;
6410
+ }
6411
+ archive[dirtyPath] = cloneBytes(bytes);
6412
+ mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, dirtyPath);
6413
+ }
6566
6414
  const hasDrawingOverride = getLocalElements(targetContentTypesDocument, "Override").some(
6567
6415
  (node) => node.getAttribute("ContentType") === DRAWING_CONTENT_TYPE
6568
6416
  );
@@ -6969,8 +6817,6 @@ var DEFAULT_MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
6969
6817
  var MAX_INTERACTIVE_WORKSHEET_XML_BYTES = 200 * 1024 * 1024;
6970
6818
  var MAX_INTERACTIVE_SHARED_STRINGS_BYTES = 50 * 1024 * 1024;
6971
6819
  var MAX_INTERACTIVE_TOTAL_XML_BYTES = 256 * 1024 * 1024;
6972
- var EMU_PER_PIXEL3 = 9525;
6973
- var IMAGE_BATCH_ROW_COUNT = 256;
6974
6820
  var DEFAULT_ZOOM_SCALE = 100;
6975
6821
  var MIN_ZOOM_SCALE = 10;
6976
6822
  var MAX_ZOOM_SCALE = 400;
@@ -7425,104 +7271,175 @@ function resolveWorkbookReference(workbook, defaultWorkbookSheetIndex, rawRefere
7425
7271
  return null;
7426
7272
  }
7427
7273
  }
7428
- function formControlInputFromDukeControl(control) {
7429
- return {
7430
- anchor: { ...control.anchor },
7431
- kind: { ...control.kind },
7432
- locked: control.locked,
7433
- name: control.name,
7434
- printable: control.printable
7435
- };
7274
+ function parseColorHex(hex) {
7275
+ const normalized = hex?.replace(/^#/, "");
7276
+ return normalized && /^[0-9a-f]{6}$/i.test(normalized) ? [0, 2, 4].map((offset) => Number.parseInt(normalized.slice(offset, offset + 2), 16)) : null;
7436
7277
  }
7437
- function getFormControlCellLink(kind) {
7438
- return "cellLink" in kind ? kind.cellLink : void 0;
7278
+ function xlsxColorToDukeColor(color) {
7279
+ if (!color) {
7280
+ return void 0;
7281
+ }
7282
+ const normalizedHex = color.hex?.replace(/^#/, "");
7283
+ 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);
7284
+ switch (colorType) {
7285
+ case "auto":
7286
+ return { colorType: "auto" };
7287
+ case "rgb": {
7288
+ const channels = color.r !== void 0 && color.g !== void 0 && color.b !== void 0 ? [color.r, color.g, color.b] : parseColorHex(color.hex);
7289
+ return channels ? { b: channels[2], colorType: "rgb", g: channels[1], r: channels[0] } : void 0;
7290
+ }
7291
+ case "argb": {
7292
+ const normalized = color.hex?.replace(/^#/, "");
7293
+ 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;
7294
+ return channels ? { a: channels[0], b: channels[3], colorType: "argb", g: channels[2], r: channels[1] } : void 0;
7295
+ }
7296
+ case "theme":
7297
+ return color.themeIndex === void 0 ? void 0 : { colorType: "theme", index: color.themeIndex, tint: color.tint ?? 0 };
7298
+ case "indexed":
7299
+ return color.paletteIndex === void 0 ? void 0 : { colorType: "indexed", index: color.paletteIndex };
7300
+ }
7439
7301
  }
7440
- function anchorContainsControl(group, control) {
7441
- const groupAnchor = group.anchor;
7442
- const controlAnchor = control.anchor;
7443
- return controlAnchor.fromCol >= groupAnchor.fromCol && controlAnchor.fromCol <= groupAnchor.toCol && controlAnchor.fromRow >= groupAnchor.fromRow && controlAnchor.fromRow <= groupAnchor.toRow;
7302
+ function xlsxFontToDukeFont(font) {
7303
+ return {
7304
+ bold: font.bold,
7305
+ charset: font.charset,
7306
+ color: xlsxColorToDukeColor(font.color),
7307
+ family: font.family,
7308
+ italic: font.italic,
7309
+ name: font.name,
7310
+ scheme: font.scheme,
7311
+ size: font.size,
7312
+ strikethrough: font.strikethrough,
7313
+ underline: font.underline,
7314
+ verticalAlign: font.verticalAlign
7315
+ };
7444
7316
  }
7445
- function resolveOptionButtonGroupIndexes(controls, targetIndex) {
7446
- const target = controls[targetIndex];
7447
- if (!target || target.kind.kind !== "optionButton") {
7448
- return [];
7449
- }
7450
- if (target.kind.cellLink) {
7451
- const targetCellLink = target.kind.cellLink;
7452
- return controls.flatMap((control, index) => control.kind.kind === "optionButton" && control.kind.cellLink === targetCellLink ? [index] : []);
7317
+ function xlsxCaptionToDukeText(caption) {
7318
+ if (typeof caption === "string") {
7319
+ return { runs: [{ text: caption }] };
7453
7320
  }
7454
- const containingGroups = controls.map((control, index) => ({ control, index })).filter(({ control }) => control.kind.kind === "groupBox" && anchorContainsControl(control, target)).sort((left, right) => {
7455
- const leftArea = (left.control.anchor.toCol - left.control.anchor.fromCol) * (left.control.anchor.toRow - left.control.anchor.fromRow);
7456
- const rightArea = (right.control.anchor.toCol - right.control.anchor.fromCol) * (right.control.anchor.toRow - right.control.anchor.fromRow);
7457
- return leftArea - rightArea;
7458
- });
7459
- const containingGroup = containingGroups[0]?.control;
7460
- if (containingGroup) {
7461
- return controls.flatMap((control, index) => control.kind.kind === "optionButton" && anchorContainsControl(containingGroup, control) ? [index] : []);
7321
+ return {
7322
+ horizontalAlignment: caption.horizontalAlignment,
7323
+ runs: caption.runs.map((run) => ({
7324
+ font: run.font ? xlsxFontToDukeFont(run.font) : void 0,
7325
+ text: run.text
7326
+ })),
7327
+ verticalAlignment: caption.verticalAlignment
7328
+ };
7329
+ }
7330
+ function xlsxFormControlKindToDukeKind(kind) {
7331
+ switch (kind.kind) {
7332
+ case "button":
7333
+ case "label":
7334
+ return { caption: xlsxCaptionToDukeText(kind.caption), kind: kind.kind };
7335
+ case "checkbox":
7336
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7337
+ case "optionButton":
7338
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7339
+ case "groupBox":
7340
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7341
+ case "editbox":
7342
+ return {
7343
+ caption: xlsxCaptionToDukeText(kind.caption),
7344
+ kind: "unknown",
7345
+ legacyObjectType: kind.legacyObjectType,
7346
+ objectType: "EditBox",
7347
+ rawObj: kind.rawObj,
7348
+ rawProperties: kind.rawProperties
7349
+ };
7350
+ case "unknown":
7351
+ return {
7352
+ caption: kind.caption ? xlsxCaptionToDukeText(kind.caption) : void 0,
7353
+ kind: "unknown",
7354
+ legacyObjectType: kind.legacyObjectType,
7355
+ objectType: kind.objectType,
7356
+ rawObj: kind.rawObj,
7357
+ rawProperties: kind.rawProperties
7358
+ };
7359
+ default:
7360
+ return { ...kind };
7462
7361
  }
7463
- const optionIndexes = controls.flatMap((control, index) => control.kind.kind === "optionButton" ? [index] : []);
7464
- const targetPosition = optionIndexes.indexOf(targetIndex);
7465
- if (targetPosition < 0) {
7466
- return [];
7362
+ }
7363
+ function dukeFormControlKindToInput(kind) {
7364
+ if (kind.kind === "optionButton") {
7365
+ return { ...kind, state: kind.state === "checked" ? "checked" : "unchecked" };
7467
7366
  }
7468
- let startPosition = targetPosition;
7469
- while (startPosition > 0) {
7470
- const control = controls[optionIndexes[startPosition] ?? -1];
7471
- if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7472
- break;
7473
- }
7474
- startPosition -= 1;
7367
+ if (kind.kind === "unknown") {
7368
+ return {
7369
+ caption: kind.caption,
7370
+ kind: "unknown",
7371
+ legacyObjectType: kind.legacyObjectType,
7372
+ objectType: kind.objectType,
7373
+ rawObj: kind.rawObj,
7374
+ rawProperties: kind.rawProperties
7375
+ };
7475
7376
  }
7476
- let endPosition = targetPosition + 1;
7477
- while (endPosition < optionIndexes.length) {
7478
- const control = controls[optionIndexes[endPosition] ?? -1];
7479
- if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7480
- break;
7377
+ return { ...kind };
7378
+ }
7379
+ function formControlInputFromDukeControl(control) {
7380
+ return {
7381
+ altText: control.altText,
7382
+ ...control.anchor ? { anchor: control.anchor } : { transform: control.transform },
7383
+ formControl: {
7384
+ kind: dukeFormControlKindToInput(control.formControl.kind),
7385
+ macroName: control.formControl.macroName,
7386
+ rawClientData: control.formControl.rawClientData
7387
+ },
7388
+ hidden: control.hidden,
7389
+ kind: "formControl",
7390
+ locked: control.locked,
7391
+ name: control.name,
7392
+ printable: control.printable,
7393
+ title: control.title
7394
+ };
7395
+ }
7396
+ function xlsxFormControlInputToDukeDrawing(input) {
7397
+ return {
7398
+ altText: input.altText,
7399
+ anchor: xlsxAnchorToDukeDrawingAnchor(input.anchor, input.editAs),
7400
+ formControl: {
7401
+ kind: xlsxFormControlKindToDukeKind(input.kind),
7402
+ macroName: input.macroName,
7403
+ rawClientData: input.rawClientData
7404
+ },
7405
+ hidden: input.hidden,
7406
+ kind: "formControl",
7407
+ locked: input.locked,
7408
+ name: input.name,
7409
+ printable: input.printable,
7410
+ title: input.title
7411
+ };
7412
+ }
7413
+ function reconcileCheckedOptionButtons(worksheet) {
7414
+ const checkedPaths = worksheet.formControls.flatMap((control) => control.formControl.kind.kind === "optionButton" && control.formControl.kind.state === "checked" ? [control.drawingPath] : []);
7415
+ for (const path of checkedPaths) {
7416
+ const current = worksheet.formControls.find((control) => control.drawingPath.length === path.length && control.drawingPath.every((segment, index) => segment === path[index]));
7417
+ if (current?.formControl.kind.kind === "optionButton" && current.formControl.kind.state === "checked") {
7418
+ worksheet.setFormControlCheckState(current.drawingPath, "checked");
7481
7419
  }
7482
- endPosition += 1;
7483
7420
  }
7484
- return optionIndexes.slice(startPosition, endPosition);
7485
7421
  }
7486
- function resolveLinkedCellValue(controls, controlIndex) {
7487
- const control = controls[controlIndex];
7488
- if (!control) {
7489
- return void 0;
7422
+ function formControlInteractionChanged(current, next) {
7423
+ if (current.kind !== next.kind) {
7424
+ return true;
7425
+ }
7426
+ if ("cellLink" in current && "cellLink" in next && current.cellLink !== next.cellLink) {
7427
+ return true;
7490
7428
  }
7491
- switch (control.kind.kind) {
7429
+ switch (current.kind) {
7492
7430
  case "checkbox":
7493
- return control.kind.state === "mixed" ? "#N/A" : control.kind.state === "checked";
7494
- case "optionButton": {
7495
- const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
7496
- const checkedIndex = groupIndexes.find((index) => controls[index]?.kind.kind === "optionButton" && controls[index]?.kind.state === "checked");
7497
- const groupPosition = checkedIndex === void 0 ? -1 : groupIndexes.indexOf(checkedIndex);
7498
- return groupPosition >= 0 ? groupPosition + 1 : "";
7499
- }
7431
+ case "optionButton":
7432
+ return next.kind === current.kind && current.state !== next.state;
7500
7433
  case "dropdown":
7501
- return control.kind.selected === void 0 ? "" : control.kind.selected + 1;
7434
+ return next.kind === "dropdown" && current.selected !== next.selected;
7502
7435
  case "listBox":
7503
- return control.kind.selection === "single" && control.kind.selected.length === 1 ? (control.kind.selected[0] ?? -1) + 1 : "";
7436
+ return next.kind === "listBox" && (current.selection !== next.selection || current.selected.length !== next.selected.length || current.selected.some((value, index) => value !== next.selected[index]));
7504
7437
  case "scrollbar":
7505
7438
  case "spinner":
7506
- return control.kind.value;
7439
+ return next.kind === current.kind && current.value !== next.value;
7507
7440
  default:
7508
- return void 0;
7509
- }
7510
- }
7511
- function syncFormControlLinkedCell(workbook, workbookSheetIndex, controls, controlIndex) {
7512
- const control = controls[controlIndex];
7513
- if (!control) {
7514
- return;
7515
- }
7516
- const cellLink = getFormControlCellLink(control.kind);
7517
- const value = resolveLinkedCellValue(controls, controlIndex);
7518
- if (!cellLink || value === void 0) {
7519
- return;
7520
- }
7521
- const target = resolveWorkbookReference(workbook, workbookSheetIndex, cellLink);
7522
- if (!target || target.range.start.row !== target.range.end.row || target.range.start.col !== target.range.end.col) {
7523
- return;
7441
+ return false;
7524
7442
  }
7525
- target.worksheet.setCell(cellAddressToA1(target.range.start), value);
7526
7443
  }
7527
7444
  function parseWorksheetFreezePanes(worksheet) {
7528
7445
  const rawFreezePanes = worksheet.freezePanes;
@@ -7856,9 +7773,6 @@ function scheduleLowPriorityTask(task) {
7856
7773
  const timeoutHandle = window.setTimeout(task, 0);
7857
7774
  return () => window.clearTimeout(timeoutHandle);
7858
7775
  }
7859
- function asFiniteNumber(value) {
7860
- return typeof value === "number" && Number.isFinite(value) ? value : null;
7861
- }
7862
7776
  function inferImageMimeType(source) {
7863
7777
  if (source.startsWith("data:")) {
7864
7778
  const separatorIndex = source.indexOf(";");
@@ -7882,18 +7796,27 @@ function inferImageMimeType(source) {
7882
7796
  return "image/png";
7883
7797
  }
7884
7798
  function inferWorksheetDirectImageMimeType(info) {
7885
- const format = typeof info.format === "string" ? info.format.trim().toLowerCase() : "";
7799
+ const format = info.format;
7886
7800
  if (format === "gif") {
7887
7801
  return "image/gif";
7888
7802
  }
7889
- if (format === "jpg" || format === "jpeg") {
7803
+ if (format === "jpeg") {
7890
7804
  return "image/jpeg";
7891
7805
  }
7892
7806
  if (format === "svg") {
7893
7807
  return "image/svg+xml";
7894
7808
  }
7895
- if (format === "webp") {
7896
- return "image/webp";
7809
+ if (format === "bmp") {
7810
+ return "image/bmp";
7811
+ }
7812
+ if (format === "tiff") {
7813
+ return "image/tiff";
7814
+ }
7815
+ if (format === "emf") {
7816
+ return "image/emf";
7817
+ }
7818
+ if (format === "wmf") {
7819
+ return "image/wmf";
7897
7820
  }
7898
7821
  if (format === "png") {
7899
7822
  return "image/png";
@@ -7915,323 +7838,108 @@ function createWorksheetDirectImageSource(data, mimeType, objectUrls) {
7915
7838
  objectUrls.push(objectUrl);
7916
7839
  return objectUrl;
7917
7840
  }
7918
- function buildWorksheetDirectImageAnchor(rawAnchor, widthEmu, heightEmu) {
7919
- const anchor = rawAnchor && typeof rawAnchor === "object" ? rawAnchor : {};
7920
- const fromCol = asFiniteNumber(anchor.fromCol) ?? 0;
7921
- const fromRow = asFiniteNumber(anchor.fromRow) ?? 0;
7922
- const fromColOffset = asFiniteNumber(anchor.fromColOffset) ?? 0;
7923
- const fromRowOffset = asFiniteNumber(anchor.fromRowOffset) ?? 0;
7924
- const toCol = asFiniteNumber(anchor.toCol);
7925
- const toRow = asFiniteNumber(anchor.toRow);
7926
- const toColOffset = asFiniteNumber(anchor.toColOffset) ?? 0;
7927
- const toRowOffset = asFiniteNumber(anchor.toRowOffset) ?? 0;
7928
- if (toCol !== null && toRow !== null) {
7929
- return {
7930
- from: {
7931
- col: Math.max(0, Math.round(fromCol)),
7932
- colOffsetEmu: Math.max(0, Math.round(fromColOffset)),
7933
- row: Math.max(0, Math.round(fromRow)),
7934
- rowOffsetEmu: Math.max(0, Math.round(fromRowOffset))
7935
- },
7936
- kind: "two-cell",
7937
- to: {
7938
- col: Math.max(0, Math.round(toCol)),
7939
- colOffsetEmu: Math.max(0, Math.round(toColOffset)),
7940
- row: Math.max(0, Math.round(toRow)),
7941
- rowOffsetEmu: Math.max(0, Math.round(toRowOffset))
7942
- }
7943
- };
7944
- }
7945
- return {
7946
- from: {
7947
- col: Math.max(0, Math.round(fromCol)),
7948
- colOffsetEmu: Math.max(0, Math.round(fromColOffset)),
7949
- row: Math.max(0, Math.round(fromRow)),
7950
- rowOffsetEmu: Math.max(0, Math.round(fromRowOffset))
7951
- },
7952
- kind: "one-cell",
7953
- sizeEmu: {
7954
- cx: Math.max(EMU_PER_PIXEL3, Math.round(widthEmu)),
7955
- cy: Math.max(EMU_PER_PIXEL3, Math.round(heightEmu))
7956
- }
7957
- };
7958
- }
7959
- function normalizeWorksheetDirectShapeParagraphs(rawParagraphs, fallbackText) {
7960
- const normalizedParagraphs = [];
7961
- if (Array.isArray(rawParagraphs)) {
7962
- for (const entry of rawParagraphs) {
7963
- const paragraph = entry && typeof entry === "object" ? entry : {};
7964
- const runs = [];
7965
- if (Array.isArray(paragraph.runs)) {
7966
- for (const runEntry of paragraph.runs) {
7967
- const run = runEntry && typeof runEntry === "object" ? runEntry : {};
7968
- const text2 = typeof run.text === "string" ? run.text : "";
7969
- if (!text2) {
7970
- continue;
7971
- }
7972
- runs.push({
7973
- bold: typeof run.bold === "boolean" ? run.bold : void 0,
7974
- color: typeof run.color === "string" && run.color.trim() ? run.color : void 0,
7975
- fontFamily: typeof run.fontFamily === "string" && run.fontFamily.trim() ? run.fontFamily : void 0,
7976
- fontSizePt: asFiniteNumber(run.fontSizePt) ?? void 0,
7977
- italic: typeof run.italic === "boolean" ? run.italic : void 0,
7978
- text: text2,
7979
- underline: typeof run.underline === "boolean" ? run.underline : void 0
7980
- });
7981
- }
7982
- }
7983
- if (runs.length === 0) {
7984
- continue;
7985
- }
7986
- const align = paragraph.align;
7987
- normalizedParagraphs.push({
7988
- align: align === "center" || align === "justify" || align === "left" || align === "right" ? align : void 0,
7989
- runs
7990
- });
7841
+ function buildWorksheetDirectApiImage(worksheet, workbookSheetIndex, drawing, objectUrls) {
7842
+ const hasSvgCompanion = Boolean(drawing.image.svgMediaPath);
7843
+ let mimeType = inferWorksheetDirectImageMimeType(drawing.image);
7844
+ let data;
7845
+ try {
7846
+ const svgData = drawing.image.format === "svg" || hasSvgCompanion ? worksheet.drawingSvgData(drawing.drawingPath) : void 0;
7847
+ if (svgData) {
7848
+ data = svgData;
7849
+ mimeType = "image/svg+xml";
7850
+ } else {
7851
+ data = worksheet.drawingImageData(drawing.drawingPath);
7991
7852
  }
7992
- }
7993
- if (normalizedParagraphs.length > 0) {
7994
- return normalizedParagraphs;
7995
- }
7996
- const text = typeof fallbackText === "string" ? fallbackText : "";
7997
- return text ? [{ runs: [{ text }] }] : [];
7998
- }
7999
- function buildWorksheetDirectApiShape(workbookSheetIndex, info, zIndex) {
8000
- const fill = info.fill && typeof info.fill === "object" ? {
8001
- color: typeof info.fill.color === "string" && info.fill.color.trim() ? info.fill.color : void 0,
8002
- none: typeof info.fill.none === "boolean" ? info.fill.none : void 0,
8003
- opacity: asFiniteNumber(info.fill.opacity) ?? void 0
8004
- } : void 0;
8005
- const stroke = info.stroke && typeof info.stroke === "object" ? {
8006
- color: typeof info.stroke.color === "string" && info.stroke.color.trim() ? info.stroke.color : void 0,
8007
- dash: typeof info.stroke.dash === "string" && info.stroke.dash.trim() ? info.stroke.dash : void 0,
8008
- headEndType: typeof info.stroke.headEndType === "string" && info.stroke.headEndType.trim() ? info.stroke.headEndType : void 0,
8009
- none: typeof info.stroke.none === "boolean" ? info.stroke.none : void 0,
8010
- opacity: asFiniteNumber(info.stroke.opacity) ?? void 0,
8011
- tailEndType: typeof info.stroke.tailEndType === "string" && info.stroke.tailEndType.trim() ? info.stroke.tailEndType : void 0,
8012
- widthPx: asFiniteNumber(info.stroke.widthPx) ?? void 0
8013
- } : void 0;
8014
- const rawSvgViewBox = info.svgViewBox && typeof info.svgViewBox === "object" ? info.svgViewBox : null;
8015
- const rawTextBox = info.textBox && typeof info.textBox === "object" ? info.textBox : null;
8016
- const rawInset = rawTextBox?.insetPx && typeof rawTextBox.insetPx === "object" ? rawTextBox.insetPx : null;
8017
- return {
8018
- anchor: buildWorksheetDirectImageAnchor(
8019
- info.anchor,
8020
- DEFAULT_COL_WIDTH * EMU_PER_PIXEL3,
8021
- DEFAULT_ROW_HEIGHT * EMU_PER_PIXEL3
8022
- ),
8023
- description: typeof info.description === "string" && info.description.trim() ? info.description : void 0,
8024
- fill,
8025
- flipH: typeof info.flipH === "boolean" ? info.flipH : void 0,
8026
- flipV: typeof info.flipV === "boolean" ? info.flipV : void 0,
8027
- geometry: typeof info.geometry === "string" && info.geometry.trim() ? info.geometry : "rect",
8028
- geometryAdjustments: info.geometryAdjustments && typeof info.geometryAdjustments === "object" ? Object.fromEntries(
8029
- Object.entries(info.geometryAdjustments).map(([key, value]) => [key, asFiniteNumber(value)]).filter((entry) => typeof entry[1] === "number")
8030
- ) : void 0,
8031
- hyperlink: typeof info.hyperlink === "string" && info.hyperlink.trim() ? info.hyperlink : void 0,
8032
- id: `shape-${workbookSheetIndex}-${String(info.id ?? zIndex)}`,
8033
- name: typeof info.name === "string" && info.name.trim() ? info.name : void 0,
8034
- paragraphs: normalizeWorksheetDirectShapeParagraphs(info.paragraphs, info.text),
8035
- rotationDeg: asFiniteNumber(info.rotationDeg) ?? void 0,
8036
- scaleX: asFiniteNumber(info.scaleX) ?? void 0,
8037
- scaleY: asFiniteNumber(info.scaleY) ?? void 0,
8038
- sheetIndex: workbookSheetIndex,
8039
- svgPath: typeof info.svgPath === "string" && info.svgPath.trim() ? info.svgPath : void 0,
8040
- svgViewBox: rawSvgViewBox && asFiniteNumber(rawSvgViewBox.width) !== null && asFiniteNumber(rawSvgViewBox.height) !== null ? {
8041
- height: asFiniteNumber(rawSvgViewBox.height) ?? 0,
8042
- width: asFiniteNumber(rawSvgViewBox.width) ?? 0
8043
- } : void 0,
8044
- stroke,
8045
- textBox: rawTextBox ? {
8046
- horizontalAlign: rawTextBox.horizontalAlign === "center" || rawTextBox.horizontalAlign === "left" ? rawTextBox.horizontalAlign : void 0,
8047
- insetPx: rawInset ? {
8048
- bottom: asFiniteNumber(rawInset.bottom) ?? 0,
8049
- left: asFiniteNumber(rawInset.left) ?? 0,
8050
- right: asFiniteNumber(rawInset.right) ?? 0,
8051
- top: asFiniteNumber(rawInset.top) ?? 0
8052
- } : void 0,
8053
- verticalAlign: rawTextBox.verticalAlign === "bottom" || rawTextBox.verticalAlign === "middle" || rawTextBox.verticalAlign === "top" ? rawTextBox.verticalAlign : void 0
8054
- } : void 0,
8055
- workbookSheetIndex,
8056
- zIndex
8057
- };
8058
- }
8059
- function buildWorksheetApiImage(workbookSheetIndex, row, col, info, zIndex) {
8060
- if (typeof info.source !== "string" || !info.source) {
7853
+ } catch {
8061
7854
  return null;
8062
7855
  }
8063
- const width = Math.max(1, Math.round(asFiniteNumber(info.width) ?? DEFAULT_COL_WIDTH));
8064
- const height = Math.max(1, Math.round(asFiniteNumber(info.height) ?? DEFAULT_ROW_HEIGHT));
8065
- const description = typeof info.altText === "string" && info.altText.trim() ? info.altText : void 0;
8066
- return {
8067
- anchor: {
8068
- from: {
8069
- col,
8070
- colOffsetEmu: 0,
8071
- row,
8072
- rowOffsetEmu: 0
8073
- },
8074
- kind: "one-cell",
8075
- sizeEmu: {
8076
- cx: width * EMU_PER_PIXEL3,
8077
- cy: height * EMU_PER_PIXEL3
8078
- }
8079
- },
8080
- description,
8081
- editable: false,
8082
- id: `worksheet-image-${workbookSheetIndex}-${row}-${col}-${zIndex}`,
8083
- mimeType: inferImageMimeType(info.source),
8084
- sheetIndex: workbookSheetIndex,
8085
- src: info.source,
8086
- workbookSheetIndex,
8087
- zIndex
8088
- };
8089
- }
8090
- function buildWorksheetDirectApiImage(workbookSheetIndex, info, zIndex, objectUrls) {
8091
- const mimeType = inferWorksheetDirectImageMimeType(info);
8092
- const src = createWorksheetDirectImageSource(info.data, mimeType, objectUrls);
7856
+ const src = createWorksheetDirectImageSource(data, mimeType, objectUrls);
8093
7857
  if (!src) {
8094
7858
  return null;
8095
7859
  }
8096
- const widthEmu = Math.max(EMU_PER_PIXEL3, Math.round(asFiniteNumber(info.widthEmu) ?? DEFAULT_COL_WIDTH * EMU_PER_PIXEL3));
8097
- const heightEmu = Math.max(EMU_PER_PIXEL3, Math.round(asFiniteNumber(info.heightEmu) ?? DEFAULT_ROW_HEIGHT * EMU_PER_PIXEL3));
7860
+ const zIndex = (drawing.drawingPath[0] ?? 0) + 1;
8098
7861
  return {
8099
- anchor: buildWorksheetDirectImageAnchor(info.anchor, widthEmu, heightEmu),
7862
+ anchor: dukeDrawingAnchorToXlsxAnchor(drawing.anchor),
7863
+ description: drawing.altText,
8100
7864
  editable: false,
8101
- id: `worksheet-image-${workbookSheetIndex}-${String(info.id ?? zIndex)}`,
8102
- mediaPath: typeof info.mediaPath === "string" && info.mediaPath.trim() ? info.mediaPath : void 0,
7865
+ id: `worksheet-image-${workbookSheetIndex}-${drawing.drawingPath.join("-")}`,
7866
+ mediaPath: drawing.image.mediaPath || void 0,
8103
7867
  mimeType,
8104
- name: typeof info.name === "string" && info.name.trim() ? info.name : void 0,
7868
+ name: drawing.name,
8105
7869
  sheetIndex: workbookSheetIndex,
8106
7870
  src,
8107
7871
  workbookSheetIndex,
8108
7872
  zIndex
8109
7873
  };
8110
7874
  }
8111
- function collectWorksheetBatchImages(workbook) {
8112
- const imagesByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, () => []);
8113
- for (let workbookSheetIndex = 0; workbookSheetIndex < workbook.sheetCount; workbookSheetIndex += 1) {
8114
- const worksheet = workbook.getSheet(workbookSheetIndex);
8115
- if (typeof worksheet.getRowsBatch !== "function") {
8116
- continue;
8117
- }
8118
- const usedRange = worksheet.usedRange();
8119
- const maxRow = usedRange?.[2] ?? -1;
8120
- if (maxRow < 0) {
8121
- continue;
8122
- }
8123
- let zIndex = 1;
8124
- let sheetFailed = false;
8125
- for (let startRow = 0; startRow <= maxRow; startRow += IMAGE_BATCH_ROW_COUNT) {
8126
- let rows;
8127
- try {
8128
- rows = worksheet.getRowsBatch(startRow, IMAGE_BATCH_ROW_COUNT, { includeImages: true });
8129
- } catch {
8130
- sheetFailed = true;
8131
- break;
8132
- }
8133
- if (!Array.isArray(rows)) {
8134
- continue;
8135
- }
8136
- for (const rowEntry of rows) {
8137
- const row = typeof rowEntry.index === "number" ? rowEntry.index : null;
8138
- if (row === null || !Array.isArray(rowEntry.cells)) {
8139
- continue;
8140
- }
8141
- for (const cellEntry of rowEntry.cells) {
8142
- const col = typeof cellEntry.col === "number" ? cellEntry.col : null;
8143
- if (col === null || !cellEntry.image || typeof cellEntry.image !== "object") {
8144
- continue;
8145
- }
8146
- const image = buildWorksheetApiImage(workbookSheetIndex, row, col, cellEntry.image, zIndex);
8147
- if (!image) {
8148
- continue;
8149
- }
8150
- imagesByWorkbookSheetIndex[workbookSheetIndex].push(image);
8151
- zIndex += 1;
8152
- }
8153
- }
8154
- }
8155
- if (sheetFailed) {
8156
- imagesByWorkbookSheetIndex[workbookSheetIndex] = [];
8157
- }
8158
- }
8159
- return imagesByWorkbookSheetIndex;
8160
- }
8161
7875
  function collectWorksheetApiImages(workbook, objectUrls) {
8162
7876
  const directImagesByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, () => []);
8163
- let didUseDirectImages = false;
8164
7877
  for (let workbookSheetIndex = 0; workbookSheetIndex < workbook.sheetCount; workbookSheetIndex += 1) {
8165
7878
  const worksheet = workbook.getSheet(workbookSheetIndex);
8166
- const rawImages = Array.isArray(worksheet.images) ? worksheet.images : [];
7879
+ const rawImages = worksheet.images;
8167
7880
  if (rawImages.length === 0) {
8168
7881
  continue;
8169
7882
  }
8170
- const nextImages = rawImages.map((info, index) => buildWorksheetDirectApiImage(workbookSheetIndex, info, index + 1, objectUrls)).filter((image) => Boolean(image));
7883
+ const nextImages = rawImages.filter((drawing) => !drawing.hidden && Boolean(drawing.anchor)).map((drawing) => buildWorksheetDirectApiImage(worksheet, workbookSheetIndex, drawing, objectUrls)).filter((image) => Boolean(image));
8171
7884
  if (nextImages.length > 0) {
8172
7885
  directImagesByWorkbookSheetIndex[workbookSheetIndex] = nextImages;
8173
- didUseDirectImages = true;
8174
7886
  }
8175
7887
  }
8176
- if (didUseDirectImages) {
8177
- return directImagesByWorkbookSheetIndex;
8178
- }
8179
- return collectWorksheetBatchImages(workbook);
8180
- }
8181
- function collectWorksheetApiShapes(workbook) {
8182
- return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
8183
- const worksheet = workbook.getSheet(workbookSheetIndex);
8184
- const rawShapes = Array.isArray(worksheet.shapes) ? worksheet.shapes : [];
8185
- return rawShapes.map((shape, index) => buildWorksheetDirectApiShape(workbookSheetIndex, shape, index + 1));
8186
- });
7888
+ return directImagesByWorkbookSheetIndex;
8187
7889
  }
8188
- function mergeParsedAndApiImages(parsedImages, apiImages) {
8189
- if (parsedImages.length === 0) {
8190
- return apiImages;
8191
- }
8192
- if (apiImages.length === 0) {
8193
- return parsedImages;
7890
+ function imageAnchorKey(anchor) {
7891
+ if (anchor.kind === "absolute") {
7892
+ return [
7893
+ "absolute",
7894
+ Math.round(anchor.positionEmu.x),
7895
+ Math.round(anchor.positionEmu.y),
7896
+ Math.round(anchor.sizeEmu.cx),
7897
+ Math.round(anchor.sizeEmu.cy)
7898
+ ].join(":");
8194
7899
  }
8195
- const normalizeTextKey = (value) => value?.trim().toLowerCase() ?? "";
8196
- const anchorKey = (anchor) => {
8197
- if (anchor.kind === "absolute") {
8198
- return [
8199
- "absolute",
8200
- Math.round(anchor.positionEmu.x),
8201
- Math.round(anchor.positionEmu.y),
8202
- Math.round(anchor.sizeEmu.cx),
8203
- Math.round(anchor.sizeEmu.cy)
8204
- ].join(":");
8205
- }
8206
- if (anchor.kind === "one-cell") {
8207
- return [
8208
- "one",
8209
- anchor.from.col,
8210
- anchor.from.row,
8211
- Math.round(anchor.from.colOffsetEmu),
8212
- Math.round(anchor.from.rowOffsetEmu),
8213
- Math.round(anchor.sizeEmu.cx),
8214
- Math.round(anchor.sizeEmu.cy)
8215
- ].join(":");
8216
- }
7900
+ if (anchor.kind === "one-cell") {
8217
7901
  return [
8218
- "two",
7902
+ "one",
8219
7903
  anchor.from.col,
8220
7904
  anchor.from.row,
8221
7905
  Math.round(anchor.from.colOffsetEmu),
8222
7906
  Math.round(anchor.from.rowOffsetEmu),
8223
- anchor.to.col,
8224
- anchor.to.row,
8225
- Math.round(anchor.to.colOffsetEmu),
8226
- Math.round(anchor.to.rowOffsetEmu)
7907
+ Math.round(anchor.sizeEmu.cx),
7908
+ Math.round(anchor.sizeEmu.cy)
8227
7909
  ].join(":");
8228
- };
7910
+ }
7911
+ return [
7912
+ "two",
7913
+ anchor.from.col,
7914
+ anchor.from.row,
7915
+ Math.round(anchor.from.colOffsetEmu),
7916
+ Math.round(anchor.from.rowOffsetEmu),
7917
+ anchor.to.col,
7918
+ anchor.to.row,
7919
+ Math.round(anchor.to.colOffsetEmu),
7920
+ Math.round(anchor.to.rowOffsetEmu)
7921
+ ].join(":");
7922
+ }
7923
+ function collectHiddenWorksheetImageAnchors(workbook) {
7924
+ return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => new Set(
7925
+ workbook.getSheet(workbookSheetIndex).images.flatMap((drawing) => drawing.hidden && drawing.anchor ? [imageAnchorKey(dukeDrawingAnchorToXlsxAnchor(drawing.anchor))] : [])
7926
+ ));
7927
+ }
7928
+ function mergeParsedAndApiImages(parsedImages, apiImages, hiddenAnchorKeys) {
7929
+ const visibleParsedImages = parsedImages.filter((image) => !hiddenAnchorKeys.has(imageAnchorKey(image.anchor)));
7930
+ if (visibleParsedImages.length === 0) {
7931
+ return apiImages;
7932
+ }
7933
+ if (apiImages.length === 0) {
7934
+ return visibleParsedImages;
7935
+ }
7936
+ const normalizeTextKey = (value) => value?.trim().toLowerCase() ?? "";
8229
7937
  const imageKeys = (image) => {
8230
7938
  const keys = [
8231
- `${normalizeTextKey(image.mediaPath)}|${normalizeTextKey(image.name)}|${anchorKey(image.anchor)}`,
8232
- `${normalizeTextKey(image.mediaPath)}|${anchorKey(image.anchor)}`,
8233
- `${normalizeTextKey(image.name)}|${anchorKey(image.anchor)}`,
8234
- `${anchorKey(image.anchor)}`
7939
+ `${normalizeTextKey(image.mediaPath)}|${normalizeTextKey(image.name)}|${imageAnchorKey(image.anchor)}`,
7940
+ `${normalizeTextKey(image.mediaPath)}|${imageAnchorKey(image.anchor)}`,
7941
+ `${normalizeTextKey(image.name)}|${imageAnchorKey(image.anchor)}`,
7942
+ `${imageAnchorKey(image.anchor)}`
8235
7943
  ];
8236
7944
  return keys.filter((key, index) => key && keys.indexOf(key) === index);
8237
7945
  };
@@ -8261,7 +7969,7 @@ function mergeParsedAndApiImages(parsedImages, apiImages) {
8261
7969
  }
8262
7970
  return null;
8263
7971
  };
8264
- const merged = parsedImages.map((image) => {
7972
+ const merged = visibleParsedImages.map((image) => {
8265
7973
  const apiImage = takeApiMatch(image);
8266
7974
  if (!apiImage) {
8267
7975
  return image;
@@ -8272,7 +7980,8 @@ function mergeParsedAndApiImages(parsedImages, apiImages) {
8272
7980
  mediaPath: apiImage.mediaPath ?? image.mediaPath,
8273
7981
  mimeType: apiImage.mimeType,
8274
7982
  name: apiImage.name ?? image.name,
8275
- src: apiImage.src
7983
+ src: apiImage.src,
7984
+ zIndex: apiImage.zIndex
8276
7985
  };
8277
7986
  });
8278
7987
  return merged;
@@ -8290,12 +7999,13 @@ function createBasicWorkbookAssets(workbook) {
8290
7999
  const objectUrls = [];
8291
8000
  return {
8292
8001
  archive: {},
8002
+ dirtyArchivePaths: /* @__PURE__ */ new Set(),
8293
8003
  formControlsByWorkbookSheetIndex: collectWorkbookFormControls(workbook),
8294
8004
  imageOriginsById: /* @__PURE__ */ new Map(),
8295
8005
  imagesByWorkbookSheetIndex: collectWorksheetApiImages(workbook, objectUrls),
8296
8006
  namedCellStyleByName: {},
8297
8007
  objectUrls,
8298
- shapesByWorkbookSheetIndex: collectWorksheetApiShapes(workbook),
8008
+ shapesByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => []),
8299
8009
  sheetOrigins: Array.from({ length: workbook.sheetCount }, () => null),
8300
8010
  sheetStatesByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => null),
8301
8011
  styleById: {},
@@ -8310,12 +8020,17 @@ function loadWorkbookImageAssets(bytes, workbook, skipXmlParsing = false) {
8310
8020
  }
8311
8021
  const parsedAssets = parseWorkbookImageAssets(bytes, workbook);
8312
8022
  const apiImagesByWorkbookSheetIndex = collectWorksheetApiImages(workbook, parsedAssets.objectUrls);
8023
+ const hiddenImageAnchorsByWorkbookSheetIndex = collectHiddenWorksheetImageAnchors(workbook);
8313
8024
  const imagesByWorkbookSheetIndex = Array.from(
8314
8025
  { length: Math.max(workbook.sheetCount, parsedAssets.imagesByWorkbookSheetIndex.length, apiImagesByWorkbookSheetIndex.length) },
8315
8026
  (_, index) => {
8316
8027
  const parsedImages = parsedAssets.imagesByWorkbookSheetIndex[index] ?? [];
8317
8028
  const apiImages = apiImagesByWorkbookSheetIndex[index] ?? [];
8318
- return mergeParsedAndApiImages(parsedImages, apiImages);
8029
+ return mergeParsedAndApiImages(
8030
+ parsedImages,
8031
+ apiImages,
8032
+ hiddenImageAnchorsByWorkbookSheetIndex[index] ?? /* @__PURE__ */ new Set()
8033
+ );
8319
8034
  }
8320
8035
  );
8321
8036
  return {
@@ -8626,7 +8341,11 @@ function useXlsxViewerController(options) {
8626
8341
  }, [skipXmlParsing]);
8627
8342
  const refreshWorkbookState = React.useCallback((targetWorkbook) => {
8628
8343
  const currentFormControls = imageAssetsRef.current?.formControlsByWorkbookSheetIndex ?? [];
8629
- const nextFormControls = refreshWorkbookFormControls(targetWorkbook, currentFormControls);
8344
+ const nextFormControls = refreshWorkbookFormControls(
8345
+ targetWorkbook,
8346
+ currentFormControls,
8347
+ imageAssetsRef.current?.themePalette
8348
+ );
8630
8349
  if (imageAssetsRef.current) {
8631
8350
  imageAssetsRef.current.formControlsByWorkbookSheetIndex = nextFormControls;
8632
8351
  }
@@ -8752,24 +8471,19 @@ function useXlsxViewerController(options) {
8752
8471
  if (!effectiveSkipXmlParsing && hasIncompleteWorkerChartSnapshot(snapshot)) {
8753
8472
  throw new Error("Worker chart payload incomplete");
8754
8473
  }
8755
- const workerImageAssets = effectiveSkipXmlParsing ? null : parseWorkbookImageAssets(
8756
- new Uint8Array(buffer),
8757
- snapshot.formControlsByWorkbookSheetIndex
8758
- );
8474
+ const workerImageAssets = effectiveSkipXmlParsing ? null : parseWorkbookImageAssets(new Uint8Array(buffer));
8759
8475
  if (!isCurrent || abortController.signal.aborted) {
8760
8476
  revokeWorkbookImageAssets(workerImageAssets);
8761
8477
  return;
8762
8478
  }
8763
8479
  setImageAssets(workerImageAssets);
8480
+ setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8764
8481
  setWorkbook(null);
8765
8482
  setSheets(snapshot.sheets);
8766
8483
  setChartsByWorkbookSheetIndex(snapshot.chartsByWorkbookSheetIndex);
8767
8484
  setChartsheets(snapshot.chartsheets);
8768
8485
  setTabs(snapshot.tabs);
8769
8486
  chartAssetsRef.current = null;
8770
- if (!workerImageAssets) {
8771
- setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8772
- }
8773
8487
  setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);
8774
8488
  setShouldAutoCalculate(false);
8775
8489
  setIsWorkerBacked(true);
@@ -9275,7 +8989,8 @@ function useXlsxViewerController(options) {
9275
8989
  return control2?.items?.slice() ?? [];
9276
8990
  }
9277
8991
  const control = target.worksheet.formControls[controlIndex];
9278
- const inputRange = control?.kind.kind === "listBox" || control?.kind.kind === "dropdown" ? control.kind.inputRange : void 0;
8992
+ const kind = control?.formControl.kind;
8993
+ const inputRange = kind?.kind === "listBox" || kind?.kind === "dropdown" ? kind.inputRange : void 0;
9279
8994
  if (!inputRange) {
9280
8995
  return [];
9281
8996
  }
@@ -9694,30 +9409,17 @@ function useXlsxViewerController(options) {
9694
9409
  return null;
9695
9410
  }
9696
9411
  recordHistoryBeforeMutation();
9697
- const controlIndex = target.worksheet.addFormControl(input);
9698
- const controls = target.worksheet.formControls;
9699
- const addedControl = controls[controlIndex];
9700
- if (addedControl?.kind.kind === "optionButton" && addedControl.kind.state === "checked") {
9701
- const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
9702
- for (const siblingIndex of groupIndexes) {
9703
- if (siblingIndex === controlIndex) {
9704
- continue;
9705
- }
9706
- const sibling = target.worksheet.formControls[siblingIndex];
9707
- if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9708
- target.worksheet.setFormControl(siblingIndex, {
9709
- ...formControlInputFromDukeControl(sibling),
9710
- kind: { ...sibling.kind, state: "unchecked" }
9711
- });
9712
- }
9713
- }
9412
+ target.worksheet.addDrawing(xlsxFormControlInputToDukeDrawing(input));
9413
+ const controlIndex = target.worksheet.formControlCount - 1;
9414
+ const addedControl = target.worksheet.formControls[controlIndex];
9415
+ const addedKind = addedControl?.formControl.kind;
9416
+ if (addedControl && (addedKind?.kind === "checkbox" || addedKind?.kind === "optionButton")) {
9417
+ target.worksheet.setFormControlCheckState(addedControl.drawingPath, addedKind.state);
9418
+ } else if (addedKind?.kind === "groupBox") {
9419
+ reconcileCheckedOptionButtons(target.worksheet);
9420
+ } else if (addedKind && "cellLink" in addedKind && addedKind.cellLink) {
9421
+ workbook.syncFormControls();
9714
9422
  }
9715
- syncFormControlLinkedCell(
9716
- workbook,
9717
- target.workbookSheetIndex,
9718
- target.worksheet.formControls,
9719
- controlIndex
9720
- );
9721
9423
  maybeRecalculateWorkbook(workbook);
9722
9424
  refreshWorkbookState(workbook);
9723
9425
  return controlIndex;
@@ -9729,34 +9431,51 @@ function useXlsxViewerController(options) {
9729
9431
  return false;
9730
9432
  }
9731
9433
  recordHistoryBeforeMutation();
9434
+ const currentInput = formControlInputFromDukeControl(currentControl);
9435
+ if (currentInput.kind !== "formControl" || !currentControl.anchor) {
9436
+ return false;
9437
+ }
9438
+ const nextAnchor = patch.anchor ? xlsxAnchorToDukeDrawingAnchor(
9439
+ patch.anchor,
9440
+ patch.editAs ?? (currentControl.anchor.type === "twoCell" ? currentControl.anchor.editAs : void 0)
9441
+ ) : patch.editAs !== void 0 && currentControl.anchor.type === "twoCell" ? { ...currentControl.anchor, editAs: patch.editAs } : currentControl.anchor;
9442
+ let nextKind = patch.kind ? xlsxFormControlKindToDukeKind(patch.kind) : dukeFormControlKindToInput(currentControl.formControl.kind);
9443
+ if (currentControl.formControl.kind.kind === "unknown" && nextKind.kind === "unknown") {
9444
+ nextKind = {
9445
+ ...nextKind,
9446
+ legacyObjectType: nextKind.legacyObjectType ?? currentControl.formControl.kind.legacyObjectType,
9447
+ rawObj: nextKind.rawObj ?? currentControl.formControl.kind.rawObj,
9448
+ rawProperties: nextKind.rawProperties ?? currentControl.formControl.kind.rawProperties
9449
+ };
9450
+ }
9732
9451
  const nextInput = {
9733
- ...formControlInputFromDukeControl(currentControl),
9734
- ...patch,
9735
- anchor: patch.anchor ? { ...patch.anchor } : { ...currentControl.anchor },
9736
- kind: patch.kind ? { ...patch.kind } : { ...currentControl.kind }
9452
+ altText: "altText" in patch ? patch.altText : currentControl.altText,
9453
+ anchor: nextAnchor,
9454
+ formControl: {
9455
+ ...currentInput.formControl,
9456
+ kind: nextKind,
9457
+ macroName: "macroName" in patch ? patch.macroName : currentControl.formControl.macroName,
9458
+ rawClientData: "rawClientData" in patch ? patch.rawClientData : currentControl.formControl.rawClientData
9459
+ },
9460
+ hidden: "hidden" in patch ? patch.hidden : currentControl.hidden,
9461
+ kind: "formControl",
9462
+ locked: "locked" in patch ? patch.locked : currentControl.locked,
9463
+ name: "name" in patch ? patch.name : currentControl.name,
9464
+ printable: "printable" in patch ? patch.printable : currentControl.printable,
9465
+ title: "title" in patch ? patch.title : currentControl.title
9737
9466
  };
9738
- if (nextInput.kind.kind === "optionButton" && nextInput.kind.state === "checked") {
9739
- const groupIndexes = resolveOptionButtonGroupIndexes(target.worksheet.formControls, controlIndex);
9740
- for (const siblingIndex of groupIndexes) {
9741
- if (siblingIndex === controlIndex) {
9742
- continue;
9743
- }
9744
- const sibling = target.worksheet.formControls[siblingIndex];
9745
- if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9746
- target.worksheet.setFormControl(siblingIndex, {
9747
- ...formControlInputFromDukeControl(sibling),
9748
- kind: { ...sibling.kind, state: "unchecked" }
9749
- });
9750
- }
9751
- }
9467
+ target.worksheet.setDrawing(currentControl.drawingPath, nextInput);
9468
+ const updatedControl = target.worksheet.formControls[controlIndex];
9469
+ const updatedKind = updatedControl?.formControl.kind;
9470
+ const interactionChanged = updatedKind ? formControlInteractionChanged(currentControl.formControl.kind, updatedKind) : false;
9471
+ const groupTopologyChanged = patch.anchor !== void 0 || updatedKind !== void 0 && updatedKind.kind !== currentControl.formControl.kind.kind && (updatedKind.kind === "groupBox" || currentControl.formControl.kind.kind === "groupBox");
9472
+ if (interactionChanged && updatedControl && (updatedKind?.kind === "checkbox" || updatedKind?.kind === "optionButton")) {
9473
+ target.worksheet.setFormControlCheckState(updatedControl.drawingPath, updatedKind.state);
9474
+ } else if (groupTopologyChanged) {
9475
+ reconcileCheckedOptionButtons(target.worksheet);
9476
+ } else if (interactionChanged && updatedKind && "cellLink" in updatedKind && updatedKind.cellLink) {
9477
+ workbook.syncFormControls();
9752
9478
  }
9753
- target.worksheet.setFormControl(controlIndex, nextInput);
9754
- syncFormControlLinkedCell(
9755
- workbook,
9756
- target.workbookSheetIndex,
9757
- target.worksheet.formControls,
9758
- controlIndex
9759
- );
9760
9479
  maybeRecalculateWorkbook(workbook);
9761
9480
  refreshWorkbookState(workbook);
9762
9481
  return true;
@@ -9767,10 +9486,16 @@ function useXlsxViewerController(options) {
9767
9486
  return false;
9768
9487
  }
9769
9488
  recordHistoryBeforeMutation();
9770
- target.worksheet.removeFormControl(controlIndex);
9489
+ const removedControl = target.worksheet.formControls[controlIndex];
9490
+ const removedKind = removedControl.formControl.kind.kind;
9491
+ target.worksheet.removeDrawing(removedControl.drawingPath);
9492
+ if (removedKind === "groupBox" || removedKind === "optionButton") {
9493
+ reconcileCheckedOptionButtons(target.worksheet);
9494
+ maybeRecalculateWorkbook(workbook);
9495
+ }
9771
9496
  refreshWorkbookState(workbook);
9772
9497
  return true;
9773
- }, [activeSheetIndex, getFormControlWorksheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9498
+ }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9774
9499
  const recordCellEditHistory = React.useCallback((cell, before, after) => {
9775
9500
  if (!activeSheet || isApplyingHistoryRef.current) {
9776
9501
  return;
@@ -17948,7 +17673,7 @@ var THUMBNAIL_MAX_COLS = 80;
17948
17673
  var THUMBNAIL_MAX_SOURCE_HEIGHT_PX = 900;
17949
17674
  var THUMBNAIL_MAX_SOURCE_WIDTH_PX = 1440;
17950
17675
  var LIVE_ZOOM_COMMIT_IDLE_MS = 48;
17951
- var WHEEL_ZOOM_SENSITIVITY = 25e-5;
17676
+ var WHEEL_ZOOM_SENSITIVITY = 25e-4;
17952
17677
  var WHEEL_LINE_DELTA_PX = 16;
17953
17678
  var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669", "#ea580c", "#db2777"];
17954
17679
  var CHART_SOURCE_HIGHLIGHT_COLOR_BY_ROLE = {
@@ -18991,6 +18716,7 @@ function resolveAxisStartOffset(actualIndex, actualIndices, sizes, indexByActual
18991
18716
  function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWidths, options) {
18992
18717
  const headerHeight = options?.headerHeight ?? HEADER_HEIGHT;
18993
18718
  const rowHeaderWidth = options?.rowHeaderWidth ?? ROW_HEADER_WIDTH;
18719
+ const anchorZoomFactor = options?.zoomFactor ?? 1;
18994
18720
  const resolveMarkerLeft = (col, colOffsetEmu) => rowHeaderWidth + resolveAxisStartOffset(
18995
18721
  col,
18996
18722
  visibleCols,
@@ -18998,7 +18724,7 @@ function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWi
18998
18724
  options?.colIndexByActual,
18999
18725
  options?.colPrefixSums,
19000
18726
  options?.actualColPrefixSums
19001
- ) + emuToPixels(colOffsetEmu);
18727
+ ) + emuToPixels(colOffsetEmu) * anchorZoomFactor;
19002
18728
  const resolveMarkerTop = (row, rowOffsetEmu) => headerHeight + resolveAxisStartOffset(
19003
18729
  row,
19004
18730
  visibleRows,
@@ -19006,21 +18732,21 @@ function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWi
19006
18732
  options?.rowIndexByActual,
19007
18733
  options?.rowPrefixSums,
19008
18734
  options?.actualRowPrefixSums
19009
- ) + emuToPixels(rowOffsetEmu);
18735
+ ) + emuToPixels(rowOffsetEmu) * anchorZoomFactor;
19010
18736
  if (anchor.kind === "absolute") {
19011
18737
  return {
19012
- height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
19013
- left: rowHeaderWidth + emuToPixels(anchor.positionEmu.x),
19014
- top: headerHeight + emuToPixels(anchor.positionEmu.y),
19015
- width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18738
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy) * anchorZoomFactor),
18739
+ left: rowHeaderWidth + emuToPixels(anchor.positionEmu.x) * anchorZoomFactor,
18740
+ top: headerHeight + emuToPixels(anchor.positionEmu.y) * anchorZoomFactor,
18741
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx) * anchorZoomFactor)
19016
18742
  };
19017
18743
  }
19018
18744
  if (anchor.kind === "one-cell") {
19019
18745
  return {
19020
- height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18746
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy) * anchorZoomFactor),
19021
18747
  left: resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu),
19022
18748
  top: resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu),
19023
- width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18749
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx) * anchorZoomFactor)
19024
18750
  };
19025
18751
  }
19026
18752
  const left = resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu);
@@ -22180,7 +21906,6 @@ function buildFormControlKindInput(control) {
22180
21906
  return {
22181
21907
  caption: control.caption ?? control.label ?? "",
22182
21908
  cellLink: control.linkedCell,
22183
- firstInGroup: control.firstInGroup,
22184
21909
  kind: "optionButton",
22185
21910
  no3D: control.no3D ?? false,
22186
21911
  state: control.state === "checked" ? "checked" : "unchecked"
@@ -23226,6 +22951,7 @@ function XlsxGrid({
23226
22951
  const canvasCornerOverlayContentRef = React4.useRef(null);
23227
22952
  const canvasImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
23228
22953
  const selectionOverlayRef = React4.useRef(null);
22954
+ const canvasSelectionLayerRef = React4.useRef(null);
23229
22955
  const activeValidationOverlayRef = React4.useRef(null);
23230
22956
  const fillHandleRef = React4.useRef(null);
23231
22957
  const tableMenuRef = React4.useRef(null);
@@ -23251,9 +22977,11 @@ function XlsxGrid({
23251
22977
  const liveGestureZoomRef = React4.useRef(null);
23252
22978
  const pendingLiveGestureZoomStateRef = React4.useRef(null);
23253
22979
  const liveGestureZoomFrameRef = React4.useRef(null);
22980
+ const liveGestureZoomTransformFrameRef = React4.useRef(null);
23254
22981
  const pendingZoomAnchorRef = React4.useRef(null);
23255
22982
  const liveZoomCommitTimerRef = React4.useRef(null);
23256
22983
  const pendingLiveZoomCommitRef = React4.useRef(null);
22984
+ const gestureScrollerRectRef = React4.useRef(null);
23257
22985
  const touchPinchStateRef = React4.useRef(null);
23258
22986
  const safariPinchStartZoomRef = React4.useRef(null);
23259
22987
  const canvasTransformStyleCacheRef = React4.useRef(/* @__PURE__ */ new WeakMap());
@@ -23337,7 +23065,10 @@ function XlsxGrid({
23337
23065
  const setInteractionMode = React4.useCallback((mode) => {
23338
23066
  interactionModeRef.current = mode;
23339
23067
  }, []);
23340
- const worksheet = getActiveWorksheet();
23068
+ const worksheet = React4.useMemo(
23069
+ () => getActiveWorksheet(),
23070
+ [activeSheetIndex, getActiveWorksheet, revision]
23071
+ );
23341
23072
  const mergedRegions = React4.useMemo(() => {
23342
23073
  const regions = [];
23343
23074
  const rawMergedRegions = Array.isArray(worksheet?.mergedRegions) ? worksheet.mergedRegions : [];
@@ -23417,8 +23148,31 @@ function XlsxGrid({
23417
23148
  const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
23418
23149
  const scrollDeltaX = paintedViewport.left - nextLiveViewport.left;
23419
23150
  const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
23420
- const liveZoomTranslateX2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.x * (1 - liveZoomScale2) : 0;
23421
- const liveZoomTranslateY2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.y * (1 - liveZoomScale2) : 0;
23151
+ const resolveClampedLiveZoomAnchor = (scrollOffset, viewportSize, scrollExtent, gestureAnchor) => {
23152
+ if (!isLiveZooming2 || liveZoomScale2 === 1) {
23153
+ return gestureAnchor;
23154
+ }
23155
+ const targetScrollExtent = Math.max(viewportSize, scrollExtent * liveZoomScale2);
23156
+ const targetScrollOffset = clampValue(
23157
+ (scrollOffset + gestureAnchor) * liveZoomScale2 - gestureAnchor,
23158
+ 0,
23159
+ Math.max(0, targetScrollExtent - viewportSize)
23160
+ );
23161
+ return (scrollOffset * liveZoomScale2 - targetScrollOffset) / (1 - liveZoomScale2);
23162
+ };
23163
+ const scroller = scrollRef.current;
23164
+ const liveZoomAnchorX = resolveClampedLiveZoomAnchor(
23165
+ nextLiveViewport.left,
23166
+ nextLiveViewport.width,
23167
+ scroller?.scrollWidth ?? nextLiveViewport.width,
23168
+ currentLiveGestureZoom?.anchor.x ?? 0
23169
+ );
23170
+ const liveZoomAnchorY = resolveClampedLiveZoomAnchor(
23171
+ nextLiveViewport.top,
23172
+ nextLiveViewport.height,
23173
+ scroller?.scrollHeight ?? nextLiveViewport.height,
23174
+ currentLiveGestureZoom?.anchor.y ?? 0
23175
+ );
23422
23176
  const applyElementTransform = (element, transform, willChange, transformOrigin = null) => {
23423
23177
  if (!element) {
23424
23178
  return;
@@ -23439,7 +23193,10 @@ function XlsxGrid({
23439
23193
  willChange
23440
23194
  });
23441
23195
  };
23442
- const applyCanvasTransform = (canvas, translateX, translateY) => {
23196
+ const resolveLiveTranslation = (baseTranslation, anchor, origin) => baseTranslation * liveZoomScale2 + (anchor - origin) * (1 - liveZoomScale2);
23197
+ const applyCanvasTransform = (canvas, baseTranslateX, baseTranslateY, originX, originY, anchorX, anchorY) => {
23198
+ const translateX = resolveLiveTranslation(baseTranslateX, anchorX, originX);
23199
+ const translateY = resolveLiveTranslation(baseTranslateY, anchorY, originY);
23443
23200
  const shouldTransform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1;
23444
23201
  applyElementTransform(
23445
23202
  canvas,
@@ -23447,85 +23204,169 @@ function XlsxGrid({
23447
23204
  shouldTransform ? "transform" : ""
23448
23205
  );
23449
23206
  };
23450
- const applyOverlayTransform = (element, translateX, translateY) => {
23207
+ const applyOverlayTransform = (element, baseTranslateX, baseTranslateY, paneLeft, paneTop, anchorX, anchorY) => {
23208
+ const translateX = resolveLiveTranslation(baseTranslateX, anchorX, paneLeft);
23209
+ const translateY = resolveLiveTranslation(baseTranslateY, anchorY, paneTop);
23451
23210
  applyElementTransform(
23452
23211
  element,
23453
- liveZoomScale2 !== 1 ? `translate3d(${translateX + liveZoomTranslateX2}px, ${translateY + liveZoomTranslateY2}px, 0) scale(${liveZoomScale2})` : `translate3d(${translateX}px, ${translateY}px, 0)`,
23212
+ liveZoomScale2 !== 1 ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : `translate3d(${baseTranslateX}px, ${baseTranslateY}px, 0)`,
23454
23213
  "transform",
23455
23214
  "0 0"
23456
23215
  );
23457
23216
  };
23217
+ const liveScrollBodyViewportWidth = Math.max(0, nextLiveViewport.width - liveFrozenPaneRight);
23218
+ const liveScrollBodyViewportHeight = Math.max(0, nextLiveViewport.height - liveFrozenPaneBottom);
23219
+ const liveScrollBodyCanvasLeft = liveFrozenPaneRight - Math.min(CANVAS_SCROLL_BUFFER_PX, liveScrollBodyViewportWidth);
23220
+ const liveScrollBodyCanvasTop = liveFrozenPaneBottom - Math.min(CANVAS_SCROLL_BUFFER_PX, liveScrollBodyViewportHeight);
23221
+ const selectionLayerTranslateX = (nextLiveViewport.left + liveZoomAnchorX) * (1 - liveZoomScale2);
23222
+ const selectionLayerTranslateY = (nextLiveViewport.top + liveZoomAnchorY) * (1 - liveZoomScale2);
23223
+ const shouldTransformSelectionLayer = experimentalCanvas && isLiveZooming2 && liveZoomScale2 !== 1;
23224
+ applyElementTransform(
23225
+ canvasSelectionLayerRef.current,
23226
+ shouldTransformSelectionLayer ? `translate3d(${selectionLayerTranslateX}px, ${selectionLayerTranslateY}px, 0) scale(${liveZoomScale2})` : "",
23227
+ shouldTransformSelectionLayer ? "transform" : "",
23228
+ "0 0"
23229
+ );
23458
23230
  applyCanvasTransform(
23459
23231
  scrollBodyCanvasRef.current,
23460
- scrollDeltaX + liveZoomTranslateX2,
23461
- scrollDeltaY + liveZoomTranslateY2
23232
+ scrollDeltaX,
23233
+ scrollDeltaY,
23234
+ liveScrollBodyCanvasLeft,
23235
+ liveScrollBodyCanvasTop,
23236
+ liveZoomAnchorX,
23237
+ liveZoomAnchorY
23462
23238
  );
23463
23239
  applyCanvasTransform(
23464
23240
  topBodyCanvasRef.current,
23465
- scrollDeltaX + liveZoomTranslateX2,
23466
- liveZoomTranslateY2
23241
+ scrollDeltaX,
23242
+ 0,
23243
+ liveScrollBodyCanvasLeft,
23244
+ liveDisplayHeaderHeight,
23245
+ liveZoomAnchorX,
23246
+ 0
23467
23247
  );
23468
23248
  applyCanvasTransform(
23469
23249
  leftBodyCanvasRef.current,
23470
- liveZoomTranslateX2,
23471
- scrollDeltaY + liveZoomTranslateY2
23250
+ 0,
23251
+ scrollDeltaY,
23252
+ liveDisplayRowHeaderWidth,
23253
+ liveScrollBodyCanvasTop,
23254
+ 0,
23255
+ liveZoomAnchorY
23472
23256
  );
23473
23257
  applyCanvasTransform(
23474
23258
  cornerBodyCanvasRef.current,
23475
- liveZoomTranslateX2,
23476
- liveZoomTranslateY2
23259
+ 0,
23260
+ 0,
23261
+ liveDisplayRowHeaderWidth,
23262
+ liveDisplayHeaderHeight,
23263
+ 0,
23264
+ 0
23477
23265
  );
23478
23266
  applyCanvasTransform(
23479
23267
  topScrollHeaderCanvasRef.current,
23480
- scrollDeltaX + liveZoomTranslateX2,
23481
- liveZoomTranslateY2
23268
+ scrollDeltaX,
23269
+ 0,
23270
+ liveScrollBodyCanvasLeft,
23271
+ 0,
23272
+ liveZoomAnchorX,
23273
+ 0
23482
23274
  );
23483
23275
  applyCanvasTransform(
23484
23276
  topFrozenHeaderCanvasRef.current,
23485
- liveZoomTranslateX2,
23486
- liveZoomTranslateY2
23277
+ 0,
23278
+ 0,
23279
+ liveDisplayRowHeaderWidth,
23280
+ 0,
23281
+ 0,
23282
+ 0
23487
23283
  );
23488
23284
  applyCanvasTransform(
23489
23285
  leftScrollHeaderCanvasRef.current,
23490
- liveZoomTranslateX2,
23491
- scrollDeltaY + liveZoomTranslateY2
23286
+ 0,
23287
+ scrollDeltaY,
23288
+ 0,
23289
+ liveScrollBodyCanvasTop,
23290
+ 0,
23291
+ liveZoomAnchorY
23492
23292
  );
23493
23293
  applyCanvasTransform(
23494
23294
  leftFrozenHeaderCanvasRef.current,
23495
- liveZoomTranslateX2,
23496
- liveZoomTranslateY2
23295
+ 0,
23296
+ 0,
23297
+ 0,
23298
+ liveDisplayHeaderHeight,
23299
+ 0,
23300
+ 0
23497
23301
  );
23498
23302
  applyCanvasTransform(
23499
23303
  cornerHeaderCanvasRef.current,
23500
- liveZoomTranslateX2,
23501
- liveZoomTranslateY2
23304
+ 0,
23305
+ 0,
23306
+ 0,
23307
+ 0,
23308
+ 0,
23309
+ 0
23502
23310
  );
23503
23311
  applyOverlayTransform(
23504
23312
  canvasScrollOverlayContentRef.current,
23505
23313
  -nextLiveViewport.left - liveFrozenPaneRight,
23506
- -nextLiveViewport.top - liveFrozenPaneBottom
23314
+ -nextLiveViewport.top - liveFrozenPaneBottom,
23315
+ liveFrozenPaneRight,
23316
+ liveFrozenPaneBottom,
23317
+ liveZoomAnchorX,
23318
+ liveZoomAnchorY
23507
23319
  );
23508
23320
  applyOverlayTransform(
23509
23321
  canvasTopOverlayContentRef.current,
23510
23322
  -nextLiveViewport.left - liveFrozenPaneRight,
23511
- -liveDisplayHeaderHeight
23323
+ -liveDisplayHeaderHeight,
23324
+ liveFrozenPaneRight,
23325
+ liveDisplayHeaderHeight,
23326
+ liveZoomAnchorX,
23327
+ 0
23512
23328
  );
23513
23329
  applyOverlayTransform(
23514
23330
  canvasLeftOverlayContentRef.current,
23515
23331
  -liveDisplayRowHeaderWidth,
23516
- -nextLiveViewport.top - liveFrozenPaneBottom
23332
+ -nextLiveViewport.top - liveFrozenPaneBottom,
23333
+ liveDisplayRowHeaderWidth,
23334
+ liveFrozenPaneBottom,
23335
+ 0,
23336
+ liveZoomAnchorY
23517
23337
  );
23518
23338
  applyOverlayTransform(
23519
23339
  canvasCornerOverlayContentRef.current,
23520
23340
  -liveDisplayRowHeaderWidth,
23521
- -liveDisplayHeaderHeight
23341
+ -liveDisplayHeaderHeight,
23342
+ liveDisplayRowHeaderWidth,
23343
+ liveDisplayHeaderHeight,
23344
+ 0,
23345
+ 0
23522
23346
  );
23523
- }, [zoomScale]);
23347
+ }, [experimentalCanvas, zoomScale]);
23524
23348
  const updateLiveGestureZoomState = React4.useCallback((nextState) => {
23525
- const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
23349
+ const previousState = liveGestureZoomRef.current;
23350
+ const resolvedState = typeof nextState === "function" ? nextState(previousState) : nextState;
23526
23351
  liveGestureZoomRef.current = resolvedState;
23352
+ const shouldApplyTransformImmediately = !experimentalCanvas || previousState === null || resolvedState === null;
23353
+ if (shouldApplyTransformImmediately) {
23354
+ if (liveGestureZoomTransformFrameRef.current !== null) {
23355
+ window.cancelAnimationFrame(liveGestureZoomTransformFrameRef.current);
23356
+ liveGestureZoomTransformFrameRef.current = null;
23357
+ }
23358
+ applyCanvasViewportCompensation();
23359
+ } else if (liveGestureZoomTransformFrameRef.current === null) {
23360
+ liveGestureZoomTransformFrameRef.current = window.requestAnimationFrame(() => {
23361
+ liveGestureZoomTransformFrameRef.current = null;
23362
+ applyCanvasViewportCompensation();
23363
+ });
23364
+ }
23365
+ const shouldSyncReactState = !experimentalCanvas || previousState === null !== (resolvedState === null);
23366
+ if (!shouldSyncReactState) {
23367
+ return;
23368
+ }
23527
23369
  pendingLiveGestureZoomStateRef.current = resolvedState;
23528
- applyCanvasViewportCompensation();
23529
23370
  if (liveGestureZoomFrameRef.current !== null) {
23530
23371
  return;
23531
23372
  }
@@ -23535,7 +23376,7 @@ function XlsxGrid({
23535
23376
  pendingLiveGestureZoomStateRef.current = null;
23536
23377
  setLiveGestureZoom(pendingState);
23537
23378
  });
23538
- }, [applyCanvasViewportCompensation]);
23379
+ }, [applyCanvasViewportCompensation, experimentalCanvas]);
23539
23380
  const clearLiveZoomCommitTimer = React4.useCallback(() => {
23540
23381
  if (liveZoomCommitTimerRef.current !== null) {
23541
23382
  window.clearTimeout(liveZoomCommitTimerRef.current);
@@ -23548,6 +23389,7 @@ function XlsxGrid({
23548
23389
  pendingZoomAnchorRef.current = null;
23549
23390
  touchPinchStateRef.current = null;
23550
23391
  safariPinchStartZoomRef.current = null;
23392
+ gestureScrollerRectRef.current = null;
23551
23393
  gestureZoomScaleRef.current = committedZoomScaleRef.current;
23552
23394
  updateLiveGestureZoomState(null);
23553
23395
  }, [clearLiveZoomCommitTimer, updateLiveGestureZoomState]);
@@ -24162,6 +24004,10 @@ function XlsxGrid({
24162
24004
  window.cancelAnimationFrame(liveGestureZoomFrameRef.current);
24163
24005
  liveGestureZoomFrameRef.current = null;
24164
24006
  }
24007
+ if (liveGestureZoomTransformFrameRef.current !== null) {
24008
+ window.cancelAnimationFrame(liveGestureZoomTransformFrameRef.current);
24009
+ liveGestureZoomTransformFrameRef.current = null;
24010
+ }
24165
24011
  if (drawingViewportFrameRef.current !== null) {
24166
24012
  window.cancelAnimationFrame(drawingViewportFrameRef.current);
24167
24013
  drawingViewportFrameRef.current = null;
@@ -24425,6 +24271,7 @@ function XlsxGrid({
24425
24271
  const handleScrollerScroll = React4.useCallback((event) => {
24426
24272
  const currentScroller = event.currentTarget;
24427
24273
  cachedScrollerRectRef.current = null;
24274
+ gestureScrollerRectRef.current = null;
24428
24275
  const paintedViewport = paintedDrawingViewportRef.current;
24429
24276
  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;
24430
24277
  syncDrawingViewport(currentScroller, { immediate: shouldSyncDrawingViewportImmediately });
@@ -24459,7 +24306,9 @@ function XlsxGrid({
24459
24306
  return;
24460
24307
  }
24461
24308
  event.preventDefault();
24462
- const anchor = resolveEventAnchor(event.clientX, event.clientY, scroller.getBoundingClientRect());
24309
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24310
+ gestureScrollerRectRef.current = scrollerRect;
24311
+ const anchor = resolveEventAnchor(event.clientX, event.clientY, scrollerRect);
24463
24312
  updateLiveGestureZoomTarget(
24464
24313
  gestureZoomScaleRef.current * Math.exp(-delta * WHEEL_ZOOM_SENSITIVITY),
24465
24314
  anchor
@@ -24479,10 +24328,12 @@ function XlsxGrid({
24479
24328
  return;
24480
24329
  }
24481
24330
  event.preventDefault();
24331
+ const scrollerRect = scroller.getBoundingClientRect();
24332
+ gestureScrollerRectRef.current = scrollerRect;
24482
24333
  const anchor = resolveEventAnchor(
24483
24334
  (firstTouch.clientX + secondTouch.clientX) / 2,
24484
24335
  (firstTouch.clientY + secondTouch.clientY) / 2,
24485
- scroller.getBoundingClientRect()
24336
+ scrollerRect
24486
24337
  );
24487
24338
  clearLiveZoomCommitTimer();
24488
24339
  updateLiveGestureZoomState({
@@ -24510,10 +24361,12 @@ function XlsxGrid({
24510
24361
  return;
24511
24362
  }
24512
24363
  event.preventDefault();
24364
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24365
+ gestureScrollerRectRef.current = scrollerRect;
24513
24366
  const anchor = resolveEventAnchor(
24514
24367
  (firstTouch.clientX + secondTouch.clientX) / 2,
24515
24368
  (firstTouch.clientY + secondTouch.clientY) / 2,
24516
- scroller.getBoundingClientRect()
24369
+ scrollerRect
24517
24370
  );
24518
24371
  updateLiveGestureZoomTarget(
24519
24372
  pinchState.startZoomScale * distance / pinchState.startDistance,
@@ -24534,10 +24387,12 @@ function XlsxGrid({
24534
24387
  const handleGestureStart = (event) => {
24535
24388
  const gestureEvent = event;
24536
24389
  event.preventDefault();
24390
+ const scrollerRect = scroller.getBoundingClientRect();
24391
+ gestureScrollerRectRef.current = scrollerRect;
24537
24392
  const anchor = resolveEventAnchor(
24538
24393
  gestureEvent.clientX,
24539
24394
  gestureEvent.clientY,
24540
- scroller.getBoundingClientRect()
24395
+ scrollerRect
24541
24396
  );
24542
24397
  clearLiveZoomCommitTimer();
24543
24398
  safariPinchStartZoomRef.current = committedZoomScaleRef.current;
@@ -24555,10 +24410,12 @@ function XlsxGrid({
24555
24410
  return;
24556
24411
  }
24557
24412
  event.preventDefault();
24413
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24414
+ gestureScrollerRectRef.current = scrollerRect;
24558
24415
  const anchor = resolveEventAnchor(
24559
24416
  gestureEvent.clientX,
24560
24417
  gestureEvent.clientY,
24561
- scroller.getBoundingClientRect()
24418
+ scrollerRect
24562
24419
  );
24563
24420
  updateLiveGestureZoomTarget(startZoomScale * gestureEvent.scale, anchor);
24564
24421
  };
@@ -25552,7 +25409,8 @@ function XlsxGrid({
25552
25409
  headerHeight: displayHeaderHeight,
25553
25410
  rowIndexByActual,
25554
25411
  rowHeaderWidth: displayRowHeaderWidth,
25555
- rowPrefixSums
25412
+ rowPrefixSums,
25413
+ zoomFactor
25556
25414
  })
25557
25415
  })) : [],
25558
25416
  [
@@ -25570,7 +25428,8 @@ function XlsxGrid({
25570
25428
  rowPrefixSums,
25571
25429
  showImages,
25572
25430
  visibleCols,
25573
- visibleRows
25431
+ visibleRows,
25432
+ zoomFactor
25574
25433
  ]
25575
25434
  );
25576
25435
  const shapeRects = React4.useMemo(
@@ -25583,7 +25442,8 @@ function XlsxGrid({
25583
25442
  headerHeight: displayHeaderHeight,
25584
25443
  rowIndexByActual,
25585
25444
  rowHeaderWidth: displayRowHeaderWidth,
25586
- rowPrefixSums
25445
+ rowPrefixSums,
25446
+ zoomFactor
25587
25447
  }),
25588
25448
  shape
25589
25449
  })) : [],
@@ -25601,7 +25461,8 @@ function XlsxGrid({
25601
25461
  shapes,
25602
25462
  showImages,
25603
25463
  visibleCols,
25604
- visibleRows
25464
+ visibleRows,
25465
+ zoomFactor
25605
25466
  ]
25606
25467
  );
25607
25468
  const formControlRects = React4.useMemo(
@@ -25615,7 +25476,8 @@ function XlsxGrid({
25615
25476
  headerHeight: displayHeaderHeight,
25616
25477
  rowIndexByActual,
25617
25478
  rowHeaderWidth: displayRowHeaderWidth,
25618
- rowPrefixSums
25479
+ rowPrefixSums,
25480
+ zoomFactor
25619
25481
  })
25620
25482
  })) : [],
25621
25483
  [
@@ -25632,7 +25494,8 @@ function XlsxGrid({
25632
25494
  rowPrefixSums,
25633
25495
  showImages,
25634
25496
  visibleCols,
25635
- visibleRows
25497
+ visibleRows,
25498
+ zoomFactor
25636
25499
  ]
25637
25500
  );
25638
25501
  const chartRects = React4.useMemo(
@@ -25646,7 +25509,8 @@ function XlsxGrid({
25646
25509
  headerHeight: displayHeaderHeight,
25647
25510
  rowIndexByActual,
25648
25511
  rowHeaderWidth: displayRowHeaderWidth,
25649
- rowPrefixSums
25512
+ rowPrefixSums,
25513
+ zoomFactor
25650
25514
  })
25651
25515
  })) : [],
25652
25516
  [
@@ -25664,7 +25528,8 @@ function XlsxGrid({
25664
25528
  rowPrefixSums,
25665
25529
  showImages,
25666
25530
  visibleCols,
25667
- visibleRows
25531
+ visibleRows,
25532
+ zoomFactor
25668
25533
  ]
25669
25534
  );
25670
25535
  const shouldBakeCanvasStaticDrawings = Boolean(experimentalCanvas && readOnly && showImages);
@@ -27120,6 +26985,8 @@ function XlsxGrid({
27120
26985
  palette,
27121
26986
  rowHeaderWidth,
27122
26987
  rowSignature: buildRenderedAxisSignature(canvasVisibleRowItems, (item) => item.index),
26988
+ viewportLeft: drawingViewport.left,
26989
+ viewportTop: drawingViewport.top,
27123
26990
  zoomFactor
27124
26991
  };
27125
26992
  const nextHeaderCanvasSignature = {
@@ -27139,7 +27006,7 @@ function XlsxGrid({
27139
27006
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
27140
27007
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
27141
27008
  const previousPaintedViewport = paintedDrawingViewportRef.current;
27142
- 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);
27009
+ 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);
27143
27010
  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);
27144
27011
  const canBlitBody = Boolean(
27145
27012
  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
@@ -28366,7 +28233,8 @@ function XlsxGrid({
28366
28233
  const headerLabelLiveScale = isLiveZooming ? liveZoomScale : 1;
28367
28234
  const selectionBorderWidth = Math.max(1, zoomFactor);
28368
28235
  const shouldAnimateCanvasSelection = experimentalCanvas && enableCanvasSelectionAnimation;
28369
- 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";
28236
+ const shouldAnimateCanvasSelectionGeometry = shouldAnimateCanvasSelection && liveGestureZoom === null;
28237
+ 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";
28370
28238
  const rowColSpan = renderedCols.length + 1 + (leadingColumnSpacerWidth > 0 ? 1 : 0) + (trailingColumnSpacerWidth > 0 ? 1 : 0);
28371
28239
  const gutterSeparatorShadow = `inset -1px 0 0 ${palette.border}, inset 0 -1px 0 ${palette.border}`;
28372
28240
  const maxDrawingOverlayZIndex = Math.max(
@@ -30485,81 +30353,101 @@ function XlsxGrid({
30485
30353
  ]
30486
30354
  }
30487
30355
  ),
30488
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30356
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
30489
30357
  "div",
30490
30358
  {
30491
- ref: selectionOverlayRef,
30359
+ ref: canvasSelectionLayerRef,
30492
30360
  style: {
30493
- backgroundColor: selectionFill,
30494
- boxSizing: "border-box",
30495
- boxShadow: `inset 0 0 0 ${selectionBorderWidth}px ${selectionStroke}`,
30496
- contain: "layout paint",
30497
- height: resolvedSelectionOverlay?.height ?? 0,
30498
- left: resolvedSelectionOverlay?.left ?? 0,
30499
- opacity: resolvedSelectionOverlay ? 1 : 0,
30361
+ height: 0,
30362
+ left: 0,
30363
+ overflow: "visible",
30500
30364
  pointerEvents: "none",
30501
30365
  position: "absolute",
30502
- top: resolvedSelectionOverlay?.top ?? 0,
30503
- transition: canvasSelectionTransition,
30504
- visibility: resolvedSelectionOverlay ? "visible" : "hidden",
30505
- willChange: shouldAnimateCanvasSelection ? "left, top, width, height" : void 0,
30506
- width: resolvedSelectionOverlay?.width ?? 0,
30366
+ top: 0,
30367
+ transformOrigin: "0 0",
30368
+ transition: "none",
30369
+ width: 0,
30507
30370
  zIndex: 24
30508
- }
30509
- }
30510
- ),
30511
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30512
- "div",
30513
- {
30514
- ref: activeValidationOverlayRef,
30515
- "aria-hidden": "true",
30516
- style: {
30517
- alignItems: "center",
30518
- color: palette.mutedText,
30519
- display: "inline-flex",
30520
- fontSize: 10 * zoomFactor,
30521
- fontWeight: 700,
30522
- height: 16 * zoomFactor,
30523
- justifyContent: "center",
30524
- opacity: 0,
30525
- pointerEvents: "none",
30526
- position: "absolute",
30527
- transform: "translateY(-50%)",
30528
- visibility: "hidden",
30529
- width: 12 * zoomFactor,
30530
- zIndex: 26
30531
- },
30532
- children: "\u25BE"
30533
- }
30534
- ),
30535
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30536
- "div",
30537
- {
30538
- ref: fillHandleRef,
30539
- onPointerDown: (event) => {
30540
- if (readOnly || event.button !== 0 || !normalizedSelection || !resolvedSelectionOverlay) {
30541
- return;
30542
- }
30543
- event.preventDefault();
30544
- event.stopPropagation();
30545
- startFillDrag(event.pointerId, normalizedSelection);
30546
30371
  },
30547
- style: {
30548
- backgroundColor: selectionStroke,
30549
- border: `${Math.max(1, zoomFactor)}px solid ${palette.surface}`,
30550
- contain: "layout paint",
30551
- cursor: "crosshair",
30552
- display: !readOnly && resolvedSelectionOverlay ? "block" : "none",
30553
- height: 8 * zoomFactor,
30554
- left: resolvedSelectionOverlay ? resolvedSelectionOverlay.left + resolvedSelectionOverlay.width - 4 * zoomFactor : 0,
30555
- pointerEvents: "auto",
30556
- position: "absolute",
30557
- top: resolvedSelectionOverlay ? resolvedSelectionOverlay.top + resolvedSelectionOverlay.height - 4 * zoomFactor : 0,
30558
- transition: shouldAnimateCanvasSelection ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1)" : "none",
30559
- willChange: shouldAnimateCanvasSelection ? "left, top" : void 0,
30560
- width: 8 * zoomFactor,
30561
- zIndex: 25
30562
- }
30372
+ children: [
30373
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30374
+ "div",
30375
+ {
30376
+ ref: selectionOverlayRef,
30377
+ style: {
30378
+ backgroundColor: selectionFill,
30379
+ boxSizing: "border-box",
30380
+ boxShadow: `inset 0 0 0 ${selectionBorderWidth}px ${selectionStroke}`,
30381
+ contain: "layout paint",
30382
+ height: resolvedSelectionOverlay?.height ?? 0,
30383
+ left: resolvedSelectionOverlay?.left ?? 0,
30384
+ opacity: resolvedSelectionOverlay ? 1 : 0,
30385
+ pointerEvents: "none",
30386
+ position: "absolute",
30387
+ top: resolvedSelectionOverlay?.top ?? 0,
30388
+ transition: canvasSelectionTransition,
30389
+ visibility: resolvedSelectionOverlay ? "visible" : "hidden",
30390
+ willChange: shouldAnimateCanvasSelectionGeometry ? "left, top, width, height" : void 0,
30391
+ width: resolvedSelectionOverlay?.width ?? 0,
30392
+ zIndex: 24
30393
+ }
30394
+ }
30395
+ ),
30396
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30397
+ "div",
30398
+ {
30399
+ ref: activeValidationOverlayRef,
30400
+ "aria-hidden": "true",
30401
+ style: {
30402
+ alignItems: "center",
30403
+ color: palette.mutedText,
30404
+ display: "inline-flex",
30405
+ fontSize: 10 * zoomFactor,
30406
+ fontWeight: 700,
30407
+ height: 16 * zoomFactor,
30408
+ justifyContent: "center",
30409
+ opacity: 0,
30410
+ pointerEvents: "none",
30411
+ position: "absolute",
30412
+ transform: "translateY(-50%)",
30413
+ visibility: "hidden",
30414
+ width: 12 * zoomFactor,
30415
+ zIndex: 26
30416
+ },
30417
+ children: "\u25BE"
30418
+ }
30419
+ ),
30420
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30421
+ "div",
30422
+ {
30423
+ ref: fillHandleRef,
30424
+ onPointerDown: (event) => {
30425
+ if (readOnly || event.button !== 0 || !normalizedSelection || !resolvedSelectionOverlay) {
30426
+ return;
30427
+ }
30428
+ event.preventDefault();
30429
+ event.stopPropagation();
30430
+ startFillDrag(event.pointerId, normalizedSelection);
30431
+ },
30432
+ style: {
30433
+ backgroundColor: selectionStroke,
30434
+ border: `${Math.max(1, zoomFactor)}px solid ${palette.surface}`,
30435
+ contain: "layout paint",
30436
+ cursor: "crosshair",
30437
+ display: !readOnly && resolvedSelectionOverlay ? "block" : "none",
30438
+ height: 8 * zoomFactor,
30439
+ left: resolvedSelectionOverlay ? resolvedSelectionOverlay.left + resolvedSelectionOverlay.width - 4 * zoomFactor : 0,
30440
+ pointerEvents: "auto",
30441
+ position: "absolute",
30442
+ top: resolvedSelectionOverlay ? resolvedSelectionOverlay.top + resolvedSelectionOverlay.height - 4 * zoomFactor : 0,
30443
+ transition: shouldAnimateCanvasSelectionGeometry ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1)" : "none",
30444
+ willChange: shouldAnimateCanvasSelectionGeometry ? "left, top" : void 0,
30445
+ width: 8 * zoomFactor,
30446
+ zIndex: 25
30447
+ }
30448
+ }
30449
+ )
30450
+ ]
30563
30451
  }
30564
30452
  ),
30565
30453
  resizeGuide ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(