@extend-ai/react-xlsx 0.7.0 → 0.8.0

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
@@ -16317,6 +16317,13 @@ var IMAGE_MIN_SIZE_PX = 16;
16317
16317
  var IMAGE_HANDLE_SIZE_PX = 10;
16318
16318
  var CANVAS_RESIZE_HIT_SLOP_PX = 8;
16319
16319
  var CANVAS_VIEWPORT_OVERSCAN_PX = 240;
16320
+ var THUMBNAIL_DEFAULT_MAX_DIMENSION = 192;
16321
+ var THUMBNAIL_FALLBACK_ROWS = 12;
16322
+ var THUMBNAIL_FALLBACK_COLS = 8;
16323
+ var THUMBNAIL_MAX_ROWS = 200;
16324
+ var THUMBNAIL_MAX_COLS = 80;
16325
+ var THUMBNAIL_MAX_SOURCE_HEIGHT_PX = 900;
16326
+ var THUMBNAIL_MAX_SOURCE_WIDTH_PX = 1440;
16320
16327
  var LIVE_ZOOM_COMMIT_IDLE_MS = 48;
16321
16328
  var WHEEL_ZOOM_SENSITIVITY = 25e-5;
16322
16329
  var WHEEL_LINE_DELTA_PX = 16;
@@ -18323,6 +18330,97 @@ function resolveImageHandleStyle(position, stroke, surface, scale = 1) {
18323
18330
  function useViewerPalette(isDark = false) {
18324
18331
  return isDark ? DARK_PALETTE : LIGHT_PALETTE;
18325
18332
  }
18333
+ function normalizeThumbnailResolution(resolution) {
18334
+ if (typeof resolution === "number" && Number.isFinite(resolution) && resolution > 0) {
18335
+ return {
18336
+ maxHeight: resolution,
18337
+ maxWidth: resolution
18338
+ };
18339
+ }
18340
+ const resolvedObject = typeof resolution === "object" && resolution ? resolution : void 0;
18341
+ return {
18342
+ maxHeight: resolvedObject?.maxHeight && Number.isFinite(resolvedObject.maxHeight) && resolvedObject.maxHeight > 0 ? resolvedObject.maxHeight : THUMBNAIL_DEFAULT_MAX_DIMENSION,
18343
+ maxWidth: resolvedObject?.maxWidth && Number.isFinite(resolvedObject.maxWidth) && resolvedObject.maxWidth > 0 ? resolvedObject.maxWidth : THUMBNAIL_DEFAULT_MAX_DIMENSION
18344
+ };
18345
+ }
18346
+ function resolveThumbnailOutputSize(sourceWidth, sourceHeight, resolution) {
18347
+ const normalized = normalizeThumbnailResolution(resolution);
18348
+ const safeSourceWidth = Math.max(1, sourceWidth);
18349
+ const safeSourceHeight = Math.max(1, sourceHeight);
18350
+ const scale = Math.min(
18351
+ normalized.maxWidth / safeSourceWidth,
18352
+ normalized.maxHeight / safeSourceHeight,
18353
+ 1
18354
+ );
18355
+ return {
18356
+ height: Math.max(1, Math.round(safeSourceHeight * scale)),
18357
+ maxHeight: normalized.maxHeight,
18358
+ maxWidth: normalized.maxWidth,
18359
+ scale,
18360
+ width: Math.max(1, Math.round(safeSourceWidth * scale))
18361
+ };
18362
+ }
18363
+ function resolveThumbnailAxisIndices({
18364
+ fallbackCount,
18365
+ getSizePx,
18366
+ maxCount,
18367
+ maxLogicalPixels,
18368
+ maxUsedIndex,
18369
+ minUsedIndex,
18370
+ precomputed
18371
+ }) {
18372
+ const sourceIndices = precomputed.length > 0 ? precomputed : Array.from({
18373
+ length: Math.max(fallbackCount, Math.max(0, maxUsedIndex + 1))
18374
+ }, (_, index) => index);
18375
+ const hasUsedRange = maxUsedIndex >= minUsedIndex && maxUsedIndex >= 0;
18376
+ const preferredStart = hasUsedRange ? Math.max(0, minUsedIndex) : 0;
18377
+ const preferredEnd = hasUsedRange ? maxUsedIndex : Math.max(0, preferredStart + fallbackCount - 1);
18378
+ const picked = [];
18379
+ let totalSize = 0;
18380
+ for (const actualIndex of sourceIndices) {
18381
+ if (actualIndex < preferredStart) {
18382
+ continue;
18383
+ }
18384
+ if (hasUsedRange && actualIndex > preferredEnd) {
18385
+ break;
18386
+ }
18387
+ const size = Math.max(1, getSizePx(actualIndex));
18388
+ picked.push(actualIndex);
18389
+ totalSize += size;
18390
+ if (picked.length >= maxCount || totalSize >= maxLogicalPixels) {
18391
+ break;
18392
+ }
18393
+ }
18394
+ if (picked.length > 0) {
18395
+ return picked;
18396
+ }
18397
+ for (const actualIndex of sourceIndices) {
18398
+ const size = Math.max(1, getSizePx(actualIndex));
18399
+ picked.push(actualIndex);
18400
+ totalSize += size;
18401
+ if (picked.length >= fallbackCount || picked.length >= maxCount || totalSize >= maxLogicalPixels) {
18402
+ break;
18403
+ }
18404
+ }
18405
+ return picked.length > 0 ? picked : [0];
18406
+ }
18407
+ function buildThumbnailAxisItems(actualIndices, getSizePx) {
18408
+ const items = [];
18409
+ let offset = 0;
18410
+ for (const actualIndex of actualIndices) {
18411
+ const size = Math.max(1, getSizePx(actualIndex));
18412
+ items.push({
18413
+ actualIndex,
18414
+ size,
18415
+ start: offset
18416
+ });
18417
+ offset += size;
18418
+ }
18419
+ return {
18420
+ items,
18421
+ totalSize: offset
18422
+ };
18423
+ }
18326
18424
  function columnLabel2(col) {
18327
18425
  let label = "";
18328
18426
  let nextValue = col;
@@ -19229,7 +19327,6 @@ function getTableHeaderColumn(table, row, col) {
19229
19327
  return table.columns[index] ?? null;
19230
19328
  }
19231
19329
  function DefaultTableHeaderMenu({
19232
- close,
19233
19330
  direction,
19234
19331
  sortAscending,
19235
19332
  sortDescending
@@ -19251,10 +19348,7 @@ function DefaultTableHeaderMenu({
19251
19348
  /* @__PURE__ */ jsx3(
19252
19349
  "button",
19253
19350
  {
19254
- onClick: () => {
19255
- sortAscending();
19256
- close();
19257
- },
19351
+ onClick: sortAscending,
19258
19352
  style: {
19259
19353
  background: direction === "ascending" ? "var(--xlsx-menu-active)" : "transparent",
19260
19354
  border: "none",
@@ -19272,10 +19366,7 @@ function DefaultTableHeaderMenu({
19272
19366
  /* @__PURE__ */ jsx3(
19273
19367
  "button",
19274
19368
  {
19275
- onClick: () => {
19276
- sortDescending();
19277
- close();
19278
- },
19369
+ onClick: sortDescending,
19279
19370
  style: {
19280
19371
  background: direction === "descending" ? "var(--xlsx-menu-active)" : "transparent",
19281
19372
  border: "none",
@@ -19294,6 +19385,25 @@ function DefaultTableHeaderMenu({
19294
19385
  }
19295
19386
  );
19296
19387
  }
19388
+ function resolveTableHeaderTriggerStyle(palette, zoomFactor) {
19389
+ return {
19390
+ alignItems: "center",
19391
+ background: "transparent",
19392
+ border: "none",
19393
+ color: palette.mutedText,
19394
+ cursor: "pointer",
19395
+ display: "inline-flex",
19396
+ fontSize: 10 * zoomFactor,
19397
+ height: 16 * zoomFactor,
19398
+ justifyContent: "center",
19399
+ padding: 0,
19400
+ position: "absolute",
19401
+ right: 4 * zoomFactor,
19402
+ top: 3 * zoomFactor,
19403
+ width: 16 * zoomFactor,
19404
+ zIndex: 6
19405
+ };
19406
+ }
19297
19407
  function SegmentedControl({
19298
19408
  items,
19299
19409
  onValueChange,
@@ -23508,6 +23618,30 @@ function XlsxGrid({
23508
23618
  return null;
23509
23619
  }
23510
23620
  const direction = sortState && sortState.tableName === table.name && sortState.columnIndex === tableColumn.index ? sortState.direction : null;
23621
+ const triggerIcon = direction === "ascending" ? "\u25B2" : direction === "descending" ? "\u25BC" : "\u25BE";
23622
+ if (renderTableHeaderMenu) {
23623
+ return renderTableHeaderMenu({
23624
+ cell,
23625
+ column: tableColumn,
23626
+ direction,
23627
+ sortAscending: () => sortTable(table.name, tableColumn.index, "ascending"),
23628
+ sortDescending: () => sortTable(table.name, tableColumn.index, "descending"),
23629
+ table,
23630
+ triggerIcon,
23631
+ triggerProps: {
23632
+ "aria-haspopup": "menu",
23633
+ "aria-label": `Open menu for ${tableColumn.name}`,
23634
+ onClick: (event) => {
23635
+ event.stopPropagation();
23636
+ },
23637
+ onPointerDown: (event) => {
23638
+ event.stopPropagation();
23639
+ },
23640
+ style: resolveTableHeaderTriggerStyle(palette, zoomFactor),
23641
+ type: "button"
23642
+ }
23643
+ });
23644
+ }
23511
23645
  return /* @__PURE__ */ jsx3(
23512
23646
  "button",
23513
23647
  {
@@ -23523,27 +23657,13 @@ function XlsxGrid({
23523
23657
  );
23524
23658
  },
23525
23659
  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
23660
+ ...resolveTableHeaderTriggerStyle(palette, zoomFactor)
23541
23661
  },
23542
23662
  type: "button",
23543
- children: direction === "ascending" ? "\u25B2" : direction === "descending" ? "\u25BC" : "\u25BE"
23663
+ children: triggerIcon
23544
23664
  }
23545
23665
  );
23546
- }, [effectiveTables, palette.mutedText, sortState, zoomFactor]);
23666
+ }, [effectiveTables, palette, renderTableHeaderMenu, sortState, sortTable, zoomFactor]);
23547
23667
  const resolveCanvasColumnHeaderRect = React4.useCallback((actualCol) => {
23548
23668
  const colIndex = colIndexByActual.get(actualCol);
23549
23669
  if (colIndex === void 0) {
@@ -26339,7 +26459,7 @@ function XlsxGrid({
26339
26459
  }
26340
26460
  }
26341
26461
  ) : null,
26342
- openTableMenuState ? /* @__PURE__ */ jsx3(
26462
+ !renderTableHeaderMenu && openTableMenuState ? /* @__PURE__ */ jsx3(
26343
26463
  "div",
26344
26464
  {
26345
26465
  ref: tableMenuRef,
@@ -26350,22 +26470,23 @@ function XlsxGrid({
26350
26470
  top: openTableMenuState.top,
26351
26471
  zIndex: 50
26352
26472
  },
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(
26473
+ children: /* @__PURE__ */ jsx3(
26361
26474
  DefaultTableHeaderMenu,
26362
26475
  {
26363
- close: () => setOpenTableMenu(null),
26476
+ cell: { col: openTableMenuState.column.index + openTableMenuState.table.start.col, row: openTableMenuState.table.start.row },
26364
26477
  column: openTableMenuState.column,
26365
26478
  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
26479
+ sortAscending: () => {
26480
+ sortTable(openTableMenuState.table.name, openTableMenuState.column.index, "ascending");
26481
+ setOpenTableMenu(null);
26482
+ },
26483
+ sortDescending: () => {
26484
+ sortTable(openTableMenuState.table.name, openTableMenuState.column.index, "descending");
26485
+ setOpenTableMenu(null);
26486
+ },
26487
+ table: openTableMenuState.table,
26488
+ triggerIcon: "\u25BE",
26489
+ triggerProps: { type: "button" }
26369
26490
  }
26370
26491
  )
26371
26492
  }
@@ -26796,6 +26917,476 @@ function useXlsxViewerCharts() {
26796
26917
  ]
26797
26918
  );
26798
26919
  }
26920
+ function useXlsxViewerThumbnails(options = {}) {
26921
+ const { workbook, sheets } = useXlsxViewer();
26922
+ const { isDark } = React4.useContext(ViewerAppearanceContext);
26923
+ const palette = useViewerPalette(isDark);
26924
+ const includeHeaders = options.includeHeaders ?? true;
26925
+ const resolution = options.resolution;
26926
+ const thumbnails = React4.useMemo(() => {
26927
+ return sheets.map((sheet, sheetIndex) => {
26928
+ const worksheet = workbook?.getSheet(sheet.workbookSheetIndex) ?? null;
26929
+ const showGridLines = sheet.showGridLines ?? true;
26930
+ const hiddenRowSet = new Set(sheet.hiddenRows ?? []);
26931
+ const hiddenColSet = new Set(sheet.hiddenCols ?? []);
26932
+ const visibleRows = buildVisibleAxisIndices(
26933
+ sheet.visibleRows ?? [],
26934
+ Math.max(THUMBNAIL_FALLBACK_ROWS, (sheet.maxUsedRow ?? -1) + THUMBNAIL_FALLBACK_ROWS + 1),
26935
+ sheet.maxUsedRow ?? -1,
26936
+ hiddenRowSet
26937
+ );
26938
+ const visibleCols = buildVisibleAxisIndices(
26939
+ sheet.visibleCols ?? [],
26940
+ Math.max(THUMBNAIL_FALLBACK_COLS, (sheet.maxUsedCol ?? -1) + THUMBNAIL_FALLBACK_COLS + 1),
26941
+ sheet.maxUsedCol ?? -1,
26942
+ hiddenColSet
26943
+ );
26944
+ const resolveColumnWidthPx = (actualCol) => {
26945
+ if (worksheet && !worksheet.isColumnHidden(actualCol)) {
26946
+ const width = worksheet.getColumnWidth(actualCol);
26947
+ if (width !== void 0 && width !== null) {
26948
+ return Math.max(
26949
+ resolveRenderedSheetAxisPixels(resolveSheetColumnWidthPixels(width, sheet.columnWidthCharacterWidthPx), showGridLines),
26950
+ DEFAULT_COL_WIDTH2 / 2
26951
+ );
26952
+ }
26953
+ }
26954
+ return Math.max(
26955
+ resolveRenderedSheetAxisPixels(
26956
+ sheet.colWidthOverridesPx[actualCol] ?? sheet.defaultColWidthPx ?? DEFAULT_COL_WIDTH2,
26957
+ showGridLines
26958
+ ),
26959
+ DEFAULT_COL_WIDTH2 / 2
26960
+ );
26961
+ };
26962
+ const resolveRowHeightPx = (actualRow) => {
26963
+ if (worksheet && !worksheet.isRowHidden(actualRow)) {
26964
+ const height = worksheet.getRowHeight(actualRow);
26965
+ if (height !== void 0 && height !== null) {
26966
+ return Math.max(
26967
+ resolveRenderedSheetAxisPixels(resolveSheetRowHeightPixels(height), showGridLines),
26968
+ DEFAULT_ROW_HEIGHT2 / 1.5
26969
+ );
26970
+ }
26971
+ }
26972
+ return Math.max(
26973
+ resolveRenderedSheetAxisPixels(
26974
+ sheet.rowHeightOverridesPx[actualRow] ?? sheet.defaultRowHeightPx ?? DEFAULT_ROW_HEIGHT2,
26975
+ showGridLines
26976
+ ),
26977
+ DEFAULT_ROW_HEIGHT2 / 1.5
26978
+ );
26979
+ };
26980
+ const previewRows = resolveThumbnailAxisIndices({
26981
+ fallbackCount: THUMBNAIL_FALLBACK_ROWS,
26982
+ getSizePx: resolveRowHeightPx,
26983
+ maxCount: THUMBNAIL_MAX_ROWS,
26984
+ maxLogicalPixels: THUMBNAIL_MAX_SOURCE_HEIGHT_PX,
26985
+ maxUsedIndex: sheet.maxUsedRow ?? -1,
26986
+ minUsedIndex: Math.max(0, sheet.minUsedRow ?? 0),
26987
+ precomputed: visibleRows
26988
+ });
26989
+ const previewCols = resolveThumbnailAxisIndices({
26990
+ fallbackCount: THUMBNAIL_FALLBACK_COLS,
26991
+ getSizePx: resolveColumnWidthPx,
26992
+ maxCount: THUMBNAIL_MAX_COLS,
26993
+ maxLogicalPixels: THUMBNAIL_MAX_SOURCE_WIDTH_PX,
26994
+ maxUsedIndex: sheet.maxUsedCol ?? -1,
26995
+ minUsedIndex: Math.max(0, sheet.minUsedCol ?? 0),
26996
+ precomputed: visibleCols
26997
+ });
26998
+ const rowAxis = buildThumbnailAxisItems(previewRows, resolveRowHeightPx);
26999
+ const colAxis = buildThumbnailAxisItems(previewCols, resolveColumnWidthPx);
27000
+ const rowItemByActual = new Map(rowAxis.items.map((item) => [item.actualIndex, item]));
27001
+ const colItemByActual = new Map(colAxis.items.map((item) => [item.actualIndex, item]));
27002
+ const headerHeight = includeHeaders ? HEADER_HEIGHT : 0;
27003
+ const rowHeaderWidth = includeHeaders ? ROW_HEADER_WIDTH : 0;
27004
+ const sourceWidth = rowHeaderWidth + colAxis.totalSize;
27005
+ const sourceHeight = headerHeight + rowAxis.totalSize;
27006
+ const outputSize = resolveThumbnailOutputSize(sourceWidth, sourceHeight, resolution);
27007
+ const sourceRange = {
27008
+ start: {
27009
+ col: previewCols[0] ?? 0,
27010
+ row: previewRows[0] ?? 0
27011
+ },
27012
+ end: {
27013
+ col: previewCols[previewCols.length - 1] ?? 0,
27014
+ row: previewRows[previewRows.length - 1] ?? 0
27015
+ }
27016
+ };
27017
+ const sparklineByCell = new Map(
27018
+ (sheet.sparklines ?? []).map((sparkline) => [`${sparkline.target.row}:${sparkline.target.col}`, sparkline])
27019
+ );
27020
+ const paint = (canvas) => {
27021
+ if (!canvas) {
27022
+ return false;
27023
+ }
27024
+ const context = canvas.getContext("2d");
27025
+ if (!context) {
27026
+ return false;
27027
+ }
27028
+ const dpr = typeof window === "undefined" ? 1 : Math.max(1, window.devicePixelRatio || 1);
27029
+ const outputWidth = outputSize.width;
27030
+ const outputHeight = outputSize.height;
27031
+ const scale = outputSize.scale;
27032
+ const deviceWidth = Math.max(1, Math.round(outputWidth * dpr));
27033
+ const deviceHeight = Math.max(1, Math.round(outputHeight * dpr));
27034
+ if (canvas.width !== deviceWidth) {
27035
+ canvas.width = deviceWidth;
27036
+ }
27037
+ if (canvas.height !== deviceHeight) {
27038
+ canvas.height = deviceHeight;
27039
+ }
27040
+ if (canvas.style.width !== `${outputWidth}px`) {
27041
+ canvas.style.width = `${outputWidth}px`;
27042
+ }
27043
+ if (canvas.style.height !== `${outputHeight}px`) {
27044
+ canvas.style.height = `${outputHeight}px`;
27045
+ }
27046
+ context.setTransform(dpr * scale, 0, 0, dpr * scale, 0, 0);
27047
+ context.clearRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
27048
+ context.fillStyle = palette.canvas;
27049
+ context.fillRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
27050
+ context.fillStyle = resolveSheetSurface(sheet, palette);
27051
+ context.fillRect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
27052
+ if (includeHeaders) {
27053
+ context.fillStyle = palette.headerSurface;
27054
+ context.fillRect(rowHeaderWidth, 0, Math.max(1, colAxis.totalSize), headerHeight);
27055
+ context.fillStyle = palette.rowHeaderSurface;
27056
+ context.fillRect(0, headerHeight, rowHeaderWidth, Math.max(1, rowAxis.totalSize));
27057
+ context.fillRect(0, 0, rowHeaderWidth, headerHeight);
27058
+ }
27059
+ if (!worksheet) {
27060
+ return true;
27061
+ }
27062
+ const conditionalFormatMetricsCache = /* @__PURE__ */ new Map();
27063
+ const cellRenderCache = /* @__PURE__ */ new Map();
27064
+ const getCellData = (row, col) => {
27065
+ const cacheKey = `${row}:${col}`;
27066
+ const cached = cellRenderCache.get(cacheKey);
27067
+ if (cached) {
27068
+ return cached;
27069
+ }
27070
+ if (worksheet.isMergedSecondary(row, col)) {
27071
+ const mergedSecondaryData = {
27072
+ isMergedSecondary: true,
27073
+ style: {},
27074
+ value: ""
27075
+ };
27076
+ cellRenderCache.set(cacheKey, mergedSecondaryData);
27077
+ return mergedSecondaryData;
27078
+ }
27079
+ const merge = worksheet.getMergeSpan(row, col);
27080
+ const inheritedStyle = resolveInheritedCellStyle2(sheet, row, col);
27081
+ const worksheetStyle = worksheet.getCellStyleAt(row, col) ?? null;
27082
+ const mergedStyle = mergeResolvedCellStyle(inheritedStyle, worksheetStyle, { replaceXfSubtrees: true });
27083
+ const alignment = mergedStyle?.alignment;
27084
+ const sparkline = sparklineByCell.get(cacheKey) ?? null;
27085
+ const sparklineValues = sparkline ? sparkline.range.start.row === sparkline.range.end.row ? Array.from(
27086
+ { length: Math.abs(sparkline.range.end.col - sparkline.range.start.col) + 1 },
27087
+ (_, index) => getCellNumericValue(
27088
+ worksheet,
27089
+ sparkline.range.start.row,
27090
+ Math.min(sparkline.range.start.col, sparkline.range.end.col) + index
27091
+ )
27092
+ ) : Array.from(
27093
+ { length: Math.abs(sparkline.range.end.row - sparkline.range.start.row) + 1 },
27094
+ (_, index) => getCellNumericValue(
27095
+ worksheet,
27096
+ Math.min(sparkline.range.start.row, sparkline.range.end.row) + index,
27097
+ sparkline.range.start.col
27098
+ )
27099
+ ) : null;
27100
+ const checkboxState = mergedStyle?.cellControl ? getCellBooleanValue(worksheet, row, col) : null;
27101
+ const nextData = {
27102
+ canvas: void 0,
27103
+ checkboxState,
27104
+ colSpan: merge?.colSpan,
27105
+ conditionalColorScale: resolveConditionalColorScaleForCell(
27106
+ row,
27107
+ col,
27108
+ worksheet,
27109
+ sheet,
27110
+ conditionalFormatMetricsCache
27111
+ ),
27112
+ conditionalDataBar: resolveConditionalDataBarForCell(
27113
+ row,
27114
+ col,
27115
+ worksheet,
27116
+ sheet,
27117
+ conditionalFormatMetricsCache
27118
+ ),
27119
+ conditionalIcon: resolveConditionalIconForCell(
27120
+ row,
27121
+ col,
27122
+ worksheet,
27123
+ sheet,
27124
+ conditionalFormatMetricsCache
27125
+ ),
27126
+ isMergedSecondary: false,
27127
+ rowSpan: merge?.rowSpan,
27128
+ sparkline: sparkline && sparklineValues ? { config: sparkline, values: sparklineValues } : null,
27129
+ style: buildCellStyle(mergedStyle, palette, sheet.themePalette, {
27130
+ showGridLines
27131
+ }),
27132
+ textRotationDeg: resolveSpreadsheetTextRotation(alignment?.textRotation),
27133
+ value: sparkline ? "" : checkboxState !== null ? "" : getCellDisplayValue(worksheet, row, col, sheet)
27134
+ };
27135
+ nextData.canvas = buildCanvasCellStyleCache(nextData.style);
27136
+ cellRenderCache.set(cacheKey, nextData);
27137
+ return nextData;
27138
+ };
27139
+ for (const rowItem of rowAxis.items) {
27140
+ for (const colItem of colAxis.items) {
27141
+ const cellData = getCellData(rowItem.actualIndex, colItem.actualIndex);
27142
+ if (cellData.isMergedSecondary) {
27143
+ continue;
27144
+ }
27145
+ const colSpan = Math.max(1, cellData.colSpan ?? 1);
27146
+ const rowSpan = Math.max(1, cellData.rowSpan ?? 1);
27147
+ let width = colItem.size;
27148
+ let height = rowItem.size;
27149
+ for (let colOffset = 1; colOffset < colSpan; colOffset += 1) {
27150
+ const nextCol = colItemByActual.get(colItem.actualIndex + colOffset);
27151
+ if (!nextCol) {
27152
+ break;
27153
+ }
27154
+ width += nextCol.size;
27155
+ }
27156
+ for (let rowOffset = 1; rowOffset < rowSpan; rowOffset += 1) {
27157
+ const nextRow = rowItemByActual.get(rowItem.actualIndex + rowOffset);
27158
+ if (!nextRow) {
27159
+ break;
27160
+ }
27161
+ height += nextRow.size;
27162
+ }
27163
+ const rect = {
27164
+ height,
27165
+ left: rowHeaderWidth + colItem.start,
27166
+ top: headerHeight + rowItem.start,
27167
+ width
27168
+ };
27169
+ const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellData.style);
27170
+ const gradientFill = typeof cellData.style.backgroundImage === "string" ? resolveCanvasGradientFill(context, rect, cellData.style.backgroundImage) : null;
27171
+ const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor : resolveSheetSurface(sheet, palette));
27172
+ const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !== resolveSheetSurface(sheet, palette);
27173
+ context.fillStyle = gradientFill ?? fillColor;
27174
+ context.fillRect(rect.left, rect.top, rect.width, rect.height);
27175
+ if (cellData.conditionalDataBar) {
27176
+ const barLeft = rect.left + 4;
27177
+ const barTop = rect.top + 4;
27178
+ const barWidth = Math.max(0, (rect.width - 8) * (cellData.conditionalDataBar.widthPercent / 100));
27179
+ const barHeight = Math.max(0, rect.height - 8);
27180
+ if (barWidth > 0 && barHeight > 0) {
27181
+ context.fillStyle = resolveCanvasDataBarFill(
27182
+ context,
27183
+ barLeft,
27184
+ barTop,
27185
+ barWidth,
27186
+ barHeight,
27187
+ cellData.conditionalDataBar
27188
+ );
27189
+ context.fillRect(barLeft, barTop, barWidth, barHeight);
27190
+ if (cellData.conditionalDataBar.border !== false && cellData.conditionalDataBar.borderColor) {
27191
+ context.strokeStyle = cellData.conditionalDataBar.borderColor;
27192
+ context.lineWidth = 1;
27193
+ context.strokeRect(barLeft + 0.5, barTop + 0.5, Math.max(0, barWidth - 1), Math.max(0, barHeight - 1));
27194
+ }
27195
+ }
27196
+ }
27197
+ if (showGridLines && !hasExplicitCellFill) {
27198
+ context.strokeStyle = palette.border;
27199
+ context.lineWidth = 1;
27200
+ context.beginPath();
27201
+ if (colItem.start === 0) {
27202
+ context.moveTo(rect.left + 0.5, rect.top);
27203
+ context.lineTo(rect.left + 0.5, rect.top + rect.height);
27204
+ }
27205
+ if (rowItem.start === 0) {
27206
+ context.moveTo(rect.left, rect.top + 0.5);
27207
+ context.lineTo(rect.left + rect.width, rect.top + 0.5);
27208
+ }
27209
+ context.moveTo(rect.left + rect.width - 0.5, rect.top);
27210
+ context.lineTo(rect.left + rect.width - 0.5, rect.top + rect.height);
27211
+ context.moveTo(rect.left, rect.top + rect.height - 0.5);
27212
+ context.lineTo(rect.left + rect.width, rect.top + rect.height - 0.5);
27213
+ context.stroke();
27214
+ }
27215
+ if (canvasCellStyle.topBorder) {
27216
+ strokeCanvasBorderSide(context, "top", rect, canvasCellStyle.topBorder);
27217
+ }
27218
+ if (canvasCellStyle.rightBorder) {
27219
+ strokeCanvasBorderSide(context, "right", rect, canvasCellStyle.rightBorder);
27220
+ }
27221
+ if (canvasCellStyle.bottomBorder) {
27222
+ strokeCanvasBorderSide(context, "bottom", rect, canvasCellStyle.bottomBorder);
27223
+ }
27224
+ if (canvasCellStyle.leftBorder) {
27225
+ strokeCanvasBorderSide(context, "left", rect, canvasCellStyle.leftBorder);
27226
+ }
27227
+ const padding = canvasCellStyle.padding;
27228
+ const contentLeft = rect.left + padding.left;
27229
+ const contentTop = rect.top + padding.top;
27230
+ const contentWidth = Math.max(0, rect.width - padding.left - padding.right);
27231
+ const contentHeight = Math.max(0, rect.height - padding.top - padding.bottom);
27232
+ const rawText = cellData.value ?? "";
27233
+ context.save();
27234
+ context.beginPath();
27235
+ context.rect(contentLeft, contentTop, contentWidth, contentHeight);
27236
+ context.clip();
27237
+ context.font = canvasCellStyle.baseFont;
27238
+ context.fillStyle = canvasCellStyle.textColor;
27239
+ context.textBaseline = "middle";
27240
+ if (cellData.checkboxState != null) {
27241
+ const boxSize = Math.min(14, contentWidth, contentHeight);
27242
+ const boxLeft = rect.left + (rect.width - boxSize) / 2;
27243
+ const boxTop = rect.top + (rect.height - boxSize) / 2;
27244
+ context.strokeStyle = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
27245
+ context.lineWidth = 1.25;
27246
+ context.strokeRect(boxLeft, boxTop, boxSize, boxSize);
27247
+ if (cellData.checkboxState) {
27248
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
27249
+ context.fillRect(boxLeft + 1.5, boxTop + 1.5, Math.max(0, boxSize - 3), Math.max(0, boxSize - 3));
27250
+ }
27251
+ } else if (cellData.sparkline) {
27252
+ const sparkline = cellData.sparkline.config;
27253
+ const sparklineValues = cellData.sparkline.values;
27254
+ const points = sparklineValues.map((value, index) => ({ index, value })).filter((entry) => typeof entry.value === "number" && Number.isFinite(entry.value));
27255
+ if (points.length > 1) {
27256
+ const minValue = Math.min(...points.map((entry) => entry.value));
27257
+ const maxValue = Math.max(...points.map((entry) => entry.value));
27258
+ const sparkLeft = contentLeft + 1;
27259
+ const sparkTop = contentTop + 2;
27260
+ const sparkWidth = Math.max(1, contentWidth - 2);
27261
+ const sparkHeight = Math.max(1, contentHeight - 4);
27262
+ const xStep = points.length > 1 ? sparkWidth / (points.length - 1) : 0;
27263
+ context.strokeStyle = sparkline.color ?? "#2563eb";
27264
+ context.lineCap = "round";
27265
+ context.lineJoin = "round";
27266
+ context.lineWidth = 1.25;
27267
+ context.beginPath();
27268
+ points.forEach((entry, index) => {
27269
+ const x = sparkLeft + index * xStep;
27270
+ const y = sparkTop + sparkHeight - clampSparklineValue(entry.value, minValue, maxValue) * sparkHeight;
27271
+ if (index === 0) {
27272
+ context.moveTo(x, y);
27273
+ } else {
27274
+ context.lineTo(x, y);
27275
+ }
27276
+ });
27277
+ context.stroke();
27278
+ }
27279
+ } else if (rawText.length > 0) {
27280
+ const align = canvasCellStyle.textAlign;
27281
+ context.textAlign = align;
27282
+ const textX = align === "right" ? contentLeft + contentWidth : align === "center" ? contentLeft + contentWidth / 2 : contentLeft;
27283
+ const textY = contentTop + contentHeight / 2;
27284
+ const trailingInset = cellData.conditionalIcon ? 18 : 0;
27285
+ const maxTextWidth = Math.max(0, contentWidth - trailingInset);
27286
+ if (cellData.textRotationDeg) {
27287
+ const rotationOriginX = contentLeft + contentWidth / 2;
27288
+ context.save();
27289
+ context.translate(rotationOriginX, textY);
27290
+ context.rotate(cellData.textRotationDeg * Math.PI / 180);
27291
+ context.fillText(
27292
+ rawText,
27293
+ align === "right" ? contentWidth / 2 : align === "center" ? 0 : -(contentWidth / 2),
27294
+ 0
27295
+ );
27296
+ context.restore();
27297
+ } else if (canvasCellStyle.usesWrappedText || rawText.includes("\n")) {
27298
+ const lines = wrapCanvasText(context, rawText, maxTextWidth);
27299
+ const lineHeight = resolveCanvasLineHeight(cellData.style, 12);
27300
+ const textBlockHeight = lines.length * lineHeight;
27301
+ let textBlockTop = contentTop;
27302
+ if (cellData.style.verticalAlign === "middle") {
27303
+ textBlockTop = contentTop + (contentHeight - textBlockHeight) / 2;
27304
+ } else if (cellData.style.verticalAlign !== "top") {
27305
+ textBlockTop = contentTop + contentHeight - textBlockHeight;
27306
+ }
27307
+ lines.forEach((line, lineIndex) => {
27308
+ context.fillText(line, textX, textBlockTop + lineIndex * lineHeight + lineHeight / 2);
27309
+ });
27310
+ } else {
27311
+ const text = canvasCellStyle.textOverflowEllipsis ? truncateCanvasText(context, rawText, maxTextWidth) : rawText;
27312
+ context.fillText(text, textX, textY);
27313
+ }
27314
+ }
27315
+ if (cellData.conditionalIcon) {
27316
+ const iconSize = 10;
27317
+ const iconX = rect.left + rect.width - (padding.right + iconSize + 4);
27318
+ const iconY = rect.top + rect.height / 2;
27319
+ drawCanvasConditionalIcon(context, cellData.conditionalIcon, iconX + iconSize / 2, iconY, iconSize);
27320
+ }
27321
+ context.restore();
27322
+ }
27323
+ }
27324
+ if (includeHeaders) {
27325
+ context.strokeStyle = palette.border;
27326
+ context.lineWidth = 1;
27327
+ context.font = "600 11px ui-sans-serif, system-ui, sans-serif";
27328
+ context.fillStyle = palette.mutedText;
27329
+ context.textBaseline = "middle";
27330
+ for (const colItem of colAxis.items) {
27331
+ const left = rowHeaderWidth + colItem.start;
27332
+ context.beginPath();
27333
+ context.moveTo(left + colItem.size - 0.5, 0);
27334
+ context.lineTo(left + colItem.size - 0.5, headerHeight);
27335
+ context.moveTo(left, headerHeight - 0.5);
27336
+ context.lineTo(left + colItem.size, headerHeight - 0.5);
27337
+ context.stroke();
27338
+ context.textAlign = "center";
27339
+ context.fillText(columnLabel2(colItem.actualIndex), left + colItem.size / 2, headerHeight / 2);
27340
+ }
27341
+ for (const rowItem of rowAxis.items) {
27342
+ const top = headerHeight + rowItem.start;
27343
+ context.beginPath();
27344
+ context.moveTo(0, top + rowItem.size - 0.5);
27345
+ context.lineTo(rowHeaderWidth, top + rowItem.size - 0.5);
27346
+ context.moveTo(rowHeaderWidth - 0.5, top);
27347
+ context.lineTo(rowHeaderWidth - 0.5, top + rowItem.size);
27348
+ context.stroke();
27349
+ context.textAlign = "center";
27350
+ context.fillText(`${rowItem.actualIndex + 1}`, rowHeaderWidth / 2, top + rowItem.size / 2);
27351
+ }
27352
+ context.beginPath();
27353
+ context.moveTo(rowHeaderWidth - 0.5, 0);
27354
+ context.lineTo(rowHeaderWidth - 0.5, headerHeight);
27355
+ context.moveTo(0, headerHeight - 0.5);
27356
+ context.lineTo(rowHeaderWidth, headerHeight - 0.5);
27357
+ context.stroke();
27358
+ }
27359
+ return true;
27360
+ };
27361
+ const plan = {
27362
+ aspectRatio: sourceWidth / Math.max(1, sourceHeight),
27363
+ contentHeight: rowAxis.totalSize,
27364
+ contentWidth: colAxis.totalSize,
27365
+ height: outputSize.height,
27366
+ paint,
27367
+ sourceRange,
27368
+ width: outputSize.width
27369
+ };
27370
+ return {
27371
+ ...plan,
27372
+ sheet,
27373
+ sheetIndex,
27374
+ workbookSheetIndex: sheet.workbookSheetIndex
27375
+ };
27376
+ });
27377
+ }, [includeHeaders, palette, resolution, sheets, workbook]);
27378
+ const paintThumbnail = React4.useCallback(
27379
+ (sheetIndex, canvas) => thumbnails[sheetIndex]?.paint(canvas) ?? false,
27380
+ [thumbnails]
27381
+ );
27382
+ return React4.useMemo(
27383
+ () => ({
27384
+ paintThumbnail,
27385
+ thumbnails
27386
+ }),
27387
+ [paintThumbnail, thumbnails]
27388
+ );
27389
+ }
26799
27390
  function XlsxViewer(props) {
26800
27391
  const contextController = React4.useContext(ViewerContext);
26801
27392
  if (props.controller) {
@@ -26824,6 +27415,7 @@ export {
26824
27415
  useXlsxViewerImages,
26825
27416
  useXlsxViewerSelection,
26826
27417
  useXlsxViewerTables,
27418
+ useXlsxViewerThumbnails,
26827
27419
  useXlsxViewerZoom
26828
27420
  };
26829
27421
  //# sourceMappingURL=index.js.map