@extend-ai/react-xlsx 0.14.1 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2006,7 +2006,7 @@ function normalizeChartExLegend(raw) {
2006
2006
  return null;
2007
2007
  }
2008
2008
  const legend = raw;
2009
- const position = typeof legend.pos === "string" ? normalizeLegendPosition(String(legend.pos)) : void 0;
2009
+ const position = typeof legend.position === "string" ? normalizeLegendPosition(String(legend.position)) : void 0;
2010
2010
  return {
2011
2011
  overlay: typeof legend.overlay === "boolean" ? legend.overlay : void 0,
2012
2012
  position,
@@ -2127,11 +2127,11 @@ function buildChartExHistogramBins(values, rawSeries, sortByFrequency) {
2127
2127
  return [];
2128
2128
  }
2129
2129
  const rawRecord = rawSeries && typeof rawSeries === "object" ? rawSeries : null;
2130
- const layoutProperties = rawRecord?.layoutPr && typeof rawRecord.layoutPr === "object" ? rawRecord.layoutPr : null;
2130
+ const layoutProperties = rawRecord?.layoutProperties && typeof rawRecord.layoutProperties === "object" ? rawRecord.layoutProperties : null;
2131
2131
  const rawBinning = layoutProperties?.binning && typeof layoutProperties.binning === "object" ? layoutProperties.binning : null;
2132
2132
  const minValue = Math.min(...values);
2133
2133
  const maxValue = Math.max(...values);
2134
- const explicitWidth = typeof rawBinning?.binWidth === "number" && Number.isFinite(rawBinning.binWidth) && rawBinning.binWidth > 0 ? rawBinning.binWidth : typeof rawBinning?.width === "number" && Number.isFinite(rawBinning.width) && rawBinning.width > 0 ? rawBinning.width : void 0;
2134
+ const explicitWidth = typeof rawBinning?.binSize === "number" && Number.isFinite(rawBinning.binSize) && rawBinning.binSize > 0 ? rawBinning.binSize : void 0;
2135
2135
  const explicitCount = typeof rawBinning?.binCount === "number" && Number.isFinite(rawBinning.binCount) && rawBinning.binCount > 0 ? rawBinning.binCount : typeof rawBinning?.count === "number" && Number.isFinite(rawBinning.count) && rawBinning.count > 0 ? rawBinning.count : void 0;
2136
2136
  const closedRight = rawBinning?.intervalClosed === "r" || rawBinning?.intervalClosed === "right";
2137
2137
  const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
@@ -2184,7 +2184,7 @@ function buildChartExHistogramSeries(series, rawSeries, sortByFrequency) {
2184
2184
  const layout = resolveChartExSeriesLayout(rawSeries);
2185
2185
  const rawRecord = rawSeries && typeof rawSeries === "object" ? rawSeries : null;
2186
2186
  const hasBinning = Boolean(
2187
- layout === "clusteredColumn" && rawRecord?.layoutPr && typeof rawRecord.layoutPr === "object" && rawRecord.layoutPr.binning != null
2187
+ layout === "clusteredColumn" && rawRecord?.layoutProperties && typeof rawRecord.layoutProperties === "object" && rawRecord.layoutProperties.binning != null
2188
2188
  );
2189
2189
  if (!hasBinning) {
2190
2190
  return series;
@@ -2396,7 +2396,8 @@ function collapseChartExPointSeries(chartType, series) {
2396
2396
  ];
2397
2397
  }
2398
2398
  function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex, raw, index, themePalette) {
2399
- const chart = raw && typeof raw === "object" ? raw : {};
2399
+ const drawing = raw && typeof raw === "object" ? raw : {};
2400
+ const chart = drawing.chartEx && typeof drawing.chartEx === "object" ? drawing.chartEx : {};
2400
2401
  const plotArea = chart.plotArea && typeof chart.plotArea === "object" ? chart.plotArea : {};
2401
2402
  const rawSeries = Array.isArray(plotArea.series) ? plotArea.series : [];
2402
2403
  const seriesLayouts = rawSeries.map(resolveChartExSeriesLayout);
@@ -2451,7 +2452,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2451
2452
  }
2452
2453
  ] : [];
2453
2454
  const normalizedChart = {
2454
- anchor: normalizeChartAnchor(chart.anchor),
2455
+ anchor: normalizeChartAnchor(drawing.anchor),
2455
2456
  autoTitleDeleted: void 0,
2456
2457
  axes,
2457
2458
  axisLabelColor: void 0,
@@ -2475,7 +2476,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2475
2476
  id: `chart-ex-${workbookSheetIndex}-${index}`,
2476
2477
  is3d: void 0,
2477
2478
  legend: normalizeChartExLegend(chart.legend),
2478
- name: chartTitle,
2479
+ name: typeof drawing.name === "string" ? drawing.name : chartTitle,
2479
2480
  overlap: void 0,
2480
2481
  plotVisibleOnly: void 0,
2481
2482
  raw: chart,
@@ -2503,7 +2504,7 @@ function normalizeChartExChart(workbook, workbookSheetIndex, visibleSheetIndex,
2503
2504
  view3d: void 0,
2504
2505
  wireframe: void 0,
2505
2506
  workbookSheetIndex,
2506
- zIndex: index
2507
+ zIndex: Array.isArray(drawing.drawingPath) && typeof drawing.drawingPath[0] === "number" ? drawing.drawingPath[0] + 1 : index + 1
2507
2508
  };
2508
2509
  applyBuiltinChartDefaults(normalizedChart, themePalette);
2509
2510
  return normalizedChart;
@@ -2624,10 +2625,11 @@ function normalizeChartReference(raw) {
2624
2625
  return null;
2625
2626
  }
2626
2627
  const record = raw;
2628
+ const values = Array.isArray(record.numbers) ? record.numbers : Array.isArray(record.strings) ? record.strings : void 0;
2627
2629
  return {
2628
2630
  formula: typeof record.formula === "string" ? record.formula : void 0,
2629
2631
  refType: typeof record.refType === "string" ? record.refType : void 0,
2630
- values: Array.isArray(record.values) ? record.values : void 0
2632
+ values
2631
2633
  };
2632
2634
  }
2633
2635
  function normalizeChartAxis(raw) {
@@ -2647,8 +2649,8 @@ function normalizeChartAxis(raw) {
2647
2649
  logBase: typeof axis.logBase === "number" ? axis.logBase : void 0,
2648
2650
  orientation: typeof axis.orientation === "string" ? axis.orientation : void 0,
2649
2651
  majorUnit: typeof axis.majorUnit === "number" ? axis.majorUnit : void 0,
2650
- max: typeof axis.max === "number" ? axis.max : void 0,
2651
- min: typeof axis.min === "number" ? axis.min : void 0,
2652
+ max: typeof axis.maximum === "number" ? axis.maximum : void 0,
2653
+ min: typeof axis.minimum === "number" ? axis.minimum : void 0,
2652
2654
  majorGridlines: typeof axis.majorGridlines === "boolean" ? axis.majorGridlines : void 0,
2653
2655
  majorTickMark: typeof axis.majorTickMark === "string" ? axis.majorTickMark : void 0,
2654
2656
  minorUnit: typeof axis.minorUnit === "number" ? axis.minorUnit : void 0,
@@ -2771,11 +2773,11 @@ function normalizeChartDataLabels(raw) {
2771
2773
  pointLabels: pointLabels && pointLabels.length > 0 ? pointLabels : void 0,
2772
2774
  raw: labels,
2773
2775
  showBubbleSize: typeof labels.showBubbleSize === "boolean" ? labels.showBubbleSize : void 0,
2774
- showCategoryName: typeof labels.showCategoryName === "boolean" ? labels.showCategoryName : void 0,
2776
+ showCategoryName: typeof (labels.showCategoryName ?? labels.visibilityCategoryName) === "boolean" ? Boolean(labels.showCategoryName ?? labels.visibilityCategoryName) : void 0,
2775
2777
  showLegendKey: typeof labels.showLegendKey === "boolean" ? labels.showLegendKey : void 0,
2776
2778
  showPercent: typeof labels.showPercent === "boolean" ? labels.showPercent : void 0,
2777
- showSeriesName: typeof labels.showSeriesName === "boolean" ? labels.showSeriesName : void 0,
2778
- showValue: typeof labels.showValue === "boolean" ? labels.showValue : void 0
2779
+ showSeriesName: typeof (labels.showSeriesName ?? labels.visibilitySeriesName) === "boolean" ? Boolean(labels.showSeriesName ?? labels.visibilitySeriesName) : void 0,
2780
+ showValue: typeof (labels.showValue ?? labels.visibilityValue) === "boolean" ? Boolean(labels.showValue ?? labels.visibilityValue) : void 0
2779
2781
  };
2780
2782
  }
2781
2783
  function normalizeChartAnchor(raw) {
@@ -2787,19 +2789,52 @@ function normalizeChartAnchor(raw) {
2787
2789
  };
2788
2790
  }
2789
2791
  const anchor = raw;
2790
- const fromCol = typeof anchor.fromCol === "number" ? anchor.fromCol : 0;
2791
- const fromColOffsetEmu = typeof anchor.fromColOffset === "number" ? anchor.fromColOffset : 0;
2792
- const fromRow = typeof anchor.fromRow === "number" ? anchor.fromRow : 0;
2793
- const fromRowOffsetEmu = typeof anchor.fromRowOffset === "number" ? anchor.fromRowOffset : 0;
2794
- const rawToCol = typeof anchor.toCol === "number" ? anchor.toCol : null;
2795
- const rawToColOffsetEmu = typeof anchor.toColOffset === "number" ? anchor.toColOffset : 0;
2796
- const rawToRow = typeof anchor.toRow === "number" ? anchor.toRow : null;
2797
- const rawToRowOffsetEmu = typeof anchor.toRowOffset === "number" ? anchor.toRowOffset : 0;
2798
- const hasExplicitTo = rawToCol !== null && rawToRow !== null;
2799
- const collapsedWidth = hasExplicitTo && (rawToCol < fromCol || rawToCol === fromCol && rawToColOffsetEmu <= fromColOffsetEmu);
2800
- const collapsedHeight = hasExplicitTo && (rawToRow < fromRow || rawToRow === fromRow && rawToRowOffsetEmu <= fromRowOffsetEmu);
2801
- const fallbackToCol = Math.max(fromCol + 8, 8);
2802
- const fallbackToRow = Math.max(fromRow + 15, 15);
2792
+ const from = anchor.from && typeof anchor.from === "object" ? anchor.from : null;
2793
+ if (anchor.type === "oneCell") {
2794
+ return {
2795
+ from: {
2796
+ col: typeof from?.col === "number" ? from.col : 0,
2797
+ colOffsetEmu: typeof from?.colOffsetEmu === "number" ? from.colOffsetEmu : 0,
2798
+ row: typeof from?.row === "number" ? from.row : 0,
2799
+ rowOffsetEmu: typeof from?.rowOffsetEmu === "number" ? from.rowOffsetEmu : 0
2800
+ },
2801
+ kind: "one-cell",
2802
+ sizeEmu: {
2803
+ cx: typeof anchor.widthEmu === "number" ? anchor.widthEmu : 0,
2804
+ cy: typeof anchor.heightEmu === "number" ? anchor.heightEmu : 0
2805
+ }
2806
+ };
2807
+ }
2808
+ if (anchor.type === "absolute") {
2809
+ return {
2810
+ kind: "absolute",
2811
+ positionEmu: {
2812
+ x: typeof anchor.xEmu === "number" ? anchor.xEmu : 0,
2813
+ y: typeof anchor.yEmu === "number" ? anchor.yEmu : 0
2814
+ },
2815
+ sizeEmu: {
2816
+ cx: typeof anchor.widthEmu === "number" ? anchor.widthEmu : 0,
2817
+ cy: typeof anchor.heightEmu === "number" ? anchor.heightEmu : 0
2818
+ }
2819
+ };
2820
+ }
2821
+ const to = anchor.to && typeof anchor.to === "object" ? anchor.to : null;
2822
+ const fromColValue = from?.col;
2823
+ const fromColOffsetValue = from?.colOffsetEmu;
2824
+ const fromRowValue = from?.row;
2825
+ const fromRowOffsetValue = from?.rowOffsetEmu;
2826
+ const toColValue = to?.col;
2827
+ const toColOffsetValue = to?.colOffsetEmu;
2828
+ const toRowValue = to?.row;
2829
+ const toRowOffsetValue = to?.rowOffsetEmu;
2830
+ const fromCol = typeof fromColValue === "number" ? fromColValue : 0;
2831
+ const fromColOffsetEmu = typeof fromColOffsetValue === "number" ? fromColOffsetValue : 0;
2832
+ const fromRow = typeof fromRowValue === "number" ? fromRowValue : 0;
2833
+ const fromRowOffsetEmu = typeof fromRowOffsetValue === "number" ? fromRowOffsetValue : 0;
2834
+ const rawToCol = typeof toColValue === "number" ? toColValue : 0;
2835
+ const rawToColOffsetEmu = typeof toColOffsetValue === "number" ? toColOffsetValue : 0;
2836
+ const rawToRow = typeof toRowValue === "number" ? toRowValue : 0;
2837
+ const rawToRowOffsetEmu = typeof toRowOffsetValue === "number" ? toRowOffsetValue : 0;
2803
2838
  return {
2804
2839
  kind: "two-cell",
2805
2840
  from: {
@@ -2809,10 +2844,10 @@ function normalizeChartAnchor(raw) {
2809
2844
  rowOffsetEmu: fromRowOffsetEmu
2810
2845
  },
2811
2846
  to: {
2812
- col: !hasExplicitTo || collapsedWidth ? fallbackToCol : rawToCol,
2813
- colOffsetEmu: !hasExplicitTo || collapsedWidth ? 0 : rawToColOffsetEmu,
2814
- row: !hasExplicitTo || collapsedHeight ? fallbackToRow : rawToRow,
2815
- rowOffsetEmu: !hasExplicitTo || collapsedHeight ? 0 : rawToRowOffsetEmu
2847
+ col: rawToCol,
2848
+ colOffsetEmu: rawToColOffsetEmu,
2849
+ row: rawToRow,
2850
+ rowOffsetEmu: rawToRowOffsetEmu
2816
2851
  }
2817
2852
  };
2818
2853
  }
@@ -2926,7 +2961,7 @@ function normalizeChartTypeGroup(workbook, workbookSheetIndex, chartId, raw, ind
2926
2961
  chartType: typeof group.chartType === "string" ? group.chartType : "ColumnClustered",
2927
2962
  dataLabels: normalizeChartDataLabels(group.dataLabels),
2928
2963
  gapWidth: typeof group.gapWidth === "number" && Number.isFinite(group.gapWidth) ? group.gapWidth : void 0,
2929
- is3d: typeof group.is3d === "boolean" ? group.is3d : void 0,
2964
+ is3d: typeof group.is3D === "boolean" ? group.is3D : void 0,
2930
2965
  overlap: typeof group.overlap === "number" && Number.isFinite(group.overlap) ? group.overlap : void 0,
2931
2966
  raw: group,
2932
2967
  series: rawSeries.map((entry, seriesIndex) => normalizeChartSeries(workbook, workbookSheetIndex, `${chartId}-group-${index}`, entry, seriesIndex)),
@@ -3080,20 +3115,25 @@ function applyChartOrigins(chartsByWorkbookSheetIndex, chartOriginsById, archive
3080
3115
  }
3081
3116
  }
3082
3117
  function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkbookSheetIndex, showHiddenSheets = false) {
3118
+ const excludedChartIds = /* @__PURE__ */ new Set();
3083
3119
  const chartsByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
3084
3120
  const worksheet = workbook.getSheet(workbookSheetIndex);
3085
- const rawCharts = Array.isArray(worksheet.charts) ? worksheet.charts : [];
3086
- const rawChartsEx = Array.isArray(worksheet.chartsEx) ? worksheet.chartsEx : [];
3121
+ const rawCharts = worksheet.charts;
3122
+ const rawChartsEx = worksheet.chartsEx;
3087
3123
  const visibleSheetIndex = visibleSheetIndexByWorkbookSheetIndex.get(workbookSheetIndex) ?? workbookSheetIndex;
3088
3124
  const classicCharts = rawCharts.map((rawChart, chartIndex) => {
3089
3125
  const chartId = `chart-${workbookSheetIndex}-${chartIndex}`;
3090
- const chart = rawChart && typeof rawChart === "object" ? rawChart : {};
3091
- const rawView3d = chart.view3d && typeof chart.view3d === "object" ? chart.view3d : null;
3126
+ if (rawChart.hidden || !rawChart.anchor) {
3127
+ excludedChartIds.add(chartId);
3128
+ }
3129
+ const drawing = rawChart && typeof rawChart === "object" ? rawChart : {};
3130
+ const chart = drawing.chart && typeof drawing.chart === "object" ? drawing.chart : {};
3131
+ const rawView3d = chart.view3D && typeof chart.view3D === "object" ? chart.view3D : null;
3092
3132
  const rawSeries = Array.isArray(chart.series) ? chart.series : [];
3093
3133
  const chartLevelDataLabels = normalizeChartDataLabels(chart.dataLabels);
3094
3134
  const firstSeriesDataLabels = rawSeries.length > 0 && rawSeries[0] && typeof rawSeries[0] === "object" ? normalizeChartDataLabels(rawSeries[0].dataLabels) : null;
3095
3135
  return {
3096
- anchor: normalizeChartAnchor(chart.anchor),
3136
+ anchor: normalizeChartAnchor(drawing.anchor),
3097
3137
  autoTitleDeleted: typeof chart.autoTitleDeleted === "boolean" ? chart.autoTitleDeleted : void 0,
3098
3138
  axes: Array.isArray(chart.axes) ? chart.axes.map(normalizeChartAxis).filter((value) => Boolean(value)) : [],
3099
3139
  axisLabelColor: void 0,
@@ -3114,12 +3154,12 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3114
3154
  gapWidth: typeof chart.gapWidth === "number" ? chart.gapWidth : void 0,
3115
3155
  holeSize: typeof chart.holeSize === "number" ? chart.holeSize : void 0,
3116
3156
  id: chartId,
3117
- is3d: typeof chart.is3d === "boolean" ? chart.is3d : void 0,
3157
+ is3d: typeof chart.is3D === "boolean" ? chart.is3D : void 0,
3118
3158
  legend: normalizeLegend(chart.legend) ? {
3119
3159
  ...normalizeLegend(chart.legend),
3120
3160
  position: normalizeLegendPosition(normalizeLegend(chart.legend)?.position)
3121
3161
  } : null,
3122
- name: typeof chart.name === "string" ? chart.name : void 0,
3162
+ name: typeof drawing.name === "string" ? drawing.name : void 0,
3123
3163
  overlap: typeof chart.overlap === "number" ? chart.overlap : void 0,
3124
3164
  plotVisibleOnly: typeof chart.plotVisibleOnly === "boolean" ? chart.plotVisibleOnly : void 0,
3125
3165
  raw: chart,
@@ -3153,17 +3193,23 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3153
3193
  } : void 0,
3154
3194
  wireframe: typeof chart.wireframe === "boolean" ? chart.wireframe : void 0,
3155
3195
  workbookSheetIndex,
3156
- zIndex: 200 + chartIndex
3196
+ zIndex: Array.isArray(drawing.drawingPath) && typeof drawing.drawingPath[0] === "number" ? drawing.drawingPath[0] + 1 : chartIndex + 1
3157
3197
  };
3158
3198
  });
3159
- const modernCharts = rawChartsEx.map((rawChartEx, chartExIndex) => normalizeChartExChart(
3160
- workbook,
3161
- workbookSheetIndex,
3162
- visibleSheetIndex,
3163
- rawChartEx,
3164
- chartExIndex,
3165
- imageAssets?.themePalette ?? null
3166
- ));
3199
+ const modernCharts = rawChartsEx.map((rawChartEx, chartExIndex) => {
3200
+ const chartId = `chart-ex-${workbookSheetIndex}-${chartExIndex}`;
3201
+ if (rawChartEx.hidden || !rawChartEx.anchor) {
3202
+ excludedChartIds.add(chartId);
3203
+ }
3204
+ return normalizeChartExChart(
3205
+ workbook,
3206
+ workbookSheetIndex,
3207
+ visibleSheetIndex,
3208
+ rawChartEx,
3209
+ chartExIndex,
3210
+ imageAssets?.themePalette ?? null
3211
+ );
3212
+ });
3167
3213
  return [...classicCharts, ...modernCharts];
3168
3214
  });
3169
3215
  const chartsheets = Array.isArray(workbook.chartsheets) ? workbook.chartsheets.map((entry, index) => normalizeChartsheet(entry, index)) : [];
@@ -3171,6 +3217,12 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3171
3217
  const chartOriginsById = /* @__PURE__ */ new Map();
3172
3218
  if (imageAssets) {
3173
3219
  applyChartOrigins(chartsByWorkbookSheetIndex, chartOriginsById, imageAssets.archive, imageAssets.sheetOrigins);
3220
+ for (let index = 0; index < chartsByWorkbookSheetIndex.length; index += 1) {
3221
+ chartsByWorkbookSheetIndex[index] = (chartsByWorkbookSheetIndex[index] ?? []).filter((chart) => !excludedChartIds.has(chart.id));
3222
+ }
3223
+ for (const id of excludedChartIds) {
3224
+ chartOriginsById.delete(id);
3225
+ }
3174
3226
  for (const charts of chartsByWorkbookSheetIndex) {
3175
3227
  for (const chart of charts) {
3176
3228
  applyChartStyleFromXml(chart, chart.chartPath, imageAssets.archive, imageAssets.themePalette);
@@ -3178,6 +3230,9 @@ function loadWorkbookChartAssets(workbook, imageAssets, visibleSheetIndexByWorkb
3178
3230
  }
3179
3231
  }
3180
3232
  } else {
3233
+ for (let index = 0; index < chartsByWorkbookSheetIndex.length; index += 1) {
3234
+ chartsByWorkbookSheetIndex[index] = (chartsByWorkbookSheetIndex[index] ?? []).filter((chart) => !excludedChartIds.has(chart.id));
3235
+ }
3181
3236
  for (const charts of chartsByWorkbookSheetIndex) {
3182
3237
  for (const chart of charts) {
3183
3238
  applyBuiltinChartDefaults(chart, null);
@@ -3410,6 +3465,7 @@ function updateWorkbookChartAnchor(imageAssets, chartAssets, chartId, anchor) {
3410
3465
  }
3411
3466
  updateAnchorNode(anchorNode, anchor);
3412
3467
  imageAssets.archive[normalizeArchivePath(origin.drawingPath)] = (0, import_fflate.strToU8)(serializeXml(drawingDocument));
3468
+ imageAssets.dirtyArchivePaths.add(normalizeArchivePath(origin.drawingPath));
3413
3469
  return true;
3414
3470
  }
3415
3471
  function updateWorkbookChartDefinition(imageAssets, chartAssets, chartId, patch) {
@@ -3464,6 +3520,7 @@ function updateWorkbookChartDefinition(imageAssets, chartAssets, chartId, patch)
3464
3520
  );
3465
3521
  updateAxisNode(getLocalChildren(plotAreaNode, "valAx")[0] ?? null, patch.valueAxis);
3466
3522
  imageAssets.archive[normalizeArchivePath(origin.chartPath)] = (0, import_fflate.strToU8)(serializeXml(chartDocument));
3523
+ imageAssets.dirtyArchivePaths.add(normalizeArchivePath(origin.chartPath));
3467
3524
  return true;
3468
3525
  }
3469
3526
 
@@ -3641,7 +3698,6 @@ var PKG_REL_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
3641
3698
  var SPREADSHEET_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
3642
3699
  var CONTENT_TYPES_NS = "http://schemas.openxmlformats.org/package/2006/content-types";
3643
3700
  var DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing";
3644
- var VML_DRAWING_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/vmlDrawing";
3645
3701
  var IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
3646
3702
  var DRAWING_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawing+xml";
3647
3703
  var EMU_PER_PIXEL2 = 9525;
@@ -5068,71 +5124,174 @@ function parseMarker(node) {
5068
5124
  rowOffsetEmu
5069
5125
  };
5070
5126
  }
5071
- function parseSpreadsheetBooleanValue(value) {
5072
- if (value === null || value === void 0) {
5127
+ function dukeDrawingAnchorToXlsxAnchor(anchor) {
5128
+ if (anchor.type === "absolute") {
5129
+ return {
5130
+ kind: "absolute",
5131
+ positionEmu: { x: anchor.xEmu, y: anchor.yEmu },
5132
+ sizeEmu: { cx: anchor.widthEmu, cy: anchor.heightEmu }
5133
+ };
5134
+ }
5135
+ const from = {
5136
+ col: anchor.from.col,
5137
+ colOffsetEmu: anchor.from.colOffsetEmu ?? 0,
5138
+ row: anchor.from.row,
5139
+ rowOffsetEmu: anchor.from.rowOffsetEmu ?? 0
5140
+ };
5141
+ if (anchor.type === "oneCell") {
5142
+ return {
5143
+ from,
5144
+ kind: "one-cell",
5145
+ sizeEmu: { cx: anchor.widthEmu, cy: anchor.heightEmu }
5146
+ };
5147
+ }
5148
+ return {
5149
+ from,
5150
+ kind: "two-cell",
5151
+ to: {
5152
+ col: anchor.to.col,
5153
+ colOffsetEmu: anchor.to.colOffsetEmu ?? 0,
5154
+ row: anchor.to.row,
5155
+ rowOffsetEmu: anchor.to.rowOffsetEmu ?? 0
5156
+ }
5157
+ };
5158
+ }
5159
+ function xlsxAnchorToDukeDrawingAnchor(anchor, editAs) {
5160
+ if (anchor.kind === "absolute") {
5161
+ return {
5162
+ heightEmu: anchor.sizeEmu.cy,
5163
+ type: "absolute",
5164
+ widthEmu: anchor.sizeEmu.cx,
5165
+ xEmu: anchor.positionEmu.x,
5166
+ yEmu: anchor.positionEmu.y
5167
+ };
5168
+ }
5169
+ const from = {
5170
+ col: anchor.from.col,
5171
+ colOffsetEmu: anchor.from.colOffsetEmu,
5172
+ row: anchor.from.row,
5173
+ rowOffsetEmu: anchor.from.rowOffsetEmu
5174
+ };
5175
+ if (anchor.kind === "one-cell") {
5176
+ return {
5177
+ from,
5178
+ heightEmu: anchor.sizeEmu.cy,
5179
+ type: "oneCell",
5180
+ widthEmu: anchor.sizeEmu.cx
5181
+ };
5182
+ }
5183
+ return {
5184
+ editAs,
5185
+ from,
5186
+ to: {
5187
+ col: anchor.to.col,
5188
+ colOffsetEmu: anchor.to.colOffsetEmu,
5189
+ row: anchor.to.row,
5190
+ rowOffsetEmu: anchor.to.rowOffsetEmu
5191
+ },
5192
+ type: "twoCell"
5193
+ };
5194
+ }
5195
+ function mapDukeDrawingColor(color) {
5196
+ if (!color) {
5073
5197
  return void 0;
5074
5198
  }
5075
- const normalized = value.trim().toLowerCase();
5076
- if (!normalized) {
5077
- return true;
5199
+ switch (color.colorType) {
5200
+ case "auto":
5201
+ return { colorType: "auto" };
5202
+ case "rgb":
5203
+ return { b: color.b, colorType: "rgb", g: color.g, r: color.r };
5204
+ case "argb":
5205
+ return { a: color.a, b: color.b, colorType: "argb", g: color.g, r: color.r };
5206
+ case "theme":
5207
+ return { colorType: "theme", themeIndex: color.index, tint: color.tint };
5208
+ case "indexed":
5209
+ return { colorType: "indexed", paletteIndex: color.index };
5210
+ }
5211
+ }
5212
+ function mapDukeDrawingFont(font) {
5213
+ if (!font) {
5214
+ return void 0;
5078
5215
  }
5079
- return !["0", "false", "none", "off", "unchecked"].includes(normalized);
5216
+ return {
5217
+ bold: font.bold,
5218
+ charset: font.charset,
5219
+ color: mapDukeDrawingColor(font.color),
5220
+ family: font.family,
5221
+ italic: font.italic,
5222
+ name: font.name,
5223
+ scheme: font.scheme,
5224
+ size: font.size,
5225
+ strikethrough: font.strikethrough,
5226
+ underline: font.underline,
5227
+ verticalAlign: font.verticalAlign
5228
+ };
5080
5229
  }
5081
- function parseSpreadsheetBooleanNode(node) {
5082
- if (!node) {
5230
+ function mapDukeDrawingText(text) {
5231
+ return {
5232
+ horizontalAlignment: text.horizontalAlignment,
5233
+ runs: text.runs.map((run) => ({ font: mapDukeDrawingFont(run.font), text: run.text })),
5234
+ verticalAlignment: text.verticalAlignment
5235
+ };
5236
+ }
5237
+ function resolveDukeDrawingColor(color, themePalette) {
5238
+ if (!color) {
5083
5239
  return void 0;
5084
5240
  }
5085
- return parseSpreadsheetBooleanValue(node.getAttribute("val") ?? node.textContent);
5241
+ switch (color.colorType) {
5242
+ case "rgb":
5243
+ return resolveWorkbookColor({ rgb: [color.r, color.g, color.b].map((value) => value.toString(16).padStart(2, "0")).join("") }) ?? void 0;
5244
+ case "argb":
5245
+ return resolveWorkbookColor({ argb: [color.a, color.r, color.g, color.b].map((value) => value.toString(16).padStart(2, "0")).join("") }) ?? void 0;
5246
+ case "theme":
5247
+ return resolveWorkbookColor({ theme: color.index, tint: color.tint }, themePalette) ?? void 0;
5248
+ default:
5249
+ return void 0;
5250
+ }
5086
5251
  }
5087
- function mapDukeFormControlAnchor(anchor) {
5252
+ function resolveDukeDrawingText(text, themePalette) {
5253
+ const caption = mapDukeDrawingText(text);
5254
+ const label = normalizeControlLabel(text.runs.map((run) => run.text).join(""));
5255
+ const firstStyledRun = text.runs.find((run) => run.font)?.font;
5256
+ const horizontalAlignment = text.horizontalAlignment;
5088
5257
  return {
5089
- from: {
5090
- col: anchor.fromCol,
5091
- colOffsetEmu: anchor.fromColOffset,
5092
- row: anchor.fromRow,
5093
- rowOffsetEmu: anchor.fromRowOffset
5094
- },
5095
- kind: "two-cell",
5096
- to: {
5097
- col: anchor.toCol,
5098
- colOffsetEmu: anchor.toColOffset,
5099
- row: anchor.toRow,
5100
- rowOffsetEmu: anchor.toRowOffset
5101
- }
5258
+ caption,
5259
+ fontFamily: firstStyledRun?.name,
5260
+ fontSizePt: firstStyledRun?.size,
5261
+ label,
5262
+ textAlign: horizontalAlignment === "left" || horizontalAlignment === "center" || horizontalAlignment === "right" ? horizontalAlignment : void 0,
5263
+ textColor: resolveDukeDrawingColor(firstStyledRun?.color, themePalette)
5102
5264
  };
5103
5265
  }
5104
- function mapDukeFormControlKind(kind) {
5266
+ function mapDukeFormControlKind(kind, themePalette) {
5105
5267
  switch (kind.kind) {
5106
5268
  case "button":
5107
- return { caption: kind.caption, kind: "button", label: normalizeControlLabel(kind.caption) };
5269
+ return { ...resolveDukeDrawingText(kind.caption, themePalette), kind: "button" };
5108
5270
  case "checkbox":
5109
5271
  return {
5110
- caption: kind.caption,
5272
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5111
5273
  checked: kind.state === "checked",
5112
5274
  kind: "checkbox",
5113
- label: normalizeControlLabel(kind.caption),
5114
5275
  linkedCell: kind.cellLink,
5115
5276
  no3D: kind.no3D,
5116
5277
  state: kind.state
5117
5278
  };
5118
5279
  case "optionButton":
5119
5280
  return {
5120
- caption: kind.caption,
5281
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5121
5282
  checked: kind.state === "checked",
5122
5283
  firstInGroup: kind.firstInGroup,
5123
5284
  kind: "radio",
5124
- label: normalizeControlLabel(kind.caption),
5125
5285
  linkedCell: kind.cellLink,
5126
5286
  no3D: kind.no3D,
5127
5287
  state: kind.state
5128
5288
  };
5129
5289
  case "label":
5130
- return { caption: kind.caption, kind: "label", label: normalizeControlLabel(kind.caption) };
5290
+ return { ...resolveDukeDrawingText(kind.caption, themePalette), kind: "label" };
5131
5291
  case "groupBox":
5132
5292
  return {
5133
- caption: kind.caption,
5293
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5134
5294
  kind: "group-box",
5135
- label: normalizeControlLabel(kind.caption),
5136
5295
  no3D: kind.no3D
5137
5296
  };
5138
5297
  case "listBox":
@@ -5173,92 +5332,47 @@ function mapDukeFormControlKind(kind) {
5173
5332
  min: kind.min,
5174
5333
  value: kind.value
5175
5334
  };
5335
+ case "unknown":
5336
+ return {
5337
+ ...resolveDukeDrawingText(kind.caption, themePalette),
5338
+ kind: kind.objectType.toLowerCase() === "editbox" ? "editbox" : "unknown",
5339
+ legacyObjectType: kind.legacyObjectType,
5340
+ objectType: kind.objectType,
5341
+ rawObj: kind.rawObj,
5342
+ rawProperties: kind.rawProperties
5343
+ };
5176
5344
  }
5177
5345
  }
5178
- function mapDukeFormControl(control, controlIndex, workbookSheetIndex) {
5346
+ function mapDukeFormControl(control, controlIndex, workbookSheetIndex, themePalette) {
5179
5347
  return {
5180
- anchor: mapDukeFormControlAnchor(control.anchor),
5348
+ altText: control.altText,
5349
+ anchor: dukeDrawingAnchorToXlsxAnchor(control.anchor),
5181
5350
  controlIndex,
5182
- dukeAnchor: { ...control.anchor },
5183
- editAs: control.anchor.editAs,
5184
- hidden: false,
5351
+ editAs: control.anchor.type === "twoCell" ? control.anchor.editAs : void 0,
5352
+ hidden: control.hidden,
5185
5353
  id: `form-control-${workbookSheetIndex}-${controlIndex}`,
5186
5354
  locked: control.locked,
5355
+ macroName: control.formControl.macroName,
5187
5356
  name: control.name,
5188
5357
  printable: control.printable,
5358
+ rawClientData: control.formControl.rawClientData,
5189
5359
  sheetIndex: workbookSheetIndex,
5360
+ title: control.title,
5190
5361
  workbookSheetIndex,
5191
- zIndex: controlIndex + 1,
5192
- ...mapDukeFormControlKind(control.kind)
5362
+ zIndex: (control.drawingPath[0] ?? controlIndex) + 1,
5363
+ ...mapDukeFormControlKind(control.formControl.kind, themePalette)
5193
5364
  };
5194
5365
  }
5195
- function collectWorkbookFormControls(workbook) {
5366
+ function collectWorkbookFormControls(workbook, themePalette) {
5196
5367
  return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
5197
5368
  try {
5198
5369
  const controls = workbook.getSheet(workbookSheetIndex).formControls;
5199
- return Array.isArray(controls) ? controls.map((control, controlIndex) => mapDukeFormControl(control, controlIndex, workbookSheetIndex)) : [];
5370
+ return Array.isArray(controls) ? controls.flatMap((control, controlIndex) => control.anchor ? [mapDukeFormControl(control, controlIndex, workbookSheetIndex, themePalette)] : []) : [];
5200
5371
  } catch {
5201
5372
  return [];
5202
5373
  }
5203
5374
  });
5204
5375
  }
5205
- function parseFormControlKind(rawType) {
5206
- const normalized = (rawType ?? "").trim().toLowerCase();
5207
- switch (normalized) {
5208
- case "button":
5209
- return "button";
5210
- case "checkbox":
5211
- return "checkbox";
5212
- case "drop":
5213
- return "dropdown";
5214
- case "editbox":
5215
- return "editbox";
5216
- case "gbox":
5217
- return "group-box";
5218
- case "label":
5219
- return "label";
5220
- case "list":
5221
- return "listbox";
5222
- case "radio":
5223
- return "radio";
5224
- case "scroll":
5225
- return "scrollbar";
5226
- case "spin":
5227
- return "spinner";
5228
- default:
5229
- return "unknown";
5230
- }
5231
- }
5232
- function parseFormControlShapeId(value) {
5233
- const match = (value ?? "").match(/(\d+)(?!.*\d)/);
5234
- if (!match) {
5235
- return null;
5236
- }
5237
- const parsed = Number(match[1]);
5238
- return Number.isFinite(parsed) ? parsed : null;
5239
- }
5240
- function parseCssDeclarationValue(styleText, property) {
5241
- if (!styleText) {
5242
- return null;
5243
- }
5244
- const pattern = new RegExp(`${property}\\s*:\\s*([^;]+)`, "i");
5245
- const match = pattern.exec(styleText);
5246
- return match?.[1]?.trim() ?? null;
5247
- }
5248
- function parseControlTextAlign(styleText) {
5249
- const value = parseCssDeclarationValue(styleText, "text-align")?.toLowerCase();
5250
- if (value === "center" || value === "right") {
5251
- return value;
5252
- }
5253
- return value === "left" ? "left" : void 0;
5254
- }
5255
- function parseVmlFontSizePt(value) {
5256
- const parsed = Number(value ?? Number.NaN);
5257
- if (!Number.isFinite(parsed) || parsed <= 0) {
5258
- return void 0;
5259
- }
5260
- return parsed > 40 ? parsed / 20 : parsed;
5261
- }
5262
5376
  function normalizeControlLabel(label) {
5263
5377
  if (!label) {
5264
5378
  return void 0;
@@ -5266,16 +5380,6 @@ function normalizeControlLabel(label) {
5266
5380
  const normalized = label.replace(/\u00a0/g, " ").replace(/\s+/g, " ").trim();
5267
5381
  return normalized.length > 0 ? normalized : void 0;
5268
5382
  }
5269
- function isPlaceholderFormControlName(name) {
5270
- const normalized = normalizeControlLabel(name)?.toLowerCase();
5271
- if (!normalized) {
5272
- return false;
5273
- }
5274
- return /^(option button|group box|check box|drop down|dropdown|list box|edit box|scroll bar|spinner|spin button|button)\s+\d+$/.test(normalized);
5275
- }
5276
- function resolveNamedFormControlLabel(name) {
5277
- return isPlaceholderFormControlName(name) ? void 0 : normalizeControlLabel(name);
5278
- }
5279
5383
  function parseAnchor(anchorNode) {
5280
5384
  if (anchorNode.localName === "anchor") {
5281
5385
  const from = parseMarker(getFirstChild(anchorNode, "from"));
@@ -5956,260 +6060,8 @@ function parseDrawingObjects(archive, contentTypes, drawingPath, objectUrls, wor
5956
6060
  shapes
5957
6061
  };
5958
6062
  }
5959
- function parseSheetFormControlNodes(archive, sheetPath) {
5960
- const sheetXml = readArchiveText2(archive, sheetPath);
5961
- if (!sheetXml) {
5962
- return [];
5963
- }
5964
- const sheetDocument = parseXml2(sheetXml);
5965
- if (!sheetDocument) {
5966
- return [];
5967
- }
5968
- return getLocalElements(sheetDocument, "control").map((controlNode) => ({
5969
- anchor: parseAnchor(getFirstDescendant(controlNode, "anchor") ?? controlNode),
5970
- name: controlNode.getAttribute("name") ?? void 0,
5971
- shapeId: parseFormControlShapeId(controlNode.getAttribute("shapeId"))
5972
- }));
5973
- }
5974
- function parseVmlFormControls(archive, vmlDrawingPath) {
5975
- const xml = readArchiveText2(archive, vmlDrawingPath);
5976
- if (!xml) {
5977
- return /* @__PURE__ */ new Map();
5978
- }
5979
- const document2 = parseXml2(xml);
5980
- if (!document2) {
5981
- return /* @__PURE__ */ new Map();
5982
- }
5983
- const controls = /* @__PURE__ */ new Map();
5984
- for (const shapeNode of getLocalElements(document2, "shape")) {
5985
- const clientDataNode = getFirstChild(shapeNode, "ClientData");
5986
- if (!clientDataNode) {
5987
- continue;
5988
- }
5989
- const shapeId = parseFormControlShapeId(
5990
- shapeNode.getAttributeNS("urn:schemas-microsoft-com:office:office", "spid") ?? shapeNode.getAttribute("o:spid") ?? shapeNode.getAttribute("spid") ?? shapeNode.getAttribute("id")
5991
- );
5992
- if (shapeId === null) {
5993
- continue;
5994
- }
5995
- const styleText = shapeNode.getAttribute("style");
5996
- const textboxNode = getFirstChild(shapeNode, "textbox");
5997
- const fontNode = textboxNode ? getFirstDescendant(textboxNode, "font") : null;
5998
- const textContainerNode = textboxNode ? getFirstDescendant(textboxNode, "div") : null;
5999
- const label = normalizeControlLabel(textboxNode?.textContent);
6000
- const zIndex = Number(parseCssDeclarationValue(styleText, "z-index") ?? Number.NaN);
6001
- controls.set(shapeId, {
6002
- checked: parseSpreadsheetBooleanNode(getFirstChild(clientDataNode, "Checked")),
6003
- fontFamily: fontNode?.getAttribute("face") ?? void 0,
6004
- fontSizePt: parseVmlFontSizePt(fontNode?.getAttribute("size")),
6005
- hidden: (parseCssDeclarationValue(styleText, "visibility") ?? "").toLowerCase() === "hidden",
6006
- label,
6007
- linkedCell: normalizeControlLabel(getFirstChild(clientDataNode, "FmlaLink")?.textContent),
6008
- objectType: clientDataNode.getAttribute("ObjectType") ?? void 0,
6009
- shapeId,
6010
- textAlign: parseControlTextAlign(textContainerNode?.getAttribute("style")),
6011
- textColor: fontNode?.getAttribute("color") ?? void 0,
6012
- zIndex: Number.isFinite(zIndex) ? zIndex : controls.size + 1
6013
- });
6014
- }
6015
- return controls;
6016
- }
6017
- function parseSheetFormControls(archive, sheetPath, sheetRelationships, workbookSheetIndex, zIndexBase) {
6018
- const controlNodes = parseSheetFormControlNodes(archive, sheetPath);
6019
- if (controlNodes.length === 0) {
6020
- return [];
6021
- }
6022
- const legacyDrawingRelationship = [...sheetRelationships.values()].find(
6023
- (relationship) => relationship.type === VML_DRAWING_REL_TYPE
6024
- );
6025
- const vmlControlsByShapeId = legacyDrawingRelationship ? parseVmlFormControls(archive, legacyDrawingRelationship.target) : /* @__PURE__ */ new Map();
6026
- const parsedControls = [];
6027
- controlNodes.forEach((controlNode, index) => {
6028
- if (!controlNode.anchor) {
6029
- return;
6030
- }
6031
- const vmlControl = controlNode.shapeId !== null ? vmlControlsByShapeId.get(controlNode.shapeId) ?? null : null;
6032
- const kind = parseFormControlKind(vmlControl?.objectType);
6033
- parsedControls.push({
6034
- anchor: controlNode.anchor,
6035
- checked: vmlControl?.checked,
6036
- fontFamily: vmlControl?.fontFamily,
6037
- fontSizePt: vmlControl?.fontSizePt,
6038
- hidden: vmlControl?.hidden ?? false,
6039
- id: `form-control-${workbookSheetIndex}-${index}`,
6040
- kind,
6041
- label: vmlControl?.label,
6042
- linkedCell: vmlControl?.linkedCell,
6043
- name: controlNode.name,
6044
- sheetIndex: workbookSheetIndex,
6045
- textAlign: vmlControl?.textAlign,
6046
- textColor: vmlControl?.textColor,
6047
- workbookSheetIndex,
6048
- zIndex: zIndexBase + (vmlControl?.zIndex ?? index + 1)
6049
- });
6050
- });
6051
- return parsedControls.sort((left, right) => left.zIndex - right.zIndex);
6052
- }
6053
- function flattenShapeText(shape) {
6054
- const text = shape.paragraphs.flatMap((paragraph) => paragraph.runs.map((run) => run.text)).join(" ");
6055
- return normalizeControlLabel(text);
6056
- }
6057
- function rectArea(rect) {
6058
- return Math.max(0, rect.cx) * Math.max(0, rect.cy);
6059
- }
6060
- function rectIntersectionArea(left, right) {
6061
- const overlapX = Math.max(0, Math.min(left.x + left.cx, right.x + right.cx) - Math.max(left.x, right.x));
6062
- const overlapY = Math.max(0, Math.min(left.y + left.cy, right.y + right.cy) - Math.max(left.y, right.y));
6063
- return overlapX * overlapY;
6064
- }
6065
- function rectCenterDistance(left, right) {
6066
- const leftCenterX = left.x + left.cx / 2;
6067
- const leftCenterY = left.y + left.cy / 2;
6068
- const rightCenterX = right.x + right.cx / 2;
6069
- const rightCenterY = right.y + right.cy / 2;
6070
- return Math.hypot(leftCenterX - rightCenterX, leftCenterY - rightCenterY);
6071
- }
6072
- function findHiddenShapeControlMatch(control, shapes, sheetState) {
6073
- const controlRect = anchorToAbsoluteRect(control.anchor, sheetState);
6074
- const controlArea = Math.max(1, rectArea(controlRect));
6075
- const placeholderName = isPlaceholderFormControlName(control.name);
6076
- let bestMatch = null;
6077
- let bestScore = Number.NEGATIVE_INFINITY;
6078
- shapes.forEach((shape) => {
6079
- if (!shape.hidden) {
6080
- return;
6081
- }
6082
- const shapeRect = anchorToAbsoluteRect(shape.anchor, sheetState);
6083
- const shapeArea = Math.max(1, rectArea(shapeRect));
6084
- const intersectionArea = rectIntersectionArea(controlRect, shapeRect);
6085
- const overlapScore = intersectionArea / Math.min(controlArea, shapeArea);
6086
- const distance = rectCenterDistance(controlRect, shapeRect);
6087
- const maxDimension = Math.max(controlRect.cx, controlRect.cy, shapeRect.cx, shapeRect.cy, 1);
6088
- const distanceScore = distance / maxDimension;
6089
- const textLabel = flattenShapeText(shape);
6090
- const sameName = normalizeControlLabel(shape.name)?.toLowerCase() === normalizeControlLabel(control.name)?.toLowerCase();
6091
- let score = overlapScore * 4 - distanceScore;
6092
- if (sameName) {
6093
- score += 1;
6094
- }
6095
- if (control.kind === "group-box") {
6096
- score += textLabel ? -3 : 0.5;
6097
- score += shape.stroke?.none ? -1.5 : 0.75;
6098
- } else {
6099
- score += textLabel ? 2 : -1;
6100
- score += shape.stroke?.none ? 0.2 : -0.5;
6101
- }
6102
- if (!placeholderName && textLabel && textLabel === resolveNamedFormControlLabel(control.name)) {
6103
- score += 0.5;
6104
- }
6105
- if (score > bestScore) {
6106
- bestMatch = shape;
6107
- bestScore = score;
6108
- }
6109
- });
6110
- return bestScore >= 0.25 ? bestMatch : null;
6111
- }
6112
- function enrichFormControlsWithHiddenShapes(formControls, shapes, sheetState) {
6113
- return formControls.map((control) => {
6114
- const matchedShape = findHiddenShapeControlMatch(control, shapes, sheetState);
6115
- const matchedLabel = matchedShape ? flattenShapeText(matchedShape) : void 0;
6116
- const fallbackLabel = resolveNamedFormControlLabel(control.name);
6117
- let resolvedAnchor = control.anchor;
6118
- if (matchedShape && (control.kind === "group-box" || matchedLabel || isPlaceholderFormControlName(control.name))) {
6119
- resolvedAnchor = matchedShape.anchor;
6120
- }
6121
- return {
6122
- ...control,
6123
- anchor: resolvedAnchor,
6124
- label: control.label ?? matchedLabel ?? fallbackLabel
6125
- };
6126
- });
6127
- }
6128
- function getFormControlAnchorStart(anchor) {
6129
- return anchor.kind === "absolute" ? null : { col: anchor.from.col, row: anchor.from.row };
6130
- }
6131
- function scoreParsedFormControlMatch(dukeControl, parsedControl, parsedIndex) {
6132
- if (dukeControl.kind !== parsedControl.kind && parsedControl.kind !== "unknown") {
6133
- return Number.NEGATIVE_INFINITY;
6134
- }
6135
- let score = 10;
6136
- const dukeName = normalizeControlLabel(dukeControl.name)?.toLowerCase();
6137
- const parsedName = normalizeControlLabel(parsedControl.name)?.toLowerCase();
6138
- if (dukeName && parsedName) {
6139
- if (dukeName !== parsedName) {
6140
- return Number.NEGATIVE_INFINITY;
6141
- }
6142
- score += 100;
6143
- }
6144
- const dukeStart = getFormControlAnchorStart(dukeControl.anchor);
6145
- const parsedStart = getFormControlAnchorStart(parsedControl.anchor);
6146
- if (dukeStart && parsedStart && dukeStart.col === parsedStart.col && dukeStart.row === parsedStart.row) {
6147
- score += 40;
6148
- }
6149
- if (dukeControl.controlIndex === parsedIndex) {
6150
- score += 5;
6151
- }
6152
- return score;
6153
- }
6154
- function areDukeFormControlAnchorsEqual(left, right) {
6155
- if (!left || !right) {
6156
- return true;
6157
- }
6158
- return left.editAs === right.editAs && left.fromCol === right.fromCol && left.fromColOffset === right.fromColOffset && left.fromRow === right.fromRow && left.fromRowOffset === right.fromRowOffset && left.toCol === right.toCol && left.toColOffset === right.toColOffset && left.toRow === right.toRow && left.toRowOffset === right.toRowOffset;
6159
- }
6160
- function mergeDukeAndParsedFormControls(dukeControls, parsedControls, keepUnmatchedParsedControl = () => true) {
6161
- const usedParsedIndexes = /* @__PURE__ */ new Set();
6162
- const mergedControls = dukeControls.map((dukeControl) => {
6163
- let bestIndex = -1;
6164
- let bestScore = Number.NEGATIVE_INFINITY;
6165
- parsedControls.forEach((parsedControl2, parsedIndex) => {
6166
- if (usedParsedIndexes.has(parsedIndex)) {
6167
- return;
6168
- }
6169
- const score = scoreParsedFormControlMatch(dukeControl, parsedControl2, parsedIndex);
6170
- if (score > bestScore) {
6171
- bestIndex = parsedIndex;
6172
- bestScore = score;
6173
- }
6174
- });
6175
- const parsedControl = bestIndex >= 0 && bestScore >= 10 ? parsedControls[bestIndex] : null;
6176
- if (!parsedControl) {
6177
- return dukeControl;
6178
- }
6179
- usedParsedIndexes.add(bestIndex);
6180
- const dukeAnchorChanged = !areDukeFormControlAnchorsEqual(
6181
- parsedControl.dukeAnchor,
6182
- dukeControl.dukeAnchor
6183
- );
6184
- return {
6185
- ...parsedControl,
6186
- ...dukeControl,
6187
- anchor: dukeAnchorChanged ? dukeControl.anchor : parsedControl.anchor,
6188
- fontFamily: parsedControl.fontFamily,
6189
- fontSizePt: parsedControl.fontSizePt,
6190
- hidden: parsedControl.hidden,
6191
- id: parsedControl.id,
6192
- label: dukeControl.label ?? parsedControl.label,
6193
- name: dukeControl.name ?? parsedControl.name,
6194
- textAlign: parsedControl.textAlign,
6195
- textColor: parsedControl.textColor,
6196
- zIndex: parsedControl.zIndex
6197
- };
6198
- });
6199
- parsedControls.forEach((parsedControl, parsedIndex) => {
6200
- if (!usedParsedIndexes.has(parsedIndex) && keepUnmatchedParsedControl(parsedControl)) {
6201
- mergedControls.push(parsedControl);
6202
- }
6203
- });
6204
- return mergedControls.sort((left, right) => left.zIndex - right.zIndex);
6205
- }
6206
- function refreshWorkbookFormControls(workbook, currentControlsByWorkbookSheetIndex) {
6207
- const dukeControlsByWorkbookSheetIndex = collectWorkbookFormControls(workbook);
6208
- return dukeControlsByWorkbookSheetIndex.map((dukeControls, workbookSheetIndex) => mergeDukeAndParsedFormControls(
6209
- dukeControls,
6210
- currentControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6211
- (control) => control.controlIndex === void 0
6212
- ));
6063
+ function refreshWorkbookFormControls(workbook, _currentControlsByWorkbookSheetIndex, themePalette) {
6064
+ return collectWorkbookFormControls(workbook, themePalette);
6213
6065
  }
6214
6066
  function revokeWorkbookImageAssets(assets) {
6215
6067
  if (!assets) {
@@ -6243,7 +6095,7 @@ function parseWorkbookStructureAssetsFromArchive(archive, options) {
6243
6095
  workbookSheets
6244
6096
  };
6245
6097
  }
6246
- function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6098
+ function parseWorkbookImageAssets(bytes, workbook) {
6247
6099
  const archive = (0, import_fflate2.unzipSync)(bytes);
6248
6100
  const {
6249
6101
  contentTypes,
@@ -6262,7 +6114,7 @@ function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6262
6114
  const shapesByWorkbookSheetIndex = [];
6263
6115
  const sheetOrigins = [];
6264
6116
  const imageOriginsById = /* @__PURE__ */ new Map();
6265
- const dukeFormControlsByWorkbookSheetIndex = Array.isArray(workbookOrFormControls) ? workbookOrFormControls : collectWorkbookFormControls(workbookOrFormControls);
6117
+ const formControls = workbook ? collectWorkbookFormControls(workbook, themePalette) : [];
6266
6118
  workbookSheets.forEach((sheet, workbookSheetIndex) => {
6267
6119
  const sheetRelationships = parseRelationships(archive, relsPathForDocument(sheet.path), sheet.path);
6268
6120
  const attachments = [];
@@ -6295,23 +6147,8 @@ function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6295
6147
  mediaPaths: drawingImages.mediaPaths
6296
6148
  });
6297
6149
  }
6298
- const formControlList = parseSheetFormControls(
6299
- archive,
6300
- sheet.path,
6301
- sheetRelationships,
6302
- workbookSheetIndex,
6303
- zIndexBase
6304
- );
6305
6150
  const visibleShapeList = shapeList.filter((shape) => !shape.hidden);
6306
- const enrichedFormControlList = enrichFormControlsWithHiddenShapes(
6307
- formControlList,
6308
- shapeList,
6309
- sheetStatesByWorkbookSheetIndex[workbookSheetIndex] ?? null
6310
- );
6311
- formControlsByWorkbookSheetIndex[workbookSheetIndex] = mergeDukeAndParsedFormControls(
6312
- dukeFormControlsByWorkbookSheetIndex[workbookSheetIndex] ?? [],
6313
- enrichedFormControlList
6314
- );
6151
+ formControlsByWorkbookSheetIndex[workbookSheetIndex] = formControls[workbookSheetIndex] ?? [];
6315
6152
  imagesByWorkbookSheetIndex[workbookSheetIndex] = imageList;
6316
6153
  shapesByWorkbookSheetIndex[workbookSheetIndex] = visibleShapeList;
6317
6154
  sheetOrigins[workbookSheetIndex] = attachments.length > 0 ? {
@@ -6321,6 +6158,7 @@ function parseWorkbookImageAssets(bytes, workbookOrFormControls) {
6321
6158
  });
6322
6159
  return {
6323
6160
  archive,
6161
+ dirtyArchivePaths: /* @__PURE__ */ new Set(),
6324
6162
  formControlsByWorkbookSheetIndex,
6325
6163
  imageOriginsById,
6326
6164
  imagesByWorkbookSheetIndex,
@@ -6383,6 +6221,7 @@ function updateWorkbookImageAnchor(assets, imageId, anchor) {
6383
6221
  }
6384
6222
  updateAnchorNode2(anchorNode, anchor);
6385
6223
  assets.archive[attachment.drawingPath] = (0, import_fflate2.strToU8)(serializeXml2(drawingDocument));
6224
+ assets.dirtyArchivePaths.add(normalizeArchivePath2(attachment.drawingPath));
6386
6225
  const imageList = assets.imagesByWorkbookSheetIndex[origin.workbookSheetIndex] ?? [];
6387
6226
  const imageIndex = imageList.findIndex((image) => image.id === imageId);
6388
6227
  if (imageIndex >= 0) {
@@ -6499,7 +6338,7 @@ function appendSheetDrawingReference(sheetDocument, relationshipId) {
6499
6338
  worksheet.appendChild(drawingNode);
6500
6339
  }
6501
6340
  function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6502
- if (!sourceAssets || sheetOrigins.every((origin) => !origin?.attachments.length)) {
6341
+ if (!sourceAssets || sourceAssets.dirtyArchivePaths.size === 0) {
6503
6342
  return cloneBytes(savedBytes);
6504
6343
  }
6505
6344
  try {
@@ -6511,7 +6350,8 @@ function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6511
6350
  return cloneBytes(savedBytes);
6512
6351
  }
6513
6352
  sheetOrigins.forEach((origin, workbookSheetIndex) => {
6514
- if (!origin?.attachments.length) {
6353
+ const hasDirtyAttachment = origin?.attachments.some((attachment) => sourceAssets.dirtyArchivePaths.has(normalizeArchivePath2(attachment.drawingPath)) || attachment.drawingRelsPath && sourceAssets.dirtyArchivePaths.has(normalizeArchivePath2(attachment.drawingRelsPath)) || attachment.mediaPaths.some((path) => sourceAssets.dirtyArchivePaths.has(normalizeArchivePath2(path))));
6354
+ if (!origin?.attachments.length || !hasDirtyAttachment) {
6515
6355
  return;
6516
6356
  }
6517
6357
  const currentSheet = workbookSheets[workbookSheetIndex];
@@ -6563,6 +6403,14 @@ function mergeWorkbookImageAssets(savedBytes, sourceAssets, sheetOrigins) {
6563
6403
  archive[relsPath] = (0, import_fflate2.strToU8)(serializeXml2(relDocument));
6564
6404
  mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, relsPath);
6565
6405
  });
6406
+ for (const dirtyPath of sourceAssets.dirtyArchivePaths) {
6407
+ const bytes = sourceAssets.archive[dirtyPath];
6408
+ if (!bytes) {
6409
+ continue;
6410
+ }
6411
+ archive[dirtyPath] = cloneBytes(bytes);
6412
+ mergeContentTypeForPath(targetContentTypesDocument, originalContentTypesDocument, dirtyPath);
6413
+ }
6566
6414
  const hasDrawingOverride = getLocalElements(targetContentTypesDocument, "Override").some(
6567
6415
  (node) => node.getAttribute("ContentType") === DRAWING_CONTENT_TYPE
6568
6416
  );
@@ -6953,6 +6801,204 @@ function cloneArrayBufferForTransfer(buffer) {
6953
6801
  return buffer.slice(0);
6954
6802
  }
6955
6803
 
6804
+ // src/zip-entry-names.ts
6805
+ var BACKSLASH_BYTE = 92;
6806
+ var FORWARD_SLASH_BYTE = 47;
6807
+ var END_OF_CENTRAL_DIRECTORY_SIGNATURE = 101010256;
6808
+ var CENTRAL_FILE_HEADER_SIGNATURE = 33639248;
6809
+ var LOCAL_FILE_HEADER_SIGNATURE = 67324752;
6810
+ var END_OF_CENTRAL_DIRECTORY_LENGTH = 22;
6811
+ var CENTRAL_FILE_HEADER_LENGTH = 46;
6812
+ var LOCAL_FILE_HEADER_LENGTH = 30;
6813
+ var MAX_ZIP_COMMENT_LENGTH = 65535;
6814
+ var ZIP16_SENTINEL = 65535;
6815
+ var ZIP32_SENTINEL = 4294967295;
6816
+ function findEndOfCentralDirectoryOffset(view) {
6817
+ const firstPossibleOffset = Math.max(
6818
+ 0,
6819
+ view.byteLength - END_OF_CENTRAL_DIRECTORY_LENGTH - MAX_ZIP_COMMENT_LENGTH
6820
+ );
6821
+ for (let offset = view.byteLength - END_OF_CENTRAL_DIRECTORY_LENGTH; offset >= firstPossibleOffset; offset -= 1) {
6822
+ if (view.getUint32(offset, true) !== END_OF_CENTRAL_DIRECTORY_SIGNATURE) {
6823
+ continue;
6824
+ }
6825
+ const commentLength = view.getUint16(offset + 20, true);
6826
+ if (offset + END_OF_CENTRAL_DIRECTORY_LENGTH + commentLength === view.byteLength) {
6827
+ return offset;
6828
+ }
6829
+ }
6830
+ return null;
6831
+ }
6832
+ function readCentralDirectoryInfo(view) {
6833
+ if (view.byteLength < END_OF_CENTRAL_DIRECTORY_LENGTH) {
6834
+ return null;
6835
+ }
6836
+ const endOfCentralDirectoryOffset = findEndOfCentralDirectoryOffset(view);
6837
+ if (endOfCentralDirectoryOffset === null) {
6838
+ return null;
6839
+ }
6840
+ const diskNumber = view.getUint16(endOfCentralDirectoryOffset + 4, true);
6841
+ const centralDirectoryDiskNumber = view.getUint16(endOfCentralDirectoryOffset + 6, true);
6842
+ const entriesOnDisk = view.getUint16(endOfCentralDirectoryOffset + 8, true);
6843
+ const entryCount = view.getUint16(endOfCentralDirectoryOffset + 10, true);
6844
+ const centralDirectorySize = view.getUint32(endOfCentralDirectoryOffset + 12, true);
6845
+ const centralDirectoryOffset = view.getUint32(endOfCentralDirectoryOffset + 16, true);
6846
+ if (diskNumber !== 0 || centralDirectoryDiskNumber !== 0 || entriesOnDisk !== entryCount || entryCount === ZIP16_SENTINEL || centralDirectorySize === ZIP32_SENTINEL || centralDirectoryOffset === ZIP32_SENTINEL) {
6847
+ return null;
6848
+ }
6849
+ const centralDirectoryEndOffset = centralDirectoryOffset + centralDirectorySize;
6850
+ if (centralDirectoryOffset > endOfCentralDirectoryOffset || centralDirectoryEndOffset > endOfCentralDirectoryOffset || centralDirectoryEndOffset > view.byteLength) {
6851
+ return null;
6852
+ }
6853
+ return {
6854
+ endOffset: centralDirectoryEndOffset,
6855
+ entryCount,
6856
+ offset: centralDirectoryOffset
6857
+ };
6858
+ }
6859
+ function readCentralDirectoryEntry(view, centralDirectoryEndOffset, offset) {
6860
+ if (offset < 0 || offset + CENTRAL_FILE_HEADER_LENGTH > centralDirectoryEndOffset || offset + CENTRAL_FILE_HEADER_LENGTH > view.byteLength || view.getUint32(offset, true) !== CENTRAL_FILE_HEADER_SIGNATURE) {
6861
+ return null;
6862
+ }
6863
+ const nameLength = view.getUint16(offset + 28, true);
6864
+ const extraLength = view.getUint16(offset + 30, true);
6865
+ const commentLength = view.getUint16(offset + 32, true);
6866
+ const nameStart = offset + CENTRAL_FILE_HEADER_LENGTH;
6867
+ const nameEnd = nameStart + nameLength;
6868
+ const nextOffset = nameEnd + extraLength + commentLength;
6869
+ if (nameEnd > centralDirectoryEndOffset || nextOffset > centralDirectoryEndOffset) {
6870
+ return null;
6871
+ }
6872
+ return {
6873
+ diskNumberStart: view.getUint16(offset + 34, true),
6874
+ localHeaderOffset: view.getUint32(offset + 42, true),
6875
+ nameEnd,
6876
+ nameStart,
6877
+ nextOffset
6878
+ };
6879
+ }
6880
+ function entryNameUsesBackslash(bytes, entry) {
6881
+ for (let cursor = entry.nameStart; cursor < entry.nameEnd; cursor += 1) {
6882
+ if (bytes[cursor] === BACKSLASH_BYTE) {
6883
+ return true;
6884
+ }
6885
+ }
6886
+ return false;
6887
+ }
6888
+ function centralDirectoryUsesBackslashSeparators(bytes, view, centralDirectory) {
6889
+ let offset = centralDirectory.offset;
6890
+ for (let index = 0; index < centralDirectory.entryCount; index += 1) {
6891
+ const entry = readCentralDirectoryEntry(view, centralDirectory.endOffset, offset);
6892
+ if (!entry) {
6893
+ return null;
6894
+ }
6895
+ if (entryNameUsesBackslash(bytes, entry)) {
6896
+ return true;
6897
+ }
6898
+ offset = entry.nextOffset;
6899
+ }
6900
+ return false;
6901
+ }
6902
+ function normalizedEntryNameKey(bytes, entry) {
6903
+ let key = "";
6904
+ for (let cursor = entry.nameStart; cursor < entry.nameEnd; cursor += 1) {
6905
+ const byte = bytes[cursor] === BACKSLASH_BYTE ? FORWARD_SLASH_BYTE : bytes[cursor];
6906
+ key += String.fromCharCode(byte);
6907
+ }
6908
+ return key;
6909
+ }
6910
+ function validateEntryNamePatches(bytes, view, centralDirectory) {
6911
+ const normalizedNames = /* @__PURE__ */ new Set();
6912
+ const localHeaderOffsets = /* @__PURE__ */ new Set();
6913
+ let offset = centralDirectory.offset;
6914
+ for (let index = 0; index < centralDirectory.entryCount; index += 1) {
6915
+ const entry = readCentralDirectoryEntry(view, centralDirectory.endOffset, offset);
6916
+ if (!entry || entry.diskNumberStart !== 0 || entry.localHeaderOffset === ZIP32_SENTINEL || entry.localHeaderOffset + LOCAL_FILE_HEADER_LENGTH > centralDirectory.offset || localHeaderOffsets.has(entry.localHeaderOffset)) {
6917
+ return false;
6918
+ }
6919
+ localHeaderOffsets.add(entry.localHeaderOffset);
6920
+ const normalizedName = normalizedEntryNameKey(bytes, entry);
6921
+ if (normalizedNames.has(normalizedName)) {
6922
+ return false;
6923
+ }
6924
+ normalizedNames.add(normalizedName);
6925
+ if (!entryNameUsesBackslash(bytes, entry)) {
6926
+ offset = entry.nextOffset;
6927
+ continue;
6928
+ }
6929
+ if (view.getUint32(entry.localHeaderOffset, true) !== LOCAL_FILE_HEADER_SIGNATURE) {
6930
+ return false;
6931
+ }
6932
+ const localNameLength = view.getUint16(entry.localHeaderOffset + 26, true);
6933
+ const localNameStart = entry.localHeaderOffset + LOCAL_FILE_HEADER_LENGTH;
6934
+ const localNameEnd = localNameStart + localNameLength;
6935
+ if (localNameLength !== entry.nameEnd - entry.nameStart || localNameEnd > centralDirectory.offset) {
6936
+ return false;
6937
+ }
6938
+ for (let nameIndex = 0; nameIndex < localNameLength; nameIndex += 1) {
6939
+ const centralNameOffset = entry.nameStart + nameIndex;
6940
+ const localNameOffset = localNameStart + nameIndex;
6941
+ if (bytes[centralNameOffset] !== bytes[localNameOffset]) {
6942
+ return false;
6943
+ }
6944
+ }
6945
+ offset = entry.nextOffset;
6946
+ }
6947
+ return true;
6948
+ }
6949
+ function patchEntryNameSeparators(bytes, view, centralDirectory) {
6950
+ let offset = centralDirectory.offset;
6951
+ for (let index = 0; index < centralDirectory.entryCount; index += 1) {
6952
+ const entry = readCentralDirectoryEntry(view, centralDirectory.endOffset, offset);
6953
+ if (!entry) {
6954
+ return;
6955
+ }
6956
+ if (entryNameUsesBackslash(bytes, entry)) {
6957
+ const localNameStart = entry.localHeaderOffset + LOCAL_FILE_HEADER_LENGTH;
6958
+ for (let nameIndex = 0; nameIndex < entry.nameEnd - entry.nameStart; nameIndex += 1) {
6959
+ const centralNameOffset = entry.nameStart + nameIndex;
6960
+ if (bytes[centralNameOffset] === BACKSLASH_BYTE) {
6961
+ bytes[centralNameOffset] = FORWARD_SLASH_BYTE;
6962
+ bytes[localNameStart + nameIndex] = FORWARD_SLASH_BYTE;
6963
+ }
6964
+ }
6965
+ }
6966
+ offset = entry.nextOffset;
6967
+ }
6968
+ }
6969
+ function normalizeZipEntryNames(bytes) {
6970
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
6971
+ const centralDirectory = readCentralDirectoryInfo(view);
6972
+ if (!centralDirectory || centralDirectoryUsesBackslashSeparators(bytes, view, centralDirectory) !== true) {
6973
+ return bytes;
6974
+ }
6975
+ if (!validateEntryNamePatches(bytes, view, centralDirectory)) {
6976
+ return bytes;
6977
+ }
6978
+ const normalizedBytes = new Uint8Array(bytes.byteLength);
6979
+ normalizedBytes.set(bytes);
6980
+ const normalizedView = new DataView(
6981
+ normalizedBytes.buffer,
6982
+ normalizedBytes.byteOffset,
6983
+ normalizedBytes.byteLength
6984
+ );
6985
+ patchEntryNameSeparators(normalizedBytes, normalizedView, centralDirectory);
6986
+ return normalizedBytes;
6987
+ }
6988
+ function normalizeWorkbookArrayBuffer(buffer) {
6989
+ const bytes = new Uint8Array(buffer);
6990
+ const normalizedBytes = normalizeZipEntryNames(bytes);
6991
+ if (normalizedBytes === bytes) {
6992
+ return buffer;
6993
+ }
6994
+ if (normalizedBytes.byteOffset === 0 && normalizedBytes.byteLength === normalizedBytes.buffer.byteLength && normalizedBytes.buffer instanceof ArrayBuffer) {
6995
+ return normalizedBytes.buffer;
6996
+ }
6997
+ const normalizedBuffer = new ArrayBuffer(normalizedBytes.byteLength);
6998
+ new Uint8Array(normalizedBuffer).set(normalizedBytes);
6999
+ return normalizedBuffer;
7000
+ }
7001
+
6956
7002
  // src/controller.tsx
6957
7003
  var FORMULA_COUNT_THRESHOLD = 1e3;
6958
7004
  var DEFAULT_ROW_HEIGHT = 24;
@@ -6969,8 +7015,6 @@ var DEFAULT_MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
6969
7015
  var MAX_INTERACTIVE_WORKSHEET_XML_BYTES = 200 * 1024 * 1024;
6970
7016
  var MAX_INTERACTIVE_SHARED_STRINGS_BYTES = 50 * 1024 * 1024;
6971
7017
  var MAX_INTERACTIVE_TOTAL_XML_BYTES = 256 * 1024 * 1024;
6972
- var EMU_PER_PIXEL3 = 9525;
6973
- var IMAGE_BATCH_ROW_COUNT = 256;
6974
7018
  var DEFAULT_ZOOM_SCALE = 100;
6975
7019
  var MIN_ZOOM_SCALE = 10;
6976
7020
  var MAX_ZOOM_SCALE = 400;
@@ -7425,104 +7469,175 @@ function resolveWorkbookReference(workbook, defaultWorkbookSheetIndex, rawRefere
7425
7469
  return null;
7426
7470
  }
7427
7471
  }
7428
- function formControlInputFromDukeControl(control) {
7429
- return {
7430
- anchor: { ...control.anchor },
7431
- kind: { ...control.kind },
7432
- locked: control.locked,
7433
- name: control.name,
7434
- printable: control.printable
7435
- };
7436
- }
7437
- function getFormControlCellLink(kind) {
7438
- return "cellLink" in kind ? kind.cellLink : void 0;
7472
+ function parseColorHex(hex) {
7473
+ const normalized = hex?.replace(/^#/, "");
7474
+ return normalized && /^[0-9a-f]{6}$/i.test(normalized) ? [0, 2, 4].map((offset) => Number.parseInt(normalized.slice(offset, offset + 2), 16)) : null;
7439
7475
  }
7440
- function anchorContainsControl(group, control) {
7441
- const groupAnchor = group.anchor;
7442
- const controlAnchor = control.anchor;
7443
- return controlAnchor.fromCol >= groupAnchor.fromCol && controlAnchor.fromCol <= groupAnchor.toCol && controlAnchor.fromRow >= groupAnchor.fromRow && controlAnchor.fromRow <= groupAnchor.toRow;
7444
- }
7445
- function resolveOptionButtonGroupIndexes(controls, targetIndex) {
7446
- const target = controls[targetIndex];
7447
- if (!target || target.kind.kind !== "optionButton") {
7448
- return [];
7449
- }
7450
- if (target.kind.cellLink) {
7451
- const targetCellLink = target.kind.cellLink;
7452
- return controls.flatMap((control, index) => control.kind.kind === "optionButton" && control.kind.cellLink === targetCellLink ? [index] : []);
7453
- }
7454
- const containingGroups = controls.map((control, index) => ({ control, index })).filter(({ control }) => control.kind.kind === "groupBox" && anchorContainsControl(control, target)).sort((left, right) => {
7455
- const leftArea = (left.control.anchor.toCol - left.control.anchor.fromCol) * (left.control.anchor.toRow - left.control.anchor.fromRow);
7456
- const rightArea = (right.control.anchor.toCol - right.control.anchor.fromCol) * (right.control.anchor.toRow - right.control.anchor.fromRow);
7457
- return leftArea - rightArea;
7458
- });
7459
- const containingGroup = containingGroups[0]?.control;
7460
- if (containingGroup) {
7461
- return controls.flatMap((control, index) => control.kind.kind === "optionButton" && anchorContainsControl(containingGroup, control) ? [index] : []);
7462
- }
7463
- const optionIndexes = controls.flatMap((control, index) => control.kind.kind === "optionButton" ? [index] : []);
7464
- const targetPosition = optionIndexes.indexOf(targetIndex);
7465
- if (targetPosition < 0) {
7466
- return [];
7476
+ function xlsxColorToDukeColor(color) {
7477
+ if (!color) {
7478
+ return void 0;
7467
7479
  }
7468
- let startPosition = targetPosition;
7469
- while (startPosition > 0) {
7470
- const control = controls[optionIndexes[startPosition] ?? -1];
7471
- if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7472
- break;
7480
+ const normalizedHex = color.hex?.replace(/^#/, "");
7481
+ 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);
7482
+ switch (colorType) {
7483
+ case "auto":
7484
+ return { colorType: "auto" };
7485
+ case "rgb": {
7486
+ const channels = color.r !== void 0 && color.g !== void 0 && color.b !== void 0 ? [color.r, color.g, color.b] : parseColorHex(color.hex);
7487
+ return channels ? { b: channels[2], colorType: "rgb", g: channels[1], r: channels[0] } : void 0;
7473
7488
  }
7474
- startPosition -= 1;
7475
- }
7476
- let endPosition = targetPosition + 1;
7477
- while (endPosition < optionIndexes.length) {
7478
- const control = controls[optionIndexes[endPosition] ?? -1];
7479
- if (control?.kind.kind === "optionButton" && control.kind.firstInGroup) {
7480
- break;
7489
+ case "argb": {
7490
+ const normalized = color.hex?.replace(/^#/, "");
7491
+ 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;
7492
+ return channels ? { a: channels[0], b: channels[3], colorType: "argb", g: channels[2], r: channels[1] } : void 0;
7481
7493
  }
7482
- endPosition += 1;
7494
+ case "theme":
7495
+ return color.themeIndex === void 0 ? void 0 : { colorType: "theme", index: color.themeIndex, tint: color.tint ?? 0 };
7496
+ case "indexed":
7497
+ return color.paletteIndex === void 0 ? void 0 : { colorType: "indexed", index: color.paletteIndex };
7483
7498
  }
7484
- return optionIndexes.slice(startPosition, endPosition);
7485
7499
  }
7486
- function resolveLinkedCellValue(controls, controlIndex) {
7487
- const control = controls[controlIndex];
7488
- if (!control) {
7489
- return void 0;
7500
+ function xlsxFontToDukeFont(font) {
7501
+ return {
7502
+ bold: font.bold,
7503
+ charset: font.charset,
7504
+ color: xlsxColorToDukeColor(font.color),
7505
+ family: font.family,
7506
+ italic: font.italic,
7507
+ name: font.name,
7508
+ scheme: font.scheme,
7509
+ size: font.size,
7510
+ strikethrough: font.strikethrough,
7511
+ underline: font.underline,
7512
+ verticalAlign: font.verticalAlign
7513
+ };
7514
+ }
7515
+ function xlsxCaptionToDukeText(caption) {
7516
+ if (typeof caption === "string") {
7517
+ return { runs: [{ text: caption }] };
7490
7518
  }
7491
- switch (control.kind.kind) {
7519
+ return {
7520
+ horizontalAlignment: caption.horizontalAlignment,
7521
+ runs: caption.runs.map((run) => ({
7522
+ font: run.font ? xlsxFontToDukeFont(run.font) : void 0,
7523
+ text: run.text
7524
+ })),
7525
+ verticalAlignment: caption.verticalAlignment
7526
+ };
7527
+ }
7528
+ function xlsxFormControlKindToDukeKind(kind) {
7529
+ switch (kind.kind) {
7530
+ case "button":
7531
+ case "label":
7532
+ return { caption: xlsxCaptionToDukeText(kind.caption), kind: kind.kind };
7492
7533
  case "checkbox":
7493
- return control.kind.state === "mixed" ? "#N/A" : control.kind.state === "checked";
7494
- case "optionButton": {
7495
- const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
7496
- const checkedIndex = groupIndexes.find((index) => controls[index]?.kind.kind === "optionButton" && controls[index]?.kind.state === "checked");
7497
- const groupPosition = checkedIndex === void 0 ? -1 : groupIndexes.indexOf(checkedIndex);
7498
- return groupPosition >= 0 ? groupPosition + 1 : "";
7499
- }
7500
- case "dropdown":
7501
- return control.kind.selected === void 0 ? "" : control.kind.selected + 1;
7502
- case "listBox":
7503
- return control.kind.selection === "single" && control.kind.selected.length === 1 ? (control.kind.selected[0] ?? -1) + 1 : "";
7504
- case "scrollbar":
7505
- case "spinner":
7506
- return control.kind.value;
7534
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7535
+ case "optionButton":
7536
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7537
+ case "groupBox":
7538
+ return { ...kind, caption: xlsxCaptionToDukeText(kind.caption) };
7539
+ case "editbox":
7540
+ return {
7541
+ caption: xlsxCaptionToDukeText(kind.caption),
7542
+ kind: "unknown",
7543
+ legacyObjectType: kind.legacyObjectType,
7544
+ objectType: "EditBox",
7545
+ rawObj: kind.rawObj,
7546
+ rawProperties: kind.rawProperties
7547
+ };
7548
+ case "unknown":
7549
+ return {
7550
+ caption: kind.caption ? xlsxCaptionToDukeText(kind.caption) : void 0,
7551
+ kind: "unknown",
7552
+ legacyObjectType: kind.legacyObjectType,
7553
+ objectType: kind.objectType,
7554
+ rawObj: kind.rawObj,
7555
+ rawProperties: kind.rawProperties
7556
+ };
7507
7557
  default:
7508
- return void 0;
7558
+ return { ...kind };
7509
7559
  }
7510
7560
  }
7511
- function syncFormControlLinkedCell(workbook, workbookSheetIndex, controls, controlIndex) {
7512
- const control = controls[controlIndex];
7513
- if (!control) {
7514
- return;
7561
+ function dukeFormControlKindToInput(kind) {
7562
+ if (kind.kind === "optionButton") {
7563
+ return { ...kind, state: kind.state === "checked" ? "checked" : "unchecked" };
7515
7564
  }
7516
- const cellLink = getFormControlCellLink(control.kind);
7517
- const value = resolveLinkedCellValue(controls, controlIndex);
7518
- if (!cellLink || value === void 0) {
7519
- return;
7565
+ if (kind.kind === "unknown") {
7566
+ return {
7567
+ caption: kind.caption,
7568
+ kind: "unknown",
7569
+ legacyObjectType: kind.legacyObjectType,
7570
+ objectType: kind.objectType,
7571
+ rawObj: kind.rawObj,
7572
+ rawProperties: kind.rawProperties
7573
+ };
7520
7574
  }
7521
- const target = resolveWorkbookReference(workbook, workbookSheetIndex, cellLink);
7522
- if (!target || target.range.start.row !== target.range.end.row || target.range.start.col !== target.range.end.col) {
7523
- return;
7575
+ return { ...kind };
7576
+ }
7577
+ function formControlInputFromDukeControl(control) {
7578
+ return {
7579
+ altText: control.altText,
7580
+ ...control.anchor ? { anchor: control.anchor } : { transform: control.transform },
7581
+ formControl: {
7582
+ kind: dukeFormControlKindToInput(control.formControl.kind),
7583
+ macroName: control.formControl.macroName,
7584
+ rawClientData: control.formControl.rawClientData
7585
+ },
7586
+ hidden: control.hidden,
7587
+ kind: "formControl",
7588
+ locked: control.locked,
7589
+ name: control.name,
7590
+ printable: control.printable,
7591
+ title: control.title
7592
+ };
7593
+ }
7594
+ function xlsxFormControlInputToDukeDrawing(input) {
7595
+ return {
7596
+ altText: input.altText,
7597
+ anchor: xlsxAnchorToDukeDrawingAnchor(input.anchor, input.editAs),
7598
+ formControl: {
7599
+ kind: xlsxFormControlKindToDukeKind(input.kind),
7600
+ macroName: input.macroName,
7601
+ rawClientData: input.rawClientData
7602
+ },
7603
+ hidden: input.hidden,
7604
+ kind: "formControl",
7605
+ locked: input.locked,
7606
+ name: input.name,
7607
+ printable: input.printable,
7608
+ title: input.title
7609
+ };
7610
+ }
7611
+ function reconcileCheckedOptionButtons(worksheet) {
7612
+ const checkedPaths = worksheet.formControls.flatMap((control) => control.formControl.kind.kind === "optionButton" && control.formControl.kind.state === "checked" ? [control.drawingPath] : []);
7613
+ for (const path of checkedPaths) {
7614
+ const current = worksheet.formControls.find((control) => control.drawingPath.length === path.length && control.drawingPath.every((segment, index) => segment === path[index]));
7615
+ if (current?.formControl.kind.kind === "optionButton" && current.formControl.kind.state === "checked") {
7616
+ worksheet.setFormControlCheckState(current.drawingPath, "checked");
7617
+ }
7618
+ }
7619
+ }
7620
+ function formControlInteractionChanged(current, next) {
7621
+ if (current.kind !== next.kind) {
7622
+ return true;
7623
+ }
7624
+ if ("cellLink" in current && "cellLink" in next && current.cellLink !== next.cellLink) {
7625
+ return true;
7626
+ }
7627
+ switch (current.kind) {
7628
+ case "checkbox":
7629
+ case "optionButton":
7630
+ return next.kind === current.kind && current.state !== next.state;
7631
+ case "dropdown":
7632
+ return next.kind === "dropdown" && current.selected !== next.selected;
7633
+ case "listBox":
7634
+ return next.kind === "listBox" && (current.selection !== next.selection || current.selected.length !== next.selected.length || current.selected.some((value, index) => value !== next.selected[index]));
7635
+ case "scrollbar":
7636
+ case "spinner":
7637
+ return next.kind === current.kind && current.value !== next.value;
7638
+ default:
7639
+ return false;
7524
7640
  }
7525
- target.worksheet.setCell(cellAddressToA1(target.range.start), value);
7526
7641
  }
7527
7642
  function parseWorksheetFreezePanes(worksheet) {
7528
7643
  const rawFreezePanes = worksheet.freezePanes;
@@ -7856,9 +7971,6 @@ function scheduleLowPriorityTask(task) {
7856
7971
  const timeoutHandle = window.setTimeout(task, 0);
7857
7972
  return () => window.clearTimeout(timeoutHandle);
7858
7973
  }
7859
- function asFiniteNumber(value) {
7860
- return typeof value === "number" && Number.isFinite(value) ? value : null;
7861
- }
7862
7974
  function inferImageMimeType(source) {
7863
7975
  if (source.startsWith("data:")) {
7864
7976
  const separatorIndex = source.indexOf(";");
@@ -7882,18 +7994,27 @@ function inferImageMimeType(source) {
7882
7994
  return "image/png";
7883
7995
  }
7884
7996
  function inferWorksheetDirectImageMimeType(info) {
7885
- const format = typeof info.format === "string" ? info.format.trim().toLowerCase() : "";
7997
+ const format = info.format;
7886
7998
  if (format === "gif") {
7887
7999
  return "image/gif";
7888
8000
  }
7889
- if (format === "jpg" || format === "jpeg") {
8001
+ if (format === "jpeg") {
7890
8002
  return "image/jpeg";
7891
8003
  }
7892
8004
  if (format === "svg") {
7893
8005
  return "image/svg+xml";
7894
8006
  }
7895
- if (format === "webp") {
7896
- return "image/webp";
8007
+ if (format === "bmp") {
8008
+ return "image/bmp";
8009
+ }
8010
+ if (format === "tiff") {
8011
+ return "image/tiff";
8012
+ }
8013
+ if (format === "emf") {
8014
+ return "image/emf";
8015
+ }
8016
+ if (format === "wmf") {
8017
+ return "image/wmf";
7897
8018
  }
7898
8019
  if (format === "png") {
7899
8020
  return "image/png";
@@ -7915,323 +8036,108 @@ function createWorksheetDirectImageSource(data, mimeType, objectUrls) {
7915
8036
  objectUrls.push(objectUrl);
7916
8037
  return objectUrl;
7917
8038
  }
7918
- function buildWorksheetDirectImageAnchor(rawAnchor, widthEmu, heightEmu) {
7919
- const anchor = rawAnchor && typeof rawAnchor === "object" ? rawAnchor : {};
7920
- const fromCol = asFiniteNumber(anchor.fromCol) ?? 0;
7921
- const fromRow = asFiniteNumber(anchor.fromRow) ?? 0;
7922
- const fromColOffset = asFiniteNumber(anchor.fromColOffset) ?? 0;
7923
- const fromRowOffset = asFiniteNumber(anchor.fromRowOffset) ?? 0;
7924
- const toCol = asFiniteNumber(anchor.toCol);
7925
- const toRow = asFiniteNumber(anchor.toRow);
7926
- const toColOffset = asFiniteNumber(anchor.toColOffset) ?? 0;
7927
- const toRowOffset = asFiniteNumber(anchor.toRowOffset) ?? 0;
7928
- if (toCol !== null && toRow !== null) {
7929
- return {
7930
- from: {
7931
- col: Math.max(0, Math.round(fromCol)),
7932
- colOffsetEmu: Math.max(0, Math.round(fromColOffset)),
7933
- row: Math.max(0, Math.round(fromRow)),
7934
- rowOffsetEmu: Math.max(0, Math.round(fromRowOffset))
7935
- },
7936
- kind: "two-cell",
7937
- to: {
7938
- col: Math.max(0, Math.round(toCol)),
7939
- colOffsetEmu: Math.max(0, Math.round(toColOffset)),
7940
- row: Math.max(0, Math.round(toRow)),
7941
- rowOffsetEmu: Math.max(0, Math.round(toRowOffset))
7942
- }
7943
- };
7944
- }
7945
- return {
7946
- from: {
7947
- col: Math.max(0, Math.round(fromCol)),
7948
- colOffsetEmu: Math.max(0, Math.round(fromColOffset)),
7949
- row: Math.max(0, Math.round(fromRow)),
7950
- rowOffsetEmu: Math.max(0, Math.round(fromRowOffset))
7951
- },
7952
- kind: "one-cell",
7953
- sizeEmu: {
7954
- cx: Math.max(EMU_PER_PIXEL3, Math.round(widthEmu)),
7955
- cy: Math.max(EMU_PER_PIXEL3, Math.round(heightEmu))
7956
- }
7957
- };
7958
- }
7959
- function normalizeWorksheetDirectShapeParagraphs(rawParagraphs, fallbackText) {
7960
- const normalizedParagraphs = [];
7961
- if (Array.isArray(rawParagraphs)) {
7962
- for (const entry of rawParagraphs) {
7963
- const paragraph = entry && typeof entry === "object" ? entry : {};
7964
- const runs = [];
7965
- if (Array.isArray(paragraph.runs)) {
7966
- for (const runEntry of paragraph.runs) {
7967
- const run = runEntry && typeof runEntry === "object" ? runEntry : {};
7968
- const text2 = typeof run.text === "string" ? run.text : "";
7969
- if (!text2) {
7970
- continue;
7971
- }
7972
- runs.push({
7973
- bold: typeof run.bold === "boolean" ? run.bold : void 0,
7974
- color: typeof run.color === "string" && run.color.trim() ? run.color : void 0,
7975
- fontFamily: typeof run.fontFamily === "string" && run.fontFamily.trim() ? run.fontFamily : void 0,
7976
- fontSizePt: asFiniteNumber(run.fontSizePt) ?? void 0,
7977
- italic: typeof run.italic === "boolean" ? run.italic : void 0,
7978
- text: text2,
7979
- underline: typeof run.underline === "boolean" ? run.underline : void 0
7980
- });
7981
- }
7982
- }
7983
- if (runs.length === 0) {
7984
- continue;
7985
- }
7986
- const align = paragraph.align;
7987
- normalizedParagraphs.push({
7988
- align: align === "center" || align === "justify" || align === "left" || align === "right" ? align : void 0,
7989
- runs
7990
- });
8039
+ function buildWorksheetDirectApiImage(worksheet, workbookSheetIndex, drawing, objectUrls) {
8040
+ const hasSvgCompanion = Boolean(drawing.image.svgMediaPath);
8041
+ let mimeType = inferWorksheetDirectImageMimeType(drawing.image);
8042
+ let data;
8043
+ try {
8044
+ const svgData = drawing.image.format === "svg" || hasSvgCompanion ? worksheet.drawingSvgData(drawing.drawingPath) : void 0;
8045
+ if (svgData) {
8046
+ data = svgData;
8047
+ mimeType = "image/svg+xml";
8048
+ } else {
8049
+ data = worksheet.drawingImageData(drawing.drawingPath);
7991
8050
  }
7992
- }
7993
- if (normalizedParagraphs.length > 0) {
7994
- return normalizedParagraphs;
7995
- }
7996
- const text = typeof fallbackText === "string" ? fallbackText : "";
7997
- return text ? [{ runs: [{ text }] }] : [];
7998
- }
7999
- function buildWorksheetDirectApiShape(workbookSheetIndex, info, zIndex) {
8000
- const fill = info.fill && typeof info.fill === "object" ? {
8001
- color: typeof info.fill.color === "string" && info.fill.color.trim() ? info.fill.color : void 0,
8002
- none: typeof info.fill.none === "boolean" ? info.fill.none : void 0,
8003
- opacity: asFiniteNumber(info.fill.opacity) ?? void 0
8004
- } : void 0;
8005
- const stroke = info.stroke && typeof info.stroke === "object" ? {
8006
- color: typeof info.stroke.color === "string" && info.stroke.color.trim() ? info.stroke.color : void 0,
8007
- dash: typeof info.stroke.dash === "string" && info.stroke.dash.trim() ? info.stroke.dash : void 0,
8008
- headEndType: typeof info.stroke.headEndType === "string" && info.stroke.headEndType.trim() ? info.stroke.headEndType : void 0,
8009
- none: typeof info.stroke.none === "boolean" ? info.stroke.none : void 0,
8010
- opacity: asFiniteNumber(info.stroke.opacity) ?? void 0,
8011
- tailEndType: typeof info.stroke.tailEndType === "string" && info.stroke.tailEndType.trim() ? info.stroke.tailEndType : void 0,
8012
- widthPx: asFiniteNumber(info.stroke.widthPx) ?? void 0
8013
- } : void 0;
8014
- const rawSvgViewBox = info.svgViewBox && typeof info.svgViewBox === "object" ? info.svgViewBox : null;
8015
- const rawTextBox = info.textBox && typeof info.textBox === "object" ? info.textBox : null;
8016
- const rawInset = rawTextBox?.insetPx && typeof rawTextBox.insetPx === "object" ? rawTextBox.insetPx : null;
8017
- return {
8018
- anchor: buildWorksheetDirectImageAnchor(
8019
- info.anchor,
8020
- DEFAULT_COL_WIDTH * EMU_PER_PIXEL3,
8021
- DEFAULT_ROW_HEIGHT * EMU_PER_PIXEL3
8022
- ),
8023
- description: typeof info.description === "string" && info.description.trim() ? info.description : void 0,
8024
- fill,
8025
- flipH: typeof info.flipH === "boolean" ? info.flipH : void 0,
8026
- flipV: typeof info.flipV === "boolean" ? info.flipV : void 0,
8027
- geometry: typeof info.geometry === "string" && info.geometry.trim() ? info.geometry : "rect",
8028
- geometryAdjustments: info.geometryAdjustments && typeof info.geometryAdjustments === "object" ? Object.fromEntries(
8029
- Object.entries(info.geometryAdjustments).map(([key, value]) => [key, asFiniteNumber(value)]).filter((entry) => typeof entry[1] === "number")
8030
- ) : void 0,
8031
- hyperlink: typeof info.hyperlink === "string" && info.hyperlink.trim() ? info.hyperlink : void 0,
8032
- id: `shape-${workbookSheetIndex}-${String(info.id ?? zIndex)}`,
8033
- name: typeof info.name === "string" && info.name.trim() ? info.name : void 0,
8034
- paragraphs: normalizeWorksheetDirectShapeParagraphs(info.paragraphs, info.text),
8035
- rotationDeg: asFiniteNumber(info.rotationDeg) ?? void 0,
8036
- scaleX: asFiniteNumber(info.scaleX) ?? void 0,
8037
- scaleY: asFiniteNumber(info.scaleY) ?? void 0,
8038
- sheetIndex: workbookSheetIndex,
8039
- svgPath: typeof info.svgPath === "string" && info.svgPath.trim() ? info.svgPath : void 0,
8040
- svgViewBox: rawSvgViewBox && asFiniteNumber(rawSvgViewBox.width) !== null && asFiniteNumber(rawSvgViewBox.height) !== null ? {
8041
- height: asFiniteNumber(rawSvgViewBox.height) ?? 0,
8042
- width: asFiniteNumber(rawSvgViewBox.width) ?? 0
8043
- } : void 0,
8044
- stroke,
8045
- textBox: rawTextBox ? {
8046
- horizontalAlign: rawTextBox.horizontalAlign === "center" || rawTextBox.horizontalAlign === "left" ? rawTextBox.horizontalAlign : void 0,
8047
- insetPx: rawInset ? {
8048
- bottom: asFiniteNumber(rawInset.bottom) ?? 0,
8049
- left: asFiniteNumber(rawInset.left) ?? 0,
8050
- right: asFiniteNumber(rawInset.right) ?? 0,
8051
- top: asFiniteNumber(rawInset.top) ?? 0
8052
- } : void 0,
8053
- verticalAlign: rawTextBox.verticalAlign === "bottom" || rawTextBox.verticalAlign === "middle" || rawTextBox.verticalAlign === "top" ? rawTextBox.verticalAlign : void 0
8054
- } : void 0,
8055
- workbookSheetIndex,
8056
- zIndex
8057
- };
8058
- }
8059
- function buildWorksheetApiImage(workbookSheetIndex, row, col, info, zIndex) {
8060
- if (typeof info.source !== "string" || !info.source) {
8051
+ } catch {
8061
8052
  return null;
8062
8053
  }
8063
- const width = Math.max(1, Math.round(asFiniteNumber(info.width) ?? DEFAULT_COL_WIDTH));
8064
- const height = Math.max(1, Math.round(asFiniteNumber(info.height) ?? DEFAULT_ROW_HEIGHT));
8065
- const description = typeof info.altText === "string" && info.altText.trim() ? info.altText : void 0;
8066
- return {
8067
- anchor: {
8068
- from: {
8069
- col,
8070
- colOffsetEmu: 0,
8071
- row,
8072
- rowOffsetEmu: 0
8073
- },
8074
- kind: "one-cell",
8075
- sizeEmu: {
8076
- cx: width * EMU_PER_PIXEL3,
8077
- cy: height * EMU_PER_PIXEL3
8078
- }
8079
- },
8080
- description,
8081
- editable: false,
8082
- id: `worksheet-image-${workbookSheetIndex}-${row}-${col}-${zIndex}`,
8083
- mimeType: inferImageMimeType(info.source),
8084
- sheetIndex: workbookSheetIndex,
8085
- src: info.source,
8086
- workbookSheetIndex,
8087
- zIndex
8088
- };
8089
- }
8090
- function buildWorksheetDirectApiImage(workbookSheetIndex, info, zIndex, objectUrls) {
8091
- const mimeType = inferWorksheetDirectImageMimeType(info);
8092
- const src = createWorksheetDirectImageSource(info.data, mimeType, objectUrls);
8054
+ const src = createWorksheetDirectImageSource(data, mimeType, objectUrls);
8093
8055
  if (!src) {
8094
8056
  return null;
8095
8057
  }
8096
- const widthEmu = Math.max(EMU_PER_PIXEL3, Math.round(asFiniteNumber(info.widthEmu) ?? DEFAULT_COL_WIDTH * EMU_PER_PIXEL3));
8097
- const heightEmu = Math.max(EMU_PER_PIXEL3, Math.round(asFiniteNumber(info.heightEmu) ?? DEFAULT_ROW_HEIGHT * EMU_PER_PIXEL3));
8058
+ const zIndex = (drawing.drawingPath[0] ?? 0) + 1;
8098
8059
  return {
8099
- anchor: buildWorksheetDirectImageAnchor(info.anchor, widthEmu, heightEmu),
8060
+ anchor: dukeDrawingAnchorToXlsxAnchor(drawing.anchor),
8061
+ description: drawing.altText,
8100
8062
  editable: false,
8101
- id: `worksheet-image-${workbookSheetIndex}-${String(info.id ?? zIndex)}`,
8102
- mediaPath: typeof info.mediaPath === "string" && info.mediaPath.trim() ? info.mediaPath : void 0,
8063
+ id: `worksheet-image-${workbookSheetIndex}-${drawing.drawingPath.join("-")}`,
8064
+ mediaPath: drawing.image.mediaPath || void 0,
8103
8065
  mimeType,
8104
- name: typeof info.name === "string" && info.name.trim() ? info.name : void 0,
8066
+ name: drawing.name,
8105
8067
  sheetIndex: workbookSheetIndex,
8106
8068
  src,
8107
8069
  workbookSheetIndex,
8108
8070
  zIndex
8109
8071
  };
8110
8072
  }
8111
- function collectWorksheetBatchImages(workbook) {
8112
- const imagesByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, () => []);
8113
- for (let workbookSheetIndex = 0; workbookSheetIndex < workbook.sheetCount; workbookSheetIndex += 1) {
8114
- const worksheet = workbook.getSheet(workbookSheetIndex);
8115
- if (typeof worksheet.getRowsBatch !== "function") {
8116
- continue;
8117
- }
8118
- const usedRange = worksheet.usedRange();
8119
- const maxRow = usedRange?.[2] ?? -1;
8120
- if (maxRow < 0) {
8121
- continue;
8122
- }
8123
- let zIndex = 1;
8124
- let sheetFailed = false;
8125
- for (let startRow = 0; startRow <= maxRow; startRow += IMAGE_BATCH_ROW_COUNT) {
8126
- let rows;
8127
- try {
8128
- rows = worksheet.getRowsBatch(startRow, IMAGE_BATCH_ROW_COUNT, { includeImages: true });
8129
- } catch {
8130
- sheetFailed = true;
8131
- break;
8132
- }
8133
- if (!Array.isArray(rows)) {
8134
- continue;
8135
- }
8136
- for (const rowEntry of rows) {
8137
- const row = typeof rowEntry.index === "number" ? rowEntry.index : null;
8138
- if (row === null || !Array.isArray(rowEntry.cells)) {
8139
- continue;
8140
- }
8141
- for (const cellEntry of rowEntry.cells) {
8142
- const col = typeof cellEntry.col === "number" ? cellEntry.col : null;
8143
- if (col === null || !cellEntry.image || typeof cellEntry.image !== "object") {
8144
- continue;
8145
- }
8146
- const image = buildWorksheetApiImage(workbookSheetIndex, row, col, cellEntry.image, zIndex);
8147
- if (!image) {
8148
- continue;
8149
- }
8150
- imagesByWorkbookSheetIndex[workbookSheetIndex].push(image);
8151
- zIndex += 1;
8152
- }
8153
- }
8154
- }
8155
- if (sheetFailed) {
8156
- imagesByWorkbookSheetIndex[workbookSheetIndex] = [];
8157
- }
8158
- }
8159
- return imagesByWorkbookSheetIndex;
8160
- }
8161
8073
  function collectWorksheetApiImages(workbook, objectUrls) {
8162
8074
  const directImagesByWorkbookSheetIndex = Array.from({ length: workbook.sheetCount }, () => []);
8163
- let didUseDirectImages = false;
8164
8075
  for (let workbookSheetIndex = 0; workbookSheetIndex < workbook.sheetCount; workbookSheetIndex += 1) {
8165
8076
  const worksheet = workbook.getSheet(workbookSheetIndex);
8166
- const rawImages = Array.isArray(worksheet.images) ? worksheet.images : [];
8077
+ const rawImages = worksheet.images;
8167
8078
  if (rawImages.length === 0) {
8168
8079
  continue;
8169
8080
  }
8170
- const nextImages = rawImages.map((info, index) => buildWorksheetDirectApiImage(workbookSheetIndex, info, index + 1, objectUrls)).filter((image) => Boolean(image));
8081
+ const nextImages = rawImages.filter((drawing) => !drawing.hidden && Boolean(drawing.anchor)).map((drawing) => buildWorksheetDirectApiImage(worksheet, workbookSheetIndex, drawing, objectUrls)).filter((image) => Boolean(image));
8171
8082
  if (nextImages.length > 0) {
8172
8083
  directImagesByWorkbookSheetIndex[workbookSheetIndex] = nextImages;
8173
- didUseDirectImages = true;
8174
8084
  }
8175
8085
  }
8176
- if (didUseDirectImages) {
8177
- return directImagesByWorkbookSheetIndex;
8178
- }
8179
- return collectWorksheetBatchImages(workbook);
8180
- }
8181
- function collectWorksheetApiShapes(workbook) {
8182
- return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => {
8183
- const worksheet = workbook.getSheet(workbookSheetIndex);
8184
- const rawShapes = Array.isArray(worksheet.shapes) ? worksheet.shapes : [];
8185
- return rawShapes.map((shape, index) => buildWorksheetDirectApiShape(workbookSheetIndex, shape, index + 1));
8186
- });
8086
+ return directImagesByWorkbookSheetIndex;
8187
8087
  }
8188
- function mergeParsedAndApiImages(parsedImages, apiImages) {
8189
- if (parsedImages.length === 0) {
8190
- return apiImages;
8191
- }
8192
- if (apiImages.length === 0) {
8193
- return parsedImages;
8088
+ function imageAnchorKey(anchor) {
8089
+ if (anchor.kind === "absolute") {
8090
+ return [
8091
+ "absolute",
8092
+ Math.round(anchor.positionEmu.x),
8093
+ Math.round(anchor.positionEmu.y),
8094
+ Math.round(anchor.sizeEmu.cx),
8095
+ Math.round(anchor.sizeEmu.cy)
8096
+ ].join(":");
8194
8097
  }
8195
- const normalizeTextKey = (value) => value?.trim().toLowerCase() ?? "";
8196
- const anchorKey = (anchor) => {
8197
- if (anchor.kind === "absolute") {
8198
- return [
8199
- "absolute",
8200
- Math.round(anchor.positionEmu.x),
8201
- Math.round(anchor.positionEmu.y),
8202
- Math.round(anchor.sizeEmu.cx),
8203
- Math.round(anchor.sizeEmu.cy)
8204
- ].join(":");
8205
- }
8206
- if (anchor.kind === "one-cell") {
8207
- return [
8208
- "one",
8209
- anchor.from.col,
8210
- anchor.from.row,
8211
- Math.round(anchor.from.colOffsetEmu),
8212
- Math.round(anchor.from.rowOffsetEmu),
8213
- Math.round(anchor.sizeEmu.cx),
8214
- Math.round(anchor.sizeEmu.cy)
8215
- ].join(":");
8216
- }
8098
+ if (anchor.kind === "one-cell") {
8217
8099
  return [
8218
- "two",
8100
+ "one",
8219
8101
  anchor.from.col,
8220
8102
  anchor.from.row,
8221
8103
  Math.round(anchor.from.colOffsetEmu),
8222
8104
  Math.round(anchor.from.rowOffsetEmu),
8223
- anchor.to.col,
8224
- anchor.to.row,
8225
- Math.round(anchor.to.colOffsetEmu),
8226
- Math.round(anchor.to.rowOffsetEmu)
8105
+ Math.round(anchor.sizeEmu.cx),
8106
+ Math.round(anchor.sizeEmu.cy)
8227
8107
  ].join(":");
8228
- };
8108
+ }
8109
+ return [
8110
+ "two",
8111
+ anchor.from.col,
8112
+ anchor.from.row,
8113
+ Math.round(anchor.from.colOffsetEmu),
8114
+ Math.round(anchor.from.rowOffsetEmu),
8115
+ anchor.to.col,
8116
+ anchor.to.row,
8117
+ Math.round(anchor.to.colOffsetEmu),
8118
+ Math.round(anchor.to.rowOffsetEmu)
8119
+ ].join(":");
8120
+ }
8121
+ function collectHiddenWorksheetImageAnchors(workbook) {
8122
+ return Array.from({ length: workbook.sheetCount }, (_, workbookSheetIndex) => new Set(
8123
+ workbook.getSheet(workbookSheetIndex).images.flatMap((drawing) => drawing.hidden && drawing.anchor ? [imageAnchorKey(dukeDrawingAnchorToXlsxAnchor(drawing.anchor))] : [])
8124
+ ));
8125
+ }
8126
+ function mergeParsedAndApiImages(parsedImages, apiImages, hiddenAnchorKeys) {
8127
+ const visibleParsedImages = parsedImages.filter((image) => !hiddenAnchorKeys.has(imageAnchorKey(image.anchor)));
8128
+ if (visibleParsedImages.length === 0) {
8129
+ return apiImages;
8130
+ }
8131
+ if (apiImages.length === 0) {
8132
+ return visibleParsedImages;
8133
+ }
8134
+ const normalizeTextKey = (value) => value?.trim().toLowerCase() ?? "";
8229
8135
  const imageKeys = (image) => {
8230
8136
  const keys = [
8231
- `${normalizeTextKey(image.mediaPath)}|${normalizeTextKey(image.name)}|${anchorKey(image.anchor)}`,
8232
- `${normalizeTextKey(image.mediaPath)}|${anchorKey(image.anchor)}`,
8233
- `${normalizeTextKey(image.name)}|${anchorKey(image.anchor)}`,
8234
- `${anchorKey(image.anchor)}`
8137
+ `${normalizeTextKey(image.mediaPath)}|${normalizeTextKey(image.name)}|${imageAnchorKey(image.anchor)}`,
8138
+ `${normalizeTextKey(image.mediaPath)}|${imageAnchorKey(image.anchor)}`,
8139
+ `${normalizeTextKey(image.name)}|${imageAnchorKey(image.anchor)}`,
8140
+ `${imageAnchorKey(image.anchor)}`
8235
8141
  ];
8236
8142
  return keys.filter((key, index) => key && keys.indexOf(key) === index);
8237
8143
  };
@@ -8261,7 +8167,7 @@ function mergeParsedAndApiImages(parsedImages, apiImages) {
8261
8167
  }
8262
8168
  return null;
8263
8169
  };
8264
- const merged = parsedImages.map((image) => {
8170
+ const merged = visibleParsedImages.map((image) => {
8265
8171
  const apiImage = takeApiMatch(image);
8266
8172
  if (!apiImage) {
8267
8173
  return image;
@@ -8272,7 +8178,8 @@ function mergeParsedAndApiImages(parsedImages, apiImages) {
8272
8178
  mediaPath: apiImage.mediaPath ?? image.mediaPath,
8273
8179
  mimeType: apiImage.mimeType,
8274
8180
  name: apiImage.name ?? image.name,
8275
- src: apiImage.src
8181
+ src: apiImage.src,
8182
+ zIndex: apiImage.zIndex
8276
8183
  };
8277
8184
  });
8278
8185
  return merged;
@@ -8290,12 +8197,13 @@ function createBasicWorkbookAssets(workbook) {
8290
8197
  const objectUrls = [];
8291
8198
  return {
8292
8199
  archive: {},
8200
+ dirtyArchivePaths: /* @__PURE__ */ new Set(),
8293
8201
  formControlsByWorkbookSheetIndex: collectWorkbookFormControls(workbook),
8294
8202
  imageOriginsById: /* @__PURE__ */ new Map(),
8295
8203
  imagesByWorkbookSheetIndex: collectWorksheetApiImages(workbook, objectUrls),
8296
8204
  namedCellStyleByName: {},
8297
8205
  objectUrls,
8298
- shapesByWorkbookSheetIndex: collectWorksheetApiShapes(workbook),
8206
+ shapesByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => []),
8299
8207
  sheetOrigins: Array.from({ length: workbook.sheetCount }, () => null),
8300
8208
  sheetStatesByWorkbookSheetIndex: Array.from({ length: workbook.sheetCount }, () => null),
8301
8209
  styleById: {},
@@ -8310,12 +8218,17 @@ function loadWorkbookImageAssets(bytes, workbook, skipXmlParsing = false) {
8310
8218
  }
8311
8219
  const parsedAssets = parseWorkbookImageAssets(bytes, workbook);
8312
8220
  const apiImagesByWorkbookSheetIndex = collectWorksheetApiImages(workbook, parsedAssets.objectUrls);
8221
+ const hiddenImageAnchorsByWorkbookSheetIndex = collectHiddenWorksheetImageAnchors(workbook);
8313
8222
  const imagesByWorkbookSheetIndex = Array.from(
8314
8223
  { length: Math.max(workbook.sheetCount, parsedAssets.imagesByWorkbookSheetIndex.length, apiImagesByWorkbookSheetIndex.length) },
8315
8224
  (_, index) => {
8316
8225
  const parsedImages = parsedAssets.imagesByWorkbookSheetIndex[index] ?? [];
8317
8226
  const apiImages = apiImagesByWorkbookSheetIndex[index] ?? [];
8318
- return mergeParsedAndApiImages(parsedImages, apiImages);
8227
+ return mergeParsedAndApiImages(
8228
+ parsedImages,
8229
+ apiImages,
8230
+ hiddenImageAnchorsByWorkbookSheetIndex[index] ?? /* @__PURE__ */ new Set()
8231
+ );
8319
8232
  }
8320
8233
  );
8321
8234
  return {
@@ -8626,7 +8539,11 @@ function useXlsxViewerController(options) {
8626
8539
  }, [skipXmlParsing]);
8627
8540
  const refreshWorkbookState = React.useCallback((targetWorkbook) => {
8628
8541
  const currentFormControls = imageAssetsRef.current?.formControlsByWorkbookSheetIndex ?? [];
8629
- const nextFormControls = refreshWorkbookFormControls(targetWorkbook, currentFormControls);
8542
+ const nextFormControls = refreshWorkbookFormControls(
8543
+ targetWorkbook,
8544
+ currentFormControls,
8545
+ imageAssetsRef.current?.themePalette
8546
+ );
8630
8547
  if (imageAssetsRef.current) {
8631
8548
  imageAssetsRef.current.formControlsByWorkbookSheetIndex = nextFormControls;
8632
8549
  }
@@ -8725,6 +8642,7 @@ function useXlsxViewerController(options) {
8725
8642
  if (maxFileSizeBytes > 0 && buffer.byteLength > maxFileSizeBytes) {
8726
8643
  throw new XlsxFileSizeLimitExceededError(buffer.byteLength, maxFileSizeBytes);
8727
8644
  }
8645
+ buffer = normalizeWorkbookArrayBuffer(buffer);
8728
8646
  const preflight = preflightWorkbookBuffer(buffer);
8729
8647
  if (preflight?.tooLarge) {
8730
8648
  throw createWorkbookTooLargeError(preflight);
@@ -8752,24 +8670,19 @@ function useXlsxViewerController(options) {
8752
8670
  if (!effectiveSkipXmlParsing && hasIncompleteWorkerChartSnapshot(snapshot)) {
8753
8671
  throw new Error("Worker chart payload incomplete");
8754
8672
  }
8755
- const workerImageAssets = effectiveSkipXmlParsing ? null : parseWorkbookImageAssets(
8756
- new Uint8Array(buffer),
8757
- snapshot.formControlsByWorkbookSheetIndex
8758
- );
8673
+ const workerImageAssets = effectiveSkipXmlParsing ? null : parseWorkbookImageAssets(new Uint8Array(buffer));
8759
8674
  if (!isCurrent || abortController.signal.aborted) {
8760
8675
  revokeWorkbookImageAssets(workerImageAssets);
8761
8676
  return;
8762
8677
  }
8763
8678
  setImageAssets(workerImageAssets);
8679
+ setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8764
8680
  setWorkbook(null);
8765
8681
  setSheets(snapshot.sheets);
8766
8682
  setChartsByWorkbookSheetIndex(snapshot.chartsByWorkbookSheetIndex);
8767
8683
  setChartsheets(snapshot.chartsheets);
8768
8684
  setTabs(snapshot.tabs);
8769
8685
  chartAssetsRef.current = null;
8770
- if (!workerImageAssets) {
8771
- setFormControlsByWorkbookSheetIndex(snapshot.formControlsByWorkbookSheetIndex);
8772
- }
8773
8686
  setWorkerTablesByWorkbookSheetIndex(snapshot.tablesByWorkbookSheetIndex);
8774
8687
  setShouldAutoCalculate(false);
8775
8688
  setIsWorkerBacked(true);
@@ -9275,7 +9188,8 @@ function useXlsxViewerController(options) {
9275
9188
  return control2?.items?.slice() ?? [];
9276
9189
  }
9277
9190
  const control = target.worksheet.formControls[controlIndex];
9278
- const inputRange = control?.kind.kind === "listBox" || control?.kind.kind === "dropdown" ? control.kind.inputRange : void 0;
9191
+ const kind = control?.formControl.kind;
9192
+ const inputRange = kind?.kind === "listBox" || kind?.kind === "dropdown" ? kind.inputRange : void 0;
9279
9193
  if (!inputRange) {
9280
9194
  return [];
9281
9195
  }
@@ -9694,30 +9608,17 @@ function useXlsxViewerController(options) {
9694
9608
  return null;
9695
9609
  }
9696
9610
  recordHistoryBeforeMutation();
9697
- const controlIndex = target.worksheet.addFormControl(input);
9698
- const controls = target.worksheet.formControls;
9699
- const addedControl = controls[controlIndex];
9700
- if (addedControl?.kind.kind === "optionButton" && addedControl.kind.state === "checked") {
9701
- const groupIndexes = resolveOptionButtonGroupIndexes(controls, controlIndex);
9702
- for (const siblingIndex of groupIndexes) {
9703
- if (siblingIndex === controlIndex) {
9704
- continue;
9705
- }
9706
- const sibling = target.worksheet.formControls[siblingIndex];
9707
- if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9708
- target.worksheet.setFormControl(siblingIndex, {
9709
- ...formControlInputFromDukeControl(sibling),
9710
- kind: { ...sibling.kind, state: "unchecked" }
9711
- });
9712
- }
9713
- }
9611
+ target.worksheet.addDrawing(xlsxFormControlInputToDukeDrawing(input));
9612
+ const controlIndex = target.worksheet.formControlCount - 1;
9613
+ const addedControl = target.worksheet.formControls[controlIndex];
9614
+ const addedKind = addedControl?.formControl.kind;
9615
+ if (addedControl && (addedKind?.kind === "checkbox" || addedKind?.kind === "optionButton")) {
9616
+ target.worksheet.setFormControlCheckState(addedControl.drawingPath, addedKind.state);
9617
+ } else if (addedKind?.kind === "groupBox") {
9618
+ reconcileCheckedOptionButtons(target.worksheet);
9619
+ } else if (addedKind && "cellLink" in addedKind && addedKind.cellLink) {
9620
+ workbook.syncFormControls();
9714
9621
  }
9715
- syncFormControlLinkedCell(
9716
- workbook,
9717
- target.workbookSheetIndex,
9718
- target.worksheet.formControls,
9719
- controlIndex
9720
- );
9721
9622
  maybeRecalculateWorkbook(workbook);
9722
9623
  refreshWorkbookState(workbook);
9723
9624
  return controlIndex;
@@ -9729,34 +9630,51 @@ function useXlsxViewerController(options) {
9729
9630
  return false;
9730
9631
  }
9731
9632
  recordHistoryBeforeMutation();
9633
+ const currentInput = formControlInputFromDukeControl(currentControl);
9634
+ if (currentInput.kind !== "formControl" || !currentControl.anchor) {
9635
+ return false;
9636
+ }
9637
+ const nextAnchor = patch.anchor ? xlsxAnchorToDukeDrawingAnchor(
9638
+ patch.anchor,
9639
+ patch.editAs ?? (currentControl.anchor.type === "twoCell" ? currentControl.anchor.editAs : void 0)
9640
+ ) : patch.editAs !== void 0 && currentControl.anchor.type === "twoCell" ? { ...currentControl.anchor, editAs: patch.editAs } : currentControl.anchor;
9641
+ let nextKind = patch.kind ? xlsxFormControlKindToDukeKind(patch.kind) : dukeFormControlKindToInput(currentControl.formControl.kind);
9642
+ if (currentControl.formControl.kind.kind === "unknown" && nextKind.kind === "unknown") {
9643
+ nextKind = {
9644
+ ...nextKind,
9645
+ legacyObjectType: nextKind.legacyObjectType ?? currentControl.formControl.kind.legacyObjectType,
9646
+ rawObj: nextKind.rawObj ?? currentControl.formControl.kind.rawObj,
9647
+ rawProperties: nextKind.rawProperties ?? currentControl.formControl.kind.rawProperties
9648
+ };
9649
+ }
9732
9650
  const nextInput = {
9733
- ...formControlInputFromDukeControl(currentControl),
9734
- ...patch,
9735
- anchor: patch.anchor ? { ...patch.anchor } : { ...currentControl.anchor },
9736
- kind: patch.kind ? { ...patch.kind } : { ...currentControl.kind }
9651
+ altText: "altText" in patch ? patch.altText : currentControl.altText,
9652
+ anchor: nextAnchor,
9653
+ formControl: {
9654
+ ...currentInput.formControl,
9655
+ kind: nextKind,
9656
+ macroName: "macroName" in patch ? patch.macroName : currentControl.formControl.macroName,
9657
+ rawClientData: "rawClientData" in patch ? patch.rawClientData : currentControl.formControl.rawClientData
9658
+ },
9659
+ hidden: "hidden" in patch ? patch.hidden : currentControl.hidden,
9660
+ kind: "formControl",
9661
+ locked: "locked" in patch ? patch.locked : currentControl.locked,
9662
+ name: "name" in patch ? patch.name : currentControl.name,
9663
+ printable: "printable" in patch ? patch.printable : currentControl.printable,
9664
+ title: "title" in patch ? patch.title : currentControl.title
9737
9665
  };
9738
- if (nextInput.kind.kind === "optionButton" && nextInput.kind.state === "checked") {
9739
- const groupIndexes = resolveOptionButtonGroupIndexes(target.worksheet.formControls, controlIndex);
9740
- for (const siblingIndex of groupIndexes) {
9741
- if (siblingIndex === controlIndex) {
9742
- continue;
9743
- }
9744
- const sibling = target.worksheet.formControls[siblingIndex];
9745
- if (sibling?.kind.kind === "optionButton" && sibling.kind.state !== "unchecked") {
9746
- target.worksheet.setFormControl(siblingIndex, {
9747
- ...formControlInputFromDukeControl(sibling),
9748
- kind: { ...sibling.kind, state: "unchecked" }
9749
- });
9750
- }
9751
- }
9666
+ target.worksheet.setDrawing(currentControl.drawingPath, nextInput);
9667
+ const updatedControl = target.worksheet.formControls[controlIndex];
9668
+ const updatedKind = updatedControl?.formControl.kind;
9669
+ const interactionChanged = updatedKind ? formControlInteractionChanged(currentControl.formControl.kind, updatedKind) : false;
9670
+ const groupTopologyChanged = patch.anchor !== void 0 || updatedKind !== void 0 && updatedKind.kind !== currentControl.formControl.kind.kind && (updatedKind.kind === "groupBox" || currentControl.formControl.kind.kind === "groupBox");
9671
+ if (interactionChanged && updatedControl && (updatedKind?.kind === "checkbox" || updatedKind?.kind === "optionButton")) {
9672
+ target.worksheet.setFormControlCheckState(updatedControl.drawingPath, updatedKind.state);
9673
+ } else if (groupTopologyChanged) {
9674
+ reconcileCheckedOptionButtons(target.worksheet);
9675
+ } else if (interactionChanged && updatedKind && "cellLink" in updatedKind && updatedKind.cellLink) {
9676
+ workbook.syncFormControls();
9752
9677
  }
9753
- target.worksheet.setFormControl(controlIndex, nextInput);
9754
- syncFormControlLinkedCell(
9755
- workbook,
9756
- target.workbookSheetIndex,
9757
- target.worksheet.formControls,
9758
- controlIndex
9759
- );
9760
9678
  maybeRecalculateWorkbook(workbook);
9761
9679
  refreshWorkbookState(workbook);
9762
9680
  return true;
@@ -9767,10 +9685,16 @@ function useXlsxViewerController(options) {
9767
9685
  return false;
9768
9686
  }
9769
9687
  recordHistoryBeforeMutation();
9770
- target.worksheet.removeFormControl(controlIndex);
9688
+ const removedControl = target.worksheet.formControls[controlIndex];
9689
+ const removedKind = removedControl.formControl.kind.kind;
9690
+ target.worksheet.removeDrawing(removedControl.drawingPath);
9691
+ if (removedKind === "groupBox" || removedKind === "optionButton") {
9692
+ reconcileCheckedOptionButtons(target.worksheet);
9693
+ maybeRecalculateWorkbook(workbook);
9694
+ }
9771
9695
  refreshWorkbookState(workbook);
9772
9696
  return true;
9773
- }, [activeSheetIndex, getFormControlWorksheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9697
+ }, [activeSheetIndex, getFormControlWorksheet, maybeRecalculateWorkbook, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9774
9698
  const recordCellEditHistory = React.useCallback((cell, before, after) => {
9775
9699
  if (!activeSheet || isApplyingHistoryRef.current) {
9776
9700
  return;
@@ -17948,7 +17872,7 @@ var THUMBNAIL_MAX_COLS = 80;
17948
17872
  var THUMBNAIL_MAX_SOURCE_HEIGHT_PX = 900;
17949
17873
  var THUMBNAIL_MAX_SOURCE_WIDTH_PX = 1440;
17950
17874
  var LIVE_ZOOM_COMMIT_IDLE_MS = 48;
17951
- var WHEEL_ZOOM_SENSITIVITY = 25e-5;
17875
+ var WHEEL_ZOOM_SENSITIVITY = 25e-4;
17952
17876
  var WHEEL_LINE_DELTA_PX = 16;
17953
17877
  var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669", "#ea580c", "#db2777"];
17954
17878
  var CHART_SOURCE_HIGHLIGHT_COLOR_BY_ROLE = {
@@ -18991,6 +18915,7 @@ function resolveAxisStartOffset(actualIndex, actualIndices, sizes, indexByActual
18991
18915
  function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWidths, options) {
18992
18916
  const headerHeight = options?.headerHeight ?? HEADER_HEIGHT;
18993
18917
  const rowHeaderWidth = options?.rowHeaderWidth ?? ROW_HEADER_WIDTH;
18918
+ const anchorZoomFactor = options?.zoomFactor ?? 1;
18994
18919
  const resolveMarkerLeft = (col, colOffsetEmu) => rowHeaderWidth + resolveAxisStartOffset(
18995
18920
  col,
18996
18921
  visibleCols,
@@ -18998,7 +18923,7 @@ function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWi
18998
18923
  options?.colIndexByActual,
18999
18924
  options?.colPrefixSums,
19000
18925
  options?.actualColPrefixSums
19001
- ) + emuToPixels(colOffsetEmu);
18926
+ ) + emuToPixels(colOffsetEmu) * anchorZoomFactor;
19002
18927
  const resolveMarkerTop = (row, rowOffsetEmu) => headerHeight + resolveAxisStartOffset(
19003
18928
  row,
19004
18929
  visibleRows,
@@ -19006,21 +18931,21 @@ function resolveAnchoredRect(anchor, visibleRows, visibleCols, rowHeights, colWi
19006
18931
  options?.rowIndexByActual,
19007
18932
  options?.rowPrefixSums,
19008
18933
  options?.actualRowPrefixSums
19009
- ) + emuToPixels(rowOffsetEmu);
18934
+ ) + emuToPixels(rowOffsetEmu) * anchorZoomFactor;
19010
18935
  if (anchor.kind === "absolute") {
19011
18936
  return {
19012
- height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
19013
- left: rowHeaderWidth + emuToPixels(anchor.positionEmu.x),
19014
- top: headerHeight + emuToPixels(anchor.positionEmu.y),
19015
- width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18937
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy) * anchorZoomFactor),
18938
+ left: rowHeaderWidth + emuToPixels(anchor.positionEmu.x) * anchorZoomFactor,
18939
+ top: headerHeight + emuToPixels(anchor.positionEmu.y) * anchorZoomFactor,
18940
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx) * anchorZoomFactor)
19016
18941
  };
19017
18942
  }
19018
18943
  if (anchor.kind === "one-cell") {
19019
18944
  return {
19020
- height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18945
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy) * anchorZoomFactor),
19021
18946
  left: resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu),
19022
18947
  top: resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu),
19023
- width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18948
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx) * anchorZoomFactor)
19024
18949
  };
19025
18950
  }
19026
18951
  const left = resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu);
@@ -22180,7 +22105,6 @@ function buildFormControlKindInput(control) {
22180
22105
  return {
22181
22106
  caption: control.caption ?? control.label ?? "",
22182
22107
  cellLink: control.linkedCell,
22183
- firstInGroup: control.firstInGroup,
22184
22108
  kind: "optionButton",
22185
22109
  no3D: control.no3D ?? false,
22186
22110
  state: control.state === "checked" ? "checked" : "unchecked"
@@ -23226,6 +23150,7 @@ function XlsxGrid({
23226
23150
  const canvasCornerOverlayContentRef = React4.useRef(null);
23227
23151
  const canvasImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
23228
23152
  const selectionOverlayRef = React4.useRef(null);
23153
+ const canvasSelectionLayerRef = React4.useRef(null);
23229
23154
  const activeValidationOverlayRef = React4.useRef(null);
23230
23155
  const fillHandleRef = React4.useRef(null);
23231
23156
  const tableMenuRef = React4.useRef(null);
@@ -23251,9 +23176,11 @@ function XlsxGrid({
23251
23176
  const liveGestureZoomRef = React4.useRef(null);
23252
23177
  const pendingLiveGestureZoomStateRef = React4.useRef(null);
23253
23178
  const liveGestureZoomFrameRef = React4.useRef(null);
23179
+ const liveGestureZoomTransformFrameRef = React4.useRef(null);
23254
23180
  const pendingZoomAnchorRef = React4.useRef(null);
23255
23181
  const liveZoomCommitTimerRef = React4.useRef(null);
23256
23182
  const pendingLiveZoomCommitRef = React4.useRef(null);
23183
+ const gestureScrollerRectRef = React4.useRef(null);
23257
23184
  const touchPinchStateRef = React4.useRef(null);
23258
23185
  const safariPinchStartZoomRef = React4.useRef(null);
23259
23186
  const canvasTransformStyleCacheRef = React4.useRef(/* @__PURE__ */ new WeakMap());
@@ -23337,7 +23264,10 @@ function XlsxGrid({
23337
23264
  const setInteractionMode = React4.useCallback((mode) => {
23338
23265
  interactionModeRef.current = mode;
23339
23266
  }, []);
23340
- const worksheet = getActiveWorksheet();
23267
+ const worksheet = React4.useMemo(
23268
+ () => getActiveWorksheet(),
23269
+ [activeSheetIndex, getActiveWorksheet, revision]
23270
+ );
23341
23271
  const mergedRegions = React4.useMemo(() => {
23342
23272
  const regions = [];
23343
23273
  const rawMergedRegions = Array.isArray(worksheet?.mergedRegions) ? worksheet.mergedRegions : [];
@@ -23417,8 +23347,31 @@ function XlsxGrid({
23417
23347
  const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
23418
23348
  const scrollDeltaX = paintedViewport.left - nextLiveViewport.left;
23419
23349
  const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
23420
- const liveZoomTranslateX2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.x * (1 - liveZoomScale2) : 0;
23421
- const liveZoomTranslateY2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.y * (1 - liveZoomScale2) : 0;
23350
+ const resolveClampedLiveZoomAnchor = (scrollOffset, viewportSize, scrollExtent, gestureAnchor) => {
23351
+ if (!isLiveZooming2 || liveZoomScale2 === 1) {
23352
+ return gestureAnchor;
23353
+ }
23354
+ const targetScrollExtent = Math.max(viewportSize, scrollExtent * liveZoomScale2);
23355
+ const targetScrollOffset = clampValue(
23356
+ (scrollOffset + gestureAnchor) * liveZoomScale2 - gestureAnchor,
23357
+ 0,
23358
+ Math.max(0, targetScrollExtent - viewportSize)
23359
+ );
23360
+ return (scrollOffset * liveZoomScale2 - targetScrollOffset) / (1 - liveZoomScale2);
23361
+ };
23362
+ const scroller = scrollRef.current;
23363
+ const liveZoomAnchorX = resolveClampedLiveZoomAnchor(
23364
+ nextLiveViewport.left,
23365
+ nextLiveViewport.width,
23366
+ scroller?.scrollWidth ?? nextLiveViewport.width,
23367
+ currentLiveGestureZoom?.anchor.x ?? 0
23368
+ );
23369
+ const liveZoomAnchorY = resolveClampedLiveZoomAnchor(
23370
+ nextLiveViewport.top,
23371
+ nextLiveViewport.height,
23372
+ scroller?.scrollHeight ?? nextLiveViewport.height,
23373
+ currentLiveGestureZoom?.anchor.y ?? 0
23374
+ );
23422
23375
  const applyElementTransform = (element, transform, willChange, transformOrigin = null) => {
23423
23376
  if (!element) {
23424
23377
  return;
@@ -23439,7 +23392,10 @@ function XlsxGrid({
23439
23392
  willChange
23440
23393
  });
23441
23394
  };
23442
- const applyCanvasTransform = (canvas, translateX, translateY) => {
23395
+ const resolveLiveTranslation = (baseTranslation, anchor, origin) => baseTranslation * liveZoomScale2 + (anchor - origin) * (1 - liveZoomScale2);
23396
+ const applyCanvasTransform = (canvas, baseTranslateX, baseTranslateY, originX, originY, anchorX, anchorY) => {
23397
+ const translateX = resolveLiveTranslation(baseTranslateX, anchorX, originX);
23398
+ const translateY = resolveLiveTranslation(baseTranslateY, anchorY, originY);
23443
23399
  const shouldTransform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1;
23444
23400
  applyElementTransform(
23445
23401
  canvas,
@@ -23447,85 +23403,169 @@ function XlsxGrid({
23447
23403
  shouldTransform ? "transform" : ""
23448
23404
  );
23449
23405
  };
23450
- const applyOverlayTransform = (element, translateX, translateY) => {
23406
+ const applyOverlayTransform = (element, baseTranslateX, baseTranslateY, paneLeft, paneTop, anchorX, anchorY) => {
23407
+ const translateX = resolveLiveTranslation(baseTranslateX, anchorX, paneLeft);
23408
+ const translateY = resolveLiveTranslation(baseTranslateY, anchorY, paneTop);
23451
23409
  applyElementTransform(
23452
23410
  element,
23453
- liveZoomScale2 !== 1 ? `translate3d(${translateX + liveZoomTranslateX2}px, ${translateY + liveZoomTranslateY2}px, 0) scale(${liveZoomScale2})` : `translate3d(${translateX}px, ${translateY}px, 0)`,
23411
+ liveZoomScale2 !== 1 ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : `translate3d(${baseTranslateX}px, ${baseTranslateY}px, 0)`,
23454
23412
  "transform",
23455
23413
  "0 0"
23456
23414
  );
23457
23415
  };
23416
+ const liveScrollBodyViewportWidth = Math.max(0, nextLiveViewport.width - liveFrozenPaneRight);
23417
+ const liveScrollBodyViewportHeight = Math.max(0, nextLiveViewport.height - liveFrozenPaneBottom);
23418
+ const liveScrollBodyCanvasLeft = liveFrozenPaneRight - Math.min(CANVAS_SCROLL_BUFFER_PX, liveScrollBodyViewportWidth);
23419
+ const liveScrollBodyCanvasTop = liveFrozenPaneBottom - Math.min(CANVAS_SCROLL_BUFFER_PX, liveScrollBodyViewportHeight);
23420
+ const selectionLayerTranslateX = (nextLiveViewport.left + liveZoomAnchorX) * (1 - liveZoomScale2);
23421
+ const selectionLayerTranslateY = (nextLiveViewport.top + liveZoomAnchorY) * (1 - liveZoomScale2);
23422
+ const shouldTransformSelectionLayer = experimentalCanvas && isLiveZooming2 && liveZoomScale2 !== 1;
23423
+ applyElementTransform(
23424
+ canvasSelectionLayerRef.current,
23425
+ shouldTransformSelectionLayer ? `translate3d(${selectionLayerTranslateX}px, ${selectionLayerTranslateY}px, 0) scale(${liveZoomScale2})` : "",
23426
+ shouldTransformSelectionLayer ? "transform" : "",
23427
+ "0 0"
23428
+ );
23458
23429
  applyCanvasTransform(
23459
23430
  scrollBodyCanvasRef.current,
23460
- scrollDeltaX + liveZoomTranslateX2,
23461
- scrollDeltaY + liveZoomTranslateY2
23431
+ scrollDeltaX,
23432
+ scrollDeltaY,
23433
+ liveScrollBodyCanvasLeft,
23434
+ liveScrollBodyCanvasTop,
23435
+ liveZoomAnchorX,
23436
+ liveZoomAnchorY
23462
23437
  );
23463
23438
  applyCanvasTransform(
23464
23439
  topBodyCanvasRef.current,
23465
- scrollDeltaX + liveZoomTranslateX2,
23466
- liveZoomTranslateY2
23440
+ scrollDeltaX,
23441
+ 0,
23442
+ liveScrollBodyCanvasLeft,
23443
+ liveDisplayHeaderHeight,
23444
+ liveZoomAnchorX,
23445
+ 0
23467
23446
  );
23468
23447
  applyCanvasTransform(
23469
23448
  leftBodyCanvasRef.current,
23470
- liveZoomTranslateX2,
23471
- scrollDeltaY + liveZoomTranslateY2
23449
+ 0,
23450
+ scrollDeltaY,
23451
+ liveDisplayRowHeaderWidth,
23452
+ liveScrollBodyCanvasTop,
23453
+ 0,
23454
+ liveZoomAnchorY
23472
23455
  );
23473
23456
  applyCanvasTransform(
23474
23457
  cornerBodyCanvasRef.current,
23475
- liveZoomTranslateX2,
23476
- liveZoomTranslateY2
23458
+ 0,
23459
+ 0,
23460
+ liveDisplayRowHeaderWidth,
23461
+ liveDisplayHeaderHeight,
23462
+ 0,
23463
+ 0
23477
23464
  );
23478
23465
  applyCanvasTransform(
23479
23466
  topScrollHeaderCanvasRef.current,
23480
- scrollDeltaX + liveZoomTranslateX2,
23481
- liveZoomTranslateY2
23467
+ scrollDeltaX,
23468
+ 0,
23469
+ liveScrollBodyCanvasLeft,
23470
+ 0,
23471
+ liveZoomAnchorX,
23472
+ 0
23482
23473
  );
23483
23474
  applyCanvasTransform(
23484
23475
  topFrozenHeaderCanvasRef.current,
23485
- liveZoomTranslateX2,
23486
- liveZoomTranslateY2
23476
+ 0,
23477
+ 0,
23478
+ liveDisplayRowHeaderWidth,
23479
+ 0,
23480
+ 0,
23481
+ 0
23487
23482
  );
23488
23483
  applyCanvasTransform(
23489
23484
  leftScrollHeaderCanvasRef.current,
23490
- liveZoomTranslateX2,
23491
- scrollDeltaY + liveZoomTranslateY2
23485
+ 0,
23486
+ scrollDeltaY,
23487
+ 0,
23488
+ liveScrollBodyCanvasTop,
23489
+ 0,
23490
+ liveZoomAnchorY
23492
23491
  );
23493
23492
  applyCanvasTransform(
23494
23493
  leftFrozenHeaderCanvasRef.current,
23495
- liveZoomTranslateX2,
23496
- liveZoomTranslateY2
23494
+ 0,
23495
+ 0,
23496
+ 0,
23497
+ liveDisplayHeaderHeight,
23498
+ 0,
23499
+ 0
23497
23500
  );
23498
23501
  applyCanvasTransform(
23499
23502
  cornerHeaderCanvasRef.current,
23500
- liveZoomTranslateX2,
23501
- liveZoomTranslateY2
23503
+ 0,
23504
+ 0,
23505
+ 0,
23506
+ 0,
23507
+ 0,
23508
+ 0
23502
23509
  );
23503
23510
  applyOverlayTransform(
23504
23511
  canvasScrollOverlayContentRef.current,
23505
23512
  -nextLiveViewport.left - liveFrozenPaneRight,
23506
- -nextLiveViewport.top - liveFrozenPaneBottom
23513
+ -nextLiveViewport.top - liveFrozenPaneBottom,
23514
+ liveFrozenPaneRight,
23515
+ liveFrozenPaneBottom,
23516
+ liveZoomAnchorX,
23517
+ liveZoomAnchorY
23507
23518
  );
23508
23519
  applyOverlayTransform(
23509
23520
  canvasTopOverlayContentRef.current,
23510
23521
  -nextLiveViewport.left - liveFrozenPaneRight,
23511
- -liveDisplayHeaderHeight
23522
+ -liveDisplayHeaderHeight,
23523
+ liveFrozenPaneRight,
23524
+ liveDisplayHeaderHeight,
23525
+ liveZoomAnchorX,
23526
+ 0
23512
23527
  );
23513
23528
  applyOverlayTransform(
23514
23529
  canvasLeftOverlayContentRef.current,
23515
23530
  -liveDisplayRowHeaderWidth,
23516
- -nextLiveViewport.top - liveFrozenPaneBottom
23531
+ -nextLiveViewport.top - liveFrozenPaneBottom,
23532
+ liveDisplayRowHeaderWidth,
23533
+ liveFrozenPaneBottom,
23534
+ 0,
23535
+ liveZoomAnchorY
23517
23536
  );
23518
23537
  applyOverlayTransform(
23519
23538
  canvasCornerOverlayContentRef.current,
23520
23539
  -liveDisplayRowHeaderWidth,
23521
- -liveDisplayHeaderHeight
23540
+ -liveDisplayHeaderHeight,
23541
+ liveDisplayRowHeaderWidth,
23542
+ liveDisplayHeaderHeight,
23543
+ 0,
23544
+ 0
23522
23545
  );
23523
- }, [zoomScale]);
23546
+ }, [experimentalCanvas, zoomScale]);
23524
23547
  const updateLiveGestureZoomState = React4.useCallback((nextState) => {
23525
- const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
23548
+ const previousState = liveGestureZoomRef.current;
23549
+ const resolvedState = typeof nextState === "function" ? nextState(previousState) : nextState;
23526
23550
  liveGestureZoomRef.current = resolvedState;
23551
+ const shouldApplyTransformImmediately = !experimentalCanvas || previousState === null || resolvedState === null;
23552
+ if (shouldApplyTransformImmediately) {
23553
+ if (liveGestureZoomTransformFrameRef.current !== null) {
23554
+ window.cancelAnimationFrame(liveGestureZoomTransformFrameRef.current);
23555
+ liveGestureZoomTransformFrameRef.current = null;
23556
+ }
23557
+ applyCanvasViewportCompensation();
23558
+ } else if (liveGestureZoomTransformFrameRef.current === null) {
23559
+ liveGestureZoomTransformFrameRef.current = window.requestAnimationFrame(() => {
23560
+ liveGestureZoomTransformFrameRef.current = null;
23561
+ applyCanvasViewportCompensation();
23562
+ });
23563
+ }
23564
+ const shouldSyncReactState = !experimentalCanvas || previousState === null !== (resolvedState === null);
23565
+ if (!shouldSyncReactState) {
23566
+ return;
23567
+ }
23527
23568
  pendingLiveGestureZoomStateRef.current = resolvedState;
23528
- applyCanvasViewportCompensation();
23529
23569
  if (liveGestureZoomFrameRef.current !== null) {
23530
23570
  return;
23531
23571
  }
@@ -23535,7 +23575,7 @@ function XlsxGrid({
23535
23575
  pendingLiveGestureZoomStateRef.current = null;
23536
23576
  setLiveGestureZoom(pendingState);
23537
23577
  });
23538
- }, [applyCanvasViewportCompensation]);
23578
+ }, [applyCanvasViewportCompensation, experimentalCanvas]);
23539
23579
  const clearLiveZoomCommitTimer = React4.useCallback(() => {
23540
23580
  if (liveZoomCommitTimerRef.current !== null) {
23541
23581
  window.clearTimeout(liveZoomCommitTimerRef.current);
@@ -23548,6 +23588,7 @@ function XlsxGrid({
23548
23588
  pendingZoomAnchorRef.current = null;
23549
23589
  touchPinchStateRef.current = null;
23550
23590
  safariPinchStartZoomRef.current = null;
23591
+ gestureScrollerRectRef.current = null;
23551
23592
  gestureZoomScaleRef.current = committedZoomScaleRef.current;
23552
23593
  updateLiveGestureZoomState(null);
23553
23594
  }, [clearLiveZoomCommitTimer, updateLiveGestureZoomState]);
@@ -24162,6 +24203,10 @@ function XlsxGrid({
24162
24203
  window.cancelAnimationFrame(liveGestureZoomFrameRef.current);
24163
24204
  liveGestureZoomFrameRef.current = null;
24164
24205
  }
24206
+ if (liveGestureZoomTransformFrameRef.current !== null) {
24207
+ window.cancelAnimationFrame(liveGestureZoomTransformFrameRef.current);
24208
+ liveGestureZoomTransformFrameRef.current = null;
24209
+ }
24165
24210
  if (drawingViewportFrameRef.current !== null) {
24166
24211
  window.cancelAnimationFrame(drawingViewportFrameRef.current);
24167
24212
  drawingViewportFrameRef.current = null;
@@ -24425,6 +24470,7 @@ function XlsxGrid({
24425
24470
  const handleScrollerScroll = React4.useCallback((event) => {
24426
24471
  const currentScroller = event.currentTarget;
24427
24472
  cachedScrollerRectRef.current = null;
24473
+ gestureScrollerRectRef.current = null;
24428
24474
  const paintedViewport = paintedDrawingViewportRef.current;
24429
24475
  const shouldSyncDrawingViewportImmediately = Math.abs(currentScroller.scrollLeft - paintedViewport.left) > CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX || Math.abs(currentScroller.scrollTop - paintedViewport.top) > CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX;
24430
24476
  syncDrawingViewport(currentScroller, { immediate: shouldSyncDrawingViewportImmediately });
@@ -24459,7 +24505,9 @@ function XlsxGrid({
24459
24505
  return;
24460
24506
  }
24461
24507
  event.preventDefault();
24462
- const anchor = resolveEventAnchor(event.clientX, event.clientY, scroller.getBoundingClientRect());
24508
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24509
+ gestureScrollerRectRef.current = scrollerRect;
24510
+ const anchor = resolveEventAnchor(event.clientX, event.clientY, scrollerRect);
24463
24511
  updateLiveGestureZoomTarget(
24464
24512
  gestureZoomScaleRef.current * Math.exp(-delta * WHEEL_ZOOM_SENSITIVITY),
24465
24513
  anchor
@@ -24479,10 +24527,12 @@ function XlsxGrid({
24479
24527
  return;
24480
24528
  }
24481
24529
  event.preventDefault();
24530
+ const scrollerRect = scroller.getBoundingClientRect();
24531
+ gestureScrollerRectRef.current = scrollerRect;
24482
24532
  const anchor = resolveEventAnchor(
24483
24533
  (firstTouch.clientX + secondTouch.clientX) / 2,
24484
24534
  (firstTouch.clientY + secondTouch.clientY) / 2,
24485
- scroller.getBoundingClientRect()
24535
+ scrollerRect
24486
24536
  );
24487
24537
  clearLiveZoomCommitTimer();
24488
24538
  updateLiveGestureZoomState({
@@ -24510,10 +24560,12 @@ function XlsxGrid({
24510
24560
  return;
24511
24561
  }
24512
24562
  event.preventDefault();
24563
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24564
+ gestureScrollerRectRef.current = scrollerRect;
24513
24565
  const anchor = resolveEventAnchor(
24514
24566
  (firstTouch.clientX + secondTouch.clientX) / 2,
24515
24567
  (firstTouch.clientY + secondTouch.clientY) / 2,
24516
- scroller.getBoundingClientRect()
24568
+ scrollerRect
24517
24569
  );
24518
24570
  updateLiveGestureZoomTarget(
24519
24571
  pinchState.startZoomScale * distance / pinchState.startDistance,
@@ -24534,10 +24586,12 @@ function XlsxGrid({
24534
24586
  const handleGestureStart = (event) => {
24535
24587
  const gestureEvent = event;
24536
24588
  event.preventDefault();
24589
+ const scrollerRect = scroller.getBoundingClientRect();
24590
+ gestureScrollerRectRef.current = scrollerRect;
24537
24591
  const anchor = resolveEventAnchor(
24538
24592
  gestureEvent.clientX,
24539
24593
  gestureEvent.clientY,
24540
- scroller.getBoundingClientRect()
24594
+ scrollerRect
24541
24595
  );
24542
24596
  clearLiveZoomCommitTimer();
24543
24597
  safariPinchStartZoomRef.current = committedZoomScaleRef.current;
@@ -24555,10 +24609,12 @@ function XlsxGrid({
24555
24609
  return;
24556
24610
  }
24557
24611
  event.preventDefault();
24612
+ const scrollerRect = gestureScrollerRectRef.current ?? scroller.getBoundingClientRect();
24613
+ gestureScrollerRectRef.current = scrollerRect;
24558
24614
  const anchor = resolveEventAnchor(
24559
24615
  gestureEvent.clientX,
24560
24616
  gestureEvent.clientY,
24561
- scroller.getBoundingClientRect()
24617
+ scrollerRect
24562
24618
  );
24563
24619
  updateLiveGestureZoomTarget(startZoomScale * gestureEvent.scale, anchor);
24564
24620
  };
@@ -25552,7 +25608,8 @@ function XlsxGrid({
25552
25608
  headerHeight: displayHeaderHeight,
25553
25609
  rowIndexByActual,
25554
25610
  rowHeaderWidth: displayRowHeaderWidth,
25555
- rowPrefixSums
25611
+ rowPrefixSums,
25612
+ zoomFactor
25556
25613
  })
25557
25614
  })) : [],
25558
25615
  [
@@ -25570,7 +25627,8 @@ function XlsxGrid({
25570
25627
  rowPrefixSums,
25571
25628
  showImages,
25572
25629
  visibleCols,
25573
- visibleRows
25630
+ visibleRows,
25631
+ zoomFactor
25574
25632
  ]
25575
25633
  );
25576
25634
  const shapeRects = React4.useMemo(
@@ -25583,7 +25641,8 @@ function XlsxGrid({
25583
25641
  headerHeight: displayHeaderHeight,
25584
25642
  rowIndexByActual,
25585
25643
  rowHeaderWidth: displayRowHeaderWidth,
25586
- rowPrefixSums
25644
+ rowPrefixSums,
25645
+ zoomFactor
25587
25646
  }),
25588
25647
  shape
25589
25648
  })) : [],
@@ -25601,7 +25660,8 @@ function XlsxGrid({
25601
25660
  shapes,
25602
25661
  showImages,
25603
25662
  visibleCols,
25604
- visibleRows
25663
+ visibleRows,
25664
+ zoomFactor
25605
25665
  ]
25606
25666
  );
25607
25667
  const formControlRects = React4.useMemo(
@@ -25615,7 +25675,8 @@ function XlsxGrid({
25615
25675
  headerHeight: displayHeaderHeight,
25616
25676
  rowIndexByActual,
25617
25677
  rowHeaderWidth: displayRowHeaderWidth,
25618
- rowPrefixSums
25678
+ rowPrefixSums,
25679
+ zoomFactor
25619
25680
  })
25620
25681
  })) : [],
25621
25682
  [
@@ -25632,7 +25693,8 @@ function XlsxGrid({
25632
25693
  rowPrefixSums,
25633
25694
  showImages,
25634
25695
  visibleCols,
25635
- visibleRows
25696
+ visibleRows,
25697
+ zoomFactor
25636
25698
  ]
25637
25699
  );
25638
25700
  const chartRects = React4.useMemo(
@@ -25646,7 +25708,8 @@ function XlsxGrid({
25646
25708
  headerHeight: displayHeaderHeight,
25647
25709
  rowIndexByActual,
25648
25710
  rowHeaderWidth: displayRowHeaderWidth,
25649
- rowPrefixSums
25711
+ rowPrefixSums,
25712
+ zoomFactor
25650
25713
  })
25651
25714
  })) : [],
25652
25715
  [
@@ -25664,7 +25727,8 @@ function XlsxGrid({
25664
25727
  rowPrefixSums,
25665
25728
  showImages,
25666
25729
  visibleCols,
25667
- visibleRows
25730
+ visibleRows,
25731
+ zoomFactor
25668
25732
  ]
25669
25733
  );
25670
25734
  const shouldBakeCanvasStaticDrawings = Boolean(experimentalCanvas && readOnly && showImages);
@@ -27120,6 +27184,8 @@ function XlsxGrid({
27120
27184
  palette,
27121
27185
  rowHeaderWidth,
27122
27186
  rowSignature: buildRenderedAxisSignature(canvasVisibleRowItems, (item) => item.index),
27187
+ viewportLeft: drawingViewport.left,
27188
+ viewportTop: drawingViewport.top,
27123
27189
  zoomFactor
27124
27190
  };
27125
27191
  const nextHeaderCanvasSignature = {
@@ -27139,7 +27205,7 @@ function XlsxGrid({
27139
27205
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
27140
27206
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
27141
27207
  const previousPaintedViewport = paintedDrawingViewportRef.current;
27142
- const shouldRepaintBody = !(previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.colSignature === nextBodyCanvasSignature.colSignature && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.rowSignature === nextBodyCanvasSignature.rowSignature && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor);
27208
+ const shouldRepaintBody = !(previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.colSignature === nextBodyCanvasSignature.colSignature && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.rowSignature === nextBodyCanvasSignature.rowSignature && previousBodyCanvasSignature.viewportLeft === nextBodyCanvasSignature.viewportLeft && previousBodyCanvasSignature.viewportTop === nextBodyCanvasSignature.viewportTop && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor);
27143
27209
  const shouldRepaintHeaders = !(previousHeaderCanvasSignature && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth && previousHeaderCanvasSignature.colSignature === nextHeaderCanvasSignature.colSignature && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth && previousHeaderCanvasSignature.rowSignature === nextHeaderCanvasSignature.rowSignature && previousHeaderCanvasSignature.viewportLeft === nextHeaderCanvasSignature.viewportLeft && previousHeaderCanvasSignature.viewportTop === nextHeaderCanvasSignature.viewportTop && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor);
27144
27210
  const canBlitBody = Boolean(
27145
27211
  shouldRepaintBody && previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor
@@ -28366,7 +28432,8 @@ function XlsxGrid({
28366
28432
  const headerLabelLiveScale = isLiveZooming ? liveZoomScale : 1;
28367
28433
  const selectionBorderWidth = Math.max(1, zoomFactor);
28368
28434
  const shouldAnimateCanvasSelection = experimentalCanvas && enableCanvasSelectionAnimation;
28369
- const canvasSelectionTransition = shouldAnimateCanvasSelection ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1), width 120ms cubic-bezier(0.22, 1, 0.36, 1), height 120ms cubic-bezier(0.22, 1, 0.36, 1), opacity 100ms linear" : "none";
28435
+ const shouldAnimateCanvasSelectionGeometry = shouldAnimateCanvasSelection && liveGestureZoom === null;
28436
+ const canvasSelectionTransition = shouldAnimateCanvasSelectionGeometry ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1), width 120ms cubic-bezier(0.22, 1, 0.36, 1), height 120ms cubic-bezier(0.22, 1, 0.36, 1), opacity 100ms linear" : "none";
28370
28437
  const rowColSpan = renderedCols.length + 1 + (leadingColumnSpacerWidth > 0 ? 1 : 0) + (trailingColumnSpacerWidth > 0 ? 1 : 0);
28371
28438
  const gutterSeparatorShadow = `inset -1px 0 0 ${palette.border}, inset 0 -1px 0 ${palette.border}`;
28372
28439
  const maxDrawingOverlayZIndex = Math.max(
@@ -30485,81 +30552,101 @@ function XlsxGrid({
30485
30552
  ]
30486
30553
  }
30487
30554
  ),
30488
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30555
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
30489
30556
  "div",
30490
30557
  {
30491
- ref: selectionOverlayRef,
30558
+ ref: canvasSelectionLayerRef,
30492
30559
  style: {
30493
- backgroundColor: selectionFill,
30494
- boxSizing: "border-box",
30495
- boxShadow: `inset 0 0 0 ${selectionBorderWidth}px ${selectionStroke}`,
30496
- contain: "layout paint",
30497
- height: resolvedSelectionOverlay?.height ?? 0,
30498
- left: resolvedSelectionOverlay?.left ?? 0,
30499
- opacity: resolvedSelectionOverlay ? 1 : 0,
30560
+ height: 0,
30561
+ left: 0,
30562
+ overflow: "visible",
30500
30563
  pointerEvents: "none",
30501
30564
  position: "absolute",
30502
- top: resolvedSelectionOverlay?.top ?? 0,
30503
- transition: canvasSelectionTransition,
30504
- visibility: resolvedSelectionOverlay ? "visible" : "hidden",
30505
- willChange: shouldAnimateCanvasSelection ? "left, top, width, height" : void 0,
30506
- width: resolvedSelectionOverlay?.width ?? 0,
30565
+ top: 0,
30566
+ transformOrigin: "0 0",
30567
+ transition: "none",
30568
+ width: 0,
30507
30569
  zIndex: 24
30508
- }
30509
- }
30510
- ),
30511
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30512
- "div",
30513
- {
30514
- ref: activeValidationOverlayRef,
30515
- "aria-hidden": "true",
30516
- style: {
30517
- alignItems: "center",
30518
- color: palette.mutedText,
30519
- display: "inline-flex",
30520
- fontSize: 10 * zoomFactor,
30521
- fontWeight: 700,
30522
- height: 16 * zoomFactor,
30523
- justifyContent: "center",
30524
- opacity: 0,
30525
- pointerEvents: "none",
30526
- position: "absolute",
30527
- transform: "translateY(-50%)",
30528
- visibility: "hidden",
30529
- width: 12 * zoomFactor,
30530
- zIndex: 26
30531
- },
30532
- children: "\u25BE"
30533
- }
30534
- ),
30535
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30536
- "div",
30537
- {
30538
- ref: fillHandleRef,
30539
- onPointerDown: (event) => {
30540
- if (readOnly || event.button !== 0 || !normalizedSelection || !resolvedSelectionOverlay) {
30541
- return;
30542
- }
30543
- event.preventDefault();
30544
- event.stopPropagation();
30545
- startFillDrag(event.pointerId, normalizedSelection);
30546
30570
  },
30547
- style: {
30548
- backgroundColor: selectionStroke,
30549
- border: `${Math.max(1, zoomFactor)}px solid ${palette.surface}`,
30550
- contain: "layout paint",
30551
- cursor: "crosshair",
30552
- display: !readOnly && resolvedSelectionOverlay ? "block" : "none",
30553
- height: 8 * zoomFactor,
30554
- left: resolvedSelectionOverlay ? resolvedSelectionOverlay.left + resolvedSelectionOverlay.width - 4 * zoomFactor : 0,
30555
- pointerEvents: "auto",
30556
- position: "absolute",
30557
- top: resolvedSelectionOverlay ? resolvedSelectionOverlay.top + resolvedSelectionOverlay.height - 4 * zoomFactor : 0,
30558
- transition: shouldAnimateCanvasSelection ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1)" : "none",
30559
- willChange: shouldAnimateCanvasSelection ? "left, top" : void 0,
30560
- width: 8 * zoomFactor,
30561
- zIndex: 25
30562
- }
30571
+ children: [
30572
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30573
+ "div",
30574
+ {
30575
+ ref: selectionOverlayRef,
30576
+ style: {
30577
+ backgroundColor: selectionFill,
30578
+ boxSizing: "border-box",
30579
+ boxShadow: `inset 0 0 0 ${selectionBorderWidth}px ${selectionStroke}`,
30580
+ contain: "layout paint",
30581
+ height: resolvedSelectionOverlay?.height ?? 0,
30582
+ left: resolvedSelectionOverlay?.left ?? 0,
30583
+ opacity: resolvedSelectionOverlay ? 1 : 0,
30584
+ pointerEvents: "none",
30585
+ position: "absolute",
30586
+ top: resolvedSelectionOverlay?.top ?? 0,
30587
+ transition: canvasSelectionTransition,
30588
+ visibility: resolvedSelectionOverlay ? "visible" : "hidden",
30589
+ willChange: shouldAnimateCanvasSelectionGeometry ? "left, top, width, height" : void 0,
30590
+ width: resolvedSelectionOverlay?.width ?? 0,
30591
+ zIndex: 24
30592
+ }
30593
+ }
30594
+ ),
30595
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30596
+ "div",
30597
+ {
30598
+ ref: activeValidationOverlayRef,
30599
+ "aria-hidden": "true",
30600
+ style: {
30601
+ alignItems: "center",
30602
+ color: palette.mutedText,
30603
+ display: "inline-flex",
30604
+ fontSize: 10 * zoomFactor,
30605
+ fontWeight: 700,
30606
+ height: 16 * zoomFactor,
30607
+ justifyContent: "center",
30608
+ opacity: 0,
30609
+ pointerEvents: "none",
30610
+ position: "absolute",
30611
+ transform: "translateY(-50%)",
30612
+ visibility: "hidden",
30613
+ width: 12 * zoomFactor,
30614
+ zIndex: 26
30615
+ },
30616
+ children: "\u25BE"
30617
+ }
30618
+ ),
30619
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
30620
+ "div",
30621
+ {
30622
+ ref: fillHandleRef,
30623
+ onPointerDown: (event) => {
30624
+ if (readOnly || event.button !== 0 || !normalizedSelection || !resolvedSelectionOverlay) {
30625
+ return;
30626
+ }
30627
+ event.preventDefault();
30628
+ event.stopPropagation();
30629
+ startFillDrag(event.pointerId, normalizedSelection);
30630
+ },
30631
+ style: {
30632
+ backgroundColor: selectionStroke,
30633
+ border: `${Math.max(1, zoomFactor)}px solid ${palette.surface}`,
30634
+ contain: "layout paint",
30635
+ cursor: "crosshair",
30636
+ display: !readOnly && resolvedSelectionOverlay ? "block" : "none",
30637
+ height: 8 * zoomFactor,
30638
+ left: resolvedSelectionOverlay ? resolvedSelectionOverlay.left + resolvedSelectionOverlay.width - 4 * zoomFactor : 0,
30639
+ pointerEvents: "auto",
30640
+ position: "absolute",
30641
+ top: resolvedSelectionOverlay ? resolvedSelectionOverlay.top + resolvedSelectionOverlay.height - 4 * zoomFactor : 0,
30642
+ transition: shouldAnimateCanvasSelectionGeometry ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1)" : "none",
30643
+ willChange: shouldAnimateCanvasSelectionGeometry ? "left, top" : void 0,
30644
+ width: 8 * zoomFactor,
30645
+ zIndex: 25
30646
+ }
30647
+ }
30648
+ )
30649
+ ]
30563
30650
  }
30564
30651
  ),
30565
30652
  resizeGuide ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(