@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.cjs CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  XlsxViewer: () => XlsxViewer,
36
36
  XlsxViewerProvider: () => XlsxViewerProvider,
37
37
  initWasm: () => initWasm,
38
+ parseXlsxRange: () => parseXlsxRange,
38
39
  setWasmSource: () => setWasmSource,
39
40
  useXlsxViewer: () => useXlsxViewer,
40
41
  useXlsxViewerCharts: () => useXlsxViewerCharts,
@@ -3789,7 +3790,8 @@ function resolveWorksheetMergeMetadata(worksheet) {
3789
3790
  hasHorizontalMerges: false,
3790
3791
  hasVerticalMerges: false,
3791
3792
  maxHorizontalMergeEndCol: -1,
3792
- maxVerticalMergeEndRow: -1
3793
+ maxVerticalMergeEndRow: -1,
3794
+ mergedRanges: []
3793
3795
  };
3794
3796
  const mergedRegions = Array.isArray(worksheet.mergedRegions) ? worksheet.mergedRegions : [];
3795
3797
  for (const rawRegion of mergedRegions) {
@@ -3820,6 +3822,7 @@ function resolveWorksheetMergeMetadata(worksheet) {
3820
3822
  if (!range) {
3821
3823
  continue;
3822
3824
  }
3825
+ mergeMetadata.mergedRanges.push(range);
3823
3826
  if (range.end.col > range.start.col) {
3824
3827
  mergeMetadata.hasHorizontalMerges = true;
3825
3828
  mergeMetadata.maxHorizontalMergeEndCol = Math.max(mergeMetadata.maxHorizontalMergeEndCol, range.end.col);
@@ -6723,6 +6726,102 @@ function tryRecalculate(workbook) {
6723
6726
  }
6724
6727
  }
6725
6728
 
6729
+ // src/range-reference.ts
6730
+ var XLSX_MAX_ROWS = 1048576;
6731
+ var XLSX_MAX_COLUMNS = 16384;
6732
+ function parseColumn(value) {
6733
+ let column = 0;
6734
+ for (const character of value.toUpperCase()) {
6735
+ column = column * 26 + character.charCodeAt(0) - 64;
6736
+ }
6737
+ return column > 0 && column <= XLSX_MAX_COLUMNS ? column - 1 : null;
6738
+ }
6739
+ function parseRow(value) {
6740
+ const row = Number(value);
6741
+ return row > 0 && row <= XLSX_MAX_ROWS ? row - 1 : null;
6742
+ }
6743
+ function parseCell(value) {
6744
+ const match = /^\$?([a-z]{1,3})\$?([1-9]\d{0,6})$/i.exec(value);
6745
+ if (!match) {
6746
+ return null;
6747
+ }
6748
+ const col = parseColumn(match[1]);
6749
+ const row = parseRow(match[2]);
6750
+ return col === null || row === null ? null : { col, row };
6751
+ }
6752
+ function parseXlsxRange(reference) {
6753
+ if (typeof reference !== "string") {
6754
+ return null;
6755
+ }
6756
+ let address = reference.trim().replace(/^=\s*/, "");
6757
+ let sheetName = null;
6758
+ if (address.startsWith("'")) {
6759
+ const match = /^'((?:[^']|'')+)'!(.+)$/.exec(address);
6760
+ if (!match) {
6761
+ return null;
6762
+ }
6763
+ sheetName = match[1].replace(/''/g, "'");
6764
+ address = match[2];
6765
+ } else if (address.includes("!")) {
6766
+ const match = /^([^\s'!\[\]:*?/\\+,;(){}=<>"&^%\-]+)!(.+)$/.exec(address);
6767
+ if (!match) {
6768
+ return null;
6769
+ }
6770
+ sheetName = match[1];
6771
+ address = match[2];
6772
+ }
6773
+ if (sheetName !== null && (sheetName.length > 31 || /[\[\]:*?/\\\u0000-\u001f]/.test(sheetName) || sheetName.startsWith("'") || sheetName.endsWith("'"))) {
6774
+ return null;
6775
+ }
6776
+ const parts = address.split(":");
6777
+ if (parts.length > 2) {
6778
+ return null;
6779
+ }
6780
+ const start = parseCell(parts[0]);
6781
+ const end = parseCell(parts[1] ?? parts[0]);
6782
+ if (start && end) {
6783
+ return {
6784
+ kind: "cells",
6785
+ range: {
6786
+ start: { col: Math.min(start.col, end.col), row: Math.min(start.row, end.row) },
6787
+ end: { col: Math.max(start.col, end.col), row: Math.max(start.row, end.row) }
6788
+ },
6789
+ sheetName
6790
+ };
6791
+ }
6792
+ const columns = /^\$?([a-z]{1,3}):\$?([a-z]{1,3})$/i.exec(address);
6793
+ if (columns) {
6794
+ const first = parseColumn(columns[1]);
6795
+ const last = parseColumn(columns[2]);
6796
+ if (first !== null && last !== null) {
6797
+ return {
6798
+ kind: "columns",
6799
+ range: {
6800
+ start: { col: Math.min(first, last), row: 0 },
6801
+ end: { col: Math.max(first, last), row: XLSX_MAX_ROWS - 1 }
6802
+ },
6803
+ sheetName
6804
+ };
6805
+ }
6806
+ }
6807
+ const rows = /^\$?([1-9]\d{0,6}):\$?([1-9]\d{0,6})$/.exec(address);
6808
+ if (rows) {
6809
+ const first = parseRow(rows[1]);
6810
+ const last = parseRow(rows[2]);
6811
+ if (first !== null && last !== null) {
6812
+ return {
6813
+ kind: "rows",
6814
+ range: {
6815
+ start: { col: 0, row: Math.min(first, last) },
6816
+ end: { col: XLSX_MAX_COLUMNS - 1, row: Math.max(first, last) }
6817
+ },
6818
+ sheetName
6819
+ };
6820
+ }
6821
+ }
6822
+ return null;
6823
+ }
6824
+
6726
6825
  // src/wasm.ts
6727
6826
  var wasmModulePromise = null;
6728
6827
  var hasConfiguredWasmSource = false;
@@ -7345,6 +7444,7 @@ function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette,
7345
7444
  hasVerticalMerges: mergeMetadata.hasVerticalMerges,
7346
7445
  maxHorizontalMergeEndCol: mergeMetadata.maxHorizontalMergeEndCol,
7347
7446
  maxVerticalMergeEndRow: mergeMetadata.maxVerticalMergeEndRow,
7447
+ mergedRanges: mergeMetadata.mergedRanges,
7348
7448
  hiddenCols: [],
7349
7449
  hiddenRows: [],
7350
7450
  minUsedCol: -1,
@@ -7432,6 +7532,7 @@ function buildSheetList(workbook, sheetStatesByWorkbookSheetIndex, themePalette,
7432
7532
  hasVerticalMerges: mergeMetadata.hasVerticalMerges,
7433
7533
  maxHorizontalMergeEndCol: mergeMetadata.maxHorizontalMergeEndCol,
7434
7534
  maxVerticalMergeEndRow: mergeMetadata.maxVerticalMergeEndRow,
7535
+ mergedRanges: mergeMetadata.mergedRanges,
7435
7536
  hiddenCols: resolveWorksheetHiddenCols(worksheet, maxCol),
7436
7537
  hiddenRows: resolveWorksheetHiddenRows(worksheet, maxRow),
7437
7538
  minUsedCol: minCol,
@@ -8433,6 +8534,7 @@ function useXlsxViewerController(options) {
8433
8534
  const [zoomScaleOverridesByTabId, setZoomScaleOverridesByTabId] = React.useState({});
8434
8535
  const [activeCell, setActiveCell] = React.useState(null);
8435
8536
  const [selection, setSelection] = React.useState(null);
8537
+ const [highlightedRanges, setHighlightedRanges] = React.useState([]);
8436
8538
  const [selectedChartId, setSelectedChartId] = React.useState(null);
8437
8539
  const [selectedChartElement, setSelectedChartElement] = React.useState(null);
8438
8540
  const [selectedImageId, setSelectedImageId] = React.useState(null);
@@ -8718,6 +8820,7 @@ function useXlsxViewerController(options) {
8718
8820
  setActiveTabIndexState(0);
8719
8821
  setActiveCell(null);
8720
8822
  setSelection(null);
8823
+ setHighlightedRanges([]);
8721
8824
  setSelectedChartId(null);
8722
8825
  setSelectedChartElement(null);
8723
8826
  setSelectedImageId(null);
@@ -8747,6 +8850,7 @@ function useXlsxViewerController(options) {
8747
8850
  setActiveTabIndexState(0);
8748
8851
  setActiveCell(null);
8749
8852
  setSelection(null);
8853
+ setHighlightedRanges([]);
8750
8854
  setSelectedChartId(null);
8751
8855
  setSelectedChartElement(null);
8752
8856
  setSelectedImageId(null);
@@ -8938,6 +9042,41 @@ function useXlsxViewerController(options) {
8938
9042
  return index;
8939
9043
  });
8940
9044
  }, [tabs]);
9045
+ const highlightRanges = React.useCallback((references, options2 = {}) => {
9046
+ const inputs = typeof references === "string" ? [references] : references;
9047
+ if (!Array.isArray(inputs) || isLoading || error) {
9048
+ return false;
9049
+ }
9050
+ const nextHighlights = [];
9051
+ let firstSheetIndex = activeSheetIndex;
9052
+ for (const reference of inputs) {
9053
+ const parsed = parseXlsxRange(reference);
9054
+ if (!parsed) {
9055
+ return false;
9056
+ }
9057
+ const requestedSheetName = parsed.sheetName?.toLowerCase();
9058
+ const sheetIndex = requestedSheetName === void 0 ? options2.sheetIndex ?? activeSheetIndex : sheets.findIndex((sheet2) => sheet2.name.toLowerCase() === requestedSheetName);
9059
+ const sheet = Number.isInteger(sheetIndex) ? sheets[sheetIndex] : void 0;
9060
+ if (!sheet) {
9061
+ return false;
9062
+ }
9063
+ if (nextHighlights.length === 0) {
9064
+ firstSheetIndex = sheetIndex;
9065
+ }
9066
+ nextHighlights.push({
9067
+ ...parsed,
9068
+ autoScroll: nextHighlights.length === 0 && (options2.autoScroll ?? true),
9069
+ sheetName: sheet.name,
9070
+ workbookSheetIndex: sheet.workbookSheetIndex
9071
+ });
9072
+ }
9073
+ setHighlightedRanges(nextHighlights);
9074
+ if (nextHighlights[0]?.autoScroll) {
9075
+ setActiveSheetIndex(firstSheetIndex);
9076
+ }
9077
+ return true;
9078
+ }, [activeSheetIndex, error, isLoading, setActiveSheetIndex, sheets]);
9079
+ const clearHighlightedRanges = React.useCallback(() => setHighlightedRanges([]), []);
8941
9080
  const setZoomScale = React.useCallback((nextZoomScale) => {
8942
9081
  const normalizedZoomScale = clampZoomScale(nextZoomScale);
8943
9082
  setZoomScaleOverridesByTabId((current) => {
@@ -10996,6 +11135,9 @@ function useXlsxViewerController(options) {
10996
11135
  clearSelectedCells,
10997
11136
  clearSelectedImage,
10998
11137
  clearSelection,
11138
+ clearHighlightedRanges,
11139
+ highlightRanges,
11140
+ highlightedRanges,
10999
11141
  continueDeferredLoad,
11000
11142
  copySelectionToClipboard,
11001
11143
  defaultZoomScale,
@@ -11116,6 +11258,9 @@ function useXlsxViewerController(options) {
11116
11258
  clearSelectedCells,
11117
11259
  clearSelectedImage,
11118
11260
  continueDeferredLoad,
11261
+ clearHighlightedRanges,
11262
+ highlightRanges,
11263
+ highlightedRanges,
11119
11264
  copySelectionToClipboard,
11120
11265
  defaultZoomScale,
11121
11266
  deferredLoadFileSize,
@@ -11799,6 +11944,62 @@ function growScrollDisplayLimit(current, growth, maximum, readOnly) {
11799
11944
  return readOnly ? Math.max(current, Math.min(maximum, nextLimit)) : nextLimit;
11800
11945
  }
11801
11946
 
11947
+ // src/range-highlight.ts
11948
+ function expandHighlightedRange(range, mergedRanges) {
11949
+ const expanded = { start: { ...range.start }, end: { ...range.end } };
11950
+ let changed;
11951
+ do {
11952
+ changed = false;
11953
+ for (const merged of mergedRanges) {
11954
+ 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) {
11955
+ continue;
11956
+ }
11957
+ const startRow = Math.min(expanded.start.row, merged.start.row);
11958
+ const startCol = Math.min(expanded.start.col, merged.start.col);
11959
+ const endRow = Math.max(expanded.end.row, merged.end.row);
11960
+ const endCol = Math.max(expanded.end.col, merged.end.col);
11961
+ if (startRow !== expanded.start.row || startCol !== expanded.start.col || endRow !== expanded.end.row || endCol !== expanded.end.col) {
11962
+ expanded.start = { row: startRow, col: startCol };
11963
+ expanded.end = { row: endRow, col: endCol };
11964
+ changed = true;
11965
+ }
11966
+ }
11967
+ } while (changed);
11968
+ return expanded;
11969
+ }
11970
+ function lowerBound(indices, value) {
11971
+ let low = 0;
11972
+ let high = indices.length;
11973
+ while (low < high) {
11974
+ const middle = low + high >>> 1;
11975
+ if (indices[middle] < value) {
11976
+ low = middle + 1;
11977
+ } else {
11978
+ high = middle;
11979
+ }
11980
+ }
11981
+ return low;
11982
+ }
11983
+ function getHighlightAxisExtent(indices, prefixSums, start, end) {
11984
+ const first = lowerBound(indices, start);
11985
+ const afterLast = lowerBound(indices, end + 1);
11986
+ if (first >= afterLast) {
11987
+ return null;
11988
+ }
11989
+ return { start: prefixSums[first] ?? 0, end: prefixSums[afterLast] ?? 0 };
11990
+ }
11991
+ function getHighlightScrollOffset(start, end, frozenExtent, viewportExtent, currentOffset) {
11992
+ if (end <= frozenExtent || viewportExtent <= frozenExtent) {
11993
+ return currentOffset;
11994
+ }
11995
+ const scrollableStart = Math.max(start, frozenExtent);
11996
+ const availableExtent = viewportExtent - frozenExtent;
11997
+ if (scrollableStart < currentOffset + frozenExtent || end - scrollableStart > availableExtent) {
11998
+ return Math.max(0, scrollableStart - frozenExtent);
11999
+ }
12000
+ return end > currentOffset + viewportExtent ? end - viewportExtent : currentOffset;
12001
+ }
12002
+
11802
12003
  // src/chart-renderer.tsx
11803
12004
  var import_d3_hierarchy = require("d3-hierarchy");
11804
12005
  var import_d3_geo = require("d3-geo");
@@ -18607,6 +18808,17 @@ function resolveViewerPalette(isDark = false, headerBackgroundColor, headerTextC
18607
18808
 
18608
18809
  // src/XlsxViewer.tsx
18609
18810
  var import_jsx_runtime3 = require("react/jsx-runtime");
18811
+ var RANGE_HIGHLIGHT_STYLES = `
18812
+ :where(.react-xlsx-range-highlight) {
18813
+ background: var(--xlsx-highlight-fill, rgb(59 130 246 / 8%));
18814
+ border: var(--xlsx-highlight-border, var(--xlsx-highlight-default-border-width, 1px) solid #3b82f6);
18815
+ border-radius: var(--xlsx-highlight-radius, 2px);
18816
+ }
18817
+ :where(.react-xlsx-range-highlight[data-dark="true"]) {
18818
+ background: var(--xlsx-highlight-fill, rgb(147 197 253 / 12%));
18819
+ border: var(--xlsx-highlight-border, var(--xlsx-highlight-default-border-width, 1px) solid #93c5fd);
18820
+ }
18821
+ `;
18610
18822
  var DEFAULT_ROW_HEIGHT2 = 24;
18611
18823
  var DEFAULT_COL_WIDTH2 = 80;
18612
18824
  var HEADER_HEIGHT = 24;
@@ -24098,6 +24310,7 @@ function XlsxGrid({
24098
24310
  errorState,
24099
24311
  fileTooLargeState,
24100
24312
  getCellStyle,
24313
+ highlightClassName,
24101
24314
  loadingComponent,
24102
24315
  loadingState,
24103
24316
  onFormControlAction,
@@ -24142,6 +24355,7 @@ function XlsxGrid({
24142
24355
  getClipboardData,
24143
24356
  getCellDisplayValue: getControllerCellDisplayValue,
24144
24357
  getFormControlItems,
24358
+ highlightedRanges,
24145
24359
  images,
24146
24360
  shapes,
24147
24361
  isLoadDeferred,
@@ -24188,6 +24402,7 @@ function XlsxGrid({
24188
24402
  ), [formControls, getFormControlItems]);
24189
24403
  const canResizeHeaders = !readOnly || allowResizeInReadOnly;
24190
24404
  const scrollRef = React4.useRef(null);
24405
+ const scrolledHighlightRef = React4.useRef(null);
24191
24406
  const wrapperRef = React4.useRef(null);
24192
24407
  const tableRef = React4.useRef(null);
24193
24408
  const gridKeyboardActiveRef = React4.useRef(false);
@@ -24376,6 +24591,10 @@ function XlsxGrid({
24376
24591
  const resolveMergeAnchorCell = React4.useCallback((cell) => {
24377
24592
  return mergedSecondaryAnchorMap.get(`${cell.row}:${cell.col}`) ?? cell;
24378
24593
  }, [mergedSecondaryAnchorMap]);
24594
+ const highlightedCellRanges = React4.useMemo(
24595
+ () => highlightedRanges.flatMap((highlight, index) => activeSheet?.workbookSheetIndex === highlight.workbookSheetIndex ? [{ highlight, index, range: expandHighlightedRange(highlight.range, activeSheet?.mergedRanges ?? mergedRegions) }] : []),
24596
+ [activeSheet?.mergedRanges, activeSheet?.workbookSheetIndex, highlightedRanges, mergedRegions]
24597
+ );
24379
24598
  const normalizedSelection = React4.useMemo(() => selection ? normalizeRange2(selection) : null, [selection]);
24380
24599
  const zoomFactor = React4.useMemo(() => Math.max(0.1, zoomScale / 100), [zoomScale]);
24381
24600
  const previousZoomFactorRef = React4.useRef(zoomFactor);
@@ -24415,7 +24634,7 @@ function XlsxGrid({
24415
24634
  frozenPaneRight: liveFrozenPaneRight
24416
24635
  } = canvasLayoutMetricsRef.current;
24417
24636
  const currentLiveGestureZoom = liveGestureZoomRef.current;
24418
- const isLiveZooming2 = currentLiveGestureZoom !== null && zoomScale === currentLiveGestureZoom.baseZoomScale;
24637
+ const isLiveZooming2 = experimentalCanvas && currentLiveGestureZoom !== null && zoomScale === currentLiveGestureZoom.baseZoomScale;
24419
24638
  const liveZoomScale2 = isLiveZooming2 ? Math.max(0.1, currentLiveGestureZoom.targetZoomScale / currentLiveGestureZoom.baseZoomScale) : 1;
24420
24639
  const scrollDeltaX = paintedViewport.left - nextLiveViewport.left;
24421
24640
  const scrollDeltaY = paintedViewport.top - nextLiveViewport.top;
@@ -25386,7 +25605,7 @@ function XlsxGrid({
25386
25605
  }, [shouldUseDomVirtualizer, syncDrawingViewport, zoomFactor]);
25387
25606
  React4.useLayoutEffect(() => {
25388
25607
  syncDrawingViewport(scrollRef.current, { immediate: true });
25389
- }, [activeSheet, activeTabIndex, displayColLimit, displayRowLimit, syncDrawingViewport, zoomFactor]);
25608
+ }, [activeSheet, activeTabIndex, displayColLimit, displayRowLimit, highlightedCellRanges, syncDrawingViewport, zoomFactor]);
25390
25609
  React4.useLayoutEffect(() => {
25391
25610
  const scroller = scrollRef.current;
25392
25611
  if (!scroller) {
@@ -26530,11 +26749,11 @@ function XlsxGrid({
26530
26749
  if (textWidth > availableTextWidth) {
26531
26750
  const baseFontSizePx = resolveCanvasFontSizePx(nextData.style, 12 * zoomFactor);
26532
26751
  const minimumFontSizePx = Math.max(6 * zoomFactor, baseFontSizePx * 0.4);
26533
- let lowerBound = minimumFontSizePx;
26752
+ let lowerBound2 = minimumFontSizePx;
26534
26753
  let upperBound = baseFontSizePx;
26535
26754
  let bestFontSizePx = minimumFontSizePx;
26536
- while (upperBound - lowerBound > 0.25) {
26537
- const candidateFontSizePx = (lowerBound + upperBound) / 2;
26755
+ while (upperBound - lowerBound2 > 0.25) {
26756
+ const candidateFontSizePx = (lowerBound2 + upperBound) / 2;
26538
26757
  const candidateStyle = {
26539
26758
  ...nextData.style,
26540
26759
  font: void 0,
@@ -26543,7 +26762,7 @@ function XlsxGrid({
26543
26762
  const candidateWidth = measureTextWidth(nextData.value, candidateStyle);
26544
26763
  if (candidateWidth <= availableTextWidth) {
26545
26764
  bestFontSizePx = candidateFontSizePx;
26546
- lowerBound = candidateFontSizePx;
26765
+ lowerBound2 = candidateFontSizePx;
26547
26766
  } else {
26548
26767
  upperBound = candidateFontSizePx;
26549
26768
  }
@@ -26594,6 +26813,51 @@ function XlsxGrid({
26594
26813
  };
26595
26814
  }, [colIndexByActual, colPrefixSums, displayHeaderHeight, displayRowHeaderWidth, displayedSelection, rowIndexByActual, rowPrefixSums]);
26596
26815
  const resolvedSelectionOverlay = selectionOverlay;
26816
+ const rangeHighlightRects = React4.useMemo(() => highlightedCellRanges.map(({ highlight, index, range }) => {
26817
+ const rows = getHighlightAxisExtent(visibleRows, rowPrefixSums, range.start.row, range.end.row);
26818
+ const cols = getHighlightAxisExtent(visibleCols, colPrefixSums, range.start.col, range.end.col);
26819
+ return {
26820
+ highlight,
26821
+ index,
26822
+ range,
26823
+ rect: rows && cols ? {
26824
+ height: rows.end - rows.start,
26825
+ left: displayRowHeaderWidth + cols.start,
26826
+ top: displayHeaderHeight + rows.start,
26827
+ width: cols.end - cols.start
26828
+ } : null
26829
+ };
26830
+ }), [colPrefixSums, displayHeaderHeight, displayRowHeaderWidth, highlightedCellRanges, rowPrefixSums, visibleCols, visibleRows]);
26831
+ React4.useEffect(() => {
26832
+ let requiredRows = 0;
26833
+ let requiredCols = 0;
26834
+ for (const { highlight, range } of highlightedCellRanges) {
26835
+ if (highlight.kind !== "columns") {
26836
+ requiredRows = Math.max(requiredRows, Math.min(XLSX_MAX_ROWS, range.end.row + OPEN_GRID_ROW_PADDING + 1));
26837
+ }
26838
+ if (highlight.kind !== "rows") {
26839
+ requiredCols = Math.max(requiredCols, Math.min(XLSX_MAX_COLUMNS, range.end.col + OPEN_GRID_COL_PADDING + 1));
26840
+ }
26841
+ }
26842
+ setDisplayRowLimit((current) => Math.max(current, requiredRows));
26843
+ setDisplayColLimit((current) => Math.max(current, requiredCols));
26844
+ }, [activeSheet?.maxUsedCol, activeSheet?.maxUsedRow, highlightedCellRanges, activeSheetIndex, isWorkerBacked]);
26845
+ React4.useEffect(() => {
26846
+ const scroller = scrollRef.current;
26847
+ const target = rangeHighlightRects.find(({ highlight: highlight2 }) => highlight2.autoScroll);
26848
+ 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) {
26849
+ return;
26850
+ }
26851
+ const { highlight, rect } = target;
26852
+ if (highlight.kind !== "columns") {
26853
+ scroller.scrollTop = getHighlightScrollOffset(rect.top, rect.top + rect.height, frozenPaneBottom, scroller.clientHeight, scroller.scrollTop);
26854
+ }
26855
+ if (highlight.kind !== "rows") {
26856
+ scroller.scrollLeft = getHighlightScrollOffset(rect.left, rect.left + rect.width, frozenPaneRight, scroller.clientWidth, scroller.scrollLeft);
26857
+ }
26858
+ scrolledHighlightRef.current = highlight;
26859
+ syncDrawingViewport(scroller, { immediate: true });
26860
+ }, [displayColLimit, displayRowLimit, drawingViewport.height, drawingViewport.width, frozenPaneBottom, frozenPaneRight, isLoading, rangeHighlightRects, syncDrawingViewport]);
26597
26861
  const { fill: selectionFill, header: selectionHeaderSurface, stroke: selectionStroke } = React4.useMemo(() => resolveSelectionColors({
26598
26862
  palette,
26599
26863
  selectionColor,
@@ -29609,6 +29873,7 @@ function XlsxGrid({
29609
29873
  backgroundColor: palette.headerSurface,
29610
29874
  borderBottom: "none",
29611
29875
  borderRight: "none",
29876
+ boxSizing: "border-box",
29612
29877
  boxShadow: gutterSeparatorShadow,
29613
29878
  color: palette.headerText,
29614
29879
  fontSize: "11px",
@@ -31300,6 +31565,51 @@ function XlsxGrid({
31300
31565
  width: "100%"
31301
31566
  }
31302
31567
  };
31568
+ const rangeHighlightNodes = rangeHighlightRects.flatMap(({ index, rect }) => rect ? [
31569
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
31570
+ "div",
31571
+ {
31572
+ "aria-hidden": "true",
31573
+ className: classNames("react-xlsx-range-highlight", highlightClassName),
31574
+ "data-dark": paletteIsDark(palette) ? "true" : void 0,
31575
+ "data-xlsx-range-highlight": index,
31576
+ style: {
31577
+ ...rect,
31578
+ ["--xlsx-highlight-default-border-width"]: `${selectionBorderWidth}px`,
31579
+ boxSizing: "border-box",
31580
+ pointerEvents: "none",
31581
+ position: "absolute",
31582
+ zIndex: maxDrawingOverlayZIndex + 1
31583
+ }
31584
+ },
31585
+ index
31586
+ )
31587
+ ] : []);
31588
+ const drawingOverlayPanes = experimentalCanvas && hasCanvasDomDrawingOverlays || rangeHighlightNodes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { ...canvasBodyViewportLayerStyle, zIndex: experimentalCanvas ? 23 : 34 }, children: [
31589
+ { pane: "scroll", style: canvasScrollOverlayPaneStyle, ref: canvasScrollOverlayContentRef, left: -drawingViewport.left - frozenPaneRight, top: -drawingViewport.top - frozenPaneBottom },
31590
+ { pane: "top", style: canvasTopOverlayPaneStyle, ref: canvasTopOverlayContentRef, left: -drawingViewport.left - frozenPaneRight, top: -displayHeaderHeight },
31591
+ { pane: "left", style: canvasLeftOverlayPaneStyle, ref: canvasLeftOverlayContentRef, left: -displayRowHeaderWidth, top: -drawingViewport.top - frozenPaneBottom },
31592
+ { pane: "corner", style: canvasCornerOverlayPaneStyle, ref: canvasCornerOverlayContentRef, left: -displayRowHeaderWidth, top: -displayHeaderHeight }
31593
+ ].map(({ pane, style, ref, left, top }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
31594
+ "div",
31595
+ {
31596
+ ref,
31597
+ style: {
31598
+ height: sheetContentHeight,
31599
+ left: 0,
31600
+ pointerEvents: "none",
31601
+ position: "absolute",
31602
+ top: 0,
31603
+ transform: `translate(${left}px, ${top}px)`,
31604
+ transformOrigin: "0 0",
31605
+ width: totalWidth
31606
+ },
31607
+ children: [
31608
+ experimentalCanvas ? paneDrawingNodes[pane] : null,
31609
+ rangeHighlightNodes
31610
+ ]
31611
+ }
31612
+ ) }, pane)) }) : null;
31303
31613
  const scrollerContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
31304
31614
  "div",
31305
31615
  {
@@ -31333,6 +31643,7 @@ function XlsxGrid({
31333
31643
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: cornerOverlayStyle, children: paneDrawingNodes.corner }),
31334
31644
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: scrollOverlayStyle, children: paneDrawingNodes.scroll })
31335
31645
  ] }) : null,
31646
+ drawingOverlayPanes,
31336
31647
  experimentalCanvas ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
31337
31648
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: canvasBodyViewportLayerStyle, children: [
31338
31649
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -31374,77 +31685,7 @@ function XlsxGrid({
31374
31685
  onPointerDown: handleCanvasBodyPointerDown,
31375
31686
  style: canvasCornerBodyStyle
31376
31687
  }
31377
- ),
31378
- hasCanvasDomDrawingOverlays ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
31379
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasScrollOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
31380
- "div",
31381
- {
31382
- ref: canvasScrollOverlayContentRef,
31383
- style: {
31384
- height: sheetContentHeight,
31385
- left: 0,
31386
- pointerEvents: "none",
31387
- position: "absolute",
31388
- top: 0,
31389
- transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
31390
- transformOrigin: "0 0",
31391
- width: totalWidth
31392
- },
31393
- children: paneDrawingNodes.scroll
31394
- }
31395
- ) }),
31396
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasTopOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
31397
- "div",
31398
- {
31399
- ref: canvasTopOverlayContentRef,
31400
- style: {
31401
- height: sheetContentHeight,
31402
- left: 0,
31403
- pointerEvents: "none",
31404
- position: "absolute",
31405
- top: 0,
31406
- transform: `translate(${-drawingViewport.left - frozenPaneRight}px, ${-displayHeaderHeight}px)`,
31407
- transformOrigin: "0 0",
31408
- width: totalWidth
31409
- },
31410
- children: paneDrawingNodes.top
31411
- }
31412
- ) }),
31413
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasLeftOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
31414
- "div",
31415
- {
31416
- ref: canvasLeftOverlayContentRef,
31417
- style: {
31418
- height: sheetContentHeight,
31419
- left: 0,
31420
- pointerEvents: "none",
31421
- position: "absolute",
31422
- top: 0,
31423
- transform: `translate(${-displayRowHeaderWidth}px, ${-drawingViewport.top - frozenPaneBottom}px)`,
31424
- transformOrigin: "0 0",
31425
- width: totalWidth
31426
- },
31427
- children: paneDrawingNodes.left
31428
- }
31429
- ) }),
31430
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: canvasCornerOverlayPaneStyle, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
31431
- "div",
31432
- {
31433
- ref: canvasCornerOverlayContentRef,
31434
- style: {
31435
- height: sheetContentHeight,
31436
- left: 0,
31437
- pointerEvents: "none",
31438
- position: "absolute",
31439
- top: 0,
31440
- transform: `translate(${-displayRowHeaderWidth}px, ${-displayHeaderHeight}px)`,
31441
- transformOrigin: "0 0",
31442
- width: totalWidth
31443
- },
31444
- children: paneDrawingNodes.corner
31445
- }
31446
- ) })
31447
- ] }) : null
31688
+ )
31448
31689
  ] }),
31449
31690
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: canvasHeaderViewportLayerStyle, children: [
31450
31691
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -31890,6 +32131,7 @@ function XlsxGrid({
31890
32131
  }
31891
32132
  function XlsxViewerInner({
31892
32133
  allowResizeInReadOnly = false,
32134
+ autoScrollToHighlightedRanges = true,
31893
32135
  className,
31894
32136
  controller,
31895
32137
  emptyState,
@@ -31902,6 +32144,8 @@ function XlsxViewerInner({
31902
32144
  headerBackgroundColor,
31903
32145
  headerTextColor,
31904
32146
  height,
32147
+ highlightClassName,
32148
+ highlightedRanges,
31905
32149
  isDark = false,
31906
32150
  loadingComponent,
31907
32151
  loadingState,
@@ -31924,6 +32168,26 @@ function XlsxViewerInner({
31924
32168
  }) {
31925
32169
  const palette = useViewerPalette(isDark, headerBackgroundColor, headerTextColor);
31926
32170
  const { displayFileName, error } = controller;
32171
+ const appliedHighlightRef = React4.useRef(null);
32172
+ const { clearHighlightedRanges, file, highlightRanges, isLoading, sheets, src } = controller;
32173
+ React4.useEffect(() => {
32174
+ if (isLoading || highlightedRanges === void 0) {
32175
+ appliedHighlightRef.current = null;
32176
+ return;
32177
+ }
32178
+ if (highlightedRanges !== null && sheets.length === 0) {
32179
+ return;
32180
+ }
32181
+ const referenceKey = JSON.stringify(typeof highlightedRanges === "string" ? [highlightedRanges] : highlightedRanges ?? []);
32182
+ const previous = appliedHighlightRef.current;
32183
+ if (previous && previous.referenceKey === referenceKey && previous.autoScroll === autoScrollToHighlightedRanges && previous.file === file && previous.src === src && previous.clear === clearHighlightedRanges) {
32184
+ return;
32185
+ }
32186
+ appliedHighlightRef.current = { autoScroll: autoScrollToHighlightedRanges, clear: clearHighlightedRanges, file, referenceKey, src };
32187
+ if (highlightedRanges === null || !highlightRanges(highlightedRanges, { autoScroll: autoScrollToHighlightedRanges })) {
32188
+ clearHighlightedRanges();
32189
+ }
32190
+ }, [autoScrollToHighlightedRanges, clearHighlightedRanges, file, highlightRanges, highlightedRanges, isLoading, sheets.length, src]);
31927
32191
  const customFileTooLarge = error instanceof XlsxFileSizeLimitExceededError ? renderCustomFileTooLarge(
31928
32192
  fileTooLargeState,
31929
32193
  {
@@ -31956,6 +32220,7 @@ function XlsxViewerInner({
31956
32220
  width: "100%"
31957
32221
  },
31958
32222
  children: [
32223
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: RANGE_HIGHLIGHT_STYLES }),
31959
32224
  resolveToolbar(toolbar, showDefaultToolbar, controller, palette),
31960
32225
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", flex: 1, minHeight: 0, minWidth: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
31961
32226
  XlsxGrid,
@@ -31969,6 +32234,7 @@ function XlsxViewerInner({
31969
32234
  experimentalCanvas,
31970
32235
  fileTooLargeState,
31971
32236
  getCellStyle,
32237
+ highlightClassName,
31972
32238
  loadingComponent,
31973
32239
  loadingState,
31974
32240
  onFormControlAction,
@@ -33126,6 +33392,7 @@ function DefaultXlsxToolbar() {
33126
33392
  XlsxViewer,
33127
33393
  XlsxViewerProvider,
33128
33394
  initWasm,
33395
+ parseXlsxRange,
33129
33396
  setWasmSource,
33130
33397
  useXlsxViewer,
33131
33398
  useXlsxViewerCharts,