@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.js
CHANGED
|
@@ -4527,6 +4527,10 @@ function parseSheetState(archive, path, options) {
|
|
|
4527
4527
|
let hasVerticalMerges = false;
|
|
4528
4528
|
let maxHorizontalMergeEndCol = -1;
|
|
4529
4529
|
let maxVerticalMergeEndRow = -1;
|
|
4530
|
+
let minContentCol = Number.POSITIVE_INFINITY;
|
|
4531
|
+
let minContentRow = Number.POSITIVE_INFINITY;
|
|
4532
|
+
let maxContentCol = -1;
|
|
4533
|
+
let maxContentRow = -1;
|
|
4530
4534
|
const columnWidthCharacterWidthPx = measureColumnCharacterWidthPx(
|
|
4531
4535
|
options?.defaultFont?.family,
|
|
4532
4536
|
options?.defaultFont?.sizePt
|
|
@@ -4539,6 +4543,26 @@ function parseSheetState(archive, path, options) {
|
|
|
4539
4543
|
sheetViewNode?.getAttribute("zoomScale") ?? sheetViewNode?.getAttribute("zoomScaleNormal") ?? Number.NaN
|
|
4540
4544
|
);
|
|
4541
4545
|
const zoomScale = Number.isFinite(rawZoomScale) && rawZoomScale > 0 ? rawZoomScale : 100;
|
|
4546
|
+
const trackContentCell = (cellRef) => {
|
|
4547
|
+
if (!cellRef) {
|
|
4548
|
+
return;
|
|
4549
|
+
}
|
|
4550
|
+
const cell = parseA1CellReference(cellRef);
|
|
4551
|
+
if (!cell) {
|
|
4552
|
+
return;
|
|
4553
|
+
}
|
|
4554
|
+
minContentCol = Math.min(minContentCol, cell.col);
|
|
4555
|
+
minContentRow = Math.min(minContentRow, cell.row);
|
|
4556
|
+
maxContentCol = Math.max(maxContentCol, cell.col);
|
|
4557
|
+
maxContentRow = Math.max(maxContentRow, cell.row);
|
|
4558
|
+
};
|
|
4559
|
+
const isMeaningfulCellNode = (cellNode) => {
|
|
4560
|
+
if (getFirstChild(cellNode, "f") || getFirstChild(cellNode, "is")) {
|
|
4561
|
+
return true;
|
|
4562
|
+
}
|
|
4563
|
+
const valueNode = getFirstChild(cellNode, "v");
|
|
4564
|
+
return Boolean(valueNode && (valueNode.textContent ?? "").length > 0);
|
|
4565
|
+
};
|
|
4542
4566
|
getLocalElements(document2, "row").forEach((rowNode) => {
|
|
4543
4567
|
const rowIndex = Number(rowNode.getAttribute("r") ?? 0) - 1;
|
|
4544
4568
|
const height = Number(rowNode.getAttribute("ht") ?? Number.NaN);
|
|
@@ -4553,17 +4577,36 @@ function parseSheetState(archive, path, options) {
|
|
|
4553
4577
|
if (rowIndex >= 0 && isHidden) {
|
|
4554
4578
|
hiddenRows.add(rowIndex);
|
|
4555
4579
|
}
|
|
4556
|
-
|
|
4557
|
-
|
|
4580
|
+
getChildElements(rowNode, "c").forEach((cellNode) => {
|
|
4581
|
+
const cellRef = cellNode.getAttribute("r");
|
|
4582
|
+
if (isMeaningfulCellNode(cellNode)) {
|
|
4583
|
+
trackContentCell(cellRef);
|
|
4584
|
+
}
|
|
4585
|
+
if (includeCachedFormulaValues) {
|
|
4558
4586
|
const formulaNode = getFirstChild(cellNode, "f");
|
|
4559
4587
|
const valueNode = getFirstChild(cellNode, "v");
|
|
4560
|
-
const cellRef = cellNode.getAttribute("r");
|
|
4561
4588
|
if (formulaNode && valueNode && cellRef) {
|
|
4562
4589
|
cachedFormulaValues[cellRef] = valueNode.textContent ?? "";
|
|
4563
4590
|
}
|
|
4564
|
-
}
|
|
4591
|
+
}
|
|
4592
|
+
});
|
|
4593
|
+
});
|
|
4594
|
+
getLocalElements(document2, "mergeCell").forEach((mergeNode) => {
|
|
4595
|
+
const reference = mergeNode.getAttribute("ref");
|
|
4596
|
+
const range = reference ? parseA1RangeReference(reference) : null;
|
|
4597
|
+
if (!range) {
|
|
4598
|
+
return;
|
|
4599
|
+
}
|
|
4600
|
+
if (range.end.col > range.start.col) {
|
|
4601
|
+
hasHorizontalMerges = true;
|
|
4602
|
+
maxHorizontalMergeEndCol = Math.max(maxHorizontalMergeEndCol, range.end.col);
|
|
4603
|
+
}
|
|
4604
|
+
if (range.end.row > range.start.row) {
|
|
4605
|
+
hasVerticalMerges = true;
|
|
4606
|
+
maxVerticalMergeEndRow = Math.max(maxVerticalMergeEndRow, range.end.row);
|
|
4565
4607
|
}
|
|
4566
4608
|
});
|
|
4609
|
+
const maxMetadataCol = Math.max(maxContentCol, maxHorizontalMergeEndCol, 0) + 256;
|
|
4567
4610
|
getLocalElements(document2, "col").forEach((colNode) => {
|
|
4568
4611
|
const min = Number(colNode.getAttribute("min") ?? 0) - 1;
|
|
4569
4612
|
const max = Number(colNode.getAttribute("max") ?? 0) - 1;
|
|
@@ -4575,7 +4618,7 @@ function parseSheetState(archive, path, options) {
|
|
|
4575
4618
|
return;
|
|
4576
4619
|
}
|
|
4577
4620
|
}
|
|
4578
|
-
for (let col = min; col <= max; col += 1) {
|
|
4621
|
+
for (let col = min; col <= Math.min(max, maxMetadataCol); col += 1) {
|
|
4579
4622
|
if (col >= 0) {
|
|
4580
4623
|
if (Number.isFinite(width)) {
|
|
4581
4624
|
const widthPx = sheetColumnWidthToPixels(width, columnWidthCharacterWidthPx);
|
|
@@ -4590,21 +4633,6 @@ function parseSheetState(archive, path, options) {
|
|
|
4590
4633
|
}
|
|
4591
4634
|
}
|
|
4592
4635
|
});
|
|
4593
|
-
getLocalElements(document2, "mergeCell").forEach((mergeNode) => {
|
|
4594
|
-
const reference = mergeNode.getAttribute("ref");
|
|
4595
|
-
const range = reference ? parseA1RangeReference(reference) : null;
|
|
4596
|
-
if (!range) {
|
|
4597
|
-
return;
|
|
4598
|
-
}
|
|
4599
|
-
if (range.end.col > range.start.col) {
|
|
4600
|
-
hasHorizontalMerges = true;
|
|
4601
|
-
maxHorizontalMergeEndCol = Math.max(maxHorizontalMergeEndCol, range.end.col);
|
|
4602
|
-
}
|
|
4603
|
-
if (range.end.row > range.start.row) {
|
|
4604
|
-
hasVerticalMerges = true;
|
|
4605
|
-
maxVerticalMergeEndRow = Math.max(maxVerticalMergeEndRow, range.end.row);
|
|
4606
|
-
}
|
|
4607
|
-
});
|
|
4608
4636
|
return {
|
|
4609
4637
|
cachedFormulaValues,
|
|
4610
4638
|
columnWidthCharacterWidthPx,
|
|
@@ -4617,6 +4645,10 @@ function parseSheetState(archive, path, options) {
|
|
|
4617
4645
|
hasVerticalMerges,
|
|
4618
4646
|
maxHorizontalMergeEndCol,
|
|
4619
4647
|
maxVerticalMergeEndRow,
|
|
4648
|
+
maxContentCol,
|
|
4649
|
+
maxContentRow,
|
|
4650
|
+
minContentCol: Number.isFinite(minContentCol) ? minContentCol : -1,
|
|
4651
|
+
minContentRow: Number.isFinite(minContentRow) ? minContentRow : -1,
|
|
4620
4652
|
hiddenCols: [...hiddenCols].sort((left, right) => left - right),
|
|
4621
4653
|
hiddenRows: [...hiddenRows].sort((left, right) => left - right),
|
|
4622
4654
|
rowHeightOverridesPx,
|
|
@@ -6645,6 +6677,20 @@ function resolveDisplayFileName(src, fileName) {
|
|
|
6645
6677
|
return lastSegment;
|
|
6646
6678
|
}
|
|
6647
6679
|
}
|
|
6680
|
+
function resolveSheetDisplayUsedRange(usedRange, sheetState) {
|
|
6681
|
+
const [minRow, minCol, maxRow, maxCol] = usedRange;
|
|
6682
|
+
const maxMeaningfulRow = Math.max(sheetState?.maxContentRow ?? -1, sheetState?.maxVerticalMergeEndRow ?? -1);
|
|
6683
|
+
const maxMeaningfulCol = Math.max(sheetState?.maxContentCol ?? -1, sheetState?.maxHorizontalMergeEndCol ?? -1);
|
|
6684
|
+
if (maxMeaningfulRow < 0 && maxMeaningfulCol < 0) {
|
|
6685
|
+
return usedRange;
|
|
6686
|
+
}
|
|
6687
|
+
return [
|
|
6688
|
+
sheetState?.minContentRow !== void 0 && sheetState.minContentRow >= 0 ? Math.min(minRow, sheetState.minContentRow) : minRow,
|
|
6689
|
+
sheetState?.minContentCol !== void 0 && sheetState.minContentCol >= 0 ? Math.min(minCol, sheetState.minContentCol) : minCol,
|
|
6690
|
+
maxMeaningfulRow >= 0 ? Math.min(maxRow, maxMeaningfulRow) : maxRow,
|
|
6691
|
+
maxMeaningfulCol >= 0 ? Math.min(maxCol, maxMeaningfulCol) : maxCol
|
|
6692
|
+
];
|
|
6693
|
+
}
|
|
6648
6694
|
function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette, styleById, namedCellStyleByName, tableStyleByName, showHiddenSheets = false) {
|
|
6649
6695
|
const sheets = [];
|
|
6650
6696
|
for (let index = 0; index < workbook.sheetCount; index += 1) {
|
|
@@ -6711,7 +6757,7 @@ function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette,
|
|
|
6711
6757
|
});
|
|
6712
6758
|
continue;
|
|
6713
6759
|
}
|
|
6714
|
-
const [minRow, minCol, maxRow, maxCol] = usedRange;
|
|
6760
|
+
const [minRow, minCol, maxRow, maxCol] = resolveSheetDisplayUsedRange(usedRange, sheetState);
|
|
6715
6761
|
let visibleRowsCache = null;
|
|
6716
6762
|
let visibleColsCache = null;
|
|
6717
6763
|
let rowHeightsCache = null;
|
|
@@ -16470,7 +16516,11 @@ var SELECTION_DRAG_THRESHOLD_PX = 4;
|
|
|
16470
16516
|
var IMAGE_MIN_SIZE_PX = 16;
|
|
16471
16517
|
var IMAGE_HANDLE_SIZE_PX = 10;
|
|
16472
16518
|
var CANVAS_RESIZE_HIT_SLOP_PX = 8;
|
|
16473
|
-
var CANVAS_VIEWPORT_OVERSCAN_PX =
|
|
16519
|
+
var CANVAS_VIEWPORT_OVERSCAN_PX = 480;
|
|
16520
|
+
var CANVAS_SCROLL_BUFFER_PX = 480;
|
|
16521
|
+
var CANVAS_DEFERRED_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX - 96;
|
|
16522
|
+
var CANVAS_IMMEDIATE_VIEWPORT_SYNC_THRESHOLD_PX = CANVAS_SCROLL_BUFFER_PX * 1.5;
|
|
16523
|
+
var CANVAS_DIRTY_CELL_CULL_MARGIN_PX = CANVAS_VIEWPORT_OVERSCAN_PX * 2;
|
|
16474
16524
|
var THUMBNAIL_DEFAULT_MAX_DIMENSION = 192;
|
|
16475
16525
|
var THUMBNAIL_FALLBACK_ROWS = 12;
|
|
16476
16526
|
var THUMBNAIL_FALLBACK_COLS = 8;
|
|
@@ -16485,6 +16535,12 @@ var CHART_SOURCE_HIGHLIGHT_COLORS = ["#2563eb", "#dc2626", "#7c3aed", "#059669",
|
|
|
16485
16535
|
var SHEET_SURFACE = "#ffffff";
|
|
16486
16536
|
var DEFAULT_CELL_PADDING = "0 4px";
|
|
16487
16537
|
var IMAGE_HANDLE_POSITIONS = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
|
|
16538
|
+
var CANVAS_CELL_STYLE_CACHE_LIMIT = 4096;
|
|
16539
|
+
var CANVAS_TEXT_MEASURE_CACHE_LIMIT = 2e4;
|
|
16540
|
+
var CANVAS_TEXT_TRUNCATE_CACHE_LIMIT = 4096;
|
|
16541
|
+
var CANVAS_TEXT_WRAP_CACHE_LIMIT = 2048;
|
|
16542
|
+
var CANVAS_PATH2D_CACHE_LIMIT = 512;
|
|
16543
|
+
var EMPTY_VIRTUAL_ITEMS = [];
|
|
16488
16544
|
var IMAGE_HANDLE_CURSOR = {
|
|
16489
16545
|
e: "ew-resize",
|
|
16490
16546
|
n: "ns-resize",
|
|
@@ -16528,6 +16584,50 @@ var NUMERIC_LENGTH_STYLE_KEYS = /* @__PURE__ */ new Set([
|
|
|
16528
16584
|
"top",
|
|
16529
16585
|
"width"
|
|
16530
16586
|
]);
|
|
16587
|
+
var canvasCellStyleCache = /* @__PURE__ */ new Map();
|
|
16588
|
+
var canvasTextMeasureCache = /* @__PURE__ */ new Map();
|
|
16589
|
+
var canvasTextTruncateCache = /* @__PURE__ */ new Map();
|
|
16590
|
+
var canvasTextWrapCache = /* @__PURE__ */ new Map();
|
|
16591
|
+
var canvasPath2DCache = /* @__PURE__ */ new Map();
|
|
16592
|
+
function rememberBoundedCacheValue(cache, key, value, limit) {
|
|
16593
|
+
if (cache.size >= limit) {
|
|
16594
|
+
const oldestKey = cache.keys().next().value;
|
|
16595
|
+
if (oldestKey !== void 0) {
|
|
16596
|
+
cache.delete(oldestKey);
|
|
16597
|
+
}
|
|
16598
|
+
}
|
|
16599
|
+
cache.set(key, value);
|
|
16600
|
+
return value;
|
|
16601
|
+
}
|
|
16602
|
+
function roundCanvasCacheWidth(width) {
|
|
16603
|
+
return Math.round(width * 4) / 4;
|
|
16604
|
+
}
|
|
16605
|
+
function measureCanvasTextWidth(context, text) {
|
|
16606
|
+
if (text.length === 0) {
|
|
16607
|
+
return 0;
|
|
16608
|
+
}
|
|
16609
|
+
const cacheKey = `${context.font}\0${text}`;
|
|
16610
|
+
const cached = canvasTextMeasureCache.get(cacheKey);
|
|
16611
|
+
if (cached !== void 0) {
|
|
16612
|
+
return cached;
|
|
16613
|
+
}
|
|
16614
|
+
return rememberBoundedCacheValue(
|
|
16615
|
+
canvasTextMeasureCache,
|
|
16616
|
+
cacheKey,
|
|
16617
|
+
context.measureText(text).width,
|
|
16618
|
+
CANVAS_TEXT_MEASURE_CACHE_LIMIT
|
|
16619
|
+
);
|
|
16620
|
+
}
|
|
16621
|
+
function getCachedCanvasPath2D(path) {
|
|
16622
|
+
if (typeof Path2D === "undefined") {
|
|
16623
|
+
return null;
|
|
16624
|
+
}
|
|
16625
|
+
const cached = canvasPath2DCache.get(path);
|
|
16626
|
+
if (cached) {
|
|
16627
|
+
return cached;
|
|
16628
|
+
}
|
|
16629
|
+
return rememberBoundedCacheValue(canvasPath2DCache, path, new Path2D(path), CANVAS_PATH2D_CACHE_LIMIT);
|
|
16630
|
+
}
|
|
16531
16631
|
function scaleCssLengthExpression(value, scale) {
|
|
16532
16632
|
if (scale === 1) {
|
|
16533
16633
|
return value;
|
|
@@ -16708,7 +16808,6 @@ function strokeCanvasBorderSide(context, side, rect, border) {
|
|
|
16708
16808
|
const right = rect.left + rect.width;
|
|
16709
16809
|
const top = rect.top;
|
|
16710
16810
|
const bottom = rect.top + rect.height;
|
|
16711
|
-
context.save();
|
|
16712
16811
|
context.strokeStyle = border.color;
|
|
16713
16812
|
context.lineWidth = border.width;
|
|
16714
16813
|
applyCanvasBorderDash(context, border.style, border.width);
|
|
@@ -16738,13 +16837,22 @@ function strokeCanvasBorderSide(context, side, rect, border) {
|
|
|
16738
16837
|
} else {
|
|
16739
16838
|
strokeLine(0);
|
|
16740
16839
|
}
|
|
16741
|
-
context.
|
|
16840
|
+
context.setLineDash([]);
|
|
16841
|
+
context.lineCap = "butt";
|
|
16842
|
+
context.lineWidth = 1;
|
|
16742
16843
|
}
|
|
16743
16844
|
function truncateCanvasText(context, text, maxWidth) {
|
|
16744
16845
|
if (maxWidth <= 0 || text.length === 0) {
|
|
16745
16846
|
return "";
|
|
16746
16847
|
}
|
|
16747
|
-
|
|
16848
|
+
const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);
|
|
16849
|
+
const cacheKey = `${context.font}\0${roundedMaxWidth}\0${text}`;
|
|
16850
|
+
const cached = canvasTextTruncateCache.get(cacheKey);
|
|
16851
|
+
if (cached !== void 0) {
|
|
16852
|
+
return cached;
|
|
16853
|
+
}
|
|
16854
|
+
if (measureCanvasTextWidth(context, text) <= maxWidth) {
|
|
16855
|
+
rememberBoundedCacheValue(canvasTextTruncateCache, cacheKey, text, CANVAS_TEXT_TRUNCATE_CACHE_LIMIT);
|
|
16748
16856
|
return text;
|
|
16749
16857
|
}
|
|
16750
16858
|
const ellipsis = "\u2026";
|
|
@@ -16753,13 +16861,18 @@ function truncateCanvasText(context, text, maxWidth) {
|
|
|
16753
16861
|
while (low < high) {
|
|
16754
16862
|
const mid = Math.ceil((low + high) / 2);
|
|
16755
16863
|
const candidate = `${text.slice(0, mid)}${ellipsis}`;
|
|
16756
|
-
if (context
|
|
16864
|
+
if (measureCanvasTextWidth(context, candidate) <= maxWidth) {
|
|
16757
16865
|
low = mid;
|
|
16758
16866
|
} else {
|
|
16759
16867
|
high = mid - 1;
|
|
16760
16868
|
}
|
|
16761
16869
|
}
|
|
16762
|
-
return
|
|
16870
|
+
return rememberBoundedCacheValue(
|
|
16871
|
+
canvasTextTruncateCache,
|
|
16872
|
+
cacheKey,
|
|
16873
|
+
low <= 0 ? ellipsis : `${text.slice(0, low)}${ellipsis}`,
|
|
16874
|
+
CANVAS_TEXT_TRUNCATE_CACHE_LIMIT
|
|
16875
|
+
);
|
|
16763
16876
|
}
|
|
16764
16877
|
function resolveCanvasFontSizePx(style, fallbackSize = 12) {
|
|
16765
16878
|
const rawFontSize = style.fontSize;
|
|
@@ -16818,7 +16931,7 @@ function resolveCanvasWrapIndex(context, text, maxWidth) {
|
|
|
16818
16931
|
while (low <= high) {
|
|
16819
16932
|
const mid = Math.floor((low + high) / 2);
|
|
16820
16933
|
const candidate = text.slice(0, mid);
|
|
16821
|
-
if (context
|
|
16934
|
+
if (measureCanvasTextWidth(context, candidate) <= maxWidth) {
|
|
16822
16935
|
best = mid;
|
|
16823
16936
|
low = mid + 1;
|
|
16824
16937
|
} else {
|
|
@@ -16835,6 +16948,12 @@ function wrapCanvasText(context, text, maxWidth) {
|
|
|
16835
16948
|
return text.replace(/\r\n?/g, "\n").split("\n");
|
|
16836
16949
|
}
|
|
16837
16950
|
const normalized = text.replace(/\r\n?/g, "\n");
|
|
16951
|
+
const roundedMaxWidth = roundCanvasCacheWidth(maxWidth);
|
|
16952
|
+
const cacheKey = `${context.font}\0${roundedMaxWidth}\0${normalized}`;
|
|
16953
|
+
const cached = canvasTextWrapCache.get(cacheKey);
|
|
16954
|
+
if (cached) {
|
|
16955
|
+
return cached;
|
|
16956
|
+
}
|
|
16838
16957
|
const paragraphs = normalized.split("\n");
|
|
16839
16958
|
const lines = [];
|
|
16840
16959
|
for (const paragraph of paragraphs) {
|
|
@@ -16844,7 +16963,7 @@ function wrapCanvasText(context, text, maxWidth) {
|
|
|
16844
16963
|
}
|
|
16845
16964
|
let remaining = paragraph;
|
|
16846
16965
|
while (remaining.length > 0) {
|
|
16847
|
-
if (context
|
|
16966
|
+
if (measureCanvasTextWidth(context, remaining) <= maxWidth) {
|
|
16848
16967
|
lines.push(remaining);
|
|
16849
16968
|
break;
|
|
16850
16969
|
}
|
|
@@ -16862,10 +16981,31 @@ function wrapCanvasText(context, text, maxWidth) {
|
|
|
16862
16981
|
remaining = remaining.slice(breakIndex).replace(/^\s+/g, "");
|
|
16863
16982
|
}
|
|
16864
16983
|
}
|
|
16865
|
-
return lines;
|
|
16984
|
+
return rememberBoundedCacheValue(canvasTextWrapCache, cacheKey, lines, CANVAS_TEXT_WRAP_CACHE_LIMIT);
|
|
16866
16985
|
}
|
|
16867
16986
|
function buildCanvasCellStyleCache(style) {
|
|
16868
|
-
|
|
16987
|
+
const cacheKey = [
|
|
16988
|
+
style.font,
|
|
16989
|
+
style.fontStyle,
|
|
16990
|
+
style.fontWeight,
|
|
16991
|
+
style.fontSize,
|
|
16992
|
+
style.fontFamily,
|
|
16993
|
+
style.borderTop,
|
|
16994
|
+
style.borderRight,
|
|
16995
|
+
style.borderBottom,
|
|
16996
|
+
style.borderLeft,
|
|
16997
|
+
style.padding,
|
|
16998
|
+
style.textAlign,
|
|
16999
|
+
style.color,
|
|
17000
|
+
style.textDecoration,
|
|
17001
|
+
style.textOverflow,
|
|
17002
|
+
style.whiteSpace
|
|
17003
|
+
].map((value) => value == null ? "" : String(value)).join("");
|
|
17004
|
+
const cached = canvasCellStyleCache.get(cacheKey);
|
|
17005
|
+
if (cached) {
|
|
17006
|
+
return cached;
|
|
17007
|
+
}
|
|
17008
|
+
return rememberBoundedCacheValue(canvasCellStyleCache, cacheKey, {
|
|
16869
17009
|
baseFont: resolveCanvasFont(style, 12),
|
|
16870
17010
|
bottomBorder: parseCanvasBorderDeclaration(style.borderBottom),
|
|
16871
17011
|
leftBorder: parseCanvasBorderDeclaration(style.borderLeft),
|
|
@@ -16877,7 +17017,7 @@ function buildCanvasCellStyleCache(style) {
|
|
|
16877
17017
|
textOverflowEllipsis: style.textOverflow === "ellipsis",
|
|
16878
17018
|
topBorder: parseCanvasBorderDeclaration(style.borderTop),
|
|
16879
17019
|
usesWrappedText: style.whiteSpace === "pre-wrap"
|
|
16880
|
-
};
|
|
17020
|
+
}, CANVAS_CELL_STYLE_CACHE_LIMIT);
|
|
16881
17021
|
}
|
|
16882
17022
|
function splitCssGradientArgs(value) {
|
|
16883
17023
|
const parts = [];
|
|
@@ -17430,6 +17570,25 @@ function resolveImageAnchorExtents(image) {
|
|
|
17430
17570
|
maxRow: Math.max(image.anchor.from.row, image.anchor.to.row)
|
|
17431
17571
|
};
|
|
17432
17572
|
}
|
|
17573
|
+
function resolveAnchoredBounds(anchor) {
|
|
17574
|
+
if (anchor.kind === "absolute") {
|
|
17575
|
+
return null;
|
|
17576
|
+
}
|
|
17577
|
+
if (anchor.kind === "one-cell") {
|
|
17578
|
+
return {
|
|
17579
|
+
maxCol: anchor.from.col,
|
|
17580
|
+
maxRow: anchor.from.row,
|
|
17581
|
+
minCol: anchor.from.col,
|
|
17582
|
+
minRow: anchor.from.row
|
|
17583
|
+
};
|
|
17584
|
+
}
|
|
17585
|
+
return {
|
|
17586
|
+
maxCol: Math.max(anchor.from.col, anchor.to.col),
|
|
17587
|
+
maxRow: Math.max(anchor.from.row, anchor.to.row),
|
|
17588
|
+
minCol: Math.min(anchor.from.col, anchor.to.col),
|
|
17589
|
+
minRow: Math.min(anchor.from.row, anchor.to.row)
|
|
17590
|
+
};
|
|
17591
|
+
}
|
|
17433
17592
|
function resolveShapeAnchorExtents(shape) {
|
|
17434
17593
|
if (shape.anchor.kind === "absolute") {
|
|
17435
17594
|
return { maxCol: 0, maxRow: 0 };
|
|
@@ -18386,6 +18545,44 @@ function resolveShapeVector(shape) {
|
|
|
18386
18545
|
}
|
|
18387
18546
|
return buildPresetShapePath(shape);
|
|
18388
18547
|
}
|
|
18548
|
+
function drawCanvasRoundedRect(context, left, top, width, height, radius) {
|
|
18549
|
+
const safeRadius = Math.max(0, Math.min(radius, width / 2, height / 2));
|
|
18550
|
+
context.beginPath();
|
|
18551
|
+
context.moveTo(left + safeRadius, top);
|
|
18552
|
+
context.lineTo(left + width - safeRadius, top);
|
|
18553
|
+
context.quadraticCurveTo(left + width, top, left + width, top + safeRadius);
|
|
18554
|
+
context.lineTo(left + width, top + height - safeRadius);
|
|
18555
|
+
context.quadraticCurveTo(left + width, top + height, left + width - safeRadius, top + height);
|
|
18556
|
+
context.lineTo(left + safeRadius, top + height);
|
|
18557
|
+
context.quadraticCurveTo(left, top + height, left, top + height - safeRadius);
|
|
18558
|
+
context.lineTo(left, top + safeRadius);
|
|
18559
|
+
context.quadraticCurveTo(left, top, left + safeRadius, top);
|
|
18560
|
+
context.closePath();
|
|
18561
|
+
}
|
|
18562
|
+
function applyCanvasShapeDash(context, dash, lineWidth) {
|
|
18563
|
+
if (!dash) {
|
|
18564
|
+
context.setLineDash([]);
|
|
18565
|
+
return;
|
|
18566
|
+
}
|
|
18567
|
+
const unit = Math.max(1, lineWidth);
|
|
18568
|
+
switch (dash) {
|
|
18569
|
+
case "dash":
|
|
18570
|
+
context.setLineDash([4 * unit, 3 * unit]);
|
|
18571
|
+
break;
|
|
18572
|
+
case "dashDot":
|
|
18573
|
+
context.setLineDash([4 * unit, 2 * unit, unit, 2 * unit]);
|
|
18574
|
+
break;
|
|
18575
|
+
case "dot":
|
|
18576
|
+
context.setLineDash([unit, 2 * unit]);
|
|
18577
|
+
break;
|
|
18578
|
+
case "lgDash":
|
|
18579
|
+
context.setLineDash([8 * unit, 3 * unit]);
|
|
18580
|
+
break;
|
|
18581
|
+
default:
|
|
18582
|
+
context.setLineDash([]);
|
|
18583
|
+
break;
|
|
18584
|
+
}
|
|
18585
|
+
}
|
|
18389
18586
|
function resolveShapeLineEndMarker(type, markerId, color, strokeWidth, rect, viewBox) {
|
|
18390
18587
|
if (type !== "triangle") {
|
|
18391
18588
|
return null;
|
|
@@ -18575,6 +18772,71 @@ function buildThumbnailAxisItems(actualIndices, getSizePx) {
|
|
|
18575
18772
|
totalSize: offset
|
|
18576
18773
|
};
|
|
18577
18774
|
}
|
|
18775
|
+
function resolveThumbnailAxisAbsoluteOffset(actualIndices, getSizePx, actualIndex) {
|
|
18776
|
+
let total = 0;
|
|
18777
|
+
for (const candidate of actualIndices) {
|
|
18778
|
+
if (candidate >= actualIndex) {
|
|
18779
|
+
break;
|
|
18780
|
+
}
|
|
18781
|
+
total += Math.max(1, getSizePx(candidate));
|
|
18782
|
+
}
|
|
18783
|
+
return total;
|
|
18784
|
+
}
|
|
18785
|
+
function resolveThumbnailAxisMarkerOffset({
|
|
18786
|
+
actualIndex,
|
|
18787
|
+
actualIndices,
|
|
18788
|
+
getSizePx,
|
|
18789
|
+
offsetEmu,
|
|
18790
|
+
previewStartActualIndex
|
|
18791
|
+
}) {
|
|
18792
|
+
return resolveThumbnailAxisAbsoluteOffset(actualIndices, getSizePx, actualIndex) - resolveThumbnailAxisAbsoluteOffset(actualIndices, getSizePx, previewStartActualIndex) + emuToPixels(offsetEmu);
|
|
18793
|
+
}
|
|
18794
|
+
function resolveThumbnailAnchoredRect(anchor, visibleRows, visibleCols, getRowHeightPx, getColWidthPx, previewRows, previewCols, options) {
|
|
18795
|
+
const previewStartRow = previewRows[0] ?? 0;
|
|
18796
|
+
const previewStartCol = previewCols[0] ?? 0;
|
|
18797
|
+
const previewOriginX = resolveThumbnailAxisAbsoluteOffset(visibleCols, getColWidthPx, previewStartCol);
|
|
18798
|
+
const previewOriginY = resolveThumbnailAxisAbsoluteOffset(visibleRows, getRowHeightPx, previewStartRow);
|
|
18799
|
+
const resolveMarkerLeft = (col, colOffsetEmu) => options.rowHeaderWidth + resolveThumbnailAxisMarkerOffset({
|
|
18800
|
+
actualIndex: col,
|
|
18801
|
+
actualIndices: visibleCols,
|
|
18802
|
+
getSizePx: getColWidthPx,
|
|
18803
|
+
offsetEmu: colOffsetEmu,
|
|
18804
|
+
previewStartActualIndex: previewStartCol
|
|
18805
|
+
});
|
|
18806
|
+
const resolveMarkerTop = (row, rowOffsetEmu) => options.headerHeight + resolveThumbnailAxisMarkerOffset({
|
|
18807
|
+
actualIndex: row,
|
|
18808
|
+
actualIndices: visibleRows,
|
|
18809
|
+
getSizePx: getRowHeightPx,
|
|
18810
|
+
offsetEmu: rowOffsetEmu,
|
|
18811
|
+
previewStartActualIndex: previewStartRow
|
|
18812
|
+
});
|
|
18813
|
+
if (anchor.kind === "absolute") {
|
|
18814
|
+
return {
|
|
18815
|
+
height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
|
|
18816
|
+
left: options.rowHeaderWidth + emuToPixels(anchor.positionEmu.x) - previewOriginX,
|
|
18817
|
+
top: options.headerHeight + emuToPixels(anchor.positionEmu.y) - previewOriginY,
|
|
18818
|
+
width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
|
|
18819
|
+
};
|
|
18820
|
+
}
|
|
18821
|
+
if (anchor.kind === "one-cell") {
|
|
18822
|
+
return {
|
|
18823
|
+
height: Math.max(1, emuToPixels(anchor.sizeEmu.cy)),
|
|
18824
|
+
left: resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu),
|
|
18825
|
+
top: resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu),
|
|
18826
|
+
width: Math.max(1, emuToPixels(anchor.sizeEmu.cx))
|
|
18827
|
+
};
|
|
18828
|
+
}
|
|
18829
|
+
const left = resolveMarkerLeft(anchor.from.col, anchor.from.colOffsetEmu);
|
|
18830
|
+
const top = resolveMarkerTop(anchor.from.row, anchor.from.rowOffsetEmu);
|
|
18831
|
+
const right = resolveMarkerLeft(anchor.to.col, anchor.to.colOffsetEmu);
|
|
18832
|
+
const bottom = resolveMarkerTop(anchor.to.row, anchor.to.rowOffsetEmu);
|
|
18833
|
+
return {
|
|
18834
|
+
height: Math.max(1, bottom - top),
|
|
18835
|
+
left,
|
|
18836
|
+
top,
|
|
18837
|
+
width: Math.max(1, right - left)
|
|
18838
|
+
};
|
|
18839
|
+
}
|
|
18578
18840
|
function columnLabel2(col) {
|
|
18579
18841
|
let label = "";
|
|
18580
18842
|
let nextValue = col;
|
|
@@ -18988,12 +19250,12 @@ function measureTextWidth(value, style) {
|
|
|
18988
19250
|
return value.length * 7;
|
|
18989
19251
|
}
|
|
18990
19252
|
textMeasureCanvas ??= document.createElement("canvas");
|
|
18991
|
-
const context = textMeasureCanvas.getContext("2d");
|
|
19253
|
+
const context = textMeasureCanvas.getContext("2d", { alpha: false });
|
|
18992
19254
|
if (!context) {
|
|
18993
19255
|
return value.length * 7;
|
|
18994
19256
|
}
|
|
18995
19257
|
context.font = buildCanvasFont(style);
|
|
18996
|
-
return context
|
|
19258
|
+
return measureCanvasTextWidth(context, value);
|
|
18997
19259
|
}
|
|
18998
19260
|
function measureWrappedTextHeight(value, style, widthPx) {
|
|
18999
19261
|
if (!value) {
|
|
@@ -19007,7 +19269,7 @@ function measureWrappedTextHeight(value, style, widthPx) {
|
|
|
19007
19269
|
return fallbackLineCount * lineHeight + padding.top + padding.bottom;
|
|
19008
19270
|
}
|
|
19009
19271
|
textMeasureCanvas ??= document.createElement("canvas");
|
|
19010
|
-
const context = textMeasureCanvas.getContext("2d");
|
|
19272
|
+
const context = textMeasureCanvas.getContext("2d", { alpha: false });
|
|
19011
19273
|
if (!context) {
|
|
19012
19274
|
return fallbackLineCount * lineHeight + padding.top + padding.bottom;
|
|
19013
19275
|
}
|
|
@@ -20105,7 +20367,7 @@ function blitCanvasWithScrollDelta(canvas, context, bufferCanvas, dpr, width, he
|
|
|
20105
20367
|
if (bufferCanvas.height !== deviceHeight) {
|
|
20106
20368
|
bufferCanvas.height = deviceHeight;
|
|
20107
20369
|
}
|
|
20108
|
-
const bufferContext = bufferCanvas.getContext("2d");
|
|
20370
|
+
const bufferContext = bufferCanvas.getContext("2d", { alpha: false });
|
|
20109
20371
|
if (!bufferContext) {
|
|
20110
20372
|
return null;
|
|
20111
20373
|
}
|
|
@@ -20360,6 +20622,222 @@ function resolveFormControlLabel(control) {
|
|
|
20360
20622
|
}
|
|
20361
20623
|
return label.replace(/\u00a0/g, " ").replace(/^\s+/, "");
|
|
20362
20624
|
}
|
|
20625
|
+
function drawStaticShapeText(context, shape, left, top, width, height, zoomFactor) {
|
|
20626
|
+
if (shape.paragraphs.length === 0) {
|
|
20627
|
+
return;
|
|
20628
|
+
}
|
|
20629
|
+
const inset = shape.textBox?.insetPx;
|
|
20630
|
+
const paddingLeft = (inset?.left ?? 6) * zoomFactor;
|
|
20631
|
+
const paddingRight = (inset?.right ?? 6) * zoomFactor;
|
|
20632
|
+
const paddingTop = (inset?.top ?? 4) * zoomFactor;
|
|
20633
|
+
const paddingBottom = (inset?.bottom ?? 4) * zoomFactor;
|
|
20634
|
+
const textLeft = left + paddingLeft;
|
|
20635
|
+
const textTop = top + paddingTop;
|
|
20636
|
+
const textWidth = Math.max(0, width - paddingLeft - paddingRight);
|
|
20637
|
+
const textHeight = Math.max(0, height - paddingTop - paddingBottom);
|
|
20638
|
+
if (textWidth <= 0 || textHeight <= 0) {
|
|
20639
|
+
return;
|
|
20640
|
+
}
|
|
20641
|
+
const lineMetrics = shape.paragraphs.map((paragraph) => {
|
|
20642
|
+
const lineHeight = Math.max(
|
|
20643
|
+
12 * zoomFactor,
|
|
20644
|
+
...paragraph.runs.map((run) => (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor * 1.2)
|
|
20645
|
+
);
|
|
20646
|
+
const widthPx = paragraph.runs.reduce((total, run) => {
|
|
20647
|
+
const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
|
|
20648
|
+
context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
|
|
20649
|
+
return total + measureCanvasTextWidth(context, run.text);
|
|
20650
|
+
}, 0);
|
|
20651
|
+
return { lineHeight, widthPx };
|
|
20652
|
+
});
|
|
20653
|
+
const totalTextHeight = lineMetrics.reduce((total, metric) => total + metric.lineHeight, 0);
|
|
20654
|
+
let y = textTop;
|
|
20655
|
+
if (shape.textBox?.verticalAlign === "middle") {
|
|
20656
|
+
y = textTop + Math.max(0, (textHeight - totalTextHeight) / 2);
|
|
20657
|
+
} else if (shape.textBox?.verticalAlign === "bottom") {
|
|
20658
|
+
y = textTop + Math.max(0, textHeight - totalTextHeight);
|
|
20659
|
+
}
|
|
20660
|
+
context.save();
|
|
20661
|
+
context.beginPath();
|
|
20662
|
+
context.rect(textLeft, textTop, textWidth, textHeight);
|
|
20663
|
+
context.clip();
|
|
20664
|
+
context.textBaseline = "middle";
|
|
20665
|
+
shape.paragraphs.forEach((paragraph, paragraphIndex) => {
|
|
20666
|
+
const metric = lineMetrics[paragraphIndex] ?? { lineHeight: 14 * zoomFactor, widthPx: 0 };
|
|
20667
|
+
let x = textLeft;
|
|
20668
|
+
const align = paragraph.align ?? shape.textBox?.horizontalAlign ?? "left";
|
|
20669
|
+
if (align === "center") {
|
|
20670
|
+
x = textLeft + Math.max(0, (textWidth - metric.widthPx) / 2);
|
|
20671
|
+
} else if (align === "right") {
|
|
20672
|
+
x = textLeft + Math.max(0, textWidth - metric.widthPx);
|
|
20673
|
+
}
|
|
20674
|
+
paragraph.runs.forEach((run) => {
|
|
20675
|
+
const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
|
|
20676
|
+
context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
|
|
20677
|
+
context.fillStyle = run.color ?? "#000000";
|
|
20678
|
+
context.textAlign = "left";
|
|
20679
|
+
const textY = y + metric.lineHeight / 2;
|
|
20680
|
+
context.fillText(run.text, x, textY);
|
|
20681
|
+
if (run.underline && run.text.length > 0) {
|
|
20682
|
+
const textWidthPx = measureCanvasTextWidth(context, run.text);
|
|
20683
|
+
context.strokeStyle = run.color ?? "#000000";
|
|
20684
|
+
context.lineWidth = Math.max(1, zoomFactor * 0.75);
|
|
20685
|
+
context.beginPath();
|
|
20686
|
+
context.moveTo(x, textY + Math.max(2, fontSize * 0.28));
|
|
20687
|
+
context.lineTo(x + textWidthPx, textY + Math.max(2, fontSize * 0.28));
|
|
20688
|
+
context.stroke();
|
|
20689
|
+
}
|
|
20690
|
+
x += measureCanvasTextWidth(context, run.text);
|
|
20691
|
+
});
|
|
20692
|
+
y += metric.lineHeight;
|
|
20693
|
+
});
|
|
20694
|
+
context.restore();
|
|
20695
|
+
}
|
|
20696
|
+
function drawStaticShape(context, shape, rect, zoomFactor) {
|
|
20697
|
+
const fillColor = shape.fill?.none ? "transparent" : shape.fill?.color ?? "transparent";
|
|
20698
|
+
const strokeColor = shape.stroke?.none ? "transparent" : shape.stroke?.color ?? "transparent";
|
|
20699
|
+
const lineWidth = Math.max(0, (shape.stroke?.widthPx ?? (shape.geometry === "line" ? 2 : 1)) * zoomFactor);
|
|
20700
|
+
const vectorShape = resolveShapeVector(shape);
|
|
20701
|
+
const opacity = Math.min(shape.fill?.opacity ?? 1, shape.stroke?.opacity ?? 1);
|
|
20702
|
+
context.save();
|
|
20703
|
+
context.translate(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
20704
|
+
if (shape.rotationDeg) {
|
|
20705
|
+
context.rotate(shape.rotationDeg * Math.PI / 180);
|
|
20706
|
+
}
|
|
20707
|
+
context.scale(shape.flipH ? -1 : 1, shape.flipV ? -1 : 1);
|
|
20708
|
+
context.globalAlpha *= opacity;
|
|
20709
|
+
context.lineWidth = lineWidth;
|
|
20710
|
+
context.strokeStyle = strokeColor;
|
|
20711
|
+
context.fillStyle = fillColor;
|
|
20712
|
+
applyCanvasShapeDash(context, shape.stroke?.dash, lineWidth);
|
|
20713
|
+
const localLeft = -rect.width / 2;
|
|
20714
|
+
const localTop = -rect.height / 2;
|
|
20715
|
+
if (vectorShape && typeof Path2D !== "undefined") {
|
|
20716
|
+
context.save();
|
|
20717
|
+
context.translate(localLeft, localTop);
|
|
20718
|
+
context.scale(
|
|
20719
|
+
rect.width / Math.max(1, vectorShape.viewBox.width),
|
|
20720
|
+
rect.height / Math.max(1, vectorShape.viewBox.height)
|
|
20721
|
+
);
|
|
20722
|
+
const path = getCachedCanvasPath2D(vectorShape.path);
|
|
20723
|
+
if (!path) {
|
|
20724
|
+
context.restore();
|
|
20725
|
+
context.restore();
|
|
20726
|
+
return;
|
|
20727
|
+
}
|
|
20728
|
+
if (fillColor !== "transparent") {
|
|
20729
|
+
context.fill(path);
|
|
20730
|
+
}
|
|
20731
|
+
if (strokeColor !== "transparent" && lineWidth > 0) {
|
|
20732
|
+
context.stroke(path);
|
|
20733
|
+
}
|
|
20734
|
+
context.restore();
|
|
20735
|
+
} else if (shape.geometry === "ellipse") {
|
|
20736
|
+
context.beginPath();
|
|
20737
|
+
context.ellipse(0, 0, rect.width / 2, rect.height / 2, 0, 0, Math.PI * 2);
|
|
20738
|
+
if (fillColor !== "transparent") {
|
|
20739
|
+
context.fill();
|
|
20740
|
+
}
|
|
20741
|
+
if (strokeColor !== "transparent" && lineWidth > 0) {
|
|
20742
|
+
context.stroke();
|
|
20743
|
+
}
|
|
20744
|
+
} else {
|
|
20745
|
+
const radius = shape.geometry === "roundRect" ? 12 * zoomFactor : 0;
|
|
20746
|
+
drawCanvasRoundedRect(context, localLeft, localTop, rect.width, rect.height, radius);
|
|
20747
|
+
if (fillColor !== "transparent") {
|
|
20748
|
+
context.fill();
|
|
20749
|
+
}
|
|
20750
|
+
if (strokeColor !== "transparent" && lineWidth > 0) {
|
|
20751
|
+
context.stroke();
|
|
20752
|
+
}
|
|
20753
|
+
}
|
|
20754
|
+
drawStaticShapeText(context, shape, localLeft, localTop, rect.width, rect.height, zoomFactor);
|
|
20755
|
+
context.restore();
|
|
20756
|
+
}
|
|
20757
|
+
function drawStaticFormControl(context, control, rect, palette, zoomFactor, sheetSurface = SHEET_SURFACE) {
|
|
20758
|
+
const label = resolveFormControlLabel(control);
|
|
20759
|
+
const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
|
|
20760
|
+
const textColor = control.textColor ?? "#000000";
|
|
20761
|
+
const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
|
|
20762
|
+
const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
|
|
20763
|
+
context.save();
|
|
20764
|
+
context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
|
|
20765
|
+
context.textBaseline = "middle";
|
|
20766
|
+
context.fillStyle = textColor;
|
|
20767
|
+
context.strokeStyle = stroke;
|
|
20768
|
+
context.lineWidth = Math.max(1, zoomFactor);
|
|
20769
|
+
if (control.kind === "group-box") {
|
|
20770
|
+
const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;
|
|
20771
|
+
drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);
|
|
20772
|
+
context.stroke();
|
|
20773
|
+
if (label) {
|
|
20774
|
+
context.fillStyle = sheetSurface;
|
|
20775
|
+
const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);
|
|
20776
|
+
context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);
|
|
20777
|
+
context.fillStyle = textColor;
|
|
20778
|
+
context.textAlign = "left";
|
|
20779
|
+
context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);
|
|
20780
|
+
}
|
|
20781
|
+
context.restore();
|
|
20782
|
+
return;
|
|
20783
|
+
}
|
|
20784
|
+
if (control.kind === "button" || control.kind === "dropdown" || control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
|
|
20785
|
+
if (control.kind === "button") {
|
|
20786
|
+
const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);
|
|
20787
|
+
gradient.addColorStop(0, "#f8fafc");
|
|
20788
|
+
gradient.addColorStop(1, "#e2e8f0");
|
|
20789
|
+
context.fillStyle = gradient;
|
|
20790
|
+
} else {
|
|
20791
|
+
context.fillStyle = "transparent";
|
|
20792
|
+
}
|
|
20793
|
+
drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === "button" ? 4 * zoomFactor : 2 * zoomFactor);
|
|
20794
|
+
if (control.kind === "button") {
|
|
20795
|
+
context.fill();
|
|
20796
|
+
}
|
|
20797
|
+
context.stroke();
|
|
20798
|
+
}
|
|
20799
|
+
let textLeft = rect.left + 2 * zoomFactor;
|
|
20800
|
+
const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
|
|
20801
|
+
if (control.kind === "checkbox") {
|
|
20802
|
+
context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
|
|
20803
|
+
if (control.checked) {
|
|
20804
|
+
context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
|
|
20805
|
+
context.fillRect(rect.left + 2 * zoomFactor + 1.5, rect.top + (rect.height - iconSize) / 2 + 1.5, Math.max(0, iconSize - 3), Math.max(0, iconSize - 3));
|
|
20806
|
+
context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
|
|
20807
|
+
context.beginPath();
|
|
20808
|
+
context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
|
|
20809
|
+
context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
|
|
20810
|
+
context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
|
|
20811
|
+
context.stroke();
|
|
20812
|
+
}
|
|
20813
|
+
textLeft += iconSize + 4 * zoomFactor;
|
|
20814
|
+
} else if (control.kind === "radio") {
|
|
20815
|
+
context.beginPath();
|
|
20816
|
+
context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
|
|
20817
|
+
context.stroke();
|
|
20818
|
+
if (control.checked) {
|
|
20819
|
+
context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
|
|
20820
|
+
context.beginPath();
|
|
20821
|
+
context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
|
|
20822
|
+
context.fill();
|
|
20823
|
+
}
|
|
20824
|
+
textLeft += iconSize + 4 * zoomFactor;
|
|
20825
|
+
}
|
|
20826
|
+
if (label) {
|
|
20827
|
+
const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);
|
|
20828
|
+
const text = truncateCanvasText(context, label, maxTextWidth);
|
|
20829
|
+
context.fillStyle = textColor;
|
|
20830
|
+
context.textAlign = control.textAlign === "right" ? "right" : control.textAlign === "center" ? "center" : "left";
|
|
20831
|
+
const textX = context.textAlign === "right" ? rect.left + rect.width - textRightInset : context.textAlign === "center" ? textLeft + maxTextWidth / 2 : textLeft;
|
|
20832
|
+
context.fillText(text, textX, rect.top + rect.height / 2);
|
|
20833
|
+
}
|
|
20834
|
+
if (control.kind === "dropdown") {
|
|
20835
|
+
context.fillStyle = textColor;
|
|
20836
|
+
context.textAlign = "center";
|
|
20837
|
+
context.fillText("\u25BC", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);
|
|
20838
|
+
}
|
|
20839
|
+
context.restore();
|
|
20840
|
+
}
|
|
20363
20841
|
function resolveConditionalDataBarForCell(row, col, worksheet, sheet, metricsCache) {
|
|
20364
20842
|
const rules = sheet?.conditionalFormatRules ?? [];
|
|
20365
20843
|
const matchingRule = rules.find(
|
|
@@ -20962,6 +21440,8 @@ function XlsxGrid({
|
|
|
20962
21440
|
selectionHeaderColor,
|
|
20963
21441
|
showImages = true
|
|
20964
21442
|
}) {
|
|
21443
|
+
const xlsxCanvasProfileTarget = typeof window !== "undefined" ? window.__xlsxCanvasProfile : void 0;
|
|
21444
|
+
const xlsxGridRenderStart = xlsxCanvasProfileTarget ? performance.now() : 0;
|
|
20965
21445
|
const {
|
|
20966
21446
|
activeCell,
|
|
20967
21447
|
activeSheet,
|
|
@@ -21045,6 +21525,11 @@ function XlsxGrid({
|
|
|
21045
21525
|
const leftFrozenHeaderCanvasRef = React4.useRef(null);
|
|
21046
21526
|
const leftScrollHeaderCanvasRef = React4.useRef(null);
|
|
21047
21527
|
const cornerHeaderCanvasRef = React4.useRef(null);
|
|
21528
|
+
const canvasScrollOverlayContentRef = React4.useRef(null);
|
|
21529
|
+
const canvasTopOverlayContentRef = React4.useRef(null);
|
|
21530
|
+
const canvasLeftOverlayContentRef = React4.useRef(null);
|
|
21531
|
+
const canvasCornerOverlayContentRef = React4.useRef(null);
|
|
21532
|
+
const canvasImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
|
|
21048
21533
|
const selectionOverlayRef = React4.useRef(null);
|
|
21049
21534
|
const activeValidationOverlayRef = React4.useRef(null);
|
|
21050
21535
|
const fillHandleRef = React4.useRef(null);
|
|
@@ -21073,6 +21558,7 @@ function XlsxGrid({
|
|
|
21073
21558
|
const pendingLiveZoomCommitRef = React4.useRef(null);
|
|
21074
21559
|
const touchPinchStateRef = React4.useRef(null);
|
|
21075
21560
|
const safariPinchStartZoomRef = React4.useRef(null);
|
|
21561
|
+
const canvasTransformStyleCacheRef = React4.useRef(/* @__PURE__ */ new WeakMap());
|
|
21076
21562
|
const displayedSelectionRef = React4.useRef(null);
|
|
21077
21563
|
const firstVisibleColRef = React4.useRef(void 0);
|
|
21078
21564
|
const lastVisibleColRef = React4.useRef(void 0);
|
|
@@ -21098,12 +21584,19 @@ function XlsxGrid({
|
|
|
21098
21584
|
const [fillPreviewRange, setFillPreviewRange] = React4.useState(null);
|
|
21099
21585
|
const [chartPreviewRect, setChartPreviewRect] = React4.useState(null);
|
|
21100
21586
|
const [liveGestureZoom, setLiveGestureZoom] = React4.useState(null);
|
|
21587
|
+
const [canvasImageLoadVersion, setCanvasImageLoadVersion] = React4.useState(0);
|
|
21101
21588
|
const liveDrawingViewportRef = React4.useRef({
|
|
21102
21589
|
height: 0,
|
|
21103
21590
|
left: 0,
|
|
21104
21591
|
top: 0,
|
|
21105
21592
|
width: 0
|
|
21106
21593
|
});
|
|
21594
|
+
const canvasLayoutMetricsRef = React4.useRef({
|
|
21595
|
+
displayHeaderHeight: HEADER_HEIGHT,
|
|
21596
|
+
displayRowHeaderWidth: ROW_HEADER_WIDTH,
|
|
21597
|
+
frozenPaneBottom: HEADER_HEIGHT,
|
|
21598
|
+
frozenPaneRight: ROW_HEADER_WIDTH
|
|
21599
|
+
});
|
|
21107
21600
|
const paintedDrawingViewportRef = React4.useRef({
|
|
21108
21601
|
height: 0,
|
|
21109
21602
|
left: 0,
|
|
@@ -21213,6 +21706,12 @@ function XlsxGrid({
|
|
|
21213
21706
|
const applyCanvasViewportCompensation = React4.useCallback((liveViewport) => {
|
|
21214
21707
|
const nextLiveViewport = liveViewport ?? liveDrawingViewportRef.current;
|
|
21215
21708
|
const paintedViewport = paintedDrawingViewportRef.current;
|
|
21709
|
+
const {
|
|
21710
|
+
displayHeaderHeight: liveDisplayHeaderHeight,
|
|
21711
|
+
displayRowHeaderWidth: liveDisplayRowHeaderWidth,
|
|
21712
|
+
frozenPaneBottom: liveFrozenPaneBottom,
|
|
21713
|
+
frozenPaneRight: liveFrozenPaneRight
|
|
21714
|
+
} = canvasLayoutMetricsRef.current;
|
|
21216
21715
|
const currentLiveGestureZoom = liveGestureZoomRef.current;
|
|
21217
21716
|
const isLiveZooming2 = currentLiveGestureZoom !== null && zoomScale === currentLiveGestureZoom.baseZoomScale;
|
|
21218
21717
|
const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
|
|
@@ -21220,12 +21719,41 @@ function XlsxGrid({
|
|
|
21220
21719
|
const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
|
|
21221
21720
|
const liveZoomTranslateX2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.x * (1 - liveZoomScale2) : 0;
|
|
21222
21721
|
const liveZoomTranslateY2 = isLiveZooming2 ? currentLiveGestureZoom.anchor.y * (1 - liveZoomScale2) : 0;
|
|
21223
|
-
const
|
|
21224
|
-
if (!
|
|
21722
|
+
const applyElementTransform = (element, transform, willChange, transformOrigin = null) => {
|
|
21723
|
+
if (!element) {
|
|
21225
21724
|
return;
|
|
21226
21725
|
}
|
|
21227
|
-
|
|
21228
|
-
|
|
21726
|
+
const cached = canvasTransformStyleCacheRef.current.get(element);
|
|
21727
|
+
if (cached?.transform !== transform || element.style.transform !== transform) {
|
|
21728
|
+
element.style.transform = transform;
|
|
21729
|
+
}
|
|
21730
|
+
if (cached?.willChange !== willChange || element.style.willChange !== willChange) {
|
|
21731
|
+
element.style.willChange = willChange;
|
|
21732
|
+
}
|
|
21733
|
+
if (transformOrigin !== null && (cached?.transformOrigin !== transformOrigin || element.style.transformOrigin !== transformOrigin)) {
|
|
21734
|
+
element.style.transformOrigin = transformOrigin;
|
|
21735
|
+
}
|
|
21736
|
+
canvasTransformStyleCacheRef.current.set(element, {
|
|
21737
|
+
transform,
|
|
21738
|
+
transformOrigin,
|
|
21739
|
+
willChange
|
|
21740
|
+
});
|
|
21741
|
+
};
|
|
21742
|
+
const applyCanvasTransform = (canvas, translateX, translateY) => {
|
|
21743
|
+
const shouldTransform = translateX !== 0 || translateY !== 0 || liveZoomScale2 !== 1;
|
|
21744
|
+
applyElementTransform(
|
|
21745
|
+
canvas,
|
|
21746
|
+
shouldTransform ? `translate3d(${translateX}px, ${translateY}px, 0) scale(${liveZoomScale2})` : "",
|
|
21747
|
+
shouldTransform ? "transform" : ""
|
|
21748
|
+
);
|
|
21749
|
+
};
|
|
21750
|
+
const applyOverlayTransform = (element, translateX, translateY) => {
|
|
21751
|
+
applyElementTransform(
|
|
21752
|
+
element,
|
|
21753
|
+
liveZoomScale2 !== 1 ? `translate3d(${translateX + liveZoomTranslateX2}px, ${translateY + liveZoomTranslateY2}px, 0) scale(${liveZoomScale2})` : `translate3d(${translateX}px, ${translateY}px, 0)`,
|
|
21754
|
+
"transform",
|
|
21755
|
+
"0 0"
|
|
21756
|
+
);
|
|
21229
21757
|
};
|
|
21230
21758
|
applyCanvasTransform(
|
|
21231
21759
|
scrollBodyCanvasRef.current,
|
|
@@ -21272,6 +21800,26 @@ function XlsxGrid({
|
|
|
21272
21800
|
liveZoomTranslateX2,
|
|
21273
21801
|
liveZoomTranslateY2
|
|
21274
21802
|
);
|
|
21803
|
+
applyOverlayTransform(
|
|
21804
|
+
canvasScrollOverlayContentRef.current,
|
|
21805
|
+
-nextLiveViewport.left - liveFrozenPaneRight,
|
|
21806
|
+
-nextLiveViewport.top - liveFrozenPaneBottom
|
|
21807
|
+
);
|
|
21808
|
+
applyOverlayTransform(
|
|
21809
|
+
canvasTopOverlayContentRef.current,
|
|
21810
|
+
-nextLiveViewport.left - liveFrozenPaneRight,
|
|
21811
|
+
-liveDisplayHeaderHeight
|
|
21812
|
+
);
|
|
21813
|
+
applyOverlayTransform(
|
|
21814
|
+
canvasLeftOverlayContentRef.current,
|
|
21815
|
+
-liveDisplayRowHeaderWidth,
|
|
21816
|
+
-nextLiveViewport.top - liveFrozenPaneBottom
|
|
21817
|
+
);
|
|
21818
|
+
applyOverlayTransform(
|
|
21819
|
+
canvasCornerOverlayContentRef.current,
|
|
21820
|
+
-liveDisplayRowHeaderWidth,
|
|
21821
|
+
-liveDisplayHeaderHeight
|
|
21822
|
+
);
|
|
21275
21823
|
}, [zoomScale]);
|
|
21276
21824
|
const updateLiveGestureZoomState = React4.useCallback((nextState) => {
|
|
21277
21825
|
const resolvedState = typeof nextState === "function" ? nextState(liveGestureZoomRef.current) : nextState;
|
|
@@ -21374,6 +21922,11 @@ function XlsxGrid({
|
|
|
21374
21922
|
if (matchesLiveViewport && matchesStateViewport) {
|
|
21375
21923
|
return;
|
|
21376
21924
|
}
|
|
21925
|
+
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);
|
|
21926
|
+
if (!shouldCommitDeferredViewport) {
|
|
21927
|
+
pendingDrawingViewportRef.current = null;
|
|
21928
|
+
return;
|
|
21929
|
+
}
|
|
21377
21930
|
pendingDrawingViewportRef.current = nextViewport;
|
|
21378
21931
|
if (drawingViewportFrameRef.current !== null) {
|
|
21379
21932
|
return;
|
|
@@ -21661,6 +22214,12 @@ function XlsxGrid({
|
|
|
21661
22214
|
) : displayRowHeaderWidth,
|
|
21662
22215
|
[displayActualColWidths, displayDefaultColWidth, displayRowHeaderWidth, frozenCols, stickyLeftByCol]
|
|
21663
22216
|
);
|
|
22217
|
+
canvasLayoutMetricsRef.current = {
|
|
22218
|
+
displayHeaderHeight,
|
|
22219
|
+
displayRowHeaderWidth,
|
|
22220
|
+
frozenPaneBottom,
|
|
22221
|
+
frozenPaneRight
|
|
22222
|
+
};
|
|
21664
22223
|
const rowPrefixSumsRef = React4.useRef(rowPrefixSums);
|
|
21665
22224
|
const colPrefixSumsRef = React4.useRef(colPrefixSums);
|
|
21666
22225
|
const firstVisibleRow = visibleRows[0];
|
|
@@ -21712,6 +22271,7 @@ function XlsxGrid({
|
|
|
21712
22271
|
}, [charts, images, shapes]);
|
|
21713
22272
|
const shouldVirtualizeRows = visibleRows.length > 0;
|
|
21714
22273
|
const shouldVirtualizeCols = visibleCols.length > 0 && frozenCols.length === 0;
|
|
22274
|
+
const shouldUseDomVirtualizer = !experimentalCanvas;
|
|
21715
22275
|
const getScrollElement = React4.useCallback(() => scrollRef.current, []);
|
|
21716
22276
|
const estimateRowSize = React4.useCallback(
|
|
21717
22277
|
(index) => displayEffectiveRowHeights[index] ?? displayDefaultRowHeight,
|
|
@@ -21725,6 +22285,7 @@ function XlsxGrid({
|
|
|
21725
22285
|
const getColItemKey = React4.useCallback((index) => visibleCols[index] ?? index, [visibleCols]);
|
|
21726
22286
|
const rowVirtualizer = useVirtualizer({
|
|
21727
22287
|
count: visibleRows.length,
|
|
22288
|
+
enabled: shouldUseDomVirtualizer,
|
|
21728
22289
|
estimateSize: estimateRowSize,
|
|
21729
22290
|
getScrollElement,
|
|
21730
22291
|
getItemKey: getRowItemKey,
|
|
@@ -21732,14 +22293,15 @@ function XlsxGrid({
|
|
|
21732
22293
|
});
|
|
21733
22294
|
const colVirtualizer = useVirtualizer({
|
|
21734
22295
|
count: visibleCols.length,
|
|
22296
|
+
enabled: shouldUseDomVirtualizer,
|
|
21735
22297
|
estimateSize: estimateColSize,
|
|
21736
22298
|
getScrollElement,
|
|
21737
22299
|
getItemKey: getColItemKey,
|
|
21738
22300
|
horizontal: true,
|
|
21739
22301
|
overscan: 6
|
|
21740
22302
|
});
|
|
21741
|
-
const currentRowVirtualItems = rowVirtualizer.getVirtualItems();
|
|
21742
|
-
const currentColVirtualItems = colVirtualizer.getVirtualItems();
|
|
22303
|
+
const currentRowVirtualItems = shouldUseDomVirtualizer ? rowVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;
|
|
22304
|
+
const currentColVirtualItems = shouldUseDomVirtualizer ? colVirtualizer.getVirtualItems() : EMPTY_VIRTUAL_ITEMS;
|
|
21743
22305
|
const frozenRowVirtualIndices = React4.useMemo(
|
|
21744
22306
|
() => frozenRows.map((row) => rowIndexByActual.get(row)).filter((index) => index !== void 0),
|
|
21745
22307
|
[frozenRows, rowIndexByActual]
|
|
@@ -21975,10 +22537,12 @@ function XlsxGrid({
|
|
|
21975
22537
|
scroller.scrollTop = scroller.scrollTop / previousZoomFactor * zoomFactor;
|
|
21976
22538
|
}
|
|
21977
22539
|
previousZoomFactorRef.current = zoomFactor;
|
|
21978
|
-
|
|
21979
|
-
|
|
22540
|
+
if (shouldUseDomVirtualizer) {
|
|
22541
|
+
rowVirtualizer.measure();
|
|
22542
|
+
colVirtualizer.measure();
|
|
22543
|
+
}
|
|
21980
22544
|
syncDrawingViewport(scroller, { immediate: true });
|
|
21981
|
-
}, [syncDrawingViewport, zoomFactor]);
|
|
22545
|
+
}, [shouldUseDomVirtualizer, syncDrawingViewport, zoomFactor]);
|
|
21982
22546
|
React4.useLayoutEffect(() => {
|
|
21983
22547
|
syncDrawingViewport(scrollRef.current, { immediate: true });
|
|
21984
22548
|
}, [activeSheet, activeTabIndex, displayColLimit, displayRowLimit, syncDrawingViewport, zoomFactor]);
|
|
@@ -22074,7 +22638,7 @@ function XlsxGrid({
|
|
|
22074
22638
|
const initialUsedCol = resolveFirstUsedVisibleIndex(visibleCols, activeSheet?.minUsedCol ?? -1);
|
|
22075
22639
|
const initialScrollTop = initialUsedRow > 0 ? sumPrefixRange(actualRowPrefixSums, 0, initialUsedRow - 1) : 0;
|
|
22076
22640
|
const initialScrollLeft = initialUsedCol > 0 ? sumPrefixRange(actualColPrefixSums, 0, initialUsedCol - 1) : 0;
|
|
22077
|
-
if (shouldVirtualizeRows) {
|
|
22641
|
+
if (shouldUseDomVirtualizer && shouldVirtualizeRows) {
|
|
22078
22642
|
rowVirtualizer.scrollToOffset(initialScrollTop);
|
|
22079
22643
|
} else if (scrollRef.current) {
|
|
22080
22644
|
scrollRef.current.scrollTop = initialScrollTop;
|
|
@@ -22082,7 +22646,7 @@ function XlsxGrid({
|
|
|
22082
22646
|
if (scrollRef.current) {
|
|
22083
22647
|
scrollRef.current.scrollLeft = initialScrollLeft;
|
|
22084
22648
|
}
|
|
22085
|
-
if (shouldVirtualizeCols && frozenCols.length === 0) {
|
|
22649
|
+
if (shouldUseDomVirtualizer && shouldVirtualizeCols && frozenCols.length === 0) {
|
|
22086
22650
|
colVirtualizer.scrollToOffset(initialScrollLeft);
|
|
22087
22651
|
}
|
|
22088
22652
|
if (scrollRef.current) {
|
|
@@ -22103,6 +22667,7 @@ function XlsxGrid({
|
|
|
22103
22667
|
isWorkerBacked,
|
|
22104
22668
|
shouldVirtualizeCols,
|
|
22105
22669
|
shouldVirtualizeRows,
|
|
22670
|
+
shouldUseDomVirtualizer,
|
|
22106
22671
|
syncDrawingViewport,
|
|
22107
22672
|
rowVirtualizer,
|
|
22108
22673
|
visibleCols,
|
|
@@ -22122,24 +22687,40 @@ function XlsxGrid({
|
|
|
22122
22687
|
setPendingNavigation(null);
|
|
22123
22688
|
}, [activeSheetIndex, pendingNavigation, selectCell]);
|
|
22124
22689
|
React4.useEffect(() => {
|
|
22125
|
-
if (shouldVirtualizeRows) {
|
|
22690
|
+
if (shouldUseDomVirtualizer && shouldVirtualizeRows) {
|
|
22126
22691
|
rowVirtualizer.measure();
|
|
22127
22692
|
}
|
|
22128
|
-
if (shouldVirtualizeCols) {
|
|
22693
|
+
if (shouldUseDomVirtualizer && shouldVirtualizeCols) {
|
|
22129
22694
|
colVirtualizer.measure();
|
|
22130
22695
|
}
|
|
22131
|
-
}, [
|
|
22696
|
+
}, [
|
|
22697
|
+
activeSheetIndex,
|
|
22698
|
+
revision,
|
|
22699
|
+
shouldUseDomVirtualizer,
|
|
22700
|
+
shouldVirtualizeCols,
|
|
22701
|
+
shouldVirtualizeRows,
|
|
22702
|
+
visibleCols.length,
|
|
22703
|
+
visibleRows.length
|
|
22704
|
+
]);
|
|
22132
22705
|
const handleScrollerScroll = React4.useCallback((event) => {
|
|
22133
22706
|
const currentScroller = event.currentTarget;
|
|
22134
22707
|
cachedScrollerRectRef.current = null;
|
|
22135
|
-
|
|
22708
|
+
const paintedViewport = paintedDrawingViewportRef.current;
|
|
22709
|
+
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;
|
|
22710
|
+
syncDrawingViewport(currentScroller, { immediate: shouldSyncDrawingViewportImmediately });
|
|
22136
22711
|
if (currentScroller.scrollHeight - (currentScroller.scrollTop + currentScroller.clientHeight) < OPEN_GRID_VERTICAL_EDGE_PX) {
|
|
22137
|
-
setDisplayRowLimit((current) =>
|
|
22712
|
+
setDisplayRowLimit((current) => {
|
|
22713
|
+
const nextLimit = current + OPEN_GRID_ROW_GROWTH;
|
|
22714
|
+
return readOnly && current < maxRowDisplayLimit ? Math.min(maxRowDisplayLimit, nextLimit) : nextLimit;
|
|
22715
|
+
});
|
|
22138
22716
|
}
|
|
22139
22717
|
if (currentScroller.scrollWidth - (currentScroller.scrollLeft + currentScroller.clientWidth) < OPEN_GRID_HORIZONTAL_EDGE_PX) {
|
|
22140
|
-
setDisplayColLimit((current) =>
|
|
22718
|
+
setDisplayColLimit((current) => {
|
|
22719
|
+
const nextLimit = current + OPEN_GRID_COL_GROWTH;
|
|
22720
|
+
return readOnly && current < maxColDisplayLimit ? Math.min(maxColDisplayLimit, nextLimit) : nextLimit;
|
|
22721
|
+
});
|
|
22141
22722
|
}
|
|
22142
|
-
}, [syncDrawingViewport]);
|
|
22723
|
+
}, [maxColDisplayLimit, maxRowDisplayLimit, readOnly, syncDrawingViewport]);
|
|
22143
22724
|
React4.useEffect(() => {
|
|
22144
22725
|
const scroller = scrollRef.current;
|
|
22145
22726
|
if (!scroller || !enableGestureZoom) {
|
|
@@ -23045,14 +23626,14 @@ function XlsxGrid({
|
|
|
23045
23626
|
},
|
|
23046
23627
|
left: {
|
|
23047
23628
|
cols: frozenColItems,
|
|
23048
|
-
rows:
|
|
23629
|
+
rows: scrollRowItems
|
|
23049
23630
|
},
|
|
23050
23631
|
scroll: {
|
|
23051
|
-
cols:
|
|
23052
|
-
rows:
|
|
23632
|
+
cols: scrollColItems,
|
|
23633
|
+
rows: scrollRowItems
|
|
23053
23634
|
},
|
|
23054
23635
|
top: {
|
|
23055
|
-
cols:
|
|
23636
|
+
cols: scrollColItems,
|
|
23056
23637
|
rows: frozenRowItems
|
|
23057
23638
|
}
|
|
23058
23639
|
};
|
|
@@ -23186,6 +23767,51 @@ function XlsxGrid({
|
|
|
23186
23767
|
visibleRows
|
|
23187
23768
|
]
|
|
23188
23769
|
);
|
|
23770
|
+
const shouldBakeCanvasStaticDrawings = Boolean(experimentalCanvas && readOnly && showImages);
|
|
23771
|
+
const shouldBakeCanvasImages = Boolean(shouldBakeCanvasStaticDrawings && !renderImage && !renderImageSelection);
|
|
23772
|
+
const bakedShapeRects = React4.useMemo(
|
|
23773
|
+
() => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => !shape.hyperlink) : [],
|
|
23774
|
+
[shapeRects, shouldBakeCanvasStaticDrawings]
|
|
23775
|
+
);
|
|
23776
|
+
const domShapeRects = React4.useMemo(
|
|
23777
|
+
() => shouldBakeCanvasStaticDrawings ? shapeRects.filter(({ shape }) => shape.hyperlink) : shapeRects,
|
|
23778
|
+
[shapeRects, shouldBakeCanvasStaticDrawings]
|
|
23779
|
+
);
|
|
23780
|
+
const bakedFormControlRects = React4.useMemo(
|
|
23781
|
+
() => shouldBakeCanvasStaticDrawings ? formControlRects : [],
|
|
23782
|
+
[formControlRects, shouldBakeCanvasStaticDrawings]
|
|
23783
|
+
);
|
|
23784
|
+
const domFormControlRects = React4.useMemo(
|
|
23785
|
+
() => shouldBakeCanvasStaticDrawings ? [] : formControlRects,
|
|
23786
|
+
[formControlRects, shouldBakeCanvasStaticDrawings]
|
|
23787
|
+
);
|
|
23788
|
+
const bakedImageRects = React4.useMemo(
|
|
23789
|
+
() => shouldBakeCanvasImages ? imageRects.filter(({ image }) => !image.hyperlink && selectedImageId !== image.id) : [],
|
|
23790
|
+
[imageRects, selectedImageId, shouldBakeCanvasImages]
|
|
23791
|
+
);
|
|
23792
|
+
const domImageRects = React4.useMemo(
|
|
23793
|
+
() => shouldBakeCanvasImages ? imageRects.filter(({ image }) => image.hyperlink || selectedImageId === image.id) : imageRects,
|
|
23794
|
+
[imageRects, selectedImageId, shouldBakeCanvasImages]
|
|
23795
|
+
);
|
|
23796
|
+
const hasCanvasDomDrawingOverlays = Boolean(
|
|
23797
|
+
showImages && (chartRects.length > 0 || domShapeRects.length > 0 || domFormControlRects.length > 0 || domImageRects.length > 0)
|
|
23798
|
+
);
|
|
23799
|
+
const bakedCanvasDrawingSignature = React4.useMemo(() => {
|
|
23800
|
+
if (!shouldBakeCanvasStaticDrawings) {
|
|
23801
|
+
return "";
|
|
23802
|
+
}
|
|
23803
|
+
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("|");
|
|
23804
|
+
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("|");
|
|
23805
|
+
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("|");
|
|
23806
|
+
return `${revision}:${canvasImageLoadVersion}:${shapeSignature}:${controlSignature}:${imageSignature}`;
|
|
23807
|
+
}, [
|
|
23808
|
+
bakedFormControlRects,
|
|
23809
|
+
bakedImageRects,
|
|
23810
|
+
bakedShapeRects,
|
|
23811
|
+
canvasImageLoadVersion,
|
|
23812
|
+
revision,
|
|
23813
|
+
shouldBakeCanvasStaticDrawings
|
|
23814
|
+
]);
|
|
23189
23815
|
const resolveMountedCellOverlayRect = React4.useCallback((element) => {
|
|
23190
23816
|
const wrapper = wrapperRef.current;
|
|
23191
23817
|
if (!wrapper) {
|
|
@@ -24054,6 +24680,24 @@ function XlsxGrid({
|
|
|
24054
24680
|
rowPrefixSums,
|
|
24055
24681
|
stickyTopByRow
|
|
24056
24682
|
]);
|
|
24683
|
+
const scrollBodyViewportWidth = Math.max(0, drawingViewport.width - frozenPaneRight);
|
|
24684
|
+
const scrollBodyViewportHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);
|
|
24685
|
+
const canvasScrollBufferX = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportWidth);
|
|
24686
|
+
const canvasScrollBufferY = Math.min(CANVAS_SCROLL_BUFFER_PX, scrollBodyViewportHeight);
|
|
24687
|
+
const scrollBodyCanvasLeft = frozenPaneRight - canvasScrollBufferX;
|
|
24688
|
+
const scrollBodyCanvasTop = frozenPaneBottom - canvasScrollBufferY;
|
|
24689
|
+
const scrollBodyCanvasWidth = scrollBodyViewportWidth + canvasScrollBufferX * 2;
|
|
24690
|
+
const scrollBodyCanvasHeight = scrollBodyViewportHeight + canvasScrollBufferY * 2;
|
|
24691
|
+
const topBodyCanvasWidth = scrollBodyCanvasWidth;
|
|
24692
|
+
const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);
|
|
24693
|
+
const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
|
|
24694
|
+
const leftBodyCanvasHeight = scrollBodyCanvasHeight;
|
|
24695
|
+
const cornerBodyCanvasWidth = leftBodyCanvasWidth;
|
|
24696
|
+
const cornerBodyCanvasHeight = topBodyCanvasHeight;
|
|
24697
|
+
const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
|
|
24698
|
+
const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
|
|
24699
|
+
const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
|
|
24700
|
+
const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
|
|
24057
24701
|
const canvasColumnHeaderCells = React4.useMemo(
|
|
24058
24702
|
() => canvasVisibleColItems.flatMap((column) => {
|
|
24059
24703
|
const rect = resolveCanvasColumnHeaderRect(column.actualCol);
|
|
@@ -24066,7 +24710,7 @@ function XlsxGrid({
|
|
|
24066
24710
|
height: displayHeaderHeight,
|
|
24067
24711
|
isFrozen,
|
|
24068
24712
|
left: rect.left,
|
|
24069
|
-
localLeft: rect.left - (isFrozen ? displayRowHeaderWidth :
|
|
24713
|
+
localLeft: rect.left - (isFrozen ? displayRowHeaderWidth : scrollBodyCanvasLeft),
|
|
24070
24714
|
width: rect.width
|
|
24071
24715
|
}];
|
|
24072
24716
|
}),
|
|
@@ -24074,8 +24718,8 @@ function XlsxGrid({
|
|
|
24074
24718
|
canvasVisibleColItems,
|
|
24075
24719
|
displayHeaderHeight,
|
|
24076
24720
|
displayRowHeaderWidth,
|
|
24077
|
-
frozenPaneRight,
|
|
24078
24721
|
resolveCanvasColumnHeaderRect,
|
|
24722
|
+
scrollBodyCanvasLeft,
|
|
24079
24723
|
stickyLeftByCol
|
|
24080
24724
|
]
|
|
24081
24725
|
);
|
|
@@ -24090,15 +24734,15 @@ function XlsxGrid({
|
|
|
24090
24734
|
actualRow: row.actualRow,
|
|
24091
24735
|
height: rect.height,
|
|
24092
24736
|
isFrozen,
|
|
24093
|
-
localTop: rect.top - (isFrozen ? displayHeaderHeight :
|
|
24737
|
+
localTop: rect.top - (isFrozen ? displayHeaderHeight : scrollBodyCanvasTop),
|
|
24094
24738
|
top: rect.top
|
|
24095
24739
|
}];
|
|
24096
24740
|
}),
|
|
24097
24741
|
[
|
|
24098
24742
|
canvasVisibleRowItems,
|
|
24099
24743
|
displayHeaderHeight,
|
|
24100
|
-
frozenPaneBottom,
|
|
24101
24744
|
resolveCanvasRowHeaderRect,
|
|
24745
|
+
scrollBodyCanvasTop,
|
|
24102
24746
|
stickyTopByRow
|
|
24103
24747
|
]
|
|
24104
24748
|
);
|
|
@@ -24365,10 +25009,46 @@ function XlsxGrid({
|
|
|
24365
25009
|
rowPrefixSums,
|
|
24366
25010
|
startCellSelection
|
|
24367
25011
|
]);
|
|
25012
|
+
const getCanvasImage = React4.useCallback((image) => {
|
|
25013
|
+
if (typeof Image === "undefined") {
|
|
25014
|
+
return null;
|
|
25015
|
+
}
|
|
25016
|
+
const cacheKey = image.id;
|
|
25017
|
+
const cached = canvasImageCacheRef.current.get(cacheKey);
|
|
25018
|
+
if (cached && cached.src === image.src) {
|
|
25019
|
+
return cached.loaded && !cached.failed ? cached.image : null;
|
|
25020
|
+
}
|
|
25021
|
+
const imageElement = new Image();
|
|
25022
|
+
const entry = {
|
|
25023
|
+
failed: false,
|
|
25024
|
+
image: imageElement,
|
|
25025
|
+
loaded: false,
|
|
25026
|
+
src: image.src
|
|
25027
|
+
};
|
|
25028
|
+
canvasImageCacheRef.current.set(cacheKey, entry);
|
|
25029
|
+
imageElement.onload = () => {
|
|
25030
|
+
entry.loaded = true;
|
|
25031
|
+
setCanvasImageLoadVersion((version) => version + 1);
|
|
25032
|
+
};
|
|
25033
|
+
imageElement.onerror = () => {
|
|
25034
|
+
entry.failed = true;
|
|
25035
|
+
setCanvasImageLoadVersion((version) => version + 1);
|
|
25036
|
+
};
|
|
25037
|
+
imageElement.src = image.src;
|
|
25038
|
+
return null;
|
|
25039
|
+
}, []);
|
|
24368
25040
|
React4.useLayoutEffect(() => {
|
|
24369
25041
|
if (!experimentalCanvas) {
|
|
24370
25042
|
return;
|
|
24371
25043
|
}
|
|
25044
|
+
const canvasProfileTarget = typeof window !== "undefined" ? window.__xlsxCanvasProfile : void 0;
|
|
25045
|
+
const canvasProfileStart = canvasProfileTarget ? performance.now() : 0;
|
|
25046
|
+
let canvasProfileBodyStart = 0;
|
|
25047
|
+
let canvasProfileBodyMs = 0;
|
|
25048
|
+
let canvasProfileCulledCells = 0;
|
|
25049
|
+
let canvasProfileDirtyRects = 0;
|
|
25050
|
+
let canvasProfileLookedUpCells = 0;
|
|
25051
|
+
let canvasProfilePaintedCells = 0;
|
|
24372
25052
|
const dpr = typeof window === "undefined" ? 1 : Math.max(1, window.devicePixelRatio || 1);
|
|
24373
25053
|
function configureCanvas(canvas, width, height, options) {
|
|
24374
25054
|
if (!canvas) {
|
|
@@ -24388,7 +25068,7 @@ function XlsxGrid({
|
|
|
24388
25068
|
if (canvas.style.height !== `${height}px`) {
|
|
24389
25069
|
canvas.style.height = `${height}px`;
|
|
24390
25070
|
}
|
|
24391
|
-
const context = canvas.getContext("2d");
|
|
25071
|
+
const context = canvas.getContext("2d", { alpha: false });
|
|
24392
25072
|
if (!context) {
|
|
24393
25073
|
return null;
|
|
24394
25074
|
}
|
|
@@ -24407,6 +25087,7 @@ function XlsxGrid({
|
|
|
24407
25087
|
const rangeSignature = buildRangeSignature(normalizedVisibleRange);
|
|
24408
25088
|
const nextBodyCanvasSignature = {
|
|
24409
25089
|
activeSheet: activeSheet ?? null,
|
|
25090
|
+
bakedDrawingSignature: bakedCanvasDrawingSignature,
|
|
24410
25091
|
bodyHeight,
|
|
24411
25092
|
bodyWidth,
|
|
24412
25093
|
colSignature: buildRenderedAxisSignature(canvasVisibleColItems, (item) => item.index),
|
|
@@ -24434,10 +25115,10 @@ function XlsxGrid({
|
|
|
24434
25115
|
const previousBodyCanvasSignature = paintedBodyCanvasSignatureRef.current;
|
|
24435
25116
|
const previousHeaderCanvasSignature = paintedHeaderCanvasSignatureRef.current;
|
|
24436
25117
|
const previousPaintedViewport = paintedDrawingViewportRef.current;
|
|
24437
|
-
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);
|
|
25118
|
+
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);
|
|
24438
25119
|
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);
|
|
24439
25120
|
const canBlitBody = Boolean(
|
|
24440
|
-
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
|
|
25121
|
+
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
|
|
24441
25122
|
);
|
|
24442
25123
|
const canBlitTopHeader = Boolean(
|
|
24443
25124
|
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
|
|
@@ -24448,49 +25129,37 @@ function XlsxGrid({
|
|
|
24448
25129
|
if (!shouldRepaintBody && !shouldRepaintHeaders) {
|
|
24449
25130
|
return;
|
|
24450
25131
|
}
|
|
24451
|
-
const scrollBodyCanvasWidth2 = Math.max(0, drawingViewport.width - frozenPaneRight);
|
|
24452
|
-
const scrollBodyCanvasHeight2 = Math.max(0, drawingViewport.height - frozenPaneBottom);
|
|
24453
|
-
const topBodyCanvasWidth2 = scrollBodyCanvasWidth2;
|
|
24454
|
-
const topBodyCanvasHeight2 = Math.max(0, frozenPaneBottom - displayHeaderHeight);
|
|
24455
|
-
const leftBodyCanvasWidth2 = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
|
|
24456
|
-
const leftBodyCanvasHeight2 = scrollBodyCanvasHeight2;
|
|
24457
|
-
const cornerBodyCanvasWidth2 = leftBodyCanvasWidth2;
|
|
24458
|
-
const cornerBodyCanvasHeight2 = topBodyCanvasHeight2;
|
|
24459
|
-
const topFrozenHeaderCanvasWidth2 = leftBodyCanvasWidth2;
|
|
24460
|
-
const topScrollHeaderCanvasWidth2 = scrollBodyCanvasWidth2;
|
|
24461
|
-
const leftFrozenHeaderCanvasHeight2 = topBodyCanvasHeight2;
|
|
24462
|
-
const leftScrollHeaderCanvasHeight2 = scrollBodyCanvasHeight2;
|
|
24463
25132
|
const paneBounds = {
|
|
24464
25133
|
corner: {
|
|
24465
|
-
height:
|
|
25134
|
+
height: cornerBodyCanvasHeight,
|
|
24466
25135
|
left: displayRowHeaderWidth,
|
|
24467
25136
|
top: displayHeaderHeight,
|
|
24468
|
-
width:
|
|
25137
|
+
width: cornerBodyCanvasWidth
|
|
24469
25138
|
},
|
|
24470
25139
|
left: {
|
|
24471
|
-
height:
|
|
25140
|
+
height: leftBodyCanvasHeight,
|
|
24472
25141
|
left: displayRowHeaderWidth,
|
|
24473
|
-
top:
|
|
24474
|
-
width:
|
|
25142
|
+
top: scrollBodyCanvasTop,
|
|
25143
|
+
width: leftBodyCanvasWidth
|
|
24475
25144
|
},
|
|
24476
25145
|
scroll: {
|
|
24477
|
-
height:
|
|
24478
|
-
left:
|
|
24479
|
-
top:
|
|
24480
|
-
width:
|
|
25146
|
+
height: scrollBodyCanvasHeight,
|
|
25147
|
+
left: scrollBodyCanvasLeft,
|
|
25148
|
+
top: scrollBodyCanvasTop,
|
|
25149
|
+
width: scrollBodyCanvasWidth
|
|
24481
25150
|
},
|
|
24482
25151
|
top: {
|
|
24483
|
-
height:
|
|
24484
|
-
left:
|
|
25152
|
+
height: topBodyCanvasHeight,
|
|
25153
|
+
left: scrollBodyCanvasLeft,
|
|
24485
25154
|
top: displayHeaderHeight,
|
|
24486
|
-
width:
|
|
25155
|
+
width: topBodyCanvasWidth
|
|
24487
25156
|
}
|
|
24488
25157
|
};
|
|
24489
25158
|
const bodyContexts = shouldRepaintBody ? {
|
|
24490
|
-
corner: configureCanvas(cornerBodyCanvasRef.current,
|
|
24491
|
-
left: configureCanvas(leftBodyCanvasRef.current,
|
|
24492
|
-
scroll: configureCanvas(scrollBodyCanvasRef.current,
|
|
24493
|
-
top: configureCanvas(topBodyCanvasRef.current,
|
|
25159
|
+
corner: configureCanvas(cornerBodyCanvasRef.current, cornerBodyCanvasWidth, cornerBodyCanvasHeight, { clear: !canBlitBody }),
|
|
25160
|
+
left: configureCanvas(leftBodyCanvasRef.current, leftBodyCanvasWidth, leftBodyCanvasHeight, { clear: !canBlitBody }),
|
|
25161
|
+
scroll: configureCanvas(scrollBodyCanvasRef.current, scrollBodyCanvasWidth, scrollBodyCanvasHeight, { clear: !canBlitBody }),
|
|
25162
|
+
top: configureCanvas(topBodyCanvasRef.current, topBodyCanvasWidth, topBodyCanvasHeight, { clear: !canBlitBody })
|
|
24494
25163
|
} : {
|
|
24495
25164
|
corner: null,
|
|
24496
25165
|
left: null,
|
|
@@ -24498,21 +25167,21 @@ function XlsxGrid({
|
|
|
24498
25167
|
top: null
|
|
24499
25168
|
};
|
|
24500
25169
|
const topHeaderContexts = shouldRepaintHeaders ? {
|
|
24501
|
-
frozen: configureCanvas(topFrozenHeaderCanvasRef.current,
|
|
24502
|
-
scroll: configureCanvas(topScrollHeaderCanvasRef.current,
|
|
25170
|
+
frozen: configureCanvas(topFrozenHeaderCanvasRef.current, topFrozenHeaderCanvasWidth, headerHeight),
|
|
25171
|
+
scroll: configureCanvas(topScrollHeaderCanvasRef.current, topScrollHeaderCanvasWidth, headerHeight, { clear: !canBlitTopHeader })
|
|
24503
25172
|
} : {
|
|
24504
25173
|
frozen: null,
|
|
24505
25174
|
scroll: null
|
|
24506
25175
|
};
|
|
24507
25176
|
const leftHeaderContexts = shouldRepaintHeaders ? {
|
|
24508
|
-
frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth,
|
|
24509
|
-
scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth,
|
|
25177
|
+
frozen: configureCanvas(leftFrozenHeaderCanvasRef.current, rowHeaderWidth, leftFrozenHeaderCanvasHeight),
|
|
25178
|
+
scroll: configureCanvas(leftScrollHeaderCanvasRef.current, rowHeaderWidth, leftScrollHeaderCanvasHeight, { clear: !canBlitLeftHeader })
|
|
24510
25179
|
} : {
|
|
24511
25180
|
frozen: null,
|
|
24512
25181
|
scroll: null
|
|
24513
25182
|
};
|
|
24514
25183
|
const cornerContext = shouldRepaintHeaders ? configureCanvas(cornerHeaderCanvasRef.current, rowHeaderWidth, headerHeight) : null;
|
|
24515
|
-
if (shouldRepaintBody && (!bodyContexts.scroll || !bodyContexts.top || !bodyContexts.left || !bodyContexts.corner) || shouldRepaintHeaders && (!cornerContext ||
|
|
25184
|
+
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)) {
|
|
24516
25185
|
return;
|
|
24517
25186
|
}
|
|
24518
25187
|
const showGridLines = activeSheet?.showGridLines ?? true;
|
|
@@ -24529,10 +25198,282 @@ function XlsxGrid({
|
|
|
24529
25198
|
scroll: [],
|
|
24530
25199
|
top: []
|
|
24531
25200
|
};
|
|
24532
|
-
let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(
|
|
24533
|
-
let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth,
|
|
25201
|
+
let topScrollHeaderDirtyRects = buildFullCanvasDirtyRect(topScrollHeaderCanvasWidth, headerHeight);
|
|
25202
|
+
let leftScrollHeaderDirtyRects = buildFullCanvasDirtyRect(rowHeaderWidth, leftScrollHeaderCanvasHeight);
|
|
25203
|
+
const bakedCanvasDrawingEntries = shouldBakeCanvasStaticDrawings ? [
|
|
25204
|
+
...bakedShapeRects.map(({ rect, shape }) => ({
|
|
25205
|
+
kind: "shape",
|
|
25206
|
+
rect,
|
|
25207
|
+
shape,
|
|
25208
|
+
zIndex: shape.zIndex
|
|
25209
|
+
})),
|
|
25210
|
+
...bakedFormControlRects.map(({ control, rect }) => ({
|
|
25211
|
+
control,
|
|
25212
|
+
kind: "formControl",
|
|
25213
|
+
rect,
|
|
25214
|
+
zIndex: control.zIndex
|
|
25215
|
+
})),
|
|
25216
|
+
...bakedImageRects.map(({ image, rect }) => ({
|
|
25217
|
+
image,
|
|
25218
|
+
kind: "image",
|
|
25219
|
+
rect,
|
|
25220
|
+
zIndex: image.zIndex
|
|
25221
|
+
}))
|
|
25222
|
+
].sort((left, right) => left.zIndex - right.zIndex) : [];
|
|
25223
|
+
const resolveBakedCanvasLocalRect = (rect, pane) => {
|
|
25224
|
+
const bounds = paneBounds[pane];
|
|
25225
|
+
const scrollX = pane === "scroll" || pane === "top" ? drawingViewport.left : 0;
|
|
25226
|
+
const scrollY = pane === "scroll" || pane === "left" ? drawingViewport.top : 0;
|
|
25227
|
+
return {
|
|
25228
|
+
height: rect.height,
|
|
25229
|
+
left: rect.left - scrollX - bounds.left,
|
|
25230
|
+
top: rect.top - scrollY - bounds.top,
|
|
25231
|
+
width: rect.width
|
|
25232
|
+
};
|
|
25233
|
+
};
|
|
25234
|
+
const drawBakedShapeText = (context, shape, left, top, width, height) => {
|
|
25235
|
+
if (shape.paragraphs.length === 0) {
|
|
25236
|
+
return;
|
|
25237
|
+
}
|
|
25238
|
+
const inset = shape.textBox?.insetPx;
|
|
25239
|
+
const paddingLeft = (inset?.left ?? 6) * zoomFactor;
|
|
25240
|
+
const paddingRight = (inset?.right ?? 6) * zoomFactor;
|
|
25241
|
+
const paddingTop = (inset?.top ?? 4) * zoomFactor;
|
|
25242
|
+
const paddingBottom = (inset?.bottom ?? 4) * zoomFactor;
|
|
25243
|
+
const textLeft = left + paddingLeft;
|
|
25244
|
+
const textTop = top + paddingTop;
|
|
25245
|
+
const textWidth = Math.max(0, width - paddingLeft - paddingRight);
|
|
25246
|
+
const textHeight = Math.max(0, height - paddingTop - paddingBottom);
|
|
25247
|
+
if (textWidth <= 0 || textHeight <= 0) {
|
|
25248
|
+
return;
|
|
25249
|
+
}
|
|
25250
|
+
const lineMetrics = shape.paragraphs.map((paragraph) => {
|
|
25251
|
+
const lineHeight = Math.max(
|
|
25252
|
+
12 * zoomFactor,
|
|
25253
|
+
...paragraph.runs.map((run) => (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor * 1.2)
|
|
25254
|
+
);
|
|
25255
|
+
const widthPx = paragraph.runs.reduce((total, run) => {
|
|
25256
|
+
const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
|
|
25257
|
+
context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
|
|
25258
|
+
return total + measureCanvasTextWidth(context, run.text);
|
|
25259
|
+
}, 0);
|
|
25260
|
+
return { lineHeight, widthPx };
|
|
25261
|
+
});
|
|
25262
|
+
const totalTextHeight = lineMetrics.reduce((total, metric) => total + metric.lineHeight, 0);
|
|
25263
|
+
let y = textTop;
|
|
25264
|
+
if (shape.textBox?.verticalAlign === "middle") {
|
|
25265
|
+
y = textTop + Math.max(0, (textHeight - totalTextHeight) / 2);
|
|
25266
|
+
} else if (shape.textBox?.verticalAlign === "bottom") {
|
|
25267
|
+
y = textTop + Math.max(0, textHeight - totalTextHeight);
|
|
25268
|
+
}
|
|
25269
|
+
context.save();
|
|
25270
|
+
context.beginPath();
|
|
25271
|
+
context.rect(textLeft, textTop, textWidth, textHeight);
|
|
25272
|
+
context.clip();
|
|
25273
|
+
context.textBaseline = "middle";
|
|
25274
|
+
shape.paragraphs.forEach((paragraph, paragraphIndex) => {
|
|
25275
|
+
const metric = lineMetrics[paragraphIndex] ?? { lineHeight: 14 * zoomFactor, widthPx: 0 };
|
|
25276
|
+
let x = textLeft;
|
|
25277
|
+
const align = paragraph.align ?? shape.textBox?.horizontalAlign ?? "left";
|
|
25278
|
+
if (align === "center") {
|
|
25279
|
+
x = textLeft + Math.max(0, (textWidth - metric.widthPx) / 2);
|
|
25280
|
+
} else if (align === "right") {
|
|
25281
|
+
x = textLeft + Math.max(0, textWidth - metric.widthPx);
|
|
25282
|
+
}
|
|
25283
|
+
paragraph.runs.forEach((run) => {
|
|
25284
|
+
const fontSize = (run.fontSizePt ?? 11) * 96 / 72 * zoomFactor;
|
|
25285
|
+
context.font = `${run.italic ? "italic " : ""}${run.bold ? "700 " : "400 "}${fontSize}px ${run.fontFamily ?? "Calibri, sans-serif"}`;
|
|
25286
|
+
context.fillStyle = run.color ?? "#000000";
|
|
25287
|
+
context.textAlign = "left";
|
|
25288
|
+
const textY = y + metric.lineHeight / 2;
|
|
25289
|
+
context.fillText(run.text, x, textY);
|
|
25290
|
+
if (run.underline && run.text.length > 0) {
|
|
25291
|
+
const textWidthPx = measureCanvasTextWidth(context, run.text);
|
|
25292
|
+
context.strokeStyle = run.color ?? "#000000";
|
|
25293
|
+
context.lineWidth = Math.max(1, zoomFactor * 0.75);
|
|
25294
|
+
context.beginPath();
|
|
25295
|
+
context.moveTo(x, textY + Math.max(2, fontSize * 0.28));
|
|
25296
|
+
context.lineTo(x + textWidthPx, textY + Math.max(2, fontSize * 0.28));
|
|
25297
|
+
context.stroke();
|
|
25298
|
+
}
|
|
25299
|
+
x += measureCanvasTextWidth(context, run.text);
|
|
25300
|
+
});
|
|
25301
|
+
y += metric.lineHeight;
|
|
25302
|
+
});
|
|
25303
|
+
context.restore();
|
|
25304
|
+
};
|
|
25305
|
+
const drawBakedShape = (context, shape, rect) => {
|
|
25306
|
+
const fillColor = shape.fill?.none ? "transparent" : shape.fill?.color ?? "transparent";
|
|
25307
|
+
const strokeColor = shape.stroke?.none ? "transparent" : shape.stroke?.color ?? "transparent";
|
|
25308
|
+
const lineWidth = Math.max(0, (shape.stroke?.widthPx ?? (shape.geometry === "line" ? 2 : 1)) * zoomFactor);
|
|
25309
|
+
const vectorShape = resolveShapeVector(shape);
|
|
25310
|
+
const opacity = Math.min(shape.fill?.opacity ?? 1, shape.stroke?.opacity ?? 1);
|
|
25311
|
+
context.save();
|
|
25312
|
+
context.translate(rect.left + rect.width / 2, rect.top + rect.height / 2);
|
|
25313
|
+
if (shape.rotationDeg) {
|
|
25314
|
+
context.rotate(shape.rotationDeg * Math.PI / 180);
|
|
25315
|
+
}
|
|
25316
|
+
context.scale(shape.flipH ? -1 : 1, shape.flipV ? -1 : 1);
|
|
25317
|
+
context.globalAlpha *= opacity;
|
|
25318
|
+
context.lineWidth = lineWidth;
|
|
25319
|
+
context.strokeStyle = strokeColor;
|
|
25320
|
+
context.fillStyle = fillColor;
|
|
25321
|
+
applyCanvasShapeDash(context, shape.stroke?.dash, lineWidth);
|
|
25322
|
+
const localLeft = -rect.width / 2;
|
|
25323
|
+
const localTop = -rect.height / 2;
|
|
25324
|
+
if (vectorShape && typeof Path2D !== "undefined") {
|
|
25325
|
+
context.save();
|
|
25326
|
+
context.translate(localLeft, localTop);
|
|
25327
|
+
context.scale(
|
|
25328
|
+
rect.width / Math.max(1, vectorShape.viewBox.width),
|
|
25329
|
+
rect.height / Math.max(1, vectorShape.viewBox.height)
|
|
25330
|
+
);
|
|
25331
|
+
const path = getCachedCanvasPath2D(vectorShape.path);
|
|
25332
|
+
if (!path) {
|
|
25333
|
+
context.restore();
|
|
25334
|
+
context.restore();
|
|
25335
|
+
return;
|
|
25336
|
+
}
|
|
25337
|
+
if (fillColor !== "transparent") {
|
|
25338
|
+
context.fill(path);
|
|
25339
|
+
}
|
|
25340
|
+
if (strokeColor !== "transparent" && lineWidth > 0) {
|
|
25341
|
+
context.stroke(path);
|
|
25342
|
+
}
|
|
25343
|
+
context.restore();
|
|
25344
|
+
} else if (shape.geometry === "ellipse") {
|
|
25345
|
+
context.beginPath();
|
|
25346
|
+
context.ellipse(0, 0, rect.width / 2, rect.height / 2, 0, 0, Math.PI * 2);
|
|
25347
|
+
if (fillColor !== "transparent") {
|
|
25348
|
+
context.fill();
|
|
25349
|
+
}
|
|
25350
|
+
if (strokeColor !== "transparent" && lineWidth > 0) {
|
|
25351
|
+
context.stroke();
|
|
25352
|
+
}
|
|
25353
|
+
} else {
|
|
25354
|
+
const radius = shape.geometry === "roundRect" ? 12 * zoomFactor : 0;
|
|
25355
|
+
drawCanvasRoundedRect(context, localLeft, localTop, rect.width, rect.height, radius);
|
|
25356
|
+
if (fillColor !== "transparent") {
|
|
25357
|
+
context.fill();
|
|
25358
|
+
}
|
|
25359
|
+
if (strokeColor !== "transparent" && lineWidth > 0) {
|
|
25360
|
+
context.stroke();
|
|
25361
|
+
}
|
|
25362
|
+
}
|
|
25363
|
+
drawBakedShapeText(context, shape, localLeft, localTop, rect.width, rect.height);
|
|
25364
|
+
context.restore();
|
|
25365
|
+
};
|
|
25366
|
+
const drawBakedFormControl = (context, control, rect) => {
|
|
25367
|
+
const label = resolveFormControlLabel(control);
|
|
25368
|
+
const stroke = paletteIsDark(palette) ? "#cbd5e1" : "#475569";
|
|
25369
|
+
const textColor = control.textColor ?? "#000000";
|
|
25370
|
+
const fontSizePx = Math.max(9 * zoomFactor, (control.fontSizePt ?? 9) * 96 / 72 * zoomFactor);
|
|
25371
|
+
const iconSize = Math.min(14 * zoomFactor, Math.max(0, rect.height - 4 * zoomFactor));
|
|
25372
|
+
context.save();
|
|
25373
|
+
context.font = `400 ${fontSizePx}px ${control.fontFamily ?? "Calibri, sans-serif"}`;
|
|
25374
|
+
context.textBaseline = "middle";
|
|
25375
|
+
context.fillStyle = textColor;
|
|
25376
|
+
context.strokeStyle = stroke;
|
|
25377
|
+
context.lineWidth = Math.max(1, zoomFactor);
|
|
25378
|
+
if (control.kind === "group-box") {
|
|
25379
|
+
const labelInset = label ? Math.max(7, fontSizePx * 0.5) : 0;
|
|
25380
|
+
drawCanvasRoundedRect(context, rect.left, rect.top + labelInset, rect.width, Math.max(0, rect.height - labelInset), 2 * zoomFactor);
|
|
25381
|
+
context.stroke();
|
|
25382
|
+
if (label) {
|
|
25383
|
+
context.fillStyle = SHEET_SURFACE;
|
|
25384
|
+
const labelWidth = Math.min(rect.width - 16 * zoomFactor, measureCanvasTextWidth(context, label) + 8 * zoomFactor);
|
|
25385
|
+
context.fillRect(rect.left + 8 * zoomFactor, rect.top, labelWidth, fontSizePx * 1.2);
|
|
25386
|
+
context.fillStyle = textColor;
|
|
25387
|
+
context.textAlign = "left";
|
|
25388
|
+
context.fillText(label, rect.left + 12 * zoomFactor, rect.top + fontSizePx * 0.55);
|
|
25389
|
+
}
|
|
25390
|
+
context.restore();
|
|
25391
|
+
return;
|
|
25392
|
+
}
|
|
25393
|
+
if (control.kind === "button" || control.kind === "dropdown" || control.kind === "editbox" || control.kind === "listbox" || control.kind === "scrollbar" || control.kind === "spinner" || control.kind === "unknown") {
|
|
25394
|
+
if (control.kind === "button") {
|
|
25395
|
+
const gradient = context.createLinearGradient(rect.left, rect.top, rect.left, rect.top + rect.height);
|
|
25396
|
+
gradient.addColorStop(0, "#f8fafc");
|
|
25397
|
+
gradient.addColorStop(1, "#e2e8f0");
|
|
25398
|
+
context.fillStyle = gradient;
|
|
25399
|
+
} else {
|
|
25400
|
+
context.fillStyle = "transparent";
|
|
25401
|
+
}
|
|
25402
|
+
drawCanvasRoundedRect(context, rect.left, rect.top, rect.width, rect.height, control.kind === "button" ? 4 * zoomFactor : 2 * zoomFactor);
|
|
25403
|
+
if (control.kind === "button") {
|
|
25404
|
+
context.fill();
|
|
25405
|
+
}
|
|
25406
|
+
context.stroke();
|
|
25407
|
+
}
|
|
25408
|
+
let textLeft = rect.left + 2 * zoomFactor;
|
|
25409
|
+
const textRightInset = control.kind === "dropdown" ? 18 * zoomFactor : 6 * zoomFactor;
|
|
25410
|
+
if (control.kind === "checkbox") {
|
|
25411
|
+
context.strokeRect(rect.left + 2 * zoomFactor, rect.top + (rect.height - iconSize) / 2, iconSize, iconSize);
|
|
25412
|
+
if (control.checked) {
|
|
25413
|
+
context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
|
|
25414
|
+
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));
|
|
25415
|
+
context.strokeStyle = paletteIsDark(palette) ? "#020617" : "#ffffff";
|
|
25416
|
+
context.beginPath();
|
|
25417
|
+
context.moveTo(rect.left + 2 * zoomFactor + iconSize * 0.24, rect.top + rect.height / 2 + iconSize * 0.06);
|
|
25418
|
+
context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.45, rect.top + rect.height / 2 + iconSize * 0.26);
|
|
25419
|
+
context.lineTo(rect.left + 2 * zoomFactor + iconSize * 0.8, rect.top + rect.height / 2 - iconSize * 0.2);
|
|
25420
|
+
context.stroke();
|
|
25421
|
+
}
|
|
25422
|
+
textLeft += iconSize + 4 * zoomFactor;
|
|
25423
|
+
} else if (control.kind === "radio") {
|
|
25424
|
+
context.beginPath();
|
|
25425
|
+
context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize / 2, 0, Math.PI * 2);
|
|
25426
|
+
context.stroke();
|
|
25427
|
+
if (control.checked) {
|
|
25428
|
+
context.fillStyle = paletteIsDark(palette) ? "#60a5fa" : "#2563eb";
|
|
25429
|
+
context.beginPath();
|
|
25430
|
+
context.arc(rect.left + 2 * zoomFactor + iconSize / 2, rect.top + rect.height / 2, iconSize * 0.25, 0, Math.PI * 2);
|
|
25431
|
+
context.fill();
|
|
25432
|
+
}
|
|
25433
|
+
textLeft += iconSize + 4 * zoomFactor;
|
|
25434
|
+
}
|
|
25435
|
+
if (label) {
|
|
25436
|
+
const maxTextWidth = Math.max(0, rect.left + rect.width - textLeft - textRightInset);
|
|
25437
|
+
const text = truncateCanvasText(context, label, maxTextWidth);
|
|
25438
|
+
context.fillStyle = textColor;
|
|
25439
|
+
context.textAlign = control.textAlign === "right" ? "right" : control.textAlign === "center" ? "center" : "left";
|
|
25440
|
+
const textX = context.textAlign === "right" ? rect.left + rect.width - textRightInset : context.textAlign === "center" ? textLeft + maxTextWidth / 2 : textLeft;
|
|
25441
|
+
context.fillText(text, textX, rect.top + rect.height / 2);
|
|
25442
|
+
}
|
|
25443
|
+
if (control.kind === "dropdown") {
|
|
25444
|
+
context.fillStyle = textColor;
|
|
25445
|
+
context.textAlign = "center";
|
|
25446
|
+
context.fillText("\u25BC", rect.left + rect.width - 10 * zoomFactor, rect.top + rect.height / 2);
|
|
25447
|
+
}
|
|
25448
|
+
context.restore();
|
|
25449
|
+
};
|
|
25450
|
+
const drawBakedCanvasDrawings = (pane, context, dirtyRects) => {
|
|
25451
|
+
if (bakedCanvasDrawingEntries.length === 0 || dirtyRects.length === 0) {
|
|
25452
|
+
return;
|
|
25453
|
+
}
|
|
25454
|
+
for (const entry of bakedCanvasDrawingEntries) {
|
|
25455
|
+
if (resolveDrawingPane(entry.rect) !== pane) {
|
|
25456
|
+
continue;
|
|
25457
|
+
}
|
|
25458
|
+
const localRect = resolveBakedCanvasLocalRect(entry.rect, pane);
|
|
25459
|
+
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)) {
|
|
25460
|
+
continue;
|
|
25461
|
+
}
|
|
25462
|
+
if (entry.kind === "shape") {
|
|
25463
|
+
drawBakedShape(context, entry.shape, localRect);
|
|
25464
|
+
} else if (entry.kind === "formControl") {
|
|
25465
|
+
drawBakedFormControl(context, entry.control, localRect);
|
|
25466
|
+
} else {
|
|
25467
|
+
const imageElement = getCanvasImage(entry.image);
|
|
25468
|
+
if (imageElement) {
|
|
25469
|
+
context.drawImage(imageElement, localRect.left, localRect.top, localRect.width, localRect.height);
|
|
25470
|
+
}
|
|
25471
|
+
}
|
|
25472
|
+
}
|
|
25473
|
+
};
|
|
24534
25474
|
const cellPaneOrder = ["scroll", "top", "left", "corner"];
|
|
24535
25475
|
if (shouldRepaintBody) {
|
|
25476
|
+
canvasProfileBodyStart = canvasProfileTarget ? performance.now() : 0;
|
|
24536
25477
|
for (const pane of Object.keys(bodyContexts)) {
|
|
24537
25478
|
const context = bodyContexts[pane];
|
|
24538
25479
|
const bounds = paneBounds[pane];
|
|
@@ -24563,6 +25504,7 @@ function XlsxGrid({
|
|
|
24563
25504
|
context.clearRect(0, 0, bounds.width, bounds.height);
|
|
24564
25505
|
}
|
|
24565
25506
|
bodyDirtyRectsByPane[pane] = dirtyRects;
|
|
25507
|
+
canvasProfileDirtyRects += dirtyRects.length;
|
|
24566
25508
|
if (dirtyRects.length === 0) {
|
|
24567
25509
|
continue;
|
|
24568
25510
|
}
|
|
@@ -24579,6 +25521,22 @@ function XlsxGrid({
|
|
|
24579
25521
|
if (!paneContext || paneDirtyRects.length === 0 || paneBoundsForCell.width <= 0 || paneBoundsForCell.height <= 0 || paneAxisItems.rows.length === 0 || paneAxisItems.cols.length === 0) {
|
|
24580
25522
|
continue;
|
|
24581
25523
|
}
|
|
25524
|
+
let hasPendingGridlinePath = false;
|
|
25525
|
+
const enqueueGridlinePath = () => {
|
|
25526
|
+
if (!hasPendingGridlinePath) {
|
|
25527
|
+
paneContext.beginPath();
|
|
25528
|
+
hasPendingGridlinePath = true;
|
|
25529
|
+
}
|
|
25530
|
+
};
|
|
25531
|
+
const flushPendingGridlines = () => {
|
|
25532
|
+
if (!hasPendingGridlinePath) {
|
|
25533
|
+
return;
|
|
25534
|
+
}
|
|
25535
|
+
paneContext.strokeStyle = palette.border;
|
|
25536
|
+
paneContext.lineWidth = 1;
|
|
25537
|
+
paneContext.stroke();
|
|
25538
|
+
hasPendingGridlinePath = false;
|
|
25539
|
+
};
|
|
24582
25540
|
const drawnMergedAnchorKeys = /* @__PURE__ */ new Set();
|
|
24583
25541
|
for (const rowItem of paneAxisItems.rows) {
|
|
24584
25542
|
for (const colItem of paneAxisItems.cols) {
|
|
@@ -24586,6 +25544,33 @@ function XlsxGrid({
|
|
|
24586
25544
|
const anchorCell = resolveMergeAnchorCell(cell);
|
|
24587
25545
|
const anchorKey = `${anchorCell.row}:${anchorCell.col}`;
|
|
24588
25546
|
let drawCell = anchorCell;
|
|
25547
|
+
const drawRowIndex = rowIndexByActual.get(drawCell.row);
|
|
25548
|
+
const drawColIndex = colIndexByActual.get(drawCell.col);
|
|
25549
|
+
if (drawRowIndex === void 0 || drawColIndex === void 0) {
|
|
25550
|
+
continue;
|
|
25551
|
+
}
|
|
25552
|
+
const baseCellLeft = displayRowHeaderWidth + (colPrefixSums[drawColIndex] ?? 0);
|
|
25553
|
+
const baseCellTop = displayHeaderHeight + (rowPrefixSums[drawRowIndex] ?? 0);
|
|
25554
|
+
const useFrozenHorizontalPosition = pane === "left" || pane === "corner";
|
|
25555
|
+
const useFrozenVerticalPosition = pane === "top" || pane === "corner";
|
|
25556
|
+
const roughLocalRect = {
|
|
25557
|
+
height: displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size,
|
|
25558
|
+
left: (useFrozenHorizontalPosition ? stickyLeftByCol.get(drawCell.col) ?? baseCellLeft - drawingViewport.left : baseCellLeft - drawingViewport.left) - paneBoundsForCell.left,
|
|
25559
|
+
top: (useFrozenVerticalPosition ? stickyTopByRow.get(drawCell.row) ?? baseCellTop - drawingViewport.top : baseCellTop - drawingViewport.top) - paneBoundsForCell.top,
|
|
25560
|
+
width: displayEffectiveColWidths[drawColIndex] ?? colItem.size
|
|
25561
|
+
};
|
|
25562
|
+
const isMergedSecondaryProbe = anchorCell.row !== cell.row || anchorCell.col !== cell.col;
|
|
25563
|
+
if (!isMergedSecondaryProbe && !intersectsCanvasDirtyRects(
|
|
25564
|
+
roughLocalRect.left - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,
|
|
25565
|
+
roughLocalRect.top - CANVAS_DIRTY_CELL_CULL_MARGIN_PX,
|
|
25566
|
+
roughLocalRect.width + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,
|
|
25567
|
+
roughLocalRect.height + CANVAS_DIRTY_CELL_CULL_MARGIN_PX * 2,
|
|
25568
|
+
paneDirtyRects
|
|
25569
|
+
)) {
|
|
25570
|
+
canvasProfileCulledCells += 1;
|
|
25571
|
+
continue;
|
|
25572
|
+
}
|
|
25573
|
+
canvasProfileLookedUpCells += 1;
|
|
24589
25574
|
let cellData = getCellData(drawCell.row, drawCell.col);
|
|
24590
25575
|
if ((cellData.colSpan || cellData.rowSpan) && drawnMergedAnchorKeys.has(anchorKey)) {
|
|
24591
25576
|
continue;
|
|
@@ -24596,16 +25581,9 @@ function XlsxGrid({
|
|
|
24596
25581
|
if (cellData.colSpan || cellData.rowSpan) {
|
|
24597
25582
|
drawnMergedAnchorKeys.add(anchorKey);
|
|
24598
25583
|
}
|
|
24599
|
-
const drawRowIndex = rowIndexByActual.get(drawCell.row);
|
|
24600
|
-
const drawColIndex = colIndexByActual.get(drawCell.col);
|
|
24601
|
-
if (drawRowIndex === void 0 || drawColIndex === void 0) {
|
|
24602
|
-
continue;
|
|
24603
|
-
}
|
|
24604
25584
|
const displayRect = cellData.colSpan || cellData.rowSpan ? resolveCellDisplayRect(drawCell) : null;
|
|
24605
|
-
const baseLeft = displayRect?.left ??
|
|
24606
|
-
const baseTop = displayRect?.top ??
|
|
24607
|
-
const useFrozenHorizontalPosition = pane === "left" || pane === "corner";
|
|
24608
|
-
const useFrozenVerticalPosition = pane === "top" || pane === "corner";
|
|
25585
|
+
const baseLeft = displayRect?.left ?? baseCellLeft;
|
|
25586
|
+
const baseTop = displayRect?.top ?? baseCellTop;
|
|
24609
25587
|
const localRect = {
|
|
24610
25588
|
height: displayRect?.height ?? (displayEffectiveRowHeights[drawRowIndex] ?? rowItem.size),
|
|
24611
25589
|
left: (useFrozenHorizontalPosition ? stickyLeftByCol.get(drawCell.col) ?? baseLeft - drawingViewport.left : baseLeft - drawingViewport.left) - paneBoundsForCell.left,
|
|
@@ -24617,13 +25595,18 @@ function XlsxGrid({
|
|
|
24617
25595
|
continue;
|
|
24618
25596
|
}
|
|
24619
25597
|
if (!intersectsCanvasDirtyRects(localRect.left, localRect.top, drawableWidth, localRect.height, paneDirtyRects)) {
|
|
25598
|
+
canvasProfileCulledCells += 1;
|
|
24620
25599
|
continue;
|
|
24621
25600
|
}
|
|
25601
|
+
canvasProfilePaintedCells += 1;
|
|
24622
25602
|
const cellStyle = cellData.style;
|
|
24623
25603
|
const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellStyle);
|
|
24624
25604
|
const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellStyle.backgroundColor === "string" ? cellStyle.backgroundColor : sheetSurface);
|
|
24625
25605
|
const gradientFill = !cellData.conditionalColorScale && typeof cellStyle.backgroundImage === "string" ? resolveCanvasGradientFill(paneContext, localRect, cellStyle.backgroundImage) : null;
|
|
24626
25606
|
const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellStyle.backgroundColor === "string" && cellStyle.backgroundColor !== sheetSurface;
|
|
25607
|
+
if (hasExplicitCellFill || cellData.chartHighlight) {
|
|
25608
|
+
flushPendingGridlines();
|
|
25609
|
+
}
|
|
24627
25610
|
paneContext.fillStyle = gradientFill ?? fillColor;
|
|
24628
25611
|
paneContext.fillRect(localRect.left, localRect.top, localRect.width, localRect.height);
|
|
24629
25612
|
if (cellData.chartHighlight) {
|
|
@@ -24669,10 +25652,8 @@ function XlsxGrid({
|
|
|
24669
25652
|
const bottomNeighborTopBorder = bottomNeighborData?.isMergedSecondary ? null : bottomNeighborData?.canvas?.topBorder ?? parseCanvasBorderDeclaration(bottomNeighborData?.style.borderTop);
|
|
24670
25653
|
const resolvedRightBorder = resolveCanvasBoundaryBorder(rightBorder, rightNeighborLeftBorder);
|
|
24671
25654
|
const resolvedBottomBorder = resolveCanvasBoundaryBorder(bottomBorder, bottomNeighborTopBorder);
|
|
24672
|
-
if (showGridLines && !hasExplicitCellFill) {
|
|
24673
|
-
|
|
24674
|
-
paneContext.lineWidth = 1;
|
|
24675
|
-
paneContext.beginPath();
|
|
25655
|
+
if (showGridLines && !hasExplicitCellFill && (!resolvedRightBorder || !resolvedBottomBorder)) {
|
|
25656
|
+
enqueueGridlinePath();
|
|
24676
25657
|
if (!resolvedRightBorder) {
|
|
24677
25658
|
paneContext.moveTo(localRect.left + localRect.width - 0.5, localRect.top);
|
|
24678
25659
|
paneContext.lineTo(localRect.left + localRect.width - 0.5, localRect.top + localRect.height);
|
|
@@ -24681,7 +25662,9 @@ function XlsxGrid({
|
|
|
24681
25662
|
paneContext.moveTo(localRect.left, localRect.top + localRect.height - 0.5);
|
|
24682
25663
|
paneContext.lineTo(localRect.left + localRect.width, localRect.top + localRect.height - 0.5);
|
|
24683
25664
|
}
|
|
24684
|
-
|
|
25665
|
+
}
|
|
25666
|
+
if (topBorder || resolvedRightBorder || resolvedBottomBorder || leftBorder || cellData.chartHighlight) {
|
|
25667
|
+
flushPendingGridlines();
|
|
24685
25668
|
}
|
|
24686
25669
|
if (topBorder && drawRowIndex === 0) {
|
|
24687
25670
|
strokeCanvasBorderSide(paneContext, "top", localRect, topBorder);
|
|
@@ -24714,17 +25697,26 @@ function XlsxGrid({
|
|
|
24714
25697
|
strokeCanvasBorderSide(paneContext, "left", localRect, highlightBorder);
|
|
24715
25698
|
}
|
|
24716
25699
|
}
|
|
25700
|
+
const rawText = cellData.value ?? "";
|
|
25701
|
+
const shouldDrawCanvasContent = cellData.checkboxState != null || cellData.sparkline || rawText.length > 0 || cellData.conditionalIcon || cellData.isTableHeader;
|
|
25702
|
+
if (!shouldDrawCanvasContent) {
|
|
25703
|
+
continue;
|
|
25704
|
+
}
|
|
24717
25705
|
const padding = canvasCellStyle.padding;
|
|
24718
25706
|
const contentLeft = localRect.left + padding.left;
|
|
24719
25707
|
const contentTop = localRect.top + padding.top;
|
|
24720
25708
|
const contentWidth = Math.max(0, localRect.width - padding.left - padding.right);
|
|
24721
25709
|
const contentHeight = Math.max(0, localRect.height - padding.top - padding.bottom);
|
|
25710
|
+
if (contentWidth <= 0 || contentHeight <= 0) {
|
|
25711
|
+
continue;
|
|
25712
|
+
}
|
|
24722
25713
|
const activeFontSizePx = cellData.shrinkToFitFontSizePx ?? resolveCanvasFontSizePx(cellStyle, 12 * zoomFactor);
|
|
24723
25714
|
const textClipOverscan = Math.max(
|
|
24724
25715
|
1,
|
|
24725
25716
|
zoomFactor * 1.5,
|
|
24726
25717
|
activeFontSizePx * (cellData.textRotationDeg ? 0.75 : 0.18)
|
|
24727
25718
|
);
|
|
25719
|
+
flushPendingGridlines();
|
|
24728
25720
|
paneContext.save();
|
|
24729
25721
|
paneContext.beginPath();
|
|
24730
25722
|
paneContext.rect(
|
|
@@ -24856,7 +25848,6 @@ function XlsxGrid({
|
|
|
24856
25848
|
const trailingInset = (cellData.conditionalIcon ? 18 * zoomFactor : 0) + (cellData.isTableHeader ? 16 * zoomFactor : 0);
|
|
24857
25849
|
const spillMaxWidth = cellData.spillWidth && cellData.spillWidth > 0 ? Math.max(0, cellData.spillWidth - trailingInset) : null;
|
|
24858
25850
|
const maxTextWidth = spillMaxWidth ?? Math.max(0, contentWidth - trailingInset);
|
|
24859
|
-
const rawText = cellData.value ?? "";
|
|
24860
25851
|
const textColor = canvasCellStyle.textColor;
|
|
24861
25852
|
const shouldEllipsizeText = canvasCellStyle.textOverflowEllipsis;
|
|
24862
25853
|
const shouldWrapText = canvasCellStyle.usesWrappedText || rawText.includes("\n");
|
|
@@ -24888,7 +25879,7 @@ function XlsxGrid({
|
|
|
24888
25879
|
const textY = textBlockTop + lineIndex * lineHeight + lineHeight / 2;
|
|
24889
25880
|
paneContext.fillText(line, textX, textY);
|
|
24890
25881
|
if (canvasCellStyle.textDecoration?.includes("underline") && line.length > 0) {
|
|
24891
|
-
const measured = Math.min(maxTextWidth, paneContext
|
|
25882
|
+
const measured = Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, line));
|
|
24892
25883
|
const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
|
|
24893
25884
|
paneContext.beginPath();
|
|
24894
25885
|
paneContext.moveTo(underlineStartX, textY + Math.max(2, lineHeight * 0.24));
|
|
@@ -24922,7 +25913,7 @@ function XlsxGrid({
|
|
|
24922
25913
|
const textY = contentTop + contentHeight / 2;
|
|
24923
25914
|
paneContext.fillText(text, textX, textY);
|
|
24924
25915
|
if (canvasCellStyle.textDecoration?.includes("underline") && text.length > 0) {
|
|
24925
|
-
const measured = shouldEllipsizeText ? Math.min(maxTextWidth, paneContext
|
|
25916
|
+
const measured = shouldEllipsizeText ? Math.min(maxTextWidth, measureCanvasTextWidth(paneContext, text)) : measureCanvasTextWidth(paneContext, text);
|
|
24926
25917
|
const underlineStartX = align === "right" ? textX - measured : align === "center" ? textX - measured / 2 : textX;
|
|
24927
25918
|
paneContext.beginPath();
|
|
24928
25919
|
paneContext.moveTo(underlineStartX, textY + 6 * zoomFactor);
|
|
@@ -24947,6 +25938,7 @@ function XlsxGrid({
|
|
|
24947
25938
|
paneContext.restore();
|
|
24948
25939
|
}
|
|
24949
25940
|
}
|
|
25941
|
+
flushPendingGridlines();
|
|
24950
25942
|
}
|
|
24951
25943
|
for (const pane of cellPaneOrder) {
|
|
24952
25944
|
const paneContext = bodyContexts[pane];
|
|
@@ -24964,7 +25956,7 @@ function XlsxGrid({
|
|
|
24964
25956
|
paneContext.textBaseline = "middle";
|
|
24965
25957
|
paneContext.fillText(spillText.text, spillText.textX, spillText.textY);
|
|
24966
25958
|
if (spillText.textDecoration?.includes("underline") && spillText.text.length > 0) {
|
|
24967
|
-
const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, paneContext
|
|
25959
|
+
const measured = spillText.ellipsize ? Math.min(spillText.maxWidth, measureCanvasTextWidth(paneContext, spillText.text)) : measureCanvasTextWidth(paneContext, spillText.text);
|
|
24968
25960
|
const underlineStartX = spillText.align === "right" ? spillText.textX - measured : spillText.align === "center" ? spillText.textX - measured / 2 : spillText.textX;
|
|
24969
25961
|
paneContext.beginPath();
|
|
24970
25962
|
paneContext.moveTo(underlineStartX, spillText.underlineY);
|
|
@@ -24976,6 +25968,14 @@ function XlsxGrid({
|
|
|
24976
25968
|
paneContext.restore();
|
|
24977
25969
|
}
|
|
24978
25970
|
}
|
|
25971
|
+
for (const pane of cellPaneOrder) {
|
|
25972
|
+
const paneContext = bodyContexts[pane];
|
|
25973
|
+
if (!paneContext) {
|
|
25974
|
+
continue;
|
|
25975
|
+
}
|
|
25976
|
+
drawBakedCanvasDrawings(pane, paneContext, bodyDirtyRectsByPane[pane]);
|
|
25977
|
+
}
|
|
25978
|
+
canvasProfileBodyMs = canvasProfileTarget ? performance.now() - canvasProfileBodyStart : 0;
|
|
24979
25979
|
}
|
|
24980
25980
|
if (shouldRepaintHeaders && cornerContext) {
|
|
24981
25981
|
const topFrozenHeaderContext = topHeaderContexts.frozen;
|
|
@@ -24990,7 +25990,7 @@ function XlsxGrid({
|
|
|
24990
25990
|
topScrollHeaderContext,
|
|
24991
25991
|
bufferCanvas,
|
|
24992
25992
|
dpr,
|
|
24993
|
-
|
|
25993
|
+
topScrollHeaderCanvasWidth,
|
|
24994
25994
|
headerHeight,
|
|
24995
25995
|
drawingViewport.left - previousPaintedViewport.left,
|
|
24996
25996
|
0
|
|
@@ -25009,7 +26009,7 @@ function XlsxGrid({
|
|
|
25009
26009
|
bufferCanvas,
|
|
25010
26010
|
dpr,
|
|
25011
26011
|
rowHeaderWidth,
|
|
25012
|
-
|
|
26012
|
+
leftScrollHeaderCanvasHeight,
|
|
25013
26013
|
0,
|
|
25014
26014
|
drawingViewport.top - previousPaintedViewport.top
|
|
25015
26015
|
);
|
|
@@ -25018,11 +26018,11 @@ function XlsxGrid({
|
|
|
25018
26018
|
}
|
|
25019
26019
|
}
|
|
25020
26020
|
}
|
|
25021
|
-
if (topFrozenHeaderContext &&
|
|
26021
|
+
if (topFrozenHeaderContext && topFrozenHeaderCanvasWidth > 0) {
|
|
25022
26022
|
topFrozenHeaderContext.fillStyle = palette.headerSurface;
|
|
25023
|
-
topFrozenHeaderContext.fillRect(0, 0,
|
|
26023
|
+
topFrozenHeaderContext.fillRect(0, 0, topFrozenHeaderCanvasWidth, headerHeight);
|
|
25024
26024
|
}
|
|
25025
|
-
if (topScrollHeaderContext &&
|
|
26025
|
+
if (topScrollHeaderContext && topScrollHeaderCanvasWidth > 0) {
|
|
25026
26026
|
topScrollHeaderContext.fillStyle = palette.headerSurface;
|
|
25027
26027
|
for (const dirtyRect of topScrollHeaderDirtyRects) {
|
|
25028
26028
|
topScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
|
|
@@ -25041,7 +26041,7 @@ function XlsxGrid({
|
|
|
25041
26041
|
if (!paneContext) {
|
|
25042
26042
|
continue;
|
|
25043
26043
|
}
|
|
25044
|
-
if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ?
|
|
26044
|
+
if (column.localLeft + column.width < 0 || column.localLeft > (column.isFrozen ? topFrozenHeaderCanvasWidth : topScrollHeaderCanvasWidth)) {
|
|
25045
26045
|
continue;
|
|
25046
26046
|
}
|
|
25047
26047
|
if (!column.isFrozen && !intersectsCanvasDirtyRects(column.localLeft, 0, column.width, column.height, topScrollHeaderDirtyRects)) {
|
|
@@ -25067,11 +26067,11 @@ function XlsxGrid({
|
|
|
25067
26067
|
column.height / 2
|
|
25068
26068
|
);
|
|
25069
26069
|
}
|
|
25070
|
-
if (leftFrozenHeaderContext &&
|
|
26070
|
+
if (leftFrozenHeaderContext && leftFrozenHeaderCanvasHeight > 0) {
|
|
25071
26071
|
leftFrozenHeaderContext.fillStyle = palette.rowHeaderSurface;
|
|
25072
|
-
leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth,
|
|
26072
|
+
leftFrozenHeaderContext.fillRect(0, 0, rowHeaderWidth, leftFrozenHeaderCanvasHeight);
|
|
25073
26073
|
}
|
|
25074
|
-
if (leftScrollHeaderContext &&
|
|
26074
|
+
if (leftScrollHeaderContext && leftScrollHeaderCanvasHeight > 0) {
|
|
25075
26075
|
leftScrollHeaderContext.fillStyle = palette.rowHeaderSurface;
|
|
25076
26076
|
for (const dirtyRect of leftScrollHeaderDirtyRects) {
|
|
25077
26077
|
leftScrollHeaderContext.fillRect(dirtyRect.left, dirtyRect.top, dirtyRect.width, dirtyRect.height);
|
|
@@ -25090,7 +26090,7 @@ function XlsxGrid({
|
|
|
25090
26090
|
if (!paneContext) {
|
|
25091
26091
|
continue;
|
|
25092
26092
|
}
|
|
25093
|
-
if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ?
|
|
26093
|
+
if (row.localTop + row.height < 0 || row.localTop > (row.isFrozen ? leftFrozenHeaderCanvasHeight : leftScrollHeaderCanvasHeight)) {
|
|
25094
26094
|
continue;
|
|
25095
26095
|
}
|
|
25096
26096
|
if (!row.isFrozen && !intersectsCanvasDirtyRects(0, row.localTop, rowHeaderWidth, row.height, leftScrollHeaderDirtyRects)) {
|
|
@@ -25130,9 +26130,29 @@ function XlsxGrid({
|
|
|
25130
26130
|
paintedHeaderCanvasSignatureRef.current = nextHeaderCanvasSignature;
|
|
25131
26131
|
}
|
|
25132
26132
|
applyCanvasViewportCompensation();
|
|
26133
|
+
if (canvasProfileTarget) {
|
|
26134
|
+
canvasProfileTarget.push({
|
|
26135
|
+
bodyMs: canvasProfileBodyMs,
|
|
26136
|
+
culledCells: canvasProfileCulledCells,
|
|
26137
|
+
dirtyRects: canvasProfileDirtyRects,
|
|
26138
|
+
lookedUpCells: canvasProfileLookedUpCells,
|
|
26139
|
+
paintedCells: canvasProfilePaintedCells,
|
|
26140
|
+
repaintBody: shouldRepaintBody,
|
|
26141
|
+
repaintHeaders: shouldRepaintHeaders,
|
|
26142
|
+
renderToLayoutMs: canvasProfileStart - xlsxGridRenderStart,
|
|
26143
|
+
totalMs: performance.now() - canvasProfileStart
|
|
26144
|
+
});
|
|
26145
|
+
if (canvasProfileTarget.length > 500) {
|
|
26146
|
+
canvasProfileTarget.splice(0, canvasProfileTarget.length - 500);
|
|
26147
|
+
}
|
|
26148
|
+
}
|
|
25133
26149
|
}, [
|
|
25134
26150
|
activeSheet,
|
|
25135
26151
|
applyCanvasViewportCompensation,
|
|
26152
|
+
bakedCanvasDrawingSignature,
|
|
26153
|
+
bakedFormControlRects,
|
|
26154
|
+
bakedImageRects,
|
|
26155
|
+
bakedShapeRects,
|
|
25136
26156
|
canvasColumnHeaderCells,
|
|
25137
26157
|
canvasPaneAxisItems,
|
|
25138
26158
|
canvasRowHeaderCells,
|
|
@@ -25153,15 +26173,16 @@ function XlsxGrid({
|
|
|
25153
26173
|
frozenPaneBottom,
|
|
25154
26174
|
frozenPaneRight,
|
|
25155
26175
|
getCellData,
|
|
26176
|
+
getCanvasImage,
|
|
25156
26177
|
getBodyBlitBufferCanvas,
|
|
25157
26178
|
getHeaderBlitBufferCanvas,
|
|
25158
26179
|
palette,
|
|
25159
26180
|
resolveCellDisplayRect,
|
|
25160
26181
|
resolveMergeAnchorCell,
|
|
25161
|
-
resizeGuide,
|
|
25162
26182
|
rowIndexByActual,
|
|
25163
26183
|
rowPrefixSums,
|
|
25164
26184
|
selectionHeaderSurface,
|
|
26185
|
+
shouldBakeCanvasStaticDrawings,
|
|
25165
26186
|
stickyLeftByCol,
|
|
25166
26187
|
stickyTopByRow,
|
|
25167
26188
|
visibleCols,
|
|
@@ -25379,7 +26400,7 @@ function XlsxGrid({
|
|
|
25379
26400
|
start: virtualRow?.start ?? (rowPrefixSums[index] ?? 0)
|
|
25380
26401
|
};
|
|
25381
26402
|
});
|
|
25382
|
-
const totalHeight = shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
|
|
26403
|
+
const totalHeight = shouldUseDomVirtualizer && shouldVirtualizeRows ? rowVirtualizer.getTotalSize() : rowPrefixSums[rowPrefixSums.length - 1] ?? 0;
|
|
25383
26404
|
const totalWidth = totalContentWidth + displayRowHeaderWidth;
|
|
25384
26405
|
const sheetContentHeight = displayHeaderHeight + totalHeight;
|
|
25385
26406
|
const isLiveZooming = liveGestureZoom !== null && zoomScale === liveGestureZoom.baseZoomScale;
|
|
@@ -25453,18 +26474,6 @@ function XlsxGrid({
|
|
|
25453
26474
|
width: 0,
|
|
25454
26475
|
zIndex: canvasHeaderOverlayZIndex
|
|
25455
26476
|
};
|
|
25456
|
-
const scrollBodyCanvasWidth = Math.max(0, drawingViewport.width - frozenPaneRight);
|
|
25457
|
-
const scrollBodyCanvasHeight = Math.max(0, drawingViewport.height - frozenPaneBottom);
|
|
25458
|
-
const topBodyCanvasWidth = scrollBodyCanvasWidth;
|
|
25459
|
-
const topBodyCanvasHeight = Math.max(0, frozenPaneBottom - displayHeaderHeight);
|
|
25460
|
-
const leftBodyCanvasWidth = Math.max(0, frozenPaneRight - displayRowHeaderWidth);
|
|
25461
|
-
const leftBodyCanvasHeight = scrollBodyCanvasHeight;
|
|
25462
|
-
const cornerBodyCanvasWidth = leftBodyCanvasWidth;
|
|
25463
|
-
const cornerBodyCanvasHeight = topBodyCanvasHeight;
|
|
25464
|
-
const topFrozenHeaderCanvasWidth = leftBodyCanvasWidth;
|
|
25465
|
-
const topScrollHeaderCanvasWidth = scrollBodyCanvasWidth;
|
|
25466
|
-
const leftFrozenHeaderCanvasHeight = topBodyCanvasHeight;
|
|
25467
|
-
const leftScrollHeaderCanvasHeight = scrollBodyCanvasHeight;
|
|
25468
26477
|
const canvasBodyBaseStyle = {
|
|
25469
26478
|
cursor: "cell",
|
|
25470
26479
|
pointerEvents: "auto",
|
|
@@ -25476,13 +26485,13 @@ function XlsxGrid({
|
|
|
25476
26485
|
const canvasScrollBodyStyle = {
|
|
25477
26486
|
...canvasBodyBaseStyle,
|
|
25478
26487
|
display: scrollBodyCanvasWidth > 0 && scrollBodyCanvasHeight > 0 ? "block" : "none",
|
|
25479
|
-
left:
|
|
25480
|
-
top:
|
|
26488
|
+
left: scrollBodyCanvasLeft,
|
|
26489
|
+
top: scrollBodyCanvasTop
|
|
25481
26490
|
};
|
|
25482
26491
|
const canvasTopBodyStyle = {
|
|
25483
26492
|
...canvasBodyBaseStyle,
|
|
25484
26493
|
display: topBodyCanvasWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
|
|
25485
|
-
left:
|
|
26494
|
+
left: scrollBodyCanvasLeft,
|
|
25486
26495
|
top: displayHeaderHeight,
|
|
25487
26496
|
zIndex: 30
|
|
25488
26497
|
};
|
|
@@ -25490,7 +26499,7 @@ function XlsxGrid({
|
|
|
25490
26499
|
...canvasBodyBaseStyle,
|
|
25491
26500
|
display: leftBodyCanvasWidth > 0 && leftBodyCanvasHeight > 0 ? "block" : "none",
|
|
25492
26501
|
left: displayRowHeaderWidth,
|
|
25493
|
-
top:
|
|
26502
|
+
top: scrollBodyCanvasTop,
|
|
25494
26503
|
zIndex: 30
|
|
25495
26504
|
};
|
|
25496
26505
|
const canvasCornerBodyStyle = {
|
|
@@ -25516,7 +26525,7 @@ function XlsxGrid({
|
|
|
25516
26525
|
const canvasTopScrollHeaderStyle = {
|
|
25517
26526
|
...canvasHeaderBaseStyle,
|
|
25518
26527
|
display: topScrollHeaderCanvasWidth > 0 && drawingViewport.height > 0 ? "block" : "none",
|
|
25519
|
-
left:
|
|
26528
|
+
left: scrollBodyCanvasLeft,
|
|
25520
26529
|
top: 0,
|
|
25521
26530
|
zIndex: canvasHeaderOverlayZIndex
|
|
25522
26531
|
};
|
|
@@ -25531,7 +26540,7 @@ function XlsxGrid({
|
|
|
25531
26540
|
...canvasHeaderBaseStyle,
|
|
25532
26541
|
display: leftScrollHeaderCanvasHeight > 0 && drawingViewport.width > 0 ? "block" : "none",
|
|
25533
26542
|
left: 0,
|
|
25534
|
-
top:
|
|
26543
|
+
top: scrollBodyCanvasTop,
|
|
25535
26544
|
zIndex: canvasHeaderOverlayZIndex
|
|
25536
26545
|
};
|
|
25537
26546
|
const canvasCornerHeaderStyle = {
|
|
@@ -26087,26 +27096,26 @@ function XlsxGrid({
|
|
|
26087
27096
|
};
|
|
26088
27097
|
const canvasScrollOverlayPaneStyle = {
|
|
26089
27098
|
...canvasOverlayPaneBaseStyle,
|
|
26090
|
-
display:
|
|
26091
|
-
height:
|
|
27099
|
+
display: scrollBodyViewportWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
|
|
27100
|
+
height: scrollBodyViewportHeight,
|
|
26092
27101
|
left: frozenPaneRight,
|
|
26093
27102
|
top: frozenPaneBottom,
|
|
26094
|
-
width:
|
|
27103
|
+
width: scrollBodyViewportWidth,
|
|
26095
27104
|
zIndex: 20
|
|
26096
27105
|
};
|
|
26097
27106
|
const canvasTopOverlayPaneStyle = {
|
|
26098
27107
|
...canvasOverlayPaneBaseStyle,
|
|
26099
|
-
display:
|
|
27108
|
+
display: scrollBodyViewportWidth > 0 && topBodyCanvasHeight > 0 ? "block" : "none",
|
|
26100
27109
|
height: topBodyCanvasHeight,
|
|
26101
27110
|
left: frozenPaneRight,
|
|
26102
27111
|
top: displayHeaderHeight,
|
|
26103
|
-
width:
|
|
27112
|
+
width: scrollBodyViewportWidth,
|
|
26104
27113
|
zIndex: 35
|
|
26105
27114
|
};
|
|
26106
27115
|
const canvasLeftOverlayPaneStyle = {
|
|
26107
27116
|
...canvasOverlayPaneBaseStyle,
|
|
26108
|
-
display: leftBodyCanvasWidth > 0 &&
|
|
26109
|
-
height:
|
|
27117
|
+
display: leftBodyCanvasWidth > 0 && scrollBodyViewportHeight > 0 ? "block" : "none",
|
|
27118
|
+
height: scrollBodyViewportHeight,
|
|
26110
27119
|
left: displayRowHeaderWidth,
|
|
26111
27120
|
top: frozenPaneBottom,
|
|
26112
27121
|
width: leftBodyCanvasWidth,
|
|
@@ -26121,8 +27130,9 @@ function XlsxGrid({
|
|
|
26121
27130
|
width: cornerBodyCanvasWidth,
|
|
26122
27131
|
zIndex: 36
|
|
26123
27132
|
};
|
|
27133
|
+
const drawingViewportCacheSignature = `${Math.floor(drawingViewport.left / CANVAS_VIEWPORT_OVERSCAN_PX)}:${Math.floor(drawingViewport.top / CANVAS_VIEWPORT_OVERSCAN_PX)}:${drawingViewport.width}:${drawingViewport.height}`;
|
|
26124
27134
|
const previousPaneDrawingNodes = paneDrawingNodesCacheRef.current;
|
|
26125
|
-
const canReusePaneDrawingNodes = previousPaneDrawingNodes !== null && previousPaneDrawingNodes.showImages === showImages && previousPaneDrawingNodes.chartRects === chartRects && previousPaneDrawingNodes.formControlRects ===
|
|
27135
|
+
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;
|
|
26126
27136
|
const paneDrawingNodes = canReusePaneDrawingNodes ? previousPaneDrawingNodes.value : !showImages ? {
|
|
26127
27137
|
corner: null,
|
|
26128
27138
|
left: null,
|
|
@@ -26131,35 +27141,35 @@ function XlsxGrid({
|
|
|
26131
27141
|
} : {
|
|
26132
27142
|
corner: /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
26133
27143
|
chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "corner")),
|
|
26134
|
-
|
|
26135
|
-
|
|
26136
|
-
|
|
27144
|
+
domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "corner")),
|
|
27145
|
+
domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "corner")),
|
|
27146
|
+
domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "corner"))
|
|
26137
27147
|
] }),
|
|
26138
27148
|
left: /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
26139
27149
|
chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "left")),
|
|
26140
|
-
|
|
26141
|
-
|
|
26142
|
-
|
|
27150
|
+
domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "left")),
|
|
27151
|
+
domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "left")),
|
|
27152
|
+
domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "left"))
|
|
26143
27153
|
] }),
|
|
26144
27154
|
scroll: /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
26145
27155
|
chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "scroll")),
|
|
26146
|
-
|
|
26147
|
-
|
|
26148
|
-
|
|
27156
|
+
domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "scroll")),
|
|
27157
|
+
domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "scroll")),
|
|
27158
|
+
domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "scroll"))
|
|
26149
27159
|
] }),
|
|
26150
27160
|
top: /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
26151
27161
|
chartRects.map(({ chart, rect }) => renderChartDrawing(chart, rect, "top")),
|
|
26152
|
-
|
|
26153
|
-
|
|
26154
|
-
|
|
27162
|
+
domShapeRects.map(({ shape, rect }) => renderShapeDrawing(shape, rect, "top")),
|
|
27163
|
+
domFormControlRects.map(({ control, rect }) => renderFormControlDrawing(control, rect, "top")),
|
|
27164
|
+
domImageRects.map(({ image, rect }) => renderImageDrawing(image, rect, "top"))
|
|
26155
27165
|
] })
|
|
26156
27166
|
};
|
|
26157
27167
|
if (!canReusePaneDrawingNodes) {
|
|
26158
27168
|
paneDrawingNodesCacheRef.current = {
|
|
26159
27169
|
chartRects,
|
|
26160
|
-
|
|
26161
|
-
formControlRects,
|
|
26162
|
-
imageRects,
|
|
27170
|
+
drawingViewportSignature: drawingViewportCacheSignature,
|
|
27171
|
+
formControlRects: domFormControlRects,
|
|
27172
|
+
imageRects: domImageRects,
|
|
26163
27173
|
isChartsLoading,
|
|
26164
27174
|
palette,
|
|
26165
27175
|
readOnly,
|
|
@@ -26169,7 +27179,7 @@ function XlsxGrid({
|
|
|
26169
27179
|
selectedChartId,
|
|
26170
27180
|
selectedImageId,
|
|
26171
27181
|
selectionStroke,
|
|
26172
|
-
shapeRects,
|
|
27182
|
+
shapeRects: domShapeRects,
|
|
26173
27183
|
showImages,
|
|
26174
27184
|
value: paneDrawingNodes
|
|
26175
27185
|
};
|
|
@@ -26677,10 +27687,11 @@ function XlsxGrid({
|
|
|
26677
27687
|
style: canvasCornerBodyStyle
|
|
26678
27688
|
}
|
|
26679
27689
|
),
|
|
26680
|
-
|
|
27690
|
+
hasCanvasDomDrawingOverlays ? /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
26681
27691
|
/* @__PURE__ */ jsx3("div", { style: canvasScrollOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
|
|
26682
27692
|
"div",
|
|
26683
27693
|
{
|
|
27694
|
+
ref: canvasScrollOverlayContentRef,
|
|
26684
27695
|
style: {
|
|
26685
27696
|
height: sheetContentHeight,
|
|
26686
27697
|
left: 0,
|
|
@@ -26688,6 +27699,7 @@ function XlsxGrid({
|
|
|
26688
27699
|
position: "absolute",
|
|
26689
27700
|
top: 0,
|
|
26690
27701
|
transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
|
|
27702
|
+
transformOrigin: "0 0",
|
|
26691
27703
|
width: totalWidth
|
|
26692
27704
|
},
|
|
26693
27705
|
children: paneDrawingNodes.scroll
|
|
@@ -26696,6 +27708,7 @@ function XlsxGrid({
|
|
|
26696
27708
|
/* @__PURE__ */ jsx3("div", { style: canvasTopOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
|
|
26697
27709
|
"div",
|
|
26698
27710
|
{
|
|
27711
|
+
ref: canvasTopOverlayContentRef,
|
|
26699
27712
|
style: {
|
|
26700
27713
|
height: sheetContentHeight,
|
|
26701
27714
|
left: 0,
|
|
@@ -26703,6 +27716,7 @@ function XlsxGrid({
|
|
|
26703
27716
|
position: "absolute",
|
|
26704
27717
|
top: 0,
|
|
26705
27718
|
transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-displayHeaderHeight}px)`,
|
|
27719
|
+
transformOrigin: "0 0",
|
|
26706
27720
|
width: totalWidth
|
|
26707
27721
|
},
|
|
26708
27722
|
children: paneDrawingNodes.top
|
|
@@ -26711,6 +27725,7 @@ function XlsxGrid({
|
|
|
26711
27725
|
/* @__PURE__ */ jsx3("div", { style: canvasLeftOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
|
|
26712
27726
|
"div",
|
|
26713
27727
|
{
|
|
27728
|
+
ref: canvasLeftOverlayContentRef,
|
|
26714
27729
|
style: {
|
|
26715
27730
|
height: sheetContentHeight,
|
|
26716
27731
|
left: 0,
|
|
@@ -26718,6 +27733,7 @@ function XlsxGrid({
|
|
|
26718
27733
|
position: "absolute",
|
|
26719
27734
|
top: 0,
|
|
26720
27735
|
transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
|
|
27736
|
+
transformOrigin: "0 0",
|
|
26721
27737
|
width: totalWidth
|
|
26722
27738
|
},
|
|
26723
27739
|
children: paneDrawingNodes.left
|
|
@@ -26726,6 +27742,7 @@ function XlsxGrid({
|
|
|
26726
27742
|
/* @__PURE__ */ jsx3("div", { style: canvasCornerOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
|
|
26727
27743
|
"div",
|
|
26728
27744
|
{
|
|
27745
|
+
ref: canvasCornerOverlayContentRef,
|
|
26729
27746
|
style: {
|
|
26730
27747
|
height: sheetContentHeight,
|
|
26731
27748
|
left: 0,
|
|
@@ -26733,6 +27750,7 @@ function XlsxGrid({
|
|
|
26733
27750
|
position: "absolute",
|
|
26734
27751
|
top: 0,
|
|
26735
27752
|
transform: `translate(${-displayRowHeaderWidth}px, ${-displayHeaderHeight}px)`,
|
|
27753
|
+
transformOrigin: "0 0",
|
|
26736
27754
|
width: totalWidth
|
|
26737
27755
|
},
|
|
26738
27756
|
children: paneDrawingNodes.corner
|
|
@@ -27549,27 +28567,95 @@ function useXlsxViewerCharts() {
|
|
|
27549
28567
|
);
|
|
27550
28568
|
}
|
|
27551
28569
|
function useXlsxViewerThumbnails(options = {}) {
|
|
27552
|
-
const { workbook, sheets } = useXlsxViewer();
|
|
28570
|
+
const { getSheetFormControls, getSheetImages, getSheetShapes, workbook, sheets } = useXlsxViewer();
|
|
27553
28571
|
const { isDark } = React4.useContext(ViewerAppearanceContext);
|
|
27554
28572
|
const palette = useViewerPalette(isDark);
|
|
27555
28573
|
const includeHeaders = options.includeHeaders ?? true;
|
|
27556
28574
|
const resolution = options.resolution;
|
|
28575
|
+
const thumbnailImageCacheRef = React4.useRef(/* @__PURE__ */ new Map());
|
|
28576
|
+
const isMountedRef = React4.useRef(false);
|
|
28577
|
+
const [thumbnailImageLoadVersion, setThumbnailImageLoadVersion] = React4.useState(0);
|
|
28578
|
+
React4.useEffect(() => {
|
|
28579
|
+
isMountedRef.current = true;
|
|
28580
|
+
return () => {
|
|
28581
|
+
isMountedRef.current = false;
|
|
28582
|
+
};
|
|
28583
|
+
}, []);
|
|
28584
|
+
const getThumbnailImage = React4.useCallback((image) => {
|
|
28585
|
+
if (typeof Image === "undefined") {
|
|
28586
|
+
return null;
|
|
28587
|
+
}
|
|
28588
|
+
const cacheKey = image.id;
|
|
28589
|
+
const cached = thumbnailImageCacheRef.current.get(cacheKey);
|
|
28590
|
+
if (cached && cached.src === image.src) {
|
|
28591
|
+
return cached.loaded && !cached.failed ? cached.image : null;
|
|
28592
|
+
}
|
|
28593
|
+
const imageElement = new Image();
|
|
28594
|
+
const entry = {
|
|
28595
|
+
failed: false,
|
|
28596
|
+
image: imageElement,
|
|
28597
|
+
loaded: false,
|
|
28598
|
+
src: image.src
|
|
28599
|
+
};
|
|
28600
|
+
thumbnailImageCacheRef.current.set(cacheKey, entry);
|
|
28601
|
+
imageElement.onload = () => {
|
|
28602
|
+
entry.loaded = true;
|
|
28603
|
+
if (isMountedRef.current) {
|
|
28604
|
+
setThumbnailImageLoadVersion((version) => version + 1);
|
|
28605
|
+
}
|
|
28606
|
+
};
|
|
28607
|
+
imageElement.onerror = () => {
|
|
28608
|
+
entry.failed = true;
|
|
28609
|
+
if (isMountedRef.current) {
|
|
28610
|
+
setThumbnailImageLoadVersion((version) => version + 1);
|
|
28611
|
+
}
|
|
28612
|
+
};
|
|
28613
|
+
imageElement.src = image.src;
|
|
28614
|
+
return null;
|
|
28615
|
+
}, []);
|
|
27557
28616
|
const thumbnails = React4.useMemo(() => {
|
|
27558
28617
|
return sheets.map((sheet, sheetIndex) => {
|
|
27559
28618
|
const worksheet = workbook?.getSheet(sheet.workbookSheetIndex) ?? null;
|
|
28619
|
+
const sheetImages = getSheetImages(sheetIndex).filter((image) => image.src).sort((left, right) => left.zIndex - right.zIndex);
|
|
28620
|
+
const sheetShapes = getSheetShapes(sheetIndex);
|
|
28621
|
+
const sheetFormControls = getSheetFormControls(sheetIndex).filter((control) => !control.hidden);
|
|
28622
|
+
const drawingBounds = [
|
|
28623
|
+
...sheetImages.map((image) => image.anchor),
|
|
28624
|
+
...sheetShapes.map((shape) => shape.anchor),
|
|
28625
|
+
...sheetFormControls.map((control) => control.anchor)
|
|
28626
|
+
].reduce((bounds, anchor) => {
|
|
28627
|
+
const drawingBound = resolveAnchoredBounds(anchor) ?? {
|
|
28628
|
+
maxCol: 0,
|
|
28629
|
+
maxRow: 0,
|
|
28630
|
+
minCol: 0,
|
|
28631
|
+
minRow: 0
|
|
28632
|
+
};
|
|
28633
|
+
return {
|
|
28634
|
+
maxCol: Math.max(bounds?.maxCol ?? drawingBound.maxCol, drawingBound.maxCol),
|
|
28635
|
+
maxRow: Math.max(bounds?.maxRow ?? drawingBound.maxRow, drawingBound.maxRow),
|
|
28636
|
+
minCol: Math.min(bounds?.minCol ?? drawingBound.minCol, drawingBound.minCol),
|
|
28637
|
+
minRow: Math.min(bounds?.minRow ?? drawingBound.minRow, drawingBound.minRow)
|
|
28638
|
+
};
|
|
28639
|
+
}, null);
|
|
28640
|
+
const sheetMinUsedRow = (sheet.maxUsedRow ?? -1) >= (sheet.minUsedRow ?? 0) ? sheet.minUsedRow ?? 0 : void 0;
|
|
28641
|
+
const sheetMinUsedCol = (sheet.maxUsedCol ?? -1) >= (sheet.minUsedCol ?? 0) ? sheet.minUsedCol ?? 0 : void 0;
|
|
28642
|
+
const effectiveMinUsedRow = Math.max(0, Math.min(sheetMinUsedRow ?? drawingBounds?.minRow ?? 0, drawingBounds?.minRow ?? sheetMinUsedRow ?? 0));
|
|
28643
|
+
const effectiveMinUsedCol = Math.max(0, Math.min(sheetMinUsedCol ?? drawingBounds?.minCol ?? 0, drawingBounds?.minCol ?? sheetMinUsedCol ?? 0));
|
|
28644
|
+
const effectiveMaxUsedRow = Math.max(sheet.maxUsedRow ?? -1, drawingBounds?.maxRow ?? -1);
|
|
28645
|
+
const effectiveMaxUsedCol = Math.max(sheet.maxUsedCol ?? -1, drawingBounds?.maxCol ?? -1);
|
|
27560
28646
|
const showGridLines = sheet.showGridLines ?? true;
|
|
27561
28647
|
const hiddenRowSet = new Set(sheet.hiddenRows ?? []);
|
|
27562
28648
|
const hiddenColSet = new Set(sheet.hiddenCols ?? []);
|
|
27563
28649
|
const visibleRows = buildVisibleAxisIndices(
|
|
27564
28650
|
sheet.visibleRows ?? [],
|
|
27565
|
-
Math.max(THUMBNAIL_FALLBACK_ROWS,
|
|
27566
|
-
|
|
28651
|
+
Math.max(THUMBNAIL_FALLBACK_ROWS, effectiveMaxUsedRow + THUMBNAIL_FALLBACK_ROWS + 1),
|
|
28652
|
+
effectiveMaxUsedRow,
|
|
27567
28653
|
hiddenRowSet
|
|
27568
28654
|
);
|
|
27569
28655
|
const visibleCols = buildVisibleAxisIndices(
|
|
27570
28656
|
sheet.visibleCols ?? [],
|
|
27571
|
-
Math.max(THUMBNAIL_FALLBACK_COLS,
|
|
27572
|
-
|
|
28657
|
+
Math.max(THUMBNAIL_FALLBACK_COLS, effectiveMaxUsedCol + THUMBNAIL_FALLBACK_COLS + 1),
|
|
28658
|
+
effectiveMaxUsedCol,
|
|
27573
28659
|
hiddenColSet
|
|
27574
28660
|
);
|
|
27575
28661
|
const resolveColumnWidthPx = (actualCol) => {
|
|
@@ -27613,8 +28699,8 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27613
28699
|
getSizePx: resolveRowHeightPx,
|
|
27614
28700
|
maxCount: THUMBNAIL_MAX_ROWS,
|
|
27615
28701
|
maxLogicalPixels: THUMBNAIL_MAX_SOURCE_HEIGHT_PX,
|
|
27616
|
-
maxUsedIndex:
|
|
27617
|
-
minUsedIndex:
|
|
28702
|
+
maxUsedIndex: effectiveMaxUsedRow,
|
|
28703
|
+
minUsedIndex: effectiveMinUsedRow,
|
|
27618
28704
|
precomputed: visibleRows
|
|
27619
28705
|
});
|
|
27620
28706
|
const previewCols = resolveThumbnailAxisIndices({
|
|
@@ -27622,8 +28708,8 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27622
28708
|
getSizePx: resolveColumnWidthPx,
|
|
27623
28709
|
maxCount: THUMBNAIL_MAX_COLS,
|
|
27624
28710
|
maxLogicalPixels: THUMBNAIL_MAX_SOURCE_WIDTH_PX,
|
|
27625
|
-
maxUsedIndex:
|
|
27626
|
-
minUsedIndex:
|
|
28711
|
+
maxUsedIndex: effectiveMaxUsedCol,
|
|
28712
|
+
minUsedIndex: effectiveMinUsedCol,
|
|
27627
28713
|
precomputed: visibleCols
|
|
27628
28714
|
});
|
|
27629
28715
|
const rowAxis = buildThumbnailAxisItems(previewRows, resolveRowHeightPx);
|
|
@@ -27648,11 +28734,79 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27648
28734
|
const sparklineByCell = new Map(
|
|
27649
28735
|
(sheet.sparklines ?? []).map((sparkline) => [`${sparkline.target.row}:${sparkline.target.col}`, sparkline])
|
|
27650
28736
|
);
|
|
28737
|
+
const thumbnailImageRects = sheetImages.map((image) => ({
|
|
28738
|
+
image,
|
|
28739
|
+
rect: resolveThumbnailAnchoredRect(
|
|
28740
|
+
image.anchor,
|
|
28741
|
+
visibleRows,
|
|
28742
|
+
visibleCols,
|
|
28743
|
+
resolveRowHeightPx,
|
|
28744
|
+
resolveColumnWidthPx,
|
|
28745
|
+
previewRows,
|
|
28746
|
+
previewCols,
|
|
28747
|
+
{
|
|
28748
|
+
headerHeight,
|
|
28749
|
+
rowHeaderWidth
|
|
28750
|
+
}
|
|
28751
|
+
)
|
|
28752
|
+
})).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
|
|
28753
|
+
const thumbnailShapeRects = sheetShapes.map((shape) => ({
|
|
28754
|
+
rect: resolveThumbnailAnchoredRect(
|
|
28755
|
+
shape.anchor,
|
|
28756
|
+
visibleRows,
|
|
28757
|
+
visibleCols,
|
|
28758
|
+
resolveRowHeightPx,
|
|
28759
|
+
resolveColumnWidthPx,
|
|
28760
|
+
previewRows,
|
|
28761
|
+
previewCols,
|
|
28762
|
+
{
|
|
28763
|
+
headerHeight,
|
|
28764
|
+
rowHeaderWidth
|
|
28765
|
+
}
|
|
28766
|
+
),
|
|
28767
|
+
shape
|
|
28768
|
+
})).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
|
|
28769
|
+
const thumbnailFormControlRects = sheetFormControls.map((control) => ({
|
|
28770
|
+
control,
|
|
28771
|
+
rect: resolveThumbnailAnchoredRect(
|
|
28772
|
+
control.anchor,
|
|
28773
|
+
visibleRows,
|
|
28774
|
+
visibleCols,
|
|
28775
|
+
resolveRowHeightPx,
|
|
28776
|
+
resolveColumnWidthPx,
|
|
28777
|
+
previewRows,
|
|
28778
|
+
previewCols,
|
|
28779
|
+
{
|
|
28780
|
+
headerHeight,
|
|
28781
|
+
rowHeaderWidth
|
|
28782
|
+
}
|
|
28783
|
+
)
|
|
28784
|
+
})).filter(({ rect }) => rect.left + rect.width >= rowHeaderWidth && rect.top + rect.height >= headerHeight && rect.left <= sourceWidth && rect.top <= sourceHeight);
|
|
28785
|
+
const thumbnailDrawingEntries = [
|
|
28786
|
+
...thumbnailShapeRects.map(({ rect, shape }) => ({
|
|
28787
|
+
kind: "shape",
|
|
28788
|
+
rect,
|
|
28789
|
+
shape,
|
|
28790
|
+
zIndex: shape.zIndex
|
|
28791
|
+
})),
|
|
28792
|
+
...thumbnailFormControlRects.map(({ control, rect }) => ({
|
|
28793
|
+
control,
|
|
28794
|
+
kind: "formControl",
|
|
28795
|
+
rect,
|
|
28796
|
+
zIndex: control.zIndex
|
|
28797
|
+
})),
|
|
28798
|
+
...thumbnailImageRects.map(({ image, rect }) => ({
|
|
28799
|
+
image,
|
|
28800
|
+
kind: "image",
|
|
28801
|
+
rect,
|
|
28802
|
+
zIndex: image.zIndex
|
|
28803
|
+
}))
|
|
28804
|
+
].sort((left, right) => left.zIndex - right.zIndex);
|
|
27651
28805
|
const paint = (canvas) => {
|
|
27652
28806
|
if (!canvas) {
|
|
27653
28807
|
return false;
|
|
27654
28808
|
}
|
|
27655
|
-
const context = canvas.getContext("2d");
|
|
28809
|
+
const context = canvas.getContext("2d", { alpha: false });
|
|
27656
28810
|
if (!context) {
|
|
27657
28811
|
return false;
|
|
27658
28812
|
}
|
|
@@ -27676,9 +28830,10 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27676
28830
|
}
|
|
27677
28831
|
context.setTransform(dpr * scale, 0, 0, dpr * scale, 0, 0);
|
|
27678
28832
|
context.clearRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
|
|
28833
|
+
const thumbnailSheetSurface = resolveSheetSurface(sheet, palette);
|
|
27679
28834
|
context.fillStyle = palette.canvas;
|
|
27680
28835
|
context.fillRect(0, 0, Math.max(1, sourceWidth), Math.max(1, sourceHeight));
|
|
27681
|
-
context.fillStyle =
|
|
28836
|
+
context.fillStyle = thumbnailSheetSurface;
|
|
27682
28837
|
context.fillRect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
|
|
27683
28838
|
if (includeHeaders) {
|
|
27684
28839
|
context.fillStyle = palette.headerSurface;
|
|
@@ -27799,8 +28954,8 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27799
28954
|
};
|
|
27800
28955
|
const canvasCellStyle = cellData.canvas ?? buildCanvasCellStyleCache(cellData.style);
|
|
27801
28956
|
const gradientFill = typeof cellData.style.backgroundImage === "string" ? resolveCanvasGradientFill(context, rect, cellData.style.backgroundImage) : null;
|
|
27802
|
-
const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor :
|
|
27803
|
-
const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !==
|
|
28957
|
+
const fillColor = cellData.conditionalColorScale?.color ?? (typeof cellData.style.backgroundColor === "string" ? cellData.style.backgroundColor : thumbnailSheetSurface);
|
|
28958
|
+
const hasExplicitCellFill = cellData.conditionalColorScale !== null || gradientFill !== null || typeof cellData.style.backgroundColor === "string" && cellData.style.backgroundColor !== thumbnailSheetSurface;
|
|
27804
28959
|
context.fillStyle = gradientFill ?? fillColor;
|
|
27805
28960
|
context.fillRect(rect.left, rect.top, rect.width, rect.height);
|
|
27806
28961
|
if (cellData.conditionalDataBar) {
|
|
@@ -27855,12 +29010,19 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27855
29010
|
if (canvasCellStyle.leftBorder) {
|
|
27856
29011
|
strokeCanvasBorderSide(context, "left", rect, canvasCellStyle.leftBorder);
|
|
27857
29012
|
}
|
|
29013
|
+
const rawText = cellData.value ?? "";
|
|
29014
|
+
const shouldDrawThumbnailContent = cellData.checkboxState != null || cellData.sparkline || rawText.length > 0 || cellData.conditionalIcon;
|
|
29015
|
+
if (!shouldDrawThumbnailContent) {
|
|
29016
|
+
continue;
|
|
29017
|
+
}
|
|
27858
29018
|
const padding = canvasCellStyle.padding;
|
|
27859
29019
|
const contentLeft = rect.left + padding.left;
|
|
27860
29020
|
const contentTop = rect.top + padding.top;
|
|
27861
29021
|
const contentWidth = Math.max(0, rect.width - padding.left - padding.right);
|
|
27862
29022
|
const contentHeight = Math.max(0, rect.height - padding.top - padding.bottom);
|
|
27863
|
-
|
|
29023
|
+
if (contentWidth <= 0 || contentHeight <= 0) {
|
|
29024
|
+
continue;
|
|
29025
|
+
}
|
|
27864
29026
|
context.save();
|
|
27865
29027
|
context.beginPath();
|
|
27866
29028
|
context.rect(contentLeft, contentTop, contentWidth, contentHeight);
|
|
@@ -27952,6 +29114,25 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
27952
29114
|
context.restore();
|
|
27953
29115
|
}
|
|
27954
29116
|
}
|
|
29117
|
+
if (thumbnailDrawingEntries.length > 0) {
|
|
29118
|
+
context.save();
|
|
29119
|
+
context.beginPath();
|
|
29120
|
+
context.rect(rowHeaderWidth, headerHeight, Math.max(1, colAxis.totalSize), Math.max(1, rowAxis.totalSize));
|
|
29121
|
+
context.clip();
|
|
29122
|
+
for (const entry of thumbnailDrawingEntries) {
|
|
29123
|
+
if (entry.kind === "shape") {
|
|
29124
|
+
drawStaticShape(context, entry.shape, entry.rect, 1);
|
|
29125
|
+
} else if (entry.kind === "formControl") {
|
|
29126
|
+
drawStaticFormControl(context, entry.control, entry.rect, palette, 1, thumbnailSheetSurface);
|
|
29127
|
+
} else {
|
|
29128
|
+
const imageElement = getThumbnailImage(entry.image);
|
|
29129
|
+
if (imageElement) {
|
|
29130
|
+
context.drawImage(imageElement, entry.rect.left, entry.rect.top, entry.rect.width, entry.rect.height);
|
|
29131
|
+
}
|
|
29132
|
+
}
|
|
29133
|
+
}
|
|
29134
|
+
context.restore();
|
|
29135
|
+
}
|
|
27955
29136
|
if (includeHeaders) {
|
|
27956
29137
|
context.strokeStyle = palette.border;
|
|
27957
29138
|
context.lineWidth = 1;
|
|
@@ -28005,7 +29186,18 @@ function useXlsxViewerThumbnails(options = {}) {
|
|
|
28005
29186
|
workbookSheetIndex: sheet.workbookSheetIndex
|
|
28006
29187
|
};
|
|
28007
29188
|
});
|
|
28008
|
-
}, [
|
|
29189
|
+
}, [
|
|
29190
|
+
getSheetFormControls,
|
|
29191
|
+
getSheetImages,
|
|
29192
|
+
getSheetShapes,
|
|
29193
|
+
getThumbnailImage,
|
|
29194
|
+
includeHeaders,
|
|
29195
|
+
palette,
|
|
29196
|
+
resolution,
|
|
29197
|
+
sheets,
|
|
29198
|
+
thumbnailImageLoadVersion,
|
|
29199
|
+
workbook
|
|
29200
|
+
]);
|
|
28009
29201
|
const paintThumbnail = React4.useCallback(
|
|
28010
29202
|
(sheetIndex, canvas) => thumbnails[sheetIndex]?.paint(canvas) ?? false,
|
|
28011
29203
|
[thumbnails]
|