@extend-ai/react-xlsx 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4220,13 +4220,36 @@ function parseWorkbookTableMetadata(archive, workbookSheets) {
4220
4220
  }
4221
4221
  return [{
4222
4222
  displayName: tableNode.getAttribute("displayName") ?? void 0,
4223
+ headerRowCount: parseWorkbookTableCount(tableNode.getAttribute("headerRowCount"), 1),
4223
4224
  headerRowCellStyle: tableNode.getAttribute("headerRowCellStyle") ?? void 0,
4224
4225
  name: tableNode.getAttribute("name") ?? void 0,
4225
- reference: tableNode.getAttribute("ref") ?? void 0
4226
+ reference: tableNode.getAttribute("ref") ?? void 0,
4227
+ totalsRowCount: parseWorkbookTableCount(tableNode.getAttribute("totalsRowCount"), 0),
4228
+ totalsRowShown: parseWorkbookTableBoolean(tableNode.getAttribute("totalsRowShown"), false)
4226
4229
  }];
4227
4230
  });
4228
4231
  });
4229
4232
  }
4233
+ function parseWorkbookTableCount(value, fallback) {
4234
+ if (value === null) {
4235
+ return fallback;
4236
+ }
4237
+ const parsed = Number.parseInt(value, 10);
4238
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
4239
+ }
4240
+ function parseWorkbookTableBoolean(value, fallback) {
4241
+ if (value === null) {
4242
+ return fallback;
4243
+ }
4244
+ const normalized = value.trim().toLowerCase();
4245
+ if (normalized === "0" || normalized === "false" || normalized === "") {
4246
+ return false;
4247
+ }
4248
+ if (normalized === "1" || normalized === "true") {
4249
+ return true;
4250
+ }
4251
+ return fallback;
4252
+ }
4230
4253
  function parseSqrefRanges(sqref) {
4231
4254
  if (!sqref) {
4232
4255
  return [];
@@ -6238,6 +6261,84 @@ function resizeImageRect(rect, handle, deltaX, deltaY, minimumSize = 16) {
6238
6261
  return { height, left, top, width };
6239
6262
  }
6240
6263
 
6264
+ // src/safe-calculate.ts
6265
+ var SHEET_REF_REGEX = /'((?:[^']|'')+)'!|([A-Za-z_\u0080-\uFFFF][\w.\u0080-\uFFFF]*)!/g;
6266
+ function collectReferencedSheetNames(workbook) {
6267
+ const referenced = /* @__PURE__ */ new Set();
6268
+ for (let sheetIdx = 0; sheetIdx < workbook.sheetCount; sheetIdx += 1) {
6269
+ let sheet;
6270
+ try {
6271
+ sheet = workbook.getSheet(sheetIdx);
6272
+ } catch {
6273
+ continue;
6274
+ }
6275
+ const cells = sheet.formulaCells;
6276
+ if (!Array.isArray(cells)) {
6277
+ continue;
6278
+ }
6279
+ for (const cell of cells) {
6280
+ const formula = cell?.formula;
6281
+ if (!formula) {
6282
+ continue;
6283
+ }
6284
+ SHEET_REF_REGEX.lastIndex = 0;
6285
+ let match;
6286
+ while ((match = SHEET_REF_REGEX.exec(formula)) !== null) {
6287
+ const raw = match[1] ?? match[2];
6288
+ if (!raw) {
6289
+ continue;
6290
+ }
6291
+ referenced.add(raw.replace(/''/g, "'"));
6292
+ }
6293
+ }
6294
+ }
6295
+ return referenced;
6296
+ }
6297
+ function hasUnresolvedSheetReferences(workbook) {
6298
+ let names;
6299
+ try {
6300
+ names = workbook.sheetNames;
6301
+ } catch {
6302
+ return false;
6303
+ }
6304
+ const known = new Set(names);
6305
+ const referenced = collectReferencedSheetNames(workbook);
6306
+ for (const name of referenced) {
6307
+ if (!known.has(name)) {
6308
+ return true;
6309
+ }
6310
+ }
6311
+ return false;
6312
+ }
6313
+ function safeCalculate(workbook, options = {}) {
6314
+ if (hasUnresolvedSheetReferences(workbook)) {
6315
+ return { workbook, calculated: false, skipReason: "unresolved-sheet-refs" };
6316
+ }
6317
+ try {
6318
+ workbook.calculate();
6319
+ return { workbook, calculated: true, skipReason: null };
6320
+ } catch (err) {
6321
+ console.warn("[react-xlsx] workbook.calculate() trapped; falling back to cached formula values", err);
6322
+ if (options.reparse) {
6323
+ try {
6324
+ return { workbook: options.reparse(), calculated: false, skipReason: "calculate-trapped" };
6325
+ } catch (reparseErr) {
6326
+ console.warn("[react-xlsx] workbook reparse after calculate trap failed", reparseErr);
6327
+ }
6328
+ }
6329
+ return { workbook, calculated: false, skipReason: "calculate-trapped" };
6330
+ }
6331
+ }
6332
+ function tryRecalculate(workbook) {
6333
+ try {
6334
+ workbook.calculate();
6335
+ return { calculated: true, error: null };
6336
+ } catch (err) {
6337
+ console.warn("[react-xlsx] workbook.calculate() trapped during recalculation", err);
6338
+ return { calculated: false, error: err };
6339
+ }
6340
+ }
6341
+
6241
6342
  // src/wasm.ts
6242
6343
  var wasmModulePromise = null;
6243
6344
  function getSheetsWasmModule() {
@@ -6832,17 +6933,18 @@ function rangeContainsCell(range, cell) {
6832
6933
  function mapWorksheetTables(worksheet, metadataForSheet) {
6833
6934
  const rawTables = worksheet?.tables ?? [];
6834
6935
  return rawTables.flatMap((table, index) => {
6835
- const reference = typeof table.reference === "string" ? table.reference : "";
6836
- const parsedRange = parseA1RangeReference2(reference);
6837
- if (!parsedRange) {
6838
- return [];
6839
- }
6840
6936
  const rawColumns = Array.isArray(table.columns) ? table.columns : [];
6841
6937
  const rawName = typeof table.name === "string" ? table.name : `Table${index + 1}`;
6842
6938
  const rawDisplayName = typeof table.displayName === "string" ? table.displayName : typeof table.name === "string" ? table.name : `Table ${index + 1}`;
6843
6939
  const metadata = metadataForSheet?.find(
6844
- (entry) => entry.name && entry.name === rawName || entry.displayName && entry.displayName === rawDisplayName || entry.reference && entry.reference === reference
6940
+ (entry) => entry.name && entry.name === rawName || entry.displayName && entry.displayName === rawDisplayName || entry.reference && entry.reference === table.reference
6845
6941
  );
6942
+ const rawReference = typeof table.reference === "string" ? table.reference : "";
6943
+ const reference = metadata?.reference ?? rawReference;
6944
+ const parsedRange = parseA1RangeReference2(reference);
6945
+ if (!parsedRange) {
6946
+ return [];
6947
+ }
6846
6948
  return [{
6847
6949
  columns: rawColumns.map((column, columnIndex) => ({
6848
6950
  id: typeof column.id === "number" ? column.id ?? columnIndex + 1 : columnIndex + 1,
@@ -6851,17 +6953,47 @@ function mapWorksheetTables(worksheet, metadataForSheet) {
6851
6953
  })),
6852
6954
  displayName: rawDisplayName,
6853
6955
  end: parsedRange.end,
6854
- headerRowCount: typeof table.headerRowCount === "number" ? table.headerRowCount : 1,
6956
+ headerRowCount: metadata?.headerRowCount ?? resolveWorkbookTableCount(table.headerRowCount, 1),
6855
6957
  headerRowCellStyle: metadata?.headerRowCellStyle,
6856
6958
  name: rawName,
6857
6959
  reference,
6858
6960
  start: parsedRange.start,
6859
6961
  styleInfo: table.styleInfo,
6860
- totalsRowCount: typeof table.totalsRowCount === "number" ? table.totalsRowCount : 0,
6861
- totalsRowShown: Boolean(table.totalsRowShown)
6962
+ totalsRowCount: metadata?.totalsRowCount ?? resolveWorkbookTableCount(table.totalsRowCount, 0),
6963
+ totalsRowShown: metadata?.totalsRowShown ?? resolveWorkbookTableBoolean(table.totalsRowShown)
6862
6964
  }];
6863
6965
  });
6864
6966
  }
6967
+ function resolveWorkbookTableCount(value, fallback) {
6968
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
6969
+ return value;
6970
+ }
6971
+ if (typeof value === "string") {
6972
+ const parsed = Number.parseInt(value, 10);
6973
+ if (Number.isFinite(parsed) && parsed >= 0) {
6974
+ return parsed;
6975
+ }
6976
+ }
6977
+ return fallback;
6978
+ }
6979
+ function resolveWorkbookTableBoolean(value) {
6980
+ if (typeof value === "boolean") {
6981
+ return value;
6982
+ }
6983
+ if (typeof value === "number") {
6984
+ return value !== 0;
6985
+ }
6986
+ if (typeof value === "string") {
6987
+ const normalized = value.trim().toLowerCase();
6988
+ if (normalized === "0" || normalized === "false" || normalized === "") {
6989
+ return false;
6990
+ }
6991
+ if (normalized === "1" || normalized === "true") {
6992
+ return true;
6993
+ }
6994
+ }
6995
+ return false;
6996
+ }
6865
6997
  function fileStem(fileName) {
6866
6998
  const normalized = fileName.trim();
6867
6999
  const lastDot = normalized.lastIndexOf(".");
@@ -6992,7 +7124,17 @@ async function resolveWorkbookBuffer({ file, src }, signal) {
6992
7124
  if (file) {
6993
7125
  buffer = file;
6994
7126
  } else if (src) {
6995
- const response = await fetch(src, { signal });
7127
+ let response;
7128
+ try {
7129
+ response = await fetch(src, { signal });
7130
+ } catch (error) {
7131
+ if (isAbortError(error)) {
7132
+ throw error;
7133
+ }
7134
+ throw new Error(
7135
+ "Failed to fetch workbook. The remote URL may be blocked by CORS, unavailable, or not directly downloadable from the browser."
7136
+ );
7137
+ }
6996
7138
  if (!response.ok) {
6997
7139
  throw new Error(`Failed to fetch workbook (status ${response.status})`);
6998
7140
  }
@@ -7004,18 +7146,21 @@ async function resolveWorkbookBuffer({ file, src }, signal) {
7004
7146
  }
7005
7147
  async function parseWorkbookBuffer(buffer) {
7006
7148
  const wasmModule = await getSheetsWasmModule();
7007
- const workbook = wasmModule.Workbook.fromBytes(new Uint8Array(buffer));
7149
+ const initialWorkbook = wasmModule.Workbook.fromBytes(new Uint8Array(buffer));
7008
7150
  let totalFormulas = 0;
7009
- for (let index = 0; index < workbook.sheetCount; index += 1) {
7010
- totalFormulas += workbook.getSheet(index).formulaCount;
7151
+ for (let index = 0; index < initialWorkbook.sheetCount; index += 1) {
7152
+ totalFormulas += initialWorkbook.getSheet(index).formulaCount;
7011
7153
  }
7012
7154
  const shouldAutoCalculate = totalFormulas <= FORMULA_COUNT_THRESHOLD;
7013
- if (shouldAutoCalculate) {
7014
- workbook.calculate();
7155
+ if (!shouldAutoCalculate) {
7156
+ return { shouldAutoCalculate, workbook: initialWorkbook };
7015
7157
  }
7158
+ const result = safeCalculate(initialWorkbook, {
7159
+ reparse: () => wasmModule.Workbook.fromBytes(new Uint8Array(buffer))
7160
+ });
7016
7161
  return {
7017
- shouldAutoCalculate,
7018
- workbook
7162
+ shouldAutoCalculate: result.calculated,
7163
+ workbook: result.workbook
7019
7164
  };
7020
7165
  }
7021
7166
  function scheduleLowPriorityTask(task) {
@@ -7548,6 +7693,7 @@ function downloadUrl(src, fileName) {
7548
7693
  }
7549
7694
  function useXlsxViewerController(options) {
7550
7695
  const {
7696
+ allowResizeInReadOnly = false,
7551
7697
  deferLoadingAboveBytes = DEFAULT_DEFER_LOADING_ABOVE_BYTES,
7552
7698
  file,
7553
7699
  fileName,
@@ -7601,6 +7747,7 @@ function useXlsxViewerController(options) {
7601
7747
  const displayFileName = React.useMemo(() => resolveDisplayFileName(src, fileName), [fileName, src]);
7602
7748
  const shouldDeferLoading = deferLoadingAboveBytes > 0;
7603
7749
  const readOnly = requestedReadOnly || forcedReadOnly;
7750
+ const canResizeReadOnly = requestedReadOnly && allowResizeInReadOnly && !forcedReadOnly;
7604
7751
  const workerSupported = useWorker && typeof Worker !== "undefined";
7605
7752
  const shouldUseWorker = workerSupported && forcedReadOnly;
7606
7753
  const shouldForceReadOnlyForBuffer = React.useCallback((bufferByteLength) => !requestedReadOnly && readOnlyAboveBytes > 0 && bufferByteLength > readOnlyAboveBytes, [readOnlyAboveBytes, requestedReadOnly]);
@@ -8254,7 +8401,10 @@ function useXlsxViewerController(options) {
8254
8401
  if (!shouldAutoCalculate) {
8255
8402
  return;
8256
8403
  }
8257
- targetWorkbook.calculate();
8404
+ const result = tryRecalculate(targetWorkbook);
8405
+ if (!result.calculated) {
8406
+ setShouldAutoCalculate(false);
8407
+ }
8258
8408
  }, [shouldAutoCalculate]);
8259
8409
  const getActiveWorksheet = React.useCallback(() => {
8260
8410
  if (!workbook || !activeSheet) {
@@ -8891,11 +9041,15 @@ function useXlsxViewerController(options) {
8891
9041
  if (!workbook) {
8892
9042
  return;
8893
9043
  }
8894
- workbook.calculate();
8895
- refreshWorkbookState(workbook);
9044
+ const result = tryRecalculate(workbook);
9045
+ if (result.calculated) {
9046
+ refreshWorkbookState(workbook);
9047
+ return;
9048
+ }
9049
+ setShouldAutoCalculate(false);
8896
9050
  }, [refreshWorkbookState, workbook]);
8897
9051
  const resizeColumn = React.useCallback((col, widthPx) => {
8898
- if (readOnly || !workbook || !activeSheet) {
9052
+ if (readOnly && !canResizeReadOnly || !workbook || !activeSheet) {
8899
9053
  return;
8900
9054
  }
8901
9055
  recordHistoryBeforeMutation();
@@ -8905,9 +9059,9 @@ function useXlsxViewerController(options) {
8905
9059
  pxToSheetColumnWidth(resolveContentSheetAxisPixels(widthPx, activeSheet.showGridLines))
8906
9060
  );
8907
9061
  refreshWorkbookState(workbook);
8908
- }, [activeSheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9062
+ }, [activeSheet, canResizeReadOnly, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
8909
9063
  const resizeRow = React.useCallback((row, heightPx) => {
8910
- if (readOnly || !workbook || !activeSheet) {
9064
+ if (readOnly && !canResizeReadOnly || !workbook || !activeSheet) {
8911
9065
  return;
8912
9066
  }
8913
9067
  recordHistoryBeforeMutation();
@@ -8917,7 +9071,7 @@ function useXlsxViewerController(options) {
8917
9071
  pxToSheetRowHeight(resolveContentSheetAxisPixels(heightPx, activeSheet.showGridLines))
8918
9072
  );
8919
9073
  refreshWorkbookState(workbook);
8920
- }, [activeSheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9074
+ }, [activeSheet, canResizeReadOnly, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
8921
9075
  const resolveAnchoredObjectRect = React.useCallback((anchor, worksheet) => {
8922
9076
  const resolveAxisSum = (index, getSize) => {
8923
9077
  let total = 0;
@@ -19912,6 +20066,109 @@ function resolveSelectionColors({
19912
20066
  stroke
19913
20067
  };
19914
20068
  }
20069
+ function buildFullCanvasDirtyRect(width, height) {
20070
+ if (width <= 0 || height <= 0) {
20071
+ return [];
20072
+ }
20073
+ return [{
20074
+ height,
20075
+ left: 0,
20076
+ top: 0,
20077
+ width
20078
+ }];
20079
+ }
20080
+ function intersectsCanvasDirtyRects(left, top, width, height, dirtyRects) {
20081
+ if (dirtyRects.length === 0 || width <= 0 || height <= 0) {
20082
+ return false;
20083
+ }
20084
+ const right = left + width;
20085
+ const bottom = top + height;
20086
+ return dirtyRects.some((dirtyRect) => left < dirtyRect.left + dirtyRect.width && right > dirtyRect.left && top < dirtyRect.top + dirtyRect.height && bottom > dirtyRect.top);
20087
+ }
20088
+ function blitCanvasWithScrollDelta(canvas, context, bufferCanvas, dpr, width, height, deltaX, deltaY) {
20089
+ if (width <= 0 || height <= 0) {
20090
+ return [];
20091
+ }
20092
+ const clampedDeltaX = Math.max(-width, Math.min(width, deltaX));
20093
+ const clampedDeltaY = Math.max(-height, Math.min(height, deltaY));
20094
+ if (Math.abs(clampedDeltaX) < 0.01 && Math.abs(clampedDeltaY) < 0.01) {
20095
+ return [];
20096
+ }
20097
+ if (Math.abs(clampedDeltaX) >= width || Math.abs(clampedDeltaY) >= height) {
20098
+ return null;
20099
+ }
20100
+ const deviceWidth = Math.max(1, Math.round(width * dpr));
20101
+ const deviceHeight = Math.max(1, Math.round(height * dpr));
20102
+ if (bufferCanvas.width !== deviceWidth) {
20103
+ bufferCanvas.width = deviceWidth;
20104
+ }
20105
+ if (bufferCanvas.height !== deviceHeight) {
20106
+ bufferCanvas.height = deviceHeight;
20107
+ }
20108
+ const bufferContext = bufferCanvas.getContext("2d");
20109
+ if (!bufferContext) {
20110
+ return null;
20111
+ }
20112
+ bufferContext.setTransform(1, 0, 0, 1, 0, 0);
20113
+ bufferContext.clearRect(0, 0, deviceWidth, deviceHeight);
20114
+ bufferContext.drawImage(canvas, 0, 0);
20115
+ const deltaDeviceX = clampedDeltaX * dpr;
20116
+ const deltaDeviceY = clampedDeltaY * dpr;
20117
+ const overlapDeviceWidth = deviceWidth - Math.abs(deltaDeviceX);
20118
+ const overlapDeviceHeight = deviceHeight - Math.abs(deltaDeviceY);
20119
+ if (overlapDeviceWidth <= 0 || overlapDeviceHeight <= 0) {
20120
+ return null;
20121
+ }
20122
+ const sourceX = deltaDeviceX > 0 ? deltaDeviceX : 0;
20123
+ const sourceY = deltaDeviceY > 0 ? deltaDeviceY : 0;
20124
+ const destinationX = deltaDeviceX > 0 ? 0 : -deltaDeviceX;
20125
+ const destinationY = deltaDeviceY > 0 ? 0 : -deltaDeviceY;
20126
+ context.setTransform(1, 0, 0, 1, 0, 0);
20127
+ context.drawImage(
20128
+ bufferCanvas,
20129
+ sourceX,
20130
+ sourceY,
20131
+ overlapDeviceWidth,
20132
+ overlapDeviceHeight,
20133
+ destinationX,
20134
+ destinationY,
20135
+ overlapDeviceWidth,
20136
+ overlapDeviceHeight
20137
+ );
20138
+ context.setTransform(dpr, 0, 0, dpr, 0, 0);
20139
+ const dirtyRects = [];
20140
+ if (clampedDeltaX > 0) {
20141
+ dirtyRects.push({
20142
+ height,
20143
+ left: Math.max(0, width - clampedDeltaX),
20144
+ top: 0,
20145
+ width: Math.min(width, clampedDeltaX)
20146
+ });
20147
+ } else if (clampedDeltaX < 0) {
20148
+ dirtyRects.push({
20149
+ height,
20150
+ left: 0,
20151
+ top: 0,
20152
+ width: Math.min(width, -clampedDeltaX)
20153
+ });
20154
+ }
20155
+ if (clampedDeltaY > 0) {
20156
+ dirtyRects.push({
20157
+ height: Math.min(height, clampedDeltaY),
20158
+ left: 0,
20159
+ top: Math.max(0, height - clampedDeltaY),
20160
+ width
20161
+ });
20162
+ } else if (clampedDeltaY < 0) {
20163
+ dirtyRects.push({
20164
+ height: Math.min(height, -clampedDeltaY),
20165
+ left: 0,
20166
+ top: 0,
20167
+ width
20168
+ });
20169
+ }
20170
+ return dirtyRects;
20171
+ }
19915
20172
  function buildConditionalFormatRuleKey(rule) {
19916
20173
  return `${rule.kind}:${rule.priority}:${rule.ranges.map((range) => `${range.start.row}:${range.start.col}-${range.end.row}:${range.end.col}`).join("|")}`;
19917
20174
  }
@@ -20290,6 +20547,7 @@ function GridRow({
20290
20547
  actualRow,
20291
20548
  editingCell,
20292
20549
  editingValue,
20550
+ frozenRowHeaderZIndex,
20293
20551
  getCellData,
20294
20552
  headerLabelLiveScale,
20295
20553
  leadingSpacerWidth,
@@ -20306,6 +20564,7 @@ function GridRow({
20306
20564
  readOnly,
20307
20565
  renderCellAdornment,
20308
20566
  rowHeight,
20567
+ rowHeaderZIndex,
20309
20568
  rowHeaderWidth,
20310
20569
  stickyLeftByCol,
20311
20570
  stickyTop,
@@ -20338,7 +20597,7 @@ function GridRow({
20338
20597
  textAlign: "center",
20339
20598
  userSelect: "none",
20340
20599
  width: rowHeaderWidth,
20341
- zIndex: stickyTop !== void 0 ? 45 : 35
20600
+ zIndex: stickyTop !== void 0 ? frozenRowHeaderZIndex : rowHeaderZIndex
20342
20601
  },
20343
20602
  children: /* @__PURE__ */ jsxs3("div", { style: { position: "relative" }, children: [
20344
20603
  /* @__PURE__ */ jsx3(
@@ -20771,8 +21030,20 @@ function XlsxGrid({
20771
21030
  const topBodyCanvasRef = React4.useRef(null);
20772
21031
  const leftBodyCanvasRef = React4.useRef(null);
20773
21032
  const cornerBodyCanvasRef = React4.useRef(null);
20774
- const topHeaderCanvasRef = React4.useRef(null);
20775
- const leftHeaderCanvasRef = React4.useRef(null);
21033
+ const bodyBlitBufferCanvasRef = React4.useRef({
21034
+ corner: null,
21035
+ left: null,
21036
+ scroll: null,
21037
+ top: null
21038
+ });
21039
+ const headerBlitBufferCanvasRef = React4.useRef({
21040
+ left: null,
21041
+ top: null
21042
+ });
21043
+ const topFrozenHeaderCanvasRef = React4.useRef(null);
21044
+ const topScrollHeaderCanvasRef = React4.useRef(null);
21045
+ const leftFrozenHeaderCanvasRef = React4.useRef(null);
21046
+ const leftScrollHeaderCanvasRef = React4.useRef(null);
20776
21047
  const cornerHeaderCanvasRef = React4.useRef(null);
20777
21048
  const selectionOverlayRef = React4.useRef(null);
20778
21049
  const activeValidationOverlayRef = React4.useRef(null);
@@ -20976,16 +21247,31 @@ function XlsxGrid({
20976
21247
  liveZoomTranslateX2,
20977
21248
  liveZoomTranslateY2
20978
21249
  );
20979
- const topHeaderCanvas = topHeaderCanvasRef.current;
20980
- if (topHeaderCanvas) {
20981
- topHeaderCanvas.style.transform = scrollDeltaX !== 0 ? `translate3d(${scrollDeltaX}px, 0, 0)` : "";
20982
- topHeaderCanvas.style.willChange = scrollDeltaX !== 0 ? "transform" : "";
20983
- }
20984
- const leftHeaderCanvas = leftHeaderCanvasRef.current;
20985
- if (leftHeaderCanvas) {
20986
- leftHeaderCanvas.style.transform = scrollDeltaY !== 0 ? `translate3d(0, ${scrollDeltaY}px, 0)` : "";
20987
- leftHeaderCanvas.style.willChange = scrollDeltaY !== 0 ? "transform" : "";
20988
- }
21250
+ applyCanvasTransform(
21251
+ topScrollHeaderCanvasRef.current,
21252
+ scrollDeltaX + liveZoomTranslateX2,
21253
+ liveZoomTranslateY2
21254
+ );
21255
+ applyCanvasTransform(
21256
+ topFrozenHeaderCanvasRef.current,
21257
+ liveZoomTranslateX2,
21258
+ liveZoomTranslateY2
21259
+ );
21260
+ applyCanvasTransform(
21261
+ leftScrollHeaderCanvasRef.current,
21262
+ liveZoomTranslateX2,
21263
+ scrollDeltaY + liveZoomTranslateY2
21264
+ );
21265
+ applyCanvasTransform(
21266
+ leftFrozenHeaderCanvasRef.current,
21267
+ liveZoomTranslateX2,
21268
+ liveZoomTranslateY2
21269
+ );
21270
+ applyCanvasTransform(
21271
+ cornerHeaderCanvasRef.current,
21272
+ liveZoomTranslateX2,
21273
+ liveZoomTranslateY2
21274
+ );
20989
21275
  }, [zoomScale]);
20990
21276
  const updateLiveGestureZoomState = React4.useCallback((nextState) => {
20991
21277
  const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
@@ -21116,6 +21402,22 @@ function XlsxGrid({
21116
21402
  scrollRef.current.style.cursor = "";
21117
21403
  }
21118
21404
  }, []);
21405
+ const getBodyBlitBufferCanvas = React4.useCallback((pane) => {
21406
+ let bufferCanvas = bodyBlitBufferCanvasRef.current[pane];
21407
+ if (!bufferCanvas && typeof document !== "undefined") {
21408
+ bufferCanvas = document.createElement("canvas");
21409
+ bodyBlitBufferCanvasRef.current[pane] = bufferCanvas;
21410
+ }
21411
+ return bufferCanvas;
21412
+ }, []);
21413
+ const getHeaderBlitBufferCanvas = React4.useCallback((axis) => {
21414
+ let bufferCanvas = headerBlitBufferCanvasRef.current[axis];
21415
+ if (!bufferCanvas && typeof document !== "undefined") {
21416
+ bufferCanvas = document.createElement("canvas");
21417
+ headerBlitBufferCanvasRef.current[axis] = bufferCanvas;
21418
+ }
21419
+ return bufferCanvas;
21420
+ }, []);
21119
21421
  const visibleRows = React4.useMemo(() => {
21120
21422
  return buildVisibleAxisIndices(
21121
21423
  activeSheet?.visibleRows ?? [],
@@ -21830,14 +22132,14 @@ function XlsxGrid({
21830
22132
  const handleScrollerScroll = React4.useCallback((event) => {
21831
22133
  const currentScroller = event.currentTarget;
21832
22134
  cachedScrollerRectRef.current = null;
21833
- syncDrawingViewport(currentScroller, { immediate: !experimentalCanvas });
22135
+ syncDrawingViewport(currentScroller, { immediate: true });
21834
22136
  if (currentScroller.scrollHeight - (currentScroller.scrollTop + currentScroller.clientHeight) < OPEN_GRID_VERTICAL_EDGE_PX) {
21835
22137
  setDisplayRowLimit((current) => current + OPEN_GRID_ROW_GROWTH);
21836
22138
  }
21837
22139
  if (currentScroller.scrollWidth - (currentScroller.scrollLeft + currentScroller.clientWidth) < OPEN_GRID_HORIZONTAL_EDGE_PX) {
21838
22140
  setDisplayColLimit((current) => current + OPEN_GRID_COL_GROWTH);
21839
22141
  }
21840
- }, [experimentalCanvas, syncDrawingViewport]);
22142
+ }, [syncDrawingViewport]);
21841
22143
  React4.useEffect(() => {
21842
22144
  const scroller = scrollRef.current;
21843
22145
  if (!scroller || !enableGestureZoom) {
@@ -22104,7 +22406,7 @@ function XlsxGrid({
22104
22406
  const geometryCell = resolvePointerCellFromGeometry(clientX, clientY);
22105
22407
  const hitCell = resolvePointerCellFromHitTest(clientX, clientY);
22106
22408
  const actualRow = hitCell && rowIndexByActual.has(hitCell.row) ? hitCell.row : geometryCell?.row;
22107
- const actualCol = geometryCell?.col ?? hitCell?.col;
22409
+ const actualCol = hitCell?.col ?? geometryCell?.col;
22108
22410
  if (actualRow === void 0 || actualCol === void 0) {
22109
22411
  return null;
22110
22412
  }
@@ -23702,6 +24004,54 @@ function XlsxGrid({
23702
24004
  rowPrefixSums,
23703
24005
  stickyTopByRow
23704
24006
  ]);
24007
+ const canvasColumnHeaderCells = React4.useMemo(
24008
+ () => canvasVisibleColItems.flatMap((column) => {
24009
+ const rect = resolveCanvasColumnHeaderRect(column.actualCol);
24010
+ if (!rect) {
24011
+ return [];
24012
+ }
24013
+ const isFrozen = stickyLeftByCol.has(column.actualCol);
24014
+ return [{
24015
+ actualCol: column.actualCol,
24016
+ height: displayHeaderHeight,
24017
+ isFrozen,
24018
+ left: rect.left,
24019
+ localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : frozenPaneRight),
24020
+ width: rect.width
24021
+ }];
24022
+ }),
24023
+ [
24024
+ canvasVisibleColItems,
24025
+ displayHeaderHeight,
24026
+ displayRowHeaderWidth,
24027
+ frozenPaneRight,
24028
+ resolveCanvasColumnHeaderRect,
24029
+ stickyLeftByCol
24030
+ ]
24031
+ );
24032
+ const canvasRowHeaderCells = React4.useMemo(
24033
+ () => canvasVisibleRowItems.flatMap((row) => {
24034
+ const rect = resolveCanvasRowHeaderRect(row.actualRow);
24035
+ if (!rect) {
24036
+ return [];
24037
+ }
24038
+ const isFrozen = stickyTopByRow.has(row.actualRow);
24039
+ return [{
24040
+ actualRow: row.actualRow,
24041
+ height: rect.height,
24042
+ isFrozen,
24043
+ localTop: rect.top - (isFrozen ? displayHeaderHeight : frozenPaneBottom),
24044
+ top: rect.top
24045
+ }];
24046
+ }),
24047
+ [
24048
+ canvasVisibleRowItems,
24049
+ displayHeaderHeight,
24050
+ frozenPaneBottom,
24051
+ resolveCanvasRowHeaderRect,
24052
+ stickyTopByRow
24053
+ ]
24054
+ );
23705
24055
  const resolveCanvasColumnResizeTarget = React4.useCallback((clientX) => {
23706
24056
  if (!canResizeHeaders) {
23707
24057
  return null;
@@ -23711,17 +24061,26 @@ function XlsxGrid({
23711
24061
  return null;
23712
24062
  }
23713
24063
  const localX = clientX - scrollerRect.left;
23714
- for (const column of canvasVisibleColItems) {
23715
- const rect = resolveCanvasColumnHeaderRect(column.actualCol);
23716
- if (!rect) {
23717
- continue;
24064
+ for (const column of canvasColumnHeaderCells) {
24065
+ if (Math.abs(localX - (column.left + column.width)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
24066
+ return { actualCol: column.actualCol, width: column.width };
23718
24067
  }
23719
- if (Math.abs(localX - (rect.left + rect.width)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
23720
- return { actualCol: column.actualCol, width: rect.width };
24068
+ }
24069
+ return null;
24070
+ }, [canResizeHeaders, canvasColumnHeaderCells]);
24071
+ const resolveCanvasColumnHeaderTarget = React4.useCallback((clientX) => {
24072
+ const scrollerRect = scrollRef.current?.getBoundingClientRect();
24073
+ if (!scrollerRect) {
24074
+ return null;
24075
+ }
24076
+ const localX = clientX - scrollerRect.left;
24077
+ for (const column of canvasColumnHeaderCells) {
24078
+ if (localX >= column.left && localX <= column.left + column.width) {
24079
+ return column.actualCol;
23721
24080
  }
23722
24081
  }
23723
24082
  return null;
23724
- }, [canResizeHeaders, canvasVisibleColItems, resolveCanvasColumnHeaderRect]);
24083
+ }, [canvasColumnHeaderCells]);
23725
24084
  const resolveCanvasRowResizeTarget = React4.useCallback((clientY) => {
23726
24085
  if (!canResizeHeaders) {
23727
24086
  return null;
@@ -23731,17 +24090,26 @@ function XlsxGrid({
23731
24090
  return null;
23732
24091
  }
23733
24092
  const localY = clientY - scrollerRect.top;
23734
- for (const row of canvasVisibleRowItems) {
23735
- const rect = resolveCanvasRowHeaderRect(row.actualRow);
23736
- if (!rect) {
23737
- continue;
24093
+ for (const row of canvasRowHeaderCells) {
24094
+ if (Math.abs(localY - (row.top + row.height)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
24095
+ return { actualRow: row.actualRow, height: row.height };
23738
24096
  }
23739
- if (Math.abs(localY - (rect.top + rect.height)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
23740
- return { actualRow: row.actualRow, height: rect.height };
24097
+ }
24098
+ return null;
24099
+ }, [canResizeHeaders, canvasRowHeaderCells]);
24100
+ const resolveCanvasRowHeaderTarget = React4.useCallback((clientY) => {
24101
+ const scrollerRect = scrollRef.current?.getBoundingClientRect();
24102
+ if (!scrollerRect) {
24103
+ return null;
24104
+ }
24105
+ const localY = clientY - scrollerRect.top;
24106
+ for (const row of canvasRowHeaderCells) {
24107
+ if (localY >= row.top && localY <= row.top + row.height) {
24108
+ return row.actualRow;
23741
24109
  }
23742
24110
  }
23743
24111
  return null;
23744
- }, [canResizeHeaders, canvasVisibleRowItems, resolveCanvasRowHeaderRect]);
24112
+ }, [canvasRowHeaderCells]);
23745
24113
  const handleCanvasColumnHeaderPointerMove = React4.useCallback((event) => {
23746
24114
  if (resizeStateRef.current?.type === "column") {
23747
24115
  event.currentTarget.style.cursor = "col-resize";
@@ -23844,17 +24212,17 @@ function XlsxGrid({
23844
24212
  startColumnResize(event.pointerId, resizeTarget.actualCol, resizeTarget.width, event.clientX);
23845
24213
  return;
23846
24214
  }
23847
- const cell = resolvePointerCellFromClient(event.clientX, event.clientY);
23848
- if (!cell) {
24215
+ const actualCol = resolveCanvasColumnHeaderTarget(event.clientX);
24216
+ if (actualCol === null) {
23849
24217
  return;
23850
24218
  }
23851
24219
  event.preventDefault();
23852
24220
  focusGrid();
23853
24221
  const currentSelection = selectionRef.current;
23854
- const anchorCol = event.shiftKey && currentSelection ? currentSelection.start.col : cell.col;
24222
+ const anchorCol = event.shiftKey && currentSelection ? currentSelection.start.col : actualCol;
23855
24223
  const initialRange = normalizeRange2({
23856
24224
  start: { row: firstVisibleRow, col: anchorCol },
23857
- end: { row: lastVisibleRow, col: cell.col }
24225
+ end: { row: lastVisibleRow, col: actualCol }
23858
24226
  });
23859
24227
  const anchorColIndex = colIndexByActual.get(anchorCol);
23860
24228
  if (anchorColIndex === void 0) {
@@ -23864,7 +24232,7 @@ function XlsxGrid({
23864
24232
  event.pointerId,
23865
24233
  { row: firstVisibleRow, col: anchorCol },
23866
24234
  "column",
23867
- { row: firstVisibleRow, col: cell.col },
24235
+ { row: firstVisibleRow, col: actualCol },
23868
24236
  {
23869
24237
  contentScaleX: 1,
23870
24238
  contentScaleY: 1,
@@ -23885,9 +24253,8 @@ function XlsxGrid({
23885
24253
  firstVisibleRow,
23886
24254
  focusGrid,
23887
24255
  lastVisibleRow,
23888
- resolveCanvasColumnHeaderRect,
24256
+ resolveCanvasColumnHeaderTarget,
23889
24257
  resolveCanvasColumnResizeTarget,
23890
- resolvePointerCellFromClient,
23891
24258
  rowPrefixSums,
23892
24259
  startCellSelection
23893
24260
  ]);
@@ -23902,17 +24269,17 @@ function XlsxGrid({
23902
24269
  startRowResize(event.pointerId, resizeTarget.actualRow, resizeTarget.height, event.clientY);
23903
24270
  return;
23904
24271
  }
23905
- const cell = resolvePointerCellFromClient(event.clientX, event.clientY);
23906
- if (!cell) {
24272
+ const actualRow = resolveCanvasRowHeaderTarget(event.clientY);
24273
+ if (actualRow === null) {
23907
24274
  return;
23908
24275
  }
23909
24276
  event.preventDefault();
23910
24277
  focusGrid();
23911
24278
  const currentSelection = selectionRef.current;
23912
- const anchorRow = event.shiftKey && currentSelection ? currentSelection.start.row : cell.row;
24279
+ const anchorRow = event.shiftKey && currentSelection ? currentSelection.start.row : actualRow;
23913
24280
  const initialRange = normalizeRange2({
23914
24281
  start: { row: anchorRow, col: firstVisibleCol },
23915
- end: { row: cell.row, col: lastVisibleCol }
24282
+ end: { row: actualRow, col: lastVisibleCol }
23916
24283
  });
23917
24284
  const anchorRowIndex = rowIndexByActual.get(anchorRow);
23918
24285
  if (anchorRowIndex === void 0) {
@@ -23922,7 +24289,7 @@ function XlsxGrid({
23922
24289
  event.pointerId,
23923
24290
  { row: anchorRow, col: firstVisibleCol },
23924
24291
  "row",
23925
- { row: cell.row, col: firstVisibleCol },
24292
+ { row: actualRow, col: firstVisibleCol },
23926
24293
  {
23927
24294
  contentScaleX: 1,
23928
24295
  contentScaleY: 1,
@@ -23942,8 +24309,8 @@ function XlsxGrid({
23942
24309
  firstVisibleCol,
23943
24310
  focusGrid,
23944
24311
  lastVisibleCol,
24312
+ resolveCanvasRowHeaderTarget,
23945
24313
  resolveCanvasRowResizeTarget,
23946
- resolvePointerCellFromClient,
23947
24314
  rowIndexByActual,
23948
24315
  rowPrefixSums,
23949
24316
  startCellSelection
@@ -23953,7 +24320,7 @@ function XlsxGrid({
23953
24320
  return;
23954
24321
  }
23955
24322
  const dpr = typeof window === "undefined" ? 1 : Math.max(1, window.devicePixelRatio || 1);
23956
- function configureCanvas(canvas, width, height) {
24323
+ function configureCanvas(canvas, width, height, options) {
23957
24324
  if (!canvas) {
23958
24325
  return null;
23959
24326
  }
@@ -23976,7 +24343,9 @@ function XlsxGrid({
23976
24343
  return null;
23977
24344
  }
23978
24345
  context.setTransform(dpr, 0, 0, dpr, 0, 0);
23979
- context.clearRect(0, 0, width, height);
24346
+ if (options?.clear !== false) {
24347
+ context.clearRect(0, 0, width, height);
24348
+ }
23980
24349
  return context;
23981
24350
  }
23982
24351
  const bodyWidth = Math.max(0, drawingViewport.width);
@@ -24008,12 +24377,24 @@ function XlsxGrid({
24008
24377
  rangeSignature,
24009
24378
  rowHeaderWidth,
24010
24379
  rowSignature: nextBodyCanvasSignature.rowSignature,
24380
+ viewportLeft: drawingViewport.left,
24381
+ viewportTop: drawingViewport.top,
24011
24382
  zoomFactor
24012
24383
  };
24013
24384
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
24014
24385
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
24386
+ const previousPaintedViewport = paintedDrawingViewportRef.current;
24015
24387
  const shouldRepaintBody = !(previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.colSignature === nextBodyCanvasSignature.colSignature && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.rowSignature === nextBodyCanvasSignature.rowSignature && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor);
24016
- 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.zoomFactor === nextHeaderCanvasSignature.zoomFactor);
24388
+ const shouldRepaintHeaders = !(previousHeaderCanvasSignature && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth && previousHeaderCanvasSignature.colSignature === nextHeaderCanvasSignature.colSignature && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth && previousHeaderCanvasSignature.rowSignature === nextHeaderCanvasSignature.rowSignature && previousHeaderCanvasSignature.viewportLeft === nextHeaderCanvasSignature.viewportLeft && previousHeaderCanvasSignature.viewportTop === nextHeaderCanvasSignature.viewportTop && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor);
24389
+ const canBlitBody = Boolean(
24390
+ shouldRepaintBody && previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor
24391
+ );
24392
+ const canBlitTopHeader = Boolean(
24393
+ 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
24394
+ );
24395
+ const canBlitLeftHeader = Boolean(
24396
+ 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
24397
+ );
24017
24398
  if (!shouldRepaintBody && !shouldRepaintHeaders) {
24018
24399
  return;
24019
24400
  }
@@ -24025,6 +24406,10 @@ function XlsxGrid({
24025
24406
  const leftBodyCanvasHeight2 = scrollBodyCanvasHeight2;
24026
24407
  const cornerBodyCanvasWidth2 = leftBodyCanvasWidth2;
24027
24408
  const cornerBodyCanvasHeight2 = topBodyCanvasHeight2;
24409
+ const topFrozenHeaderCanvasWidth2 = leftBodyCanvasWidth2;
24410
+ const topScrollHeaderCanvasWidth2 = scrollBodyCanvasWidth2;
24411
+ const leftFrozenHeaderCanvasHeight2 = topBodyCanvasHeight2;
24412
+ const leftScrollHeaderCanvasHeight2 = scrollBodyCanvasHeight2;
24028
24413
  const paneBounds = {
24029
24414
  corner: {
24030
24415
  height: cornerBodyCanvasHeight2,
@@ -24052,29 +24437,50 @@ function XlsxGrid({
24052
24437
  }
24053
24438
  };
24054
24439
  const bodyContexts = shouldRepaintBody ? {
24055
- corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth2, cornerBodyCanvasHeight2),
24056
- left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth2, leftBodyCanvasHeight2),
24057
- scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth2, scrollBodyCanvasHeight2),
24058
- top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth2, topBodyCanvasHeight2)
24440
+ corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth2, cornerBodyCanvasHeight2, { clear: !canBlitBody }),
24441
+ left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth2, leftBodyCanvasHeight2, { clear: !canBlitBody }),
24442
+ scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth2, scrollBodyCanvasHeight2, { clear: !canBlitBody }),
24443
+ top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth2, topBodyCanvasHeight2, { clear: !canBlitBody })
24059
24444
  } : {
24060
24445
  corner: null,
24061
24446
  left: null,
24062
24447
  scroll: null,
24063
24448
  top: null
24064
24449
  };
24065
- const topHeaderContext = shouldRepaintHeaders ? configureCanvas(topHeaderCanvasRef.current, bodyWidth, headerHeight) : null;
24066
- const leftHeaderContext = shouldRepaintHeaders ? configureCanvas(leftHeaderCanvasRef.current, rowHeaderWidth, bodyHeight) : null;
24450
+ const topHeaderContexts = shouldRepaintHeaders ? {
24451
+ frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth2, headerHeight),
24452
+ scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth2, headerHeight, { clear: !canBlitTopHeader })
24453
+ } : {
24454
+ frozen: null,
24455
+ scroll: null
24456
+ };
24457
+ const leftHeaderContexts = shouldRepaintHeaders ? {
24458
+ frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight2),
24459
+ scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight2, { clear: !canBlitLeftHeader })
24460
+ } : {
24461
+ frozen: null,
24462
+ scroll: null
24463
+ };
24067
24464
  const cornerContext = shouldRepaintHeaders ? configureCanvas(cornerHeaderCanvasRef.current, rowHeaderWidth, headerHeight) : null;
24068
- if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!topHeaderContext || !leftHeaderContext || !cornerContext)) {
24465
+ if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!cornerContext || topFrozenHeaderCanvasWidth2 > 0 && !topHeaderContexts.frozen || topScrollHeaderCanvasWidth2 > 0 && !topHeaderContexts.scroll || leftFrozenHeaderCanvasHeight2 > 0 && !leftHeaderContexts.frozen || leftScrollHeaderCanvasHeight2 > 0 && !leftHeaderContexts.scroll)) {
24069
24466
  return;
24070
24467
  }
24071
24468
  const showGridLines = activeSheet?.showGridLines ?? true;
24469
+ const sheetSurface = resolveSheetSurface(activeSheet, palette);
24072
24470
  const deferredSpillTextsByPane = {
24073
24471
  corner: [],
24074
24472
  left: [],
24075
24473
  scroll: [],
24076
24474
  top: []
24077
24475
  };
24476
+ const bodyDirtyRectsByPane = {
24477
+ corner: [],
24478
+ left: [],
24479
+ scroll: [],
24480
+ top: []
24481
+ };
24482
+ let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth2, headerHeight);
24483
+ let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight2);
24078
24484
  const cellPaneOrder = ["scroll", "top", "left", "corner"];
24079
24485
  if (shouldRepaintBody) {
24080
24486
  for (const pane of Object.keys(bodyContexts)) {
@@ -24083,14 +24489,44 @@ function XlsxGrid({
24083
24489
  if (!context || bounds.width <= 0 || bounds.height <= 0) {
24084
24490
  continue;
24085
24491
  }
24086
- context.fillStyle = resolveSheetSurface(activeSheet, palette);
24087
- context.fillRect(0, 0, bounds.width, bounds.height);
24492
+ let dirtyRects = buildFullCanvasDirtyRect(bounds.width, bounds.height);
24493
+ if (canBlitBody) {
24494
+ const bufferCanvas = getBodyBlitBufferCanvas(pane);
24495
+ const deltaX = pane === "scroll" || pane === "top" ? drawingViewport.left - previousPaintedViewport.left : 0;
24496
+ const deltaY = pane === "scroll" || pane === "left" ? drawingViewport.top - previousPaintedViewport.top : 0;
24497
+ if (bufferCanvas) {
24498
+ const blittedDirtyRects = blitCanvasWithScrollDelta(
24499
+ pane === "corner" ? cornerBodyCanvasRef.current : pane === "left" ? leftBodyCanvasRef.current : pane === "top" ? topBodyCanvasRef.current : scrollBodyCanvasRef.current,
24500
+ context,
24501
+ bufferCanvas,
24502
+ dpr,
24503
+ bounds.width,
24504
+ bounds.height,
24505
+ deltaX,
24506
+ deltaY
24507
+ );
24508
+ if (blittedDirtyRects) {
24509
+ dirtyRects = blittedDirtyRects;
24510
+ }
24511
+ }
24512
+ } else {
24513
+ context.clearRect(0, 0, bounds.width, bounds.height);
24514
+ }
24515
+ bodyDirtyRectsByPane[pane] = dirtyRects;
24516
+ if (dirtyRects.length === 0) {
24517
+ continue;
24518
+ }
24519
+ context.fillStyle = sheetSurface;
24520
+ for (const dirtyRect of dirtyRects) {
24521
+ context.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
24522
+ }
24088
24523
  }
24089
24524
  for (const pane of cellPaneOrder) {
24090
24525
  const paneContext = bodyContexts[pane];
24091
24526
  const paneBoundsForCell = paneBounds[pane];
24527
+ const paneDirtyRects = bodyDirtyRectsByPane[pane];
24092
24528
  const paneAxisItems = canvasPaneAxisItems[pane];
24093
- if (!paneContext || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
24529
+ if (!paneContext || paneDirtyRects.length === 0 || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
24094
24530
  continue;
24095
24531
  }
24096
24532
  const drawnMergedAnchorKeys = /* @__PURE__ */ new Set();
@@ -24130,11 +24566,14 @@ function XlsxGrid({
24130
24566
  if (localRect.left + drawableWidth < 0 || localRect.top + localRect.height < 0 || localRect.left > paneBoundsForCell.width || localRect.top > paneBoundsForCell.height) {
24131
24567
  continue;
24132
24568
  }
24569
+ if (!intersectsCanvasDirtyRects(localRect.left, localRect.top, drawableWidth, localRect.height, paneDirtyRects)) {
24570
+ continue;
24571
+ }
24133
24572
  const cellStyle = cellData.style;
24134
24573
  const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellStyle);
24135
- const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : resolveSheetSurface(activeSheet, palette));
24574
+ const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : sheetSurface);
24136
24575
  const gradientFill = !cellData.conditionalColorScale && typeof cellStyle.backgroundImage === "string" ? resolveCanvasGradientFill(paneContext, localRect, cellStyle.backgroundImage) : null;
24137
- const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== resolveSheetSurface(activeSheet, palette);
24576
+ const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== sheetSurface;
24138
24577
  paneContext.fillStyle = gradientFill ?? fillColor;
24139
24578
  paneContext.fillRect(localRect.left, localRect.top, localRect.width, localRect.height);
24140
24579
  if (cellData.chartHighlight) {
@@ -24488,57 +24927,141 @@ function XlsxGrid({
24488
24927
  }
24489
24928
  }
24490
24929
  }
24491
- if (shouldRepaintHeaders && topHeaderContext && leftHeaderContext && cornerContext) {
24492
- topHeaderContext.fillStyle = palette.headerSurface;
24493
- topHeaderContext.fillRect(0, 0, bodyWidth, headerHeight);
24494
- topHeaderContext.strokeStyle = palette.border;
24495
- topHeaderContext.lineWidth = 1;
24496
- for (const colItem of canvasVisibleColItems) {
24497
- const rect = resolveCanvasColumnHeaderRect(colItem.actualCol);
24498
- if (!rect || rect.left + rect.width < displayRowHeaderWidth || rect.left > bodyWidth) {
24930
+ if (shouldRepaintHeaders && cornerContext) {
24931
+ const topFrozenHeaderContext = topHeaderContexts.frozen;
24932
+ const topScrollHeaderContext = topHeaderContexts.scroll;
24933
+ const leftFrozenHeaderContext = leftHeaderContexts.frozen;
24934
+ const leftScrollHeaderContext = leftHeaderContexts.scroll;
24935
+ if (canBlitTopHeader) {
24936
+ const bufferCanvas = getHeaderBlitBufferCanvas("top");
24937
+ if (bufferCanvas && topScrollHeaderContext && topScrollHeaderCanvasRef.current) {
24938
+ const blittedDirtyRects = blitCanvasWithScrollDelta(
24939
+ topScrollHeaderCanvasRef.current,
24940
+ topScrollHeaderContext,
24941
+ bufferCanvas,
24942
+ dpr,
24943
+ topScrollHeaderCanvasWidth2,
24944
+ headerHeight,
24945
+ drawingViewport.left - previousPaintedViewport.left,
24946
+ 0
24947
+ );
24948
+ if (blittedDirtyRects) {
24949
+ topScrollHeaderDirtyRects = blittedDirtyRects;
24950
+ }
24951
+ }
24952
+ }
24953
+ if (canBlitLeftHeader) {
24954
+ const bufferCanvas = getHeaderBlitBufferCanvas("left");
24955
+ if (bufferCanvas && leftScrollHeaderContext && leftScrollHeaderCanvasRef.current) {
24956
+ const blittedDirtyRects = blitCanvasWithScrollDelta(
24957
+ leftScrollHeaderCanvasRef.current,
24958
+ leftScrollHeaderContext,
24959
+ bufferCanvas,
24960
+ dpr,
24961
+ rowHeaderWidth,
24962
+ leftScrollHeaderCanvasHeight2,
24963
+ 0,
24964
+ drawingViewport.top - previousPaintedViewport.top
24965
+ );
24966
+ if (blittedDirtyRects) {
24967
+ leftScrollHeaderDirtyRects = blittedDirtyRects;
24968
+ }
24969
+ }
24970
+ }
24971
+ if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth2 > 0) {
24972
+ topFrozenHeaderContext.fillStyle = palette.headerSurface;
24973
+ topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth2, headerHeight);
24974
+ }
24975
+ if (topScrollHeaderContext && topScrollHeaderCanvasWidth2 > 0) {
24976
+ topScrollHeaderContext.fillStyle = palette.headerSurface;
24977
+ for (const dirtyRect of topScrollHeaderDirtyRects) {
24978
+ topScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
24979
+ }
24980
+ }
24981
+ if (topFrozenHeaderContext) {
24982
+ topFrozenHeaderContext.strokeStyle = palette.border;
24983
+ topFrozenHeaderContext.lineWidth = 1;
24984
+ }
24985
+ if (topScrollHeaderContext) {
24986
+ topScrollHeaderContext.strokeStyle = palette.border;
24987
+ topScrollHeaderContext.lineWidth = 1;
24988
+ }
24989
+ for (const column of canvasColumnHeaderCells) {
24990
+ const paneContext = column.isFrozen ? topFrozenHeaderContext : topScrollHeaderContext;
24991
+ if (!paneContext) {
24992
+ continue;
24993
+ }
24994
+ if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth2 : topScrollHeaderCanvasWidth2)) {
24995
+ continue;
24996
+ }
24997
+ if (!column.isFrozen && !intersectsCanvasDirtyRects(column.localLeft, 0, column.width, column.height, topScrollHeaderDirtyRects)) {
24998
+ continue;
24999
+ }
25000
+ const selected = normalizedVisibleRange && column.actualCol >= normalizedVisibleRange.start.col && column.actualCol <= normalizedVisibleRange.end.col;
25001
+ paneContext.fillStyle = selected ? selectionHeaderSurface : palette.headerSurface;
25002
+ paneContext.fillRect(column.localLeft, 0, column.width, column.height);
25003
+ paneContext.strokeStyle = palette.border;
25004
+ paneContext.beginPath();
25005
+ paneContext.moveTo(column.localLeft + column.width - 0.5, 0);
25006
+ paneContext.lineTo(column.localLeft + column.width - 0.5, column.height);
25007
+ paneContext.moveTo(column.localLeft, column.height - 0.5);
25008
+ paneContext.lineTo(column.localLeft + column.width, column.height - 0.5);
25009
+ paneContext.stroke();
25010
+ paneContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
25011
+ paneContext.fillStyle = palette.mutedText;
25012
+ paneContext.textAlign = "center";
25013
+ paneContext.textBaseline = "middle";
25014
+ paneContext.fillText(
25015
+ columnLabel2(column.actualCol),
25016
+ column.localLeft + column.width / 2,
25017
+ column.height / 2
25018
+ );
25019
+ }
25020
+ if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight2 > 0) {
25021
+ leftFrozenHeaderContext.fillStyle = palette.rowHeaderSurface;
25022
+ leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight2);
25023
+ }
25024
+ if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight2 > 0) {
25025
+ leftScrollHeaderContext.fillStyle = palette.rowHeaderSurface;
25026
+ for (const dirtyRect of leftScrollHeaderDirtyRects) {
25027
+ leftScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
25028
+ }
25029
+ }
25030
+ if (leftFrozenHeaderContext) {
25031
+ leftFrozenHeaderContext.strokeStyle = palette.border;
25032
+ leftFrozenHeaderContext.lineWidth = 1;
25033
+ }
25034
+ if (leftScrollHeaderContext) {
25035
+ leftScrollHeaderContext.strokeStyle = palette.border;
25036
+ leftScrollHeaderContext.lineWidth = 1;
25037
+ }
25038
+ for (const row of canvasRowHeaderCells) {
25039
+ const paneContext = row.isFrozen ? leftFrozenHeaderContext : leftScrollHeaderContext;
25040
+ if (!paneContext) {
24499
25041
  continue;
24500
25042
  }
24501
- const selected = normalizedVisibleRange && colItem.actualCol >= normalizedVisibleRange.start.col && colItem.actualCol <= normalizedVisibleRange.end.col;
24502
- topHeaderContext.fillStyle = selected ? selectionHeaderSurface : palette.headerSurface;
24503
- topHeaderContext.fillRect(rect.left, 0, rect.width, headerHeight);
24504
- topHeaderContext.strokeStyle = palette.border;
24505
- topHeaderContext.beginPath();
24506
- topHeaderContext.moveTo(rect.left + rect.width - 0.5, 0);
24507
- topHeaderContext.lineTo(rect.left + rect.width - 0.5, headerHeight);
24508
- topHeaderContext.moveTo(rect.left, headerHeight - 0.5);
24509
- topHeaderContext.lineTo(rect.left + rect.width, headerHeight - 0.5);
24510
- topHeaderContext.stroke();
24511
- topHeaderContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
24512
- topHeaderContext.fillStyle = palette.mutedText;
24513
- topHeaderContext.textAlign = "center";
24514
- topHeaderContext.textBaseline = "middle";
24515
- topHeaderContext.fillText(columnLabel2(colItem.actualCol), rect.left + rect.width / 2, headerHeight / 2);
24516
- }
24517
- leftHeaderContext.fillStyle = palette.rowHeaderSurface;
24518
- leftHeaderContext.fillRect(0, 0, rowHeaderWidth, bodyHeight);
24519
- leftHeaderContext.strokeStyle = palette.border;
24520
- leftHeaderContext.lineWidth = 1;
24521
- for (const rowItem of canvasVisibleRowItems) {
24522
- const rect = resolveCanvasRowHeaderRect(rowItem.actualRow);
24523
- if (!rect || rect.top + rect.height < displayHeaderHeight || rect.top > bodyHeight) {
25043
+ if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight2 : leftScrollHeaderCanvasHeight2)) {
24524
25044
  continue;
24525
25045
  }
24526
- const selected = normalizedVisibleRange && rowItem.actualRow >= normalizedVisibleRange.start.row && rowItem.actualRow <= normalizedVisibleRange.end.row;
24527
- leftHeaderContext.fillStyle = selected ? selectionHeaderSurface : palette.rowHeaderSurface;
24528
- leftHeaderContext.fillRect(0, rect.top, rowHeaderWidth, rect.height);
24529
- leftHeaderContext.beginPath();
24530
- leftHeaderContext.moveTo(0, rect.top + rect.height - 0.5);
24531
- leftHeaderContext.lineTo(rowHeaderWidth, rect.top + rect.height - 0.5);
24532
- leftHeaderContext.moveTo(rowHeaderWidth - 0.5, rect.top);
24533
- leftHeaderContext.lineTo(rowHeaderWidth - 0.5, rect.top + rect.height);
24534
- leftHeaderContext.stroke();
24535
- leftHeaderContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
24536
- leftHeaderContext.fillStyle = palette.mutedText;
24537
- leftHeaderContext.textAlign = "center";
24538
- leftHeaderContext.textBaseline = "middle";
24539
- leftHeaderContext.fillText(`${rowItem.actualRow + 1}`, rowHeaderWidth / 2, rect.top + rect.height / 2);
24540
- }
24541
- cornerContext.fillStyle = palette.rowHeaderSurface;
25046
+ if (!row.isFrozen && !intersectsCanvasDirtyRects(0, row.localTop, rowHeaderWidth, row.height, leftScrollHeaderDirtyRects)) {
25047
+ continue;
25048
+ }
25049
+ const selected = normalizedVisibleRange && row.actualRow >= normalizedVisibleRange.start.row && row.actualRow <= normalizedVisibleRange.end.row;
25050
+ paneContext.fillStyle = selected ? selectionHeaderSurface : palette.rowHeaderSurface;
25051
+ paneContext.fillRect(0, row.localTop, rowHeaderWidth, row.height);
25052
+ paneContext.beginPath();
25053
+ paneContext.moveTo(0, row.localTop + row.height - 0.5);
25054
+ paneContext.lineTo(rowHeaderWidth, row.localTop + row.height - 0.5);
25055
+ paneContext.moveTo(rowHeaderWidth - 0.5, row.localTop);
25056
+ paneContext.lineTo(rowHeaderWidth - 0.5, row.localTop + row.height);
25057
+ paneContext.stroke();
25058
+ paneContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
25059
+ paneContext.fillStyle = palette.mutedText;
25060
+ paneContext.textAlign = "center";
25061
+ paneContext.textBaseline = "middle";
25062
+ paneContext.fillText(`${row.actualRow + 1}`, rowHeaderWidth / 2, row.localTop + row.height / 2);
25063
+ }
25064
+ cornerContext.fillStyle = palette.headerSurface;
24542
25065
  cornerContext.fillRect(0, 0, rowHeaderWidth, headerHeight);
24543
25066
  cornerContext.strokeStyle = palette.border;
24544
25067
  cornerContext.lineWidth = 1;
@@ -24560,7 +25083,9 @@ function XlsxGrid({
24560
25083
  }, [
24561
25084
  activeSheet,
24562
25085
  applyCanvasViewportCompensation,
25086
+ canvasColumnHeaderCells,
24563
25087
  canvasPaneAxisItems,
25088
+ canvasRowHeaderCells,
24564
25089
  canvasVisibleColItems,
24565
25090
  canvasVisibleRowItems,
24566
25091
  colIndexByActual,
@@ -24575,11 +25100,13 @@ function XlsxGrid({
24575
25100
  drawingViewport.height,
24576
25101
  drawingViewport.width,
24577
25102
  experimentalCanvas,
25103
+ frozenPaneBottom,
25104
+ frozenPaneRight,
24578
25105
  getCellData,
25106
+ getBodyBlitBufferCanvas,
25107
+ getHeaderBlitBufferCanvas,
24579
25108
  palette,
24580
25109
  resolveCellDisplayRect,
24581
- resolveCanvasColumnHeaderRect,
24582
- resolveCanvasRowHeaderRect,
24583
25110
  resolveMergeAnchorCell,
24584
25111
  resizeGuide,
24585
25112
  rowIndexByActual,
@@ -24817,7 +25344,18 @@ function XlsxGrid({
24817
25344
  const canvasSelectionTransition = shouldAnimateCanvasSelection ? "left 120ms cubic-bezier(0.22, 1, 0.36, 1), top 120ms cubic-bezier(0.22, 1, 0.36, 1), width 120ms cubic-bezier(0.22, 1, 0.36, 1), height 120ms cubic-bezier(0.22, 1, 0.36, 1), opacity 100ms linear" : "none";
24818
25345
  const rowColSpan = renderedCols.length + 1 + (leadingColumnSpacerWidth > 0 ? 1 : 0) + (trailingColumnSpacerWidth > 0 ? 1 : 0);
24819
25346
  const gutterSeparatorShadow = `inset -1px 0 0 ${palette.border}, inset 0 -1px 0 ${palette.border}`;
24820
- const canvasHeaderOverlayZIndex = 1e5;
25347
+ const maxDrawingOverlayZIndex = Math.max(
25348
+ 0,
25349
+ ...shapes.map((shape) => shape.zIndex + 20),
25350
+ ...formControls.map((control) => control.zIndex + 20),
25351
+ ...images.map((image) => image.zIndex + 22),
25352
+ ...charts.map((chart) => chart.zIndex + 22)
25353
+ );
25354
+ const rowHeaderOverlayZIndex = Math.max(35, maxDrawingOverlayZIndex + 1);
25355
+ const canvasHeaderOverlayZIndex = Math.max(50, rowHeaderOverlayZIndex + 1);
25356
+ const stickyHeaderOverlayZIndex = canvasHeaderOverlayZIndex + 1;
25357
+ const cornerHeaderOverlayZIndex = canvasHeaderOverlayZIndex + 2;
25358
+ const frozenRowHeaderOverlayZIndex = rowHeaderOverlayZIndex;
24821
25359
  const headerCellStyle = scaleCssProperties({
24822
25360
  backgroundColor: palette.headerSurface,
24823
25361
  borderBottom: "none",
@@ -24834,7 +25372,7 @@ function XlsxGrid({
24834
25372
  top: 0,
24835
25373
  userSelect: "none",
24836
25374
  whiteSpace: "nowrap",
24837
- zIndex: 50
25375
+ zIndex: canvasHeaderOverlayZIndex
24838
25376
  }, zoomFactor);
24839
25377
  const columnResizeHandleStyle = scaleCssProperties({
24840
25378
  backgroundColor: "transparent",
@@ -24873,6 +25411,10 @@ function XlsxGrid({
24873
25411
  const leftBodyCanvasHeight = scrollBodyCanvasHeight;
24874
25412
  const cornerBodyCanvasWidth = leftBodyCanvasWidth;
24875
25413
  const cornerBodyCanvasHeight = topBodyCanvasHeight;
25414
+ const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
25415
+ const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
25416
+ const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
25417
+ const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
24876
25418
  const canvasBodyBaseStyle = {
24877
25419
  cursor: "cell",
24878
25420
  pointerEvents: "auto",
@@ -24908,22 +25450,38 @@ function XlsxGrid({
24908
25450
  top: displayHeaderHeight,
24909
25451
  zIndex: 31
24910
25452
  };
24911
- const canvasTopHeaderStyle = {
25453
+ const canvasHeaderBaseStyle = {
24912
25454
  cursor: "default",
24913
- display: drawingViewport.width > 0 && drawingViewport.height > 0 ? "block" : "none",
24914
- left: 0,
24915
25455
  pointerEvents: "auto",
24916
25456
  position: "absolute",
25457
+ transformOrigin: "0 0"
25458
+ };
25459
+ const canvasTopFrozenHeaderStyle = {
25460
+ ...canvasHeaderBaseStyle,
25461
+ display: topFrozenHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
25462
+ left: displayRowHeaderWidth,
25463
+ top: 0,
25464
+ zIndex: stickyHeaderOverlayZIndex
25465
+ };
25466
+ const canvasTopScrollHeaderStyle = {
25467
+ ...canvasHeaderBaseStyle,
25468
+ display: topScrollHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
25469
+ left: frozenPaneRight,
24917
25470
  top: 0,
24918
25471
  zIndex: canvasHeaderOverlayZIndex
24919
25472
  };
24920
- const canvasLeftHeaderStyle = {
24921
- cursor: "default",
24922
- display: drawingViewport.width > 0 && drawingViewport.height > 0 ? "block" : "none",
25473
+ const canvasLeftFrozenHeaderStyle = {
25474
+ ...canvasHeaderBaseStyle,
25475
+ display: leftFrozenHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
24923
25476
  left: 0,
24924
- pointerEvents: "auto",
24925
- position: "absolute",
24926
- top: 0,
25477
+ top: displayHeaderHeight,
25478
+ zIndex: stickyHeaderOverlayZIndex
25479
+ };
25480
+ const canvasLeftScrollHeaderStyle = {
25481
+ ...canvasHeaderBaseStyle,
25482
+ display: leftScrollHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
25483
+ left: 0,
25484
+ top: frozenPaneBottom,
24927
25485
  zIndex: canvasHeaderOverlayZIndex
24928
25486
  };
24929
25487
  const canvasCornerHeaderStyle = {
@@ -24932,7 +25490,8 @@ function XlsxGrid({
24932
25490
  pointerEvents: "none",
24933
25491
  position: "absolute",
24934
25492
  top: 0,
24935
- zIndex: canvasHeaderOverlayZIndex + 1
25493
+ transformOrigin: "0 0",
25494
+ zIndex: cornerHeaderOverlayZIndex
24936
25495
  };
24937
25496
  const editingOverlayRect = experimentalCanvas && editingCell ? resolveCellDisplayRect(editingCell) : null;
24938
25497
  const activeCellAdornment = experimentalCanvas && activeCell ? renderCellAdornment(activeCell) : null;
@@ -26078,7 +26637,7 @@ function XlsxGrid({
26078
26637
  pointerEvents: "none",
26079
26638
  position: "absolute",
26080
26639
  top: 0,
26081
- transform: `translate(${-drawingViewport.left}px, ${-drawingViewport.top}px)`,
26640
+ transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
26082
26641
  width: totalWidth
26083
26642
  },
26084
26643
  children: paneDrawingNodes.scroll
@@ -26093,7 +26652,7 @@ function XlsxGrid({
26093
26652
  pointerEvents: "none",
26094
26653
  position: "absolute",
26095
26654
  top: 0,
26096
- transform: `translate(${-drawingViewport.left}px, ${-displayHeaderHeight}px)`,
26655
+ transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-displayHeaderHeight}px)`,
26097
26656
  width: totalWidth
26098
26657
  },
26099
26658
  children: paneDrawingNodes.top
@@ -26108,7 +26667,7 @@ function XlsxGrid({
26108
26667
  pointerEvents: "none",
26109
26668
  position: "absolute",
26110
26669
  top: 0,
26111
- transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top}px)`,
26670
+ transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
26112
26671
  width: totalWidth
26113
26672
  },
26114
26673
  children: paneDrawingNodes.left
@@ -26135,21 +26694,41 @@ function XlsxGrid({
26135
26694
  /* @__PURE__ */ jsx3(
26136
26695
  "canvas",
26137
26696
  {
26138
- ref: topHeaderCanvasRef,
26697
+ ref: topFrozenHeaderCanvasRef,
26139
26698
  onPointerLeave: handleCanvasHeaderPointerLeave,
26140
26699
  onPointerMove: handleCanvasColumnHeaderPointerMove,
26141
26700
  onPointerDown: handleCanvasColumnHeaderPointerDown,
26142
- style: canvasTopHeaderStyle
26701
+ style: canvasTopFrozenHeaderStyle
26702
+ }
26703
+ ),
26704
+ /* @__PURE__ */ jsx3(
26705
+ "canvas",
26706
+ {
26707
+ ref: topScrollHeaderCanvasRef,
26708
+ onPointerLeave: handleCanvasHeaderPointerLeave,
26709
+ onPointerMove: handleCanvasColumnHeaderPointerMove,
26710
+ onPointerDown: handleCanvasColumnHeaderPointerDown,
26711
+ style: canvasTopScrollHeaderStyle
26712
+ }
26713
+ ),
26714
+ /* @__PURE__ */ jsx3(
26715
+ "canvas",
26716
+ {
26717
+ ref: leftFrozenHeaderCanvasRef,
26718
+ onPointerLeave: handleCanvasHeaderPointerLeave,
26719
+ onPointerMove: handleCanvasRowHeaderPointerMove,
26720
+ onPointerDown: handleCanvasRowHeaderPointerDown,
26721
+ style: canvasLeftFrozenHeaderStyle
26143
26722
  }
26144
26723
  ),
26145
26724
  /* @__PURE__ */ jsx3(
26146
26725
  "canvas",
26147
26726
  {
26148
- ref: leftHeaderCanvasRef,
26727
+ ref: leftScrollHeaderCanvasRef,
26149
26728
  onPointerLeave: handleCanvasHeaderPointerLeave,
26150
26729
  onPointerMove: handleCanvasRowHeaderPointerMove,
26151
26730
  onPointerDown: handleCanvasRowHeaderPointerDown,
26152
- style: canvasLeftHeaderStyle
26731
+ style: canvasLeftScrollHeaderStyle
26153
26732
  }
26154
26733
  ),
26155
26734
  /* @__PURE__ */ jsx3("canvas", { ref: cornerHeaderCanvasRef, style: canvasCornerHeaderStyle })
@@ -26246,7 +26825,7 @@ function XlsxGrid({
26246
26825
  )),
26247
26826
  trailingColumnSpacerWidth > 0 ? /* @__PURE__ */ jsx3("col", { style: { width: trailingColumnSpacerWidth } }) : null
26248
26827
  ] }),
26249
- /* @__PURE__ */ jsx3("thead", { style: { position: "sticky", top: 0, zIndex: 50 }, children: /* @__PURE__ */ jsxs3("tr", { children: [
26828
+ /* @__PURE__ */ jsx3("thead", { style: { position: "sticky", top: 0, zIndex: canvasHeaderOverlayZIndex }, children: /* @__PURE__ */ jsxs3("tr", { children: [
26250
26829
  /* @__PURE__ */ jsx3(
26251
26830
  "th",
26252
26831
  {
@@ -26255,7 +26834,7 @@ function XlsxGrid({
26255
26834
  backgroundColor: palette.headerSurface,
26256
26835
  left: 0,
26257
26836
  width: displayRowHeaderWidth,
26258
- zIndex: 60
26837
+ zIndex: cornerHeaderOverlayZIndex
26259
26838
  }
26260
26839
  }
26261
26840
  ),
@@ -26269,7 +26848,7 @@ function XlsxGrid({
26269
26848
  style: {
26270
26849
  ...headerCellStyle,
26271
26850
  left: stickyLeftByCol.get(column.actualCol),
26272
- zIndex: stickyLeftByCol.has(column.actualCol) ? 55 : headerCellStyle.zIndex
26851
+ zIndex: stickyLeftByCol.has(column.actualCol) ? stickyHeaderOverlayZIndex : headerCellStyle.zIndex
26273
26852
  },
26274
26853
  children: /* @__PURE__ */ jsxs3("div", { style: { position: "relative" }, children: [
26275
26854
  /* @__PURE__ */ jsx3(
@@ -26324,6 +26903,7 @@ function XlsxGrid({
26324
26903
  actualRow,
26325
26904
  editingCell,
26326
26905
  editingValue,
26906
+ frozenRowHeaderZIndex: frozenRowHeaderOverlayZIndex,
26327
26907
  getCellData,
26328
26908
  leadingSpacerWidth: leadingColumnSpacerWidth,
26329
26909
  onCellClick: handleCellClick,
@@ -26340,6 +26920,7 @@ function XlsxGrid({
26340
26920
  readOnly,
26341
26921
  renderCellAdornment,
26342
26922
  rowHeight: virtualRow.size,
26923
+ rowHeaderZIndex: rowHeaderOverlayZIndex,
26343
26924
  rowHeaderWidth: displayRowHeaderWidth,
26344
26925
  stickyLeftByCol,
26345
26926
  stickyTop: stickyTopByRow.get(actualRow),