@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.cjs CHANGED
@@ -4268,13 +4268,36 @@ function parseWorkbookTableMetadata(archive, workbookSheets) {
4268
4268
  }
4269
4269
  return [{
4270
4270
  displayName: tableNode.getAttribute("displayName") ?? void 0,
4271
+ headerRowCount: parseWorkbookTableCount(tableNode.getAttribute("headerRowCount"), 1),
4271
4272
  headerRowCellStyle: tableNode.getAttribute("headerRowCellStyle") ?? void 0,
4272
4273
  name: tableNode.getAttribute("name") ?? void 0,
4273
- reference: tableNode.getAttribute("ref") ?? void 0
4274
+ reference: tableNode.getAttribute("ref") ?? void 0,
4275
+ totalsRowCount: parseWorkbookTableCount(tableNode.getAttribute("totalsRowCount"), 0),
4276
+ totalsRowShown: parseWorkbookTableBoolean(tableNode.getAttribute("totalsRowShown"), false)
4274
4277
  }];
4275
4278
  });
4276
4279
  });
4277
4280
  }
4281
+ function parseWorkbookTableCount(value, fallback) {
4282
+ if (value === null) {
4283
+ return fallback;
4284
+ }
4285
+ const parsed = Number.parseInt(value, 10);
4286
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
4287
+ }
4288
+ function parseWorkbookTableBoolean(value, fallback) {
4289
+ if (value === null) {
4290
+ return fallback;
4291
+ }
4292
+ const normalized = value.trim().toLowerCase();
4293
+ if (normalized === "0" || normalized === "false" || normalized === "") {
4294
+ return false;
4295
+ }
4296
+ if (normalized === "1" || normalized === "true") {
4297
+ return true;
4298
+ }
4299
+ return fallback;
4300
+ }
4278
4301
  function parseSqrefRanges(sqref) {
4279
4302
  if (!sqref) {
4280
4303
  return [];
@@ -6286,6 +6309,84 @@ function resizeImageRect(rect, handle, deltaX, deltaY, minimumSize = 16) {
6286
6309
  return { height, left, top, width };
6287
6310
  }
6288
6311
 
6312
+ // src/safe-calculate.ts
6313
+ var SHEET_REF_REGEX = /'((?:[^']|'')+)'!|([A-Za-z_\u0080-\uFFFF][\w.\u0080-\uFFFF]*)!/g;
6314
+ function collectReferencedSheetNames(workbook) {
6315
+ const referenced = /* @__PURE__ */ new Set();
6316
+ for (let sheetIdx = 0; sheetIdx < workbook.sheetCount; sheetIdx += 1) {
6317
+ let sheet;
6318
+ try {
6319
+ sheet = workbook.getSheet(sheetIdx);
6320
+ } catch {
6321
+ continue;
6322
+ }
6323
+ const cells = sheet.formulaCells;
6324
+ if (!Array.isArray(cells)) {
6325
+ continue;
6326
+ }
6327
+ for (const cell of cells) {
6328
+ const formula = cell?.formula;
6329
+ if (!formula) {
6330
+ continue;
6331
+ }
6332
+ SHEET_REF_REGEX.lastIndex = 0;
6333
+ let match;
6334
+ while ((match = SHEET_REF_REGEX.exec(formula)) !== null) {
6335
+ const raw = match[1] ?? match[2];
6336
+ if (!raw) {
6337
+ continue;
6338
+ }
6339
+ referenced.add(raw.replace(/''/g, "'"));
6340
+ }
6341
+ }
6342
+ }
6343
+ return referenced;
6344
+ }
6345
+ function hasUnresolvedSheetReferences(workbook) {
6346
+ let names;
6347
+ try {
6348
+ names = workbook.sheetNames;
6349
+ } catch {
6350
+ return false;
6351
+ }
6352
+ const known = new Set(names);
6353
+ const referenced = collectReferencedSheetNames(workbook);
6354
+ for (const name of referenced) {
6355
+ if (!known.has(name)) {
6356
+ return true;
6357
+ }
6358
+ }
6359
+ return false;
6360
+ }
6361
+ function safeCalculate(workbook, options = {}) {
6362
+ if (hasUnresolvedSheetReferences(workbook)) {
6363
+ return { workbook, calculated: false, skipReason: "unresolved-sheet-refs" };
6364
+ }
6365
+ try {
6366
+ workbook.calculate();
6367
+ return { workbook, calculated: true, skipReason: null };
6368
+ } catch (err) {
6369
+ console.warn("[react-xlsx] workbook.calculate() trapped; falling back to cached formula values", err);
6370
+ if (options.reparse) {
6371
+ try {
6372
+ return { workbook: options.reparse(), calculated: false, skipReason: "calculate-trapped" };
6373
+ } catch (reparseErr) {
6374
+ console.warn("[react-xlsx] workbook reparse after calculate trap failed", reparseErr);
6375
+ }
6376
+ }
6377
+ return { workbook, calculated: false, skipReason: "calculate-trapped" };
6378
+ }
6379
+ }
6380
+ function tryRecalculate(workbook) {
6381
+ try {
6382
+ workbook.calculate();
6383
+ return { calculated: true, error: null };
6384
+ } catch (err) {
6385
+ console.warn("[react-xlsx] workbook.calculate() trapped during recalculation", err);
6386
+ return { calculated: false, error: err };
6387
+ }
6388
+ }
6389
+
6289
6390
  // src/wasm.ts
6290
6391
  var wasmModulePromise = null;
6291
6392
  function getSheetsWasmModule() {
@@ -6881,17 +6982,18 @@ function rangeContainsCell(range, cell) {
6881
6982
  function mapWorksheetTables(worksheet, metadataForSheet) {
6882
6983
  const rawTables = worksheet?.tables ?? [];
6883
6984
  return rawTables.flatMap((table, index) => {
6884
- const reference = typeof table.reference === "string" ? table.reference : "";
6885
- const parsedRange = parseA1RangeReference2(reference);
6886
- if (!parsedRange) {
6887
- return [];
6888
- }
6889
6985
  const rawColumns = Array.isArray(table.columns) ? table.columns : [];
6890
6986
  const rawName = typeof table.name === "string" ? table.name : `Table${index + 1}`;
6891
6987
  const rawDisplayName = typeof table.displayName === "string" ? table.displayName : typeof table.name === "string" ? table.name : `Table ${index + 1}`;
6892
6988
  const metadata = metadataForSheet?.find(
6893
- (entry) => entry.name && entry.name === rawName || entry.displayName && entry.displayName === rawDisplayName || entry.reference && entry.reference === reference
6989
+ (entry) => entry.name && entry.name === rawName || entry.displayName && entry.displayName === rawDisplayName || entry.reference && entry.reference === table.reference
6894
6990
  );
6991
+ const rawReference = typeof table.reference === "string" ? table.reference : "";
6992
+ const reference = metadata?.reference ?? rawReference;
6993
+ const parsedRange = parseA1RangeReference2(reference);
6994
+ if (!parsedRange) {
6995
+ return [];
6996
+ }
6895
6997
  return [{
6896
6998
  columns: rawColumns.map((column, columnIndex) => ({
6897
6999
  id: typeof column.id === "number" ? column.id ?? columnIndex + 1 : columnIndex + 1,
@@ -6900,17 +7002,47 @@ function mapWorksheetTables(worksheet, metadataForSheet) {
6900
7002
  })),
6901
7003
  displayName: rawDisplayName,
6902
7004
  end: parsedRange.end,
6903
- headerRowCount: typeof table.headerRowCount === "number" ? table.headerRowCount : 1,
7005
+ headerRowCount: metadata?.headerRowCount ?? resolveWorkbookTableCount(table.headerRowCount, 1),
6904
7006
  headerRowCellStyle: metadata?.headerRowCellStyle,
6905
7007
  name: rawName,
6906
7008
  reference,
6907
7009
  start: parsedRange.start,
6908
7010
  styleInfo: table.styleInfo,
6909
- totalsRowCount: typeof table.totalsRowCount === "number" ? table.totalsRowCount : 0,
6910
- totalsRowShown: Boolean(table.totalsRowShown)
7011
+ totalsRowCount: metadata?.totalsRowCount ?? resolveWorkbookTableCount(table.totalsRowCount, 0),
7012
+ totalsRowShown: metadata?.totalsRowShown ?? resolveWorkbookTableBoolean(table.totalsRowShown)
6911
7013
  }];
6912
7014
  });
6913
7015
  }
7016
+ function resolveWorkbookTableCount(value, fallback) {
7017
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
7018
+ return value;
7019
+ }
7020
+ if (typeof value === "string") {
7021
+ const parsed = Number.parseInt(value, 10);
7022
+ if (Number.isFinite(parsed) && parsed >= 0) {
7023
+ return parsed;
7024
+ }
7025
+ }
7026
+ return fallback;
7027
+ }
7028
+ function resolveWorkbookTableBoolean(value) {
7029
+ if (typeof value === "boolean") {
7030
+ return value;
7031
+ }
7032
+ if (typeof value === "number") {
7033
+ return value !== 0;
7034
+ }
7035
+ if (typeof value === "string") {
7036
+ const normalized = value.trim().toLowerCase();
7037
+ if (normalized === "0" || normalized === "false" || normalized === "") {
7038
+ return false;
7039
+ }
7040
+ if (normalized === "1" || normalized === "true") {
7041
+ return true;
7042
+ }
7043
+ }
7044
+ return false;
7045
+ }
6914
7046
  function fileStem(fileName) {
6915
7047
  const normalized = fileName.trim();
6916
7048
  const lastDot = normalized.lastIndexOf(".");
@@ -7041,7 +7173,17 @@ async function resolveWorkbookBuffer({ file, src }, signal) {
7041
7173
  if (file) {
7042
7174
  buffer = file;
7043
7175
  } else if (src) {
7044
- const response = await fetch(src, { signal });
7176
+ let response;
7177
+ try {
7178
+ response = await fetch(src, { signal });
7179
+ } catch (error) {
7180
+ if (isAbortError(error)) {
7181
+ throw error;
7182
+ }
7183
+ throw new Error(
7184
+ "Failed to fetch workbook. The remote URL may be blocked by CORS, unavailable, or not directly downloadable from the browser."
7185
+ );
7186
+ }
7045
7187
  if (!response.ok) {
7046
7188
  throw new Error(`Failed to fetch workbook (status ${response.status})`);
7047
7189
  }
@@ -7053,18 +7195,21 @@ async function resolveWorkbookBuffer({ file, src }, signal) {
7053
7195
  }
7054
7196
  async function parseWorkbookBuffer(buffer) {
7055
7197
  const wasmModule = await getSheetsWasmModule();
7056
- const workbook = wasmModule.Workbook.fromBytes(new Uint8Array(buffer));
7198
+ const initialWorkbook = wasmModule.Workbook.fromBytes(new Uint8Array(buffer));
7057
7199
  let totalFormulas = 0;
7058
- for (let index = 0; index < workbook.sheetCount; index += 1) {
7059
- totalFormulas += workbook.getSheet(index).formulaCount;
7200
+ for (let index = 0; index < initialWorkbook.sheetCount; index += 1) {
7201
+ totalFormulas += initialWorkbook.getSheet(index).formulaCount;
7060
7202
  }
7061
7203
  const shouldAutoCalculate = totalFormulas <= FORMULA_COUNT_THRESHOLD;
7062
- if (shouldAutoCalculate) {
7063
- workbook.calculate();
7204
+ if (!shouldAutoCalculate) {
7205
+ return { shouldAutoCalculate, workbook: initialWorkbook };
7064
7206
  }
7207
+ const result = safeCalculate(initialWorkbook, {
7208
+ reparse: () => wasmModule.Workbook.fromBytes(new Uint8Array(buffer))
7209
+ });
7065
7210
  return {
7066
- shouldAutoCalculate,
7067
- workbook
7211
+ shouldAutoCalculate: result.calculated,
7212
+ workbook: result.workbook
7068
7213
  };
7069
7214
  }
7070
7215
  function scheduleLowPriorityTask(task) {
@@ -7597,6 +7742,7 @@ function downloadUrl(src, fileName) {
7597
7742
  }
7598
7743
  function useXlsxViewerController(options) {
7599
7744
  const {
7745
+ allowResizeInReadOnly = false,
7600
7746
  deferLoadingAboveBytes = DEFAULT_DEFER_LOADING_ABOVE_BYTES,
7601
7747
  file,
7602
7748
  fileName,
@@ -7650,6 +7796,7 @@ function useXlsxViewerController(options) {
7650
7796
  const displayFileName = React.useMemo(() => resolveDisplayFileName(src, fileName), [fileName, src]);
7651
7797
  const shouldDeferLoading = deferLoadingAboveBytes > 0;
7652
7798
  const readOnly = requestedReadOnly || forcedReadOnly;
7799
+ const canResizeReadOnly = requestedReadOnly && allowResizeInReadOnly && !forcedReadOnly;
7653
7800
  const workerSupported = useWorker && typeof Worker !== "undefined";
7654
7801
  const shouldUseWorker = workerSupported && forcedReadOnly;
7655
7802
  const shouldForceReadOnlyForBuffer = React.useCallback((bufferByteLength) => !requestedReadOnly && readOnlyAboveBytes > 0 && bufferByteLength > readOnlyAboveBytes, [readOnlyAboveBytes, requestedReadOnly]);
@@ -8303,7 +8450,10 @@ function useXlsxViewerController(options) {
8303
8450
  if (!shouldAutoCalculate) {
8304
8451
  return;
8305
8452
  }
8306
- targetWorkbook.calculate();
8453
+ const result = tryRecalculate(targetWorkbook);
8454
+ if (!result.calculated) {
8455
+ setShouldAutoCalculate(false);
8456
+ }
8307
8457
  }, [shouldAutoCalculate]);
8308
8458
  const getActiveWorksheet = React.useCallback(() => {
8309
8459
  if (!workbook || !activeSheet) {
@@ -8940,11 +9090,15 @@ function useXlsxViewerController(options) {
8940
9090
  if (!workbook) {
8941
9091
  return;
8942
9092
  }
8943
- workbook.calculate();
8944
- refreshWorkbookState(workbook);
9093
+ const result = tryRecalculate(workbook);
9094
+ if (result.calculated) {
9095
+ refreshWorkbookState(workbook);
9096
+ return;
9097
+ }
9098
+ setShouldAutoCalculate(false);
8945
9099
  }, [refreshWorkbookState, workbook]);
8946
9100
  const resizeColumn = React.useCallback((col, widthPx) => {
8947
- if (readOnly || !workbook || !activeSheet) {
9101
+ if (readOnly && !canResizeReadOnly || !workbook || !activeSheet) {
8948
9102
  return;
8949
9103
  }
8950
9104
  recordHistoryBeforeMutation();
@@ -8954,9 +9108,9 @@ function useXlsxViewerController(options) {
8954
9108
  pxToSheetColumnWidth(resolveContentSheetAxisPixels(widthPx, activeSheet.showGridLines))
8955
9109
  );
8956
9110
  refreshWorkbookState(workbook);
8957
- }, [activeSheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9111
+ }, [activeSheet, canResizeReadOnly, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
8958
9112
  const resizeRow = React.useCallback((row, heightPx) => {
8959
- if (readOnly || !workbook || !activeSheet) {
9113
+ if (readOnly && !canResizeReadOnly || !workbook || !activeSheet) {
8960
9114
  return;
8961
9115
  }
8962
9116
  recordHistoryBeforeMutation();
@@ -8966,7 +9120,7 @@ function useXlsxViewerController(options) {
8966
9120
  pxToSheetRowHeight(resolveContentSheetAxisPixels(heightPx, activeSheet.showGridLines))
8967
9121
  );
8968
9122
  refreshWorkbookState(workbook);
8969
- }, [activeSheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
9123
+ }, [activeSheet, canResizeReadOnly, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
8970
9124
  const resolveAnchoredObjectRect = React.useCallback((anchor, worksheet) => {
8971
9125
  const resolveAxisSum = (index, getSize) => {
8972
9126
  let total = 0;
@@ -19944,6 +20098,109 @@ function resolveSelectionColors({
19944
20098
  stroke
19945
20099
  };
19946
20100
  }
20101
+ function buildFullCanvasDirtyRect(width, height) {
20102
+ if (width <= 0 || height <= 0) {
20103
+ return [];
20104
+ }
20105
+ return [{
20106
+ height,
20107
+ left: 0,
20108
+ top: 0,
20109
+ width
20110
+ }];
20111
+ }
20112
+ function intersectsCanvasDirtyRects(left, top, width, height, dirtyRects) {
20113
+ if (dirtyRects.length === 0 || width <= 0 || height <= 0) {
20114
+ return false;
20115
+ }
20116
+ const right = left + width;
20117
+ const bottom = top + height;
20118
+ return dirtyRects.some((dirtyRect) => left < dirtyRect.left + dirtyRect.width && right > dirtyRect.left && top < dirtyRect.top + dirtyRect.height && bottom > dirtyRect.top);
20119
+ }
20120
+ function blitCanvasWithScrollDelta(canvas, context, bufferCanvas, dpr, width, height, deltaX, deltaY) {
20121
+ if (width <= 0 || height <= 0) {
20122
+ return [];
20123
+ }
20124
+ const clampedDeltaX = Math.max(-width, Math.min(width, deltaX));
20125
+ const clampedDeltaY = Math.max(-height, Math.min(height, deltaY));
20126
+ if (Math.abs(clampedDeltaX) < 0.01 && Math.abs(clampedDeltaY) < 0.01) {
20127
+ return [];
20128
+ }
20129
+ if (Math.abs(clampedDeltaX) >= width || Math.abs(clampedDeltaY) >= height) {
20130
+ return null;
20131
+ }
20132
+ const deviceWidth = Math.max(1, Math.round(width * dpr));
20133
+ const deviceHeight = Math.max(1, Math.round(height * dpr));
20134
+ if (bufferCanvas.width !== deviceWidth) {
20135
+ bufferCanvas.width = deviceWidth;
20136
+ }
20137
+ if (bufferCanvas.height !== deviceHeight) {
20138
+ bufferCanvas.height = deviceHeight;
20139
+ }
20140
+ const bufferContext = bufferCanvas.getContext("2d");
20141
+ if (!bufferContext) {
20142
+ return null;
20143
+ }
20144
+ bufferContext.setTransform(1, 0, 0, 1, 0, 0);
20145
+ bufferContext.clearRect(0, 0, deviceWidth, deviceHeight);
20146
+ bufferContext.drawImage(canvas, 0, 0);
20147
+ const deltaDeviceX = clampedDeltaX * dpr;
20148
+ const deltaDeviceY = clampedDeltaY * dpr;
20149
+ const overlapDeviceWidth = deviceWidth - Math.abs(deltaDeviceX);
20150
+ const overlapDeviceHeight = deviceHeight - Math.abs(deltaDeviceY);
20151
+ if (overlapDeviceWidth <= 0 || overlapDeviceHeight <= 0) {
20152
+ return null;
20153
+ }
20154
+ const sourceX = deltaDeviceX > 0 ? deltaDeviceX : 0;
20155
+ const sourceY = deltaDeviceY > 0 ? deltaDeviceY : 0;
20156
+ const destinationX = deltaDeviceX > 0 ? 0 : -deltaDeviceX;
20157
+ const destinationY = deltaDeviceY > 0 ? 0 : -deltaDeviceY;
20158
+ context.setTransform(1, 0, 0, 1, 0, 0);
20159
+ context.drawImage(
20160
+ bufferCanvas,
20161
+ sourceX,
20162
+ sourceY,
20163
+ overlapDeviceWidth,
20164
+ overlapDeviceHeight,
20165
+ destinationX,
20166
+ destinationY,
20167
+ overlapDeviceWidth,
20168
+ overlapDeviceHeight
20169
+ );
20170
+ context.setTransform(dpr, 0, 0, dpr, 0, 0);
20171
+ const dirtyRects = [];
20172
+ if (clampedDeltaX > 0) {
20173
+ dirtyRects.push({
20174
+ height,
20175
+ left: Math.max(0, width - clampedDeltaX),
20176
+ top: 0,
20177
+ width: Math.min(width, clampedDeltaX)
20178
+ });
20179
+ } else if (clampedDeltaX < 0) {
20180
+ dirtyRects.push({
20181
+ height,
20182
+ left: 0,
20183
+ top: 0,
20184
+ width: Math.min(width, -clampedDeltaX)
20185
+ });
20186
+ }
20187
+ if (clampedDeltaY > 0) {
20188
+ dirtyRects.push({
20189
+ height: Math.min(height, clampedDeltaY),
20190
+ left: 0,
20191
+ top: Math.max(0, height - clampedDeltaY),
20192
+ width
20193
+ });
20194
+ } else if (clampedDeltaY < 0) {
20195
+ dirtyRects.push({
20196
+ height: Math.min(height, -clampedDeltaY),
20197
+ left: 0,
20198
+ top: 0,
20199
+ width
20200
+ });
20201
+ }
20202
+ return dirtyRects;
20203
+ }
19947
20204
  function buildConditionalFormatRuleKey(rule) {
19948
20205
  return `${rule.kind}:${rule.priority}:${rule.ranges.map((range) => `${range.start.row}:${range.start.col}-${range.end.row}:${range.end.col}`).join("|")}`;
19949
20206
  }
@@ -20322,6 +20579,7 @@ function GridRow({
20322
20579
  actualRow,
20323
20580
  editingCell,
20324
20581
  editingValue,
20582
+ frozenRowHeaderZIndex,
20325
20583
  getCellData,
20326
20584
  headerLabelLiveScale,
20327
20585
  leadingSpacerWidth,
@@ -20338,6 +20596,7 @@ function GridRow({
20338
20596
  readOnly,
20339
20597
  renderCellAdornment,
20340
20598
  rowHeight,
20599
+ rowHeaderZIndex,
20341
20600
  rowHeaderWidth,
20342
20601
  stickyLeftByCol,
20343
20602
  stickyTop,
@@ -20370,7 +20629,7 @@ function GridRow({
20370
20629
  textAlign: "center",
20371
20630
  userSelect: "none",
20372
20631
  width: rowHeaderWidth,
20373
- zIndex: stickyTop !== void 0 ? 45 : 35
20632
+ zIndex: stickyTop !== void 0 ? frozenRowHeaderZIndex : rowHeaderZIndex
20374
20633
  },
20375
20634
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative" }, children: [
20376
20635
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -20803,8 +21062,20 @@ function XlsxGrid({
20803
21062
  const topBodyCanvasRef = React4.useRef(null);
20804
21063
  const leftBodyCanvasRef = React4.useRef(null);
20805
21064
  const cornerBodyCanvasRef = React4.useRef(null);
20806
- const topHeaderCanvasRef = React4.useRef(null);
20807
- const leftHeaderCanvasRef = React4.useRef(null);
21065
+ const bodyBlitBufferCanvasRef = React4.useRef({
21066
+ corner: null,
21067
+ left: null,
21068
+ scroll: null,
21069
+ top: null
21070
+ });
21071
+ const headerBlitBufferCanvasRef = React4.useRef({
21072
+ left: null,
21073
+ top: null
21074
+ });
21075
+ const topFrozenHeaderCanvasRef = React4.useRef(null);
21076
+ const topScrollHeaderCanvasRef = React4.useRef(null);
21077
+ const leftFrozenHeaderCanvasRef = React4.useRef(null);
21078
+ const leftScrollHeaderCanvasRef = React4.useRef(null);
20808
21079
  const cornerHeaderCanvasRef = React4.useRef(null);
20809
21080
  const selectionOverlayRef = React4.useRef(null);
20810
21081
  const activeValidationOverlayRef = React4.useRef(null);
@@ -21008,16 +21279,31 @@ function XlsxGrid({
21008
21279
  liveZoomTranslateX2,
21009
21280
  liveZoomTranslateY2
21010
21281
  );
21011
- const topHeaderCanvas = topHeaderCanvasRef.current;
21012
- if (topHeaderCanvas) {
21013
- topHeaderCanvas.style.transform = scrollDeltaX !== 0 ? `translate3d(${scrollDeltaX}px, 0, 0)` : "";
21014
- topHeaderCanvas.style.willChange = scrollDeltaX !== 0 ? "transform" : "";
21015
- }
21016
- const leftHeaderCanvas = leftHeaderCanvasRef.current;
21017
- if (leftHeaderCanvas) {
21018
- leftHeaderCanvas.style.transform = scrollDeltaY !== 0 ? `translate3d(0, ${scrollDeltaY}px, 0)` : "";
21019
- leftHeaderCanvas.style.willChange = scrollDeltaY !== 0 ? "transform" : "";
21020
- }
21282
+ applyCanvasTransform(
21283
+ topScrollHeaderCanvasRef.current,
21284
+ scrollDeltaX + liveZoomTranslateX2,
21285
+ liveZoomTranslateY2
21286
+ );
21287
+ applyCanvasTransform(
21288
+ topFrozenHeaderCanvasRef.current,
21289
+ liveZoomTranslateX2,
21290
+ liveZoomTranslateY2
21291
+ );
21292
+ applyCanvasTransform(
21293
+ leftScrollHeaderCanvasRef.current,
21294
+ liveZoomTranslateX2,
21295
+ scrollDeltaY + liveZoomTranslateY2
21296
+ );
21297
+ applyCanvasTransform(
21298
+ leftFrozenHeaderCanvasRef.current,
21299
+ liveZoomTranslateX2,
21300
+ liveZoomTranslateY2
21301
+ );
21302
+ applyCanvasTransform(
21303
+ cornerHeaderCanvasRef.current,
21304
+ liveZoomTranslateX2,
21305
+ liveZoomTranslateY2
21306
+ );
21021
21307
  }, [zoomScale]);
21022
21308
  const updateLiveGestureZoomState = React4.useCallback((nextState) => {
21023
21309
  const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
@@ -21148,6 +21434,22 @@ function XlsxGrid({
21148
21434
  scrollRef.current.style.cursor = "";
21149
21435
  }
21150
21436
  }, []);
21437
+ const getBodyBlitBufferCanvas = React4.useCallback((pane) => {
21438
+ let bufferCanvas = bodyBlitBufferCanvasRef.current[pane];
21439
+ if (!bufferCanvas && typeof document !== "undefined") {
21440
+ bufferCanvas = document.createElement("canvas");
21441
+ bodyBlitBufferCanvasRef.current[pane] = bufferCanvas;
21442
+ }
21443
+ return bufferCanvas;
21444
+ }, []);
21445
+ const getHeaderBlitBufferCanvas = React4.useCallback((axis) => {
21446
+ let bufferCanvas = headerBlitBufferCanvasRef.current[axis];
21447
+ if (!bufferCanvas && typeof document !== "undefined") {
21448
+ bufferCanvas = document.createElement("canvas");
21449
+ headerBlitBufferCanvasRef.current[axis] = bufferCanvas;
21450
+ }
21451
+ return bufferCanvas;
21452
+ }, []);
21151
21453
  const visibleRows = React4.useMemo(() => {
21152
21454
  return buildVisibleAxisIndices(
21153
21455
  activeSheet?.visibleRows ?? [],
@@ -21862,14 +22164,14 @@ function XlsxGrid({
21862
22164
  const handleScrollerScroll = React4.useCallback((event) => {
21863
22165
  const currentScroller = event.currentTarget;
21864
22166
  cachedScrollerRectRef.current = null;
21865
- syncDrawingViewport(currentScroller, { immediate: !experimentalCanvas });
22167
+ syncDrawingViewport(currentScroller, { immediate: true });
21866
22168
  if (currentScroller.scrollHeight - (currentScroller.scrollTop + currentScroller.clientHeight) < OPEN_GRID_VERTICAL_EDGE_PX) {
21867
22169
  setDisplayRowLimit((current) => current + OPEN_GRID_ROW_GROWTH);
21868
22170
  }
21869
22171
  if (currentScroller.scrollWidth - (currentScroller.scrollLeft + currentScroller.clientWidth) < OPEN_GRID_HORIZONTAL_EDGE_PX) {
21870
22172
  setDisplayColLimit((current) => current + OPEN_GRID_COL_GROWTH);
21871
22173
  }
21872
- }, [experimentalCanvas, syncDrawingViewport]);
22174
+ }, [syncDrawingViewport]);
21873
22175
  React4.useEffect(() => {
21874
22176
  const scroller = scrollRef.current;
21875
22177
  if (!scroller || !enableGestureZoom) {
@@ -22136,7 +22438,7 @@ function XlsxGrid({
22136
22438
  const geometryCell = resolvePointerCellFromGeometry(clientX, clientY);
22137
22439
  const hitCell = resolvePointerCellFromHitTest(clientX, clientY);
22138
22440
  const actualRow = hitCell && rowIndexByActual.has(hitCell.row) ? hitCell.row : geometryCell?.row;
22139
- const actualCol = geometryCell?.col ?? hitCell?.col;
22441
+ const actualCol = hitCell?.col ?? geometryCell?.col;
22140
22442
  if (actualRow === void 0 || actualCol === void 0) {
22141
22443
  return null;
22142
22444
  }
@@ -23734,6 +24036,54 @@ function XlsxGrid({
23734
24036
  rowPrefixSums,
23735
24037
  stickyTopByRow
23736
24038
  ]);
24039
+ const canvasColumnHeaderCells = React4.useMemo(
24040
+ () => canvasVisibleColItems.flatMap((column) => {
24041
+ const rect = resolveCanvasColumnHeaderRect(column.actualCol);
24042
+ if (!rect) {
24043
+ return [];
24044
+ }
24045
+ const isFrozen = stickyLeftByCol.has(column.actualCol);
24046
+ return [{
24047
+ actualCol: column.actualCol,
24048
+ height: displayHeaderHeight,
24049
+ isFrozen,
24050
+ left: rect.left,
24051
+ localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : frozenPaneRight),
24052
+ width: rect.width
24053
+ }];
24054
+ }),
24055
+ [
24056
+ canvasVisibleColItems,
24057
+ displayHeaderHeight,
24058
+ displayRowHeaderWidth,
24059
+ frozenPaneRight,
24060
+ resolveCanvasColumnHeaderRect,
24061
+ stickyLeftByCol
24062
+ ]
24063
+ );
24064
+ const canvasRowHeaderCells = React4.useMemo(
24065
+ () => canvasVisibleRowItems.flatMap((row) => {
24066
+ const rect = resolveCanvasRowHeaderRect(row.actualRow);
24067
+ if (!rect) {
24068
+ return [];
24069
+ }
24070
+ const isFrozen = stickyTopByRow.has(row.actualRow);
24071
+ return [{
24072
+ actualRow: row.actualRow,
24073
+ height: rect.height,
24074
+ isFrozen,
24075
+ localTop: rect.top - (isFrozen ? displayHeaderHeight : frozenPaneBottom),
24076
+ top: rect.top
24077
+ }];
24078
+ }),
24079
+ [
24080
+ canvasVisibleRowItems,
24081
+ displayHeaderHeight,
24082
+ frozenPaneBottom,
24083
+ resolveCanvasRowHeaderRect,
24084
+ stickyTopByRow
24085
+ ]
24086
+ );
23737
24087
  const resolveCanvasColumnResizeTarget = React4.useCallback((clientX) => {
23738
24088
  if (!canResizeHeaders) {
23739
24089
  return null;
@@ -23743,17 +24093,26 @@ function XlsxGrid({
23743
24093
  return null;
23744
24094
  }
23745
24095
  const localX = clientX - scrollerRect.left;
23746
- for (const column of canvasVisibleColItems) {
23747
- const rect = resolveCanvasColumnHeaderRect(column.actualCol);
23748
- if (!rect) {
23749
- continue;
24096
+ for (const column of canvasColumnHeaderCells) {
24097
+ if (Math.abs(localX - (column.left + column.width)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
24098
+ return { actualCol: column.actualCol, width: column.width };
23750
24099
  }
23751
- if (Math.abs(localX - (rect.left + rect.width)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
23752
- return { actualCol: column.actualCol, width: rect.width };
24100
+ }
24101
+ return null;
24102
+ }, [canResizeHeaders, canvasColumnHeaderCells]);
24103
+ const resolveCanvasColumnHeaderTarget = React4.useCallback((clientX) => {
24104
+ const scrollerRect = scrollRef.current?.getBoundingClientRect();
24105
+ if (!scrollerRect) {
24106
+ return null;
24107
+ }
24108
+ const localX = clientX - scrollerRect.left;
24109
+ for (const column of canvasColumnHeaderCells) {
24110
+ if (localX >= column.left && localX <= column.left + column.width) {
24111
+ return column.actualCol;
23753
24112
  }
23754
24113
  }
23755
24114
  return null;
23756
- }, [canResizeHeaders, canvasVisibleColItems, resolveCanvasColumnHeaderRect]);
24115
+ }, [canvasColumnHeaderCells]);
23757
24116
  const resolveCanvasRowResizeTarget = React4.useCallback((clientY) => {
23758
24117
  if (!canResizeHeaders) {
23759
24118
  return null;
@@ -23763,17 +24122,26 @@ function XlsxGrid({
23763
24122
  return null;
23764
24123
  }
23765
24124
  const localY = clientY - scrollerRect.top;
23766
- for (const row of canvasVisibleRowItems) {
23767
- const rect = resolveCanvasRowHeaderRect(row.actualRow);
23768
- if (!rect) {
23769
- continue;
24125
+ for (const row of canvasRowHeaderCells) {
24126
+ if (Math.abs(localY - (row.top + row.height)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
24127
+ return { actualRow: row.actualRow, height: row.height };
23770
24128
  }
23771
- if (Math.abs(localY - (rect.top + rect.height)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
23772
- return { actualRow: row.actualRow, height: rect.height };
24129
+ }
24130
+ return null;
24131
+ }, [canResizeHeaders, canvasRowHeaderCells]);
24132
+ const resolveCanvasRowHeaderTarget = React4.useCallback((clientY) => {
24133
+ const scrollerRect = scrollRef.current?.getBoundingClientRect();
24134
+ if (!scrollerRect) {
24135
+ return null;
24136
+ }
24137
+ const localY = clientY - scrollerRect.top;
24138
+ for (const row of canvasRowHeaderCells) {
24139
+ if (localY >= row.top && localY <= row.top + row.height) {
24140
+ return row.actualRow;
23773
24141
  }
23774
24142
  }
23775
24143
  return null;
23776
- }, [canResizeHeaders, canvasVisibleRowItems, resolveCanvasRowHeaderRect]);
24144
+ }, [canvasRowHeaderCells]);
23777
24145
  const handleCanvasColumnHeaderPointerMove = React4.useCallback((event) => {
23778
24146
  if (resizeStateRef.current?.type === "column") {
23779
24147
  event.currentTarget.style.cursor = "col-resize";
@@ -23876,17 +24244,17 @@ function XlsxGrid({
23876
24244
  startColumnResize(event.pointerId, resizeTarget.actualCol, resizeTarget.width, event.clientX);
23877
24245
  return;
23878
24246
  }
23879
- const cell = resolvePointerCellFromClient(event.clientX, event.clientY);
23880
- if (!cell) {
24247
+ const actualCol = resolveCanvasColumnHeaderTarget(event.clientX);
24248
+ if (actualCol === null) {
23881
24249
  return;
23882
24250
  }
23883
24251
  event.preventDefault();
23884
24252
  focusGrid();
23885
24253
  const currentSelection = selectionRef.current;
23886
- const anchorCol = event.shiftKey && currentSelection ? currentSelection.start.col : cell.col;
24254
+ const anchorCol = event.shiftKey && currentSelection ? currentSelection.start.col : actualCol;
23887
24255
  const initialRange = normalizeRange2({
23888
24256
  start: { row: firstVisibleRow, col: anchorCol },
23889
- end: { row: lastVisibleRow, col: cell.col }
24257
+ end: { row: lastVisibleRow, col: actualCol }
23890
24258
  });
23891
24259
  const anchorColIndex = colIndexByActual.get(anchorCol);
23892
24260
  if (anchorColIndex === void 0) {
@@ -23896,7 +24264,7 @@ function XlsxGrid({
23896
24264
  event.pointerId,
23897
24265
  { row: firstVisibleRow, col: anchorCol },
23898
24266
  "column",
23899
- { row: firstVisibleRow, col: cell.col },
24267
+ { row: firstVisibleRow, col: actualCol },
23900
24268
  {
23901
24269
  contentScaleX: 1,
23902
24270
  contentScaleY: 1,
@@ -23917,9 +24285,8 @@ function XlsxGrid({
23917
24285
  firstVisibleRow,
23918
24286
  focusGrid,
23919
24287
  lastVisibleRow,
23920
- resolveCanvasColumnHeaderRect,
24288
+ resolveCanvasColumnHeaderTarget,
23921
24289
  resolveCanvasColumnResizeTarget,
23922
- resolvePointerCellFromClient,
23923
24290
  rowPrefixSums,
23924
24291
  startCellSelection
23925
24292
  ]);
@@ -23934,17 +24301,17 @@ function XlsxGrid({
23934
24301
  startRowResize(event.pointerId, resizeTarget.actualRow, resizeTarget.height, event.clientY);
23935
24302
  return;
23936
24303
  }
23937
- const cell = resolvePointerCellFromClient(event.clientX, event.clientY);
23938
- if (!cell) {
24304
+ const actualRow = resolveCanvasRowHeaderTarget(event.clientY);
24305
+ if (actualRow === null) {
23939
24306
  return;
23940
24307
  }
23941
24308
  event.preventDefault();
23942
24309
  focusGrid();
23943
24310
  const currentSelection = selectionRef.current;
23944
- const anchorRow = event.shiftKey && currentSelection ? currentSelection.start.row : cell.row;
24311
+ const anchorRow = event.shiftKey && currentSelection ? currentSelection.start.row : actualRow;
23945
24312
  const initialRange = normalizeRange2({
23946
24313
  start: { row: anchorRow, col: firstVisibleCol },
23947
- end: { row: cell.row, col: lastVisibleCol }
24314
+ end: { row: actualRow, col: lastVisibleCol }
23948
24315
  });
23949
24316
  const anchorRowIndex = rowIndexByActual.get(anchorRow);
23950
24317
  if (anchorRowIndex === void 0) {
@@ -23954,7 +24321,7 @@ function XlsxGrid({
23954
24321
  event.pointerId,
23955
24322
  { row: anchorRow, col: firstVisibleCol },
23956
24323
  "row",
23957
- { row: cell.row, col: firstVisibleCol },
24324
+ { row: actualRow, col: firstVisibleCol },
23958
24325
  {
23959
24326
  contentScaleX: 1,
23960
24327
  contentScaleY: 1,
@@ -23974,8 +24341,8 @@ function XlsxGrid({
23974
24341
  firstVisibleCol,
23975
24342
  focusGrid,
23976
24343
  lastVisibleCol,
24344
+ resolveCanvasRowHeaderTarget,
23977
24345
  resolveCanvasRowResizeTarget,
23978
- resolvePointerCellFromClient,
23979
24346
  rowIndexByActual,
23980
24347
  rowPrefixSums,
23981
24348
  startCellSelection
@@ -23985,7 +24352,7 @@ function XlsxGrid({
23985
24352
  return;
23986
24353
  }
23987
24354
  const dpr = typeof window === "undefined" ? 1 : Math.max(1, window.devicePixelRatio || 1);
23988
- function configureCanvas(canvas, width, height) {
24355
+ function configureCanvas(canvas, width, height, options) {
23989
24356
  if (!canvas) {
23990
24357
  return null;
23991
24358
  }
@@ -24008,7 +24375,9 @@ function XlsxGrid({
24008
24375
  return null;
24009
24376
  }
24010
24377
  context.setTransform(dpr, 0, 0, dpr, 0, 0);
24011
- context.clearRect(0, 0, width, height);
24378
+ if (options?.clear !== false) {
24379
+ context.clearRect(0, 0, width, height);
24380
+ }
24012
24381
  return context;
24013
24382
  }
24014
24383
  const bodyWidth = Math.max(0, drawingViewport.width);
@@ -24040,12 +24409,24 @@ function XlsxGrid({
24040
24409
  rangeSignature,
24041
24410
  rowHeaderWidth,
24042
24411
  rowSignature: nextBodyCanvasSignature.rowSignature,
24412
+ viewportLeft: drawingViewport.left,
24413
+ viewportTop: drawingViewport.top,
24043
24414
  zoomFactor
24044
24415
  };
24045
24416
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
24046
24417
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
24418
+ const previousPaintedViewport = paintedDrawingViewportRef.current;
24047
24419
  const shouldRepaintBody = !(previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.colSignature === nextBodyCanvasSignature.colSignature && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.rowSignature === nextBodyCanvasSignature.rowSignature && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor);
24048
- 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);
24420
+ const shouldRepaintHeaders = !(previousHeaderCanvasSignature && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth && previousHeaderCanvasSignature.colSignature === nextHeaderCanvasSignature.colSignature && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth && previousHeaderCanvasSignature.rowSignature === nextHeaderCanvasSignature.rowSignature && previousHeaderCanvasSignature.viewportLeft === nextHeaderCanvasSignature.viewportLeft && previousHeaderCanvasSignature.viewportTop === nextHeaderCanvasSignature.viewportTop && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor);
24421
+ const canBlitBody = Boolean(
24422
+ shouldRepaintBody && previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor
24423
+ );
24424
+ const canBlitTopHeader = Boolean(
24425
+ 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
24426
+ );
24427
+ const canBlitLeftHeader = Boolean(
24428
+ 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
24429
+ );
24049
24430
  if (!shouldRepaintBody && !shouldRepaintHeaders) {
24050
24431
  return;
24051
24432
  }
@@ -24057,6 +24438,10 @@ function XlsxGrid({
24057
24438
  const leftBodyCanvasHeight2 = scrollBodyCanvasHeight2;
24058
24439
  const cornerBodyCanvasWidth2 = leftBodyCanvasWidth2;
24059
24440
  const cornerBodyCanvasHeight2 = topBodyCanvasHeight2;
24441
+ const topFrozenHeaderCanvasWidth2 = leftBodyCanvasWidth2;
24442
+ const topScrollHeaderCanvasWidth2 = scrollBodyCanvasWidth2;
24443
+ const leftFrozenHeaderCanvasHeight2 = topBodyCanvasHeight2;
24444
+ const leftScrollHeaderCanvasHeight2 = scrollBodyCanvasHeight2;
24060
24445
  const paneBounds = {
24061
24446
  corner: {
24062
24447
  height: cornerBodyCanvasHeight2,
@@ -24084,29 +24469,50 @@ function XlsxGrid({
24084
24469
  }
24085
24470
  };
24086
24471
  const bodyContexts = shouldRepaintBody ? {
24087
- corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth2, cornerBodyCanvasHeight2),
24088
- left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth2, leftBodyCanvasHeight2),
24089
- scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth2, scrollBodyCanvasHeight2),
24090
- top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth2, topBodyCanvasHeight2)
24472
+ corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth2, cornerBodyCanvasHeight2, { clear: !canBlitBody }),
24473
+ left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth2, leftBodyCanvasHeight2, { clear: !canBlitBody }),
24474
+ scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth2, scrollBodyCanvasHeight2, { clear: !canBlitBody }),
24475
+ top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth2, topBodyCanvasHeight2, { clear: !canBlitBody })
24091
24476
  } : {
24092
24477
  corner: null,
24093
24478
  left: null,
24094
24479
  scroll: null,
24095
24480
  top: null
24096
24481
  };
24097
- const topHeaderContext = shouldRepaintHeaders ? configureCanvas(topHeaderCanvasRef.current, bodyWidth, headerHeight) : null;
24098
- const leftHeaderContext = shouldRepaintHeaders ? configureCanvas(leftHeaderCanvasRef.current, rowHeaderWidth, bodyHeight) : null;
24482
+ const topHeaderContexts = shouldRepaintHeaders ? {
24483
+ frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth2, headerHeight),
24484
+ scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth2, headerHeight, { clear: !canBlitTopHeader })
24485
+ } : {
24486
+ frozen: null,
24487
+ scroll: null
24488
+ };
24489
+ const leftHeaderContexts = shouldRepaintHeaders ? {
24490
+ frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight2),
24491
+ scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight2, { clear: !canBlitLeftHeader })
24492
+ } : {
24493
+ frozen: null,
24494
+ scroll: null
24495
+ };
24099
24496
  const cornerContext = shouldRepaintHeaders ? configureCanvas(cornerHeaderCanvasRef.current, rowHeaderWidth, headerHeight) : null;
24100
- if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!topHeaderContext || !leftHeaderContext || !cornerContext)) {
24497
+ if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!cornerContext || topFrozenHeaderCanvasWidth2 > 0 && !topHeaderContexts.frozen || topScrollHeaderCanvasWidth2 > 0 && !topHeaderContexts.scroll || leftFrozenHeaderCanvasHeight2 > 0 && !leftHeaderContexts.frozen || leftScrollHeaderCanvasHeight2 > 0 && !leftHeaderContexts.scroll)) {
24101
24498
  return;
24102
24499
  }
24103
24500
  const showGridLines = activeSheet?.showGridLines ?? true;
24501
+ const sheetSurface = resolveSheetSurface(activeSheet, palette);
24104
24502
  const deferredSpillTextsByPane = {
24105
24503
  corner: [],
24106
24504
  left: [],
24107
24505
  scroll: [],
24108
24506
  top: []
24109
24507
  };
24508
+ const bodyDirtyRectsByPane = {
24509
+ corner: [],
24510
+ left: [],
24511
+ scroll: [],
24512
+ top: []
24513
+ };
24514
+ let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth2, headerHeight);
24515
+ let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight2);
24110
24516
  const cellPaneOrder = ["scroll", "top", "left", "corner"];
24111
24517
  if (shouldRepaintBody) {
24112
24518
  for (const pane of Object.keys(bodyContexts)) {
@@ -24115,14 +24521,44 @@ function XlsxGrid({
24115
24521
  if (!context || bounds.width <= 0 || bounds.height <= 0) {
24116
24522
  continue;
24117
24523
  }
24118
- context.fillStyle = resolveSheetSurface(activeSheet, palette);
24119
- context.fillRect(0, 0, bounds.width, bounds.height);
24524
+ let dirtyRects = buildFullCanvasDirtyRect(bounds.width, bounds.height);
24525
+ if (canBlitBody) {
24526
+ const bufferCanvas = getBodyBlitBufferCanvas(pane);
24527
+ const deltaX = pane === "scroll" || pane === "top" ? drawingViewport.left - previousPaintedViewport.left : 0;
24528
+ const deltaY = pane === "scroll" || pane === "left" ? drawingViewport.top - previousPaintedViewport.top : 0;
24529
+ if (bufferCanvas) {
24530
+ const blittedDirtyRects = blitCanvasWithScrollDelta(
24531
+ pane === "corner" ? cornerBodyCanvasRef.current : pane === "left" ? leftBodyCanvasRef.current : pane === "top" ? topBodyCanvasRef.current : scrollBodyCanvasRef.current,
24532
+ context,
24533
+ bufferCanvas,
24534
+ dpr,
24535
+ bounds.width,
24536
+ bounds.height,
24537
+ deltaX,
24538
+ deltaY
24539
+ );
24540
+ if (blittedDirtyRects) {
24541
+ dirtyRects = blittedDirtyRects;
24542
+ }
24543
+ }
24544
+ } else {
24545
+ context.clearRect(0, 0, bounds.width, bounds.height);
24546
+ }
24547
+ bodyDirtyRectsByPane[pane] = dirtyRects;
24548
+ if (dirtyRects.length === 0) {
24549
+ continue;
24550
+ }
24551
+ context.fillStyle = sheetSurface;
24552
+ for (const dirtyRect of dirtyRects) {
24553
+ context.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
24554
+ }
24120
24555
  }
24121
24556
  for (const pane of cellPaneOrder) {
24122
24557
  const paneContext = bodyContexts[pane];
24123
24558
  const paneBoundsForCell = paneBounds[pane];
24559
+ const paneDirtyRects = bodyDirtyRectsByPane[pane];
24124
24560
  const paneAxisItems = canvasPaneAxisItems[pane];
24125
- if (!paneContext || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
24561
+ if (!paneContext || paneDirtyRects.length === 0 || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
24126
24562
  continue;
24127
24563
  }
24128
24564
  const drawnMergedAnchorKeys = /* @__PURE__ */ new Set();
@@ -24162,11 +24598,14 @@ function XlsxGrid({
24162
24598
  if (localRect.left + drawableWidth < 0 || localRect.top + localRect.height < 0 || localRect.left > paneBoundsForCell.width || localRect.top > paneBoundsForCell.height) {
24163
24599
  continue;
24164
24600
  }
24601
+ if (!intersectsCanvasDirtyRects(localRect.left, localRect.top, drawableWidth, localRect.height, paneDirtyRects)) {
24602
+ continue;
24603
+ }
24165
24604
  const cellStyle = cellData.style;
24166
24605
  const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellStyle);
24167
- const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : resolveSheetSurface(activeSheet, palette));
24606
+ const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : sheetSurface);
24168
24607
  const gradientFill = !cellData.conditionalColorScale && typeof cellStyle.backgroundImage === "string" ? resolveCanvasGradientFill(paneContext, localRect, cellStyle.backgroundImage) : null;
24169
- const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== resolveSheetSurface(activeSheet, palette);
24608
+ const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== sheetSurface;
24170
24609
  paneContext.fillStyle = gradientFill ?? fillColor;
24171
24610
  paneContext.fillRect(localRect.left, localRect.top, localRect.width, localRect.height);
24172
24611
  if (cellData.chartHighlight) {
@@ -24520,57 +24959,141 @@ function XlsxGrid({
24520
24959
  }
24521
24960
  }
24522
24961
  }
24523
- if (shouldRepaintHeaders && topHeaderContext && leftHeaderContext && cornerContext) {
24524
- topHeaderContext.fillStyle = palette.headerSurface;
24525
- topHeaderContext.fillRect(0, 0, bodyWidth, headerHeight);
24526
- topHeaderContext.strokeStyle = palette.border;
24527
- topHeaderContext.lineWidth = 1;
24528
- for (const colItem of canvasVisibleColItems) {
24529
- const rect = resolveCanvasColumnHeaderRect(colItem.actualCol);
24530
- if (!rect || rect.left + rect.width < displayRowHeaderWidth || rect.left > bodyWidth) {
24962
+ if (shouldRepaintHeaders && cornerContext) {
24963
+ const topFrozenHeaderContext = topHeaderContexts.frozen;
24964
+ const topScrollHeaderContext = topHeaderContexts.scroll;
24965
+ const leftFrozenHeaderContext = leftHeaderContexts.frozen;
24966
+ const leftScrollHeaderContext = leftHeaderContexts.scroll;
24967
+ if (canBlitTopHeader) {
24968
+ const bufferCanvas = getHeaderBlitBufferCanvas("top");
24969
+ if (bufferCanvas && topScrollHeaderContext && topScrollHeaderCanvasRef.current) {
24970
+ const blittedDirtyRects = blitCanvasWithScrollDelta(
24971
+ topScrollHeaderCanvasRef.current,
24972
+ topScrollHeaderContext,
24973
+ bufferCanvas,
24974
+ dpr,
24975
+ topScrollHeaderCanvasWidth2,
24976
+ headerHeight,
24977
+ drawingViewport.left - previousPaintedViewport.left,
24978
+ 0
24979
+ );
24980
+ if (blittedDirtyRects) {
24981
+ topScrollHeaderDirtyRects = blittedDirtyRects;
24982
+ }
24983
+ }
24984
+ }
24985
+ if (canBlitLeftHeader) {
24986
+ const bufferCanvas = getHeaderBlitBufferCanvas("left");
24987
+ if (bufferCanvas && leftScrollHeaderContext && leftScrollHeaderCanvasRef.current) {
24988
+ const blittedDirtyRects = blitCanvasWithScrollDelta(
24989
+ leftScrollHeaderCanvasRef.current,
24990
+ leftScrollHeaderContext,
24991
+ bufferCanvas,
24992
+ dpr,
24993
+ rowHeaderWidth,
24994
+ leftScrollHeaderCanvasHeight2,
24995
+ 0,
24996
+ drawingViewport.top - previousPaintedViewport.top
24997
+ );
24998
+ if (blittedDirtyRects) {
24999
+ leftScrollHeaderDirtyRects = blittedDirtyRects;
25000
+ }
25001
+ }
25002
+ }
25003
+ if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth2 > 0) {
25004
+ topFrozenHeaderContext.fillStyle = palette.headerSurface;
25005
+ topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth2, headerHeight);
25006
+ }
25007
+ if (topScrollHeaderContext && topScrollHeaderCanvasWidth2 > 0) {
25008
+ topScrollHeaderContext.fillStyle = palette.headerSurface;
25009
+ for (const dirtyRect of topScrollHeaderDirtyRects) {
25010
+ topScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
25011
+ }
25012
+ }
25013
+ if (topFrozenHeaderContext) {
25014
+ topFrozenHeaderContext.strokeStyle = palette.border;
25015
+ topFrozenHeaderContext.lineWidth = 1;
25016
+ }
25017
+ if (topScrollHeaderContext) {
25018
+ topScrollHeaderContext.strokeStyle = palette.border;
25019
+ topScrollHeaderContext.lineWidth = 1;
25020
+ }
25021
+ for (const column of canvasColumnHeaderCells) {
25022
+ const paneContext = column.isFrozen ? topFrozenHeaderContext : topScrollHeaderContext;
25023
+ if (!paneContext) {
25024
+ continue;
25025
+ }
25026
+ if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth2 : topScrollHeaderCanvasWidth2)) {
25027
+ continue;
25028
+ }
25029
+ if (!column.isFrozen && !intersectsCanvasDirtyRects(column.localLeft, 0, column.width, column.height, topScrollHeaderDirtyRects)) {
25030
+ continue;
25031
+ }
25032
+ const selected = normalizedVisibleRange && column.actualCol >= normalizedVisibleRange.start.col && column.actualCol <= normalizedVisibleRange.end.col;
25033
+ paneContext.fillStyle = selected ? selectionHeaderSurface : palette.headerSurface;
25034
+ paneContext.fillRect(column.localLeft, 0, column.width, column.height);
25035
+ paneContext.strokeStyle = palette.border;
25036
+ paneContext.beginPath();
25037
+ paneContext.moveTo(column.localLeft + column.width - 0.5, 0);
25038
+ paneContext.lineTo(column.localLeft + column.width - 0.5, column.height);
25039
+ paneContext.moveTo(column.localLeft, column.height - 0.5);
25040
+ paneContext.lineTo(column.localLeft + column.width, column.height - 0.5);
25041
+ paneContext.stroke();
25042
+ paneContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
25043
+ paneContext.fillStyle = palette.mutedText;
25044
+ paneContext.textAlign = "center";
25045
+ paneContext.textBaseline = "middle";
25046
+ paneContext.fillText(
25047
+ columnLabel2(column.actualCol),
25048
+ column.localLeft + column.width / 2,
25049
+ column.height / 2
25050
+ );
25051
+ }
25052
+ if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight2 > 0) {
25053
+ leftFrozenHeaderContext.fillStyle = palette.rowHeaderSurface;
25054
+ leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight2);
25055
+ }
25056
+ if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight2 > 0) {
25057
+ leftScrollHeaderContext.fillStyle = palette.rowHeaderSurface;
25058
+ for (const dirtyRect of leftScrollHeaderDirtyRects) {
25059
+ leftScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
25060
+ }
25061
+ }
25062
+ if (leftFrozenHeaderContext) {
25063
+ leftFrozenHeaderContext.strokeStyle = palette.border;
25064
+ leftFrozenHeaderContext.lineWidth = 1;
25065
+ }
25066
+ if (leftScrollHeaderContext) {
25067
+ leftScrollHeaderContext.strokeStyle = palette.border;
25068
+ leftScrollHeaderContext.lineWidth = 1;
25069
+ }
25070
+ for (const row of canvasRowHeaderCells) {
25071
+ const paneContext = row.isFrozen ? leftFrozenHeaderContext : leftScrollHeaderContext;
25072
+ if (!paneContext) {
24531
25073
  continue;
24532
25074
  }
24533
- const selected = normalizedVisibleRange && colItem.actualCol >= normalizedVisibleRange.start.col && colItem.actualCol <= normalizedVisibleRange.end.col;
24534
- topHeaderContext.fillStyle = selected ? selectionHeaderSurface : palette.headerSurface;
24535
- topHeaderContext.fillRect(rect.left, 0, rect.width, headerHeight);
24536
- topHeaderContext.strokeStyle = palette.border;
24537
- topHeaderContext.beginPath();
24538
- topHeaderContext.moveTo(rect.left + rect.width - 0.5, 0);
24539
- topHeaderContext.lineTo(rect.left + rect.width - 0.5, headerHeight);
24540
- topHeaderContext.moveTo(rect.left, headerHeight - 0.5);
24541
- topHeaderContext.lineTo(rect.left + rect.width, headerHeight - 0.5);
24542
- topHeaderContext.stroke();
24543
- topHeaderContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
24544
- topHeaderContext.fillStyle = palette.mutedText;
24545
- topHeaderContext.textAlign = "center";
24546
- topHeaderContext.textBaseline = "middle";
24547
- topHeaderContext.fillText(columnLabel2(colItem.actualCol), rect.left + rect.width / 2, headerHeight / 2);
24548
- }
24549
- leftHeaderContext.fillStyle = palette.rowHeaderSurface;
24550
- leftHeaderContext.fillRect(0, 0, rowHeaderWidth, bodyHeight);
24551
- leftHeaderContext.strokeStyle = palette.border;
24552
- leftHeaderContext.lineWidth = 1;
24553
- for (const rowItem of canvasVisibleRowItems) {
24554
- const rect = resolveCanvasRowHeaderRect(rowItem.actualRow);
24555
- if (!rect || rect.top + rect.height < displayHeaderHeight || rect.top > bodyHeight) {
25075
+ if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight2 : leftScrollHeaderCanvasHeight2)) {
24556
25076
  continue;
24557
25077
  }
24558
- const selected = normalizedVisibleRange && rowItem.actualRow >= normalizedVisibleRange.start.row && rowItem.actualRow <= normalizedVisibleRange.end.row;
24559
- leftHeaderContext.fillStyle = selected ? selectionHeaderSurface : palette.rowHeaderSurface;
24560
- leftHeaderContext.fillRect(0, rect.top, rowHeaderWidth, rect.height);
24561
- leftHeaderContext.beginPath();
24562
- leftHeaderContext.moveTo(0, rect.top + rect.height - 0.5);
24563
- leftHeaderContext.lineTo(rowHeaderWidth, rect.top + rect.height - 0.5);
24564
- leftHeaderContext.moveTo(rowHeaderWidth - 0.5, rect.top);
24565
- leftHeaderContext.lineTo(rowHeaderWidth - 0.5, rect.top + rect.height);
24566
- leftHeaderContext.stroke();
24567
- leftHeaderContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
24568
- leftHeaderContext.fillStyle = palette.mutedText;
24569
- leftHeaderContext.textAlign = "center";
24570
- leftHeaderContext.textBaseline = "middle";
24571
- leftHeaderContext.fillText(`${rowItem.actualRow + 1}`, rowHeaderWidth / 2, rect.top + rect.height / 2);
24572
- }
24573
- cornerContext.fillStyle = palette.rowHeaderSurface;
25078
+ if (!row.isFrozen && !intersectsCanvasDirtyRects(0, row.localTop, rowHeaderWidth, row.height, leftScrollHeaderDirtyRects)) {
25079
+ continue;
25080
+ }
25081
+ const selected = normalizedVisibleRange && row.actualRow >= normalizedVisibleRange.start.row && row.actualRow <= normalizedVisibleRange.end.row;
25082
+ paneContext.fillStyle = selected ? selectionHeaderSurface : palette.rowHeaderSurface;
25083
+ paneContext.fillRect(0, row.localTop, rowHeaderWidth, row.height);
25084
+ paneContext.beginPath();
25085
+ paneContext.moveTo(0, row.localTop + row.height - 0.5);
25086
+ paneContext.lineTo(rowHeaderWidth, row.localTop + row.height - 0.5);
25087
+ paneContext.moveTo(rowHeaderWidth - 0.5, row.localTop);
25088
+ paneContext.lineTo(rowHeaderWidth - 0.5, row.localTop + row.height);
25089
+ paneContext.stroke();
25090
+ paneContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
25091
+ paneContext.fillStyle = palette.mutedText;
25092
+ paneContext.textAlign = "center";
25093
+ paneContext.textBaseline = "middle";
25094
+ paneContext.fillText(`${row.actualRow + 1}`, rowHeaderWidth / 2, row.localTop + row.height / 2);
25095
+ }
25096
+ cornerContext.fillStyle = palette.headerSurface;
24574
25097
  cornerContext.fillRect(0, 0, rowHeaderWidth, headerHeight);
24575
25098
  cornerContext.strokeStyle = palette.border;
24576
25099
  cornerContext.lineWidth = 1;
@@ -24592,7 +25115,9 @@ function XlsxGrid({
24592
25115
  }, [
24593
25116
  activeSheet,
24594
25117
  applyCanvasViewportCompensation,
25118
+ canvasColumnHeaderCells,
24595
25119
  canvasPaneAxisItems,
25120
+ canvasRowHeaderCells,
24596
25121
  canvasVisibleColItems,
24597
25122
  canvasVisibleRowItems,
24598
25123
  colIndexByActual,
@@ -24607,11 +25132,13 @@ function XlsxGrid({
24607
25132
  drawingViewport.height,
24608
25133
  drawingViewport.width,
24609
25134
  experimentalCanvas,
25135
+ frozenPaneBottom,
25136
+ frozenPaneRight,
24610
25137
  getCellData,
25138
+ getBodyBlitBufferCanvas,
25139
+ getHeaderBlitBufferCanvas,
24611
25140
  palette,
24612
25141
  resolveCellDisplayRect,
24613
- resolveCanvasColumnHeaderRect,
24614
- resolveCanvasRowHeaderRect,
24615
25142
  resolveMergeAnchorCell,
24616
25143
  resizeGuide,
24617
25144
  rowIndexByActual,
@@ -24849,7 +25376,18 @@ function XlsxGrid({
24849
25376
  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";
24850
25377
  const rowColSpan = renderedCols.length + 1 + (leadingColumnSpacerWidth > 0 ? 1 : 0) + (trailingColumnSpacerWidth > 0 ? 1 : 0);
24851
25378
  const gutterSeparatorShadow = `inset -1px 0 0 ${palette.border}, inset 0 -1px 0 ${palette.border}`;
24852
- const canvasHeaderOverlayZIndex = 1e5;
25379
+ const maxDrawingOverlayZIndex = Math.max(
25380
+ 0,
25381
+ ...shapes.map((shape) => shape.zIndex + 20),
25382
+ ...formControls.map((control) => control.zIndex + 20),
25383
+ ...images.map((image) => image.zIndex + 22),
25384
+ ...charts.map((chart) => chart.zIndex + 22)
25385
+ );
25386
+ const rowHeaderOverlayZIndex = Math.max(35, maxDrawingOverlayZIndex + 1);
25387
+ const canvasHeaderOverlayZIndex = Math.max(50, rowHeaderOverlayZIndex + 1);
25388
+ const stickyHeaderOverlayZIndex = canvasHeaderOverlayZIndex + 1;
25389
+ const cornerHeaderOverlayZIndex = canvasHeaderOverlayZIndex + 2;
25390
+ const frozenRowHeaderOverlayZIndex = rowHeaderOverlayZIndex;
24853
25391
  const headerCellStyle = scaleCssProperties({
24854
25392
  backgroundColor: palette.headerSurface,
24855
25393
  borderBottom: "none",
@@ -24866,7 +25404,7 @@ function XlsxGrid({
24866
25404
  top: 0,
24867
25405
  userSelect: "none",
24868
25406
  whiteSpace: "nowrap",
24869
- zIndex: 50
25407
+ zIndex: canvasHeaderOverlayZIndex
24870
25408
  }, zoomFactor);
24871
25409
  const columnResizeHandleStyle = scaleCssProperties({
24872
25410
  backgroundColor: "transparent",
@@ -24905,6 +25443,10 @@ function XlsxGrid({
24905
25443
  const leftBodyCanvasHeight = scrollBodyCanvasHeight;
24906
25444
  const cornerBodyCanvasWidth = leftBodyCanvasWidth;
24907
25445
  const cornerBodyCanvasHeight = topBodyCanvasHeight;
25446
+ const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
25447
+ const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
25448
+ const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
25449
+ const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
24908
25450
  const canvasBodyBaseStyle = {
24909
25451
  cursor: "cell",
24910
25452
  pointerEvents: "auto",
@@ -24940,22 +25482,38 @@ function XlsxGrid({
24940
25482
  top: displayHeaderHeight,
24941
25483
  zIndex: 31
24942
25484
  };
24943
- const canvasTopHeaderStyle = {
25485
+ const canvasHeaderBaseStyle = {
24944
25486
  cursor: "default",
24945
- display: drawingViewport.width > 0 && drawingViewport.height > 0 ? "block" : "none",
24946
- left: 0,
24947
25487
  pointerEvents: "auto",
24948
25488
  position: "absolute",
25489
+ transformOrigin: "0 0"
25490
+ };
25491
+ const canvasTopFrozenHeaderStyle = {
25492
+ ...canvasHeaderBaseStyle,
25493
+ display: topFrozenHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
25494
+ left: displayRowHeaderWidth,
25495
+ top: 0,
25496
+ zIndex: stickyHeaderOverlayZIndex
25497
+ };
25498
+ const canvasTopScrollHeaderStyle = {
25499
+ ...canvasHeaderBaseStyle,
25500
+ display: topScrollHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
25501
+ left: frozenPaneRight,
24949
25502
  top: 0,
24950
25503
  zIndex: canvasHeaderOverlayZIndex
24951
25504
  };
24952
- const canvasLeftHeaderStyle = {
24953
- cursor: "default",
24954
- display: drawingViewport.width > 0 && drawingViewport.height > 0 ? "block" : "none",
25505
+ const canvasLeftFrozenHeaderStyle = {
25506
+ ...canvasHeaderBaseStyle,
25507
+ display: leftFrozenHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
24955
25508
  left: 0,
24956
- pointerEvents: "auto",
24957
- position: "absolute",
24958
- top: 0,
25509
+ top: displayHeaderHeight,
25510
+ zIndex: stickyHeaderOverlayZIndex
25511
+ };
25512
+ const canvasLeftScrollHeaderStyle = {
25513
+ ...canvasHeaderBaseStyle,
25514
+ display: leftScrollHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
25515
+ left: 0,
25516
+ top: frozenPaneBottom,
24959
25517
  zIndex: canvasHeaderOverlayZIndex
24960
25518
  };
24961
25519
  const canvasCornerHeaderStyle = {
@@ -24964,7 +25522,8 @@ function XlsxGrid({
24964
25522
  pointerEvents: "none",
24965
25523
  position: "absolute",
24966
25524
  top: 0,
24967
- zIndex: canvasHeaderOverlayZIndex + 1
25525
+ transformOrigin: "0 0",
25526
+ zIndex: cornerHeaderOverlayZIndex
24968
25527
  };
24969
25528
  const editingOverlayRect = experimentalCanvas && editingCell ? resolveCellDisplayRect(editingCell) : null;
24970
25529
  const activeCellAdornment = experimentalCanvas && activeCell ? renderCellAdornment(activeCell) : null;
@@ -26110,7 +26669,7 @@ function XlsxGrid({
26110
26669
  pointerEvents: "none",
26111
26670
  position: "absolute",
26112
26671
  top: 0,
26113
- transform: `translate(${-drawingViewport.left}px, ${-drawingViewport.top}px)`,
26672
+ transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
26114
26673
  width: totalWidth
26115
26674
  },
26116
26675
  children: paneDrawingNodes.scroll
@@ -26125,7 +26684,7 @@ function XlsxGrid({
26125
26684
  pointerEvents: "none",
26126
26685
  position: "absolute",
26127
26686
  top: 0,
26128
- transform: `translate(${-drawingViewport.left}px, ${-displayHeaderHeight}px)`,
26687
+ transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-displayHeaderHeight}px)`,
26129
26688
  width: totalWidth
26130
26689
  },
26131
26690
  children: paneDrawingNodes.top
@@ -26140,7 +26699,7 @@ function XlsxGrid({
26140
26699
  pointerEvents: "none",
26141
26700
  position: "absolute",
26142
26701
  top: 0,
26143
- transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top}px)`,
26702
+ transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
26144
26703
  width: totalWidth
26145
26704
  },
26146
26705
  children: paneDrawingNodes.left
@@ -26167,21 +26726,41 @@ function XlsxGrid({
26167
26726
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26168
26727
  "canvas",
26169
26728
  {
26170
- ref: topHeaderCanvasRef,
26729
+ ref: topFrozenHeaderCanvasRef,
26171
26730
  onPointerLeave: handleCanvasHeaderPointerLeave,
26172
26731
  onPointerMove: handleCanvasColumnHeaderPointerMove,
26173
26732
  onPointerDown: handleCanvasColumnHeaderPointerDown,
26174
- style: canvasTopHeaderStyle
26733
+ style: canvasTopFrozenHeaderStyle
26734
+ }
26735
+ ),
26736
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26737
+ "canvas",
26738
+ {
26739
+ ref: topScrollHeaderCanvasRef,
26740
+ onPointerLeave: handleCanvasHeaderPointerLeave,
26741
+ onPointerMove: handleCanvasColumnHeaderPointerMove,
26742
+ onPointerDown: handleCanvasColumnHeaderPointerDown,
26743
+ style: canvasTopScrollHeaderStyle
26744
+ }
26745
+ ),
26746
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26747
+ "canvas",
26748
+ {
26749
+ ref: leftFrozenHeaderCanvasRef,
26750
+ onPointerLeave: handleCanvasHeaderPointerLeave,
26751
+ onPointerMove: handleCanvasRowHeaderPointerMove,
26752
+ onPointerDown: handleCanvasRowHeaderPointerDown,
26753
+ style: canvasLeftFrozenHeaderStyle
26175
26754
  }
26176
26755
  ),
26177
26756
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26178
26757
  "canvas",
26179
26758
  {
26180
- ref: leftHeaderCanvasRef,
26759
+ ref: leftScrollHeaderCanvasRef,
26181
26760
  onPointerLeave: handleCanvasHeaderPointerLeave,
26182
26761
  onPointerMove: handleCanvasRowHeaderPointerMove,
26183
26762
  onPointerDown: handleCanvasRowHeaderPointerDown,
26184
- style: canvasLeftHeaderStyle
26763
+ style: canvasLeftScrollHeaderStyle
26185
26764
  }
26186
26765
  ),
26187
26766
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("canvas", { ref: cornerHeaderCanvasRef, style: canvasCornerHeaderStyle })
@@ -26278,7 +26857,7 @@ function XlsxGrid({
26278
26857
  )),
26279
26858
  trailingColumnSpacerWidth > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("col", { style: { width: trailingColumnSpacerWidth } }) : null
26280
26859
  ] }),
26281
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { style: { position: "sticky", top: 0, zIndex: 50 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
26860
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("thead", { style: { position: "sticky", top: 0, zIndex: canvasHeaderOverlayZIndex }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("tr", { children: [
26282
26861
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
26283
26862
  "th",
26284
26863
  {
@@ -26287,7 +26866,7 @@ function XlsxGrid({
26287
26866
  backgroundColor: palette.headerSurface,
26288
26867
  left: 0,
26289
26868
  width: displayRowHeaderWidth,
26290
- zIndex: 60
26869
+ zIndex: cornerHeaderOverlayZIndex
26291
26870
  }
26292
26871
  }
26293
26872
  ),
@@ -26301,7 +26880,7 @@ function XlsxGrid({
26301
26880
  style: {
26302
26881
  ...headerCellStyle,
26303
26882
  left: stickyLeftByCol.get(column.actualCol),
26304
- zIndex: stickyLeftByCol.has(column.actualCol) ? 55 : headerCellStyle.zIndex
26883
+ zIndex: stickyLeftByCol.has(column.actualCol) ? stickyHeaderOverlayZIndex : headerCellStyle.zIndex
26305
26884
  },
26306
26885
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative" }, children: [
26307
26886
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -26356,6 +26935,7 @@ function XlsxGrid({
26356
26935
  actualRow,
26357
26936
  editingCell,
26358
26937
  editingValue,
26938
+ frozenRowHeaderZIndex: frozenRowHeaderOverlayZIndex,
26359
26939
  getCellData,
26360
26940
  leadingSpacerWidth: leadingColumnSpacerWidth,
26361
26941
  onCellClick: handleCellClick,
@@ -26372,6 +26952,7 @@ function XlsxGrid({
26372
26952
  readOnly,
26373
26953
  renderCellAdornment,
26374
26954
  rowHeight: virtualRow.size,
26955
+ rowHeaderZIndex: rowHeaderOverlayZIndex,
26375
26956
  rowHeaderWidth: displayRowHeaderWidth,
26376
26957
  stickyLeftByCol,
26377
26958
  stickyTop: stickyTopByRow.get(actualRow),