@extend-ai/react-xlsx 0.7.0 → 0.8.1

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 [];
@@ -6832,17 +6855,18 @@ function rangeContainsCell(range, cell) {
6832
6855
  function mapWorksheetTables(worksheet, metadataForSheet) {
6833
6856
  const rawTables = worksheet?.tables ?? [];
6834
6857
  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
6858
  const rawColumns = Array.isArray(table.columns) ? table.columns : [];
6841
6859
  const rawName = typeof table.name === "string" ? table.name : `Table${index + 1}`;
6842
6860
  const rawDisplayName = typeof table.displayName === "string" ? table.displayName : typeof table.name === "string" ? table.name : `Table ${index + 1}`;
6843
6861
  const metadata = metadataForSheet?.find(
6844
- (entry) => entry.name && entry.name === rawName || entry.displayName && entry.displayName === rawDisplayName || entry.reference && entry.reference === reference
6862
+ (entry) => entry.name && entry.name === rawName || entry.displayName && entry.displayName === rawDisplayName || entry.reference && entry.reference === table.reference
6845
6863
  );
6864
+ const rawReference = typeof table.reference === "string" ? table.reference : "";
6865
+ const reference = metadata?.reference ?? rawReference;
6866
+ const parsedRange = parseA1RangeReference2(reference);
6867
+ if (!parsedRange) {
6868
+ return [];
6869
+ }
6846
6870
  return [{
6847
6871
  columns: rawColumns.map((column, columnIndex) => ({
6848
6872
  id: typeof column.id === "number" ? column.id ?? columnIndex + 1 : columnIndex + 1,
@@ -6851,17 +6875,47 @@ function mapWorksheetTables(worksheet, metadataForSheet) {
6851
6875
  })),
6852
6876
  displayName: rawDisplayName,
6853
6877
  end: parsedRange.end,
6854
- headerRowCount: typeof table.headerRowCount === "number" ? table.headerRowCount : 1,
6878
+ headerRowCount: metadata?.headerRowCount ?? resolveWorkbookTableCount(table.headerRowCount, 1),
6855
6879
  headerRowCellStyle: metadata?.headerRowCellStyle,
6856
6880
  name: rawName,
6857
6881
  reference,
6858
6882
  start: parsedRange.start,
6859
6883
  styleInfo: table.styleInfo,
6860
- totalsRowCount: typeof table.totalsRowCount === "number" ? table.totalsRowCount : 0,
6861
- totalsRowShown: Boolean(table.totalsRowShown)
6884
+ totalsRowCount: metadata?.totalsRowCount ?? resolveWorkbookTableCount(table.totalsRowCount, 0),
6885
+ totalsRowShown: metadata?.totalsRowShown ?? resolveWorkbookTableBoolean(table.totalsRowShown)
6862
6886
  }];
6863
6887
  });
6864
6888
  }
6889
+ function resolveWorkbookTableCount(value, fallback) {
6890
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
6891
+ return value;
6892
+ }
6893
+ if (typeof value === "string") {
6894
+ const parsed = Number.parseInt(value, 10);
6895
+ if (Number.isFinite(parsed) && parsed >= 0) {
6896
+ return parsed;
6897
+ }
6898
+ }
6899
+ return fallback;
6900
+ }
6901
+ function resolveWorkbookTableBoolean(value) {
6902
+ if (typeof value === "boolean") {
6903
+ return value;
6904
+ }
6905
+ if (typeof value === "number") {
6906
+ return value !== 0;
6907
+ }
6908
+ if (typeof value === "string") {
6909
+ const normalized = value.trim().toLowerCase();
6910
+ if (normalized === "0" || normalized === "false" || normalized === "") {
6911
+ return false;
6912
+ }
6913
+ if (normalized === "1" || normalized === "true") {
6914
+ return true;
6915
+ }
6916
+ }
6917
+ return false;
6918
+ }
6865
6919
  function fileStem(fileName) {
6866
6920
  const normalized = fileName.trim();
6867
6921
  const lastDot = normalized.lastIndexOf(".");
@@ -6992,7 +7046,17 @@ async function resolveWorkbookBuffer({ file, src }, signal) {
6992
7046
  if (file) {
6993
7047
  buffer = file;
6994
7048
  } else if (src) {
6995
- const response = await fetch(src, { signal });
7049
+ let response;
7050
+ try {
7051
+ response = await fetch(src, { signal });
7052
+ } catch (error) {
7053
+ if (isAbortError(error)) {
7054
+ throw error;
7055
+ }
7056
+ throw new Error(
7057
+ "Failed to fetch workbook. The remote URL may be blocked by CORS, unavailable, or not directly downloadable from the browser."
7058
+ );
7059
+ }
6996
7060
  if (!response.ok) {
6997
7061
  throw new Error(`Failed to fetch workbook (status ${response.status})`);
6998
7062
  }
@@ -7548,6 +7612,7 @@ function downloadUrl(src, fileName) {
7548
7612
  }
7549
7613
  function useXlsxViewerController(options) {
7550
7614
  const {
7615
+ allowResizeInReadOnly = false,
7551
7616
  deferLoadingAboveBytes = DEFAULT_DEFER_LOADING_ABOVE_BYTES,
7552
7617
  file,
7553
7618
  fileName,
@@ -7601,6 +7666,7 @@ function useXlsxViewerController(options) {
7601
7666
  const displayFileName = React.useMemo(() => resolveDisplayFileName(src, fileName), [fileName, src]);
7602
7667
  const shouldDeferLoading = deferLoadingAboveBytes > 0;
7603
7668
  const readOnly = requestedReadOnly || forcedReadOnly;
7669
+ const canResizeReadOnly = requestedReadOnly && allowResizeInReadOnly && !forcedReadOnly;
7604
7670
  const workerSupported = useWorker && typeof Worker !== "undefined";
7605
7671
  const shouldUseWorker = workerSupported && forcedReadOnly;
7606
7672
  const shouldForceReadOnlyForBuffer = React.useCallback((bufferByteLength) => !requestedReadOnly && readOnlyAboveBytes > 0 && bufferByteLength > readOnlyAboveBytes, [readOnlyAboveBytes, requestedReadOnly]);
@@ -8895,7 +8961,7 @@ function useXlsxViewerController(options) {
8895
8961
  refreshWorkbookState(workbook);
8896
8962
  }, [refreshWorkbookState, workbook]);
8897
8963
  const resizeColumn = React.useCallback((col, widthPx) => {
8898
- if (readOnly || !workbook || !activeSheet) {
8964
+ if (readOnly && !canResizeReadOnly || !workbook || !activeSheet) {
8899
8965
  return;
8900
8966
  }
8901
8967
  recordHistoryBeforeMutation();
@@ -8905,9 +8971,9 @@ function useXlsxViewerController(options) {
8905
8971
  pxToSheetColumnWidth(resolveContentSheetAxisPixels(widthPx, activeSheet.showGridLines))
8906
8972
  );
8907
8973
  refreshWorkbookState(workbook);
8908
- }, [activeSheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
8974
+ }, [activeSheet, canResizeReadOnly, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
8909
8975
  const resizeRow = React.useCallback((row, heightPx) => {
8910
- if (readOnly || !workbook || !activeSheet) {
8976
+ if (readOnly && !canResizeReadOnly || !workbook || !activeSheet) {
8911
8977
  return;
8912
8978
  }
8913
8979
  recordHistoryBeforeMutation();
@@ -8917,7 +8983,7 @@ function useXlsxViewerController(options) {
8917
8983
  pxToSheetRowHeight(resolveContentSheetAxisPixels(heightPx, activeSheet.showGridLines))
8918
8984
  );
8919
8985
  refreshWorkbookState(workbook);
8920
- }, [activeSheet, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
8986
+ }, [activeSheet, canResizeReadOnly, readOnly, recordHistoryBeforeMutation, refreshWorkbookState, workbook]);
8921
8987
  const resolveAnchoredObjectRect = React.useCallback((anchor, worksheet) => {
8922
8988
  const resolveAxisSum = (index, getSize) => {
8923
8989
  let total = 0;
@@ -16317,6 +16383,13 @@ var IMAGE_MIN_SIZE_PX = 16;
16317
16383
  var IMAGE_HANDLE_SIZE_PX = 10;
16318
16384
  var CANVAS_RESIZE_HIT_SLOP_PX = 8;
16319
16385
  var CANVAS_VIEWPORT_OVERSCAN_PX = 240;
16386
+ var THUMBNAIL_DEFAULT_MAX_DIMENSION = 192;
16387
+ var THUMBNAIL_FALLBACK_ROWS = 12;
16388
+ var THUMBNAIL_FALLBACK_COLS = 8;
16389
+ var THUMBNAIL_MAX_ROWS = 200;
16390
+ var THUMBNAIL_MAX_COLS = 80;
16391
+ var THUMBNAIL_MAX_SOURCE_HEIGHT_PX = 900;
16392
+ var THUMBNAIL_MAX_SOURCE_WIDTH_PX = 1440;
16320
16393
  var LIVE_ZOOM_COMMIT_IDLE_MS = 48;
16321
16394
  var WHEEL_ZOOM_SENSITIVITY = 25e-5;
16322
16395
  var WHEEL_LINE_DELTA_PX = 16;
@@ -18323,6 +18396,97 @@ function resolveImageHandleStyle(position, stroke, surface, scale = 1) {
18323
18396
  function useViewerPalette(isDark = false) {
18324
18397
  return isDark ? DARK_PALETTE : LIGHT_PALETTE;
18325
18398
  }
18399
+ function normalizeThumbnailResolution(resolution) {
18400
+ if (typeof resolution === "number" && Number.isFinite(resolution) && resolution > 0) {
18401
+ return {
18402
+ maxHeight: resolution,
18403
+ maxWidth: resolution
18404
+ };
18405
+ }
18406
+ const resolvedObject = typeof resolution === "object" && resolution ? resolution : void 0;
18407
+ return {
18408
+ maxHeight: resolvedObject?.maxHeight && Number.isFinite(resolvedObject.maxHeight) && resolvedObject.maxHeight > 0 ? resolvedObject.maxHeight : THUMBNAIL_DEFAULT_MAX_DIMENSION,
18409
+ maxWidth: resolvedObject?.maxWidth && Number.isFinite(resolvedObject.maxWidth) && resolvedObject.maxWidth > 0 ? resolvedObject.maxWidth : THUMBNAIL_DEFAULT_MAX_DIMENSION
18410
+ };
18411
+ }
18412
+ function resolveThumbnailOutputSize(sourceWidth, sourceHeight, resolution) {
18413
+ const normalized = normalizeThumbnailResolution(resolution);
18414
+ const safeSourceWidth = Math.max(1, sourceWidth);
18415
+ const safeSourceHeight = Math.max(1, sourceHeight);
18416
+ const scale = Math.min(
18417
+ normalized.maxWidth / safeSourceWidth,
18418
+ normalized.maxHeight / safeSourceHeight,
18419
+ 1
18420
+ );
18421
+ return {
18422
+ height: Math.max(1, Math.round(safeSourceHeight * scale)),
18423
+ maxHeight: normalized.maxHeight,
18424
+ maxWidth: normalized.maxWidth,
18425
+ scale,
18426
+ width: Math.max(1, Math.round(safeSourceWidth * scale))
18427
+ };
18428
+ }
18429
+ function resolveThumbnailAxisIndices({
18430
+ fallbackCount,
18431
+ getSizePx,
18432
+ maxCount,
18433
+ maxLogicalPixels,
18434
+ maxUsedIndex,
18435
+ minUsedIndex,
18436
+ precomputed
18437
+ }) {
18438
+ const sourceIndices = precomputed.length > 0 ? precomputed : Array.from({
18439
+ length: Math.max(fallbackCount, Math.max(0, maxUsedIndex + 1))
18440
+ }, (_, index) => index);
18441
+ const hasUsedRange = maxUsedIndex >= minUsedIndex && maxUsedIndex >= 0;
18442
+ const preferredStart = hasUsedRange ? Math.max(0, minUsedIndex) : 0;
18443
+ const preferredEnd = hasUsedRange ? maxUsedIndex : Math.max(0, preferredStart + fallbackCount - 1);
18444
+ const picked = [];
18445
+ let totalSize = 0;
18446
+ for (const actualIndex of sourceIndices) {
18447
+ if (actualIndex < preferredStart) {
18448
+ continue;
18449
+ }
18450
+ if (hasUsedRange && actualIndex > preferredEnd) {
18451
+ break;
18452
+ }
18453
+ const size = Math.max(1, getSizePx(actualIndex));
18454
+ picked.push(actualIndex);
18455
+ totalSize += size;
18456
+ if (picked.length >= maxCount || totalSize >= maxLogicalPixels) {
18457
+ break;
18458
+ }
18459
+ }
18460
+ if (picked.length > 0) {
18461
+ return picked;
18462
+ }
18463
+ for (const actualIndex of sourceIndices) {
18464
+ const size = Math.max(1, getSizePx(actualIndex));
18465
+ picked.push(actualIndex);
18466
+ totalSize += size;
18467
+ if (picked.length >= fallbackCount || picked.length >= maxCount || totalSize >= maxLogicalPixels) {
18468
+ break;
18469
+ }
18470
+ }
18471
+ return picked.length > 0 ? picked : [0];
18472
+ }
18473
+ function buildThumbnailAxisItems(actualIndices, getSizePx) {
18474
+ const items = [];
18475
+ let offset = 0;
18476
+ for (const actualIndex of actualIndices) {
18477
+ const size = Math.max(1, getSizePx(actualIndex));
18478
+ items.push({
18479
+ actualIndex,
18480
+ size,
18481
+ start: offset
18482
+ });
18483
+ offset += size;
18484
+ }
18485
+ return {
18486
+ items,
18487
+ totalSize: offset
18488
+ };
18489
+ }
18326
18490
  function columnLabel2(col) {
18327
18491
  let label = "";
18328
18492
  let nextValue = col;
@@ -19229,7 +19393,6 @@ function getTableHeaderColumn(table, row, col) {
19229
19393
  return table.columns[index] ?? null;
19230
19394
  }
19231
19395
  function DefaultTableHeaderMenu({
19232
- close,
19233
19396
  direction,
19234
19397
  sortAscending,
19235
19398
  sortDescending
@@ -19251,10 +19414,7 @@ function DefaultTableHeaderMenu({
19251
19414
  /* @__PURE__ */ jsx3(
19252
19415
  "button",
19253
19416
  {
19254
- onClick: () => {
19255
- sortAscending();
19256
- close();
19257
- },
19417
+ onClick: sortAscending,
19258
19418
  style: {
19259
19419
  background: direction === "ascending" ? "var(--xlsx-menu-active)" : "transparent",
19260
19420
  border: "none",
@@ -19272,10 +19432,7 @@ function DefaultTableHeaderMenu({
19272
19432
  /* @__PURE__ */ jsx3(
19273
19433
  "button",
19274
19434
  {
19275
- onClick: () => {
19276
- sortDescending();
19277
- close();
19278
- },
19435
+ onClick: sortDescending,
19279
19436
  style: {
19280
19437
  background: direction === "descending" ? "var(--xlsx-menu-active)" : "transparent",
19281
19438
  border: "none",
@@ -19294,6 +19451,25 @@ function DefaultTableHeaderMenu({
19294
19451
  }
19295
19452
  );
19296
19453
  }
19454
+ function resolveTableHeaderTriggerStyle(palette, zoomFactor) {
19455
+ return {
19456
+ alignItems: "center",
19457
+ background: "transparent",
19458
+ border: "none",
19459
+ color: palette.mutedText,
19460
+ cursor: "pointer",
19461
+ display: "inline-flex",
19462
+ fontSize: 10 * zoomFactor,
19463
+ height: 16 * zoomFactor,
19464
+ justifyContent: "center",
19465
+ padding: 0,
19466
+ position: "absolute",
19467
+ right: 4 * zoomFactor,
19468
+ top: 3 * zoomFactor,
19469
+ width: 16 * zoomFactor,
19470
+ zIndex: 6
19471
+ };
19472
+ }
19297
19473
  function SegmentedControl({
19298
19474
  items,
19299
19475
  onValueChange,
@@ -19802,6 +19978,109 @@ function resolveSelectionColors({
19802
19978
  stroke
19803
19979
  };
19804
19980
  }
19981
+ function buildFullCanvasDirtyRect(width, height) {
19982
+ if (width <= 0 || height <= 0) {
19983
+ return [];
19984
+ }
19985
+ return [{
19986
+ height,
19987
+ left: 0,
19988
+ top: 0,
19989
+ width
19990
+ }];
19991
+ }
19992
+ function intersectsCanvasDirtyRects(left, top, width, height, dirtyRects) {
19993
+ if (dirtyRects.length === 0 || width <= 0 || height <= 0) {
19994
+ return false;
19995
+ }
19996
+ const right = left + width;
19997
+ const bottom = top + height;
19998
+ return dirtyRects.some((dirtyRect) => left < dirtyRect.left + dirtyRect.width && right > dirtyRect.left && top < dirtyRect.top + dirtyRect.height && bottom > dirtyRect.top);
19999
+ }
20000
+ function blitCanvasWithScrollDelta(canvas, context, bufferCanvas, dpr, width, height, deltaX, deltaY) {
20001
+ if (width <= 0 || height <= 0) {
20002
+ return [];
20003
+ }
20004
+ const clampedDeltaX = Math.max(-width, Math.min(width, deltaX));
20005
+ const clampedDeltaY = Math.max(-height, Math.min(height, deltaY));
20006
+ if (Math.abs(clampedDeltaX) < 0.01 && Math.abs(clampedDeltaY) < 0.01) {
20007
+ return [];
20008
+ }
20009
+ if (Math.abs(clampedDeltaX) >= width || Math.abs(clampedDeltaY) >= height) {
20010
+ return null;
20011
+ }
20012
+ const deviceWidth = Math.max(1, Math.round(width * dpr));
20013
+ const deviceHeight = Math.max(1, Math.round(height * dpr));
20014
+ if (bufferCanvas.width !== deviceWidth) {
20015
+ bufferCanvas.width = deviceWidth;
20016
+ }
20017
+ if (bufferCanvas.height !== deviceHeight) {
20018
+ bufferCanvas.height = deviceHeight;
20019
+ }
20020
+ const bufferContext = bufferCanvas.getContext("2d");
20021
+ if (!bufferContext) {
20022
+ return null;
20023
+ }
20024
+ bufferContext.setTransform(1, 0, 0, 1, 0, 0);
20025
+ bufferContext.clearRect(0, 0, deviceWidth, deviceHeight);
20026
+ bufferContext.drawImage(canvas, 0, 0);
20027
+ const deltaDeviceX = clampedDeltaX * dpr;
20028
+ const deltaDeviceY = clampedDeltaY * dpr;
20029
+ const overlapDeviceWidth = deviceWidth - Math.abs(deltaDeviceX);
20030
+ const overlapDeviceHeight = deviceHeight - Math.abs(deltaDeviceY);
20031
+ if (overlapDeviceWidth <= 0 || overlapDeviceHeight <= 0) {
20032
+ return null;
20033
+ }
20034
+ const sourceX = deltaDeviceX > 0 ? deltaDeviceX : 0;
20035
+ const sourceY = deltaDeviceY > 0 ? deltaDeviceY : 0;
20036
+ const destinationX = deltaDeviceX > 0 ? 0 : -deltaDeviceX;
20037
+ const destinationY = deltaDeviceY > 0 ? 0 : -deltaDeviceY;
20038
+ context.setTransform(1, 0, 0, 1, 0, 0);
20039
+ context.drawImage(
20040
+ bufferCanvas,
20041
+ sourceX,
20042
+ sourceY,
20043
+ overlapDeviceWidth,
20044
+ overlapDeviceHeight,
20045
+ destinationX,
20046
+ destinationY,
20047
+ overlapDeviceWidth,
20048
+ overlapDeviceHeight
20049
+ );
20050
+ context.setTransform(dpr, 0, 0, dpr, 0, 0);
20051
+ const dirtyRects = [];
20052
+ if (clampedDeltaX > 0) {
20053
+ dirtyRects.push({
20054
+ height,
20055
+ left: Math.max(0, width - clampedDeltaX),
20056
+ top: 0,
20057
+ width: Math.min(width, clampedDeltaX)
20058
+ });
20059
+ } else if (clampedDeltaX < 0) {
20060
+ dirtyRects.push({
20061
+ height,
20062
+ left: 0,
20063
+ top: 0,
20064
+ width: Math.min(width, -clampedDeltaX)
20065
+ });
20066
+ }
20067
+ if (clampedDeltaY > 0) {
20068
+ dirtyRects.push({
20069
+ height: Math.min(height, clampedDeltaY),
20070
+ left: 0,
20071
+ top: Math.max(0, height - clampedDeltaY),
20072
+ width
20073
+ });
20074
+ } else if (clampedDeltaY < 0) {
20075
+ dirtyRects.push({
20076
+ height: Math.min(height, -clampedDeltaY),
20077
+ left: 0,
20078
+ top: 0,
20079
+ width
20080
+ });
20081
+ }
20082
+ return dirtyRects;
20083
+ }
19805
20084
  function buildConditionalFormatRuleKey(rule) {
19806
20085
  return `${rule.kind}:${rule.priority}:${rule.ranges.map((range) => `${range.start.row}:${range.start.col}-${range.end.row}:${range.end.col}`).join("|")}`;
19807
20086
  }
@@ -20661,8 +20940,20 @@ function XlsxGrid({
20661
20940
  const topBodyCanvasRef = React4.useRef(null);
20662
20941
  const leftBodyCanvasRef = React4.useRef(null);
20663
20942
  const cornerBodyCanvasRef = React4.useRef(null);
20664
- const topHeaderCanvasRef = React4.useRef(null);
20665
- const leftHeaderCanvasRef = React4.useRef(null);
20943
+ const bodyBlitBufferCanvasRef = React4.useRef({
20944
+ corner: null,
20945
+ left: null,
20946
+ scroll: null,
20947
+ top: null
20948
+ });
20949
+ const headerBlitBufferCanvasRef = React4.useRef({
20950
+ left: null,
20951
+ top: null
20952
+ });
20953
+ const topFrozenHeaderCanvasRef = React4.useRef(null);
20954
+ const topScrollHeaderCanvasRef = React4.useRef(null);
20955
+ const leftFrozenHeaderCanvasRef = React4.useRef(null);
20956
+ const leftScrollHeaderCanvasRef = React4.useRef(null);
20666
20957
  const cornerHeaderCanvasRef = React4.useRef(null);
20667
20958
  const selectionOverlayRef = React4.useRef(null);
20668
20959
  const activeValidationOverlayRef = React4.useRef(null);
@@ -20866,16 +21157,31 @@ function XlsxGrid({
20866
21157
  liveZoomTranslateX2,
20867
21158
  liveZoomTranslateY2
20868
21159
  );
20869
- const topHeaderCanvas = topHeaderCanvasRef.current;
20870
- if (topHeaderCanvas) {
20871
- topHeaderCanvas.style.transform = scrollDeltaX !== 0 ? `translate3d(${scrollDeltaX}px, 0, 0)` : "";
20872
- topHeaderCanvas.style.willChange = scrollDeltaX !== 0 ? "transform" : "";
20873
- }
20874
- const leftHeaderCanvas = leftHeaderCanvasRef.current;
20875
- if (leftHeaderCanvas) {
20876
- leftHeaderCanvas.style.transform = scrollDeltaY !== 0 ? `translate3d(0, ${scrollDeltaY}px, 0)` : "";
20877
- leftHeaderCanvas.style.willChange = scrollDeltaY !== 0 ? "transform" : "";
20878
- }
21160
+ applyCanvasTransform(
21161
+ topScrollHeaderCanvasRef.current,
21162
+ scrollDeltaX + liveZoomTranslateX2,
21163
+ liveZoomTranslateY2
21164
+ );
21165
+ applyCanvasTransform(
21166
+ topFrozenHeaderCanvasRef.current,
21167
+ liveZoomTranslateX2,
21168
+ liveZoomTranslateY2
21169
+ );
21170
+ applyCanvasTransform(
21171
+ leftScrollHeaderCanvasRef.current,
21172
+ liveZoomTranslateX2,
21173
+ scrollDeltaY + liveZoomTranslateY2
21174
+ );
21175
+ applyCanvasTransform(
21176
+ leftFrozenHeaderCanvasRef.current,
21177
+ liveZoomTranslateX2,
21178
+ liveZoomTranslateY2
21179
+ );
21180
+ applyCanvasTransform(
21181
+ cornerHeaderCanvasRef.current,
21182
+ liveZoomTranslateX2,
21183
+ liveZoomTranslateY2
21184
+ );
20879
21185
  }, [zoomScale]);
20880
21186
  const updateLiveGestureZoomState = React4.useCallback((nextState) => {
20881
21187
  const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
@@ -21006,6 +21312,22 @@ function XlsxGrid({
21006
21312
  scrollRef.current.style.cursor = "";
21007
21313
  }
21008
21314
  }, []);
21315
+ const getBodyBlitBufferCanvas = React4.useCallback((pane) => {
21316
+ let bufferCanvas = bodyBlitBufferCanvasRef.current[pane];
21317
+ if (!bufferCanvas && typeof document !== "undefined") {
21318
+ bufferCanvas = document.createElement("canvas");
21319
+ bodyBlitBufferCanvasRef.current[pane] = bufferCanvas;
21320
+ }
21321
+ return bufferCanvas;
21322
+ }, []);
21323
+ const getHeaderBlitBufferCanvas = React4.useCallback((axis) => {
21324
+ let bufferCanvas = headerBlitBufferCanvasRef.current[axis];
21325
+ if (!bufferCanvas && typeof document !== "undefined") {
21326
+ bufferCanvas = document.createElement("canvas");
21327
+ headerBlitBufferCanvasRef.current[axis] = bufferCanvas;
21328
+ }
21329
+ return bufferCanvas;
21330
+ }, []);
21009
21331
  const visibleRows = React4.useMemo(() => {
21010
21332
  return buildVisibleAxisIndices(
21011
21333
  activeSheet?.visibleRows ?? [],
@@ -21720,14 +22042,14 @@ function XlsxGrid({
21720
22042
  const handleScrollerScroll = React4.useCallback((event) => {
21721
22043
  const currentScroller = event.currentTarget;
21722
22044
  cachedScrollerRectRef.current = null;
21723
- syncDrawingViewport(currentScroller, { immediate: !experimentalCanvas });
22045
+ syncDrawingViewport(currentScroller, { immediate: true });
21724
22046
  if (currentScroller.scrollHeight - (currentScroller.scrollTop + currentScroller.clientHeight) < OPEN_GRID_VERTICAL_EDGE_PX) {
21725
22047
  setDisplayRowLimit((current) => current + OPEN_GRID_ROW_GROWTH);
21726
22048
  }
21727
22049
  if (currentScroller.scrollWidth - (currentScroller.scrollLeft + currentScroller.clientWidth) < OPEN_GRID_HORIZONTAL_EDGE_PX) {
21728
22050
  setDisplayColLimit((current) => current + OPEN_GRID_COL_GROWTH);
21729
22051
  }
21730
- }, [experimentalCanvas, syncDrawingViewport]);
22052
+ }, [syncDrawingViewport]);
21731
22053
  React4.useEffect(() => {
21732
22054
  const scroller = scrollRef.current;
21733
22055
  if (!scroller || !enableGestureZoom) {
@@ -23508,6 +23830,30 @@ function XlsxGrid({
23508
23830
  return null;
23509
23831
  }
23510
23832
  const direction = sortState && sortState.tableName === table.name && sortState.columnIndex === tableColumn.index ? sortState.direction : null;
23833
+ const triggerIcon = direction === "ascending" ? "\u25B2" : direction === "descending" ? "\u25BC" : "\u25BE";
23834
+ if (renderTableHeaderMenu) {
23835
+ return renderTableHeaderMenu({
23836
+ cell,
23837
+ column: tableColumn,
23838
+ direction,
23839
+ sortAscending: () => sortTable(table.name, tableColumn.index, "ascending"),
23840
+ sortDescending: () => sortTable(table.name, tableColumn.index, "descending"),
23841
+ table,
23842
+ triggerIcon,
23843
+ triggerProps: {
23844
+ "aria-haspopup": "menu",
23845
+ "aria-label": `Open menu for ${tableColumn.name}`,
23846
+ onClick: (event) => {
23847
+ event.stopPropagation();
23848
+ },
23849
+ onPointerDown: (event) => {
23850
+ event.stopPropagation();
23851
+ },
23852
+ style: resolveTableHeaderTriggerStyle(palette, zoomFactor),
23853
+ type: "button"
23854
+ }
23855
+ });
23856
+ }
23511
23857
  return /* @__PURE__ */ jsx3(
23512
23858
  "button",
23513
23859
  {
@@ -23523,27 +23869,13 @@ function XlsxGrid({
23523
23869
  );
23524
23870
  },
23525
23871
  style: {
23526
- alignItems: "center",
23527
- background: "transparent",
23528
- border: "none",
23529
- color: palette.mutedText,
23530
- cursor: "pointer",
23531
- display: "inline-flex",
23532
- fontSize: 10 * zoomFactor,
23533
- height: 16 * zoomFactor,
23534
- justifyContent: "center",
23535
- padding: 0,
23536
- position: "absolute",
23537
- right: 4 * zoomFactor,
23538
- top: 3 * zoomFactor,
23539
- width: 16 * zoomFactor,
23540
- zIndex: 6
23872
+ ...resolveTableHeaderTriggerStyle(palette, zoomFactor)
23541
23873
  },
23542
23874
  type: "button",
23543
- children: direction === "ascending" ? "\u25B2" : direction === "descending" ? "\u25BC" : "\u25BE"
23875
+ children: triggerIcon
23544
23876
  }
23545
23877
  );
23546
- }, [effectiveTables, palette.mutedText, sortState, zoomFactor]);
23878
+ }, [effectiveTables, palette, renderTableHeaderMenu, sortState, sortTable, zoomFactor]);
23547
23879
  const resolveCanvasColumnHeaderRect = React4.useCallback((actualCol) => {
23548
23880
  const colIndex = colIndexByActual.get(actualCol);
23549
23881
  if (colIndex === void 0) {
@@ -23582,6 +23914,54 @@ function XlsxGrid({
23582
23914
  rowPrefixSums,
23583
23915
  stickyTopByRow
23584
23916
  ]);
23917
+ const canvasColumnHeaderCells = React4.useMemo(
23918
+ () => canvasVisibleColItems.flatMap((column) => {
23919
+ const rect = resolveCanvasColumnHeaderRect(column.actualCol);
23920
+ if (!rect) {
23921
+ return [];
23922
+ }
23923
+ const isFrozen = stickyLeftByCol.has(column.actualCol);
23924
+ return [{
23925
+ actualCol: column.actualCol,
23926
+ height: displayHeaderHeight,
23927
+ isFrozen,
23928
+ left: rect.left,
23929
+ localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : frozenPaneRight),
23930
+ width: rect.width
23931
+ }];
23932
+ }),
23933
+ [
23934
+ canvasVisibleColItems,
23935
+ displayHeaderHeight,
23936
+ displayRowHeaderWidth,
23937
+ frozenPaneRight,
23938
+ resolveCanvasColumnHeaderRect,
23939
+ stickyLeftByCol
23940
+ ]
23941
+ );
23942
+ const canvasRowHeaderCells = React4.useMemo(
23943
+ () => canvasVisibleRowItems.flatMap((row) => {
23944
+ const rect = resolveCanvasRowHeaderRect(row.actualRow);
23945
+ if (!rect) {
23946
+ return [];
23947
+ }
23948
+ const isFrozen = stickyTopByRow.has(row.actualRow);
23949
+ return [{
23950
+ actualRow: row.actualRow,
23951
+ height: rect.height,
23952
+ isFrozen,
23953
+ localTop: rect.top - (isFrozen ? displayHeaderHeight : frozenPaneBottom),
23954
+ top: rect.top
23955
+ }];
23956
+ }),
23957
+ [
23958
+ canvasVisibleRowItems,
23959
+ displayHeaderHeight,
23960
+ frozenPaneBottom,
23961
+ resolveCanvasRowHeaderRect,
23962
+ stickyTopByRow
23963
+ ]
23964
+ );
23585
23965
  const resolveCanvasColumnResizeTarget = React4.useCallback((clientX) => {
23586
23966
  if (!canResizeHeaders) {
23587
23967
  return null;
@@ -23591,17 +23971,26 @@ function XlsxGrid({
23591
23971
  return null;
23592
23972
  }
23593
23973
  const localX = clientX - scrollerRect.left;
23594
- for (const column of canvasVisibleColItems) {
23595
- const rect = resolveCanvasColumnHeaderRect(column.actualCol);
23596
- if (!rect) {
23597
- continue;
23974
+ for (const column of canvasColumnHeaderCells) {
23975
+ if (Math.abs(localX - (column.left + column.width)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
23976
+ return { actualCol: column.actualCol, width: column.width };
23598
23977
  }
23599
- if (Math.abs(localX - (rect.left + rect.width)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
23600
- return { actualCol: column.actualCol, width: rect.width };
23978
+ }
23979
+ return null;
23980
+ }, [canResizeHeaders, canvasColumnHeaderCells]);
23981
+ const resolveCanvasColumnHeaderTarget = React4.useCallback((clientX) => {
23982
+ const scrollerRect = scrollRef.current?.getBoundingClientRect();
23983
+ if (!scrollerRect) {
23984
+ return null;
23985
+ }
23986
+ const localX = clientX - scrollerRect.left;
23987
+ for (const column of canvasColumnHeaderCells) {
23988
+ if (localX >= column.left && localX <= column.left + column.width) {
23989
+ return column.actualCol;
23601
23990
  }
23602
23991
  }
23603
23992
  return null;
23604
- }, [canResizeHeaders, canvasVisibleColItems, resolveCanvasColumnHeaderRect]);
23993
+ }, [canvasColumnHeaderCells]);
23605
23994
  const resolveCanvasRowResizeTarget = React4.useCallback((clientY) => {
23606
23995
  if (!canResizeHeaders) {
23607
23996
  return null;
@@ -23611,17 +24000,26 @@ function XlsxGrid({
23611
24000
  return null;
23612
24001
  }
23613
24002
  const localY = clientY - scrollerRect.top;
23614
- for (const row of canvasVisibleRowItems) {
23615
- const rect = resolveCanvasRowHeaderRect(row.actualRow);
23616
- if (!rect) {
23617
- continue;
24003
+ for (const row of canvasRowHeaderCells) {
24004
+ if (Math.abs(localY - (row.top + row.height)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
24005
+ return { actualRow: row.actualRow, height: row.height };
23618
24006
  }
23619
- if (Math.abs(localY - (rect.top + rect.height)) <= CANVAS_RESIZE_HIT_SLOP_PX) {
23620
- return { actualRow: row.actualRow, height: rect.height };
24007
+ }
24008
+ return null;
24009
+ }, [canResizeHeaders, canvasRowHeaderCells]);
24010
+ const resolveCanvasRowHeaderTarget = React4.useCallback((clientY) => {
24011
+ const scrollerRect = scrollRef.current?.getBoundingClientRect();
24012
+ if (!scrollerRect) {
24013
+ return null;
24014
+ }
24015
+ const localY = clientY - scrollerRect.top;
24016
+ for (const row of canvasRowHeaderCells) {
24017
+ if (localY >= row.top && localY <= row.top + row.height) {
24018
+ return row.actualRow;
23621
24019
  }
23622
24020
  }
23623
24021
  return null;
23624
- }, [canResizeHeaders, canvasVisibleRowItems, resolveCanvasRowHeaderRect]);
24022
+ }, [canvasRowHeaderCells]);
23625
24023
  const handleCanvasColumnHeaderPointerMove = React4.useCallback((event) => {
23626
24024
  if (resizeStateRef.current?.type === "column") {
23627
24025
  event.currentTarget.style.cursor = "col-resize";
@@ -23724,17 +24122,17 @@ function XlsxGrid({
23724
24122
  startColumnResize(event.pointerId, resizeTarget.actualCol, resizeTarget.width, event.clientX);
23725
24123
  return;
23726
24124
  }
23727
- const cell = resolvePointerCellFromClient(event.clientX, event.clientY);
23728
- if (!cell) {
24125
+ const actualCol = resolveCanvasColumnHeaderTarget(event.clientX);
24126
+ if (actualCol === null) {
23729
24127
  return;
23730
24128
  }
23731
24129
  event.preventDefault();
23732
24130
  focusGrid();
23733
24131
  const currentSelection = selectionRef.current;
23734
- const anchorCol = event.shiftKey && currentSelection ? currentSelection.start.col : cell.col;
24132
+ const anchorCol = event.shiftKey && currentSelection ? currentSelection.start.col : actualCol;
23735
24133
  const initialRange = normalizeRange2({
23736
24134
  start: { row: firstVisibleRow, col: anchorCol },
23737
- end: { row: lastVisibleRow, col: cell.col }
24135
+ end: { row: lastVisibleRow, col: actualCol }
23738
24136
  });
23739
24137
  const anchorColIndex = colIndexByActual.get(anchorCol);
23740
24138
  if (anchorColIndex === void 0) {
@@ -23744,7 +24142,7 @@ function XlsxGrid({
23744
24142
  event.pointerId,
23745
24143
  { row: firstVisibleRow, col: anchorCol },
23746
24144
  "column",
23747
- { row: firstVisibleRow, col: cell.col },
24145
+ { row: firstVisibleRow, col: actualCol },
23748
24146
  {
23749
24147
  contentScaleX: 1,
23750
24148
  contentScaleY: 1,
@@ -23765,9 +24163,8 @@ function XlsxGrid({
23765
24163
  firstVisibleRow,
23766
24164
  focusGrid,
23767
24165
  lastVisibleRow,
23768
- resolveCanvasColumnHeaderRect,
24166
+ resolveCanvasColumnHeaderTarget,
23769
24167
  resolveCanvasColumnResizeTarget,
23770
- resolvePointerCellFromClient,
23771
24168
  rowPrefixSums,
23772
24169
  startCellSelection
23773
24170
  ]);
@@ -23782,17 +24179,17 @@ function XlsxGrid({
23782
24179
  startRowResize(event.pointerId, resizeTarget.actualRow, resizeTarget.height, event.clientY);
23783
24180
  return;
23784
24181
  }
23785
- const cell = resolvePointerCellFromClient(event.clientX, event.clientY);
23786
- if (!cell) {
24182
+ const actualRow = resolveCanvasRowHeaderTarget(event.clientY);
24183
+ if (actualRow === null) {
23787
24184
  return;
23788
24185
  }
23789
24186
  event.preventDefault();
23790
24187
  focusGrid();
23791
24188
  const currentSelection = selectionRef.current;
23792
- const anchorRow = event.shiftKey && currentSelection ? currentSelection.start.row : cell.row;
24189
+ const anchorRow = event.shiftKey && currentSelection ? currentSelection.start.row : actualRow;
23793
24190
  const initialRange = normalizeRange2({
23794
24191
  start: { row: anchorRow, col: firstVisibleCol },
23795
- end: { row: cell.row, col: lastVisibleCol }
24192
+ end: { row: actualRow, col: lastVisibleCol }
23796
24193
  });
23797
24194
  const anchorRowIndex = rowIndexByActual.get(anchorRow);
23798
24195
  if (anchorRowIndex === void 0) {
@@ -23802,7 +24199,7 @@ function XlsxGrid({
23802
24199
  event.pointerId,
23803
24200
  { row: anchorRow, col: firstVisibleCol },
23804
24201
  "row",
23805
- { row: cell.row, col: firstVisibleCol },
24202
+ { row: actualRow, col: firstVisibleCol },
23806
24203
  {
23807
24204
  contentScaleX: 1,
23808
24205
  contentScaleY: 1,
@@ -23822,8 +24219,8 @@ function XlsxGrid({
23822
24219
  firstVisibleCol,
23823
24220
  focusGrid,
23824
24221
  lastVisibleCol,
24222
+ resolveCanvasRowHeaderTarget,
23825
24223
  resolveCanvasRowResizeTarget,
23826
- resolvePointerCellFromClient,
23827
24224
  rowIndexByActual,
23828
24225
  rowPrefixSums,
23829
24226
  startCellSelection
@@ -23833,7 +24230,7 @@ function XlsxGrid({
23833
24230
  return;
23834
24231
  }
23835
24232
  const dpr = typeof window === "undefined" ? 1 : Math.max(1, window.devicePixelRatio || 1);
23836
- function configureCanvas(canvas, width, height) {
24233
+ function configureCanvas(canvas, width, height, options) {
23837
24234
  if (!canvas) {
23838
24235
  return null;
23839
24236
  }
@@ -23856,7 +24253,9 @@ function XlsxGrid({
23856
24253
  return null;
23857
24254
  }
23858
24255
  context.setTransform(dpr, 0, 0, dpr, 0, 0);
23859
- context.clearRect(0, 0, width, height);
24256
+ if (options?.clear !== false) {
24257
+ context.clearRect(0, 0, width, height);
24258
+ }
23860
24259
  return context;
23861
24260
  }
23862
24261
  const bodyWidth = Math.max(0, drawingViewport.width);
@@ -23888,12 +24287,24 @@ function XlsxGrid({
23888
24287
  rangeSignature,
23889
24288
  rowHeaderWidth,
23890
24289
  rowSignature: nextBodyCanvasSignature.rowSignature,
24290
+ viewportLeft: drawingViewport.left,
24291
+ viewportTop: drawingViewport.top,
23891
24292
  zoomFactor
23892
24293
  };
23893
24294
  const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
23894
24295
  const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
24296
+ const previousPaintedViewport = paintedDrawingViewportRef.current;
23895
24297
  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);
23896
- 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);
24298
+ 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);
24299
+ const canBlitBody = Boolean(
24300
+ 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
24301
+ );
24302
+ const canBlitTopHeader = Boolean(
24303
+ 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
24304
+ );
24305
+ const canBlitLeftHeader = Boolean(
24306
+ 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
24307
+ );
23897
24308
  if (!shouldRepaintBody && !shouldRepaintHeaders) {
23898
24309
  return;
23899
24310
  }
@@ -23905,6 +24316,10 @@ function XlsxGrid({
23905
24316
  const leftBodyCanvasHeight2 = scrollBodyCanvasHeight2;
23906
24317
  const cornerBodyCanvasWidth2 = leftBodyCanvasWidth2;
23907
24318
  const cornerBodyCanvasHeight2 = topBodyCanvasHeight2;
24319
+ const topFrozenHeaderCanvasWidth2 = leftBodyCanvasWidth2;
24320
+ const topScrollHeaderCanvasWidth2 = scrollBodyCanvasWidth2;
24321
+ const leftFrozenHeaderCanvasHeight2 = topBodyCanvasHeight2;
24322
+ const leftScrollHeaderCanvasHeight2 = scrollBodyCanvasHeight2;
23908
24323
  const paneBounds = {
23909
24324
  corner: {
23910
24325
  height: cornerBodyCanvasHeight2,
@@ -23932,29 +24347,50 @@ function XlsxGrid({
23932
24347
  }
23933
24348
  };
23934
24349
  const bodyContexts = shouldRepaintBody ? {
23935
- corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth2, cornerBodyCanvasHeight2),
23936
- left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth2, leftBodyCanvasHeight2),
23937
- scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth2, scrollBodyCanvasHeight2),
23938
- top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth2, topBodyCanvasHeight2)
24350
+ corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth2, cornerBodyCanvasHeight2, { clear: !canBlitBody }),
24351
+ left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth2, leftBodyCanvasHeight2, { clear: !canBlitBody }),
24352
+ scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth2, scrollBodyCanvasHeight2, { clear: !canBlitBody }),
24353
+ top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth2, topBodyCanvasHeight2, { clear: !canBlitBody })
23939
24354
  } : {
23940
24355
  corner: null,
23941
24356
  left: null,
23942
24357
  scroll: null,
23943
24358
  top: null
23944
24359
  };
23945
- const topHeaderContext = shouldRepaintHeaders ? configureCanvas(topHeaderCanvasRef.current, bodyWidth, headerHeight) : null;
23946
- const leftHeaderContext = shouldRepaintHeaders ? configureCanvas(leftHeaderCanvasRef.current, rowHeaderWidth, bodyHeight) : null;
24360
+ const topHeaderContexts = shouldRepaintHeaders ? {
24361
+ frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth2, headerHeight),
24362
+ scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth2, headerHeight, { clear: !canBlitTopHeader })
24363
+ } : {
24364
+ frozen: null,
24365
+ scroll: null
24366
+ };
24367
+ const leftHeaderContexts = shouldRepaintHeaders ? {
24368
+ frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight2),
24369
+ scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight2, { clear: !canBlitLeftHeader })
24370
+ } : {
24371
+ frozen: null,
24372
+ scroll: null
24373
+ };
23947
24374
  const cornerContext = shouldRepaintHeaders ? configureCanvas(cornerHeaderCanvasRef.current, rowHeaderWidth, headerHeight) : null;
23948
- if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!topHeaderContext || !leftHeaderContext || !cornerContext)) {
24375
+ 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)) {
23949
24376
  return;
23950
24377
  }
23951
24378
  const showGridLines = activeSheet?.showGridLines ?? true;
24379
+ const sheetSurface = resolveSheetSurface(activeSheet, palette);
23952
24380
  const deferredSpillTextsByPane = {
23953
24381
  corner: [],
23954
24382
  left: [],
23955
24383
  scroll: [],
23956
24384
  top: []
23957
24385
  };
24386
+ const bodyDirtyRectsByPane = {
24387
+ corner: [],
24388
+ left: [],
24389
+ scroll: [],
24390
+ top: []
24391
+ };
24392
+ let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth2, headerHeight);
24393
+ let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight2);
23958
24394
  const cellPaneOrder = ["scroll", "top", "left", "corner"];
23959
24395
  if (shouldRepaintBody) {
23960
24396
  for (const pane of Object.keys(bodyContexts)) {
@@ -23963,14 +24399,44 @@ function XlsxGrid({
23963
24399
  if (!context || bounds.width <= 0 || bounds.height <= 0) {
23964
24400
  continue;
23965
24401
  }
23966
- context.fillStyle = resolveSheetSurface(activeSheet, palette);
23967
- context.fillRect(0, 0, bounds.width, bounds.height);
24402
+ let dirtyRects = buildFullCanvasDirtyRect(bounds.width, bounds.height);
24403
+ if (canBlitBody) {
24404
+ const bufferCanvas = getBodyBlitBufferCanvas(pane);
24405
+ const deltaX = pane === "scroll" || pane === "top" ? drawingViewport.left - previousPaintedViewport.left : 0;
24406
+ const deltaY = pane === "scroll" || pane === "left" ? drawingViewport.top - previousPaintedViewport.top : 0;
24407
+ if (bufferCanvas) {
24408
+ const blittedDirtyRects = blitCanvasWithScrollDelta(
24409
+ pane === "corner" ? cornerBodyCanvasRef.current : pane === "left" ? leftBodyCanvasRef.current : pane === "top" ? topBodyCanvasRef.current : scrollBodyCanvasRef.current,
24410
+ context,
24411
+ bufferCanvas,
24412
+ dpr,
24413
+ bounds.width,
24414
+ bounds.height,
24415
+ deltaX,
24416
+ deltaY
24417
+ );
24418
+ if (blittedDirtyRects) {
24419
+ dirtyRects = blittedDirtyRects;
24420
+ }
24421
+ }
24422
+ } else {
24423
+ context.clearRect(0, 0, bounds.width, bounds.height);
24424
+ }
24425
+ bodyDirtyRectsByPane[pane] = dirtyRects;
24426
+ if (dirtyRects.length === 0) {
24427
+ continue;
24428
+ }
24429
+ context.fillStyle = sheetSurface;
24430
+ for (const dirtyRect of dirtyRects) {
24431
+ context.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
24432
+ }
23968
24433
  }
23969
24434
  for (const pane of cellPaneOrder) {
23970
24435
  const paneContext = bodyContexts[pane];
23971
24436
  const paneBoundsForCell = paneBounds[pane];
24437
+ const paneDirtyRects = bodyDirtyRectsByPane[pane];
23972
24438
  const paneAxisItems = canvasPaneAxisItems[pane];
23973
- if (!paneContext || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
24439
+ if (!paneContext || paneDirtyRects.length === 0 || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
23974
24440
  continue;
23975
24441
  }
23976
24442
  const drawnMergedAnchorKeys = /* @__PURE__ */ new Set();
@@ -24010,11 +24476,14 @@ function XlsxGrid({
24010
24476
  if (localRect.left + drawableWidth < 0 || localRect.top + localRect.height < 0 || localRect.left > paneBoundsForCell.width || localRect.top > paneBoundsForCell.height) {
24011
24477
  continue;
24012
24478
  }
24479
+ if (!intersectsCanvasDirtyRects(localRect.left, localRect.top, drawableWidth, localRect.height, paneDirtyRects)) {
24480
+ continue;
24481
+ }
24013
24482
  const cellStyle = cellData.style;
24014
24483
  const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellStyle);
24015
- const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : resolveSheetSurface(activeSheet, palette));
24484
+ const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : sheetSurface);
24016
24485
  const gradientFill = !cellData.conditionalColorScale && typeof cellStyle.backgroundImage === "string" ? resolveCanvasGradientFill(paneContext, localRect, cellStyle.backgroundImage) : null;
24017
- const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== resolveSheetSurface(activeSheet, palette);
24486
+ const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== sheetSurface;
24018
24487
  paneContext.fillStyle = gradientFill ?? fillColor;
24019
24488
  paneContext.fillRect(localRect.left, localRect.top, localRect.width, localRect.height);
24020
24489
  if (cellData.chartHighlight) {
@@ -24368,55 +24837,139 @@ function XlsxGrid({
24368
24837
  }
24369
24838
  }
24370
24839
  }
24371
- if (shouldRepaintHeaders && topHeaderContext && leftHeaderContext && cornerContext) {
24372
- topHeaderContext.fillStyle = palette.headerSurface;
24373
- topHeaderContext.fillRect(0, 0, bodyWidth, headerHeight);
24374
- topHeaderContext.strokeStyle = palette.border;
24375
- topHeaderContext.lineWidth = 1;
24376
- for (const colItem of canvasVisibleColItems) {
24377
- const rect = resolveCanvasColumnHeaderRect(colItem.actualCol);
24378
- if (!rect || rect.left + rect.width < displayRowHeaderWidth || rect.left > bodyWidth) {
24840
+ if (shouldRepaintHeaders && cornerContext) {
24841
+ const topFrozenHeaderContext = topHeaderContexts.frozen;
24842
+ const topScrollHeaderContext = topHeaderContexts.scroll;
24843
+ const leftFrozenHeaderContext = leftHeaderContexts.frozen;
24844
+ const leftScrollHeaderContext = leftHeaderContexts.scroll;
24845
+ if (canBlitTopHeader) {
24846
+ const bufferCanvas = getHeaderBlitBufferCanvas("top");
24847
+ if (bufferCanvas && topScrollHeaderContext && topScrollHeaderCanvasRef.current) {
24848
+ const blittedDirtyRects = blitCanvasWithScrollDelta(
24849
+ topScrollHeaderCanvasRef.current,
24850
+ topScrollHeaderContext,
24851
+ bufferCanvas,
24852
+ dpr,
24853
+ topScrollHeaderCanvasWidth2,
24854
+ headerHeight,
24855
+ drawingViewport.left - previousPaintedViewport.left,
24856
+ 0
24857
+ );
24858
+ if (blittedDirtyRects) {
24859
+ topScrollHeaderDirtyRects = blittedDirtyRects;
24860
+ }
24861
+ }
24862
+ }
24863
+ if (canBlitLeftHeader) {
24864
+ const bufferCanvas = getHeaderBlitBufferCanvas("left");
24865
+ if (bufferCanvas && leftScrollHeaderContext && leftScrollHeaderCanvasRef.current) {
24866
+ const blittedDirtyRects = blitCanvasWithScrollDelta(
24867
+ leftScrollHeaderCanvasRef.current,
24868
+ leftScrollHeaderContext,
24869
+ bufferCanvas,
24870
+ dpr,
24871
+ rowHeaderWidth,
24872
+ leftScrollHeaderCanvasHeight2,
24873
+ 0,
24874
+ drawingViewport.top - previousPaintedViewport.top
24875
+ );
24876
+ if (blittedDirtyRects) {
24877
+ leftScrollHeaderDirtyRects = blittedDirtyRects;
24878
+ }
24879
+ }
24880
+ }
24881
+ if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth2 > 0) {
24882
+ topFrozenHeaderContext.fillStyle = palette.headerSurface;
24883
+ topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth2, headerHeight);
24884
+ }
24885
+ if (topScrollHeaderContext && topScrollHeaderCanvasWidth2 > 0) {
24886
+ topScrollHeaderContext.fillStyle = palette.headerSurface;
24887
+ for (const dirtyRect of topScrollHeaderDirtyRects) {
24888
+ topScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
24889
+ }
24890
+ }
24891
+ if (topFrozenHeaderContext) {
24892
+ topFrozenHeaderContext.strokeStyle = palette.border;
24893
+ topFrozenHeaderContext.lineWidth = 1;
24894
+ }
24895
+ if (topScrollHeaderContext) {
24896
+ topScrollHeaderContext.strokeStyle = palette.border;
24897
+ topScrollHeaderContext.lineWidth = 1;
24898
+ }
24899
+ for (const column of canvasColumnHeaderCells) {
24900
+ const paneContext = column.isFrozen ? topFrozenHeaderContext : topScrollHeaderContext;
24901
+ if (!paneContext) {
24902
+ continue;
24903
+ }
24904
+ if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth2 : topScrollHeaderCanvasWidth2)) {
24905
+ continue;
24906
+ }
24907
+ if (!column.isFrozen && !intersectsCanvasDirtyRects(column.localLeft, 0, column.width, column.height, topScrollHeaderDirtyRects)) {
24908
+ continue;
24909
+ }
24910
+ const selected = normalizedVisibleRange && column.actualCol >= normalizedVisibleRange.start.col && column.actualCol <= normalizedVisibleRange.end.col;
24911
+ paneContext.fillStyle = selected ? selectionHeaderSurface : palette.headerSurface;
24912
+ paneContext.fillRect(column.localLeft, 0, column.width, column.height);
24913
+ paneContext.strokeStyle = palette.border;
24914
+ paneContext.beginPath();
24915
+ paneContext.moveTo(column.localLeft + column.width - 0.5, 0);
24916
+ paneContext.lineTo(column.localLeft + column.width - 0.5, column.height);
24917
+ paneContext.moveTo(column.localLeft, column.height - 0.5);
24918
+ paneContext.lineTo(column.localLeft + column.width, column.height - 0.5);
24919
+ paneContext.stroke();
24920
+ paneContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
24921
+ paneContext.fillStyle = palette.mutedText;
24922
+ paneContext.textAlign = "center";
24923
+ paneContext.textBaseline = "middle";
24924
+ paneContext.fillText(
24925
+ columnLabel2(column.actualCol),
24926
+ column.localLeft + column.width / 2,
24927
+ column.height / 2
24928
+ );
24929
+ }
24930
+ if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight2 > 0) {
24931
+ leftFrozenHeaderContext.fillStyle = palette.rowHeaderSurface;
24932
+ leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight2);
24933
+ }
24934
+ if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight2 > 0) {
24935
+ leftScrollHeaderContext.fillStyle = palette.rowHeaderSurface;
24936
+ for (const dirtyRect of leftScrollHeaderDirtyRects) {
24937
+ leftScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
24938
+ }
24939
+ }
24940
+ if (leftFrozenHeaderContext) {
24941
+ leftFrozenHeaderContext.strokeStyle = palette.border;
24942
+ leftFrozenHeaderContext.lineWidth = 1;
24943
+ }
24944
+ if (leftScrollHeaderContext) {
24945
+ leftScrollHeaderContext.strokeStyle = palette.border;
24946
+ leftScrollHeaderContext.lineWidth = 1;
24947
+ }
24948
+ for (const row of canvasRowHeaderCells) {
24949
+ const paneContext = row.isFrozen ? leftFrozenHeaderContext : leftScrollHeaderContext;
24950
+ if (!paneContext) {
24951
+ continue;
24952
+ }
24953
+ if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight2 : leftScrollHeaderCanvasHeight2)) {
24379
24954
  continue;
24380
24955
  }
24381
- const selected = normalizedVisibleRange && colItem.actualCol >= normalizedVisibleRange.start.col && colItem.actualCol <= normalizedVisibleRange.end.col;
24382
- topHeaderContext.fillStyle = selected ? selectionHeaderSurface : palette.headerSurface;
24383
- topHeaderContext.fillRect(rect.left, 0, rect.width, headerHeight);
24384
- topHeaderContext.strokeStyle = palette.border;
24385
- topHeaderContext.beginPath();
24386
- topHeaderContext.moveTo(rect.left + rect.width - 0.5, 0);
24387
- topHeaderContext.lineTo(rect.left + rect.width - 0.5, headerHeight);
24388
- topHeaderContext.moveTo(rect.left, headerHeight - 0.5);
24389
- topHeaderContext.lineTo(rect.left + rect.width, headerHeight - 0.5);
24390
- topHeaderContext.stroke();
24391
- topHeaderContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
24392
- topHeaderContext.fillStyle = palette.mutedText;
24393
- topHeaderContext.textAlign = "center";
24394
- topHeaderContext.textBaseline = "middle";
24395
- topHeaderContext.fillText(columnLabel2(colItem.actualCol), rect.left + rect.width / 2, headerHeight / 2);
24396
- }
24397
- leftHeaderContext.fillStyle = palette.rowHeaderSurface;
24398
- leftHeaderContext.fillRect(0, 0, rowHeaderWidth, bodyHeight);
24399
- leftHeaderContext.strokeStyle = palette.border;
24400
- leftHeaderContext.lineWidth = 1;
24401
- for (const rowItem of canvasVisibleRowItems) {
24402
- const rect = resolveCanvasRowHeaderRect(rowItem.actualRow);
24403
- if (!rect || rect.top + rect.height < displayHeaderHeight || rect.top > bodyHeight) {
24956
+ if (!row.isFrozen && !intersectsCanvasDirtyRects(0, row.localTop, rowHeaderWidth, row.height, leftScrollHeaderDirtyRects)) {
24404
24957
  continue;
24405
24958
  }
24406
- const selected = normalizedVisibleRange && rowItem.actualRow >= normalizedVisibleRange.start.row && rowItem.actualRow <= normalizedVisibleRange.end.row;
24407
- leftHeaderContext.fillStyle = selected ? selectionHeaderSurface : palette.rowHeaderSurface;
24408
- leftHeaderContext.fillRect(0, rect.top, rowHeaderWidth, rect.height);
24409
- leftHeaderContext.beginPath();
24410
- leftHeaderContext.moveTo(0, rect.top + rect.height - 0.5);
24411
- leftHeaderContext.lineTo(rowHeaderWidth, rect.top + rect.height - 0.5);
24412
- leftHeaderContext.moveTo(rowHeaderWidth - 0.5, rect.top);
24413
- leftHeaderContext.lineTo(rowHeaderWidth - 0.5, rect.top + rect.height);
24414
- leftHeaderContext.stroke();
24415
- leftHeaderContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
24416
- leftHeaderContext.fillStyle = palette.mutedText;
24417
- leftHeaderContext.textAlign = "center";
24418
- leftHeaderContext.textBaseline = "middle";
24419
- leftHeaderContext.fillText(`${rowItem.actualRow + 1}`, rowHeaderWidth / 2, rect.top + rect.height / 2);
24959
+ const selected = normalizedVisibleRange && row.actualRow >= normalizedVisibleRange.start.row && row.actualRow <= normalizedVisibleRange.end.row;
24960
+ paneContext.fillStyle = selected ? selectionHeaderSurface : palette.rowHeaderSurface;
24961
+ paneContext.fillRect(0, row.localTop, rowHeaderWidth, row.height);
24962
+ paneContext.beginPath();
24963
+ paneContext.moveTo(0, row.localTop + row.height - 0.5);
24964
+ paneContext.lineTo(rowHeaderWidth, row.localTop + row.height - 0.5);
24965
+ paneContext.moveTo(rowHeaderWidth - 0.5, row.localTop);
24966
+ paneContext.lineTo(rowHeaderWidth - 0.5, row.localTop + row.height);
24967
+ paneContext.stroke();
24968
+ paneContext.font = `600 ${11 * zoomFactor}px ui-sans-serif, system-ui, sans-serif`;
24969
+ paneContext.fillStyle = palette.mutedText;
24970
+ paneContext.textAlign = "center";
24971
+ paneContext.textBaseline = "middle";
24972
+ paneContext.fillText(`${row.actualRow + 1}`, rowHeaderWidth / 2, row.localTop + row.height / 2);
24420
24973
  }
24421
24974
  cornerContext.fillStyle = palette.rowHeaderSurface;
24422
24975
  cornerContext.fillRect(0, 0, rowHeaderWidth, headerHeight);
@@ -24440,7 +24993,9 @@ function XlsxGrid({
24440
24993
  }, [
24441
24994
  activeSheet,
24442
24995
  applyCanvasViewportCompensation,
24996
+ canvasColumnHeaderCells,
24443
24997
  canvasPaneAxisItems,
24998
+ canvasRowHeaderCells,
24444
24999
  canvasVisibleColItems,
24445
25000
  canvasVisibleRowItems,
24446
25001
  colIndexByActual,
@@ -24455,11 +25010,13 @@ function XlsxGrid({
24455
25010
  drawingViewport.height,
24456
25011
  drawingViewport.width,
24457
25012
  experimentalCanvas,
25013
+ frozenPaneBottom,
25014
+ frozenPaneRight,
24458
25015
  getCellData,
25016
+ getBodyBlitBufferCanvas,
25017
+ getHeaderBlitBufferCanvas,
24459
25018
  palette,
24460
25019
  resolveCellDisplayRect,
24461
- resolveCanvasColumnHeaderRect,
24462
- resolveCanvasRowHeaderRect,
24463
25020
  resolveMergeAnchorCell,
24464
25021
  resizeGuide,
24465
25022
  rowIndexByActual,
@@ -24753,6 +25310,10 @@ function XlsxGrid({
24753
25310
  const leftBodyCanvasHeight = scrollBodyCanvasHeight;
24754
25311
  const cornerBodyCanvasWidth = leftBodyCanvasWidth;
24755
25312
  const cornerBodyCanvasHeight = topBodyCanvasHeight;
25313
+ const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
25314
+ const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
25315
+ const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
25316
+ const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
24756
25317
  const canvasBodyBaseStyle = {
24757
25318
  cursor: "cell",
24758
25319
  pointerEvents: "auto",
@@ -24788,22 +25349,38 @@ function XlsxGrid({
24788
25349
  top: displayHeaderHeight,
24789
25350
  zIndex: 31
24790
25351
  };
24791
- const canvasTopHeaderStyle = {
25352
+ const canvasHeaderBaseStyle = {
24792
25353
  cursor: "default",
24793
- display: drawingViewport.width > 0 && drawingViewport.height > 0 ? "block" : "none",
24794
- left: 0,
24795
25354
  pointerEvents: "auto",
24796
25355
  position: "absolute",
25356
+ transformOrigin: "0 0"
25357
+ };
25358
+ const canvasTopFrozenHeaderStyle = {
25359
+ ...canvasHeaderBaseStyle,
25360
+ display: topFrozenHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
25361
+ left: displayRowHeaderWidth,
25362
+ top: 0,
25363
+ zIndex: canvasHeaderOverlayZIndex + 1
25364
+ };
25365
+ const canvasTopScrollHeaderStyle = {
25366
+ ...canvasHeaderBaseStyle,
25367
+ display: topScrollHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
25368
+ left: frozenPaneRight,
24797
25369
  top: 0,
24798
25370
  zIndex: canvasHeaderOverlayZIndex
24799
25371
  };
24800
- const canvasLeftHeaderStyle = {
24801
- cursor: "default",
24802
- display: drawingViewport.width > 0 && drawingViewport.height > 0 ? "block" : "none",
25372
+ const canvasLeftFrozenHeaderStyle = {
25373
+ ...canvasHeaderBaseStyle,
25374
+ display: leftFrozenHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
24803
25375
  left: 0,
24804
- pointerEvents: "auto",
24805
- position: "absolute",
24806
- top: 0,
25376
+ top: displayHeaderHeight,
25377
+ zIndex: canvasHeaderOverlayZIndex + 1
25378
+ };
25379
+ const canvasLeftScrollHeaderStyle = {
25380
+ ...canvasHeaderBaseStyle,
25381
+ display: leftScrollHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
25382
+ left: 0,
25383
+ top: frozenPaneBottom,
24807
25384
  zIndex: canvasHeaderOverlayZIndex
24808
25385
  };
24809
25386
  const canvasCornerHeaderStyle = {
@@ -24812,6 +25389,7 @@ function XlsxGrid({
24812
25389
  pointerEvents: "none",
24813
25390
  position: "absolute",
24814
25391
  top: 0,
25392
+ transformOrigin: "0 0",
24815
25393
  zIndex: canvasHeaderOverlayZIndex + 1
24816
25394
  };
24817
25395
  const editingOverlayRect = experimentalCanvas && editingCell ? resolveCellDisplayRect(editingCell) : null;
@@ -25958,7 +26536,7 @@ function XlsxGrid({
25958
26536
  pointerEvents: "none",
25959
26537
  position: "absolute",
25960
26538
  top: 0,
25961
- transform: `translate(${-drawingViewport.left}px, ${-drawingViewport.top}px)`,
26539
+ transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
25962
26540
  width: totalWidth
25963
26541
  },
25964
26542
  children: paneDrawingNodes.scroll
@@ -25973,7 +26551,7 @@ function XlsxGrid({
25973
26551
  pointerEvents: "none",
25974
26552
  position: "absolute",
25975
26553
  top: 0,
25976
- transform: `translate(${-drawingViewport.left}px, ${-displayHeaderHeight}px)`,
26554
+ transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-displayHeaderHeight}px)`,
25977
26555
  width: totalWidth
25978
26556
  },
25979
26557
  children: paneDrawingNodes.top
@@ -25988,7 +26566,7 @@ function XlsxGrid({
25988
26566
  pointerEvents: "none",
25989
26567
  position: "absolute",
25990
26568
  top: 0,
25991
- transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top}px)`,
26569
+ transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
25992
26570
  width: totalWidth
25993
26571
  },
25994
26572
  children: paneDrawingNodes.left
@@ -26015,21 +26593,41 @@ function XlsxGrid({
26015
26593
  /* @__PURE__ */ jsx3(
26016
26594
  "canvas",
26017
26595
  {
26018
- ref: topHeaderCanvasRef,
26596
+ ref: topFrozenHeaderCanvasRef,
26597
+ onPointerLeave: handleCanvasHeaderPointerLeave,
26598
+ onPointerMove: handleCanvasColumnHeaderPointerMove,
26599
+ onPointerDown: handleCanvasColumnHeaderPointerDown,
26600
+ style: canvasTopFrozenHeaderStyle
26601
+ }
26602
+ ),
26603
+ /* @__PURE__ */ jsx3(
26604
+ "canvas",
26605
+ {
26606
+ ref: topScrollHeaderCanvasRef,
26019
26607
  onPointerLeave: handleCanvasHeaderPointerLeave,
26020
26608
  onPointerMove: handleCanvasColumnHeaderPointerMove,
26021
26609
  onPointerDown: handleCanvasColumnHeaderPointerDown,
26022
- style: canvasTopHeaderStyle
26610
+ style: canvasTopScrollHeaderStyle
26023
26611
  }
26024
26612
  ),
26025
26613
  /* @__PURE__ */ jsx3(
26026
26614
  "canvas",
26027
26615
  {
26028
- ref: leftHeaderCanvasRef,
26616
+ ref: leftFrozenHeaderCanvasRef,
26029
26617
  onPointerLeave: handleCanvasHeaderPointerLeave,
26030
26618
  onPointerMove: handleCanvasRowHeaderPointerMove,
26031
26619
  onPointerDown: handleCanvasRowHeaderPointerDown,
26032
- style: canvasLeftHeaderStyle
26620
+ style: canvasLeftFrozenHeaderStyle
26621
+ }
26622
+ ),
26623
+ /* @__PURE__ */ jsx3(
26624
+ "canvas",
26625
+ {
26626
+ ref: leftScrollHeaderCanvasRef,
26627
+ onPointerLeave: handleCanvasHeaderPointerLeave,
26628
+ onPointerMove: handleCanvasRowHeaderPointerMove,
26629
+ onPointerDown: handleCanvasRowHeaderPointerDown,
26630
+ style: canvasLeftScrollHeaderStyle
26033
26631
  }
26034
26632
  ),
26035
26633
  /* @__PURE__ */ jsx3("canvas", { ref: cornerHeaderCanvasRef, style: canvasCornerHeaderStyle })
@@ -26339,7 +26937,7 @@ function XlsxGrid({
26339
26937
  }
26340
26938
  }
26341
26939
  ) : null,
26342
- openTableMenuState ? /* @__PURE__ */ jsx3(
26940
+ !renderTableHeaderMenu && openTableMenuState ? /* @__PURE__ */ jsx3(
26343
26941
  "div",
26344
26942
  {
26345
26943
  ref: tableMenuRef,
@@ -26350,22 +26948,23 @@ function XlsxGrid({
26350
26948
  top: openTableMenuState.top,
26351
26949
  zIndex: 50
26352
26950
  },
26353
- children: renderTableHeaderMenu ? renderTableHeaderMenu({
26354
- close: () => setOpenTableMenu(null),
26355
- column: openTableMenuState.column,
26356
- direction: sortState && sortState.tableName === openTableMenuState.table.name && sortState.columnIndex === openTableMenuState.column.index ? sortState.direction : null,
26357
- sortAscending: () => sortTable(openTableMenuState.table.name, openTableMenuState.column.index, "ascending"),
26358
- sortDescending: () => sortTable(openTableMenuState.table.name, openTableMenuState.column.index, "descending"),
26359
- table: openTableMenuState.table
26360
- }) : /* @__PURE__ */ jsx3(
26951
+ children: /* @__PURE__ */ jsx3(
26361
26952
  DefaultTableHeaderMenu,
26362
26953
  {
26363
- close: () => setOpenTableMenu(null),
26954
+ cell: { col: openTableMenuState.column.index + openTableMenuState.table.start.col, row: openTableMenuState.table.start.row },
26364
26955
  column: openTableMenuState.column,
26365
26956
  direction: sortState && sortState.tableName === openTableMenuState.table.name && sortState.columnIndex === openTableMenuState.column.index ? sortState.direction : null,
26366
- sortAscending: () => sortTable(openTableMenuState.table.name, openTableMenuState.column.index, "ascending"),
26367
- sortDescending: () => sortTable(openTableMenuState.table.name, openTableMenuState.column.index, "descending"),
26368
- table: openTableMenuState.table
26957
+ sortAscending: () => {
26958
+ sortTable(openTableMenuState.table.name, openTableMenuState.column.index, "ascending");
26959
+ setOpenTableMenu(null);
26960
+ },
26961
+ sortDescending: () => {
26962
+ sortTable(openTableMenuState.table.name, openTableMenuState.column.index, "descending");
26963
+ setOpenTableMenu(null);
26964
+ },
26965
+ table: openTableMenuState.table,
26966
+ triggerIcon: "\u25BE",
26967
+ triggerProps: { type: "button" }
26369
26968
  }
26370
26969
  )
26371
26970
  }
@@ -26796,6 +27395,476 @@ function useXlsxViewerCharts() {
26796
27395
  ]
26797
27396
  );
26798
27397
  }
27398
+ function useXlsxViewerThumbnails(options = {}) {
27399
+ const { workbook, sheets } = useXlsxViewer();
27400
+ const { isDark } = React4.useContext(ViewerAppearanceContext);
27401
+ const palette = useViewerPalette(isDark);
27402
+ const includeHeaders = options.includeHeaders ?? true;
27403
+ const resolution = options.resolution;
27404
+ const thumbnails = React4.useMemo(() => {
27405
+ return sheets.map((sheet, sheetIndex) => {
27406
+ const worksheet = workbook?.getSheet(sheet.workbookSheetIndex) ?? null;
27407
+ const showGridLines = sheet.showGridLines ?? true;
27408
+ const hiddenRowSet = new Set(sheet.hiddenRows ?? []);
27409
+ const hiddenColSet = new Set(sheet.hiddenCols ?? []);
27410
+ const visibleRows = buildVisibleAxisIndices(
27411
+ sheet.visibleRows ?? [],
27412
+ Math.max(THUMBNAIL_FALLBACK_ROWS, (sheet.maxUsedRow ?? -1) + THUMBNAIL_FALLBACK_ROWS + 1),
27413
+ sheet.maxUsedRow ?? -1,
27414
+ hiddenRowSet
27415
+ );
27416
+ const visibleCols = buildVisibleAxisIndices(
27417
+ sheet.visibleCols ?? [],
27418
+ Math.max(THUMBNAIL_FALLBACK_COLS, (sheet.maxUsedCol ?? -1) + THUMBNAIL_FALLBACK_COLS + 1),
27419
+ sheet.maxUsedCol ?? -1,
27420
+ hiddenColSet
27421
+ );
27422
+ const resolveColumnWidthPx = (actualCol) => {
27423
+ if (worksheet && !worksheet.isColumnHidden(actualCol)) {
27424
+ const width = worksheet.getColumnWidth(actualCol);
27425
+ if (width !== void 0 && width !== null) {
27426
+ return Math.max(
27427
+ resolveRenderedSheetAxisPixels(resolveSheetColumnWidthPixels(width, sheet.columnWidthCharacterWidthPx), showGridLines),
27428
+ DEFAULT_COL_WIDTH2 / 2
27429
+ );
27430
+ }
27431
+ }
27432
+ return Math.max(
27433
+ resolveRenderedSheetAxisPixels(
27434
+ sheet.colWidthOverridesPx[actualCol] ?? sheet.defaultColWidthPx ?? DEFAULT_COL_WIDTH2,
27435
+ showGridLines
27436
+ ),
27437
+ DEFAULT_COL_WIDTH2 / 2
27438
+ );
27439
+ };
27440
+ const resolveRowHeightPx = (actualRow) => {
27441
+ if (worksheet && !worksheet.isRowHidden(actualRow)) {
27442
+ const height = worksheet.getRowHeight(actualRow);
27443
+ if (height !== void 0 && height !== null) {
27444
+ return Math.max(
27445
+ resolveRenderedSheetAxisPixels(resolveSheetRowHeightPixels(height), showGridLines),
27446
+ DEFAULT_ROW_HEIGHT2 / 1.5
27447
+ );
27448
+ }
27449
+ }
27450
+ return Math.max(
27451
+ resolveRenderedSheetAxisPixels(
27452
+ sheet.rowHeightOverridesPx[actualRow] ?? sheet.defaultRowHeightPx ?? DEFAULT_ROW_HEIGHT2,
27453
+ showGridLines
27454
+ ),
27455
+ DEFAULT_ROW_HEIGHT2 / 1.5
27456
+ );
27457
+ };
27458
+ const previewRows = resolveThumbnailAxisIndices({
27459
+ fallbackCount: THUMBNAIL_FALLBACK_ROWS,
27460
+ getSizePx: resolveRowHeightPx,
27461
+ maxCount: THUMBNAIL_MAX_ROWS,
27462
+ maxLogicalPixels: THUMBNAIL_MAX_SOURCE_HEIGHT_PX,
27463
+ maxUsedIndex: sheet.maxUsedRow ?? -1,
27464
+ minUsedIndex: Math.max(0, sheet.minUsedRow ?? 0),
27465
+ precomputed: visibleRows
27466
+ });
27467
+ const previewCols = resolveThumbnailAxisIndices({
27468
+ fallbackCount: THUMBNAIL_FALLBACK_COLS,
27469
+ getSizePx: resolveColumnWidthPx,
27470
+ maxCount: THUMBNAIL_MAX_COLS,
27471
+ maxLogicalPixels: THUMBNAIL_MAX_SOURCE_WIDTH_PX,
27472
+ maxUsedIndex: sheet.maxUsedCol ?? -1,
27473
+ minUsedIndex: Math.max(0, sheet.minUsedCol ?? 0),
27474
+ precomputed: visibleCols
27475
+ });
27476
+ const rowAxis = buildThumbnailAxisItems(previewRows, resolveRowHeightPx);
27477
+ const colAxis = buildThumbnailAxisItems(previewCols, resolveColumnWidthPx);
27478
+ const rowItemByActual = new Map(rowAxis.items.map((item) => [item.actualIndex, item]));
27479
+ const colItemByActual = new Map(colAxis.items.map((item) => [item.actualIndex, item]));
27480
+ const headerHeight = includeHeaders ? HEADER_HEIGHT : 0;
27481
+ const rowHeaderWidth = includeHeaders ? ROW_HEADER_WIDTH : 0;
27482
+ const sourceWidth = rowHeaderWidth + colAxis.totalSize;
27483
+ const sourceHeight = headerHeight + rowAxis.totalSize;
27484
+ const outputSize = resolveThumbnailOutputSize(sourceWidth, sourceHeight, resolution);
27485
+ const sourceRange = {
27486
+ start: {
27487
+ col: previewCols[0] ?? 0,
27488
+ row: previewRows[0] ?? 0
27489
+ },
27490
+ end: {
27491
+ col: previewCols[previewCols.length - 1] ?? 0,
27492
+ row: previewRows[previewRows.length - 1] ?? 0
27493
+ }
27494
+ };
27495
+ const sparklineByCell = new Map(
27496
+ (sheet.sparklines ?? []).map((sparkline) => [`${sparkline.target.row}:${sparkline.target.col}`, sparkline])
27497
+ );
27498
+ const paint = (canvas) => {
27499
+ if (!canvas) {
27500
+ return false;
27501
+ }
27502
+ const context = canvas.getContext("2d");
27503
+ if (!context) {
27504
+ return false;
27505
+ }
27506
+ const dpr = typeof window === "undefined" ? 1 : Math.max(1, window.devicePixelRatio || 1);
27507
+ const outputWidth = outputSize.width;
27508
+ const outputHeight = outputSize.height;
27509
+ const scale = outputSize.scale;
27510
+ const deviceWidth = Math.max(1, Math.round(outputWidth * dpr));
27511
+ const deviceHeight = Math.max(1, Math.round(outputHeight * dpr));
27512
+ if (canvas.width !== deviceWidth) {
27513
+ canvas.width = deviceWidth;
27514
+ }
27515
+ if (canvas.height !== deviceHeight) {
27516
+ canvas.height = deviceHeight;
27517
+ }
27518
+ if (canvas.style.width !== `${outputWidth}px`) {
27519
+ canvas.style.width = `${outputWidth}px`;
27520
+ }
27521
+ if (canvas.style.height !== `${outputHeight}px`) {
27522
+ canvas.style.height = `${outputHeight}px`;
27523
+ }
27524
+ context.setTransform(dpr * scale, 0, 0, dpr * scale, 0, 0);
27525
+ context.clearRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
27526
+ context.fillStyle = palette.canvas;
27527
+ context.fillRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
27528
+ context.fillStyle = resolveSheetSurface(sheet, palette);
27529
+ context.fillRect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
27530
+ if (includeHeaders) {
27531
+ context.fillStyle = palette.headerSurface;
27532
+ context.fillRect(rowHeaderWidth, 0, Math.max(1, colAxis.totalSize), headerHeight);
27533
+ context.fillStyle = palette.rowHeaderSurface;
27534
+ context.fillRect(0, headerHeight, rowHeaderWidth, Math.max(1, rowAxis.totalSize));
27535
+ context.fillRect(0, 0, rowHeaderWidth, headerHeight);
27536
+ }
27537
+ if (!worksheet) {
27538
+ return true;
27539
+ }
27540
+ const conditionalFormatMetricsCache = /* @__PURE__ */ new Map();
27541
+ const cellRenderCache = /* @__PURE__ */ new Map();
27542
+ const getCellData = (row, col) => {
27543
+ const cacheKey = `${row}:${col}`;
27544
+ const cached = cellRenderCache.get(cacheKey);
27545
+ if (cached) {
27546
+ return cached;
27547
+ }
27548
+ if (worksheet.isMergedSecondary(row, col)) {
27549
+ const mergedSecondaryData = {
27550
+ isMergedSecondary: true,
27551
+ style: {},
27552
+ value: ""
27553
+ };
27554
+ cellRenderCache.set(cacheKey, mergedSecondaryData);
27555
+ return mergedSecondaryData;
27556
+ }
27557
+ const merge = worksheet.getMergeSpan(row, col);
27558
+ const inheritedStyle = resolveInheritedCellStyle2(sheet, row, col);
27559
+ const worksheetStyle = worksheet.getCellStyleAt(row, col) ?? null;
27560
+ const mergedStyle = mergeResolvedCellStyle(inheritedStyle, worksheetStyle, { replaceXfSubtrees: true });
27561
+ const alignment = mergedStyle?.alignment;
27562
+ const sparkline = sparklineByCell.get(cacheKey) ?? null;
27563
+ const sparklineValues = sparkline ? sparkline.range.start.row === sparkline.range.end.row ? Array.from(
27564
+ { length: Math.abs(sparkline.range.end.col - sparkline.range.start.col) + 1 },
27565
+ (_, index) => getCellNumericValue(
27566
+ worksheet,
27567
+ sparkline.range.start.row,
27568
+ Math.min(sparkline.range.start.col, sparkline.range.end.col) + index
27569
+ )
27570
+ ) : Array.from(
27571
+ { length: Math.abs(sparkline.range.end.row - sparkline.range.start.row) + 1 },
27572
+ (_, index) => getCellNumericValue(
27573
+ worksheet,
27574
+ Math.min(sparkline.range.start.row, sparkline.range.end.row) + index,
27575
+ sparkline.range.start.col
27576
+ )
27577
+ ) : null;
27578
+ const checkboxState = mergedStyle?.cellControl ? getCellBooleanValue(worksheet, row, col) : null;
27579
+ const nextData = {
27580
+ canvas: void 0,
27581
+ checkboxState,
27582
+ colSpan: merge?.colSpan,
27583
+ conditionalColorScale: resolveConditionalColorScaleForCell(
27584
+ row,
27585
+ col,
27586
+ worksheet,
27587
+ sheet,
27588
+ conditionalFormatMetricsCache
27589
+ ),
27590
+ conditionalDataBar: resolveConditionalDataBarForCell(
27591
+ row,
27592
+ col,
27593
+ worksheet,
27594
+ sheet,
27595
+ conditionalFormatMetricsCache
27596
+ ),
27597
+ conditionalIcon: resolveConditionalIconForCell(
27598
+ row,
27599
+ col,
27600
+ worksheet,
27601
+ sheet,
27602
+ conditionalFormatMetricsCache
27603
+ ),
27604
+ isMergedSecondary: false,
27605
+ rowSpan: merge?.rowSpan,
27606
+ sparkline: sparkline && sparklineValues ? { config: sparkline, values: sparklineValues } : null,
27607
+ style: buildCellStyle(mergedStyle, palette, sheet.themePalette, {
27608
+ showGridLines
27609
+ }),
27610
+ textRotationDeg: resolveSpreadsheetTextRotation(alignment?.textRotation),
27611
+ value: sparkline ? "" : checkboxState !== null ? "" : getCellDisplayValue(worksheet, row, col, sheet)
27612
+ };
27613
+ nextData.canvas = buildCanvasCellStyleCache(nextData.style);
27614
+ cellRenderCache.set(cacheKey, nextData);
27615
+ return nextData;
27616
+ };
27617
+ for (const rowItem of rowAxis.items) {
27618
+ for (const colItem of colAxis.items) {
27619
+ const cellData = getCellData(rowItem.actualIndex, colItem.actualIndex);
27620
+ if (cellData.isMergedSecondary) {
27621
+ continue;
27622
+ }
27623
+ const colSpan = Math.max(1, cellData.colSpan ?? 1);
27624
+ const rowSpan = Math.max(1, cellData.rowSpan ?? 1);
27625
+ let width = colItem.size;
27626
+ let height = rowItem.size;
27627
+ for (let colOffset = 1; colOffset < colSpan; colOffset += 1) {
27628
+ const nextCol = colItemByActual.get(colItem.actualIndex + colOffset);
27629
+ if (!nextCol) {
27630
+ break;
27631
+ }
27632
+ width += nextCol.size;
27633
+ }
27634
+ for (let rowOffset = 1; rowOffset < rowSpan; rowOffset += 1) {
27635
+ const nextRow = rowItemByActual.get(rowItem.actualIndex + rowOffset);
27636
+ if (!nextRow) {
27637
+ break;
27638
+ }
27639
+ height += nextRow.size;
27640
+ }
27641
+ const rect = {
27642
+ height,
27643
+ left: rowHeaderWidth + colItem.start,
27644
+ top: headerHeight + rowItem.start,
27645
+ width
27646
+ };
27647
+ const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellData.style);
27648
+ const gradientFill = typeof cellData.style.backgroundImage === "string" ? resolveCanvasGradientFill(context, rect, cellData.style.backgroundImage) : null;
27649
+ const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor : resolveSheetSurface(sheet, palette));
27650
+ const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !== resolveSheetSurface(sheet, palette);
27651
+ context.fillStyle = gradientFill ?? fillColor;
27652
+ context.fillRect(rect.left, rect.top, rect.width, rect.height);
27653
+ if (cellData.conditionalDataBar) {
27654
+ const barLeft = rect.left + 4;
27655
+ const barTop = rect.top + 4;
27656
+ const barWidth = Math.max(0, (rect.width - 8) * (cellData.conditionalDataBar.widthPercent / 100));
27657
+ const barHeight = Math.max(0, rect.height - 8);
27658
+ if (barWidth > 0 && barHeight > 0) {
27659
+ context.fillStyle = resolveCanvasDataBarFill(
27660
+ context,
27661
+ barLeft,
27662
+ barTop,
27663
+ barWidth,
27664
+ barHeight,
27665
+ cellData.conditionalDataBar
27666
+ );
27667
+ context.fillRect(barLeft, barTop, barWidth, barHeight);
27668
+ if (cellData.conditionalDataBar.border !== false && cellData.conditionalDataBar.borderColor) {
27669
+ context.strokeStyle = cellData.conditionalDataBar.borderColor;
27670
+ context.lineWidth = 1;
27671
+ context.strokeRect(barLeft + 0.5, barTop + 0.5, Math.max(0, barWidth - 1), Math.max(0, barHeight - 1));
27672
+ }
27673
+ }
27674
+ }
27675
+ if (showGridLines && !hasExplicitCellFill) {
27676
+ context.strokeStyle = palette.border;
27677
+ context.lineWidth = 1;
27678
+ context.beginPath();
27679
+ if (colItem.start === 0) {
27680
+ context.moveTo(rect.left + 0.5, rect.top);
27681
+ context.lineTo(rect.left + 0.5, rect.top + rect.height);
27682
+ }
27683
+ if (rowItem.start === 0) {
27684
+ context.moveTo(rect.left, rect.top + 0.5);
27685
+ context.lineTo(rect.left + rect.width, rect.top + 0.5);
27686
+ }
27687
+ context.moveTo(rect.left + rect.width - 0.5, rect.top);
27688
+ context.lineTo(rect.left + rect.width - 0.5, rect.top + rect.height);
27689
+ context.moveTo(rect.left, rect.top + rect.height - 0.5);
27690
+ context.lineTo(rect.left + rect.width, rect.top + rect.height - 0.5);
27691
+ context.stroke();
27692
+ }
27693
+ if (canvasCellStyle.topBorder) {
27694
+ strokeCanvasBorderSide(context, "top", rect, canvasCellStyle.topBorder);
27695
+ }
27696
+ if (canvasCellStyle.rightBorder) {
27697
+ strokeCanvasBorderSide(context, "right", rect, canvasCellStyle.rightBorder);
27698
+ }
27699
+ if (canvasCellStyle.bottomBorder) {
27700
+ strokeCanvasBorderSide(context, "bottom", rect, canvasCellStyle.bottomBorder);
27701
+ }
27702
+ if (canvasCellStyle.leftBorder) {
27703
+ strokeCanvasBorderSide(context, "left", rect, canvasCellStyle.leftBorder);
27704
+ }
27705
+ const padding = canvasCellStyle.padding;
27706
+ const contentLeft = rect.left + padding.left;
27707
+ const contentTop = rect.top + padding.top;
27708
+ const contentWidth = Math.max(0, rect.width - padding.left - padding.right);
27709
+ const contentHeight = Math.max(0, rect.height - padding.top - padding.bottom);
27710
+ const rawText = cellData.value ?? "";
27711
+ context.save();
27712
+ context.beginPath();
27713
+ context.rect(contentLeft, contentTop, contentWidth, contentHeight);
27714
+ context.clip();
27715
+ context.font = canvasCellStyle.baseFont;
27716
+ context.fillStyle = canvasCellStyle.textColor;
27717
+ context.textBaseline = "middle";
27718
+ if (cellData.checkboxState != null) {
27719
+ const boxSize = Math.min(14, contentWidth, contentHeight);
27720
+ const boxLeft = rect.left + (rect.width - boxSize) / 2;
27721
+ const boxTop = rect.top + (rect.height - boxSize) / 2;
27722
+ context.strokeStyle = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
27723
+ context.lineWidth = 1.25;
27724
+ context.strokeRect(boxLeft, boxTop, boxSize, boxSize);
27725
+ if (cellData.checkboxState) {
27726
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
27727
+ context.fillRect(boxLeft + 1.5, boxTop + 1.5, Math.max(0, boxSize - 3), Math.max(0, boxSize - 3));
27728
+ }
27729
+ } else if (cellData.sparkline) {
27730
+ const sparkline = cellData.sparkline.config;
27731
+ const sparklineValues = cellData.sparkline.values;
27732
+ const points = sparklineValues.map((value, index) => ({ index, value })).filter((entry) => typeof entry.value === "number" && Number.isFinite(entry.value));
27733
+ if (points.length > 1) {
27734
+ const minValue = Math.min(...points.map((entry) => entry.value));
27735
+ const maxValue = Math.max(...points.map((entry) => entry.value));
27736
+ const sparkLeft = contentLeft + 1;
27737
+ const sparkTop = contentTop + 2;
27738
+ const sparkWidth = Math.max(1, contentWidth - 2);
27739
+ const sparkHeight = Math.max(1, contentHeight - 4);
27740
+ const xStep = points.length > 1 ? sparkWidth / (points.length - 1) : 0;
27741
+ context.strokeStyle = sparkline.color ?? "#2563eb";
27742
+ context.lineCap = "round";
27743
+ context.lineJoin = "round";
27744
+ context.lineWidth = 1.25;
27745
+ context.beginPath();
27746
+ points.forEach((entry, index) => {
27747
+ const x = sparkLeft + index * xStep;
27748
+ const y = sparkTop + sparkHeight - clampSparklineValue(entry.value, minValue, maxValue) * sparkHeight;
27749
+ if (index === 0) {
27750
+ context.moveTo(x, y);
27751
+ } else {
27752
+ context.lineTo(x, y);
27753
+ }
27754
+ });
27755
+ context.stroke();
27756
+ }
27757
+ } else if (rawText.length > 0) {
27758
+ const align = canvasCellStyle.textAlign;
27759
+ context.textAlign = align;
27760
+ const textX = align === "right" ? contentLeft + contentWidth : align === "center" ? contentLeft + contentWidth / 2 : contentLeft;
27761
+ const textY = contentTop + contentHeight / 2;
27762
+ const trailingInset = cellData.conditionalIcon ? 18 : 0;
27763
+ const maxTextWidth = Math.max(0, contentWidth - trailingInset);
27764
+ if (cellData.textRotationDeg) {
27765
+ const rotationOriginX = contentLeft + contentWidth / 2;
27766
+ context.save();
27767
+ context.translate(rotationOriginX, textY);
27768
+ context.rotate(cellData.textRotationDeg * Math.PI / 180);
27769
+ context.fillText(
27770
+ rawText,
27771
+ align === "right" ? contentWidth / 2 : align === "center" ? 0 : -(contentWidth / 2),
27772
+ 0
27773
+ );
27774
+ context.restore();
27775
+ } else if (canvasCellStyle.usesWrappedText || rawText.includes("\n")) {
27776
+ const lines = wrapCanvasText(context, rawText, maxTextWidth);
27777
+ const lineHeight = resolveCanvasLineHeight(cellData.style, 12);
27778
+ const textBlockHeight = lines.length * lineHeight;
27779
+ let textBlockTop = contentTop;
27780
+ if (cellData.style.verticalAlign === "middle") {
27781
+ textBlockTop = contentTop + (contentHeight - textBlockHeight) / 2;
27782
+ } else if (cellData.style.verticalAlign !== "top") {
27783
+ textBlockTop = contentTop + contentHeight - textBlockHeight;
27784
+ }
27785
+ lines.forEach((line, lineIndex) => {
27786
+ context.fillText(line, textX, textBlockTop + lineIndex * lineHeight + lineHeight / 2);
27787
+ });
27788
+ } else {
27789
+ const text = canvasCellStyle.textOverflowEllipsis ? truncateCanvasText(context, rawText, maxTextWidth) : rawText;
27790
+ context.fillText(text, textX, textY);
27791
+ }
27792
+ }
27793
+ if (cellData.conditionalIcon) {
27794
+ const iconSize = 10;
27795
+ const iconX = rect.left + rect.width - (padding.right + iconSize + 4);
27796
+ const iconY = rect.top + rect.height / 2;
27797
+ drawCanvasConditionalIcon(context, cellData.conditionalIcon, iconX + iconSize / 2, iconY, iconSize);
27798
+ }
27799
+ context.restore();
27800
+ }
27801
+ }
27802
+ if (includeHeaders) {
27803
+ context.strokeStyle = palette.border;
27804
+ context.lineWidth = 1;
27805
+ context.font = "600 11px ui-sans-serif, system-ui, sans-serif";
27806
+ context.fillStyle = palette.mutedText;
27807
+ context.textBaseline = "middle";
27808
+ for (const colItem of colAxis.items) {
27809
+ const left = rowHeaderWidth + colItem.start;
27810
+ context.beginPath();
27811
+ context.moveTo(left + colItem.size - 0.5, 0);
27812
+ context.lineTo(left + colItem.size - 0.5, headerHeight);
27813
+ context.moveTo(left, headerHeight - 0.5);
27814
+ context.lineTo(left + colItem.size, headerHeight - 0.5);
27815
+ context.stroke();
27816
+ context.textAlign = "center";
27817
+ context.fillText(columnLabel2(colItem.actualIndex), left + colItem.size / 2, headerHeight / 2);
27818
+ }
27819
+ for (const rowItem of rowAxis.items) {
27820
+ const top = headerHeight + rowItem.start;
27821
+ context.beginPath();
27822
+ context.moveTo(0, top + rowItem.size - 0.5);
27823
+ context.lineTo(rowHeaderWidth, top + rowItem.size - 0.5);
27824
+ context.moveTo(rowHeaderWidth - 0.5, top);
27825
+ context.lineTo(rowHeaderWidth - 0.5, top + rowItem.size);
27826
+ context.stroke();
27827
+ context.textAlign = "center";
27828
+ context.fillText(`${rowItem.actualIndex + 1}`, rowHeaderWidth / 2, top + rowItem.size / 2);
27829
+ }
27830
+ context.beginPath();
27831
+ context.moveTo(rowHeaderWidth - 0.5, 0);
27832
+ context.lineTo(rowHeaderWidth - 0.5, headerHeight);
27833
+ context.moveTo(0, headerHeight - 0.5);
27834
+ context.lineTo(rowHeaderWidth, headerHeight - 0.5);
27835
+ context.stroke();
27836
+ }
27837
+ return true;
27838
+ };
27839
+ const plan = {
27840
+ aspectRatio: sourceWidth / Math.max(1, sourceHeight),
27841
+ contentHeight: rowAxis.totalSize,
27842
+ contentWidth: colAxis.totalSize,
27843
+ height: outputSize.height,
27844
+ paint,
27845
+ sourceRange,
27846
+ width: outputSize.width
27847
+ };
27848
+ return {
27849
+ ...plan,
27850
+ sheet,
27851
+ sheetIndex,
27852
+ workbookSheetIndex: sheet.workbookSheetIndex
27853
+ };
27854
+ });
27855
+ }, [includeHeaders, palette, resolution, sheets, workbook]);
27856
+ const paintThumbnail = React4.useCallback(
27857
+ (sheetIndex, canvas) => thumbnails[sheetIndex]?.paint(canvas) ?? false,
27858
+ [thumbnails]
27859
+ );
27860
+ return React4.useMemo(
27861
+ () => ({
27862
+ paintThumbnail,
27863
+ thumbnails
27864
+ }),
27865
+ [paintThumbnail, thumbnails]
27866
+ );
27867
+ }
26799
27868
  function XlsxViewer(props) {
26800
27869
  const contextController = React4.useContext(ViewerContext);
26801
27870
  if (props.controller) {
@@ -26824,6 +27893,7 @@ export {
26824
27893
  useXlsxViewerImages,
26825
27894
  useXlsxViewerSelection,
26826
27895
  useXlsxViewerTables,
27896
+ useXlsxViewerThumbnails,
26827
27897
  useXlsxViewerZoom
26828
27898
  };
26829
27899
  //# sourceMappingURL=index.js.map