@extend-ai/react-xlsx 0.14.0 → 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) {
@@ -6262,7 +6114,7 @@ function parseWorkbookImageAssets(bytes, workbook) {
6262
6114
  const shapesByWorkbookSheetIndex = [];
6263
6115
  const sheetOrigins = [];
6264
6116
  const imageOriginsById = /* @__PURE__ */ new Map();
6265
- const dukeFormControlsByWorkbookSheetIndex = collectWorkbookFormControls(workbook);
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, workbook) {
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, workbook) {
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 [];
7317
+ function xlsxCaptionToDukeText(caption) {
7318
+ if (typeof caption === "string") {
7319
+ return { runs: [{ text: caption }] };
7449
7320
  }
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] : []);
7453
- }
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,13 +8471,19 @@ function useXlsxViewerController(options) {
8752
8471
  if (!effectiveSkipXmlParsing && hasIncompleteWorkerChartSnapshot(snapshot)) {
8753
8472
  throw new Error("Worker chart payload incomplete");
8754
8473
  }
8474
+ const workerImageAssets = effectiveSkipXmlParsing ? null : parseWorkbookImageAssets(new Uint8Array(buffer));
8475
+ if (!isCurrent || abortController.signal.aborted) {
8476
+ revokeWorkbookImageAssets(workerImageAssets);
8477
+ return;
8478
+ }
8479
+ setImageAssets(workerImageAssets);
8480
+ setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8755
8481
  setWorkbook(null);
8756
8482
  setSheets(snapshot.sheets);
8757
8483
  setChartsByWorkbookSheetIndex(snapshot.chartsByWorkbookSheetIndex);
8758
8484
  setChartsheets(snapshot.chartsheets);
8759
8485
  setTabs(snapshot.tabs);
8760
8486
  chartAssetsRef.current = null;
8761
- setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8762
8487
  setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);
8763
8488
  setShouldAutoCalculate(false);
8764
8489
  setIsWorkerBacked(true);
@@ -9256,10 +8981,16 @@ function useXlsxViewerController(options) {
9256
8981
  const getFormControlItems = React.useCallback((controlIndex, sheetIndex = activeSheetIndex) => {
9257
8982
  const target = getFormControlWorksheet(sheetIndex);
9258
8983
  if (!workbook || !target) {
9259
- return [];
8984
+ const targetSheet = sheets[sheetIndex];
8985
+ if (!targetSheet) {
8986
+ return [];
8987
+ }
8988
+ const control2 = (formControlsByWorkbookSheetIndex[targetSheet.workbookSheetIndex] ?? []).find((entry) => entry.controlIndex === controlIndex);
8989
+ return control2?.items?.slice() ?? [];
9260
8990
  }
9261
8991
  const control = target.worksheet.formControls[controlIndex];
9262
- 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;
9263
8994
  if (!inputRange) {
9264
8995
  return [];
9265
8996
  }
@@ -9274,7 +9005,7 @@ function useXlsxViewerController(options) {
9274
9005
  }
9275
9006
  }
9276
9007
  return items;
9277
- }, [activeSheetIndex, getFormControlWorksheet, workbook]);
9008
+ }, [activeSheetIndex, formControlsByWorkbookSheetIndex, getFormControlWorksheet, sheets, workbook]);
9278
9009
  const getSheetShapes = React.useCallback((sheetIndex = activeSheetIndex) => {
9279
9010
  const targetSheet = sheets[sheetIndex];
9280
9011
  if (!targetSheet) {
@@ -9678,30 +9409,17 @@ function useXlsxViewerController(options) {
9678
9409
  return null;
9679
9410
  }
9680
9411
  recordHistoryBeforeMutation();
9681
- const controlIndex = target.worksheet.addFormControl(input);
9682
- const controls = target.worksheet.formControls;
9683
- const addedControl = controls[controlIndex];
9684
- if (addedControl?.kind.kind === "optionButton" && addedControl.kind.state === "checked") {
9685
- const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
9686
- for (const siblingIndex of groupIndexes) {
9687
- if (siblingIndex === controlIndex) {
9688
- continue;
9689
- }
9690
- const sibling = target.worksheet.formControls[siblingIndex];
9691
- if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9692
- target.worksheet.setFormControl(siblingIndex, {
9693
- ...formControlInputFromDukeControl(sibling),
9694
- kind: { ...sibling.kind, state: "unchecked" }
9695
- });
9696
- }
9697
- }
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();
9698
9422
  }
9699
- syncFormControlLinkedCell(
9700
- workbook,
9701
- target.workbookSheetIndex,
9702
- target.worksheet.formControls,
9703
- controlIndex
9704
- );
9705
9423
  maybeRecalculateWorkbook(workbook);
9706
9424
  refreshWorkbookState(workbook);
9707
9425
  return controlIndex;
@@ -9712,35 +9430,52 @@ function useXlsxViewerController(options) {
9712
9430
  if (readOnly || !workbook || !target || !currentControl) {
9713
9431
  return false;
9714
9432
  }
9715
- recordHistoryBeforeMutation();
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
+ }
9716
9451
  const nextInput = {
9717
- ...formControlInputFromDukeControl(currentControl),
9718
- ...patch,
9719
- anchor: patch.anchor ? { ...patch.anchor } : { ...currentControl.anchor },
9720
- 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
9721
9466
  };
9722
- if (nextInput.kind.kind === "optionButton" && nextInput.kind.state === "checked") {
9723
- const groupIndexes = resolveOptionButtonGroupIndexes(target.worksheet.formControls, controlIndex);
9724
- for (const siblingIndex of groupIndexes) {
9725
- if (siblingIndex === controlIndex) {
9726
- continue;
9727
- }
9728
- const sibling = target.worksheet.formControls[siblingIndex];
9729
- if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9730
- target.worksheet.setFormControl(siblingIndex, {
9731
- ...formControlInputFromDukeControl(sibling),
9732
- kind: { ...sibling.kind, state: "unchecked" }
9733
- });
9734
- }
9735
- }
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();
9736
9478
  }
9737
- target.worksheet.setFormControl(controlIndex, nextInput);
9738
- syncFormControlLinkedCell(
9739
- workbook,
9740
- target.workbookSheetIndex,
9741
- target.worksheet.formControls,
9742
- controlIndex
9743
- );
9744
9479
  maybeRecalculateWorkbook(workbook);
9745
9480
  refreshWorkbookState(workbook);
9746
9481
  return true;
@@ -9751,10 +9486,16 @@ function useXlsxViewerController(options) {
9751
9486
  return false;
9752
9487
  }
9753
9488
  recordHistoryBeforeMutation();
9754
- 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
+ }
9755
9496
  refreshWorkbookState(workbook);
9756
9497
  return true;
9757
- }, [activeSheetIndex, getFormControlWorksheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9498
+ }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9758
9499
  const recordCellEditHistory = React.useCallback((cell, before, after) => {
9759
9500
  if (!activeSheet || isApplyingHistoryRef.current) {
9760
9501
  return;
@@ -17932,7 +17673,7 @@ var THUMBNAIL_MAX_COLS = 80;
17932
17673
  var THUMBNAIL_MAX_SOURCE_HEIGHT_PX = 900;
17933
17674
  var THUMBNAIL_MAX_SOURCE_WIDTH_PX = 1440;
17934
17675
  var LIVE_ZOOM_COMMIT_IDLE_MS = 48;
17935
- var WHEEL_ZOOM_SENSITIVITY = 25e-5;
17676
+ var WHEEL_ZOOM_SENSITIVITY = 25e-4;
17936
17677
  var WHEEL_LINE_DELTA_PX = 16;
17937
17678
  var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669", "#ea580c", "#db2777"];
17938
17679
  var CHART_SOURCE_HIGHLIGHT_COLOR_BY_ROLE = {
@@ -18975,6 +18716,7 @@ function resolveAxisStartOffset(actualIndex, actualIndices, sizes, indexByActual
18975
18716
  function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWidths, options) {
18976
18717
  const headerHeight = options?.headerHeight ?? HEADER_HEIGHT;
18977
18718
  const rowHeaderWidth = options?.rowHeaderWidth ?? ROW_HEADER_WIDTH;
18719
+ const anchorZoomFactor = options?.zoomFactor ?? 1;
18978
18720
  const resolveMarkerLeft = (col, colOffsetEmu) => rowHeaderWidth + resolveAxisStartOffset(
18979
18721
  col,
18980
18722
  visibleCols,
@@ -18982,7 +18724,7 @@ function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWi
18982
18724
  options?.colIndexByActual,
18983
18725
  options?.colPrefixSums,
18984
18726
  options?.actualColPrefixSums
18985
- ) + emuToPixels(colOffsetEmu);
18727
+ ) + emuToPixels(colOffsetEmu) * anchorZoomFactor;
18986
18728
  const resolveMarkerTop = (row, rowOffsetEmu) => headerHeight + resolveAxisStartOffset(
18987
18729
  row,
18988
18730
  visibleRows,
@@ -18990,21 +18732,21 @@ function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWi
18990
18732
  options?.rowIndexByActual,
18991
18733
  options?.rowPrefixSums,
18992
18734
  options?.actualRowPrefixSums
18993
- ) + emuToPixels(rowOffsetEmu);
18735
+ ) + emuToPixels(rowOffsetEmu) * anchorZoomFactor;
18994
18736
  if (anchor.kind === "absolute") {
18995
18737
  return {
18996
- height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18997
- left: rowHeaderWidth + emuToPixels(anchor.positionEmu.x),
18998
- top: headerHeight + emuToPixels(anchor.positionEmu.y),
18999
- 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)
19000
18742
  };
19001
18743
  }
19002
18744
  if (anchor.kind === "one-cell") {
19003
18745
  return {
19004
- height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18746
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy) * anchorZoomFactor),
19005
18747
  left: resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu),
19006
18748
  top: resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu),
19007
- width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18749
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx) * anchorZoomFactor)
19008
18750
  };
19009
18751
  }
19010
18752
  const left = resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu);
@@ -20809,6 +20551,40 @@ function getCellBooleanValue(worksheet, row, col) {
20809
20551
  }
20810
20552
  return null;
20811
20553
  }
20554
+ function getBatchedCellNumericValue(cell) {
20555
+ if (!cell) {
20556
+ return null;
20557
+ }
20558
+ const text = cell.value.trim();
20559
+ if (text.length === 0) {
20560
+ return null;
20561
+ }
20562
+ if (text.toLowerCase() === "true") {
20563
+ return 1;
20564
+ }
20565
+ if (text.toLowerCase() === "false") {
20566
+ return 0;
20567
+ }
20568
+ const value = Number(text.replace(/,/g, ""));
20569
+ return Number.isFinite(value) ? value : null;
20570
+ }
20571
+ function getBatchedCellBooleanValue(cell) {
20572
+ if (!cell) {
20573
+ return null;
20574
+ }
20575
+ const text = cell.value.trim().toLowerCase();
20576
+ if (text.length === 0) {
20577
+ return null;
20578
+ }
20579
+ if (text === "true") {
20580
+ return true;
20581
+ }
20582
+ if (text === "false") {
20583
+ return false;
20584
+ }
20585
+ const value = Number(text);
20586
+ return Number.isFinite(value) ? value !== 0 : null;
20587
+ }
20812
20588
  function parseA1RangeReference3(reference) {
20813
20589
  const [startRef, endRef = startRef] = reference.split(":");
20814
20590
  const start = parseA1CellReference3(startRef ?? "");
@@ -22130,7 +21906,6 @@ function buildFormControlKindInput(control) {
22130
21906
  return {
22131
21907
  caption: control.caption ?? control.label ?? "",
22132
21908
  cellLink: control.linkedCell,
22133
- firstInGroup: control.firstInGroup,
22134
21909
  kind: "optionButton",
22135
21910
  no3D: control.no3D ?? false,
22136
21911
  state: control.state === "checked" ? "checked" : "unchecked"
@@ -23062,6 +22837,7 @@ function XlsxGrid({
23062
22837
  onFormControlAction,
23063
22838
  onFormControlChange,
23064
22839
  renderChartLoading,
22840
+ renderFormControl,
23065
22841
  palette,
23066
22842
  renderImage,
23067
22843
  renderImageSelection,
@@ -23175,6 +22951,7 @@ function XlsxGrid({
23175
22951
  const canvasCornerOverlayContentRef = React4.useRef(null);
23176
22952
  const canvasImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
23177
22953
  const selectionOverlayRef = React4.useRef(null);
22954
+ const canvasSelectionLayerRef = React4.useRef(null);
23178
22955
  const activeValidationOverlayRef = React4.useRef(null);
23179
22956
  const fillHandleRef = React4.useRef(null);
23180
22957
  const tableMenuRef = React4.useRef(null);
@@ -23200,9 +22977,11 @@ function XlsxGrid({
23200
22977
  const liveGestureZoomRef = React4.useRef(null);
23201
22978
  const pendingLiveGestureZoomStateRef = React4.useRef(null);
23202
22979
  const liveGestureZoomFrameRef = React4.useRef(null);
22980
+ const liveGestureZoomTransformFrameRef = React4.useRef(null);
23203
22981
  const pendingZoomAnchorRef = React4.useRef(null);
23204
22982
  const liveZoomCommitTimerRef = React4.useRef(null);
23205
22983
  const pendingLiveZoomCommitRef = React4.useRef(null);
22984
+ const gestureScrollerRectRef = React4.useRef(null);
23206
22985
  const touchPinchStateRef = React4.useRef(null);
23207
22986
  const safariPinchStartZoomRef = React4.useRef(null);
23208
22987
  const canvasTransformStyleCacheRef = React4.useRef(/* @__PURE__ */ new WeakMap());
@@ -23286,7 +23065,10 @@ function XlsxGrid({
23286
23065
  const setInteractionMode = React4.useCallback((mode) => {
23287
23066
  interactionModeRef.current = mode;
23288
23067
  }, []);
23289
- const worksheet = getActiveWorksheet();
23068
+ const worksheet = React4.useMemo(
23069
+ () => getActiveWorksheet(),
23070
+ [activeSheetIndex, getActiveWorksheet, revision]
23071
+ );
23290
23072
  const mergedRegions = React4.useMemo(() => {
23291
23073
  const regions = [];
23292
23074
  const rawMergedRegions = Array.isArray(worksheet?.mergedRegions) ? worksheet.mergedRegions : [];
@@ -23366,8 +23148,31 @@ function XlsxGrid({
23366
23148
  const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
23367
23149
  const scrollDeltaX = paintedViewport.left - nextLiveViewport.left;
23368
23150
  const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
23369
- const liveZoomTranslateX2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.x * (1 - liveZoomScale2) : 0;
23370
- 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
+ );
23371
23176
  const applyElementTransform = (element, transform, willChange, transformOrigin = null) => {
23372
23177
  if (!element) {
23373
23178
  return;
@@ -23388,7 +23193,10 @@ function XlsxGrid({
23388
23193
  willChange
23389
23194
  });
23390
23195
  };
23391
- 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);
23392
23200
  const shouldTransform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1;
23393
23201
  applyElementTransform(
23394
23202
  canvas,
@@ -23396,85 +23204,169 @@ function XlsxGrid({
23396
23204
  shouldTransform ? "transform" : ""
23397
23205
  );
23398
23206
  };
23399
- 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);
23400
23210
  applyElementTransform(
23401
23211
  element,
23402
- 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)`,
23403
23213
  "transform",
23404
23214
  "0 0"
23405
23215
  );
23406
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
+ );
23407
23230
  applyCanvasTransform(
23408
23231
  scrollBodyCanvasRef.current,
23409
- scrollDeltaX + liveZoomTranslateX2,
23410
- scrollDeltaY + liveZoomTranslateY2
23232
+ scrollDeltaX,
23233
+ scrollDeltaY,
23234
+ liveScrollBodyCanvasLeft,
23235
+ liveScrollBodyCanvasTop,
23236
+ liveZoomAnchorX,
23237
+ liveZoomAnchorY
23411
23238
  );
23412
23239
  applyCanvasTransform(
23413
23240
  topBodyCanvasRef.current,
23414
- scrollDeltaX + liveZoomTranslateX2,
23415
- liveZoomTranslateY2
23241
+ scrollDeltaX,
23242
+ 0,
23243
+ liveScrollBodyCanvasLeft,
23244
+ liveDisplayHeaderHeight,
23245
+ liveZoomAnchorX,
23246
+ 0
23416
23247
  );
23417
23248
  applyCanvasTransform(
23418
23249
  leftBodyCanvasRef.current,
23419
- liveZoomTranslateX2,
23420
- scrollDeltaY + liveZoomTranslateY2
23250
+ 0,
23251
+ scrollDeltaY,
23252
+ liveDisplayRowHeaderWidth,
23253
+ liveScrollBodyCanvasTop,
23254
+ 0,
23255
+ liveZoomAnchorY
23421
23256
  );
23422
23257
  applyCanvasTransform(
23423
23258
  cornerBodyCanvasRef.current,
23424
- liveZoomTranslateX2,
23425
- liveZoomTranslateY2
23259
+ 0,
23260
+ 0,
23261
+ liveDisplayRowHeaderWidth,
23262
+ liveDisplayHeaderHeight,
23263
+ 0,
23264
+ 0
23426
23265
  );
23427
23266
  applyCanvasTransform(
23428
23267
  topScrollHeaderCanvasRef.current,
23429
- scrollDeltaX + liveZoomTranslateX2,
23430
- liveZoomTranslateY2
23268
+ scrollDeltaX,
23269
+ 0,
23270
+ liveScrollBodyCanvasLeft,
23271
+ 0,
23272
+ liveZoomAnchorX,
23273
+ 0
23431
23274
  );
23432
23275
  applyCanvasTransform(
23433
23276
  topFrozenHeaderCanvasRef.current,
23434
- liveZoomTranslateX2,
23435
- liveZoomTranslateY2
23277
+ 0,
23278
+ 0,
23279
+ liveDisplayRowHeaderWidth,
23280
+ 0,
23281
+ 0,
23282
+ 0
23436
23283
  );
23437
23284
  applyCanvasTransform(
23438
23285
  leftScrollHeaderCanvasRef.current,
23439
- liveZoomTranslateX2,
23440
- scrollDeltaY + liveZoomTranslateY2
23286
+ 0,
23287
+ scrollDeltaY,
23288
+ 0,
23289
+ liveScrollBodyCanvasTop,
23290
+ 0,
23291
+ liveZoomAnchorY
23441
23292
  );
23442
23293
  applyCanvasTransform(
23443
23294
  leftFrozenHeaderCanvasRef.current,
23444
- liveZoomTranslateX2,
23445
- liveZoomTranslateY2
23295
+ 0,
23296
+ 0,
23297
+ 0,
23298
+ liveDisplayHeaderHeight,
23299
+ 0,
23300
+ 0
23446
23301
  );
23447
23302
  applyCanvasTransform(
23448
23303
  cornerHeaderCanvasRef.current,
23449
- liveZoomTranslateX2,
23450
- liveZoomTranslateY2
23304
+ 0,
23305
+ 0,
23306
+ 0,
23307
+ 0,
23308
+ 0,
23309
+ 0
23451
23310
  );
23452
23311
  applyOverlayTransform(
23453
23312
  canvasScrollOverlayContentRef.current,
23454
23313
  -nextLiveViewport.left - liveFrozenPaneRight,
23455
- -nextLiveViewport.top - liveFrozenPaneBottom
23314
+ -nextLiveViewport.top - liveFrozenPaneBottom,
23315
+ liveFrozenPaneRight,
23316
+ liveFrozenPaneBottom,
23317
+ liveZoomAnchorX,
23318
+ liveZoomAnchorY
23456
23319
  );
23457
23320
  applyOverlayTransform(
23458
23321
  canvasTopOverlayContentRef.current,
23459
23322
  -nextLiveViewport.left - liveFrozenPaneRight,
23460
- -liveDisplayHeaderHeight
23323
+ -liveDisplayHeaderHeight,
23324
+ liveFrozenPaneRight,
23325
+ liveDisplayHeaderHeight,
23326
+ liveZoomAnchorX,
23327
+ 0
23461
23328
  );
23462
23329
  applyOverlayTransform(
23463
23330
  canvasLeftOverlayContentRef.current,
23464
23331
  -liveDisplayRowHeaderWidth,
23465
- -nextLiveViewport.top - liveFrozenPaneBottom
23332
+ -nextLiveViewport.top - liveFrozenPaneBottom,
23333
+ liveDisplayRowHeaderWidth,
23334
+ liveFrozenPaneBottom,
23335
+ 0,
23336
+ liveZoomAnchorY
23466
23337
  );
23467
23338
  applyOverlayTransform(
23468
23339
  canvasCornerOverlayContentRef.current,
23469
23340
  -liveDisplayRowHeaderWidth,
23470
- -liveDisplayHeaderHeight
23341
+ -liveDisplayHeaderHeight,
23342
+ liveDisplayRowHeaderWidth,
23343
+ liveDisplayHeaderHeight,
23344
+ 0,
23345
+ 0
23471
23346
  );
23472
- }, [zoomScale]);
23347
+ }, [experimentalCanvas, zoomScale]);
23473
23348
  const updateLiveGestureZoomState = React4.useCallback((nextState) => {
23474
- const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
23349
+ const previousState = liveGestureZoomRef.current;
23350
+ const resolvedState = typeof nextState === "function" ? nextState(previousState) : nextState;
23475
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
+ }
23476
23369
  pendingLiveGestureZoomStateRef.current = resolvedState;
23477
- applyCanvasViewportCompensation();
23478
23370
  if (liveGestureZoomFrameRef.current !== null) {
23479
23371
  return;
23480
23372
  }
@@ -23484,7 +23376,7 @@ function XlsxGrid({
23484
23376
  pendingLiveGestureZoomStateRef.current = null;
23485
23377
  setLiveGestureZoom(pendingState);
23486
23378
  });
23487
- }, [applyCanvasViewportCompensation]);
23379
+ }, [applyCanvasViewportCompensation, experimentalCanvas]);
23488
23380
  const clearLiveZoomCommitTimer = React4.useCallback(() => {
23489
23381
  if (liveZoomCommitTimerRef.current !== null) {
23490
23382
  window.clearTimeout(liveZoomCommitTimerRef.current);
@@ -23497,6 +23389,7 @@ function XlsxGrid({
23497
23389
  pendingZoomAnchorRef.current = null;
23498
23390
  touchPinchStateRef.current = null;
23499
23391
  safariPinchStartZoomRef.current = null;
23392
+ gestureScrollerRectRef.current = null;
23500
23393
  gestureZoomScaleRef.current = committedZoomScaleRef.current;
23501
23394
  updateLiveGestureZoomState(null);
23502
23395
  }, [clearLiveZoomCommitTimer, updateLiveGestureZoomState]);
@@ -24111,6 +24004,10 @@ function XlsxGrid({
24111
24004
  window.cancelAnimationFrame(liveGestureZoomFrameRef.current);
24112
24005
  liveGestureZoomFrameRef.current = null;
24113
24006
  }
24007
+ if (liveGestureZoomTransformFrameRef.current !== null) {
24008
+ window.cancelAnimationFrame(liveGestureZoomTransformFrameRef.current);
24009
+ liveGestureZoomTransformFrameRef.current = null;
24010
+ }
24114
24011
  if (drawingViewportFrameRef.current !== null) {
24115
24012
  window.cancelAnimationFrame(drawingViewportFrameRef.current);
24116
24013
  drawingViewportFrameRef.current = null;
@@ -24374,6 +24271,7 @@ function XlsxGrid({
24374
24271
  const handleScrollerScroll = React4.useCallback((event) => {
24375
24272
  const currentScroller = event.currentTarget;
24376
24273
  cachedScrollerRectRef.current = null;
24274
+ gestureScrollerRectRef.current = null;
24377
24275
  const paintedViewport = paintedDrawingViewportRef.current;
24378
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;
24379
24277
  syncDrawingViewport(currentScroller, { immediate: shouldSyncDrawingViewportImmediately });
@@ -24408,7 +24306,9 @@ function XlsxGrid({
24408
24306
  return;
24409
24307
  }
24410
24308
  event.preventDefault();
24411
- 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);
24412
24312
  updateLiveGestureZoomTarget(
24413
24313
  gestureZoomScaleRef.current * Math.exp(-delta * WHEEL_ZOOM_SENSITIVITY),
24414
24314
  anchor
@@ -24428,10 +24328,12 @@ function XlsxGrid({
24428
24328
  return;
24429
24329
  }
24430
24330
  event.preventDefault();
24331
+ const scrollerRect = scroller.getBoundingClientRect();
24332
+ gestureScrollerRectRef.current = scrollerRect;
24431
24333
  const anchor = resolveEventAnchor(
24432
24334
  (firstTouch.clientX + secondTouch.clientX) / 2,
24433
24335
  (firstTouch.clientY + secondTouch.clientY) / 2,
24434
- scroller.getBoundingClientRect()
24336
+ scrollerRect
24435
24337
  );
24436
24338
  clearLiveZoomCommitTimer();
24437
24339
  updateLiveGestureZoomState({
@@ -24459,10 +24361,12 @@ function XlsxGrid({
24459
24361
  return;
24460
24362
  }
24461
24363
  event.preventDefault();
24364
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24365
+ gestureScrollerRectRef.current = scrollerRect;
24462
24366
  const anchor = resolveEventAnchor(
24463
24367
  (firstTouch.clientX + secondTouch.clientX) / 2,
24464
24368
  (firstTouch.clientY + secondTouch.clientY) / 2,
24465
- scroller.getBoundingClientRect()
24369
+ scrollerRect
24466
24370
  );
24467
24371
  updateLiveGestureZoomTarget(
24468
24372
  pinchState.startZoomScale * distance / pinchState.startDistance,
@@ -24483,10 +24387,12 @@ function XlsxGrid({
24483
24387
  const handleGestureStart = (event) => {
24484
24388
  const gestureEvent = event;
24485
24389
  event.preventDefault();
24390
+ const scrollerRect = scroller.getBoundingClientRect();
24391
+ gestureScrollerRectRef.current = scrollerRect;
24486
24392
  const anchor = resolveEventAnchor(
24487
24393
  gestureEvent.clientX,
24488
24394
  gestureEvent.clientY,
24489
- scroller.getBoundingClientRect()
24395
+ scrollerRect
24490
24396
  );
24491
24397
  clearLiveZoomCommitTimer();
24492
24398
  safariPinchStartZoomRef.current = committedZoomScaleRef.current;
@@ -24504,10 +24410,12 @@ function XlsxGrid({
24504
24410
  return;
24505
24411
  }
24506
24412
  event.preventDefault();
24413
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24414
+ gestureScrollerRectRef.current = scrollerRect;
24507
24415
  const anchor = resolveEventAnchor(
24508
24416
  gestureEvent.clientX,
24509
24417
  gestureEvent.clientY,
24510
- scroller.getBoundingClientRect()
24418
+ scrollerRect
24511
24419
  );
24512
24420
  updateLiveGestureZoomTarget(startZoomScale * gestureEvent.scale, anchor);
24513
24421
  };
@@ -25501,7 +25409,8 @@ function XlsxGrid({
25501
25409
  headerHeight: displayHeaderHeight,
25502
25410
  rowIndexByActual,
25503
25411
  rowHeaderWidth: displayRowHeaderWidth,
25504
- rowPrefixSums
25412
+ rowPrefixSums,
25413
+ zoomFactor
25505
25414
  })
25506
25415
  })) : [],
25507
25416
  [
@@ -25519,7 +25428,8 @@ function XlsxGrid({
25519
25428
  rowPrefixSums,
25520
25429
  showImages,
25521
25430
  visibleCols,
25522
- visibleRows
25431
+ visibleRows,
25432
+ zoomFactor
25523
25433
  ]
25524
25434
  );
25525
25435
  const shapeRects = React4.useMemo(
@@ -25532,7 +25442,8 @@ function XlsxGrid({
25532
25442
  headerHeight: displayHeaderHeight,
25533
25443
  rowIndexByActual,
25534
25444
  rowHeaderWidth: displayRowHeaderWidth,
25535
- rowPrefixSums
25445
+ rowPrefixSums,
25446
+ zoomFactor
25536
25447
  }),
25537
25448
  shape
25538
25449
  })) : [],
@@ -25550,7 +25461,8 @@ function XlsxGrid({
25550
25461
  shapes,
25551
25462
  showImages,
25552
25463
  visibleCols,
25553
- visibleRows
25464
+ visibleRows,
25465
+ zoomFactor
25554
25466
  ]
25555
25467
  );
25556
25468
  const formControlRects = React4.useMemo(
@@ -25564,7 +25476,8 @@ function XlsxGrid({
25564
25476
  headerHeight: displayHeaderHeight,
25565
25477
  rowIndexByActual,
25566
25478
  rowHeaderWidth: displayRowHeaderWidth,
25567
- rowPrefixSums
25479
+ rowPrefixSums,
25480
+ zoomFactor
25568
25481
  })
25569
25482
  })) : [],
25570
25483
  [
@@ -25581,7 +25494,8 @@ function XlsxGrid({
25581
25494
  rowPrefixSums,
25582
25495
  showImages,
25583
25496
  visibleCols,
25584
- visibleRows
25497
+ visibleRows,
25498
+ zoomFactor
25585
25499
  ]
25586
25500
  );
25587
25501
  const chartRects = React4.useMemo(
@@ -25595,7 +25509,8 @@ function XlsxGrid({
25595
25509
  headerHeight: displayHeaderHeight,
25596
25510
  rowIndexByActual,
25597
25511
  rowHeaderWidth: displayRowHeaderWidth,
25598
- rowPrefixSums
25512
+ rowPrefixSums,
25513
+ zoomFactor
25599
25514
  })
25600
25515
  })) : [],
25601
25516
  [
@@ -25613,7 +25528,8 @@ function XlsxGrid({
25613
25528
  rowPrefixSums,
25614
25529
  showImages,
25615
25530
  visibleCols,
25616
- visibleRows
25531
+ visibleRows,
25532
+ zoomFactor
25617
25533
  ]
25618
25534
  );
25619
25535
  const shouldBakeCanvasStaticDrawings = Boolean(experimentalCanvas && readOnly && showImages);
@@ -25626,14 +25542,7 @@ function XlsxGrid({
25626
25542
  () => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => shape.hyperlink) : shapeRects,
25627
25543
  [shapeRects, shouldBakeCanvasStaticDrawings]
25628
25544
  );
25629
- const bakedFormControlRects = React4.useMemo(
25630
- () => shouldBakeCanvasStaticDrawings ? formControlRects : [],
25631
- [formControlRects, shouldBakeCanvasStaticDrawings]
25632
- );
25633
- const domFormControlRects = React4.useMemo(
25634
- () => shouldBakeCanvasStaticDrawings ? [] : formControlRects,
25635
- [formControlRects, shouldBakeCanvasStaticDrawings]
25636
- );
25545
+ const domFormControlRects = formControlRects;
25637
25546
  const bakedImageRects = React4.useMemo(
25638
25547
  () => shouldBakeCanvasImages ? imageRects.filter(({ image }) => !image.hyperlink && selectedImageId !== image.id) : [],
25639
25548
  [imageRects, selectedImageId, shouldBakeCanvasImages]
@@ -25650,11 +25559,9 @@ function XlsxGrid({
25650
25559
  return "";
25651
25560
  }
25652
25561
  const shapeSignature = bakedShapeRects.map(({ rect, shape }) => `s:${shape.id}:${shape.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
25653
- const controlSignature = bakedFormControlRects.map(({ control, rect }) => `f:${control.id}:${control.zIndex}:${control.state ?? Boolean(control.checked)}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
25654
25562
  const imageSignature = bakedImageRects.map(({ image, rect }) => `i:${image.id}:${image.src}:${image.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
25655
- return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${controlSignature}:${imageSignature}`;
25563
+ return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${imageSignature}`;
25656
25564
  }, [
25657
- bakedFormControlRects,
25658
25565
  bakedImageRects,
25659
25566
  bakedShapeRects,
25660
25567
  canvasImageLoadVersion,
@@ -27078,6 +26985,8 @@ function XlsxGrid({
27078
26985
  palette,
27079
26986
  rowHeaderWidth,
27080
26987
  rowSignature: buildRenderedAxisSignature(canvasVisibleRowItems, (item) => item.index),
26988
+ viewportLeft: drawingViewport.left,
26989
+ viewportTop: drawingViewport.top,
27081
26990
  zoomFactor
27082
26991
  };
27083
26992
  const nextHeaderCanvasSignature = {
@@ -27097,7 +27006,7 @@ function XlsxGrid({
27097
27006
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
27098
27007
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
27099
27008
  const previousPaintedViewport = paintedDrawingViewportRef.current;
27100
- 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);
27101
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);
27102
27011
  const canBlitBody = Boolean(
27103
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
@@ -27189,12 +27098,6 @@ function XlsxGrid({
27189
27098
  shape,
27190
27099
  zIndex: shape.zIndex
27191
27100
  })),
27192
- ...bakedFormControlRects.map(({ control, rect }) => ({
27193
- control,
27194
- kind: "formControl",
27195
- rect,
27196
- zIndex: control.zIndex
27197
- })),
27198
27101
  ...bakedImageRects.map(({ image, rect }) => ({
27199
27102
  image,
27200
27103
  kind: "image",
@@ -27345,97 +27248,6 @@ function XlsxGrid({
27345
27248
  drawBakedShapeText(context, shape, localLeft, localTop, rect.width, rect.height);
27346
27249
  context.restore();
27347
27250
  };
27348
- const drawBakedFormControl = (context, control, rect) => {
27349
- const label = resolveFormControlLabel(control);
27350
- const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
27351
- const textColor = control.textColor ?? "#000000";
27352
- const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
27353
- const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
27354
- const controlChecked = isFormControlChecked(control);
27355
- const controlMixed = isFormControlMixed(control);
27356
- context.save();
27357
- context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
27358
- context.textBaseline = "middle";
27359
- context.fillStyle = textColor;
27360
- context.strokeStyle = stroke;
27361
- context.lineWidth = Math.max(1, zoomFactor);
27362
- if (control.kind === "group-box") {
27363
- const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;
27364
- drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);
27365
- context.stroke();
27366
- if (label) {
27367
- context.fillStyle = SHEET_SURFACE;
27368
- const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);
27369
- context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);
27370
- context.fillStyle = textColor;
27371
- context.textAlign = "left";
27372
- context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);
27373
- }
27374
- context.restore();
27375
- return;
27376
- }
27377
- if (control.kind === "button" || control.kind === "dropdown" || control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
27378
- if (control.kind === "button") {
27379
- const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);
27380
- gradient.addColorStop(0, "#f8fafc");
27381
- gradient.addColorStop(1, "#e2e8f0");
27382
- context.fillStyle = gradient;
27383
- } else {
27384
- context.fillStyle = "transparent";
27385
- }
27386
- drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === "button" ? 4 * zoomFactor : 2 * zoomFactor);
27387
- if (control.kind === "button") {
27388
- context.fill();
27389
- }
27390
- context.stroke();
27391
- }
27392
- let textLeft = rect.left + 2 * zoomFactor;
27393
- const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
27394
- if (control.kind === "checkbox") {
27395
- context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
27396
- if (controlChecked || controlMixed) {
27397
- context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
27398
- context.fillRect(rect.left + 2 * zoomFactor + 1.5, rect.top + (rect.height - iconSize) / 2 + 1.5, Math.max(0, iconSize - 3), Math.max(0, iconSize - 3));
27399
- context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
27400
- context.beginPath();
27401
- if (controlMixed) {
27402
- context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.25, rect.top + rect.height / 2);
27403
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.75, rect.top + rect.height / 2);
27404
- } else {
27405
- context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
27406
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
27407
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
27408
- }
27409
- context.stroke();
27410
- }
27411
- textLeft += iconSize + 4 * zoomFactor;
27412
- } else if (control.kind === "radio") {
27413
- context.beginPath();
27414
- context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
27415
- context.stroke();
27416
- if (controlChecked) {
27417
- context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
27418
- context.beginPath();
27419
- context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
27420
- context.fill();
27421
- }
27422
- textLeft += iconSize + 4 * zoomFactor;
27423
- }
27424
- if (label) {
27425
- const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);
27426
- const text = truncateCanvasText(context, label, maxTextWidth);
27427
- context.fillStyle = textColor;
27428
- context.textAlign = control.textAlign === "right" ? "right" : control.textAlign === "center" ? "center" : "left";
27429
- const textX = context.textAlign === "right" ? rect.left + rect.width - textRightInset : context.textAlign === "center" ? textLeft + maxTextWidth / 2 : textLeft;
27430
- context.fillText(text, textX, rect.top + rect.height / 2);
27431
- }
27432
- if (control.kind === "dropdown") {
27433
- context.fillStyle = textColor;
27434
- context.textAlign = "center";
27435
- context.fillText("\u25BC", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);
27436
- }
27437
- context.restore();
27438
- };
27439
27251
  const drawBakedCanvasDrawings = (pane, context, dirtyRects) => {
27440
27252
  if (bakedCanvasDrawingEntries.length === 0 || dirtyRects.length === 0) {
27441
27253
  return;
@@ -27450,8 +27262,6 @@ function XlsxGrid({
27450
27262
  }
27451
27263
  if (entry.kind === "shape") {
27452
27264
  drawBakedShape(context, entry.shape, localRect);
27453
- } else if (entry.kind === "formControl") {
27454
- drawBakedFormControl(context, entry.control, localRect);
27455
27265
  } else {
27456
27266
  const imageElement = getCanvasImage(entry.image);
27457
27267
  if (imageElement) {
@@ -28145,7 +27955,6 @@ function XlsxGrid({
28145
27955
  activeSheet,
28146
27956
  applyCanvasViewportCompensation,
28147
27957
  bakedCanvasDrawingSignature,
28148
- bakedFormControlRects,
28149
27958
  bakedImageRects,
28150
27959
  bakedShapeRects,
28151
27960
  canvasColumnHeaderCells,
@@ -28424,7 +28233,8 @@ function XlsxGrid({
28424
28233
  const headerLabelLiveScale = isLiveZooming ? liveZoomScale : 1;
28425
28234
  const selectionBorderWidth = Math.max(1, zoomFactor);
28426
28235
  const shouldAnimateCanvasSelection = experimentalCanvas && enableCanvasSelectionAnimation;
28427
- 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";
28428
28238
  const rowColSpan = renderedCols.length + 1 + (leadingColumnSpacerWidth > 0 ? 1 : 0) + (trailingColumnSpacerWidth > 0 ? 1 : 0);
28429
28239
  const gutterSeparatorShadow = `inset -1px 0 0 ${palette.border}, inset 0 -1px 0 ${palette.border}`;
28430
28240
  const maxDrawingOverlayZIndex = Math.max(
@@ -28764,8 +28574,8 @@ function XlsxGrid({
28764
28574
  event.stopPropagation();
28765
28575
  };
28766
28576
  const commitControlKind = (kind, nextControl) => {
28767
- if (control.controlIndex === void 0) {
28768
- return;
28577
+ if (readOnly || control.controlIndex === void 0) {
28578
+ return false;
28769
28579
  }
28770
28580
  const didUpdate = updateFormControl(control.controlIndex, { kind }, control.sheetIndex);
28771
28581
  if (didUpdate) {
@@ -28775,7 +28585,77 @@ function XlsxGrid({
28775
28585
  type: "change"
28776
28586
  });
28777
28587
  }
28588
+ return didUpdate;
28589
+ };
28590
+ const setControlState = (state = "unchecked") => {
28591
+ const kind = buildFormControlKindInput(control);
28592
+ if (kind?.kind === "checkbox") {
28593
+ return commitControlKind(
28594
+ { ...kind, state },
28595
+ { checked: state === "checked", state }
28596
+ );
28597
+ }
28598
+ if (kind?.kind === "optionButton" && state !== "mixed") {
28599
+ return commitControlKind(
28600
+ { ...kind, state },
28601
+ { checked: state === "checked", state }
28602
+ );
28603
+ }
28604
+ return false;
28605
+ };
28606
+ const setControlSelected = (selected) => {
28607
+ const kind = buildFormControlKindInput(control);
28608
+ if (kind?.kind === "dropdown") {
28609
+ const nextSelected = typeof selected === "number" ? selected : void 0;
28610
+ return commitControlKind(
28611
+ { ...kind, selected: nextSelected },
28612
+ { selected: nextSelected }
28613
+ );
28614
+ }
28615
+ if (kind?.kind === "listBox") {
28616
+ const nextSelected = Array.isArray(selected) ? selected : typeof selected === "number" ? [selected] : [];
28617
+ return commitControlKind(
28618
+ { ...kind, selected: nextSelected },
28619
+ { selected: nextSelected }
28620
+ );
28621
+ }
28622
+ return false;
28623
+ };
28624
+ const setControlValue = (value) => {
28625
+ if (!Number.isFinite(value)) {
28626
+ return false;
28627
+ }
28628
+ const kind = buildFormControlKindInput(control);
28629
+ if (kind?.kind !== "scrollbar" && kind?.kind !== "spinner") {
28630
+ return false;
28631
+ }
28632
+ const nextValue = Math.max(kind.min, Math.min(kind.max, value));
28633
+ return commitControlKind({ ...kind, value: nextValue }, { value: nextValue });
28634
+ };
28635
+ const activateControl = () => {
28636
+ if (!readOnly && control.kind === "button") {
28637
+ onFormControlAction?.({ control, type: "activate" });
28638
+ }
28778
28639
  };
28640
+ if (renderFormControl) {
28641
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(React4.Fragment, { children: renderFormControl({
28642
+ activate: activateControl,
28643
+ checked: isFormControlChecked(control),
28644
+ control,
28645
+ disabled: !isInteractiveControl,
28646
+ items: controlItems,
28647
+ label: controlLabel,
28648
+ mixed: isFormControlMixed(control),
28649
+ readOnly,
28650
+ rect,
28651
+ setSelected: setControlSelected,
28652
+ setState: setControlState,
28653
+ setValue: setControlValue,
28654
+ stopPropagation: stopControlPointerEvent,
28655
+ style: commonStyle,
28656
+ zoomFactor
28657
+ }) }, `${pane}-${control.id}`);
28658
+ }
28779
28659
  if (control.kind === "group-box") {
28780
28660
  const hasLabel = controlLabel.length > 0;
28781
28661
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -28819,19 +28699,16 @@ function XlsxGrid({
28819
28699
  }
28820
28700
  if (control.kind === "radio") {
28821
28701
  const checked = isFormControlChecked(control);
28822
- const kind = buildFormControlKindInput(control);
28823
28702
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
28824
28703
  "button",
28825
28704
  {
28705
+ "aria-disabled": readOnly || control.controlIndex === void 0,
28826
28706
  "aria-checked": checked,
28827
- disabled: readOnly || control.controlIndex === void 0,
28707
+ disabled: !readOnly && control.controlIndex === void 0,
28828
28708
  onClick: (event) => {
28829
28709
  event.stopPropagation();
28830
- if (!checked && kind?.kind === "optionButton") {
28831
- commitControlKind(
28832
- { ...kind, state: "checked" },
28833
- { checked: true, state: "checked" }
28834
- );
28710
+ if (!checked) {
28711
+ setControlState("checked");
28835
28712
  }
28836
28713
  },
28837
28714
  onPointerDown: stopControlPointerEvent,
@@ -28843,6 +28720,7 @@ function XlsxGrid({
28843
28720
  boxSizing: "border-box",
28844
28721
  padding: `0 ${Math.max(1, zoomFactor)}px`
28845
28722
  },
28723
+ tabIndex: readOnly ? -1 : void 0,
28846
28724
  type: "button",
28847
28725
  children: [
28848
28726
  renderRadioControl(checked, palette, zoomFactor),
@@ -28855,21 +28733,16 @@ function XlsxGrid({
28855
28733
  if (control.kind === "checkbox") {
28856
28734
  const checked = isFormControlChecked(control);
28857
28735
  const mixed = isFormControlMixed(control);
28858
- const kind = buildFormControlKindInput(control);
28859
28736
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
28860
28737
  "button",
28861
28738
  {
28739
+ "aria-disabled": readOnly || control.controlIndex === void 0,
28862
28740
  "aria-checked": mixed ? "mixed" : checked,
28863
- disabled: readOnly || control.controlIndex === void 0,
28741
+ disabled: !readOnly && control.controlIndex === void 0,
28864
28742
  onClick: (event) => {
28865
28743
  event.stopPropagation();
28866
- if (kind?.kind === "checkbox") {
28867
- const nextState = checked && !mixed ? "unchecked" : "checked";
28868
- commitControlKind(
28869
- { ...kind, state: nextState },
28870
- { checked: nextState === "checked", state: nextState }
28871
- );
28872
- }
28744
+ const nextState = checked && !mixed ? "unchecked" : "checked";
28745
+ setControlState(nextState);
28873
28746
  },
28874
28747
  onPointerDown: stopControlPointerEvent,
28875
28748
  role: "checkbox",
@@ -28880,6 +28753,7 @@ function XlsxGrid({
28880
28753
  boxSizing: "border-box",
28881
28754
  padding: `0 ${Math.max(1, zoomFactor)}px`
28882
28755
  },
28756
+ tabIndex: readOnly ? -1 : void 0,
28883
28757
  type: "button",
28884
28758
  children: [
28885
28759
  renderCheckboxControl(checked, palette, zoomFactor, mixed),
@@ -28893,10 +28767,11 @@ function XlsxGrid({
28893
28767
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
28894
28768
  "button",
28895
28769
  {
28896
- disabled: readOnly || !onFormControlAction,
28770
+ "aria-disabled": readOnly || !onFormControlAction,
28771
+ disabled: !readOnly && !onFormControlAction,
28897
28772
  onClick: (event) => {
28898
28773
  event.stopPropagation();
28899
- onFormControlAction?.({ control, type: "activate" });
28774
+ activateControl();
28900
28775
  },
28901
28776
  onPointerDown: stopControlPointerEvent,
28902
28777
  style: {
@@ -28909,6 +28784,7 @@ function XlsxGrid({
28909
28784
  padding: `0 ${6 * zoomFactor}px`,
28910
28785
  textAlign: "center"
28911
28786
  },
28787
+ tabIndex: readOnly ? -1 : void 0,
28912
28788
  type: "button",
28913
28789
  children: controlLabel
28914
28790
  },
@@ -28916,22 +28792,17 @@ function XlsxGrid({
28916
28792
  );
28917
28793
  }
28918
28794
  if (control.kind === "dropdown") {
28919
- const kind = buildFormControlKindInput(control);
28920
28795
  const selected = typeof control.selected === "number" ? control.selected : void 0;
28921
28796
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
28922
28797
  "select",
28923
28798
  {
28799
+ "aria-disabled": readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28924
28800
  "aria-label": controlLabel || control.name || "Dropdown form control",
28925
- disabled: readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28801
+ disabled: !readOnly && (control.controlIndex === void 0 || controlItems.length === 0),
28926
28802
  onChange: (event) => {
28927
28803
  event.stopPropagation();
28928
- if (kind?.kind === "dropdown") {
28929
- const nextSelected = event.currentTarget.value === "" ? void 0 : Number(event.currentTarget.value);
28930
- commitControlKind(
28931
- { ...kind, selected: nextSelected },
28932
- { selected: nextSelected }
28933
- );
28934
- }
28804
+ const nextSelected = event.currentTarget.value === "" ? void 0 : Number(event.currentTarget.value);
28805
+ setControlSelected(nextSelected);
28935
28806
  },
28936
28807
  onClick: stopControlPointerEvent,
28937
28808
  onPointerDown: stopControlPointerEvent,
@@ -28943,6 +28814,7 @@ function XlsxGrid({
28943
28814
  boxSizing: "border-box",
28944
28815
  padding: `0 ${4 * zoomFactor}px`
28945
28816
  },
28817
+ tabIndex: readOnly ? -1 : void 0,
28946
28818
  value: selected === void 0 ? "" : String(selected),
28947
28819
  children: [
28948
28820
  selected === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: controlLabel }) : null,
@@ -28953,24 +28825,19 @@ function XlsxGrid({
28953
28825
  );
28954
28826
  }
28955
28827
  if (control.kind === "listbox") {
28956
- const kind = buildFormControlKindInput(control);
28957
28828
  const selected = Array.isArray(control.selected) ? control.selected : control.selected === void 0 ? [] : [control.selected];
28958
28829
  const allowsMultipleSelection = (control.selection ?? "single") !== "single";
28959
28830
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
28960
28831
  "select",
28961
28832
  {
28833
+ "aria-disabled": readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28962
28834
  "aria-label": controlLabel || control.name || "List box form control",
28963
- disabled: readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28835
+ disabled: !readOnly && (control.controlIndex === void 0 || controlItems.length === 0),
28964
28836
  multiple: allowsMultipleSelection,
28965
28837
  onChange: (event) => {
28966
28838
  event.stopPropagation();
28967
- if (kind?.kind === "listBox") {
28968
- const nextSelected = Array.from(event.currentTarget.selectedOptions, (option) => Number(option.value));
28969
- commitControlKind(
28970
- { ...kind, selected: nextSelected },
28971
- { selected: nextSelected }
28972
- );
28973
- }
28839
+ const nextSelected = Array.from(event.currentTarget.selectedOptions, (option) => Number(option.value));
28840
+ setControlSelected(nextSelected);
28974
28841
  },
28975
28842
  onClick: stopControlPointerEvent,
28976
28843
  onPointerDown: stopControlPointerEvent,
@@ -28985,6 +28852,7 @@ function XlsxGrid({
28985
28852
  display: "block",
28986
28853
  padding: 0
28987
28854
  },
28855
+ tabIndex: readOnly ? -1 : void 0,
28988
28856
  value: allowsMultipleSelection ? selected.map(String) : selected[0] === void 0 ? "" : String(selected[0]),
28989
28857
  children: controlItems.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: index, children: item }, index))
28990
28858
  },
@@ -28992,22 +28860,19 @@ function XlsxGrid({
28992
28860
  );
28993
28861
  }
28994
28862
  if (control.kind === "scrollbar") {
28995
- const kind = buildFormControlKindInput(control);
28996
28863
  const min = control.min ?? 0;
28997
28864
  const max = Math.max(min, control.max ?? 100);
28998
28865
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
28999
28866
  "input",
29000
28867
  {
28868
+ "aria-disabled": readOnly || control.controlIndex === void 0,
29001
28869
  "aria-label": controlLabel || control.name || "Scrollbar form control",
29002
- disabled: readOnly || control.controlIndex === void 0,
28870
+ disabled: !readOnly && control.controlIndex === void 0,
29003
28871
  max,
29004
28872
  min,
29005
28873
  onChange: (event) => {
29006
28874
  event.stopPropagation();
29007
- if (kind?.kind === "scrollbar") {
29008
- const value = event.currentTarget.valueAsNumber;
29009
- commitControlKind({ ...kind, value }, { value });
29010
- }
28875
+ setControlValue(event.currentTarget.valueAsNumber);
29011
28876
  },
29012
28877
  onClick: stopControlPointerEvent,
29013
28878
  onPointerDown: stopControlPointerEvent,
@@ -29019,6 +28884,7 @@ function XlsxGrid({
29019
28884
  padding: 0,
29020
28885
  writingMode: control.horizontal === false ? "vertical-lr" : void 0
29021
28886
  },
28887
+ tabIndex: readOnly ? -1 : void 0,
29022
28888
  type: "range",
29023
28889
  value: Math.max(min, Math.min(max, control.value ?? min))
29024
28890
  },
@@ -29026,22 +28892,19 @@ function XlsxGrid({
29026
28892
  );
29027
28893
  }
29028
28894
  if (control.kind === "spinner") {
29029
- const kind = buildFormControlKindInput(control);
29030
28895
  const min = control.min ?? 0;
29031
28896
  const max = Math.max(min, control.max ?? 100);
29032
28897
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
29033
28898
  "input",
29034
28899
  {
28900
+ "aria-disabled": readOnly || control.controlIndex === void 0,
29035
28901
  "aria-label": controlLabel || control.name || "Spinner form control",
29036
- disabled: readOnly || control.controlIndex === void 0,
28902
+ disabled: !readOnly && control.controlIndex === void 0,
29037
28903
  max,
29038
28904
  min,
29039
28905
  onChange: (event) => {
29040
28906
  event.stopPropagation();
29041
- if (kind?.kind === "spinner" && Number.isFinite(event.currentTarget.valueAsNumber)) {
29042
- const value = Math.max(min, Math.min(max, event.currentTarget.valueAsNumber));
29043
- commitControlKind({ ...kind, value }, { value });
29044
- }
28907
+ setControlValue(event.currentTarget.valueAsNumber);
29045
28908
  },
29046
28909
  onClick: stopControlPointerEvent,
29047
28910
  onPointerDown: stopControlPointerEvent,
@@ -29054,6 +28917,7 @@ function XlsxGrid({
29054
28917
  boxSizing: "border-box",
29055
28918
  padding: `0 ${4 * zoomFactor}px`
29056
28919
  },
28920
+ tabIndex: readOnly ? -1 : void 0,
29057
28921
  type: "number",
29058
28922
  value: Math.max(min, Math.min(max, control.value ?? min))
29059
28923
  },
@@ -29357,7 +29221,7 @@ function XlsxGrid({
29357
29221
  };
29358
29222
  const drawingViewportCacheSignature = `${Math.floor(drawingViewport.left / CANVAS_VIEWPORT_OVERSCAN_PX)}:${Math.floor(drawingViewport.top / CANVAS_VIEWPORT_OVERSCAN_PX)}:${drawingViewport.width}:${drawingViewport.height}`;
29359
29223
  const previousPaneDrawingNodes = paneDrawingNodesCacheRef.current;
29360
- const canReusePaneDrawingNodes = previousPaneDrawingNodes !== null && previousPaneDrawingNodes.showImages === showImages && previousPaneDrawingNodes.chartRects === chartRects && previousPaneDrawingNodes.formControlRects === domFormControlRects && previousPaneDrawingNodes.shapeRects === domShapeRects && previousPaneDrawingNodes.imageRects === domImageRects && previousPaneDrawingNodes.selectedChartId === selectedChartId && previousPaneDrawingNodes.selectedImageId === selectedImageId && previousPaneDrawingNodes.readOnly === readOnly && previousPaneDrawingNodes.selectionStroke === selectionStroke && previousPaneDrawingNodes.renderChartLoading === renderChartLoading && previousPaneDrawingNodes.renderImage === renderImage && previousPaneDrawingNodes.renderImageSelection === renderImageSelection && previousPaneDrawingNodes.isChartsLoading === isChartsLoading && previousPaneDrawingNodes.selectedChartElement === selectedChartElement && previousPaneDrawingNodes.palette === palette && previousPaneDrawingNodes.drawingViewportSignature === drawingViewportCacheSignature;
29224
+ const canReusePaneDrawingNodes = previousPaneDrawingNodes !== null && previousPaneDrawingNodes.showImages === showImages && previousPaneDrawingNodes.updateFormControl === updateFormControl && previousPaneDrawingNodes.chartRects === chartRects && previousPaneDrawingNodes.formControlRects === domFormControlRects && previousPaneDrawingNodes.formControlItemsById === formControlItemsById && previousPaneDrawingNodes.shapeRects === domShapeRects && previousPaneDrawingNodes.imageRects === domImageRects && previousPaneDrawingNodes.selectedChartId === selectedChartId && previousPaneDrawingNodes.selectedImageId === selectedImageId && previousPaneDrawingNodes.readOnly === readOnly && previousPaneDrawingNodes.onFormControlAction === onFormControlAction && previousPaneDrawingNodes.onFormControlChange === onFormControlChange && previousPaneDrawingNodes.selectionStroke === selectionStroke && previousPaneDrawingNodes.renderChartLoading === renderChartLoading && previousPaneDrawingNodes.renderFormControl === renderFormControl && previousPaneDrawingNodes.renderImage === renderImage && previousPaneDrawingNodes.renderImageSelection === renderImageSelection && previousPaneDrawingNodes.isChartsLoading === isChartsLoading && previousPaneDrawingNodes.selectedChartElement === selectedChartElement && previousPaneDrawingNodes.palette === palette && previousPaneDrawingNodes.drawingViewportSignature === drawingViewportCacheSignature;
29361
29225
  const paneDrawingNodes = canReusePaneDrawingNodes ? previousPaneDrawingNodes.value : !showImages ? {
29362
29226
  corner: null,
29363
29227
  left: null,
@@ -29393,12 +29257,16 @@ function XlsxGrid({
29393
29257
  paneDrawingNodesCacheRef.current = {
29394
29258
  chartRects,
29395
29259
  drawingViewportSignature: drawingViewportCacheSignature,
29260
+ formControlItemsById,
29396
29261
  formControlRects: domFormControlRects,
29397
29262
  imageRects: domImageRects,
29398
29263
  isChartsLoading,
29264
+ onFormControlAction,
29265
+ onFormControlChange,
29399
29266
  palette,
29400
29267
  readOnly,
29401
29268
  renderChartLoading,
29269
+ renderFormControl,
29402
29270
  renderImage,
29403
29271
  renderImageSelection,
29404
29272
  selectedChartElement,
@@ -29407,6 +29275,7 @@ function XlsxGrid({
29407
29275
  selectionStroke,
29408
29276
  shapeRects: domShapeRects,
29409
29277
  showImages,
29278
+ updateFormControl,
29410
29279
  value: paneDrawingNodes
29411
29280
  };
29412
29281
  }
@@ -30484,81 +30353,101 @@ function XlsxGrid({
30484
30353
  ]
30485
30354
  }
30486
30355
  ),
30487
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30356
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
30488
30357
  "div",
30489
30358
  {
30490
- ref: selectionOverlayRef,
30359
+ ref: canvasSelectionLayerRef,
30491
30360
  style: {
30492
- backgroundColor: selectionFill,
30493
- boxSizing: "border-box",
30494
- boxShadow: `inset 0 0 0 ${selectionBorderWidth}px ${selectionStroke}`,
30495
- contain: "layout paint",
30496
- height: resolvedSelectionOverlay?.height ?? 0,
30497
- left: resolvedSelectionOverlay?.left ?? 0,
30498
- opacity: resolvedSelectionOverlay ? 1 : 0,
30361
+ height: 0,
30362
+ left: 0,
30363
+ overflow: "visible",
30499
30364
  pointerEvents: "none",
30500
30365
  position: "absolute",
30501
- top: resolvedSelectionOverlay?.top ?? 0,
30502
- transition: canvasSelectionTransition,
30503
- visibility: resolvedSelectionOverlay ? "visible" : "hidden",
30504
- willChange: shouldAnimateCanvasSelection ? "left, top, width, height" : void 0,
30505
- width: resolvedSelectionOverlay?.width ?? 0,
30366
+ top: 0,
30367
+ transformOrigin: "0 0",
30368
+ transition: "none",
30369
+ width: 0,
30506
30370
  zIndex: 24
30507
- }
30508
- }
30509
- ),
30510
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30511
- "div",
30512
- {
30513
- ref: activeValidationOverlayRef,
30514
- "aria-hidden": "true",
30515
- style: {
30516
- alignItems: "center",
30517
- color: palette.mutedText,
30518
- display: "inline-flex",
30519
- fontSize: 10 * zoomFactor,
30520
- fontWeight: 700,
30521
- height: 16 * zoomFactor,
30522
- justifyContent: "center",
30523
- opacity: 0,
30524
- pointerEvents: "none",
30525
- position: "absolute",
30526
- transform: "translateY(-50%)",
30527
- visibility: "hidden",
30528
- width: 12 * zoomFactor,
30529
- zIndex: 26
30530
- },
30531
- children: "\u25BE"
30532
- }
30533
- ),
30534
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30535
- "div",
30536
- {
30537
- ref: fillHandleRef,
30538
- onPointerDown: (event) => {
30539
- if (readOnly || event.button !== 0 || !normalizedSelection || !resolvedSelectionOverlay) {
30540
- return;
30541
- }
30542
- event.preventDefault();
30543
- event.stopPropagation();
30544
- startFillDrag(event.pointerId, normalizedSelection);
30545
30371
  },
30546
- style: {
30547
- backgroundColor: selectionStroke,
30548
- border: `${Math.max(1, zoomFactor)}px solid ${palette.surface}`,
30549
- contain: "layout paint",
30550
- cursor: "crosshair",
30551
- display: !readOnly && resolvedSelectionOverlay ? "block" : "none",
30552
- height: 8 * zoomFactor,
30553
- left: resolvedSelectionOverlay ? resolvedSelectionOverlay.left + resolvedSelectionOverlay.width - 4 * zoomFactor : 0,
30554
- pointerEvents: "auto",
30555
- position: "absolute",
30556
- top: resolvedSelectionOverlay ? resolvedSelectionOverlay.top + resolvedSelectionOverlay.height - 4 * zoomFactor : 0,
30557
- transition: shouldAnimateCanvasSelection ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1)" : "none",
30558
- willChange: shouldAnimateCanvasSelection ? "left, top" : void 0,
30559
- width: 8 * zoomFactor,
30560
- zIndex: 25
30561
- }
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
+ ]
30562
30451
  }
30563
30452
  ),
30564
30453
  resizeGuide ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -30636,6 +30525,7 @@ function XlsxViewerInner({
30636
30525
  onFormControlAction,
30637
30526
  onFormControlChange,
30638
30527
  renderChartLoading,
30528
+ renderFormControl,
30639
30529
  renderImage,
30640
30530
  renderImageSelection,
30641
30531
  renderScroller,
@@ -30701,6 +30591,7 @@ function XlsxViewerInner({
30701
30591
  onFormControlChange,
30702
30592
  palette,
30703
30593
  renderChartLoading,
30594
+ renderFormControl,
30704
30595
  renderImage,
30705
30596
  renderImageSelection,
30706
30597
  renderScroller,
@@ -31121,20 +31012,87 @@ function useXlsxViewerCharts() {
31121
31012
  );
31122
31013
  }
31123
31014
  function useXlsxViewerThumbnails(options = {}) {
31124
- const { getSheetFormControls, getSheetImages, getSheetShapes, workbook, sheets } = useXlsxViewer();
31015
+ const {
31016
+ getRowsBatchAsync,
31017
+ getSheetFormControls,
31018
+ getSheetImages,
31019
+ getSheetShapes,
31020
+ workbook,
31021
+ sheets
31022
+ } = useXlsxViewer();
31125
31023
  const { isDark } = React4.useContext(ViewerAppearanceContext);
31126
31024
  const palette = useViewerPalette(isDark);
31127
31025
  const includeHeaders = options.includeHeaders ?? true;
31128
31026
  const resolution = options.resolution;
31129
31027
  const thumbnailImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
31028
+ const workerThumbnailRowBatchCacheRef = React4.useRef(/* @__PURE__ */ new Map());
31029
+ const workerThumbnailRowBatchRequestRef = React4.useRef(
31030
+ /* @__PURE__ */ new Map()
31031
+ );
31130
31032
  const isMountedRef = React4.useRef(false);
31131
31033
  const [thumbnailImageLoadVersion, setThumbnailImageLoadVersion] = React4.useState(0);
31034
+ const [workerThumbnailRowBatchVersion, setWorkerThumbnailRowBatchVersion] = React4.useState(0);
31132
31035
  React4.useEffect(() => {
31133
31036
  isMountedRef.current = true;
31134
31037
  return () => {
31135
31038
  isMountedRef.current = false;
31136
31039
  };
31137
31040
  }, []);
31041
+ React4.useEffect(() => {
31042
+ const currentSheets = new Set(sheets);
31043
+ for (const sheet of workerThumbnailRowBatchCacheRef.current.keys()) {
31044
+ if (!currentSheets.has(sheet)) {
31045
+ workerThumbnailRowBatchCacheRef.current.delete(sheet);
31046
+ }
31047
+ }
31048
+ for (const sheet of workerThumbnailRowBatchRequestRef.current.keys()) {
31049
+ if (!currentSheets.has(sheet)) {
31050
+ workerThumbnailRowBatchRequestRef.current.delete(sheet);
31051
+ }
31052
+ }
31053
+ }, [sheets]);
31054
+ const getWorkerThumbnailRowBatch = React4.useCallback((sheet, startRow, endRow) => {
31055
+ const cachedBatch = workerThumbnailRowBatchCacheRef.current.get(sheet);
31056
+ if (cachedBatch && startRow >= cachedBatch.startRow && endRow <= cachedBatch.endRow) {
31057
+ return cachedBatch;
31058
+ }
31059
+ if (!getRowsBatchAsync) {
31060
+ return null;
31061
+ }
31062
+ const pendingRequest = workerThumbnailRowBatchRequestRef.current.get(sheet);
31063
+ if (!pendingRequest) {
31064
+ const request = {
31065
+ endRow: Math.max(endRow, cachedBatch?.endRow ?? endRow),
31066
+ startRow: Math.min(startRow, cachedBatch?.startRow ?? startRow)
31067
+ };
31068
+ workerThumbnailRowBatchRequestRef.current.set(sheet, request);
31069
+ void getRowsBatchAsync(
31070
+ sheet.workbookSheetIndex,
31071
+ request.startRow,
31072
+ request.endRow - request.startRow + 1
31073
+ ).then((rows) => {
31074
+ if (workerThumbnailRowBatchRequestRef.current.get(sheet) !== request) {
31075
+ return;
31076
+ }
31077
+ workerThumbnailRowBatchRequestRef.current.delete(sheet);
31078
+ if (!rows) {
31079
+ return;
31080
+ }
31081
+ workerThumbnailRowBatchCacheRef.current.set(
31082
+ sheet,
31083
+ buildWorksheetBatchWindow(rows, sheet, request.startRow, request.endRow)
31084
+ );
31085
+ if (isMountedRef.current) {
31086
+ setWorkerThumbnailRowBatchVersion((version) => version + 1);
31087
+ }
31088
+ }).catch(() => {
31089
+ if (workerThumbnailRowBatchRequestRef.current.get(sheet) === request) {
31090
+ workerThumbnailRowBatchRequestRef.current.delete(sheet);
31091
+ }
31092
+ });
31093
+ }
31094
+ return null;
31095
+ }, [getRowsBatchAsync]);
31138
31096
  const getThumbnailImage = React4.useCallback((image) => {
31139
31097
  if (typeof Image === "undefined") {
31140
31098
  return null;
@@ -31360,6 +31318,10 @@ function useXlsxViewerThumbnails(options = {}) {
31360
31318
  if (!canvas) {
31361
31319
  return false;
31362
31320
  }
31321
+ const workerRowBatch = worksheet ? null : getWorkerThumbnailRowBatch(sheet, sourceRange.start.row, sourceRange.end.row);
31322
+ if (!worksheet && !workerRowBatch) {
31323
+ return false;
31324
+ }
31363
31325
  const context = canvas.getContext("2d", { alpha: false });
31364
31326
  if (!context) {
31365
31327
  return false;
@@ -31396,9 +31358,6 @@ function useXlsxViewerThumbnails(options = {}) {
31396
31358
  context.fillRect(0, headerHeight, rowHeaderWidth, Math.max(1, rowAxis.totalSize));
31397
31359
  context.fillRect(0, 0, rowHeaderWidth, headerHeight);
31398
31360
  }
31399
- if (!worksheet) {
31400
- return true;
31401
- }
31402
31361
  const conditionalFormatMetricsCache = /* @__PURE__ */ new Map();
31403
31362
  const cellRenderCache = /* @__PURE__ */ new Map();
31404
31363
  const getCellData = (row, col) => {
@@ -31407,7 +31366,9 @@ function useXlsxViewerThumbnails(options = {}) {
31407
31366
  if (cached) {
31408
31367
  return cached;
31409
31368
  }
31410
- if (worksheet.isMergedSecondary(row, col)) {
31369
+ const batchCoversRow = workerRowBatch ? row >= workerRowBatch.startRow && row <= workerRowBatch.endRow : false;
31370
+ const batchedCell = batchCoversRow ? workerRowBatch?.cells.get(cacheKey) : void 0;
31371
+ if (batchedCell?.isMergedSecondary || worksheet?.isMergedSecondary(row, col)) {
31411
31372
  const mergedSecondaryData = {
31412
31373
  isMergedSecondary: true,
31413
31374
  style: {},
@@ -31416,53 +31377,34 @@ function useXlsxViewerThumbnails(options = {}) {
31416
31377
  cellRenderCache.set(cacheKey, mergedSecondaryData);
31417
31378
  return mergedSecondaryData;
31418
31379
  }
31419
- const merge = worksheet.getMergeSpan(row, col);
31380
+ const merge = worksheet ? worksheet.getMergeSpan(row, col) : batchedCell?.mergeSpan;
31420
31381
  const inheritedStyle = resolveInheritedCellStyle2(sheet, row, col);
31421
- const worksheetStyle = worksheet.getCellStyleAt(row, col) ?? null;
31382
+ const worksheetStyle = worksheet ? worksheet.getCellStyleAt(row, col) ?? null : batchedCell?.style ?? null;
31422
31383
  const mergedStyle = mergeResolvedCellStyle(inheritedStyle, worksheetStyle, { replaceXfSubtrees: true });
31423
31384
  const alignment = mergedStyle?.alignment;
31424
31385
  const sparkline = sparklineByCell.get(cacheKey) ?? null;
31386
+ const getNumericValue = (targetRow, targetCol) => worksheet ? getCellNumericValue(worksheet, targetRow, targetCol) : getBatchedCellNumericValue(workerRowBatch?.cells.get(`${targetRow}:${targetCol}`));
31425
31387
  const sparklineValues = sparkline ? sparkline.range.start.row === sparkline.range.end.row ? Array.from(
31426
31388
  { length: Math.abs(sparkline.range.end.col - sparkline.range.start.col) + 1 },
31427
- (_, index) => getCellNumericValue(
31428
- worksheet,
31389
+ (_, index) => getNumericValue(
31429
31390
  sparkline.range.start.row,
31430
31391
  Math.min(sparkline.range.start.col, sparkline.range.end.col) + index
31431
31392
  )
31432
31393
  ) : Array.from(
31433
31394
  { length: Math.abs(sparkline.range.end.row - sparkline.range.start.row) + 1 },
31434
- (_, index) => getCellNumericValue(
31435
- worksheet,
31395
+ (_, index) => getNumericValue(
31436
31396
  Math.min(sparkline.range.start.row, sparkline.range.end.row) + index,
31437
31397
  sparkline.range.start.col
31438
31398
  )
31439
31399
  ) : null;
31440
- const checkboxState = mergedStyle?.cellControl ? getCellBooleanValue(worksheet, row, col) : null;
31400
+ const checkboxState = mergedStyle?.cellControl ? worksheet ? getCellBooleanValue(worksheet, row, col) : getBatchedCellBooleanValue(batchedCell) : null;
31441
31401
  const nextData = {
31442
31402
  canvas: void 0,
31443
31403
  checkboxState,
31444
31404
  colSpan: merge?.colSpan,
31445
- conditionalColorScale: resolveConditionalColorScaleForCell(
31446
- row,
31447
- col,
31448
- worksheet,
31449
- sheet,
31450
- conditionalFormatMetricsCache
31451
- ),
31452
- conditionalDataBar: resolveConditionalDataBarForCell(
31453
- row,
31454
- col,
31455
- worksheet,
31456
- sheet,
31457
- conditionalFormatMetricsCache
31458
- ),
31459
- conditionalIcon: resolveConditionalIconForCell(
31460
- row,
31461
- col,
31462
- worksheet,
31463
- sheet,
31464
- conditionalFormatMetricsCache
31465
- ),
31405
+ conditionalColorScale: worksheet ? resolveConditionalColorScaleForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache) : null,
31406
+ conditionalDataBar: worksheet ? resolveConditionalDataBarForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache) : null,
31407
+ conditionalIcon: worksheet ? resolveConditionalIconForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache) : null,
31466
31408
  isMergedSecondary: false,
31467
31409
  rowSpan: merge?.rowSpan,
31468
31410
  sparkline: sparkline && sparklineValues ? { config: sparkline, values: sparklineValues } : null,
@@ -31470,7 +31412,7 @@ function useXlsxViewerThumbnails(options = {}) {
31470
31412
  showGridLines
31471
31413
  }),
31472
31414
  textRotationDeg: resolveSpreadsheetTextRotation(alignment?.textRotation),
31473
- value: sparkline ? "" : checkboxState !== null ? "" : getCellDisplayValue(worksheet, row, col, sheet)
31415
+ value: sparkline ? "" : checkboxState !== null ? "" : worksheet ? getCellDisplayValue(worksheet, row, col, sheet) : batchedCell?.value ?? ""
31474
31416
  };
31475
31417
  nextData.canvas = buildCanvasCellStyleCache(nextData.style);
31476
31418
  cellRenderCache.set(cacheKey, nextData);
@@ -31744,12 +31686,14 @@ function useXlsxViewerThumbnails(options = {}) {
31744
31686
  getSheetFormControls,
31745
31687
  getSheetImages,
31746
31688
  getSheetShapes,
31689
+ getWorkerThumbnailRowBatch,
31747
31690
  getThumbnailImage,
31748
31691
  includeHeaders,
31749
31692
  palette,
31750
31693
  resolution,
31751
31694
  sheets,
31752
31695
  thumbnailImageLoadVersion,
31696
+ workerThumbnailRowBatchVersion,
31753
31697
  workbook
31754
31698
  ]);
31755
31699
  const paintThumbnail = React4.useCallback(