@extend-ai/react-xlsx 0.8.2 → 0.8.4

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
@@ -4575,6 +4575,10 @@ function parseSheetState(archive, path, options) {
4575
4575
  let hasVerticalMerges = false;
4576
4576
  let maxHorizontalMergeEndCol = -1;
4577
4577
  let maxVerticalMergeEndRow = -1;
4578
+ let minContentCol = Number.POSITIVE_INFINITY;
4579
+ let minContentRow = Number.POSITIVE_INFINITY;
4580
+ let maxContentCol = -1;
4581
+ let maxContentRow = -1;
4578
4582
  const columnWidthCharacterWidthPx = measureColumnCharacterWidthPx(
4579
4583
  options?.defaultFont?.family,
4580
4584
  options?.defaultFont?.sizePt
@@ -4587,6 +4591,26 @@ function parseSheetState(archive, path, options) {
4587
4591
  sheetViewNode?.getAttribute("zoomScale") ?? sheetViewNode?.getAttribute("zoomScaleNormal") ?? Number.NaN
4588
4592
  );
4589
4593
  const zoomScale = Number.isFinite(rawZoomScale) && rawZoomScale > 0 ? rawZoomScale : 100;
4594
+ const trackContentCell = (cellRef) => {
4595
+ if (!cellRef) {
4596
+ return;
4597
+ }
4598
+ const cell = parseA1CellReference(cellRef);
4599
+ if (!cell) {
4600
+ return;
4601
+ }
4602
+ minContentCol = Math.min(minContentCol, cell.col);
4603
+ minContentRow = Math.min(minContentRow, cell.row);
4604
+ maxContentCol = Math.max(maxContentCol, cell.col);
4605
+ maxContentRow = Math.max(maxContentRow, cell.row);
4606
+ };
4607
+ const isMeaningfulCellNode = (cellNode) => {
4608
+ if (getFirstChild(cellNode, "f") || getFirstChild(cellNode, "is")) {
4609
+ return true;
4610
+ }
4611
+ const valueNode = getFirstChild(cellNode, "v");
4612
+ return Boolean(valueNode && (valueNode.textContent ?? "").length > 0);
4613
+ };
4590
4614
  getLocalElements(document2, "row").forEach((rowNode) => {
4591
4615
  const rowIndex = Number(rowNode.getAttribute("r") ?? 0) - 1;
4592
4616
  const height = Number(rowNode.getAttribute("ht") ?? Number.NaN);
@@ -4601,17 +4625,36 @@ function parseSheetState(archive, path, options) {
4601
4625
  if (rowIndex >= 0 && isHidden) {
4602
4626
  hiddenRows.add(rowIndex);
4603
4627
  }
4604
- if (includeCachedFormulaValues) {
4605
- getChildElements(rowNode, "c").forEach((cellNode) => {
4628
+ getChildElements(rowNode, "c").forEach((cellNode) => {
4629
+ const cellRef = cellNode.getAttribute("r");
4630
+ if (isMeaningfulCellNode(cellNode)) {
4631
+ trackContentCell(cellRef);
4632
+ }
4633
+ if (includeCachedFormulaValues) {
4606
4634
  const formulaNode = getFirstChild(cellNode, "f");
4607
4635
  const valueNode = getFirstChild(cellNode, "v");
4608
- const cellRef = cellNode.getAttribute("r");
4609
4636
  if (formulaNode && valueNode && cellRef) {
4610
4637
  cachedFormulaValues[cellRef] = valueNode.textContent ?? "";
4611
4638
  }
4612
- });
4639
+ }
4640
+ });
4641
+ });
4642
+ getLocalElements(document2, "mergeCell").forEach((mergeNode) => {
4643
+ const reference = mergeNode.getAttribute("ref");
4644
+ const range = reference ? parseA1RangeReference(reference) : null;
4645
+ if (!range) {
4646
+ return;
4647
+ }
4648
+ if (range.end.col > range.start.col) {
4649
+ hasHorizontalMerges = true;
4650
+ maxHorizontalMergeEndCol = Math.max(maxHorizontalMergeEndCol, range.end.col);
4651
+ }
4652
+ if (range.end.row > range.start.row) {
4653
+ hasVerticalMerges = true;
4654
+ maxVerticalMergeEndRow = Math.max(maxVerticalMergeEndRow, range.end.row);
4613
4655
  }
4614
4656
  });
4657
+ const maxMetadataCol = Math.max(maxContentCol, maxHorizontalMergeEndCol, 0) + 256;
4615
4658
  getLocalElements(document2, "col").forEach((colNode) => {
4616
4659
  const min = Number(colNode.getAttribute("min") ?? 0) - 1;
4617
4660
  const max = Number(colNode.getAttribute("max") ?? 0) - 1;
@@ -4623,7 +4666,7 @@ function parseSheetState(archive, path, options) {
4623
4666
  return;
4624
4667
  }
4625
4668
  }
4626
- for (let col = min; col <= max; col += 1) {
4669
+ for (let col = min; col <= Math.min(max, maxMetadataCol); col += 1) {
4627
4670
  if (col >= 0) {
4628
4671
  if (Number.isFinite(width)) {
4629
4672
  const widthPx = sheetColumnWidthToPixels(width, columnWidthCharacterWidthPx);
@@ -4638,21 +4681,6 @@ function parseSheetState(archive, path, options) {
4638
4681
  }
4639
4682
  }
4640
4683
  });
4641
- getLocalElements(document2, "mergeCell").forEach((mergeNode) => {
4642
- const reference = mergeNode.getAttribute("ref");
4643
- const range = reference ? parseA1RangeReference(reference) : null;
4644
- if (!range) {
4645
- return;
4646
- }
4647
- if (range.end.col > range.start.col) {
4648
- hasHorizontalMerges = true;
4649
- maxHorizontalMergeEndCol = Math.max(maxHorizontalMergeEndCol, range.end.col);
4650
- }
4651
- if (range.end.row > range.start.row) {
4652
- hasVerticalMerges = true;
4653
- maxVerticalMergeEndRow = Math.max(maxVerticalMergeEndRow, range.end.row);
4654
- }
4655
- });
4656
4684
  return {
4657
4685
  cachedFormulaValues,
4658
4686
  columnWidthCharacterWidthPx,
@@ -4665,6 +4693,10 @@ function parseSheetState(archive, path, options) {
4665
4693
  hasVerticalMerges,
4666
4694
  maxHorizontalMergeEndCol,
4667
4695
  maxVerticalMergeEndRow,
4696
+ maxContentCol,
4697
+ maxContentRow,
4698
+ minContentCol: Number.isFinite(minContentCol) ? minContentCol : -1,
4699
+ minContentRow: Number.isFinite(minContentRow) ? minContentRow : -1,
4668
4700
  hiddenCols: [...hiddenCols].sort((left, right) => left - right),
4669
4701
  hiddenRows: [...hiddenRows].sort((left, right) => left - right),
4670
4702
  rowHeightOverridesPx,
@@ -6694,6 +6726,20 @@ function resolveDisplayFileName(src, fileName) {
6694
6726
  return lastSegment;
6695
6727
  }
6696
6728
  }
6729
+ function resolveSheetDisplayUsedRange(usedRange, sheetState) {
6730
+ const [minRow, minCol, maxRow, maxCol] = usedRange;
6731
+ const maxMeaningfulRow = Math.max(sheetState?.maxContentRow ?? -1, sheetState?.maxVerticalMergeEndRow ?? -1);
6732
+ const maxMeaningfulCol = Math.max(sheetState?.maxContentCol ?? -1, sheetState?.maxHorizontalMergeEndCol ?? -1);
6733
+ if (maxMeaningfulRow < 0 && maxMeaningfulCol < 0) {
6734
+ return usedRange;
6735
+ }
6736
+ return [
6737
+ sheetState?.minContentRow !== void 0 && sheetState.minContentRow >= 0 ? Math.min(minRow, sheetState.minContentRow) : minRow,
6738
+ sheetState?.minContentCol !== void 0 && sheetState.minContentCol >= 0 ? Math.min(minCol, sheetState.minContentCol) : minCol,
6739
+ maxMeaningfulRow >= 0 ? Math.min(maxRow, maxMeaningfulRow) : maxRow,
6740
+ maxMeaningfulCol >= 0 ? Math.min(maxCol, maxMeaningfulCol) : maxCol
6741
+ ];
6742
+ }
6697
6743
  function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette, styleById, namedCellStyleByName, tableStyleByName, showHiddenSheets = false) {
6698
6744
  const sheets = [];
6699
6745
  for (let index = 0; index < workbook.sheetCount; index += 1) {
@@ -6760,7 +6806,7 @@ function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette,
6760
6806
  });
6761
6807
  continue;
6762
6808
  }
6763
- const [minRow, minCol, maxRow, maxCol] = usedRange;
6809
+ const [minRow, minCol, maxRow, maxCol] = resolveSheetDisplayUsedRange(usedRange, sheetState);
6764
6810
  let visibleRowsCache = null;
6765
6811
  let visibleColsCache = null;
6766
6812
  let rowHeightsCache = null;
@@ -16502,7 +16548,11 @@ var SELECTION_DRAG_THRESHOLD_PX = 4;
16502
16548
  var IMAGE_MIN_SIZE_PX = 16;
16503
16549
  var IMAGE_HANDLE_SIZE_PX = 10;
16504
16550
  var CANVAS_RESIZE_HIT_SLOP_PX = 8;
16505
- var CANVAS_VIEWPORT_OVERSCAN_PX = 240;
16551
+ var CANVAS_VIEWPORT_OVERSCAN_PX = 480;
16552
+ var CANVAS_SCROLL_BUFFER_PX = 480;
16553
+ var CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX - 96;
16554
+ var CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX * 1.5;
16555
+ var CANVAS_DIRTY_CELL_CULL_MARGIN_PX = CANVAS_VIEWPORT_OVERSCAN_PX * 2;
16506
16556
  var THUMBNAIL_DEFAULT_MAX_DIMENSION = 192;
16507
16557
  var THUMBNAIL_FALLBACK_ROWS = 12;
16508
16558
  var THUMBNAIL_FALLBACK_COLS = 8;
@@ -16517,6 +16567,12 @@ var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669",
16517
16567
  var SHEET_SURFACE = "#ffffff";
16518
16568
  var DEFAULT_CELL_PADDING = "0 4px";
16519
16569
  var IMAGE_HANDLE_POSITIONS = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
16570
+ var CANVAS_CELL_STYLE_CACHE_LIMIT = 4096;
16571
+ var CANVAS_TEXT_MEASURE_CACHE_LIMIT = 2e4;
16572
+ var CANVAS_TEXT_TRUNCATE_CACHE_LIMIT = 4096;
16573
+ var CANVAS_TEXT_WRAP_CACHE_LIMIT = 2048;
16574
+ var CANVAS_PATH2D_CACHE_LIMIT = 512;
16575
+ var EMPTY_VIRTUAL_ITEMS = [];
16520
16576
  var IMAGE_HANDLE_CURSOR = {
16521
16577
  e: "ew-resize",
16522
16578
  n: "ns-resize",
@@ -16560,6 +16616,50 @@ var NUMERIC_LENGTH_STYLE_KEYS = /* @__PURE__ */ new Set([
16560
16616
  "top",
16561
16617
  "width"
16562
16618
  ]);
16619
+ var canvasCellStyleCache = /* @__PURE__ */ new Map();
16620
+ var canvasTextMeasureCache = /* @__PURE__ */ new Map();
16621
+ var canvasTextTruncateCache = /* @__PURE__ */ new Map();
16622
+ var canvasTextWrapCache = /* @__PURE__ */ new Map();
16623
+ var canvasPath2DCache = /* @__PURE__ */ new Map();
16624
+ function rememberBoundedCacheValue(cache, key, value, limit) {
16625
+ if (cache.size >= limit) {
16626
+ const oldestKey = cache.keys().next().value;
16627
+ if (oldestKey !== void 0) {
16628
+ cache.delete(oldestKey);
16629
+ }
16630
+ }
16631
+ cache.set(key, value);
16632
+ return value;
16633
+ }
16634
+ function roundCanvasCacheWidth(width) {
16635
+ return Math.round(width * 4) / 4;
16636
+ }
16637
+ function measureCanvasTextWidth(context, text) {
16638
+ if (text.length === 0) {
16639
+ return 0;
16640
+ }
16641
+ const cacheKey = `${context.font}\0${text}`;
16642
+ const cached = canvasTextMeasureCache.get(cacheKey);
16643
+ if (cached !== void 0) {
16644
+ return cached;
16645
+ }
16646
+ return rememberBoundedCacheValue(
16647
+ canvasTextMeasureCache,
16648
+ cacheKey,
16649
+ context.measureText(text).width,
16650
+ CANVAS_TEXT_MEASURE_CACHE_LIMIT
16651
+ );
16652
+ }
16653
+ function getCachedCanvasPath2D(path) {
16654
+ if (typeof Path2D === "undefined") {
16655
+ return null;
16656
+ }
16657
+ const cached = canvasPath2DCache.get(path);
16658
+ if (cached) {
16659
+ return cached;
16660
+ }
16661
+ return rememberBoundedCacheValue(canvasPath2DCache, path, new Path2D(path), CANVAS_PATH2D_CACHE_LIMIT);
16662
+ }
16563
16663
  function scaleCssLengthExpression(value, scale) {
16564
16664
  if (scale === 1) {
16565
16665
  return value;
@@ -16740,7 +16840,6 @@ function strokeCanvasBorderSide(context, side, rect, border) {
16740
16840
  const right = rect.left + rect.width;
16741
16841
  const top = rect.top;
16742
16842
  const bottom = rect.top + rect.height;
16743
- context.save();
16744
16843
  context.strokeStyle = border.color;
16745
16844
  context.lineWidth = border.width;
16746
16845
  applyCanvasBorderDash(context, border.style, border.width);
@@ -16770,13 +16869,22 @@ function strokeCanvasBorderSide(context, side, rect, border) {
16770
16869
  } else {
16771
16870
  strokeLine(0);
16772
16871
  }
16773
- context.restore();
16872
+ context.setLineDash([]);
16873
+ context.lineCap = "butt";
16874
+ context.lineWidth = 1;
16774
16875
  }
16775
16876
  function truncateCanvasText(context, text, maxWidth) {
16776
16877
  if (maxWidth <= 0 || text.length === 0) {
16777
16878
  return "";
16778
16879
  }
16779
- if (context.measureText(text).width <= maxWidth) {
16880
+ const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);
16881
+ const cacheKey = `${context.font}\0${roundedMaxWidth}\0${text}`;
16882
+ const cached = canvasTextTruncateCache.get(cacheKey);
16883
+ if (cached !== void 0) {
16884
+ return cached;
16885
+ }
16886
+ if (measureCanvasTextWidth(context, text) <= maxWidth) {
16887
+ rememberBoundedCacheValue(canvasTextTruncateCache, cacheKey, text, CANVAS_TEXT_TRUNCATE_CACHE_LIMIT);
16780
16888
  return text;
16781
16889
  }
16782
16890
  const ellipsis = "\u2026";
@@ -16785,13 +16893,18 @@ function truncateCanvasText(context, text, maxWidth) {
16785
16893
  while (low < high) {
16786
16894
  const mid = Math.ceil((low + high) / 2);
16787
16895
  const candidate = `${text.slice(0, mid)}${ellipsis}`;
16788
- if (context.measureText(candidate).width <= maxWidth) {
16896
+ if (measureCanvasTextWidth(context, candidate) <= maxWidth) {
16789
16897
  low = mid;
16790
16898
  } else {
16791
16899
  high = mid - 1;
16792
16900
  }
16793
16901
  }
16794
- return low <= 0 ? ellipsis : `${text.slice(0, low)}${ellipsis}`;
16902
+ return rememberBoundedCacheValue(
16903
+ canvasTextTruncateCache,
16904
+ cacheKey,
16905
+ low <= 0 ? ellipsis : `${text.slice(0, low)}${ellipsis}`,
16906
+ CANVAS_TEXT_TRUNCATE_CACHE_LIMIT
16907
+ );
16795
16908
  }
16796
16909
  function resolveCanvasFontSizePx(style, fallbackSize = 12) {
16797
16910
  const rawFontSize = style.fontSize;
@@ -16850,7 +16963,7 @@ function resolveCanvasWrapIndex(context, text, maxWidth) {
16850
16963
  while (low <= high) {
16851
16964
  const mid = Math.floor((low + high) / 2);
16852
16965
  const candidate = text.slice(0, mid);
16853
- if (context.measureText(candidate).width <= maxWidth) {
16966
+ if (measureCanvasTextWidth(context, candidate) <= maxWidth) {
16854
16967
  best = mid;
16855
16968
  low = mid + 1;
16856
16969
  } else {
@@ -16867,6 +16980,12 @@ function wrapCanvasText(context, text, maxWidth) {
16867
16980
  return text.replace(/\r\n?/g, "\n").split("\n");
16868
16981
  }
16869
16982
  const normalized = text.replace(/\r\n?/g, "\n");
16983
+ const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);
16984
+ const cacheKey = `${context.font}\0${roundedMaxWidth}\0${normalized}`;
16985
+ const cached = canvasTextWrapCache.get(cacheKey);
16986
+ if (cached) {
16987
+ return cached;
16988
+ }
16870
16989
  const paragraphs = normalized.split("\n");
16871
16990
  const lines = [];
16872
16991
  for (const paragraph of paragraphs) {
@@ -16876,7 +16995,7 @@ function wrapCanvasText(context, text, maxWidth) {
16876
16995
  }
16877
16996
  let remaining = paragraph;
16878
16997
  while (remaining.length > 0) {
16879
- if (context.measureText(remaining).width <= maxWidth) {
16998
+ if (measureCanvasTextWidth(context, remaining) <= maxWidth) {
16880
16999
  lines.push(remaining);
16881
17000
  break;
16882
17001
  }
@@ -16894,10 +17013,31 @@ function wrapCanvasText(context, text, maxWidth) {
16894
17013
  remaining = remaining.slice(breakIndex).replace(/^\s+/g, "");
16895
17014
  }
16896
17015
  }
16897
- return lines;
17016
+ return rememberBoundedCacheValue(canvasTextWrapCache, cacheKey, lines, CANVAS_TEXT_WRAP_CACHE_LIMIT);
16898
17017
  }
16899
17018
  function buildCanvasCellStyleCache(style) {
16900
- return {
17019
+ const cacheKey = [
17020
+ style.font,
17021
+ style.fontStyle,
17022
+ style.fontWeight,
17023
+ style.fontSize,
17024
+ style.fontFamily,
17025
+ style.borderTop,
17026
+ style.borderRight,
17027
+ style.borderBottom,
17028
+ style.borderLeft,
17029
+ style.padding,
17030
+ style.textAlign,
17031
+ style.color,
17032
+ style.textDecoration,
17033
+ style.textOverflow,
17034
+ style.whiteSpace
17035
+ ].map((value) => value == null ? "" : String(value)).join("");
17036
+ const cached = canvasCellStyleCache.get(cacheKey);
17037
+ if (cached) {
17038
+ return cached;
17039
+ }
17040
+ return rememberBoundedCacheValue(canvasCellStyleCache, cacheKey, {
16901
17041
  baseFont: resolveCanvasFont(style, 12),
16902
17042
  bottomBorder: parseCanvasBorderDeclaration(style.borderBottom),
16903
17043
  leftBorder: parseCanvasBorderDeclaration(style.borderLeft),
@@ -16909,7 +17049,7 @@ function buildCanvasCellStyleCache(style) {
16909
17049
  textOverflowEllipsis: style.textOverflow === "ellipsis",
16910
17050
  topBorder: parseCanvasBorderDeclaration(style.borderTop),
16911
17051
  usesWrappedText: style.whiteSpace === "pre-wrap"
16912
- };
17052
+ }, CANVAS_CELL_STYLE_CACHE_LIMIT);
16913
17053
  }
16914
17054
  function splitCssGradientArgs(value) {
16915
17055
  const parts = [];
@@ -18418,6 +18558,44 @@ function resolveShapeVector(shape) {
18418
18558
  }
18419
18559
  return buildPresetShapePath(shape);
18420
18560
  }
18561
+ function drawCanvasRoundedRect(context, left, top, width, height, radius) {
18562
+ const safeRadius = Math.max(0, Math.min(radius, width / 2, height / 2));
18563
+ context.beginPath();
18564
+ context.moveTo(left + safeRadius, top);
18565
+ context.lineTo(left + width - safeRadius, top);
18566
+ context.quadraticCurveTo(left + width, top, left + width, top + safeRadius);
18567
+ context.lineTo(left + width, top + height - safeRadius);
18568
+ context.quadraticCurveTo(left + width, top + height, left + width - safeRadius, top + height);
18569
+ context.lineTo(left + safeRadius, top + height);
18570
+ context.quadraticCurveTo(left, top + height, left, top + height - safeRadius);
18571
+ context.lineTo(left, top + safeRadius);
18572
+ context.quadraticCurveTo(left, top, left + safeRadius, top);
18573
+ context.closePath();
18574
+ }
18575
+ function applyCanvasShapeDash(context, dash, lineWidth) {
18576
+ if (!dash) {
18577
+ context.setLineDash([]);
18578
+ return;
18579
+ }
18580
+ const unit = Math.max(1, lineWidth);
18581
+ switch (dash) {
18582
+ case "dash":
18583
+ context.setLineDash([4 * unit, 3 * unit]);
18584
+ break;
18585
+ case "dashDot":
18586
+ context.setLineDash([4 * unit, 2 * unit, unit, 2 * unit]);
18587
+ break;
18588
+ case "dot":
18589
+ context.setLineDash([unit, 2 * unit]);
18590
+ break;
18591
+ case "lgDash":
18592
+ context.setLineDash([8 * unit, 3 * unit]);
18593
+ break;
18594
+ default:
18595
+ context.setLineDash([]);
18596
+ break;
18597
+ }
18598
+ }
18421
18599
  function resolveShapeLineEndMarker(type, markerId, color, strokeWidth, rect, viewBox) {
18422
18600
  if (type !== "triangle") {
18423
18601
  return null;
@@ -19020,12 +19198,12 @@ function measureTextWidth(value, style) {
19020
19198
  return value.length * 7;
19021
19199
  }
19022
19200
  textMeasureCanvas ??= document.createElement("canvas");
19023
- const context = textMeasureCanvas.getContext("2d");
19201
+ const context = textMeasureCanvas.getContext("2d", { alpha: false });
19024
19202
  if (!context) {
19025
19203
  return value.length * 7;
19026
19204
  }
19027
19205
  context.font = buildCanvasFont(style);
19028
- return context.measureText(value).width;
19206
+ return measureCanvasTextWidth(context, value);
19029
19207
  }
19030
19208
  function measureWrappedTextHeight(value, style, widthPx) {
19031
19209
  if (!value) {
@@ -19039,7 +19217,7 @@ function measureWrappedTextHeight(value, style, widthPx) {
19039
19217
  return fallbackLineCount * lineHeight + padding.top + padding.bottom;
19040
19218
  }
19041
19219
  textMeasureCanvas ??= document.createElement("canvas");
19042
- const context = textMeasureCanvas.getContext("2d");
19220
+ const context = textMeasureCanvas.getContext("2d", { alpha: false });
19043
19221
  if (!context) {
19044
19222
  return fallbackLineCount * lineHeight + padding.top + padding.bottom;
19045
19223
  }
@@ -20137,7 +20315,7 @@ function blitCanvasWithScrollDelta(canvas, context, bufferCanvas, dpr, width, he
20137
20315
  if (bufferCanvas.height !== deviceHeight) {
20138
20316
  bufferCanvas.height = deviceHeight;
20139
20317
  }
20140
- const bufferContext = bufferCanvas.getContext("2d");
20318
+ const bufferContext = bufferCanvas.getContext("2d", { alpha: false });
20141
20319
  if (!bufferContext) {
20142
20320
  return null;
20143
20321
  }
@@ -20988,12 +21166,14 @@ function XlsxGrid({
20988
21166
  renderImageSelection,
20989
21167
  renderTableHeaderMenu,
20990
21168
  enableGestureZoom = true,
20991
- experimentalCanvas = false,
21169
+ experimentalCanvas = true,
20992
21170
  selectionColor,
20993
21171
  selectionFillColor,
20994
21172
  selectionHeaderColor,
20995
21173
  showImages = true
20996
21174
  }) {
21175
+ const xlsxCanvasProfileTarget = typeof window !== "undefined" ? window.__xlsxCanvasProfile : void 0;
21176
+ const xlsxGridRenderStart = xlsxCanvasProfileTarget ? performance.now() : 0;
20997
21177
  const {
20998
21178
  activeCell,
20999
21179
  activeSheet,
@@ -21077,6 +21257,11 @@ function XlsxGrid({
21077
21257
  const leftFrozenHeaderCanvasRef = React4.useRef(null);
21078
21258
  const leftScrollHeaderCanvasRef = React4.useRef(null);
21079
21259
  const cornerHeaderCanvasRef = React4.useRef(null);
21260
+ const canvasScrollOverlayContentRef = React4.useRef(null);
21261
+ const canvasTopOverlayContentRef = React4.useRef(null);
21262
+ const canvasLeftOverlayContentRef = React4.useRef(null);
21263
+ const canvasCornerOverlayContentRef = React4.useRef(null);
21264
+ const canvasImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
21080
21265
  const selectionOverlayRef = React4.useRef(null);
21081
21266
  const activeValidationOverlayRef = React4.useRef(null);
21082
21267
  const fillHandleRef = React4.useRef(null);
@@ -21105,6 +21290,7 @@ function XlsxGrid({
21105
21290
  const pendingLiveZoomCommitRef = React4.useRef(null);
21106
21291
  const touchPinchStateRef = React4.useRef(null);
21107
21292
  const safariPinchStartZoomRef = React4.useRef(null);
21293
+ const canvasTransformStyleCacheRef = React4.useRef(/* @__PURE__ */ new WeakMap());
21108
21294
  const displayedSelectionRef = React4.useRef(null);
21109
21295
  const firstVisibleColRef = React4.useRef(void 0);
21110
21296
  const lastVisibleColRef = React4.useRef(void 0);
@@ -21130,12 +21316,19 @@ function XlsxGrid({
21130
21316
  const [fillPreviewRange, setFillPreviewRange] = React4.useState(null);
21131
21317
  const [chartPreviewRect, setChartPreviewRect] = React4.useState(null);
21132
21318
  const [liveGestureZoom, setLiveGestureZoom] = React4.useState(null);
21319
+ const [canvasImageLoadVersion, setCanvasImageLoadVersion] = React4.useState(0);
21133
21320
  const liveDrawingViewportRef = React4.useRef({
21134
21321
  height: 0,
21135
21322
  left: 0,
21136
21323
  top: 0,
21137
21324
  width: 0
21138
21325
  });
21326
+ const canvasLayoutMetricsRef = React4.useRef({
21327
+ displayHeaderHeight: HEADER_HEIGHT,
21328
+ displayRowHeaderWidth: ROW_HEADER_WIDTH,
21329
+ frozenPaneBottom: HEADER_HEIGHT,
21330
+ frozenPaneRight: ROW_HEADER_WIDTH
21331
+ });
21139
21332
  const paintedDrawingViewportRef = React4.useRef({
21140
21333
  height: 0,
21141
21334
  left: 0,
@@ -21245,6 +21438,12 @@ function XlsxGrid({
21245
21438
  const applyCanvasViewportCompensation = React4.useCallback((liveViewport) => {
21246
21439
  const nextLiveViewport = liveViewport ?? liveDrawingViewportRef.current;
21247
21440
  const paintedViewport = paintedDrawingViewportRef.current;
21441
+ const {
21442
+ displayHeaderHeight: liveDisplayHeaderHeight,
21443
+ displayRowHeaderWidth: liveDisplayRowHeaderWidth,
21444
+ frozenPaneBottom: liveFrozenPaneBottom,
21445
+ frozenPaneRight: liveFrozenPaneRight
21446
+ } = canvasLayoutMetricsRef.current;
21248
21447
  const currentLiveGestureZoom = liveGestureZoomRef.current;
21249
21448
  const isLiveZooming2 = currentLiveGestureZoom !== null && zoomScale === currentLiveGestureZoom.baseZoomScale;
21250
21449
  const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
@@ -21252,12 +21451,41 @@ function XlsxGrid({
21252
21451
  const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
21253
21452
  const liveZoomTranslateX2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.x * (1 - liveZoomScale2) : 0;
21254
21453
  const liveZoomTranslateY2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.y * (1 - liveZoomScale2) : 0;
21255
- const applyCanvasTransform = (canvas, translateX, translateY) => {
21256
- if (!canvas) {
21454
+ const applyElementTransform = (element, transform, willChange, transformOrigin = null) => {
21455
+ if (!element) {
21257
21456
  return;
21258
21457
  }
21259
- canvas.style.transform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1 ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : "";
21260
- canvas.style.willChange = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1 ? "transform" : "";
21458
+ const cached = canvasTransformStyleCacheRef.current.get(element);
21459
+ if (cached?.transform !== transform || element.style.transform !== transform) {
21460
+ element.style.transform = transform;
21461
+ }
21462
+ if (cached?.willChange !== willChange || element.style.willChange !== willChange) {
21463
+ element.style.willChange = willChange;
21464
+ }
21465
+ if (transformOrigin !== null && (cached?.transformOrigin !== transformOrigin || element.style.transformOrigin !== transformOrigin)) {
21466
+ element.style.transformOrigin = transformOrigin;
21467
+ }
21468
+ canvasTransformStyleCacheRef.current.set(element, {
21469
+ transform,
21470
+ transformOrigin,
21471
+ willChange
21472
+ });
21473
+ };
21474
+ const applyCanvasTransform = (canvas, translateX, translateY) => {
21475
+ const shouldTransform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1;
21476
+ applyElementTransform(
21477
+ canvas,
21478
+ shouldTransform ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : "",
21479
+ shouldTransform ? "transform" : ""
21480
+ );
21481
+ };
21482
+ const applyOverlayTransform = (element, translateX, translateY) => {
21483
+ applyElementTransform(
21484
+ element,
21485
+ liveZoomScale2 !== 1 ? `translate3d(${translateX + liveZoomTranslateX2}px, ${translateY + liveZoomTranslateY2}px, 0) scale(${liveZoomScale2})` : `translate3d(${translateX}px, ${translateY}px, 0)`,
21486
+ "transform",
21487
+ "0 0"
21488
+ );
21261
21489
  };
21262
21490
  applyCanvasTransform(
21263
21491
  scrollBodyCanvasRef.current,
@@ -21304,6 +21532,26 @@ function XlsxGrid({
21304
21532
  liveZoomTranslateX2,
21305
21533
  liveZoomTranslateY2
21306
21534
  );
21535
+ applyOverlayTransform(
21536
+ canvasScrollOverlayContentRef.current,
21537
+ -nextLiveViewport.left - liveFrozenPaneRight,
21538
+ -nextLiveViewport.top - liveFrozenPaneBottom
21539
+ );
21540
+ applyOverlayTransform(
21541
+ canvasTopOverlayContentRef.current,
21542
+ -nextLiveViewport.left - liveFrozenPaneRight,
21543
+ -liveDisplayHeaderHeight
21544
+ );
21545
+ applyOverlayTransform(
21546
+ canvasLeftOverlayContentRef.current,
21547
+ -liveDisplayRowHeaderWidth,
21548
+ -nextLiveViewport.top - liveFrozenPaneBottom
21549
+ );
21550
+ applyOverlayTransform(
21551
+ canvasCornerOverlayContentRef.current,
21552
+ -liveDisplayRowHeaderWidth,
21553
+ -liveDisplayHeaderHeight
21554
+ );
21307
21555
  }, [zoomScale]);
21308
21556
  const updateLiveGestureZoomState = React4.useCallback((nextState) => {
21309
21557
  const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
@@ -21406,6 +21654,11 @@ function XlsxGrid({
21406
21654
  if (matchesLiveViewport && matchesStateViewport) {
21407
21655
  return;
21408
21656
  }
21657
+ const shouldCommitDeferredViewport = !matchesStateViewport && (stateViewport.width !== nextViewport.width || stateViewport.height !== nextViewport.height || Math.abs(nextViewport.left - stateViewport.left) >= CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX || Math.abs(nextViewport.top - stateViewport.top) >= CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX);
21658
+ if (!shouldCommitDeferredViewport) {
21659
+ pendingDrawingViewportRef.current = null;
21660
+ return;
21661
+ }
21409
21662
  pendingDrawingViewportRef.current = nextViewport;
21410
21663
  if (drawingViewportFrameRef.current !== null) {
21411
21664
  return;
@@ -21693,6 +21946,12 @@ function XlsxGrid({
21693
21946
  ) : displayRowHeaderWidth,
21694
21947
  [displayActualColWidths, displayDefaultColWidth, displayRowHeaderWidth, frozenCols, stickyLeftByCol]
21695
21948
  );
21949
+ canvasLayoutMetricsRef.current = {
21950
+ displayHeaderHeight,
21951
+ displayRowHeaderWidth,
21952
+ frozenPaneBottom,
21953
+ frozenPaneRight
21954
+ };
21696
21955
  const rowPrefixSumsRef = React4.useRef(rowPrefixSums);
21697
21956
  const colPrefixSumsRef = React4.useRef(colPrefixSums);
21698
21957
  const firstVisibleRow = visibleRows[0];
@@ -21744,6 +22003,7 @@ function XlsxGrid({
21744
22003
  }, [charts, images, shapes]);
21745
22004
  const shouldVirtualizeRows = visibleRows.length > 0;
21746
22005
  const shouldVirtualizeCols = visibleCols.length > 0 && frozenCols.length === 0;
22006
+ const shouldUseDomVirtualizer = !experimentalCanvas;
21747
22007
  const getScrollElement = React4.useCallback(() => scrollRef.current, []);
21748
22008
  const estimateRowSize = React4.useCallback(
21749
22009
  (index) => displayEffectiveRowHeights[index] ?? displayDefaultRowHeight,
@@ -21757,6 +22017,7 @@ function XlsxGrid({
21757
22017
  const getColItemKey = React4.useCallback((index) => visibleCols[index] ?? index, [visibleCols]);
21758
22018
  const rowVirtualizer = (0, import_react_virtual.useVirtualizer)({
21759
22019
  count: visibleRows.length,
22020
+ enabled: shouldUseDomVirtualizer,
21760
22021
  estimateSize: estimateRowSize,
21761
22022
  getScrollElement,
21762
22023
  getItemKey: getRowItemKey,
@@ -21764,14 +22025,15 @@ function XlsxGrid({
21764
22025
  });
21765
22026
  const colVirtualizer = (0, import_react_virtual.useVirtualizer)({
21766
22027
  count: visibleCols.length,
22028
+ enabled: shouldUseDomVirtualizer,
21767
22029
  estimateSize: estimateColSize,
21768
22030
  getScrollElement,
21769
22031
  getItemKey: getColItemKey,
21770
22032
  horizontal: true,
21771
22033
  overscan: 6
21772
22034
  });
21773
- const currentRowVirtualItems = rowVirtualizer.getVirtualItems();
21774
- const currentColVirtualItems = colVirtualizer.getVirtualItems();
22035
+ const currentRowVirtualItems = shouldUseDomVirtualizer ? rowVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;
22036
+ const currentColVirtualItems = shouldUseDomVirtualizer ? colVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;
21775
22037
  const frozenRowVirtualIndices = React4.useMemo(
21776
22038
  () => frozenRows.map((row) => rowIndexByActual.get(row)).filter((index) => index !== void 0),
21777
22039
  [frozenRows, rowIndexByActual]
@@ -22007,10 +22269,12 @@ function XlsxGrid({
22007
22269
  scroller.scrollTop = scroller.scrollTop / previousZoomFactor * zoomFactor;
22008
22270
  }
22009
22271
  previousZoomFactorRef.current = zoomFactor;
22010
- rowVirtualizer.measure();
22011
- colVirtualizer.measure();
22272
+ if (shouldUseDomVirtualizer) {
22273
+ rowVirtualizer.measure();
22274
+ colVirtualizer.measure();
22275
+ }
22012
22276
  syncDrawingViewport(scroller, { immediate: true });
22013
- }, [syncDrawingViewport, zoomFactor]);
22277
+ }, [shouldUseDomVirtualizer, syncDrawingViewport, zoomFactor]);
22014
22278
  React4.useLayoutEffect(() => {
22015
22279
  syncDrawingViewport(scrollRef.current, { immediate: true });
22016
22280
  }, [activeSheet, activeTabIndex, displayColLimit, displayRowLimit, syncDrawingViewport, zoomFactor]);
@@ -22106,7 +22370,7 @@ function XlsxGrid({
22106
22370
  const initialUsedCol = resolveFirstUsedVisibleIndex(visibleCols, activeSheet?.minUsedCol ?? -1);
22107
22371
  const initialScrollTop = initialUsedRow > 0 ? sumPrefixRange(actualRowPrefixSums, 0, initialUsedRow - 1) : 0;
22108
22372
  const initialScrollLeft = initialUsedCol > 0 ? sumPrefixRange(actualColPrefixSums, 0, initialUsedCol - 1) : 0;
22109
- if (shouldVirtualizeRows) {
22373
+ if (shouldUseDomVirtualizer && shouldVirtualizeRows) {
22110
22374
  rowVirtualizer.scrollToOffset(initialScrollTop);
22111
22375
  } else if (scrollRef.current) {
22112
22376
  scrollRef.current.scrollTop = initialScrollTop;
@@ -22114,7 +22378,7 @@ function XlsxGrid({
22114
22378
  if (scrollRef.current) {
22115
22379
  scrollRef.current.scrollLeft = initialScrollLeft;
22116
22380
  }
22117
- if (shouldVirtualizeCols && frozenCols.length === 0) {
22381
+ if (shouldUseDomVirtualizer && shouldVirtualizeCols && frozenCols.length === 0) {
22118
22382
  colVirtualizer.scrollToOffset(initialScrollLeft);
22119
22383
  }
22120
22384
  if (scrollRef.current) {
@@ -22135,6 +22399,7 @@ function XlsxGrid({
22135
22399
  isWorkerBacked,
22136
22400
  shouldVirtualizeCols,
22137
22401
  shouldVirtualizeRows,
22402
+ shouldUseDomVirtualizer,
22138
22403
  syncDrawingViewport,
22139
22404
  rowVirtualizer,
22140
22405
  visibleCols,
@@ -22154,24 +22419,40 @@ function XlsxGrid({
22154
22419
  setPendingNavigation(null);
22155
22420
  }, [activeSheetIndex, pendingNavigation, selectCell]);
22156
22421
  React4.useEffect(() => {
22157
- if (shouldVirtualizeRows) {
22422
+ if (shouldUseDomVirtualizer && shouldVirtualizeRows) {
22158
22423
  rowVirtualizer.measure();
22159
22424
  }
22160
- if (shouldVirtualizeCols) {
22425
+ if (shouldUseDomVirtualizer && shouldVirtualizeCols) {
22161
22426
  colVirtualizer.measure();
22162
22427
  }
22163
- }, [activeSheetIndex, revision, shouldVirtualizeCols, shouldVirtualizeRows, visibleCols.length, visibleRows.length]);
22428
+ }, [
22429
+ activeSheetIndex,
22430
+ revision,
22431
+ shouldUseDomVirtualizer,
22432
+ shouldVirtualizeCols,
22433
+ shouldVirtualizeRows,
22434
+ visibleCols.length,
22435
+ visibleRows.length
22436
+ ]);
22164
22437
  const handleScrollerScroll = React4.useCallback((event) => {
22165
22438
  const currentScroller = event.currentTarget;
22166
22439
  cachedScrollerRectRef.current = null;
22167
- syncDrawingViewport(currentScroller, { immediate: true });
22440
+ const paintedViewport = paintedDrawingViewportRef.current;
22441
+ 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;
22442
+ syncDrawingViewport(currentScroller, { immediate: shouldSyncDrawingViewportImmediately });
22168
22443
  if (currentScroller.scrollHeight - (currentScroller.scrollTop + currentScroller.clientHeight) < OPEN_GRID_VERTICAL_EDGE_PX) {
22169
- setDisplayRowLimit((current) => current + OPEN_GRID_ROW_GROWTH);
22444
+ setDisplayRowLimit((current) => {
22445
+ const nextLimit = current + OPEN_GRID_ROW_GROWTH;
22446
+ return readOnly && current < maxRowDisplayLimit ? Math.min(maxRowDisplayLimit, nextLimit) : nextLimit;
22447
+ });
22170
22448
  }
22171
22449
  if (currentScroller.scrollWidth - (currentScroller.scrollLeft + currentScroller.clientWidth) < OPEN_GRID_HORIZONTAL_EDGE_PX) {
22172
- setDisplayColLimit((current) => current + OPEN_GRID_COL_GROWTH);
22450
+ setDisplayColLimit((current) => {
22451
+ const nextLimit = current + OPEN_GRID_COL_GROWTH;
22452
+ return readOnly && current < maxColDisplayLimit ? Math.min(maxColDisplayLimit, nextLimit) : nextLimit;
22453
+ });
22173
22454
  }
22174
- }, [syncDrawingViewport]);
22455
+ }, [maxColDisplayLimit, maxRowDisplayLimit, readOnly, syncDrawingViewport]);
22175
22456
  React4.useEffect(() => {
22176
22457
  const scroller = scrollRef.current;
22177
22458
  if (!scroller || !enableGestureZoom) {
@@ -22428,17 +22709,17 @@ function XlsxGrid({
22428
22709
  }, []);
22429
22710
  const resolvePointerCellFromClient = React4.useCallback((clientX, clientY) => {
22430
22711
  const geometryCell = resolvePointerCellFromGeometry(clientX, clientY);
22431
- if (geometryCell && !worksheet?.isMergedSecondary(geometryCell.row, geometryCell.col)) {
22432
- return resolveMergeAnchorCell(geometryCell);
22712
+ if (geometryCell) {
22713
+ return geometryCell;
22433
22714
  }
22434
- const resolvedCell = resolvePointerCellFromHitTest(clientX, clientY) ?? geometryCell;
22715
+ const resolvedCell = resolvePointerCellFromHitTest(clientX, clientY);
22435
22716
  return resolvedCell ? resolveMergeAnchorCell(resolvedCell) : null;
22436
- }, [resolveMergeAnchorCell, resolvePointerCellFromGeometry, resolvePointerCellFromHitTest, worksheet]);
22717
+ }, [resolveMergeAnchorCell, resolvePointerCellFromGeometry, resolvePointerCellFromHitTest]);
22437
22718
  const resolveDraggedSelectionCell = React4.useCallback((dragState, clientX, clientY) => {
22438
22719
  const geometryCell = resolvePointerCellFromGeometry(clientX, clientY);
22439
22720
  const hitCell = resolvePointerCellFromHitTest(clientX, clientY);
22440
- const actualRow = hitCell && rowIndexByActual.has(hitCell.row) ? hitCell.row : geometryCell?.row;
22441
- const actualCol = hitCell?.col ?? geometryCell?.col;
22721
+ const actualRow = geometryCell?.row ?? (hitCell && rowIndexByActual.has(hitCell.row) ? hitCell.row : void 0);
22722
+ const actualCol = geometryCell?.col ?? hitCell?.col;
22442
22723
  if (actualRow === void 0 || actualCol === void 0) {
22443
22724
  return null;
22444
22725
  }
@@ -22448,8 +22729,8 @@ function XlsxGrid({
22448
22729
  if (dragState.axis === "column") {
22449
22730
  return { row: dragState.originCell.row, col: actualCol };
22450
22731
  }
22451
- return resolveMergeAnchorCell({ row: actualRow, col: actualCol });
22452
- }, [resolveMergeAnchorCell, resolvePointerCellFromGeometry, resolvePointerCellFromHitTest, rowIndexByActual]);
22732
+ return { row: actualRow, col: actualCol };
22733
+ }, [resolvePointerCellFromGeometry, resolvePointerCellFromHitTest, rowIndexByActual]);
22453
22734
  const resolveCellPointerOrigin = React4.useCallback((cell, rect, clientX, clientY) => {
22454
22735
  const rowIndex = rowIndexByActual.get(cell.row);
22455
22736
  const colIndex = colIndexByActual.get(cell.col);
@@ -22467,6 +22748,40 @@ function XlsxGrid({
22467
22748
  originContentY: (rowPrefixSums[rowIndex] ?? 0) + clampContentOffset((clientY - rect.top) / contentScaleY, displayHeight)
22468
22749
  };
22469
22750
  }, [colIndexByActual, colPrefixSums, displayDefaultColWidth, displayDefaultRowHeight, displayEffectiveColWidths, displayEffectiveRowHeights, rowIndexByActual, rowPrefixSums]);
22751
+ const resolveCellPointerOriginFromClient = React4.useCallback((cell, clientX, clientY) => {
22752
+ const scroller = scrollRef.current;
22753
+ const rowIndex = rowIndexByActual.get(cell.row);
22754
+ const colIndex = colIndexByActual.get(cell.col);
22755
+ if (!scroller || rowIndex === void 0 || colIndex === void 0) {
22756
+ return null;
22757
+ }
22758
+ const scrollerRect = cachedScrollerRectRef.current ?? scroller.getBoundingClientRect();
22759
+ const pointerOffsetX = clientX - scrollerRect.left;
22760
+ const pointerOffsetY = clientY - scrollerRect.top;
22761
+ const localX = pointerOffsetX + (pointerOffsetX >= frozenPaneRight ? scroller.scrollLeft : 0);
22762
+ const localY = pointerOffsetY + (pointerOffsetY >= frozenPaneBottom ? scroller.scrollTop : 0);
22763
+ const displayWidth = displayEffectiveColWidths[colIndex] ?? displayDefaultColWidth;
22764
+ const displayHeight = displayEffectiveRowHeights[rowIndex] ?? displayDefaultRowHeight;
22765
+ return {
22766
+ contentScaleX: 1,
22767
+ contentScaleY: 1,
22768
+ originContentX: (colPrefixSums[colIndex] ?? 0) + clampContentOffset(localX - displayRowHeaderWidth - (colPrefixSums[colIndex] ?? 0), displayWidth),
22769
+ originContentY: (rowPrefixSums[rowIndex] ?? 0) + clampContentOffset(localY - displayHeaderHeight - (rowPrefixSums[rowIndex] ?? 0), displayHeight)
22770
+ };
22771
+ }, [
22772
+ colIndexByActual,
22773
+ colPrefixSums,
22774
+ displayDefaultColWidth,
22775
+ displayDefaultRowHeight,
22776
+ displayEffectiveColWidths,
22777
+ displayEffectiveRowHeights,
22778
+ displayHeaderHeight,
22779
+ displayRowHeaderWidth,
22780
+ frozenPaneBottom,
22781
+ frozenPaneRight,
22782
+ rowIndexByActual,
22783
+ rowPrefixSums
22784
+ ]);
22470
22785
  const resolveRowPointerOrigin = React4.useCallback((actualRow, rect, clientY) => {
22471
22786
  const rowIndex = rowIndexByActual.get(actualRow);
22472
22787
  if (rowIndex === void 0) {
@@ -23043,14 +23358,14 @@ function XlsxGrid({
23043
23358
  },
23044
23359
  left: {
23045
23360
  cols: frozenColItems,
23046
- rows: frozenRows.length > 0 ? canvasVisibleRowItems : scrollRowItems
23361
+ rows: scrollRowItems
23047
23362
  },
23048
23363
  scroll: {
23049
- cols: frozenCols.length > 0 ? canvasVisibleColItems : scrollColItems,
23050
- rows: frozenRows.length > 0 ? canvasVisibleRowItems : scrollRowItems
23364
+ cols: scrollColItems,
23365
+ rows: scrollRowItems
23051
23366
  },
23052
23367
  top: {
23053
- cols: frozenCols.length > 0 ? canvasVisibleColItems : scrollColItems,
23368
+ cols: scrollColItems,
23054
23369
  rows: frozenRowItems
23055
23370
  }
23056
23371
  };
@@ -23184,6 +23499,51 @@ function XlsxGrid({
23184
23499
  visibleRows
23185
23500
  ]
23186
23501
  );
23502
+ const shouldBakeCanvasStaticDrawings = Boolean(experimentalCanvas && readOnly && showImages);
23503
+ const shouldBakeCanvasImages = Boolean(shouldBakeCanvasStaticDrawings && !renderImage && !renderImageSelection);
23504
+ const bakedShapeRects = React4.useMemo(
23505
+ () => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => !shape.hyperlink) : [],
23506
+ [shapeRects, shouldBakeCanvasStaticDrawings]
23507
+ );
23508
+ const domShapeRects = React4.useMemo(
23509
+ () => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => shape.hyperlink) : shapeRects,
23510
+ [shapeRects, shouldBakeCanvasStaticDrawings]
23511
+ );
23512
+ const bakedFormControlRects = React4.useMemo(
23513
+ () => shouldBakeCanvasStaticDrawings ? formControlRects : [],
23514
+ [formControlRects, shouldBakeCanvasStaticDrawings]
23515
+ );
23516
+ const domFormControlRects = React4.useMemo(
23517
+ () => shouldBakeCanvasStaticDrawings ? [] : formControlRects,
23518
+ [formControlRects, shouldBakeCanvasStaticDrawings]
23519
+ );
23520
+ const bakedImageRects = React4.useMemo(
23521
+ () => shouldBakeCanvasImages ? imageRects.filter(({ image }) => !image.hyperlink && selectedImageId !== image.id) : [],
23522
+ [imageRects, selectedImageId, shouldBakeCanvasImages]
23523
+ );
23524
+ const domImageRects = React4.useMemo(
23525
+ () => shouldBakeCanvasImages ? imageRects.filter(({ image }) => image.hyperlink || selectedImageId === image.id) : imageRects,
23526
+ [imageRects, selectedImageId, shouldBakeCanvasImages]
23527
+ );
23528
+ const hasCanvasDomDrawingOverlays = Boolean(
23529
+ showImages && (chartRects.length > 0 || domShapeRects.length > 0 || domFormControlRects.length > 0 || domImageRects.length > 0)
23530
+ );
23531
+ const bakedCanvasDrawingSignature = React4.useMemo(() => {
23532
+ if (!shouldBakeCanvasStaticDrawings) {
23533
+ return "";
23534
+ }
23535
+ const shapeSignature = bakedShapeRects.map(({ rect, shape }) => `s:${shape.id}:${shape.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
23536
+ const controlSignature = bakedFormControlRects.map(({ control, rect }) => `f:${control.id}:${control.zIndex}:${Boolean(control.checked)}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
23537
+ const imageSignature = bakedImageRects.map(({ image, rect }) => `i:${image.id}:${image.src}:${image.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
23538
+ return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${controlSignature}:${imageSignature}`;
23539
+ }, [
23540
+ bakedFormControlRects,
23541
+ bakedImageRects,
23542
+ bakedShapeRects,
23543
+ canvasImageLoadVersion,
23544
+ revision,
23545
+ shouldBakeCanvasStaticDrawings
23546
+ ]);
23187
23547
  const resolveMountedCellOverlayRect = React4.useCallback((element) => {
23188
23548
  const wrapper = wrapperRef.current;
23189
23549
  if (!wrapper) {
@@ -23215,10 +23575,11 @@ function XlsxGrid({
23215
23575
  }, [resolveMountedCellOverlayRect]);
23216
23576
  const resolveGeometryOverlayRect = React4.useCallback((range) => {
23217
23577
  const normalized = normalizeRange2(range);
23218
- const startCell = resolveMergeAnchorCell(normalized.start);
23219
23578
  const isSingleCellSelection = normalized.start.row === normalized.end.row && normalized.start.col === normalized.end.col;
23220
- const merge = isSingleCellSelection ? worksheet?.getMergeSpan(startCell.row, startCell.col) : null;
23221
- const endCell = isSingleCellSelection ? {
23579
+ const isMergedSecondarySelection = isSingleCellSelection ? worksheet?.isMergedSecondary(normalized.start.row, normalized.start.col) === true : false;
23580
+ const startCell = isSingleCellSelection && !isMergedSecondarySelection ? resolveMergeAnchorCell(normalized.start) : normalized.start;
23581
+ const merge = isSingleCellSelection && !isMergedSecondarySelection ? worksheet?.getMergeSpan(startCell.row, startCell.col) : null;
23582
+ const endCell = isSingleCellSelection && !isMergedSecondarySelection ? {
23222
23583
  row: startCell.row + Math.max(1, merge?.rowSpan ?? 1) - 1,
23223
23584
  col: startCell.col + Math.max(1, merge?.colSpan ?? 1) - 1
23224
23585
  } : normalized.end;
@@ -23850,13 +24211,14 @@ function XlsxGrid({
23850
24211
  }
23851
24212
  event.preventDefault();
23852
24213
  focusGrid();
24214
+ const targetCell = event.currentTarget.colSpan > 1 || event.currentTarget.rowSpan > 1 ? resolvePointerCellFromGeometry(event.clientX, event.clientY) ?? cell : cell;
23853
24215
  const currentSelection = selectionRef.current;
23854
- const anchor = event.shiftKey && currentSelection ? currentSelection.start : cell;
23855
- const initialRange = normalizeRange2({ start: anchor, end: cell });
23856
- const isActive = isSameCell(activeCellRef.current, cell);
24216
+ const anchor = event.shiftKey && currentSelection ? currentSelection.start : targetCell;
24217
+ const initialRange = normalizeRange2({ start: anchor, end: targetCell });
24218
+ const isActive = isSameCell(activeCellRef.current, targetCell);
23857
24219
  const committedOnPointerDown = !isActive || !editingCellRef.current;
23858
- const pointerOrigin = resolveCellPointerOrigin(cell, event.currentTarget.getBoundingClientRect(), event.clientX, event.clientY);
23859
- const originOverlayRect = resolveMountedCellOverlayRect(event.currentTarget);
24220
+ const pointerOrigin = targetCell.row === cell.row && targetCell.col === cell.col ? resolveCellPointerOrigin(cell, event.currentTarget.getBoundingClientRect(), event.clientX, event.clientY) : resolveCellPointerOriginFromClient(targetCell, event.clientX, event.clientY);
24221
+ const originOverlayRect = targetCell.row === cell.row && targetCell.col === cell.col ? resolveMountedCellOverlayRect(event.currentTarget) : resolveOverlayRect(initialRange);
23860
24222
  if (!pointerOrigin) {
23861
24223
  return;
23862
24224
  }
@@ -23864,7 +24226,7 @@ function XlsxGrid({
23864
24226
  event.pointerId,
23865
24227
  anchor,
23866
24228
  "cell",
23867
- cell,
24229
+ targetCell,
23868
24230
  pointerOrigin,
23869
24231
  originOverlayRect,
23870
24232
  committedOnPointerDown,
@@ -23872,11 +24234,25 @@ function XlsxGrid({
23872
24234
  event.clientX,
23873
24235
  event.clientY
23874
24236
  );
23875
- applyPreviewOverlayFromElement(event.currentTarget, initialRange);
24237
+ if (targetCell.row === cell.row && targetCell.col === cell.col) {
24238
+ applyPreviewOverlayFromElement(event.currentTarget, initialRange);
24239
+ } else {
24240
+ applyPreviewOverlay(initialRange);
24241
+ }
23876
24242
  if (committedOnPointerDown) {
23877
24243
  commitSelectionRange(initialRange);
23878
24244
  }
23879
- }, [applyPreviewOverlayFromElement, commitSelectionRange, focusGrid, resolveCellPointerOrigin, resolveMountedCellOverlayRect]);
24245
+ }, [
24246
+ applyPreviewOverlay,
24247
+ applyPreviewOverlayFromElement,
24248
+ commitSelectionRange,
24249
+ focusGrid,
24250
+ resolveCellPointerOrigin,
24251
+ resolveCellPointerOriginFromClient,
24252
+ resolveMountedCellOverlayRect,
24253
+ resolveOverlayRect,
24254
+ resolvePointerCellFromGeometry
24255
+ ]);
23880
24256
  const handleRowPointerDown = React4.useCallback((event, actualRow) => {
23881
24257
  if (event.button !== 0 || firstVisibleCol === void 0 || lastVisibleCol === void 0) {
23882
24258
  return;
@@ -24036,6 +24412,24 @@ function XlsxGrid({
24036
24412
  rowPrefixSums,
24037
24413
  stickyTopByRow
24038
24414
  ]);
24415
+ const scrollBodyViewportWidth = Math.max(0, drawingViewport.width - frozenPaneRight);
24416
+ const scrollBodyViewportHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);
24417
+ const canvasScrollBufferX = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportWidth);
24418
+ const canvasScrollBufferY = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportHeight);
24419
+ const scrollBodyCanvasLeft = frozenPaneRight - canvasScrollBufferX;
24420
+ const scrollBodyCanvasTop = frozenPaneBottom - canvasScrollBufferY;
24421
+ const scrollBodyCanvasWidth = scrollBodyViewportWidth + canvasScrollBufferX * 2;
24422
+ const scrollBodyCanvasHeight = scrollBodyViewportHeight + canvasScrollBufferY * 2;
24423
+ const topBodyCanvasWidth = scrollBodyCanvasWidth;
24424
+ const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);
24425
+ const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
24426
+ const leftBodyCanvasHeight = scrollBodyCanvasHeight;
24427
+ const cornerBodyCanvasWidth = leftBodyCanvasWidth;
24428
+ const cornerBodyCanvasHeight = topBodyCanvasHeight;
24429
+ const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
24430
+ const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
24431
+ const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
24432
+ const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
24039
24433
  const canvasColumnHeaderCells = React4.useMemo(
24040
24434
  () => canvasVisibleColItems.flatMap((column) => {
24041
24435
  const rect = resolveCanvasColumnHeaderRect(column.actualCol);
@@ -24048,7 +24442,7 @@ function XlsxGrid({
24048
24442
  height: displayHeaderHeight,
24049
24443
  isFrozen,
24050
24444
  left: rect.left,
24051
- localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : frozenPaneRight),
24445
+ localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : scrollBodyCanvasLeft),
24052
24446
  width: rect.width
24053
24447
  }];
24054
24448
  }),
@@ -24056,8 +24450,8 @@ function XlsxGrid({
24056
24450
  canvasVisibleColItems,
24057
24451
  displayHeaderHeight,
24058
24452
  displayRowHeaderWidth,
24059
- frozenPaneRight,
24060
24453
  resolveCanvasColumnHeaderRect,
24454
+ scrollBodyCanvasLeft,
24061
24455
  stickyLeftByCol
24062
24456
  ]
24063
24457
  );
@@ -24072,15 +24466,15 @@ function XlsxGrid({
24072
24466
  actualRow: row.actualRow,
24073
24467
  height: rect.height,
24074
24468
  isFrozen,
24075
- localTop: rect.top - (isFrozen ? displayHeaderHeight : frozenPaneBottom),
24469
+ localTop: rect.top - (isFrozen ? displayHeaderHeight : scrollBodyCanvasTop),
24076
24470
  top: rect.top
24077
24471
  }];
24078
24472
  }),
24079
24473
  [
24080
24474
  canvasVisibleRowItems,
24081
24475
  displayHeaderHeight,
24082
- frozenPaneBottom,
24083
24476
  resolveCanvasRowHeaderRect,
24477
+ scrollBodyCanvasTop,
24084
24478
  stickyTopByRow
24085
24479
  ]
24086
24480
  );
@@ -24347,10 +24741,46 @@ function XlsxGrid({
24347
24741
  rowPrefixSums,
24348
24742
  startCellSelection
24349
24743
  ]);
24744
+ const getCanvasImage = React4.useCallback((image) => {
24745
+ if (typeof Image === "undefined") {
24746
+ return null;
24747
+ }
24748
+ const cacheKey = image.id;
24749
+ const cached = canvasImageCacheRef.current.get(cacheKey);
24750
+ if (cached && cached.src === image.src) {
24751
+ return cached.loaded && !cached.failed ? cached.image : null;
24752
+ }
24753
+ const imageElement = new Image();
24754
+ const entry = {
24755
+ failed: false,
24756
+ image: imageElement,
24757
+ loaded: false,
24758
+ src: image.src
24759
+ };
24760
+ canvasImageCacheRef.current.set(cacheKey, entry);
24761
+ imageElement.onload = () => {
24762
+ entry.loaded = true;
24763
+ setCanvasImageLoadVersion((version) => version + 1);
24764
+ };
24765
+ imageElement.onerror = () => {
24766
+ entry.failed = true;
24767
+ setCanvasImageLoadVersion((version) => version + 1);
24768
+ };
24769
+ imageElement.src = image.src;
24770
+ return null;
24771
+ }, []);
24350
24772
  React4.useLayoutEffect(() => {
24351
24773
  if (!experimentalCanvas) {
24352
24774
  return;
24353
24775
  }
24776
+ const canvasProfileTarget = typeof window !== "undefined" ? window.__xlsxCanvasProfile : void 0;
24777
+ const canvasProfileStart = canvasProfileTarget ? performance.now() : 0;
24778
+ let canvasProfileBodyStart = 0;
24779
+ let canvasProfileBodyMs = 0;
24780
+ let canvasProfileCulledCells = 0;
24781
+ let canvasProfileDirtyRects = 0;
24782
+ let canvasProfileLookedUpCells = 0;
24783
+ let canvasProfilePaintedCells = 0;
24354
24784
  const dpr = typeof window === "undefined" ? 1 : Math.max(1, window.devicePixelRatio || 1);
24355
24785
  function configureCanvas(canvas, width, height, options) {
24356
24786
  if (!canvas) {
@@ -24370,7 +24800,7 @@ function XlsxGrid({
24370
24800
  if (canvas.style.height !== `${height}px`) {
24371
24801
  canvas.style.height = `${height}px`;
24372
24802
  }
24373
- const context = canvas.getContext("2d");
24803
+ const context = canvas.getContext("2d", { alpha: false });
24374
24804
  if (!context) {
24375
24805
  return null;
24376
24806
  }
@@ -24389,6 +24819,7 @@ function XlsxGrid({
24389
24819
  const rangeSignature = buildRangeSignature(normalizedVisibleRange);
24390
24820
  const nextBodyCanvasSignature = {
24391
24821
  activeSheet: activeSheet ?? null,
24822
+ bakedDrawingSignature: bakedCanvasDrawingSignature,
24392
24823
  bodyHeight,
24393
24824
  bodyWidth,
24394
24825
  colSignature: buildRenderedAxisSignature(canvasVisibleColItems, (item) => item.index),
@@ -24416,10 +24847,10 @@ function XlsxGrid({
24416
24847
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
24417
24848
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
24418
24849
  const previousPaintedViewport = paintedDrawingViewportRef.current;
24419
- const shouldRepaintBody = !(previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && 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);
24850
+ 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);
24420
24851
  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);
24421
24852
  const canBlitBody = Boolean(
24422
- shouldRepaintBody && previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && 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
24853
+ 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
24423
24854
  );
24424
24855
  const canBlitTopHeader = Boolean(
24425
24856
  shouldRepaintHeaders && previousHeaderCanvasSignature && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor
@@ -24430,49 +24861,37 @@ function XlsxGrid({
24430
24861
  if (!shouldRepaintBody && !shouldRepaintHeaders) {
24431
24862
  return;
24432
24863
  }
24433
- const scrollBodyCanvasWidth2 = Math.max(0, drawingViewport.width - frozenPaneRight);
24434
- const scrollBodyCanvasHeight2 = Math.max(0, drawingViewport.height - frozenPaneBottom);
24435
- const topBodyCanvasWidth2 = scrollBodyCanvasWidth2;
24436
- const topBodyCanvasHeight2 = Math.max(0, frozenPaneBottom - displayHeaderHeight);
24437
- const leftBodyCanvasWidth2 = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
24438
- const leftBodyCanvasHeight2 = scrollBodyCanvasHeight2;
24439
- const cornerBodyCanvasWidth2 = leftBodyCanvasWidth2;
24440
- const cornerBodyCanvasHeight2 = topBodyCanvasHeight2;
24441
- const topFrozenHeaderCanvasWidth2 = leftBodyCanvasWidth2;
24442
- const topScrollHeaderCanvasWidth2 = scrollBodyCanvasWidth2;
24443
- const leftFrozenHeaderCanvasHeight2 = topBodyCanvasHeight2;
24444
- const leftScrollHeaderCanvasHeight2 = scrollBodyCanvasHeight2;
24445
24864
  const paneBounds = {
24446
24865
  corner: {
24447
- height: cornerBodyCanvasHeight2,
24866
+ height: cornerBodyCanvasHeight,
24448
24867
  left: displayRowHeaderWidth,
24449
24868
  top: displayHeaderHeight,
24450
- width: cornerBodyCanvasWidth2
24869
+ width: cornerBodyCanvasWidth
24451
24870
  },
24452
24871
  left: {
24453
- height: leftBodyCanvasHeight2,
24872
+ height: leftBodyCanvasHeight,
24454
24873
  left: displayRowHeaderWidth,
24455
- top: frozenPaneBottom,
24456
- width: leftBodyCanvasWidth2
24874
+ top: scrollBodyCanvasTop,
24875
+ width: leftBodyCanvasWidth
24457
24876
  },
24458
24877
  scroll: {
24459
- height: scrollBodyCanvasHeight2,
24460
- left: frozenPaneRight,
24461
- top: frozenPaneBottom,
24462
- width: scrollBodyCanvasWidth2
24878
+ height: scrollBodyCanvasHeight,
24879
+ left: scrollBodyCanvasLeft,
24880
+ top: scrollBodyCanvasTop,
24881
+ width: scrollBodyCanvasWidth
24463
24882
  },
24464
24883
  top: {
24465
- height: topBodyCanvasHeight2,
24466
- left: frozenPaneRight,
24884
+ height: topBodyCanvasHeight,
24885
+ left: scrollBodyCanvasLeft,
24467
24886
  top: displayHeaderHeight,
24468
- width: topBodyCanvasWidth2
24887
+ width: topBodyCanvasWidth
24469
24888
  }
24470
24889
  };
24471
24890
  const bodyContexts = shouldRepaintBody ? {
24472
- corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth2, cornerBodyCanvasHeight2, { clear: !canBlitBody }),
24473
- left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth2, leftBodyCanvasHeight2, { clear: !canBlitBody }),
24474
- scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth2, scrollBodyCanvasHeight2, { clear: !canBlitBody }),
24475
- top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth2, topBodyCanvasHeight2, { clear: !canBlitBody })
24891
+ corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth, cornerBodyCanvasHeight, { clear: !canBlitBody }),
24892
+ left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth, leftBodyCanvasHeight, { clear: !canBlitBody }),
24893
+ scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth, scrollBodyCanvasHeight, { clear: !canBlitBody }),
24894
+ top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth, topBodyCanvasHeight, { clear: !canBlitBody })
24476
24895
  } : {
24477
24896
  corner: null,
24478
24897
  left: null,
@@ -24480,21 +24899,21 @@ function XlsxGrid({
24480
24899
  top: null
24481
24900
  };
24482
24901
  const topHeaderContexts = shouldRepaintHeaders ? {
24483
- frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth2, headerHeight),
24484
- scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth2, headerHeight, { clear: !canBlitTopHeader })
24902
+ frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth, headerHeight),
24903
+ scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth, headerHeight, { clear: !canBlitTopHeader })
24485
24904
  } : {
24486
24905
  frozen: null,
24487
24906
  scroll: null
24488
24907
  };
24489
24908
  const leftHeaderContexts = shouldRepaintHeaders ? {
24490
- frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight2),
24491
- scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight2, { clear: !canBlitLeftHeader })
24909
+ frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight),
24910
+ scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight, { clear: !canBlitLeftHeader })
24492
24911
  } : {
24493
24912
  frozen: null,
24494
24913
  scroll: null
24495
24914
  };
24496
24915
  const cornerContext = shouldRepaintHeaders ? configureCanvas(cornerHeaderCanvasRef.current, rowHeaderWidth, headerHeight) : null;
24497
- if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!cornerContext || topFrozenHeaderCanvasWidth2 > 0 && !topHeaderContexts.frozen || topScrollHeaderCanvasWidth2 > 0 && !topHeaderContexts.scroll || leftFrozenHeaderCanvasHeight2 > 0 && !leftHeaderContexts.frozen || leftScrollHeaderCanvasHeight2 > 0 && !leftHeaderContexts.scroll)) {
24916
+ if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!cornerContext || topFrozenHeaderCanvasWidth > 0 && !topHeaderContexts.frozen || topScrollHeaderCanvasWidth > 0 && !topHeaderContexts.scroll || leftFrozenHeaderCanvasHeight > 0 && !leftHeaderContexts.frozen || leftScrollHeaderCanvasHeight > 0 && !leftHeaderContexts.scroll)) {
24498
24917
  return;
24499
24918
  }
24500
24919
  const showGridLines = activeSheet?.showGridLines ?? true;
@@ -24511,10 +24930,282 @@ function XlsxGrid({
24511
24930
  scroll: [],
24512
24931
  top: []
24513
24932
  };
24514
- let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth2, headerHeight);
24515
- let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight2);
24933
+ let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth, headerHeight);
24934
+ let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight);
24935
+ const bakedCanvasDrawingEntries = shouldBakeCanvasStaticDrawings ? [
24936
+ ...bakedShapeRects.map(({ rect, shape }) => ({
24937
+ kind: "shape",
24938
+ rect,
24939
+ shape,
24940
+ zIndex: shape.zIndex
24941
+ })),
24942
+ ...bakedFormControlRects.map(({ control, rect }) => ({
24943
+ control,
24944
+ kind: "formControl",
24945
+ rect,
24946
+ zIndex: control.zIndex
24947
+ })),
24948
+ ...bakedImageRects.map(({ image, rect }) => ({
24949
+ image,
24950
+ kind: "image",
24951
+ rect,
24952
+ zIndex: image.zIndex
24953
+ }))
24954
+ ].sort((left, right) => left.zIndex - right.zIndex) : [];
24955
+ const resolveBakedCanvasLocalRect = (rect, pane) => {
24956
+ const bounds = paneBounds[pane];
24957
+ const scrollX = pane === "scroll" || pane === "top" ? drawingViewport.left : 0;
24958
+ const scrollY = pane === "scroll" || pane === "left" ? drawingViewport.top : 0;
24959
+ return {
24960
+ height: rect.height,
24961
+ left: rect.left - scrollX - bounds.left,
24962
+ top: rect.top - scrollY - bounds.top,
24963
+ width: rect.width
24964
+ };
24965
+ };
24966
+ const drawBakedShapeText = (context, shape, left, top, width, height) => {
24967
+ if (shape.paragraphs.length === 0) {
24968
+ return;
24969
+ }
24970
+ const inset = shape.textBox?.insetPx;
24971
+ const paddingLeft = (inset?.left ?? 6) * zoomFactor;
24972
+ const paddingRight = (inset?.right ?? 6) * zoomFactor;
24973
+ const paddingTop = (inset?.top ?? 4) * zoomFactor;
24974
+ const paddingBottom = (inset?.bottom ?? 4) * zoomFactor;
24975
+ const textLeft = left + paddingLeft;
24976
+ const textTop = top + paddingTop;
24977
+ const textWidth = Math.max(0, width - paddingLeft - paddingRight);
24978
+ const textHeight = Math.max(0, height - paddingTop - paddingBottom);
24979
+ if (textWidth <= 0 || textHeight <= 0) {
24980
+ return;
24981
+ }
24982
+ const lineMetrics = shape.paragraphs.map((paragraph) => {
24983
+ const lineHeight = Math.max(
24984
+ 12 * zoomFactor,
24985
+ ...paragraph.runs.map((run) => (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor * 1.2)
24986
+ );
24987
+ const widthPx = paragraph.runs.reduce((total, run) => {
24988
+ const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
24989
+ context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
24990
+ return total + measureCanvasTextWidth(context, run.text);
24991
+ }, 0);
24992
+ return { lineHeight, widthPx };
24993
+ });
24994
+ const totalTextHeight = lineMetrics.reduce((total, metric) => total + metric.lineHeight, 0);
24995
+ let y = textTop;
24996
+ if (shape.textBox?.verticalAlign === "middle") {
24997
+ y = textTop + Math.max(0, (textHeight - totalTextHeight) / 2);
24998
+ } else if (shape.textBox?.verticalAlign === "bottom") {
24999
+ y = textTop + Math.max(0, textHeight - totalTextHeight);
25000
+ }
25001
+ context.save();
25002
+ context.beginPath();
25003
+ context.rect(textLeft, textTop, textWidth, textHeight);
25004
+ context.clip();
25005
+ context.textBaseline = "middle";
25006
+ shape.paragraphs.forEach((paragraph, paragraphIndex) => {
25007
+ const metric = lineMetrics[paragraphIndex] ?? { lineHeight: 14 * zoomFactor, widthPx: 0 };
25008
+ let x = textLeft;
25009
+ const align = paragraph.align ?? shape.textBox?.horizontalAlign ?? "left";
25010
+ if (align === "center") {
25011
+ x = textLeft + Math.max(0, (textWidth - metric.widthPx) / 2);
25012
+ } else if (align === "right") {
25013
+ x = textLeft + Math.max(0, textWidth - metric.widthPx);
25014
+ }
25015
+ paragraph.runs.forEach((run) => {
25016
+ const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
25017
+ context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
25018
+ context.fillStyle = run.color ?? "#000000";
25019
+ context.textAlign = "left";
25020
+ const textY = y + metric.lineHeight / 2;
25021
+ context.fillText(run.text, x, textY);
25022
+ if (run.underline && run.text.length > 0) {
25023
+ const textWidthPx = measureCanvasTextWidth(context, run.text);
25024
+ context.strokeStyle = run.color ?? "#000000";
25025
+ context.lineWidth = Math.max(1, zoomFactor * 0.75);
25026
+ context.beginPath();
25027
+ context.moveTo(x, textY + Math.max(2, fontSize * 0.28));
25028
+ context.lineTo(x + textWidthPx, textY + Math.max(2, fontSize * 0.28));
25029
+ context.stroke();
25030
+ }
25031
+ x += measureCanvasTextWidth(context, run.text);
25032
+ });
25033
+ y += metric.lineHeight;
25034
+ });
25035
+ context.restore();
25036
+ };
25037
+ const drawBakedShape = (context, shape, rect) => {
25038
+ const fillColor = shape.fill?.none ? "transparent" : shape.fill?.color ?? "transparent";
25039
+ const strokeColor = shape.stroke?.none ? "transparent" : shape.stroke?.color ?? "transparent";
25040
+ const lineWidth = Math.max(0, (shape.stroke?.widthPx ?? (shape.geometry === "line" ? 2 : 1)) * zoomFactor);
25041
+ const vectorShape = resolveShapeVector(shape);
25042
+ const opacity = Math.min(shape.fill?.opacity ?? 1, shape.stroke?.opacity ?? 1);
25043
+ context.save();
25044
+ context.translate(rect.left + rect.width / 2, rect.top + rect.height / 2);
25045
+ if (shape.rotationDeg) {
25046
+ context.rotate(shape.rotationDeg * Math.PI / 180);
25047
+ }
25048
+ context.scale(shape.flipH ? -1 : 1, shape.flipV ? -1 : 1);
25049
+ context.globalAlpha *= opacity;
25050
+ context.lineWidth = lineWidth;
25051
+ context.strokeStyle = strokeColor;
25052
+ context.fillStyle = fillColor;
25053
+ applyCanvasShapeDash(context, shape.stroke?.dash, lineWidth);
25054
+ const localLeft = -rect.width / 2;
25055
+ const localTop = -rect.height / 2;
25056
+ if (vectorShape && typeof Path2D !== "undefined") {
25057
+ context.save();
25058
+ context.translate(localLeft, localTop);
25059
+ context.scale(
25060
+ rect.width / Math.max(1, vectorShape.viewBox.width),
25061
+ rect.height / Math.max(1, vectorShape.viewBox.height)
25062
+ );
25063
+ const path = getCachedCanvasPath2D(vectorShape.path);
25064
+ if (!path) {
25065
+ context.restore();
25066
+ context.restore();
25067
+ return;
25068
+ }
25069
+ if (fillColor !== "transparent") {
25070
+ context.fill(path);
25071
+ }
25072
+ if (strokeColor !== "transparent" && lineWidth > 0) {
25073
+ context.stroke(path);
25074
+ }
25075
+ context.restore();
25076
+ } else if (shape.geometry === "ellipse") {
25077
+ context.beginPath();
25078
+ context.ellipse(0, 0, rect.width / 2, rect.height / 2, 0, 0, Math.PI * 2);
25079
+ if (fillColor !== "transparent") {
25080
+ context.fill();
25081
+ }
25082
+ if (strokeColor !== "transparent" && lineWidth > 0) {
25083
+ context.stroke();
25084
+ }
25085
+ } else {
25086
+ const radius = shape.geometry === "roundRect" ? 12 * zoomFactor : 0;
25087
+ drawCanvasRoundedRect(context, localLeft, localTop, rect.width, rect.height, radius);
25088
+ if (fillColor !== "transparent") {
25089
+ context.fill();
25090
+ }
25091
+ if (strokeColor !== "transparent" && lineWidth > 0) {
25092
+ context.stroke();
25093
+ }
25094
+ }
25095
+ drawBakedShapeText(context, shape, localLeft, localTop, rect.width, rect.height);
25096
+ context.restore();
25097
+ };
25098
+ const drawBakedFormControl = (context, control, rect) => {
25099
+ const label = resolveFormControlLabel(control);
25100
+ const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
25101
+ const textColor = control.textColor ?? "#000000";
25102
+ const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
25103
+ const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
25104
+ context.save();
25105
+ context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
25106
+ context.textBaseline = "middle";
25107
+ context.fillStyle = textColor;
25108
+ context.strokeStyle = stroke;
25109
+ context.lineWidth = Math.max(1, zoomFactor);
25110
+ if (control.kind === "group-box") {
25111
+ const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;
25112
+ drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);
25113
+ context.stroke();
25114
+ if (label) {
25115
+ context.fillStyle = SHEET_SURFACE;
25116
+ const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);
25117
+ context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);
25118
+ context.fillStyle = textColor;
25119
+ context.textAlign = "left";
25120
+ context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);
25121
+ }
25122
+ context.restore();
25123
+ return;
25124
+ }
25125
+ if (control.kind === "button" || control.kind === "dropdown" || control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
25126
+ if (control.kind === "button") {
25127
+ const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);
25128
+ gradient.addColorStop(0, "#f8fafc");
25129
+ gradient.addColorStop(1, "#e2e8f0");
25130
+ context.fillStyle = gradient;
25131
+ } else {
25132
+ context.fillStyle = "transparent";
25133
+ }
25134
+ drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === "button" ? 4 * zoomFactor : 2 * zoomFactor);
25135
+ if (control.kind === "button") {
25136
+ context.fill();
25137
+ }
25138
+ context.stroke();
25139
+ }
25140
+ let textLeft = rect.left + 2 * zoomFactor;
25141
+ const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
25142
+ if (control.kind === "checkbox") {
25143
+ context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
25144
+ if (control.checked) {
25145
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
25146
+ context.fillRect(rect.left + 2 * zoomFactor + 1.5, rect.top + (rect.height - iconSize) / 2 + 1.5, Math.max(0, iconSize - 3), Math.max(0, iconSize - 3));
25147
+ context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
25148
+ context.beginPath();
25149
+ context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
25150
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
25151
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
25152
+ context.stroke();
25153
+ }
25154
+ textLeft += iconSize + 4 * zoomFactor;
25155
+ } else if (control.kind === "radio") {
25156
+ context.beginPath();
25157
+ context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
25158
+ context.stroke();
25159
+ if (control.checked) {
25160
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
25161
+ context.beginPath();
25162
+ context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
25163
+ context.fill();
25164
+ }
25165
+ textLeft += iconSize + 4 * zoomFactor;
25166
+ }
25167
+ if (label) {
25168
+ const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);
25169
+ const text = truncateCanvasText(context, label, maxTextWidth);
25170
+ context.fillStyle = textColor;
25171
+ context.textAlign = control.textAlign === "right" ? "right" : control.textAlign === "center" ? "center" : "left";
25172
+ const textX = context.textAlign === "right" ? rect.left + rect.width - textRightInset : context.textAlign === "center" ? textLeft + maxTextWidth / 2 : textLeft;
25173
+ context.fillText(text, textX, rect.top + rect.height / 2);
25174
+ }
25175
+ if (control.kind === "dropdown") {
25176
+ context.fillStyle = textColor;
25177
+ context.textAlign = "center";
25178
+ context.fillText("\u25BC", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);
25179
+ }
25180
+ context.restore();
25181
+ };
25182
+ const drawBakedCanvasDrawings = (pane, context, dirtyRects) => {
25183
+ if (bakedCanvasDrawingEntries.length === 0 || dirtyRects.length === 0) {
25184
+ return;
25185
+ }
25186
+ for (const entry of bakedCanvasDrawingEntries) {
25187
+ if (resolveDrawingPane(entry.rect) !== pane) {
25188
+ continue;
25189
+ }
25190
+ const localRect = resolveBakedCanvasLocalRect(entry.rect, pane);
25191
+ if (localRect.left + localRect.width < 0 || localRect.top + localRect.height < 0 || localRect.left > paneBounds[pane].width || localRect.top > paneBounds[pane].height || !intersectsCanvasDirtyRects(localRect.left, localRect.top, localRect.width, localRect.height, dirtyRects)) {
25192
+ continue;
25193
+ }
25194
+ if (entry.kind === "shape") {
25195
+ drawBakedShape(context, entry.shape, localRect);
25196
+ } else if (entry.kind === "formControl") {
25197
+ drawBakedFormControl(context, entry.control, localRect);
25198
+ } else {
25199
+ const imageElement = getCanvasImage(entry.image);
25200
+ if (imageElement) {
25201
+ context.drawImage(imageElement, localRect.left, localRect.top, localRect.width, localRect.height);
25202
+ }
25203
+ }
25204
+ }
25205
+ };
24516
25206
  const cellPaneOrder = ["scroll", "top", "left", "corner"];
24517
25207
  if (shouldRepaintBody) {
25208
+ canvasProfileBodyStart = canvasProfileTarget ? performance.now() : 0;
24518
25209
  for (const pane of Object.keys(bodyContexts)) {
24519
25210
  const context = bodyContexts[pane];
24520
25211
  const bounds = paneBounds[pane];
@@ -24545,6 +25236,7 @@ function XlsxGrid({
24545
25236
  context.clearRect(0, 0, bounds.width, bounds.height);
24546
25237
  }
24547
25238
  bodyDirtyRectsByPane[pane] = dirtyRects;
25239
+ canvasProfileDirtyRects += dirtyRects.length;
24548
25240
  if (dirtyRects.length === 0) {
24549
25241
  continue;
24550
25242
  }
@@ -24561,6 +25253,22 @@ function XlsxGrid({
24561
25253
  if (!paneContext || paneDirtyRects.length === 0 || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
24562
25254
  continue;
24563
25255
  }
25256
+ let hasPendingGridlinePath = false;
25257
+ const enqueueGridlinePath = () => {
25258
+ if (!hasPendingGridlinePath) {
25259
+ paneContext.beginPath();
25260
+ hasPendingGridlinePath = true;
25261
+ }
25262
+ };
25263
+ const flushPendingGridlines = () => {
25264
+ if (!hasPendingGridlinePath) {
25265
+ return;
25266
+ }
25267
+ paneContext.strokeStyle = palette.border;
25268
+ paneContext.lineWidth = 1;
25269
+ paneContext.stroke();
25270
+ hasPendingGridlinePath = false;
25271
+ };
24564
25272
  const drawnMergedAnchorKeys = /* @__PURE__ */ new Set();
24565
25273
  for (const rowItem of paneAxisItems.rows) {
24566
25274
  for (const colItem of paneAxisItems.cols) {
@@ -24568,6 +25276,33 @@ function XlsxGrid({
24568
25276
  const anchorCell = resolveMergeAnchorCell(cell);
24569
25277
  const anchorKey = `${anchorCell.row}:${anchorCell.col}`;
24570
25278
  let drawCell = anchorCell;
25279
+ const drawRowIndex = rowIndexByActual.get(drawCell.row);
25280
+ const drawColIndex = colIndexByActual.get(drawCell.col);
25281
+ if (drawRowIndex === void 0 || drawColIndex === void 0) {
25282
+ continue;
25283
+ }
25284
+ const baseCellLeft = displayRowHeaderWidth + (colPrefixSums[drawColIndex] ?? 0);
25285
+ const baseCellTop = displayHeaderHeight + (rowPrefixSums[drawRowIndex] ?? 0);
25286
+ const useFrozenHorizontalPosition = pane === "left" || pane === "corner";
25287
+ const useFrozenVerticalPosition = pane === "top" || pane === "corner";
25288
+ const roughLocalRect = {
25289
+ height: displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size,
25290
+ left: (useFrozenHorizontalPosition ? stickyLeftByCol.get(drawCell.col) ?? baseCellLeft - drawingViewport.left : baseCellLeft - drawingViewport.left) - paneBoundsForCell.left,
25291
+ top: (useFrozenVerticalPosition ? stickyTopByRow.get(drawCell.row) ?? baseCellTop - drawingViewport.top : baseCellTop - drawingViewport.top) - paneBoundsForCell.top,
25292
+ width: displayEffectiveColWidths[drawColIndex] ?? colItem.size
25293
+ };
25294
+ const isMergedSecondaryProbe = anchorCell.row !== cell.row || anchorCell.col !== cell.col;
25295
+ if (!isMergedSecondaryProbe && !intersectsCanvasDirtyRects(
25296
+ roughLocalRect.left - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,
25297
+ roughLocalRect.top - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,
25298
+ roughLocalRect.width + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,
25299
+ roughLocalRect.height + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,
25300
+ paneDirtyRects
25301
+ )) {
25302
+ canvasProfileCulledCells += 1;
25303
+ continue;
25304
+ }
25305
+ canvasProfileLookedUpCells += 1;
24571
25306
  let cellData = getCellData(drawCell.row, drawCell.col);
24572
25307
  if ((cellData.colSpan || cellData.rowSpan) && drawnMergedAnchorKeys.has(anchorKey)) {
24573
25308
  continue;
@@ -24578,16 +25313,9 @@ function XlsxGrid({
24578
25313
  if (cellData.colSpan || cellData.rowSpan) {
24579
25314
  drawnMergedAnchorKeys.add(anchorKey);
24580
25315
  }
24581
- const drawRowIndex = rowIndexByActual.get(drawCell.row);
24582
- const drawColIndex = colIndexByActual.get(drawCell.col);
24583
- if (drawRowIndex === void 0 || drawColIndex === void 0) {
24584
- continue;
24585
- }
24586
25316
  const displayRect = cellData.colSpan || cellData.rowSpan ? resolveCellDisplayRect(drawCell) : null;
24587
- const baseLeft = displayRect?.left ?? displayRowHeaderWidth + (colPrefixSums[drawColIndex] ?? 0);
24588
- const baseTop = displayRect?.top ?? displayHeaderHeight + (rowPrefixSums[drawRowIndex] ?? 0);
24589
- const useFrozenHorizontalPosition = pane === "left" || pane === "corner";
24590
- const useFrozenVerticalPosition = pane === "top" || pane === "corner";
25317
+ const baseLeft = displayRect?.left ?? baseCellLeft;
25318
+ const baseTop = displayRect?.top ?? baseCellTop;
24591
25319
  const localRect = {
24592
25320
  height: displayRect?.height ?? (displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size),
24593
25321
  left: (useFrozenHorizontalPosition ? stickyLeftByCol.get(drawCell.col) ?? baseLeft - drawingViewport.left : baseLeft - drawingViewport.left) - paneBoundsForCell.left,
@@ -24599,13 +25327,18 @@ function XlsxGrid({
24599
25327
  continue;
24600
25328
  }
24601
25329
  if (!intersectsCanvasDirtyRects(localRect.left, localRect.top, drawableWidth, localRect.height, paneDirtyRects)) {
25330
+ canvasProfileCulledCells += 1;
24602
25331
  continue;
24603
25332
  }
25333
+ canvasProfilePaintedCells += 1;
24604
25334
  const cellStyle = cellData.style;
24605
25335
  const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellStyle);
24606
25336
  const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : sheetSurface);
24607
25337
  const gradientFill = !cellData.conditionalColorScale && typeof cellStyle.backgroundImage === "string" ? resolveCanvasGradientFill(paneContext, localRect, cellStyle.backgroundImage) : null;
24608
25338
  const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== sheetSurface;
25339
+ if (hasExplicitCellFill || cellData.chartHighlight) {
25340
+ flushPendingGridlines();
25341
+ }
24609
25342
  paneContext.fillStyle = gradientFill ?? fillColor;
24610
25343
  paneContext.fillRect(localRect.left, localRect.top, localRect.width, localRect.height);
24611
25344
  if (cellData.chartHighlight) {
@@ -24651,10 +25384,8 @@ function XlsxGrid({
24651
25384
  const bottomNeighborTopBorder = bottomNeighborData?.isMergedSecondary ? null : bottomNeighborData?.canvas?.topBorder ?? parseCanvasBorderDeclaration(bottomNeighborData?.style.borderTop);
24652
25385
  const resolvedRightBorder = resolveCanvasBoundaryBorder(rightBorder, rightNeighborLeftBorder);
24653
25386
  const resolvedBottomBorder = resolveCanvasBoundaryBorder(bottomBorder, bottomNeighborTopBorder);
24654
- if (showGridLines && !hasExplicitCellFill) {
24655
- paneContext.strokeStyle = palette.border;
24656
- paneContext.lineWidth = 1;
24657
- paneContext.beginPath();
25387
+ if (showGridLines && !hasExplicitCellFill && (!resolvedRightBorder || !resolvedBottomBorder)) {
25388
+ enqueueGridlinePath();
24658
25389
  if (!resolvedRightBorder) {
24659
25390
  paneContext.moveTo(localRect.left + localRect.width - 0.5, localRect.top);
24660
25391
  paneContext.lineTo(localRect.left + localRect.width - 0.5, localRect.top + localRect.height);
@@ -24663,7 +25394,9 @@ function XlsxGrid({
24663
25394
  paneContext.moveTo(localRect.left, localRect.top + localRect.height - 0.5);
24664
25395
  paneContext.lineTo(localRect.left + localRect.width, localRect.top + localRect.height - 0.5);
24665
25396
  }
24666
- paneContext.stroke();
25397
+ }
25398
+ if (topBorder || resolvedRightBorder || resolvedBottomBorder || leftBorder || cellData.chartHighlight) {
25399
+ flushPendingGridlines();
24667
25400
  }
24668
25401
  if (topBorder && drawRowIndex === 0) {
24669
25402
  strokeCanvasBorderSide(paneContext, "top", localRect, topBorder);
@@ -24696,17 +25429,26 @@ function XlsxGrid({
24696
25429
  strokeCanvasBorderSide(paneContext, "left", localRect, highlightBorder);
24697
25430
  }
24698
25431
  }
25432
+ const rawText = cellData.value ?? "";
25433
+ const shouldDrawCanvasContent = cellData.checkboxState != null || cellData.sparkline || rawText.length > 0 || cellData.conditionalIcon || cellData.isTableHeader;
25434
+ if (!shouldDrawCanvasContent) {
25435
+ continue;
25436
+ }
24699
25437
  const padding = canvasCellStyle.padding;
24700
25438
  const contentLeft = localRect.left + padding.left;
24701
25439
  const contentTop = localRect.top + padding.top;
24702
25440
  const contentWidth = Math.max(0, localRect.width - padding.left - padding.right);
24703
25441
  const contentHeight = Math.max(0, localRect.height - padding.top - padding.bottom);
25442
+ if (contentWidth <= 0 || contentHeight <= 0) {
25443
+ continue;
25444
+ }
24704
25445
  const activeFontSizePx = cellData.shrinkToFitFontSizePx ?? resolveCanvasFontSizePx(cellStyle, 12 * zoomFactor);
24705
25446
  const textClipOverscan = Math.max(
24706
25447
  1,
24707
25448
  zoomFactor * 1.5,
24708
25449
  activeFontSizePx * (cellData.textRotationDeg ? 0.75 : 0.18)
24709
25450
  );
25451
+ flushPendingGridlines();
24710
25452
  paneContext.save();
24711
25453
  paneContext.beginPath();
24712
25454
  paneContext.rect(
@@ -24838,7 +25580,6 @@ function XlsxGrid({
24838
25580
  const trailingInset = (cellData.conditionalIcon ? 18 * zoomFactor : 0) + (cellData.isTableHeader ? 16 * zoomFactor : 0);
24839
25581
  const spillMaxWidth = cellData.spillWidth && cellData.spillWidth > 0 ? Math.max(0, cellData.spillWidth - trailingInset) : null;
24840
25582
  const maxTextWidth = spillMaxWidth ?? Math.max(0, contentWidth - trailingInset);
24841
- const rawText = cellData.value ?? "";
24842
25583
  const textColor = canvasCellStyle.textColor;
24843
25584
  const shouldEllipsizeText = canvasCellStyle.textOverflowEllipsis;
24844
25585
  const shouldWrapText = canvasCellStyle.usesWrappedText || rawText.includes("\n");
@@ -24870,7 +25611,7 @@ function XlsxGrid({
24870
25611
  const textY = textBlockTop + lineIndex * lineHeight + lineHeight / 2;
24871
25612
  paneContext.fillText(line, textX, textY);
24872
25613
  if (canvasCellStyle.textDecoration?.includes("underline") && line.length > 0) {
24873
- const measured = Math.min(maxTextWidth, paneContext.measureText(line).width);
25614
+ const measured = Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, line));
24874
25615
  const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
24875
25616
  paneContext.beginPath();
24876
25617
  paneContext.moveTo(underlineStartX, textY + Math.max(2, lineHeight * 0.24));
@@ -24904,7 +25645,7 @@ function XlsxGrid({
24904
25645
  const textY = contentTop + contentHeight / 2;
24905
25646
  paneContext.fillText(text, textX, textY);
24906
25647
  if (canvasCellStyle.textDecoration?.includes("underline") && text.length > 0) {
24907
- const measured = shouldEllipsizeText ? Math.min(maxTextWidth, paneContext.measureText(text).width) : paneContext.measureText(text).width;
25648
+ const measured = shouldEllipsizeText ? Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, text)) : measureCanvasTextWidth(paneContext, text);
24908
25649
  const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
24909
25650
  paneContext.beginPath();
24910
25651
  paneContext.moveTo(underlineStartX, textY + 6 * zoomFactor);
@@ -24929,6 +25670,7 @@ function XlsxGrid({
24929
25670
  paneContext.restore();
24930
25671
  }
24931
25672
  }
25673
+ flushPendingGridlines();
24932
25674
  }
24933
25675
  for (const pane of cellPaneOrder) {
24934
25676
  const paneContext = bodyContexts[pane];
@@ -24946,7 +25688,7 @@ function XlsxGrid({
24946
25688
  paneContext.textBaseline = "middle";
24947
25689
  paneContext.fillText(spillText.text, spillText.textX, spillText.textY);
24948
25690
  if (spillText.textDecoration?.includes("underline") && spillText.text.length > 0) {
24949
- const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, paneContext.measureText(spillText.text).width) : paneContext.measureText(spillText.text).width;
25691
+ const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, measureCanvasTextWidth(paneContext, spillText.text)) : measureCanvasTextWidth(paneContext, spillText.text);
24950
25692
  const underlineStartX = spillText.align === "right" ? spillText.textX - measured : spillText.align === "center" ? spillText.textX - measured / 2 : spillText.textX;
24951
25693
  paneContext.beginPath();
24952
25694
  paneContext.moveTo(underlineStartX, spillText.underlineY);
@@ -24958,6 +25700,14 @@ function XlsxGrid({
24958
25700
  paneContext.restore();
24959
25701
  }
24960
25702
  }
25703
+ for (const pane of cellPaneOrder) {
25704
+ const paneContext = bodyContexts[pane];
25705
+ if (!paneContext) {
25706
+ continue;
25707
+ }
25708
+ drawBakedCanvasDrawings(pane, paneContext, bodyDirtyRectsByPane[pane]);
25709
+ }
25710
+ canvasProfileBodyMs = canvasProfileTarget ? performance.now() - canvasProfileBodyStart : 0;
24961
25711
  }
24962
25712
  if (shouldRepaintHeaders && cornerContext) {
24963
25713
  const topFrozenHeaderContext = topHeaderContexts.frozen;
@@ -24972,7 +25722,7 @@ function XlsxGrid({
24972
25722
  topScrollHeaderContext,
24973
25723
  bufferCanvas,
24974
25724
  dpr,
24975
- topScrollHeaderCanvasWidth2,
25725
+ topScrollHeaderCanvasWidth,
24976
25726
  headerHeight,
24977
25727
  drawingViewport.left - previousPaintedViewport.left,
24978
25728
  0
@@ -24991,7 +25741,7 @@ function XlsxGrid({
24991
25741
  bufferCanvas,
24992
25742
  dpr,
24993
25743
  rowHeaderWidth,
24994
- leftScrollHeaderCanvasHeight2,
25744
+ leftScrollHeaderCanvasHeight,
24995
25745
  0,
24996
25746
  drawingViewport.top - previousPaintedViewport.top
24997
25747
  );
@@ -25000,11 +25750,11 @@ function XlsxGrid({
25000
25750
  }
25001
25751
  }
25002
25752
  }
25003
- if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth2 > 0) {
25753
+ if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth > 0) {
25004
25754
  topFrozenHeaderContext.fillStyle = palette.headerSurface;
25005
- topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth2, headerHeight);
25755
+ topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth, headerHeight);
25006
25756
  }
25007
- if (topScrollHeaderContext && topScrollHeaderCanvasWidth2 > 0) {
25757
+ if (topScrollHeaderContext && topScrollHeaderCanvasWidth > 0) {
25008
25758
  topScrollHeaderContext.fillStyle = palette.headerSurface;
25009
25759
  for (const dirtyRect of topScrollHeaderDirtyRects) {
25010
25760
  topScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
@@ -25023,7 +25773,7 @@ function XlsxGrid({
25023
25773
  if (!paneContext) {
25024
25774
  continue;
25025
25775
  }
25026
- if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth2 : topScrollHeaderCanvasWidth2)) {
25776
+ if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth : topScrollHeaderCanvasWidth)) {
25027
25777
  continue;
25028
25778
  }
25029
25779
  if (!column.isFrozen && !intersectsCanvasDirtyRects(column.localLeft, 0, column.width, column.height, topScrollHeaderDirtyRects)) {
@@ -25049,11 +25799,11 @@ function XlsxGrid({
25049
25799
  column.height / 2
25050
25800
  );
25051
25801
  }
25052
- if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight2 > 0) {
25802
+ if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight > 0) {
25053
25803
  leftFrozenHeaderContext.fillStyle = palette.rowHeaderSurface;
25054
- leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight2);
25804
+ leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight);
25055
25805
  }
25056
- if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight2 > 0) {
25806
+ if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight > 0) {
25057
25807
  leftScrollHeaderContext.fillStyle = palette.rowHeaderSurface;
25058
25808
  for (const dirtyRect of leftScrollHeaderDirtyRects) {
25059
25809
  leftScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
@@ -25072,7 +25822,7 @@ function XlsxGrid({
25072
25822
  if (!paneContext) {
25073
25823
  continue;
25074
25824
  }
25075
- if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight2 : leftScrollHeaderCanvasHeight2)) {
25825
+ if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight : leftScrollHeaderCanvasHeight)) {
25076
25826
  continue;
25077
25827
  }
25078
25828
  if (!row.isFrozen && !intersectsCanvasDirtyRects(0, row.localTop, rowHeaderWidth, row.height, leftScrollHeaderDirtyRects)) {
@@ -25112,9 +25862,29 @@ function XlsxGrid({
25112
25862
  paintedHeaderCanvasSignatureRef.current = nextHeaderCanvasSignature;
25113
25863
  }
25114
25864
  applyCanvasViewportCompensation();
25865
+ if (canvasProfileTarget) {
25866
+ canvasProfileTarget.push({
25867
+ bodyMs: canvasProfileBodyMs,
25868
+ culledCells: canvasProfileCulledCells,
25869
+ dirtyRects: canvasProfileDirtyRects,
25870
+ lookedUpCells: canvasProfileLookedUpCells,
25871
+ paintedCells: canvasProfilePaintedCells,
25872
+ repaintBody: shouldRepaintBody,
25873
+ repaintHeaders: shouldRepaintHeaders,
25874
+ renderToLayoutMs: canvasProfileStart - xlsxGridRenderStart,
25875
+ totalMs: performance.now() - canvasProfileStart
25876
+ });
25877
+ if (canvasProfileTarget.length > 500) {
25878
+ canvasProfileTarget.splice(0, canvasProfileTarget.length - 500);
25879
+ }
25880
+ }
25115
25881
  }, [
25116
25882
  activeSheet,
25117
25883
  applyCanvasViewportCompensation,
25884
+ bakedCanvasDrawingSignature,
25885
+ bakedFormControlRects,
25886
+ bakedImageRects,
25887
+ bakedShapeRects,
25118
25888
  canvasColumnHeaderCells,
25119
25889
  canvasPaneAxisItems,
25120
25890
  canvasRowHeaderCells,
@@ -25135,15 +25905,16 @@ function XlsxGrid({
25135
25905
  frozenPaneBottom,
25136
25906
  frozenPaneRight,
25137
25907
  getCellData,
25908
+ getCanvasImage,
25138
25909
  getBodyBlitBufferCanvas,
25139
25910
  getHeaderBlitBufferCanvas,
25140
25911
  palette,
25141
25912
  resolveCellDisplayRect,
25142
25913
  resolveMergeAnchorCell,
25143
- resizeGuide,
25144
25914
  rowIndexByActual,
25145
25915
  rowPrefixSums,
25146
25916
  selectionHeaderSurface,
25917
+ shouldBakeCanvasStaticDrawings,
25147
25918
  stickyLeftByCol,
25148
25919
  stickyTopByRow,
25149
25920
  visibleCols,
@@ -25361,7 +26132,7 @@ function XlsxGrid({
25361
26132
  start: virtualRow?.start ?? (rowPrefixSums[index] ?? 0)
25362
26133
  };
25363
26134
  });
25364
- const totalHeight = shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
26135
+ const totalHeight = shouldUseDomVirtualizer && shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
25365
26136
  const totalWidth = totalContentWidth + displayRowHeaderWidth;
25366
26137
  const sheetContentHeight = displayHeaderHeight + totalHeight;
25367
26138
  const isLiveZooming = liveGestureZoom !== null && zoomScale === liveGestureZoom.baseZoomScale;
@@ -25435,18 +26206,6 @@ function XlsxGrid({
25435
26206
  width: 0,
25436
26207
  zIndex: canvasHeaderOverlayZIndex
25437
26208
  };
25438
- const scrollBodyCanvasWidth = Math.max(0, drawingViewport.width - frozenPaneRight);
25439
- const scrollBodyCanvasHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);
25440
- const topBodyCanvasWidth = scrollBodyCanvasWidth;
25441
- const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);
25442
- const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
25443
- const leftBodyCanvasHeight = scrollBodyCanvasHeight;
25444
- const cornerBodyCanvasWidth = leftBodyCanvasWidth;
25445
- const cornerBodyCanvasHeight = topBodyCanvasHeight;
25446
- const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
25447
- const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
25448
- const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
25449
- const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
25450
26209
  const canvasBodyBaseStyle = {
25451
26210
  cursor: "cell",
25452
26211
  pointerEvents: "auto",
@@ -25458,13 +26217,13 @@ function XlsxGrid({
25458
26217
  const canvasScrollBodyStyle = {
25459
26218
  ...canvasBodyBaseStyle,
25460
26219
  display: scrollBodyCanvasWidth > 0 && scrollBodyCanvasHeight > 0 ? "block" : "none",
25461
- left: frozenPaneRight,
25462
- top: frozenPaneBottom
26220
+ left: scrollBodyCanvasLeft,
26221
+ top: scrollBodyCanvasTop
25463
26222
  };
25464
26223
  const canvasTopBodyStyle = {
25465
26224
  ...canvasBodyBaseStyle,
25466
26225
  display: topBodyCanvasWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
25467
- left: frozenPaneRight,
26226
+ left: scrollBodyCanvasLeft,
25468
26227
  top: displayHeaderHeight,
25469
26228
  zIndex: 30
25470
26229
  };
@@ -25472,7 +26231,7 @@ function XlsxGrid({
25472
26231
  ...canvasBodyBaseStyle,
25473
26232
  display: leftBodyCanvasWidth > 0 && leftBodyCanvasHeight > 0 ? "block" : "none",
25474
26233
  left: displayRowHeaderWidth,
25475
- top: frozenPaneBottom,
26234
+ top: scrollBodyCanvasTop,
25476
26235
  zIndex: 30
25477
26236
  };
25478
26237
  const canvasCornerBodyStyle = {
@@ -25498,7 +26257,7 @@ function XlsxGrid({
25498
26257
  const canvasTopScrollHeaderStyle = {
25499
26258
  ...canvasHeaderBaseStyle,
25500
26259
  display: topScrollHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
25501
- left: frozenPaneRight,
26260
+ left: scrollBodyCanvasLeft,
25502
26261
  top: 0,
25503
26262
  zIndex: canvasHeaderOverlayZIndex
25504
26263
  };
@@ -25513,7 +26272,7 @@ function XlsxGrid({
25513
26272
  ...canvasHeaderBaseStyle,
25514
26273
  display: leftScrollHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
25515
26274
  left: 0,
25516
- top: frozenPaneBottom,
26275
+ top: scrollBodyCanvasTop,
25517
26276
  zIndex: canvasHeaderOverlayZIndex
25518
26277
  };
25519
26278
  const canvasCornerHeaderStyle = {
@@ -26069,26 +26828,26 @@ function XlsxGrid({
26069
26828
  };
26070
26829
  const canvasScrollOverlayPaneStyle = {
26071
26830
  ...canvasOverlayPaneBaseStyle,
26072
- display: scrollBodyCanvasWidth > 0 && scrollBodyCanvasHeight > 0 ? "block" : "none",
26073
- height: scrollBodyCanvasHeight,
26831
+ display: scrollBodyViewportWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
26832
+ height: scrollBodyViewportHeight,
26074
26833
  left: frozenPaneRight,
26075
26834
  top: frozenPaneBottom,
26076
- width: scrollBodyCanvasWidth,
26835
+ width: scrollBodyViewportWidth,
26077
26836
  zIndex: 20
26078
26837
  };
26079
26838
  const canvasTopOverlayPaneStyle = {
26080
26839
  ...canvasOverlayPaneBaseStyle,
26081
- display: topBodyCanvasWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
26840
+ display: scrollBodyViewportWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
26082
26841
  height: topBodyCanvasHeight,
26083
26842
  left: frozenPaneRight,
26084
26843
  top: displayHeaderHeight,
26085
- width: topBodyCanvasWidth,
26844
+ width: scrollBodyViewportWidth,
26086
26845
  zIndex: 35
26087
26846
  };
26088
26847
  const canvasLeftOverlayPaneStyle = {
26089
26848
  ...canvasOverlayPaneBaseStyle,
26090
- display: leftBodyCanvasWidth > 0 && leftBodyCanvasHeight > 0 ? "block" : "none",
26091
- height: leftBodyCanvasHeight,
26849
+ display: leftBodyCanvasWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
26850
+ height: scrollBodyViewportHeight,
26092
26851
  left: displayRowHeaderWidth,
26093
26852
  top: frozenPaneBottom,
26094
26853
  width: leftBodyCanvasWidth,
@@ -26103,8 +26862,9 @@ function XlsxGrid({
26103
26862
  width: cornerBodyCanvasWidth,
26104
26863
  zIndex: 36
26105
26864
  };
26865
+ const drawingViewportCacheSignature = `${Math.floor(drawingViewport.left / CANVAS_VIEWPORT_OVERSCAN_PX)}:${Math.floor(drawingViewport.top / CANVAS_VIEWPORT_OVERSCAN_PX)}:${drawingViewport.width}:${drawingViewport.height}`;
26106
26866
  const previousPaneDrawingNodes = paneDrawingNodesCacheRef.current;
26107
- const canReusePaneDrawingNodes = previousPaneDrawingNodes !== null && previousPaneDrawingNodes.showImages === showImages && previousPaneDrawingNodes.chartRects === chartRects && previousPaneDrawingNodes.formControlRects === formControlRects && previousPaneDrawingNodes.shapeRects === shapeRects && previousPaneDrawingNodes.imageRects === imageRects && previousPaneDrawingNodes.selectedChartId === selectedChartId && previousPaneDrawingNodes.selectedImageId === selectedImageId && previousPaneDrawingNodes.readOnly === readOnly && previousPaneDrawingNodes.selectionStroke === selectionStroke && previousPaneDrawingNodes.renderChartLoading === renderChartLoading && previousPaneDrawingNodes.renderImage === renderImage && previousPaneDrawingNodes.renderImageSelection === renderImageSelection && previousPaneDrawingNodes.isChartsLoading === isChartsLoading && previousPaneDrawingNodes.palette === palette && previousPaneDrawingNodes.drawingViewport.left === drawingViewport.left && previousPaneDrawingNodes.drawingViewport.top === drawingViewport.top && previousPaneDrawingNodes.drawingViewport.width === drawingViewport.width && previousPaneDrawingNodes.drawingViewport.height === drawingViewport.height;
26867
+ const canReusePaneDrawingNodes = previousPaneDrawingNodes !== null && previousPaneDrawingNodes.showImages === showImages && previousPaneDrawingNodes.chartRects === chartRects && previousPaneDrawingNodes.formControlRects === domFormControlRects && previousPaneDrawingNodes.shapeRects === domShapeRects && previousPaneDrawingNodes.imageRects === domImageRects && previousPaneDrawingNodes.selectedChartId === selectedChartId && previousPaneDrawingNodes.selectedImageId === selectedImageId && previousPaneDrawingNodes.readOnly === readOnly && previousPaneDrawingNodes.selectionStroke === selectionStroke && previousPaneDrawingNodes.renderChartLoading === renderChartLoading && previousPaneDrawingNodes.renderImage === renderImage && previousPaneDrawingNodes.renderImageSelection === renderImageSelection && previousPaneDrawingNodes.isChartsLoading === isChartsLoading && previousPaneDrawingNodes.palette === palette && previousPaneDrawingNodes.drawingViewportSignature === drawingViewportCacheSignature;
26108
26868
  const paneDrawingNodes = canReusePaneDrawingNodes ? previousPaneDrawingNodes.value : !showImages ? {
26109
26869
  corner: null,
26110
26870
  left: null,
@@ -26113,35 +26873,35 @@ function XlsxGrid({
26113
26873
  } : {
26114
26874
  corner: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
26115
26875
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "corner")),
26116
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "corner")),
26117
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "corner")),
26118
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "corner"))
26876
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "corner")),
26877
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "corner")),
26878
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "corner"))
26119
26879
  ] }),
26120
26880
  left: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
26121
26881
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "left")),
26122
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "left")),
26123
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "left")),
26124
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "left"))
26882
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "left")),
26883
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "left")),
26884
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "left"))
26125
26885
  ] }),
26126
26886
  scroll: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
26127
26887
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "scroll")),
26128
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "scroll")),
26129
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "scroll")),
26130
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "scroll"))
26888
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "scroll")),
26889
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "scroll")),
26890
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "scroll"))
26131
26891
  ] }),
26132
26892
  top: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
26133
26893
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "top")),
26134
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "top")),
26135
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "top")),
26136
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "top"))
26894
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "top")),
26895
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "top")),
26896
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "top"))
26137
26897
  ] })
26138
26898
  };
26139
26899
  if (!canReusePaneDrawingNodes) {
26140
26900
  paneDrawingNodesCacheRef.current = {
26141
26901
  chartRects,
26142
- drawingViewport,
26143
- formControlRects,
26144
- imageRects,
26902
+ drawingViewportSignature: drawingViewportCacheSignature,
26903
+ formControlRects: domFormControlRects,
26904
+ imageRects: domImageRects,
26145
26905
  isChartsLoading,
26146
26906
  palette,
26147
26907
  readOnly,
@@ -26151,7 +26911,7 @@ function XlsxGrid({
26151
26911
  selectedChartId,
26152
26912
  selectedImageId,
26153
26913
  selectionStroke,
26154
- shapeRects,
26914
+ shapeRects: domShapeRects,
26155
26915
  showImages,
26156
26916
  value: paneDrawingNodes
26157
26917
  };
@@ -26659,10 +27419,11 @@ function XlsxGrid({
26659
27419
  style: canvasCornerBodyStyle
26660
27420
  }
26661
27421
  ),
26662
- showImages ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
27422
+ hasCanvasDomDrawingOverlays ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
26663
27423
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasScrollOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26664
27424
  "div",
26665
27425
  {
27426
+ ref: canvasScrollOverlayContentRef,
26666
27427
  style: {
26667
27428
  height: sheetContentHeight,
26668
27429
  left: 0,
@@ -26670,6 +27431,7 @@ function XlsxGrid({
26670
27431
  position: "absolute",
26671
27432
  top: 0,
26672
27433
  transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
27434
+ transformOrigin: "0 0",
26673
27435
  width: totalWidth
26674
27436
  },
26675
27437
  children: paneDrawingNodes.scroll
@@ -26678,6 +27440,7 @@ function XlsxGrid({
26678
27440
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasTopOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26679
27441
  "div",
26680
27442
  {
27443
+ ref: canvasTopOverlayContentRef,
26681
27444
  style: {
26682
27445
  height: sheetContentHeight,
26683
27446
  left: 0,
@@ -26685,6 +27448,7 @@ function XlsxGrid({
26685
27448
  position: "absolute",
26686
27449
  top: 0,
26687
27450
  transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-displayHeaderHeight}px)`,
27451
+ transformOrigin: "0 0",
26688
27452
  width: totalWidth
26689
27453
  },
26690
27454
  children: paneDrawingNodes.top
@@ -26693,6 +27457,7 @@ function XlsxGrid({
26693
27457
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasLeftOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26694
27458
  "div",
26695
27459
  {
27460
+ ref: canvasLeftOverlayContentRef,
26696
27461
  style: {
26697
27462
  height: sheetContentHeight,
26698
27463
  left: 0,
@@ -26700,6 +27465,7 @@ function XlsxGrid({
26700
27465
  position: "absolute",
26701
27466
  top: 0,
26702
27467
  transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
27468
+ transformOrigin: "0 0",
26703
27469
  width: totalWidth
26704
27470
  },
26705
27471
  children: paneDrawingNodes.left
@@ -26708,6 +27474,7 @@ function XlsxGrid({
26708
27474
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasCornerOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26709
27475
  "div",
26710
27476
  {
27477
+ ref: canvasCornerOverlayContentRef,
26711
27478
  style: {
26712
27479
  height: sheetContentHeight,
26713
27480
  left: 0,
@@ -26715,6 +27482,7 @@ function XlsxGrid({
26715
27482
  position: "absolute",
26716
27483
  top: 0,
26717
27484
  transform: `translate(${-displayRowHeaderWidth}px, ${-displayHeaderHeight}px)`,
27485
+ transformOrigin: "0 0",
26718
27486
  width: totalWidth
26719
27487
  },
26720
27488
  children: paneDrawingNodes.corner
@@ -27121,7 +27889,7 @@ function XlsxViewerInner({
27121
27889
  enableCanvasSelectionAnimation = true,
27122
27890
  enableGestureZoom = true,
27123
27891
  errorState,
27124
- experimentalCanvas = false,
27892
+ experimentalCanvas = true,
27125
27893
  fileTooLargeState,
27126
27894
  height,
27127
27895
  isDark = false,
@@ -27634,7 +28402,7 @@ function useXlsxViewerThumbnails(options = {}) {
27634
28402
  if (!canvas) {
27635
28403
  return false;
27636
28404
  }
27637
- const context = canvas.getContext("2d");
28405
+ const context = canvas.getContext("2d", { alpha: false });
27638
28406
  if (!context) {
27639
28407
  return false;
27640
28408
  }
@@ -27658,9 +28426,10 @@ function useXlsxViewerThumbnails(options = {}) {
27658
28426
  }
27659
28427
  context.setTransform(dpr * scale, 0, 0, dpr * scale, 0, 0);
27660
28428
  context.clearRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
28429
+ const thumbnailSheetSurface = resolveSheetSurface(sheet, palette);
27661
28430
  context.fillStyle = palette.canvas;
27662
28431
  context.fillRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
27663
- context.fillStyle = resolveSheetSurface(sheet, palette);
28432
+ context.fillStyle = thumbnailSheetSurface;
27664
28433
  context.fillRect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
27665
28434
  if (includeHeaders) {
27666
28435
  context.fillStyle = palette.headerSurface;
@@ -27781,8 +28550,8 @@ function useXlsxViewerThumbnails(options = {}) {
27781
28550
  };
27782
28551
  const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellData.style);
27783
28552
  const gradientFill = typeof cellData.style.backgroundImage === "string" ? resolveCanvasGradientFill(context, rect, cellData.style.backgroundImage) : null;
27784
- const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor : resolveSheetSurface(sheet, palette));
27785
- const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !== resolveSheetSurface(sheet, palette);
28553
+ const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor : thumbnailSheetSurface);
28554
+ const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !== thumbnailSheetSurface;
27786
28555
  context.fillStyle = gradientFill ?? fillColor;
27787
28556
  context.fillRect(rect.left, rect.top, rect.width, rect.height);
27788
28557
  if (cellData.conditionalDataBar) {
@@ -27837,12 +28606,19 @@ function useXlsxViewerThumbnails(options = {}) {
27837
28606
  if (canvasCellStyle.leftBorder) {
27838
28607
  strokeCanvasBorderSide(context, "left", rect, canvasCellStyle.leftBorder);
27839
28608
  }
28609
+ const rawText = cellData.value ?? "";
28610
+ const shouldDrawThumbnailContent = cellData.checkboxState != null || cellData.sparkline || rawText.length > 0 || cellData.conditionalIcon;
28611
+ if (!shouldDrawThumbnailContent) {
28612
+ continue;
28613
+ }
27840
28614
  const padding = canvasCellStyle.padding;
27841
28615
  const contentLeft = rect.left + padding.left;
27842
28616
  const contentTop = rect.top + padding.top;
27843
28617
  const contentWidth = Math.max(0, rect.width - padding.left - padding.right);
27844
28618
  const contentHeight = Math.max(0, rect.height - padding.top - padding.bottom);
27845
- const rawText = cellData.value ?? "";
28619
+ if (contentWidth <= 0 || contentHeight <= 0) {
28620
+ continue;
28621
+ }
27846
28622
  context.save();
27847
28623
  context.beginPath();
27848
28624
  context.rect(contentLeft, contentTop, contentWidth, contentHeight);