@extend-ai/react-xlsx 0.8.4 → 0.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -17602,6 +17602,25 @@ function resolveImageAnchorExtents(image) {
17602
17602
  maxRow: Math.max(image.anchor.from.row, image.anchor.to.row)
17603
17603
  };
17604
17604
  }
17605
+ function resolveAnchoredBounds(anchor) {
17606
+ if (anchor.kind === "absolute") {
17607
+ return null;
17608
+ }
17609
+ if (anchor.kind === "one-cell") {
17610
+ return {
17611
+ maxCol: anchor.from.col,
17612
+ maxRow: anchor.from.row,
17613
+ minCol: anchor.from.col,
17614
+ minRow: anchor.from.row
17615
+ };
17616
+ }
17617
+ return {
17618
+ maxCol: Math.max(anchor.from.col, anchor.to.col),
17619
+ maxRow: Math.max(anchor.from.row, anchor.to.row),
17620
+ minCol: Math.min(anchor.from.col, anchor.to.col),
17621
+ minRow: Math.min(anchor.from.row, anchor.to.row)
17622
+ };
17623
+ }
17605
17624
  function resolveShapeAnchorExtents(shape) {
17606
17625
  if (shape.anchor.kind === "absolute") {
17607
17626
  return { maxCol: 0, maxRow: 0 };
@@ -18785,6 +18804,71 @@ function buildThumbnailAxisItems(actualIndices, getSizePx) {
18785
18804
  totalSize: offset
18786
18805
  };
18787
18806
  }
18807
+ function resolveThumbnailAxisAbsoluteOffset(actualIndices, getSizePx, actualIndex) {
18808
+ let total = 0;
18809
+ for (const candidate of actualIndices) {
18810
+ if (candidate >= actualIndex) {
18811
+ break;
18812
+ }
18813
+ total += Math.max(1, getSizePx(candidate));
18814
+ }
18815
+ return total;
18816
+ }
18817
+ function resolveThumbnailAxisMarkerOffset({
18818
+ actualIndex,
18819
+ actualIndices,
18820
+ getSizePx,
18821
+ offsetEmu,
18822
+ previewStartActualIndex
18823
+ }) {
18824
+ return resolveThumbnailAxisAbsoluteOffset(actualIndices, getSizePx, actualIndex) - resolveThumbnailAxisAbsoluteOffset(actualIndices, getSizePx, previewStartActualIndex) + emuToPixels(offsetEmu);
18825
+ }
18826
+ function resolveThumbnailAnchoredRect(anchor, visibleRows, visibleCols, getRowHeightPx, getColWidthPx, previewRows, previewCols, options) {
18827
+ const previewStartRow = previewRows[0] ?? 0;
18828
+ const previewStartCol = previewCols[0] ?? 0;
18829
+ const previewOriginX = resolveThumbnailAxisAbsoluteOffset(visibleCols, getColWidthPx, previewStartCol);
18830
+ const previewOriginY = resolveThumbnailAxisAbsoluteOffset(visibleRows, getRowHeightPx, previewStartRow);
18831
+ const resolveMarkerLeft = (col, colOffsetEmu) => options.rowHeaderWidth + resolveThumbnailAxisMarkerOffset({
18832
+ actualIndex: col,
18833
+ actualIndices: visibleCols,
18834
+ getSizePx: getColWidthPx,
18835
+ offsetEmu: colOffsetEmu,
18836
+ previewStartActualIndex: previewStartCol
18837
+ });
18838
+ const resolveMarkerTop = (row, rowOffsetEmu) => options.headerHeight + resolveThumbnailAxisMarkerOffset({
18839
+ actualIndex: row,
18840
+ actualIndices: visibleRows,
18841
+ getSizePx: getRowHeightPx,
18842
+ offsetEmu: rowOffsetEmu,
18843
+ previewStartActualIndex: previewStartRow
18844
+ });
18845
+ if (anchor.kind === "absolute") {
18846
+ return {
18847
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18848
+ left: options.rowHeaderWidth + emuToPixels(anchor.positionEmu.x) - previewOriginX,
18849
+ top: options.headerHeight + emuToPixels(anchor.positionEmu.y) - previewOriginY,
18850
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18851
+ };
18852
+ }
18853
+ if (anchor.kind === "one-cell") {
18854
+ return {
18855
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18856
+ left: resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu),
18857
+ top: resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu),
18858
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18859
+ };
18860
+ }
18861
+ const left = resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu);
18862
+ const top = resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu);
18863
+ const right = resolveMarkerLeft(anchor.to.col, anchor.to.colOffsetEmu);
18864
+ const bottom = resolveMarkerTop(anchor.to.row, anchor.to.rowOffsetEmu);
18865
+ return {
18866
+ height: Math.max(1, bottom - top),
18867
+ left,
18868
+ top,
18869
+ width: Math.max(1, right - left)
18870
+ };
18871
+ }
18788
18872
  function columnLabel2(col) {
18789
18873
  let label = "";
18790
18874
  let nextValue = col;
@@ -20570,6 +20654,222 @@ function resolveFormControlLabel(control) {
20570
20654
  }
20571
20655
  return label.replace(/\u00a0/g, " ").replace(/^\s+/, "");
20572
20656
  }
20657
+ function drawStaticShapeText(context, shape, left, top, width, height, zoomFactor) {
20658
+ if (shape.paragraphs.length === 0) {
20659
+ return;
20660
+ }
20661
+ const inset = shape.textBox?.insetPx;
20662
+ const paddingLeft = (inset?.left ?? 6) * zoomFactor;
20663
+ const paddingRight = (inset?.right ?? 6) * zoomFactor;
20664
+ const paddingTop = (inset?.top ?? 4) * zoomFactor;
20665
+ const paddingBottom = (inset?.bottom ?? 4) * zoomFactor;
20666
+ const textLeft = left + paddingLeft;
20667
+ const textTop = top + paddingTop;
20668
+ const textWidth = Math.max(0, width - paddingLeft - paddingRight);
20669
+ const textHeight = Math.max(0, height - paddingTop - paddingBottom);
20670
+ if (textWidth <= 0 || textHeight <= 0) {
20671
+ return;
20672
+ }
20673
+ const lineMetrics = shape.paragraphs.map((paragraph) => {
20674
+ const lineHeight = Math.max(
20675
+ 12 * zoomFactor,
20676
+ ...paragraph.runs.map((run) => (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor * 1.2)
20677
+ );
20678
+ const widthPx = paragraph.runs.reduce((total, run) => {
20679
+ const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
20680
+ context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
20681
+ return total + measureCanvasTextWidth(context, run.text);
20682
+ }, 0);
20683
+ return { lineHeight, widthPx };
20684
+ });
20685
+ const totalTextHeight = lineMetrics.reduce((total, metric) => total + metric.lineHeight, 0);
20686
+ let y = textTop;
20687
+ if (shape.textBox?.verticalAlign === "middle") {
20688
+ y = textTop + Math.max(0, (textHeight - totalTextHeight) / 2);
20689
+ } else if (shape.textBox?.verticalAlign === "bottom") {
20690
+ y = textTop + Math.max(0, textHeight - totalTextHeight);
20691
+ }
20692
+ context.save();
20693
+ context.beginPath();
20694
+ context.rect(textLeft, textTop, textWidth, textHeight);
20695
+ context.clip();
20696
+ context.textBaseline = "middle";
20697
+ shape.paragraphs.forEach((paragraph, paragraphIndex) => {
20698
+ const metric = lineMetrics[paragraphIndex] ?? { lineHeight: 14 * zoomFactor, widthPx: 0 };
20699
+ let x = textLeft;
20700
+ const align = paragraph.align ?? shape.textBox?.horizontalAlign ?? "left";
20701
+ if (align === "center") {
20702
+ x = textLeft + Math.max(0, (textWidth - metric.widthPx) / 2);
20703
+ } else if (align === "right") {
20704
+ x = textLeft + Math.max(0, textWidth - metric.widthPx);
20705
+ }
20706
+ paragraph.runs.forEach((run) => {
20707
+ const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
20708
+ context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
20709
+ context.fillStyle = run.color ?? "#000000";
20710
+ context.textAlign = "left";
20711
+ const textY = y + metric.lineHeight / 2;
20712
+ context.fillText(run.text, x, textY);
20713
+ if (run.underline && run.text.length > 0) {
20714
+ const textWidthPx = measureCanvasTextWidth(context, run.text);
20715
+ context.strokeStyle = run.color ?? "#000000";
20716
+ context.lineWidth = Math.max(1, zoomFactor * 0.75);
20717
+ context.beginPath();
20718
+ context.moveTo(x, textY + Math.max(2, fontSize * 0.28));
20719
+ context.lineTo(x + textWidthPx, textY + Math.max(2, fontSize * 0.28));
20720
+ context.stroke();
20721
+ }
20722
+ x += measureCanvasTextWidth(context, run.text);
20723
+ });
20724
+ y += metric.lineHeight;
20725
+ });
20726
+ context.restore();
20727
+ }
20728
+ function drawStaticShape(context, shape, rect, zoomFactor) {
20729
+ const fillColor = shape.fill?.none ? "transparent" : shape.fill?.color ?? "transparent";
20730
+ const strokeColor = shape.stroke?.none ? "transparent" : shape.stroke?.color ?? "transparent";
20731
+ const lineWidth = Math.max(0, (shape.stroke?.widthPx ?? (shape.geometry === "line" ? 2 : 1)) * zoomFactor);
20732
+ const vectorShape = resolveShapeVector(shape);
20733
+ const opacity = Math.min(shape.fill?.opacity ?? 1, shape.stroke?.opacity ?? 1);
20734
+ context.save();
20735
+ context.translate(rect.left + rect.width / 2, rect.top + rect.height / 2);
20736
+ if (shape.rotationDeg) {
20737
+ context.rotate(shape.rotationDeg * Math.PI / 180);
20738
+ }
20739
+ context.scale(shape.flipH ? -1 : 1, shape.flipV ? -1 : 1);
20740
+ context.globalAlpha *= opacity;
20741
+ context.lineWidth = lineWidth;
20742
+ context.strokeStyle = strokeColor;
20743
+ context.fillStyle = fillColor;
20744
+ applyCanvasShapeDash(context, shape.stroke?.dash, lineWidth);
20745
+ const localLeft = -rect.width / 2;
20746
+ const localTop = -rect.height / 2;
20747
+ if (vectorShape && typeof Path2D !== "undefined") {
20748
+ context.save();
20749
+ context.translate(localLeft, localTop);
20750
+ context.scale(
20751
+ rect.width / Math.max(1, vectorShape.viewBox.width),
20752
+ rect.height / Math.max(1, vectorShape.viewBox.height)
20753
+ );
20754
+ const path = getCachedCanvasPath2D(vectorShape.path);
20755
+ if (!path) {
20756
+ context.restore();
20757
+ context.restore();
20758
+ return;
20759
+ }
20760
+ if (fillColor !== "transparent") {
20761
+ context.fill(path);
20762
+ }
20763
+ if (strokeColor !== "transparent" && lineWidth > 0) {
20764
+ context.stroke(path);
20765
+ }
20766
+ context.restore();
20767
+ } else if (shape.geometry === "ellipse") {
20768
+ context.beginPath();
20769
+ context.ellipse(0, 0, rect.width / 2, rect.height / 2, 0, 0, Math.PI * 2);
20770
+ if (fillColor !== "transparent") {
20771
+ context.fill();
20772
+ }
20773
+ if (strokeColor !== "transparent" && lineWidth > 0) {
20774
+ context.stroke();
20775
+ }
20776
+ } else {
20777
+ const radius = shape.geometry === "roundRect" ? 12 * zoomFactor : 0;
20778
+ drawCanvasRoundedRect(context, localLeft, localTop, rect.width, rect.height, radius);
20779
+ if (fillColor !== "transparent") {
20780
+ context.fill();
20781
+ }
20782
+ if (strokeColor !== "transparent" && lineWidth > 0) {
20783
+ context.stroke();
20784
+ }
20785
+ }
20786
+ drawStaticShapeText(context, shape, localLeft, localTop, rect.width, rect.height, zoomFactor);
20787
+ context.restore();
20788
+ }
20789
+ function drawStaticFormControl(context, control, rect, palette, zoomFactor, sheetSurface = SHEET_SURFACE) {
20790
+ const label = resolveFormControlLabel(control);
20791
+ const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
20792
+ const textColor = control.textColor ?? "#000000";
20793
+ const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
20794
+ const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
20795
+ context.save();
20796
+ context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
20797
+ context.textBaseline = "middle";
20798
+ context.fillStyle = textColor;
20799
+ context.strokeStyle = stroke;
20800
+ context.lineWidth = Math.max(1, zoomFactor);
20801
+ if (control.kind === "group-box") {
20802
+ const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;
20803
+ drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);
20804
+ context.stroke();
20805
+ if (label) {
20806
+ context.fillStyle = sheetSurface;
20807
+ const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);
20808
+ context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);
20809
+ context.fillStyle = textColor;
20810
+ context.textAlign = "left";
20811
+ context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);
20812
+ }
20813
+ context.restore();
20814
+ return;
20815
+ }
20816
+ if (control.kind === "button" || control.kind === "dropdown" || control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
20817
+ if (control.kind === "button") {
20818
+ const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);
20819
+ gradient.addColorStop(0, "#f8fafc");
20820
+ gradient.addColorStop(1, "#e2e8f0");
20821
+ context.fillStyle = gradient;
20822
+ } else {
20823
+ context.fillStyle = "transparent";
20824
+ }
20825
+ drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === "button" ? 4 * zoomFactor : 2 * zoomFactor);
20826
+ if (control.kind === "button") {
20827
+ context.fill();
20828
+ }
20829
+ context.stroke();
20830
+ }
20831
+ let textLeft = rect.left + 2 * zoomFactor;
20832
+ const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
20833
+ if (control.kind === "checkbox") {
20834
+ context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
20835
+ if (control.checked) {
20836
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
20837
+ context.fillRect(rect.left + 2 * zoomFactor + 1.5, rect.top + (rect.height - iconSize) / 2 + 1.5, Math.max(0, iconSize - 3), Math.max(0, iconSize - 3));
20838
+ context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
20839
+ context.beginPath();
20840
+ context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
20841
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
20842
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
20843
+ context.stroke();
20844
+ }
20845
+ textLeft += iconSize + 4 * zoomFactor;
20846
+ } else if (control.kind === "radio") {
20847
+ context.beginPath();
20848
+ context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
20849
+ context.stroke();
20850
+ if (control.checked) {
20851
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
20852
+ context.beginPath();
20853
+ context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
20854
+ context.fill();
20855
+ }
20856
+ textLeft += iconSize + 4 * zoomFactor;
20857
+ }
20858
+ if (label) {
20859
+ const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);
20860
+ const text = truncateCanvasText(context, label, maxTextWidth);
20861
+ context.fillStyle = textColor;
20862
+ context.textAlign = control.textAlign === "right" ? "right" : control.textAlign === "center" ? "center" : "left";
20863
+ const textX = context.textAlign === "right" ? rect.left + rect.width - textRightInset : context.textAlign === "center" ? textLeft + maxTextWidth / 2 : textLeft;
20864
+ context.fillText(text, textX, rect.top + rect.height / 2);
20865
+ }
20866
+ if (control.kind === "dropdown") {
20867
+ context.fillStyle = textColor;
20868
+ context.textAlign = "center";
20869
+ context.fillText("\u25BC", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);
20870
+ }
20871
+ context.restore();
20872
+ }
20573
20873
  function resolveConditionalDataBarForCell(row, col, worksheet, sheet, metricsCache) {
20574
20874
  const rules = sheet?.conditionalFormatRules ?? [];
20575
20875
  const matchingRule = rules.find(
@@ -28299,27 +28599,95 @@ function useXlsxViewerCharts() {
28299
28599
  );
28300
28600
  }
28301
28601
  function useXlsxViewerThumbnails(options = {}) {
28302
- const { workbook, sheets } = useXlsxViewer();
28602
+ const { getSheetFormControls, getSheetImages, getSheetShapes, workbook, sheets } = useXlsxViewer();
28303
28603
  const { isDark } = React4.useContext(ViewerAppearanceContext);
28304
28604
  const palette = useViewerPalette(isDark);
28305
28605
  const includeHeaders = options.includeHeaders ?? true;
28306
28606
  const resolution = options.resolution;
28607
+ const thumbnailImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
28608
+ const isMountedRef = React4.useRef(false);
28609
+ const [thumbnailImageLoadVersion, setThumbnailImageLoadVersion] = React4.useState(0);
28610
+ React4.useEffect(() => {
28611
+ isMountedRef.current = true;
28612
+ return () => {
28613
+ isMountedRef.current = false;
28614
+ };
28615
+ }, []);
28616
+ const getThumbnailImage = React4.useCallback((image) => {
28617
+ if (typeof Image === "undefined") {
28618
+ return null;
28619
+ }
28620
+ const cacheKey = image.id;
28621
+ const cached = thumbnailImageCacheRef.current.get(cacheKey);
28622
+ if (cached && cached.src === image.src) {
28623
+ return cached.loaded && !cached.failed ? cached.image : null;
28624
+ }
28625
+ const imageElement = new Image();
28626
+ const entry = {
28627
+ failed: false,
28628
+ image: imageElement,
28629
+ loaded: false,
28630
+ src: image.src
28631
+ };
28632
+ thumbnailImageCacheRef.current.set(cacheKey, entry);
28633
+ imageElement.onload = () => {
28634
+ entry.loaded = true;
28635
+ if (isMountedRef.current) {
28636
+ setThumbnailImageLoadVersion((version) => version + 1);
28637
+ }
28638
+ };
28639
+ imageElement.onerror = () => {
28640
+ entry.failed = true;
28641
+ if (isMountedRef.current) {
28642
+ setThumbnailImageLoadVersion((version) => version + 1);
28643
+ }
28644
+ };
28645
+ imageElement.src = image.src;
28646
+ return null;
28647
+ }, []);
28307
28648
  const thumbnails = React4.useMemo(() => {
28308
28649
  return sheets.map((sheet, sheetIndex) => {
28309
28650
  const worksheet = workbook?.getSheet(sheet.workbookSheetIndex) ?? null;
28651
+ const sheetImages = getSheetImages(sheetIndex).filter((image) => image.src).sort((left, right) => left.zIndex - right.zIndex);
28652
+ const sheetShapes = getSheetShapes(sheetIndex);
28653
+ const sheetFormControls = getSheetFormControls(sheetIndex).filter((control) => !control.hidden);
28654
+ const drawingBounds = [
28655
+ ...sheetImages.map((image) => image.anchor),
28656
+ ...sheetShapes.map((shape) => shape.anchor),
28657
+ ...sheetFormControls.map((control) => control.anchor)
28658
+ ].reduce((bounds, anchor) => {
28659
+ const drawingBound = resolveAnchoredBounds(anchor) ?? {
28660
+ maxCol: 0,
28661
+ maxRow: 0,
28662
+ minCol: 0,
28663
+ minRow: 0
28664
+ };
28665
+ return {
28666
+ maxCol: Math.max(bounds?.maxCol ?? drawingBound.maxCol, drawingBound.maxCol),
28667
+ maxRow: Math.max(bounds?.maxRow ?? drawingBound.maxRow, drawingBound.maxRow),
28668
+ minCol: Math.min(bounds?.minCol ?? drawingBound.minCol, drawingBound.minCol),
28669
+ minRow: Math.min(bounds?.minRow ?? drawingBound.minRow, drawingBound.minRow)
28670
+ };
28671
+ }, null);
28672
+ const sheetMinUsedRow = (sheet.maxUsedRow ?? -1) >= (sheet.minUsedRow ?? 0) ? sheet.minUsedRow ?? 0 : void 0;
28673
+ const sheetMinUsedCol = (sheet.maxUsedCol ?? -1) >= (sheet.minUsedCol ?? 0) ? sheet.minUsedCol ?? 0 : void 0;
28674
+ const effectiveMinUsedRow = Math.max(0, Math.min(sheetMinUsedRow ?? drawingBounds?.minRow ?? 0, drawingBounds?.minRow ?? sheetMinUsedRow ?? 0));
28675
+ const effectiveMinUsedCol = Math.max(0, Math.min(sheetMinUsedCol ?? drawingBounds?.minCol ?? 0, drawingBounds?.minCol ?? sheetMinUsedCol ?? 0));
28676
+ const effectiveMaxUsedRow = Math.max(sheet.maxUsedRow ?? -1, drawingBounds?.maxRow ?? -1);
28677
+ const effectiveMaxUsedCol = Math.max(sheet.maxUsedCol ?? -1, drawingBounds?.maxCol ?? -1);
28310
28678
  const showGridLines = sheet.showGridLines ?? true;
28311
28679
  const hiddenRowSet = new Set(sheet.hiddenRows ?? []);
28312
28680
  const hiddenColSet = new Set(sheet.hiddenCols ?? []);
28313
28681
  const visibleRows = buildVisibleAxisIndices(
28314
28682
  sheet.visibleRows ?? [],
28315
- Math.max(THUMBNAIL_FALLBACK_ROWS, (sheet.maxUsedRow ?? -1) + THUMBNAIL_FALLBACK_ROWS + 1),
28316
- sheet.maxUsedRow ?? -1,
28683
+ Math.max(THUMBNAIL_FALLBACK_ROWS, effectiveMaxUsedRow + THUMBNAIL_FALLBACK_ROWS + 1),
28684
+ effectiveMaxUsedRow,
28317
28685
  hiddenRowSet
28318
28686
  );
28319
28687
  const visibleCols = buildVisibleAxisIndices(
28320
28688
  sheet.visibleCols ?? [],
28321
- Math.max(THUMBNAIL_FALLBACK_COLS, (sheet.maxUsedCol ?? -1) + THUMBNAIL_FALLBACK_COLS + 1),
28322
- sheet.maxUsedCol ?? -1,
28689
+ Math.max(THUMBNAIL_FALLBACK_COLS, effectiveMaxUsedCol + THUMBNAIL_FALLBACK_COLS + 1),
28690
+ effectiveMaxUsedCol,
28323
28691
  hiddenColSet
28324
28692
  );
28325
28693
  const resolveColumnWidthPx = (actualCol) => {
@@ -28363,8 +28731,8 @@ function useXlsxViewerThumbnails(options = {}) {
28363
28731
  getSizePx: resolveRowHeightPx,
28364
28732
  maxCount: THUMBNAIL_MAX_ROWS,
28365
28733
  maxLogicalPixels: THUMBNAIL_MAX_SOURCE_HEIGHT_PX,
28366
- maxUsedIndex: sheet.maxUsedRow ?? -1,
28367
- minUsedIndex: Math.max(0, sheet.minUsedRow ?? 0),
28734
+ maxUsedIndex: effectiveMaxUsedRow,
28735
+ minUsedIndex: effectiveMinUsedRow,
28368
28736
  precomputed: visibleRows
28369
28737
  });
28370
28738
  const previewCols = resolveThumbnailAxisIndices({
@@ -28372,8 +28740,8 @@ function useXlsxViewerThumbnails(options = {}) {
28372
28740
  getSizePx: resolveColumnWidthPx,
28373
28741
  maxCount: THUMBNAIL_MAX_COLS,
28374
28742
  maxLogicalPixels: THUMBNAIL_MAX_SOURCE_WIDTH_PX,
28375
- maxUsedIndex: sheet.maxUsedCol ?? -1,
28376
- minUsedIndex: Math.max(0, sheet.minUsedCol ?? 0),
28743
+ maxUsedIndex: effectiveMaxUsedCol,
28744
+ minUsedIndex: effectiveMinUsedCol,
28377
28745
  precomputed: visibleCols
28378
28746
  });
28379
28747
  const rowAxis = buildThumbnailAxisItems(previewRows, resolveRowHeightPx);
@@ -28398,6 +28766,74 @@ function useXlsxViewerThumbnails(options = {}) {
28398
28766
  const sparklineByCell = new Map(
28399
28767
  (sheet.sparklines ?? []).map((sparkline) => [`${sparkline.target.row}:${sparkline.target.col}`, sparkline])
28400
28768
  );
28769
+ const thumbnailImageRects = sheetImages.map((image) => ({
28770
+ image,
28771
+ rect: resolveThumbnailAnchoredRect(
28772
+ image.anchor,
28773
+ visibleRows,
28774
+ visibleCols,
28775
+ resolveRowHeightPx,
28776
+ resolveColumnWidthPx,
28777
+ previewRows,
28778
+ previewCols,
28779
+ {
28780
+ headerHeight,
28781
+ rowHeaderWidth
28782
+ }
28783
+ )
28784
+ })).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
28785
+ const thumbnailShapeRects = sheetShapes.map((shape) => ({
28786
+ rect: resolveThumbnailAnchoredRect(
28787
+ shape.anchor,
28788
+ visibleRows,
28789
+ visibleCols,
28790
+ resolveRowHeightPx,
28791
+ resolveColumnWidthPx,
28792
+ previewRows,
28793
+ previewCols,
28794
+ {
28795
+ headerHeight,
28796
+ rowHeaderWidth
28797
+ }
28798
+ ),
28799
+ shape
28800
+ })).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
28801
+ const thumbnailFormControlRects = sheetFormControls.map((control) => ({
28802
+ control,
28803
+ rect: resolveThumbnailAnchoredRect(
28804
+ control.anchor,
28805
+ visibleRows,
28806
+ visibleCols,
28807
+ resolveRowHeightPx,
28808
+ resolveColumnWidthPx,
28809
+ previewRows,
28810
+ previewCols,
28811
+ {
28812
+ headerHeight,
28813
+ rowHeaderWidth
28814
+ }
28815
+ )
28816
+ })).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
28817
+ const thumbnailDrawingEntries = [
28818
+ ...thumbnailShapeRects.map(({ rect, shape }) => ({
28819
+ kind: "shape",
28820
+ rect,
28821
+ shape,
28822
+ zIndex: shape.zIndex
28823
+ })),
28824
+ ...thumbnailFormControlRects.map(({ control, rect }) => ({
28825
+ control,
28826
+ kind: "formControl",
28827
+ rect,
28828
+ zIndex: control.zIndex
28829
+ })),
28830
+ ...thumbnailImageRects.map(({ image, rect }) => ({
28831
+ image,
28832
+ kind: "image",
28833
+ rect,
28834
+ zIndex: image.zIndex
28835
+ }))
28836
+ ].sort((left, right) => left.zIndex - right.zIndex);
28401
28837
  const paint = (canvas) => {
28402
28838
  if (!canvas) {
28403
28839
  return false;
@@ -28710,6 +29146,25 @@ function useXlsxViewerThumbnails(options = {}) {
28710
29146
  context.restore();
28711
29147
  }
28712
29148
  }
29149
+ if (thumbnailDrawingEntries.length > 0) {
29150
+ context.save();
29151
+ context.beginPath();
29152
+ context.rect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
29153
+ context.clip();
29154
+ for (const entry of thumbnailDrawingEntries) {
29155
+ if (entry.kind === "shape") {
29156
+ drawStaticShape(context, entry.shape, entry.rect, 1);
29157
+ } else if (entry.kind === "formControl") {
29158
+ drawStaticFormControl(context, entry.control, entry.rect, palette, 1, thumbnailSheetSurface);
29159
+ } else {
29160
+ const imageElement = getThumbnailImage(entry.image);
29161
+ if (imageElement) {
29162
+ context.drawImage(imageElement, entry.rect.left, entry.rect.top, entry.rect.width, entry.rect.height);
29163
+ }
29164
+ }
29165
+ }
29166
+ context.restore();
29167
+ }
28713
29168
  if (includeHeaders) {
28714
29169
  context.strokeStyle = palette.border;
28715
29170
  context.lineWidth = 1;
@@ -28763,7 +29218,18 @@ function useXlsxViewerThumbnails(options = {}) {
28763
29218
  workbookSheetIndex: sheet.workbookSheetIndex
28764
29219
  };
28765
29220
  });
28766
- }, [includeHeaders, palette, resolution, sheets, workbook]);
29221
+ }, [
29222
+ getSheetFormControls,
29223
+ getSheetImages,
29224
+ getSheetShapes,
29225
+ getThumbnailImage,
29226
+ includeHeaders,
29227
+ palette,
29228
+ resolution,
29229
+ sheets,
29230
+ thumbnailImageLoadVersion,
29231
+ workbook
29232
+ ]);
28767
29233
  const paintThumbnail = React4.useCallback(
28768
29234
  (sheetIndex, canvas) => thumbnails[sheetIndex]?.paint(canvas) ?? false,
28769
29235
  [thumbnails]