@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.js CHANGED
@@ -17570,6 +17570,25 @@ function resolveImageAnchorExtents(image) {
17570
17570
  maxRow: Math.max(image.anchor.from.row, image.anchor.to.row)
17571
17571
  };
17572
17572
  }
17573
+ function resolveAnchoredBounds(anchor) {
17574
+ if (anchor.kind === "absolute") {
17575
+ return null;
17576
+ }
17577
+ if (anchor.kind === "one-cell") {
17578
+ return {
17579
+ maxCol: anchor.from.col,
17580
+ maxRow: anchor.from.row,
17581
+ minCol: anchor.from.col,
17582
+ minRow: anchor.from.row
17583
+ };
17584
+ }
17585
+ return {
17586
+ maxCol: Math.max(anchor.from.col, anchor.to.col),
17587
+ maxRow: Math.max(anchor.from.row, anchor.to.row),
17588
+ minCol: Math.min(anchor.from.col, anchor.to.col),
17589
+ minRow: Math.min(anchor.from.row, anchor.to.row)
17590
+ };
17591
+ }
17573
17592
  function resolveShapeAnchorExtents(shape) {
17574
17593
  if (shape.anchor.kind === "absolute") {
17575
17594
  return { maxCol: 0, maxRow: 0 };
@@ -18753,6 +18772,71 @@ function buildThumbnailAxisItems(actualIndices, getSizePx) {
18753
18772
  totalSize: offset
18754
18773
  };
18755
18774
  }
18775
+ function resolveThumbnailAxisAbsoluteOffset(actualIndices, getSizePx, actualIndex) {
18776
+ let total = 0;
18777
+ for (const candidate of actualIndices) {
18778
+ if (candidate >= actualIndex) {
18779
+ break;
18780
+ }
18781
+ total += Math.max(1, getSizePx(candidate));
18782
+ }
18783
+ return total;
18784
+ }
18785
+ function resolveThumbnailAxisMarkerOffset({
18786
+ actualIndex,
18787
+ actualIndices,
18788
+ getSizePx,
18789
+ offsetEmu,
18790
+ previewStartActualIndex
18791
+ }) {
18792
+ return resolveThumbnailAxisAbsoluteOffset(actualIndices, getSizePx, actualIndex) - resolveThumbnailAxisAbsoluteOffset(actualIndices, getSizePx, previewStartActualIndex) + emuToPixels(offsetEmu);
18793
+ }
18794
+ function resolveThumbnailAnchoredRect(anchor, visibleRows, visibleCols, getRowHeightPx, getColWidthPx, previewRows, previewCols, options) {
18795
+ const previewStartRow = previewRows[0] ?? 0;
18796
+ const previewStartCol = previewCols[0] ?? 0;
18797
+ const previewOriginX = resolveThumbnailAxisAbsoluteOffset(visibleCols, getColWidthPx, previewStartCol);
18798
+ const previewOriginY = resolveThumbnailAxisAbsoluteOffset(visibleRows, getRowHeightPx, previewStartRow);
18799
+ const resolveMarkerLeft = (col, colOffsetEmu) => options.rowHeaderWidth + resolveThumbnailAxisMarkerOffset({
18800
+ actualIndex: col,
18801
+ actualIndices: visibleCols,
18802
+ getSizePx: getColWidthPx,
18803
+ offsetEmu: colOffsetEmu,
18804
+ previewStartActualIndex: previewStartCol
18805
+ });
18806
+ const resolveMarkerTop = (row, rowOffsetEmu) => options.headerHeight + resolveThumbnailAxisMarkerOffset({
18807
+ actualIndex: row,
18808
+ actualIndices: visibleRows,
18809
+ getSizePx: getRowHeightPx,
18810
+ offsetEmu: rowOffsetEmu,
18811
+ previewStartActualIndex: previewStartRow
18812
+ });
18813
+ if (anchor.kind === "absolute") {
18814
+ return {
18815
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18816
+ left: options.rowHeaderWidth + emuToPixels(anchor.positionEmu.x) - previewOriginX,
18817
+ top: options.headerHeight + emuToPixels(anchor.positionEmu.y) - previewOriginY,
18818
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18819
+ };
18820
+ }
18821
+ if (anchor.kind === "one-cell") {
18822
+ return {
18823
+ height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
18824
+ left: resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu),
18825
+ top: resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu),
18826
+ width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
18827
+ };
18828
+ }
18829
+ const left = resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu);
18830
+ const top = resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu);
18831
+ const right = resolveMarkerLeft(anchor.to.col, anchor.to.colOffsetEmu);
18832
+ const bottom = resolveMarkerTop(anchor.to.row, anchor.to.rowOffsetEmu);
18833
+ return {
18834
+ height: Math.max(1, bottom - top),
18835
+ left,
18836
+ top,
18837
+ width: Math.max(1, right - left)
18838
+ };
18839
+ }
18756
18840
  function columnLabel2(col) {
18757
18841
  let label = "";
18758
18842
  let nextValue = col;
@@ -20538,6 +20622,222 @@ function resolveFormControlLabel(control) {
20538
20622
  }
20539
20623
  return label.replace(/\u00a0/g, " ").replace(/^\s+/, "");
20540
20624
  }
20625
+ function drawStaticShapeText(context, shape, left, top, width, height, zoomFactor) {
20626
+ if (shape.paragraphs.length === 0) {
20627
+ return;
20628
+ }
20629
+ const inset = shape.textBox?.insetPx;
20630
+ const paddingLeft = (inset?.left ?? 6) * zoomFactor;
20631
+ const paddingRight = (inset?.right ?? 6) * zoomFactor;
20632
+ const paddingTop = (inset?.top ?? 4) * zoomFactor;
20633
+ const paddingBottom = (inset?.bottom ?? 4) * zoomFactor;
20634
+ const textLeft = left + paddingLeft;
20635
+ const textTop = top + paddingTop;
20636
+ const textWidth = Math.max(0, width - paddingLeft - paddingRight);
20637
+ const textHeight = Math.max(0, height - paddingTop - paddingBottom);
20638
+ if (textWidth <= 0 || textHeight <= 0) {
20639
+ return;
20640
+ }
20641
+ const lineMetrics = shape.paragraphs.map((paragraph) => {
20642
+ const lineHeight = Math.max(
20643
+ 12 * zoomFactor,
20644
+ ...paragraph.runs.map((run) => (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor * 1.2)
20645
+ );
20646
+ const widthPx = paragraph.runs.reduce((total, run) => {
20647
+ const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
20648
+ context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
20649
+ return total + measureCanvasTextWidth(context, run.text);
20650
+ }, 0);
20651
+ return { lineHeight, widthPx };
20652
+ });
20653
+ const totalTextHeight = lineMetrics.reduce((total, metric) => total + metric.lineHeight, 0);
20654
+ let y = textTop;
20655
+ if (shape.textBox?.verticalAlign === "middle") {
20656
+ y = textTop + Math.max(0, (textHeight - totalTextHeight) / 2);
20657
+ } else if (shape.textBox?.verticalAlign === "bottom") {
20658
+ y = textTop + Math.max(0, textHeight - totalTextHeight);
20659
+ }
20660
+ context.save();
20661
+ context.beginPath();
20662
+ context.rect(textLeft, textTop, textWidth, textHeight);
20663
+ context.clip();
20664
+ context.textBaseline = "middle";
20665
+ shape.paragraphs.forEach((paragraph, paragraphIndex) => {
20666
+ const metric = lineMetrics[paragraphIndex] ?? { lineHeight: 14 * zoomFactor, widthPx: 0 };
20667
+ let x = textLeft;
20668
+ const align = paragraph.align ?? shape.textBox?.horizontalAlign ?? "left";
20669
+ if (align === "center") {
20670
+ x = textLeft + Math.max(0, (textWidth - metric.widthPx) / 2);
20671
+ } else if (align === "right") {
20672
+ x = textLeft + Math.max(0, textWidth - metric.widthPx);
20673
+ }
20674
+ paragraph.runs.forEach((run) => {
20675
+ const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
20676
+ context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
20677
+ context.fillStyle = run.color ?? "#000000";
20678
+ context.textAlign = "left";
20679
+ const textY = y + metric.lineHeight / 2;
20680
+ context.fillText(run.text, x, textY);
20681
+ if (run.underline && run.text.length > 0) {
20682
+ const textWidthPx = measureCanvasTextWidth(context, run.text);
20683
+ context.strokeStyle = run.color ?? "#000000";
20684
+ context.lineWidth = Math.max(1, zoomFactor * 0.75);
20685
+ context.beginPath();
20686
+ context.moveTo(x, textY + Math.max(2, fontSize * 0.28));
20687
+ context.lineTo(x + textWidthPx, textY + Math.max(2, fontSize * 0.28));
20688
+ context.stroke();
20689
+ }
20690
+ x += measureCanvasTextWidth(context, run.text);
20691
+ });
20692
+ y += metric.lineHeight;
20693
+ });
20694
+ context.restore();
20695
+ }
20696
+ function drawStaticShape(context, shape, rect, zoomFactor) {
20697
+ const fillColor = shape.fill?.none ? "transparent" : shape.fill?.color ?? "transparent";
20698
+ const strokeColor = shape.stroke?.none ? "transparent" : shape.stroke?.color ?? "transparent";
20699
+ const lineWidth = Math.max(0, (shape.stroke?.widthPx ?? (shape.geometry === "line" ? 2 : 1)) * zoomFactor);
20700
+ const vectorShape = resolveShapeVector(shape);
20701
+ const opacity = Math.min(shape.fill?.opacity ?? 1, shape.stroke?.opacity ?? 1);
20702
+ context.save();
20703
+ context.translate(rect.left + rect.width / 2, rect.top + rect.height / 2);
20704
+ if (shape.rotationDeg) {
20705
+ context.rotate(shape.rotationDeg * Math.PI / 180);
20706
+ }
20707
+ context.scale(shape.flipH ? -1 : 1, shape.flipV ? -1 : 1);
20708
+ context.globalAlpha *= opacity;
20709
+ context.lineWidth = lineWidth;
20710
+ context.strokeStyle = strokeColor;
20711
+ context.fillStyle = fillColor;
20712
+ applyCanvasShapeDash(context, shape.stroke?.dash, lineWidth);
20713
+ const localLeft = -rect.width / 2;
20714
+ const localTop = -rect.height / 2;
20715
+ if (vectorShape && typeof Path2D !== "undefined") {
20716
+ context.save();
20717
+ context.translate(localLeft, localTop);
20718
+ context.scale(
20719
+ rect.width / Math.max(1, vectorShape.viewBox.width),
20720
+ rect.height / Math.max(1, vectorShape.viewBox.height)
20721
+ );
20722
+ const path = getCachedCanvasPath2D(vectorShape.path);
20723
+ if (!path) {
20724
+ context.restore();
20725
+ context.restore();
20726
+ return;
20727
+ }
20728
+ if (fillColor !== "transparent") {
20729
+ context.fill(path);
20730
+ }
20731
+ if (strokeColor !== "transparent" && lineWidth > 0) {
20732
+ context.stroke(path);
20733
+ }
20734
+ context.restore();
20735
+ } else if (shape.geometry === "ellipse") {
20736
+ context.beginPath();
20737
+ context.ellipse(0, 0, rect.width / 2, rect.height / 2, 0, 0, Math.PI * 2);
20738
+ if (fillColor !== "transparent") {
20739
+ context.fill();
20740
+ }
20741
+ if (strokeColor !== "transparent" && lineWidth > 0) {
20742
+ context.stroke();
20743
+ }
20744
+ } else {
20745
+ const radius = shape.geometry === "roundRect" ? 12 * zoomFactor : 0;
20746
+ drawCanvasRoundedRect(context, localLeft, localTop, rect.width, rect.height, radius);
20747
+ if (fillColor !== "transparent") {
20748
+ context.fill();
20749
+ }
20750
+ if (strokeColor !== "transparent" && lineWidth > 0) {
20751
+ context.stroke();
20752
+ }
20753
+ }
20754
+ drawStaticShapeText(context, shape, localLeft, localTop, rect.width, rect.height, zoomFactor);
20755
+ context.restore();
20756
+ }
20757
+ function drawStaticFormControl(context, control, rect, palette, zoomFactor, sheetSurface = SHEET_SURFACE) {
20758
+ const label = resolveFormControlLabel(control);
20759
+ const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
20760
+ const textColor = control.textColor ?? "#000000";
20761
+ const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
20762
+ const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
20763
+ context.save();
20764
+ context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
20765
+ context.textBaseline = "middle";
20766
+ context.fillStyle = textColor;
20767
+ context.strokeStyle = stroke;
20768
+ context.lineWidth = Math.max(1, zoomFactor);
20769
+ if (control.kind === "group-box") {
20770
+ const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;
20771
+ drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);
20772
+ context.stroke();
20773
+ if (label) {
20774
+ context.fillStyle = sheetSurface;
20775
+ const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);
20776
+ context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);
20777
+ context.fillStyle = textColor;
20778
+ context.textAlign = "left";
20779
+ context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);
20780
+ }
20781
+ context.restore();
20782
+ return;
20783
+ }
20784
+ if (control.kind === "button" || control.kind === "dropdown" || control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
20785
+ if (control.kind === "button") {
20786
+ const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);
20787
+ gradient.addColorStop(0, "#f8fafc");
20788
+ gradient.addColorStop(1, "#e2e8f0");
20789
+ context.fillStyle = gradient;
20790
+ } else {
20791
+ context.fillStyle = "transparent";
20792
+ }
20793
+ drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === "button" ? 4 * zoomFactor : 2 * zoomFactor);
20794
+ if (control.kind === "button") {
20795
+ context.fill();
20796
+ }
20797
+ context.stroke();
20798
+ }
20799
+ let textLeft = rect.left + 2 * zoomFactor;
20800
+ const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
20801
+ if (control.kind === "checkbox") {
20802
+ context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
20803
+ if (control.checked) {
20804
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
20805
+ 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));
20806
+ context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
20807
+ context.beginPath();
20808
+ context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
20809
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
20810
+ context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
20811
+ context.stroke();
20812
+ }
20813
+ textLeft += iconSize + 4 * zoomFactor;
20814
+ } else if (control.kind === "radio") {
20815
+ context.beginPath();
20816
+ context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
20817
+ context.stroke();
20818
+ if (control.checked) {
20819
+ context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
20820
+ context.beginPath();
20821
+ context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
20822
+ context.fill();
20823
+ }
20824
+ textLeft += iconSize + 4 * zoomFactor;
20825
+ }
20826
+ if (label) {
20827
+ const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);
20828
+ const text = truncateCanvasText(context, label, maxTextWidth);
20829
+ context.fillStyle = textColor;
20830
+ context.textAlign = control.textAlign === "right" ? "right" : control.textAlign === "center" ? "center" : "left";
20831
+ const textX = context.textAlign === "right" ? rect.left + rect.width - textRightInset : context.textAlign === "center" ? textLeft + maxTextWidth / 2 : textLeft;
20832
+ context.fillText(text, textX, rect.top + rect.height / 2);
20833
+ }
20834
+ if (control.kind === "dropdown") {
20835
+ context.fillStyle = textColor;
20836
+ context.textAlign = "center";
20837
+ context.fillText("\u25BC", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);
20838
+ }
20839
+ context.restore();
20840
+ }
20541
20841
  function resolveConditionalDataBarForCell(row, col, worksheet, sheet, metricsCache) {
20542
20842
  const rules = sheet?.conditionalFormatRules ?? [];
20543
20843
  const matchingRule = rules.find(
@@ -27899,11 +28199,13 @@ function XlsxViewerInner({
27899
28199
  flex: "1 1 auto",
27900
28200
  flexDirection: "column",
27901
28201
  inlineSize: "100%",
28202
+ isolation: "isolate",
27902
28203
  maxHeight: "100%",
27903
28204
  maxWidth: "100%",
27904
28205
  minHeight: 0,
27905
28206
  minWidth: 0,
27906
28207
  overflow: "hidden",
28208
+ position: "relative",
27907
28209
  width: "100%"
27908
28210
  },
27909
28211
  children: [
@@ -28267,27 +28569,95 @@ function useXlsxViewerCharts() {
28267
28569
  );
28268
28570
  }
28269
28571
  function useXlsxViewerThumbnails(options = {}) {
28270
- const { workbook, sheets } = useXlsxViewer();
28572
+ const { getSheetFormControls, getSheetImages, getSheetShapes, workbook, sheets } = useXlsxViewer();
28271
28573
  const { isDark } = React4.useContext(ViewerAppearanceContext);
28272
28574
  const palette = useViewerPalette(isDark);
28273
28575
  const includeHeaders = options.includeHeaders ?? true;
28274
28576
  const resolution = options.resolution;
28577
+ const thumbnailImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
28578
+ const isMountedRef = React4.useRef(false);
28579
+ const [thumbnailImageLoadVersion, setThumbnailImageLoadVersion] = React4.useState(0);
28580
+ React4.useEffect(() => {
28581
+ isMountedRef.current = true;
28582
+ return () => {
28583
+ isMountedRef.current = false;
28584
+ };
28585
+ }, []);
28586
+ const getThumbnailImage = React4.useCallback((image) => {
28587
+ if (typeof Image === "undefined") {
28588
+ return null;
28589
+ }
28590
+ const cacheKey = image.id;
28591
+ const cached = thumbnailImageCacheRef.current.get(cacheKey);
28592
+ if (cached && cached.src === image.src) {
28593
+ return cached.loaded && !cached.failed ? cached.image : null;
28594
+ }
28595
+ const imageElement = new Image();
28596
+ const entry = {
28597
+ failed: false,
28598
+ image: imageElement,
28599
+ loaded: false,
28600
+ src: image.src
28601
+ };
28602
+ thumbnailImageCacheRef.current.set(cacheKey, entry);
28603
+ imageElement.onload = () => {
28604
+ entry.loaded = true;
28605
+ if (isMountedRef.current) {
28606
+ setThumbnailImageLoadVersion((version) => version + 1);
28607
+ }
28608
+ };
28609
+ imageElement.onerror = () => {
28610
+ entry.failed = true;
28611
+ if (isMountedRef.current) {
28612
+ setThumbnailImageLoadVersion((version) => version + 1);
28613
+ }
28614
+ };
28615
+ imageElement.src = image.src;
28616
+ return null;
28617
+ }, []);
28275
28618
  const thumbnails = React4.useMemo(() => {
28276
28619
  return sheets.map((sheet, sheetIndex) => {
28277
28620
  const worksheet = workbook?.getSheet(sheet.workbookSheetIndex) ?? null;
28621
+ const sheetImages = getSheetImages(sheetIndex).filter((image) => image.src).sort((left, right) => left.zIndex - right.zIndex);
28622
+ const sheetShapes = getSheetShapes(sheetIndex);
28623
+ const sheetFormControls = getSheetFormControls(sheetIndex).filter((control) => !control.hidden);
28624
+ const drawingBounds = [
28625
+ ...sheetImages.map((image) => image.anchor),
28626
+ ...sheetShapes.map((shape) => shape.anchor),
28627
+ ...sheetFormControls.map((control) => control.anchor)
28628
+ ].reduce((bounds, anchor) => {
28629
+ const drawingBound = resolveAnchoredBounds(anchor) ?? {
28630
+ maxCol: 0,
28631
+ maxRow: 0,
28632
+ minCol: 0,
28633
+ minRow: 0
28634
+ };
28635
+ return {
28636
+ maxCol: Math.max(bounds?.maxCol ?? drawingBound.maxCol, drawingBound.maxCol),
28637
+ maxRow: Math.max(bounds?.maxRow ?? drawingBound.maxRow, drawingBound.maxRow),
28638
+ minCol: Math.min(bounds?.minCol ?? drawingBound.minCol, drawingBound.minCol),
28639
+ minRow: Math.min(bounds?.minRow ?? drawingBound.minRow, drawingBound.minRow)
28640
+ };
28641
+ }, null);
28642
+ const sheetMinUsedRow = (sheet.maxUsedRow ?? -1) >= (sheet.minUsedRow ?? 0) ? sheet.minUsedRow ?? 0 : void 0;
28643
+ const sheetMinUsedCol = (sheet.maxUsedCol ?? -1) >= (sheet.minUsedCol ?? 0) ? sheet.minUsedCol ?? 0 : void 0;
28644
+ const effectiveMinUsedRow = Math.max(0, Math.min(sheetMinUsedRow ?? drawingBounds?.minRow ?? 0, drawingBounds?.minRow ?? sheetMinUsedRow ?? 0));
28645
+ const effectiveMinUsedCol = Math.max(0, Math.min(sheetMinUsedCol ?? drawingBounds?.minCol ?? 0, drawingBounds?.minCol ?? sheetMinUsedCol ?? 0));
28646
+ const effectiveMaxUsedRow = Math.max(sheet.maxUsedRow ?? -1, drawingBounds?.maxRow ?? -1);
28647
+ const effectiveMaxUsedCol = Math.max(sheet.maxUsedCol ?? -1, drawingBounds?.maxCol ?? -1);
28278
28648
  const showGridLines = sheet.showGridLines ?? true;
28279
28649
  const hiddenRowSet = new Set(sheet.hiddenRows ?? []);
28280
28650
  const hiddenColSet = new Set(sheet.hiddenCols ?? []);
28281
28651
  const visibleRows = buildVisibleAxisIndices(
28282
28652
  sheet.visibleRows ?? [],
28283
- Math.max(THUMBNAIL_FALLBACK_ROWS, (sheet.maxUsedRow ?? -1) + THUMBNAIL_FALLBACK_ROWS + 1),
28284
- sheet.maxUsedRow ?? -1,
28653
+ Math.max(THUMBNAIL_FALLBACK_ROWS, effectiveMaxUsedRow + THUMBNAIL_FALLBACK_ROWS + 1),
28654
+ effectiveMaxUsedRow,
28285
28655
  hiddenRowSet
28286
28656
  );
28287
28657
  const visibleCols = buildVisibleAxisIndices(
28288
28658
  sheet.visibleCols ?? [],
28289
- Math.max(THUMBNAIL_FALLBACK_COLS, (sheet.maxUsedCol ?? -1) + THUMBNAIL_FALLBACK_COLS + 1),
28290
- sheet.maxUsedCol ?? -1,
28659
+ Math.max(THUMBNAIL_FALLBACK_COLS, effectiveMaxUsedCol + THUMBNAIL_FALLBACK_COLS + 1),
28660
+ effectiveMaxUsedCol,
28291
28661
  hiddenColSet
28292
28662
  );
28293
28663
  const resolveColumnWidthPx = (actualCol) => {
@@ -28331,8 +28701,8 @@ function useXlsxViewerThumbnails(options = {}) {
28331
28701
  getSizePx: resolveRowHeightPx,
28332
28702
  maxCount: THUMBNAIL_MAX_ROWS,
28333
28703
  maxLogicalPixels: THUMBNAIL_MAX_SOURCE_HEIGHT_PX,
28334
- maxUsedIndex: sheet.maxUsedRow ?? -1,
28335
- minUsedIndex: Math.max(0, sheet.minUsedRow ?? 0),
28704
+ maxUsedIndex: effectiveMaxUsedRow,
28705
+ minUsedIndex: effectiveMinUsedRow,
28336
28706
  precomputed: visibleRows
28337
28707
  });
28338
28708
  const previewCols = resolveThumbnailAxisIndices({
@@ -28340,8 +28710,8 @@ function useXlsxViewerThumbnails(options = {}) {
28340
28710
  getSizePx: resolveColumnWidthPx,
28341
28711
  maxCount: THUMBNAIL_MAX_COLS,
28342
28712
  maxLogicalPixels: THUMBNAIL_MAX_SOURCE_WIDTH_PX,
28343
- maxUsedIndex: sheet.maxUsedCol ?? -1,
28344
- minUsedIndex: Math.max(0, sheet.minUsedCol ?? 0),
28713
+ maxUsedIndex: effectiveMaxUsedCol,
28714
+ minUsedIndex: effectiveMinUsedCol,
28345
28715
  precomputed: visibleCols
28346
28716
  });
28347
28717
  const rowAxis = buildThumbnailAxisItems(previewRows, resolveRowHeightPx);
@@ -28366,6 +28736,74 @@ function useXlsxViewerThumbnails(options = {}) {
28366
28736
  const sparklineByCell = new Map(
28367
28737
  (sheet.sparklines ?? []).map((sparkline) => [`${sparkline.target.row}:${sparkline.target.col}`, sparkline])
28368
28738
  );
28739
+ const thumbnailImageRects = sheetImages.map((image) => ({
28740
+ image,
28741
+ rect: resolveThumbnailAnchoredRect(
28742
+ image.anchor,
28743
+ visibleRows,
28744
+ visibleCols,
28745
+ resolveRowHeightPx,
28746
+ resolveColumnWidthPx,
28747
+ previewRows,
28748
+ previewCols,
28749
+ {
28750
+ headerHeight,
28751
+ rowHeaderWidth
28752
+ }
28753
+ )
28754
+ })).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
28755
+ const thumbnailShapeRects = sheetShapes.map((shape) => ({
28756
+ rect: resolveThumbnailAnchoredRect(
28757
+ shape.anchor,
28758
+ visibleRows,
28759
+ visibleCols,
28760
+ resolveRowHeightPx,
28761
+ resolveColumnWidthPx,
28762
+ previewRows,
28763
+ previewCols,
28764
+ {
28765
+ headerHeight,
28766
+ rowHeaderWidth
28767
+ }
28768
+ ),
28769
+ shape
28770
+ })).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
28771
+ const thumbnailFormControlRects = sheetFormControls.map((control) => ({
28772
+ control,
28773
+ rect: resolveThumbnailAnchoredRect(
28774
+ control.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 thumbnailDrawingEntries = [
28788
+ ...thumbnailShapeRects.map(({ rect, shape }) => ({
28789
+ kind: "shape",
28790
+ rect,
28791
+ shape,
28792
+ zIndex: shape.zIndex
28793
+ })),
28794
+ ...thumbnailFormControlRects.map(({ control, rect }) => ({
28795
+ control,
28796
+ kind: "formControl",
28797
+ rect,
28798
+ zIndex: control.zIndex
28799
+ })),
28800
+ ...thumbnailImageRects.map(({ image, rect }) => ({
28801
+ image,
28802
+ kind: "image",
28803
+ rect,
28804
+ zIndex: image.zIndex
28805
+ }))
28806
+ ].sort((left, right) => left.zIndex - right.zIndex);
28369
28807
  const paint = (canvas) => {
28370
28808
  if (!canvas) {
28371
28809
  return false;
@@ -28678,6 +29116,25 @@ function useXlsxViewerThumbnails(options = {}) {
28678
29116
  context.restore();
28679
29117
  }
28680
29118
  }
29119
+ if (thumbnailDrawingEntries.length > 0) {
29120
+ context.save();
29121
+ context.beginPath();
29122
+ context.rect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
29123
+ context.clip();
29124
+ for (const entry of thumbnailDrawingEntries) {
29125
+ if (entry.kind === "shape") {
29126
+ drawStaticShape(context, entry.shape, entry.rect, 1);
29127
+ } else if (entry.kind === "formControl") {
29128
+ drawStaticFormControl(context, entry.control, entry.rect, palette, 1, thumbnailSheetSurface);
29129
+ } else {
29130
+ const imageElement = getThumbnailImage(entry.image);
29131
+ if (imageElement) {
29132
+ context.drawImage(imageElement, entry.rect.left, entry.rect.top, entry.rect.width, entry.rect.height);
29133
+ }
29134
+ }
29135
+ }
29136
+ context.restore();
29137
+ }
28681
29138
  if (includeHeaders) {
28682
29139
  context.strokeStyle = palette.border;
28683
29140
  context.lineWidth = 1;
@@ -28731,7 +29188,18 @@ function useXlsxViewerThumbnails(options = {}) {
28731
29188
  workbookSheetIndex: sheet.workbookSheetIndex
28732
29189
  };
28733
29190
  });
28734
- }, [includeHeaders, palette, resolution, sheets, workbook]);
29191
+ }, [
29192
+ getSheetFormControls,
29193
+ getSheetImages,
29194
+ getSheetShapes,
29195
+ getThumbnailImage,
29196
+ includeHeaders,
29197
+ palette,
29198
+ resolution,
29199
+ sheets,
29200
+ thumbnailImageLoadVersion,
29201
+ workbook
29202
+ ]);
28735
29203
  const paintThumbnail = React4.useCallback(
28736
29204
  (sheetIndex, canvas) => thumbnails[sheetIndex]?.paint(canvas) ?? false,
28737
29205
  [thumbnails]