@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/README.md +392 -0
- package/dist/index.cjs +476 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +476 -10
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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,
|
|
28284
|
-
|
|
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,
|
|
28290
|
-
|
|
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:
|
|
28335
|
-
minUsedIndex:
|
|
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:
|
|
28344
|
-
minUsedIndex:
|
|
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
|
-
}, [
|
|
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]
|