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