@extend-ai/react-xlsx 0.16.6 → 0.17.0

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/dist/index.js CHANGED
@@ -3739,7 +3739,8 @@ function resolveWorksheetMergeMetadata(worksheet) {
3739
3739
  hasHorizontalMerges: false,
3740
3740
  hasVerticalMerges: false,
3741
3741
  maxHorizontalMergeEndCol: -1,
3742
- maxVerticalMergeEndRow: -1
3742
+ maxVerticalMergeEndRow: -1,
3743
+ mergedRanges: []
3743
3744
  };
3744
3745
  const mergedRegions = Array.isArray(worksheet.mergedRegions) ? worksheet.mergedRegions : [];
3745
3746
  for (const rawRegion of mergedRegions) {
@@ -3770,6 +3771,7 @@ function resolveWorksheetMergeMetadata(worksheet) {
3770
3771
  if (!range) {
3771
3772
  continue;
3772
3773
  }
3774
+ mergeMetadata.mergedRanges.push(range);
3773
3775
  if (range.end.col > range.start.col) {
3774
3776
  mergeMetadata.hasHorizontalMerges = true;
3775
3777
  mergeMetadata.maxHorizontalMergeEndCol = Math.max(mergeMetadata.maxHorizontalMergeEndCol, range.end.col);
@@ -6673,6 +6675,102 @@ function tryRecalculate(workbook) {
6673
6675
  }
6674
6676
  }
6675
6677
 
6678
+ // src/range-reference.ts
6679
+ var XLSX_MAX_ROWS = 1048576;
6680
+ var XLSX_MAX_COLUMNS = 16384;
6681
+ function parseColumn(value) {
6682
+ let column = 0;
6683
+ for (const character of value.toUpperCase()) {
6684
+ column = column * 26 + character.charCodeAt(0) - 64;
6685
+ }
6686
+ return column > 0 && column <= XLSX_MAX_COLUMNS ? column - 1 : null;
6687
+ }
6688
+ function parseRow(value) {
6689
+ const row = Number(value);
6690
+ return row > 0 && row <= XLSX_MAX_ROWS ? row - 1 : null;
6691
+ }
6692
+ function parseCell(value) {
6693
+ const match = /^\$?([a-z]{1,3})\$?([1-9]\d{0,6})$/i.exec(value);
6694
+ if (!match) {
6695
+ return null;
6696
+ }
6697
+ const col = parseColumn(match[1]);
6698
+ const row = parseRow(match[2]);
6699
+ return col === null || row === null ? null : { col, row };
6700
+ }
6701
+ function parseXlsxRange(reference) {
6702
+ if (typeof reference !== "string") {
6703
+ return null;
6704
+ }
6705
+ let address = reference.trim().replace(/^=\s*/, "");
6706
+ let sheetName = null;
6707
+ if (address.startsWith("'")) {
6708
+ const match = /^'((?:[^']|'')+)'!(.+)$/.exec(address);
6709
+ if (!match) {
6710
+ return null;
6711
+ }
6712
+ sheetName = match[1].replace(/''/g, "'");
6713
+ address = match[2];
6714
+ } else if (address.includes("!")) {
6715
+ const match = /^([^\s'!\[\]:*?/\\+,;(){}=<>"&^%\-]+)!(.+)$/.exec(address);
6716
+ if (!match) {
6717
+ return null;
6718
+ }
6719
+ sheetName = match[1];
6720
+ address = match[2];
6721
+ }
6722
+ if (sheetName !== null && (sheetName.length > 31 || /[\[\]:*?/\\\u0000-\u001f]/.test(sheetName) || sheetName.startsWith("'") || sheetName.endsWith("'"))) {
6723
+ return null;
6724
+ }
6725
+ const parts = address.split(":");
6726
+ if (parts.length > 2) {
6727
+ return null;
6728
+ }
6729
+ const start = parseCell(parts[0]);
6730
+ const end = parseCell(parts[1] ?? parts[0]);
6731
+ if (start && end) {
6732
+ return {
6733
+ kind: "cells",
6734
+ range: {
6735
+ start: { col: Math.min(start.col, end.col), row: Math.min(start.row, end.row) },
6736
+ end: { col: Math.max(start.col, end.col), row: Math.max(start.row, end.row) }
6737
+ },
6738
+ sheetName
6739
+ };
6740
+ }
6741
+ const columns = /^\$?([a-z]{1,3}):\$?([a-z]{1,3})$/i.exec(address);
6742
+ if (columns) {
6743
+ const first = parseColumn(columns[1]);
6744
+ const last = parseColumn(columns[2]);
6745
+ if (first !== null && last !== null) {
6746
+ return {
6747
+ kind: "columns",
6748
+ range: {
6749
+ start: { col: Math.min(first, last), row: 0 },
6750
+ end: { col: Math.max(first, last), row: XLSX_MAX_ROWS - 1 }
6751
+ },
6752
+ sheetName
6753
+ };
6754
+ }
6755
+ }
6756
+ const rows = /^\$?([1-9]\d{0,6}):\$?([1-9]\d{0,6})$/.exec(address);
6757
+ if (rows) {
6758
+ const first = parseRow(rows[1]);
6759
+ const last = parseRow(rows[2]);
6760
+ if (first !== null && last !== null) {
6761
+ return {
6762
+ kind: "rows",
6763
+ range: {
6764
+ start: { col: 0, row: Math.min(first, last) },
6765
+ end: { col: XLSX_MAX_COLUMNS - 1, row: Math.max(first, last) }
6766
+ },
6767
+ sheetName
6768
+ };
6769
+ }
6770
+ }
6771
+ return null;
6772
+ }
6773
+
6676
6774
  // src/wasm.ts
6677
6775
  var wasmModulePromise = null;
6678
6776
  var hasConfiguredWasmSource = false;
@@ -7294,6 +7392,7 @@ function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette,
7294
7392
  hasVerticalMerges: mergeMetadata.hasVerticalMerges,
7295
7393
  maxHorizontalMergeEndCol: mergeMetadata.maxHorizontalMergeEndCol,
7296
7394
  maxVerticalMergeEndRow: mergeMetadata.maxVerticalMergeEndRow,
7395
+ mergedRanges: mergeMetadata.mergedRanges,
7297
7396
  hiddenCols: [],
7298
7397
  hiddenRows: [],
7299
7398
  minUsedCol: -1,
@@ -7381,6 +7480,7 @@ function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette,
7381
7480
  hasVerticalMerges: mergeMetadata.hasVerticalMerges,
7382
7481
  maxHorizontalMergeEndCol: mergeMetadata.maxHorizontalMergeEndCol,
7383
7482
  maxVerticalMergeEndRow: mergeMetadata.maxVerticalMergeEndRow,
7483
+ mergedRanges: mergeMetadata.mergedRanges,
7384
7484
  hiddenCols: resolveWorksheetHiddenCols(worksheet, maxCol),
7385
7485
  hiddenRows: resolveWorksheetHiddenRows(worksheet, maxRow),
7386
7486
  minUsedCol: minCol,
@@ -8382,6 +8482,7 @@ function useXlsxViewerController(options) {
8382
8482
  const [zoomScaleOverridesByTabId, setZoomScaleOverridesByTabId] = React.useState({});
8383
8483
  const [activeCell, setActiveCell] = React.useState(null);
8384
8484
  const [selection, setSelection] = React.useState(null);
8485
+ const [highlightedRanges, setHighlightedRanges] = React.useState([]);
8385
8486
  const [selectedChartId, setSelectedChartId] = React.useState(null);
8386
8487
  const [selectedChartElement, setSelectedChartElement] = React.useState(null);
8387
8488
  const [selectedImageId, setSelectedImageId] = React.useState(null);
@@ -8667,6 +8768,7 @@ function useXlsxViewerController(options) {
8667
8768
  setActiveTabIndexState(0);
8668
8769
  setActiveCell(null);
8669
8770
  setSelection(null);
8771
+ setHighlightedRanges([]);
8670
8772
  setSelectedChartId(null);
8671
8773
  setSelectedChartElement(null);
8672
8774
  setSelectedImageId(null);
@@ -8696,6 +8798,7 @@ function useXlsxViewerController(options) {
8696
8798
  setActiveTabIndexState(0);
8697
8799
  setActiveCell(null);
8698
8800
  setSelection(null);
8801
+ setHighlightedRanges([]);
8699
8802
  setSelectedChartId(null);
8700
8803
  setSelectedChartElement(null);
8701
8804
  setSelectedImageId(null);
@@ -8887,6 +8990,41 @@ function useXlsxViewerController(options) {
8887
8990
  return index;
8888
8991
  });
8889
8992
  }, [tabs]);
8993
+ const highlightRanges = React.useCallback((references, options2 = {}) => {
8994
+ const inputs = typeof references === "string" ? [references] : references;
8995
+ if (!Array.isArray(inputs) || isLoading || error) {
8996
+ return false;
8997
+ }
8998
+ const nextHighlights = [];
8999
+ let firstSheetIndex = activeSheetIndex;
9000
+ for (const reference of inputs) {
9001
+ const parsed = parseXlsxRange(reference);
9002
+ if (!parsed) {
9003
+ return false;
9004
+ }
9005
+ const requestedSheetName = parsed.sheetName?.toLowerCase();
9006
+ const sheetIndex = requestedSheetName === void 0 ? options2.sheetIndex ?? activeSheetIndex : sheets.findIndex((sheet2) => sheet2.name.toLowerCase() === requestedSheetName);
9007
+ const sheet = Number.isInteger(sheetIndex) ? sheets[sheetIndex] : void 0;
9008
+ if (!sheet) {
9009
+ return false;
9010
+ }
9011
+ if (nextHighlights.length === 0) {
9012
+ firstSheetIndex = sheetIndex;
9013
+ }
9014
+ nextHighlights.push({
9015
+ ...parsed,
9016
+ autoScroll: nextHighlights.length === 0 && (options2.autoScroll ?? true),
9017
+ sheetName: sheet.name,
9018
+ workbookSheetIndex: sheet.workbookSheetIndex
9019
+ });
9020
+ }
9021
+ setHighlightedRanges(nextHighlights);
9022
+ if (nextHighlights[0]?.autoScroll) {
9023
+ setActiveSheetIndex(firstSheetIndex);
9024
+ }
9025
+ return true;
9026
+ }, [activeSheetIndex, error, isLoading, setActiveSheetIndex, sheets]);
9027
+ const clearHighlightedRanges = React.useCallback(() => setHighlightedRanges([]), []);
8890
9028
  const setZoomScale = React.useCallback((nextZoomScale) => {
8891
9029
  const normalizedZoomScale = clampZoomScale(nextZoomScale);
8892
9030
  setZoomScaleOverridesByTabId((current) => {
@@ -10945,6 +11083,9 @@ function useXlsxViewerController(options) {
10945
11083
  clearSelectedCells,
10946
11084
  clearSelectedImage,
10947
11085
  clearSelection,
11086
+ clearHighlightedRanges,
11087
+ highlightRanges,
11088
+ highlightedRanges,
10948
11089
  continueDeferredLoad,
10949
11090
  copySelectionToClipboard,
10950
11091
  defaultZoomScale,
@@ -11065,6 +11206,9 @@ function useXlsxViewerController(options) {
11065
11206
  clearSelectedCells,
11066
11207
  clearSelectedImage,
11067
11208
  continueDeferredLoad,
11209
+ clearHighlightedRanges,
11210
+ highlightRanges,
11211
+ highlightedRanges,
11068
11212
  copySelectionToClipboard,
11069
11213
  defaultZoomScale,
11070
11214
  deferredLoadFileSize,
@@ -11750,6 +11894,62 @@ function growScrollDisplayLimit(current, growth, maximum, readOnly) {
11750
11894
  return readOnly ? Math.max(current, Math.min(maximum, nextLimit)) : nextLimit;
11751
11895
  }
11752
11896
 
11897
+ // src/range-highlight.ts
11898
+ function expandHighlightedRange(range, mergedRanges) {
11899
+ const expanded = { start: { ...range.start }, end: { ...range.end } };
11900
+ let changed;
11901
+ do {
11902
+ changed = false;
11903
+ for (const merged of mergedRanges) {
11904
+ if (merged.end.row < expanded.start.row || merged.start.row > expanded.end.row || merged.end.col < expanded.start.col || merged.start.col > expanded.end.col) {
11905
+ continue;
11906
+ }
11907
+ const startRow = Math.min(expanded.start.row, merged.start.row);
11908
+ const startCol = Math.min(expanded.start.col, merged.start.col);
11909
+ const endRow = Math.max(expanded.end.row, merged.end.row);
11910
+ const endCol = Math.max(expanded.end.col, merged.end.col);
11911
+ if (startRow !== expanded.start.row || startCol !== expanded.start.col || endRow !== expanded.end.row || endCol !== expanded.end.col) {
11912
+ expanded.start = { row: startRow, col: startCol };
11913
+ expanded.end = { row: endRow, col: endCol };
11914
+ changed = true;
11915
+ }
11916
+ }
11917
+ } while (changed);
11918
+ return expanded;
11919
+ }
11920
+ function lowerBound(indices, value) {
11921
+ let low = 0;
11922
+ let high = indices.length;
11923
+ while (low < high) {
11924
+ const middle = low + high >>> 1;
11925
+ if (indices[middle] < value) {
11926
+ low = middle + 1;
11927
+ } else {
11928
+ high = middle;
11929
+ }
11930
+ }
11931
+ return low;
11932
+ }
11933
+ function getHighlightAxisExtent(indices, prefixSums, start, end) {
11934
+ const first = lowerBound(indices, start);
11935
+ const afterLast = lowerBound(indices, end + 1);
11936
+ if (first >= afterLast) {
11937
+ return null;
11938
+ }
11939
+ return { start: prefixSums[first] ?? 0, end: prefixSums[afterLast] ?? 0 };
11940
+ }
11941
+ function getHighlightScrollOffset(start, end, frozenExtent, viewportExtent, currentOffset) {
11942
+ if (end <= frozenExtent || viewportExtent <= frozenExtent) {
11943
+ return currentOffset;
11944
+ }
11945
+ const scrollableStart = Math.max(start, frozenExtent);
11946
+ const availableExtent = viewportExtent - frozenExtent;
11947
+ if (scrollableStart < currentOffset + frozenExtent || end - scrollableStart > availableExtent) {
11948
+ return Math.max(0, scrollableStart - frozenExtent);
11949
+ }
11950
+ return end > currentOffset + viewportExtent ? end - viewportExtent : currentOffset;
11951
+ }
11952
+
11753
11953
  // src/chart-renderer.tsx
11754
11954
  import { hierarchy as d3Hierarchy, partition as d3Partition, treemap as d3Treemap, treemapBinary, treemapDice } from "d3-hierarchy";
11755
11955
  import { geoIdentity, geoMercator, geoNaturalEarth1, geoPath } from "d3-geo";
@@ -18573,6 +18773,17 @@ function resolveViewerPalette(isDark = false, headerBackgroundColor, headerTextC
18573
18773
 
18574
18774
  // src/XlsxViewer.tsx
18575
18775
  import { Fragment as Fragment4, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
18776
+ var RANGE_HIGHLIGHT_STYLES = `
18777
+ :where(.react-xlsx-range-highlight) {
18778
+ background: var(--xlsx-highlight-fill, rgb(59 130 246 / 8%));
18779
+ border: var(--xlsx-highlight-border, var(--xlsx-highlight-default-border-width, 1px) solid #3b82f6);
18780
+ border-radius: var(--xlsx-highlight-radius, 2px);
18781
+ }
18782
+ :where(.react-xlsx-range-highlight[data-dark="true"]) {
18783
+ background: var(--xlsx-highlight-fill, rgb(147 197 253 / 12%));
18784
+ border: var(--xlsx-highlight-border, var(--xlsx-highlight-default-border-width, 1px) solid #93c5fd);
18785
+ }
18786
+ `;
18576
18787
  var DEFAULT_ROW_HEIGHT2 = 24;
18577
18788
  var DEFAULT_COL_WIDTH2 = 80;
18578
18789
  var HEADER_HEIGHT = 24;
@@ -24064,6 +24275,7 @@ function XlsxGrid({
24064
24275
  errorState,
24065
24276
  fileTooLargeState,
24066
24277
  getCellStyle,
24278
+ highlightClassName,
24067
24279
  loadingComponent,
24068
24280
  loadingState,
24069
24281
  onFormControlAction,
@@ -24108,6 +24320,7 @@ function XlsxGrid({
24108
24320
  getClipboardData,
24109
24321
  getCellDisplayValue: getControllerCellDisplayValue,
24110
24322
  getFormControlItems,
24323
+ highlightedRanges,
24111
24324
  images,
24112
24325
  shapes,
24113
24326
  isLoadDeferred,
@@ -24154,6 +24367,7 @@ function XlsxGrid({
24154
24367
  ), [formControls, getFormControlItems]);
24155
24368
  const canResizeHeaders = !readOnly || allowResizeInReadOnly;
24156
24369
  const scrollRef = React4.useRef(null);
24370
+ const scrolledHighlightRef = React4.useRef(null);
24157
24371
  const wrapperRef = React4.useRef(null);
24158
24372
  const tableRef = React4.useRef(null);
24159
24373
  const gridKeyboardActiveRef = React4.useRef(false);
@@ -24342,6 +24556,10 @@ function XlsxGrid({
24342
24556
  const resolveMergeAnchorCell = React4.useCallback((cell) => {
24343
24557
  return mergedSecondaryAnchorMap.get(`${cell.row}:${cell.col}`) ?? cell;
24344
24558
  }, [mergedSecondaryAnchorMap]);
24559
+ const highlightedCellRanges = React4.useMemo(
24560
+ () => highlightedRanges.flatMap((highlight, index) => activeSheet?.workbookSheetIndex === highlight.workbookSheetIndex ? [{ highlight, index, range: expandHighlightedRange(highlight.range, activeSheet?.mergedRanges ?? mergedRegions) }] : []),
24561
+ [activeSheet?.mergedRanges, activeSheet?.workbookSheetIndex, highlightedRanges, mergedRegions]
24562
+ );
24345
24563
  const normalizedSelection = React4.useMemo(() => selection ? normalizeRange2(selection) : null, [selection]);
24346
24564
  const zoomFactor = React4.useMemo(() => Math.max(0.1, zoomScale / 100), [zoomScale]);
24347
24565
  const previousZoomFactorRef = React4.useRef(zoomFactor);
@@ -24381,7 +24599,7 @@ function XlsxGrid({
24381
24599
  frozenPaneRight: liveFrozenPaneRight
24382
24600
  } = canvasLayoutMetricsRef.current;
24383
24601
  const currentLiveGestureZoom = liveGestureZoomRef.current;
24384
- const isLiveZooming2 = currentLiveGestureZoom !== null && zoomScale === currentLiveGestureZoom.baseZoomScale;
24602
+ const isLiveZooming2 = experimentalCanvas && currentLiveGestureZoom !== null && zoomScale === currentLiveGestureZoom.baseZoomScale;
24385
24603
  const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
24386
24604
  const scrollDeltaX = paintedViewport.left - nextLiveViewport.left;
24387
24605
  const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
@@ -25352,7 +25570,7 @@ function XlsxGrid({
25352
25570
  }, [shouldUseDomVirtualizer, syncDrawingViewport, zoomFactor]);
25353
25571
  React4.useLayoutEffect(() => {
25354
25572
  syncDrawingViewport(scrollRef.current, { immediate: true });
25355
- }, [activeSheet, activeTabIndex, displayColLimit, displayRowLimit, syncDrawingViewport, zoomFactor]);
25573
+ }, [activeSheet, activeTabIndex, displayColLimit, displayRowLimit, highlightedCellRanges, syncDrawingViewport, zoomFactor]);
25356
25574
  React4.useLayoutEffect(() => {
25357
25575
  const scroller = scrollRef.current;
25358
25576
  if (!scroller) {
@@ -26496,11 +26714,11 @@ function XlsxGrid({
26496
26714
  if (textWidth > availableTextWidth) {
26497
26715
  const baseFontSizePx = resolveCanvasFontSizePx(nextData.style, 12 * zoomFactor);
26498
26716
  const minimumFontSizePx = Math.max(6 * zoomFactor, baseFontSizePx * 0.4);
26499
- let lowerBound = minimumFontSizePx;
26717
+ let lowerBound2 = minimumFontSizePx;
26500
26718
  let upperBound = baseFontSizePx;
26501
26719
  let bestFontSizePx = minimumFontSizePx;
26502
- while (upperBound - lowerBound > 0.25) {
26503
- const candidateFontSizePx = (lowerBound + upperBound) / 2;
26720
+ while (upperBound - lowerBound2 > 0.25) {
26721
+ const candidateFontSizePx = (lowerBound2 + upperBound) / 2;
26504
26722
  const candidateStyle = {
26505
26723
  ...nextData.style,
26506
26724
  font: void 0,
@@ -26509,7 +26727,7 @@ function XlsxGrid({
26509
26727
  const candidateWidth = measureTextWidth(nextData.value, candidateStyle);
26510
26728
  if (candidateWidth <= availableTextWidth) {
26511
26729
  bestFontSizePx = candidateFontSizePx;
26512
- lowerBound = candidateFontSizePx;
26730
+ lowerBound2 = candidateFontSizePx;
26513
26731
  } else {
26514
26732
  upperBound = candidateFontSizePx;
26515
26733
  }
@@ -26560,6 +26778,51 @@ function XlsxGrid({
26560
26778
  };
26561
26779
  }, [colIndexByActual, colPrefixSums, displayHeaderHeight, displayRowHeaderWidth, displayedSelection, rowIndexByActual, rowPrefixSums]);
26562
26780
  const resolvedSelectionOverlay = selectionOverlay;
26781
+ const rangeHighlightRects = React4.useMemo(() => highlightedCellRanges.map(({ highlight, index, range }) => {
26782
+ const rows = getHighlightAxisExtent(visibleRows, rowPrefixSums, range.start.row, range.end.row);
26783
+ const cols = getHighlightAxisExtent(visibleCols, colPrefixSums, range.start.col, range.end.col);
26784
+ return {
26785
+ highlight,
26786
+ index,
26787
+ range,
26788
+ rect: rows && cols ? {
26789
+ height: rows.end - rows.start,
26790
+ left: displayRowHeaderWidth + cols.start,
26791
+ top: displayHeaderHeight + rows.start,
26792
+ width: cols.end - cols.start
26793
+ } : null
26794
+ };
26795
+ }), [colPrefixSums, displayHeaderHeight, displayRowHeaderWidth, highlightedCellRanges, rowPrefixSums, visibleCols, visibleRows]);
26796
+ React4.useEffect(() => {
26797
+ let requiredRows = 0;
26798
+ let requiredCols = 0;
26799
+ for (const { highlight, range } of highlightedCellRanges) {
26800
+ if (highlight.kind !== "columns") {
26801
+ requiredRows = Math.max(requiredRows, Math.min(XLSX_MAX_ROWS, range.end.row + OPEN_GRID_ROW_PADDING + 1));
26802
+ }
26803
+ if (highlight.kind !== "rows") {
26804
+ requiredCols = Math.max(requiredCols, Math.min(XLSX_MAX_COLUMNS, range.end.col + OPEN_GRID_COL_PADDING + 1));
26805
+ }
26806
+ }
26807
+ setDisplayRowLimit((current) => Math.max(current, requiredRows));
26808
+ setDisplayColLimit((current) => Math.max(current, requiredCols));
26809
+ }, [activeSheet?.maxUsedCol, activeSheet?.maxUsedRow, highlightedCellRanges, activeSheetIndex, isWorkerBacked]);
26810
+ React4.useEffect(() => {
26811
+ const scroller = scrollRef.current;
26812
+ const target = rangeHighlightRects.find(({ highlight: highlight2 }) => highlight2.autoScroll);
26813
+ if (!scroller || !target?.rect || scrolledHighlightRef.current === target.highlight || isLoading || scroller.clientWidth === 0 || scroller.clientHeight === 0 || target.highlight.kind !== "columns" && displayRowLimit <= target.range.end.row || target.highlight.kind !== "rows" && displayColLimit <= target.range.end.col) {
26814
+ return;
26815
+ }
26816
+ const { highlight, rect } = target;
26817
+ if (highlight.kind !== "columns") {
26818
+ scroller.scrollTop = getHighlightScrollOffset(rect.top, rect.top + rect.height, frozenPaneBottom, scroller.clientHeight, scroller.scrollTop);
26819
+ }
26820
+ if (highlight.kind !== "rows") {
26821
+ scroller.scrollLeft = getHighlightScrollOffset(rect.left, rect.left + rect.width, frozenPaneRight, scroller.clientWidth, scroller.scrollLeft);
26822
+ }
26823
+ scrolledHighlightRef.current = highlight;
26824
+ syncDrawingViewport(scroller, { immediate: true });
26825
+ }, [displayColLimit, displayRowLimit, drawingViewport.height, drawingViewport.width, frozenPaneBottom, frozenPaneRight, isLoading, rangeHighlightRects, syncDrawingViewport]);
26563
26826
  const { fill: selectionFill, header: selectionHeaderSurface, stroke: selectionStroke } = React4.useMemo(() => resolveSelectionColors({
26564
26827
  palette,
26565
26828
  selectionColor,
@@ -29575,6 +29838,7 @@ function XlsxGrid({
29575
29838
  backgroundColor: palette.headerSurface,
29576
29839
  borderBottom: "none",
29577
29840
  borderRight: "none",
29841
+ boxSizing: "border-box",
29578
29842
  boxShadow: gutterSeparatorShadow,
29579
29843
  color: palette.headerText,
29580
29844
  fontSize: "11px",
@@ -31266,6 +31530,51 @@ function XlsxGrid({
31266
31530
  width: "100%"
31267
31531
  }
31268
31532
  };
31533
+ const rangeHighlightNodes = rangeHighlightRects.flatMap(({ index, rect }) => rect ? [
31534
+ /* @__PURE__ */ jsx3(
31535
+ "div",
31536
+ {
31537
+ "aria-hidden": "true",
31538
+ className: classNames("react-xlsx-range-highlight", highlightClassName),
31539
+ "data-dark": paletteIsDark(palette) ? "true" : void 0,
31540
+ "data-xlsx-range-highlight": index,
31541
+ style: {
31542
+ ...rect,
31543
+ ["--xlsx-highlight-default-border-width"]: `${selectionBorderWidth}px`,
31544
+ boxSizing: "border-box",
31545
+ pointerEvents: "none",
31546
+ position: "absolute",
31547
+ zIndex: maxDrawingOverlayZIndex + 1
31548
+ }
31549
+ },
31550
+ index
31551
+ )
31552
+ ] : []);
31553
+ const drawingOverlayPanes = experimentalCanvas && hasCanvasDomDrawingOverlays || rangeHighlightNodes.length > 0 ? /* @__PURE__ */ jsx3("div", { style: { ...canvasBodyViewportLayerStyle, zIndex: experimentalCanvas ? 23 : 34 }, children: [
31554
+ { pane: "scroll", style: canvasScrollOverlayPaneStyle, ref: canvasScrollOverlayContentRef, left: -drawingViewport.left - frozenPaneRight, top: -drawingViewport.top - frozenPaneBottom },
31555
+ { pane: "top", style: canvasTopOverlayPaneStyle, ref: canvasTopOverlayContentRef, left: -drawingViewport.left - frozenPaneRight, top: -displayHeaderHeight },
31556
+ { pane: "left", style: canvasLeftOverlayPaneStyle, ref: canvasLeftOverlayContentRef, left: -displayRowHeaderWidth, top: -drawingViewport.top - frozenPaneBottom },
31557
+ { pane: "corner", style: canvasCornerOverlayPaneStyle, ref: canvasCornerOverlayContentRef, left: -displayRowHeaderWidth, top: -displayHeaderHeight }
31558
+ ].map(({ pane, style, ref, left, top }) => /* @__PURE__ */ jsx3("div", { style, children: /* @__PURE__ */ jsxs3(
31559
+ "div",
31560
+ {
31561
+ ref,
31562
+ style: {
31563
+ height: sheetContentHeight,
31564
+ left: 0,
31565
+ pointerEvents: "none",
31566
+ position: "absolute",
31567
+ top: 0,
31568
+ transform: `translate(${left}px, ${top}px)`,
31569
+ transformOrigin: "0 0",
31570
+ width: totalWidth
31571
+ },
31572
+ children: [
31573
+ experimentalCanvas ? paneDrawingNodes[pane] : null,
31574
+ rangeHighlightNodes
31575
+ ]
31576
+ }
31577
+ ) }, pane)) }) : null;
31269
31578
  const scrollerContent = /* @__PURE__ */ jsx3(
31270
31579
  "div",
31271
31580
  {
@@ -31299,6 +31608,7 @@ function XlsxGrid({
31299
31608
  /* @__PURE__ */ jsx3("div", { style: cornerOverlayStyle, children: paneDrawingNodes.corner }),
31300
31609
  /* @__PURE__ */ jsx3("div", { style: scrollOverlayStyle, children: paneDrawingNodes.scroll })
31301
31610
  ] }) : null,
31611
+ drawingOverlayPanes,
31302
31612
  experimentalCanvas ? /* @__PURE__ */ jsxs3(Fragment4, { children: [
31303
31613
  /* @__PURE__ */ jsxs3("div", { style: canvasBodyViewportLayerStyle, children: [
31304
31614
  /* @__PURE__ */ jsx3(
@@ -31340,77 +31650,7 @@ function XlsxGrid({
31340
31650
  onPointerDown: handleCanvasBodyPointerDown,
31341
31651
  style: canvasCornerBodyStyle
31342
31652
  }
31343
- ),
31344
- hasCanvasDomDrawingOverlays ? /* @__PURE__ */ jsxs3(Fragment4, { children: [
31345
- /* @__PURE__ */ jsx3("div", { style: canvasScrollOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
31346
- "div",
31347
- {
31348
- ref: canvasScrollOverlayContentRef,
31349
- style: {
31350
- height: sheetContentHeight,
31351
- left: 0,
31352
- pointerEvents: "none",
31353
- position: "absolute",
31354
- top: 0,
31355
- transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
31356
- transformOrigin: "0 0",
31357
- width: totalWidth
31358
- },
31359
- children: paneDrawingNodes.scroll
31360
- }
31361
- ) }),
31362
- /* @__PURE__ */ jsx3("div", { style: canvasTopOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
31363
- "div",
31364
- {
31365
- ref: canvasTopOverlayContentRef,
31366
- style: {
31367
- height: sheetContentHeight,
31368
- left: 0,
31369
- pointerEvents: "none",
31370
- position: "absolute",
31371
- top: 0,
31372
- transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-displayHeaderHeight}px)`,
31373
- transformOrigin: "0 0",
31374
- width: totalWidth
31375
- },
31376
- children: paneDrawingNodes.top
31377
- }
31378
- ) }),
31379
- /* @__PURE__ */ jsx3("div", { style: canvasLeftOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
31380
- "div",
31381
- {
31382
- ref: canvasLeftOverlayContentRef,
31383
- style: {
31384
- height: sheetContentHeight,
31385
- left: 0,
31386
- pointerEvents: "none",
31387
- position: "absolute",
31388
- top: 0,
31389
- transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
31390
- transformOrigin: "0 0",
31391
- width: totalWidth
31392
- },
31393
- children: paneDrawingNodes.left
31394
- }
31395
- ) }),
31396
- /* @__PURE__ */ jsx3("div", { style: canvasCornerOverlayPaneStyle, children: /* @__PURE__ */ jsx3(
31397
- "div",
31398
- {
31399
- ref: canvasCornerOverlayContentRef,
31400
- style: {
31401
- height: sheetContentHeight,
31402
- left: 0,
31403
- pointerEvents: "none",
31404
- position: "absolute",
31405
- top: 0,
31406
- transform: `translate(${-displayRowHeaderWidth}px, ${-displayHeaderHeight}px)`,
31407
- transformOrigin: "0 0",
31408
- width: totalWidth
31409
- },
31410
- children: paneDrawingNodes.corner
31411
- }
31412
- ) })
31413
- ] }) : null
31653
+ )
31414
31654
  ] }),
31415
31655
  /* @__PURE__ */ jsxs3("div", { style: canvasHeaderViewportLayerStyle, children: [
31416
31656
  /* @__PURE__ */ jsx3(
@@ -31856,6 +32096,7 @@ function XlsxGrid({
31856
32096
  }
31857
32097
  function XlsxViewerInner({
31858
32098
  allowResizeInReadOnly = false,
32099
+ autoScrollToHighlightedRanges = true,
31859
32100
  className,
31860
32101
  controller,
31861
32102
  emptyState,
@@ -31868,6 +32109,8 @@ function XlsxViewerInner({
31868
32109
  headerBackgroundColor,
31869
32110
  headerTextColor,
31870
32111
  height,
32112
+ highlightClassName,
32113
+ highlightedRanges,
31871
32114
  isDark = false,
31872
32115
  loadingComponent,
31873
32116
  loadingState,
@@ -31890,6 +32133,26 @@ function XlsxViewerInner({
31890
32133
  }) {
31891
32134
  const palette = useViewerPalette(isDark, headerBackgroundColor, headerTextColor);
31892
32135
  const { displayFileName, error } = controller;
32136
+ const appliedHighlightRef = React4.useRef(null);
32137
+ const { clearHighlightedRanges, file, highlightRanges, isLoading, sheets, src } = controller;
32138
+ React4.useEffect(() => {
32139
+ if (isLoading || highlightedRanges === void 0) {
32140
+ appliedHighlightRef.current = null;
32141
+ return;
32142
+ }
32143
+ if (highlightedRanges !== null && sheets.length === 0) {
32144
+ return;
32145
+ }
32146
+ const referenceKey = JSON.stringify(typeof highlightedRanges === "string" ? [highlightedRanges] : highlightedRanges ?? []);
32147
+ const previous = appliedHighlightRef.current;
32148
+ if (previous && previous.referenceKey === referenceKey && previous.autoScroll === autoScrollToHighlightedRanges && previous.file === file && previous.src === src && previous.clear === clearHighlightedRanges) {
32149
+ return;
32150
+ }
32151
+ appliedHighlightRef.current = { autoScroll: autoScrollToHighlightedRanges, clear: clearHighlightedRanges, file, referenceKey, src };
32152
+ if (highlightedRanges === null || !highlightRanges(highlightedRanges, { autoScroll: autoScrollToHighlightedRanges })) {
32153
+ clearHighlightedRanges();
32154
+ }
32155
+ }, [autoScrollToHighlightedRanges, clearHighlightedRanges, file, highlightRanges, highlightedRanges, isLoading, sheets.length, src]);
31893
32156
  const customFileTooLarge = error instanceof XlsxFileSizeLimitExceededError ? renderCustomFileTooLarge(
31894
32157
  fileTooLargeState,
31895
32158
  {
@@ -31922,6 +32185,7 @@ function XlsxViewerInner({
31922
32185
  width: "100%"
31923
32186
  },
31924
32187
  children: [
32188
+ /* @__PURE__ */ jsx3("style", { children: RANGE_HIGHLIGHT_STYLES }),
31925
32189
  resolveToolbar(toolbar, showDefaultToolbar, controller, palette),
31926
32190
  /* @__PURE__ */ jsx3("div", { style: { display: "flex", flex: 1, minHeight: 0, minWidth: 0 }, children: /* @__PURE__ */ jsx3(
31927
32191
  XlsxGrid,
@@ -31935,6 +32199,7 @@ function XlsxViewerInner({
31935
32199
  experimentalCanvas,
31936
32200
  fileTooLargeState,
31937
32201
  getCellStyle,
32202
+ highlightClassName,
31938
32203
  loadingComponent,
31939
32204
  loadingState,
31940
32205
  onFormControlAction,
@@ -33091,6 +33356,7 @@ export {
33091
33356
  XlsxViewer,
33092
33357
  XlsxViewerProvider,
33093
33358
  initWasm,
33359
+ parseXlsxRange,
33094
33360
  setWasmSource,
33095
33361
  useXlsxViewer,
33096
33362
  useXlsxViewerCharts,