@extend-ai/react-xlsx 0.8.3 → 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 +1380 -188
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1380 -188
- package/dist/index.js.map +1 -1
- package/dist/xlsx-worker.js +67 -21
- package/dist/xlsx-worker.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -4575,6 +4575,10 @@ function parseSheetState(archive, path, options) {
|
|
|
4575
4575
|
let hasVerticalMerges = false;
|
|
4576
4576
|
let maxHorizontalMergeEndCol = -1;
|
|
4577
4577
|
let maxVerticalMergeEndRow = -1;
|
|
4578
|
+
let minContentCol = Number.POSITIVE_INFINITY;
|
|
4579
|
+
let minContentRow = Number.POSITIVE_INFINITY;
|
|
4580
|
+
let maxContentCol = -1;
|
|
4581
|
+
let maxContentRow = -1;
|
|
4578
4582
|
const columnWidthCharacterWidthPx = measureColumnCharacterWidthPx(
|
|
4579
4583
|
options?.defaultFont?.family,
|
|
4580
4584
|
options?.defaultFont?.sizePt
|
|
@@ -4587,6 +4591,26 @@ function parseSheetState(archive, path, options) {
|
|
|
4587
4591
|
sheetViewNode?.getAttribute("zoomScale") ?? sheetViewNode?.getAttribute("zoomScaleNormal") ?? Number.NaN
|
|
4588
4592
|
);
|
|
4589
4593
|
const zoomScale = Number.isFinite(rawZoomScale) && rawZoomScale > 0 ? rawZoomScale : 100;
|
|
4594
|
+
const trackContentCell = (cellRef) => {
|
|
4595
|
+
if (!cellRef) {
|
|
4596
|
+
return;
|
|
4597
|
+
}
|
|
4598
|
+
const cell = parseA1CellReference(cellRef);
|
|
4599
|
+
if (!cell) {
|
|
4600
|
+
return;
|
|
4601
|
+
}
|
|
4602
|
+
minContentCol = Math.min(minContentCol, cell.col);
|
|
4603
|
+
minContentRow = Math.min(minContentRow, cell.row);
|
|
4604
|
+
maxContentCol = Math.max(maxContentCol, cell.col);
|
|
4605
|
+
maxContentRow = Math.max(maxContentRow, cell.row);
|
|
4606
|
+
};
|
|
4607
|
+
const isMeaningfulCellNode = (cellNode) => {
|
|
4608
|
+
if (getFirstChild(cellNode, "f") || getFirstChild(cellNode, "is")) {
|
|
4609
|
+
return true;
|
|
4610
|
+
}
|
|
4611
|
+
const valueNode = getFirstChild(cellNode, "v");
|
|
4612
|
+
return Boolean(valueNode && (valueNode.textContent ?? "").length > 0);
|
|
4613
|
+
};
|
|
4590
4614
|
getLocalElements(document2, "row").forEach((rowNode) => {
|
|
4591
4615
|
const rowIndex = Number(rowNode.getAttribute("r") ?? 0) - 1;
|
|
4592
4616
|
const height = Number(rowNode.getAttribute("ht") ?? Number.NaN);
|
|
@@ -4601,17 +4625,36 @@ function parseSheetState(archive, path, options) {
|
|
|
4601
4625
|
if (rowIndex >= 0 && isHidden) {
|
|
4602
4626
|
hiddenRows.add(rowIndex);
|
|
4603
4627
|
}
|
|
4604
|
-
|
|
4605
|
-
|
|
4628
|
+
getChildElements(rowNode, "c").forEach((cellNode) => {
|
|
4629
|
+
const cellRef = cellNode.getAttribute("r");
|
|
4630
|
+
if (isMeaningfulCellNode(cellNode)) {
|
|
4631
|
+
trackContentCell(cellRef);
|
|
4632
|
+
}
|
|
4633
|
+
if (includeCachedFormulaValues) {
|
|
4606
4634
|
const formulaNode = getFirstChild(cellNode, "f");
|
|
4607
4635
|
const valueNode = getFirstChild(cellNode, "v");
|
|
4608
|
-
const cellRef = cellNode.getAttribute("r");
|
|
4609
4636
|
if (formulaNode && valueNode && cellRef) {
|
|
4610
4637
|
cachedFormulaValues[cellRef] = valueNode.textContent ?? "";
|
|
4611
4638
|
}
|
|
4612
|
-
}
|
|
4639
|
+
}
|
|
4640
|
+
});
|
|
4641
|
+
});
|
|
4642
|
+
getLocalElements(document2, "mergeCell").forEach((mergeNode) => {
|
|
4643
|
+
const reference = mergeNode.getAttribute("ref");
|
|
4644
|
+
const range = reference ? parseA1RangeReference(reference) : null;
|
|
4645
|
+
if (!range) {
|
|
4646
|
+
return;
|
|
4647
|
+
}
|
|
4648
|
+
if (range.end.col > range.start.col) {
|
|
4649
|
+
hasHorizontalMerges = true;
|
|
4650
|
+
maxHorizontalMergeEndCol = Math.max(maxHorizontalMergeEndCol, range.end.col);
|
|
4651
|
+
}
|
|
4652
|
+
if (range.end.row > range.start.row) {
|
|
4653
|
+
hasVerticalMerges = true;
|
|
4654
|
+
maxVerticalMergeEndRow = Math.max(maxVerticalMergeEndRow, range.end.row);
|
|
4613
4655
|
}
|
|
4614
4656
|
});
|
|
4657
|
+
const maxMetadataCol = Math.max(maxContentCol, maxHorizontalMergeEndCol, 0) + 256;
|
|
4615
4658
|
getLocalElements(document2, "col").forEach((colNode) => {
|
|
4616
4659
|
const min = Number(colNode.getAttribute("min") ?? 0) - 1;
|
|
4617
4660
|
const max = Number(colNode.getAttribute("max") ?? 0) - 1;
|
|
@@ -4623,7 +4666,7 @@ function parseSheetState(archive, path, options) {
|
|
|
4623
4666
|
return;
|
|
4624
4667
|
}
|
|
4625
4668
|
}
|
|
4626
|
-
for (let col = min; col <= max; col += 1) {
|
|
4669
|
+
for (let col = min; col <= Math.min(max, maxMetadataCol); col += 1) {
|
|
4627
4670
|
if (col >= 0) {
|
|
4628
4671
|
if (Number.isFinite(width)) {
|
|
4629
4672
|
const widthPx = sheetColumnWidthToPixels(width, columnWidthCharacterWidthPx);
|
|
@@ -4638,21 +4681,6 @@ function parseSheetState(archive, path, options) {
|
|
|
4638
4681
|
}
|
|
4639
4682
|
}
|
|
4640
4683
|
});
|
|
4641
|
-
getLocalElements(document2, "mergeCell").forEach((mergeNode) => {
|
|
4642
|
-
const reference = mergeNode.getAttribute("ref");
|
|
4643
|
-
const range = reference ? parseA1RangeReference(reference) : null;
|
|
4644
|
-
if (!range) {
|
|
4645
|
-
return;
|
|
4646
|
-
}
|
|
4647
|
-
if (range.end.col > range.start.col) {
|
|
4648
|
-
hasHorizontalMerges = true;
|
|
4649
|
-
maxHorizontalMergeEndCol = Math.max(maxHorizontalMergeEndCol, range.end.col);
|
|
4650
|
-
}
|
|
4651
|
-
if (range.end.row > range.start.row) {
|
|
4652
|
-
hasVerticalMerges = true;
|
|
4653
|
-
maxVerticalMergeEndRow = Math.max(maxVerticalMergeEndRow, range.end.row);
|
|
4654
|
-
}
|
|
4655
|
-
});
|
|
4656
4684
|
return {
|
|
4657
4685
|
cachedFormulaValues,
|
|
4658
4686
|
columnWidthCharacterWidthPx,
|
|
@@ -4665,6 +4693,10 @@ function parseSheetState(archive, path, options) {
|
|
|
4665
4693
|
hasVerticalMerges,
|
|
4666
4694
|
maxHorizontalMergeEndCol,
|
|
4667
4695
|
maxVerticalMergeEndRow,
|
|
4696
|
+
maxContentCol,
|
|
4697
|
+
maxContentRow,
|
|
4698
|
+
minContentCol: Number.isFinite(minContentCol) ? minContentCol : -1,
|
|
4699
|
+
minContentRow: Number.isFinite(minContentRow) ? minContentRow : -1,
|
|
4668
4700
|
hiddenCols: [...hiddenCols].sort((left, right) => left - right),
|
|
4669
4701
|
hiddenRows: [...hiddenRows].sort((left, right) => left - right),
|
|
4670
4702
|
rowHeightOverridesPx,
|
|
@@ -6694,6 +6726,20 @@ function resolveDisplayFileName(src, fileName) {
|
|
|
6694
6726
|
return lastSegment;
|
|
6695
6727
|
}
|
|
6696
6728
|
}
|
|
6729
|
+
function resolveSheetDisplayUsedRange(usedRange, sheetState) {
|
|
6730
|
+
const [minRow, minCol, maxRow, maxCol] = usedRange;
|
|
6731
|
+
const maxMeaningfulRow = Math.max(sheetState?.maxContentRow ?? -1, sheetState?.maxVerticalMergeEndRow ?? -1);
|
|
6732
|
+
const maxMeaningfulCol = Math.max(sheetState?.maxContentCol ?? -1, sheetState?.maxHorizontalMergeEndCol ?? -1);
|
|
6733
|
+
if (maxMeaningfulRow < 0 && maxMeaningfulCol < 0) {
|
|
6734
|
+
return usedRange;
|
|
6735
|
+
}
|
|
6736
|
+
return [
|
|
6737
|
+
sheetState?.minContentRow !== void 0 && sheetState.minContentRow >= 0 ? Math.min(minRow, sheetState.minContentRow) : minRow,
|
|
6738
|
+
sheetState?.minContentCol !== void 0 && sheetState.minContentCol >= 0 ? Math.min(minCol, sheetState.minContentCol) : minCol,
|
|
6739
|
+
maxMeaningfulRow >= 0 ? Math.min(maxRow, maxMeaningfulRow) : maxRow,
|
|
6740
|
+
maxMeaningfulCol >= 0 ? Math.min(maxCol, maxMeaningfulCol) : maxCol
|
|
6741
|
+
];
|
|
6742
|
+
}
|
|
6697
6743
|
function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette, styleById, namedCellStyleByName, tableStyleByName, showHiddenSheets = false) {
|
|
6698
6744
|
const sheets = [];
|
|
6699
6745
|
for (let index = 0; index < workbook.sheetCount; index += 1) {
|
|
@@ -6760,7 +6806,7 @@ function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette,
|
|
|
6760
6806
|
});
|
|
6761
6807
|
continue;
|
|
6762
6808
|
}
|
|
6763
|
-
const [minRow, minCol, maxRow, maxCol] = usedRange;
|
|
6809
|
+
const [minRow, minCol, maxRow, maxCol] = resolveSheetDisplayUsedRange(usedRange, sheetState);
|
|
6764
6810
|
let visibleRowsCache = null;
|
|
6765
6811
|
let visibleColsCache = null;
|
|
6766
6812
|
let rowHeightsCache = null;
|
|
@@ -16502,7 +16548,11 @@ var SELECTION_DRAG_THRESHOLD_PX = 4;
|
|
|
16502
16548
|
var IMAGE_MIN_SIZE_PX = 16;
|
|
16503
16549
|
var IMAGE_HANDLE_SIZE_PX = 10;
|
|
16504
16550
|
var CANVAS_RESIZE_HIT_SLOP_PX = 8;
|
|
16505
|
-
var CANVAS_VIEWPORT_OVERSCAN_PX =
|
|
16551
|
+
var CANVAS_VIEWPORT_OVERSCAN_PX = 480;
|
|
16552
|
+
var CANVAS_SCROLL_BUFFER_PX = 480;
|
|
16553
|
+
var CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX - 96;
|
|
16554
|
+
var CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX * 1.5;
|
|
16555
|
+
var CANVAS_DIRTY_CELL_CULL_MARGIN_PX = CANVAS_VIEWPORT_OVERSCAN_PX * 2;
|
|
16506
16556
|
var THUMBNAIL_DEFAULT_MAX_DIMENSION = 192;
|
|
16507
16557
|
var THUMBNAIL_FALLBACK_ROWS = 12;
|
|
16508
16558
|
var THUMBNAIL_FALLBACK_COLS = 8;
|
|
@@ -16517,6 +16567,12 @@ var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669",
|
|
|
16517
16567
|
var SHEET_SURFACE = "#ffffff";
|
|
16518
16568
|
var DEFAULT_CELL_PADDING = "0 4px";
|
|
16519
16569
|
var IMAGE_HANDLE_POSITIONS = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
|
|
16570
|
+
var CANVAS_CELL_STYLE_CACHE_LIMIT = 4096;
|
|
16571
|
+
var CANVAS_TEXT_MEASURE_CACHE_LIMIT = 2e4;
|
|
16572
|
+
var CANVAS_TEXT_TRUNCATE_CACHE_LIMIT = 4096;
|
|
16573
|
+
var CANVAS_TEXT_WRAP_CACHE_LIMIT = 2048;
|
|
16574
|
+
var CANVAS_PATH2D_CACHE_LIMIT = 512;
|
|
16575
|
+
var EMPTY_VIRTUAL_ITEMS = [];
|
|
16520
16576
|
var IMAGE_HANDLE_CURSOR = {
|
|
16521
16577
|
e: "ew-resize",
|
|
16522
16578
|
n: "ns-resize",
|
|
@@ -16560,6 +16616,50 @@ var NUMERIC_LENGTH_STYLE_KEYS = /* @__PURE__ */ new Set([
|
|
|
16560
16616
|
"top",
|
|
16561
16617
|
"width"
|
|
16562
16618
|
]);
|
|
16619
|
+
var canvasCellStyleCache = /* @__PURE__ */ new Map();
|
|
16620
|
+
var canvasTextMeasureCache = /* @__PURE__ */ new Map();
|
|
16621
|
+
var canvasTextTruncateCache = /* @__PURE__ */ new Map();
|
|
16622
|
+
var canvasTextWrapCache = /* @__PURE__ */ new Map();
|
|
16623
|
+
var canvasPath2DCache = /* @__PURE__ */ new Map();
|
|
16624
|
+
function rememberBoundedCacheValue(cache, key, value, limit) {
|
|
16625
|
+
if (cache.size >= limit) {
|
|
16626
|
+
const oldestKey = cache.keys().next().value;
|
|
16627
|
+
if (oldestKey !== void 0) {
|
|
16628
|
+
cache.delete(oldestKey);
|
|
16629
|
+
}
|
|
16630
|
+
}
|
|
16631
|
+
cache.set(key, value);
|
|
16632
|
+
return value;
|
|
16633
|
+
}
|
|
16634
|
+
function roundCanvasCacheWidth(width) {
|
|
16635
|
+
return Math.round(width * 4) / 4;
|
|
16636
|
+
}
|
|
16637
|
+
function measureCanvasTextWidth(context, text) {
|
|
16638
|
+
if (text.length === 0) {
|
|
16639
|
+
return 0;
|
|
16640
|
+
}
|
|
16641
|
+
const cacheKey = `${context.font}\0${text}`;
|
|
16642
|
+
const cached = canvasTextMeasureCache.get(cacheKey);
|
|
16643
|
+
if (cached !== void 0) {
|
|
16644
|
+
return cached;
|
|
16645
|
+
}
|
|
16646
|
+
return rememberBoundedCacheValue(
|
|
16647
|
+
canvasTextMeasureCache,
|
|
16648
|
+
cacheKey,
|
|
16649
|
+
context.measureText(text).width,
|
|
16650
|
+
CANVAS_TEXT_MEASURE_CACHE_LIMIT
|
|
16651
|
+
);
|
|
16652
|
+
}
|
|
16653
|
+
function getCachedCanvasPath2D(path) {
|
|
16654
|
+
if (typeof Path2D === "undefined") {
|
|
16655
|
+
return null;
|
|
16656
|
+
}
|
|
16657
|
+
const cached = canvasPath2DCache.get(path);
|
|
16658
|
+
if (cached) {
|
|
16659
|
+
return cached;
|
|
16660
|
+
}
|
|
16661
|
+
return rememberBoundedCacheValue(canvasPath2DCache, path, new Path2D(path), CANVAS_PATH2D_CACHE_LIMIT);
|
|
16662
|
+
}
|
|
16563
16663
|
function scaleCssLengthExpression(value, scale) {
|
|
16564
16664
|
if (scale === 1) {
|
|
16565
16665
|
return value;
|
|
@@ -16740,7 +16840,6 @@ function strokeCanvasBorderSide(context, side, rect, border) {
|
|
|
16740
16840
|
const right = rect.left + rect.width;
|
|
16741
16841
|
const top = rect.top;
|
|
16742
16842
|
const bottom = rect.top + rect.height;
|
|
16743
|
-
context.save();
|
|
16744
16843
|
context.strokeStyle = border.color;
|
|
16745
16844
|
context.lineWidth = border.width;
|
|
16746
16845
|
applyCanvasBorderDash(context, border.style, border.width);
|
|
@@ -16770,13 +16869,22 @@ function strokeCanvasBorderSide(context, side, rect, border) {
|
|
|
16770
16869
|
} else {
|
|
16771
16870
|
strokeLine(0);
|
|
16772
16871
|
}
|
|
16773
|
-
context.
|
|
16872
|
+
context.setLineDash([]);
|
|
16873
|
+
context.lineCap = "butt";
|
|
16874
|
+
context.lineWidth = 1;
|
|
16774
16875
|
}
|
|
16775
16876
|
function truncateCanvasText(context, text, maxWidth) {
|
|
16776
16877
|
if (maxWidth <= 0 || text.length === 0) {
|
|
16777
16878
|
return "";
|
|
16778
16879
|
}
|
|
16779
|
-
|
|
16880
|
+
const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);
|
|
16881
|
+
const cacheKey = `${context.font}\0${roundedMaxWidth}\0${text}`;
|
|
16882
|
+
const cached = canvasTextTruncateCache.get(cacheKey);
|
|
16883
|
+
if (cached !== void 0) {
|
|
16884
|
+
return cached;
|
|
16885
|
+
}
|
|
16886
|
+
if (measureCanvasTextWidth(context, text) <= maxWidth) {
|
|
16887
|
+
rememberBoundedCacheValue(canvasTextTruncateCache, cacheKey, text, CANVAS_TEXT_TRUNCATE_CACHE_LIMIT);
|
|
16780
16888
|
return text;
|
|
16781
16889
|
}
|
|
16782
16890
|
const ellipsis = "\u2026";
|
|
@@ -16785,13 +16893,18 @@ function truncateCanvasText(context, text, maxWidth) {
|
|
|
16785
16893
|
while (low < high) {
|
|
16786
16894
|
const mid = Math.ceil((low + high) / 2);
|
|
16787
16895
|
const candidate = `${text.slice(0, mid)}${ellipsis}`;
|
|
16788
|
-
if (context
|
|
16896
|
+
if (measureCanvasTextWidth(context, candidate) <= maxWidth) {
|
|
16789
16897
|
low = mid;
|
|
16790
16898
|
} else {
|
|
16791
16899
|
high = mid - 1;
|
|
16792
16900
|
}
|
|
16793
16901
|
}
|
|
16794
|
-
return
|
|
16902
|
+
return rememberBoundedCacheValue(
|
|
16903
|
+
canvasTextTruncateCache,
|
|
16904
|
+
cacheKey,
|
|
16905
|
+
low <= 0 ? ellipsis : `${text.slice(0, low)}${ellipsis}`,
|
|
16906
|
+
CANVAS_TEXT_TRUNCATE_CACHE_LIMIT
|
|
16907
|
+
);
|
|
16795
16908
|
}
|
|
16796
16909
|
function resolveCanvasFontSizePx(style, fallbackSize = 12) {
|
|
16797
16910
|
const rawFontSize = style.fontSize;
|
|
@@ -16850,7 +16963,7 @@ function resolveCanvasWrapIndex(context, text, maxWidth) {
|
|
|
16850
16963
|
while (low <= high) {
|
|
16851
16964
|
const mid = Math.floor((low + high) / 2);
|
|
16852
16965
|
const candidate = text.slice(0, mid);
|
|
16853
|
-
if (context
|
|
16966
|
+
if (measureCanvasTextWidth(context, candidate) <= maxWidth) {
|
|
16854
16967
|
best = mid;
|
|
16855
16968
|
low = mid + 1;
|
|
16856
16969
|
} else {
|
|
@@ -16867,6 +16980,12 @@ function wrapCanvasText(context, text, maxWidth) {
|
|
|
16867
16980
|
return text.replace(/\r\n?/g, "\n").split("\n");
|
|
16868
16981
|
}
|
|
16869
16982
|
const normalized = text.replace(/\r\n?/g, "\n");
|
|
16983
|
+
const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);
|
|
16984
|
+
const cacheKey = `${context.font}\0${roundedMaxWidth}\0${normalized}`;
|
|
16985
|
+
const cached = canvasTextWrapCache.get(cacheKey);
|
|
16986
|
+
if (cached) {
|
|
16987
|
+
return cached;
|
|
16988
|
+
}
|
|
16870
16989
|
const paragraphs = normalized.split("\n");
|
|
16871
16990
|
const lines = [];
|
|
16872
16991
|
for (const paragraph of paragraphs) {
|
|
@@ -16876,7 +16995,7 @@ function wrapCanvasText(context, text, maxWidth) {
|
|
|
16876
16995
|
}
|
|
16877
16996
|
let remaining = paragraph;
|
|
16878
16997
|
while (remaining.length > 0) {
|
|
16879
|
-
if (context
|
|
16998
|
+
if (measureCanvasTextWidth(context, remaining) <= maxWidth) {
|
|
16880
16999
|
lines.push(remaining);
|
|
16881
17000
|
break;
|
|
16882
17001
|
}
|
|
@@ -16894,10 +17013,31 @@ function wrapCanvasText(context, text, maxWidth) {
|
|
|
16894
17013
|
remaining = remaining.slice(breakIndex).replace(/^\s+/g, "");
|
|
16895
17014
|
}
|
|
16896
17015
|
}
|
|
16897
|
-
return lines;
|
|
17016
|
+
return rememberBoundedCacheValue(canvasTextWrapCache, cacheKey, lines, CANVAS_TEXT_WRAP_CACHE_LIMIT);
|
|
16898
17017
|
}
|
|
16899
17018
|
function buildCanvasCellStyleCache(style) {
|
|
16900
|
-
|
|
17019
|
+
const cacheKey = [
|
|
17020
|
+
style.font,
|
|
17021
|
+
style.fontStyle,
|
|
17022
|
+
style.fontWeight,
|
|
17023
|
+
style.fontSize,
|
|
17024
|
+
style.fontFamily,
|
|
17025
|
+
style.borderTop,
|
|
17026
|
+
style.borderRight,
|
|
17027
|
+
style.borderBottom,
|
|
17028
|
+
style.borderLeft,
|
|
17029
|
+
style.padding,
|
|
17030
|
+
style.textAlign,
|
|
17031
|
+
style.color,
|
|
17032
|
+
style.textDecoration,
|
|
17033
|
+
style.textOverflow,
|
|
17034
|
+
style.whiteSpace
|
|
17035
|
+
].map((value) => value == null ? "" : String(value)).join("");
|
|
17036
|
+
const cached = canvasCellStyleCache.get(cacheKey);
|
|
17037
|
+
if (cached) {
|
|
17038
|
+
return cached;
|
|
17039
|
+
}
|
|
17040
|
+
return rememberBoundedCacheValue(canvasCellStyleCache, cacheKey, {
|
|
16901
17041
|
baseFont: resolveCanvasFont(style, 12),
|
|
16902
17042
|
bottomBorder: parseCanvasBorderDeclaration(style.borderBottom),
|
|
16903
17043
|
leftBorder: parseCanvasBorderDeclaration(style.borderLeft),
|
|
@@ -16909,7 +17049,7 @@ function buildCanvasCellStyleCache(style) {
|
|
|
16909
17049
|
textOverflowEllipsis: style.textOverflow === "ellipsis",
|
|
16910
17050
|
topBorder: parseCanvasBorderDeclaration(style.borderTop),
|
|
16911
17051
|
usesWrappedText: style.whiteSpace === "pre-wrap"
|
|
16912
|
-
};
|
|
17052
|
+
}, CANVAS_CELL_STYLE_CACHE_LIMIT);
|
|
16913
17053
|
}
|
|
16914
17054
|
function splitCssGradientArgs(value) {
|
|
16915
17055
|
const parts = [];
|
|
@@ -17462,6 +17602,25 @@ function resolveImageAnchorExtents(image) {
|
|
|
17462
17602
|
maxRow: Math.max(image.anchor.from.row, image.anchor.to.row)
|
|
17463
17603
|
};
|
|
17464
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
|
+
}
|
|
17465
17624
|
function resolveShapeAnchorExtents(shape) {
|
|
17466
17625
|
if (shape.anchor.kind === "absolute") {
|
|
17467
17626
|
return { maxCol: 0, maxRow: 0 };
|
|
@@ -18418,6 +18577,44 @@ function resolveShapeVector(shape) {
|
|
|
18418
18577
|
}
|
|
18419
18578
|
return buildPresetShapePath(shape);
|
|
18420
18579
|
}
|
|
18580
|
+
function drawCanvasRoundedRect(context, left, top, width, height, radius) {
|
|
18581
|
+
const safeRadius = Math.max(0, Math.min(radius, width / 2, height / 2));
|
|
18582
|
+
context.beginPath();
|
|
18583
|
+
context.moveTo(left + safeRadius, top);
|
|
18584
|
+
context.lineTo(left + width - safeRadius, top);
|
|
18585
|
+
context.quadraticCurveTo(left + width, top, left + width, top + safeRadius);
|
|
18586
|
+
context.lineTo(left + width, top + height - safeRadius);
|
|
18587
|
+
context.quadraticCurveTo(left + width, top + height, left + width - safeRadius, top + height);
|
|
18588
|
+
context.lineTo(left + safeRadius, top + height);
|
|
18589
|
+
context.quadraticCurveTo(left, top + height, left, top + height - safeRadius);
|
|
18590
|
+
context.lineTo(left, top + safeRadius);
|
|
18591
|
+
context.quadraticCurveTo(left, top, left + safeRadius, top);
|
|
18592
|
+
context.closePath();
|
|
18593
|
+
}
|
|
18594
|
+
function applyCanvasShapeDash(context, dash, lineWidth) {
|
|
18595
|
+
if (!dash) {
|
|
18596
|
+
context.setLineDash([]);
|
|
18597
|
+
return;
|
|
18598
|
+
}
|
|
18599
|
+
const unit = Math.max(1, lineWidth);
|
|
18600
|
+
switch (dash) {
|
|
18601
|
+
case "dash":
|
|
18602
|
+
context.setLineDash([4 * unit, 3 * unit]);
|
|
18603
|
+
break;
|
|
18604
|
+
case "dashDot":
|
|
18605
|
+
context.setLineDash([4 * unit, 2 * unit, unit, 2 * unit]);
|
|
18606
|
+
break;
|
|
18607
|
+
case "dot":
|
|
18608
|
+
context.setLineDash([unit, 2 * unit]);
|
|
18609
|
+
break;
|
|
18610
|
+
case "lgDash":
|
|
18611
|
+
context.setLineDash([8 * unit, 3 * unit]);
|
|
18612
|
+
break;
|
|
18613
|
+
default:
|
|
18614
|
+
context.setLineDash([]);
|
|
18615
|
+
break;
|
|
18616
|
+
}
|
|
18617
|
+
}
|
|
18421
18618
|
function resolveShapeLineEndMarker(type, markerId, color, strokeWidth, rect, viewBox) {
|
|
18422
18619
|
if (type !== "triangle") {
|
|
18423
18620
|
return null;
|
|
@@ -18607,6 +18804,71 @@ function buildThumbnailAxisItems(actualIndices, getSizePx) {
|
|
|
18607
18804
|
totalSize: offset
|
|
18608
18805
|
};
|
|
18609
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
|
+
}
|
|
18610
18872
|
function columnLabel2(col) {
|
|
18611
18873
|
let label = "";
|
|
18612
18874
|
let nextValue = col;
|
|
@@ -19020,12 +19282,12 @@ function measureTextWidth(value, style) {
|
|
|
19020
19282
|
return value.length * 7;
|
|
19021
19283
|
}
|
|
19022
19284
|
textMeasureCanvas ??= document.createElement("canvas");
|
|
19023
|
-
const context = textMeasureCanvas.getContext("2d");
|
|
19285
|
+
const context = textMeasureCanvas.getContext("2d", { alpha: false });
|
|
19024
19286
|
if (!context) {
|
|
19025
19287
|
return value.length * 7;
|
|
19026
19288
|
}
|
|
19027
19289
|
context.font = buildCanvasFont(style);
|
|
19028
|
-
return context
|
|
19290
|
+
return measureCanvasTextWidth(context, value);
|
|
19029
19291
|
}
|
|
19030
19292
|
function measureWrappedTextHeight(value, style, widthPx) {
|
|
19031
19293
|
if (!value) {
|
|
@@ -19039,7 +19301,7 @@ function measureWrappedTextHeight(value, style, widthPx) {
|
|
|
19039
19301
|
return fallbackLineCount * lineHeight + padding.top + padding.bottom;
|
|
19040
19302
|
}
|
|
19041
19303
|
textMeasureCanvas ??= document.createElement("canvas");
|
|
19042
|
-
const context = textMeasureCanvas.getContext("2d");
|
|
19304
|
+
const context = textMeasureCanvas.getContext("2d", { alpha: false });
|
|
19043
19305
|
if (!context) {
|
|
19044
19306
|
return fallbackLineCount * lineHeight + padding.top + padding.bottom;
|
|
19045
19307
|
}
|
|
@@ -20137,7 +20399,7 @@ function blitCanvasWithScrollDelta(canvas, context, bufferCanvas, dpr, width, he
|
|
|
20137
20399
|
if (bufferCanvas.height !== deviceHeight) {
|
|
20138
20400
|
bufferCanvas.height = deviceHeight;
|
|
20139
20401
|
}
|
|
20140
|
-
const bufferContext = bufferCanvas.getContext("2d");
|
|
20402
|
+
const bufferContext = bufferCanvas.getContext("2d", { alpha: false });
|
|
20141
20403
|
if (!bufferContext) {
|
|
20142
20404
|
return null;
|
|
20143
20405
|
}
|
|
@@ -20392,6 +20654,222 @@ function resolveFormControlLabel(control) {
|
|
|
20392
20654
|
}
|
|
20393
20655
|
return label.replace(/\u00a0/g, " ").replace(/^\s+/, "");
|
|
20394
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
|
+
}
|
|
20395
20873
|
function resolveConditionalDataBarForCell(row, col, worksheet, sheet, metricsCache) {
|
|
20396
20874
|
const rules = sheet?.conditionalFormatRules ?? [];
|
|
20397
20875
|
const matchingRule = rules.find(
|
|
@@ -20994,6 +21472,8 @@ function XlsxGrid({
|
|
|
20994
21472
|
selectionHeaderColor,
|
|
20995
21473
|
showImages = true
|
|
20996
21474
|
}) {
|
|
21475
|
+
const xlsxCanvasProfileTarget = typeof window !== "undefined" ? window.__xlsxCanvasProfile : void 0;
|
|
21476
|
+
const xlsxGridRenderStart = xlsxCanvasProfileTarget ? performance.now() : 0;
|
|
20997
21477
|
const {
|
|
20998
21478
|
activeCell,
|
|
20999
21479
|
activeSheet,
|
|
@@ -21077,6 +21557,11 @@ function XlsxGrid({
|
|
|
21077
21557
|
const leftFrozenHeaderCanvasRef = React4.useRef(null);
|
|
21078
21558
|
const leftScrollHeaderCanvasRef = React4.useRef(null);
|
|
21079
21559
|
const cornerHeaderCanvasRef = React4.useRef(null);
|
|
21560
|
+
const canvasScrollOverlayContentRef = React4.useRef(null);
|
|
21561
|
+
const canvasTopOverlayContentRef = React4.useRef(null);
|
|
21562
|
+
const canvasLeftOverlayContentRef = React4.useRef(null);
|
|
21563
|
+
const canvasCornerOverlayContentRef = React4.useRef(null);
|
|
21564
|
+
const canvasImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
|
|
21080
21565
|
const selectionOverlayRef = React4.useRef(null);
|
|
21081
21566
|
const activeValidationOverlayRef = React4.useRef(null);
|
|
21082
21567
|
const fillHandleRef = React4.useRef(null);
|
|
@@ -21105,6 +21590,7 @@ function XlsxGrid({
|
|
|
21105
21590
|
const pendingLiveZoomCommitRef = React4.useRef(null);
|
|
21106
21591
|
const touchPinchStateRef = React4.useRef(null);
|
|
21107
21592
|
const safariPinchStartZoomRef = React4.useRef(null);
|
|
21593
|
+
const canvasTransformStyleCacheRef = React4.useRef(/* @__PURE__ */ new WeakMap());
|
|
21108
21594
|
const displayedSelectionRef = React4.useRef(null);
|
|
21109
21595
|
const firstVisibleColRef = React4.useRef(void 0);
|
|
21110
21596
|
const lastVisibleColRef = React4.useRef(void 0);
|
|
@@ -21130,12 +21616,19 @@ function XlsxGrid({
|
|
|
21130
21616
|
const [fillPreviewRange, setFillPreviewRange] = React4.useState(null);
|
|
21131
21617
|
const [chartPreviewRect, setChartPreviewRect] = React4.useState(null);
|
|
21132
21618
|
const [liveGestureZoom, setLiveGestureZoom] = React4.useState(null);
|
|
21619
|
+
const [canvasImageLoadVersion, setCanvasImageLoadVersion] = React4.useState(0);
|
|
21133
21620
|
const liveDrawingViewportRef = React4.useRef({
|
|
21134
21621
|
height: 0,
|
|
21135
21622
|
left: 0,
|
|
21136
21623
|
top: 0,
|
|
21137
21624
|
width: 0
|
|
21138
21625
|
});
|
|
21626
|
+
const canvasLayoutMetricsRef = React4.useRef({
|
|
21627
|
+
displayHeaderHeight: HEADER_HEIGHT,
|
|
21628
|
+
displayRowHeaderWidth: ROW_HEADER_WIDTH,
|
|
21629
|
+
frozenPaneBottom: HEADER_HEIGHT,
|
|
21630
|
+
frozenPaneRight: ROW_HEADER_WIDTH
|
|
21631
|
+
});
|
|
21139
21632
|
const paintedDrawingViewportRef = React4.useRef({
|
|
21140
21633
|
height: 0,
|
|
21141
21634
|
left: 0,
|
|
@@ -21245,6 +21738,12 @@ function XlsxGrid({
|
|
|
21245
21738
|
const applyCanvasViewportCompensation = React4.useCallback((liveViewport) => {
|
|
21246
21739
|
const nextLiveViewport = liveViewport ?? liveDrawingViewportRef.current;
|
|
21247
21740
|
const paintedViewport = paintedDrawingViewportRef.current;
|
|
21741
|
+
const {
|
|
21742
|
+
displayHeaderHeight: liveDisplayHeaderHeight,
|
|
21743
|
+
displayRowHeaderWidth: liveDisplayRowHeaderWidth,
|
|
21744
|
+
frozenPaneBottom: liveFrozenPaneBottom,
|
|
21745
|
+
frozenPaneRight: liveFrozenPaneRight
|
|
21746
|
+
} = canvasLayoutMetricsRef.current;
|
|
21248
21747
|
const currentLiveGestureZoom = liveGestureZoomRef.current;
|
|
21249
21748
|
const isLiveZooming2 = currentLiveGestureZoom !== null && zoomScale === currentLiveGestureZoom.baseZoomScale;
|
|
21250
21749
|
const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
|
|
@@ -21252,12 +21751,41 @@ function XlsxGrid({
|
|
|
21252
21751
|
const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
|
|
21253
21752
|
const liveZoomTranslateX2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.x * (1 - liveZoomScale2) : 0;
|
|
21254
21753
|
const liveZoomTranslateY2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.y * (1 - liveZoomScale2) : 0;
|
|
21255
|
-
const
|
|
21256
|
-
if (!
|
|
21754
|
+
const applyElementTransform = (element, transform, willChange, transformOrigin = null) => {
|
|
21755
|
+
if (!element) {
|
|
21257
21756
|
return;
|
|
21258
21757
|
}
|
|
21259
|
-
|
|
21260
|
-
|
|
21758
|
+
const cached = canvasTransformStyleCacheRef.current.get(element);
|
|
21759
|
+
if (cached?.transform !== transform || element.style.transform !== transform) {
|
|
21760
|
+
element.style.transform = transform;
|
|
21761
|
+
}
|
|
21762
|
+
if (cached?.willChange !== willChange || element.style.willChange !== willChange) {
|
|
21763
|
+
element.style.willChange = willChange;
|
|
21764
|
+
}
|
|
21765
|
+
if (transformOrigin !== null && (cached?.transformOrigin !== transformOrigin || element.style.transformOrigin !== transformOrigin)) {
|
|
21766
|
+
element.style.transformOrigin = transformOrigin;
|
|
21767
|
+
}
|
|
21768
|
+
canvasTransformStyleCacheRef.current.set(element, {
|
|
21769
|
+
transform,
|
|
21770
|
+
transformOrigin,
|
|
21771
|
+
willChange
|
|
21772
|
+
});
|
|
21773
|
+
};
|
|
21774
|
+
const applyCanvasTransform = (canvas, translateX, translateY) => {
|
|
21775
|
+
const shouldTransform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1;
|
|
21776
|
+
applyElementTransform(
|
|
21777
|
+
canvas,
|
|
21778
|
+
shouldTransform ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : "",
|
|
21779
|
+
shouldTransform ? "transform" : ""
|
|
21780
|
+
);
|
|
21781
|
+
};
|
|
21782
|
+
const applyOverlayTransform = (element, translateX, translateY) => {
|
|
21783
|
+
applyElementTransform(
|
|
21784
|
+
element,
|
|
21785
|
+
liveZoomScale2 !== 1 ? `translate3d(${translateX + liveZoomTranslateX2}px, ${translateY + liveZoomTranslateY2}px, 0) scale(${liveZoomScale2})` : `translate3d(${translateX}px, ${translateY}px, 0)`,
|
|
21786
|
+
"transform",
|
|
21787
|
+
"0 0"
|
|
21788
|
+
);
|
|
21261
21789
|
};
|
|
21262
21790
|
applyCanvasTransform(
|
|
21263
21791
|
scrollBodyCanvasRef.current,
|
|
@@ -21304,6 +21832,26 @@ function XlsxGrid({
|
|
|
21304
21832
|
liveZoomTranslateX2,
|
|
21305
21833
|
liveZoomTranslateY2
|
|
21306
21834
|
);
|
|
21835
|
+
applyOverlayTransform(
|
|
21836
|
+
canvasScrollOverlayContentRef.current,
|
|
21837
|
+
-nextLiveViewport.left - liveFrozenPaneRight,
|
|
21838
|
+
-nextLiveViewport.top - liveFrozenPaneBottom
|
|
21839
|
+
);
|
|
21840
|
+
applyOverlayTransform(
|
|
21841
|
+
canvasTopOverlayContentRef.current,
|
|
21842
|
+
-nextLiveViewport.left - liveFrozenPaneRight,
|
|
21843
|
+
-liveDisplayHeaderHeight
|
|
21844
|
+
);
|
|
21845
|
+
applyOverlayTransform(
|
|
21846
|
+
canvasLeftOverlayContentRef.current,
|
|
21847
|
+
-liveDisplayRowHeaderWidth,
|
|
21848
|
+
-nextLiveViewport.top - liveFrozenPaneBottom
|
|
21849
|
+
);
|
|
21850
|
+
applyOverlayTransform(
|
|
21851
|
+
canvasCornerOverlayContentRef.current,
|
|
21852
|
+
-liveDisplayRowHeaderWidth,
|
|
21853
|
+
-liveDisplayHeaderHeight
|
|
21854
|
+
);
|
|
21307
21855
|
}, [zoomScale]);
|
|
21308
21856
|
const updateLiveGestureZoomState = React4.useCallback((nextState) => {
|
|
21309
21857
|
const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
|
|
@@ -21406,6 +21954,11 @@ function XlsxGrid({
|
|
|
21406
21954
|
if (matchesLiveViewport && matchesStateViewport) {
|
|
21407
21955
|
return;
|
|
21408
21956
|
}
|
|
21957
|
+
const shouldCommitDeferredViewport = !matchesStateViewport && (stateViewport.width !== nextViewport.width || stateViewport.height !== nextViewport.height || Math.abs(nextViewport.left - stateViewport.left) >= CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX || Math.abs(nextViewport.top - stateViewport.top) >= CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX);
|
|
21958
|
+
if (!shouldCommitDeferredViewport) {
|
|
21959
|
+
pendingDrawingViewportRef.current = null;
|
|
21960
|
+
return;
|
|
21961
|
+
}
|
|
21409
21962
|
pendingDrawingViewportRef.current = nextViewport;
|
|
21410
21963
|
if (drawingViewportFrameRef.current !== null) {
|
|
21411
21964
|
return;
|
|
@@ -21693,6 +22246,12 @@ function XlsxGrid({
|
|
|
21693
22246
|
) : displayRowHeaderWidth,
|
|
21694
22247
|
[displayActualColWidths, displayDefaultColWidth, displayRowHeaderWidth, frozenCols, stickyLeftByCol]
|
|
21695
22248
|
);
|
|
22249
|
+
canvasLayoutMetricsRef.current = {
|
|
22250
|
+
displayHeaderHeight,
|
|
22251
|
+
displayRowHeaderWidth,
|
|
22252
|
+
frozenPaneBottom,
|
|
22253
|
+
frozenPaneRight
|
|
22254
|
+
};
|
|
21696
22255
|
const rowPrefixSumsRef = React4.useRef(rowPrefixSums);
|
|
21697
22256
|
const colPrefixSumsRef = React4.useRef(colPrefixSums);
|
|
21698
22257
|
const firstVisibleRow = visibleRows[0];
|
|
@@ -21744,6 +22303,7 @@ function XlsxGrid({
|
|
|
21744
22303
|
}, [charts, images, shapes]);
|
|
21745
22304
|
const shouldVirtualizeRows = visibleRows.length > 0;
|
|
21746
22305
|
const shouldVirtualizeCols = visibleCols.length > 0 && frozenCols.length === 0;
|
|
22306
|
+
const shouldUseDomVirtualizer = !experimentalCanvas;
|
|
21747
22307
|
const getScrollElement = React4.useCallback(() => scrollRef.current, []);
|
|
21748
22308
|
const estimateRowSize = React4.useCallback(
|
|
21749
22309
|
(index) => displayEffectiveRowHeights[index] ?? displayDefaultRowHeight,
|
|
@@ -21757,6 +22317,7 @@ function XlsxGrid({
|
|
|
21757
22317
|
const getColItemKey = React4.useCallback((index) => visibleCols[index] ?? index, [visibleCols]);
|
|
21758
22318
|
const rowVirtualizer = (0, import_react_virtual.useVirtualizer)({
|
|
21759
22319
|
count: visibleRows.length,
|
|
22320
|
+
enabled: shouldUseDomVirtualizer,
|
|
21760
22321
|
estimateSize: estimateRowSize,
|
|
21761
22322
|
getScrollElement,
|
|
21762
22323
|
getItemKey: getRowItemKey,
|
|
@@ -21764,14 +22325,15 @@ function XlsxGrid({
|
|
|
21764
22325
|
});
|
|
21765
22326
|
const colVirtualizer = (0, import_react_virtual.useVirtualizer)({
|
|
21766
22327
|
count: visibleCols.length,
|
|
22328
|
+
enabled: shouldUseDomVirtualizer,
|
|
21767
22329
|
estimateSize: estimateColSize,
|
|
21768
22330
|
getScrollElement,
|
|
21769
22331
|
getItemKey: getColItemKey,
|
|
21770
22332
|
horizontal: true,
|
|
21771
22333
|
overscan: 6
|
|
21772
22334
|
});
|
|
21773
|
-
const currentRowVirtualItems = rowVirtualizer.getVirtualItems();
|
|
21774
|
-
const currentColVirtualItems = colVirtualizer.getVirtualItems();
|
|
22335
|
+
const currentRowVirtualItems = shouldUseDomVirtualizer ? rowVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;
|
|
22336
|
+
const currentColVirtualItems = shouldUseDomVirtualizer ? colVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;
|
|
21775
22337
|
const frozenRowVirtualIndices = React4.useMemo(
|
|
21776
22338
|
() => frozenRows.map((row) => rowIndexByActual.get(row)).filter((index) => index !== void 0),
|
|
21777
22339
|
[frozenRows, rowIndexByActual]
|
|
@@ -22007,10 +22569,12 @@ function XlsxGrid({
|
|
|
22007
22569
|
scroller.scrollTop = scroller.scrollTop / previousZoomFactor * zoomFactor;
|
|
22008
22570
|
}
|
|
22009
22571
|
previousZoomFactorRef.current = zoomFactor;
|
|
22010
|
-
|
|
22011
|
-
|
|
22572
|
+
if (shouldUseDomVirtualizer) {
|
|
22573
|
+
rowVirtualizer.measure();
|
|
22574
|
+
colVirtualizer.measure();
|
|
22575
|
+
}
|
|
22012
22576
|
syncDrawingViewport(scroller, { immediate: true });
|
|
22013
|
-
}, [syncDrawingViewport, zoomFactor]);
|
|
22577
|
+
}, [shouldUseDomVirtualizer, syncDrawingViewport, zoomFactor]);
|
|
22014
22578
|
React4.useLayoutEffect(() => {
|
|
22015
22579
|
syncDrawingViewport(scrollRef.current, { immediate: true });
|
|
22016
22580
|
}, [activeSheet, activeTabIndex, displayColLimit, displayRowLimit, syncDrawingViewport, zoomFactor]);
|
|
@@ -22106,7 +22670,7 @@ function XlsxGrid({
|
|
|
22106
22670
|
const initialUsedCol = resolveFirstUsedVisibleIndex(visibleCols, activeSheet?.minUsedCol ?? -1);
|
|
22107
22671
|
const initialScrollTop = initialUsedRow > 0 ? sumPrefixRange(actualRowPrefixSums, 0, initialUsedRow - 1) : 0;
|
|
22108
22672
|
const initialScrollLeft = initialUsedCol > 0 ? sumPrefixRange(actualColPrefixSums, 0, initialUsedCol - 1) : 0;
|
|
22109
|
-
if (shouldVirtualizeRows) {
|
|
22673
|
+
if (shouldUseDomVirtualizer && shouldVirtualizeRows) {
|
|
22110
22674
|
rowVirtualizer.scrollToOffset(initialScrollTop);
|
|
22111
22675
|
} else if (scrollRef.current) {
|
|
22112
22676
|
scrollRef.current.scrollTop = initialScrollTop;
|
|
@@ -22114,7 +22678,7 @@ function XlsxGrid({
|
|
|
22114
22678
|
if (scrollRef.current) {
|
|
22115
22679
|
scrollRef.current.scrollLeft = initialScrollLeft;
|
|
22116
22680
|
}
|
|
22117
|
-
if (shouldVirtualizeCols && frozenCols.length === 0) {
|
|
22681
|
+
if (shouldUseDomVirtualizer && shouldVirtualizeCols && frozenCols.length === 0) {
|
|
22118
22682
|
colVirtualizer.scrollToOffset(initialScrollLeft);
|
|
22119
22683
|
}
|
|
22120
22684
|
if (scrollRef.current) {
|
|
@@ -22135,6 +22699,7 @@ function XlsxGrid({
|
|
|
22135
22699
|
isWorkerBacked,
|
|
22136
22700
|
shouldVirtualizeCols,
|
|
22137
22701
|
shouldVirtualizeRows,
|
|
22702
|
+
shouldUseDomVirtualizer,
|
|
22138
22703
|
syncDrawingViewport,
|
|
22139
22704
|
rowVirtualizer,
|
|
22140
22705
|
visibleCols,
|
|
@@ -22154,24 +22719,40 @@ function XlsxGrid({
|
|
|
22154
22719
|
setPendingNavigation(null);
|
|
22155
22720
|
}, [activeSheetIndex, pendingNavigation, selectCell]);
|
|
22156
22721
|
React4.useEffect(() => {
|
|
22157
|
-
if (shouldVirtualizeRows) {
|
|
22722
|
+
if (shouldUseDomVirtualizer && shouldVirtualizeRows) {
|
|
22158
22723
|
rowVirtualizer.measure();
|
|
22159
22724
|
}
|
|
22160
|
-
if (shouldVirtualizeCols) {
|
|
22725
|
+
if (shouldUseDomVirtualizer && shouldVirtualizeCols) {
|
|
22161
22726
|
colVirtualizer.measure();
|
|
22162
22727
|
}
|
|
22163
|
-
}, [
|
|
22728
|
+
}, [
|
|
22729
|
+
activeSheetIndex,
|
|
22730
|
+
revision,
|
|
22731
|
+
shouldUseDomVirtualizer,
|
|
22732
|
+
shouldVirtualizeCols,
|
|
22733
|
+
shouldVirtualizeRows,
|
|
22734
|
+
visibleCols.length,
|
|
22735
|
+
visibleRows.length
|
|
22736
|
+
]);
|
|
22164
22737
|
const handleScrollerScroll = React4.useCallback((event) => {
|
|
22165
22738
|
const currentScroller = event.currentTarget;
|
|
22166
22739
|
cachedScrollerRectRef.current = null;
|
|
22167
|
-
|
|
22740
|
+
const paintedViewport = paintedDrawingViewportRef.current;
|
|
22741
|
+
const shouldSyncDrawingViewportImmediately = Math.abs(currentScroller.scrollLeft - paintedViewport.left) > CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX || Math.abs(currentScroller.scrollTop - paintedViewport.top) > CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX;
|
|
22742
|
+
syncDrawingViewport(currentScroller, { immediate: shouldSyncDrawingViewportImmediately });
|
|
22168
22743
|
if (currentScroller.scrollHeight - (currentScroller.scrollTop + currentScroller.clientHeight) < OPEN_GRID_VERTICAL_EDGE_PX) {
|
|
22169
|
-
setDisplayRowLimit((current) =>
|
|
22744
|
+
setDisplayRowLimit((current) => {
|
|
22745
|
+
const nextLimit = current + OPEN_GRID_ROW_GROWTH;
|
|
22746
|
+
return readOnly && current < maxRowDisplayLimit ? Math.min(maxRowDisplayLimit, nextLimit) : nextLimit;
|
|
22747
|
+
});
|
|
22170
22748
|
}
|
|
22171
22749
|
if (currentScroller.scrollWidth - (currentScroller.scrollLeft + currentScroller.clientWidth) < OPEN_GRID_HORIZONTAL_EDGE_PX) {
|
|
22172
|
-
setDisplayColLimit((current) =>
|
|
22750
|
+
setDisplayColLimit((current) => {
|
|
22751
|
+
const nextLimit = current + OPEN_GRID_COL_GROWTH;
|
|
22752
|
+
return readOnly && current < maxColDisplayLimit ? Math.min(maxColDisplayLimit, nextLimit) : nextLimit;
|
|
22753
|
+
});
|
|
22173
22754
|
}
|
|
22174
|
-
}, [syncDrawingViewport]);
|
|
22755
|
+
}, [maxColDisplayLimit, maxRowDisplayLimit, readOnly, syncDrawingViewport]);
|
|
22175
22756
|
React4.useEffect(() => {
|
|
22176
22757
|
const scroller = scrollRef.current;
|
|
22177
22758
|
if (!scroller || !enableGestureZoom) {
|
|
@@ -23077,14 +23658,14 @@ function XlsxGrid({
|
|
|
23077
23658
|
},
|
|
23078
23659
|
left: {
|
|
23079
23660
|
cols: frozenColItems,
|
|
23080
|
-
rows:
|
|
23661
|
+
rows: scrollRowItems
|
|
23081
23662
|
},
|
|
23082
23663
|
scroll: {
|
|
23083
|
-
cols:
|
|
23084
|
-
rows:
|
|
23664
|
+
cols: scrollColItems,
|
|
23665
|
+
rows: scrollRowItems
|
|
23085
23666
|
},
|
|
23086
23667
|
top: {
|
|
23087
|
-
cols:
|
|
23668
|
+
cols: scrollColItems,
|
|
23088
23669
|
rows: frozenRowItems
|
|
23089
23670
|
}
|
|
23090
23671
|
};
|
|
@@ -23218,6 +23799,51 @@ function XlsxGrid({
|
|
|
23218
23799
|
visibleRows
|
|
23219
23800
|
]
|
|
23220
23801
|
);
|
|
23802
|
+
const shouldBakeCanvasStaticDrawings = Boolean(experimentalCanvas && readOnly && showImages);
|
|
23803
|
+
const shouldBakeCanvasImages = Boolean(shouldBakeCanvasStaticDrawings && !renderImage && !renderImageSelection);
|
|
23804
|
+
const bakedShapeRects = React4.useMemo(
|
|
23805
|
+
() => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => !shape.hyperlink) : [],
|
|
23806
|
+
[shapeRects, shouldBakeCanvasStaticDrawings]
|
|
23807
|
+
);
|
|
23808
|
+
const domShapeRects = React4.useMemo(
|
|
23809
|
+
() => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => shape.hyperlink) : shapeRects,
|
|
23810
|
+
[shapeRects, shouldBakeCanvasStaticDrawings]
|
|
23811
|
+
);
|
|
23812
|
+
const bakedFormControlRects = React4.useMemo(
|
|
23813
|
+
() => shouldBakeCanvasStaticDrawings ? formControlRects : [],
|
|
23814
|
+
[formControlRects, shouldBakeCanvasStaticDrawings]
|
|
23815
|
+
);
|
|
23816
|
+
const domFormControlRects = React4.useMemo(
|
|
23817
|
+
() => shouldBakeCanvasStaticDrawings ? [] : formControlRects,
|
|
23818
|
+
[formControlRects, shouldBakeCanvasStaticDrawings]
|
|
23819
|
+
);
|
|
23820
|
+
const bakedImageRects = React4.useMemo(
|
|
23821
|
+
() => shouldBakeCanvasImages ? imageRects.filter(({ image }) => !image.hyperlink && selectedImageId !== image.id) : [],
|
|
23822
|
+
[imageRects, selectedImageId, shouldBakeCanvasImages]
|
|
23823
|
+
);
|
|
23824
|
+
const domImageRects = React4.useMemo(
|
|
23825
|
+
() => shouldBakeCanvasImages ? imageRects.filter(({ image }) => image.hyperlink || selectedImageId === image.id) : imageRects,
|
|
23826
|
+
[imageRects, selectedImageId, shouldBakeCanvasImages]
|
|
23827
|
+
);
|
|
23828
|
+
const hasCanvasDomDrawingOverlays = Boolean(
|
|
23829
|
+
showImages && (chartRects.length > 0 || domShapeRects.length > 0 || domFormControlRects.length > 0 || domImageRects.length > 0)
|
|
23830
|
+
);
|
|
23831
|
+
const bakedCanvasDrawingSignature = React4.useMemo(() => {
|
|
23832
|
+
if (!shouldBakeCanvasStaticDrawings) {
|
|
23833
|
+
return "";
|
|
23834
|
+
}
|
|
23835
|
+
const shapeSignature = bakedShapeRects.map(({ rect, shape }) => `s:${shape.id}:${shape.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
|
|
23836
|
+
const controlSignature = bakedFormControlRects.map(({ control, rect }) => `f:${control.id}:${control.zIndex}:${Boolean(control.checked)}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
|
|
23837
|
+
const imageSignature = bakedImageRects.map(({ image, rect }) => `i:${image.id}:${image.src}:${image.zIndex}:${Math.round(rect.left)}:${Math.round(rect.top)}:${Math.round(rect.width)}:${Math.round(rect.height)}`).join("|");
|
|
23838
|
+
return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${controlSignature}:${imageSignature}`;
|
|
23839
|
+
}, [
|
|
23840
|
+
bakedFormControlRects,
|
|
23841
|
+
bakedImageRects,
|
|
23842
|
+
bakedShapeRects,
|
|
23843
|
+
canvasImageLoadVersion,
|
|
23844
|
+
revision,
|
|
23845
|
+
shouldBakeCanvasStaticDrawings
|
|
23846
|
+
]);
|
|
23221
23847
|
const resolveMountedCellOverlayRect = React4.useCallback((element) => {
|
|
23222
23848
|
const wrapper = wrapperRef.current;
|
|
23223
23849
|
if (!wrapper) {
|
|
@@ -24086,6 +24712,24 @@ function XlsxGrid({
|
|
|
24086
24712
|
rowPrefixSums,
|
|
24087
24713
|
stickyTopByRow
|
|
24088
24714
|
]);
|
|
24715
|
+
const scrollBodyViewportWidth = Math.max(0, drawingViewport.width - frozenPaneRight);
|
|
24716
|
+
const scrollBodyViewportHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);
|
|
24717
|
+
const canvasScrollBufferX = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportWidth);
|
|
24718
|
+
const canvasScrollBufferY = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportHeight);
|
|
24719
|
+
const scrollBodyCanvasLeft = frozenPaneRight - canvasScrollBufferX;
|
|
24720
|
+
const scrollBodyCanvasTop = frozenPaneBottom - canvasScrollBufferY;
|
|
24721
|
+
const scrollBodyCanvasWidth = scrollBodyViewportWidth + canvasScrollBufferX * 2;
|
|
24722
|
+
const scrollBodyCanvasHeight = scrollBodyViewportHeight + canvasScrollBufferY * 2;
|
|
24723
|
+
const topBodyCanvasWidth = scrollBodyCanvasWidth;
|
|
24724
|
+
const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);
|
|
24725
|
+
const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
|
|
24726
|
+
const leftBodyCanvasHeight = scrollBodyCanvasHeight;
|
|
24727
|
+
const cornerBodyCanvasWidth = leftBodyCanvasWidth;
|
|
24728
|
+
const cornerBodyCanvasHeight = topBodyCanvasHeight;
|
|
24729
|
+
const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
|
|
24730
|
+
const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
|
|
24731
|
+
const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
|
|
24732
|
+
const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
|
|
24089
24733
|
const canvasColumnHeaderCells = React4.useMemo(
|
|
24090
24734
|
() => canvasVisibleColItems.flatMap((column) => {
|
|
24091
24735
|
const rect = resolveCanvasColumnHeaderRect(column.actualCol);
|
|
@@ -24098,7 +24742,7 @@ function XlsxGrid({
|
|
|
24098
24742
|
height: displayHeaderHeight,
|
|
24099
24743
|
isFrozen,
|
|
24100
24744
|
left: rect.left,
|
|
24101
|
-
localLeft: rect.left - (isFrozen ? displayRowHeaderWidth :
|
|
24745
|
+
localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : scrollBodyCanvasLeft),
|
|
24102
24746
|
width: rect.width
|
|
24103
24747
|
}];
|
|
24104
24748
|
}),
|
|
@@ -24106,8 +24750,8 @@ function XlsxGrid({
|
|
|
24106
24750
|
canvasVisibleColItems,
|
|
24107
24751
|
displayHeaderHeight,
|
|
24108
24752
|
displayRowHeaderWidth,
|
|
24109
|
-
frozenPaneRight,
|
|
24110
24753
|
resolveCanvasColumnHeaderRect,
|
|
24754
|
+
scrollBodyCanvasLeft,
|
|
24111
24755
|
stickyLeftByCol
|
|
24112
24756
|
]
|
|
24113
24757
|
);
|
|
@@ -24122,15 +24766,15 @@ function XlsxGrid({
|
|
|
24122
24766
|
actualRow: row.actualRow,
|
|
24123
24767
|
height: rect.height,
|
|
24124
24768
|
isFrozen,
|
|
24125
|
-
localTop: rect.top - (isFrozen ? displayHeaderHeight :
|
|
24769
|
+
localTop: rect.top - (isFrozen ? displayHeaderHeight : scrollBodyCanvasTop),
|
|
24126
24770
|
top: rect.top
|
|
24127
24771
|
}];
|
|
24128
24772
|
}),
|
|
24129
24773
|
[
|
|
24130
24774
|
canvasVisibleRowItems,
|
|
24131
24775
|
displayHeaderHeight,
|
|
24132
|
-
frozenPaneBottom,
|
|
24133
24776
|
resolveCanvasRowHeaderRect,
|
|
24777
|
+
scrollBodyCanvasTop,
|
|
24134
24778
|
stickyTopByRow
|
|
24135
24779
|
]
|
|
24136
24780
|
);
|
|
@@ -24397,10 +25041,46 @@ function XlsxGrid({
|
|
|
24397
25041
|
rowPrefixSums,
|
|
24398
25042
|
startCellSelection
|
|
24399
25043
|
]);
|
|
25044
|
+
const getCanvasImage = React4.useCallback((image) => {
|
|
25045
|
+
if (typeof Image === "undefined") {
|
|
25046
|
+
return null;
|
|
25047
|
+
}
|
|
25048
|
+
const cacheKey = image.id;
|
|
25049
|
+
const cached = canvasImageCacheRef.current.get(cacheKey);
|
|
25050
|
+
if (cached && cached.src === image.src) {
|
|
25051
|
+
return cached.loaded && !cached.failed ? cached.image : null;
|
|
25052
|
+
}
|
|
25053
|
+
const imageElement = new Image();
|
|
25054
|
+
const entry = {
|
|
25055
|
+
failed: false,
|
|
25056
|
+
image: imageElement,
|
|
25057
|
+
loaded: false,
|
|
25058
|
+
src: image.src
|
|
25059
|
+
};
|
|
25060
|
+
canvasImageCacheRef.current.set(cacheKey, entry);
|
|
25061
|
+
imageElement.onload = () => {
|
|
25062
|
+
entry.loaded = true;
|
|
25063
|
+
setCanvasImageLoadVersion((version) => version + 1);
|
|
25064
|
+
};
|
|
25065
|
+
imageElement.onerror = () => {
|
|
25066
|
+
entry.failed = true;
|
|
25067
|
+
setCanvasImageLoadVersion((version) => version + 1);
|
|
25068
|
+
};
|
|
25069
|
+
imageElement.src = image.src;
|
|
25070
|
+
return null;
|
|
25071
|
+
}, []);
|
|
24400
25072
|
React4.useLayoutEffect(() => {
|
|
24401
25073
|
if (!experimentalCanvas) {
|
|
24402
25074
|
return;
|
|
24403
25075
|
}
|
|
25076
|
+
const canvasProfileTarget = typeof window !== "undefined" ? window.__xlsxCanvasProfile : void 0;
|
|
25077
|
+
const canvasProfileStart = canvasProfileTarget ? performance.now() : 0;
|
|
25078
|
+
let canvasProfileBodyStart = 0;
|
|
25079
|
+
let canvasProfileBodyMs = 0;
|
|
25080
|
+
let canvasProfileCulledCells = 0;
|
|
25081
|
+
let canvasProfileDirtyRects = 0;
|
|
25082
|
+
let canvasProfileLookedUpCells = 0;
|
|
25083
|
+
let canvasProfilePaintedCells = 0;
|
|
24404
25084
|
const dpr = typeof window === "undefined" ? 1 : Math.max(1, window.devicePixelRatio || 1);
|
|
24405
25085
|
function configureCanvas(canvas, width, height, options) {
|
|
24406
25086
|
if (!canvas) {
|
|
@@ -24420,7 +25100,7 @@ function XlsxGrid({
|
|
|
24420
25100
|
if (canvas.style.height !== `${height}px`) {
|
|
24421
25101
|
canvas.style.height = `${height}px`;
|
|
24422
25102
|
}
|
|
24423
|
-
const context = canvas.getContext("2d");
|
|
25103
|
+
const context = canvas.getContext("2d", { alpha: false });
|
|
24424
25104
|
if (!context) {
|
|
24425
25105
|
return null;
|
|
24426
25106
|
}
|
|
@@ -24439,6 +25119,7 @@ function XlsxGrid({
|
|
|
24439
25119
|
const rangeSignature = buildRangeSignature(normalizedVisibleRange);
|
|
24440
25120
|
const nextBodyCanvasSignature = {
|
|
24441
25121
|
activeSheet: activeSheet ?? null,
|
|
25122
|
+
bakedDrawingSignature: bakedCanvasDrawingSignature,
|
|
24442
25123
|
bodyHeight,
|
|
24443
25124
|
bodyWidth,
|
|
24444
25125
|
colSignature: buildRenderedAxisSignature(canvasVisibleColItems, (item) => item.index),
|
|
@@ -24466,10 +25147,10 @@ function XlsxGrid({
|
|
|
24466
25147
|
const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
|
|
24467
25148
|
const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
|
|
24468
25149
|
const previousPaintedViewport = paintedDrawingViewportRef.current;
|
|
24469
|
-
const shouldRepaintBody = !(previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.colSignature === nextBodyCanvasSignature.colSignature && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.rowSignature === nextBodyCanvasSignature.rowSignature && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor);
|
|
25150
|
+
const shouldRepaintBody = !(previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.colSignature === nextBodyCanvasSignature.colSignature && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.rowSignature === nextBodyCanvasSignature.rowSignature && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor);
|
|
24470
25151
|
const shouldRepaintHeaders = !(previousHeaderCanvasSignature && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth && previousHeaderCanvasSignature.colSignature === nextHeaderCanvasSignature.colSignature && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth && previousHeaderCanvasSignature.rowSignature === nextHeaderCanvasSignature.rowSignature && previousHeaderCanvasSignature.viewportLeft === nextHeaderCanvasSignature.viewportLeft && previousHeaderCanvasSignature.viewportTop === nextHeaderCanvasSignature.viewportTop && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor);
|
|
24471
25152
|
const canBlitBody = Boolean(
|
|
24472
|
-
shouldRepaintBody && previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor
|
|
25153
|
+
shouldRepaintBody && previousBodyCanvasSignature && previousBodyCanvasSignature.activeSheet === nextBodyCanvasSignature.activeSheet && previousBodyCanvasSignature.bakedDrawingSignature === nextBodyCanvasSignature.bakedDrawingSignature && previousBodyCanvasSignature.bodyHeight === nextBodyCanvasSignature.bodyHeight && previousBodyCanvasSignature.bodyWidth === nextBodyCanvasSignature.bodyWidth && previousBodyCanvasSignature.getCellData === nextBodyCanvasSignature.getCellData && previousBodyCanvasSignature.headerHeight === nextBodyCanvasSignature.headerHeight && previousBodyCanvasSignature.palette === nextBodyCanvasSignature.palette && previousBodyCanvasSignature.rowHeaderWidth === nextBodyCanvasSignature.rowHeaderWidth && previousBodyCanvasSignature.zoomFactor === nextBodyCanvasSignature.zoomFactor
|
|
24473
25154
|
);
|
|
24474
25155
|
const canBlitTopHeader = Boolean(
|
|
24475
25156
|
shouldRepaintHeaders && previousHeaderCanvasSignature && previousHeaderCanvasSignature.activeSheet === nextHeaderCanvasSignature.activeSheet && previousHeaderCanvasSignature.bodyHeight === nextHeaderCanvasSignature.bodyHeight && previousHeaderCanvasSignature.bodyWidth === nextHeaderCanvasSignature.bodyWidth && previousHeaderCanvasSignature.headerHeight === nextHeaderCanvasSignature.headerHeight && previousHeaderCanvasSignature.palette === nextHeaderCanvasSignature.palette && previousHeaderCanvasSignature.rangeSignature === nextHeaderCanvasSignature.rangeSignature && previousHeaderCanvasSignature.rowHeaderWidth === nextHeaderCanvasSignature.rowHeaderWidth && previousHeaderCanvasSignature.zoomFactor === nextHeaderCanvasSignature.zoomFactor
|
|
@@ -24480,49 +25161,37 @@ function XlsxGrid({
|
|
|
24480
25161
|
if (!shouldRepaintBody && !shouldRepaintHeaders) {
|
|
24481
25162
|
return;
|
|
24482
25163
|
}
|
|
24483
|
-
const scrollBodyCanvasWidth2 = Math.max(0, drawingViewport.width - frozenPaneRight);
|
|
24484
|
-
const scrollBodyCanvasHeight2 = Math.max(0, drawingViewport.height - frozenPaneBottom);
|
|
24485
|
-
const topBodyCanvasWidth2 = scrollBodyCanvasWidth2;
|
|
24486
|
-
const topBodyCanvasHeight2 = Math.max(0, frozenPaneBottom - displayHeaderHeight);
|
|
24487
|
-
const leftBodyCanvasWidth2 = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
|
|
24488
|
-
const leftBodyCanvasHeight2 = scrollBodyCanvasHeight2;
|
|
24489
|
-
const cornerBodyCanvasWidth2 = leftBodyCanvasWidth2;
|
|
24490
|
-
const cornerBodyCanvasHeight2 = topBodyCanvasHeight2;
|
|
24491
|
-
const topFrozenHeaderCanvasWidth2 = leftBodyCanvasWidth2;
|
|
24492
|
-
const topScrollHeaderCanvasWidth2 = scrollBodyCanvasWidth2;
|
|
24493
|
-
const leftFrozenHeaderCanvasHeight2 = topBodyCanvasHeight2;
|
|
24494
|
-
const leftScrollHeaderCanvasHeight2 = scrollBodyCanvasHeight2;
|
|
24495
25164
|
const paneBounds = {
|
|
24496
25165
|
corner: {
|
|
24497
|
-
height:
|
|
25166
|
+
height: cornerBodyCanvasHeight,
|
|
24498
25167
|
left: displayRowHeaderWidth,
|
|
24499
25168
|
top: displayHeaderHeight,
|
|
24500
|
-
width:
|
|
25169
|
+
width: cornerBodyCanvasWidth
|
|
24501
25170
|
},
|
|
24502
25171
|
left: {
|
|
24503
|
-
height:
|
|
25172
|
+
height: leftBodyCanvasHeight,
|
|
24504
25173
|
left: displayRowHeaderWidth,
|
|
24505
|
-
top:
|
|
24506
|
-
width:
|
|
25174
|
+
top: scrollBodyCanvasTop,
|
|
25175
|
+
width: leftBodyCanvasWidth
|
|
24507
25176
|
},
|
|
24508
25177
|
scroll: {
|
|
24509
|
-
height:
|
|
24510
|
-
left:
|
|
24511
|
-
top:
|
|
24512
|
-
width:
|
|
25178
|
+
height: scrollBodyCanvasHeight,
|
|
25179
|
+
left: scrollBodyCanvasLeft,
|
|
25180
|
+
top: scrollBodyCanvasTop,
|
|
25181
|
+
width: scrollBodyCanvasWidth
|
|
24513
25182
|
},
|
|
24514
25183
|
top: {
|
|
24515
|
-
height:
|
|
24516
|
-
left:
|
|
25184
|
+
height: topBodyCanvasHeight,
|
|
25185
|
+
left: scrollBodyCanvasLeft,
|
|
24517
25186
|
top: displayHeaderHeight,
|
|
24518
|
-
width:
|
|
25187
|
+
width: topBodyCanvasWidth
|
|
24519
25188
|
}
|
|
24520
25189
|
};
|
|
24521
25190
|
const bodyContexts = shouldRepaintBody ? {
|
|
24522
|
-
corner: configureCanvas(cornerBodyCanvasRef.current,
|
|
24523
|
-
left: configureCanvas(leftBodyCanvasRef.current,
|
|
24524
|
-
scroll: configureCanvas(scrollBodyCanvasRef.current,
|
|
24525
|
-
top: configureCanvas(topBodyCanvasRef.current,
|
|
25191
|
+
corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth, cornerBodyCanvasHeight, { clear: !canBlitBody }),
|
|
25192
|
+
left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth, leftBodyCanvasHeight, { clear: !canBlitBody }),
|
|
25193
|
+
scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth, scrollBodyCanvasHeight, { clear: !canBlitBody }),
|
|
25194
|
+
top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth, topBodyCanvasHeight, { clear: !canBlitBody })
|
|
24526
25195
|
} : {
|
|
24527
25196
|
corner: null,
|
|
24528
25197
|
left: null,
|
|
@@ -24530,21 +25199,21 @@ function XlsxGrid({
|
|
|
24530
25199
|
top: null
|
|
24531
25200
|
};
|
|
24532
25201
|
const topHeaderContexts = shouldRepaintHeaders ? {
|
|
24533
|
-
frozen: configureCanvas(topFrozenHeaderCanvasRef.current,
|
|
24534
|
-
scroll: configureCanvas(topScrollHeaderCanvasRef.current,
|
|
25202
|
+
frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth, headerHeight),
|
|
25203
|
+
scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth, headerHeight, { clear: !canBlitTopHeader })
|
|
24535
25204
|
} : {
|
|
24536
25205
|
frozen: null,
|
|
24537
25206
|
scroll: null
|
|
24538
25207
|
};
|
|
24539
25208
|
const leftHeaderContexts = shouldRepaintHeaders ? {
|
|
24540
|
-
frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth,
|
|
24541
|
-
scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth,
|
|
25209
|
+
frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight),
|
|
25210
|
+
scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight, { clear: !canBlitLeftHeader })
|
|
24542
25211
|
} : {
|
|
24543
25212
|
frozen: null,
|
|
24544
25213
|
scroll: null
|
|
24545
25214
|
};
|
|
24546
25215
|
const cornerContext = shouldRepaintHeaders ? configureCanvas(cornerHeaderCanvasRef.current, rowHeaderWidth, headerHeight) : null;
|
|
24547
|
-
if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!cornerContext ||
|
|
25216
|
+
if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!cornerContext || topFrozenHeaderCanvasWidth > 0 && !topHeaderContexts.frozen || topScrollHeaderCanvasWidth > 0 && !topHeaderContexts.scroll || leftFrozenHeaderCanvasHeight > 0 && !leftHeaderContexts.frozen || leftScrollHeaderCanvasHeight > 0 && !leftHeaderContexts.scroll)) {
|
|
24548
25217
|
return;
|
|
24549
25218
|
}
|
|
24550
25219
|
const showGridLines = activeSheet?.showGridLines ?? true;
|
|
@@ -24561,10 +25230,282 @@ function XlsxGrid({
|
|
|
24561
25230
|
scroll: [],
|
|
24562
25231
|
top: []
|
|
24563
25232
|
};
|
|
24564
|
-
let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(
|
|
24565
|
-
let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth,
|
|
25233
|
+
let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth, headerHeight);
|
|
25234
|
+
let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight);
|
|
25235
|
+
const bakedCanvasDrawingEntries = shouldBakeCanvasStaticDrawings ? [
|
|
25236
|
+
...bakedShapeRects.map(({ rect, shape }) => ({
|
|
25237
|
+
kind: "shape",
|
|
25238
|
+
rect,
|
|
25239
|
+
shape,
|
|
25240
|
+
zIndex: shape.zIndex
|
|
25241
|
+
})),
|
|
25242
|
+
...bakedFormControlRects.map(({ control, rect }) => ({
|
|
25243
|
+
control,
|
|
25244
|
+
kind: "formControl",
|
|
25245
|
+
rect,
|
|
25246
|
+
zIndex: control.zIndex
|
|
25247
|
+
})),
|
|
25248
|
+
...bakedImageRects.map(({ image, rect }) => ({
|
|
25249
|
+
image,
|
|
25250
|
+
kind: "image",
|
|
25251
|
+
rect,
|
|
25252
|
+
zIndex: image.zIndex
|
|
25253
|
+
}))
|
|
25254
|
+
].sort((left, right) => left.zIndex - right.zIndex) : [];
|
|
25255
|
+
const resolveBakedCanvasLocalRect = (rect, pane) => {
|
|
25256
|
+
const bounds = paneBounds[pane];
|
|
25257
|
+
const scrollX = pane === "scroll" || pane === "top" ? drawingViewport.left : 0;
|
|
25258
|
+
const scrollY = pane === "scroll" || pane === "left" ? drawingViewport.top : 0;
|
|
25259
|
+
return {
|
|
25260
|
+
height: rect.height,
|
|
25261
|
+
left: rect.left - scrollX - bounds.left,
|
|
25262
|
+
top: rect.top - scrollY - bounds.top,
|
|
25263
|
+
width: rect.width
|
|
25264
|
+
};
|
|
25265
|
+
};
|
|
25266
|
+
const drawBakedShapeText = (context, shape, left, top, width, height) => {
|
|
25267
|
+
if (shape.paragraphs.length === 0) {
|
|
25268
|
+
return;
|
|
25269
|
+
}
|
|
25270
|
+
const inset = shape.textBox?.insetPx;
|
|
25271
|
+
const paddingLeft = (inset?.left ?? 6) * zoomFactor;
|
|
25272
|
+
const paddingRight = (inset?.right ?? 6) * zoomFactor;
|
|
25273
|
+
const paddingTop = (inset?.top ?? 4) * zoomFactor;
|
|
25274
|
+
const paddingBottom = (inset?.bottom ?? 4) * zoomFactor;
|
|
25275
|
+
const textLeft = left + paddingLeft;
|
|
25276
|
+
const textTop = top + paddingTop;
|
|
25277
|
+
const textWidth = Math.max(0, width - paddingLeft - paddingRight);
|
|
25278
|
+
const textHeight = Math.max(0, height - paddingTop - paddingBottom);
|
|
25279
|
+
if (textWidth <= 0 || textHeight <= 0) {
|
|
25280
|
+
return;
|
|
25281
|
+
}
|
|
25282
|
+
const lineMetrics = shape.paragraphs.map((paragraph) => {
|
|
25283
|
+
const lineHeight = Math.max(
|
|
25284
|
+
12 * zoomFactor,
|
|
25285
|
+
...paragraph.runs.map((run) => (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor * 1.2)
|
|
25286
|
+
);
|
|
25287
|
+
const widthPx = paragraph.runs.reduce((total, run) => {
|
|
25288
|
+
const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
|
|
25289
|
+
context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
|
|
25290
|
+
return total + measureCanvasTextWidth(context, run.text);
|
|
25291
|
+
}, 0);
|
|
25292
|
+
return { lineHeight, widthPx };
|
|
25293
|
+
});
|
|
25294
|
+
const totalTextHeight = lineMetrics.reduce((total, metric) => total + metric.lineHeight, 0);
|
|
25295
|
+
let y = textTop;
|
|
25296
|
+
if (shape.textBox?.verticalAlign === "middle") {
|
|
25297
|
+
y = textTop + Math.max(0, (textHeight - totalTextHeight) / 2);
|
|
25298
|
+
} else if (shape.textBox?.verticalAlign === "bottom") {
|
|
25299
|
+
y = textTop + Math.max(0, textHeight - totalTextHeight);
|
|
25300
|
+
}
|
|
25301
|
+
context.save();
|
|
25302
|
+
context.beginPath();
|
|
25303
|
+
context.rect(textLeft, textTop, textWidth, textHeight);
|
|
25304
|
+
context.clip();
|
|
25305
|
+
context.textBaseline = "middle";
|
|
25306
|
+
shape.paragraphs.forEach((paragraph, paragraphIndex) => {
|
|
25307
|
+
const metric = lineMetrics[paragraphIndex] ?? { lineHeight: 14 * zoomFactor, widthPx: 0 };
|
|
25308
|
+
let x = textLeft;
|
|
25309
|
+
const align = paragraph.align ?? shape.textBox?.horizontalAlign ?? "left";
|
|
25310
|
+
if (align === "center") {
|
|
25311
|
+
x = textLeft + Math.max(0, (textWidth - metric.widthPx) / 2);
|
|
25312
|
+
} else if (align === "right") {
|
|
25313
|
+
x = textLeft + Math.max(0, textWidth - metric.widthPx);
|
|
25314
|
+
}
|
|
25315
|
+
paragraph.runs.forEach((run) => {
|
|
25316
|
+
const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
|
|
25317
|
+
context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
|
|
25318
|
+
context.fillStyle = run.color ?? "#000000";
|
|
25319
|
+
context.textAlign = "left";
|
|
25320
|
+
const textY = y + metric.lineHeight / 2;
|
|
25321
|
+
context.fillText(run.text, x, textY);
|
|
25322
|
+
if (run.underline && run.text.length > 0) {
|
|
25323
|
+
const textWidthPx = measureCanvasTextWidth(context, run.text);
|
|
25324
|
+
context.strokeStyle = run.color ?? "#000000";
|
|
25325
|
+
context.lineWidth = Math.max(1, zoomFactor * 0.75);
|
|
25326
|
+
context.beginPath();
|
|
25327
|
+
context.moveTo(x, textY + Math.max(2, fontSize * 0.28));
|
|
25328
|
+
context.lineTo(x + textWidthPx, textY + Math.max(2, fontSize * 0.28));
|
|
25329
|
+
context.stroke();
|
|
25330
|
+
}
|
|
25331
|
+
x += measureCanvasTextWidth(context, run.text);
|
|
25332
|
+
});
|
|
25333
|
+
y += metric.lineHeight;
|
|
25334
|
+
});
|
|
25335
|
+
context.restore();
|
|
25336
|
+
};
|
|
25337
|
+
const drawBakedShape = (context, shape, rect) => {
|
|
25338
|
+
const fillColor = shape.fill?.none ? "transparent" : shape.fill?.color ?? "transparent";
|
|
25339
|
+
const strokeColor = shape.stroke?.none ? "transparent" : shape.stroke?.color ?? "transparent";
|
|
25340
|
+
const lineWidth = Math.max(0, (shape.stroke?.widthPx ?? (shape.geometry === "line" ? 2 : 1)) * zoomFactor);
|
|
25341
|
+
const vectorShape = resolveShapeVector(shape);
|
|
25342
|
+
const opacity = Math.min(shape.fill?.opacity ?? 1, shape.stroke?.opacity ?? 1);
|
|
25343
|
+
context.save();
|
|
25344
|
+
context.translate(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
25345
|
+
if (shape.rotationDeg) {
|
|
25346
|
+
context.rotate(shape.rotationDeg * Math.PI / 180);
|
|
25347
|
+
}
|
|
25348
|
+
context.scale(shape.flipH ? -1 : 1, shape.flipV ? -1 : 1);
|
|
25349
|
+
context.globalAlpha *= opacity;
|
|
25350
|
+
context.lineWidth = lineWidth;
|
|
25351
|
+
context.strokeStyle = strokeColor;
|
|
25352
|
+
context.fillStyle = fillColor;
|
|
25353
|
+
applyCanvasShapeDash(context, shape.stroke?.dash, lineWidth);
|
|
25354
|
+
const localLeft = -rect.width / 2;
|
|
25355
|
+
const localTop = -rect.height / 2;
|
|
25356
|
+
if (vectorShape && typeof Path2D !== "undefined") {
|
|
25357
|
+
context.save();
|
|
25358
|
+
context.translate(localLeft, localTop);
|
|
25359
|
+
context.scale(
|
|
25360
|
+
rect.width / Math.max(1, vectorShape.viewBox.width),
|
|
25361
|
+
rect.height / Math.max(1, vectorShape.viewBox.height)
|
|
25362
|
+
);
|
|
25363
|
+
const path = getCachedCanvasPath2D(vectorShape.path);
|
|
25364
|
+
if (!path) {
|
|
25365
|
+
context.restore();
|
|
25366
|
+
context.restore();
|
|
25367
|
+
return;
|
|
25368
|
+
}
|
|
25369
|
+
if (fillColor !== "transparent") {
|
|
25370
|
+
context.fill(path);
|
|
25371
|
+
}
|
|
25372
|
+
if (strokeColor !== "transparent" && lineWidth > 0) {
|
|
25373
|
+
context.stroke(path);
|
|
25374
|
+
}
|
|
25375
|
+
context.restore();
|
|
25376
|
+
} else if (shape.geometry === "ellipse") {
|
|
25377
|
+
context.beginPath();
|
|
25378
|
+
context.ellipse(0, 0, rect.width / 2, rect.height / 2, 0, 0, Math.PI * 2);
|
|
25379
|
+
if (fillColor !== "transparent") {
|
|
25380
|
+
context.fill();
|
|
25381
|
+
}
|
|
25382
|
+
if (strokeColor !== "transparent" && lineWidth > 0) {
|
|
25383
|
+
context.stroke();
|
|
25384
|
+
}
|
|
25385
|
+
} else {
|
|
25386
|
+
const radius = shape.geometry === "roundRect" ? 12 * zoomFactor : 0;
|
|
25387
|
+
drawCanvasRoundedRect(context, localLeft, localTop, rect.width, rect.height, radius);
|
|
25388
|
+
if (fillColor !== "transparent") {
|
|
25389
|
+
context.fill();
|
|
25390
|
+
}
|
|
25391
|
+
if (strokeColor !== "transparent" && lineWidth > 0) {
|
|
25392
|
+
context.stroke();
|
|
25393
|
+
}
|
|
25394
|
+
}
|
|
25395
|
+
drawBakedShapeText(context, shape, localLeft, localTop, rect.width, rect.height);
|
|
25396
|
+
context.restore();
|
|
25397
|
+
};
|
|
25398
|
+
const drawBakedFormControl = (context, control, rect) => {
|
|
25399
|
+
const label = resolveFormControlLabel(control);
|
|
25400
|
+
const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
|
|
25401
|
+
const textColor = control.textColor ?? "#000000";
|
|
25402
|
+
const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
|
|
25403
|
+
const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
|
|
25404
|
+
context.save();
|
|
25405
|
+
context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
|
|
25406
|
+
context.textBaseline = "middle";
|
|
25407
|
+
context.fillStyle = textColor;
|
|
25408
|
+
context.strokeStyle = stroke;
|
|
25409
|
+
context.lineWidth = Math.max(1, zoomFactor);
|
|
25410
|
+
if (control.kind === "group-box") {
|
|
25411
|
+
const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;
|
|
25412
|
+
drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);
|
|
25413
|
+
context.stroke();
|
|
25414
|
+
if (label) {
|
|
25415
|
+
context.fillStyle = SHEET_SURFACE;
|
|
25416
|
+
const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);
|
|
25417
|
+
context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);
|
|
25418
|
+
context.fillStyle = textColor;
|
|
25419
|
+
context.textAlign = "left";
|
|
25420
|
+
context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);
|
|
25421
|
+
}
|
|
25422
|
+
context.restore();
|
|
25423
|
+
return;
|
|
25424
|
+
}
|
|
25425
|
+
if (control.kind === "button" || control.kind === "dropdown" || control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
|
|
25426
|
+
if (control.kind === "button") {
|
|
25427
|
+
const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);
|
|
25428
|
+
gradient.addColorStop(0, "#f8fafc");
|
|
25429
|
+
gradient.addColorStop(1, "#e2e8f0");
|
|
25430
|
+
context.fillStyle = gradient;
|
|
25431
|
+
} else {
|
|
25432
|
+
context.fillStyle = "transparent";
|
|
25433
|
+
}
|
|
25434
|
+
drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === "button" ? 4 * zoomFactor : 2 * zoomFactor);
|
|
25435
|
+
if (control.kind === "button") {
|
|
25436
|
+
context.fill();
|
|
25437
|
+
}
|
|
25438
|
+
context.stroke();
|
|
25439
|
+
}
|
|
25440
|
+
let textLeft = rect.left + 2 * zoomFactor;
|
|
25441
|
+
const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
|
|
25442
|
+
if (control.kind === "checkbox") {
|
|
25443
|
+
context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
|
|
25444
|
+
if (control.checked) {
|
|
25445
|
+
context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
|
|
25446
|
+
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));
|
|
25447
|
+
context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
|
|
25448
|
+
context.beginPath();
|
|
25449
|
+
context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
|
|
25450
|
+
context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
|
|
25451
|
+
context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
|
|
25452
|
+
context.stroke();
|
|
25453
|
+
}
|
|
25454
|
+
textLeft += iconSize + 4 * zoomFactor;
|
|
25455
|
+
} else if (control.kind === "radio") {
|
|
25456
|
+
context.beginPath();
|
|
25457
|
+
context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
|
|
25458
|
+
context.stroke();
|
|
25459
|
+
if (control.checked) {
|
|
25460
|
+
context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
|
|
25461
|
+
context.beginPath();
|
|
25462
|
+
context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
|
|
25463
|
+
context.fill();
|
|
25464
|
+
}
|
|
25465
|
+
textLeft += iconSize + 4 * zoomFactor;
|
|
25466
|
+
}
|
|
25467
|
+
if (label) {
|
|
25468
|
+
const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);
|
|
25469
|
+
const text = truncateCanvasText(context, label, maxTextWidth);
|
|
25470
|
+
context.fillStyle = textColor;
|
|
25471
|
+
context.textAlign = control.textAlign === "right" ? "right" : control.textAlign === "center" ? "center" : "left";
|
|
25472
|
+
const textX = context.textAlign === "right" ? rect.left + rect.width - textRightInset : context.textAlign === "center" ? textLeft + maxTextWidth / 2 : textLeft;
|
|
25473
|
+
context.fillText(text, textX, rect.top + rect.height / 2);
|
|
25474
|
+
}
|
|
25475
|
+
if (control.kind === "dropdown") {
|
|
25476
|
+
context.fillStyle = textColor;
|
|
25477
|
+
context.textAlign = "center";
|
|
25478
|
+
context.fillText("\u25BC", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);
|
|
25479
|
+
}
|
|
25480
|
+
context.restore();
|
|
25481
|
+
};
|
|
25482
|
+
const drawBakedCanvasDrawings = (pane, context, dirtyRects) => {
|
|
25483
|
+
if (bakedCanvasDrawingEntries.length === 0 || dirtyRects.length === 0) {
|
|
25484
|
+
return;
|
|
25485
|
+
}
|
|
25486
|
+
for (const entry of bakedCanvasDrawingEntries) {
|
|
25487
|
+
if (resolveDrawingPane(entry.rect) !== pane) {
|
|
25488
|
+
continue;
|
|
25489
|
+
}
|
|
25490
|
+
const localRect = resolveBakedCanvasLocalRect(entry.rect, pane);
|
|
25491
|
+
if (localRect.left + localRect.width < 0 || localRect.top + localRect.height < 0 || localRect.left > paneBounds[pane].width || localRect.top > paneBounds[pane].height || !intersectsCanvasDirtyRects(localRect.left, localRect.top, localRect.width, localRect.height, dirtyRects)) {
|
|
25492
|
+
continue;
|
|
25493
|
+
}
|
|
25494
|
+
if (entry.kind === "shape") {
|
|
25495
|
+
drawBakedShape(context, entry.shape, localRect);
|
|
25496
|
+
} else if (entry.kind === "formControl") {
|
|
25497
|
+
drawBakedFormControl(context, entry.control, localRect);
|
|
25498
|
+
} else {
|
|
25499
|
+
const imageElement = getCanvasImage(entry.image);
|
|
25500
|
+
if (imageElement) {
|
|
25501
|
+
context.drawImage(imageElement, localRect.left, localRect.top, localRect.width, localRect.height);
|
|
25502
|
+
}
|
|
25503
|
+
}
|
|
25504
|
+
}
|
|
25505
|
+
};
|
|
24566
25506
|
const cellPaneOrder = ["scroll", "top", "left", "corner"];
|
|
24567
25507
|
if (shouldRepaintBody) {
|
|
25508
|
+
canvasProfileBodyStart = canvasProfileTarget ? performance.now() : 0;
|
|
24568
25509
|
for (const pane of Object.keys(bodyContexts)) {
|
|
24569
25510
|
const context = bodyContexts[pane];
|
|
24570
25511
|
const bounds = paneBounds[pane];
|
|
@@ -24595,6 +25536,7 @@ function XlsxGrid({
|
|
|
24595
25536
|
context.clearRect(0, 0, bounds.width, bounds.height);
|
|
24596
25537
|
}
|
|
24597
25538
|
bodyDirtyRectsByPane[pane] = dirtyRects;
|
|
25539
|
+
canvasProfileDirtyRects += dirtyRects.length;
|
|
24598
25540
|
if (dirtyRects.length === 0) {
|
|
24599
25541
|
continue;
|
|
24600
25542
|
}
|
|
@@ -24611,6 +25553,22 @@ function XlsxGrid({
|
|
|
24611
25553
|
if (!paneContext || paneDirtyRects.length === 0 || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
|
|
24612
25554
|
continue;
|
|
24613
25555
|
}
|
|
25556
|
+
let hasPendingGridlinePath = false;
|
|
25557
|
+
const enqueueGridlinePath = () => {
|
|
25558
|
+
if (!hasPendingGridlinePath) {
|
|
25559
|
+
paneContext.beginPath();
|
|
25560
|
+
hasPendingGridlinePath = true;
|
|
25561
|
+
}
|
|
25562
|
+
};
|
|
25563
|
+
const flushPendingGridlines = () => {
|
|
25564
|
+
if (!hasPendingGridlinePath) {
|
|
25565
|
+
return;
|
|
25566
|
+
}
|
|
25567
|
+
paneContext.strokeStyle = palette.border;
|
|
25568
|
+
paneContext.lineWidth = 1;
|
|
25569
|
+
paneContext.stroke();
|
|
25570
|
+
hasPendingGridlinePath = false;
|
|
25571
|
+
};
|
|
24614
25572
|
const drawnMergedAnchorKeys = /* @__PURE__ */ new Set();
|
|
24615
25573
|
for (const rowItem of paneAxisItems.rows) {
|
|
24616
25574
|
for (const colItem of paneAxisItems.cols) {
|
|
@@ -24618,6 +25576,33 @@ function XlsxGrid({
|
|
|
24618
25576
|
const anchorCell = resolveMergeAnchorCell(cell);
|
|
24619
25577
|
const anchorKey = `${anchorCell.row}:${anchorCell.col}`;
|
|
24620
25578
|
let drawCell = anchorCell;
|
|
25579
|
+
const drawRowIndex = rowIndexByActual.get(drawCell.row);
|
|
25580
|
+
const drawColIndex = colIndexByActual.get(drawCell.col);
|
|
25581
|
+
if (drawRowIndex === void 0 || drawColIndex === void 0) {
|
|
25582
|
+
continue;
|
|
25583
|
+
}
|
|
25584
|
+
const baseCellLeft = displayRowHeaderWidth + (colPrefixSums[drawColIndex] ?? 0);
|
|
25585
|
+
const baseCellTop = displayHeaderHeight + (rowPrefixSums[drawRowIndex] ?? 0);
|
|
25586
|
+
const useFrozenHorizontalPosition = pane === "left" || pane === "corner";
|
|
25587
|
+
const useFrozenVerticalPosition = pane === "top" || pane === "corner";
|
|
25588
|
+
const roughLocalRect = {
|
|
25589
|
+
height: displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size,
|
|
25590
|
+
left: (useFrozenHorizontalPosition ? stickyLeftByCol.get(drawCell.col) ?? baseCellLeft - drawingViewport.left : baseCellLeft - drawingViewport.left) - paneBoundsForCell.left,
|
|
25591
|
+
top: (useFrozenVerticalPosition ? stickyTopByRow.get(drawCell.row) ?? baseCellTop - drawingViewport.top : baseCellTop - drawingViewport.top) - paneBoundsForCell.top,
|
|
25592
|
+
width: displayEffectiveColWidths[drawColIndex] ?? colItem.size
|
|
25593
|
+
};
|
|
25594
|
+
const isMergedSecondaryProbe = anchorCell.row !== cell.row || anchorCell.col !== cell.col;
|
|
25595
|
+
if (!isMergedSecondaryProbe && !intersectsCanvasDirtyRects(
|
|
25596
|
+
roughLocalRect.left - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,
|
|
25597
|
+
roughLocalRect.top - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,
|
|
25598
|
+
roughLocalRect.width + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,
|
|
25599
|
+
roughLocalRect.height + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,
|
|
25600
|
+
paneDirtyRects
|
|
25601
|
+
)) {
|
|
25602
|
+
canvasProfileCulledCells += 1;
|
|
25603
|
+
continue;
|
|
25604
|
+
}
|
|
25605
|
+
canvasProfileLookedUpCells += 1;
|
|
24621
25606
|
let cellData = getCellData(drawCell.row, drawCell.col);
|
|
24622
25607
|
if ((cellData.colSpan || cellData.rowSpan) && drawnMergedAnchorKeys.has(anchorKey)) {
|
|
24623
25608
|
continue;
|
|
@@ -24628,16 +25613,9 @@ function XlsxGrid({
|
|
|
24628
25613
|
if (cellData.colSpan || cellData.rowSpan) {
|
|
24629
25614
|
drawnMergedAnchorKeys.add(anchorKey);
|
|
24630
25615
|
}
|
|
24631
|
-
const drawRowIndex = rowIndexByActual.get(drawCell.row);
|
|
24632
|
-
const drawColIndex = colIndexByActual.get(drawCell.col);
|
|
24633
|
-
if (drawRowIndex === void 0 || drawColIndex === void 0) {
|
|
24634
|
-
continue;
|
|
24635
|
-
}
|
|
24636
25616
|
const displayRect = cellData.colSpan || cellData.rowSpan ? resolveCellDisplayRect(drawCell) : null;
|
|
24637
|
-
const baseLeft = displayRect?.left ??
|
|
24638
|
-
const baseTop = displayRect?.top ??
|
|
24639
|
-
const useFrozenHorizontalPosition = pane === "left" || pane === "corner";
|
|
24640
|
-
const useFrozenVerticalPosition = pane === "top" || pane === "corner";
|
|
25617
|
+
const baseLeft = displayRect?.left ?? baseCellLeft;
|
|
25618
|
+
const baseTop = displayRect?.top ?? baseCellTop;
|
|
24641
25619
|
const localRect = {
|
|
24642
25620
|
height: displayRect?.height ?? (displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size),
|
|
24643
25621
|
left: (useFrozenHorizontalPosition ? stickyLeftByCol.get(drawCell.col) ?? baseLeft - drawingViewport.left : baseLeft - drawingViewport.left) - paneBoundsForCell.left,
|
|
@@ -24649,13 +25627,18 @@ function XlsxGrid({
|
|
|
24649
25627
|
continue;
|
|
24650
25628
|
}
|
|
24651
25629
|
if (!intersectsCanvasDirtyRects(localRect.left, localRect.top, drawableWidth, localRect.height, paneDirtyRects)) {
|
|
25630
|
+
canvasProfileCulledCells += 1;
|
|
24652
25631
|
continue;
|
|
24653
25632
|
}
|
|
25633
|
+
canvasProfilePaintedCells += 1;
|
|
24654
25634
|
const cellStyle = cellData.style;
|
|
24655
25635
|
const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellStyle);
|
|
24656
25636
|
const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : sheetSurface);
|
|
24657
25637
|
const gradientFill = !cellData.conditionalColorScale && typeof cellStyle.backgroundImage === "string" ? resolveCanvasGradientFill(paneContext, localRect, cellStyle.backgroundImage) : null;
|
|
24658
25638
|
const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== sheetSurface;
|
|
25639
|
+
if (hasExplicitCellFill || cellData.chartHighlight) {
|
|
25640
|
+
flushPendingGridlines();
|
|
25641
|
+
}
|
|
24659
25642
|
paneContext.fillStyle = gradientFill ?? fillColor;
|
|
24660
25643
|
paneContext.fillRect(localRect.left, localRect.top, localRect.width, localRect.height);
|
|
24661
25644
|
if (cellData.chartHighlight) {
|
|
@@ -24701,10 +25684,8 @@ function XlsxGrid({
|
|
|
24701
25684
|
const bottomNeighborTopBorder = bottomNeighborData?.isMergedSecondary ? null : bottomNeighborData?.canvas?.topBorder ?? parseCanvasBorderDeclaration(bottomNeighborData?.style.borderTop);
|
|
24702
25685
|
const resolvedRightBorder = resolveCanvasBoundaryBorder(rightBorder, rightNeighborLeftBorder);
|
|
24703
25686
|
const resolvedBottomBorder = resolveCanvasBoundaryBorder(bottomBorder, bottomNeighborTopBorder);
|
|
24704
|
-
if (showGridLines && !hasExplicitCellFill) {
|
|
24705
|
-
|
|
24706
|
-
paneContext.lineWidth = 1;
|
|
24707
|
-
paneContext.beginPath();
|
|
25687
|
+
if (showGridLines && !hasExplicitCellFill && (!resolvedRightBorder || !resolvedBottomBorder)) {
|
|
25688
|
+
enqueueGridlinePath();
|
|
24708
25689
|
if (!resolvedRightBorder) {
|
|
24709
25690
|
paneContext.moveTo(localRect.left + localRect.width - 0.5, localRect.top);
|
|
24710
25691
|
paneContext.lineTo(localRect.left + localRect.width - 0.5, localRect.top + localRect.height);
|
|
@@ -24713,7 +25694,9 @@ function XlsxGrid({
|
|
|
24713
25694
|
paneContext.moveTo(localRect.left, localRect.top + localRect.height - 0.5);
|
|
24714
25695
|
paneContext.lineTo(localRect.left + localRect.width, localRect.top + localRect.height - 0.5);
|
|
24715
25696
|
}
|
|
24716
|
-
|
|
25697
|
+
}
|
|
25698
|
+
if (topBorder || resolvedRightBorder || resolvedBottomBorder || leftBorder || cellData.chartHighlight) {
|
|
25699
|
+
flushPendingGridlines();
|
|
24717
25700
|
}
|
|
24718
25701
|
if (topBorder && drawRowIndex === 0) {
|
|
24719
25702
|
strokeCanvasBorderSide(paneContext, "top", localRect, topBorder);
|
|
@@ -24746,17 +25729,26 @@ function XlsxGrid({
|
|
|
24746
25729
|
strokeCanvasBorderSide(paneContext, "left", localRect, highlightBorder);
|
|
24747
25730
|
}
|
|
24748
25731
|
}
|
|
25732
|
+
const rawText = cellData.value ?? "";
|
|
25733
|
+
const shouldDrawCanvasContent = cellData.checkboxState != null || cellData.sparkline || rawText.length > 0 || cellData.conditionalIcon || cellData.isTableHeader;
|
|
25734
|
+
if (!shouldDrawCanvasContent) {
|
|
25735
|
+
continue;
|
|
25736
|
+
}
|
|
24749
25737
|
const padding = canvasCellStyle.padding;
|
|
24750
25738
|
const contentLeft = localRect.left + padding.left;
|
|
24751
25739
|
const contentTop = localRect.top + padding.top;
|
|
24752
25740
|
const contentWidth = Math.max(0, localRect.width - padding.left - padding.right);
|
|
24753
25741
|
const contentHeight = Math.max(0, localRect.height - padding.top - padding.bottom);
|
|
25742
|
+
if (contentWidth <= 0 || contentHeight <= 0) {
|
|
25743
|
+
continue;
|
|
25744
|
+
}
|
|
24754
25745
|
const activeFontSizePx = cellData.shrinkToFitFontSizePx ?? resolveCanvasFontSizePx(cellStyle, 12 * zoomFactor);
|
|
24755
25746
|
const textClipOverscan = Math.max(
|
|
24756
25747
|
1,
|
|
24757
25748
|
zoomFactor * 1.5,
|
|
24758
25749
|
activeFontSizePx * (cellData.textRotationDeg ? 0.75 : 0.18)
|
|
24759
25750
|
);
|
|
25751
|
+
flushPendingGridlines();
|
|
24760
25752
|
paneContext.save();
|
|
24761
25753
|
paneContext.beginPath();
|
|
24762
25754
|
paneContext.rect(
|
|
@@ -24888,7 +25880,6 @@ function XlsxGrid({
|
|
|
24888
25880
|
const trailingInset = (cellData.conditionalIcon ? 18 * zoomFactor : 0) + (cellData.isTableHeader ? 16 * zoomFactor : 0);
|
|
24889
25881
|
const spillMaxWidth = cellData.spillWidth && cellData.spillWidth > 0 ? Math.max(0, cellData.spillWidth - trailingInset) : null;
|
|
24890
25882
|
const maxTextWidth = spillMaxWidth ?? Math.max(0, contentWidth - trailingInset);
|
|
24891
|
-
const rawText = cellData.value ?? "";
|
|
24892
25883
|
const textColor = canvasCellStyle.textColor;
|
|
24893
25884
|
const shouldEllipsizeText = canvasCellStyle.textOverflowEllipsis;
|
|
24894
25885
|
const shouldWrapText = canvasCellStyle.usesWrappedText || rawText.includes("\n");
|
|
@@ -24920,7 +25911,7 @@ function XlsxGrid({
|
|
|
24920
25911
|
const textY = textBlockTop + lineIndex * lineHeight + lineHeight / 2;
|
|
24921
25912
|
paneContext.fillText(line, textX, textY);
|
|
24922
25913
|
if (canvasCellStyle.textDecoration?.includes("underline") && line.length > 0) {
|
|
24923
|
-
const measured = Math.min(maxTextWidth, paneContext
|
|
25914
|
+
const measured = Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, line));
|
|
24924
25915
|
const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
|
|
24925
25916
|
paneContext.beginPath();
|
|
24926
25917
|
paneContext.moveTo(underlineStartX, textY + Math.max(2, lineHeight * 0.24));
|
|
@@ -24954,7 +25945,7 @@ function XlsxGrid({
|
|
|
24954
25945
|
const textY = contentTop + contentHeight / 2;
|
|
24955
25946
|
paneContext.fillText(text, textX, textY);
|
|
24956
25947
|
if (canvasCellStyle.textDecoration?.includes("underline") && text.length > 0) {
|
|
24957
|
-
const measured = shouldEllipsizeText ? Math.min(maxTextWidth, paneContext
|
|
25948
|
+
const measured = shouldEllipsizeText ? Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, text)) : measureCanvasTextWidth(paneContext, text);
|
|
24958
25949
|
const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
|
|
24959
25950
|
paneContext.beginPath();
|
|
24960
25951
|
paneContext.moveTo(underlineStartX, textY + 6 * zoomFactor);
|
|
@@ -24979,6 +25970,7 @@ function XlsxGrid({
|
|
|
24979
25970
|
paneContext.restore();
|
|
24980
25971
|
}
|
|
24981
25972
|
}
|
|
25973
|
+
flushPendingGridlines();
|
|
24982
25974
|
}
|
|
24983
25975
|
for (const pane of cellPaneOrder) {
|
|
24984
25976
|
const paneContext = bodyContexts[pane];
|
|
@@ -24996,7 +25988,7 @@ function XlsxGrid({
|
|
|
24996
25988
|
paneContext.textBaseline = "middle";
|
|
24997
25989
|
paneContext.fillText(spillText.text, spillText.textX, spillText.textY);
|
|
24998
25990
|
if (spillText.textDecoration?.includes("underline") && spillText.text.length > 0) {
|
|
24999
|
-
const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, paneContext
|
|
25991
|
+
const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, measureCanvasTextWidth(paneContext, spillText.text)) : measureCanvasTextWidth(paneContext, spillText.text);
|
|
25000
25992
|
const underlineStartX = spillText.align === "right" ? spillText.textX - measured : spillText.align === "center" ? spillText.textX - measured / 2 : spillText.textX;
|
|
25001
25993
|
paneContext.beginPath();
|
|
25002
25994
|
paneContext.moveTo(underlineStartX, spillText.underlineY);
|
|
@@ -25008,6 +26000,14 @@ function XlsxGrid({
|
|
|
25008
26000
|
paneContext.restore();
|
|
25009
26001
|
}
|
|
25010
26002
|
}
|
|
26003
|
+
for (const pane of cellPaneOrder) {
|
|
26004
|
+
const paneContext = bodyContexts[pane];
|
|
26005
|
+
if (!paneContext) {
|
|
26006
|
+
continue;
|
|
26007
|
+
}
|
|
26008
|
+
drawBakedCanvasDrawings(pane, paneContext, bodyDirtyRectsByPane[pane]);
|
|
26009
|
+
}
|
|
26010
|
+
canvasProfileBodyMs = canvasProfileTarget ? performance.now() - canvasProfileBodyStart : 0;
|
|
25011
26011
|
}
|
|
25012
26012
|
if (shouldRepaintHeaders && cornerContext) {
|
|
25013
26013
|
const topFrozenHeaderContext = topHeaderContexts.frozen;
|
|
@@ -25022,7 +26022,7 @@ function XlsxGrid({
|
|
|
25022
26022
|
topScrollHeaderContext,
|
|
25023
26023
|
bufferCanvas,
|
|
25024
26024
|
dpr,
|
|
25025
|
-
|
|
26025
|
+
topScrollHeaderCanvasWidth,
|
|
25026
26026
|
headerHeight,
|
|
25027
26027
|
drawingViewport.left - previousPaintedViewport.left,
|
|
25028
26028
|
0
|
|
@@ -25041,7 +26041,7 @@ function XlsxGrid({
|
|
|
25041
26041
|
bufferCanvas,
|
|
25042
26042
|
dpr,
|
|
25043
26043
|
rowHeaderWidth,
|
|
25044
|
-
|
|
26044
|
+
leftScrollHeaderCanvasHeight,
|
|
25045
26045
|
0,
|
|
25046
26046
|
drawingViewport.top - previousPaintedViewport.top
|
|
25047
26047
|
);
|
|
@@ -25050,11 +26050,11 @@ function XlsxGrid({
|
|
|
25050
26050
|
}
|
|
25051
26051
|
}
|
|
25052
26052
|
}
|
|
25053
|
-
if (topFrozenHeaderContext &&
|
|
26053
|
+
if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth > 0) {
|
|
25054
26054
|
topFrozenHeaderContext.fillStyle = palette.headerSurface;
|
|
25055
|
-
topFrozenHeaderContext.fillRect(0, 0,
|
|
26055
|
+
topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth, headerHeight);
|
|
25056
26056
|
}
|
|
25057
|
-
if (topScrollHeaderContext &&
|
|
26057
|
+
if (topScrollHeaderContext && topScrollHeaderCanvasWidth > 0) {
|
|
25058
26058
|
topScrollHeaderContext.fillStyle = palette.headerSurface;
|
|
25059
26059
|
for (const dirtyRect of topScrollHeaderDirtyRects) {
|
|
25060
26060
|
topScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
|
|
@@ -25073,7 +26073,7 @@ function XlsxGrid({
|
|
|
25073
26073
|
if (!paneContext) {
|
|
25074
26074
|
continue;
|
|
25075
26075
|
}
|
|
25076
|
-
if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ?
|
|
26076
|
+
if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth : topScrollHeaderCanvasWidth)) {
|
|
25077
26077
|
continue;
|
|
25078
26078
|
}
|
|
25079
26079
|
if (!column.isFrozen && !intersectsCanvasDirtyRects(column.localLeft, 0, column.width, column.height, topScrollHeaderDirtyRects)) {
|
|
@@ -25099,11 +26099,11 @@ function XlsxGrid({
|
|
|
25099
26099
|
column.height / 2
|
|
25100
26100
|
);
|
|
25101
26101
|
}
|
|
25102
|
-
if (leftFrozenHeaderContext &&
|
|
26102
|
+
if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight > 0) {
|
|
25103
26103
|
leftFrozenHeaderContext.fillStyle = palette.rowHeaderSurface;
|
|
25104
|
-
leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth,
|
|
26104
|
+
leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight);
|
|
25105
26105
|
}
|
|
25106
|
-
if (leftScrollHeaderContext &&
|
|
26106
|
+
if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight > 0) {
|
|
25107
26107
|
leftScrollHeaderContext.fillStyle = palette.rowHeaderSurface;
|
|
25108
26108
|
for (const dirtyRect of leftScrollHeaderDirtyRects) {
|
|
25109
26109
|
leftScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
|
|
@@ -25122,7 +26122,7 @@ function XlsxGrid({
|
|
|
25122
26122
|
if (!paneContext) {
|
|
25123
26123
|
continue;
|
|
25124
26124
|
}
|
|
25125
|
-
if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ?
|
|
26125
|
+
if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight : leftScrollHeaderCanvasHeight)) {
|
|
25126
26126
|
continue;
|
|
25127
26127
|
}
|
|
25128
26128
|
if (!row.isFrozen && !intersectsCanvasDirtyRects(0, row.localTop, rowHeaderWidth, row.height, leftScrollHeaderDirtyRects)) {
|
|
@@ -25162,9 +26162,29 @@ function XlsxGrid({
|
|
|
25162
26162
|
paintedHeaderCanvasSignatureRef.current = nextHeaderCanvasSignature;
|
|
25163
26163
|
}
|
|
25164
26164
|
applyCanvasViewportCompensation();
|
|
26165
|
+
if (canvasProfileTarget) {
|
|
26166
|
+
canvasProfileTarget.push({
|
|
26167
|
+
bodyMs: canvasProfileBodyMs,
|
|
26168
|
+
culledCells: canvasProfileCulledCells,
|
|
26169
|
+
dirtyRects: canvasProfileDirtyRects,
|
|
26170
|
+
lookedUpCells: canvasProfileLookedUpCells,
|
|
26171
|
+
paintedCells: canvasProfilePaintedCells,
|
|
26172
|
+
repaintBody: shouldRepaintBody,
|
|
26173
|
+
repaintHeaders: shouldRepaintHeaders,
|
|
26174
|
+
renderToLayoutMs: canvasProfileStart - xlsxGridRenderStart,
|
|
26175
|
+
totalMs: performance.now() - canvasProfileStart
|
|
26176
|
+
});
|
|
26177
|
+
if (canvasProfileTarget.length > 500) {
|
|
26178
|
+
canvasProfileTarget.splice(0, canvasProfileTarget.length - 500);
|
|
26179
|
+
}
|
|
26180
|
+
}
|
|
25165
26181
|
}, [
|
|
25166
26182
|
activeSheet,
|
|
25167
26183
|
applyCanvasViewportCompensation,
|
|
26184
|
+
bakedCanvasDrawingSignature,
|
|
26185
|
+
bakedFormControlRects,
|
|
26186
|
+
bakedImageRects,
|
|
26187
|
+
bakedShapeRects,
|
|
25168
26188
|
canvasColumnHeaderCells,
|
|
25169
26189
|
canvasPaneAxisItems,
|
|
25170
26190
|
canvasRowHeaderCells,
|
|
@@ -25185,15 +26205,16 @@ function XlsxGrid({
|
|
|
25185
26205
|
frozenPaneBottom,
|
|
25186
26206
|
frozenPaneRight,
|
|
25187
26207
|
getCellData,
|
|
26208
|
+
getCanvasImage,
|
|
25188
26209
|
getBodyBlitBufferCanvas,
|
|
25189
26210
|
getHeaderBlitBufferCanvas,
|
|
25190
26211
|
palette,
|
|
25191
26212
|
resolveCellDisplayRect,
|
|
25192
26213
|
resolveMergeAnchorCell,
|
|
25193
|
-
resizeGuide,
|
|
25194
26214
|
rowIndexByActual,
|
|
25195
26215
|
rowPrefixSums,
|
|
25196
26216
|
selectionHeaderSurface,
|
|
26217
|
+
shouldBakeCanvasStaticDrawings,
|
|
25197
26218
|
stickyLeftByCol,
|
|
25198
26219
|
stickyTopByRow,
|
|
25199
26220
|
visibleCols,
|
|
@@ -25411,7 +26432,7 @@ function XlsxGrid({
|
|
|
25411
26432
|
start: virtualRow?.start ?? (rowPrefixSums[index] ?? 0)
|
|
25412
26433
|
};
|
|
25413
26434
|
});
|
|
25414
|
-
const totalHeight = shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
|
|
26435
|
+
const totalHeight = shouldUseDomVirtualizer && shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
|
|
25415
26436
|
const totalWidth = totalContentWidth + displayRowHeaderWidth;
|
|
25416
26437
|
const sheetContentHeight = displayHeaderHeight + totalHeight;
|
|
25417
26438
|
const isLiveZooming = liveGestureZoom !== null && zoomScale === liveGestureZoom.baseZoomScale;
|
|
@@ -25485,18 +26506,6 @@ function XlsxGrid({
|
|
|
25485
26506
|
width: 0,
|
|
25486
26507
|
zIndex: canvasHeaderOverlayZIndex
|
|
25487
26508
|
};
|
|
25488
|
-
const scrollBodyCanvasWidth = Math.max(0, drawingViewport.width - frozenPaneRight);
|
|
25489
|
-
const scrollBodyCanvasHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);
|
|
25490
|
-
const topBodyCanvasWidth = scrollBodyCanvasWidth;
|
|
25491
|
-
const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);
|
|
25492
|
-
const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
|
|
25493
|
-
const leftBodyCanvasHeight = scrollBodyCanvasHeight;
|
|
25494
|
-
const cornerBodyCanvasWidth = leftBodyCanvasWidth;
|
|
25495
|
-
const cornerBodyCanvasHeight = topBodyCanvasHeight;
|
|
25496
|
-
const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
|
|
25497
|
-
const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
|
|
25498
|
-
const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
|
|
25499
|
-
const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
|
|
25500
26509
|
const canvasBodyBaseStyle = {
|
|
25501
26510
|
cursor: "cell",
|
|
25502
26511
|
pointerEvents: "auto",
|
|
@@ -25508,13 +26517,13 @@ function XlsxGrid({
|
|
|
25508
26517
|
const canvasScrollBodyStyle = {
|
|
25509
26518
|
...canvasBodyBaseStyle,
|
|
25510
26519
|
display: scrollBodyCanvasWidth > 0 && scrollBodyCanvasHeight > 0 ? "block" : "none",
|
|
25511
|
-
left:
|
|
25512
|
-
top:
|
|
26520
|
+
left: scrollBodyCanvasLeft,
|
|
26521
|
+
top: scrollBodyCanvasTop
|
|
25513
26522
|
};
|
|
25514
26523
|
const canvasTopBodyStyle = {
|
|
25515
26524
|
...canvasBodyBaseStyle,
|
|
25516
26525
|
display: topBodyCanvasWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
|
|
25517
|
-
left:
|
|
26526
|
+
left: scrollBodyCanvasLeft,
|
|
25518
26527
|
top: displayHeaderHeight,
|
|
25519
26528
|
zIndex: 30
|
|
25520
26529
|
};
|
|
@@ -25522,7 +26531,7 @@ function XlsxGrid({
|
|
|
25522
26531
|
...canvasBodyBaseStyle,
|
|
25523
26532
|
display: leftBodyCanvasWidth > 0 && leftBodyCanvasHeight > 0 ? "block" : "none",
|
|
25524
26533
|
left: displayRowHeaderWidth,
|
|
25525
|
-
top:
|
|
26534
|
+
top: scrollBodyCanvasTop,
|
|
25526
26535
|
zIndex: 30
|
|
25527
26536
|
};
|
|
25528
26537
|
const canvasCornerBodyStyle = {
|
|
@@ -25548,7 +26557,7 @@ function XlsxGrid({
|
|
|
25548
26557
|
const canvasTopScrollHeaderStyle = {
|
|
25549
26558
|
...canvasHeaderBaseStyle,
|
|
25550
26559
|
display: topScrollHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
|
|
25551
|
-
left:
|
|
26560
|
+
left: scrollBodyCanvasLeft,
|
|
25552
26561
|
top: 0,
|
|
25553
26562
|
zIndex: canvasHeaderOverlayZIndex
|
|
25554
26563
|
};
|
|
@@ -25563,7 +26572,7 @@ function XlsxGrid({
|
|
|
25563
26572
|
...canvasHeaderBaseStyle,
|
|
25564
26573
|
display: leftScrollHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
|
|
25565
26574
|
left: 0,
|
|
25566
|
-
top:
|
|
26575
|
+
top: scrollBodyCanvasTop,
|
|
25567
26576
|
zIndex: canvasHeaderOverlayZIndex
|
|
25568
26577
|
};
|
|
25569
26578
|
const canvasCornerHeaderStyle = {
|
|
@@ -26119,26 +27128,26 @@ function XlsxGrid({
|
|
|
26119
27128
|
};
|
|
26120
27129
|
const canvasScrollOverlayPaneStyle = {
|
|
26121
27130
|
...canvasOverlayPaneBaseStyle,
|
|
26122
|
-
display:
|
|
26123
|
-
height:
|
|
27131
|
+
display: scrollBodyViewportWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
|
|
27132
|
+
height: scrollBodyViewportHeight,
|
|
26124
27133
|
left: frozenPaneRight,
|
|
26125
27134
|
top: frozenPaneBottom,
|
|
26126
|
-
width:
|
|
27135
|
+
width: scrollBodyViewportWidth,
|
|
26127
27136
|
zIndex: 20
|
|
26128
27137
|
};
|
|
26129
27138
|
const canvasTopOverlayPaneStyle = {
|
|
26130
27139
|
...canvasOverlayPaneBaseStyle,
|
|
26131
|
-
display:
|
|
27140
|
+
display: scrollBodyViewportWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
|
|
26132
27141
|
height: topBodyCanvasHeight,
|
|
26133
27142
|
left: frozenPaneRight,
|
|
26134
27143
|
top: displayHeaderHeight,
|
|
26135
|
-
width:
|
|
27144
|
+
width: scrollBodyViewportWidth,
|
|
26136
27145
|
zIndex: 35
|
|
26137
27146
|
};
|
|
26138
27147
|
const canvasLeftOverlayPaneStyle = {
|
|
26139
27148
|
...canvasOverlayPaneBaseStyle,
|
|
26140
|
-
display: leftBodyCanvasWidth > 0 &&
|
|
26141
|
-
height:
|
|
27149
|
+
display: leftBodyCanvasWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
|
|
27150
|
+
height: scrollBodyViewportHeight,
|
|
26142
27151
|
left: displayRowHeaderWidth,
|
|
26143
27152
|
top: frozenPaneBottom,
|
|
26144
27153
|
width: leftBodyCanvasWidth,
|
|
@@ -26153,8 +27162,9 @@ function XlsxGrid({
|
|
|
26153
27162
|
width: cornerBodyCanvasWidth,
|
|
26154
27163
|
zIndex: 36
|
|
26155
27164
|
};
|
|
27165
|
+
const drawingViewportCacheSignature = `${Math.floor(drawingViewport.left / CANVAS_VIEWPORT_OVERSCAN_PX)}:${Math.floor(drawingViewport.top / CANVAS_VIEWPORT_OVERSCAN_PX)}:${drawingViewport.width}:${drawingViewport.height}`;
|
|
26156
27166
|
const previousPaneDrawingNodes = paneDrawingNodesCacheRef.current;
|
|
26157
|
-
const canReusePaneDrawingNodes = previousPaneDrawingNodes !== null && previousPaneDrawingNodes.showImages === showImages && previousPaneDrawingNodes.chartRects === chartRects && previousPaneDrawingNodes.formControlRects ===
|
|
27167
|
+
const canReusePaneDrawingNodes = previousPaneDrawingNodes !== null && previousPaneDrawingNodes.showImages === showImages && previousPaneDrawingNodes.chartRects === chartRects && previousPaneDrawingNodes.formControlRects === domFormControlRects && previousPaneDrawingNodes.shapeRects === domShapeRects && previousPaneDrawingNodes.imageRects === domImageRects && previousPaneDrawingNodes.selectedChartId === selectedChartId && previousPaneDrawingNodes.selectedImageId === selectedImageId && previousPaneDrawingNodes.readOnly === readOnly && previousPaneDrawingNodes.selectionStroke === selectionStroke && previousPaneDrawingNodes.renderChartLoading === renderChartLoading && previousPaneDrawingNodes.renderImage === renderImage && previousPaneDrawingNodes.renderImageSelection === renderImageSelection && previousPaneDrawingNodes.isChartsLoading === isChartsLoading && previousPaneDrawingNodes.palette === palette && previousPaneDrawingNodes.drawingViewportSignature === drawingViewportCacheSignature;
|
|
26158
27168
|
const paneDrawingNodes = canReusePaneDrawingNodes ? previousPaneDrawingNodes.value : !showImages ? {
|
|
26159
27169
|
corner: null,
|
|
26160
27170
|
left: null,
|
|
@@ -26163,35 +27173,35 @@ function XlsxGrid({
|
|
|
26163
27173
|
} : {
|
|
26164
27174
|
corner: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
26165
27175
|
chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "corner")),
|
|
26166
|
-
|
|
26167
|
-
|
|
26168
|
-
|
|
27176
|
+
domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "corner")),
|
|
27177
|
+
domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "corner")),
|
|
27178
|
+
domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "corner"))
|
|
26169
27179
|
] }),
|
|
26170
27180
|
left: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
26171
27181
|
chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "left")),
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
|
|
27182
|
+
domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "left")),
|
|
27183
|
+
domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "left")),
|
|
27184
|
+
domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "left"))
|
|
26175
27185
|
] }),
|
|
26176
27186
|
scroll: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
26177
27187
|
chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "scroll")),
|
|
26178
|
-
|
|
26179
|
-
|
|
26180
|
-
|
|
27188
|
+
domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "scroll")),
|
|
27189
|
+
domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "scroll")),
|
|
27190
|
+
domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "scroll"))
|
|
26181
27191
|
] }),
|
|
26182
27192
|
top: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
26183
27193
|
chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "top")),
|
|
26184
|
-
|
|
26185
|
-
|
|
26186
|
-
|
|
27194
|
+
domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "top")),
|
|
27195
|
+
domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "top")),
|
|
27196
|
+
domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "top"))
|
|
26187
27197
|
] })
|
|
26188
27198
|
};
|
|
26189
27199
|
if (!canReusePaneDrawingNodes) {
|
|
26190
27200
|
paneDrawingNodesCacheRef.current = {
|
|
26191
27201
|
chartRects,
|
|
26192
|
-
|
|
26193
|
-
formControlRects,
|
|
26194
|
-
imageRects,
|
|
27202
|
+
drawingViewportSignature: drawingViewportCacheSignature,
|
|
27203
|
+
formControlRects: domFormControlRects,
|
|
27204
|
+
imageRects: domImageRects,
|
|
26195
27205
|
isChartsLoading,
|
|
26196
27206
|
palette,
|
|
26197
27207
|
readOnly,
|
|
@@ -26201,7 +27211,7 @@ function XlsxGrid({
|
|
|
26201
27211
|
selectedChartId,
|
|
26202
27212
|
selectedImageId,
|
|
26203
27213
|
selectionStroke,
|
|
26204
|
-
shapeRects,
|
|
27214
|
+
shapeRects: domShapeRects,
|
|
26205
27215
|
showImages,
|
|
26206
27216
|
value: paneDrawingNodes
|
|
26207
27217
|
};
|
|
@@ -26709,10 +27719,11 @@ function XlsxGrid({
|
|
|
26709
27719
|
style: canvasCornerBodyStyle
|
|
26710
27720
|
}
|
|
26711
27721
|
),
|
|
26712
|
-
|
|
27722
|
+
hasCanvasDomDrawingOverlays ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
26713
27723
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasScrollOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
26714
27724
|
"div",
|
|
26715
27725
|
{
|
|
27726
|
+
ref: canvasScrollOverlayContentRef,
|
|
26716
27727
|
style: {
|
|
26717
27728
|
height: sheetContentHeight,
|
|
26718
27729
|
left: 0,
|
|
@@ -26720,6 +27731,7 @@ function XlsxGrid({
|
|
|
26720
27731
|
position: "absolute",
|
|
26721
27732
|
top: 0,
|
|
26722
27733
|
transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
|
|
27734
|
+
transformOrigin: "0 0",
|
|
26723
27735
|
width: totalWidth
|
|
26724
27736
|
},
|
|
26725
27737
|
children: paneDrawingNodes.scroll
|
|
@@ -26728,6 +27740,7 @@ function XlsxGrid({
|
|
|
26728
27740
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasTopOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
26729
27741
|
"div",
|
|
26730
27742
|
{
|
|
27743
|
+
ref: canvasTopOverlayContentRef,
|
|
26731
27744
|
style: {
|
|
26732
27745
|
height: sheetContentHeight,
|
|
26733
27746
|
left: 0,
|
|
@@ -26735,6 +27748,7 @@ function XlsxGrid({
|
|
|
26735
27748
|
position: "absolute",
|
|
26736
27749
|
top: 0,
|
|
26737
27750
|
transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-displayHeaderHeight}px)`,
|
|
27751
|
+
transformOrigin: "0 0",
|
|
26738
27752
|
width: totalWidth
|
|
26739
27753
|
},
|
|
26740
27754
|
children: paneDrawingNodes.top
|
|
@@ -26743,6 +27757,7 @@ function XlsxGrid({
|
|
|
26743
27757
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasLeftOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
26744
27758
|
"div",
|
|
26745
27759
|
{
|
|
27760
|
+
ref: canvasLeftOverlayContentRef,
|
|
26746
27761
|
style: {
|
|
26747
27762
|
height: sheetContentHeight,
|
|
26748
27763
|
left: 0,
|
|
@@ -26750,6 +27765,7 @@ function XlsxGrid({
|
|
|
26750
27765
|
position: "absolute",
|
|
26751
27766
|
top: 0,
|
|
26752
27767
|
transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
|
|
27768
|
+
transformOrigin: "0 0",
|
|
26753
27769
|
width: totalWidth
|
|
26754
27770
|
},
|
|
26755
27771
|
children: paneDrawingNodes.left
|
|
@@ -26758,6 +27774,7 @@ function XlsxGrid({
|
|
|
26758
27774
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasCornerOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
26759
27775
|
"div",
|
|
26760
27776
|
{
|
|
27777
|
+
ref: canvasCornerOverlayContentRef,
|
|
26761
27778
|
style: {
|
|
26762
27779
|
height: sheetContentHeight,
|
|
26763
27780
|
left: 0,
|
|
@@ -26765,6 +27782,7 @@ function XlsxGrid({
|
|
|
26765
27782
|
position: "absolute",
|
|
26766
27783
|
top: 0,
|
|
26767
27784
|
transform: `translate(${-displayRowHeaderWidth}px, ${-displayHeaderHeight}px)`,
|
|
27785
|
+
transformOrigin: "0 0",
|
|
26768
27786
|
width: totalWidth
|
|
26769
27787
|
},
|
|
26770
27788
|
children: paneDrawingNodes.corner
|
|
@@ -27581,27 +28599,95 @@ function useXlsxViewerCharts() {
|
|
|
27581
28599
|
);
|
|
27582
28600
|
}
|
|
27583
28601
|
function useXlsxViewerThumbnails(options = {}) {
|
|
27584
|
-
const { workbook, sheets } = useXlsxViewer();
|
|
28602
|
+
const { getSheetFormControls, getSheetImages, getSheetShapes, workbook, sheets } = useXlsxViewer();
|
|
27585
28603
|
const { isDark } = React4.useContext(ViewerAppearanceContext);
|
|
27586
28604
|
const palette = useViewerPalette(isDark);
|
|
27587
28605
|
const includeHeaders = options.includeHeaders ?? true;
|
|
27588
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
|
+
}, []);
|
|
27589
28648
|
const thumbnails = React4.useMemo(() => {
|
|
27590
28649
|
return sheets.map((sheet, sheetIndex) => {
|
|
27591
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);
|
|
27592
28678
|
const showGridLines = sheet.showGridLines ?? true;
|
|
27593
28679
|
const hiddenRowSet = new Set(sheet.hiddenRows ?? []);
|
|
27594
28680
|
const hiddenColSet = new Set(sheet.hiddenCols ?? []);
|
|
27595
28681
|
const visibleRows = buildVisibleAxisIndices(
|
|
27596
28682
|
sheet.visibleRows ?? [],
|
|
27597
|
-
Math.max(THUMBNAIL_FALLBACK_ROWS,
|
|
27598
|
-
|
|
28683
|
+
Math.max(THUMBNAIL_FALLBACK_ROWS, effectiveMaxUsedRow + THUMBNAIL_FALLBACK_ROWS + 1),
|
|
28684
|
+
effectiveMaxUsedRow,
|
|
27599
28685
|
hiddenRowSet
|
|
27600
28686
|
);
|
|
27601
28687
|
const visibleCols = buildVisibleAxisIndices(
|
|
27602
28688
|
sheet.visibleCols ?? [],
|
|
27603
|
-
Math.max(THUMBNAIL_FALLBACK_COLS,
|
|
27604
|
-
|
|
28689
|
+
Math.max(THUMBNAIL_FALLBACK_COLS, effectiveMaxUsedCol + THUMBNAIL_FALLBACK_COLS + 1),
|
|
28690
|
+
effectiveMaxUsedCol,
|
|
27605
28691
|
hiddenColSet
|
|
27606
28692
|
);
|
|
27607
28693
|
const resolveColumnWidthPx = (actualCol) => {
|
|
@@ -27645,8 +28731,8 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27645
28731
|
getSizePx: resolveRowHeightPx,
|
|
27646
28732
|
maxCount: THUMBNAIL_MAX_ROWS,
|
|
27647
28733
|
maxLogicalPixels: THUMBNAIL_MAX_SOURCE_HEIGHT_PX,
|
|
27648
|
-
maxUsedIndex:
|
|
27649
|
-
minUsedIndex:
|
|
28734
|
+
maxUsedIndex: effectiveMaxUsedRow,
|
|
28735
|
+
minUsedIndex: effectiveMinUsedRow,
|
|
27650
28736
|
precomputed: visibleRows
|
|
27651
28737
|
});
|
|
27652
28738
|
const previewCols = resolveThumbnailAxisIndices({
|
|
@@ -27654,8 +28740,8 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27654
28740
|
getSizePx: resolveColumnWidthPx,
|
|
27655
28741
|
maxCount: THUMBNAIL_MAX_COLS,
|
|
27656
28742
|
maxLogicalPixels: THUMBNAIL_MAX_SOURCE_WIDTH_PX,
|
|
27657
|
-
maxUsedIndex:
|
|
27658
|
-
minUsedIndex:
|
|
28743
|
+
maxUsedIndex: effectiveMaxUsedCol,
|
|
28744
|
+
minUsedIndex: effectiveMinUsedCol,
|
|
27659
28745
|
precomputed: visibleCols
|
|
27660
28746
|
});
|
|
27661
28747
|
const rowAxis = buildThumbnailAxisItems(previewRows, resolveRowHeightPx);
|
|
@@ -27680,11 +28766,79 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27680
28766
|
const sparklineByCell = new Map(
|
|
27681
28767
|
(sheet.sparklines ?? []).map((sparkline) => [`${sparkline.target.row}:${sparkline.target.col}`, sparkline])
|
|
27682
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);
|
|
27683
28837
|
const paint = (canvas) => {
|
|
27684
28838
|
if (!canvas) {
|
|
27685
28839
|
return false;
|
|
27686
28840
|
}
|
|
27687
|
-
const context = canvas.getContext("2d");
|
|
28841
|
+
const context = canvas.getContext("2d", { alpha: false });
|
|
27688
28842
|
if (!context) {
|
|
27689
28843
|
return false;
|
|
27690
28844
|
}
|
|
@@ -27708,9 +28862,10 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27708
28862
|
}
|
|
27709
28863
|
context.setTransform(dpr * scale, 0, 0, dpr * scale, 0, 0);
|
|
27710
28864
|
context.clearRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
|
|
28865
|
+
const thumbnailSheetSurface = resolveSheetSurface(sheet, palette);
|
|
27711
28866
|
context.fillStyle = palette.canvas;
|
|
27712
28867
|
context.fillRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
|
|
27713
|
-
context.fillStyle =
|
|
28868
|
+
context.fillStyle = thumbnailSheetSurface;
|
|
27714
28869
|
context.fillRect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
|
|
27715
28870
|
if (includeHeaders) {
|
|
27716
28871
|
context.fillStyle = palette.headerSurface;
|
|
@@ -27831,8 +28986,8 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27831
28986
|
};
|
|
27832
28987
|
const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellData.style);
|
|
27833
28988
|
const gradientFill = typeof cellData.style.backgroundImage === "string" ? resolveCanvasGradientFill(context, rect, cellData.style.backgroundImage) : null;
|
|
27834
|
-
const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor :
|
|
27835
|
-
const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !==
|
|
28989
|
+
const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor : thumbnailSheetSurface);
|
|
28990
|
+
const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !== thumbnailSheetSurface;
|
|
27836
28991
|
context.fillStyle = gradientFill ?? fillColor;
|
|
27837
28992
|
context.fillRect(rect.left, rect.top, rect.width, rect.height);
|
|
27838
28993
|
if (cellData.conditionalDataBar) {
|
|
@@ -27887,12 +29042,19 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27887
29042
|
if (canvasCellStyle.leftBorder) {
|
|
27888
29043
|
strokeCanvasBorderSide(context, "left", rect, canvasCellStyle.leftBorder);
|
|
27889
29044
|
}
|
|
29045
|
+
const rawText = cellData.value ?? "";
|
|
29046
|
+
const shouldDrawThumbnailContent = cellData.checkboxState != null || cellData.sparkline || rawText.length > 0 || cellData.conditionalIcon;
|
|
29047
|
+
if (!shouldDrawThumbnailContent) {
|
|
29048
|
+
continue;
|
|
29049
|
+
}
|
|
27890
29050
|
const padding = canvasCellStyle.padding;
|
|
27891
29051
|
const contentLeft = rect.left + padding.left;
|
|
27892
29052
|
const contentTop = rect.top + padding.top;
|
|
27893
29053
|
const contentWidth = Math.max(0, rect.width - padding.left - padding.right);
|
|
27894
29054
|
const contentHeight = Math.max(0, rect.height - padding.top - padding.bottom);
|
|
27895
|
-
|
|
29055
|
+
if (contentWidth <= 0 || contentHeight <= 0) {
|
|
29056
|
+
continue;
|
|
29057
|
+
}
|
|
27896
29058
|
context.save();
|
|
27897
29059
|
context.beginPath();
|
|
27898
29060
|
context.rect(contentLeft, contentTop, contentWidth, contentHeight);
|
|
@@ -27984,6 +29146,25 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27984
29146
|
context.restore();
|
|
27985
29147
|
}
|
|
27986
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
|
+
}
|
|
27987
29168
|
if (includeHeaders) {
|
|
27988
29169
|
context.strokeStyle = palette.border;
|
|
27989
29170
|
context.lineWidth = 1;
|
|
@@ -28037,7 +29218,18 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
28037
29218
|
workbookSheetIndex: sheet.workbookSheetIndex
|
|
28038
29219
|
};
|
|
28039
29220
|
});
|
|
28040
|
-
}, [
|
|
29221
|
+
}, [
|
|
29222
|
+
getSheetFormControls,
|
|
29223
|
+
getSheetImages,
|
|
29224
|
+
getSheetShapes,
|
|
29225
|
+
getThumbnailImage,
|
|
29226
|
+
includeHeaders,
|
|
29227
|
+
palette,
|
|
29228
|
+
resolution,
|
|
29229
|
+
sheets,
|
|
29230
|
+
thumbnailImageLoadVersion,
|
|
29231
|
+
workbook
|
|
29232
|
+
]);
|
|
28041
29233
|
const paintThumbnail = React4.useCallback(
|
|
28042
29234
|
(sheetIndex, canvas) => thumbnails[sheetIndex]?.paint(canvas) ?? false,
|
|
28043
29235
|
[thumbnails]
|