@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.js CHANGED
@@ -4527,6 +4527,10 @@ function parseSheetState(archive, path, options) {
4527
4527
  let hasVerticalMerges = false;
4528
4528
  let maxHorizontalMergeEndCol = -1;
4529
4529
  let maxVerticalMergeEndRow = -1;
4530
+ let minContentCol = Number.POSITIVE_INFINITY;
4531
+ let minContentRow = Number.POSITIVE_INFINITY;
4532
+ let maxContentCol = -1;
4533
+ let maxContentRow = -1;
4530
4534
  const columnWidthCharacterWidthPx = measureColumnCharacterWidthPx(
4531
4535
  options?.defaultFont?.family,
4532
4536
  options?.defaultFont?.sizePt
@@ -4539,6 +4543,26 @@ function parseSheetState(archive, path, options) {
4539
4543
  sheetViewNode?.getAttribute("zoomScale") ?? sheetViewNode?.getAttribute("zoomScaleNormal") ?? Number.NaN
4540
4544
  );
4541
4545
  const zoomScale = Number.isFinite(rawZoomScale) && rawZoomScale > 0 ? rawZoomScale : 100;
4546
+ const trackContentCell = (cellRef) => {
4547
+ if (!cellRef) {
4548
+ return;
4549
+ }
4550
+ const cell = parseA1CellReference(cellRef);
4551
+ if (!cell) {
4552
+ return;
4553
+ }
4554
+ minContentCol = Math.min(minContentCol, cell.col);
4555
+ minContentRow = Math.min(minContentRow, cell.row);
4556
+ maxContentCol = Math.max(maxContentCol, cell.col);
4557
+ maxContentRow = Math.max(maxContentRow, cell.row);
4558
+ };
4559
+ const isMeaningfulCellNode = (cellNode) => {
4560
+ if (getFirstChild(cellNode, "f") || getFirstChild(cellNode, "is")) {
4561
+ return true;
4562
+ }
4563
+ const valueNode = getFirstChild(cellNode, "v");
4564
+ return Boolean(valueNode && (valueNode.textContent ?? "").length > 0);
4565
+ };
4542
4566
  getLocalElements(document2, "row").forEach((rowNode) => {
4543
4567
  const rowIndex = Number(rowNode.getAttribute("r") ?? 0) - 1;
4544
4568
  const height = Number(rowNode.getAttribute("ht") ?? Number.NaN);
@@ -4553,17 +4577,36 @@ function parseSheetState(archive, path, options) {
4553
4577
  if (rowIndex >= 0 && isHidden) {
4554
4578
  hiddenRows.add(rowIndex);
4555
4579
  }
4556
- if (includeCachedFormulaValues) {
4557
- getChildElements(rowNode, "c").forEach((cellNode) => {
4580
+ getChildElements(rowNode, "c").forEach((cellNode) => {
4581
+ const cellRef = cellNode.getAttribute("r");
4582
+ if (isMeaningfulCellNode(cellNode)) {
4583
+ trackContentCell(cellRef);
4584
+ }
4585
+ if (includeCachedFormulaValues) {
4558
4586
  const formulaNode = getFirstChild(cellNode, "f");
4559
4587
  const valueNode = getFirstChild(cellNode, "v");
4560
- const cellRef = cellNode.getAttribute("r");
4561
4588
  if (formulaNode && valueNode && cellRef) {
4562
4589
  cachedFormulaValues[cellRef] = valueNode.textContent ?? "";
4563
4590
  }
4564
- });
4591
+ }
4592
+ });
4593
+ });
4594
+ getLocalElements(document2, "mergeCell").forEach((mergeNode) => {
4595
+ const reference = mergeNode.getAttribute("ref");
4596
+ const range = reference ? parseA1RangeReference(reference) : null;
4597
+ if (!range) {
4598
+ return;
4599
+ }
4600
+ if (range.end.col > range.start.col) {
4601
+ hasHorizontalMerges = true;
4602
+ maxHorizontalMergeEndCol = Math.max(maxHorizontalMergeEndCol, range.end.col);
4603
+ }
4604
+ if (range.end.row > range.start.row) {
4605
+ hasVerticalMerges = true;
4606
+ maxVerticalMergeEndRow = Math.max(maxVerticalMergeEndRow, range.end.row);
4565
4607
  }
4566
4608
  });
4609
+ const maxMetadataCol = Math.max(maxContentCol, maxHorizontalMergeEndCol, 0) + 256;
4567
4610
  getLocalElements(document2, "col").forEach((colNode) => {
4568
4611
  const min = Number(colNode.getAttribute("min") ?? 0) - 1;
4569
4612
  const max = Number(colNode.getAttribute("max") ?? 0) - 1;
@@ -4575,7 +4618,7 @@ function parseSheetState(archive, path, options) {
4575
4618
  return;
4576
4619
  }
4577
4620
  }
4578
- for (let col = min; col <= max; col += 1) {
4621
+ for (let col = min; col <= Math.min(max, maxMetadataCol); col += 1) {
4579
4622
  if (col >= 0) {
4580
4623
  if (Number.isFinite(width)) {
4581
4624
  const widthPx = sheetColumnWidthToPixels(width, columnWidthCharacterWidthPx);
@@ -4590,21 +4633,6 @@ function parseSheetState(archive, path, options) {
4590
4633
  }
4591
4634
  }
4592
4635
  });
4593
- getLocalElements(document2, "mergeCell").forEach((mergeNode) => {
4594
- const reference = mergeNode.getAttribute("ref");
4595
- const range = reference ? parseA1RangeReference(reference) : null;
4596
- if (!range) {
4597
- return;
4598
- }
4599
- if (range.end.col > range.start.col) {
4600
- hasHorizontalMerges = true;
4601
- maxHorizontalMergeEndCol = Math.max(maxHorizontalMergeEndCol, range.end.col);
4602
- }
4603
- if (range.end.row > range.start.row) {
4604
- hasVerticalMerges = true;
4605
- maxVerticalMergeEndRow = Math.max(maxVerticalMergeEndRow, range.end.row);
4606
- }
4607
- });
4608
4636
  return {
4609
4637
  cachedFormulaValues,
4610
4638
  columnWidthCharacterWidthPx,
@@ -4617,6 +4645,10 @@ function parseSheetState(archive, path, options) {
4617
4645
  hasVerticalMerges,
4618
4646
  maxHorizontalMergeEndCol,
4619
4647
  maxVerticalMergeEndRow,
4648
+ maxContentCol,
4649
+ maxContentRow,
4650
+ minContentCol: Number.isFinite(minContentCol) ? minContentCol : -1,
4651
+ minContentRow: Number.isFinite(minContentRow) ? minContentRow : -1,
4620
4652
  hiddenCols: [...hiddenCols].sort((left, right) => left - right),
4621
4653
  hiddenRows: [...hiddenRows].sort((left, right) => left - right),
4622
4654
  rowHeightOverridesPx,
@@ -6645,6 +6677,20 @@ function resolveDisplayFileName(src, fileName) {
6645
6677
  return lastSegment;
6646
6678
  }
6647
6679
  }
6680
+ function resolveSheetDisplayUsedRange(usedRange, sheetState) {
6681
+ const [minRow, minCol, maxRow, maxCol] = usedRange;
6682
+ const maxMeaningfulRow = Math.max(sheetState?.maxContentRow ?? -1, sheetState?.maxVerticalMergeEndRow ?? -1);
6683
+ const maxMeaningfulCol = Math.max(sheetState?.maxContentCol ?? -1, sheetState?.maxHorizontalMergeEndCol ?? -1);
6684
+ if (maxMeaningfulRow < 0 && maxMeaningfulCol < 0) {
6685
+ return usedRange;
6686
+ }
6687
+ return [
6688
+ sheetState?.minContentRow !== void 0 && sheetState.minContentRow >= 0 ? Math.min(minRow, sheetState.minContentRow) : minRow,
6689
+ sheetState?.minContentCol !== void 0 && sheetState.minContentCol >= 0 ? Math.min(minCol, sheetState.minContentCol) : minCol,
6690
+ maxMeaningfulRow >= 0 ? Math.min(maxRow, maxMeaningfulRow) : maxRow,
6691
+ maxMeaningfulCol >= 0 ? Math.min(maxCol, maxMeaningfulCol) : maxCol
6692
+ ];
6693
+ }
6648
6694
  function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette, styleById, namedCellStyleByName, tableStyleByName, showHiddenSheets = false) {
6649
6695
  const sheets = [];
6650
6696
  for (let index = 0; index < workbook.sheetCount; index += 1) {
@@ -6711,7 +6757,7 @@ function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette,
6711
6757
  });
6712
6758
  continue;
6713
6759
  }
6714
- const [minRow, minCol, maxRow, maxCol] = usedRange;
6760
+ const [minRow, minCol, maxRow, maxCol] = resolveSheetDisplayUsedRange(usedRange, sheetState);
6715
6761
  let visibleRowsCache = null;
6716
6762
  let visibleColsCache = null;
6717
6763
  let rowHeightsCache = null;
@@ -16470,7 +16516,11 @@ var SELECTION_DRAG_THRESHOLD_PX = 4;
16470
16516
  var IMAGE_MIN_SIZE_PX = 16;
16471
16517
  var IMAGE_HANDLE_SIZE_PX = 10;
16472
16518
  var CANVAS_RESIZE_HIT_SLOP_PX = 8;
16473
- var CANVAS_VIEWPORT_OVERSCAN_PX = 240;
16519
+ var CANVAS_VIEWPORT_OVERSCAN_PX = 480;
16520
+ var CANVAS_SCROLL_BUFFER_PX = 480;
16521
+ var CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX - 96;
16522
+ var CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX * 1.5;
16523
+ var CANVAS_DIRTY_CELL_CULL_MARGIN_PX = CANVAS_VIEWPORT_OVERSCAN_PX * 2;
16474
16524
  var THUMBNAIL_DEFAULT_MAX_DIMENSION = 192;
16475
16525
  var THUMBNAIL_FALLBACK_ROWS = 12;
16476
16526
  var THUMBNAIL_FALLBACK_COLS = 8;
@@ -16485,6 +16535,12 @@ var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669",
16485
16535
  var SHEET_SURFACE = "#ffffff";
16486
16536
  var DEFAULT_CELL_PADDING = "0 4px";
16487
16537
  var IMAGE_HANDLE_POSITIONS = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
16538
+ var CANVAS_CELL_STYLE_CACHE_LIMIT = 4096;
16539
+ var CANVAS_TEXT_MEASURE_CACHE_LIMIT = 2e4;
16540
+ var CANVAS_TEXT_TRUNCATE_CACHE_LIMIT = 4096;
16541
+ var CANVAS_TEXT_WRAP_CACHE_LIMIT = 2048;
16542
+ var CANVAS_PATH2D_CACHE_LIMIT = 512;
16543
+ var EMPTY_VIRTUAL_ITEMS = [];
16488
16544
  var IMAGE_HANDLE_CURSOR = {
16489
16545
  e: "ew-resize",
16490
16546
  n: "ns-resize",
@@ -16528,6 +16584,50 @@ var NUMERIC_LENGTH_STYLE_KEYS = /* @__PURE__ */ new Set([
16528
16584
  "top",
16529
16585
  "width"
16530
16586
  ]);
16587
+ var canvasCellStyleCache = /* @__PURE__ */ new Map();
16588
+ var canvasTextMeasureCache = /* @__PURE__ */ new Map();
16589
+ var canvasTextTruncateCache = /* @__PURE__ */ new Map();
16590
+ var canvasTextWrapCache = /* @__PURE__ */ new Map();
16591
+ var canvasPath2DCache = /* @__PURE__ */ new Map();
16592
+ function rememberBoundedCacheValue(cache, key, value, limit) {
16593
+ if (cache.size >= limit) {
16594
+ const oldestKey = cache.keys().next().value;
16595
+ if (oldestKey !== void 0) {
16596
+ cache.delete(oldestKey);
16597
+ }
16598
+ }
16599
+ cache.set(key, value);
16600
+ return value;
16601
+ }
16602
+ function roundCanvasCacheWidth(width) {
16603
+ return Math.round(width * 4) / 4;
16604
+ }
16605
+ function measureCanvasTextWidth(context, text) {
16606
+ if (text.length === 0) {
16607
+ return 0;
16608
+ }
16609
+ const cacheKey = `${context.font}\0${text}`;
16610
+ const cached = canvasTextMeasureCache.get(cacheKey);
16611
+ if (cached !== void 0) {
16612
+ return cached;
16613
+ }
16614
+ return rememberBoundedCacheValue(
16615
+ canvasTextMeasureCache,
16616
+ cacheKey,
16617
+ context.measureText(text).width,
16618
+ CANVAS_TEXT_MEASURE_CACHE_LIMIT
16619
+ );
16620
+ }
16621
+ function getCachedCanvasPath2D(path) {
16622
+ if (typeof Path2D === "undefined") {
16623
+ return null;
16624
+ }
16625
+ const cached = canvasPath2DCache.get(path);
16626
+ if (cached) {
16627
+ return cached;
16628
+ }
16629
+ return rememberBoundedCacheValue(canvasPath2DCache, path, new Path2D(path), CANVAS_PATH2D_CACHE_LIMIT);
16630
+ }
16531
16631
  function scaleCssLengthExpression(value, scale) {
16532
16632
  if (scale === 1) {
16533
16633
  return value;
@@ -16708,7 +16808,6 @@ function strokeCanvasBorderSide(context, side, rect, border) {
16708
16808
  const right = rect.left + rect.width;
16709
16809
  const top = rect.top;
16710
16810
  const bottom = rect.top + rect.height;
16711
- context.save();
16712
16811
  context.strokeStyle = border.color;
16713
16812
  context.lineWidth = border.width;
16714
16813
  applyCanvasBorderDash(context, border.style, border.width);
@@ -16738,13 +16837,22 @@ function strokeCanvasBorderSide(context, side, rect, border) {
16738
16837
  } else {
16739
16838
  strokeLine(0);
16740
16839
  }
16741
- context.restore();
16840
+ context.setLineDash([]);
16841
+ context.lineCap = "butt";
16842
+ context.lineWidth = 1;
16742
16843
  }
16743
16844
  function truncateCanvasText(context, text, maxWidth) {
16744
16845
  if (maxWidth <= 0 || text.length === 0) {
16745
16846
  return "";
16746
16847
  }
16747
- if (context.measureText(text).width <= maxWidth) {
16848
+ const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);
16849
+ const cacheKey = `${context.font}\0${roundedMaxWidth}\0${text}`;
16850
+ const cached = canvasTextTruncateCache.get(cacheKey);
16851
+ if (cached !== void 0) {
16852
+ return cached;
16853
+ }
16854
+ if (measureCanvasTextWidth(context, text) <= maxWidth) {
16855
+ rememberBoundedCacheValue(canvasTextTruncateCache, cacheKey, text, CANVAS_TEXT_TRUNCATE_CACHE_LIMIT);
16748
16856
  return text;
16749
16857
  }
16750
16858
  const ellipsis = "\u2026";
@@ -16753,13 +16861,18 @@ function truncateCanvasText(context, text, maxWidth) {
16753
16861
  while (low < high) {
16754
16862
  const mid = Math.ceil((low + high) / 2);
16755
16863
  const candidate = `${text.slice(0, mid)}${ellipsis}`;
16756
- if (context.measureText(candidate).width <= maxWidth) {
16864
+ if (measureCanvasTextWidth(context, candidate) <= maxWidth) {
16757
16865
  low = mid;
16758
16866
  } else {
16759
16867
  high = mid - 1;
16760
16868
  }
16761
16869
  }
16762
- return low <= 0 ? ellipsis : `${text.slice(0, low)}${ellipsis}`;
16870
+ return rememberBoundedCacheValue(
16871
+ canvasTextTruncateCache,
16872
+ cacheKey,
16873
+ low <= 0 ? ellipsis : `${text.slice(0, low)}${ellipsis}`,
16874
+ CANVAS_TEXT_TRUNCATE_CACHE_LIMIT
16875
+ );
16763
16876
  }
16764
16877
  function resolveCanvasFontSizePx(style, fallbackSize = 12) {
16765
16878
  const rawFontSize = style.fontSize;
@@ -16818,7 +16931,7 @@ function resolveCanvasWrapIndex(context, text, maxWidth) {
16818
16931
  while (low <= high) {
16819
16932
  const mid = Math.floor((low + high) / 2);
16820
16933
  const candidate = text.slice(0, mid);
16821
- if (context.measureText(candidate).width <= maxWidth) {
16934
+ if (measureCanvasTextWidth(context, candidate) <= maxWidth) {
16822
16935
  best = mid;
16823
16936
  low = mid + 1;
16824
16937
  } else {
@@ -16835,6 +16948,12 @@ function wrapCanvasText(context, text, maxWidth) {
16835
16948
  return text.replace(/\r\n?/g, "\n").split("\n");
16836
16949
  }
16837
16950
  const normalized = text.replace(/\r\n?/g, "\n");
16951
+ const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);
16952
+ const cacheKey = `${context.font}\0${roundedMaxWidth}\0${normalized}`;
16953
+ const cached = canvasTextWrapCache.get(cacheKey);
16954
+ if (cached) {
16955
+ return cached;
16956
+ }
16838
16957
  const paragraphs = normalized.split("\n");
16839
16958
  const lines = [];
16840
16959
  for (const paragraph of paragraphs) {
@@ -16844,7 +16963,7 @@ function wrapCanvasText(context, text, maxWidth) {
16844
16963
  }
16845
16964
  let remaining = paragraph;
16846
16965
  while (remaining.length > 0) {
16847
- if (context.measureText(remaining).width <= maxWidth) {
16966
+ if (measureCanvasTextWidth(context, remaining) <= maxWidth) {
16848
16967
  lines.push(remaining);
16849
16968
  break;
16850
16969
  }
@@ -16862,10 +16981,31 @@ function wrapCanvasText(context, text, maxWidth) {
16862
16981
  remaining = remaining.slice(breakIndex).replace(/^\s+/g, "");
16863
16982
  }
16864
16983
  }
16865
- return lines;
16984
+ return rememberBoundedCacheValue(canvasTextWrapCache, cacheKey, lines, CANVAS_TEXT_WRAP_CACHE_LIMIT);
16866
16985
  }
16867
16986
  function buildCanvasCellStyleCache(style) {
16868
- return {
16987
+ const cacheKey = [
16988
+ style.font,
16989
+ style.fontStyle,
16990
+ style.fontWeight,
16991
+ style.fontSize,
16992
+ style.fontFamily,
16993
+ style.borderTop,
16994
+ style.borderRight,
16995
+ style.borderBottom,
16996
+ style.borderLeft,
16997
+ style.padding,
16998
+ style.textAlign,
16999
+ style.color,
17000
+ style.textDecoration,
17001
+ style.textOverflow,
17002
+ style.whiteSpace
17003
+ ].map((value) => value == null ? "" : String(value)).join("");
17004
+ const cached = canvasCellStyleCache.get(cacheKey);
17005
+ if (cached) {
17006
+ return cached;
17007
+ }
17008
+ return rememberBoundedCacheValue(canvasCellStyleCache, cacheKey, {
16869
17009
  baseFont: resolveCanvasFont(style, 12),
16870
17010
  bottomBorder: parseCanvasBorderDeclaration(style.borderBottom),
16871
17011
  leftBorder: parseCanvasBorderDeclaration(style.borderLeft),
@@ -16877,7 +17017,7 @@ function buildCanvasCellStyleCache(style) {
16877
17017
  textOverflowEllipsis: style.textOverflow === "ellipsis",
16878
17018
  topBorder: parseCanvasBorderDeclaration(style.borderTop),
16879
17019
  usesWrappedText: style.whiteSpace === "pre-wrap"
16880
- };
17020
+ }, CANVAS_CELL_STYLE_CACHE_LIMIT);
16881
17021
  }
16882
17022
  function splitCssGradientArgs(value) {
16883
17023
  const parts = [];
@@ -18386,6 +18526,44 @@ function resolveShapeVector(shape) {
18386
18526
  }
18387
18527
  return buildPresetShapePath(shape);
18388
18528
  }
18529
+ function drawCanvasRoundedRect(context, left, top, width, height, radius) {
18530
+ const safeRadius = Math.max(0, Math.min(radius, width / 2, height / 2));
18531
+ context.beginPath();
18532
+ context.moveTo(left + safeRadius, top);
18533
+ context.lineTo(left + width - safeRadius, top);
18534
+ context.quadraticCurveTo(left + width, top, left + width, top + safeRadius);
18535
+ context.lineTo(left + width, top + height - safeRadius);
18536
+ context.quadraticCurveTo(left + width, top + height, left + width - safeRadius, top + height);
18537
+ context.lineTo(left + safeRadius, top + height);
18538
+ context.quadraticCurveTo(left, top + height, left, top + height - safeRadius);
18539
+ context.lineTo(left, top + safeRadius);
18540
+ context.quadraticCurveTo(left, top, left + safeRadius, top);
18541
+ context.closePath();
18542
+ }
18543
+ function applyCanvasShapeDash(context, dash, lineWidth) {
18544
+ if (!dash) {
18545
+ context.setLineDash([]);
18546
+ return;
18547
+ }
18548
+ const unit = Math.max(1, lineWidth);
18549
+ switch (dash) {
18550
+ case "dash":
18551
+ context.setLineDash([4 * unit, 3 * unit]);
18552
+ break;
18553
+ case "dashDot":
18554
+ context.setLineDash([4 * unit, 2 * unit, unit, 2 * unit]);
18555
+ break;
18556
+ case "dot":
18557
+ context.setLineDash([unit, 2 * unit]);
18558
+ break;
18559
+ case "lgDash":
18560
+ context.setLineDash([8 * unit, 3 * unit]);
18561
+ break;
18562
+ default:
18563
+ context.setLineDash([]);
18564
+ break;
18565
+ }
18566
+ }
18389
18567
  function resolveShapeLineEndMarker(type, markerId, color, strokeWidth, rect, viewBox) {
18390
18568
  if (type !== "triangle") {
18391
18569
  return null;
@@ -18988,12 +19166,12 @@ function measureTextWidth(value, style) {
18988
19166
  return value.length * 7;
18989
19167
  }
18990
19168
  textMeasureCanvas ??= document.createElement("canvas");
18991
- const context = textMeasureCanvas.getContext("2d");
19169
+ const context = textMeasureCanvas.getContext("2d", { alpha: false });
18992
19170
  if (!context) {
18993
19171
  return value.length * 7;
18994
19172
  }
18995
19173
  context.font = buildCanvasFont(style);
18996
- return context.measureText(value).width;
19174
+ return measureCanvasTextWidth(context, value);
18997
19175
  }
18998
19176
  function measureWrappedTextHeight(value, style, widthPx) {
18999
19177
  if (!value) {
@@ -19007,7 +19185,7 @@ function measureWrappedTextHeight(value, style, widthPx) {
19007
19185
  return fallbackLineCount * lineHeight + padding.top + padding.bottom;
19008
19186
  }
19009
19187
  textMeasureCanvas ??= document.createElement("canvas");
19010
- const context = textMeasureCanvas.getContext("2d");
19188
+ const context = textMeasureCanvas.getContext("2d", { alpha: false });
19011
19189
  if (!context) {
19012
19190
  return fallbackLineCount * lineHeight + padding.top + padding.bottom;
19013
19191
  }
@@ -20105,7 +20283,7 @@ function blitCanvasWithScrollDelta(canvas, context, bufferCanvas, dpr, width, he
20105
20283
  if (bufferCanvas.height !== deviceHeight) {
20106
20284
  bufferCanvas.height = deviceHeight;
20107
20285
  }
20108
- const bufferContext = bufferCanvas.getContext("2d");
20286
+ const bufferContext = bufferCanvas.getContext("2d", { alpha: false });
20109
20287
  if (!bufferContext) {
20110
20288
  return null;
20111
20289
  }
@@ -20956,12 +21134,14 @@ function XlsxGrid({
20956
21134
  renderImageSelection,
20957
21135
  renderTableHeaderMenu,
20958
21136
  enableGestureZoom = true,
20959
- experimentalCanvas = false,
21137
+ experimentalCanvas = true,
20960
21138
  selectionColor,
20961
21139
  selectionFillColor,
20962
21140
  selectionHeaderColor,
20963
21141
  showImages = true
20964
21142
  }) {
21143
+ const xlsxCanvasProfileTarget = typeof window !== "undefined" ? window.__xlsxCanvasProfile : void 0;
21144
+ const xlsxGridRenderStart = xlsxCanvasProfileTarget ? performance.now() : 0;
20965
21145
  const {
20966
21146
  activeCell,
20967
21147
  activeSheet,
@@ -21045,6 +21225,11 @@ function XlsxGrid({
21045
21225
  const leftFrozenHeaderCanvasRef = React4.useRef(null);
21046
21226
  const leftScrollHeaderCanvasRef = React4.useRef(null);
21047
21227
  const cornerHeaderCanvasRef = React4.useRef(null);
21228
+ const canvasScrollOverlayContentRef = React4.useRef(null);
21229
+ const canvasTopOverlayContentRef = React4.useRef(null);
21230
+ const canvasLeftOverlayContentRef = React4.useRef(null);
21231
+ const canvasCornerOverlayContentRef = React4.useRef(null);
21232
+ const canvasImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
21048
21233
  const selectionOverlayRef = React4.useRef(null);
21049
21234
  const activeValidationOverlayRef = React4.useRef(null);
21050
21235
  const fillHandleRef = React4.useRef(null);
@@ -21073,6 +21258,7 @@ function XlsxGrid({
21073
21258
  const pendingLiveZoomCommitRef = React4.useRef(null);
21074
21259
  const touchPinchStateRef = React4.useRef(null);
21075
21260
  const safariPinchStartZoomRef = React4.useRef(null);
21261
+ const canvasTransformStyleCacheRef = React4.useRef(/* @__PURE__ */ new WeakMap());
21076
21262
  const displayedSelectionRef = React4.useRef(null);
21077
21263
  const firstVisibleColRef = React4.useRef(void 0);
21078
21264
  const lastVisibleColRef = React4.useRef(void 0);
@@ -21098,12 +21284,19 @@ function XlsxGrid({
21098
21284
  const [fillPreviewRange, setFillPreviewRange] = React4.useState(null);
21099
21285
  const [chartPreviewRect, setChartPreviewRect] = React4.useState(null);
21100
21286
  const [liveGestureZoom, setLiveGestureZoom] = React4.useState(null);
21287
+ const [canvasImageLoadVersion, setCanvasImageLoadVersion] = React4.useState(0);
21101
21288
  const liveDrawingViewportRef = React4.useRef({
21102
21289
  height: 0,
21103
21290
  left: 0,
21104
21291
  top: 0,
21105
21292
  width: 0
21106
21293
  });
21294
+ const canvasLayoutMetricsRef = React4.useRef({
21295
+ displayHeaderHeight: HEADER_HEIGHT,
21296
+ displayRowHeaderWidth: ROW_HEADER_WIDTH,
21297
+ frozenPaneBottom: HEADER_HEIGHT,
21298
+ frozenPaneRight: ROW_HEADER_WIDTH
21299
+ });
21107
21300
  const paintedDrawingViewportRef = React4.useRef({
21108
21301
  height: 0,
21109
21302
  left: 0,
@@ -21213,6 +21406,12 @@ function XlsxGrid({
21213
21406
  const applyCanvasViewportCompensation = React4.useCallback((liveViewport) => {
21214
21407
  const nextLiveViewport = liveViewport ?? liveDrawingViewportRef.current;
21215
21408
  const paintedViewport = paintedDrawingViewportRef.current;
21409
+ const {
21410
+ displayHeaderHeight: liveDisplayHeaderHeight,
21411
+ displayRowHeaderWidth: liveDisplayRowHeaderWidth,
21412
+ frozenPaneBottom: liveFrozenPaneBottom,
21413
+ frozenPaneRight: liveFrozenPaneRight
21414
+ } = canvasLayoutMetricsRef.current;
21216
21415
  const currentLiveGestureZoom = liveGestureZoomRef.current;
21217
21416
  const isLiveZooming2 = currentLiveGestureZoom !== null && zoomScale === currentLiveGestureZoom.baseZoomScale;
21218
21417
  const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
@@ -21220,12 +21419,41 @@ function XlsxGrid({
21220
21419
  const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
21221
21420
  const liveZoomTranslateX2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.x * (1 - liveZoomScale2) : 0;
21222
21421
  const liveZoomTranslateY2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.y * (1 - liveZoomScale2) : 0;
21223
- const applyCanvasTransform = (canvas, translateX, translateY) => {
21224
- if (!canvas) {
21422
+ const applyElementTransform = (element, transform, willChange, transformOrigin = null) => {
21423
+ if (!element) {
21225
21424
  return;
21226
21425
  }
21227
- canvas.style.transform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1 ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : "";
21228
- canvas.style.willChange = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1 ? "transform" : "";
21426
+ const cached = canvasTransformStyleCacheRef.current.get(element);
21427
+ if (cached?.transform !== transform || element.style.transform !== transform) {
21428
+ element.style.transform = transform;
21429
+ }
21430
+ if (cached?.willChange !== willChange || element.style.willChange !== willChange) {
21431
+ element.style.willChange = willChange;
21432
+ }
21433
+ if (transformOrigin !== null && (cached?.transformOrigin !== transformOrigin || element.style.transformOrigin !== transformOrigin)) {
21434
+ element.style.transformOrigin = transformOrigin;
21435
+ }
21436
+ canvasTransformStyleCacheRef.current.set(element, {
21437
+ transform,
21438
+ transformOrigin,
21439
+ willChange
21440
+ });
21441
+ };
21442
+ const applyCanvasTransform = (canvas, translateX, translateY) => {
21443
+ const shouldTransform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1;
21444
+ applyElementTransform(
21445
+ canvas,
21446
+ shouldTransform ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : "",
21447
+ shouldTransform ? "transform" : ""
21448
+ );
21449
+ };
21450
+ const applyOverlayTransform = (element, translateX, translateY) => {
21451
+ applyElementTransform(
21452
+ element,
21453
+ liveZoomScale2 !== 1 ? `translate3d(${translateX + liveZoomTranslateX2}px, ${translateY + liveZoomTranslateY2}px, 0) scale(${liveZoomScale2})` : `translate3d(${translateX}px, ${translateY}px, 0)`,
21454
+ "transform",
21455
+ "0 0"
21456
+ );
21229
21457
  };
21230
21458
  applyCanvasTransform(
21231
21459
  scrollBodyCanvasRef.current,
@@ -21272,6 +21500,26 @@ function XlsxGrid({
21272
21500
  liveZoomTranslateX2,
21273
21501
  liveZoomTranslateY2
21274
21502
  );
21503
+ applyOverlayTransform(
21504
+ canvasScrollOverlayContentRef.current,
21505
+ -nextLiveViewport.left - liveFrozenPaneRight,
21506
+ -nextLiveViewport.top - liveFrozenPaneBottom
21507
+ );
21508
+ applyOverlayTransform(
21509
+ canvasTopOverlayContentRef.current,
21510
+ -nextLiveViewport.left - liveFrozenPaneRight,
21511
+ -liveDisplayHeaderHeight
21512
+ );
21513
+ applyOverlayTransform(
21514
+ canvasLeftOverlayContentRef.current,
21515
+ -liveDisplayRowHeaderWidth,
21516
+ -nextLiveViewport.top - liveFrozenPaneBottom
21517
+ );
21518
+ applyOverlayTransform(
21519
+ canvasCornerOverlayContentRef.current,
21520
+ -liveDisplayRowHeaderWidth,
21521
+ -liveDisplayHeaderHeight
21522
+ );
21275
21523
  }, [zoomScale]);
21276
21524
  const updateLiveGestureZoomState = React4.useCallback((nextState) => {
21277
21525
  const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
@@ -21374,6 +21622,11 @@ function XlsxGrid({
21374
21622
  if (matchesLiveViewport && matchesStateViewport) {
21375
21623
  return;
21376
21624
  }
21625
+ 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);
21626
+ if (!shouldCommitDeferredViewport) {
21627
+ pendingDrawingViewportRef.current = null;
21628
+ return;
21629
+ }
21377
21630
  pendingDrawingViewportRef.current = nextViewport;
21378
21631
  if (drawingViewportFrameRef.current !== null) {
21379
21632
  return;
@@ -21661,6 +21914,12 @@ function XlsxGrid({
21661
21914
  ) : displayRowHeaderWidth,
21662
21915
  [displayActualColWidths, displayDefaultColWidth, displayRowHeaderWidth, frozenCols, stickyLeftByCol]
21663
21916
  );
21917
+ canvasLayoutMetricsRef.current = {
21918
+ displayHeaderHeight,
21919
+ displayRowHeaderWidth,
21920
+ frozenPaneBottom,
21921
+ frozenPaneRight
21922
+ };
21664
21923
  const rowPrefixSumsRef = React4.useRef(rowPrefixSums);
21665
21924
  const colPrefixSumsRef = React4.useRef(colPrefixSums);
21666
21925
  const firstVisibleRow = visibleRows[0];
@@ -21712,6 +21971,7 @@ function XlsxGrid({
21712
21971
  }, [charts, images, shapes]);
21713
21972
  const shouldVirtualizeRows = visibleRows.length > 0;
21714
21973
  const shouldVirtualizeCols = visibleCols.length > 0 && frozenCols.length === 0;
21974
+ const shouldUseDomVirtualizer = !experimentalCanvas;
21715
21975
  const getScrollElement = React4.useCallback(() => scrollRef.current, []);
21716
21976
  const estimateRowSize = React4.useCallback(
21717
21977
  (index) => displayEffectiveRowHeights[index] ?? displayDefaultRowHeight,
@@ -21725,6 +21985,7 @@ function XlsxGrid({
21725
21985
  const getColItemKey = React4.useCallback((index) => visibleCols[index] ?? index, [visibleCols]);
21726
21986
  const rowVirtualizer = useVirtualizer({
21727
21987
  count: visibleRows.length,
21988
+ enabled: shouldUseDomVirtualizer,
21728
21989
  estimateSize: estimateRowSize,
21729
21990
  getScrollElement,
21730
21991
  getItemKey: getRowItemKey,
@@ -21732,14 +21993,15 @@ function XlsxGrid({
21732
21993
  });
21733
21994
  const colVirtualizer = useVirtualizer({
21734
21995
  count: visibleCols.length,
21996
+ enabled: shouldUseDomVirtualizer,
21735
21997
  estimateSize: estimateColSize,
21736
21998
  getScrollElement,
21737
21999
  getItemKey: getColItemKey,
21738
22000
  horizontal: true,
21739
22001
  overscan: 6
21740
22002
  });
21741
- const currentRowVirtualItems = rowVirtualizer.getVirtualItems();
21742
- const currentColVirtualItems = colVirtualizer.getVirtualItems();
22003
+ const currentRowVirtualItems = shouldUseDomVirtualizer ? rowVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;
22004
+ const currentColVirtualItems = shouldUseDomVirtualizer ? colVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;
21743
22005
  const frozenRowVirtualIndices = React4.useMemo(
21744
22006
  () => frozenRows.map((row) => rowIndexByActual.get(row)).filter((index) => index !== void 0),
21745
22007
  [frozenRows, rowIndexByActual]
@@ -21975,10 +22237,12 @@ function XlsxGrid({
21975
22237
  scroller.scrollTop = scroller.scrollTop / previousZoomFactor * zoomFactor;
21976
22238
  }
21977
22239
  previousZoomFactorRef.current = zoomFactor;
21978
- rowVirtualizer.measure();
21979
- colVirtualizer.measure();
22240
+ if (shouldUseDomVirtualizer) {
22241
+ rowVirtualizer.measure();
22242
+ colVirtualizer.measure();
22243
+ }
21980
22244
  syncDrawingViewport(scroller, { immediate: true });
21981
- }, [syncDrawingViewport, zoomFactor]);
22245
+ }, [shouldUseDomVirtualizer, syncDrawingViewport, zoomFactor]);
21982
22246
  React4.useLayoutEffect(() => {
21983
22247
  syncDrawingViewport(scrollRef.current, { immediate: true });
21984
22248
  }, [activeSheet, activeTabIndex, displayColLimit, displayRowLimit, syncDrawingViewport, zoomFactor]);
@@ -22074,7 +22338,7 @@ function XlsxGrid({
22074
22338
  const initialUsedCol = resolveFirstUsedVisibleIndex(visibleCols, activeSheet?.minUsedCol ?? -1);
22075
22339
  const initialScrollTop = initialUsedRow > 0 ? sumPrefixRange(actualRowPrefixSums, 0, initialUsedRow - 1) : 0;
22076
22340
  const initialScrollLeft = initialUsedCol > 0 ? sumPrefixRange(actualColPrefixSums, 0, initialUsedCol - 1) : 0;
22077
- if (shouldVirtualizeRows) {
22341
+ if (shouldUseDomVirtualizer && shouldVirtualizeRows) {
22078
22342
  rowVirtualizer.scrollToOffset(initialScrollTop);
22079
22343
  } else if (scrollRef.current) {
22080
22344
  scrollRef.current.scrollTop = initialScrollTop;
@@ -22082,7 +22346,7 @@ function XlsxGrid({
22082
22346
  if (scrollRef.current) {
22083
22347
  scrollRef.current.scrollLeft = initialScrollLeft;
22084
22348
  }
22085
- if (shouldVirtualizeCols && frozenCols.length === 0) {
22349
+ if (shouldUseDomVirtualizer && shouldVirtualizeCols && frozenCols.length === 0) {
22086
22350
  colVirtualizer.scrollToOffset(initialScrollLeft);
22087
22351
  }
22088
22352
  if (scrollRef.current) {
@@ -22103,6 +22367,7 @@ function XlsxGrid({
22103
22367
  isWorkerBacked,
22104
22368
  shouldVirtualizeCols,
22105
22369
  shouldVirtualizeRows,
22370
+ shouldUseDomVirtualizer,
22106
22371
  syncDrawingViewport,
22107
22372
  rowVirtualizer,
22108
22373
  visibleCols,
@@ -22122,24 +22387,40 @@ function XlsxGrid({
22122
22387
  setPendingNavigation(null);
22123
22388
  }, [activeSheetIndex, pendingNavigation, selectCell]);
22124
22389
  React4.useEffect(() => {
22125
- if (shouldVirtualizeRows) {
22390
+ if (shouldUseDomVirtualizer && shouldVirtualizeRows) {
22126
22391
  rowVirtualizer.measure();
22127
22392
  }
22128
- if (shouldVirtualizeCols) {
22393
+ if (shouldUseDomVirtualizer && shouldVirtualizeCols) {
22129
22394
  colVirtualizer.measure();
22130
22395
  }
22131
- }, [activeSheetIndex, revision, shouldVirtualizeCols, shouldVirtualizeRows, visibleCols.length, visibleRows.length]);
22396
+ }, [
22397
+ activeSheetIndex,
22398
+ revision,
22399
+ shouldUseDomVirtualizer,
22400
+ shouldVirtualizeCols,
22401
+ shouldVirtualizeRows,
22402
+ visibleCols.length,
22403
+ visibleRows.length
22404
+ ]);
22132
22405
  const handleScrollerScroll = React4.useCallback((event) => {
22133
22406
  const currentScroller = event.currentTarget;
22134
22407
  cachedScrollerRectRef.current = null;
22135
- syncDrawingViewport(currentScroller, { immediate: true });
22408
+ const paintedViewport = paintedDrawingViewportRef.current;
22409
+ 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;
22410
+ syncDrawingViewport(currentScroller, { immediate: shouldSyncDrawingViewportImmediately });
22136
22411
  if (currentScroller.scrollHeight - (currentScroller.scrollTop + currentScroller.clientHeight) < OPEN_GRID_VERTICAL_EDGE_PX) {
22137
- setDisplayRowLimit((current) => current + OPEN_GRID_ROW_GROWTH);
22412
+ setDisplayRowLimit((current) => {
22413
+ const nextLimit = current + OPEN_GRID_ROW_GROWTH;
22414
+ return readOnly && current < maxRowDisplayLimit ? Math.min(maxRowDisplayLimit, nextLimit) : nextLimit;
22415
+ });
22138
22416
  }
22139
22417
  if (currentScroller.scrollWidth - (currentScroller.scrollLeft + currentScroller.clientWidth) < OPEN_GRID_HORIZONTAL_EDGE_PX) {
22140
- setDisplayColLimit((current) => current + OPEN_GRID_COL_GROWTH);
22418
+ setDisplayColLimit((current) => {
22419
+ const nextLimit = current + OPEN_GRID_COL_GROWTH;
22420
+ return readOnly && current < maxColDisplayLimit ? Math.min(maxColDisplayLimit, nextLimit) : nextLimit;
22421
+ });
22141
22422
  }
22142
- }, [syncDrawingViewport]);
22423
+ }, [maxColDisplayLimit, maxRowDisplayLimit, readOnly, syncDrawingViewport]);
22143
22424
  React4.useEffect(() => {
22144
22425
  const scroller = scrollRef.current;
22145
22426
  if (!scroller || !enableGestureZoom) {
@@ -22396,17 +22677,17 @@ function XlsxGrid({
22396
22677
  }, []);
22397
22678
  const resolvePointerCellFromClient = React4.useCallback((clientX, clientY) => {
22398
22679
  const geometryCell = resolvePointerCellFromGeometry(clientX, clientY);
22399
- if (geometryCell && !worksheet?.isMergedSecondary(geometryCell.row, geometryCell.col)) {
22400
- return resolveMergeAnchorCell(geometryCell);
22680
+ if (geometryCell) {
22681
+ return geometryCell;
22401
22682
  }
22402
- const resolvedCell = resolvePointerCellFromHitTest(clientX, clientY) ?? geometryCell;
22683
+ const resolvedCell = resolvePointerCellFromHitTest(clientX, clientY);
22403
22684
  return resolvedCell ? resolveMergeAnchorCell(resolvedCell) : null;
22404
- }, [resolveMergeAnchorCell, resolvePointerCellFromGeometry, resolvePointerCellFromHitTest, worksheet]);
22685
+ }, [resolveMergeAnchorCell, resolvePointerCellFromGeometry, resolvePointerCellFromHitTest]);
22405
22686
  const resolveDraggedSelectionCell = React4.useCallback((dragState, clientX, clientY) => {
22406
22687
  const geometryCell = resolvePointerCellFromGeometry(clientX, clientY);
22407
22688
  const hitCell = resolvePointerCellFromHitTest(clientX, clientY);
22408
- const actualRow = hitCell && rowIndexByActual.has(hitCell.row) ? hitCell.row : geometryCell?.row;
22409
- const actualCol = hitCell?.col ?? geometryCell?.col;
22689
+ const actualRow = geometryCell?.row ?? (hitCell && rowIndexByActual.has(hitCell.row) ? hitCell.row : void 0);
22690
+ const actualCol = geometryCell?.col ?? hitCell?.col;
22410
22691
  if (actualRow === void 0 || actualCol === void 0) {
22411
22692
  return null;
22412
22693
  }
@@ -22416,8 +22697,8 @@ function XlsxGrid({
22416
22697
  if (dragState.axis === "column") {
22417
22698
  return { row: dragState.originCell.row, col: actualCol };
22418
22699
  }
22419
- return resolveMergeAnchorCell({ row: actualRow, col: actualCol });
22420
- }, [resolveMergeAnchorCell, resolvePointerCellFromGeometry, resolvePointerCellFromHitTest, rowIndexByActual]);
22700
+ return { row: actualRow, col: actualCol };
22701
+ }, [resolvePointerCellFromGeometry, resolvePointerCellFromHitTest, rowIndexByActual]);
22421
22702
  const resolveCellPointerOrigin = React4.useCallback((cell, rect, clientX, clientY) => {
22422
22703
  const rowIndex = rowIndexByActual.get(cell.row);
22423
22704
  const colIndex = colIndexByActual.get(cell.col);
@@ -22435,6 +22716,40 @@ function XlsxGrid({
22435
22716
  originContentY: (rowPrefixSums[rowIndex] ?? 0) + clampContentOffset((clientY - rect.top) / contentScaleY, displayHeight)
22436
22717
  };
22437
22718
  }, [colIndexByActual, colPrefixSums, displayDefaultColWidth, displayDefaultRowHeight, displayEffectiveColWidths, displayEffectiveRowHeights, rowIndexByActual, rowPrefixSums]);
22719
+ const resolveCellPointerOriginFromClient = React4.useCallback((cell, clientX, clientY) => {
22720
+ const scroller = scrollRef.current;
22721
+ const rowIndex = rowIndexByActual.get(cell.row);
22722
+ const colIndex = colIndexByActual.get(cell.col);
22723
+ if (!scroller || rowIndex === void 0 || colIndex === void 0) {
22724
+ return null;
22725
+ }
22726
+ const scrollerRect = cachedScrollerRectRef.current ?? scroller.getBoundingClientRect();
22727
+ const pointerOffsetX = clientX - scrollerRect.left;
22728
+ const pointerOffsetY = clientY - scrollerRect.top;
22729
+ const localX = pointerOffsetX + (pointerOffsetX >= frozenPaneRight ? scroller.scrollLeft : 0);
22730
+ const localY = pointerOffsetY + (pointerOffsetY >= frozenPaneBottom ? scroller.scrollTop : 0);
22731
+ const displayWidth = displayEffectiveColWidths[colIndex] ?? displayDefaultColWidth;
22732
+ const displayHeight = displayEffectiveRowHeights[rowIndex] ?? displayDefaultRowHeight;
22733
+ return {
22734
+ contentScaleX: 1,
22735
+ contentScaleY: 1,
22736
+ originContentX: (colPrefixSums[colIndex] ?? 0) + clampContentOffset(localX - displayRowHeaderWidth - (colPrefixSums[colIndex] ?? 0), displayWidth),
22737
+ originContentY: (rowPrefixSums[rowIndex] ?? 0) + clampContentOffset(localY - displayHeaderHeight - (rowPrefixSums[rowIndex] ?? 0), displayHeight)
22738
+ };
22739
+ }, [
22740
+ colIndexByActual,
22741
+ colPrefixSums,
22742
+ displayDefaultColWidth,
22743
+ displayDefaultRowHeight,
22744
+ displayEffectiveColWidths,
22745
+ displayEffectiveRowHeights,
22746
+ displayHeaderHeight,
22747
+ displayRowHeaderWidth,
22748
+ frozenPaneBottom,
22749
+ frozenPaneRight,
22750
+ rowIndexByActual,
22751
+ rowPrefixSums
22752
+ ]);
22438
22753
  const resolveRowPointerOrigin = React4.useCallback((actualRow, rect, clientY) => {
22439
22754
  const rowIndex = rowIndexByActual.get(actualRow);
22440
22755
  if (rowIndex === void 0) {
@@ -23011,14 +23326,14 @@ function XlsxGrid({
23011
23326
  },
23012
23327
  left: {
23013
23328
  cols: frozenColItems,
23014
- rows: frozenRows.length > 0 ? canvasVisibleRowItems : scrollRowItems
23329
+ rows: scrollRowItems
23015
23330
  },
23016
23331
  scroll: {
23017
- cols: frozenCols.length > 0 ? canvasVisibleColItems : scrollColItems,
23018
- rows: frozenRows.length > 0 ? canvasVisibleRowItems : scrollRowItems
23332
+ cols: scrollColItems,
23333
+ rows: scrollRowItems
23019
23334
  },
23020
23335
  top: {
23021
- cols: frozenCols.length > 0 ? canvasVisibleColItems : scrollColItems,
23336
+ cols: scrollColItems,
23022
23337
  rows: frozenRowItems
23023
23338
  }
23024
23339
  };
@@ -23152,6 +23467,51 @@ function XlsxGrid({
23152
23467
  visibleRows
23153
23468
  ]
23154
23469
  );
23470
+ const shouldBakeCanvasStaticDrawings = Boolean(experimentalCanvas && readOnly && showImages);
23471
+ const shouldBakeCanvasImages = Boolean(shouldBakeCanvasStaticDrawings && !renderImage && !renderImageSelection);
23472
+ const bakedShapeRects = React4.useMemo(
23473
+ () => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => !shape.hyperlink) : [],
23474
+ [shapeRects, shouldBakeCanvasStaticDrawings]
23475
+ );
23476
+ const domShapeRects = React4.useMemo(
23477
+ () => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => shape.hyperlink) : shapeRects,
23478
+ [shapeRects, shouldBakeCanvasStaticDrawings]
23479
+ );
23480
+ const bakedFormControlRects = React4.useMemo(
23481
+ () => shouldBakeCanvasStaticDrawings ? formControlRects : [],
23482
+ [formControlRects, shouldBakeCanvasStaticDrawings]
23483
+ );
23484
+ const domFormControlRects = React4.useMemo(
23485
+ () => shouldBakeCanvasStaticDrawings ? [] : formControlRects,
23486
+ [formControlRects, shouldBakeCanvasStaticDrawings]
23487
+ );
23488
+ const bakedImageRects = React4.useMemo(
23489
+ () => shouldBakeCanvasImages ? imageRects.filter(({ image }) => !image.hyperlink && selectedImageId !== image.id) : [],
23490
+ [imageRects, selectedImageId, shouldBakeCanvasImages]
23491
+ );
23492
+ const domImageRects = React4.useMemo(
23493
+ () => shouldBakeCanvasImages ? imageRects.filter(({ image }) => image.hyperlink || selectedImageId === image.id) : imageRects,
23494
+ [imageRects, selectedImageId, shouldBakeCanvasImages]
23495
+ );
23496
+ const hasCanvasDomDrawingOverlays = Boolean(
23497
+ showImages && (chartRects.length > 0 || domShapeRects.length > 0 || domFormControlRects.length > 0 || domImageRects.length > 0)
23498
+ );
23499
+ const bakedCanvasDrawingSignature = React4.useMemo(() => {
23500
+ if (!shouldBakeCanvasStaticDrawings) {
23501
+ return "";
23502
+ }
23503
+ 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("|");
23504
+ 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("|");
23505
+ 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("|");
23506
+ return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${controlSignature}:${imageSignature}`;
23507
+ }, [
23508
+ bakedFormControlRects,
23509
+ bakedImageRects,
23510
+ bakedShapeRects,
23511
+ canvasImageLoadVersion,
23512
+ revision,
23513
+ shouldBakeCanvasStaticDrawings
23514
+ ]);
23155
23515
  const resolveMountedCellOverlayRect = React4.useCallback((element) => {
23156
23516
  const wrapper = wrapperRef.current;
23157
23517
  if (!wrapper) {
@@ -23183,10 +23543,11 @@ function XlsxGrid({
23183
23543
  }, [resolveMountedCellOverlayRect]);
23184
23544
  const resolveGeometryOverlayRect = React4.useCallback((range) => {
23185
23545
  const normalized = normalizeRange2(range);
23186
- const startCell = resolveMergeAnchorCell(normalized.start);
23187
23546
  const isSingleCellSelection = normalized.start.row === normalized.end.row && normalized.start.col === normalized.end.col;
23188
- const merge = isSingleCellSelection ? worksheet?.getMergeSpan(startCell.row, startCell.col) : null;
23189
- const endCell = isSingleCellSelection ? {
23547
+ const isMergedSecondarySelection = isSingleCellSelection ? worksheet?.isMergedSecondary(normalized.start.row, normalized.start.col) === true : false;
23548
+ const startCell = isSingleCellSelection && !isMergedSecondarySelection ? resolveMergeAnchorCell(normalized.start) : normalized.start;
23549
+ const merge = isSingleCellSelection && !isMergedSecondarySelection ? worksheet?.getMergeSpan(startCell.row, startCell.col) : null;
23550
+ const endCell = isSingleCellSelection && !isMergedSecondarySelection ? {
23190
23551
  row: startCell.row + Math.max(1, merge?.rowSpan ?? 1) - 1,
23191
23552
  col: startCell.col + Math.max(1, merge?.colSpan ?? 1) - 1
23192
23553
  } : normalized.end;
@@ -23818,13 +24179,14 @@ function XlsxGrid({
23818
24179
  }
23819
24180
  event.preventDefault();
23820
24181
  focusGrid();
24182
+ const targetCell = event.currentTarget.colSpan > 1 || event.currentTarget.rowSpan > 1 ? resolvePointerCellFromGeometry(event.clientX, event.clientY) ?? cell : cell;
23821
24183
  const currentSelection = selectionRef.current;
23822
- const anchor = event.shiftKey && currentSelection ? currentSelection.start : cell;
23823
- const initialRange = normalizeRange2({ start: anchor, end: cell });
23824
- const isActive = isSameCell(activeCellRef.current, cell);
24184
+ const anchor = event.shiftKey && currentSelection ? currentSelection.start : targetCell;
24185
+ const initialRange = normalizeRange2({ start: anchor, end: targetCell });
24186
+ const isActive = isSameCell(activeCellRef.current, targetCell);
23825
24187
  const committedOnPointerDown = !isActive || !editingCellRef.current;
23826
- const pointerOrigin = resolveCellPointerOrigin(cell, event.currentTarget.getBoundingClientRect(), event.clientX, event.clientY);
23827
- const originOverlayRect = resolveMountedCellOverlayRect(event.currentTarget);
24188
+ 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);
24189
+ const originOverlayRect = targetCell.row === cell.row && targetCell.col === cell.col ? resolveMountedCellOverlayRect(event.currentTarget) : resolveOverlayRect(initialRange);
23828
24190
  if (!pointerOrigin) {
23829
24191
  return;
23830
24192
  }
@@ -23832,7 +24194,7 @@ function XlsxGrid({
23832
24194
  event.pointerId,
23833
24195
  anchor,
23834
24196
  "cell",
23835
- cell,
24197
+ targetCell,
23836
24198
  pointerOrigin,
23837
24199
  originOverlayRect,
23838
24200
  committedOnPointerDown,
@@ -23840,11 +24202,25 @@ function XlsxGrid({
23840
24202
  event.clientX,
23841
24203
  event.clientY
23842
24204
  );
23843
- applyPreviewOverlayFromElement(event.currentTarget, initialRange);
24205
+ if (targetCell.row === cell.row && targetCell.col === cell.col) {
24206
+ applyPreviewOverlayFromElement(event.currentTarget, initialRange);
24207
+ } else {
24208
+ applyPreviewOverlay(initialRange);
24209
+ }
23844
24210
  if (committedOnPointerDown) {
23845
24211
  commitSelectionRange(initialRange);
23846
24212
  }
23847
- }, [applyPreviewOverlayFromElement, commitSelectionRange, focusGrid, resolveCellPointerOrigin, resolveMountedCellOverlayRect]);
24213
+ }, [
24214
+ applyPreviewOverlay,
24215
+ applyPreviewOverlayFromElement,
24216
+ commitSelectionRange,
24217
+ focusGrid,
24218
+ resolveCellPointerOrigin,
24219
+ resolveCellPointerOriginFromClient,
24220
+ resolveMountedCellOverlayRect,
24221
+ resolveOverlayRect,
24222
+ resolvePointerCellFromGeometry
24223
+ ]);
23848
24224
  const handleRowPointerDown = React4.useCallback((event, actualRow) => {
23849
24225
  if (event.button !== 0 || firstVisibleCol === void 0 || lastVisibleCol === void 0) {
23850
24226
  return;
@@ -24004,6 +24380,24 @@ function XlsxGrid({
24004
24380
  rowPrefixSums,
24005
24381
  stickyTopByRow
24006
24382
  ]);
24383
+ const scrollBodyViewportWidth = Math.max(0, drawingViewport.width - frozenPaneRight);
24384
+ const scrollBodyViewportHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);
24385
+ const canvasScrollBufferX = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportWidth);
24386
+ const canvasScrollBufferY = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportHeight);
24387
+ const scrollBodyCanvasLeft = frozenPaneRight - canvasScrollBufferX;
24388
+ const scrollBodyCanvasTop = frozenPaneBottom - canvasScrollBufferY;
24389
+ const scrollBodyCanvasWidth = scrollBodyViewportWidth + canvasScrollBufferX * 2;
24390
+ const scrollBodyCanvasHeight = scrollBodyViewportHeight + canvasScrollBufferY * 2;
24391
+ const topBodyCanvasWidth = scrollBodyCanvasWidth;
24392
+ const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);
24393
+ const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
24394
+ const leftBodyCanvasHeight = scrollBodyCanvasHeight;
24395
+ const cornerBodyCanvasWidth = leftBodyCanvasWidth;
24396
+ const cornerBodyCanvasHeight = topBodyCanvasHeight;
24397
+ const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
24398
+ const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
24399
+ const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
24400
+ const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
24007
24401
  const canvasColumnHeaderCells = React4.useMemo(
24008
24402
  () => canvasVisibleColItems.flatMap((column) => {
24009
24403
  const rect = resolveCanvasColumnHeaderRect(column.actualCol);
@@ -24016,7 +24410,7 @@ function XlsxGrid({
24016
24410
  height: displayHeaderHeight,
24017
24411
  isFrozen,
24018
24412
  left: rect.left,
24019
- localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : frozenPaneRight),
24413
+ localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : scrollBodyCanvasLeft),
24020
24414
  width: rect.width
24021
24415
  }];
24022
24416
  }),
@@ -24024,8 +24418,8 @@ function XlsxGrid({
24024
24418
  canvasVisibleColItems,
24025
24419
  displayHeaderHeight,
24026
24420
  displayRowHeaderWidth,
24027
- frozenPaneRight,
24028
24421
  resolveCanvasColumnHeaderRect,
24422
+ scrollBodyCanvasLeft,
24029
24423
  stickyLeftByCol
24030
24424
  ]
24031
24425
  );
@@ -24040,15 +24434,15 @@ function XlsxGrid({
24040
24434
  actualRow: row.actualRow,
24041
24435
  height: rect.height,
24042
24436
  isFrozen,
24043
- localTop: rect.top - (isFrozen ? displayHeaderHeight : frozenPaneBottom),
24437
+ localTop: rect.top - (isFrozen ? displayHeaderHeight : scrollBodyCanvasTop),
24044
24438
  top: rect.top
24045
24439
  }];
24046
24440
  }),
24047
24441
  [
24048
24442
  canvasVisibleRowItems,
24049
24443
  displayHeaderHeight,
24050
- frozenPaneBottom,
24051
24444
  resolveCanvasRowHeaderRect,
24445
+ scrollBodyCanvasTop,
24052
24446
  stickyTopByRow
24053
24447
  ]
24054
24448
  );
@@ -24315,10 +24709,46 @@ function XlsxGrid({
24315
24709
  rowPrefixSums,
24316
24710
  startCellSelection
24317
24711
  ]);
24712
+ const getCanvasImage = React4.useCallback((image) => {
24713
+ if (typeof Image === "undefined") {
24714
+ return null;
24715
+ }
24716
+ const cacheKey = image.id;
24717
+ const cached = canvasImageCacheRef.current.get(cacheKey);
24718
+ if (cached && cached.src === image.src) {
24719
+ return cached.loaded && !cached.failed ? cached.image : null;
24720
+ }
24721
+ const imageElement = new Image();
24722
+ const entry = {
24723
+ failed: false,
24724
+ image: imageElement,
24725
+ loaded: false,
24726
+ src: image.src
24727
+ };
24728
+ canvasImageCacheRef.current.set(cacheKey, entry);
24729
+ imageElement.onload = () => {
24730
+ entry.loaded = true;
24731
+ setCanvasImageLoadVersion((version) => version + 1);
24732
+ };
24733
+ imageElement.onerror = () => {
24734
+ entry.failed = true;
24735
+ setCanvasImageLoadVersion((version) => version + 1);
24736
+ };
24737
+ imageElement.src = image.src;
24738
+ return null;
24739
+ }, []);
24318
24740
  React4.useLayoutEffect(() => {
24319
24741
  if (!experimentalCanvas) {
24320
24742
  return;
24321
24743
  }
24744
+ const canvasProfileTarget = typeof window !== "undefined" ? window.__xlsxCanvasProfile : void 0;
24745
+ const canvasProfileStart = canvasProfileTarget ? performance.now() : 0;
24746
+ let canvasProfileBodyStart = 0;
24747
+ let canvasProfileBodyMs = 0;
24748
+ let canvasProfileCulledCells = 0;
24749
+ let canvasProfileDirtyRects = 0;
24750
+ let canvasProfileLookedUpCells = 0;
24751
+ let canvasProfilePaintedCells = 0;
24322
24752
  const dpr = typeof window === "undefined" ? 1 : Math.max(1, window.devicePixelRatio || 1);
24323
24753
  function configureCanvas(canvas, width, height, options) {
24324
24754
  if (!canvas) {
@@ -24338,7 +24768,7 @@ function XlsxGrid({
24338
24768
  if (canvas.style.height !== `${height}px`) {
24339
24769
  canvas.style.height = `${height}px`;
24340
24770
  }
24341
- const context = canvas.getContext("2d");
24771
+ const context = canvas.getContext("2d", { alpha: false });
24342
24772
  if (!context) {
24343
24773
  return null;
24344
24774
  }
@@ -24357,6 +24787,7 @@ function XlsxGrid({
24357
24787
  const rangeSignature = buildRangeSignature(normalizedVisibleRange);
24358
24788
  const nextBodyCanvasSignature = {
24359
24789
  activeSheet: activeSheet ?? null,
24790
+ bakedDrawingSignature: bakedCanvasDrawingSignature,
24360
24791
  bodyHeight,
24361
24792
  bodyWidth,
24362
24793
  colSignature: buildRenderedAxisSignature(canvasVisibleColItems, (item) => item.index),
@@ -24384,10 +24815,10 @@ function XlsxGrid({
24384
24815
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
24385
24816
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
24386
24817
  const previousPaintedViewport = paintedDrawingViewportRef.current;
24387
- 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);
24818
+ 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);
24388
24819
  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);
24389
24820
  const canBlitBody = Boolean(
24390
- 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
24821
+ 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
24391
24822
  );
24392
24823
  const canBlitTopHeader = Boolean(
24393
24824
  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
@@ -24398,49 +24829,37 @@ function XlsxGrid({
24398
24829
  if (!shouldRepaintBody && !shouldRepaintHeaders) {
24399
24830
  return;
24400
24831
  }
24401
- const scrollBodyCanvasWidth2 = Math.max(0, drawingViewport.width - frozenPaneRight);
24402
- const scrollBodyCanvasHeight2 = Math.max(0, drawingViewport.height - frozenPaneBottom);
24403
- const topBodyCanvasWidth2 = scrollBodyCanvasWidth2;
24404
- const topBodyCanvasHeight2 = Math.max(0, frozenPaneBottom - displayHeaderHeight);
24405
- const leftBodyCanvasWidth2 = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
24406
- const leftBodyCanvasHeight2 = scrollBodyCanvasHeight2;
24407
- const cornerBodyCanvasWidth2 = leftBodyCanvasWidth2;
24408
- const cornerBodyCanvasHeight2 = topBodyCanvasHeight2;
24409
- const topFrozenHeaderCanvasWidth2 = leftBodyCanvasWidth2;
24410
- const topScrollHeaderCanvasWidth2 = scrollBodyCanvasWidth2;
24411
- const leftFrozenHeaderCanvasHeight2 = topBodyCanvasHeight2;
24412
- const leftScrollHeaderCanvasHeight2 = scrollBodyCanvasHeight2;
24413
24832
  const paneBounds = {
24414
24833
  corner: {
24415
- height: cornerBodyCanvasHeight2,
24834
+ height: cornerBodyCanvasHeight,
24416
24835
  left: displayRowHeaderWidth,
24417
24836
  top: displayHeaderHeight,
24418
- width: cornerBodyCanvasWidth2
24837
+ width: cornerBodyCanvasWidth
24419
24838
  },
24420
24839
  left: {
24421
- height: leftBodyCanvasHeight2,
24840
+ height: leftBodyCanvasHeight,
24422
24841
  left: displayRowHeaderWidth,
24423
- top: frozenPaneBottom,
24424
- width: leftBodyCanvasWidth2
24842
+ top: scrollBodyCanvasTop,
24843
+ width: leftBodyCanvasWidth
24425
24844
  },
24426
24845
  scroll: {
24427
- height: scrollBodyCanvasHeight2,
24428
- left: frozenPaneRight,
24429
- top: frozenPaneBottom,
24430
- width: scrollBodyCanvasWidth2
24846
+ height: scrollBodyCanvasHeight,
24847
+ left: scrollBodyCanvasLeft,
24848
+ top: scrollBodyCanvasTop,
24849
+ width: scrollBodyCanvasWidth
24431
24850
  },
24432
24851
  top: {
24433
- height: topBodyCanvasHeight2,
24434
- left: frozenPaneRight,
24852
+ height: topBodyCanvasHeight,
24853
+ left: scrollBodyCanvasLeft,
24435
24854
  top: displayHeaderHeight,
24436
- width: topBodyCanvasWidth2
24855
+ width: topBodyCanvasWidth
24437
24856
  }
24438
24857
  };
24439
24858
  const bodyContexts = shouldRepaintBody ? {
24440
- corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth2, cornerBodyCanvasHeight2, { clear: !canBlitBody }),
24441
- left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth2, leftBodyCanvasHeight2, { clear: !canBlitBody }),
24442
- scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth2, scrollBodyCanvasHeight2, { clear: !canBlitBody }),
24443
- top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth2, topBodyCanvasHeight2, { clear: !canBlitBody })
24859
+ corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth, cornerBodyCanvasHeight, { clear: !canBlitBody }),
24860
+ left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth, leftBodyCanvasHeight, { clear: !canBlitBody }),
24861
+ scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth, scrollBodyCanvasHeight, { clear: !canBlitBody }),
24862
+ top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth, topBodyCanvasHeight, { clear: !canBlitBody })
24444
24863
  } : {
24445
24864
  corner: null,
24446
24865
  left: null,
@@ -24448,21 +24867,21 @@ function XlsxGrid({
24448
24867
  top: null
24449
24868
  };
24450
24869
  const topHeaderContexts = shouldRepaintHeaders ? {
24451
- frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth2, headerHeight),
24452
- scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth2, headerHeight, { clear: !canBlitTopHeader })
24870
+ frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth, headerHeight),
24871
+ scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth, headerHeight, { clear: !canBlitTopHeader })
24453
24872
  } : {
24454
24873
  frozen: null,
24455
24874
  scroll: null
24456
24875
  };
24457
24876
  const leftHeaderContexts = shouldRepaintHeaders ? {
24458
- frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight2),
24459
- scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight2, { clear: !canBlitLeftHeader })
24877
+ frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight),
24878
+ scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight, { clear: !canBlitLeftHeader })
24460
24879
  } : {
24461
24880
  frozen: null,
24462
24881
  scroll: null
24463
24882
  };
24464
24883
  const cornerContext = shouldRepaintHeaders ? configureCanvas(cornerHeaderCanvasRef.current, rowHeaderWidth, headerHeight) : null;
24465
- 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)) {
24884
+ 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)) {
24466
24885
  return;
24467
24886
  }
24468
24887
  const showGridLines = activeSheet?.showGridLines ?? true;
@@ -24479,10 +24898,282 @@ function XlsxGrid({
24479
24898
  scroll: [],
24480
24899
  top: []
24481
24900
  };
24482
- let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth2, headerHeight);
24483
- let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight2);
24901
+ let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth, headerHeight);
24902
+ let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight);
24903
+ const bakedCanvasDrawingEntries = shouldBakeCanvasStaticDrawings ? [
24904
+ ...bakedShapeRects.map(({ rect, shape }) => ({
24905
+ kind: "shape",
24906
+ rect,
24907
+ shape,
24908
+ zIndex: shape.zIndex
24909
+ })),
24910
+ ...bakedFormControlRects.map(({ control, rect }) => ({
24911
+ control,
24912
+ kind: "formControl",
24913
+ rect,
24914
+ zIndex: control.zIndex
24915
+ })),
24916
+ ...bakedImageRects.map(({ image, rect }) => ({
24917
+ image,
24918
+ kind: "image",
24919
+ rect,
24920
+ zIndex: image.zIndex
24921
+ }))
24922
+ ].sort((left, right) => left.zIndex - right.zIndex) : [];
24923
+ const resolveBakedCanvasLocalRect = (rect, pane) => {
24924
+ const bounds = paneBounds[pane];
24925
+ const scrollX = pane === "scroll" || pane === "top" ? drawingViewport.left : 0;
24926
+ const scrollY = pane === "scroll" || pane === "left" ? drawingViewport.top : 0;
24927
+ return {
24928
+ height: rect.height,
24929
+ left: rect.left - scrollX - bounds.left,
24930
+ top: rect.top - scrollY - bounds.top,
24931
+ width: rect.width
24932
+ };
24933
+ };
24934
+ const drawBakedShapeText = (context, shape, left, top, width, height) => {
24935
+ if (shape.paragraphs.length === 0) {
24936
+ return;
24937
+ }
24938
+ const inset = shape.textBox?.insetPx;
24939
+ const paddingLeft = (inset?.left ?? 6) * zoomFactor;
24940
+ const paddingRight = (inset?.right ?? 6) * zoomFactor;
24941
+ const paddingTop = (inset?.top ?? 4) * zoomFactor;
24942
+ const paddingBottom = (inset?.bottom ?? 4) * zoomFactor;
24943
+ const textLeft = left + paddingLeft;
24944
+ const textTop = top + paddingTop;
24945
+ const textWidth = Math.max(0, width - paddingLeft - paddingRight);
24946
+ const textHeight = Math.max(0, height - paddingTop - paddingBottom);
24947
+ if (textWidth <= 0 || textHeight <= 0) {
24948
+ return;
24949
+ }
24950
+ const lineMetrics = shape.paragraphs.map((paragraph) => {
24951
+ const lineHeight = Math.max(
24952
+ 12 * zoomFactor,
24953
+ ...paragraph.runs.map((run) => (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor * 1.2)
24954
+ );
24955
+ const widthPx = paragraph.runs.reduce((total, run) => {
24956
+ const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
24957
+ context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
24958
+ return total + measureCanvasTextWidth(context, run.text);
24959
+ }, 0);
24960
+ return { lineHeight, widthPx };
24961
+ });
24962
+ const totalTextHeight = lineMetrics.reduce((total, metric) => total + metric.lineHeight, 0);
24963
+ let y = textTop;
24964
+ if (shape.textBox?.verticalAlign === "middle") {
24965
+ y = textTop + Math.max(0, (textHeight - totalTextHeight) / 2);
24966
+ } else if (shape.textBox?.verticalAlign === "bottom") {
24967
+ y = textTop + Math.max(0, textHeight - totalTextHeight);
24968
+ }
24969
+ context.save();
24970
+ context.beginPath();
24971
+ context.rect(textLeft, textTop, textWidth, textHeight);
24972
+ context.clip();
24973
+ context.textBaseline = "middle";
24974
+ shape.paragraphs.forEach((paragraph, paragraphIndex) => {
24975
+ const metric = lineMetrics[paragraphIndex] ?? { lineHeight: 14 * zoomFactor, widthPx: 0 };
24976
+ let x = textLeft;
24977
+ const align = paragraph.align ?? shape.textBox?.horizontalAlign ?? "left";
24978
+ if (align === "center") {
24979
+ x = textLeft + Math.max(0, (textWidth - metric.widthPx) / 2);
24980
+ } else if (align === "right") {
24981
+ x = textLeft + Math.max(0, textWidth - metric.widthPx);
24982
+ }
24983
+ paragraph.runs.forEach((run) => {
24984
+ const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
24985
+ context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
24986
+ context.fillStyle = run.color ?? "#000000";
24987
+ context.textAlign = "left";
24988
+ const textY = y + metric.lineHeight / 2;
24989
+ context.fillText(run.text, x, textY);
24990
+ if (run.underline && run.text.length > 0) {
24991
+ const textWidthPx = measureCanvasTextWidth(context, run.text);
24992
+ context.strokeStyle = run.color ?? "#000000";
24993
+ context.lineWidth = Math.max(1, zoomFactor * 0.75);
24994
+ context.beginPath();
24995
+ context.moveTo(x, textY + Math.max(2, fontSize * 0.28));
24996
+ context.lineTo(x + textWidthPx, textY + Math.max(2, fontSize * 0.28));
24997
+ context.stroke();
24998
+ }
24999
+ x += measureCanvasTextWidth(context, run.text);
25000
+ });
25001
+ y += metric.lineHeight;
25002
+ });
25003
+ context.restore();
25004
+ };
25005
+ const drawBakedShape = (context, shape, rect) => {
25006
+ const fillColor = shape.fill?.none ? "transparent" : shape.fill?.color ?? "transparent";
25007
+ const strokeColor = shape.stroke?.none ? "transparent" : shape.stroke?.color ?? "transparent";
25008
+ const lineWidth = Math.max(0, (shape.stroke?.widthPx ?? (shape.geometry === "line" ? 2 : 1)) * zoomFactor);
25009
+ const vectorShape = resolveShapeVector(shape);
25010
+ const opacity = Math.min(shape.fill?.opacity ?? 1, shape.stroke?.opacity ?? 1);
25011
+ context.save();
25012
+ context.translate(rect.left + rect.width / 2, rect.top + rect.height / 2);
25013
+ if (shape.rotationDeg) {
25014
+ context.rotate(shape.rotationDeg * Math.PI / 180);
25015
+ }
25016
+ context.scale(shape.flipH ? -1 : 1, shape.flipV ? -1 : 1);
25017
+ context.globalAlpha *= opacity;
25018
+ context.lineWidth = lineWidth;
25019
+ context.strokeStyle = strokeColor;
25020
+ context.fillStyle = fillColor;
25021
+ applyCanvasShapeDash(context, shape.stroke?.dash, lineWidth);
25022
+ const localLeft = -rect.width / 2;
25023
+ const localTop = -rect.height / 2;
25024
+ if (vectorShape && typeof Path2D !== "undefined") {
25025
+ context.save();
25026
+ context.translate(localLeft, localTop);
25027
+ context.scale(
25028
+ rect.width / Math.max(1, vectorShape.viewBox.width),
25029
+ rect.height / Math.max(1, vectorShape.viewBox.height)
25030
+ );
25031
+ const path = getCachedCanvasPath2D(vectorShape.path);
25032
+ if (!path) {
25033
+ context.restore();
25034
+ context.restore();
25035
+ return;
25036
+ }
25037
+ if (fillColor !== "transparent") {
25038
+ context.fill(path);
25039
+ }
25040
+ if (strokeColor !== "transparent" && lineWidth > 0) {
25041
+ context.stroke(path);
25042
+ }
25043
+ context.restore();
25044
+ } else if (shape.geometry === "ellipse") {
25045
+ context.beginPath();
25046
+ context.ellipse(0, 0, rect.width / 2, rect.height / 2, 0, 0, Math.PI * 2);
25047
+ if (fillColor !== "transparent") {
25048
+ context.fill();
25049
+ }
25050
+ if (strokeColor !== "transparent" && lineWidth > 0) {
25051
+ context.stroke();
25052
+ }
25053
+ } else {
25054
+ const radius = shape.geometry === "roundRect" ? 12 * zoomFactor : 0;
25055
+ drawCanvasRoundedRect(context, localLeft, localTop, rect.width, rect.height, radius);
25056
+ if (fillColor !== "transparent") {
25057
+ context.fill();
25058
+ }
25059
+ if (strokeColor !== "transparent" && lineWidth > 0) {
25060
+ context.stroke();
25061
+ }
25062
+ }
25063
+ drawBakedShapeText(context, shape, localLeft, localTop, rect.width, rect.height);
25064
+ context.restore();
25065
+ };
25066
+ const drawBakedFormControl = (context, control, rect) => {
25067
+ const label = resolveFormControlLabel(control);
25068
+ const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
25069
+ const textColor = control.textColor ?? "#000000";
25070
+ const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
25071
+ const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
25072
+ context.save();
25073
+ context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
25074
+ context.textBaseline = "middle";
25075
+ context.fillStyle = textColor;
25076
+ context.strokeStyle = stroke;
25077
+ context.lineWidth = Math.max(1, zoomFactor);
25078
+ if (control.kind === "group-box") {
25079
+ const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;
25080
+ drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);
25081
+ context.stroke();
25082
+ if (label) {
25083
+ context.fillStyle = SHEET_SURFACE;
25084
+ const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);
25085
+ context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);
25086
+ context.fillStyle = textColor;
25087
+ context.textAlign = "left";
25088
+ context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);
25089
+ }
25090
+ context.restore();
25091
+ return;
25092
+ }
25093
+ if (control.kind === "button" || control.kind === "dropdown" || control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
25094
+ if (control.kind === "button") {
25095
+ const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);
25096
+ gradient.addColorStop(0, "#f8fafc");
25097
+ gradient.addColorStop(1, "#e2e8f0");
25098
+ context.fillStyle = gradient;
25099
+ } else {
25100
+ context.fillStyle = "transparent";
25101
+ }
25102
+ drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === "button" ? 4 * zoomFactor : 2 * zoomFactor);
25103
+ if (control.kind === "button") {
25104
+ context.fill();
25105
+ }
25106
+ context.stroke();
25107
+ }
25108
+ let textLeft = rect.left + 2 * zoomFactor;
25109
+ const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
25110
+ if (control.kind === "checkbox") {
25111
+ context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
25112
+ if (control.checked) {
25113
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
25114
+ 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));
25115
+ context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
25116
+ context.beginPath();
25117
+ context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
25118
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
25119
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
25120
+ context.stroke();
25121
+ }
25122
+ textLeft += iconSize + 4 * zoomFactor;
25123
+ } else if (control.kind === "radio") {
25124
+ context.beginPath();
25125
+ context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
25126
+ context.stroke();
25127
+ if (control.checked) {
25128
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
25129
+ context.beginPath();
25130
+ context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
25131
+ context.fill();
25132
+ }
25133
+ textLeft += iconSize + 4 * zoomFactor;
25134
+ }
25135
+ if (label) {
25136
+ const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);
25137
+ const text = truncateCanvasText(context, label, maxTextWidth);
25138
+ context.fillStyle = textColor;
25139
+ context.textAlign = control.textAlign === "right" ? "right" : control.textAlign === "center" ? "center" : "left";
25140
+ const textX = context.textAlign === "right" ? rect.left + rect.width - textRightInset : context.textAlign === "center" ? textLeft + maxTextWidth / 2 : textLeft;
25141
+ context.fillText(text, textX, rect.top + rect.height / 2);
25142
+ }
25143
+ if (control.kind === "dropdown") {
25144
+ context.fillStyle = textColor;
25145
+ context.textAlign = "center";
25146
+ context.fillText("\u25BC", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);
25147
+ }
25148
+ context.restore();
25149
+ };
25150
+ const drawBakedCanvasDrawings = (pane, context, dirtyRects) => {
25151
+ if (bakedCanvasDrawingEntries.length === 0 || dirtyRects.length === 0) {
25152
+ return;
25153
+ }
25154
+ for (const entry of bakedCanvasDrawingEntries) {
25155
+ if (resolveDrawingPane(entry.rect) !== pane) {
25156
+ continue;
25157
+ }
25158
+ const localRect = resolveBakedCanvasLocalRect(entry.rect, pane);
25159
+ 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)) {
25160
+ continue;
25161
+ }
25162
+ if (entry.kind === "shape") {
25163
+ drawBakedShape(context, entry.shape, localRect);
25164
+ } else if (entry.kind === "formControl") {
25165
+ drawBakedFormControl(context, entry.control, localRect);
25166
+ } else {
25167
+ const imageElement = getCanvasImage(entry.image);
25168
+ if (imageElement) {
25169
+ context.drawImage(imageElement, localRect.left, localRect.top, localRect.width, localRect.height);
25170
+ }
25171
+ }
25172
+ }
25173
+ };
24484
25174
  const cellPaneOrder = ["scroll", "top", "left", "corner"];
24485
25175
  if (shouldRepaintBody) {
25176
+ canvasProfileBodyStart = canvasProfileTarget ? performance.now() : 0;
24486
25177
  for (const pane of Object.keys(bodyContexts)) {
24487
25178
  const context = bodyContexts[pane];
24488
25179
  const bounds = paneBounds[pane];
@@ -24513,6 +25204,7 @@ function XlsxGrid({
24513
25204
  context.clearRect(0, 0, bounds.width, bounds.height);
24514
25205
  }
24515
25206
  bodyDirtyRectsByPane[pane] = dirtyRects;
25207
+ canvasProfileDirtyRects += dirtyRects.length;
24516
25208
  if (dirtyRects.length === 0) {
24517
25209
  continue;
24518
25210
  }
@@ -24529,6 +25221,22 @@ function XlsxGrid({
24529
25221
  if (!paneContext || paneDirtyRects.length === 0 || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
24530
25222
  continue;
24531
25223
  }
25224
+ let hasPendingGridlinePath = false;
25225
+ const enqueueGridlinePath = () => {
25226
+ if (!hasPendingGridlinePath) {
25227
+ paneContext.beginPath();
25228
+ hasPendingGridlinePath = true;
25229
+ }
25230
+ };
25231
+ const flushPendingGridlines = () => {
25232
+ if (!hasPendingGridlinePath) {
25233
+ return;
25234
+ }
25235
+ paneContext.strokeStyle = palette.border;
25236
+ paneContext.lineWidth = 1;
25237
+ paneContext.stroke();
25238
+ hasPendingGridlinePath = false;
25239
+ };
24532
25240
  const drawnMergedAnchorKeys = /* @__PURE__ */ new Set();
24533
25241
  for (const rowItem of paneAxisItems.rows) {
24534
25242
  for (const colItem of paneAxisItems.cols) {
@@ -24536,6 +25244,33 @@ function XlsxGrid({
24536
25244
  const anchorCell = resolveMergeAnchorCell(cell);
24537
25245
  const anchorKey = `${anchorCell.row}:${anchorCell.col}`;
24538
25246
  let drawCell = anchorCell;
25247
+ const drawRowIndex = rowIndexByActual.get(drawCell.row);
25248
+ const drawColIndex = colIndexByActual.get(drawCell.col);
25249
+ if (drawRowIndex === void 0 || drawColIndex === void 0) {
25250
+ continue;
25251
+ }
25252
+ const baseCellLeft = displayRowHeaderWidth + (colPrefixSums[drawColIndex] ?? 0);
25253
+ const baseCellTop = displayHeaderHeight + (rowPrefixSums[drawRowIndex] ?? 0);
25254
+ const useFrozenHorizontalPosition = pane === "left" || pane === "corner";
25255
+ const useFrozenVerticalPosition = pane === "top" || pane === "corner";
25256
+ const roughLocalRect = {
25257
+ height: displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size,
25258
+ left: (useFrozenHorizontalPosition ? stickyLeftByCol.get(drawCell.col) ?? baseCellLeft - drawingViewport.left : baseCellLeft - drawingViewport.left) - paneBoundsForCell.left,
25259
+ top: (useFrozenVerticalPosition ? stickyTopByRow.get(drawCell.row) ?? baseCellTop - drawingViewport.top : baseCellTop - drawingViewport.top) - paneBoundsForCell.top,
25260
+ width: displayEffectiveColWidths[drawColIndex] ?? colItem.size
25261
+ };
25262
+ const isMergedSecondaryProbe = anchorCell.row !== cell.row || anchorCell.col !== cell.col;
25263
+ if (!isMergedSecondaryProbe && !intersectsCanvasDirtyRects(
25264
+ roughLocalRect.left - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,
25265
+ roughLocalRect.top - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,
25266
+ roughLocalRect.width + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,
25267
+ roughLocalRect.height + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,
25268
+ paneDirtyRects
25269
+ )) {
25270
+ canvasProfileCulledCells += 1;
25271
+ continue;
25272
+ }
25273
+ canvasProfileLookedUpCells += 1;
24539
25274
  let cellData = getCellData(drawCell.row, drawCell.col);
24540
25275
  if ((cellData.colSpan || cellData.rowSpan) && drawnMergedAnchorKeys.has(anchorKey)) {
24541
25276
  continue;
@@ -24546,16 +25281,9 @@ function XlsxGrid({
24546
25281
  if (cellData.colSpan || cellData.rowSpan) {
24547
25282
  drawnMergedAnchorKeys.add(anchorKey);
24548
25283
  }
24549
- const drawRowIndex = rowIndexByActual.get(drawCell.row);
24550
- const drawColIndex = colIndexByActual.get(drawCell.col);
24551
- if (drawRowIndex === void 0 || drawColIndex === void 0) {
24552
- continue;
24553
- }
24554
25284
  const displayRect = cellData.colSpan || cellData.rowSpan ? resolveCellDisplayRect(drawCell) : null;
24555
- const baseLeft = displayRect?.left ?? displayRowHeaderWidth + (colPrefixSums[drawColIndex] ?? 0);
24556
- const baseTop = displayRect?.top ?? displayHeaderHeight + (rowPrefixSums[drawRowIndex] ?? 0);
24557
- const useFrozenHorizontalPosition = pane === "left" || pane === "corner";
24558
- const useFrozenVerticalPosition = pane === "top" || pane === "corner";
25285
+ const baseLeft = displayRect?.left ?? baseCellLeft;
25286
+ const baseTop = displayRect?.top ?? baseCellTop;
24559
25287
  const localRect = {
24560
25288
  height: displayRect?.height ?? (displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size),
24561
25289
  left: (useFrozenHorizontalPosition ? stickyLeftByCol.get(drawCell.col) ?? baseLeft - drawingViewport.left : baseLeft - drawingViewport.left) - paneBoundsForCell.left,
@@ -24567,13 +25295,18 @@ function XlsxGrid({
24567
25295
  continue;
24568
25296
  }
24569
25297
  if (!intersectsCanvasDirtyRects(localRect.left, localRect.top, drawableWidth, localRect.height, paneDirtyRects)) {
25298
+ canvasProfileCulledCells += 1;
24570
25299
  continue;
24571
25300
  }
25301
+ canvasProfilePaintedCells += 1;
24572
25302
  const cellStyle = cellData.style;
24573
25303
  const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellStyle);
24574
25304
  const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : sheetSurface);
24575
25305
  const gradientFill = !cellData.conditionalColorScale && typeof cellStyle.backgroundImage === "string" ? resolveCanvasGradientFill(paneContext, localRect, cellStyle.backgroundImage) : null;
24576
25306
  const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== sheetSurface;
25307
+ if (hasExplicitCellFill || cellData.chartHighlight) {
25308
+ flushPendingGridlines();
25309
+ }
24577
25310
  paneContext.fillStyle = gradientFill ?? fillColor;
24578
25311
  paneContext.fillRect(localRect.left, localRect.top, localRect.width, localRect.height);
24579
25312
  if (cellData.chartHighlight) {
@@ -24619,10 +25352,8 @@ function XlsxGrid({
24619
25352
  const bottomNeighborTopBorder = bottomNeighborData?.isMergedSecondary ? null : bottomNeighborData?.canvas?.topBorder ?? parseCanvasBorderDeclaration(bottomNeighborData?.style.borderTop);
24620
25353
  const resolvedRightBorder = resolveCanvasBoundaryBorder(rightBorder, rightNeighborLeftBorder);
24621
25354
  const resolvedBottomBorder = resolveCanvasBoundaryBorder(bottomBorder, bottomNeighborTopBorder);
24622
- if (showGridLines && !hasExplicitCellFill) {
24623
- paneContext.strokeStyle = palette.border;
24624
- paneContext.lineWidth = 1;
24625
- paneContext.beginPath();
25355
+ if (showGridLines && !hasExplicitCellFill && (!resolvedRightBorder || !resolvedBottomBorder)) {
25356
+ enqueueGridlinePath();
24626
25357
  if (!resolvedRightBorder) {
24627
25358
  paneContext.moveTo(localRect.left + localRect.width - 0.5, localRect.top);
24628
25359
  paneContext.lineTo(localRect.left + localRect.width - 0.5, localRect.top + localRect.height);
@@ -24631,7 +25362,9 @@ function XlsxGrid({
24631
25362
  paneContext.moveTo(localRect.left, localRect.top + localRect.height - 0.5);
24632
25363
  paneContext.lineTo(localRect.left + localRect.width, localRect.top + localRect.height - 0.5);
24633
25364
  }
24634
- paneContext.stroke();
25365
+ }
25366
+ if (topBorder || resolvedRightBorder || resolvedBottomBorder || leftBorder || cellData.chartHighlight) {
25367
+ flushPendingGridlines();
24635
25368
  }
24636
25369
  if (topBorder && drawRowIndex === 0) {
24637
25370
  strokeCanvasBorderSide(paneContext, "top", localRect, topBorder);
@@ -24664,17 +25397,26 @@ function XlsxGrid({
24664
25397
  strokeCanvasBorderSide(paneContext, "left", localRect, highlightBorder);
24665
25398
  }
24666
25399
  }
25400
+ const rawText = cellData.value ?? "";
25401
+ const shouldDrawCanvasContent = cellData.checkboxState != null || cellData.sparkline || rawText.length > 0 || cellData.conditionalIcon || cellData.isTableHeader;
25402
+ if (!shouldDrawCanvasContent) {
25403
+ continue;
25404
+ }
24667
25405
  const padding = canvasCellStyle.padding;
24668
25406
  const contentLeft = localRect.left + padding.left;
24669
25407
  const contentTop = localRect.top + padding.top;
24670
25408
  const contentWidth = Math.max(0, localRect.width - padding.left - padding.right);
24671
25409
  const contentHeight = Math.max(0, localRect.height - padding.top - padding.bottom);
25410
+ if (contentWidth <= 0 || contentHeight <= 0) {
25411
+ continue;
25412
+ }
24672
25413
  const activeFontSizePx = cellData.shrinkToFitFontSizePx ?? resolveCanvasFontSizePx(cellStyle, 12 * zoomFactor);
24673
25414
  const textClipOverscan = Math.max(
24674
25415
  1,
24675
25416
  zoomFactor * 1.5,
24676
25417
  activeFontSizePx * (cellData.textRotationDeg ? 0.75 : 0.18)
24677
25418
  );
25419
+ flushPendingGridlines();
24678
25420
  paneContext.save();
24679
25421
  paneContext.beginPath();
24680
25422
  paneContext.rect(
@@ -24806,7 +25548,6 @@ function XlsxGrid({
24806
25548
  const trailingInset = (cellData.conditionalIcon ? 18 * zoomFactor : 0) + (cellData.isTableHeader ? 16 * zoomFactor : 0);
24807
25549
  const spillMaxWidth = cellData.spillWidth && cellData.spillWidth > 0 ? Math.max(0, cellData.spillWidth - trailingInset) : null;
24808
25550
  const maxTextWidth = spillMaxWidth ?? Math.max(0, contentWidth - trailingInset);
24809
- const rawText = cellData.value ?? "";
24810
25551
  const textColor = canvasCellStyle.textColor;
24811
25552
  const shouldEllipsizeText = canvasCellStyle.textOverflowEllipsis;
24812
25553
  const shouldWrapText = canvasCellStyle.usesWrappedText || rawText.includes("\n");
@@ -24838,7 +25579,7 @@ function XlsxGrid({
24838
25579
  const textY = textBlockTop + lineIndex * lineHeight + lineHeight / 2;
24839
25580
  paneContext.fillText(line, textX, textY);
24840
25581
  if (canvasCellStyle.textDecoration?.includes("underline") && line.length > 0) {
24841
- const measured = Math.min(maxTextWidth, paneContext.measureText(line).width);
25582
+ const measured = Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, line));
24842
25583
  const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
24843
25584
  paneContext.beginPath();
24844
25585
  paneContext.moveTo(underlineStartX, textY + Math.max(2, lineHeight * 0.24));
@@ -24872,7 +25613,7 @@ function XlsxGrid({
24872
25613
  const textY = contentTop + contentHeight / 2;
24873
25614
  paneContext.fillText(text, textX, textY);
24874
25615
  if (canvasCellStyle.textDecoration?.includes("underline") && text.length > 0) {
24875
- const measured = shouldEllipsizeText ? Math.min(maxTextWidth, paneContext.measureText(text).width) : paneContext.measureText(text).width;
25616
+ const measured = shouldEllipsizeText ? Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, text)) : measureCanvasTextWidth(paneContext, text);
24876
25617
  const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
24877
25618
  paneContext.beginPath();
24878
25619
  paneContext.moveTo(underlineStartX, textY + 6 * zoomFactor);
@@ -24897,6 +25638,7 @@ function XlsxGrid({
24897
25638
  paneContext.restore();
24898
25639
  }
24899
25640
  }
25641
+ flushPendingGridlines();
24900
25642
  }
24901
25643
  for (const pane of cellPaneOrder) {
24902
25644
  const paneContext = bodyContexts[pane];
@@ -24914,7 +25656,7 @@ function XlsxGrid({
24914
25656
  paneContext.textBaseline = "middle";
24915
25657
  paneContext.fillText(spillText.text, spillText.textX, spillText.textY);
24916
25658
  if (spillText.textDecoration?.includes("underline") && spillText.text.length > 0) {
24917
- const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, paneContext.measureText(spillText.text).width) : paneContext.measureText(spillText.text).width;
25659
+ const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, measureCanvasTextWidth(paneContext, spillText.text)) : measureCanvasTextWidth(paneContext, spillText.text);
24918
25660
  const underlineStartX = spillText.align === "right" ? spillText.textX - measured : spillText.align === "center" ? spillText.textX - measured / 2 : spillText.textX;
24919
25661
  paneContext.beginPath();
24920
25662
  paneContext.moveTo(underlineStartX, spillText.underlineY);
@@ -24926,6 +25668,14 @@ function XlsxGrid({
24926
25668
  paneContext.restore();
24927
25669
  }
24928
25670
  }
25671
+ for (const pane of cellPaneOrder) {
25672
+ const paneContext = bodyContexts[pane];
25673
+ if (!paneContext) {
25674
+ continue;
25675
+ }
25676
+ drawBakedCanvasDrawings(pane, paneContext, bodyDirtyRectsByPane[pane]);
25677
+ }
25678
+ canvasProfileBodyMs = canvasProfileTarget ? performance.now() - canvasProfileBodyStart : 0;
24929
25679
  }
24930
25680
  if (shouldRepaintHeaders && cornerContext) {
24931
25681
  const topFrozenHeaderContext = topHeaderContexts.frozen;
@@ -24940,7 +25690,7 @@ function XlsxGrid({
24940
25690
  topScrollHeaderContext,
24941
25691
  bufferCanvas,
24942
25692
  dpr,
24943
- topScrollHeaderCanvasWidth2,
25693
+ topScrollHeaderCanvasWidth,
24944
25694
  headerHeight,
24945
25695
  drawingViewport.left - previousPaintedViewport.left,
24946
25696
  0
@@ -24959,7 +25709,7 @@ function XlsxGrid({
24959
25709
  bufferCanvas,
24960
25710
  dpr,
24961
25711
  rowHeaderWidth,
24962
- leftScrollHeaderCanvasHeight2,
25712
+ leftScrollHeaderCanvasHeight,
24963
25713
  0,
24964
25714
  drawingViewport.top - previousPaintedViewport.top
24965
25715
  );
@@ -24968,11 +25718,11 @@ function XlsxGrid({
24968
25718
  }
24969
25719
  }
24970
25720
  }
24971
- if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth2 > 0) {
25721
+ if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth > 0) {
24972
25722
  topFrozenHeaderContext.fillStyle = palette.headerSurface;
24973
- topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth2, headerHeight);
25723
+ topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth, headerHeight);
24974
25724
  }
24975
- if (topScrollHeaderContext && topScrollHeaderCanvasWidth2 > 0) {
25725
+ if (topScrollHeaderContext && topScrollHeaderCanvasWidth > 0) {
24976
25726
  topScrollHeaderContext.fillStyle = palette.headerSurface;
24977
25727
  for (const dirtyRect of topScrollHeaderDirtyRects) {
24978
25728
  topScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
@@ -24991,7 +25741,7 @@ function XlsxGrid({
24991
25741
  if (!paneContext) {
24992
25742
  continue;
24993
25743
  }
24994
- if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth2 : topScrollHeaderCanvasWidth2)) {
25744
+ if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth : topScrollHeaderCanvasWidth)) {
24995
25745
  continue;
24996
25746
  }
24997
25747
  if (!column.isFrozen && !intersectsCanvasDirtyRects(column.localLeft, 0, column.width, column.height, topScrollHeaderDirtyRects)) {
@@ -25017,11 +25767,11 @@ function XlsxGrid({
25017
25767
  column.height / 2
25018
25768
  );
25019
25769
  }
25020
- if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight2 > 0) {
25770
+ if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight > 0) {
25021
25771
  leftFrozenHeaderContext.fillStyle = palette.rowHeaderSurface;
25022
- leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight2);
25772
+ leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight);
25023
25773
  }
25024
- if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight2 > 0) {
25774
+ if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight > 0) {
25025
25775
  leftScrollHeaderContext.fillStyle = palette.rowHeaderSurface;
25026
25776
  for (const dirtyRect of leftScrollHeaderDirtyRects) {
25027
25777
  leftScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
@@ -25040,7 +25790,7 @@ function XlsxGrid({
25040
25790
  if (!paneContext) {
25041
25791
  continue;
25042
25792
  }
25043
- if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight2 : leftScrollHeaderCanvasHeight2)) {
25793
+ if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight : leftScrollHeaderCanvasHeight)) {
25044
25794
  continue;
25045
25795
  }
25046
25796
  if (!row.isFrozen && !intersectsCanvasDirtyRects(0, row.localTop, rowHeaderWidth, row.height, leftScrollHeaderDirtyRects)) {
@@ -25080,9 +25830,29 @@ function XlsxGrid({
25080
25830
  paintedHeaderCanvasSignatureRef.current = nextHeaderCanvasSignature;
25081
25831
  }
25082
25832
  applyCanvasViewportCompensation();
25833
+ if (canvasProfileTarget) {
25834
+ canvasProfileTarget.push({
25835
+ bodyMs: canvasProfileBodyMs,
25836
+ culledCells: canvasProfileCulledCells,
25837
+ dirtyRects: canvasProfileDirtyRects,
25838
+ lookedUpCells: canvasProfileLookedUpCells,
25839
+ paintedCells: canvasProfilePaintedCells,
25840
+ repaintBody: shouldRepaintBody,
25841
+ repaintHeaders: shouldRepaintHeaders,
25842
+ renderToLayoutMs: canvasProfileStart - xlsxGridRenderStart,
25843
+ totalMs: performance.now() - canvasProfileStart
25844
+ });
25845
+ if (canvasProfileTarget.length > 500) {
25846
+ canvasProfileTarget.splice(0, canvasProfileTarget.length - 500);
25847
+ }
25848
+ }
25083
25849
  }, [
25084
25850
  activeSheet,
25085
25851
  applyCanvasViewportCompensation,
25852
+ bakedCanvasDrawingSignature,
25853
+ bakedFormControlRects,
25854
+ bakedImageRects,
25855
+ bakedShapeRects,
25086
25856
  canvasColumnHeaderCells,
25087
25857
  canvasPaneAxisItems,
25088
25858
  canvasRowHeaderCells,
@@ -25103,15 +25873,16 @@ function XlsxGrid({
25103
25873
  frozenPaneBottom,
25104
25874
  frozenPaneRight,
25105
25875
  getCellData,
25876
+ getCanvasImage,
25106
25877
  getBodyBlitBufferCanvas,
25107
25878
  getHeaderBlitBufferCanvas,
25108
25879
  palette,
25109
25880
  resolveCellDisplayRect,
25110
25881
  resolveMergeAnchorCell,
25111
- resizeGuide,
25112
25882
  rowIndexByActual,
25113
25883
  rowPrefixSums,
25114
25884
  selectionHeaderSurface,
25885
+ shouldBakeCanvasStaticDrawings,
25115
25886
  stickyLeftByCol,
25116
25887
  stickyTopByRow,
25117
25888
  visibleCols,
@@ -25329,7 +26100,7 @@ function XlsxGrid({
25329
26100
  start: virtualRow?.start ?? (rowPrefixSums[index] ?? 0)
25330
26101
  };
25331
26102
  });
25332
- const totalHeight = shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
26103
+ const totalHeight = shouldUseDomVirtualizer && shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
25333
26104
  const totalWidth = totalContentWidth + displayRowHeaderWidth;
25334
26105
  const sheetContentHeight = displayHeaderHeight + totalHeight;
25335
26106
  const isLiveZooming = liveGestureZoom !== null && zoomScale === liveGestureZoom.baseZoomScale;
@@ -25403,18 +26174,6 @@ function XlsxGrid({
25403
26174
  width: 0,
25404
26175
  zIndex: canvasHeaderOverlayZIndex
25405
26176
  };
25406
- const scrollBodyCanvasWidth = Math.max(0, drawingViewport.width - frozenPaneRight);
25407
- const scrollBodyCanvasHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);
25408
- const topBodyCanvasWidth = scrollBodyCanvasWidth;
25409
- const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);
25410
- const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
25411
- const leftBodyCanvasHeight = scrollBodyCanvasHeight;
25412
- const cornerBodyCanvasWidth = leftBodyCanvasWidth;
25413
- const cornerBodyCanvasHeight = topBodyCanvasHeight;
25414
- const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
25415
- const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
25416
- const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
25417
- const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
25418
26177
  const canvasBodyBaseStyle = {
25419
26178
  cursor: "cell",
25420
26179
  pointerEvents: "auto",
@@ -25426,13 +26185,13 @@ function XlsxGrid({
25426
26185
  const canvasScrollBodyStyle = {
25427
26186
  ...canvasBodyBaseStyle,
25428
26187
  display: scrollBodyCanvasWidth > 0 && scrollBodyCanvasHeight > 0 ? "block" : "none",
25429
- left: frozenPaneRight,
25430
- top: frozenPaneBottom
26188
+ left: scrollBodyCanvasLeft,
26189
+ top: scrollBodyCanvasTop
25431
26190
  };
25432
26191
  const canvasTopBodyStyle = {
25433
26192
  ...canvasBodyBaseStyle,
25434
26193
  display: topBodyCanvasWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
25435
- left: frozenPaneRight,
26194
+ left: scrollBodyCanvasLeft,
25436
26195
  top: displayHeaderHeight,
25437
26196
  zIndex: 30
25438
26197
  };
@@ -25440,7 +26199,7 @@ function XlsxGrid({
25440
26199
  ...canvasBodyBaseStyle,
25441
26200
  display: leftBodyCanvasWidth > 0 && leftBodyCanvasHeight > 0 ? "block" : "none",
25442
26201
  left: displayRowHeaderWidth,
25443
- top: frozenPaneBottom,
26202
+ top: scrollBodyCanvasTop,
25444
26203
  zIndex: 30
25445
26204
  };
25446
26205
  const canvasCornerBodyStyle = {
@@ -25466,7 +26225,7 @@ function XlsxGrid({
25466
26225
  const canvasTopScrollHeaderStyle = {
25467
26226
  ...canvasHeaderBaseStyle,
25468
26227
  display: topScrollHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
25469
- left: frozenPaneRight,
26228
+ left: scrollBodyCanvasLeft,
25470
26229
  top: 0,
25471
26230
  zIndex: canvasHeaderOverlayZIndex
25472
26231
  };
@@ -25481,7 +26240,7 @@ function XlsxGrid({
25481
26240
  ...canvasHeaderBaseStyle,
25482
26241
  display: leftScrollHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
25483
26242
  left: 0,
25484
- top: frozenPaneBottom,
26243
+ top: scrollBodyCanvasTop,
25485
26244
  zIndex: canvasHeaderOverlayZIndex
25486
26245
  };
25487
26246
  const canvasCornerHeaderStyle = {
@@ -26037,26 +26796,26 @@ function XlsxGrid({
26037
26796
  };
26038
26797
  const canvasScrollOverlayPaneStyle = {
26039
26798
  ...canvasOverlayPaneBaseStyle,
26040
- display: scrollBodyCanvasWidth > 0 && scrollBodyCanvasHeight > 0 ? "block" : "none",
26041
- height: scrollBodyCanvasHeight,
26799
+ display: scrollBodyViewportWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
26800
+ height: scrollBodyViewportHeight,
26042
26801
  left: frozenPaneRight,
26043
26802
  top: frozenPaneBottom,
26044
- width: scrollBodyCanvasWidth,
26803
+ width: scrollBodyViewportWidth,
26045
26804
  zIndex: 20
26046
26805
  };
26047
26806
  const canvasTopOverlayPaneStyle = {
26048
26807
  ...canvasOverlayPaneBaseStyle,
26049
- display: topBodyCanvasWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
26808
+ display: scrollBodyViewportWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
26050
26809
  height: topBodyCanvasHeight,
26051
26810
  left: frozenPaneRight,
26052
26811
  top: displayHeaderHeight,
26053
- width: topBodyCanvasWidth,
26812
+ width: scrollBodyViewportWidth,
26054
26813
  zIndex: 35
26055
26814
  };
26056
26815
  const canvasLeftOverlayPaneStyle = {
26057
26816
  ...canvasOverlayPaneBaseStyle,
26058
- display: leftBodyCanvasWidth > 0 && leftBodyCanvasHeight > 0 ? "block" : "none",
26059
- height: leftBodyCanvasHeight,
26817
+ display: leftBodyCanvasWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
26818
+ height: scrollBodyViewportHeight,
26060
26819
  left: displayRowHeaderWidth,
26061
26820
  top: frozenPaneBottom,
26062
26821
  width: leftBodyCanvasWidth,
@@ -26071,8 +26830,9 @@ function XlsxGrid({
26071
26830
  width: cornerBodyCanvasWidth,
26072
26831
  zIndex: 36
26073
26832
  };
26833
+ const drawingViewportCacheSignature = `${Math.floor(drawingViewport.left / CANVAS_VIEWPORT_OVERSCAN_PX)}:${Math.floor(drawingViewport.top / CANVAS_VIEWPORT_OVERSCAN_PX)}:${drawingViewport.width}:${drawingViewport.height}`;
26074
26834
  const previousPaneDrawingNodes = paneDrawingNodesCacheRef.current;
26075
- 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;
26835
+ 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;
26076
26836
  const paneDrawingNodes = canReusePaneDrawingNodes ? previousPaneDrawingNodes.value : !showImages ? {
26077
26837
  corner: null,
26078
26838
  left: null,
@@ -26081,35 +26841,35 @@ function XlsxGrid({
26081
26841
  } : {
26082
26842
  corner: /* @__PURE__ */ jsxs3(Fragment4, { children: [
26083
26843
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "corner")),
26084
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "corner")),
26085
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "corner")),
26086
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "corner"))
26844
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "corner")),
26845
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "corner")),
26846
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "corner"))
26087
26847
  ] }),
26088
26848
  left: /* @__PURE__ */ jsxs3(Fragment4, { children: [
26089
26849
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "left")),
26090
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "left")),
26091
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "left")),
26092
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "left"))
26850
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "left")),
26851
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "left")),
26852
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "left"))
26093
26853
  ] }),
26094
26854
  scroll: /* @__PURE__ */ jsxs3(Fragment4, { children: [
26095
26855
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "scroll")),
26096
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "scroll")),
26097
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "scroll")),
26098
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "scroll"))
26856
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "scroll")),
26857
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "scroll")),
26858
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "scroll"))
26099
26859
  ] }),
26100
26860
  top: /* @__PURE__ */ jsxs3(Fragment4, { children: [
26101
26861
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "top")),
26102
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "top")),
26103
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "top")),
26104
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "top"))
26862
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "top")),
26863
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "top")),
26864
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "top"))
26105
26865
  ] })
26106
26866
  };
26107
26867
  if (!canReusePaneDrawingNodes) {
26108
26868
  paneDrawingNodesCacheRef.current = {
26109
26869
  chartRects,
26110
- drawingViewport,
26111
- formControlRects,
26112
- imageRects,
26870
+ drawingViewportSignature: drawingViewportCacheSignature,
26871
+ formControlRects: domFormControlRects,
26872
+ imageRects: domImageRects,
26113
26873
  isChartsLoading,
26114
26874
  palette,
26115
26875
  readOnly,
@@ -26119,7 +26879,7 @@ function XlsxGrid({
26119
26879
  selectedChartId,
26120
26880
  selectedImageId,
26121
26881
  selectionStroke,
26122
- shapeRects,
26882
+ shapeRects: domShapeRects,
26123
26883
  showImages,
26124
26884
  value: paneDrawingNodes
26125
26885
  };
@@ -26627,10 +27387,11 @@ function XlsxGrid({
26627
27387
  style: canvasCornerBodyStyle
26628
27388
  }
26629
27389
  ),
26630
- showImages ? /* @__PURE__ */ jsxs3(Fragment4, { children: [
27390
+ hasCanvasDomDrawingOverlays ? /* @__PURE__ */ jsxs3(Fragment4, { children: [
26631
27391
  /* @__PURE__ */ jsx3("div", { style: canvasScrollOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
26632
27392
  "div",
26633
27393
  {
27394
+ ref: canvasScrollOverlayContentRef,
26634
27395
  style: {
26635
27396
  height: sheetContentHeight,
26636
27397
  left: 0,
@@ -26638,6 +27399,7 @@ function XlsxGrid({
26638
27399
  position: "absolute",
26639
27400
  top: 0,
26640
27401
  transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
27402
+ transformOrigin: "0 0",
26641
27403
  width: totalWidth
26642
27404
  },
26643
27405
  children: paneDrawingNodes.scroll
@@ -26646,6 +27408,7 @@ function XlsxGrid({
26646
27408
  /* @__PURE__ */ jsx3("div", { style: canvasTopOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
26647
27409
  "div",
26648
27410
  {
27411
+ ref: canvasTopOverlayContentRef,
26649
27412
  style: {
26650
27413
  height: sheetContentHeight,
26651
27414
  left: 0,
@@ -26653,6 +27416,7 @@ function XlsxGrid({
26653
27416
  position: "absolute",
26654
27417
  top: 0,
26655
27418
  transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-displayHeaderHeight}px)`,
27419
+ transformOrigin: "0 0",
26656
27420
  width: totalWidth
26657
27421
  },
26658
27422
  children: paneDrawingNodes.top
@@ -26661,6 +27425,7 @@ function XlsxGrid({
26661
27425
  /* @__PURE__ */ jsx3("div", { style: canvasLeftOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
26662
27426
  "div",
26663
27427
  {
27428
+ ref: canvasLeftOverlayContentRef,
26664
27429
  style: {
26665
27430
  height: sheetContentHeight,
26666
27431
  left: 0,
@@ -26668,6 +27433,7 @@ function XlsxGrid({
26668
27433
  position: "absolute",
26669
27434
  top: 0,
26670
27435
  transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
27436
+ transformOrigin: "0 0",
26671
27437
  width: totalWidth
26672
27438
  },
26673
27439
  children: paneDrawingNodes.left
@@ -26676,6 +27442,7 @@ function XlsxGrid({
26676
27442
  /* @__PURE__ */ jsx3("div", { style: canvasCornerOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
26677
27443
  "div",
26678
27444
  {
27445
+ ref: canvasCornerOverlayContentRef,
26679
27446
  style: {
26680
27447
  height: sheetContentHeight,
26681
27448
  left: 0,
@@ -26683,6 +27450,7 @@ function XlsxGrid({
26683
27450
  position: "absolute",
26684
27451
  top: 0,
26685
27452
  transform: `translate(${-displayRowHeaderWidth}px, ${-displayHeaderHeight}px)`,
27453
+ transformOrigin: "0 0",
26686
27454
  width: totalWidth
26687
27455
  },
26688
27456
  children: paneDrawingNodes.corner
@@ -27089,7 +27857,7 @@ function XlsxViewerInner({
27089
27857
  enableCanvasSelectionAnimation = true,
27090
27858
  enableGestureZoom = true,
27091
27859
  errorState,
27092
- experimentalCanvas = false,
27860
+ experimentalCanvas = true,
27093
27861
  fileTooLargeState,
27094
27862
  height,
27095
27863
  isDark = false,
@@ -27602,7 +28370,7 @@ function useXlsxViewerThumbnails(options = {}) {
27602
28370
  if (!canvas) {
27603
28371
  return false;
27604
28372
  }
27605
- const context = canvas.getContext("2d");
28373
+ const context = canvas.getContext("2d", { alpha: false });
27606
28374
  if (!context) {
27607
28375
  return false;
27608
28376
  }
@@ -27626,9 +28394,10 @@ function useXlsxViewerThumbnails(options = {}) {
27626
28394
  }
27627
28395
  context.setTransform(dpr * scale, 0, 0, dpr * scale, 0, 0);
27628
28396
  context.clearRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
28397
+ const thumbnailSheetSurface = resolveSheetSurface(sheet, palette);
27629
28398
  context.fillStyle = palette.canvas;
27630
28399
  context.fillRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
27631
- context.fillStyle = resolveSheetSurface(sheet, palette);
28400
+ context.fillStyle = thumbnailSheetSurface;
27632
28401
  context.fillRect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
27633
28402
  if (includeHeaders) {
27634
28403
  context.fillStyle = palette.headerSurface;
@@ -27749,8 +28518,8 @@ function useXlsxViewerThumbnails(options = {}) {
27749
28518
  };
27750
28519
  const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellData.style);
27751
28520
  const gradientFill = typeof cellData.style.backgroundImage === "string" ? resolveCanvasGradientFill(context, rect, cellData.style.backgroundImage) : null;
27752
- const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor : resolveSheetSurface(sheet, palette));
27753
- const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !== resolveSheetSurface(sheet, palette);
28521
+ const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor : thumbnailSheetSurface);
28522
+ const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !== thumbnailSheetSurface;
27754
28523
  context.fillStyle = gradientFill ?? fillColor;
27755
28524
  context.fillRect(rect.left, rect.top, rect.width, rect.height);
27756
28525
  if (cellData.conditionalDataBar) {
@@ -27805,12 +28574,19 @@ function useXlsxViewerThumbnails(options = {}) {
27805
28574
  if (canvasCellStyle.leftBorder) {
27806
28575
  strokeCanvasBorderSide(context, "left", rect, canvasCellStyle.leftBorder);
27807
28576
  }
28577
+ const rawText = cellData.value ?? "";
28578
+ const shouldDrawThumbnailContent = cellData.checkboxState != null || cellData.sparkline || rawText.length > 0 || cellData.conditionalIcon;
28579
+ if (!shouldDrawThumbnailContent) {
28580
+ continue;
28581
+ }
27808
28582
  const padding = canvasCellStyle.padding;
27809
28583
  const contentLeft = rect.left + padding.left;
27810
28584
  const contentTop = rect.top + padding.top;
27811
28585
  const contentWidth = Math.max(0, rect.width - padding.left - padding.right);
27812
28586
  const contentHeight = Math.max(0, rect.height - padding.top - padding.bottom);
27813
- const rawText = cellData.value ?? "";
28587
+ if (contentWidth <= 0 || contentHeight <= 0) {
28588
+ continue;
28589
+ }
27814
28590
  context.save();
27815
28591
  context.beginPath();
27816
28592
  context.rect(contentLeft, contentTop, contentWidth, contentHeight);