@extend-ai/react-xlsx 0.8.4 → 0.8.7

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(
@@ -27931,11 +28231,13 @@ function XlsxViewerInner({
27931
28231
  flex: "1 1 auto",
27932
28232
  flexDirection: "column",
27933
28233
  inlineSize: "100%",
28234
+ isolation: "isolate",
27934
28235
  maxHeight: "100%",
27935
28236
  maxWidth: "100%",
27936
28237
  minHeight: 0,
27937
28238
  minWidth: 0,
27938
28239
  overflow: "hidden",
28240
+ position: "relative",
27939
28241
  width: "100%"
27940
28242
  },
27941
28243
  children: [
@@ -28299,27 +28601,95 @@ function useXlsxViewerCharts() {
28299
28601
  );
28300
28602
  }
28301
28603
  function useXlsxViewerThumbnails(options = {}) {
28302
- const { workbook, sheets } = useXlsxViewer();
28604
+ const { getSheetFormControls, getSheetImages, getSheetShapes, workbook, sheets } = useXlsxViewer();
28303
28605
  const { isDark } = React4.useContext(ViewerAppearanceContext);
28304
28606
  const palette = useViewerPalette(isDark);
28305
28607
  const includeHeaders = options.includeHeaders ?? true;
28306
28608
  const resolution = options.resolution;
28609
+ const thumbnailImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
28610
+ const isMountedRef = React4.useRef(false);
28611
+ const [thumbnailImageLoadVersion, setThumbnailImageLoadVersion] = React4.useState(0);
28612
+ React4.useEffect(() => {
28613
+ isMountedRef.current = true;
28614
+ return () => {
28615
+ isMountedRef.current = false;
28616
+ };
28617
+ }, []);
28618
+ const getThumbnailImage = React4.useCallback((image) => {
28619
+ if (typeof Image === "undefined") {
28620
+ return null;
28621
+ }
28622
+ const cacheKey = image.id;
28623
+ const cached = thumbnailImageCacheRef.current.get(cacheKey);
28624
+ if (cached && cached.src === image.src) {
28625
+ return cached.loaded && !cached.failed ? cached.image : null;
28626
+ }
28627
+ const imageElement = new Image();
28628
+ const entry = {
28629
+ failed: false,
28630
+ image: imageElement,
28631
+ loaded: false,
28632
+ src: image.src
28633
+ };
28634
+ thumbnailImageCacheRef.current.set(cacheKey, entry);
28635
+ imageElement.onload = () => {
28636
+ entry.loaded = true;
28637
+ if (isMountedRef.current) {
28638
+ setThumbnailImageLoadVersion((version) => version + 1);
28639
+ }
28640
+ };
28641
+ imageElement.onerror = () => {
28642
+ entry.failed = true;
28643
+ if (isMountedRef.current) {
28644
+ setThumbnailImageLoadVersion((version) => version + 1);
28645
+ }
28646
+ };
28647
+ imageElement.src = image.src;
28648
+ return null;
28649
+ }, []);
28307
28650
  const thumbnails = React4.useMemo(() => {
28308
28651
  return sheets.map((sheet, sheetIndex) => {
28309
28652
  const worksheet = workbook?.getSheet(sheet.workbookSheetIndex) ?? null;
28653
+ const sheetImages = getSheetImages(sheetIndex).filter((image) => image.src).sort((left, right) => left.zIndex - right.zIndex);
28654
+ const sheetShapes = getSheetShapes(sheetIndex);
28655
+ const sheetFormControls = getSheetFormControls(sheetIndex).filter((control) => !control.hidden);
28656
+ const drawingBounds = [
28657
+ ...sheetImages.map((image) => image.anchor),
28658
+ ...sheetShapes.map((shape) => shape.anchor),
28659
+ ...sheetFormControls.map((control) => control.anchor)
28660
+ ].reduce((bounds, anchor) => {
28661
+ const drawingBound = resolveAnchoredBounds(anchor) ?? {
28662
+ maxCol: 0,
28663
+ maxRow: 0,
28664
+ minCol: 0,
28665
+ minRow: 0
28666
+ };
28667
+ return {
28668
+ maxCol: Math.max(bounds?.maxCol ?? drawingBound.maxCol, drawingBound.maxCol),
28669
+ maxRow: Math.max(bounds?.maxRow ?? drawingBound.maxRow, drawingBound.maxRow),
28670
+ minCol: Math.min(bounds?.minCol ?? drawingBound.minCol, drawingBound.minCol),
28671
+ minRow: Math.min(bounds?.minRow ?? drawingBound.minRow, drawingBound.minRow)
28672
+ };
28673
+ }, null);
28674
+ const sheetMinUsedRow = (sheet.maxUsedRow ?? -1) >= (sheet.minUsedRow ?? 0) ? sheet.minUsedRow ?? 0 : void 0;
28675
+ const sheetMinUsedCol = (sheet.maxUsedCol ?? -1) >= (sheet.minUsedCol ?? 0) ? sheet.minUsedCol ?? 0 : void 0;
28676
+ const effectiveMinUsedRow = Math.max(0, Math.min(sheetMinUsedRow ?? drawingBounds?.minRow ?? 0, drawingBounds?.minRow ?? sheetMinUsedRow ?? 0));
28677
+ const effectiveMinUsedCol = Math.max(0, Math.min(sheetMinUsedCol ?? drawingBounds?.minCol ?? 0, drawingBounds?.minCol ?? sheetMinUsedCol ?? 0));
28678
+ const effectiveMaxUsedRow = Math.max(sheet.maxUsedRow ?? -1, drawingBounds?.maxRow ?? -1);
28679
+ const effectiveMaxUsedCol = Math.max(sheet.maxUsedCol ?? -1, drawingBounds?.maxCol ?? -1);
28310
28680
  const showGridLines = sheet.showGridLines ?? true;
28311
28681
  const hiddenRowSet = new Set(sheet.hiddenRows ?? []);
28312
28682
  const hiddenColSet = new Set(sheet.hiddenCols ?? []);
28313
28683
  const visibleRows = buildVisibleAxisIndices(
28314
28684
  sheet.visibleRows ?? [],
28315
- Math.max(THUMBNAIL_FALLBACK_ROWS, (sheet.maxUsedRow ?? -1) + THUMBNAIL_FALLBACK_ROWS + 1),
28316
- sheet.maxUsedRow ?? -1,
28685
+ Math.max(THUMBNAIL_FALLBACK_ROWS, effectiveMaxUsedRow + THUMBNAIL_FALLBACK_ROWS + 1),
28686
+ effectiveMaxUsedRow,
28317
28687
  hiddenRowSet
28318
28688
  );
28319
28689
  const visibleCols = buildVisibleAxisIndices(
28320
28690
  sheet.visibleCols ?? [],
28321
- Math.max(THUMBNAIL_FALLBACK_COLS, (sheet.maxUsedCol ?? -1) + THUMBNAIL_FALLBACK_COLS + 1),
28322
- sheet.maxUsedCol ?? -1,
28691
+ Math.max(THUMBNAIL_FALLBACK_COLS, effectiveMaxUsedCol + THUMBNAIL_FALLBACK_COLS + 1),
28692
+ effectiveMaxUsedCol,
28323
28693
  hiddenColSet
28324
28694
  );
28325
28695
  const resolveColumnWidthPx = (actualCol) => {
@@ -28363,8 +28733,8 @@ function useXlsxViewerThumbnails(options = {}) {
28363
28733
  getSizePx: resolveRowHeightPx,
28364
28734
  maxCount: THUMBNAIL_MAX_ROWS,
28365
28735
  maxLogicalPixels: THUMBNAIL_MAX_SOURCE_HEIGHT_PX,
28366
- maxUsedIndex: sheet.maxUsedRow ?? -1,
28367
- minUsedIndex: Math.max(0, sheet.minUsedRow ?? 0),
28736
+ maxUsedIndex: effectiveMaxUsedRow,
28737
+ minUsedIndex: effectiveMinUsedRow,
28368
28738
  precomputed: visibleRows
28369
28739
  });
28370
28740
  const previewCols = resolveThumbnailAxisIndices({
@@ -28372,8 +28742,8 @@ function useXlsxViewerThumbnails(options = {}) {
28372
28742
  getSizePx: resolveColumnWidthPx,
28373
28743
  maxCount: THUMBNAIL_MAX_COLS,
28374
28744
  maxLogicalPixels: THUMBNAIL_MAX_SOURCE_WIDTH_PX,
28375
- maxUsedIndex: sheet.maxUsedCol ?? -1,
28376
- minUsedIndex: Math.max(0, sheet.minUsedCol ?? 0),
28745
+ maxUsedIndex: effectiveMaxUsedCol,
28746
+ minUsedIndex: effectiveMinUsedCol,
28377
28747
  precomputed: visibleCols
28378
28748
  });
28379
28749
  const rowAxis = buildThumbnailAxisItems(previewRows, resolveRowHeightPx);
@@ -28398,6 +28768,74 @@ function useXlsxViewerThumbnails(options = {}) {
28398
28768
  const sparklineByCell = new Map(
28399
28769
  (sheet.sparklines ?? []).map((sparkline) => [`${sparkline.target.row}:${sparkline.target.col}`, sparkline])
28400
28770
  );
28771
+ const thumbnailImageRects = sheetImages.map((image) => ({
28772
+ image,
28773
+ rect: resolveThumbnailAnchoredRect(
28774
+ image.anchor,
28775
+ visibleRows,
28776
+ visibleCols,
28777
+ resolveRowHeightPx,
28778
+ resolveColumnWidthPx,
28779
+ previewRows,
28780
+ previewCols,
28781
+ {
28782
+ headerHeight,
28783
+ rowHeaderWidth
28784
+ }
28785
+ )
28786
+ })).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
28787
+ const thumbnailShapeRects = sheetShapes.map((shape) => ({
28788
+ rect: resolveThumbnailAnchoredRect(
28789
+ shape.anchor,
28790
+ visibleRows,
28791
+ visibleCols,
28792
+ resolveRowHeightPx,
28793
+ resolveColumnWidthPx,
28794
+ previewRows,
28795
+ previewCols,
28796
+ {
28797
+ headerHeight,
28798
+ rowHeaderWidth
28799
+ }
28800
+ ),
28801
+ shape
28802
+ })).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
28803
+ const thumbnailFormControlRects = sheetFormControls.map((control) => ({
28804
+ control,
28805
+ rect: resolveThumbnailAnchoredRect(
28806
+ control.anchor,
28807
+ visibleRows,
28808
+ visibleCols,
28809
+ resolveRowHeightPx,
28810
+ resolveColumnWidthPx,
28811
+ previewRows,
28812
+ previewCols,
28813
+ {
28814
+ headerHeight,
28815
+ rowHeaderWidth
28816
+ }
28817
+ )
28818
+ })).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
28819
+ const thumbnailDrawingEntries = [
28820
+ ...thumbnailShapeRects.map(({ rect, shape }) => ({
28821
+ kind: "shape",
28822
+ rect,
28823
+ shape,
28824
+ zIndex: shape.zIndex
28825
+ })),
28826
+ ...thumbnailFormControlRects.map(({ control, rect }) => ({
28827
+ control,
28828
+ kind: "formControl",
28829
+ rect,
28830
+ zIndex: control.zIndex
28831
+ })),
28832
+ ...thumbnailImageRects.map(({ image, rect }) => ({
28833
+ image,
28834
+ kind: "image",
28835
+ rect,
28836
+ zIndex: image.zIndex
28837
+ }))
28838
+ ].sort((left, right) => left.zIndex - right.zIndex);
28401
28839
  const paint = (canvas) => {
28402
28840
  if (!canvas) {
28403
28841
  return false;
@@ -28710,6 +29148,25 @@ function useXlsxViewerThumbnails(options = {}) {
28710
29148
  context.restore();
28711
29149
  }
28712
29150
  }
29151
+ if (thumbnailDrawingEntries.length > 0) {
29152
+ context.save();
29153
+ context.beginPath();
29154
+ context.rect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
29155
+ context.clip();
29156
+ for (const entry of thumbnailDrawingEntries) {
29157
+ if (entry.kind === "shape") {
29158
+ drawStaticShape(context, entry.shape, entry.rect, 1);
29159
+ } else if (entry.kind === "formControl") {
29160
+ drawStaticFormControl(context, entry.control, entry.rect, palette, 1, thumbnailSheetSurface);
29161
+ } else {
29162
+ const imageElement = getThumbnailImage(entry.image);
29163
+ if (imageElement) {
29164
+ context.drawImage(imageElement, entry.rect.left, entry.rect.top, entry.rect.width, entry.rect.height);
29165
+ }
29166
+ }
29167
+ }
29168
+ context.restore();
29169
+ }
28713
29170
  if (includeHeaders) {
28714
29171
  context.strokeStyle = palette.border;
28715
29172
  context.lineWidth = 1;
@@ -28763,7 +29220,18 @@ function useXlsxViewerThumbnails(options = {}) {
28763
29220
  workbookSheetIndex: sheet.workbookSheetIndex
28764
29221
  };
28765
29222
  });
28766
- }, [includeHeaders, palette, resolution, sheets, workbook]);
29223
+ }, [
29224
+ getSheetFormControls,
29225
+ getSheetImages,
29226
+ getSheetShapes,
29227
+ getThumbnailImage,
29228
+ includeHeaders,
29229
+ palette,
29230
+ resolution,
29231
+ sheets,
29232
+ thumbnailImageLoadVersion,
29233
+ workbook
29234
+ ]);
28767
29235
  const paintThumbnail = React4.useCallback(
28768
29236
  (sheetIndex, canvas) => thumbnails[sheetIndex]?.paint(canvas) ?? false,
28769
29237
  [thumbnails]