@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.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(
@@ -28267,27 +28567,95 @@ function useXlsxViewerCharts() {
28267
28567
  );
28268
28568
  }
28269
28569
  function useXlsxViewerThumbnails(options = {}) {
28270
- const { workbook, sheets } = useXlsxViewer();
28570
+ const { getSheetFormControls, getSheetImages, getSheetShapes, workbook, sheets } = useXlsxViewer();
28271
28571
  const { isDark } = React4.useContext(ViewerAppearanceContext);
28272
28572
  const palette = useViewerPalette(isDark);
28273
28573
  const includeHeaders = options.includeHeaders ?? true;
28274
28574
  const resolution = options.resolution;
28575
+ const thumbnailImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
28576
+ const isMountedRef = React4.useRef(false);
28577
+ const [thumbnailImageLoadVersion, setThumbnailImageLoadVersion] = React4.useState(0);
28578
+ React4.useEffect(() => {
28579
+ isMountedRef.current = true;
28580
+ return () => {
28581
+ isMountedRef.current = false;
28582
+ };
28583
+ }, []);
28584
+ const getThumbnailImage = React4.useCallback((image) => {
28585
+ if (typeof Image === "undefined") {
28586
+ return null;
28587
+ }
28588
+ const cacheKey = image.id;
28589
+ const cached = thumbnailImageCacheRef.current.get(cacheKey);
28590
+ if (cached && cached.src === image.src) {
28591
+ return cached.loaded && !cached.failed ? cached.image : null;
28592
+ }
28593
+ const imageElement = new Image();
28594
+ const entry = {
28595
+ failed: false,
28596
+ image: imageElement,
28597
+ loaded: false,
28598
+ src: image.src
28599
+ };
28600
+ thumbnailImageCacheRef.current.set(cacheKey, entry);
28601
+ imageElement.onload = () => {
28602
+ entry.loaded = true;
28603
+ if (isMountedRef.current) {
28604
+ setThumbnailImageLoadVersion((version) => version + 1);
28605
+ }
28606
+ };
28607
+ imageElement.onerror = () => {
28608
+ entry.failed = true;
28609
+ if (isMountedRef.current) {
28610
+ setThumbnailImageLoadVersion((version) => version + 1);
28611
+ }
28612
+ };
28613
+ imageElement.src = image.src;
28614
+ return null;
28615
+ }, []);
28275
28616
  const thumbnails = React4.useMemo(() => {
28276
28617
  return sheets.map((sheet, sheetIndex) => {
28277
28618
  const worksheet = workbook?.getSheet(sheet.workbookSheetIndex) ?? null;
28619
+ const sheetImages = getSheetImages(sheetIndex).filter((image) => image.src).sort((left, right) => left.zIndex - right.zIndex);
28620
+ const sheetShapes = getSheetShapes(sheetIndex);
28621
+ const sheetFormControls = getSheetFormControls(sheetIndex).filter((control) => !control.hidden);
28622
+ const drawingBounds = [
28623
+ ...sheetImages.map((image) => image.anchor),
28624
+ ...sheetShapes.map((shape) => shape.anchor),
28625
+ ...sheetFormControls.map((control) => control.anchor)
28626
+ ].reduce((bounds, anchor) => {
28627
+ const drawingBound = resolveAnchoredBounds(anchor) ?? {
28628
+ maxCol: 0,
28629
+ maxRow: 0,
28630
+ minCol: 0,
28631
+ minRow: 0
28632
+ };
28633
+ return {
28634
+ maxCol: Math.max(bounds?.maxCol ?? drawingBound.maxCol, drawingBound.maxCol),
28635
+ maxRow: Math.max(bounds?.maxRow ?? drawingBound.maxRow, drawingBound.maxRow),
28636
+ minCol: Math.min(bounds?.minCol ?? drawingBound.minCol, drawingBound.minCol),
28637
+ minRow: Math.min(bounds?.minRow ?? drawingBound.minRow, drawingBound.minRow)
28638
+ };
28639
+ }, null);
28640
+ const sheetMinUsedRow = (sheet.maxUsedRow ?? -1) >= (sheet.minUsedRow ?? 0) ? sheet.minUsedRow ?? 0 : void 0;
28641
+ const sheetMinUsedCol = (sheet.maxUsedCol ?? -1) >= (sheet.minUsedCol ?? 0) ? sheet.minUsedCol ?? 0 : void 0;
28642
+ const effectiveMinUsedRow = Math.max(0, Math.min(sheetMinUsedRow ?? drawingBounds?.minRow ?? 0, drawingBounds?.minRow ?? sheetMinUsedRow ?? 0));
28643
+ const effectiveMinUsedCol = Math.max(0, Math.min(sheetMinUsedCol ?? drawingBounds?.minCol ?? 0, drawingBounds?.minCol ?? sheetMinUsedCol ?? 0));
28644
+ const effectiveMaxUsedRow = Math.max(sheet.maxUsedRow ?? -1, drawingBounds?.maxRow ?? -1);
28645
+ const effectiveMaxUsedCol = Math.max(sheet.maxUsedCol ?? -1, drawingBounds?.maxCol ?? -1);
28278
28646
  const showGridLines = sheet.showGridLines ?? true;
28279
28647
  const hiddenRowSet = new Set(sheet.hiddenRows ?? []);
28280
28648
  const hiddenColSet = new Set(sheet.hiddenCols ?? []);
28281
28649
  const visibleRows = buildVisibleAxisIndices(
28282
28650
  sheet.visibleRows ?? [],
28283
- Math.max(THUMBNAIL_FALLBACK_ROWS, (sheet.maxUsedRow ?? -1) + THUMBNAIL_FALLBACK_ROWS + 1),
28284
- sheet.maxUsedRow ?? -1,
28651
+ Math.max(THUMBNAIL_FALLBACK_ROWS, effectiveMaxUsedRow + THUMBNAIL_FALLBACK_ROWS + 1),
28652
+ effectiveMaxUsedRow,
28285
28653
  hiddenRowSet
28286
28654
  );
28287
28655
  const visibleCols = buildVisibleAxisIndices(
28288
28656
  sheet.visibleCols ?? [],
28289
- Math.max(THUMBNAIL_FALLBACK_COLS, (sheet.maxUsedCol ?? -1) + THUMBNAIL_FALLBACK_COLS + 1),
28290
- sheet.maxUsedCol ?? -1,
28657
+ Math.max(THUMBNAIL_FALLBACK_COLS, effectiveMaxUsedCol + THUMBNAIL_FALLBACK_COLS + 1),
28658
+ effectiveMaxUsedCol,
28291
28659
  hiddenColSet
28292
28660
  );
28293
28661
  const resolveColumnWidthPx = (actualCol) => {
@@ -28331,8 +28699,8 @@ function useXlsxViewerThumbnails(options = {}) {
28331
28699
  getSizePx: resolveRowHeightPx,
28332
28700
  maxCount: THUMBNAIL_MAX_ROWS,
28333
28701
  maxLogicalPixels: THUMBNAIL_MAX_SOURCE_HEIGHT_PX,
28334
- maxUsedIndex: sheet.maxUsedRow ?? -1,
28335
- minUsedIndex: Math.max(0, sheet.minUsedRow ?? 0),
28702
+ maxUsedIndex: effectiveMaxUsedRow,
28703
+ minUsedIndex: effectiveMinUsedRow,
28336
28704
  precomputed: visibleRows
28337
28705
  });
28338
28706
  const previewCols = resolveThumbnailAxisIndices({
@@ -28340,8 +28708,8 @@ function useXlsxViewerThumbnails(options = {}) {
28340
28708
  getSizePx: resolveColumnWidthPx,
28341
28709
  maxCount: THUMBNAIL_MAX_COLS,
28342
28710
  maxLogicalPixels: THUMBNAIL_MAX_SOURCE_WIDTH_PX,
28343
- maxUsedIndex: sheet.maxUsedCol ?? -1,
28344
- minUsedIndex: Math.max(0, sheet.minUsedCol ?? 0),
28711
+ maxUsedIndex: effectiveMaxUsedCol,
28712
+ minUsedIndex: effectiveMinUsedCol,
28345
28713
  precomputed: visibleCols
28346
28714
  });
28347
28715
  const rowAxis = buildThumbnailAxisItems(previewRows, resolveRowHeightPx);
@@ -28366,6 +28734,74 @@ function useXlsxViewerThumbnails(options = {}) {
28366
28734
  const sparklineByCell = new Map(
28367
28735
  (sheet.sparklines ?? []).map((sparkline) => [`${sparkline.target.row}:${sparkline.target.col}`, sparkline])
28368
28736
  );
28737
+ const thumbnailImageRects = sheetImages.map((image) => ({
28738
+ image,
28739
+ rect: resolveThumbnailAnchoredRect(
28740
+ image.anchor,
28741
+ visibleRows,
28742
+ visibleCols,
28743
+ resolveRowHeightPx,
28744
+ resolveColumnWidthPx,
28745
+ previewRows,
28746
+ previewCols,
28747
+ {
28748
+ headerHeight,
28749
+ rowHeaderWidth
28750
+ }
28751
+ )
28752
+ })).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
28753
+ const thumbnailShapeRects = sheetShapes.map((shape) => ({
28754
+ rect: resolveThumbnailAnchoredRect(
28755
+ shape.anchor,
28756
+ visibleRows,
28757
+ visibleCols,
28758
+ resolveRowHeightPx,
28759
+ resolveColumnWidthPx,
28760
+ previewRows,
28761
+ previewCols,
28762
+ {
28763
+ headerHeight,
28764
+ rowHeaderWidth
28765
+ }
28766
+ ),
28767
+ shape
28768
+ })).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
28769
+ const thumbnailFormControlRects = sheetFormControls.map((control) => ({
28770
+ control,
28771
+ rect: resolveThumbnailAnchoredRect(
28772
+ control.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 thumbnailDrawingEntries = [
28786
+ ...thumbnailShapeRects.map(({ rect, shape }) => ({
28787
+ kind: "shape",
28788
+ rect,
28789
+ shape,
28790
+ zIndex: shape.zIndex
28791
+ })),
28792
+ ...thumbnailFormControlRects.map(({ control, rect }) => ({
28793
+ control,
28794
+ kind: "formControl",
28795
+ rect,
28796
+ zIndex: control.zIndex
28797
+ })),
28798
+ ...thumbnailImageRects.map(({ image, rect }) => ({
28799
+ image,
28800
+ kind: "image",
28801
+ rect,
28802
+ zIndex: image.zIndex
28803
+ }))
28804
+ ].sort((left, right) => left.zIndex - right.zIndex);
28369
28805
  const paint = (canvas) => {
28370
28806
  if (!canvas) {
28371
28807
  return false;
@@ -28678,6 +29114,25 @@ function useXlsxViewerThumbnails(options = {}) {
28678
29114
  context.restore();
28679
29115
  }
28680
29116
  }
29117
+ if (thumbnailDrawingEntries.length > 0) {
29118
+ context.save();
29119
+ context.beginPath();
29120
+ context.rect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
29121
+ context.clip();
29122
+ for (const entry of thumbnailDrawingEntries) {
29123
+ if (entry.kind === "shape") {
29124
+ drawStaticShape(context, entry.shape, entry.rect, 1);
29125
+ } else if (entry.kind === "formControl") {
29126
+ drawStaticFormControl(context, entry.control, entry.rect, palette, 1, thumbnailSheetSurface);
29127
+ } else {
29128
+ const imageElement = getThumbnailImage(entry.image);
29129
+ if (imageElement) {
29130
+ context.drawImage(imageElement, entry.rect.left, entry.rect.top, entry.rect.width, entry.rect.height);
29131
+ }
29132
+ }
29133
+ }
29134
+ context.restore();
29135
+ }
28681
29136
  if (includeHeaders) {
28682
29137
  context.strokeStyle = palette.border;
28683
29138
  context.lineWidth = 1;
@@ -28731,7 +29186,18 @@ function useXlsxViewerThumbnails(options = {}) {
28731
29186
  workbookSheetIndex: sheet.workbookSheetIndex
28732
29187
  };
28733
29188
  });
28734
- }, [includeHeaders, palette, resolution, sheets, workbook]);
29189
+ }, [
29190
+ getSheetFormControls,
29191
+ getSheetImages,
29192
+ getSheetShapes,
29193
+ getThumbnailImage,
29194
+ includeHeaders,
29195
+ palette,
29196
+ resolution,
29197
+ sheets,
29198
+ thumbnailImageLoadVersion,
29199
+ workbook
29200
+ ]);
28735
29201
  const paintThumbnail = React4.useCallback(
28736
29202
  (sheetIndex, canvas) => thumbnails[sheetIndex]?.paint(canvas) ?? false,
28737
29203
  [thumbnails]