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