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