@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.js CHANGED
@@ -1956,7 +1956,7 @@ function normalizeChartExLegend(raw) {
1956
1956
  return null;
1957
1957
  }
1958
1958
  const legend = raw;
1959
- const position = typeof legend.pos === "string" ? normalizeLegendPosition(String(legend.pos)) : void 0;
1959
+ const position = typeof legend.position === "string" ? normalizeLegendPosition(String(legend.position)) : void 0;
1960
1960
  return {
1961
1961
  overlay: typeof legend.overlay === "boolean" ? legend.overlay : void 0,
1962
1962
  position,
@@ -2077,11 +2077,11 @@ function buildChartExHistogramBins(values, rawSeries, sortByFrequency) {
2077
2077
  return [];
2078
2078
  }
2079
2079
  const rawRecord = rawSeries && typeof rawSeries === "object" ? rawSeries : null;
2080
- const layoutProperties = rawRecord?.layoutPr && typeof rawRecord.layoutPr === "object" ? rawRecord.layoutPr : null;
2080
+ const layoutProperties = rawRecord?.layoutProperties && typeof rawRecord.layoutProperties === "object" ? rawRecord.layoutProperties : null;
2081
2081
  const rawBinning = layoutProperties?.binning && typeof layoutProperties.binning === "object" ? layoutProperties.binning : null;
2082
2082
  const minValue = Math.min(...values);
2083
2083
  const maxValue = Math.max(...values);
2084
- const explicitWidth = typeof rawBinning?.binWidth === "number" && Number.isFinite(rawBinning.binWidth) && rawBinning.binWidth > 0 ? rawBinning.binWidth : typeof rawBinning?.width === "number" && Number.isFinite(rawBinning.width) && rawBinning.width > 0 ? rawBinning.width : void 0;
2084
+ const explicitWidth = typeof rawBinning?.binSize === "number" && Number.isFinite(rawBinning.binSize) && rawBinning.binSize > 0 ? rawBinning.binSize : void 0;
2085
2085
  const explicitCount = typeof rawBinning?.binCount === "number" && Number.isFinite(rawBinning.binCount) && rawBinning.binCount > 0 ? rawBinning.binCount : typeof rawBinning?.count === "number" && Number.isFinite(rawBinning.count) && rawBinning.count > 0 ? rawBinning.count : void 0;
2086
2086
  const closedRight = rawBinning?.intervalClosed === "r" || rawBinning?.intervalClosed === "right";
2087
2087
  const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
@@ -2134,7 +2134,7 @@ function buildChartExHistogramSeries(series, rawSeries, sortByFrequency) {
2134
2134
  const layout = resolveChartExSeriesLayout(rawSeries);
2135
2135
  const rawRecord = rawSeries && typeof rawSeries === "object" ? rawSeries : null;
2136
2136
  const hasBinning = Boolean(
2137
- layout === "clusteredColumn" && rawRecord?.layoutPr && typeof rawRecord.layoutPr === "object" && rawRecord.layoutPr.binning != null
2137
+ layout === "clusteredColumn" && rawRecord?.layoutProperties && typeof rawRecord.layoutProperties === "object" && rawRecord.layoutProperties.binning != null
2138
2138
  );
2139
2139
  if (!hasBinning) {
2140
2140
  return series;
@@ -2346,7 +2346,8 @@ function collapseChartExPointSeries(chartType, series) {
2346
2346
  ];
2347
2347
  }
2348
2348
  function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex, raw, index, themePalette) {
2349
- const chart = raw && typeof raw === "object" ? raw : {};
2349
+ const drawing = raw && typeof raw === "object" ? raw : {};
2350
+ const chart = drawing.chartEx && typeof drawing.chartEx === "object" ? drawing.chartEx : {};
2350
2351
  const plotArea = chart.plotArea && typeof chart.plotArea === "object" ? chart.plotArea : {};
2351
2352
  const rawSeries = Array.isArray(plotArea.series) ? plotArea.series : [];
2352
2353
  const seriesLayouts = rawSeries.map(resolveChartExSeriesLayout);
@@ -2401,7 +2402,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2401
2402
  }
2402
2403
  ] : [];
2403
2404
  const normalizedChart = {
2404
- anchor: normalizeChartAnchor(chart.anchor),
2405
+ anchor: normalizeChartAnchor(drawing.anchor),
2405
2406
  autoTitleDeleted: void 0,
2406
2407
  axes,
2407
2408
  axisLabelColor: void 0,
@@ -2425,7 +2426,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2425
2426
  id: `chart-ex-${workbookSheetIndex}-${index}`,
2426
2427
  is3d: void 0,
2427
2428
  legend: normalizeChartExLegend(chart.legend),
2428
- name: chartTitle,
2429
+ name: typeof drawing.name === "string" ? drawing.name : chartTitle,
2429
2430
  overlap: void 0,
2430
2431
  plotVisibleOnly: void 0,
2431
2432
  raw: chart,
@@ -2453,7 +2454,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2453
2454
  view3d: void 0,
2454
2455
  wireframe: void 0,
2455
2456
  workbookSheetIndex,
2456
- zIndex: index
2457
+ zIndex: Array.isArray(drawing.drawingPath) && typeof drawing.drawingPath[0] === "number" ? drawing.drawingPath[0] + 1 : index + 1
2457
2458
  };
2458
2459
  applyBuiltinChartDefaults(normalizedChart, themePalette);
2459
2460
  return normalizedChart;
@@ -2574,10 +2575,11 @@ function normalizeChartReference(raw) {
2574
2575
  return null;
2575
2576
  }
2576
2577
  const record = raw;
2578
+ const values = Array.isArray(record.numbers) ? record.numbers : Array.isArray(record.strings) ? record.strings : void 0;
2577
2579
  return {
2578
2580
  formula: typeof record.formula === "string" ? record.formula : void 0,
2579
2581
  refType: typeof record.refType === "string" ? record.refType : void 0,
2580
- values: Array.isArray(record.values) ? record.values : void 0
2582
+ values
2581
2583
  };
2582
2584
  }
2583
2585
  function normalizeChartAxis(raw) {
@@ -2597,8 +2599,8 @@ function normalizeChartAxis(raw) {
2597
2599
  logBase: typeof axis.logBase === "number" ? axis.logBase : void 0,
2598
2600
  orientation: typeof axis.orientation === "string" ? axis.orientation : void 0,
2599
2601
  majorUnit: typeof axis.majorUnit === "number" ? axis.majorUnit : void 0,
2600
- max: typeof axis.max === "number" ? axis.max : void 0,
2601
- min: typeof axis.min === "number" ? axis.min : void 0,
2602
+ max: typeof axis.maximum === "number" ? axis.maximum : void 0,
2603
+ min: typeof axis.minimum === "number" ? axis.minimum : void 0,
2602
2604
  majorGridlines: typeof axis.majorGridlines === "boolean" ? axis.majorGridlines : void 0,
2603
2605
  majorTickMark: typeof axis.majorTickMark === "string" ? axis.majorTickMark : void 0,
2604
2606
  minorUnit: typeof axis.minorUnit === "number" ? axis.minorUnit : void 0,
@@ -2721,11 +2723,11 @@ function normalizeChartDataLabels(raw) {
2721
2723
  pointLabels: pointLabels && pointLabels.length > 0 ? pointLabels : void 0,
2722
2724
  raw: labels,
2723
2725
  showBubbleSize: typeof labels.showBubbleSize === "boolean" ? labels.showBubbleSize : void 0,
2724
- showCategoryName: typeof labels.showCategoryName === "boolean" ? labels.showCategoryName : void 0,
2726
+ showCategoryName: typeof (labels.showCategoryName ?? labels.visibilityCategoryName) === "boolean" ? Boolean(labels.showCategoryName ?? labels.visibilityCategoryName) : void 0,
2725
2727
  showLegendKey: typeof labels.showLegendKey === "boolean" ? labels.showLegendKey : void 0,
2726
2728
  showPercent: typeof labels.showPercent === "boolean" ? labels.showPercent : void 0,
2727
- showSeriesName: typeof labels.showSeriesName === "boolean" ? labels.showSeriesName : void 0,
2728
- showValue: typeof labels.showValue === "boolean" ? labels.showValue : void 0
2729
+ showSeriesName: typeof (labels.showSeriesName ?? labels.visibilitySeriesName) === "boolean" ? Boolean(labels.showSeriesName ?? labels.visibilitySeriesName) : void 0,
2730
+ showValue: typeof (labels.showValue ?? labels.visibilityValue) === "boolean" ? Boolean(labels.showValue ?? labels.visibilityValue) : void 0
2729
2731
  };
2730
2732
  }
2731
2733
  function normalizeChartAnchor(raw) {
@@ -2737,19 +2739,52 @@ function normalizeChartAnchor(raw) {
2737
2739
  };
2738
2740
  }
2739
2741
  const anchor = raw;
2740
- const fromCol = typeof anchor.fromCol === "number" ? anchor.fromCol : 0;
2741
- const fromColOffsetEmu = typeof anchor.fromColOffset === "number" ? anchor.fromColOffset : 0;
2742
- const fromRow = typeof anchor.fromRow === "number" ? anchor.fromRow : 0;
2743
- const fromRowOffsetEmu = typeof anchor.fromRowOffset === "number" ? anchor.fromRowOffset : 0;
2744
- const rawToCol = typeof anchor.toCol === "number" ? anchor.toCol : null;
2745
- const rawToColOffsetEmu = typeof anchor.toColOffset === "number" ? anchor.toColOffset : 0;
2746
- const rawToRow = typeof anchor.toRow === "number" ? anchor.toRow : null;
2747
- const rawToRowOffsetEmu = typeof anchor.toRowOffset === "number" ? anchor.toRowOffset : 0;
2748
- const hasExplicitTo = rawToCol !== null && rawToRow !== null;
2749
- const collapsedWidth = hasExplicitTo && (rawToCol < fromCol || rawToCol === fromCol && rawToColOffsetEmu <= fromColOffsetEmu);
2750
- const collapsedHeight = hasExplicitTo && (rawToRow < fromRow || rawToRow === fromRow && rawToRowOffsetEmu <= fromRowOffsetEmu);
2751
- const fallbackToCol = Math.max(fromCol + 8, 8);
2752
- const fallbackToRow = Math.max(fromRow + 15, 15);
2742
+ const from = anchor.from && typeof anchor.from === "object" ? anchor.from : null;
2743
+ if (anchor.type === "oneCell") {
2744
+ return {
2745
+ from: {
2746
+ col: typeof from?.col === "number" ? from.col : 0,
2747
+ colOffsetEmu: typeof from?.colOffsetEmu === "number" ? from.colOffsetEmu : 0,
2748
+ row: typeof from?.row === "number" ? from.row : 0,
2749
+ rowOffsetEmu: typeof from?.rowOffsetEmu === "number" ? from.rowOffsetEmu : 0
2750
+ },
2751
+ kind: "one-cell",
2752
+ sizeEmu: {
2753
+ cx: typeof anchor.widthEmu === "number" ? anchor.widthEmu : 0,
2754
+ cy: typeof anchor.heightEmu === "number" ? anchor.heightEmu : 0
2755
+ }
2756
+ };
2757
+ }
2758
+ if (anchor.type === "absolute") {
2759
+ return {
2760
+ kind: "absolute",
2761
+ positionEmu: {
2762
+ x: typeof anchor.xEmu === "number" ? anchor.xEmu : 0,
2763
+ y: typeof anchor.yEmu === "number" ? anchor.yEmu : 0
2764
+ },
2765
+ sizeEmu: {
2766
+ cx: typeof anchor.widthEmu === "number" ? anchor.widthEmu : 0,
2767
+ cy: typeof anchor.heightEmu === "number" ? anchor.heightEmu : 0
2768
+ }
2769
+ };
2770
+ }
2771
+ const to = anchor.to && typeof anchor.to === "object" ? anchor.to : null;
2772
+ const fromColValue = from?.col;
2773
+ const fromColOffsetValue = from?.colOffsetEmu;
2774
+ const fromRowValue = from?.row;
2775
+ const fromRowOffsetValue = from?.rowOffsetEmu;
2776
+ const toColValue = to?.col;
2777
+ const toColOffsetValue = to?.colOffsetEmu;
2778
+ const toRowValue = to?.row;
2779
+ const toRowOffsetValue = to?.rowOffsetEmu;
2780
+ const fromCol = typeof fromColValue === "number" ? fromColValue : 0;
2781
+ const fromColOffsetEmu = typeof fromColOffsetValue === "number" ? fromColOffsetValue : 0;
2782
+ const fromRow = typeof fromRowValue === "number" ? fromRowValue : 0;
2783
+ const fromRowOffsetEmu = typeof fromRowOffsetValue === "number" ? fromRowOffsetValue : 0;
2784
+ const rawToCol = typeof toColValue === "number" ? toColValue : 0;
2785
+ const rawToColOffsetEmu = typeof toColOffsetValue === "number" ? toColOffsetValue : 0;
2786
+ const rawToRow = typeof toRowValue === "number" ? toRowValue : 0;
2787
+ const rawToRowOffsetEmu = typeof toRowOffsetValue === "number" ? toRowOffsetValue : 0;
2753
2788
  return {
2754
2789
  kind: "two-cell",
2755
2790
  from: {
@@ -2759,10 +2794,10 @@ function normalizeChartAnchor(raw) {
2759
2794
  rowOffsetEmu: fromRowOffsetEmu
2760
2795
  },
2761
2796
  to: {
2762
- col: !hasExplicitTo || collapsedWidth ? fallbackToCol : rawToCol,
2763
- colOffsetEmu: !hasExplicitTo || collapsedWidth ? 0 : rawToColOffsetEmu,
2764
- row: !hasExplicitTo || collapsedHeight ? fallbackToRow : rawToRow,
2765
- rowOffsetEmu: !hasExplicitTo || collapsedHeight ? 0 : rawToRowOffsetEmu
2797
+ col: rawToCol,
2798
+ colOffsetEmu: rawToColOffsetEmu,
2799
+ row: rawToRow,
2800
+ rowOffsetEmu: rawToRowOffsetEmu
2766
2801
  }
2767
2802
  };
2768
2803
  }
@@ -2876,7 +2911,7 @@ function normalizeChartTypeGroup(workbook, workbookSheetIndex, chartId, raw, ind
2876
2911
  chartType: typeof group.chartType === "string" ? group.chartType : "ColumnClustered",
2877
2912
  dataLabels: normalizeChartDataLabels(group.dataLabels),
2878
2913
  gapWidth: typeof group.gapWidth === "number" && Number.isFinite(group.gapWidth) ? group.gapWidth : void 0,
2879
- is3d: typeof group.is3d === "boolean" ? group.is3d : void 0,
2914
+ is3d: typeof group.is3D === "boolean" ? group.is3D : void 0,
2880
2915
  overlap: typeof group.overlap === "number" && Number.isFinite(group.overlap) ? group.overlap : void 0,
2881
2916
  raw: group,
2882
2917
  series: rawSeries.map((entry, seriesIndex) => normalizeChartSeries(workbook, workbookSheetIndex, `${chartId}-group-${index}`, entry, seriesIndex)),
@@ -3030,20 +3065,25 @@ function applyChartOrigins(chartsByWorkbookSheetIndex, chartOriginsById, archive
3030
3065
  }
3031
3066
  }
3032
3067
  function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkbookSheetIndex, showHiddenSheets = false) {
3068
+ const excludedChartIds = /* @__PURE__ */ new Set();
3033
3069
  const chartsByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
3034
3070
  const worksheet = workbook.getSheet(workbookSheetIndex);
3035
- const rawCharts = Array.isArray(worksheet.charts) ? worksheet.charts : [];
3036
- const rawChartsEx = Array.isArray(worksheet.chartsEx) ? worksheet.chartsEx : [];
3071
+ const rawCharts = worksheet.charts;
3072
+ const rawChartsEx = worksheet.chartsEx;
3037
3073
  const visibleSheetIndex = visibleSheetIndexByWorkbookSheetIndex.get(workbookSheetIndex) ?? workbookSheetIndex;
3038
3074
  const classicCharts = rawCharts.map((rawChart, chartIndex) => {
3039
3075
  const chartId = `chart-${workbookSheetIndex}-${chartIndex}`;
3040
- const chart = rawChart && typeof rawChart === "object" ? rawChart : {};
3041
- const rawView3d = chart.view3d && typeof chart.view3d === "object" ? chart.view3d : null;
3076
+ if (rawChart.hidden || !rawChart.anchor) {
3077
+ excludedChartIds.add(chartId);
3078
+ }
3079
+ const drawing = rawChart && typeof rawChart === "object" ? rawChart : {};
3080
+ const chart = drawing.chart && typeof drawing.chart === "object" ? drawing.chart : {};
3081
+ const rawView3d = chart.view3D && typeof chart.view3D === "object" ? chart.view3D : null;
3042
3082
  const rawSeries = Array.isArray(chart.series) ? chart.series : [];
3043
3083
  const chartLevelDataLabels = normalizeChartDataLabels(chart.dataLabels);
3044
3084
  const firstSeriesDataLabels = rawSeries.length > 0 && rawSeries[0] && typeof rawSeries[0] === "object" ? normalizeChartDataLabels(rawSeries[0].dataLabels) : null;
3045
3085
  return {
3046
- anchor: normalizeChartAnchor(chart.anchor),
3086
+ anchor: normalizeChartAnchor(drawing.anchor),
3047
3087
  autoTitleDeleted: typeof chart.autoTitleDeleted === "boolean" ? chart.autoTitleDeleted : void 0,
3048
3088
  axes: Array.isArray(chart.axes) ? chart.axes.map(normalizeChartAxis).filter((value) => Boolean(value)) : [],
3049
3089
  axisLabelColor: void 0,
@@ -3064,12 +3104,12 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3064
3104
  gapWidth: typeof chart.gapWidth === "number" ? chart.gapWidth : void 0,
3065
3105
  holeSize: typeof chart.holeSize === "number" ? chart.holeSize : void 0,
3066
3106
  id: chartId,
3067
- is3d: typeof chart.is3d === "boolean" ? chart.is3d : void 0,
3107
+ is3d: typeof chart.is3D === "boolean" ? chart.is3D : void 0,
3068
3108
  legend: normalizeLegend(chart.legend) ? {
3069
3109
  ...normalizeLegend(chart.legend),
3070
3110
  position: normalizeLegendPosition(normalizeLegend(chart.legend)?.position)
3071
3111
  } : null,
3072
- name: typeof chart.name === "string" ? chart.name : void 0,
3112
+ name: typeof drawing.name === "string" ? drawing.name : void 0,
3073
3113
  overlap: typeof chart.overlap === "number" ? chart.overlap : void 0,
3074
3114
  plotVisibleOnly: typeof chart.plotVisibleOnly === "boolean" ? chart.plotVisibleOnly : void 0,
3075
3115
  raw: chart,
@@ -3103,17 +3143,23 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3103
3143
  } : void 0,
3104
3144
  wireframe: typeof chart.wireframe === "boolean" ? chart.wireframe : void 0,
3105
3145
  workbookSheetIndex,
3106
- zIndex: 200 + chartIndex
3146
+ zIndex: Array.isArray(drawing.drawingPath) && typeof drawing.drawingPath[0] === "number" ? drawing.drawingPath[0] + 1 : chartIndex + 1
3107
3147
  };
3108
3148
  });
3109
- const modernCharts = rawChartsEx.map((rawChartEx, chartExIndex) => normalizeChartExChart(
3110
- workbook,
3111
- workbookSheetIndex,
3112
- visibleSheetIndex,
3113
- rawChartEx,
3114
- chartExIndex,
3115
- imageAssets?.themePalette ?? null
3116
- ));
3149
+ const modernCharts = rawChartsEx.map((rawChartEx, chartExIndex) => {
3150
+ const chartId = `chart-ex-${workbookSheetIndex}-${chartExIndex}`;
3151
+ if (rawChartEx.hidden || !rawChartEx.anchor) {
3152
+ excludedChartIds.add(chartId);
3153
+ }
3154
+ return normalizeChartExChart(
3155
+ workbook,
3156
+ workbookSheetIndex,
3157
+ visibleSheetIndex,
3158
+ rawChartEx,
3159
+ chartExIndex,
3160
+ imageAssets?.themePalette ?? null
3161
+ );
3162
+ });
3117
3163
  return [...classicCharts, ...modernCharts];
3118
3164
  });
3119
3165
  const chartsheets = Array.isArray(workbook.chartsheets) ? workbook.chartsheets.map((entry, index) => normalizeChartsheet(entry, index)) : [];
@@ -3121,6 +3167,12 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3121
3167
  const chartOriginsById = /* @__PURE__ */ new Map();
3122
3168
  if (imageAssets) {
3123
3169
  applyChartOrigins(chartsByWorkbookSheetIndex, chartOriginsById, imageAssets.archive, imageAssets.sheetOrigins);
3170
+ for (let index = 0; index < chartsByWorkbookSheetIndex.length; index += 1) {
3171
+ chartsByWorkbookSheetIndex[index] = (chartsByWorkbookSheetIndex[index] ?? []).filter((chart) => !excludedChartIds.has(chart.id));
3172
+ }
3173
+ for (const id of excludedChartIds) {
3174
+ chartOriginsById.delete(id);
3175
+ }
3124
3176
  for (const charts of chartsByWorkbookSheetIndex) {
3125
3177
  for (const chart of charts) {
3126
3178
  applyChartStyleFromXml(chart, chart.chartPath, imageAssets.archive, imageAssets.themePalette);
@@ -3128,6 +3180,9 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3128
3180
  }
3129
3181
  }
3130
3182
  } else {
3183
+ for (let index = 0; index < chartsByWorkbookSheetIndex.length; index += 1) {
3184
+ chartsByWorkbookSheetIndex[index] = (chartsByWorkbookSheetIndex[index] ?? []).filter((chart) => !excludedChartIds.has(chart.id));
3185
+ }
3131
3186
  for (const charts of chartsByWorkbookSheetIndex) {
3132
3187
  for (const chart of charts) {
3133
3188
  applyBuiltinChartDefaults(chart, null);
@@ -3360,6 +3415,7 @@ function updateWorkbookChartAnchor(imageAssets, chartAssets, chartId, anchor) {
3360
3415
  }
3361
3416
  updateAnchorNode(anchorNode, anchor);
3362
3417
  imageAssets.archive[normalizeArchivePath(origin.drawingPath)] = strToU8(serializeXml(drawingDocument));
3418
+ imageAssets.dirtyArchivePaths.add(normalizeArchivePath(origin.drawingPath));
3363
3419
  return true;
3364
3420
  }
3365
3421
  function updateWorkbookChartDefinition(imageAssets, chartAssets, chartId, patch) {
@@ -3414,6 +3470,7 @@ function updateWorkbookChartDefinition(imageAssets, chartAssets, chartId, patch)
3414
3470
  );
3415
3471
  updateAxisNode(getLocalChildren(plotAreaNode, "valAx")[0] ?? null, patch.valueAxis);
3416
3472
  imageAssets.archive[normalizeArchivePath(origin.chartPath)] = strToU8(serializeXml(chartDocument));
3473
+ imageAssets.dirtyArchivePaths.add(normalizeArchivePath(origin.chartPath));
3417
3474
  return true;
3418
3475
  }
3419
3476
 
@@ -3591,7 +3648,6 @@ var PKG_REL_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
3591
3648
  var SPREADSHEET_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
3592
3649
  var CONTENT_TYPES_NS = "http://schemas.openxmlformats.org/package/2006/content-types";
3593
3650
  var DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing";
3594
- var VML_DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing";
3595
3651
  var IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
3596
3652
  var DRAWING_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawing+xml";
3597
3653
  var EMU_PER_PIXEL2 = 9525;
@@ -5018,71 +5074,174 @@ function parseMarker(node) {
5018
5074
  rowOffsetEmu
5019
5075
  };
5020
5076
  }
5021
- function parseSpreadsheetBooleanValue(value) {
5022
- if (value === null || value === void 0) {
5077
+ function dukeDrawingAnchorToXlsxAnchor(anchor) {
5078
+ if (anchor.type === "absolute") {
5079
+ return {
5080
+ kind: "absolute",
5081
+ positionEmu: { x: anchor.xEmu, y: anchor.yEmu },
5082
+ sizeEmu: { cx: anchor.widthEmu, cy: anchor.heightEmu }
5083
+ };
5084
+ }
5085
+ const from = {
5086
+ col: anchor.from.col,
5087
+ colOffsetEmu: anchor.from.colOffsetEmu ?? 0,
5088
+ row: anchor.from.row,
5089
+ rowOffsetEmu: anchor.from.rowOffsetEmu ?? 0
5090
+ };
5091
+ if (anchor.type === "oneCell") {
5092
+ return {
5093
+ from,
5094
+ kind: "one-cell",
5095
+ sizeEmu: { cx: anchor.widthEmu, cy: anchor.heightEmu }
5096
+ };
5097
+ }
5098
+ return {
5099
+ from,
5100
+ kind: "two-cell",
5101
+ to: {
5102
+ col: anchor.to.col,
5103
+ colOffsetEmu: anchor.to.colOffsetEmu ?? 0,
5104
+ row: anchor.to.row,
5105
+ rowOffsetEmu: anchor.to.rowOffsetEmu ?? 0
5106
+ }
5107
+ };
5108
+ }
5109
+ function xlsxAnchorToDukeDrawingAnchor(anchor, editAs) {
5110
+ if (anchor.kind === "absolute") {
5111
+ return {
5112
+ heightEmu: anchor.sizeEmu.cy,
5113
+ type: "absolute",
5114
+ widthEmu: anchor.sizeEmu.cx,
5115
+ xEmu: anchor.positionEmu.x,
5116
+ yEmu: anchor.positionEmu.y
5117
+ };
5118
+ }
5119
+ const from = {
5120
+ col: anchor.from.col,
5121
+ colOffsetEmu: anchor.from.colOffsetEmu,
5122
+ row: anchor.from.row,
5123
+ rowOffsetEmu: anchor.from.rowOffsetEmu
5124
+ };
5125
+ if (anchor.kind === "one-cell") {
5126
+ return {
5127
+ from,
5128
+ heightEmu: anchor.sizeEmu.cy,
5129
+ type: "oneCell",
5130
+ widthEmu: anchor.sizeEmu.cx
5131
+ };
5132
+ }
5133
+ return {
5134
+ editAs,
5135
+ from,
5136
+ to: {
5137
+ col: anchor.to.col,
5138
+ colOffsetEmu: anchor.to.colOffsetEmu,
5139
+ row: anchor.to.row,
5140
+ rowOffsetEmu: anchor.to.rowOffsetEmu
5141
+ },
5142
+ type: "twoCell"
5143
+ };
5144
+ }
5145
+ function mapDukeDrawingColor(color) {
5146
+ if (!color) {
5023
5147
  return void 0;
5024
5148
  }
5025
- const normalized = value.trim().toLowerCase();
5026
- if (!normalized) {
5027
- return true;
5149
+ switch (color.colorType) {
5150
+ case "auto":
5151
+ return { colorType: "auto" };
5152
+ case "rgb":
5153
+ return { b: color.b, colorType: "rgb", g: color.g, r: color.r };
5154
+ case "argb":
5155
+ return { a: color.a, b: color.b, colorType: "argb", g: color.g, r: color.r };
5156
+ case "theme":
5157
+ return { colorType: "theme", themeIndex: color.index, tint: color.tint };
5158
+ case "indexed":
5159
+ return { colorType: "indexed", paletteIndex: color.index };
5160
+ }
5161
+ }
5162
+ function mapDukeDrawingFont(font) {
5163
+ if (!font) {
5164
+ return void 0;
5028
5165
  }
5029
- return !["0", "false", "none", "off", "unchecked"].includes(normalized);
5166
+ return {
5167
+ bold: font.bold,
5168
+ charset: font.charset,
5169
+ color: mapDukeDrawingColor(font.color),
5170
+ family: font.family,
5171
+ italic: font.italic,
5172
+ name: font.name,
5173
+ scheme: font.scheme,
5174
+ size: font.size,
5175
+ strikethrough: font.strikethrough,
5176
+ underline: font.underline,
5177
+ verticalAlign: font.verticalAlign
5178
+ };
5030
5179
  }
5031
- function parseSpreadsheetBooleanNode(node) {
5032
- if (!node) {
5180
+ function mapDukeDrawingText(text) {
5181
+ return {
5182
+ horizontalAlignment: text.horizontalAlignment,
5183
+ runs: text.runs.map((run) => ({ font: mapDukeDrawingFont(run.font), text: run.text })),
5184
+ verticalAlignment: text.verticalAlignment
5185
+ };
5186
+ }
5187
+ function resolveDukeDrawingColor(color, themePalette) {
5188
+ if (!color) {
5033
5189
  return void 0;
5034
5190
  }
5035
- return parseSpreadsheetBooleanValue(node.getAttribute("val") ?? node.textContent);
5191
+ switch (color.colorType) {
5192
+ case "rgb":
5193
+ return resolveWorkbookColor({ rgb: [color.r, color.g, color.b].map((value) => value.toString(16).padStart(2, "0")).join("") }) ?? void 0;
5194
+ case "argb":
5195
+ return resolveWorkbookColor({ argb: [color.a, color.r, color.g, color.b].map((value) => value.toString(16).padStart(2, "0")).join("") }) ?? void 0;
5196
+ case "theme":
5197
+ return resolveWorkbookColor({ theme: color.index, tint: color.tint }, themePalette) ?? void 0;
5198
+ default:
5199
+ return void 0;
5200
+ }
5036
5201
  }
5037
- function mapDukeFormControlAnchor(anchor) {
5202
+ function resolveDukeDrawingText(text, themePalette) {
5203
+ const caption = mapDukeDrawingText(text);
5204
+ const label = normalizeControlLabel(text.runs.map((run) => run.text).join(""));
5205
+ const firstStyledRun = text.runs.find((run) => run.font)?.font;
5206
+ const horizontalAlignment = text.horizontalAlignment;
5038
5207
  return {
5039
- from: {
5040
- col: anchor.fromCol,
5041
- colOffsetEmu: anchor.fromColOffset,
5042
- row: anchor.fromRow,
5043
- rowOffsetEmu: anchor.fromRowOffset
5044
- },
5045
- kind: "two-cell",
5046
- to: {
5047
- col: anchor.toCol,
5048
- colOffsetEmu: anchor.toColOffset,
5049
- row: anchor.toRow,
5050
- rowOffsetEmu: anchor.toRowOffset
5051
- }
5208
+ caption,
5209
+ fontFamily: firstStyledRun?.name,
5210
+ fontSizePt: firstStyledRun?.size,
5211
+ label,
5212
+ textAlign: horizontalAlignment === "left" || horizontalAlignment === "center" || horizontalAlignment === "right" ? horizontalAlignment : void 0,
5213
+ textColor: resolveDukeDrawingColor(firstStyledRun?.color, themePalette)
5052
5214
  };
5053
5215
  }
5054
- function mapDukeFormControlKind(kind) {
5216
+ function mapDukeFormControlKind(kind, themePalette) {
5055
5217
  switch (kind.kind) {
5056
5218
  case "button":
5057
- return { caption: kind.caption, kind: "button", label: normalizeControlLabel(kind.caption) };
5219
+ return { ...resolveDukeDrawingText(kind.caption, themePalette), kind: "button" };
5058
5220
  case "checkbox":
5059
5221
  return {
5060
- caption: kind.caption,
5222
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5061
5223
  checked: kind.state === "checked",
5062
5224
  kind: "checkbox",
5063
- label: normalizeControlLabel(kind.caption),
5064
5225
  linkedCell: kind.cellLink,
5065
5226
  no3D: kind.no3D,
5066
5227
  state: kind.state
5067
5228
  };
5068
5229
  case "optionButton":
5069
5230
  return {
5070
- caption: kind.caption,
5231
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5071
5232
  checked: kind.state === "checked",
5072
5233
  firstInGroup: kind.firstInGroup,
5073
5234
  kind: "radio",
5074
- label: normalizeControlLabel(kind.caption),
5075
5235
  linkedCell: kind.cellLink,
5076
5236
  no3D: kind.no3D,
5077
5237
  state: kind.state
5078
5238
  };
5079
5239
  case "label":
5080
- return { caption: kind.caption, kind: "label", label: normalizeControlLabel(kind.caption) };
5240
+ return { ...resolveDukeDrawingText(kind.caption, themePalette), kind: "label" };
5081
5241
  case "groupBox":
5082
5242
  return {
5083
- caption: kind.caption,
5243
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5084
5244
  kind: "group-box",
5085
- label: normalizeControlLabel(kind.caption),
5086
5245
  no3D: kind.no3D
5087
5246
  };
5088
5247
  case "listBox":
@@ -5123,92 +5282,47 @@ function mapDukeFormControlKind(kind) {
5123
5282
  min: kind.min,
5124
5283
  value: kind.value
5125
5284
  };
5285
+ case "unknown":
5286
+ return {
5287
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5288
+ kind: kind.objectType.toLowerCase() === "editbox" ? "editbox" : "unknown",
5289
+ legacyObjectType: kind.legacyObjectType,
5290
+ objectType: kind.objectType,
5291
+ rawObj: kind.rawObj,
5292
+ rawProperties: kind.rawProperties
5293
+ };
5126
5294
  }
5127
5295
  }
5128
- function mapDukeFormControl(control, controlIndex, workbookSheetIndex) {
5296
+ function mapDukeFormControl(control, controlIndex, workbookSheetIndex, themePalette) {
5129
5297
  return {
5130
- anchor: mapDukeFormControlAnchor(control.anchor),
5298
+ altText: control.altText,
5299
+ anchor: dukeDrawingAnchorToXlsxAnchor(control.anchor),
5131
5300
  controlIndex,
5132
- dukeAnchor: { ...control.anchor },
5133
- editAs: control.anchor.editAs,
5134
- hidden: false,
5301
+ editAs: control.anchor.type === "twoCell" ? control.anchor.editAs : void 0,
5302
+ hidden: control.hidden,
5135
5303
  id: `form-control-${workbookSheetIndex}-${controlIndex}`,
5136
5304
  locked: control.locked,
5305
+ macroName: control.formControl.macroName,
5137
5306
  name: control.name,
5138
5307
  printable: control.printable,
5308
+ rawClientData: control.formControl.rawClientData,
5139
5309
  sheetIndex: workbookSheetIndex,
5310
+ title: control.title,
5140
5311
  workbookSheetIndex,
5141
- zIndex: controlIndex + 1,
5142
- ...mapDukeFormControlKind(control.kind)
5312
+ zIndex: (control.drawingPath[0] ?? controlIndex) + 1,
5313
+ ...mapDukeFormControlKind(control.formControl.kind, themePalette)
5143
5314
  };
5144
5315
  }
5145
- function collectWorkbookFormControls(workbook) {
5316
+ function collectWorkbookFormControls(workbook, themePalette) {
5146
5317
  return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
5147
5318
  try {
5148
5319
  const controls = workbook.getSheet(workbookSheetIndex).formControls;
5149
- return Array.isArray(controls) ? controls.map((control, controlIndex) => mapDukeFormControl(control, controlIndex, workbookSheetIndex)) : [];
5320
+ return Array.isArray(controls) ? controls.flatMap((control, controlIndex) => control.anchor ? [mapDukeFormControl(control, controlIndex, workbookSheetIndex, themePalette)] : []) : [];
5150
5321
  } catch {
5151
5322
  return [];
5152
5323
  }
5153
5324
  });
5154
5325
  }
5155
- function parseFormControlKind(rawType) {
5156
- const normalized = (rawType ?? "").trim().toLowerCase();
5157
- switch (normalized) {
5158
- case "button":
5159
- return "button";
5160
- case "checkbox":
5161
- return "checkbox";
5162
- case "drop":
5163
- return "dropdown";
5164
- case "editbox":
5165
- return "editbox";
5166
- case "gbox":
5167
- return "group-box";
5168
- case "label":
5169
- return "label";
5170
- case "list":
5171
- return "listbox";
5172
- case "radio":
5173
- return "radio";
5174
- case "scroll":
5175
- return "scrollbar";
5176
- case "spin":
5177
- return "spinner";
5178
- default:
5179
- return "unknown";
5180
- }
5181
- }
5182
- function parseFormControlShapeId(value) {
5183
- const match = (value ?? "").match(/(\d+)(?!.*\d)/);
5184
- if (!match) {
5185
- return null;
5186
- }
5187
- const parsed = Number(match[1]);
5188
- return Number.isFinite(parsed) ? parsed : null;
5189
- }
5190
- function parseCssDeclarationValue(styleText, property) {
5191
- if (!styleText) {
5192
- return null;
5193
- }
5194
- const pattern = new RegExp(`${property}\\s*:\\s*([^;]+)`, "i");
5195
- const match = pattern.exec(styleText);
5196
- return match?.[1]?.trim() ?? null;
5197
- }
5198
- function parseControlTextAlign(styleText) {
5199
- const value = parseCssDeclarationValue(styleText, "text-align")?.toLowerCase();
5200
- if (value === "center" || value === "right") {
5201
- return value;
5202
- }
5203
- return value === "left" ? "left" : void 0;
5204
- }
5205
- function parseVmlFontSizePt(value) {
5206
- const parsed = Number(value ?? Number.NaN);
5207
- if (!Number.isFinite(parsed) || parsed <= 0) {
5208
- return void 0;
5209
- }
5210
- return parsed > 40 ? parsed / 20 : parsed;
5211
- }
5212
5326
  function normalizeControlLabel(label) {
5213
5327
  if (!label) {
5214
5328
  return void 0;
@@ -5216,16 +5330,6 @@ function normalizeControlLabel(label) {
5216
5330
  const normalized = label.replace(/\u00a0/g, " ").replace(/\s+/g, " ").trim();
5217
5331
  return normalized.length > 0 ? normalized : void 0;
5218
5332
  }
5219
- function isPlaceholderFormControlName(name) {
5220
- const normalized = normalizeControlLabel(name)?.toLowerCase();
5221
- if (!normalized) {
5222
- return false;
5223
- }
5224
- return /^(option button|group box|check box|drop down|dropdown|list box|edit box|scroll bar|spinner|spin button|button)\s+\d+$/.test(normalized);
5225
- }
5226
- function resolveNamedFormControlLabel(name) {
5227
- return isPlaceholderFormControlName(name) ? void 0 : normalizeControlLabel(name);
5228
- }
5229
5333
  function parseAnchor(anchorNode) {
5230
5334
  if (anchorNode.localName === "anchor") {
5231
5335
  const from = parseMarker(getFirstChild(anchorNode, "from"));
@@ -5906,260 +6010,8 @@ function parseDrawingObjects(archive, contentTypes, drawingPath, objectUrls, wor
5906
6010
  shapes
5907
6011
  };
5908
6012
  }
5909
- function parseSheetFormControlNodes(archive, sheetPath) {
5910
- const sheetXml = readArchiveText2(archive, sheetPath);
5911
- if (!sheetXml) {
5912
- return [];
5913
- }
5914
- const sheetDocument = parseXml2(sheetXml);
5915
- if (!sheetDocument) {
5916
- return [];
5917
- }
5918
- return getLocalElements(sheetDocument, "control").map((controlNode) => ({
5919
- anchor: parseAnchor(getFirstDescendant(controlNode, "anchor") ?? controlNode),
5920
- name: controlNode.getAttribute("name") ?? void 0,
5921
- shapeId: parseFormControlShapeId(controlNode.getAttribute("shapeId"))
5922
- }));
5923
- }
5924
- function parseVmlFormControls(archive, vmlDrawingPath) {
5925
- const xml = readArchiveText2(archive, vmlDrawingPath);
5926
- if (!xml) {
5927
- return /* @__PURE__ */ new Map();
5928
- }
5929
- const document2 = parseXml2(xml);
5930
- if (!document2) {
5931
- return /* @__PURE__ */ new Map();
5932
- }
5933
- const controls = /* @__PURE__ */ new Map();
5934
- for (const shapeNode of getLocalElements(document2, "shape")) {
5935
- const clientDataNode = getFirstChild(shapeNode, "ClientData");
5936
- if (!clientDataNode) {
5937
- continue;
5938
- }
5939
- const shapeId = parseFormControlShapeId(
5940
- shapeNode.getAttributeNS("urn:schemas-microsoft-com:office:office", "spid") ?? shapeNode.getAttribute("o:spid") ?? shapeNode.getAttribute("spid") ?? shapeNode.getAttribute("id")
5941
- );
5942
- if (shapeId === null) {
5943
- continue;
5944
- }
5945
- const styleText = shapeNode.getAttribute("style");
5946
- const textboxNode = getFirstChild(shapeNode, "textbox");
5947
- const fontNode = textboxNode ? getFirstDescendant(textboxNode, "font") : null;
5948
- const textContainerNode = textboxNode ? getFirstDescendant(textboxNode, "div") : null;
5949
- const label = normalizeControlLabel(textboxNode?.textContent);
5950
- const zIndex = Number(parseCssDeclarationValue(styleText, "z-index") ?? Number.NaN);
5951
- controls.set(shapeId, {
5952
- checked: parseSpreadsheetBooleanNode(getFirstChild(clientDataNode, "Checked")),
5953
- fontFamily: fontNode?.getAttribute("face") ?? void 0,
5954
- fontSizePt: parseVmlFontSizePt(fontNode?.getAttribute("size")),
5955
- hidden: (parseCssDeclarationValue(styleText, "visibility") ?? "").toLowerCase() === "hidden",
5956
- label,
5957
- linkedCell: normalizeControlLabel(getFirstChild(clientDataNode, "FmlaLink")?.textContent),
5958
- objectType: clientDataNode.getAttribute("ObjectType") ?? void 0,
5959
- shapeId,
5960
- textAlign: parseControlTextAlign(textContainerNode?.getAttribute("style")),
5961
- textColor: fontNode?.getAttribute("color") ?? void 0,
5962
- zIndex: Number.isFinite(zIndex) ? zIndex : controls.size + 1
5963
- });
5964
- }
5965
- return controls;
5966
- }
5967
- function parseSheetFormControls(archive, sheetPath, sheetRelationships, workbookSheetIndex, zIndexBase) {
5968
- const controlNodes = parseSheetFormControlNodes(archive, sheetPath);
5969
- if (controlNodes.length === 0) {
5970
- return [];
5971
- }
5972
- const legacyDrawingRelationship = [...sheetRelationships.values()].find(
5973
- (relationship) => relationship.type === VML_DRAWING_REL_TYPE
5974
- );
5975
- const vmlControlsByShapeId = legacyDrawingRelationship ? parseVmlFormControls(archive, legacyDrawingRelationship.target) : /* @__PURE__ */ new Map();
5976
- const parsedControls = [];
5977
- controlNodes.forEach((controlNode, index) => {
5978
- if (!controlNode.anchor) {
5979
- return;
5980
- }
5981
- const vmlControl = controlNode.shapeId !== null ? vmlControlsByShapeId.get(controlNode.shapeId) ?? null : null;
5982
- const kind = parseFormControlKind(vmlControl?.objectType);
5983
- parsedControls.push({
5984
- anchor: controlNode.anchor,
5985
- checked: vmlControl?.checked,
5986
- fontFamily: vmlControl?.fontFamily,
5987
- fontSizePt: vmlControl?.fontSizePt,
5988
- hidden: vmlControl?.hidden ?? false,
5989
- id: `form-control-${workbookSheetIndex}-${index}`,
5990
- kind,
5991
- label: vmlControl?.label,
5992
- linkedCell: vmlControl?.linkedCell,
5993
- name: controlNode.name,
5994
- sheetIndex: workbookSheetIndex,
5995
- textAlign: vmlControl?.textAlign,
5996
- textColor: vmlControl?.textColor,
5997
- workbookSheetIndex,
5998
- zIndex: zIndexBase + (vmlControl?.zIndex ?? index + 1)
5999
- });
6000
- });
6001
- return parsedControls.sort((left, right) => left.zIndex - right.zIndex);
6002
- }
6003
- function flattenShapeText(shape) {
6004
- const text = shape.paragraphs.flatMap((paragraph) => paragraph.runs.map((run) => run.text)).join(" ");
6005
- return normalizeControlLabel(text);
6006
- }
6007
- function rectArea(rect) {
6008
- return Math.max(0, rect.cx) * Math.max(0, rect.cy);
6009
- }
6010
- function rectIntersectionArea(left, right) {
6011
- const overlapX = Math.max(0, Math.min(left.x + left.cx, right.x + right.cx) - Math.max(left.x, right.x));
6012
- const overlapY = Math.max(0, Math.min(left.y + left.cy, right.y + right.cy) - Math.max(left.y, right.y));
6013
- return overlapX * overlapY;
6014
- }
6015
- function rectCenterDistance(left, right) {
6016
- const leftCenterX = left.x + left.cx / 2;
6017
- const leftCenterY = left.y + left.cy / 2;
6018
- const rightCenterX = right.x + right.cx / 2;
6019
- const rightCenterY = right.y + right.cy / 2;
6020
- return Math.hypot(leftCenterX - rightCenterX, leftCenterY - rightCenterY);
6021
- }
6022
- function findHiddenShapeControlMatch(control, shapes, sheetState) {
6023
- const controlRect = anchorToAbsoluteRect(control.anchor, sheetState);
6024
- const controlArea = Math.max(1, rectArea(controlRect));
6025
- const placeholderName = isPlaceholderFormControlName(control.name);
6026
- let bestMatch = null;
6027
- let bestScore = Number.NEGATIVE_INFINITY;
6028
- shapes.forEach((shape) => {
6029
- if (!shape.hidden) {
6030
- return;
6031
- }
6032
- const shapeRect = anchorToAbsoluteRect(shape.anchor, sheetState);
6033
- const shapeArea = Math.max(1, rectArea(shapeRect));
6034
- const intersectionArea = rectIntersectionArea(controlRect, shapeRect);
6035
- const overlapScore = intersectionArea / Math.min(controlArea, shapeArea);
6036
- const distance = rectCenterDistance(controlRect, shapeRect);
6037
- const maxDimension = Math.max(controlRect.cx, controlRect.cy, shapeRect.cx, shapeRect.cy, 1);
6038
- const distanceScore = distance / maxDimension;
6039
- const textLabel = flattenShapeText(shape);
6040
- const sameName = normalizeControlLabel(shape.name)?.toLowerCase() === normalizeControlLabel(control.name)?.toLowerCase();
6041
- let score = overlapScore * 4 - distanceScore;
6042
- if (sameName) {
6043
- score += 1;
6044
- }
6045
- if (control.kind === "group-box") {
6046
- score += textLabel ? -3 : 0.5;
6047
- score += shape.stroke?.none ? -1.5 : 0.75;
6048
- } else {
6049
- score += textLabel ? 2 : -1;
6050
- score += shape.stroke?.none ? 0.2 : -0.5;
6051
- }
6052
- if (!placeholderName && textLabel && textLabel === resolveNamedFormControlLabel(control.name)) {
6053
- score += 0.5;
6054
- }
6055
- if (score > bestScore) {
6056
- bestMatch = shape;
6057
- bestScore = score;
6058
- }
6059
- });
6060
- return bestScore >= 0.25 ? bestMatch : null;
6061
- }
6062
- function enrichFormControlsWithHiddenShapes(formControls, shapes, sheetState) {
6063
- return formControls.map((control) => {
6064
- const matchedShape = findHiddenShapeControlMatch(control, shapes, sheetState);
6065
- const matchedLabel = matchedShape ? flattenShapeText(matchedShape) : void 0;
6066
- const fallbackLabel = resolveNamedFormControlLabel(control.name);
6067
- let resolvedAnchor = control.anchor;
6068
- if (matchedShape && (control.kind === "group-box" || matchedLabel || isPlaceholderFormControlName(control.name))) {
6069
- resolvedAnchor = matchedShape.anchor;
6070
- }
6071
- return {
6072
- ...control,
6073
- anchor: resolvedAnchor,
6074
- label: control.label ?? matchedLabel ?? fallbackLabel
6075
- };
6076
- });
6077
- }
6078
- function getFormControlAnchorStart(anchor) {
6079
- return anchor.kind === "absolute" ? null : { col: anchor.from.col, row: anchor.from.row };
6080
- }
6081
- function scoreParsedFormControlMatch(dukeControl, parsedControl, parsedIndex) {
6082
- if (dukeControl.kind !== parsedControl.kind && parsedControl.kind !== "unknown") {
6083
- return Number.NEGATIVE_INFINITY;
6084
- }
6085
- let score = 10;
6086
- const dukeName = normalizeControlLabel(dukeControl.name)?.toLowerCase();
6087
- const parsedName = normalizeControlLabel(parsedControl.name)?.toLowerCase();
6088
- if (dukeName && parsedName) {
6089
- if (dukeName !== parsedName) {
6090
- return Number.NEGATIVE_INFINITY;
6091
- }
6092
- score += 100;
6093
- }
6094
- const dukeStart = getFormControlAnchorStart(dukeControl.anchor);
6095
- const parsedStart = getFormControlAnchorStart(parsedControl.anchor);
6096
- if (dukeStart && parsedStart && dukeStart.col === parsedStart.col && dukeStart.row === parsedStart.row) {
6097
- score += 40;
6098
- }
6099
- if (dukeControl.controlIndex === parsedIndex) {
6100
- score += 5;
6101
- }
6102
- return score;
6103
- }
6104
- function areDukeFormControlAnchorsEqual(left, right) {
6105
- if (!left || !right) {
6106
- return true;
6107
- }
6108
- return left.editAs === right.editAs && left.fromCol === right.fromCol && left.fromColOffset === right.fromColOffset && left.fromRow === right.fromRow && left.fromRowOffset === right.fromRowOffset && left.toCol === right.toCol && left.toColOffset === right.toColOffset && left.toRow === right.toRow && left.toRowOffset === right.toRowOffset;
6109
- }
6110
- function mergeDukeAndParsedFormControls(dukeControls, parsedControls, keepUnmatchedParsedControl = () => true) {
6111
- const usedParsedIndexes = /* @__PURE__ */ new Set();
6112
- const mergedControls = dukeControls.map((dukeControl) => {
6113
- let bestIndex = -1;
6114
- let bestScore = Number.NEGATIVE_INFINITY;
6115
- parsedControls.forEach((parsedControl2, parsedIndex) => {
6116
- if (usedParsedIndexes.has(parsedIndex)) {
6117
- return;
6118
- }
6119
- const score = scoreParsedFormControlMatch(dukeControl, parsedControl2, parsedIndex);
6120
- if (score > bestScore) {
6121
- bestIndex = parsedIndex;
6122
- bestScore = score;
6123
- }
6124
- });
6125
- const parsedControl = bestIndex >= 0 && bestScore >= 10 ? parsedControls[bestIndex] : null;
6126
- if (!parsedControl) {
6127
- return dukeControl;
6128
- }
6129
- usedParsedIndexes.add(bestIndex);
6130
- const dukeAnchorChanged = !areDukeFormControlAnchorsEqual(
6131
- parsedControl.dukeAnchor,
6132
- dukeControl.dukeAnchor
6133
- );
6134
- return {
6135
- ...parsedControl,
6136
- ...dukeControl,
6137
- anchor: dukeAnchorChanged ? dukeControl.anchor : parsedControl.anchor,
6138
- fontFamily: parsedControl.fontFamily,
6139
- fontSizePt: parsedControl.fontSizePt,
6140
- hidden: parsedControl.hidden,
6141
- id: parsedControl.id,
6142
- label: dukeControl.label ?? parsedControl.label,
6143
- name: dukeControl.name ?? parsedControl.name,
6144
- textAlign: parsedControl.textAlign,
6145
- textColor: parsedControl.textColor,
6146
- zIndex: parsedControl.zIndex
6147
- };
6148
- });
6149
- parsedControls.forEach((parsedControl, parsedIndex) => {
6150
- if (!usedParsedIndexes.has(parsedIndex) && keepUnmatchedParsedControl(parsedControl)) {
6151
- mergedControls.push(parsedControl);
6152
- }
6153
- });
6154
- return mergedControls.sort((left, right) => left.zIndex - right.zIndex);
6155
- }
6156
- function refreshWorkbookFormControls(workbook, currentControlsByWorkbookSheetIndex) {
6157
- const dukeControlsByWorkbookSheetIndex = collectWorkbookFormControls(workbook);
6158
- return dukeControlsByWorkbookSheetIndex.map((dukeControls, workbookSheetIndex) => mergeDukeAndParsedFormControls(
6159
- dukeControls,
6160
- currentControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6161
- (control) => control.controlIndex === void 0
6162
- ));
6013
+ function refreshWorkbookFormControls(workbook, _currentControlsByWorkbookSheetIndex, themePalette) {
6014
+ return collectWorkbookFormControls(workbook, themePalette);
6163
6015
  }
6164
6016
  function revokeWorkbookImageAssets(assets) {
6165
6017
  if (!assets) {
@@ -6212,7 +6064,7 @@ function parseWorkbookImageAssets(bytes, workbook) {
6212
6064
  const shapesByWorkbookSheetIndex = [];
6213
6065
  const sheetOrigins = [];
6214
6066
  const imageOriginsById = /* @__PURE__ */ new Map();
6215
- const dukeFormControlsByWorkbookSheetIndex = collectWorkbookFormControls(workbook);
6067
+ const formControls = workbook ? collectWorkbookFormControls(workbook, themePalette) : [];
6216
6068
  workbookSheets.forEach((sheet, workbookSheetIndex) => {
6217
6069
  const sheetRelationships = parseRelationships(archive, relsPathForDocument(sheet.path), sheet.path);
6218
6070
  const attachments = [];
@@ -6245,23 +6097,8 @@ function parseWorkbookImageAssets(bytes, workbook) {
6245
6097
  mediaPaths: drawingImages.mediaPaths
6246
6098
  });
6247
6099
  }
6248
- const formControlList = parseSheetFormControls(
6249
- archive,
6250
- sheet.path,
6251
- sheetRelationships,
6252
- workbookSheetIndex,
6253
- zIndexBase
6254
- );
6255
6100
  const visibleShapeList = shapeList.filter((shape) => !shape.hidden);
6256
- const enrichedFormControlList = enrichFormControlsWithHiddenShapes(
6257
- formControlList,
6258
- shapeList,
6259
- sheetStatesByWorkbookSheetIndex[workbookSheetIndex] ?? null
6260
- );
6261
- formControlsByWorkbookSheetIndex[workbookSheetIndex] = mergeDukeAndParsedFormControls(
6262
- dukeFormControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6263
- enrichedFormControlList
6264
- );
6101
+ formControlsByWorkbookSheetIndex[workbookSheetIndex] = formControls[workbookSheetIndex] ?? [];
6265
6102
  imagesByWorkbookSheetIndex[workbookSheetIndex] = imageList;
6266
6103
  shapesByWorkbookSheetIndex[workbookSheetIndex] = visibleShapeList;
6267
6104
  sheetOrigins[workbookSheetIndex] = attachments.length > 0 ? {
@@ -6271,6 +6108,7 @@ function parseWorkbookImageAssets(bytes, workbook) {
6271
6108
  });
6272
6109
  return {
6273
6110
  archive,
6111
+ dirtyArchivePaths: /* @__PURE__ */ new Set(),
6274
6112
  formControlsByWorkbookSheetIndex,
6275
6113
  imageOriginsById,
6276
6114
  imagesByWorkbookSheetIndex,
@@ -6333,6 +6171,7 @@ function updateWorkbookImageAnchor(assets, imageId, anchor) {
6333
6171
  }
6334
6172
  updateAnchorNode2(anchorNode, anchor);
6335
6173
  assets.archive[attachment.drawingPath] = strToU82(serializeXml2(drawingDocument));
6174
+ assets.dirtyArchivePaths.add(normalizeArchivePath2(attachment.drawingPath));
6336
6175
  const imageList = assets.imagesByWorkbookSheetIndex[origin.workbookSheetIndex] ?? [];
6337
6176
  const imageIndex = imageList.findIndex((image) => image.id === imageId);
6338
6177
  if (imageIndex >= 0) {
@@ -6449,7 +6288,7 @@ function appendSheetDrawingReference(sheetDocument, relationshipId) {
6449
6288
  worksheet.appendChild(drawingNode);
6450
6289
  }
6451
6290
  function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6452
- if (!sourceAssets || sheetOrigins.every((origin) => !origin?.attachments.length)) {
6291
+ if (!sourceAssets || sourceAssets.dirtyArchivePaths.size === 0) {
6453
6292
  return cloneBytes(savedBytes);
6454
6293
  }
6455
6294
  try {
@@ -6461,7 +6300,8 @@ function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6461
6300
  return cloneBytes(savedBytes);
6462
6301
  }
6463
6302
  sheetOrigins.forEach((origin, workbookSheetIndex) => {
6464
- if (!origin?.attachments.length) {
6303
+ const hasDirtyAttachment = origin?.attachments.some((attachment) => sourceAssets.dirtyArchivePaths.has(normalizeArchivePath2(attachment.drawingPath)) || attachment.drawingRelsPath && sourceAssets.dirtyArchivePaths.has(normalizeArchivePath2(attachment.drawingRelsPath)) || attachment.mediaPaths.some((path) => sourceAssets.dirtyArchivePaths.has(normalizeArchivePath2(path))));
6304
+ if (!origin?.attachments.length || !hasDirtyAttachment) {
6465
6305
  return;
6466
6306
  }
6467
6307
  const currentSheet = workbookSheets[workbookSheetIndex];
@@ -6513,6 +6353,14 @@ function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6513
6353
  archive[relsPath] = strToU82(serializeXml2(relDocument));
6514
6354
  mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, relsPath);
6515
6355
  });
6356
+ for (const dirtyPath of sourceAssets.dirtyArchivePaths) {
6357
+ const bytes = sourceAssets.archive[dirtyPath];
6358
+ if (!bytes) {
6359
+ continue;
6360
+ }
6361
+ archive[dirtyPath] = cloneBytes(bytes);
6362
+ mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, dirtyPath);
6363
+ }
6516
6364
  const hasDrawingOverride = getLocalElements(targetContentTypesDocument, "Override").some(
6517
6365
  (node) => node.getAttribute("ContentType") === DRAWING_CONTENT_TYPE
6518
6366
  );
@@ -6918,8 +6766,6 @@ var DEFAULT_MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
6918
6766
  var MAX_INTERACTIVE_WORKSHEET_XML_BYTES = 200 * 1024 * 1024;
6919
6767
  var MAX_INTERACTIVE_SHARED_STRINGS_BYTES = 50 * 1024 * 1024;
6920
6768
  var MAX_INTERACTIVE_TOTAL_XML_BYTES = 256 * 1024 * 1024;
6921
- var EMU_PER_PIXEL3 = 9525;
6922
- var IMAGE_BATCH_ROW_COUNT = 256;
6923
6769
  var DEFAULT_ZOOM_SCALE = 100;
6924
6770
  var MIN_ZOOM_SCALE = 10;
6925
6771
  var MAX_ZOOM_SCALE = 400;
@@ -7374,104 +7220,175 @@ function resolveWorkbookReference(workbook, defaultWorkbookSheetIndex, rawRefere
7374
7220
  return null;
7375
7221
  }
7376
7222
  }
7377
- function formControlInputFromDukeControl(control) {
7378
- return {
7379
- anchor: { ...control.anchor },
7380
- kind: { ...control.kind },
7381
- locked: control.locked,
7382
- name: control.name,
7383
- printable: control.printable
7384
- };
7223
+ function parseColorHex(hex) {
7224
+ const normalized = hex?.replace(/^#/, "");
7225
+ return normalized && /^[0-9a-f]{6}$/i.test(normalized) ? [0, 2, 4].map((offset) => Number.parseInt(normalized.slice(offset, offset + 2), 16)) : null;
7385
7226
  }
7386
- function getFormControlCellLink(kind) {
7387
- return "cellLink" in kind ? kind.cellLink : void 0;
7227
+ function xlsxColorToDukeColor(color) {
7228
+ if (!color) {
7229
+ return void 0;
7230
+ }
7231
+ const normalizedHex = color.hex?.replace(/^#/, "");
7232
+ 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);
7233
+ switch (colorType) {
7234
+ case "auto":
7235
+ return { colorType: "auto" };
7236
+ case "rgb": {
7237
+ const channels = color.r !== void 0 && color.g !== void 0 && color.b !== void 0 ? [color.r, color.g, color.b] : parseColorHex(color.hex);
7238
+ return channels ? { b: channels[2], colorType: "rgb", g: channels[1], r: channels[0] } : void 0;
7239
+ }
7240
+ case "argb": {
7241
+ const normalized = color.hex?.replace(/^#/, "");
7242
+ 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;
7243
+ return channels ? { a: channels[0], b: channels[3], colorType: "argb", g: channels[2], r: channels[1] } : void 0;
7244
+ }
7245
+ case "theme":
7246
+ return color.themeIndex === void 0 ? void 0 : { colorType: "theme", index: color.themeIndex, tint: color.tint ?? 0 };
7247
+ case "indexed":
7248
+ return color.paletteIndex === void 0 ? void 0 : { colorType: "indexed", index: color.paletteIndex };
7249
+ }
7388
7250
  }
7389
- function anchorContainsControl(group, control) {
7390
- const groupAnchor = group.anchor;
7391
- const controlAnchor = control.anchor;
7392
- return controlAnchor.fromCol >= groupAnchor.fromCol && controlAnchor.fromCol <= groupAnchor.toCol && controlAnchor.fromRow >= groupAnchor.fromRow && controlAnchor.fromRow <= groupAnchor.toRow;
7251
+ function xlsxFontToDukeFont(font) {
7252
+ return {
7253
+ bold: font.bold,
7254
+ charset: font.charset,
7255
+ color: xlsxColorToDukeColor(font.color),
7256
+ family: font.family,
7257
+ italic: font.italic,
7258
+ name: font.name,
7259
+ scheme: font.scheme,
7260
+ size: font.size,
7261
+ strikethrough: font.strikethrough,
7262
+ underline: font.underline,
7263
+ verticalAlign: font.verticalAlign
7264
+ };
7393
7265
  }
7394
- function resolveOptionButtonGroupIndexes(controls, targetIndex) {
7395
- const target = controls[targetIndex];
7396
- if (!target || target.kind.kind !== "optionButton") {
7397
- return [];
7266
+ function xlsxCaptionToDukeText(caption) {
7267
+ if (typeof caption === "string") {
7268
+ return { runs: [{ text: caption }] };
7398
7269
  }
7399
- if (target.kind.cellLink) {
7400
- const targetCellLink = target.kind.cellLink;
7401
- return controls.flatMap((control, index) => control.kind.kind === "optionButton" && control.kind.cellLink === targetCellLink ? [index] : []);
7402
- }
7403
- const containingGroups = controls.map((control, index) => ({ control, index })).filter(({ control }) => control.kind.kind === "groupBox" && anchorContainsControl(control, target)).sort((left, right) => {
7404
- const leftArea = (left.control.anchor.toCol - left.control.anchor.fromCol) * (left.control.anchor.toRow - left.control.anchor.fromRow);
7405
- const rightArea = (right.control.anchor.toCol - right.control.anchor.fromCol) * (right.control.anchor.toRow - right.control.anchor.fromRow);
7406
- return leftArea - rightArea;
7407
- });
7408
- const containingGroup = containingGroups[0]?.control;
7409
- if (containingGroup) {
7410
- return controls.flatMap((control, index) => control.kind.kind === "optionButton" && anchorContainsControl(containingGroup, control) ? [index] : []);
7270
+ return {
7271
+ horizontalAlignment: caption.horizontalAlignment,
7272
+ runs: caption.runs.map((run) => ({
7273
+ font: run.font ? xlsxFontToDukeFont(run.font) : void 0,
7274
+ text: run.text
7275
+ })),
7276
+ verticalAlignment: caption.verticalAlignment
7277
+ };
7278
+ }
7279
+ function xlsxFormControlKindToDukeKind(kind) {
7280
+ switch (kind.kind) {
7281
+ case "button":
7282
+ case "label":
7283
+ return { caption: xlsxCaptionToDukeText(kind.caption), kind: kind.kind };
7284
+ case "checkbox":
7285
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7286
+ case "optionButton":
7287
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7288
+ case "groupBox":
7289
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7290
+ case "editbox":
7291
+ return {
7292
+ caption: xlsxCaptionToDukeText(kind.caption),
7293
+ kind: "unknown",
7294
+ legacyObjectType: kind.legacyObjectType,
7295
+ objectType: "EditBox",
7296
+ rawObj: kind.rawObj,
7297
+ rawProperties: kind.rawProperties
7298
+ };
7299
+ case "unknown":
7300
+ return {
7301
+ caption: kind.caption ? xlsxCaptionToDukeText(kind.caption) : void 0,
7302
+ kind: "unknown",
7303
+ legacyObjectType: kind.legacyObjectType,
7304
+ objectType: kind.objectType,
7305
+ rawObj: kind.rawObj,
7306
+ rawProperties: kind.rawProperties
7307
+ };
7308
+ default:
7309
+ return { ...kind };
7411
7310
  }
7412
- const optionIndexes = controls.flatMap((control, index) => control.kind.kind === "optionButton" ? [index] : []);
7413
- const targetPosition = optionIndexes.indexOf(targetIndex);
7414
- if (targetPosition < 0) {
7415
- return [];
7311
+ }
7312
+ function dukeFormControlKindToInput(kind) {
7313
+ if (kind.kind === "optionButton") {
7314
+ return { ...kind, state: kind.state === "checked" ? "checked" : "unchecked" };
7416
7315
  }
7417
- let startPosition = targetPosition;
7418
- while (startPosition > 0) {
7419
- const control = controls[optionIndexes[startPosition] ?? -1];
7420
- if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7421
- break;
7422
- }
7423
- startPosition -= 1;
7316
+ if (kind.kind === "unknown") {
7317
+ return {
7318
+ caption: kind.caption,
7319
+ kind: "unknown",
7320
+ legacyObjectType: kind.legacyObjectType,
7321
+ objectType: kind.objectType,
7322
+ rawObj: kind.rawObj,
7323
+ rawProperties: kind.rawProperties
7324
+ };
7424
7325
  }
7425
- let endPosition = targetPosition + 1;
7426
- while (endPosition < optionIndexes.length) {
7427
- const control = controls[optionIndexes[endPosition] ?? -1];
7428
- if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7429
- break;
7326
+ return { ...kind };
7327
+ }
7328
+ function formControlInputFromDukeControl(control) {
7329
+ return {
7330
+ altText: control.altText,
7331
+ ...control.anchor ? { anchor: control.anchor } : { transform: control.transform },
7332
+ formControl: {
7333
+ kind: dukeFormControlKindToInput(control.formControl.kind),
7334
+ macroName: control.formControl.macroName,
7335
+ rawClientData: control.formControl.rawClientData
7336
+ },
7337
+ hidden: control.hidden,
7338
+ kind: "formControl",
7339
+ locked: control.locked,
7340
+ name: control.name,
7341
+ printable: control.printable,
7342
+ title: control.title
7343
+ };
7344
+ }
7345
+ function xlsxFormControlInputToDukeDrawing(input) {
7346
+ return {
7347
+ altText: input.altText,
7348
+ anchor: xlsxAnchorToDukeDrawingAnchor(input.anchor, input.editAs),
7349
+ formControl: {
7350
+ kind: xlsxFormControlKindToDukeKind(input.kind),
7351
+ macroName: input.macroName,
7352
+ rawClientData: input.rawClientData
7353
+ },
7354
+ hidden: input.hidden,
7355
+ kind: "formControl",
7356
+ locked: input.locked,
7357
+ name: input.name,
7358
+ printable: input.printable,
7359
+ title: input.title
7360
+ };
7361
+ }
7362
+ function reconcileCheckedOptionButtons(worksheet) {
7363
+ const checkedPaths = worksheet.formControls.flatMap((control) => control.formControl.kind.kind === "optionButton" && control.formControl.kind.state === "checked" ? [control.drawingPath] : []);
7364
+ for (const path of checkedPaths) {
7365
+ const current = worksheet.formControls.find((control) => control.drawingPath.length === path.length && control.drawingPath.every((segment, index) => segment === path[index]));
7366
+ if (current?.formControl.kind.kind === "optionButton" && current.formControl.kind.state === "checked") {
7367
+ worksheet.setFormControlCheckState(current.drawingPath, "checked");
7430
7368
  }
7431
- endPosition += 1;
7432
7369
  }
7433
- return optionIndexes.slice(startPosition, endPosition);
7434
7370
  }
7435
- function resolveLinkedCellValue(controls, controlIndex) {
7436
- const control = controls[controlIndex];
7437
- if (!control) {
7438
- return void 0;
7371
+ function formControlInteractionChanged(current, next) {
7372
+ if (current.kind !== next.kind) {
7373
+ return true;
7374
+ }
7375
+ if ("cellLink" in current && "cellLink" in next && current.cellLink !== next.cellLink) {
7376
+ return true;
7439
7377
  }
7440
- switch (control.kind.kind) {
7378
+ switch (current.kind) {
7441
7379
  case "checkbox":
7442
- return control.kind.state === "mixed" ? "#N/A" : control.kind.state === "checked";
7443
- case "optionButton": {
7444
- const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
7445
- const checkedIndex = groupIndexes.find((index) => controls[index]?.kind.kind === "optionButton" && controls[index]?.kind.state === "checked");
7446
- const groupPosition = checkedIndex === void 0 ? -1 : groupIndexes.indexOf(checkedIndex);
7447
- return groupPosition >= 0 ? groupPosition + 1 : "";
7448
- }
7380
+ case "optionButton":
7381
+ return next.kind === current.kind && current.state !== next.state;
7449
7382
  case "dropdown":
7450
- return control.kind.selected === void 0 ? "" : control.kind.selected + 1;
7383
+ return next.kind === "dropdown" && current.selected !== next.selected;
7451
7384
  case "listBox":
7452
- return control.kind.selection === "single" && control.kind.selected.length === 1 ? (control.kind.selected[0] ?? -1) + 1 : "";
7385
+ return next.kind === "listBox" && (current.selection !== next.selection || current.selected.length !== next.selected.length || current.selected.some((value, index) => value !== next.selected[index]));
7453
7386
  case "scrollbar":
7454
7387
  case "spinner":
7455
- return control.kind.value;
7388
+ return next.kind === current.kind && current.value !== next.value;
7456
7389
  default:
7457
- return void 0;
7458
- }
7459
- }
7460
- function syncFormControlLinkedCell(workbook, workbookSheetIndex, controls, controlIndex) {
7461
- const control = controls[controlIndex];
7462
- if (!control) {
7463
- return;
7464
- }
7465
- const cellLink = getFormControlCellLink(control.kind);
7466
- const value = resolveLinkedCellValue(controls, controlIndex);
7467
- if (!cellLink || value === void 0) {
7468
- return;
7469
- }
7470
- const target = resolveWorkbookReference(workbook, workbookSheetIndex, cellLink);
7471
- if (!target || target.range.start.row !== target.range.end.row || target.range.start.col !== target.range.end.col) {
7472
- return;
7390
+ return false;
7473
7391
  }
7474
- target.worksheet.setCell(cellAddressToA1(target.range.start), value);
7475
7392
  }
7476
7393
  function parseWorksheetFreezePanes(worksheet) {
7477
7394
  const rawFreezePanes = worksheet.freezePanes;
@@ -7805,9 +7722,6 @@ function scheduleLowPriorityTask(task) {
7805
7722
  const timeoutHandle = window.setTimeout(task, 0);
7806
7723
  return () => window.clearTimeout(timeoutHandle);
7807
7724
  }
7808
- function asFiniteNumber(value) {
7809
- return typeof value === "number" && Number.isFinite(value) ? value : null;
7810
- }
7811
7725
  function inferImageMimeType(source) {
7812
7726
  if (source.startsWith("data:")) {
7813
7727
  const separatorIndex = source.indexOf(";");
@@ -7831,18 +7745,27 @@ function inferImageMimeType(source) {
7831
7745
  return "image/png";
7832
7746
  }
7833
7747
  function inferWorksheetDirectImageMimeType(info) {
7834
- const format = typeof info.format === "string" ? info.format.trim().toLowerCase() : "";
7748
+ const format = info.format;
7835
7749
  if (format === "gif") {
7836
7750
  return "image/gif";
7837
7751
  }
7838
- if (format === "jpg" || format === "jpeg") {
7752
+ if (format === "jpeg") {
7839
7753
  return "image/jpeg";
7840
7754
  }
7841
7755
  if (format === "svg") {
7842
7756
  return "image/svg+xml";
7843
7757
  }
7844
- if (format === "webp") {
7845
- return "image/webp";
7758
+ if (format === "bmp") {
7759
+ return "image/bmp";
7760
+ }
7761
+ if (format === "tiff") {
7762
+ return "image/tiff";
7763
+ }
7764
+ if (format === "emf") {
7765
+ return "image/emf";
7766
+ }
7767
+ if (format === "wmf") {
7768
+ return "image/wmf";
7846
7769
  }
7847
7770
  if (format === "png") {
7848
7771
  return "image/png";
@@ -7864,323 +7787,108 @@ function createWorksheetDirectImageSource(data, mimeType, objectUrls) {
7864
7787
  objectUrls.push(objectUrl);
7865
7788
  return objectUrl;
7866
7789
  }
7867
- function buildWorksheetDirectImageAnchor(rawAnchor, widthEmu, heightEmu) {
7868
- const anchor = rawAnchor && typeof rawAnchor === "object" ? rawAnchor : {};
7869
- const fromCol = asFiniteNumber(anchor.fromCol) ?? 0;
7870
- const fromRow = asFiniteNumber(anchor.fromRow) ?? 0;
7871
- const fromColOffset = asFiniteNumber(anchor.fromColOffset) ?? 0;
7872
- const fromRowOffset = asFiniteNumber(anchor.fromRowOffset) ?? 0;
7873
- const toCol = asFiniteNumber(anchor.toCol);
7874
- const toRow = asFiniteNumber(anchor.toRow);
7875
- const toColOffset = asFiniteNumber(anchor.toColOffset) ?? 0;
7876
- const toRowOffset = asFiniteNumber(anchor.toRowOffset) ?? 0;
7877
- if (toCol !== null && toRow !== null) {
7878
- return {
7879
- from: {
7880
- col: Math.max(0, Math.round(fromCol)),
7881
- colOffsetEmu: Math.max(0, Math.round(fromColOffset)),
7882
- row: Math.max(0, Math.round(fromRow)),
7883
- rowOffsetEmu: Math.max(0, Math.round(fromRowOffset))
7884
- },
7885
- kind: "two-cell",
7886
- to: {
7887
- col: Math.max(0, Math.round(toCol)),
7888
- colOffsetEmu: Math.max(0, Math.round(toColOffset)),
7889
- row: Math.max(0, Math.round(toRow)),
7890
- rowOffsetEmu: Math.max(0, Math.round(toRowOffset))
7891
- }
7892
- };
7893
- }
7894
- return {
7895
- from: {
7896
- col: Math.max(0, Math.round(fromCol)),
7897
- colOffsetEmu: Math.max(0, Math.round(fromColOffset)),
7898
- row: Math.max(0, Math.round(fromRow)),
7899
- rowOffsetEmu: Math.max(0, Math.round(fromRowOffset))
7900
- },
7901
- kind: "one-cell",
7902
- sizeEmu: {
7903
- cx: Math.max(EMU_PER_PIXEL3, Math.round(widthEmu)),
7904
- cy: Math.max(EMU_PER_PIXEL3, Math.round(heightEmu))
7905
- }
7906
- };
7907
- }
7908
- function normalizeWorksheetDirectShapeParagraphs(rawParagraphs, fallbackText) {
7909
- const normalizedParagraphs = [];
7910
- if (Array.isArray(rawParagraphs)) {
7911
- for (const entry of rawParagraphs) {
7912
- const paragraph = entry && typeof entry === "object" ? entry : {};
7913
- const runs = [];
7914
- if (Array.isArray(paragraph.runs)) {
7915
- for (const runEntry of paragraph.runs) {
7916
- const run = runEntry && typeof runEntry === "object" ? runEntry : {};
7917
- const text2 = typeof run.text === "string" ? run.text : "";
7918
- if (!text2) {
7919
- continue;
7920
- }
7921
- runs.push({
7922
- bold: typeof run.bold === "boolean" ? run.bold : void 0,
7923
- color: typeof run.color === "string" && run.color.trim() ? run.color : void 0,
7924
- fontFamily: typeof run.fontFamily === "string" && run.fontFamily.trim() ? run.fontFamily : void 0,
7925
- fontSizePt: asFiniteNumber(run.fontSizePt) ?? void 0,
7926
- italic: typeof run.italic === "boolean" ? run.italic : void 0,
7927
- text: text2,
7928
- underline: typeof run.underline === "boolean" ? run.underline : void 0
7929
- });
7930
- }
7931
- }
7932
- if (runs.length === 0) {
7933
- continue;
7934
- }
7935
- const align = paragraph.align;
7936
- normalizedParagraphs.push({
7937
- align: align === "center" || align === "justify" || align === "left" || align === "right" ? align : void 0,
7938
- runs
7939
- });
7790
+ function buildWorksheetDirectApiImage(worksheet, workbookSheetIndex, drawing, objectUrls) {
7791
+ const hasSvgCompanion = Boolean(drawing.image.svgMediaPath);
7792
+ let mimeType = inferWorksheetDirectImageMimeType(drawing.image);
7793
+ let data;
7794
+ try {
7795
+ const svgData = drawing.image.format === "svg" || hasSvgCompanion ? worksheet.drawingSvgData(drawing.drawingPath) : void 0;
7796
+ if (svgData) {
7797
+ data = svgData;
7798
+ mimeType = "image/svg+xml";
7799
+ } else {
7800
+ data = worksheet.drawingImageData(drawing.drawingPath);
7940
7801
  }
7941
- }
7942
- if (normalizedParagraphs.length > 0) {
7943
- return normalizedParagraphs;
7944
- }
7945
- const text = typeof fallbackText === "string" ? fallbackText : "";
7946
- return text ? [{ runs: [{ text }] }] : [];
7947
- }
7948
- function buildWorksheetDirectApiShape(workbookSheetIndex, info, zIndex) {
7949
- const fill = info.fill && typeof info.fill === "object" ? {
7950
- color: typeof info.fill.color === "string" && info.fill.color.trim() ? info.fill.color : void 0,
7951
- none: typeof info.fill.none === "boolean" ? info.fill.none : void 0,
7952
- opacity: asFiniteNumber(info.fill.opacity) ?? void 0
7953
- } : void 0;
7954
- const stroke = info.stroke && typeof info.stroke === "object" ? {
7955
- color: typeof info.stroke.color === "string" && info.stroke.color.trim() ? info.stroke.color : void 0,
7956
- dash: typeof info.stroke.dash === "string" && info.stroke.dash.trim() ? info.stroke.dash : void 0,
7957
- headEndType: typeof info.stroke.headEndType === "string" && info.stroke.headEndType.trim() ? info.stroke.headEndType : void 0,
7958
- none: typeof info.stroke.none === "boolean" ? info.stroke.none : void 0,
7959
- opacity: asFiniteNumber(info.stroke.opacity) ?? void 0,
7960
- tailEndType: typeof info.stroke.tailEndType === "string" && info.stroke.tailEndType.trim() ? info.stroke.tailEndType : void 0,
7961
- widthPx: asFiniteNumber(info.stroke.widthPx) ?? void 0
7962
- } : void 0;
7963
- const rawSvgViewBox = info.svgViewBox && typeof info.svgViewBox === "object" ? info.svgViewBox : null;
7964
- const rawTextBox = info.textBox && typeof info.textBox === "object" ? info.textBox : null;
7965
- const rawInset = rawTextBox?.insetPx && typeof rawTextBox.insetPx === "object" ? rawTextBox.insetPx : null;
7966
- return {
7967
- anchor: buildWorksheetDirectImageAnchor(
7968
- info.anchor,
7969
- DEFAULT_COL_WIDTH * EMU_PER_PIXEL3,
7970
- DEFAULT_ROW_HEIGHT * EMU_PER_PIXEL3
7971
- ),
7972
- description: typeof info.description === "string" && info.description.trim() ? info.description : void 0,
7973
- fill,
7974
- flipH: typeof info.flipH === "boolean" ? info.flipH : void 0,
7975
- flipV: typeof info.flipV === "boolean" ? info.flipV : void 0,
7976
- geometry: typeof info.geometry === "string" && info.geometry.trim() ? info.geometry : "rect",
7977
- geometryAdjustments: info.geometryAdjustments && typeof info.geometryAdjustments === "object" ? Object.fromEntries(
7978
- Object.entries(info.geometryAdjustments).map(([key, value]) => [key, asFiniteNumber(value)]).filter((entry) => typeof entry[1] === "number")
7979
- ) : void 0,
7980
- hyperlink: typeof info.hyperlink === "string" && info.hyperlink.trim() ? info.hyperlink : void 0,
7981
- id: `shape-${workbookSheetIndex}-${String(info.id ?? zIndex)}`,
7982
- name: typeof info.name === "string" && info.name.trim() ? info.name : void 0,
7983
- paragraphs: normalizeWorksheetDirectShapeParagraphs(info.paragraphs, info.text),
7984
- rotationDeg: asFiniteNumber(info.rotationDeg) ?? void 0,
7985
- scaleX: asFiniteNumber(info.scaleX) ?? void 0,
7986
- scaleY: asFiniteNumber(info.scaleY) ?? void 0,
7987
- sheetIndex: workbookSheetIndex,
7988
- svgPath: typeof info.svgPath === "string" && info.svgPath.trim() ? info.svgPath : void 0,
7989
- svgViewBox: rawSvgViewBox && asFiniteNumber(rawSvgViewBox.width) !== null && asFiniteNumber(rawSvgViewBox.height) !== null ? {
7990
- height: asFiniteNumber(rawSvgViewBox.height) ?? 0,
7991
- width: asFiniteNumber(rawSvgViewBox.width) ?? 0
7992
- } : void 0,
7993
- stroke,
7994
- textBox: rawTextBox ? {
7995
- horizontalAlign: rawTextBox.horizontalAlign === "center" || rawTextBox.horizontalAlign === "left" ? rawTextBox.horizontalAlign : void 0,
7996
- insetPx: rawInset ? {
7997
- bottom: asFiniteNumber(rawInset.bottom) ?? 0,
7998
- left: asFiniteNumber(rawInset.left) ?? 0,
7999
- right: asFiniteNumber(rawInset.right) ?? 0,
8000
- top: asFiniteNumber(rawInset.top) ?? 0
8001
- } : void 0,
8002
- verticalAlign: rawTextBox.verticalAlign === "bottom" || rawTextBox.verticalAlign === "middle" || rawTextBox.verticalAlign === "top" ? rawTextBox.verticalAlign : void 0
8003
- } : void 0,
8004
- workbookSheetIndex,
8005
- zIndex
8006
- };
8007
- }
8008
- function buildWorksheetApiImage(workbookSheetIndex, row, col, info, zIndex) {
8009
- if (typeof info.source !== "string" || !info.source) {
7802
+ } catch {
8010
7803
  return null;
8011
7804
  }
8012
- const width = Math.max(1, Math.round(asFiniteNumber(info.width) ?? DEFAULT_COL_WIDTH));
8013
- const height = Math.max(1, Math.round(asFiniteNumber(info.height) ?? DEFAULT_ROW_HEIGHT));
8014
- const description = typeof info.altText === "string" && info.altText.trim() ? info.altText : void 0;
8015
- return {
8016
- anchor: {
8017
- from: {
8018
- col,
8019
- colOffsetEmu: 0,
8020
- row,
8021
- rowOffsetEmu: 0
8022
- },
8023
- kind: "one-cell",
8024
- sizeEmu: {
8025
- cx: width * EMU_PER_PIXEL3,
8026
- cy: height * EMU_PER_PIXEL3
8027
- }
8028
- },
8029
- description,
8030
- editable: false,
8031
- id: `worksheet-image-${workbookSheetIndex}-${row}-${col}-${zIndex}`,
8032
- mimeType: inferImageMimeType(info.source),
8033
- sheetIndex: workbookSheetIndex,
8034
- src: info.source,
8035
- workbookSheetIndex,
8036
- zIndex
8037
- };
8038
- }
8039
- function buildWorksheetDirectApiImage(workbookSheetIndex, info, zIndex, objectUrls) {
8040
- const mimeType = inferWorksheetDirectImageMimeType(info);
8041
- const src = createWorksheetDirectImageSource(info.data, mimeType, objectUrls);
7805
+ const src = createWorksheetDirectImageSource(data, mimeType, objectUrls);
8042
7806
  if (!src) {
8043
7807
  return null;
8044
7808
  }
8045
- const widthEmu = Math.max(EMU_PER_PIXEL3, Math.round(asFiniteNumber(info.widthEmu) ?? DEFAULT_COL_WIDTH * EMU_PER_PIXEL3));
8046
- const heightEmu = Math.max(EMU_PER_PIXEL3, Math.round(asFiniteNumber(info.heightEmu) ?? DEFAULT_ROW_HEIGHT * EMU_PER_PIXEL3));
7809
+ const zIndex = (drawing.drawingPath[0] ?? 0) + 1;
8047
7810
  return {
8048
- anchor: buildWorksheetDirectImageAnchor(info.anchor, widthEmu, heightEmu),
7811
+ anchor: dukeDrawingAnchorToXlsxAnchor(drawing.anchor),
7812
+ description: drawing.altText,
8049
7813
  editable: false,
8050
- id: `worksheet-image-${workbookSheetIndex}-${String(info.id ?? zIndex)}`,
8051
- mediaPath: typeof info.mediaPath === "string" && info.mediaPath.trim() ? info.mediaPath : void 0,
7814
+ id: `worksheet-image-${workbookSheetIndex}-${drawing.drawingPath.join("-")}`,
7815
+ mediaPath: drawing.image.mediaPath || void 0,
8052
7816
  mimeType,
8053
- name: typeof info.name === "string" && info.name.trim() ? info.name : void 0,
7817
+ name: drawing.name,
8054
7818
  sheetIndex: workbookSheetIndex,
8055
7819
  src,
8056
7820
  workbookSheetIndex,
8057
7821
  zIndex
8058
7822
  };
8059
7823
  }
8060
- function collectWorksheetBatchImages(workbook) {
8061
- const imagesByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, () => []);
8062
- for (let workbookSheetIndex = 0; workbookSheetIndex < workbook.sheetCount; workbookSheetIndex += 1) {
8063
- const worksheet = workbook.getSheet(workbookSheetIndex);
8064
- if (typeof worksheet.getRowsBatch !== "function") {
8065
- continue;
8066
- }
8067
- const usedRange = worksheet.usedRange();
8068
- const maxRow = usedRange?.[2] ?? -1;
8069
- if (maxRow < 0) {
8070
- continue;
8071
- }
8072
- let zIndex = 1;
8073
- let sheetFailed = false;
8074
- for (let startRow = 0; startRow <= maxRow; startRow += IMAGE_BATCH_ROW_COUNT) {
8075
- let rows;
8076
- try {
8077
- rows = worksheet.getRowsBatch(startRow, IMAGE_BATCH_ROW_COUNT, { includeImages: true });
8078
- } catch {
8079
- sheetFailed = true;
8080
- break;
8081
- }
8082
- if (!Array.isArray(rows)) {
8083
- continue;
8084
- }
8085
- for (const rowEntry of rows) {
8086
- const row = typeof rowEntry.index === "number" ? rowEntry.index : null;
8087
- if (row === null || !Array.isArray(rowEntry.cells)) {
8088
- continue;
8089
- }
8090
- for (const cellEntry of rowEntry.cells) {
8091
- const col = typeof cellEntry.col === "number" ? cellEntry.col : null;
8092
- if (col === null || !cellEntry.image || typeof cellEntry.image !== "object") {
8093
- continue;
8094
- }
8095
- const image = buildWorksheetApiImage(workbookSheetIndex, row, col, cellEntry.image, zIndex);
8096
- if (!image) {
8097
- continue;
8098
- }
8099
- imagesByWorkbookSheetIndex[workbookSheetIndex].push(image);
8100
- zIndex += 1;
8101
- }
8102
- }
8103
- }
8104
- if (sheetFailed) {
8105
- imagesByWorkbookSheetIndex[workbookSheetIndex] = [];
8106
- }
8107
- }
8108
- return imagesByWorkbookSheetIndex;
8109
- }
8110
7824
  function collectWorksheetApiImages(workbook, objectUrls) {
8111
7825
  const directImagesByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, () => []);
8112
- let didUseDirectImages = false;
8113
7826
  for (let workbookSheetIndex = 0; workbookSheetIndex < workbook.sheetCount; workbookSheetIndex += 1) {
8114
7827
  const worksheet = workbook.getSheet(workbookSheetIndex);
8115
- const rawImages = Array.isArray(worksheet.images) ? worksheet.images : [];
7828
+ const rawImages = worksheet.images;
8116
7829
  if (rawImages.length === 0) {
8117
7830
  continue;
8118
7831
  }
8119
- const nextImages = rawImages.map((info, index) => buildWorksheetDirectApiImage(workbookSheetIndex, info, index + 1, objectUrls)).filter((image) => Boolean(image));
7832
+ const nextImages = rawImages.filter((drawing) => !drawing.hidden && Boolean(drawing.anchor)).map((drawing) => buildWorksheetDirectApiImage(worksheet, workbookSheetIndex, drawing, objectUrls)).filter((image) => Boolean(image));
8120
7833
  if (nextImages.length > 0) {
8121
7834
  directImagesByWorkbookSheetIndex[workbookSheetIndex] = nextImages;
8122
- didUseDirectImages = true;
8123
7835
  }
8124
7836
  }
8125
- if (didUseDirectImages) {
8126
- return directImagesByWorkbookSheetIndex;
8127
- }
8128
- return collectWorksheetBatchImages(workbook);
8129
- }
8130
- function collectWorksheetApiShapes(workbook) {
8131
- return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
8132
- const worksheet = workbook.getSheet(workbookSheetIndex);
8133
- const rawShapes = Array.isArray(worksheet.shapes) ? worksheet.shapes : [];
8134
- return rawShapes.map((shape, index) => buildWorksheetDirectApiShape(workbookSheetIndex, shape, index + 1));
8135
- });
7837
+ return directImagesByWorkbookSheetIndex;
8136
7838
  }
8137
- function mergeParsedAndApiImages(parsedImages, apiImages) {
8138
- if (parsedImages.length === 0) {
8139
- return apiImages;
8140
- }
8141
- if (apiImages.length === 0) {
8142
- return parsedImages;
7839
+ function imageAnchorKey(anchor) {
7840
+ if (anchor.kind === "absolute") {
7841
+ return [
7842
+ "absolute",
7843
+ Math.round(anchor.positionEmu.x),
7844
+ Math.round(anchor.positionEmu.y),
7845
+ Math.round(anchor.sizeEmu.cx),
7846
+ Math.round(anchor.sizeEmu.cy)
7847
+ ].join(":");
8143
7848
  }
8144
- const normalizeTextKey = (value) => value?.trim().toLowerCase() ?? "";
8145
- const anchorKey = (anchor) => {
8146
- if (anchor.kind === "absolute") {
8147
- return [
8148
- "absolute",
8149
- Math.round(anchor.positionEmu.x),
8150
- Math.round(anchor.positionEmu.y),
8151
- Math.round(anchor.sizeEmu.cx),
8152
- Math.round(anchor.sizeEmu.cy)
8153
- ].join(":");
8154
- }
8155
- if (anchor.kind === "one-cell") {
8156
- return [
8157
- "one",
8158
- anchor.from.col,
8159
- anchor.from.row,
8160
- Math.round(anchor.from.colOffsetEmu),
8161
- Math.round(anchor.from.rowOffsetEmu),
8162
- Math.round(anchor.sizeEmu.cx),
8163
- Math.round(anchor.sizeEmu.cy)
8164
- ].join(":");
8165
- }
7849
+ if (anchor.kind === "one-cell") {
8166
7850
  return [
8167
- "two",
7851
+ "one",
8168
7852
  anchor.from.col,
8169
7853
  anchor.from.row,
8170
7854
  Math.round(anchor.from.colOffsetEmu),
8171
7855
  Math.round(anchor.from.rowOffsetEmu),
8172
- anchor.to.col,
8173
- anchor.to.row,
8174
- Math.round(anchor.to.colOffsetEmu),
8175
- Math.round(anchor.to.rowOffsetEmu)
7856
+ Math.round(anchor.sizeEmu.cx),
7857
+ Math.round(anchor.sizeEmu.cy)
8176
7858
  ].join(":");
8177
- };
7859
+ }
7860
+ return [
7861
+ "two",
7862
+ anchor.from.col,
7863
+ anchor.from.row,
7864
+ Math.round(anchor.from.colOffsetEmu),
7865
+ Math.round(anchor.from.rowOffsetEmu),
7866
+ anchor.to.col,
7867
+ anchor.to.row,
7868
+ Math.round(anchor.to.colOffsetEmu),
7869
+ Math.round(anchor.to.rowOffsetEmu)
7870
+ ].join(":");
7871
+ }
7872
+ function collectHiddenWorksheetImageAnchors(workbook) {
7873
+ return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => new Set(
7874
+ workbook.getSheet(workbookSheetIndex).images.flatMap((drawing) => drawing.hidden && drawing.anchor ? [imageAnchorKey(dukeDrawingAnchorToXlsxAnchor(drawing.anchor))] : [])
7875
+ ));
7876
+ }
7877
+ function mergeParsedAndApiImages(parsedImages, apiImages, hiddenAnchorKeys) {
7878
+ const visibleParsedImages = parsedImages.filter((image) => !hiddenAnchorKeys.has(imageAnchorKey(image.anchor)));
7879
+ if (visibleParsedImages.length === 0) {
7880
+ return apiImages;
7881
+ }
7882
+ if (apiImages.length === 0) {
7883
+ return visibleParsedImages;
7884
+ }
7885
+ const normalizeTextKey = (value) => value?.trim().toLowerCase() ?? "";
8178
7886
  const imageKeys = (image) => {
8179
7887
  const keys = [
8180
- `${normalizeTextKey(image.mediaPath)}|${normalizeTextKey(image.name)}|${anchorKey(image.anchor)}`,
8181
- `${normalizeTextKey(image.mediaPath)}|${anchorKey(image.anchor)}`,
8182
- `${normalizeTextKey(image.name)}|${anchorKey(image.anchor)}`,
8183
- `${anchorKey(image.anchor)}`
7888
+ `${normalizeTextKey(image.mediaPath)}|${normalizeTextKey(image.name)}|${imageAnchorKey(image.anchor)}`,
7889
+ `${normalizeTextKey(image.mediaPath)}|${imageAnchorKey(image.anchor)}`,
7890
+ `${normalizeTextKey(image.name)}|${imageAnchorKey(image.anchor)}`,
7891
+ `${imageAnchorKey(image.anchor)}`
8184
7892
  ];
8185
7893
  return keys.filter((key, index) => key && keys.indexOf(key) === index);
8186
7894
  };
@@ -8210,7 +7918,7 @@ function mergeParsedAndApiImages(parsedImages, apiImages) {
8210
7918
  }
8211
7919
  return null;
8212
7920
  };
8213
- const merged = parsedImages.map((image) => {
7921
+ const merged = visibleParsedImages.map((image) => {
8214
7922
  const apiImage = takeApiMatch(image);
8215
7923
  if (!apiImage) {
8216
7924
  return image;
@@ -8221,7 +7929,8 @@ function mergeParsedAndApiImages(parsedImages, apiImages) {
8221
7929
  mediaPath: apiImage.mediaPath ?? image.mediaPath,
8222
7930
  mimeType: apiImage.mimeType,
8223
7931
  name: apiImage.name ?? image.name,
8224
- src: apiImage.src
7932
+ src: apiImage.src,
7933
+ zIndex: apiImage.zIndex
8225
7934
  };
8226
7935
  });
8227
7936
  return merged;
@@ -8239,12 +7948,13 @@ function createBasicWorkbookAssets(workbook) {
8239
7948
  const objectUrls = [];
8240
7949
  return {
8241
7950
  archive: {},
7951
+ dirtyArchivePaths: /* @__PURE__ */ new Set(),
8242
7952
  formControlsByWorkbookSheetIndex: collectWorkbookFormControls(workbook),
8243
7953
  imageOriginsById: /* @__PURE__ */ new Map(),
8244
7954
  imagesByWorkbookSheetIndex: collectWorksheetApiImages(workbook, objectUrls),
8245
7955
  namedCellStyleByName: {},
8246
7956
  objectUrls,
8247
- shapesByWorkbookSheetIndex: collectWorksheetApiShapes(workbook),
7957
+ shapesByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => []),
8248
7958
  sheetOrigins: Array.from({ length: workbook.sheetCount }, () => null),
8249
7959
  sheetStatesByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => null),
8250
7960
  styleById: {},
@@ -8259,12 +7969,17 @@ function loadWorkbookImageAssets(bytes, workbook, skipXmlParsing = false) {
8259
7969
  }
8260
7970
  const parsedAssets = parseWorkbookImageAssets(bytes, workbook);
8261
7971
  const apiImagesByWorkbookSheetIndex = collectWorksheetApiImages(workbook, parsedAssets.objectUrls);
7972
+ const hiddenImageAnchorsByWorkbookSheetIndex = collectHiddenWorksheetImageAnchors(workbook);
8262
7973
  const imagesByWorkbookSheetIndex = Array.from(
8263
7974
  { length: Math.max(workbook.sheetCount, parsedAssets.imagesByWorkbookSheetIndex.length, apiImagesByWorkbookSheetIndex.length) },
8264
7975
  (_, index) => {
8265
7976
  const parsedImages = parsedAssets.imagesByWorkbookSheetIndex[index] ?? [];
8266
7977
  const apiImages = apiImagesByWorkbookSheetIndex[index] ?? [];
8267
- return mergeParsedAndApiImages(parsedImages, apiImages);
7978
+ return mergeParsedAndApiImages(
7979
+ parsedImages,
7980
+ apiImages,
7981
+ hiddenImageAnchorsByWorkbookSheetIndex[index] ?? /* @__PURE__ */ new Set()
7982
+ );
8268
7983
  }
8269
7984
  );
8270
7985
  return {
@@ -8575,7 +8290,11 @@ function useXlsxViewerController(options) {
8575
8290
  }, [skipXmlParsing]);
8576
8291
  const refreshWorkbookState = React.useCallback((targetWorkbook) => {
8577
8292
  const currentFormControls = imageAssetsRef.current?.formControlsByWorkbookSheetIndex ?? [];
8578
- const nextFormControls = refreshWorkbookFormControls(targetWorkbook, currentFormControls);
8293
+ const nextFormControls = refreshWorkbookFormControls(
8294
+ targetWorkbook,
8295
+ currentFormControls,
8296
+ imageAssetsRef.current?.themePalette
8297
+ );
8579
8298
  if (imageAssetsRef.current) {
8580
8299
  imageAssetsRef.current.formControlsByWorkbookSheetIndex = nextFormControls;
8581
8300
  }
@@ -8701,13 +8420,19 @@ function useXlsxViewerController(options) {
8701
8420
  if (!effectiveSkipXmlParsing && hasIncompleteWorkerChartSnapshot(snapshot)) {
8702
8421
  throw new Error("Worker chart payload incomplete");
8703
8422
  }
8423
+ const workerImageAssets = effectiveSkipXmlParsing ? null : parseWorkbookImageAssets(new Uint8Array(buffer));
8424
+ if (!isCurrent || abortController.signal.aborted) {
8425
+ revokeWorkbookImageAssets(workerImageAssets);
8426
+ return;
8427
+ }
8428
+ setImageAssets(workerImageAssets);
8429
+ setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8704
8430
  setWorkbook(null);
8705
8431
  setSheets(snapshot.sheets);
8706
8432
  setChartsByWorkbookSheetIndex(snapshot.chartsByWorkbookSheetIndex);
8707
8433
  setChartsheets(snapshot.chartsheets);
8708
8434
  setTabs(snapshot.tabs);
8709
8435
  chartAssetsRef.current = null;
8710
- setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8711
8436
  setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);
8712
8437
  setShouldAutoCalculate(false);
8713
8438
  setIsWorkerBacked(true);
@@ -9205,10 +8930,16 @@ function useXlsxViewerController(options) {
9205
8930
  const getFormControlItems = React.useCallback((controlIndex, sheetIndex = activeSheetIndex) => {
9206
8931
  const target = getFormControlWorksheet(sheetIndex);
9207
8932
  if (!workbook || !target) {
9208
- return [];
8933
+ const targetSheet = sheets[sheetIndex];
8934
+ if (!targetSheet) {
8935
+ return [];
8936
+ }
8937
+ const control2 = (formControlsByWorkbookSheetIndex[targetSheet.workbookSheetIndex] ?? []).find((entry) => entry.controlIndex === controlIndex);
8938
+ return control2?.items?.slice() ?? [];
9209
8939
  }
9210
8940
  const control = target.worksheet.formControls[controlIndex];
9211
- const inputRange = control?.kind.kind === "listBox" || control?.kind.kind === "dropdown" ? control.kind.inputRange : void 0;
8941
+ const kind = control?.formControl.kind;
8942
+ const inputRange = kind?.kind === "listBox" || kind?.kind === "dropdown" ? kind.inputRange : void 0;
9212
8943
  if (!inputRange) {
9213
8944
  return [];
9214
8945
  }
@@ -9223,7 +8954,7 @@ function useXlsxViewerController(options) {
9223
8954
  }
9224
8955
  }
9225
8956
  return items;
9226
- }, [activeSheetIndex, getFormControlWorksheet, workbook]);
8957
+ }, [activeSheetIndex, formControlsByWorkbookSheetIndex, getFormControlWorksheet, sheets, workbook]);
9227
8958
  const getSheetShapes = React.useCallback((sheetIndex = activeSheetIndex) => {
9228
8959
  const targetSheet = sheets[sheetIndex];
9229
8960
  if (!targetSheet) {
@@ -9627,30 +9358,17 @@ function useXlsxViewerController(options) {
9627
9358
  return null;
9628
9359
  }
9629
9360
  recordHistoryBeforeMutation();
9630
- const controlIndex = target.worksheet.addFormControl(input);
9631
- const controls = target.worksheet.formControls;
9632
- const addedControl = controls[controlIndex];
9633
- if (addedControl?.kind.kind === "optionButton" && addedControl.kind.state === "checked") {
9634
- const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
9635
- for (const siblingIndex of groupIndexes) {
9636
- if (siblingIndex === controlIndex) {
9637
- continue;
9638
- }
9639
- const sibling = target.worksheet.formControls[siblingIndex];
9640
- if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9641
- target.worksheet.setFormControl(siblingIndex, {
9642
- ...formControlInputFromDukeControl(sibling),
9643
- kind: { ...sibling.kind, state: "unchecked" }
9644
- });
9645
- }
9646
- }
9361
+ target.worksheet.addDrawing(xlsxFormControlInputToDukeDrawing(input));
9362
+ const controlIndex = target.worksheet.formControlCount - 1;
9363
+ const addedControl = target.worksheet.formControls[controlIndex];
9364
+ const addedKind = addedControl?.formControl.kind;
9365
+ if (addedControl && (addedKind?.kind === "checkbox" || addedKind?.kind === "optionButton")) {
9366
+ target.worksheet.setFormControlCheckState(addedControl.drawingPath, addedKind.state);
9367
+ } else if (addedKind?.kind === "groupBox") {
9368
+ reconcileCheckedOptionButtons(target.worksheet);
9369
+ } else if (addedKind && "cellLink" in addedKind && addedKind.cellLink) {
9370
+ workbook.syncFormControls();
9647
9371
  }
9648
- syncFormControlLinkedCell(
9649
- workbook,
9650
- target.workbookSheetIndex,
9651
- target.worksheet.formControls,
9652
- controlIndex
9653
- );
9654
9372
  maybeRecalculateWorkbook(workbook);
9655
9373
  refreshWorkbookState(workbook);
9656
9374
  return controlIndex;
@@ -9661,35 +9379,52 @@ function useXlsxViewerController(options) {
9661
9379
  if (readOnly || !workbook || !target || !currentControl) {
9662
9380
  return false;
9663
9381
  }
9664
- recordHistoryBeforeMutation();
9382
+ recordHistoryBeforeMutation();
9383
+ const currentInput = formControlInputFromDukeControl(currentControl);
9384
+ if (currentInput.kind !== "formControl" || !currentControl.anchor) {
9385
+ return false;
9386
+ }
9387
+ const nextAnchor = patch.anchor ? xlsxAnchorToDukeDrawingAnchor(
9388
+ patch.anchor,
9389
+ patch.editAs ?? (currentControl.anchor.type === "twoCell" ? currentControl.anchor.editAs : void 0)
9390
+ ) : patch.editAs !== void 0 && currentControl.anchor.type === "twoCell" ? { ...currentControl.anchor, editAs: patch.editAs } : currentControl.anchor;
9391
+ let nextKind = patch.kind ? xlsxFormControlKindToDukeKind(patch.kind) : dukeFormControlKindToInput(currentControl.formControl.kind);
9392
+ if (currentControl.formControl.kind.kind === "unknown" && nextKind.kind === "unknown") {
9393
+ nextKind = {
9394
+ ...nextKind,
9395
+ legacyObjectType: nextKind.legacyObjectType ?? currentControl.formControl.kind.legacyObjectType,
9396
+ rawObj: nextKind.rawObj ?? currentControl.formControl.kind.rawObj,
9397
+ rawProperties: nextKind.rawProperties ?? currentControl.formControl.kind.rawProperties
9398
+ };
9399
+ }
9665
9400
  const nextInput = {
9666
- ...formControlInputFromDukeControl(currentControl),
9667
- ...patch,
9668
- anchor: patch.anchor ? { ...patch.anchor } : { ...currentControl.anchor },
9669
- kind: patch.kind ? { ...patch.kind } : { ...currentControl.kind }
9401
+ altText: "altText" in patch ? patch.altText : currentControl.altText,
9402
+ anchor: nextAnchor,
9403
+ formControl: {
9404
+ ...currentInput.formControl,
9405
+ kind: nextKind,
9406
+ macroName: "macroName" in patch ? patch.macroName : currentControl.formControl.macroName,
9407
+ rawClientData: "rawClientData" in patch ? patch.rawClientData : currentControl.formControl.rawClientData
9408
+ },
9409
+ hidden: "hidden" in patch ? patch.hidden : currentControl.hidden,
9410
+ kind: "formControl",
9411
+ locked: "locked" in patch ? patch.locked : currentControl.locked,
9412
+ name: "name" in patch ? patch.name : currentControl.name,
9413
+ printable: "printable" in patch ? patch.printable : currentControl.printable,
9414
+ title: "title" in patch ? patch.title : currentControl.title
9670
9415
  };
9671
- if (nextInput.kind.kind === "optionButton" && nextInput.kind.state === "checked") {
9672
- const groupIndexes = resolveOptionButtonGroupIndexes(target.worksheet.formControls, controlIndex);
9673
- for (const siblingIndex of groupIndexes) {
9674
- if (siblingIndex === controlIndex) {
9675
- continue;
9676
- }
9677
- const sibling = target.worksheet.formControls[siblingIndex];
9678
- if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9679
- target.worksheet.setFormControl(siblingIndex, {
9680
- ...formControlInputFromDukeControl(sibling),
9681
- kind: { ...sibling.kind, state: "unchecked" }
9682
- });
9683
- }
9684
- }
9416
+ target.worksheet.setDrawing(currentControl.drawingPath, nextInput);
9417
+ const updatedControl = target.worksheet.formControls[controlIndex];
9418
+ const updatedKind = updatedControl?.formControl.kind;
9419
+ const interactionChanged = updatedKind ? formControlInteractionChanged(currentControl.formControl.kind, updatedKind) : false;
9420
+ const groupTopologyChanged = patch.anchor !== void 0 || updatedKind !== void 0 && updatedKind.kind !== currentControl.formControl.kind.kind && (updatedKind.kind === "groupBox" || currentControl.formControl.kind.kind === "groupBox");
9421
+ if (interactionChanged && updatedControl && (updatedKind?.kind === "checkbox" || updatedKind?.kind === "optionButton")) {
9422
+ target.worksheet.setFormControlCheckState(updatedControl.drawingPath, updatedKind.state);
9423
+ } else if (groupTopologyChanged) {
9424
+ reconcileCheckedOptionButtons(target.worksheet);
9425
+ } else if (interactionChanged && updatedKind && "cellLink" in updatedKind && updatedKind.cellLink) {
9426
+ workbook.syncFormControls();
9685
9427
  }
9686
- target.worksheet.setFormControl(controlIndex, nextInput);
9687
- syncFormControlLinkedCell(
9688
- workbook,
9689
- target.workbookSheetIndex,
9690
- target.worksheet.formControls,
9691
- controlIndex
9692
- );
9693
9428
  maybeRecalculateWorkbook(workbook);
9694
9429
  refreshWorkbookState(workbook);
9695
9430
  return true;
@@ -9700,10 +9435,16 @@ function useXlsxViewerController(options) {
9700
9435
  return false;
9701
9436
  }
9702
9437
  recordHistoryBeforeMutation();
9703
- target.worksheet.removeFormControl(controlIndex);
9438
+ const removedControl = target.worksheet.formControls[controlIndex];
9439
+ const removedKind = removedControl.formControl.kind.kind;
9440
+ target.worksheet.removeDrawing(removedControl.drawingPath);
9441
+ if (removedKind === "groupBox" || removedKind === "optionButton") {
9442
+ reconcileCheckedOptionButtons(target.worksheet);
9443
+ maybeRecalculateWorkbook(workbook);
9444
+ }
9704
9445
  refreshWorkbookState(workbook);
9705
9446
  return true;
9706
- }, [activeSheetIndex, getFormControlWorksheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9447
+ }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9707
9448
  const recordCellEditHistory = React.useCallback((cell, before, after) => {
9708
9449
  if (!activeSheet || isApplyingHistoryRef.current) {
9709
9450
  return;
@@ -17898,7 +17639,7 @@ var THUMBNAIL_MAX_COLS = 80;
17898
17639
  var THUMBNAIL_MAX_SOURCE_HEIGHT_PX = 900;
17899
17640
  var THUMBNAIL_MAX_SOURCE_WIDTH_PX = 1440;
17900
17641
  var LIVE_ZOOM_COMMIT_IDLE_MS = 48;
17901
- var WHEEL_ZOOM_SENSITIVITY = 25e-5;
17642
+ var WHEEL_ZOOM_SENSITIVITY = 25e-4;
17902
17643
  var WHEEL_LINE_DELTA_PX = 16;
17903
17644
  var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669", "#ea580c", "#db2777"];
17904
17645
  var CHART_SOURCE_HIGHLIGHT_COLOR_BY_ROLE = {
@@ -18941,6 +18682,7 @@ function resolveAxisStartOffset(actualIndex, actualIndices, sizes, indexByActual
18941
18682
  function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWidths, options) {
18942
18683
  const headerHeight = options?.headerHeight ?? HEADER_HEIGHT;
18943
18684
  const rowHeaderWidth = options?.rowHeaderWidth ?? ROW_HEADER_WIDTH;
18685
+ const anchorZoomFactor = options?.zoomFactor ?? 1;
18944
18686
  const resolveMarkerLeft = (col, colOffsetEmu) => rowHeaderWidth + resolveAxisStartOffset(
18945
18687
  col,
18946
18688
  visibleCols,
@@ -18948,7 +18690,7 @@ function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWi
18948
18690
  options?.colIndexByActual,
18949
18691
  options?.colPrefixSums,
18950
18692
  options?.actualColPrefixSums
18951
- ) + emuToPixels(colOffsetEmu);
18693
+ ) + emuToPixels(colOffsetEmu) * anchorZoomFactor;
18952
18694
  const resolveMarkerTop = (row, rowOffsetEmu) => headerHeight + resolveAxisStartOffset(
18953
18695
  row,
18954
18696
  visibleRows,
@@ -18956,21 +18698,21 @@ function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWi
18956
18698
  options?.rowIndexByActual,
18957
18699
  options?.rowPrefixSums,
18958
18700
  options?.actualRowPrefixSums
18959
- ) + emuToPixels(rowOffsetEmu);
18701
+ ) + emuToPixels(rowOffsetEmu) * anchorZoomFactor;
18960
18702
  if (anchor.kind === "absolute") {
18961
18703
  return {
18962
- height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18963
- left: rowHeaderWidth + emuToPixels(anchor.positionEmu.x),
18964
- top: headerHeight + emuToPixels(anchor.positionEmu.y),
18965
- width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18704
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy) * anchorZoomFactor),
18705
+ left: rowHeaderWidth + emuToPixels(anchor.positionEmu.x) * anchorZoomFactor,
18706
+ top: headerHeight + emuToPixels(anchor.positionEmu.y) * anchorZoomFactor,
18707
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx) * anchorZoomFactor)
18966
18708
  };
18967
18709
  }
18968
18710
  if (anchor.kind === "one-cell") {
18969
18711
  return {
18970
- height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18712
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy) * anchorZoomFactor),
18971
18713
  left: resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu),
18972
18714
  top: resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu),
18973
- width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18715
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx) * anchorZoomFactor)
18974
18716
  };
18975
18717
  }
18976
18718
  const left = resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu);
@@ -20775,6 +20517,40 @@ function getCellBooleanValue(worksheet, row, col) {
20775
20517
  }
20776
20518
  return null;
20777
20519
  }
20520
+ function getBatchedCellNumericValue(cell) {
20521
+ if (!cell) {
20522
+ return null;
20523
+ }
20524
+ const text = cell.value.trim();
20525
+ if (text.length === 0) {
20526
+ return null;
20527
+ }
20528
+ if (text.toLowerCase() === "true") {
20529
+ return 1;
20530
+ }
20531
+ if (text.toLowerCase() === "false") {
20532
+ return 0;
20533
+ }
20534
+ const value = Number(text.replace(/,/g, ""));
20535
+ return Number.isFinite(value) ? value : null;
20536
+ }
20537
+ function getBatchedCellBooleanValue(cell) {
20538
+ if (!cell) {
20539
+ return null;
20540
+ }
20541
+ const text = cell.value.trim().toLowerCase();
20542
+ if (text.length === 0) {
20543
+ return null;
20544
+ }
20545
+ if (text === "true") {
20546
+ return true;
20547
+ }
20548
+ if (text === "false") {
20549
+ return false;
20550
+ }
20551
+ const value = Number(text);
20552
+ return Number.isFinite(value) ? value !== 0 : null;
20553
+ }
20778
20554
  function parseA1RangeReference3(reference) {
20779
20555
  const [startRef, endRef = startRef] = reference.split(":");
20780
20556
  const start = parseA1CellReference3(startRef ?? "");
@@ -22096,7 +21872,6 @@ function buildFormControlKindInput(control) {
22096
21872
  return {
22097
21873
  caption: control.caption ?? control.label ?? "",
22098
21874
  cellLink: control.linkedCell,
22099
- firstInGroup: control.firstInGroup,
22100
21875
  kind: "optionButton",
22101
21876
  no3D: control.no3D ?? false,
22102
21877
  state: control.state === "checked" ? "checked" : "unchecked"
@@ -23028,6 +22803,7 @@ function XlsxGrid({
23028
22803
  onFormControlAction,
23029
22804
  onFormControlChange,
23030
22805
  renderChartLoading,
22806
+ renderFormControl,
23031
22807
  palette,
23032
22808
  renderImage,
23033
22809
  renderImageSelection,
@@ -23141,6 +22917,7 @@ function XlsxGrid({
23141
22917
  const canvasCornerOverlayContentRef = React4.useRef(null);
23142
22918
  const canvasImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
23143
22919
  const selectionOverlayRef = React4.useRef(null);
22920
+ const canvasSelectionLayerRef = React4.useRef(null);
23144
22921
  const activeValidationOverlayRef = React4.useRef(null);
23145
22922
  const fillHandleRef = React4.useRef(null);
23146
22923
  const tableMenuRef = React4.useRef(null);
@@ -23166,9 +22943,11 @@ function XlsxGrid({
23166
22943
  const liveGestureZoomRef = React4.useRef(null);
23167
22944
  const pendingLiveGestureZoomStateRef = React4.useRef(null);
23168
22945
  const liveGestureZoomFrameRef = React4.useRef(null);
22946
+ const liveGestureZoomTransformFrameRef = React4.useRef(null);
23169
22947
  const pendingZoomAnchorRef = React4.useRef(null);
23170
22948
  const liveZoomCommitTimerRef = React4.useRef(null);
23171
22949
  const pendingLiveZoomCommitRef = React4.useRef(null);
22950
+ const gestureScrollerRectRef = React4.useRef(null);
23172
22951
  const touchPinchStateRef = React4.useRef(null);
23173
22952
  const safariPinchStartZoomRef = React4.useRef(null);
23174
22953
  const canvasTransformStyleCacheRef = React4.useRef(/* @__PURE__ */ new WeakMap());
@@ -23252,7 +23031,10 @@ function XlsxGrid({
23252
23031
  const setInteractionMode = React4.useCallback((mode) => {
23253
23032
  interactionModeRef.current = mode;
23254
23033
  }, []);
23255
- const worksheet = getActiveWorksheet();
23034
+ const worksheet = React4.useMemo(
23035
+ () => getActiveWorksheet(),
23036
+ [activeSheetIndex, getActiveWorksheet, revision]
23037
+ );
23256
23038
  const mergedRegions = React4.useMemo(() => {
23257
23039
  const regions = [];
23258
23040
  const rawMergedRegions = Array.isArray(worksheet?.mergedRegions) ? worksheet.mergedRegions : [];
@@ -23332,8 +23114,31 @@ function XlsxGrid({
23332
23114
  const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
23333
23115
  const scrollDeltaX = paintedViewport.left - nextLiveViewport.left;
23334
23116
  const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
23335
- const liveZoomTranslateX2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.x * (1 - liveZoomScale2) : 0;
23336
- const liveZoomTranslateY2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.y * (1 - liveZoomScale2) : 0;
23117
+ const resolveClampedLiveZoomAnchor = (scrollOffset, viewportSize, scrollExtent, gestureAnchor) => {
23118
+ if (!isLiveZooming2 || liveZoomScale2 === 1) {
23119
+ return gestureAnchor;
23120
+ }
23121
+ const targetScrollExtent = Math.max(viewportSize, scrollExtent * liveZoomScale2);
23122
+ const targetScrollOffset = clampValue(
23123
+ (scrollOffset + gestureAnchor) * liveZoomScale2 - gestureAnchor,
23124
+ 0,
23125
+ Math.max(0, targetScrollExtent - viewportSize)
23126
+ );
23127
+ return (scrollOffset * liveZoomScale2 - targetScrollOffset) / (1 - liveZoomScale2);
23128
+ };
23129
+ const scroller = scrollRef.current;
23130
+ const liveZoomAnchorX = resolveClampedLiveZoomAnchor(
23131
+ nextLiveViewport.left,
23132
+ nextLiveViewport.width,
23133
+ scroller?.scrollWidth ?? nextLiveViewport.width,
23134
+ currentLiveGestureZoom?.anchor.x ?? 0
23135
+ );
23136
+ const liveZoomAnchorY = resolveClampedLiveZoomAnchor(
23137
+ nextLiveViewport.top,
23138
+ nextLiveViewport.height,
23139
+ scroller?.scrollHeight ?? nextLiveViewport.height,
23140
+ currentLiveGestureZoom?.anchor.y ?? 0
23141
+ );
23337
23142
  const applyElementTransform = (element, transform, willChange, transformOrigin = null) => {
23338
23143
  if (!element) {
23339
23144
  return;
@@ -23354,7 +23159,10 @@ function XlsxGrid({
23354
23159
  willChange
23355
23160
  });
23356
23161
  };
23357
- const applyCanvasTransform = (canvas, translateX, translateY) => {
23162
+ const resolveLiveTranslation = (baseTranslation, anchor, origin) => baseTranslation * liveZoomScale2 + (anchor - origin) * (1 - liveZoomScale2);
23163
+ const applyCanvasTransform = (canvas, baseTranslateX, baseTranslateY, originX, originY, anchorX, anchorY) => {
23164
+ const translateX = resolveLiveTranslation(baseTranslateX, anchorX, originX);
23165
+ const translateY = resolveLiveTranslation(baseTranslateY, anchorY, originY);
23358
23166
  const shouldTransform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1;
23359
23167
  applyElementTransform(
23360
23168
  canvas,
@@ -23362,85 +23170,169 @@ function XlsxGrid({
23362
23170
  shouldTransform ? "transform" : ""
23363
23171
  );
23364
23172
  };
23365
- const applyOverlayTransform = (element, translateX, translateY) => {
23173
+ const applyOverlayTransform = (element, baseTranslateX, baseTranslateY, paneLeft, paneTop, anchorX, anchorY) => {
23174
+ const translateX = resolveLiveTranslation(baseTranslateX, anchorX, paneLeft);
23175
+ const translateY = resolveLiveTranslation(baseTranslateY, anchorY, paneTop);
23366
23176
  applyElementTransform(
23367
23177
  element,
23368
- liveZoomScale2 !== 1 ? `translate3d(${translateX + liveZoomTranslateX2}px, ${translateY + liveZoomTranslateY2}px, 0) scale(${liveZoomScale2})` : `translate3d(${translateX}px, ${translateY}px, 0)`,
23178
+ liveZoomScale2 !== 1 ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : `translate3d(${baseTranslateX}px, ${baseTranslateY}px, 0)`,
23369
23179
  "transform",
23370
23180
  "0 0"
23371
23181
  );
23372
23182
  };
23183
+ const liveScrollBodyViewportWidth = Math.max(0, nextLiveViewport.width - liveFrozenPaneRight);
23184
+ const liveScrollBodyViewportHeight = Math.max(0, nextLiveViewport.height - liveFrozenPaneBottom);
23185
+ const liveScrollBodyCanvasLeft = liveFrozenPaneRight - Math.min(CANVAS_SCROLL_BUFFER_PX, liveScrollBodyViewportWidth);
23186
+ const liveScrollBodyCanvasTop = liveFrozenPaneBottom - Math.min(CANVAS_SCROLL_BUFFER_PX, liveScrollBodyViewportHeight);
23187
+ const selectionLayerTranslateX = (nextLiveViewport.left + liveZoomAnchorX) * (1 - liveZoomScale2);
23188
+ const selectionLayerTranslateY = (nextLiveViewport.top + liveZoomAnchorY) * (1 - liveZoomScale2);
23189
+ const shouldTransformSelectionLayer = experimentalCanvas && isLiveZooming2 && liveZoomScale2 !== 1;
23190
+ applyElementTransform(
23191
+ canvasSelectionLayerRef.current,
23192
+ shouldTransformSelectionLayer ? `translate3d(${selectionLayerTranslateX}px, ${selectionLayerTranslateY}px, 0) scale(${liveZoomScale2})` : "",
23193
+ shouldTransformSelectionLayer ? "transform" : "",
23194
+ "0 0"
23195
+ );
23373
23196
  applyCanvasTransform(
23374
23197
  scrollBodyCanvasRef.current,
23375
- scrollDeltaX + liveZoomTranslateX2,
23376
- scrollDeltaY + liveZoomTranslateY2
23198
+ scrollDeltaX,
23199
+ scrollDeltaY,
23200
+ liveScrollBodyCanvasLeft,
23201
+ liveScrollBodyCanvasTop,
23202
+ liveZoomAnchorX,
23203
+ liveZoomAnchorY
23377
23204
  );
23378
23205
  applyCanvasTransform(
23379
23206
  topBodyCanvasRef.current,
23380
- scrollDeltaX + liveZoomTranslateX2,
23381
- liveZoomTranslateY2
23207
+ scrollDeltaX,
23208
+ 0,
23209
+ liveScrollBodyCanvasLeft,
23210
+ liveDisplayHeaderHeight,
23211
+ liveZoomAnchorX,
23212
+ 0
23382
23213
  );
23383
23214
  applyCanvasTransform(
23384
23215
  leftBodyCanvasRef.current,
23385
- liveZoomTranslateX2,
23386
- scrollDeltaY + liveZoomTranslateY2
23216
+ 0,
23217
+ scrollDeltaY,
23218
+ liveDisplayRowHeaderWidth,
23219
+ liveScrollBodyCanvasTop,
23220
+ 0,
23221
+ liveZoomAnchorY
23387
23222
  );
23388
23223
  applyCanvasTransform(
23389
23224
  cornerBodyCanvasRef.current,
23390
- liveZoomTranslateX2,
23391
- liveZoomTranslateY2
23225
+ 0,
23226
+ 0,
23227
+ liveDisplayRowHeaderWidth,
23228
+ liveDisplayHeaderHeight,
23229
+ 0,
23230
+ 0
23392
23231
  );
23393
23232
  applyCanvasTransform(
23394
23233
  topScrollHeaderCanvasRef.current,
23395
- scrollDeltaX + liveZoomTranslateX2,
23396
- liveZoomTranslateY2
23234
+ scrollDeltaX,
23235
+ 0,
23236
+ liveScrollBodyCanvasLeft,
23237
+ 0,
23238
+ liveZoomAnchorX,
23239
+ 0
23397
23240
  );
23398
23241
  applyCanvasTransform(
23399
23242
  topFrozenHeaderCanvasRef.current,
23400
- liveZoomTranslateX2,
23401
- liveZoomTranslateY2
23243
+ 0,
23244
+ 0,
23245
+ liveDisplayRowHeaderWidth,
23246
+ 0,
23247
+ 0,
23248
+ 0
23402
23249
  );
23403
23250
  applyCanvasTransform(
23404
23251
  leftScrollHeaderCanvasRef.current,
23405
- liveZoomTranslateX2,
23406
- scrollDeltaY + liveZoomTranslateY2
23252
+ 0,
23253
+ scrollDeltaY,
23254
+ 0,
23255
+ liveScrollBodyCanvasTop,
23256
+ 0,
23257
+ liveZoomAnchorY
23407
23258
  );
23408
23259
  applyCanvasTransform(
23409
23260
  leftFrozenHeaderCanvasRef.current,
23410
- liveZoomTranslateX2,
23411
- liveZoomTranslateY2
23261
+ 0,
23262
+ 0,
23263
+ 0,
23264
+ liveDisplayHeaderHeight,
23265
+ 0,
23266
+ 0
23412
23267
  );
23413
23268
  applyCanvasTransform(
23414
23269
  cornerHeaderCanvasRef.current,
23415
- liveZoomTranslateX2,
23416
- liveZoomTranslateY2
23270
+ 0,
23271
+ 0,
23272
+ 0,
23273
+ 0,
23274
+ 0,
23275
+ 0
23417
23276
  );
23418
23277
  applyOverlayTransform(
23419
23278
  canvasScrollOverlayContentRef.current,
23420
23279
  -nextLiveViewport.left - liveFrozenPaneRight,
23421
- -nextLiveViewport.top - liveFrozenPaneBottom
23280
+ -nextLiveViewport.top - liveFrozenPaneBottom,
23281
+ liveFrozenPaneRight,
23282
+ liveFrozenPaneBottom,
23283
+ liveZoomAnchorX,
23284
+ liveZoomAnchorY
23422
23285
  );
23423
23286
  applyOverlayTransform(
23424
23287
  canvasTopOverlayContentRef.current,
23425
23288
  -nextLiveViewport.left - liveFrozenPaneRight,
23426
- -liveDisplayHeaderHeight
23289
+ -liveDisplayHeaderHeight,
23290
+ liveFrozenPaneRight,
23291
+ liveDisplayHeaderHeight,
23292
+ liveZoomAnchorX,
23293
+ 0
23427
23294
  );
23428
23295
  applyOverlayTransform(
23429
23296
  canvasLeftOverlayContentRef.current,
23430
23297
  -liveDisplayRowHeaderWidth,
23431
- -nextLiveViewport.top - liveFrozenPaneBottom
23298
+ -nextLiveViewport.top - liveFrozenPaneBottom,
23299
+ liveDisplayRowHeaderWidth,
23300
+ liveFrozenPaneBottom,
23301
+ 0,
23302
+ liveZoomAnchorY
23432
23303
  );
23433
23304
  applyOverlayTransform(
23434
23305
  canvasCornerOverlayContentRef.current,
23435
23306
  -liveDisplayRowHeaderWidth,
23436
- -liveDisplayHeaderHeight
23307
+ -liveDisplayHeaderHeight,
23308
+ liveDisplayRowHeaderWidth,
23309
+ liveDisplayHeaderHeight,
23310
+ 0,
23311
+ 0
23437
23312
  );
23438
- }, [zoomScale]);
23313
+ }, [experimentalCanvas, zoomScale]);
23439
23314
  const updateLiveGestureZoomState = React4.useCallback((nextState) => {
23440
- const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
23315
+ const previousState = liveGestureZoomRef.current;
23316
+ const resolvedState = typeof nextState === "function" ? nextState(previousState) : nextState;
23441
23317
  liveGestureZoomRef.current = resolvedState;
23318
+ const shouldApplyTransformImmediately = !experimentalCanvas || previousState === null || resolvedState === null;
23319
+ if (shouldApplyTransformImmediately) {
23320
+ if (liveGestureZoomTransformFrameRef.current !== null) {
23321
+ window.cancelAnimationFrame(liveGestureZoomTransformFrameRef.current);
23322
+ liveGestureZoomTransformFrameRef.current = null;
23323
+ }
23324
+ applyCanvasViewportCompensation();
23325
+ } else if (liveGestureZoomTransformFrameRef.current === null) {
23326
+ liveGestureZoomTransformFrameRef.current = window.requestAnimationFrame(() => {
23327
+ liveGestureZoomTransformFrameRef.current = null;
23328
+ applyCanvasViewportCompensation();
23329
+ });
23330
+ }
23331
+ const shouldSyncReactState = !experimentalCanvas || previousState === null !== (resolvedState === null);
23332
+ if (!shouldSyncReactState) {
23333
+ return;
23334
+ }
23442
23335
  pendingLiveGestureZoomStateRef.current = resolvedState;
23443
- applyCanvasViewportCompensation();
23444
23336
  if (liveGestureZoomFrameRef.current !== null) {
23445
23337
  return;
23446
23338
  }
@@ -23450,7 +23342,7 @@ function XlsxGrid({
23450
23342
  pendingLiveGestureZoomStateRef.current = null;
23451
23343
  setLiveGestureZoom(pendingState);
23452
23344
  });
23453
- }, [applyCanvasViewportCompensation]);
23345
+ }, [applyCanvasViewportCompensation, experimentalCanvas]);
23454
23346
  const clearLiveZoomCommitTimer = React4.useCallback(() => {
23455
23347
  if (liveZoomCommitTimerRef.current !== null) {
23456
23348
  window.clearTimeout(liveZoomCommitTimerRef.current);
@@ -23463,6 +23355,7 @@ function XlsxGrid({
23463
23355
  pendingZoomAnchorRef.current = null;
23464
23356
  touchPinchStateRef.current = null;
23465
23357
  safariPinchStartZoomRef.current = null;
23358
+ gestureScrollerRectRef.current = null;
23466
23359
  gestureZoomScaleRef.current = committedZoomScaleRef.current;
23467
23360
  updateLiveGestureZoomState(null);
23468
23361
  }, [clearLiveZoomCommitTimer, updateLiveGestureZoomState]);
@@ -24077,6 +23970,10 @@ function XlsxGrid({
24077
23970
  window.cancelAnimationFrame(liveGestureZoomFrameRef.current);
24078
23971
  liveGestureZoomFrameRef.current = null;
24079
23972
  }
23973
+ if (liveGestureZoomTransformFrameRef.current !== null) {
23974
+ window.cancelAnimationFrame(liveGestureZoomTransformFrameRef.current);
23975
+ liveGestureZoomTransformFrameRef.current = null;
23976
+ }
24080
23977
  if (drawingViewportFrameRef.current !== null) {
24081
23978
  window.cancelAnimationFrame(drawingViewportFrameRef.current);
24082
23979
  drawingViewportFrameRef.current = null;
@@ -24340,6 +24237,7 @@ function XlsxGrid({
24340
24237
  const handleScrollerScroll = React4.useCallback((event) => {
24341
24238
  const currentScroller = event.currentTarget;
24342
24239
  cachedScrollerRectRef.current = null;
24240
+ gestureScrollerRectRef.current = null;
24343
24241
  const paintedViewport = paintedDrawingViewportRef.current;
24344
24242
  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;
24345
24243
  syncDrawingViewport(currentScroller, { immediate: shouldSyncDrawingViewportImmediately });
@@ -24374,7 +24272,9 @@ function XlsxGrid({
24374
24272
  return;
24375
24273
  }
24376
24274
  event.preventDefault();
24377
- const anchor = resolveEventAnchor(event.clientX, event.clientY, scroller.getBoundingClientRect());
24275
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24276
+ gestureScrollerRectRef.current = scrollerRect;
24277
+ const anchor = resolveEventAnchor(event.clientX, event.clientY, scrollerRect);
24378
24278
  updateLiveGestureZoomTarget(
24379
24279
  gestureZoomScaleRef.current * Math.exp(-delta * WHEEL_ZOOM_SENSITIVITY),
24380
24280
  anchor
@@ -24394,10 +24294,12 @@ function XlsxGrid({
24394
24294
  return;
24395
24295
  }
24396
24296
  event.preventDefault();
24297
+ const scrollerRect = scroller.getBoundingClientRect();
24298
+ gestureScrollerRectRef.current = scrollerRect;
24397
24299
  const anchor = resolveEventAnchor(
24398
24300
  (firstTouch.clientX + secondTouch.clientX) / 2,
24399
24301
  (firstTouch.clientY + secondTouch.clientY) / 2,
24400
- scroller.getBoundingClientRect()
24302
+ scrollerRect
24401
24303
  );
24402
24304
  clearLiveZoomCommitTimer();
24403
24305
  updateLiveGestureZoomState({
@@ -24425,10 +24327,12 @@ function XlsxGrid({
24425
24327
  return;
24426
24328
  }
24427
24329
  event.preventDefault();
24330
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24331
+ gestureScrollerRectRef.current = scrollerRect;
24428
24332
  const anchor = resolveEventAnchor(
24429
24333
  (firstTouch.clientX + secondTouch.clientX) / 2,
24430
24334
  (firstTouch.clientY + secondTouch.clientY) / 2,
24431
- scroller.getBoundingClientRect()
24335
+ scrollerRect
24432
24336
  );
24433
24337
  updateLiveGestureZoomTarget(
24434
24338
  pinchState.startZoomScale * distance / pinchState.startDistance,
@@ -24449,10 +24353,12 @@ function XlsxGrid({
24449
24353
  const handleGestureStart = (event) => {
24450
24354
  const gestureEvent = event;
24451
24355
  event.preventDefault();
24356
+ const scrollerRect = scroller.getBoundingClientRect();
24357
+ gestureScrollerRectRef.current = scrollerRect;
24452
24358
  const anchor = resolveEventAnchor(
24453
24359
  gestureEvent.clientX,
24454
24360
  gestureEvent.clientY,
24455
- scroller.getBoundingClientRect()
24361
+ scrollerRect
24456
24362
  );
24457
24363
  clearLiveZoomCommitTimer();
24458
24364
  safariPinchStartZoomRef.current = committedZoomScaleRef.current;
@@ -24470,10 +24376,12 @@ function XlsxGrid({
24470
24376
  return;
24471
24377
  }
24472
24378
  event.preventDefault();
24379
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24380
+ gestureScrollerRectRef.current = scrollerRect;
24473
24381
  const anchor = resolveEventAnchor(
24474
24382
  gestureEvent.clientX,
24475
24383
  gestureEvent.clientY,
24476
- scroller.getBoundingClientRect()
24384
+ scrollerRect
24477
24385
  );
24478
24386
  updateLiveGestureZoomTarget(startZoomScale * gestureEvent.scale, anchor);
24479
24387
  };
@@ -25467,7 +25375,8 @@ function XlsxGrid({
25467
25375
  headerHeight: displayHeaderHeight,
25468
25376
  rowIndexByActual,
25469
25377
  rowHeaderWidth: displayRowHeaderWidth,
25470
- rowPrefixSums
25378
+ rowPrefixSums,
25379
+ zoomFactor
25471
25380
  })
25472
25381
  })) : [],
25473
25382
  [
@@ -25485,7 +25394,8 @@ function XlsxGrid({
25485
25394
  rowPrefixSums,
25486
25395
  showImages,
25487
25396
  visibleCols,
25488
- visibleRows
25397
+ visibleRows,
25398
+ zoomFactor
25489
25399
  ]
25490
25400
  );
25491
25401
  const shapeRects = React4.useMemo(
@@ -25498,7 +25408,8 @@ function XlsxGrid({
25498
25408
  headerHeight: displayHeaderHeight,
25499
25409
  rowIndexByActual,
25500
25410
  rowHeaderWidth: displayRowHeaderWidth,
25501
- rowPrefixSums
25411
+ rowPrefixSums,
25412
+ zoomFactor
25502
25413
  }),
25503
25414
  shape
25504
25415
  })) : [],
@@ -25516,7 +25427,8 @@ function XlsxGrid({
25516
25427
  shapes,
25517
25428
  showImages,
25518
25429
  visibleCols,
25519
- visibleRows
25430
+ visibleRows,
25431
+ zoomFactor
25520
25432
  ]
25521
25433
  );
25522
25434
  const formControlRects = React4.useMemo(
@@ -25530,7 +25442,8 @@ function XlsxGrid({
25530
25442
  headerHeight: displayHeaderHeight,
25531
25443
  rowIndexByActual,
25532
25444
  rowHeaderWidth: displayRowHeaderWidth,
25533
- rowPrefixSums
25445
+ rowPrefixSums,
25446
+ zoomFactor
25534
25447
  })
25535
25448
  })) : [],
25536
25449
  [
@@ -25547,7 +25460,8 @@ function XlsxGrid({
25547
25460
  rowPrefixSums,
25548
25461
  showImages,
25549
25462
  visibleCols,
25550
- visibleRows
25463
+ visibleRows,
25464
+ zoomFactor
25551
25465
  ]
25552
25466
  );
25553
25467
  const chartRects = React4.useMemo(
@@ -25561,7 +25475,8 @@ function XlsxGrid({
25561
25475
  headerHeight: displayHeaderHeight,
25562
25476
  rowIndexByActual,
25563
25477
  rowHeaderWidth: displayRowHeaderWidth,
25564
- rowPrefixSums
25478
+ rowPrefixSums,
25479
+ zoomFactor
25565
25480
  })
25566
25481
  })) : [],
25567
25482
  [
@@ -25579,7 +25494,8 @@ function XlsxGrid({
25579
25494
  rowPrefixSums,
25580
25495
  showImages,
25581
25496
  visibleCols,
25582
- visibleRows
25497
+ visibleRows,
25498
+ zoomFactor
25583
25499
  ]
25584
25500
  );
25585
25501
  const shouldBakeCanvasStaticDrawings = Boolean(experimentalCanvas && readOnly && showImages);
@@ -25592,14 +25508,7 @@ function XlsxGrid({
25592
25508
  () => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => shape.hyperlink) : shapeRects,
25593
25509
  [shapeRects, shouldBakeCanvasStaticDrawings]
25594
25510
  );
25595
- const bakedFormControlRects = React4.useMemo(
25596
- () => shouldBakeCanvasStaticDrawings ? formControlRects : [],
25597
- [formControlRects, shouldBakeCanvasStaticDrawings]
25598
- );
25599
- const domFormControlRects = React4.useMemo(
25600
- () => shouldBakeCanvasStaticDrawings ? [] : formControlRects,
25601
- [formControlRects, shouldBakeCanvasStaticDrawings]
25602
- );
25511
+ const domFormControlRects = formControlRects;
25603
25512
  const bakedImageRects = React4.useMemo(
25604
25513
  () => shouldBakeCanvasImages ? imageRects.filter(({ image }) => !image.hyperlink && selectedImageId !== image.id) : [],
25605
25514
  [imageRects, selectedImageId, shouldBakeCanvasImages]
@@ -25616,11 +25525,9 @@ function XlsxGrid({
25616
25525
  return "";
25617
25526
  }
25618
25527
  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("|");
25619
- 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("|");
25620
25528
  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("|");
25621
- return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${controlSignature}:${imageSignature}`;
25529
+ return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${imageSignature}`;
25622
25530
  }, [
25623
- bakedFormControlRects,
25624
25531
  bakedImageRects,
25625
25532
  bakedShapeRects,
25626
25533
  canvasImageLoadVersion,
@@ -27044,6 +26951,8 @@ function XlsxGrid({
27044
26951
  palette,
27045
26952
  rowHeaderWidth,
27046
26953
  rowSignature: buildRenderedAxisSignature(canvasVisibleRowItems, (item) => item.index),
26954
+ viewportLeft: drawingViewport.left,
26955
+ viewportTop: drawingViewport.top,
27047
26956
  zoomFactor
27048
26957
  };
27049
26958
  const nextHeaderCanvasSignature = {
@@ -27063,7 +26972,7 @@ function XlsxGrid({
27063
26972
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
27064
26973
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
27065
26974
  const previousPaintedViewport = paintedDrawingViewportRef.current;
27066
- 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);
26975
+ 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);
27067
26976
  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);
27068
26977
  const canBlitBody = Boolean(
27069
26978
  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
@@ -27155,12 +27064,6 @@ function XlsxGrid({
27155
27064
  shape,
27156
27065
  zIndex: shape.zIndex
27157
27066
  })),
27158
- ...bakedFormControlRects.map(({ control, rect }) => ({
27159
- control,
27160
- kind: "formControl",
27161
- rect,
27162
- zIndex: control.zIndex
27163
- })),
27164
27067
  ...bakedImageRects.map(({ image, rect }) => ({
27165
27068
  image,
27166
27069
  kind: "image",
@@ -27311,97 +27214,6 @@ function XlsxGrid({
27311
27214
  drawBakedShapeText(context, shape, localLeft, localTop, rect.width, rect.height);
27312
27215
  context.restore();
27313
27216
  };
27314
- const drawBakedFormControl = (context, control, rect) => {
27315
- const label = resolveFormControlLabel(control);
27316
- const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
27317
- const textColor = control.textColor ?? "#000000";
27318
- const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
27319
- const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
27320
- const controlChecked = isFormControlChecked(control);
27321
- const controlMixed = isFormControlMixed(control);
27322
- context.save();
27323
- context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
27324
- context.textBaseline = "middle";
27325
- context.fillStyle = textColor;
27326
- context.strokeStyle = stroke;
27327
- context.lineWidth = Math.max(1, zoomFactor);
27328
- if (control.kind === "group-box") {
27329
- const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;
27330
- drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);
27331
- context.stroke();
27332
- if (label) {
27333
- context.fillStyle = SHEET_SURFACE;
27334
- const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);
27335
- context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);
27336
- context.fillStyle = textColor;
27337
- context.textAlign = "left";
27338
- context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);
27339
- }
27340
- context.restore();
27341
- return;
27342
- }
27343
- if (control.kind === "button" || control.kind === "dropdown" || control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
27344
- if (control.kind === "button") {
27345
- const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);
27346
- gradient.addColorStop(0, "#f8fafc");
27347
- gradient.addColorStop(1, "#e2e8f0");
27348
- context.fillStyle = gradient;
27349
- } else {
27350
- context.fillStyle = "transparent";
27351
- }
27352
- drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === "button" ? 4 * zoomFactor : 2 * zoomFactor);
27353
- if (control.kind === "button") {
27354
- context.fill();
27355
- }
27356
- context.stroke();
27357
- }
27358
- let textLeft = rect.left + 2 * zoomFactor;
27359
- const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
27360
- if (control.kind === "checkbox") {
27361
- context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
27362
- if (controlChecked || controlMixed) {
27363
- context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
27364
- 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));
27365
- context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
27366
- context.beginPath();
27367
- if (controlMixed) {
27368
- context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.25, rect.top + rect.height / 2);
27369
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.75, rect.top + rect.height / 2);
27370
- } else {
27371
- context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
27372
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
27373
- context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
27374
- }
27375
- context.stroke();
27376
- }
27377
- textLeft += iconSize + 4 * zoomFactor;
27378
- } else if (control.kind === "radio") {
27379
- context.beginPath();
27380
- context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
27381
- context.stroke();
27382
- if (controlChecked) {
27383
- context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
27384
- context.beginPath();
27385
- context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
27386
- context.fill();
27387
- }
27388
- textLeft += iconSize + 4 * zoomFactor;
27389
- }
27390
- if (label) {
27391
- const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);
27392
- const text = truncateCanvasText(context, label, maxTextWidth);
27393
- context.fillStyle = textColor;
27394
- context.textAlign = control.textAlign === "right" ? "right" : control.textAlign === "center" ? "center" : "left";
27395
- const textX = context.textAlign === "right" ? rect.left + rect.width - textRightInset : context.textAlign === "center" ? textLeft + maxTextWidth / 2 : textLeft;
27396
- context.fillText(text, textX, rect.top + rect.height / 2);
27397
- }
27398
- if (control.kind === "dropdown") {
27399
- context.fillStyle = textColor;
27400
- context.textAlign = "center";
27401
- context.fillText("\u25BC", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);
27402
- }
27403
- context.restore();
27404
- };
27405
27217
  const drawBakedCanvasDrawings = (pane, context, dirtyRects) => {
27406
27218
  if (bakedCanvasDrawingEntries.length === 0 || dirtyRects.length === 0) {
27407
27219
  return;
@@ -27416,8 +27228,6 @@ function XlsxGrid({
27416
27228
  }
27417
27229
  if (entry.kind === "shape") {
27418
27230
  drawBakedShape(context, entry.shape, localRect);
27419
- } else if (entry.kind === "formControl") {
27420
- drawBakedFormControl(context, entry.control, localRect);
27421
27231
  } else {
27422
27232
  const imageElement = getCanvasImage(entry.image);
27423
27233
  if (imageElement) {
@@ -28111,7 +27921,6 @@ function XlsxGrid({
28111
27921
  activeSheet,
28112
27922
  applyCanvasViewportCompensation,
28113
27923
  bakedCanvasDrawingSignature,
28114
- bakedFormControlRects,
28115
27924
  bakedImageRects,
28116
27925
  bakedShapeRects,
28117
27926
  canvasColumnHeaderCells,
@@ -28390,7 +28199,8 @@ function XlsxGrid({
28390
28199
  const headerLabelLiveScale = isLiveZooming ? liveZoomScale : 1;
28391
28200
  const selectionBorderWidth = Math.max(1, zoomFactor);
28392
28201
  const shouldAnimateCanvasSelection = experimentalCanvas && enableCanvasSelectionAnimation;
28393
- 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";
28202
+ const shouldAnimateCanvasSelectionGeometry = shouldAnimateCanvasSelection && liveGestureZoom === null;
28203
+ 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";
28394
28204
  const rowColSpan = renderedCols.length + 1 + (leadingColumnSpacerWidth > 0 ? 1 : 0) + (trailingColumnSpacerWidth > 0 ? 1 : 0);
28395
28205
  const gutterSeparatorShadow = `inset -1px 0 0 ${palette.border}, inset 0 -1px 0 ${palette.border}`;
28396
28206
  const maxDrawingOverlayZIndex = Math.max(
@@ -28730,8 +28540,8 @@ function XlsxGrid({
28730
28540
  event.stopPropagation();
28731
28541
  };
28732
28542
  const commitControlKind = (kind, nextControl) => {
28733
- if (control.controlIndex === void 0) {
28734
- return;
28543
+ if (readOnly || control.controlIndex === void 0) {
28544
+ return false;
28735
28545
  }
28736
28546
  const didUpdate = updateFormControl(control.controlIndex, { kind }, control.sheetIndex);
28737
28547
  if (didUpdate) {
@@ -28741,7 +28551,77 @@ function XlsxGrid({
28741
28551
  type: "change"
28742
28552
  });
28743
28553
  }
28554
+ return didUpdate;
28555
+ };
28556
+ const setControlState = (state = "unchecked") => {
28557
+ const kind = buildFormControlKindInput(control);
28558
+ if (kind?.kind === "checkbox") {
28559
+ return commitControlKind(
28560
+ { ...kind, state },
28561
+ { checked: state === "checked", state }
28562
+ );
28563
+ }
28564
+ if (kind?.kind === "optionButton" && state !== "mixed") {
28565
+ return commitControlKind(
28566
+ { ...kind, state },
28567
+ { checked: state === "checked", state }
28568
+ );
28569
+ }
28570
+ return false;
28571
+ };
28572
+ const setControlSelected = (selected) => {
28573
+ const kind = buildFormControlKindInput(control);
28574
+ if (kind?.kind === "dropdown") {
28575
+ const nextSelected = typeof selected === "number" ? selected : void 0;
28576
+ return commitControlKind(
28577
+ { ...kind, selected: nextSelected },
28578
+ { selected: nextSelected }
28579
+ );
28580
+ }
28581
+ if (kind?.kind === "listBox") {
28582
+ const nextSelected = Array.isArray(selected) ? selected : typeof selected === "number" ? [selected] : [];
28583
+ return commitControlKind(
28584
+ { ...kind, selected: nextSelected },
28585
+ { selected: nextSelected }
28586
+ );
28587
+ }
28588
+ return false;
28589
+ };
28590
+ const setControlValue = (value) => {
28591
+ if (!Number.isFinite(value)) {
28592
+ return false;
28593
+ }
28594
+ const kind = buildFormControlKindInput(control);
28595
+ if (kind?.kind !== "scrollbar" && kind?.kind !== "spinner") {
28596
+ return false;
28597
+ }
28598
+ const nextValue = Math.max(kind.min, Math.min(kind.max, value));
28599
+ return commitControlKind({ ...kind, value: nextValue }, { value: nextValue });
28600
+ };
28601
+ const activateControl = () => {
28602
+ if (!readOnly && control.kind === "button") {
28603
+ onFormControlAction?.({ control, type: "activate" });
28604
+ }
28744
28605
  };
28606
+ if (renderFormControl) {
28607
+ return /* @__PURE__ */ jsx3(React4.Fragment, { children: renderFormControl({
28608
+ activate: activateControl,
28609
+ checked: isFormControlChecked(control),
28610
+ control,
28611
+ disabled: !isInteractiveControl,
28612
+ items: controlItems,
28613
+ label: controlLabel,
28614
+ mixed: isFormControlMixed(control),
28615
+ readOnly,
28616
+ rect,
28617
+ setSelected: setControlSelected,
28618
+ setState: setControlState,
28619
+ setValue: setControlValue,
28620
+ stopPropagation: stopControlPointerEvent,
28621
+ style: commonStyle,
28622
+ zoomFactor
28623
+ }) }, `${pane}-${control.id}`);
28624
+ }
28745
28625
  if (control.kind === "group-box") {
28746
28626
  const hasLabel = controlLabel.length > 0;
28747
28627
  return /* @__PURE__ */ jsx3(
@@ -28785,19 +28665,16 @@ function XlsxGrid({
28785
28665
  }
28786
28666
  if (control.kind === "radio") {
28787
28667
  const checked = isFormControlChecked(control);
28788
- const kind = buildFormControlKindInput(control);
28789
28668
  return /* @__PURE__ */ jsxs3(
28790
28669
  "button",
28791
28670
  {
28671
+ "aria-disabled": readOnly || control.controlIndex === void 0,
28792
28672
  "aria-checked": checked,
28793
- disabled: readOnly || control.controlIndex === void 0,
28673
+ disabled: !readOnly && control.controlIndex === void 0,
28794
28674
  onClick: (event) => {
28795
28675
  event.stopPropagation();
28796
- if (!checked && kind?.kind === "optionButton") {
28797
- commitControlKind(
28798
- { ...kind, state: "checked" },
28799
- { checked: true, state: "checked" }
28800
- );
28676
+ if (!checked) {
28677
+ setControlState("checked");
28801
28678
  }
28802
28679
  },
28803
28680
  onPointerDown: stopControlPointerEvent,
@@ -28809,6 +28686,7 @@ function XlsxGrid({
28809
28686
  boxSizing: "border-box",
28810
28687
  padding: `0 ${Math.max(1, zoomFactor)}px`
28811
28688
  },
28689
+ tabIndex: readOnly ? -1 : void 0,
28812
28690
  type: "button",
28813
28691
  children: [
28814
28692
  renderRadioControl(checked, palette, zoomFactor),
@@ -28821,21 +28699,16 @@ function XlsxGrid({
28821
28699
  if (control.kind === "checkbox") {
28822
28700
  const checked = isFormControlChecked(control);
28823
28701
  const mixed = isFormControlMixed(control);
28824
- const kind = buildFormControlKindInput(control);
28825
28702
  return /* @__PURE__ */ jsxs3(
28826
28703
  "button",
28827
28704
  {
28705
+ "aria-disabled": readOnly || control.controlIndex === void 0,
28828
28706
  "aria-checked": mixed ? "mixed" : checked,
28829
- disabled: readOnly || control.controlIndex === void 0,
28707
+ disabled: !readOnly && control.controlIndex === void 0,
28830
28708
  onClick: (event) => {
28831
28709
  event.stopPropagation();
28832
- if (kind?.kind === "checkbox") {
28833
- const nextState = checked && !mixed ? "unchecked" : "checked";
28834
- commitControlKind(
28835
- { ...kind, state: nextState },
28836
- { checked: nextState === "checked", state: nextState }
28837
- );
28838
- }
28710
+ const nextState = checked && !mixed ? "unchecked" : "checked";
28711
+ setControlState(nextState);
28839
28712
  },
28840
28713
  onPointerDown: stopControlPointerEvent,
28841
28714
  role: "checkbox",
@@ -28846,6 +28719,7 @@ function XlsxGrid({
28846
28719
  boxSizing: "border-box",
28847
28720
  padding: `0 ${Math.max(1, zoomFactor)}px`
28848
28721
  },
28722
+ tabIndex: readOnly ? -1 : void 0,
28849
28723
  type: "button",
28850
28724
  children: [
28851
28725
  renderCheckboxControl(checked, palette, zoomFactor, mixed),
@@ -28859,10 +28733,11 @@ function XlsxGrid({
28859
28733
  return /* @__PURE__ */ jsx3(
28860
28734
  "button",
28861
28735
  {
28862
- disabled: readOnly || !onFormControlAction,
28736
+ "aria-disabled": readOnly || !onFormControlAction,
28737
+ disabled: !readOnly && !onFormControlAction,
28863
28738
  onClick: (event) => {
28864
28739
  event.stopPropagation();
28865
- onFormControlAction?.({ control, type: "activate" });
28740
+ activateControl();
28866
28741
  },
28867
28742
  onPointerDown: stopControlPointerEvent,
28868
28743
  style: {
@@ -28875,6 +28750,7 @@ function XlsxGrid({
28875
28750
  padding: `0 ${6 * zoomFactor}px`,
28876
28751
  textAlign: "center"
28877
28752
  },
28753
+ tabIndex: readOnly ? -1 : void 0,
28878
28754
  type: "button",
28879
28755
  children: controlLabel
28880
28756
  },
@@ -28882,22 +28758,17 @@ function XlsxGrid({
28882
28758
  );
28883
28759
  }
28884
28760
  if (control.kind === "dropdown") {
28885
- const kind = buildFormControlKindInput(control);
28886
28761
  const selected = typeof control.selected === "number" ? control.selected : void 0;
28887
28762
  return /* @__PURE__ */ jsxs3(
28888
28763
  "select",
28889
28764
  {
28765
+ "aria-disabled": readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28890
28766
  "aria-label": controlLabel || control.name || "Dropdown form control",
28891
- disabled: readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28767
+ disabled: !readOnly && (control.controlIndex === void 0 || controlItems.length === 0),
28892
28768
  onChange: (event) => {
28893
28769
  event.stopPropagation();
28894
- if (kind?.kind === "dropdown") {
28895
- const nextSelected = event.currentTarget.value === "" ? void 0 : Number(event.currentTarget.value);
28896
- commitControlKind(
28897
- { ...kind, selected: nextSelected },
28898
- { selected: nextSelected }
28899
- );
28900
- }
28770
+ const nextSelected = event.currentTarget.value === "" ? void 0 : Number(event.currentTarget.value);
28771
+ setControlSelected(nextSelected);
28901
28772
  },
28902
28773
  onClick: stopControlPointerEvent,
28903
28774
  onPointerDown: stopControlPointerEvent,
@@ -28909,6 +28780,7 @@ function XlsxGrid({
28909
28780
  boxSizing: "border-box",
28910
28781
  padding: `0 ${4 * zoomFactor}px`
28911
28782
  },
28783
+ tabIndex: readOnly ? -1 : void 0,
28912
28784
  value: selected === void 0 ? "" : String(selected),
28913
28785
  children: [
28914
28786
  selected === void 0 ? /* @__PURE__ */ jsx3("option", { value: "", children: controlLabel }) : null,
@@ -28919,24 +28791,19 @@ function XlsxGrid({
28919
28791
  );
28920
28792
  }
28921
28793
  if (control.kind === "listbox") {
28922
- const kind = buildFormControlKindInput(control);
28923
28794
  const selected = Array.isArray(control.selected) ? control.selected : control.selected === void 0 ? [] : [control.selected];
28924
28795
  const allowsMultipleSelection = (control.selection ?? "single") !== "single";
28925
28796
  return /* @__PURE__ */ jsx3(
28926
28797
  "select",
28927
28798
  {
28799
+ "aria-disabled": readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28928
28800
  "aria-label": controlLabel || control.name || "List box form control",
28929
- disabled: readOnly || control.controlIndex === void 0 || controlItems.length === 0,
28801
+ disabled: !readOnly && (control.controlIndex === void 0 || controlItems.length === 0),
28930
28802
  multiple: allowsMultipleSelection,
28931
28803
  onChange: (event) => {
28932
28804
  event.stopPropagation();
28933
- if (kind?.kind === "listBox") {
28934
- const nextSelected = Array.from(event.currentTarget.selectedOptions, (option) => Number(option.value));
28935
- commitControlKind(
28936
- { ...kind, selected: nextSelected },
28937
- { selected: nextSelected }
28938
- );
28939
- }
28805
+ const nextSelected = Array.from(event.currentTarget.selectedOptions, (option) => Number(option.value));
28806
+ setControlSelected(nextSelected);
28940
28807
  },
28941
28808
  onClick: stopControlPointerEvent,
28942
28809
  onPointerDown: stopControlPointerEvent,
@@ -28951,6 +28818,7 @@ function XlsxGrid({
28951
28818
  display: "block",
28952
28819
  padding: 0
28953
28820
  },
28821
+ tabIndex: readOnly ? -1 : void 0,
28954
28822
  value: allowsMultipleSelection ? selected.map(String) : selected[0] === void 0 ? "" : String(selected[0]),
28955
28823
  children: controlItems.map((item, index) => /* @__PURE__ */ jsx3("option", { value: index, children: item }, index))
28956
28824
  },
@@ -28958,22 +28826,19 @@ function XlsxGrid({
28958
28826
  );
28959
28827
  }
28960
28828
  if (control.kind === "scrollbar") {
28961
- const kind = buildFormControlKindInput(control);
28962
28829
  const min = control.min ?? 0;
28963
28830
  const max = Math.max(min, control.max ?? 100);
28964
28831
  return /* @__PURE__ */ jsx3(
28965
28832
  "input",
28966
28833
  {
28834
+ "aria-disabled": readOnly || control.controlIndex === void 0,
28967
28835
  "aria-label": controlLabel || control.name || "Scrollbar form control",
28968
- disabled: readOnly || control.controlIndex === void 0,
28836
+ disabled: !readOnly && control.controlIndex === void 0,
28969
28837
  max,
28970
28838
  min,
28971
28839
  onChange: (event) => {
28972
28840
  event.stopPropagation();
28973
- if (kind?.kind === "scrollbar") {
28974
- const value = event.currentTarget.valueAsNumber;
28975
- commitControlKind({ ...kind, value }, { value });
28976
- }
28841
+ setControlValue(event.currentTarget.valueAsNumber);
28977
28842
  },
28978
28843
  onClick: stopControlPointerEvent,
28979
28844
  onPointerDown: stopControlPointerEvent,
@@ -28985,6 +28850,7 @@ function XlsxGrid({
28985
28850
  padding: 0,
28986
28851
  writingMode: control.horizontal === false ? "vertical-lr" : void 0
28987
28852
  },
28853
+ tabIndex: readOnly ? -1 : void 0,
28988
28854
  type: "range",
28989
28855
  value: Math.max(min, Math.min(max, control.value ?? min))
28990
28856
  },
@@ -28992,22 +28858,19 @@ function XlsxGrid({
28992
28858
  );
28993
28859
  }
28994
28860
  if (control.kind === "spinner") {
28995
- const kind = buildFormControlKindInput(control);
28996
28861
  const min = control.min ?? 0;
28997
28862
  const max = Math.max(min, control.max ?? 100);
28998
28863
  return /* @__PURE__ */ jsx3(
28999
28864
  "input",
29000
28865
  {
28866
+ "aria-disabled": readOnly || control.controlIndex === void 0,
29001
28867
  "aria-label": controlLabel || control.name || "Spinner form control",
29002
- disabled: readOnly || control.controlIndex === void 0,
28868
+ disabled: !readOnly && control.controlIndex === void 0,
29003
28869
  max,
29004
28870
  min,
29005
28871
  onChange: (event) => {
29006
28872
  event.stopPropagation();
29007
- if (kind?.kind === "spinner" && Number.isFinite(event.currentTarget.valueAsNumber)) {
29008
- const value = Math.max(min, Math.min(max, event.currentTarget.valueAsNumber));
29009
- commitControlKind({ ...kind, value }, { value });
29010
- }
28873
+ setControlValue(event.currentTarget.valueAsNumber);
29011
28874
  },
29012
28875
  onClick: stopControlPointerEvent,
29013
28876
  onPointerDown: stopControlPointerEvent,
@@ -29020,6 +28883,7 @@ function XlsxGrid({
29020
28883
  boxSizing: "border-box",
29021
28884
  padding: `0 ${4 * zoomFactor}px`
29022
28885
  },
28886
+ tabIndex: readOnly ? -1 : void 0,
29023
28887
  type: "number",
29024
28888
  value: Math.max(min, Math.min(max, control.value ?? min))
29025
28889
  },
@@ -29323,7 +29187,7 @@ function XlsxGrid({
29323
29187
  };
29324
29188
  const drawingViewportCacheSignature = `${Math.floor(drawingViewport.left / CANVAS_VIEWPORT_OVERSCAN_PX)}:${Math.floor(drawingViewport.top / CANVAS_VIEWPORT_OVERSCAN_PX)}:${drawingViewport.width}:${drawingViewport.height}`;
29325
29189
  const previousPaneDrawingNodes = paneDrawingNodesCacheRef.current;
29326
- 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;
29190
+ 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;
29327
29191
  const paneDrawingNodes = canReusePaneDrawingNodes ? previousPaneDrawingNodes.value : !showImages ? {
29328
29192
  corner: null,
29329
29193
  left: null,
@@ -29359,12 +29223,16 @@ function XlsxGrid({
29359
29223
  paneDrawingNodesCacheRef.current = {
29360
29224
  chartRects,
29361
29225
  drawingViewportSignature: drawingViewportCacheSignature,
29226
+ formControlItemsById,
29362
29227
  formControlRects: domFormControlRects,
29363
29228
  imageRects: domImageRects,
29364
29229
  isChartsLoading,
29230
+ onFormControlAction,
29231
+ onFormControlChange,
29365
29232
  palette,
29366
29233
  readOnly,
29367
29234
  renderChartLoading,
29235
+ renderFormControl,
29368
29236
  renderImage,
29369
29237
  renderImageSelection,
29370
29238
  selectedChartElement,
@@ -29373,6 +29241,7 @@ function XlsxGrid({
29373
29241
  selectionStroke,
29374
29242
  shapeRects: domShapeRects,
29375
29243
  showImages,
29244
+ updateFormControl,
29376
29245
  value: paneDrawingNodes
29377
29246
  };
29378
29247
  }
@@ -30450,81 +30319,101 @@ function XlsxGrid({
30450
30319
  ]
30451
30320
  }
30452
30321
  ),
30453
- /* @__PURE__ */ jsx3(
30322
+ /* @__PURE__ */ jsxs3(
30454
30323
  "div",
30455
30324
  {
30456
- ref: selectionOverlayRef,
30325
+ ref: canvasSelectionLayerRef,
30457
30326
  style: {
30458
- backgroundColor: selectionFill,
30459
- boxSizing: "border-box",
30460
- boxShadow: `inset 0 0 0 ${selectionBorderWidth}px ${selectionStroke}`,
30461
- contain: "layout paint",
30462
- height: resolvedSelectionOverlay?.height ?? 0,
30463
- left: resolvedSelectionOverlay?.left ?? 0,
30464
- opacity: resolvedSelectionOverlay ? 1 : 0,
30327
+ height: 0,
30328
+ left: 0,
30329
+ overflow: "visible",
30465
30330
  pointerEvents: "none",
30466
30331
  position: "absolute",
30467
- top: resolvedSelectionOverlay?.top ?? 0,
30468
- transition: canvasSelectionTransition,
30469
- visibility: resolvedSelectionOverlay ? "visible" : "hidden",
30470
- willChange: shouldAnimateCanvasSelection ? "left, top, width, height" : void 0,
30471
- width: resolvedSelectionOverlay?.width ?? 0,
30332
+ top: 0,
30333
+ transformOrigin: "0 0",
30334
+ transition: "none",
30335
+ width: 0,
30472
30336
  zIndex: 24
30473
- }
30474
- }
30475
- ),
30476
- /* @__PURE__ */ jsx3(
30477
- "div",
30478
- {
30479
- ref: activeValidationOverlayRef,
30480
- "aria-hidden": "true",
30481
- style: {
30482
- alignItems: "center",
30483
- color: palette.mutedText,
30484
- display: "inline-flex",
30485
- fontSize: 10 * zoomFactor,
30486
- fontWeight: 700,
30487
- height: 16 * zoomFactor,
30488
- justifyContent: "center",
30489
- opacity: 0,
30490
- pointerEvents: "none",
30491
- position: "absolute",
30492
- transform: "translateY(-50%)",
30493
- visibility: "hidden",
30494
- width: 12 * zoomFactor,
30495
- zIndex: 26
30496
- },
30497
- children: "\u25BE"
30498
- }
30499
- ),
30500
- /* @__PURE__ */ jsx3(
30501
- "div",
30502
- {
30503
- ref: fillHandleRef,
30504
- onPointerDown: (event) => {
30505
- if (readOnly || event.button !== 0 || !normalizedSelection || !resolvedSelectionOverlay) {
30506
- return;
30507
- }
30508
- event.preventDefault();
30509
- event.stopPropagation();
30510
- startFillDrag(event.pointerId, normalizedSelection);
30511
30337
  },
30512
- style: {
30513
- backgroundColor: selectionStroke,
30514
- border: `${Math.max(1, zoomFactor)}px solid ${palette.surface}`,
30515
- contain: "layout paint",
30516
- cursor: "crosshair",
30517
- display: !readOnly && resolvedSelectionOverlay ? "block" : "none",
30518
- height: 8 * zoomFactor,
30519
- left: resolvedSelectionOverlay ? resolvedSelectionOverlay.left + resolvedSelectionOverlay.width - 4 * zoomFactor : 0,
30520
- pointerEvents: "auto",
30521
- position: "absolute",
30522
- top: resolvedSelectionOverlay ? resolvedSelectionOverlay.top + resolvedSelectionOverlay.height - 4 * zoomFactor : 0,
30523
- transition: shouldAnimateCanvasSelection ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1)" : "none",
30524
- willChange: shouldAnimateCanvasSelection ? "left, top" : void 0,
30525
- width: 8 * zoomFactor,
30526
- zIndex: 25
30527
- }
30338
+ children: [
30339
+ /* @__PURE__ */ jsx3(
30340
+ "div",
30341
+ {
30342
+ ref: selectionOverlayRef,
30343
+ style: {
30344
+ backgroundColor: selectionFill,
30345
+ boxSizing: "border-box",
30346
+ boxShadow: `inset 0 0 0 ${selectionBorderWidth}px ${selectionStroke}`,
30347
+ contain: "layout paint",
30348
+ height: resolvedSelectionOverlay?.height ?? 0,
30349
+ left: resolvedSelectionOverlay?.left ?? 0,
30350
+ opacity: resolvedSelectionOverlay ? 1 : 0,
30351
+ pointerEvents: "none",
30352
+ position: "absolute",
30353
+ top: resolvedSelectionOverlay?.top ?? 0,
30354
+ transition: canvasSelectionTransition,
30355
+ visibility: resolvedSelectionOverlay ? "visible" : "hidden",
30356
+ willChange: shouldAnimateCanvasSelectionGeometry ? "left, top, width, height" : void 0,
30357
+ width: resolvedSelectionOverlay?.width ?? 0,
30358
+ zIndex: 24
30359
+ }
30360
+ }
30361
+ ),
30362
+ /* @__PURE__ */ jsx3(
30363
+ "div",
30364
+ {
30365
+ ref: activeValidationOverlayRef,
30366
+ "aria-hidden": "true",
30367
+ style: {
30368
+ alignItems: "center",
30369
+ color: palette.mutedText,
30370
+ display: "inline-flex",
30371
+ fontSize: 10 * zoomFactor,
30372
+ fontWeight: 700,
30373
+ height: 16 * zoomFactor,
30374
+ justifyContent: "center",
30375
+ opacity: 0,
30376
+ pointerEvents: "none",
30377
+ position: "absolute",
30378
+ transform: "translateY(-50%)",
30379
+ visibility: "hidden",
30380
+ width: 12 * zoomFactor,
30381
+ zIndex: 26
30382
+ },
30383
+ children: "\u25BE"
30384
+ }
30385
+ ),
30386
+ /* @__PURE__ */ jsx3(
30387
+ "div",
30388
+ {
30389
+ ref: fillHandleRef,
30390
+ onPointerDown: (event) => {
30391
+ if (readOnly || event.button !== 0 || !normalizedSelection || !resolvedSelectionOverlay) {
30392
+ return;
30393
+ }
30394
+ event.preventDefault();
30395
+ event.stopPropagation();
30396
+ startFillDrag(event.pointerId, normalizedSelection);
30397
+ },
30398
+ style: {
30399
+ backgroundColor: selectionStroke,
30400
+ border: `${Math.max(1, zoomFactor)}px solid ${palette.surface}`,
30401
+ contain: "layout paint",
30402
+ cursor: "crosshair",
30403
+ display: !readOnly && resolvedSelectionOverlay ? "block" : "none",
30404
+ height: 8 * zoomFactor,
30405
+ left: resolvedSelectionOverlay ? resolvedSelectionOverlay.left + resolvedSelectionOverlay.width - 4 * zoomFactor : 0,
30406
+ pointerEvents: "auto",
30407
+ position: "absolute",
30408
+ top: resolvedSelectionOverlay ? resolvedSelectionOverlay.top + resolvedSelectionOverlay.height - 4 * zoomFactor : 0,
30409
+ transition: shouldAnimateCanvasSelectionGeometry ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1)" : "none",
30410
+ willChange: shouldAnimateCanvasSelectionGeometry ? "left, top" : void 0,
30411
+ width: 8 * zoomFactor,
30412
+ zIndex: 25
30413
+ }
30414
+ }
30415
+ )
30416
+ ]
30528
30417
  }
30529
30418
  ),
30530
30419
  resizeGuide ? /* @__PURE__ */ jsx3(
@@ -30602,6 +30491,7 @@ function XlsxViewerInner({
30602
30491
  onFormControlAction,
30603
30492
  onFormControlChange,
30604
30493
  renderChartLoading,
30494
+ renderFormControl,
30605
30495
  renderImage,
30606
30496
  renderImageSelection,
30607
30497
  renderScroller,
@@ -30667,6 +30557,7 @@ function XlsxViewerInner({
30667
30557
  onFormControlChange,
30668
30558
  palette,
30669
30559
  renderChartLoading,
30560
+ renderFormControl,
30670
30561
  renderImage,
30671
30562
  renderImageSelection,
30672
30563
  renderScroller,
@@ -31087,20 +30978,87 @@ function useXlsxViewerCharts() {
31087
30978
  );
31088
30979
  }
31089
30980
  function useXlsxViewerThumbnails(options = {}) {
31090
- const { getSheetFormControls, getSheetImages, getSheetShapes, workbook, sheets } = useXlsxViewer();
30981
+ const {
30982
+ getRowsBatchAsync,
30983
+ getSheetFormControls,
30984
+ getSheetImages,
30985
+ getSheetShapes,
30986
+ workbook,
30987
+ sheets
30988
+ } = useXlsxViewer();
31091
30989
  const { isDark } = React4.useContext(ViewerAppearanceContext);
31092
30990
  const palette = useViewerPalette(isDark);
31093
30991
  const includeHeaders = options.includeHeaders ?? true;
31094
30992
  const resolution = options.resolution;
31095
30993
  const thumbnailImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
30994
+ const workerThumbnailRowBatchCacheRef = React4.useRef(/* @__PURE__ */ new Map());
30995
+ const workerThumbnailRowBatchRequestRef = React4.useRef(
30996
+ /* @__PURE__ */ new Map()
30997
+ );
31096
30998
  const isMountedRef = React4.useRef(false);
31097
30999
  const [thumbnailImageLoadVersion, setThumbnailImageLoadVersion] = React4.useState(0);
31000
+ const [workerThumbnailRowBatchVersion, setWorkerThumbnailRowBatchVersion] = React4.useState(0);
31098
31001
  React4.useEffect(() => {
31099
31002
  isMountedRef.current = true;
31100
31003
  return () => {
31101
31004
  isMountedRef.current = false;
31102
31005
  };
31103
31006
  }, []);
31007
+ React4.useEffect(() => {
31008
+ const currentSheets = new Set(sheets);
31009
+ for (const sheet of workerThumbnailRowBatchCacheRef.current.keys()) {
31010
+ if (!currentSheets.has(sheet)) {
31011
+ workerThumbnailRowBatchCacheRef.current.delete(sheet);
31012
+ }
31013
+ }
31014
+ for (const sheet of workerThumbnailRowBatchRequestRef.current.keys()) {
31015
+ if (!currentSheets.has(sheet)) {
31016
+ workerThumbnailRowBatchRequestRef.current.delete(sheet);
31017
+ }
31018
+ }
31019
+ }, [sheets]);
31020
+ const getWorkerThumbnailRowBatch = React4.useCallback((sheet, startRow, endRow) => {
31021
+ const cachedBatch = workerThumbnailRowBatchCacheRef.current.get(sheet);
31022
+ if (cachedBatch && startRow >= cachedBatch.startRow && endRow <= cachedBatch.endRow) {
31023
+ return cachedBatch;
31024
+ }
31025
+ if (!getRowsBatchAsync) {
31026
+ return null;
31027
+ }
31028
+ const pendingRequest = workerThumbnailRowBatchRequestRef.current.get(sheet);
31029
+ if (!pendingRequest) {
31030
+ const request = {
31031
+ endRow: Math.max(endRow, cachedBatch?.endRow ?? endRow),
31032
+ startRow: Math.min(startRow, cachedBatch?.startRow ?? startRow)
31033
+ };
31034
+ workerThumbnailRowBatchRequestRef.current.set(sheet, request);
31035
+ void getRowsBatchAsync(
31036
+ sheet.workbookSheetIndex,
31037
+ request.startRow,
31038
+ request.endRow - request.startRow + 1
31039
+ ).then((rows) => {
31040
+ if (workerThumbnailRowBatchRequestRef.current.get(sheet) !== request) {
31041
+ return;
31042
+ }
31043
+ workerThumbnailRowBatchRequestRef.current.delete(sheet);
31044
+ if (!rows) {
31045
+ return;
31046
+ }
31047
+ workerThumbnailRowBatchCacheRef.current.set(
31048
+ sheet,
31049
+ buildWorksheetBatchWindow(rows, sheet, request.startRow, request.endRow)
31050
+ );
31051
+ if (isMountedRef.current) {
31052
+ setWorkerThumbnailRowBatchVersion((version) => version + 1);
31053
+ }
31054
+ }).catch(() => {
31055
+ if (workerThumbnailRowBatchRequestRef.current.get(sheet) === request) {
31056
+ workerThumbnailRowBatchRequestRef.current.delete(sheet);
31057
+ }
31058
+ });
31059
+ }
31060
+ return null;
31061
+ }, [getRowsBatchAsync]);
31104
31062
  const getThumbnailImage = React4.useCallback((image) => {
31105
31063
  if (typeof Image === "undefined") {
31106
31064
  return null;
@@ -31326,6 +31284,10 @@ function useXlsxViewerThumbnails(options = {}) {
31326
31284
  if (!canvas) {
31327
31285
  return false;
31328
31286
  }
31287
+ const workerRowBatch = worksheet ? null : getWorkerThumbnailRowBatch(sheet, sourceRange.start.row, sourceRange.end.row);
31288
+ if (!worksheet && !workerRowBatch) {
31289
+ return false;
31290
+ }
31329
31291
  const context = canvas.getContext("2d", { alpha: false });
31330
31292
  if (!context) {
31331
31293
  return false;
@@ -31362,9 +31324,6 @@ function useXlsxViewerThumbnails(options = {}) {
31362
31324
  context.fillRect(0, headerHeight, rowHeaderWidth, Math.max(1, rowAxis.totalSize));
31363
31325
  context.fillRect(0, 0, rowHeaderWidth, headerHeight);
31364
31326
  }
31365
- if (!worksheet) {
31366
- return true;
31367
- }
31368
31327
  const conditionalFormatMetricsCache = /* @__PURE__ */ new Map();
31369
31328
  const cellRenderCache = /* @__PURE__ */ new Map();
31370
31329
  const getCellData = (row, col) => {
@@ -31373,7 +31332,9 @@ function useXlsxViewerThumbnails(options = {}) {
31373
31332
  if (cached) {
31374
31333
  return cached;
31375
31334
  }
31376
- if (worksheet.isMergedSecondary(row, col)) {
31335
+ const batchCoversRow = workerRowBatch ? row >= workerRowBatch.startRow && row <= workerRowBatch.endRow : false;
31336
+ const batchedCell = batchCoversRow ? workerRowBatch?.cells.get(cacheKey) : void 0;
31337
+ if (batchedCell?.isMergedSecondary || worksheet?.isMergedSecondary(row, col)) {
31377
31338
  const mergedSecondaryData = {
31378
31339
  isMergedSecondary: true,
31379
31340
  style: {},
@@ -31382,53 +31343,34 @@ function useXlsxViewerThumbnails(options = {}) {
31382
31343
  cellRenderCache.set(cacheKey, mergedSecondaryData);
31383
31344
  return mergedSecondaryData;
31384
31345
  }
31385
- const merge = worksheet.getMergeSpan(row, col);
31346
+ const merge = worksheet ? worksheet.getMergeSpan(row, col) : batchedCell?.mergeSpan;
31386
31347
  const inheritedStyle = resolveInheritedCellStyle2(sheet, row, col);
31387
- const worksheetStyle = worksheet.getCellStyleAt(row, col) ?? null;
31348
+ const worksheetStyle = worksheet ? worksheet.getCellStyleAt(row, col) ?? null : batchedCell?.style ?? null;
31388
31349
  const mergedStyle = mergeResolvedCellStyle(inheritedStyle, worksheetStyle, { replaceXfSubtrees: true });
31389
31350
  const alignment = mergedStyle?.alignment;
31390
31351
  const sparkline = sparklineByCell.get(cacheKey) ?? null;
31352
+ const getNumericValue = (targetRow, targetCol) => worksheet ? getCellNumericValue(worksheet, targetRow, targetCol) : getBatchedCellNumericValue(workerRowBatch?.cells.get(`${targetRow}:${targetCol}`));
31391
31353
  const sparklineValues = sparkline ? sparkline.range.start.row === sparkline.range.end.row ? Array.from(
31392
31354
  { length: Math.abs(sparkline.range.end.col - sparkline.range.start.col) + 1 },
31393
- (_, index) => getCellNumericValue(
31394
- worksheet,
31355
+ (_, index) => getNumericValue(
31395
31356
  sparkline.range.start.row,
31396
31357
  Math.min(sparkline.range.start.col, sparkline.range.end.col) + index
31397
31358
  )
31398
31359
  ) : Array.from(
31399
31360
  { length: Math.abs(sparkline.range.end.row - sparkline.range.start.row) + 1 },
31400
- (_, index) => getCellNumericValue(
31401
- worksheet,
31361
+ (_, index) => getNumericValue(
31402
31362
  Math.min(sparkline.range.start.row, sparkline.range.end.row) + index,
31403
31363
  sparkline.range.start.col
31404
31364
  )
31405
31365
  ) : null;
31406
- const checkboxState = mergedStyle?.cellControl ? getCellBooleanValue(worksheet, row, col) : null;
31366
+ const checkboxState = mergedStyle?.cellControl ? worksheet ? getCellBooleanValue(worksheet, row, col) : getBatchedCellBooleanValue(batchedCell) : null;
31407
31367
  const nextData = {
31408
31368
  canvas: void 0,
31409
31369
  checkboxState,
31410
31370
  colSpan: merge?.colSpan,
31411
- conditionalColorScale: resolveConditionalColorScaleForCell(
31412
- row,
31413
- col,
31414
- worksheet,
31415
- sheet,
31416
- conditionalFormatMetricsCache
31417
- ),
31418
- conditionalDataBar: resolveConditionalDataBarForCell(
31419
- row,
31420
- col,
31421
- worksheet,
31422
- sheet,
31423
- conditionalFormatMetricsCache
31424
- ),
31425
- conditionalIcon: resolveConditionalIconForCell(
31426
- row,
31427
- col,
31428
- worksheet,
31429
- sheet,
31430
- conditionalFormatMetricsCache
31431
- ),
31371
+ conditionalColorScale: worksheet ? resolveConditionalColorScaleForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache) : null,
31372
+ conditionalDataBar: worksheet ? resolveConditionalDataBarForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache) : null,
31373
+ conditionalIcon: worksheet ? resolveConditionalIconForCell(row, col, worksheet, sheet, conditionalFormatMetricsCache) : null,
31432
31374
  isMergedSecondary: false,
31433
31375
  rowSpan: merge?.rowSpan,
31434
31376
  sparkline: sparkline && sparklineValues ? { config: sparkline, values: sparklineValues } : null,
@@ -31436,7 +31378,7 @@ function useXlsxViewerThumbnails(options = {}) {
31436
31378
  showGridLines
31437
31379
  }),
31438
31380
  textRotationDeg: resolveSpreadsheetTextRotation(alignment?.textRotation),
31439
- value: sparkline ? "" : checkboxState !== null ? "" : getCellDisplayValue(worksheet, row, col, sheet)
31381
+ value: sparkline ? "" : checkboxState !== null ? "" : worksheet ? getCellDisplayValue(worksheet, row, col, sheet) : batchedCell?.value ?? ""
31440
31382
  };
31441
31383
  nextData.canvas = buildCanvasCellStyleCache(nextData.style);
31442
31384
  cellRenderCache.set(cacheKey, nextData);
@@ -31710,12 +31652,14 @@ function useXlsxViewerThumbnails(options = {}) {
31710
31652
  getSheetFormControls,
31711
31653
  getSheetImages,
31712
31654
  getSheetShapes,
31655
+ getWorkerThumbnailRowBatch,
31713
31656
  getThumbnailImage,
31714
31657
  includeHeaders,
31715
31658
  palette,
31716
31659
  resolution,
31717
31660
  sheets,
31718
31661
  thumbnailImageLoadVersion,
31662
+ workerThumbnailRowBatchVersion,
31719
31663
  workbook
31720
31664
  ]);
31721
31665
  const paintThumbnail = React4.useCallback(