@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.cjs CHANGED
@@ -4575,6 +4575,10 @@ function parseSheetState(archive, path, options) {
4575
4575
  let hasVerticalMerges = false;
4576
4576
  let maxHorizontalMergeEndCol = -1;
4577
4577
  let maxVerticalMergeEndRow = -1;
4578
+ let minContentCol = Number.POSITIVE_INFINITY;
4579
+ let minContentRow = Number.POSITIVE_INFINITY;
4580
+ let maxContentCol = -1;
4581
+ let maxContentRow = -1;
4578
4582
  const columnWidthCharacterWidthPx = measureColumnCharacterWidthPx(
4579
4583
  options?.defaultFont?.family,
4580
4584
  options?.defaultFont?.sizePt
@@ -4587,6 +4591,26 @@ function parseSheetState(archive, path, options) {
4587
4591
  sheetViewNode?.getAttribute("zoomScale") ?? sheetViewNode?.getAttribute("zoomScaleNormal") ?? Number.NaN
4588
4592
  );
4589
4593
  const zoomScale = Number.isFinite(rawZoomScale) && rawZoomScale > 0 ? rawZoomScale : 100;
4594
+ const trackContentCell = (cellRef) => {
4595
+ if (!cellRef) {
4596
+ return;
4597
+ }
4598
+ const cell = parseA1CellReference(cellRef);
4599
+ if (!cell) {
4600
+ return;
4601
+ }
4602
+ minContentCol = Math.min(minContentCol, cell.col);
4603
+ minContentRow = Math.min(minContentRow, cell.row);
4604
+ maxContentCol = Math.max(maxContentCol, cell.col);
4605
+ maxContentRow = Math.max(maxContentRow, cell.row);
4606
+ };
4607
+ const isMeaningfulCellNode = (cellNode) => {
4608
+ if (getFirstChild(cellNode, "f") || getFirstChild(cellNode, "is")) {
4609
+ return true;
4610
+ }
4611
+ const valueNode = getFirstChild(cellNode, "v");
4612
+ return Boolean(valueNode && (valueNode.textContent ?? "").length > 0);
4613
+ };
4590
4614
  getLocalElements(document2, "row").forEach((rowNode) => {
4591
4615
  const rowIndex = Number(rowNode.getAttribute("r") ?? 0) - 1;
4592
4616
  const height = Number(rowNode.getAttribute("ht") ?? Number.NaN);
@@ -4601,17 +4625,36 @@ function parseSheetState(archive, path, options) {
4601
4625
  if (rowIndex >= 0 && isHidden) {
4602
4626
  hiddenRows.add(rowIndex);
4603
4627
  }
4604
- if (includeCachedFormulaValues) {
4605
- getChildElements(rowNode, "c").forEach((cellNode) => {
4628
+ getChildElements(rowNode, "c").forEach((cellNode) => {
4629
+ const cellRef = cellNode.getAttribute("r");
4630
+ if (isMeaningfulCellNode(cellNode)) {
4631
+ trackContentCell(cellRef);
4632
+ }
4633
+ if (includeCachedFormulaValues) {
4606
4634
  const formulaNode = getFirstChild(cellNode, "f");
4607
4635
  const valueNode = getFirstChild(cellNode, "v");
4608
- const cellRef = cellNode.getAttribute("r");
4609
4636
  if (formulaNode && valueNode && cellRef) {
4610
4637
  cachedFormulaValues[cellRef] = valueNode.textContent ?? "";
4611
4638
  }
4612
- });
4639
+ }
4640
+ });
4641
+ });
4642
+ getLocalElements(document2, "mergeCell").forEach((mergeNode) => {
4643
+ const reference = mergeNode.getAttribute("ref");
4644
+ const range = reference ? parseA1RangeReference(reference) : null;
4645
+ if (!range) {
4646
+ return;
4647
+ }
4648
+ if (range.end.col > range.start.col) {
4649
+ hasHorizontalMerges = true;
4650
+ maxHorizontalMergeEndCol = Math.max(maxHorizontalMergeEndCol, range.end.col);
4651
+ }
4652
+ if (range.end.row > range.start.row) {
4653
+ hasVerticalMerges = true;
4654
+ maxVerticalMergeEndRow = Math.max(maxVerticalMergeEndRow, range.end.row);
4613
4655
  }
4614
4656
  });
4657
+ const maxMetadataCol = Math.max(maxContentCol, maxHorizontalMergeEndCol, 0) + 256;
4615
4658
  getLocalElements(document2, "col").forEach((colNode) => {
4616
4659
  const min = Number(colNode.getAttribute("min") ?? 0) - 1;
4617
4660
  const max = Number(colNode.getAttribute("max") ?? 0) - 1;
@@ -4623,7 +4666,7 @@ function parseSheetState(archive, path, options) {
4623
4666
  return;
4624
4667
  }
4625
4668
  }
4626
- for (let col = min; col <= max; col += 1) {
4669
+ for (let col = min; col <= Math.min(max, maxMetadataCol); col += 1) {
4627
4670
  if (col >= 0) {
4628
4671
  if (Number.isFinite(width)) {
4629
4672
  const widthPx = sheetColumnWidthToPixels(width, columnWidthCharacterWidthPx);
@@ -4638,21 +4681,6 @@ function parseSheetState(archive, path, options) {
4638
4681
  }
4639
4682
  }
4640
4683
  });
4641
- getLocalElements(document2, "mergeCell").forEach((mergeNode) => {
4642
- const reference = mergeNode.getAttribute("ref");
4643
- const range = reference ? parseA1RangeReference(reference) : null;
4644
- if (!range) {
4645
- return;
4646
- }
4647
- if (range.end.col > range.start.col) {
4648
- hasHorizontalMerges = true;
4649
- maxHorizontalMergeEndCol = Math.max(maxHorizontalMergeEndCol, range.end.col);
4650
- }
4651
- if (range.end.row > range.start.row) {
4652
- hasVerticalMerges = true;
4653
- maxVerticalMergeEndRow = Math.max(maxVerticalMergeEndRow, range.end.row);
4654
- }
4655
- });
4656
4684
  return {
4657
4685
  cachedFormulaValues,
4658
4686
  columnWidthCharacterWidthPx,
@@ -4665,6 +4693,10 @@ function parseSheetState(archive, path, options) {
4665
4693
  hasVerticalMerges,
4666
4694
  maxHorizontalMergeEndCol,
4667
4695
  maxVerticalMergeEndRow,
4696
+ maxContentCol,
4697
+ maxContentRow,
4698
+ minContentCol: Number.isFinite(minContentCol) ? minContentCol : -1,
4699
+ minContentRow: Number.isFinite(minContentRow) ? minContentRow : -1,
4668
4700
  hiddenCols: [...hiddenCols].sort((left, right) => left - right),
4669
4701
  hiddenRows: [...hiddenRows].sort((left, right) => left - right),
4670
4702
  rowHeightOverridesPx,
@@ -6694,6 +6726,20 @@ function resolveDisplayFileName(src, fileName) {
6694
6726
  return lastSegment;
6695
6727
  }
6696
6728
  }
6729
+ function resolveSheetDisplayUsedRange(usedRange, sheetState) {
6730
+ const [minRow, minCol, maxRow, maxCol] = usedRange;
6731
+ const maxMeaningfulRow = Math.max(sheetState?.maxContentRow ?? -1, sheetState?.maxVerticalMergeEndRow ?? -1);
6732
+ const maxMeaningfulCol = Math.max(sheetState?.maxContentCol ?? -1, sheetState?.maxHorizontalMergeEndCol ?? -1);
6733
+ if (maxMeaningfulRow < 0 && maxMeaningfulCol < 0) {
6734
+ return usedRange;
6735
+ }
6736
+ return [
6737
+ sheetState?.minContentRow !== void 0 && sheetState.minContentRow >= 0 ? Math.min(minRow, sheetState.minContentRow) : minRow,
6738
+ sheetState?.minContentCol !== void 0 && sheetState.minContentCol >= 0 ? Math.min(minCol, sheetState.minContentCol) : minCol,
6739
+ maxMeaningfulRow >= 0 ? Math.min(maxRow, maxMeaningfulRow) : maxRow,
6740
+ maxMeaningfulCol >= 0 ? Math.min(maxCol, maxMeaningfulCol) : maxCol
6741
+ ];
6742
+ }
6697
6743
  function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette, styleById, namedCellStyleByName, tableStyleByName, showHiddenSheets = false) {
6698
6744
  const sheets = [];
6699
6745
  for (let index = 0; index < workbook.sheetCount; index += 1) {
@@ -6760,7 +6806,7 @@ function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette,
6760
6806
  });
6761
6807
  continue;
6762
6808
  }
6763
- const [minRow, minCol, maxRow, maxCol] = usedRange;
6809
+ const [minRow, minCol, maxRow, maxCol] = resolveSheetDisplayUsedRange(usedRange, sheetState);
6764
6810
  let visibleRowsCache = null;
6765
6811
  let visibleColsCache = null;
6766
6812
  let rowHeightsCache = null;
@@ -16502,7 +16548,11 @@ var SELECTION_DRAG_THRESHOLD_PX = 4;
16502
16548
  var IMAGE_MIN_SIZE_PX = 16;
16503
16549
  var IMAGE_HANDLE_SIZE_PX = 10;
16504
16550
  var CANVAS_RESIZE_HIT_SLOP_PX = 8;
16505
- var CANVAS_VIEWPORT_OVERSCAN_PX = 240;
16551
+ var CANVAS_VIEWPORT_OVERSCAN_PX = 480;
16552
+ var CANVAS_SCROLL_BUFFER_PX = 480;
16553
+ var CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX - 96;
16554
+ var CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX * 1.5;
16555
+ var CANVAS_DIRTY_CELL_CULL_MARGIN_PX = CANVAS_VIEWPORT_OVERSCAN_PX * 2;
16506
16556
  var THUMBNAIL_DEFAULT_MAX_DIMENSION = 192;
16507
16557
  var THUMBNAIL_FALLBACK_ROWS = 12;
16508
16558
  var THUMBNAIL_FALLBACK_COLS = 8;
@@ -16517,6 +16567,12 @@ var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669",
16517
16567
  var SHEET_SURFACE = "#ffffff";
16518
16568
  var DEFAULT_CELL_PADDING = "0 4px";
16519
16569
  var IMAGE_HANDLE_POSITIONS = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
16570
+ var CANVAS_CELL_STYLE_CACHE_LIMIT = 4096;
16571
+ var CANVAS_TEXT_MEASURE_CACHE_LIMIT = 2e4;
16572
+ var CANVAS_TEXT_TRUNCATE_CACHE_LIMIT = 4096;
16573
+ var CANVAS_TEXT_WRAP_CACHE_LIMIT = 2048;
16574
+ var CANVAS_PATH2D_CACHE_LIMIT = 512;
16575
+ var EMPTY_VIRTUAL_ITEMS = [];
16520
16576
  var IMAGE_HANDLE_CURSOR = {
16521
16577
  e: "ew-resize",
16522
16578
  n: "ns-resize",
@@ -16560,6 +16616,50 @@ var NUMERIC_LENGTH_STYLE_KEYS = /* @__PURE__ */ new Set([
16560
16616
  "top",
16561
16617
  "width"
16562
16618
  ]);
16619
+ var canvasCellStyleCache = /* @__PURE__ */ new Map();
16620
+ var canvasTextMeasureCache = /* @__PURE__ */ new Map();
16621
+ var canvasTextTruncateCache = /* @__PURE__ */ new Map();
16622
+ var canvasTextWrapCache = /* @__PURE__ */ new Map();
16623
+ var canvasPath2DCache = /* @__PURE__ */ new Map();
16624
+ function rememberBoundedCacheValue(cache, key, value, limit) {
16625
+ if (cache.size >= limit) {
16626
+ const oldestKey = cache.keys().next().value;
16627
+ if (oldestKey !== void 0) {
16628
+ cache.delete(oldestKey);
16629
+ }
16630
+ }
16631
+ cache.set(key, value);
16632
+ return value;
16633
+ }
16634
+ function roundCanvasCacheWidth(width) {
16635
+ return Math.round(width * 4) / 4;
16636
+ }
16637
+ function measureCanvasTextWidth(context, text) {
16638
+ if (text.length === 0) {
16639
+ return 0;
16640
+ }
16641
+ const cacheKey = `${context.font}\0${text}`;
16642
+ const cached = canvasTextMeasureCache.get(cacheKey);
16643
+ if (cached !== void 0) {
16644
+ return cached;
16645
+ }
16646
+ return rememberBoundedCacheValue(
16647
+ canvasTextMeasureCache,
16648
+ cacheKey,
16649
+ context.measureText(text).width,
16650
+ CANVAS_TEXT_MEASURE_CACHE_LIMIT
16651
+ );
16652
+ }
16653
+ function getCachedCanvasPath2D(path) {
16654
+ if (typeof Path2D === "undefined") {
16655
+ return null;
16656
+ }
16657
+ const cached = canvasPath2DCache.get(path);
16658
+ if (cached) {
16659
+ return cached;
16660
+ }
16661
+ return rememberBoundedCacheValue(canvasPath2DCache, path, new Path2D(path), CANVAS_PATH2D_CACHE_LIMIT);
16662
+ }
16563
16663
  function scaleCssLengthExpression(value, scale) {
16564
16664
  if (scale === 1) {
16565
16665
  return value;
@@ -16740,7 +16840,6 @@ function strokeCanvasBorderSide(context, side, rect, border) {
16740
16840
  const right = rect.left + rect.width;
16741
16841
  const top = rect.top;
16742
16842
  const bottom = rect.top + rect.height;
16743
- context.save();
16744
16843
  context.strokeStyle = border.color;
16745
16844
  context.lineWidth = border.width;
16746
16845
  applyCanvasBorderDash(context, border.style, border.width);
@@ -16770,13 +16869,22 @@ function strokeCanvasBorderSide(context, side, rect, border) {
16770
16869
  } else {
16771
16870
  strokeLine(0);
16772
16871
  }
16773
- context.restore();
16872
+ context.setLineDash([]);
16873
+ context.lineCap = "butt";
16874
+ context.lineWidth = 1;
16774
16875
  }
16775
16876
  function truncateCanvasText(context, text, maxWidth) {
16776
16877
  if (maxWidth <= 0 || text.length === 0) {
16777
16878
  return "";
16778
16879
  }
16779
- if (context.measureText(text).width <= maxWidth) {
16880
+ const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);
16881
+ const cacheKey = `${context.font}\0${roundedMaxWidth}\0${text}`;
16882
+ const cached = canvasTextTruncateCache.get(cacheKey);
16883
+ if (cached !== void 0) {
16884
+ return cached;
16885
+ }
16886
+ if (measureCanvasTextWidth(context, text) <= maxWidth) {
16887
+ rememberBoundedCacheValue(canvasTextTruncateCache, cacheKey, text, CANVAS_TEXT_TRUNCATE_CACHE_LIMIT);
16780
16888
  return text;
16781
16889
  }
16782
16890
  const ellipsis = "\u2026";
@@ -16785,13 +16893,18 @@ function truncateCanvasText(context, text, maxWidth) {
16785
16893
  while (low < high) {
16786
16894
  const mid = Math.ceil((low + high) / 2);
16787
16895
  const candidate = `${text.slice(0, mid)}${ellipsis}`;
16788
- if (context.measureText(candidate).width <= maxWidth) {
16896
+ if (measureCanvasTextWidth(context, candidate) <= maxWidth) {
16789
16897
  low = mid;
16790
16898
  } else {
16791
16899
  high = mid - 1;
16792
16900
  }
16793
16901
  }
16794
- return low <= 0 ? ellipsis : `${text.slice(0, low)}${ellipsis}`;
16902
+ return rememberBoundedCacheValue(
16903
+ canvasTextTruncateCache,
16904
+ cacheKey,
16905
+ low <= 0 ? ellipsis : `${text.slice(0, low)}${ellipsis}`,
16906
+ CANVAS_TEXT_TRUNCATE_CACHE_LIMIT
16907
+ );
16795
16908
  }
16796
16909
  function resolveCanvasFontSizePx(style, fallbackSize = 12) {
16797
16910
  const rawFontSize = style.fontSize;
@@ -16850,7 +16963,7 @@ function resolveCanvasWrapIndex(context, text, maxWidth) {
16850
16963
  while (low <= high) {
16851
16964
  const mid = Math.floor((low + high) / 2);
16852
16965
  const candidate = text.slice(0, mid);
16853
- if (context.measureText(candidate).width <= maxWidth) {
16966
+ if (measureCanvasTextWidth(context, candidate) <= maxWidth) {
16854
16967
  best = mid;
16855
16968
  low = mid + 1;
16856
16969
  } else {
@@ -16867,6 +16980,12 @@ function wrapCanvasText(context, text, maxWidth) {
16867
16980
  return text.replace(/\r\n?/g, "\n").split("\n");
16868
16981
  }
16869
16982
  const normalized = text.replace(/\r\n?/g, "\n");
16983
+ const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);
16984
+ const cacheKey = `${context.font}\0${roundedMaxWidth}\0${normalized}`;
16985
+ const cached = canvasTextWrapCache.get(cacheKey);
16986
+ if (cached) {
16987
+ return cached;
16988
+ }
16870
16989
  const paragraphs = normalized.split("\n");
16871
16990
  const lines = [];
16872
16991
  for (const paragraph of paragraphs) {
@@ -16876,7 +16995,7 @@ function wrapCanvasText(context, text, maxWidth) {
16876
16995
  }
16877
16996
  let remaining = paragraph;
16878
16997
  while (remaining.length > 0) {
16879
- if (context.measureText(remaining).width <= maxWidth) {
16998
+ if (measureCanvasTextWidth(context, remaining) <= maxWidth) {
16880
16999
  lines.push(remaining);
16881
17000
  break;
16882
17001
  }
@@ -16894,10 +17013,31 @@ function wrapCanvasText(context, text, maxWidth) {
16894
17013
  remaining = remaining.slice(breakIndex).replace(/^\s+/g, "");
16895
17014
  }
16896
17015
  }
16897
- return lines;
17016
+ return rememberBoundedCacheValue(canvasTextWrapCache, cacheKey, lines, CANVAS_TEXT_WRAP_CACHE_LIMIT);
16898
17017
  }
16899
17018
  function buildCanvasCellStyleCache(style) {
16900
- return {
17019
+ const cacheKey = [
17020
+ style.font,
17021
+ style.fontStyle,
17022
+ style.fontWeight,
17023
+ style.fontSize,
17024
+ style.fontFamily,
17025
+ style.borderTop,
17026
+ style.borderRight,
17027
+ style.borderBottom,
17028
+ style.borderLeft,
17029
+ style.padding,
17030
+ style.textAlign,
17031
+ style.color,
17032
+ style.textDecoration,
17033
+ style.textOverflow,
17034
+ style.whiteSpace
17035
+ ].map((value) => value == null ? "" : String(value)).join("");
17036
+ const cached = canvasCellStyleCache.get(cacheKey);
17037
+ if (cached) {
17038
+ return cached;
17039
+ }
17040
+ return rememberBoundedCacheValue(canvasCellStyleCache, cacheKey, {
16901
17041
  baseFont: resolveCanvasFont(style, 12),
16902
17042
  bottomBorder: parseCanvasBorderDeclaration(style.borderBottom),
16903
17043
  leftBorder: parseCanvasBorderDeclaration(style.borderLeft),
@@ -16909,7 +17049,7 @@ function buildCanvasCellStyleCache(style) {
16909
17049
  textOverflowEllipsis: style.textOverflow === "ellipsis",
16910
17050
  topBorder: parseCanvasBorderDeclaration(style.borderTop),
16911
17051
  usesWrappedText: style.whiteSpace === "pre-wrap"
16912
- };
17052
+ }, CANVAS_CELL_STYLE_CACHE_LIMIT);
16913
17053
  }
16914
17054
  function splitCssGradientArgs(value) {
16915
17055
  const parts = [];
@@ -18418,6 +18558,44 @@ function resolveShapeVector(shape) {
18418
18558
  }
18419
18559
  return buildPresetShapePath(shape);
18420
18560
  }
18561
+ function drawCanvasRoundedRect(context, left, top, width, height, radius) {
18562
+ const safeRadius = Math.max(0, Math.min(radius, width / 2, height / 2));
18563
+ context.beginPath();
18564
+ context.moveTo(left + safeRadius, top);
18565
+ context.lineTo(left + width - safeRadius, top);
18566
+ context.quadraticCurveTo(left + width, top, left + width, top + safeRadius);
18567
+ context.lineTo(left + width, top + height - safeRadius);
18568
+ context.quadraticCurveTo(left + width, top + height, left + width - safeRadius, top + height);
18569
+ context.lineTo(left + safeRadius, top + height);
18570
+ context.quadraticCurveTo(left, top + height, left, top + height - safeRadius);
18571
+ context.lineTo(left, top + safeRadius);
18572
+ context.quadraticCurveTo(left, top, left + safeRadius, top);
18573
+ context.closePath();
18574
+ }
18575
+ function applyCanvasShapeDash(context, dash, lineWidth) {
18576
+ if (!dash) {
18577
+ context.setLineDash([]);
18578
+ return;
18579
+ }
18580
+ const unit = Math.max(1, lineWidth);
18581
+ switch (dash) {
18582
+ case "dash":
18583
+ context.setLineDash([4 * unit, 3 * unit]);
18584
+ break;
18585
+ case "dashDot":
18586
+ context.setLineDash([4 * unit, 2 * unit, unit, 2 * unit]);
18587
+ break;
18588
+ case "dot":
18589
+ context.setLineDash([unit, 2 * unit]);
18590
+ break;
18591
+ case "lgDash":
18592
+ context.setLineDash([8 * unit, 3 * unit]);
18593
+ break;
18594
+ default:
18595
+ context.setLineDash([]);
18596
+ break;
18597
+ }
18598
+ }
18421
18599
  function resolveShapeLineEndMarker(type, markerId, color, strokeWidth, rect, viewBox) {
18422
18600
  if (type !== "triangle") {
18423
18601
  return null;
@@ -19020,12 +19198,12 @@ function measureTextWidth(value, style) {
19020
19198
  return value.length * 7;
19021
19199
  }
19022
19200
  textMeasureCanvas ??= document.createElement("canvas");
19023
- const context = textMeasureCanvas.getContext("2d");
19201
+ const context = textMeasureCanvas.getContext("2d", { alpha: false });
19024
19202
  if (!context) {
19025
19203
  return value.length * 7;
19026
19204
  }
19027
19205
  context.font = buildCanvasFont(style);
19028
- return context.measureText(value).width;
19206
+ return measureCanvasTextWidth(context, value);
19029
19207
  }
19030
19208
  function measureWrappedTextHeight(value, style, widthPx) {
19031
19209
  if (!value) {
@@ -19039,7 +19217,7 @@ function measureWrappedTextHeight(value, style, widthPx) {
19039
19217
  return fallbackLineCount * lineHeight + padding.top + padding.bottom;
19040
19218
  }
19041
19219
  textMeasureCanvas ??= document.createElement("canvas");
19042
- const context = textMeasureCanvas.getContext("2d");
19220
+ const context = textMeasureCanvas.getContext("2d", { alpha: false });
19043
19221
  if (!context) {
19044
19222
  return fallbackLineCount * lineHeight + padding.top + padding.bottom;
19045
19223
  }
@@ -20137,7 +20315,7 @@ function blitCanvasWithScrollDelta(canvas, context, bufferCanvas, dpr, width, he
20137
20315
  if (bufferCanvas.height !== deviceHeight) {
20138
20316
  bufferCanvas.height = deviceHeight;
20139
20317
  }
20140
- const bufferContext = bufferCanvas.getContext("2d");
20318
+ const bufferContext = bufferCanvas.getContext("2d", { alpha: false });
20141
20319
  if (!bufferContext) {
20142
20320
  return null;
20143
20321
  }
@@ -20994,6 +21172,8 @@ function XlsxGrid({
20994
21172
  selectionHeaderColor,
20995
21173
  showImages = true
20996
21174
  }) {
21175
+ const xlsxCanvasProfileTarget = typeof window !== "undefined" ? window.__xlsxCanvasProfile : void 0;
21176
+ const xlsxGridRenderStart = xlsxCanvasProfileTarget ? performance.now() : 0;
20997
21177
  const {
20998
21178
  activeCell,
20999
21179
  activeSheet,
@@ -21077,6 +21257,11 @@ function XlsxGrid({
21077
21257
  const leftFrozenHeaderCanvasRef = React4.useRef(null);
21078
21258
  const leftScrollHeaderCanvasRef = React4.useRef(null);
21079
21259
  const cornerHeaderCanvasRef = React4.useRef(null);
21260
+ const canvasScrollOverlayContentRef = React4.useRef(null);
21261
+ const canvasTopOverlayContentRef = React4.useRef(null);
21262
+ const canvasLeftOverlayContentRef = React4.useRef(null);
21263
+ const canvasCornerOverlayContentRef = React4.useRef(null);
21264
+ const canvasImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
21080
21265
  const selectionOverlayRef = React4.useRef(null);
21081
21266
  const activeValidationOverlayRef = React4.useRef(null);
21082
21267
  const fillHandleRef = React4.useRef(null);
@@ -21105,6 +21290,7 @@ function XlsxGrid({
21105
21290
  const pendingLiveZoomCommitRef = React4.useRef(null);
21106
21291
  const touchPinchStateRef = React4.useRef(null);
21107
21292
  const safariPinchStartZoomRef = React4.useRef(null);
21293
+ const canvasTransformStyleCacheRef = React4.useRef(/* @__PURE__ */ new WeakMap());
21108
21294
  const displayedSelectionRef = React4.useRef(null);
21109
21295
  const firstVisibleColRef = React4.useRef(void 0);
21110
21296
  const lastVisibleColRef = React4.useRef(void 0);
@@ -21130,12 +21316,19 @@ function XlsxGrid({
21130
21316
  const [fillPreviewRange, setFillPreviewRange] = React4.useState(null);
21131
21317
  const [chartPreviewRect, setChartPreviewRect] = React4.useState(null);
21132
21318
  const [liveGestureZoom, setLiveGestureZoom] = React4.useState(null);
21319
+ const [canvasImageLoadVersion, setCanvasImageLoadVersion] = React4.useState(0);
21133
21320
  const liveDrawingViewportRef = React4.useRef({
21134
21321
  height: 0,
21135
21322
  left: 0,
21136
21323
  top: 0,
21137
21324
  width: 0
21138
21325
  });
21326
+ const canvasLayoutMetricsRef = React4.useRef({
21327
+ displayHeaderHeight: HEADER_HEIGHT,
21328
+ displayRowHeaderWidth: ROW_HEADER_WIDTH,
21329
+ frozenPaneBottom: HEADER_HEIGHT,
21330
+ frozenPaneRight: ROW_HEADER_WIDTH
21331
+ });
21139
21332
  const paintedDrawingViewportRef = React4.useRef({
21140
21333
  height: 0,
21141
21334
  left: 0,
@@ -21245,6 +21438,12 @@ function XlsxGrid({
21245
21438
  const applyCanvasViewportCompensation = React4.useCallback((liveViewport) => {
21246
21439
  const nextLiveViewport = liveViewport ?? liveDrawingViewportRef.current;
21247
21440
  const paintedViewport = paintedDrawingViewportRef.current;
21441
+ const {
21442
+ displayHeaderHeight: liveDisplayHeaderHeight,
21443
+ displayRowHeaderWidth: liveDisplayRowHeaderWidth,
21444
+ frozenPaneBottom: liveFrozenPaneBottom,
21445
+ frozenPaneRight: liveFrozenPaneRight
21446
+ } = canvasLayoutMetricsRef.current;
21248
21447
  const currentLiveGestureZoom = liveGestureZoomRef.current;
21249
21448
  const isLiveZooming2 = currentLiveGestureZoom !== null && zoomScale === currentLiveGestureZoom.baseZoomScale;
21250
21449
  const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
@@ -21252,12 +21451,41 @@ function XlsxGrid({
21252
21451
  const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
21253
21452
  const liveZoomTranslateX2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.x * (1 - liveZoomScale2) : 0;
21254
21453
  const liveZoomTranslateY2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.y * (1 - liveZoomScale2) : 0;
21255
- const applyCanvasTransform = (canvas, translateX, translateY) => {
21256
- if (!canvas) {
21454
+ const applyElementTransform = (element, transform, willChange, transformOrigin = null) => {
21455
+ if (!element) {
21257
21456
  return;
21258
21457
  }
21259
- canvas.style.transform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1 ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : "";
21260
- canvas.style.willChange = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1 ? "transform" : "";
21458
+ const cached = canvasTransformStyleCacheRef.current.get(element);
21459
+ if (cached?.transform !== transform || element.style.transform !== transform) {
21460
+ element.style.transform = transform;
21461
+ }
21462
+ if (cached?.willChange !== willChange || element.style.willChange !== willChange) {
21463
+ element.style.willChange = willChange;
21464
+ }
21465
+ if (transformOrigin !== null && (cached?.transformOrigin !== transformOrigin || element.style.transformOrigin !== transformOrigin)) {
21466
+ element.style.transformOrigin = transformOrigin;
21467
+ }
21468
+ canvasTransformStyleCacheRef.current.set(element, {
21469
+ transform,
21470
+ transformOrigin,
21471
+ willChange
21472
+ });
21473
+ };
21474
+ const applyCanvasTransform = (canvas, translateX, translateY) => {
21475
+ const shouldTransform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1;
21476
+ applyElementTransform(
21477
+ canvas,
21478
+ shouldTransform ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : "",
21479
+ shouldTransform ? "transform" : ""
21480
+ );
21481
+ };
21482
+ const applyOverlayTransform = (element, translateX, translateY) => {
21483
+ applyElementTransform(
21484
+ element,
21485
+ liveZoomScale2 !== 1 ? `translate3d(${translateX + liveZoomTranslateX2}px, ${translateY + liveZoomTranslateY2}px, 0) scale(${liveZoomScale2})` : `translate3d(${translateX}px, ${translateY}px, 0)`,
21486
+ "transform",
21487
+ "0 0"
21488
+ );
21261
21489
  };
21262
21490
  applyCanvasTransform(
21263
21491
  scrollBodyCanvasRef.current,
@@ -21304,6 +21532,26 @@ function XlsxGrid({
21304
21532
  liveZoomTranslateX2,
21305
21533
  liveZoomTranslateY2
21306
21534
  );
21535
+ applyOverlayTransform(
21536
+ canvasScrollOverlayContentRef.current,
21537
+ -nextLiveViewport.left - liveFrozenPaneRight,
21538
+ -nextLiveViewport.top - liveFrozenPaneBottom
21539
+ );
21540
+ applyOverlayTransform(
21541
+ canvasTopOverlayContentRef.current,
21542
+ -nextLiveViewport.left - liveFrozenPaneRight,
21543
+ -liveDisplayHeaderHeight
21544
+ );
21545
+ applyOverlayTransform(
21546
+ canvasLeftOverlayContentRef.current,
21547
+ -liveDisplayRowHeaderWidth,
21548
+ -nextLiveViewport.top - liveFrozenPaneBottom
21549
+ );
21550
+ applyOverlayTransform(
21551
+ canvasCornerOverlayContentRef.current,
21552
+ -liveDisplayRowHeaderWidth,
21553
+ -liveDisplayHeaderHeight
21554
+ );
21307
21555
  }, [zoomScale]);
21308
21556
  const updateLiveGestureZoomState = React4.useCallback((nextState) => {
21309
21557
  const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
@@ -21406,6 +21654,11 @@ function XlsxGrid({
21406
21654
  if (matchesLiveViewport && matchesStateViewport) {
21407
21655
  return;
21408
21656
  }
21657
+ const shouldCommitDeferredViewport = !matchesStateViewport && (stateViewport.width !== nextViewport.width || stateViewport.height !== nextViewport.height || Math.abs(nextViewport.left - stateViewport.left) >= CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX || Math.abs(nextViewport.top - stateViewport.top) >= CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX);
21658
+ if (!shouldCommitDeferredViewport) {
21659
+ pendingDrawingViewportRef.current = null;
21660
+ return;
21661
+ }
21409
21662
  pendingDrawingViewportRef.current = nextViewport;
21410
21663
  if (drawingViewportFrameRef.current !== null) {
21411
21664
  return;
@@ -21693,6 +21946,12 @@ function XlsxGrid({
21693
21946
  ) : displayRowHeaderWidth,
21694
21947
  [displayActualColWidths, displayDefaultColWidth, displayRowHeaderWidth, frozenCols, stickyLeftByCol]
21695
21948
  );
21949
+ canvasLayoutMetricsRef.current = {
21950
+ displayHeaderHeight,
21951
+ displayRowHeaderWidth,
21952
+ frozenPaneBottom,
21953
+ frozenPaneRight
21954
+ };
21696
21955
  const rowPrefixSumsRef = React4.useRef(rowPrefixSums);
21697
21956
  const colPrefixSumsRef = React4.useRef(colPrefixSums);
21698
21957
  const firstVisibleRow = visibleRows[0];
@@ -21744,6 +22003,7 @@ function XlsxGrid({
21744
22003
  }, [charts, images, shapes]);
21745
22004
  const shouldVirtualizeRows = visibleRows.length > 0;
21746
22005
  const shouldVirtualizeCols = visibleCols.length > 0 && frozenCols.length === 0;
22006
+ const shouldUseDomVirtualizer = !experimentalCanvas;
21747
22007
  const getScrollElement = React4.useCallback(() => scrollRef.current, []);
21748
22008
  const estimateRowSize = React4.useCallback(
21749
22009
  (index) => displayEffectiveRowHeights[index] ?? displayDefaultRowHeight,
@@ -21757,6 +22017,7 @@ function XlsxGrid({
21757
22017
  const getColItemKey = React4.useCallback((index) => visibleCols[index] ?? index, [visibleCols]);
21758
22018
  const rowVirtualizer = (0, import_react_virtual.useVirtualizer)({
21759
22019
  count: visibleRows.length,
22020
+ enabled: shouldUseDomVirtualizer,
21760
22021
  estimateSize: estimateRowSize,
21761
22022
  getScrollElement,
21762
22023
  getItemKey: getRowItemKey,
@@ -21764,14 +22025,15 @@ function XlsxGrid({
21764
22025
  });
21765
22026
  const colVirtualizer = (0, import_react_virtual.useVirtualizer)({
21766
22027
  count: visibleCols.length,
22028
+ enabled: shouldUseDomVirtualizer,
21767
22029
  estimateSize: estimateColSize,
21768
22030
  getScrollElement,
21769
22031
  getItemKey: getColItemKey,
21770
22032
  horizontal: true,
21771
22033
  overscan: 6
21772
22034
  });
21773
- const currentRowVirtualItems = rowVirtualizer.getVirtualItems();
21774
- const currentColVirtualItems = colVirtualizer.getVirtualItems();
22035
+ const currentRowVirtualItems = shouldUseDomVirtualizer ? rowVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;
22036
+ const currentColVirtualItems = shouldUseDomVirtualizer ? colVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;
21775
22037
  const frozenRowVirtualIndices = React4.useMemo(
21776
22038
  () => frozenRows.map((row) => rowIndexByActual.get(row)).filter((index) => index !== void 0),
21777
22039
  [frozenRows, rowIndexByActual]
@@ -22007,10 +22269,12 @@ function XlsxGrid({
22007
22269
  scroller.scrollTop = scroller.scrollTop / previousZoomFactor * zoomFactor;
22008
22270
  }
22009
22271
  previousZoomFactorRef.current = zoomFactor;
22010
- rowVirtualizer.measure();
22011
- colVirtualizer.measure();
22272
+ if (shouldUseDomVirtualizer) {
22273
+ rowVirtualizer.measure();
22274
+ colVirtualizer.measure();
22275
+ }
22012
22276
  syncDrawingViewport(scroller, { immediate: true });
22013
- }, [syncDrawingViewport, zoomFactor]);
22277
+ }, [shouldUseDomVirtualizer, syncDrawingViewport, zoomFactor]);
22014
22278
  React4.useLayoutEffect(() => {
22015
22279
  syncDrawingViewport(scrollRef.current, { immediate: true });
22016
22280
  }, [activeSheet, activeTabIndex, displayColLimit, displayRowLimit, syncDrawingViewport, zoomFactor]);
@@ -22106,7 +22370,7 @@ function XlsxGrid({
22106
22370
  const initialUsedCol = resolveFirstUsedVisibleIndex(visibleCols, activeSheet?.minUsedCol ?? -1);
22107
22371
  const initialScrollTop = initialUsedRow > 0 ? sumPrefixRange(actualRowPrefixSums, 0, initialUsedRow - 1) : 0;
22108
22372
  const initialScrollLeft = initialUsedCol > 0 ? sumPrefixRange(actualColPrefixSums, 0, initialUsedCol - 1) : 0;
22109
- if (shouldVirtualizeRows) {
22373
+ if (shouldUseDomVirtualizer && shouldVirtualizeRows) {
22110
22374
  rowVirtualizer.scrollToOffset(initialScrollTop);
22111
22375
  } else if (scrollRef.current) {
22112
22376
  scrollRef.current.scrollTop = initialScrollTop;
@@ -22114,7 +22378,7 @@ function XlsxGrid({
22114
22378
  if (scrollRef.current) {
22115
22379
  scrollRef.current.scrollLeft = initialScrollLeft;
22116
22380
  }
22117
- if (shouldVirtualizeCols && frozenCols.length === 0) {
22381
+ if (shouldUseDomVirtualizer && shouldVirtualizeCols && frozenCols.length === 0) {
22118
22382
  colVirtualizer.scrollToOffset(initialScrollLeft);
22119
22383
  }
22120
22384
  if (scrollRef.current) {
@@ -22135,6 +22399,7 @@ function XlsxGrid({
22135
22399
  isWorkerBacked,
22136
22400
  shouldVirtualizeCols,
22137
22401
  shouldVirtualizeRows,
22402
+ shouldUseDomVirtualizer,
22138
22403
  syncDrawingViewport,
22139
22404
  rowVirtualizer,
22140
22405
  visibleCols,
@@ -22154,24 +22419,40 @@ function XlsxGrid({
22154
22419
  setPendingNavigation(null);
22155
22420
  }, [activeSheetIndex, pendingNavigation, selectCell]);
22156
22421
  React4.useEffect(() => {
22157
- if (shouldVirtualizeRows) {
22422
+ if (shouldUseDomVirtualizer && shouldVirtualizeRows) {
22158
22423
  rowVirtualizer.measure();
22159
22424
  }
22160
- if (shouldVirtualizeCols) {
22425
+ if (shouldUseDomVirtualizer && shouldVirtualizeCols) {
22161
22426
  colVirtualizer.measure();
22162
22427
  }
22163
- }, [activeSheetIndex, revision, shouldVirtualizeCols, shouldVirtualizeRows, visibleCols.length, visibleRows.length]);
22428
+ }, [
22429
+ activeSheetIndex,
22430
+ revision,
22431
+ shouldUseDomVirtualizer,
22432
+ shouldVirtualizeCols,
22433
+ shouldVirtualizeRows,
22434
+ visibleCols.length,
22435
+ visibleRows.length
22436
+ ]);
22164
22437
  const handleScrollerScroll = React4.useCallback((event) => {
22165
22438
  const currentScroller = event.currentTarget;
22166
22439
  cachedScrollerRectRef.current = null;
22167
- syncDrawingViewport(currentScroller, { immediate: true });
22440
+ const paintedViewport = paintedDrawingViewportRef.current;
22441
+ const shouldSyncDrawingViewportImmediately = Math.abs(currentScroller.scrollLeft - paintedViewport.left) > CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX || Math.abs(currentScroller.scrollTop - paintedViewport.top) > CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX;
22442
+ syncDrawingViewport(currentScroller, { immediate: shouldSyncDrawingViewportImmediately });
22168
22443
  if (currentScroller.scrollHeight - (currentScroller.scrollTop + currentScroller.clientHeight) < OPEN_GRID_VERTICAL_EDGE_PX) {
22169
- setDisplayRowLimit((current) => current + OPEN_GRID_ROW_GROWTH);
22444
+ setDisplayRowLimit((current) => {
22445
+ const nextLimit = current + OPEN_GRID_ROW_GROWTH;
22446
+ return readOnly && current < maxRowDisplayLimit ? Math.min(maxRowDisplayLimit, nextLimit) : nextLimit;
22447
+ });
22170
22448
  }
22171
22449
  if (currentScroller.scrollWidth - (currentScroller.scrollLeft + currentScroller.clientWidth) < OPEN_GRID_HORIZONTAL_EDGE_PX) {
22172
- setDisplayColLimit((current) => current + OPEN_GRID_COL_GROWTH);
22450
+ setDisplayColLimit((current) => {
22451
+ const nextLimit = current + OPEN_GRID_COL_GROWTH;
22452
+ return readOnly && current < maxColDisplayLimit ? Math.min(maxColDisplayLimit, nextLimit) : nextLimit;
22453
+ });
22173
22454
  }
22174
- }, [syncDrawingViewport]);
22455
+ }, [maxColDisplayLimit, maxRowDisplayLimit, readOnly, syncDrawingViewport]);
22175
22456
  React4.useEffect(() => {
22176
22457
  const scroller = scrollRef.current;
22177
22458
  if (!scroller || !enableGestureZoom) {
@@ -23077,14 +23358,14 @@ function XlsxGrid({
23077
23358
  },
23078
23359
  left: {
23079
23360
  cols: frozenColItems,
23080
- rows: frozenRows.length > 0 ? canvasVisibleRowItems : scrollRowItems
23361
+ rows: scrollRowItems
23081
23362
  },
23082
23363
  scroll: {
23083
- cols: frozenCols.length > 0 ? canvasVisibleColItems : scrollColItems,
23084
- rows: frozenRows.length > 0 ? canvasVisibleRowItems : scrollRowItems
23364
+ cols: scrollColItems,
23365
+ rows: scrollRowItems
23085
23366
  },
23086
23367
  top: {
23087
- cols: frozenCols.length > 0 ? canvasVisibleColItems : scrollColItems,
23368
+ cols: scrollColItems,
23088
23369
  rows: frozenRowItems
23089
23370
  }
23090
23371
  };
@@ -23218,6 +23499,51 @@ function XlsxGrid({
23218
23499
  visibleRows
23219
23500
  ]
23220
23501
  );
23502
+ const shouldBakeCanvasStaticDrawings = Boolean(experimentalCanvas && readOnly && showImages);
23503
+ const shouldBakeCanvasImages = Boolean(shouldBakeCanvasStaticDrawings && !renderImage && !renderImageSelection);
23504
+ const bakedShapeRects = React4.useMemo(
23505
+ () => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => !shape.hyperlink) : [],
23506
+ [shapeRects, shouldBakeCanvasStaticDrawings]
23507
+ );
23508
+ const domShapeRects = React4.useMemo(
23509
+ () => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => shape.hyperlink) : shapeRects,
23510
+ [shapeRects, shouldBakeCanvasStaticDrawings]
23511
+ );
23512
+ const bakedFormControlRects = React4.useMemo(
23513
+ () => shouldBakeCanvasStaticDrawings ? formControlRects : [],
23514
+ [formControlRects, shouldBakeCanvasStaticDrawings]
23515
+ );
23516
+ const domFormControlRects = React4.useMemo(
23517
+ () => shouldBakeCanvasStaticDrawings ? [] : formControlRects,
23518
+ [formControlRects, shouldBakeCanvasStaticDrawings]
23519
+ );
23520
+ const bakedImageRects = React4.useMemo(
23521
+ () => shouldBakeCanvasImages ? imageRects.filter(({ image }) => !image.hyperlink && selectedImageId !== image.id) : [],
23522
+ [imageRects, selectedImageId, shouldBakeCanvasImages]
23523
+ );
23524
+ const domImageRects = React4.useMemo(
23525
+ () => shouldBakeCanvasImages ? imageRects.filter(({ image }) => image.hyperlink || selectedImageId === image.id) : imageRects,
23526
+ [imageRects, selectedImageId, shouldBakeCanvasImages]
23527
+ );
23528
+ const hasCanvasDomDrawingOverlays = Boolean(
23529
+ showImages && (chartRects.length > 0 || domShapeRects.length > 0 || domFormControlRects.length > 0 || domImageRects.length > 0)
23530
+ );
23531
+ const bakedCanvasDrawingSignature = React4.useMemo(() => {
23532
+ if (!shouldBakeCanvasStaticDrawings) {
23533
+ return "";
23534
+ }
23535
+ const shapeSignature = bakedShapeRects.map(({ rect, shape }) => `s:${shape.id}:${shape.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
23536
+ const controlSignature = bakedFormControlRects.map(({ control, rect }) => `f:${control.id}:${control.zIndex}:${Boolean(control.checked)}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
23537
+ const imageSignature = bakedImageRects.map(({ image, rect }) => `i:${image.id}:${image.src}:${image.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
23538
+ return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${controlSignature}:${imageSignature}`;
23539
+ }, [
23540
+ bakedFormControlRects,
23541
+ bakedImageRects,
23542
+ bakedShapeRects,
23543
+ canvasImageLoadVersion,
23544
+ revision,
23545
+ shouldBakeCanvasStaticDrawings
23546
+ ]);
23221
23547
  const resolveMountedCellOverlayRect = React4.useCallback((element) => {
23222
23548
  const wrapper = wrapperRef.current;
23223
23549
  if (!wrapper) {
@@ -24086,6 +24412,24 @@ function XlsxGrid({
24086
24412
  rowPrefixSums,
24087
24413
  stickyTopByRow
24088
24414
  ]);
24415
+ const scrollBodyViewportWidth = Math.max(0, drawingViewport.width - frozenPaneRight);
24416
+ const scrollBodyViewportHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);
24417
+ const canvasScrollBufferX = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportWidth);
24418
+ const canvasScrollBufferY = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportHeight);
24419
+ const scrollBodyCanvasLeft = frozenPaneRight - canvasScrollBufferX;
24420
+ const scrollBodyCanvasTop = frozenPaneBottom - canvasScrollBufferY;
24421
+ const scrollBodyCanvasWidth = scrollBodyViewportWidth + canvasScrollBufferX * 2;
24422
+ const scrollBodyCanvasHeight = scrollBodyViewportHeight + canvasScrollBufferY * 2;
24423
+ const topBodyCanvasWidth = scrollBodyCanvasWidth;
24424
+ const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);
24425
+ const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
24426
+ const leftBodyCanvasHeight = scrollBodyCanvasHeight;
24427
+ const cornerBodyCanvasWidth = leftBodyCanvasWidth;
24428
+ const cornerBodyCanvasHeight = topBodyCanvasHeight;
24429
+ const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
24430
+ const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
24431
+ const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
24432
+ const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
24089
24433
  const canvasColumnHeaderCells = React4.useMemo(
24090
24434
  () => canvasVisibleColItems.flatMap((column) => {
24091
24435
  const rect = resolveCanvasColumnHeaderRect(column.actualCol);
@@ -24098,7 +24442,7 @@ function XlsxGrid({
24098
24442
  height: displayHeaderHeight,
24099
24443
  isFrozen,
24100
24444
  left: rect.left,
24101
- localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : frozenPaneRight),
24445
+ localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : scrollBodyCanvasLeft),
24102
24446
  width: rect.width
24103
24447
  }];
24104
24448
  }),
@@ -24106,8 +24450,8 @@ function XlsxGrid({
24106
24450
  canvasVisibleColItems,
24107
24451
  displayHeaderHeight,
24108
24452
  displayRowHeaderWidth,
24109
- frozenPaneRight,
24110
24453
  resolveCanvasColumnHeaderRect,
24454
+ scrollBodyCanvasLeft,
24111
24455
  stickyLeftByCol
24112
24456
  ]
24113
24457
  );
@@ -24122,15 +24466,15 @@ function XlsxGrid({
24122
24466
  actualRow: row.actualRow,
24123
24467
  height: rect.height,
24124
24468
  isFrozen,
24125
- localTop: rect.top - (isFrozen ? displayHeaderHeight : frozenPaneBottom),
24469
+ localTop: rect.top - (isFrozen ? displayHeaderHeight : scrollBodyCanvasTop),
24126
24470
  top: rect.top
24127
24471
  }];
24128
24472
  }),
24129
24473
  [
24130
24474
  canvasVisibleRowItems,
24131
24475
  displayHeaderHeight,
24132
- frozenPaneBottom,
24133
24476
  resolveCanvasRowHeaderRect,
24477
+ scrollBodyCanvasTop,
24134
24478
  stickyTopByRow
24135
24479
  ]
24136
24480
  );
@@ -24397,10 +24741,46 @@ function XlsxGrid({
24397
24741
  rowPrefixSums,
24398
24742
  startCellSelection
24399
24743
  ]);
24744
+ const getCanvasImage = React4.useCallback((image) => {
24745
+ if (typeof Image === "undefined") {
24746
+ return null;
24747
+ }
24748
+ const cacheKey = image.id;
24749
+ const cached = canvasImageCacheRef.current.get(cacheKey);
24750
+ if (cached && cached.src === image.src) {
24751
+ return cached.loaded && !cached.failed ? cached.image : null;
24752
+ }
24753
+ const imageElement = new Image();
24754
+ const entry = {
24755
+ failed: false,
24756
+ image: imageElement,
24757
+ loaded: false,
24758
+ src: image.src
24759
+ };
24760
+ canvasImageCacheRef.current.set(cacheKey, entry);
24761
+ imageElement.onload = () => {
24762
+ entry.loaded = true;
24763
+ setCanvasImageLoadVersion((version) => version + 1);
24764
+ };
24765
+ imageElement.onerror = () => {
24766
+ entry.failed = true;
24767
+ setCanvasImageLoadVersion((version) => version + 1);
24768
+ };
24769
+ imageElement.src = image.src;
24770
+ return null;
24771
+ }, []);
24400
24772
  React4.useLayoutEffect(() => {
24401
24773
  if (!experimentalCanvas) {
24402
24774
  return;
24403
24775
  }
24776
+ const canvasProfileTarget = typeof window !== "undefined" ? window.__xlsxCanvasProfile : void 0;
24777
+ const canvasProfileStart = canvasProfileTarget ? performance.now() : 0;
24778
+ let canvasProfileBodyStart = 0;
24779
+ let canvasProfileBodyMs = 0;
24780
+ let canvasProfileCulledCells = 0;
24781
+ let canvasProfileDirtyRects = 0;
24782
+ let canvasProfileLookedUpCells = 0;
24783
+ let canvasProfilePaintedCells = 0;
24404
24784
  const dpr = typeof window === "undefined" ? 1 : Math.max(1, window.devicePixelRatio || 1);
24405
24785
  function configureCanvas(canvas, width, height, options) {
24406
24786
  if (!canvas) {
@@ -24420,7 +24800,7 @@ function XlsxGrid({
24420
24800
  if (canvas.style.height !== `${height}px`) {
24421
24801
  canvas.style.height = `${height}px`;
24422
24802
  }
24423
- const context = canvas.getContext("2d");
24803
+ const context = canvas.getContext("2d", { alpha: false });
24424
24804
  if (!context) {
24425
24805
  return null;
24426
24806
  }
@@ -24439,6 +24819,7 @@ function XlsxGrid({
24439
24819
  const rangeSignature = buildRangeSignature(normalizedVisibleRange);
24440
24820
  const nextBodyCanvasSignature = {
24441
24821
  activeSheet: activeSheet ?? null,
24822
+ bakedDrawingSignature: bakedCanvasDrawingSignature,
24442
24823
  bodyHeight,
24443
24824
  bodyWidth,
24444
24825
  colSignature: buildRenderedAxisSignature(canvasVisibleColItems, (item) => item.index),
@@ -24466,10 +24847,10 @@ function XlsxGrid({
24466
24847
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
24467
24848
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
24468
24849
  const previousPaintedViewport = paintedDrawingViewportRef.current;
24469
- const shouldRepaintBody = !(previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.colSignature === nextBodyCanvasSignature.colSignature && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.rowSignature === nextBodyCanvasSignature.rowSignature && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor);
24850
+ const shouldRepaintBody = !(previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.colSignature === nextBodyCanvasSignature.colSignature && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.rowSignature === nextBodyCanvasSignature.rowSignature && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor);
24470
24851
  const shouldRepaintHeaders = !(previousHeaderCanvasSignature && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth && previousHeaderCanvasSignature.colSignature === nextHeaderCanvasSignature.colSignature && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth && previousHeaderCanvasSignature.rowSignature === nextHeaderCanvasSignature.rowSignature && previousHeaderCanvasSignature.viewportLeft === nextHeaderCanvasSignature.viewportLeft && previousHeaderCanvasSignature.viewportTop === nextHeaderCanvasSignature.viewportTop && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor);
24471
24852
  const canBlitBody = Boolean(
24472
- shouldRepaintBody && previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor
24853
+ shouldRepaintBody && previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor
24473
24854
  );
24474
24855
  const canBlitTopHeader = Boolean(
24475
24856
  shouldRepaintHeaders && previousHeaderCanvasSignature && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor
@@ -24480,49 +24861,37 @@ function XlsxGrid({
24480
24861
  if (!shouldRepaintBody && !shouldRepaintHeaders) {
24481
24862
  return;
24482
24863
  }
24483
- const scrollBodyCanvasWidth2 = Math.max(0, drawingViewport.width - frozenPaneRight);
24484
- const scrollBodyCanvasHeight2 = Math.max(0, drawingViewport.height - frozenPaneBottom);
24485
- const topBodyCanvasWidth2 = scrollBodyCanvasWidth2;
24486
- const topBodyCanvasHeight2 = Math.max(0, frozenPaneBottom - displayHeaderHeight);
24487
- const leftBodyCanvasWidth2 = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
24488
- const leftBodyCanvasHeight2 = scrollBodyCanvasHeight2;
24489
- const cornerBodyCanvasWidth2 = leftBodyCanvasWidth2;
24490
- const cornerBodyCanvasHeight2 = topBodyCanvasHeight2;
24491
- const topFrozenHeaderCanvasWidth2 = leftBodyCanvasWidth2;
24492
- const topScrollHeaderCanvasWidth2 = scrollBodyCanvasWidth2;
24493
- const leftFrozenHeaderCanvasHeight2 = topBodyCanvasHeight2;
24494
- const leftScrollHeaderCanvasHeight2 = scrollBodyCanvasHeight2;
24495
24864
  const paneBounds = {
24496
24865
  corner: {
24497
- height: cornerBodyCanvasHeight2,
24866
+ height: cornerBodyCanvasHeight,
24498
24867
  left: displayRowHeaderWidth,
24499
24868
  top: displayHeaderHeight,
24500
- width: cornerBodyCanvasWidth2
24869
+ width: cornerBodyCanvasWidth
24501
24870
  },
24502
24871
  left: {
24503
- height: leftBodyCanvasHeight2,
24872
+ height: leftBodyCanvasHeight,
24504
24873
  left: displayRowHeaderWidth,
24505
- top: frozenPaneBottom,
24506
- width: leftBodyCanvasWidth2
24874
+ top: scrollBodyCanvasTop,
24875
+ width: leftBodyCanvasWidth
24507
24876
  },
24508
24877
  scroll: {
24509
- height: scrollBodyCanvasHeight2,
24510
- left: frozenPaneRight,
24511
- top: frozenPaneBottom,
24512
- width: scrollBodyCanvasWidth2
24878
+ height: scrollBodyCanvasHeight,
24879
+ left: scrollBodyCanvasLeft,
24880
+ top: scrollBodyCanvasTop,
24881
+ width: scrollBodyCanvasWidth
24513
24882
  },
24514
24883
  top: {
24515
- height: topBodyCanvasHeight2,
24516
- left: frozenPaneRight,
24884
+ height: topBodyCanvasHeight,
24885
+ left: scrollBodyCanvasLeft,
24517
24886
  top: displayHeaderHeight,
24518
- width: topBodyCanvasWidth2
24887
+ width: topBodyCanvasWidth
24519
24888
  }
24520
24889
  };
24521
24890
  const bodyContexts = shouldRepaintBody ? {
24522
- corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth2, cornerBodyCanvasHeight2, { clear: !canBlitBody }),
24523
- left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth2, leftBodyCanvasHeight2, { clear: !canBlitBody }),
24524
- scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth2, scrollBodyCanvasHeight2, { clear: !canBlitBody }),
24525
- top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth2, topBodyCanvasHeight2, { clear: !canBlitBody })
24891
+ corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth, cornerBodyCanvasHeight, { clear: !canBlitBody }),
24892
+ left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth, leftBodyCanvasHeight, { clear: !canBlitBody }),
24893
+ scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth, scrollBodyCanvasHeight, { clear: !canBlitBody }),
24894
+ top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth, topBodyCanvasHeight, { clear: !canBlitBody })
24526
24895
  } : {
24527
24896
  corner: null,
24528
24897
  left: null,
@@ -24530,21 +24899,21 @@ function XlsxGrid({
24530
24899
  top: null
24531
24900
  };
24532
24901
  const topHeaderContexts = shouldRepaintHeaders ? {
24533
- frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth2, headerHeight),
24534
- scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth2, headerHeight, { clear: !canBlitTopHeader })
24902
+ frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth, headerHeight),
24903
+ scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth, headerHeight, { clear: !canBlitTopHeader })
24535
24904
  } : {
24536
24905
  frozen: null,
24537
24906
  scroll: null
24538
24907
  };
24539
24908
  const leftHeaderContexts = shouldRepaintHeaders ? {
24540
- frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight2),
24541
- scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight2, { clear: !canBlitLeftHeader })
24909
+ frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight),
24910
+ scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight, { clear: !canBlitLeftHeader })
24542
24911
  } : {
24543
24912
  frozen: null,
24544
24913
  scroll: null
24545
24914
  };
24546
24915
  const cornerContext = shouldRepaintHeaders ? configureCanvas(cornerHeaderCanvasRef.current, rowHeaderWidth, headerHeight) : null;
24547
- if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!cornerContext || topFrozenHeaderCanvasWidth2 > 0 && !topHeaderContexts.frozen || topScrollHeaderCanvasWidth2 > 0 && !topHeaderContexts.scroll || leftFrozenHeaderCanvasHeight2 > 0 && !leftHeaderContexts.frozen || leftScrollHeaderCanvasHeight2 > 0 && !leftHeaderContexts.scroll)) {
24916
+ if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!cornerContext || topFrozenHeaderCanvasWidth > 0 && !topHeaderContexts.frozen || topScrollHeaderCanvasWidth > 0 && !topHeaderContexts.scroll || leftFrozenHeaderCanvasHeight > 0 && !leftHeaderContexts.frozen || leftScrollHeaderCanvasHeight > 0 && !leftHeaderContexts.scroll)) {
24548
24917
  return;
24549
24918
  }
24550
24919
  const showGridLines = activeSheet?.showGridLines ?? true;
@@ -24561,10 +24930,282 @@ function XlsxGrid({
24561
24930
  scroll: [],
24562
24931
  top: []
24563
24932
  };
24564
- let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth2, headerHeight);
24565
- let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight2);
24933
+ let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth, headerHeight);
24934
+ let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight);
24935
+ const bakedCanvasDrawingEntries = shouldBakeCanvasStaticDrawings ? [
24936
+ ...bakedShapeRects.map(({ rect, shape }) => ({
24937
+ kind: "shape",
24938
+ rect,
24939
+ shape,
24940
+ zIndex: shape.zIndex
24941
+ })),
24942
+ ...bakedFormControlRects.map(({ control, rect }) => ({
24943
+ control,
24944
+ kind: "formControl",
24945
+ rect,
24946
+ zIndex: control.zIndex
24947
+ })),
24948
+ ...bakedImageRects.map(({ image, rect }) => ({
24949
+ image,
24950
+ kind: "image",
24951
+ rect,
24952
+ zIndex: image.zIndex
24953
+ }))
24954
+ ].sort((left, right) => left.zIndex - right.zIndex) : [];
24955
+ const resolveBakedCanvasLocalRect = (rect, pane) => {
24956
+ const bounds = paneBounds[pane];
24957
+ const scrollX = pane === "scroll" || pane === "top" ? drawingViewport.left : 0;
24958
+ const scrollY = pane === "scroll" || pane === "left" ? drawingViewport.top : 0;
24959
+ return {
24960
+ height: rect.height,
24961
+ left: rect.left - scrollX - bounds.left,
24962
+ top: rect.top - scrollY - bounds.top,
24963
+ width: rect.width
24964
+ };
24965
+ };
24966
+ const drawBakedShapeText = (context, shape, left, top, width, height) => {
24967
+ if (shape.paragraphs.length === 0) {
24968
+ return;
24969
+ }
24970
+ const inset = shape.textBox?.insetPx;
24971
+ const paddingLeft = (inset?.left ?? 6) * zoomFactor;
24972
+ const paddingRight = (inset?.right ?? 6) * zoomFactor;
24973
+ const paddingTop = (inset?.top ?? 4) * zoomFactor;
24974
+ const paddingBottom = (inset?.bottom ?? 4) * zoomFactor;
24975
+ const textLeft = left + paddingLeft;
24976
+ const textTop = top + paddingTop;
24977
+ const textWidth = Math.max(0, width - paddingLeft - paddingRight);
24978
+ const textHeight = Math.max(0, height - paddingTop - paddingBottom);
24979
+ if (textWidth <= 0 || textHeight <= 0) {
24980
+ return;
24981
+ }
24982
+ const lineMetrics = shape.paragraphs.map((paragraph) => {
24983
+ const lineHeight = Math.max(
24984
+ 12 * zoomFactor,
24985
+ ...paragraph.runs.map((run) => (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor * 1.2)
24986
+ );
24987
+ const widthPx = paragraph.runs.reduce((total, run) => {
24988
+ const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
24989
+ context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
24990
+ return total + measureCanvasTextWidth(context, run.text);
24991
+ }, 0);
24992
+ return { lineHeight, widthPx };
24993
+ });
24994
+ const totalTextHeight = lineMetrics.reduce((total, metric) => total + metric.lineHeight, 0);
24995
+ let y = textTop;
24996
+ if (shape.textBox?.verticalAlign === "middle") {
24997
+ y = textTop + Math.max(0, (textHeight - totalTextHeight) / 2);
24998
+ } else if (shape.textBox?.verticalAlign === "bottom") {
24999
+ y = textTop + Math.max(0, textHeight - totalTextHeight);
25000
+ }
25001
+ context.save();
25002
+ context.beginPath();
25003
+ context.rect(textLeft, textTop, textWidth, textHeight);
25004
+ context.clip();
25005
+ context.textBaseline = "middle";
25006
+ shape.paragraphs.forEach((paragraph, paragraphIndex) => {
25007
+ const metric = lineMetrics[paragraphIndex] ?? { lineHeight: 14 * zoomFactor, widthPx: 0 };
25008
+ let x = textLeft;
25009
+ const align = paragraph.align ?? shape.textBox?.horizontalAlign ?? "left";
25010
+ if (align === "center") {
25011
+ x = textLeft + Math.max(0, (textWidth - metric.widthPx) / 2);
25012
+ } else if (align === "right") {
25013
+ x = textLeft + Math.max(0, textWidth - metric.widthPx);
25014
+ }
25015
+ paragraph.runs.forEach((run) => {
25016
+ const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
25017
+ context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
25018
+ context.fillStyle = run.color ?? "#000000";
25019
+ context.textAlign = "left";
25020
+ const textY = y + metric.lineHeight / 2;
25021
+ context.fillText(run.text, x, textY);
25022
+ if (run.underline && run.text.length > 0) {
25023
+ const textWidthPx = measureCanvasTextWidth(context, run.text);
25024
+ context.strokeStyle = run.color ?? "#000000";
25025
+ context.lineWidth = Math.max(1, zoomFactor * 0.75);
25026
+ context.beginPath();
25027
+ context.moveTo(x, textY + Math.max(2, fontSize * 0.28));
25028
+ context.lineTo(x + textWidthPx, textY + Math.max(2, fontSize * 0.28));
25029
+ context.stroke();
25030
+ }
25031
+ x += measureCanvasTextWidth(context, run.text);
25032
+ });
25033
+ y += metric.lineHeight;
25034
+ });
25035
+ context.restore();
25036
+ };
25037
+ const drawBakedShape = (context, shape, rect) => {
25038
+ const fillColor = shape.fill?.none ? "transparent" : shape.fill?.color ?? "transparent";
25039
+ const strokeColor = shape.stroke?.none ? "transparent" : shape.stroke?.color ?? "transparent";
25040
+ const lineWidth = Math.max(0, (shape.stroke?.widthPx ?? (shape.geometry === "line" ? 2 : 1)) * zoomFactor);
25041
+ const vectorShape = resolveShapeVector(shape);
25042
+ const opacity = Math.min(shape.fill?.opacity ?? 1, shape.stroke?.opacity ?? 1);
25043
+ context.save();
25044
+ context.translate(rect.left + rect.width / 2, rect.top + rect.height / 2);
25045
+ if (shape.rotationDeg) {
25046
+ context.rotate(shape.rotationDeg * Math.PI / 180);
25047
+ }
25048
+ context.scale(shape.flipH ? -1 : 1, shape.flipV ? -1 : 1);
25049
+ context.globalAlpha *= opacity;
25050
+ context.lineWidth = lineWidth;
25051
+ context.strokeStyle = strokeColor;
25052
+ context.fillStyle = fillColor;
25053
+ applyCanvasShapeDash(context, shape.stroke?.dash, lineWidth);
25054
+ const localLeft = -rect.width / 2;
25055
+ const localTop = -rect.height / 2;
25056
+ if (vectorShape && typeof Path2D !== "undefined") {
25057
+ context.save();
25058
+ context.translate(localLeft, localTop);
25059
+ context.scale(
25060
+ rect.width / Math.max(1, vectorShape.viewBox.width),
25061
+ rect.height / Math.max(1, vectorShape.viewBox.height)
25062
+ );
25063
+ const path = getCachedCanvasPath2D(vectorShape.path);
25064
+ if (!path) {
25065
+ context.restore();
25066
+ context.restore();
25067
+ return;
25068
+ }
25069
+ if (fillColor !== "transparent") {
25070
+ context.fill(path);
25071
+ }
25072
+ if (strokeColor !== "transparent" && lineWidth > 0) {
25073
+ context.stroke(path);
25074
+ }
25075
+ context.restore();
25076
+ } else if (shape.geometry === "ellipse") {
25077
+ context.beginPath();
25078
+ context.ellipse(0, 0, rect.width / 2, rect.height / 2, 0, 0, Math.PI * 2);
25079
+ if (fillColor !== "transparent") {
25080
+ context.fill();
25081
+ }
25082
+ if (strokeColor !== "transparent" && lineWidth > 0) {
25083
+ context.stroke();
25084
+ }
25085
+ } else {
25086
+ const radius = shape.geometry === "roundRect" ? 12 * zoomFactor : 0;
25087
+ drawCanvasRoundedRect(context, localLeft, localTop, rect.width, rect.height, radius);
25088
+ if (fillColor !== "transparent") {
25089
+ context.fill();
25090
+ }
25091
+ if (strokeColor !== "transparent" && lineWidth > 0) {
25092
+ context.stroke();
25093
+ }
25094
+ }
25095
+ drawBakedShapeText(context, shape, localLeft, localTop, rect.width, rect.height);
25096
+ context.restore();
25097
+ };
25098
+ const drawBakedFormControl = (context, control, rect) => {
25099
+ const label = resolveFormControlLabel(control);
25100
+ const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
25101
+ const textColor = control.textColor ?? "#000000";
25102
+ const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
25103
+ const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
25104
+ context.save();
25105
+ context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
25106
+ context.textBaseline = "middle";
25107
+ context.fillStyle = textColor;
25108
+ context.strokeStyle = stroke;
25109
+ context.lineWidth = Math.max(1, zoomFactor);
25110
+ if (control.kind === "group-box") {
25111
+ const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;
25112
+ drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);
25113
+ context.stroke();
25114
+ if (label) {
25115
+ context.fillStyle = SHEET_SURFACE;
25116
+ const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);
25117
+ context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);
25118
+ context.fillStyle = textColor;
25119
+ context.textAlign = "left";
25120
+ context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);
25121
+ }
25122
+ context.restore();
25123
+ return;
25124
+ }
25125
+ if (control.kind === "button" || control.kind === "dropdown" || control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
25126
+ if (control.kind === "button") {
25127
+ const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);
25128
+ gradient.addColorStop(0, "#f8fafc");
25129
+ gradient.addColorStop(1, "#e2e8f0");
25130
+ context.fillStyle = gradient;
25131
+ } else {
25132
+ context.fillStyle = "transparent";
25133
+ }
25134
+ drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === "button" ? 4 * zoomFactor : 2 * zoomFactor);
25135
+ if (control.kind === "button") {
25136
+ context.fill();
25137
+ }
25138
+ context.stroke();
25139
+ }
25140
+ let textLeft = rect.left + 2 * zoomFactor;
25141
+ const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
25142
+ if (control.kind === "checkbox") {
25143
+ context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
25144
+ if (control.checked) {
25145
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
25146
+ context.fillRect(rect.left + 2 * zoomFactor + 1.5, rect.top + (rect.height - iconSize) / 2 + 1.5, Math.max(0, iconSize - 3), Math.max(0, iconSize - 3));
25147
+ context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
25148
+ context.beginPath();
25149
+ context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
25150
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
25151
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
25152
+ context.stroke();
25153
+ }
25154
+ textLeft += iconSize + 4 * zoomFactor;
25155
+ } else if (control.kind === "radio") {
25156
+ context.beginPath();
25157
+ context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
25158
+ context.stroke();
25159
+ if (control.checked) {
25160
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
25161
+ context.beginPath();
25162
+ context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
25163
+ context.fill();
25164
+ }
25165
+ textLeft += iconSize + 4 * zoomFactor;
25166
+ }
25167
+ if (label) {
25168
+ const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);
25169
+ const text = truncateCanvasText(context, label, maxTextWidth);
25170
+ context.fillStyle = textColor;
25171
+ context.textAlign = control.textAlign === "right" ? "right" : control.textAlign === "center" ? "center" : "left";
25172
+ const textX = context.textAlign === "right" ? rect.left + rect.width - textRightInset : context.textAlign === "center" ? textLeft + maxTextWidth / 2 : textLeft;
25173
+ context.fillText(text, textX, rect.top + rect.height / 2);
25174
+ }
25175
+ if (control.kind === "dropdown") {
25176
+ context.fillStyle = textColor;
25177
+ context.textAlign = "center";
25178
+ context.fillText("\u25BC", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);
25179
+ }
25180
+ context.restore();
25181
+ };
25182
+ const drawBakedCanvasDrawings = (pane, context, dirtyRects) => {
25183
+ if (bakedCanvasDrawingEntries.length === 0 || dirtyRects.length === 0) {
25184
+ return;
25185
+ }
25186
+ for (const entry of bakedCanvasDrawingEntries) {
25187
+ if (resolveDrawingPane(entry.rect) !== pane) {
25188
+ continue;
25189
+ }
25190
+ const localRect = resolveBakedCanvasLocalRect(entry.rect, pane);
25191
+ if (localRect.left + localRect.width < 0 || localRect.top + localRect.height < 0 || localRect.left > paneBounds[pane].width || localRect.top > paneBounds[pane].height || !intersectsCanvasDirtyRects(localRect.left, localRect.top, localRect.width, localRect.height, dirtyRects)) {
25192
+ continue;
25193
+ }
25194
+ if (entry.kind === "shape") {
25195
+ drawBakedShape(context, entry.shape, localRect);
25196
+ } else if (entry.kind === "formControl") {
25197
+ drawBakedFormControl(context, entry.control, localRect);
25198
+ } else {
25199
+ const imageElement = getCanvasImage(entry.image);
25200
+ if (imageElement) {
25201
+ context.drawImage(imageElement, localRect.left, localRect.top, localRect.width, localRect.height);
25202
+ }
25203
+ }
25204
+ }
25205
+ };
24566
25206
  const cellPaneOrder = ["scroll", "top", "left", "corner"];
24567
25207
  if (shouldRepaintBody) {
25208
+ canvasProfileBodyStart = canvasProfileTarget ? performance.now() : 0;
24568
25209
  for (const pane of Object.keys(bodyContexts)) {
24569
25210
  const context = bodyContexts[pane];
24570
25211
  const bounds = paneBounds[pane];
@@ -24595,6 +25236,7 @@ function XlsxGrid({
24595
25236
  context.clearRect(0, 0, bounds.width, bounds.height);
24596
25237
  }
24597
25238
  bodyDirtyRectsByPane[pane] = dirtyRects;
25239
+ canvasProfileDirtyRects += dirtyRects.length;
24598
25240
  if (dirtyRects.length === 0) {
24599
25241
  continue;
24600
25242
  }
@@ -24611,6 +25253,22 @@ function XlsxGrid({
24611
25253
  if (!paneContext || paneDirtyRects.length === 0 || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
24612
25254
  continue;
24613
25255
  }
25256
+ let hasPendingGridlinePath = false;
25257
+ const enqueueGridlinePath = () => {
25258
+ if (!hasPendingGridlinePath) {
25259
+ paneContext.beginPath();
25260
+ hasPendingGridlinePath = true;
25261
+ }
25262
+ };
25263
+ const flushPendingGridlines = () => {
25264
+ if (!hasPendingGridlinePath) {
25265
+ return;
25266
+ }
25267
+ paneContext.strokeStyle = palette.border;
25268
+ paneContext.lineWidth = 1;
25269
+ paneContext.stroke();
25270
+ hasPendingGridlinePath = false;
25271
+ };
24614
25272
  const drawnMergedAnchorKeys = /* @__PURE__ */ new Set();
24615
25273
  for (const rowItem of paneAxisItems.rows) {
24616
25274
  for (const colItem of paneAxisItems.cols) {
@@ -24618,6 +25276,33 @@ function XlsxGrid({
24618
25276
  const anchorCell = resolveMergeAnchorCell(cell);
24619
25277
  const anchorKey = `${anchorCell.row}:${anchorCell.col}`;
24620
25278
  let drawCell = anchorCell;
25279
+ const drawRowIndex = rowIndexByActual.get(drawCell.row);
25280
+ const drawColIndex = colIndexByActual.get(drawCell.col);
25281
+ if (drawRowIndex === void 0 || drawColIndex === void 0) {
25282
+ continue;
25283
+ }
25284
+ const baseCellLeft = displayRowHeaderWidth + (colPrefixSums[drawColIndex] ?? 0);
25285
+ const baseCellTop = displayHeaderHeight + (rowPrefixSums[drawRowIndex] ?? 0);
25286
+ const useFrozenHorizontalPosition = pane === "left" || pane === "corner";
25287
+ const useFrozenVerticalPosition = pane === "top" || pane === "corner";
25288
+ const roughLocalRect = {
25289
+ height: displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size,
25290
+ left: (useFrozenHorizontalPosition ? stickyLeftByCol.get(drawCell.col) ?? baseCellLeft - drawingViewport.left : baseCellLeft - drawingViewport.left) - paneBoundsForCell.left,
25291
+ top: (useFrozenVerticalPosition ? stickyTopByRow.get(drawCell.row) ?? baseCellTop - drawingViewport.top : baseCellTop - drawingViewport.top) - paneBoundsForCell.top,
25292
+ width: displayEffectiveColWidths[drawColIndex] ?? colItem.size
25293
+ };
25294
+ const isMergedSecondaryProbe = anchorCell.row !== cell.row || anchorCell.col !== cell.col;
25295
+ if (!isMergedSecondaryProbe && !intersectsCanvasDirtyRects(
25296
+ roughLocalRect.left - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,
25297
+ roughLocalRect.top - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,
25298
+ roughLocalRect.width + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,
25299
+ roughLocalRect.height + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,
25300
+ paneDirtyRects
25301
+ )) {
25302
+ canvasProfileCulledCells += 1;
25303
+ continue;
25304
+ }
25305
+ canvasProfileLookedUpCells += 1;
24621
25306
  let cellData = getCellData(drawCell.row, drawCell.col);
24622
25307
  if ((cellData.colSpan || cellData.rowSpan) && drawnMergedAnchorKeys.has(anchorKey)) {
24623
25308
  continue;
@@ -24628,16 +25313,9 @@ function XlsxGrid({
24628
25313
  if (cellData.colSpan || cellData.rowSpan) {
24629
25314
  drawnMergedAnchorKeys.add(anchorKey);
24630
25315
  }
24631
- const drawRowIndex = rowIndexByActual.get(drawCell.row);
24632
- const drawColIndex = colIndexByActual.get(drawCell.col);
24633
- if (drawRowIndex === void 0 || drawColIndex === void 0) {
24634
- continue;
24635
- }
24636
25316
  const displayRect = cellData.colSpan || cellData.rowSpan ? resolveCellDisplayRect(drawCell) : null;
24637
- const baseLeft = displayRect?.left ?? displayRowHeaderWidth + (colPrefixSums[drawColIndex] ?? 0);
24638
- const baseTop = displayRect?.top ?? displayHeaderHeight + (rowPrefixSums[drawRowIndex] ?? 0);
24639
- const useFrozenHorizontalPosition = pane === "left" || pane === "corner";
24640
- const useFrozenVerticalPosition = pane === "top" || pane === "corner";
25317
+ const baseLeft = displayRect?.left ?? baseCellLeft;
25318
+ const baseTop = displayRect?.top ?? baseCellTop;
24641
25319
  const localRect = {
24642
25320
  height: displayRect?.height ?? (displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size),
24643
25321
  left: (useFrozenHorizontalPosition ? stickyLeftByCol.get(drawCell.col) ?? baseLeft - drawingViewport.left : baseLeft - drawingViewport.left) - paneBoundsForCell.left,
@@ -24649,13 +25327,18 @@ function XlsxGrid({
24649
25327
  continue;
24650
25328
  }
24651
25329
  if (!intersectsCanvasDirtyRects(localRect.left, localRect.top, drawableWidth, localRect.height, paneDirtyRects)) {
25330
+ canvasProfileCulledCells += 1;
24652
25331
  continue;
24653
25332
  }
25333
+ canvasProfilePaintedCells += 1;
24654
25334
  const cellStyle = cellData.style;
24655
25335
  const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellStyle);
24656
25336
  const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : sheetSurface);
24657
25337
  const gradientFill = !cellData.conditionalColorScale && typeof cellStyle.backgroundImage === "string" ? resolveCanvasGradientFill(paneContext, localRect, cellStyle.backgroundImage) : null;
24658
25338
  const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== sheetSurface;
25339
+ if (hasExplicitCellFill || cellData.chartHighlight) {
25340
+ flushPendingGridlines();
25341
+ }
24659
25342
  paneContext.fillStyle = gradientFill ?? fillColor;
24660
25343
  paneContext.fillRect(localRect.left, localRect.top, localRect.width, localRect.height);
24661
25344
  if (cellData.chartHighlight) {
@@ -24701,10 +25384,8 @@ function XlsxGrid({
24701
25384
  const bottomNeighborTopBorder = bottomNeighborData?.isMergedSecondary ? null : bottomNeighborData?.canvas?.topBorder ?? parseCanvasBorderDeclaration(bottomNeighborData?.style.borderTop);
24702
25385
  const resolvedRightBorder = resolveCanvasBoundaryBorder(rightBorder, rightNeighborLeftBorder);
24703
25386
  const resolvedBottomBorder = resolveCanvasBoundaryBorder(bottomBorder, bottomNeighborTopBorder);
24704
- if (showGridLines && !hasExplicitCellFill) {
24705
- paneContext.strokeStyle = palette.border;
24706
- paneContext.lineWidth = 1;
24707
- paneContext.beginPath();
25387
+ if (showGridLines && !hasExplicitCellFill && (!resolvedRightBorder || !resolvedBottomBorder)) {
25388
+ enqueueGridlinePath();
24708
25389
  if (!resolvedRightBorder) {
24709
25390
  paneContext.moveTo(localRect.left + localRect.width - 0.5, localRect.top);
24710
25391
  paneContext.lineTo(localRect.left + localRect.width - 0.5, localRect.top + localRect.height);
@@ -24713,7 +25394,9 @@ function XlsxGrid({
24713
25394
  paneContext.moveTo(localRect.left, localRect.top + localRect.height - 0.5);
24714
25395
  paneContext.lineTo(localRect.left + localRect.width, localRect.top + localRect.height - 0.5);
24715
25396
  }
24716
- paneContext.stroke();
25397
+ }
25398
+ if (topBorder || resolvedRightBorder || resolvedBottomBorder || leftBorder || cellData.chartHighlight) {
25399
+ flushPendingGridlines();
24717
25400
  }
24718
25401
  if (topBorder && drawRowIndex === 0) {
24719
25402
  strokeCanvasBorderSide(paneContext, "top", localRect, topBorder);
@@ -24746,17 +25429,26 @@ function XlsxGrid({
24746
25429
  strokeCanvasBorderSide(paneContext, "left", localRect, highlightBorder);
24747
25430
  }
24748
25431
  }
25432
+ const rawText = cellData.value ?? "";
25433
+ const shouldDrawCanvasContent = cellData.checkboxState != null || cellData.sparkline || rawText.length > 0 || cellData.conditionalIcon || cellData.isTableHeader;
25434
+ if (!shouldDrawCanvasContent) {
25435
+ continue;
25436
+ }
24749
25437
  const padding = canvasCellStyle.padding;
24750
25438
  const contentLeft = localRect.left + padding.left;
24751
25439
  const contentTop = localRect.top + padding.top;
24752
25440
  const contentWidth = Math.max(0, localRect.width - padding.left - padding.right);
24753
25441
  const contentHeight = Math.max(0, localRect.height - padding.top - padding.bottom);
25442
+ if (contentWidth <= 0 || contentHeight <= 0) {
25443
+ continue;
25444
+ }
24754
25445
  const activeFontSizePx = cellData.shrinkToFitFontSizePx ?? resolveCanvasFontSizePx(cellStyle, 12 * zoomFactor);
24755
25446
  const textClipOverscan = Math.max(
24756
25447
  1,
24757
25448
  zoomFactor * 1.5,
24758
25449
  activeFontSizePx * (cellData.textRotationDeg ? 0.75 : 0.18)
24759
25450
  );
25451
+ flushPendingGridlines();
24760
25452
  paneContext.save();
24761
25453
  paneContext.beginPath();
24762
25454
  paneContext.rect(
@@ -24888,7 +25580,6 @@ function XlsxGrid({
24888
25580
  const trailingInset = (cellData.conditionalIcon ? 18 * zoomFactor : 0) + (cellData.isTableHeader ? 16 * zoomFactor : 0);
24889
25581
  const spillMaxWidth = cellData.spillWidth && cellData.spillWidth > 0 ? Math.max(0, cellData.spillWidth - trailingInset) : null;
24890
25582
  const maxTextWidth = spillMaxWidth ?? Math.max(0, contentWidth - trailingInset);
24891
- const rawText = cellData.value ?? "";
24892
25583
  const textColor = canvasCellStyle.textColor;
24893
25584
  const shouldEllipsizeText = canvasCellStyle.textOverflowEllipsis;
24894
25585
  const shouldWrapText = canvasCellStyle.usesWrappedText || rawText.includes("\n");
@@ -24920,7 +25611,7 @@ function XlsxGrid({
24920
25611
  const textY = textBlockTop + lineIndex * lineHeight + lineHeight / 2;
24921
25612
  paneContext.fillText(line, textX, textY);
24922
25613
  if (canvasCellStyle.textDecoration?.includes("underline") && line.length > 0) {
24923
- const measured = Math.min(maxTextWidth, paneContext.measureText(line).width);
25614
+ const measured = Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, line));
24924
25615
  const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
24925
25616
  paneContext.beginPath();
24926
25617
  paneContext.moveTo(underlineStartX, textY + Math.max(2, lineHeight * 0.24));
@@ -24954,7 +25645,7 @@ function XlsxGrid({
24954
25645
  const textY = contentTop + contentHeight / 2;
24955
25646
  paneContext.fillText(text, textX, textY);
24956
25647
  if (canvasCellStyle.textDecoration?.includes("underline") && text.length > 0) {
24957
- const measured = shouldEllipsizeText ? Math.min(maxTextWidth, paneContext.measureText(text).width) : paneContext.measureText(text).width;
25648
+ const measured = shouldEllipsizeText ? Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, text)) : measureCanvasTextWidth(paneContext, text);
24958
25649
  const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
24959
25650
  paneContext.beginPath();
24960
25651
  paneContext.moveTo(underlineStartX, textY + 6 * zoomFactor);
@@ -24979,6 +25670,7 @@ function XlsxGrid({
24979
25670
  paneContext.restore();
24980
25671
  }
24981
25672
  }
25673
+ flushPendingGridlines();
24982
25674
  }
24983
25675
  for (const pane of cellPaneOrder) {
24984
25676
  const paneContext = bodyContexts[pane];
@@ -24996,7 +25688,7 @@ function XlsxGrid({
24996
25688
  paneContext.textBaseline = "middle";
24997
25689
  paneContext.fillText(spillText.text, spillText.textX, spillText.textY);
24998
25690
  if (spillText.textDecoration?.includes("underline") && spillText.text.length > 0) {
24999
- const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, paneContext.measureText(spillText.text).width) : paneContext.measureText(spillText.text).width;
25691
+ const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, measureCanvasTextWidth(paneContext, spillText.text)) : measureCanvasTextWidth(paneContext, spillText.text);
25000
25692
  const underlineStartX = spillText.align === "right" ? spillText.textX - measured : spillText.align === "center" ? spillText.textX - measured / 2 : spillText.textX;
25001
25693
  paneContext.beginPath();
25002
25694
  paneContext.moveTo(underlineStartX, spillText.underlineY);
@@ -25008,6 +25700,14 @@ function XlsxGrid({
25008
25700
  paneContext.restore();
25009
25701
  }
25010
25702
  }
25703
+ for (const pane of cellPaneOrder) {
25704
+ const paneContext = bodyContexts[pane];
25705
+ if (!paneContext) {
25706
+ continue;
25707
+ }
25708
+ drawBakedCanvasDrawings(pane, paneContext, bodyDirtyRectsByPane[pane]);
25709
+ }
25710
+ canvasProfileBodyMs = canvasProfileTarget ? performance.now() - canvasProfileBodyStart : 0;
25011
25711
  }
25012
25712
  if (shouldRepaintHeaders && cornerContext) {
25013
25713
  const topFrozenHeaderContext = topHeaderContexts.frozen;
@@ -25022,7 +25722,7 @@ function XlsxGrid({
25022
25722
  topScrollHeaderContext,
25023
25723
  bufferCanvas,
25024
25724
  dpr,
25025
- topScrollHeaderCanvasWidth2,
25725
+ topScrollHeaderCanvasWidth,
25026
25726
  headerHeight,
25027
25727
  drawingViewport.left - previousPaintedViewport.left,
25028
25728
  0
@@ -25041,7 +25741,7 @@ function XlsxGrid({
25041
25741
  bufferCanvas,
25042
25742
  dpr,
25043
25743
  rowHeaderWidth,
25044
- leftScrollHeaderCanvasHeight2,
25744
+ leftScrollHeaderCanvasHeight,
25045
25745
  0,
25046
25746
  drawingViewport.top - previousPaintedViewport.top
25047
25747
  );
@@ -25050,11 +25750,11 @@ function XlsxGrid({
25050
25750
  }
25051
25751
  }
25052
25752
  }
25053
- if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth2 > 0) {
25753
+ if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth > 0) {
25054
25754
  topFrozenHeaderContext.fillStyle = palette.headerSurface;
25055
- topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth2, headerHeight);
25755
+ topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth, headerHeight);
25056
25756
  }
25057
- if (topScrollHeaderContext && topScrollHeaderCanvasWidth2 > 0) {
25757
+ if (topScrollHeaderContext && topScrollHeaderCanvasWidth > 0) {
25058
25758
  topScrollHeaderContext.fillStyle = palette.headerSurface;
25059
25759
  for (const dirtyRect of topScrollHeaderDirtyRects) {
25060
25760
  topScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
@@ -25073,7 +25773,7 @@ function XlsxGrid({
25073
25773
  if (!paneContext) {
25074
25774
  continue;
25075
25775
  }
25076
- if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth2 : topScrollHeaderCanvasWidth2)) {
25776
+ if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth : topScrollHeaderCanvasWidth)) {
25077
25777
  continue;
25078
25778
  }
25079
25779
  if (!column.isFrozen && !intersectsCanvasDirtyRects(column.localLeft, 0, column.width, column.height, topScrollHeaderDirtyRects)) {
@@ -25099,11 +25799,11 @@ function XlsxGrid({
25099
25799
  column.height / 2
25100
25800
  );
25101
25801
  }
25102
- if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight2 > 0) {
25802
+ if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight > 0) {
25103
25803
  leftFrozenHeaderContext.fillStyle = palette.rowHeaderSurface;
25104
- leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight2);
25804
+ leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight);
25105
25805
  }
25106
- if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight2 > 0) {
25806
+ if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight > 0) {
25107
25807
  leftScrollHeaderContext.fillStyle = palette.rowHeaderSurface;
25108
25808
  for (const dirtyRect of leftScrollHeaderDirtyRects) {
25109
25809
  leftScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
@@ -25122,7 +25822,7 @@ function XlsxGrid({
25122
25822
  if (!paneContext) {
25123
25823
  continue;
25124
25824
  }
25125
- if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight2 : leftScrollHeaderCanvasHeight2)) {
25825
+ if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight : leftScrollHeaderCanvasHeight)) {
25126
25826
  continue;
25127
25827
  }
25128
25828
  if (!row.isFrozen && !intersectsCanvasDirtyRects(0, row.localTop, rowHeaderWidth, row.height, leftScrollHeaderDirtyRects)) {
@@ -25162,9 +25862,29 @@ function XlsxGrid({
25162
25862
  paintedHeaderCanvasSignatureRef.current = nextHeaderCanvasSignature;
25163
25863
  }
25164
25864
  applyCanvasViewportCompensation();
25865
+ if (canvasProfileTarget) {
25866
+ canvasProfileTarget.push({
25867
+ bodyMs: canvasProfileBodyMs,
25868
+ culledCells: canvasProfileCulledCells,
25869
+ dirtyRects: canvasProfileDirtyRects,
25870
+ lookedUpCells: canvasProfileLookedUpCells,
25871
+ paintedCells: canvasProfilePaintedCells,
25872
+ repaintBody: shouldRepaintBody,
25873
+ repaintHeaders: shouldRepaintHeaders,
25874
+ renderToLayoutMs: canvasProfileStart - xlsxGridRenderStart,
25875
+ totalMs: performance.now() - canvasProfileStart
25876
+ });
25877
+ if (canvasProfileTarget.length > 500) {
25878
+ canvasProfileTarget.splice(0, canvasProfileTarget.length - 500);
25879
+ }
25880
+ }
25165
25881
  }, [
25166
25882
  activeSheet,
25167
25883
  applyCanvasViewportCompensation,
25884
+ bakedCanvasDrawingSignature,
25885
+ bakedFormControlRects,
25886
+ bakedImageRects,
25887
+ bakedShapeRects,
25168
25888
  canvasColumnHeaderCells,
25169
25889
  canvasPaneAxisItems,
25170
25890
  canvasRowHeaderCells,
@@ -25185,15 +25905,16 @@ function XlsxGrid({
25185
25905
  frozenPaneBottom,
25186
25906
  frozenPaneRight,
25187
25907
  getCellData,
25908
+ getCanvasImage,
25188
25909
  getBodyBlitBufferCanvas,
25189
25910
  getHeaderBlitBufferCanvas,
25190
25911
  palette,
25191
25912
  resolveCellDisplayRect,
25192
25913
  resolveMergeAnchorCell,
25193
- resizeGuide,
25194
25914
  rowIndexByActual,
25195
25915
  rowPrefixSums,
25196
25916
  selectionHeaderSurface,
25917
+ shouldBakeCanvasStaticDrawings,
25197
25918
  stickyLeftByCol,
25198
25919
  stickyTopByRow,
25199
25920
  visibleCols,
@@ -25411,7 +26132,7 @@ function XlsxGrid({
25411
26132
  start: virtualRow?.start ?? (rowPrefixSums[index] ?? 0)
25412
26133
  };
25413
26134
  });
25414
- const totalHeight = shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
26135
+ const totalHeight = shouldUseDomVirtualizer && shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
25415
26136
  const totalWidth = totalContentWidth + displayRowHeaderWidth;
25416
26137
  const sheetContentHeight = displayHeaderHeight + totalHeight;
25417
26138
  const isLiveZooming = liveGestureZoom !== null && zoomScale === liveGestureZoom.baseZoomScale;
@@ -25485,18 +26206,6 @@ function XlsxGrid({
25485
26206
  width: 0,
25486
26207
  zIndex: canvasHeaderOverlayZIndex
25487
26208
  };
25488
- const scrollBodyCanvasWidth = Math.max(0, drawingViewport.width - frozenPaneRight);
25489
- const scrollBodyCanvasHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);
25490
- const topBodyCanvasWidth = scrollBodyCanvasWidth;
25491
- const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);
25492
- const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
25493
- const leftBodyCanvasHeight = scrollBodyCanvasHeight;
25494
- const cornerBodyCanvasWidth = leftBodyCanvasWidth;
25495
- const cornerBodyCanvasHeight = topBodyCanvasHeight;
25496
- const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
25497
- const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
25498
- const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
25499
- const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
25500
26209
  const canvasBodyBaseStyle = {
25501
26210
  cursor: "cell",
25502
26211
  pointerEvents: "auto",
@@ -25508,13 +26217,13 @@ function XlsxGrid({
25508
26217
  const canvasScrollBodyStyle = {
25509
26218
  ...canvasBodyBaseStyle,
25510
26219
  display: scrollBodyCanvasWidth > 0 && scrollBodyCanvasHeight > 0 ? "block" : "none",
25511
- left: frozenPaneRight,
25512
- top: frozenPaneBottom
26220
+ left: scrollBodyCanvasLeft,
26221
+ top: scrollBodyCanvasTop
25513
26222
  };
25514
26223
  const canvasTopBodyStyle = {
25515
26224
  ...canvasBodyBaseStyle,
25516
26225
  display: topBodyCanvasWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
25517
- left: frozenPaneRight,
26226
+ left: scrollBodyCanvasLeft,
25518
26227
  top: displayHeaderHeight,
25519
26228
  zIndex: 30
25520
26229
  };
@@ -25522,7 +26231,7 @@ function XlsxGrid({
25522
26231
  ...canvasBodyBaseStyle,
25523
26232
  display: leftBodyCanvasWidth > 0 && leftBodyCanvasHeight > 0 ? "block" : "none",
25524
26233
  left: displayRowHeaderWidth,
25525
- top: frozenPaneBottom,
26234
+ top: scrollBodyCanvasTop,
25526
26235
  zIndex: 30
25527
26236
  };
25528
26237
  const canvasCornerBodyStyle = {
@@ -25548,7 +26257,7 @@ function XlsxGrid({
25548
26257
  const canvasTopScrollHeaderStyle = {
25549
26258
  ...canvasHeaderBaseStyle,
25550
26259
  display: topScrollHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
25551
- left: frozenPaneRight,
26260
+ left: scrollBodyCanvasLeft,
25552
26261
  top: 0,
25553
26262
  zIndex: canvasHeaderOverlayZIndex
25554
26263
  };
@@ -25563,7 +26272,7 @@ function XlsxGrid({
25563
26272
  ...canvasHeaderBaseStyle,
25564
26273
  display: leftScrollHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
25565
26274
  left: 0,
25566
- top: frozenPaneBottom,
26275
+ top: scrollBodyCanvasTop,
25567
26276
  zIndex: canvasHeaderOverlayZIndex
25568
26277
  };
25569
26278
  const canvasCornerHeaderStyle = {
@@ -26119,26 +26828,26 @@ function XlsxGrid({
26119
26828
  };
26120
26829
  const canvasScrollOverlayPaneStyle = {
26121
26830
  ...canvasOverlayPaneBaseStyle,
26122
- display: scrollBodyCanvasWidth > 0 && scrollBodyCanvasHeight > 0 ? "block" : "none",
26123
- height: scrollBodyCanvasHeight,
26831
+ display: scrollBodyViewportWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
26832
+ height: scrollBodyViewportHeight,
26124
26833
  left: frozenPaneRight,
26125
26834
  top: frozenPaneBottom,
26126
- width: scrollBodyCanvasWidth,
26835
+ width: scrollBodyViewportWidth,
26127
26836
  zIndex: 20
26128
26837
  };
26129
26838
  const canvasTopOverlayPaneStyle = {
26130
26839
  ...canvasOverlayPaneBaseStyle,
26131
- display: topBodyCanvasWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
26840
+ display: scrollBodyViewportWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
26132
26841
  height: topBodyCanvasHeight,
26133
26842
  left: frozenPaneRight,
26134
26843
  top: displayHeaderHeight,
26135
- width: topBodyCanvasWidth,
26844
+ width: scrollBodyViewportWidth,
26136
26845
  zIndex: 35
26137
26846
  };
26138
26847
  const canvasLeftOverlayPaneStyle = {
26139
26848
  ...canvasOverlayPaneBaseStyle,
26140
- display: leftBodyCanvasWidth > 0 && leftBodyCanvasHeight > 0 ? "block" : "none",
26141
- height: leftBodyCanvasHeight,
26849
+ display: leftBodyCanvasWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
26850
+ height: scrollBodyViewportHeight,
26142
26851
  left: displayRowHeaderWidth,
26143
26852
  top: frozenPaneBottom,
26144
26853
  width: leftBodyCanvasWidth,
@@ -26153,8 +26862,9 @@ function XlsxGrid({
26153
26862
  width: cornerBodyCanvasWidth,
26154
26863
  zIndex: 36
26155
26864
  };
26865
+ const drawingViewportCacheSignature = `${Math.floor(drawingViewport.left / CANVAS_VIEWPORT_OVERSCAN_PX)}:${Math.floor(drawingViewport.top / CANVAS_VIEWPORT_OVERSCAN_PX)}:${drawingViewport.width}:${drawingViewport.height}`;
26156
26866
  const previousPaneDrawingNodes = paneDrawingNodesCacheRef.current;
26157
- const canReusePaneDrawingNodes = previousPaneDrawingNodes !== null && previousPaneDrawingNodes.showImages === showImages && previousPaneDrawingNodes.chartRects === chartRects && previousPaneDrawingNodes.formControlRects === formControlRects && previousPaneDrawingNodes.shapeRects === shapeRects && previousPaneDrawingNodes.imageRects === imageRects && previousPaneDrawingNodes.selectedChartId === selectedChartId && previousPaneDrawingNodes.selectedImageId === selectedImageId && previousPaneDrawingNodes.readOnly === readOnly && previousPaneDrawingNodes.selectionStroke === selectionStroke && previousPaneDrawingNodes.renderChartLoading === renderChartLoading && previousPaneDrawingNodes.renderImage === renderImage && previousPaneDrawingNodes.renderImageSelection === renderImageSelection && previousPaneDrawingNodes.isChartsLoading === isChartsLoading && previousPaneDrawingNodes.palette === palette && previousPaneDrawingNodes.drawingViewport.left === drawingViewport.left && previousPaneDrawingNodes.drawingViewport.top === drawingViewport.top && previousPaneDrawingNodes.drawingViewport.width === drawingViewport.width && previousPaneDrawingNodes.drawingViewport.height === drawingViewport.height;
26867
+ const canReusePaneDrawingNodes = previousPaneDrawingNodes !== null && previousPaneDrawingNodes.showImages === showImages && previousPaneDrawingNodes.chartRects === chartRects && previousPaneDrawingNodes.formControlRects === domFormControlRects && previousPaneDrawingNodes.shapeRects === domShapeRects && previousPaneDrawingNodes.imageRects === domImageRects && previousPaneDrawingNodes.selectedChartId === selectedChartId && previousPaneDrawingNodes.selectedImageId === selectedImageId && previousPaneDrawingNodes.readOnly === readOnly && previousPaneDrawingNodes.selectionStroke === selectionStroke && previousPaneDrawingNodes.renderChartLoading === renderChartLoading && previousPaneDrawingNodes.renderImage === renderImage && previousPaneDrawingNodes.renderImageSelection === renderImageSelection && previousPaneDrawingNodes.isChartsLoading === isChartsLoading && previousPaneDrawingNodes.palette === palette && previousPaneDrawingNodes.drawingViewportSignature === drawingViewportCacheSignature;
26158
26868
  const paneDrawingNodes = canReusePaneDrawingNodes ? previousPaneDrawingNodes.value : !showImages ? {
26159
26869
  corner: null,
26160
26870
  left: null,
@@ -26163,35 +26873,35 @@ function XlsxGrid({
26163
26873
  } : {
26164
26874
  corner: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
26165
26875
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "corner")),
26166
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "corner")),
26167
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "corner")),
26168
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "corner"))
26876
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "corner")),
26877
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "corner")),
26878
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "corner"))
26169
26879
  ] }),
26170
26880
  left: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
26171
26881
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "left")),
26172
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "left")),
26173
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "left")),
26174
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "left"))
26882
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "left")),
26883
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "left")),
26884
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "left"))
26175
26885
  ] }),
26176
26886
  scroll: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
26177
26887
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "scroll")),
26178
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "scroll")),
26179
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "scroll")),
26180
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "scroll"))
26888
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "scroll")),
26889
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "scroll")),
26890
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "scroll"))
26181
26891
  ] }),
26182
26892
  top: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
26183
26893
  chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "top")),
26184
- shapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "top")),
26185
- formControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "top")),
26186
- imageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "top"))
26894
+ domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "top")),
26895
+ domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "top")),
26896
+ domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "top"))
26187
26897
  ] })
26188
26898
  };
26189
26899
  if (!canReusePaneDrawingNodes) {
26190
26900
  paneDrawingNodesCacheRef.current = {
26191
26901
  chartRects,
26192
- drawingViewport,
26193
- formControlRects,
26194
- imageRects,
26902
+ drawingViewportSignature: drawingViewportCacheSignature,
26903
+ formControlRects: domFormControlRects,
26904
+ imageRects: domImageRects,
26195
26905
  isChartsLoading,
26196
26906
  palette,
26197
26907
  readOnly,
@@ -26201,7 +26911,7 @@ function XlsxGrid({
26201
26911
  selectedChartId,
26202
26912
  selectedImageId,
26203
26913
  selectionStroke,
26204
- shapeRects,
26914
+ shapeRects: domShapeRects,
26205
26915
  showImages,
26206
26916
  value: paneDrawingNodes
26207
26917
  };
@@ -26709,10 +27419,11 @@ function XlsxGrid({
26709
27419
  style: canvasCornerBodyStyle
26710
27420
  }
26711
27421
  ),
26712
- showImages ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
27422
+ hasCanvasDomDrawingOverlays ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
26713
27423
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasScrollOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26714
27424
  "div",
26715
27425
  {
27426
+ ref: canvasScrollOverlayContentRef,
26716
27427
  style: {
26717
27428
  height: sheetContentHeight,
26718
27429
  left: 0,
@@ -26720,6 +27431,7 @@ function XlsxGrid({
26720
27431
  position: "absolute",
26721
27432
  top: 0,
26722
27433
  transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
27434
+ transformOrigin: "0 0",
26723
27435
  width: totalWidth
26724
27436
  },
26725
27437
  children: paneDrawingNodes.scroll
@@ -26728,6 +27440,7 @@ function XlsxGrid({
26728
27440
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasTopOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26729
27441
  "div",
26730
27442
  {
27443
+ ref: canvasTopOverlayContentRef,
26731
27444
  style: {
26732
27445
  height: sheetContentHeight,
26733
27446
  left: 0,
@@ -26735,6 +27448,7 @@ function XlsxGrid({
26735
27448
  position: "absolute",
26736
27449
  top: 0,
26737
27450
  transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-displayHeaderHeight}px)`,
27451
+ transformOrigin: "0 0",
26738
27452
  width: totalWidth
26739
27453
  },
26740
27454
  children: paneDrawingNodes.top
@@ -26743,6 +27457,7 @@ function XlsxGrid({
26743
27457
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasLeftOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26744
27458
  "div",
26745
27459
  {
27460
+ ref: canvasLeftOverlayContentRef,
26746
27461
  style: {
26747
27462
  height: sheetContentHeight,
26748
27463
  left: 0,
@@ -26750,6 +27465,7 @@ function XlsxGrid({
26750
27465
  position: "absolute",
26751
27466
  top: 0,
26752
27467
  transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
27468
+ transformOrigin: "0 0",
26753
27469
  width: totalWidth
26754
27470
  },
26755
27471
  children: paneDrawingNodes.left
@@ -26758,6 +27474,7 @@ function XlsxGrid({
26758
27474
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasCornerOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26759
27475
  "div",
26760
27476
  {
27477
+ ref: canvasCornerOverlayContentRef,
26761
27478
  style: {
26762
27479
  height: sheetContentHeight,
26763
27480
  left: 0,
@@ -26765,6 +27482,7 @@ function XlsxGrid({
26765
27482
  position: "absolute",
26766
27483
  top: 0,
26767
27484
  transform: `translate(${-displayRowHeaderWidth}px, ${-displayHeaderHeight}px)`,
27485
+ transformOrigin: "0 0",
26768
27486
  width: totalWidth
26769
27487
  },
26770
27488
  children: paneDrawingNodes.corner
@@ -27684,7 +28402,7 @@ function useXlsxViewerThumbnails(options = {}) {
27684
28402
  if (!canvas) {
27685
28403
  return false;
27686
28404
  }
27687
- const context = canvas.getContext("2d");
28405
+ const context = canvas.getContext("2d", { alpha: false });
27688
28406
  if (!context) {
27689
28407
  return false;
27690
28408
  }
@@ -27708,9 +28426,10 @@ function useXlsxViewerThumbnails(options = {}) {
27708
28426
  }
27709
28427
  context.setTransform(dpr * scale, 0, 0, dpr * scale, 0, 0);
27710
28428
  context.clearRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
28429
+ const thumbnailSheetSurface = resolveSheetSurface(sheet, palette);
27711
28430
  context.fillStyle = palette.canvas;
27712
28431
  context.fillRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
27713
- context.fillStyle = resolveSheetSurface(sheet, palette);
28432
+ context.fillStyle = thumbnailSheetSurface;
27714
28433
  context.fillRect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
27715
28434
  if (includeHeaders) {
27716
28435
  context.fillStyle = palette.headerSurface;
@@ -27831,8 +28550,8 @@ function useXlsxViewerThumbnails(options = {}) {
27831
28550
  };
27832
28551
  const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellData.style);
27833
28552
  const gradientFill = typeof cellData.style.backgroundImage === "string" ? resolveCanvasGradientFill(context, rect, cellData.style.backgroundImage) : null;
27834
- const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor : resolveSheetSurface(sheet, palette));
27835
- const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !== resolveSheetSurface(sheet, palette);
28553
+ const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor : thumbnailSheetSurface);
28554
+ const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !== thumbnailSheetSurface;
27836
28555
  context.fillStyle = gradientFill ?? fillColor;
27837
28556
  context.fillRect(rect.left, rect.top, rect.width, rect.height);
27838
28557
  if (cellData.conditionalDataBar) {
@@ -27887,12 +28606,19 @@ function useXlsxViewerThumbnails(options = {}) {
27887
28606
  if (canvasCellStyle.leftBorder) {
27888
28607
  strokeCanvasBorderSide(context, "left", rect, canvasCellStyle.leftBorder);
27889
28608
  }
28609
+ const rawText = cellData.value ?? "";
28610
+ const shouldDrawThumbnailContent = cellData.checkboxState != null || cellData.sparkline || rawText.length > 0 || cellData.conditionalIcon;
28611
+ if (!shouldDrawThumbnailContent) {
28612
+ continue;
28613
+ }
27890
28614
  const padding = canvasCellStyle.padding;
27891
28615
  const contentLeft = rect.left + padding.left;
27892
28616
  const contentTop = rect.top + padding.top;
27893
28617
  const contentWidth = Math.max(0, rect.width - padding.left - padding.right);
27894
28618
  const contentHeight = Math.max(0, rect.height - padding.top - padding.bottom);
27895
- const rawText = cellData.value ?? "";
28619
+ if (contentWidth <= 0 || contentHeight <= 0) {
28620
+ continue;
28621
+ }
27896
28622
  context.save();
27897
28623
  context.beginPath();
27898
28624
  context.rect(contentLeft, contentTop, contentWidth, contentHeight);