@extend-ai/react-xlsx 0.8.3 → 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
  }
@@ -20962,6 +21140,8 @@ function XlsxGrid({
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) {
@@ -23045,14 +23326,14 @@ function XlsxGrid({
23045
23326
  },
23046
23327
  left: {
23047
23328
  cols: frozenColItems,
23048
- rows: frozenRows.length > 0 ? canvasVisibleRowItems : scrollRowItems
23329
+ rows: scrollRowItems
23049
23330
  },
23050
23331
  scroll: {
23051
- cols: frozenCols.length > 0 ? canvasVisibleColItems : scrollColItems,
23052
- rows: frozenRows.length > 0 ? canvasVisibleRowItems : scrollRowItems
23332
+ cols: scrollColItems,
23333
+ rows: scrollRowItems
23053
23334
  },
23054
23335
  top: {
23055
- cols: frozenCols.length > 0 ? canvasVisibleColItems : scrollColItems,
23336
+ cols: scrollColItems,
23056
23337
  rows: frozenRowItems
23057
23338
  }
23058
23339
  };
@@ -23186,6 +23467,51 @@ function XlsxGrid({
23186
23467
  visibleRows
23187
23468
  ]
23188
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
+ ]);
23189
23515
  const resolveMountedCellOverlayRect = React4.useCallback((element) => {
23190
23516
  const wrapper = wrapperRef.current;
23191
23517
  if (!wrapper) {
@@ -24054,6 +24380,24 @@ function XlsxGrid({
24054
24380
  rowPrefixSums,
24055
24381
  stickyTopByRow
24056
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;
24057
24401
  const canvasColumnHeaderCells = React4.useMemo(
24058
24402
  () => canvasVisibleColItems.flatMap((column) => {
24059
24403
  const rect = resolveCanvasColumnHeaderRect(column.actualCol);
@@ -24066,7 +24410,7 @@ function XlsxGrid({
24066
24410
  height: displayHeaderHeight,
24067
24411
  isFrozen,
24068
24412
  left: rect.left,
24069
- localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : frozenPaneRight),
24413
+ localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : scrollBodyCanvasLeft),
24070
24414
  width: rect.width
24071
24415
  }];
24072
24416
  }),
@@ -24074,8 +24418,8 @@ function XlsxGrid({
24074
24418
  canvasVisibleColItems,
24075
24419
  displayHeaderHeight,
24076
24420
  displayRowHeaderWidth,
24077
- frozenPaneRight,
24078
24421
  resolveCanvasColumnHeaderRect,
24422
+ scrollBodyCanvasLeft,
24079
24423
  stickyLeftByCol
24080
24424
  ]
24081
24425
  );
@@ -24090,15 +24434,15 @@ function XlsxGrid({
24090
24434
  actualRow: row.actualRow,
24091
24435
  height: rect.height,
24092
24436
  isFrozen,
24093
- localTop: rect.top - (isFrozen ? displayHeaderHeight : frozenPaneBottom),
24437
+ localTop: rect.top - (isFrozen ? displayHeaderHeight : scrollBodyCanvasTop),
24094
24438
  top: rect.top
24095
24439
  }];
24096
24440
  }),
24097
24441
  [
24098
24442
  canvasVisibleRowItems,
24099
24443
  displayHeaderHeight,
24100
- frozenPaneBottom,
24101
24444
  resolveCanvasRowHeaderRect,
24445
+ scrollBodyCanvasTop,
24102
24446
  stickyTopByRow
24103
24447
  ]
24104
24448
  );
@@ -24365,10 +24709,46 @@ function XlsxGrid({
24365
24709
  rowPrefixSums,
24366
24710
  startCellSelection
24367
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
+ }, []);
24368
24740
  React4.useLayoutEffect(() => {
24369
24741
  if (!experimentalCanvas) {
24370
24742
  return;
24371
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;
24372
24752
  const dpr = typeof window === "undefined" ? 1 : Math.max(1, window.devicePixelRatio || 1);
24373
24753
  function configureCanvas(canvas, width, height, options) {
24374
24754
  if (!canvas) {
@@ -24388,7 +24768,7 @@ function XlsxGrid({
24388
24768
  if (canvas.style.height !== `${height}px`) {
24389
24769
  canvas.style.height = `${height}px`;
24390
24770
  }
24391
- const context = canvas.getContext("2d");
24771
+ const context = canvas.getContext("2d", { alpha: false });
24392
24772
  if (!context) {
24393
24773
  return null;
24394
24774
  }
@@ -24407,6 +24787,7 @@ function XlsxGrid({
24407
24787
  const rangeSignature = buildRangeSignature(normalizedVisibleRange);
24408
24788
  const nextBodyCanvasSignature = {
24409
24789
  activeSheet: activeSheet ?? null,
24790
+ bakedDrawingSignature: bakedCanvasDrawingSignature,
24410
24791
  bodyHeight,
24411
24792
  bodyWidth,
24412
24793
  colSignature: buildRenderedAxisSignature(canvasVisibleColItems, (item) => item.index),
@@ -24434,10 +24815,10 @@ function XlsxGrid({
24434
24815
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
24435
24816
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
24436
24817
  const previousPaintedViewport = paintedDrawingViewportRef.current;
24437
- 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);
24438
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);
24439
24820
  const canBlitBody = Boolean(
24440
- 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
24441
24822
  );
24442
24823
  const canBlitTopHeader = Boolean(
24443
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
@@ -24448,49 +24829,37 @@ function XlsxGrid({
24448
24829
  if (!shouldRepaintBody && !shouldRepaintHeaders) {
24449
24830
  return;
24450
24831
  }
24451
- const scrollBodyCanvasWidth2 = Math.max(0, drawingViewport.width - frozenPaneRight);
24452
- const scrollBodyCanvasHeight2 = Math.max(0, drawingViewport.height - frozenPaneBottom);
24453
- const topBodyCanvasWidth2 = scrollBodyCanvasWidth2;
24454
- const topBodyCanvasHeight2 = Math.max(0, frozenPaneBottom - displayHeaderHeight);
24455
- const leftBodyCanvasWidth2 = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
24456
- const leftBodyCanvasHeight2 = scrollBodyCanvasHeight2;
24457
- const cornerBodyCanvasWidth2 = leftBodyCanvasWidth2;
24458
- const cornerBodyCanvasHeight2 = topBodyCanvasHeight2;
24459
- const topFrozenHeaderCanvasWidth2 = leftBodyCanvasWidth2;
24460
- const topScrollHeaderCanvasWidth2 = scrollBodyCanvasWidth2;
24461
- const leftFrozenHeaderCanvasHeight2 = topBodyCanvasHeight2;
24462
- const leftScrollHeaderCanvasHeight2 = scrollBodyCanvasHeight2;
24463
24832
  const paneBounds = {
24464
24833
  corner: {
24465
- height: cornerBodyCanvasHeight2,
24834
+ height: cornerBodyCanvasHeight,
24466
24835
  left: displayRowHeaderWidth,
24467
24836
  top: displayHeaderHeight,
24468
- width: cornerBodyCanvasWidth2
24837
+ width: cornerBodyCanvasWidth
24469
24838
  },
24470
24839
  left: {
24471
- height: leftBodyCanvasHeight2,
24840
+ height: leftBodyCanvasHeight,
24472
24841
  left: displayRowHeaderWidth,
24473
- top: frozenPaneBottom,
24474
- width: leftBodyCanvasWidth2
24842
+ top: scrollBodyCanvasTop,
24843
+ width: leftBodyCanvasWidth
24475
24844
  },
24476
24845
  scroll: {
24477
- height: scrollBodyCanvasHeight2,
24478
- left: frozenPaneRight,
24479
- top: frozenPaneBottom,
24480
- width: scrollBodyCanvasWidth2
24846
+ height: scrollBodyCanvasHeight,
24847
+ left: scrollBodyCanvasLeft,
24848
+ top: scrollBodyCanvasTop,
24849
+ width: scrollBodyCanvasWidth
24481
24850
  },
24482
24851
  top: {
24483
- height: topBodyCanvasHeight2,
24484
- left: frozenPaneRight,
24852
+ height: topBodyCanvasHeight,
24853
+ left: scrollBodyCanvasLeft,
24485
24854
  top: displayHeaderHeight,
24486
- width: topBodyCanvasWidth2
24855
+ width: topBodyCanvasWidth
24487
24856
  }
24488
24857
  };
24489
24858
  const bodyContexts = shouldRepaintBody ? {
24490
- corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth2, cornerBodyCanvasHeight2, { clear: !canBlitBody }),
24491
- left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth2, leftBodyCanvasHeight2, { clear: !canBlitBody }),
24492
- scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth2, scrollBodyCanvasHeight2, { clear: !canBlitBody }),
24493
- 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 })
24494
24863
  } : {
24495
24864
  corner: null,
24496
24865
  left: null,
@@ -24498,21 +24867,21 @@ function XlsxGrid({
24498
24867
  top: null
24499
24868
  };
24500
24869
  const topHeaderContexts = shouldRepaintHeaders ? {
24501
- frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth2, headerHeight),
24502
- scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth2, headerHeight, { clear: !canBlitTopHeader })
24870
+ frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth, headerHeight),
24871
+ scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth, headerHeight, { clear: !canBlitTopHeader })
24503
24872
  } : {
24504
24873
  frozen: null,
24505
24874
  scroll: null
24506
24875
  };
24507
24876
  const leftHeaderContexts = shouldRepaintHeaders ? {
24508
- frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight2),
24509
- scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight2, { clear: !canBlitLeftHeader })
24877
+ frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight),
24878
+ scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight, { clear: !canBlitLeftHeader })
24510
24879
  } : {
24511
24880
  frozen: null,
24512
24881
  scroll: null
24513
24882
  };
24514
24883
  const cornerContext = shouldRepaintHeaders ? configureCanvas(cornerHeaderCanvasRef.current, rowHeaderWidth, headerHeight) : null;
24515
- 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)) {
24516
24885
  return;
24517
24886
  }
24518
24887
  const showGridLines = activeSheet?.showGridLines ?? true;
@@ -24529,10 +24898,282 @@ function XlsxGrid({
24529
24898
  scroll: [],
24530
24899
  top: []
24531
24900
  };
24532
- let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth2, headerHeight);
24533
- 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
+ };
24534
25174
  const cellPaneOrder = ["scroll", "top", "left", "corner"];
24535
25175
  if (shouldRepaintBody) {
25176
+ canvasProfileBodyStart = canvasProfileTarget ? performance.now() : 0;
24536
25177
  for (const pane of Object.keys(bodyContexts)) {
24537
25178
  const context = bodyContexts[pane];
24538
25179
  const bounds = paneBounds[pane];
@@ -24563,6 +25204,7 @@ function XlsxGrid({
24563
25204
  context.clearRect(0, 0, bounds.width, bounds.height);
24564
25205
  }
24565
25206
  bodyDirtyRectsByPane[pane] = dirtyRects;
25207
+ canvasProfileDirtyRects += dirtyRects.length;
24566
25208
  if (dirtyRects.length === 0) {
24567
25209
  continue;
24568
25210
  }
@@ -24579,6 +25221,22 @@ function XlsxGrid({
24579
25221
  if (!paneContext || paneDirtyRects.length === 0 || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
24580
25222
  continue;
24581
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
+ };
24582
25240
  const drawnMergedAnchorKeys = /* @__PURE__ */ new Set();
24583
25241
  for (const rowItem of paneAxisItems.rows) {
24584
25242
  for (const colItem of paneAxisItems.cols) {
@@ -24586,6 +25244,33 @@ function XlsxGrid({
24586
25244
  const anchorCell = resolveMergeAnchorCell(cell);
24587
25245
  const anchorKey = `${anchorCell.row}:${anchorCell.col}`;
24588
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;
24589
25274
  let cellData = getCellData(drawCell.row, drawCell.col);
24590
25275
  if ((cellData.colSpan || cellData.rowSpan) && drawnMergedAnchorKeys.has(anchorKey)) {
24591
25276
  continue;
@@ -24596,16 +25281,9 @@ function XlsxGrid({
24596
25281
  if (cellData.colSpan || cellData.rowSpan) {
24597
25282
  drawnMergedAnchorKeys.add(anchorKey);
24598
25283
  }
24599
- const drawRowIndex = rowIndexByActual.get(drawCell.row);
24600
- const drawColIndex = colIndexByActual.get(drawCell.col);
24601
- if (drawRowIndex === void 0 || drawColIndex === void 0) {
24602
- continue;
24603
- }
24604
25284
  const displayRect = cellData.colSpan || cellData.rowSpan ? resolveCellDisplayRect(drawCell) : null;
24605
- const baseLeft = displayRect?.left ?? displayRowHeaderWidth + (colPrefixSums[drawColIndex] ?? 0);
24606
- const baseTop = displayRect?.top ?? displayHeaderHeight + (rowPrefixSums[drawRowIndex] ?? 0);
24607
- const useFrozenHorizontalPosition = pane === "left" || pane === "corner";
24608
- const useFrozenVerticalPosition = pane === "top" || pane === "corner";
25285
+ const baseLeft = displayRect?.left ?? baseCellLeft;
25286
+ const baseTop = displayRect?.top ?? baseCellTop;
24609
25287
  const localRect = {
24610
25288
  height: displayRect?.height ?? (displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size),
24611
25289
  left: (useFrozenHorizontalPosition ? stickyLeftByCol.get(drawCell.col) ?? baseLeft - drawingViewport.left : baseLeft - drawingViewport.left) - paneBoundsForCell.left,
@@ -24617,13 +25295,18 @@ function XlsxGrid({
24617
25295
  continue;
24618
25296
  }
24619
25297
  if (!intersectsCanvasDirtyRects(localRect.left, localRect.top, drawableWidth, localRect.height, paneDirtyRects)) {
25298
+ canvasProfileCulledCells += 1;
24620
25299
  continue;
24621
25300
  }
25301
+ canvasProfilePaintedCells += 1;
24622
25302
  const cellStyle = cellData.style;
24623
25303
  const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellStyle);
24624
25304
  const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : sheetSurface);
24625
25305
  const gradientFill = !cellData.conditionalColorScale && typeof cellStyle.backgroundImage === "string" ? resolveCanvasGradientFill(paneContext, localRect, cellStyle.backgroundImage) : null;
24626
25306
  const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== sheetSurface;
25307
+ if (hasExplicitCellFill || cellData.chartHighlight) {
25308
+ flushPendingGridlines();
25309
+ }
24627
25310
  paneContext.fillStyle = gradientFill ?? fillColor;
24628
25311
  paneContext.fillRect(localRect.left, localRect.top, localRect.width, localRect.height);
24629
25312
  if (cellData.chartHighlight) {
@@ -24669,10 +25352,8 @@ function XlsxGrid({
24669
25352
  const bottomNeighborTopBorder = bottomNeighborData?.isMergedSecondary ? null : bottomNeighborData?.canvas?.topBorder ?? parseCanvasBorderDeclaration(bottomNeighborData?.style.borderTop);
24670
25353
  const resolvedRightBorder = resolveCanvasBoundaryBorder(rightBorder, rightNeighborLeftBorder);
24671
25354
  const resolvedBottomBorder = resolveCanvasBoundaryBorder(bottomBorder, bottomNeighborTopBorder);
24672
- if (showGridLines && !hasExplicitCellFill) {
24673
- paneContext.strokeStyle = palette.border;
24674
- paneContext.lineWidth = 1;
24675
- paneContext.beginPath();
25355
+ if (showGridLines && !hasExplicitCellFill && (!resolvedRightBorder || !resolvedBottomBorder)) {
25356
+ enqueueGridlinePath();
24676
25357
  if (!resolvedRightBorder) {
24677
25358
  paneContext.moveTo(localRect.left + localRect.width - 0.5, localRect.top);
24678
25359
  paneContext.lineTo(localRect.left + localRect.width - 0.5, localRect.top + localRect.height);
@@ -24681,7 +25362,9 @@ function XlsxGrid({
24681
25362
  paneContext.moveTo(localRect.left, localRect.top + localRect.height - 0.5);
24682
25363
  paneContext.lineTo(localRect.left + localRect.width, localRect.top + localRect.height - 0.5);
24683
25364
  }
24684
- paneContext.stroke();
25365
+ }
25366
+ if (topBorder || resolvedRightBorder || resolvedBottomBorder || leftBorder || cellData.chartHighlight) {
25367
+ flushPendingGridlines();
24685
25368
  }
24686
25369
  if (topBorder && drawRowIndex === 0) {
24687
25370
  strokeCanvasBorderSide(paneContext, "top", localRect, topBorder);
@@ -24714,17 +25397,26 @@ function XlsxGrid({
24714
25397
  strokeCanvasBorderSide(paneContext, "left", localRect, highlightBorder);
24715
25398
  }
24716
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
+ }
24717
25405
  const padding = canvasCellStyle.padding;
24718
25406
  const contentLeft = localRect.left + padding.left;
24719
25407
  const contentTop = localRect.top + padding.top;
24720
25408
  const contentWidth = Math.max(0, localRect.width - padding.left - padding.right);
24721
25409
  const contentHeight = Math.max(0, localRect.height - padding.top - padding.bottom);
25410
+ if (contentWidth <= 0 || contentHeight <= 0) {
25411
+ continue;
25412
+ }
24722
25413
  const activeFontSizePx = cellData.shrinkToFitFontSizePx ?? resolveCanvasFontSizePx(cellStyle, 12 * zoomFactor);
24723
25414
  const textClipOverscan = Math.max(
24724
25415
  1,
24725
25416
  zoomFactor * 1.5,
24726
25417
  activeFontSizePx * (cellData.textRotationDeg ? 0.75 : 0.18)
24727
25418
  );
25419
+ flushPendingGridlines();
24728
25420
  paneContext.save();
24729
25421
  paneContext.beginPath();
24730
25422
  paneContext.rect(
@@ -24856,7 +25548,6 @@ function XlsxGrid({
24856
25548
  const trailingInset = (cellData.conditionalIcon ? 18 * zoomFactor : 0) + (cellData.isTableHeader ? 16 * zoomFactor : 0);
24857
25549
  const spillMaxWidth = cellData.spillWidth && cellData.spillWidth > 0 ? Math.max(0, cellData.spillWidth - trailingInset) : null;
24858
25550
  const maxTextWidth = spillMaxWidth ?? Math.max(0, contentWidth - trailingInset);
24859
- const rawText = cellData.value ?? "";
24860
25551
  const textColor = canvasCellStyle.textColor;
24861
25552
  const shouldEllipsizeText = canvasCellStyle.textOverflowEllipsis;
24862
25553
  const shouldWrapText = canvasCellStyle.usesWrappedText || rawText.includes("\n");
@@ -24888,7 +25579,7 @@ function XlsxGrid({
24888
25579
  const textY = textBlockTop + lineIndex * lineHeight + lineHeight / 2;
24889
25580
  paneContext.fillText(line, textX, textY);
24890
25581
  if (canvasCellStyle.textDecoration?.includes("underline") && line.length > 0) {
24891
- const measured = Math.min(maxTextWidth, paneContext.measureText(line).width);
25582
+ const measured = Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, line));
24892
25583
  const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
24893
25584
  paneContext.beginPath();
24894
25585
  paneContext.moveTo(underlineStartX, textY + Math.max(2, lineHeight * 0.24));
@@ -24922,7 +25613,7 @@ function XlsxGrid({
24922
25613
  const textY = contentTop + contentHeight / 2;
24923
25614
  paneContext.fillText(text, textX, textY);
24924
25615
  if (canvasCellStyle.textDecoration?.includes("underline") && text.length > 0) {
24925
- 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);
24926
25617
  const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
24927
25618
  paneContext.beginPath();
24928
25619
  paneContext.moveTo(underlineStartX, textY + 6 * zoomFactor);
@@ -24947,6 +25638,7 @@ function XlsxGrid({
24947
25638
  paneContext.restore();
24948
25639
  }
24949
25640
  }
25641
+ flushPendingGridlines();
24950
25642
  }
24951
25643
  for (const pane of cellPaneOrder) {
24952
25644
  const paneContext = bodyContexts[pane];
@@ -24964,7 +25656,7 @@ function XlsxGrid({
24964
25656
  paneContext.textBaseline = "middle";
24965
25657
  paneContext.fillText(spillText.text, spillText.textX, spillText.textY);
24966
25658
  if (spillText.textDecoration?.includes("underline") && spillText.text.length > 0) {
24967
- 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);
24968
25660
  const underlineStartX = spillText.align === "right" ? spillText.textX - measured : spillText.align === "center" ? spillText.textX - measured / 2 : spillText.textX;
24969
25661
  paneContext.beginPath();
24970
25662
  paneContext.moveTo(underlineStartX, spillText.underlineY);
@@ -24976,6 +25668,14 @@ function XlsxGrid({
24976
25668
  paneContext.restore();
24977
25669
  }
24978
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;
24979
25679
  }
24980
25680
  if (shouldRepaintHeaders && cornerContext) {
24981
25681
  const topFrozenHeaderContext = topHeaderContexts.frozen;
@@ -24990,7 +25690,7 @@ function XlsxGrid({
24990
25690
  topScrollHeaderContext,
24991
25691
  bufferCanvas,
24992
25692
  dpr,
24993
- topScrollHeaderCanvasWidth2,
25693
+ topScrollHeaderCanvasWidth,
24994
25694
  headerHeight,
24995
25695
  drawingViewport.left - previousPaintedViewport.left,
24996
25696
  0
@@ -25009,7 +25709,7 @@ function XlsxGrid({
25009
25709
  bufferCanvas,
25010
25710
  dpr,
25011
25711
  rowHeaderWidth,
25012
- leftScrollHeaderCanvasHeight2,
25712
+ leftScrollHeaderCanvasHeight,
25013
25713
  0,
25014
25714
  drawingViewport.top - previousPaintedViewport.top
25015
25715
  );
@@ -25018,11 +25718,11 @@ function XlsxGrid({
25018
25718
  }
25019
25719
  }
25020
25720
  }
25021
- if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth2 > 0) {
25721
+ if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth > 0) {
25022
25722
  topFrozenHeaderContext.fillStyle = palette.headerSurface;
25023
- topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth2, headerHeight);
25723
+ topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth, headerHeight);
25024
25724
  }
25025
- if (topScrollHeaderContext && topScrollHeaderCanvasWidth2 > 0) {
25725
+ if (topScrollHeaderContext && topScrollHeaderCanvasWidth > 0) {
25026
25726
  topScrollHeaderContext.fillStyle = palette.headerSurface;
25027
25727
  for (const dirtyRect of topScrollHeaderDirtyRects) {
25028
25728
  topScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
@@ -25041,7 +25741,7 @@ function XlsxGrid({
25041
25741
  if (!paneContext) {
25042
25742
  continue;
25043
25743
  }
25044
- 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)) {
25045
25745
  continue;
25046
25746
  }
25047
25747
  if (!column.isFrozen && !intersectsCanvasDirtyRects(column.localLeft, 0, column.width, column.height, topScrollHeaderDirtyRects)) {
@@ -25067,11 +25767,11 @@ function XlsxGrid({
25067
25767
  column.height / 2
25068
25768
  );
25069
25769
  }
25070
- if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight2 > 0) {
25770
+ if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight > 0) {
25071
25771
  leftFrozenHeaderContext.fillStyle = palette.rowHeaderSurface;
25072
- leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight2);
25772
+ leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight);
25073
25773
  }
25074
- if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight2 > 0) {
25774
+ if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight > 0) {
25075
25775
  leftScrollHeaderContext.fillStyle = palette.rowHeaderSurface;
25076
25776
  for (const dirtyRect of leftScrollHeaderDirtyRects) {
25077
25777
  leftScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
@@ -25090,7 +25790,7 @@ function XlsxGrid({
25090
25790
  if (!paneContext) {
25091
25791
  continue;
25092
25792
  }
25093
- 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)) {
25094
25794
  continue;
25095
25795
  }
25096
25796
  if (!row.isFrozen && !intersectsCanvasDirtyRects(0, row.localTop, rowHeaderWidth, row.height, leftScrollHeaderDirtyRects)) {
@@ -25130,9 +25830,29 @@ function XlsxGrid({
25130
25830
  paintedHeaderCanvasSignatureRef.current = nextHeaderCanvasSignature;
25131
25831
  }
25132
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
+ }
25133
25849
  }, [
25134
25850
  activeSheet,
25135
25851
  applyCanvasViewportCompensation,
25852
+ bakedCanvasDrawingSignature,
25853
+ bakedFormControlRects,
25854
+ bakedImageRects,
25855
+ bakedShapeRects,
25136
25856
  canvasColumnHeaderCells,
25137
25857
  canvasPaneAxisItems,
25138
25858
  canvasRowHeaderCells,
@@ -25153,15 +25873,16 @@ function XlsxGrid({
25153
25873
  frozenPaneBottom,
25154
25874
  frozenPaneRight,
25155
25875
  getCellData,
25876
+ getCanvasImage,
25156
25877
  getBodyBlitBufferCanvas,
25157
25878
  getHeaderBlitBufferCanvas,
25158
25879
  palette,
25159
25880
  resolveCellDisplayRect,
25160
25881
  resolveMergeAnchorCell,
25161
- resizeGuide,
25162
25882
  rowIndexByActual,
25163
25883
  rowPrefixSums,
25164
25884
  selectionHeaderSurface,
25885
+ shouldBakeCanvasStaticDrawings,
25165
25886
  stickyLeftByCol,
25166
25887
  stickyTopByRow,
25167
25888
  visibleCols,
@@ -25379,7 +26100,7 @@ function XlsxGrid({
25379
26100
  start: virtualRow?.start ?? (rowPrefixSums[index] ?? 0)
25380
26101
  };
25381
26102
  });
25382
- const totalHeight = shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
26103
+ const totalHeight = shouldUseDomVirtualizer && shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
25383
26104
  const totalWidth = totalContentWidth + displayRowHeaderWidth;
25384
26105
  const sheetContentHeight = displayHeaderHeight + totalHeight;
25385
26106
  const isLiveZooming = liveGestureZoom !== null && zoomScale === liveGestureZoom.baseZoomScale;
@@ -25453,18 +26174,6 @@ function XlsxGrid({
25453
26174
  width: 0,
25454
26175
  zIndex: canvasHeaderOverlayZIndex
25455
26176
  };
25456
- const scrollBodyCanvasWidth = Math.max(0, drawingViewport.width - frozenPaneRight);
25457
- const scrollBodyCanvasHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);
25458
- const topBodyCanvasWidth = scrollBodyCanvasWidth;
25459
- const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);
25460
- const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
25461
- const leftBodyCanvasHeight = scrollBodyCanvasHeight;
25462
- const cornerBodyCanvasWidth = leftBodyCanvasWidth;
25463
- const cornerBodyCanvasHeight = topBodyCanvasHeight;
25464
- const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
25465
- const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
25466
- const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
25467
- const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
25468
26177
  const canvasBodyBaseStyle = {
25469
26178
  cursor: "cell",
25470
26179
  pointerEvents: "auto",
@@ -25476,13 +26185,13 @@ function XlsxGrid({
25476
26185
  const canvasScrollBodyStyle = {
25477
26186
  ...canvasBodyBaseStyle,
25478
26187
  display: scrollBodyCanvasWidth > 0 && scrollBodyCanvasHeight > 0 ? "block" : "none",
25479
- left: frozenPaneRight,
25480
- top: frozenPaneBottom
26188
+ left: scrollBodyCanvasLeft,
26189
+ top: scrollBodyCanvasTop
25481
26190
  };
25482
26191
  const canvasTopBodyStyle = {
25483
26192
  ...canvasBodyBaseStyle,
25484
26193
  display: topBodyCanvasWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
25485
- left: frozenPaneRight,
26194
+ left: scrollBodyCanvasLeft,
25486
26195
  top: displayHeaderHeight,
25487
26196
  zIndex: 30
25488
26197
  };
@@ -25490,7 +26199,7 @@ function XlsxGrid({
25490
26199
  ...canvasBodyBaseStyle,
25491
26200
  display: leftBodyCanvasWidth > 0 && leftBodyCanvasHeight > 0 ? "block" : "none",
25492
26201
  left: displayRowHeaderWidth,
25493
- top: frozenPaneBottom,
26202
+ top: scrollBodyCanvasTop,
25494
26203
  zIndex: 30
25495
26204
  };
25496
26205
  const canvasCornerBodyStyle = {
@@ -25516,7 +26225,7 @@ function XlsxGrid({
25516
26225
  const canvasTopScrollHeaderStyle = {
25517
26226
  ...canvasHeaderBaseStyle,
25518
26227
  display: topScrollHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
25519
- left: frozenPaneRight,
26228
+ left: scrollBodyCanvasLeft,
25520
26229
  top: 0,
25521
26230
  zIndex: canvasHeaderOverlayZIndex
25522
26231
  };
@@ -25531,7 +26240,7 @@ function XlsxGrid({
25531
26240
  ...canvasHeaderBaseStyle,
25532
26241
  display: leftScrollHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
25533
26242
  left: 0,
25534
- top: frozenPaneBottom,
26243
+ top: scrollBodyCanvasTop,
25535
26244
  zIndex: canvasHeaderOverlayZIndex
25536
26245
  };
25537
26246
  const canvasCornerHeaderStyle = {
@@ -26087,26 +26796,26 @@ function XlsxGrid({
26087
26796
  };
26088
26797
  const canvasScrollOverlayPaneStyle = {
26089
26798
  ...canvasOverlayPaneBaseStyle,
26090
- display: scrollBodyCanvasWidth > 0 && scrollBodyCanvasHeight > 0 ? "block" : "none",
26091
- height: scrollBodyCanvasHeight,
26799
+ display: scrollBodyViewportWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
26800
+ height: scrollBodyViewportHeight,
26092
26801
  left: frozenPaneRight,
26093
26802
  top: frozenPaneBottom,
26094
- width: scrollBodyCanvasWidth,
26803
+ width: scrollBodyViewportWidth,
26095
26804
  zIndex: 20
26096
26805
  };
26097
26806
  const canvasTopOverlayPaneStyle = {
26098
26807
  ...canvasOverlayPaneBaseStyle,
26099
- display: topBodyCanvasWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
26808
+ display: scrollBodyViewportWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
26100
26809
  height: topBodyCanvasHeight,
26101
26810
  left: frozenPaneRight,
26102
26811
  top: displayHeaderHeight,
26103
- width: topBodyCanvasWidth,
26812
+ width: scrollBodyViewportWidth,
26104
26813
  zIndex: 35
26105
26814
  };
26106
26815
  const canvasLeftOverlayPaneStyle = {
26107
26816
  ...canvasOverlayPaneBaseStyle,
26108
- display: leftBodyCanvasWidth > 0 && leftBodyCanvasHeight > 0 ? "block" : "none",
26109
- height: leftBodyCanvasHeight,
26817
+ display: leftBodyCanvasWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
26818
+ height: scrollBodyViewportHeight,
26110
26819
  left: displayRowHeaderWidth,
26111
26820
  top: frozenPaneBottom,
26112
26821
  width: leftBodyCanvasWidth,
@@ -26121,8 +26830,9 @@ function XlsxGrid({
26121
26830
  width: cornerBodyCanvasWidth,
26122
26831
  zIndex: 36
26123
26832
  };
26833
+ const drawingViewportCacheSignature = `${Math.floor(drawingViewport.left / CANVAS_VIEWPORT_OVERSCAN_PX)}:${Math.floor(drawingViewport.top / CANVAS_VIEWPORT_OVERSCAN_PX)}:${drawingViewport.width}:${drawingViewport.height}`;
26124
26834
  const previousPaneDrawingNodes = paneDrawingNodesCacheRef.current;
26125
- 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;
26126
26836
  const paneDrawingNodes = canReusePaneDrawingNodes ? previousPaneDrawingNodes.value : !showImages ? {
26127
26837
  corner: null,
26128
26838
  left: null,
@@ -26131,35 +26841,35 @@ function XlsxGrid({
26131
26841
  } : {
26132
26842
  corner: /* @__PURE__ */ jsxs3(Fragment4, { children: [
26133
26843
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "corner")),
26134
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "corner")),
26135
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "corner")),
26136
- 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"))
26137
26847
  ] }),
26138
26848
  left: /* @__PURE__ */ jsxs3(Fragment4, { children: [
26139
26849
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "left")),
26140
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "left")),
26141
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "left")),
26142
- 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"))
26143
26853
  ] }),
26144
26854
  scroll: /* @__PURE__ */ jsxs3(Fragment4, { children: [
26145
26855
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "scroll")),
26146
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "scroll")),
26147
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "scroll")),
26148
- 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"))
26149
26859
  ] }),
26150
26860
  top: /* @__PURE__ */ jsxs3(Fragment4, { children: [
26151
26861
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "top")),
26152
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "top")),
26153
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "top")),
26154
- 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"))
26155
26865
  ] })
26156
26866
  };
26157
26867
  if (!canReusePaneDrawingNodes) {
26158
26868
  paneDrawingNodesCacheRef.current = {
26159
26869
  chartRects,
26160
- drawingViewport,
26161
- formControlRects,
26162
- imageRects,
26870
+ drawingViewportSignature: drawingViewportCacheSignature,
26871
+ formControlRects: domFormControlRects,
26872
+ imageRects: domImageRects,
26163
26873
  isChartsLoading,
26164
26874
  palette,
26165
26875
  readOnly,
@@ -26169,7 +26879,7 @@ function XlsxGrid({
26169
26879
  selectedChartId,
26170
26880
  selectedImageId,
26171
26881
  selectionStroke,
26172
- shapeRects,
26882
+ shapeRects: domShapeRects,
26173
26883
  showImages,
26174
26884
  value: paneDrawingNodes
26175
26885
  };
@@ -26677,10 +27387,11 @@ function XlsxGrid({
26677
27387
  style: canvasCornerBodyStyle
26678
27388
  }
26679
27389
  ),
26680
- showImages ? /* @__PURE__ */ jsxs3(Fragment4, { children: [
27390
+ hasCanvasDomDrawingOverlays ? /* @__PURE__ */ jsxs3(Fragment4, { children: [
26681
27391
  /* @__PURE__ */ jsx3("div", { style: canvasScrollOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
26682
27392
  "div",
26683
27393
  {
27394
+ ref: canvasScrollOverlayContentRef,
26684
27395
  style: {
26685
27396
  height: sheetContentHeight,
26686
27397
  left: 0,
@@ -26688,6 +27399,7 @@ function XlsxGrid({
26688
27399
  position: "absolute",
26689
27400
  top: 0,
26690
27401
  transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
27402
+ transformOrigin: "0 0",
26691
27403
  width: totalWidth
26692
27404
  },
26693
27405
  children: paneDrawingNodes.scroll
@@ -26696,6 +27408,7 @@ function XlsxGrid({
26696
27408
  /* @__PURE__ */ jsx3("div", { style: canvasTopOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
26697
27409
  "div",
26698
27410
  {
27411
+ ref: canvasTopOverlayContentRef,
26699
27412
  style: {
26700
27413
  height: sheetContentHeight,
26701
27414
  left: 0,
@@ -26703,6 +27416,7 @@ function XlsxGrid({
26703
27416
  position: "absolute",
26704
27417
  top: 0,
26705
27418
  transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-displayHeaderHeight}px)`,
27419
+ transformOrigin: "0 0",
26706
27420
  width: totalWidth
26707
27421
  },
26708
27422
  children: paneDrawingNodes.top
@@ -26711,6 +27425,7 @@ function XlsxGrid({
26711
27425
  /* @__PURE__ */ jsx3("div", { style: canvasLeftOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
26712
27426
  "div",
26713
27427
  {
27428
+ ref: canvasLeftOverlayContentRef,
26714
27429
  style: {
26715
27430
  height: sheetContentHeight,
26716
27431
  left: 0,
@@ -26718,6 +27433,7 @@ function XlsxGrid({
26718
27433
  position: "absolute",
26719
27434
  top: 0,
26720
27435
  transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
27436
+ transformOrigin: "0 0",
26721
27437
  width: totalWidth
26722
27438
  },
26723
27439
  children: paneDrawingNodes.left
@@ -26726,6 +27442,7 @@ function XlsxGrid({
26726
27442
  /* @__PURE__ */ jsx3("div", { style: canvasCornerOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
26727
27443
  "div",
26728
27444
  {
27445
+ ref: canvasCornerOverlayContentRef,
26729
27446
  style: {
26730
27447
  height: sheetContentHeight,
26731
27448
  left: 0,
@@ -26733,6 +27450,7 @@ function XlsxGrid({
26733
27450
  position: "absolute",
26734
27451
  top: 0,
26735
27452
  transform: `translate(${-displayRowHeaderWidth}px, ${-displayHeaderHeight}px)`,
27453
+ transformOrigin: "0 0",
26736
27454
  width: totalWidth
26737
27455
  },
26738
27456
  children: paneDrawingNodes.corner
@@ -27652,7 +28370,7 @@ function useXlsxViewerThumbnails(options = {}) {
27652
28370
  if (!canvas) {
27653
28371
  return false;
27654
28372
  }
27655
- const context = canvas.getContext("2d");
28373
+ const context = canvas.getContext("2d", { alpha: false });
27656
28374
  if (!context) {
27657
28375
  return false;
27658
28376
  }
@@ -27676,9 +28394,10 @@ function useXlsxViewerThumbnails(options = {}) {
27676
28394
  }
27677
28395
  context.setTransform(dpr * scale, 0, 0, dpr * scale, 0, 0);
27678
28396
  context.clearRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
28397
+ const thumbnailSheetSurface = resolveSheetSurface(sheet, palette);
27679
28398
  context.fillStyle = palette.canvas;
27680
28399
  context.fillRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
27681
- context.fillStyle = resolveSheetSurface(sheet, palette);
28400
+ context.fillStyle = thumbnailSheetSurface;
27682
28401
  context.fillRect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
27683
28402
  if (includeHeaders) {
27684
28403
  context.fillStyle = palette.headerSurface;
@@ -27799,8 +28518,8 @@ function useXlsxViewerThumbnails(options = {}) {
27799
28518
  };
27800
28519
  const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellData.style);
27801
28520
  const gradientFill = typeof cellData.style.backgroundImage === "string" ? resolveCanvasGradientFill(context, rect, cellData.style.backgroundImage) : null;
27802
- const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor : resolveSheetSurface(sheet, palette));
27803
- 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;
27804
28523
  context.fillStyle = gradientFill ?? fillColor;
27805
28524
  context.fillRect(rect.left, rect.top, rect.width, rect.height);
27806
28525
  if (cellData.conditionalDataBar) {
@@ -27855,12 +28574,19 @@ function useXlsxViewerThumbnails(options = {}) {
27855
28574
  if (canvasCellStyle.leftBorder) {
27856
28575
  strokeCanvasBorderSide(context, "left", rect, canvasCellStyle.leftBorder);
27857
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
+ }
27858
28582
  const padding = canvasCellStyle.padding;
27859
28583
  const contentLeft = rect.left + padding.left;
27860
28584
  const contentTop = rect.top + padding.top;
27861
28585
  const contentWidth = Math.max(0, rect.width - padding.left - padding.right);
27862
28586
  const contentHeight = Math.max(0, rect.height - padding.top - padding.bottom);
27863
- const rawText = cellData.value ?? "";
28587
+ if (contentWidth <= 0 || contentHeight <= 0) {
28588
+ continue;
28589
+ }
27864
28590
  context.save();
27865
28591
  context.beginPath();
27866
28592
  context.rect(contentLeft, contentTop, contentWidth, contentHeight);