@extend-ai/react-xlsx 0.10.0 → 0.10.2

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
@@ -17376,6 +17376,18 @@ function rangesEqual(left, right) {
17376
17376
  function isPrintableKey(event) {
17377
17377
  return event.key.length === 1 && !event.altKey && !event.ctrlKey && !event.metaKey;
17378
17378
  }
17379
+ function getInteractiveFocusTarget(target) {
17380
+ if (!(target instanceof Element)) {
17381
+ return null;
17382
+ }
17383
+ return target.closest(
17384
+ "a[href], button, input, select, textarea, [contenteditable=''], [contenteditable='true'], [role='button'], [role='menu'], [role='menuitem'], [role='textbox'], [tabindex]:not([tabindex='-1'])"
17385
+ );
17386
+ }
17387
+ function isInteractiveFocusTarget(target, container) {
17388
+ const interactiveElement = getInteractiveFocusTarget(target);
17389
+ return Boolean(interactiveElement && interactiveElement !== container && container.contains(interactiveElement));
17390
+ }
17379
17391
  function buildPrefixSums(values) {
17380
17392
  const prefix = new Array(values.length + 1).fill(0);
17381
17393
  for (let index = 0; index < values.length; index += 1) {
@@ -21053,6 +21065,21 @@ function resolveFirstUsedVisibleIndex(visibleIndices, minUsedIndex) {
21053
21065
  const firstVisibleUsed = visibleIndices.find((index) => index >= minUsedIndex);
21054
21066
  return firstVisibleUsed ?? visibleIndices[0] ?? -1;
21055
21067
  }
21068
+ function resolveLastUsedVisibleIndex(visibleIndices, maxUsedIndex) {
21069
+ if (visibleIndices.length === 0) {
21070
+ return -1;
21071
+ }
21072
+ if (maxUsedIndex < 0) {
21073
+ return visibleIndices[visibleIndices.length - 1] ?? -1;
21074
+ }
21075
+ for (let index = visibleIndices.length - 1; index >= 0; index -= 1) {
21076
+ const visibleIndex = visibleIndices[index];
21077
+ if (visibleIndex !== void 0 && visibleIndex <= maxUsedIndex) {
21078
+ return visibleIndex;
21079
+ }
21080
+ }
21081
+ return visibleIndices[visibleIndices.length - 1] ?? -1;
21082
+ }
21056
21083
  function GridRow({
21057
21084
  actualRow,
21058
21085
  editingCell,
@@ -21064,8 +21091,10 @@ function GridRow({
21064
21091
  onCellClick,
21065
21092
  onCellDoubleClick,
21066
21093
  onCellPointerDown,
21094
+ onEditingBlur,
21067
21095
  onEditingCancel,
21068
21096
  onEditingCommit,
21097
+ onEditingNavigate,
21069
21098
  onEditingValueChange,
21070
21099
  onRowHeaderRef,
21071
21100
  onRowPointerDown,
@@ -21351,7 +21380,7 @@ function GridRow({
21351
21380
  "input",
21352
21381
  {
21353
21382
  autoFocus: true,
21354
- onBlur: onEditingCommit,
21383
+ onBlur: onEditingBlur,
21355
21384
  onChange: (event) => onEditingValueChange(event.target.value),
21356
21385
  onKeyDown: (event) => {
21357
21386
  event.stopPropagation();
@@ -21363,6 +21392,11 @@ function GridRow({
21363
21392
  if (event.key === "Escape") {
21364
21393
  event.preventDefault();
21365
21394
  onEditingCancel();
21395
+ return;
21396
+ }
21397
+ if (event.key === "ArrowDown" || event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp") {
21398
+ event.preventDefault();
21399
+ onEditingNavigate(event.key, event.currentTarget.value);
21366
21400
  }
21367
21401
  },
21368
21402
  style: {
@@ -21438,7 +21472,7 @@ function GridRow({
21438
21472
  ] });
21439
21473
  }
21440
21474
  var MemoGridRow = React4.memo(GridRow, (prev, next) => {
21441
- if (prev.actualRow !== next.actualRow || prev.rowHeight !== next.rowHeight || prev.headerLabelLiveScale !== next.headerLabelLiveScale || prev.palette !== next.palette || prev.readOnly !== next.readOnly || prev.visibleCols !== next.visibleCols || prev.leadingSpacerWidth !== next.leadingSpacerWidth || prev.rowHeaderWidth !== next.rowHeaderWidth || prev.stickyLeftByCol !== next.stickyLeftByCol || prev.stickyTop !== next.stickyTop || prev.trailingSpacerWidth !== next.trailingSpacerWidth || prev.zoomFactor !== next.zoomFactor || prev.getCellData !== next.getCellData || prev.onCellClick !== next.onCellClick || prev.onCellDoubleClick !== next.onCellDoubleClick || prev.onCellPointerDown !== next.onCellPointerDown || prev.onEditingCancel !== next.onEditingCancel || prev.onEditingCommit !== next.onEditingCommit || prev.onEditingValueChange !== next.onEditingValueChange || prev.onRowHeaderRef !== next.onRowHeaderRef || prev.onRowPointerDown !== next.onRowPointerDown || prev.onRowResizePointerDown !== next.onRowResizePointerDown || prev.renderCellAdornment !== next.renderCellAdornment) {
21475
+ if (prev.actualRow !== next.actualRow || prev.rowHeight !== next.rowHeight || prev.headerLabelLiveScale !== next.headerLabelLiveScale || prev.palette !== next.palette || prev.readOnly !== next.readOnly || prev.visibleCols !== next.visibleCols || prev.leadingSpacerWidth !== next.leadingSpacerWidth || prev.rowHeaderWidth !== next.rowHeaderWidth || prev.stickyLeftByCol !== next.stickyLeftByCol || prev.stickyTop !== next.stickyTop || prev.trailingSpacerWidth !== next.trailingSpacerWidth || prev.zoomFactor !== next.zoomFactor || prev.getCellData !== next.getCellData || prev.onCellClick !== next.onCellClick || prev.onCellDoubleClick !== next.onCellDoubleClick || prev.onCellPointerDown !== next.onCellPointerDown || prev.onEditingBlur !== next.onEditingBlur || prev.onEditingCancel !== next.onEditingCancel || prev.onEditingCommit !== next.onEditingCommit || prev.onEditingNavigate !== next.onEditingNavigate || prev.onEditingValueChange !== next.onEditingValueChange || prev.onRowHeaderRef !== next.onRowHeaderRef || prev.onRowPointerDown !== next.onRowPointerDown || prev.onRowResizePointerDown !== next.onRowResizePointerDown || prev.renderCellAdornment !== next.renderCellAdornment) {
21442
21476
  return false;
21443
21477
  }
21444
21478
  const prevEditingCol = prev.editingCell?.row === prev.actualRow ? prev.editingCell.col : -1;
@@ -21539,6 +21573,9 @@ function XlsxGrid({
21539
21573
  const scrollRef = React4.useRef(null);
21540
21574
  const wrapperRef = React4.useRef(null);
21541
21575
  const tableRef = React4.useRef(null);
21576
+ const gridKeyboardActiveRef = React4.useRef(false);
21577
+ const gridKeyboardHandlerRef = React4.useRef(null);
21578
+ const axisSelectionRef = React4.useRef(null);
21542
21579
  const scrollBodyCanvasRef = React4.useRef(null);
21543
21580
  const topBodyCanvasRef = React4.useRef(null);
21544
21581
  const leftBodyCanvasRef = React4.useRef(null);
@@ -23180,9 +23217,50 @@ function XlsxGrid({
23180
23217
  selectionPreviewRangeRef.current = null;
23181
23218
  setInteractionMode("idle");
23182
23219
  }, [activeSheetIndex, clearGlobalCursor, revision]);
23183
- const focusGrid = React4.useCallback(() => {
23184
- scrollRef.current?.focus();
23220
+ const focusGridElement = React4.useCallback((element, options) => {
23221
+ if (document.activeElement !== element) {
23222
+ element.focus(options ?? { preventScroll: true });
23223
+ }
23185
23224
  }, []);
23225
+ const focusGrid = React4.useCallback((options) => {
23226
+ const scroller = scrollRef.current;
23227
+ if (scroller) {
23228
+ gridKeyboardActiveRef.current = true;
23229
+ focusGridElement(scroller, options);
23230
+ }
23231
+ }, [focusGridElement]);
23232
+ React4.useEffect(() => {
23233
+ const handleDocumentPointerDown = (event) => {
23234
+ const scroller = scrollRef.current;
23235
+ if (!scroller || !(event.target instanceof Node) || !scroller.contains(event.target)) {
23236
+ gridKeyboardActiveRef.current = false;
23237
+ return;
23238
+ }
23239
+ if (isInteractiveFocusTarget(event.target, scroller)) {
23240
+ gridKeyboardActiveRef.current = false;
23241
+ return;
23242
+ }
23243
+ gridKeyboardActiveRef.current = true;
23244
+ focusGridElement(scroller);
23245
+ };
23246
+ const handleDocumentKeyDown = (event) => {
23247
+ if (!gridKeyboardActiveRef.current || event.defaultPrevented) {
23248
+ return;
23249
+ }
23250
+ const scroller = scrollRef.current;
23251
+ const interactiveTarget = getInteractiveFocusTarget(event.target);
23252
+ if (!scroller || interactiveTarget && interactiveTarget !== scroller) {
23253
+ return;
23254
+ }
23255
+ gridKeyboardHandlerRef.current?.(event);
23256
+ };
23257
+ document.addEventListener("pointerdown", handleDocumentPointerDown, true);
23258
+ document.addEventListener("keydown", handleDocumentKeyDown);
23259
+ return () => {
23260
+ document.removeEventListener("pointerdown", handleDocumentPointerDown, true);
23261
+ document.removeEventListener("keydown", handleDocumentKeyDown);
23262
+ };
23263
+ }, [focusGridElement]);
23186
23264
  const openHyperlink = React4.useCallback((target, location) => {
23187
23265
  const internalTarget = parseInternalSheetLink(location ?? target);
23188
23266
  if (internalTarget) {
@@ -23221,21 +23299,74 @@ function XlsxGrid({
23221
23299
  return;
23222
23300
  }
23223
23301
  if (readOnly) {
23302
+ editingCellRef.current = null;
23224
23303
  setEditingCell(null);
23225
23304
  setEditingValue("");
23226
23305
  focusGrid();
23227
23306
  return;
23228
23307
  }
23229
23308
  setCellValue(editingCell, editingValue);
23309
+ editingCellRef.current = null;
23230
23310
  setEditingCell(null);
23231
23311
  setEditingValue("");
23232
23312
  focusGrid();
23233
23313
  }, [editingCell, editingValue, focusGrid, readOnly, setCellValue]);
23234
23314
  const cancelEditing = React4.useCallback(() => {
23315
+ editingCellRef.current = null;
23235
23316
  setEditingCell(null);
23236
23317
  setEditingValue("");
23237
23318
  focusGrid();
23238
23319
  }, [focusGrid]);
23320
+ const handleEditingBlur = React4.useCallback(() => {
23321
+ if (!editingCellRef.current) {
23322
+ return;
23323
+ }
23324
+ commitEditing();
23325
+ }, [commitEditing]);
23326
+ const handleEditingNavigate = React4.useCallback((key, value) => {
23327
+ const cell = editingCellRef.current;
23328
+ if (!cell) {
23329
+ return;
23330
+ }
23331
+ const currentRowIndex = rowIndexByActual.get(cell.row);
23332
+ const currentColIndex = colIndexByActual.get(cell.col);
23333
+ if (currentRowIndex === void 0 || currentColIndex === void 0) {
23334
+ if (!readOnlyRef.current) {
23335
+ setCellValue(cell, value);
23336
+ }
23337
+ editingCellRef.current = null;
23338
+ setEditingCell(null);
23339
+ setEditingValue("");
23340
+ focusGrid();
23341
+ return;
23342
+ }
23343
+ let nextRowIndex = currentRowIndex;
23344
+ let nextColIndex = currentColIndex;
23345
+ switch (key) {
23346
+ case "ArrowDown":
23347
+ nextRowIndex += 1;
23348
+ break;
23349
+ case "ArrowLeft":
23350
+ nextColIndex -= 1;
23351
+ break;
23352
+ case "ArrowRight":
23353
+ nextColIndex += 1;
23354
+ break;
23355
+ case "ArrowUp":
23356
+ nextRowIndex -= 1;
23357
+ break;
23358
+ default:
23359
+ return;
23360
+ }
23361
+ if (!readOnlyRef.current) {
23362
+ setCellValue(cell, value);
23363
+ }
23364
+ editingCellRef.current = null;
23365
+ setEditingCell(null);
23366
+ setEditingValue("");
23367
+ moveSelection(nextRowIndex, nextColIndex, false);
23368
+ focusGrid();
23369
+ }, [colIndexByActual, focusGrid, rowIndexByActual, setCellValue, visibleCols, visibleRows]);
23239
23370
  React4.useEffect(() => {
23240
23371
  commitEditingRef.current = commitEditing;
23241
23372
  }, [commitEditing]);
@@ -24208,6 +24339,7 @@ function XlsxGrid({
24208
24339
  overlay.style.visibility = "visible";
24209
24340
  }, [getCellData, resolveOverlayRect, zoomFactor]);
24210
24341
  const commitSelectionRange = React4.useCallback((range) => {
24342
+ gridKeyboardActiveRef.current = true;
24211
24343
  const normalized = normalizeRange2(range);
24212
24344
  if (selectionRef.current && rangesEqual(selectionRef.current, normalized) && isSameCell(activeCellRef.current, normalized.end) && selectedChartIdRef.current === null && selectedImageIdRef.current === null) {
24213
24345
  return;
@@ -24501,6 +24633,23 @@ function XlsxGrid({
24501
24633
  if (nextRange && (dragState?.didDrag || !dragState?.committedOnPointerDown)) {
24502
24634
  selectionPreviewRangeRef.current = nextRange;
24503
24635
  displayedSelectionRef.current = nextRange;
24636
+ if (dragState?.axis === "column") {
24637
+ const normalized = normalizeRange2(nextRange);
24638
+ axisSelectionRef.current = {
24639
+ axis: "column",
24640
+ startCol: normalized.start.col,
24641
+ endCol: normalized.end.col
24642
+ };
24643
+ } else if (dragState?.axis === "row") {
24644
+ const normalized = normalizeRange2(nextRange);
24645
+ axisSelectionRef.current = {
24646
+ axis: "row",
24647
+ startRow: normalized.start.row,
24648
+ endRow: normalized.end.row
24649
+ };
24650
+ } else {
24651
+ axisSelectionRef.current = null;
24652
+ }
24504
24653
  commitSelectionRange(nextRange);
24505
24654
  } else if (!nextRange) {
24506
24655
  selectionPreviewRangeRef.current = null;
@@ -24619,6 +24768,7 @@ function XlsxGrid({
24619
24768
  }
24620
24769
  event.preventDefault();
24621
24770
  focusGrid();
24771
+ axisSelectionRef.current = null;
24622
24772
  const targetCell = event.currentTarget.colSpan > 1 || event.currentTarget.rowSpan > 1 ? resolvePointerCellFromGeometry(event.clientX, event.clientY) ?? cell : cell;
24623
24773
  const currentSelection = selectionRef.current;
24624
24774
  const anchor = event.shiftKey && currentSelection ? currentSelection.start : targetCell;
@@ -24694,6 +24844,7 @@ function XlsxGrid({
24694
24844
  event.clientX,
24695
24845
  event.clientY
24696
24846
  );
24847
+ axisSelectionRef.current = { axis: "row", startRow: anchorRow, endRow: actualRow };
24697
24848
  commitSelectionRange(initialRange);
24698
24849
  }, [commitSelectionRange, firstVisibleCol, focusGrid, lastVisibleCol, resolveRowPointerOrigin]);
24699
24850
  const handleColumnPointerDown = React4.useCallback((event, actualCol) => {
@@ -24725,6 +24876,7 @@ function XlsxGrid({
24725
24876
  event.clientX,
24726
24877
  event.clientY
24727
24878
  );
24879
+ axisSelectionRef.current = { axis: "column", startCol: anchorCol, endCol: actualCol };
24728
24880
  commitSelectionRange(initialRange);
24729
24881
  }, [commitSelectionRange, firstVisibleRow, focusGrid, lastVisibleRow, resolveColumnPointerOrigin]);
24730
24882
  const handleRowResizePointerDown = React4.useCallback((event, actualRow, rowHeight) => {
@@ -24981,6 +25133,7 @@ function XlsxGrid({
24981
25133
  }
24982
25134
  event.preventDefault();
24983
25135
  focusGrid();
25136
+ axisSelectionRef.current = null;
24984
25137
  const currentSelection = selectionRef.current;
24985
25138
  const anchor = event.shiftKey && currentSelection ? currentSelection.start : cell;
24986
25139
  const initialRange = normalizeRange2({ start: anchor, end: cell });
@@ -25090,6 +25243,7 @@ function XlsxGrid({
25090
25243
  event.clientX,
25091
25244
  event.clientY
25092
25245
  );
25246
+ axisSelectionRef.current = { axis: "column", startCol: anchorCol, endCol: actualCol };
25093
25247
  commitSelectionRange(initialRange);
25094
25248
  }, [
25095
25249
  colIndexByActual,
@@ -25148,6 +25302,7 @@ function XlsxGrid({
25148
25302
  event.clientX,
25149
25303
  event.clientY
25150
25304
  );
25305
+ axisSelectionRef.current = { axis: "row", startRow: anchorRow, endRow: actualRow };
25151
25306
  commitSelectionRange(initialRange);
25152
25307
  }, [
25153
25308
  colPrefixSums,
@@ -27607,17 +27762,298 @@ function XlsxGrid({
27607
27762
  }
27608
27763
  return { row: firstVisibleRow, col: firstVisibleCol };
27609
27764
  }
27765
+ function ensureCellVisible(rowIndex, colIndex) {
27766
+ const scroller = scrollRef.current;
27767
+ if (!scroller) {
27768
+ return;
27769
+ }
27770
+ let nextScrollTop = scroller.scrollTop;
27771
+ let nextScrollLeft = scroller.scrollLeft;
27772
+ const rowStart = displayHeaderHeight + (rowPrefixSums[rowIndex] ?? 0);
27773
+ const rowEnd = displayHeaderHeight + (rowPrefixSums[rowIndex + 1] ?? rowStart);
27774
+ const colStart = displayRowHeaderWidth + (colPrefixSums[colIndex] ?? 0);
27775
+ const colEnd = displayRowHeaderWidth + (colPrefixSums[colIndex + 1] ?? colStart);
27776
+ if (rowEnd > frozenPaneBottom) {
27777
+ const visibleTop = scroller.scrollTop + frozenPaneBottom;
27778
+ const visibleBottom = scroller.scrollTop + scroller.clientHeight;
27779
+ if (rowStart < visibleTop) {
27780
+ nextScrollTop = rowStart - frozenPaneBottom;
27781
+ } else if (rowEnd > visibleBottom) {
27782
+ nextScrollTop = rowEnd - scroller.clientHeight;
27783
+ }
27784
+ }
27785
+ if (colEnd > frozenPaneRight) {
27786
+ const visibleLeft = scroller.scrollLeft + frozenPaneRight;
27787
+ const visibleRight = scroller.scrollLeft + scroller.clientWidth;
27788
+ if (colStart < visibleLeft) {
27789
+ nextScrollLeft = colStart - frozenPaneRight;
27790
+ } else if (colEnd > visibleRight) {
27791
+ nextScrollLeft = colEnd - scroller.clientWidth;
27792
+ }
27793
+ }
27794
+ nextScrollTop = Math.max(0, Math.min(nextScrollTop, scroller.scrollHeight - scroller.clientHeight));
27795
+ nextScrollLeft = Math.max(0, Math.min(nextScrollLeft, scroller.scrollWidth - scroller.clientWidth));
27796
+ if (nextScrollTop !== scroller.scrollTop) {
27797
+ scroller.scrollTop = nextScrollTop;
27798
+ }
27799
+ if (nextScrollLeft !== scroller.scrollLeft) {
27800
+ scroller.scrollLeft = nextScrollLeft;
27801
+ }
27802
+ syncDrawingViewport(scroller, { immediate: true });
27803
+ }
27610
27804
  function moveSelection(nextRowIndex, nextColIndex, extend) {
27611
- const nextRow = visibleRows[nextRowIndex];
27612
- const nextCol = visibleCols[nextColIndex];
27805
+ const clampedRowIndex = Math.max(0, Math.min(nextRowIndex, visibleRows.length - 1));
27806
+ const clampedColIndex = Math.max(0, Math.min(nextColIndex, visibleCols.length - 1));
27807
+ const nextRow = visibleRows[clampedRowIndex];
27808
+ const nextCol = visibleCols[clampedColIndex];
27613
27809
  if (nextRow === void 0 || nextCol === void 0) {
27614
27810
  return;
27615
27811
  }
27812
+ const nextRange = { start: { row: nextRow, col: nextCol }, end: { row: nextRow, col: nextCol } };
27813
+ axisSelectionRef.current = null;
27814
+ selectionPreviewRangeRef.current = null;
27815
+ displayedSelectionRef.current = nextRange;
27816
+ applyPreviewOverlay(nextRange);
27616
27817
  selectCell({ row: nextRow, col: nextCol }, extend ? { extend: true } : void 0);
27818
+ ensureCellVisible(clampedRowIndex, clampedColIndex);
27617
27819
  }
27820
+ function resolvePageRowIndex(currentRowIndex, direction) {
27821
+ const scroller = scrollRef.current;
27822
+ const viewportHeight = Math.max(
27823
+ displayDefaultRowHeight,
27824
+ (scroller?.clientHeight ?? displayDefaultRowHeight * 20) - frozenPaneBottom
27825
+ );
27826
+ const currentOffset = rowPrefixSums[currentRowIndex] ?? 0;
27827
+ const targetOffset = direction > 0 ? currentOffset + viewportHeight : currentOffset - viewportHeight;
27828
+ return Math.max(0, Math.min(findIndexForOffsetPrefix(rowPrefixSums, targetOffset), visibleRows.length - 1));
27829
+ }
27830
+ function resolvePageColIndex(currentColIndex, direction) {
27831
+ const scroller = scrollRef.current;
27832
+ const viewportWidth = Math.max(
27833
+ displayDefaultColWidth,
27834
+ (scroller?.clientWidth ?? displayDefaultColWidth * 8) - frozenPaneRight
27835
+ );
27836
+ const currentOffset = colPrefixSums[currentColIndex] ?? 0;
27837
+ const targetOffset = direction > 0 ? currentOffset + viewportWidth : currentOffset - viewportWidth;
27838
+ return Math.max(0, Math.min(findIndexForOffsetPrefix(colPrefixSums, targetOffset), visibleCols.length - 1));
27839
+ }
27840
+ function resolveColumnSelectionNavigation(eventKey) {
27841
+ const axisSelection = axisSelectionRef.current;
27842
+ if (axisSelection?.axis !== "column" || firstVisibleRow === void 0) {
27843
+ return null;
27844
+ }
27845
+ const startCol = Math.min(axisSelection.startCol, axisSelection.endCol);
27846
+ const endCol = Math.max(axisSelection.startCol, axisSelection.endCol);
27847
+ const targetCol = eventKey === "ArrowLeft" ? startCol - 1 : eventKey === "ArrowRight" ? endCol + 1 : null;
27848
+ if (targetCol === null) {
27849
+ return null;
27850
+ }
27851
+ const rowIndex = rowIndexByActual.get(firstVisibleRow);
27852
+ const colIndex = colIndexByActual.get(targetCol);
27853
+ if (rowIndex === void 0 || colIndex === void 0) {
27854
+ return null;
27855
+ }
27856
+ return { colIndex, rowIndex };
27857
+ }
27858
+ function resolveRowSelectionNavigation(eventKey) {
27859
+ const axisSelection = axisSelectionRef.current;
27860
+ if (axisSelection?.axis !== "row" || firstVisibleCol === void 0) {
27861
+ return null;
27862
+ }
27863
+ const startRow = Math.min(axisSelection.startRow, axisSelection.endRow);
27864
+ const endRow = Math.max(axisSelection.startRow, axisSelection.endRow);
27865
+ const targetRow = eventKey === "ArrowUp" ? startRow - 1 : eventKey === "ArrowDown" ? endRow + 1 : null;
27866
+ if (targetRow === null) {
27867
+ return null;
27868
+ }
27869
+ const rowIndex = rowIndexByActual.get(targetRow);
27870
+ const colIndex = colIndexByActual.get(firstVisibleCol);
27871
+ if (rowIndex === void 0 || colIndex === void 0) {
27872
+ return null;
27873
+ }
27874
+ return { colIndex, rowIndex };
27875
+ }
27876
+ function handleGridKeyDown(event) {
27877
+ if (editingCell) {
27878
+ return;
27879
+ }
27880
+ if (!readOnly && (event.metaKey || event.ctrlKey) && !event.altKey) {
27881
+ const normalizedKey = event.key.toLowerCase();
27882
+ if (normalizedKey === "z" && event.shiftKey) {
27883
+ event.preventDefault();
27884
+ redo();
27885
+ return;
27886
+ }
27887
+ if (normalizedKey === "z") {
27888
+ event.preventDefault();
27889
+ undo();
27890
+ return;
27891
+ }
27892
+ if (normalizedKey === "y") {
27893
+ event.preventDefault();
27894
+ redo();
27895
+ return;
27896
+ }
27897
+ }
27898
+ const currentCell = resolveCurrentCell();
27899
+ if (!currentCell) {
27900
+ return;
27901
+ }
27902
+ const currentRowIndex = rowIndexByActual.get(currentCell.row) ?? 0;
27903
+ const currentColIndex = colIndexByActual.get(currentCell.col) ?? 0;
27904
+ const isCommandNavigation = event.ctrlKey || event.metaKey;
27905
+ if (!readOnly && isPrintableKey(event)) {
27906
+ event.preventDefault();
27907
+ startEditing(currentCell, event.key);
27908
+ return;
27909
+ }
27910
+ switch (event.key) {
27911
+ case "ArrowDown":
27912
+ event.preventDefault();
27913
+ if (!event.shiftKey && !isCommandNavigation) {
27914
+ const target = resolveRowSelectionNavigation(event.key);
27915
+ if (target) {
27916
+ moveSelection(target.rowIndex, target.colIndex, false);
27917
+ break;
27918
+ }
27919
+ }
27920
+ moveSelection(
27921
+ isCommandNavigation ? rowIndexByActual.get(resolveLastUsedVisibleIndex(visibleRows, activeSheet?.maxUsedRow ?? -1)) ?? visibleRows.length - 1 : currentRowIndex + 1,
27922
+ currentColIndex,
27923
+ event.shiftKey
27924
+ );
27925
+ break;
27926
+ case "ArrowUp":
27927
+ event.preventDefault();
27928
+ if (!event.shiftKey && !isCommandNavigation) {
27929
+ const target = resolveRowSelectionNavigation(event.key);
27930
+ if (target) {
27931
+ moveSelection(target.rowIndex, target.colIndex, false);
27932
+ break;
27933
+ }
27934
+ }
27935
+ moveSelection(
27936
+ isCommandNavigation ? rowIndexByActual.get(resolveFirstUsedVisibleIndex(visibleRows, activeSheet?.minUsedRow ?? -1)) ?? 0 : currentRowIndex - 1,
27937
+ currentColIndex,
27938
+ event.shiftKey
27939
+ );
27940
+ break;
27941
+ case "ArrowLeft":
27942
+ event.preventDefault();
27943
+ if (!event.shiftKey && !isCommandNavigation) {
27944
+ const target = resolveColumnSelectionNavigation(event.key);
27945
+ if (target) {
27946
+ moveSelection(target.rowIndex, target.colIndex, false);
27947
+ break;
27948
+ }
27949
+ }
27950
+ moveSelection(
27951
+ currentRowIndex,
27952
+ isCommandNavigation ? colIndexByActual.get(resolveFirstUsedVisibleIndex(visibleCols, activeSheet?.minUsedCol ?? -1)) ?? 0 : currentColIndex - 1,
27953
+ event.shiftKey
27954
+ );
27955
+ break;
27956
+ case "ArrowRight":
27957
+ event.preventDefault();
27958
+ if (!event.shiftKey && !isCommandNavigation) {
27959
+ const target = resolveColumnSelectionNavigation(event.key);
27960
+ if (target) {
27961
+ moveSelection(target.rowIndex, target.colIndex, false);
27962
+ break;
27963
+ }
27964
+ }
27965
+ moveSelection(
27966
+ currentRowIndex,
27967
+ isCommandNavigation ? colIndexByActual.get(resolveLastUsedVisibleIndex(visibleCols, activeSheet?.maxUsedCol ?? -1)) ?? visibleCols.length - 1 : currentColIndex + 1,
27968
+ event.shiftKey
27969
+ );
27970
+ break;
27971
+ case "Home":
27972
+ event.preventDefault();
27973
+ moveSelection(
27974
+ isCommandNavigation ? rowIndexByActual.get(resolveFirstUsedVisibleIndex(visibleRows, activeSheet?.minUsedRow ?? -1)) ?? 0 : currentRowIndex,
27975
+ colIndexByActual.get(resolveFirstUsedVisibleIndex(visibleCols, activeSheet?.minUsedCol ?? -1)) ?? 0,
27976
+ event.shiftKey
27977
+ );
27978
+ break;
27979
+ case "End":
27980
+ event.preventDefault();
27981
+ moveSelection(
27982
+ isCommandNavigation ? rowIndexByActual.get(resolveLastUsedVisibleIndex(visibleRows, activeSheet?.maxUsedRow ?? -1)) ?? visibleRows.length - 1 : currentRowIndex,
27983
+ colIndexByActual.get(resolveLastUsedVisibleIndex(visibleCols, activeSheet?.maxUsedCol ?? -1)) ?? visibleCols.length - 1,
27984
+ event.shiftKey
27985
+ );
27986
+ break;
27987
+ case "PageDown":
27988
+ event.preventDefault();
27989
+ if (event.altKey) {
27990
+ moveSelection(currentRowIndex, resolvePageColIndex(currentColIndex, 1), event.shiftKey);
27991
+ break;
27992
+ }
27993
+ moveSelection(resolvePageRowIndex(currentRowIndex, 1), currentColIndex, event.shiftKey);
27994
+ break;
27995
+ case "PageUp":
27996
+ event.preventDefault();
27997
+ if (event.altKey) {
27998
+ moveSelection(currentRowIndex, resolvePageColIndex(currentColIndex, -1), event.shiftKey);
27999
+ break;
28000
+ }
28001
+ moveSelection(resolvePageRowIndex(currentRowIndex, -1), currentColIndex, event.shiftKey);
28002
+ break;
28003
+ case "Tab":
28004
+ event.preventDefault();
28005
+ if (event.shiftKey) {
28006
+ moveSelection(
28007
+ currentColIndex > 0 ? currentRowIndex : currentRowIndex - 1,
28008
+ currentColIndex > 0 ? currentColIndex - 1 : visibleCols.length - 1,
28009
+ false
28010
+ );
28011
+ } else {
28012
+ moveSelection(
28013
+ currentColIndex < visibleCols.length - 1 ? currentRowIndex : currentRowIndex + 1,
28014
+ currentColIndex < visibleCols.length - 1 ? currentColIndex + 1 : 0,
28015
+ false
28016
+ );
28017
+ }
28018
+ break;
28019
+ case "Enter":
28020
+ event.preventDefault();
28021
+ if (event.metaKey || event.ctrlKey || event.altKey) {
28022
+ break;
28023
+ }
28024
+ if (event.shiftKey) {
28025
+ moveSelection(currentRowIndex - 1, currentColIndex, false);
28026
+ break;
28027
+ }
28028
+ moveSelection(currentRowIndex + 1, currentColIndex, false);
28029
+ break;
28030
+ case "Backspace":
28031
+ case "Delete":
28032
+ if (!readOnly) {
28033
+ event.preventDefault();
28034
+ clearSelectedCells();
28035
+ }
28036
+ break;
28037
+ case "F2":
28038
+ if (!readOnly) {
28039
+ event.preventDefault();
28040
+ startEditing(currentCell);
28041
+ }
28042
+ break;
28043
+ default:
28044
+ break;
28045
+ }
28046
+ }
28047
+ gridKeyboardHandlerRef.current = handleGridKeyDown;
27618
28048
  const scrollerViewportProps = {
27619
28049
  key: activeTabIndex,
27620
28050
  ref: scrollRef,
28051
+ "aria-colcount": Math.max(activeSheet?.colCount ?? 0, displayColLimit),
28052
+ "aria-keyshortcuts": "ArrowUp ArrowDown ArrowLeft ArrowRight Home End PageUp PageDown Control+Home Control+End",
28053
+ "aria-label": activeSheet ? `${activeSheet.name} worksheet grid` : "Workbook grid",
28054
+ "aria-readonly": readOnly,
28055
+ "aria-rowcount": Math.max(activeSheet?.rowCount ?? 0, displayRowLimit),
28056
+ role: "grid",
27621
28057
  onScroll: handleScrollerScroll,
27622
28058
  onCopy: (event) => {
27623
28059
  if (editingCell) {
@@ -27637,92 +28073,27 @@ function XlsxGrid({
27637
28073
  }
27638
28074
  void copySelectionToClipboard();
27639
28075
  },
27640
- onKeyDown: (event) => {
27641
- if (editingCell) {
28076
+ onPointerDownCapture: (event) => {
28077
+ if (event.button !== 0) {
27642
28078
  return;
27643
28079
  }
27644
- if (!readOnly && (event.metaKey || event.ctrlKey) && !event.altKey) {
27645
- const normalizedKey = event.key.toLowerCase();
27646
- if (normalizedKey === "z" && event.shiftKey) {
27647
- event.preventDefault();
27648
- redo();
27649
- return;
27650
- }
27651
- if (normalizedKey === "z") {
27652
- event.preventDefault();
27653
- undo();
27654
- return;
27655
- }
27656
- if (normalizedKey === "y") {
27657
- event.preventDefault();
27658
- redo();
27659
- return;
27660
- }
27661
- }
27662
- const currentCell = resolveCurrentCell();
27663
- if (!currentCell) {
28080
+ if (isInteractiveFocusTarget(event.target, event.currentTarget)) {
27664
28081
  return;
27665
28082
  }
27666
- const currentRowIndex = rowIndexByActual.get(currentCell.row) ?? 0;
27667
- const currentColIndex = colIndexByActual.get(currentCell.col) ?? 0;
27668
- if (!readOnly && isPrintableKey(event)) {
27669
- event.preventDefault();
27670
- startEditing(currentCell, event.key);
28083
+ gridKeyboardActiveRef.current = true;
28084
+ focusGridElement(event.currentTarget);
28085
+ },
28086
+ onClickCapture: (event) => {
28087
+ if (isInteractiveFocusTarget(event.target, event.currentTarget)) {
27671
28088
  return;
27672
28089
  }
27673
- switch (event.key) {
27674
- case "ArrowDown":
27675
- event.preventDefault();
27676
- moveSelection(Math.min(currentRowIndex + 1, visibleRows.length - 1), currentColIndex, event.shiftKey);
27677
- break;
27678
- case "ArrowUp":
27679
- event.preventDefault();
27680
- moveSelection(Math.max(currentRowIndex - 1, 0), currentColIndex, event.shiftKey);
27681
- break;
27682
- case "ArrowLeft":
27683
- event.preventDefault();
27684
- moveSelection(currentRowIndex, Math.max(currentColIndex - 1, 0), event.shiftKey);
27685
- break;
27686
- case "ArrowRight":
27687
- event.preventDefault();
27688
- moveSelection(currentRowIndex, Math.min(currentColIndex + 1, visibleCols.length - 1), event.shiftKey);
27689
- break;
27690
- case "Tab":
27691
- event.preventDefault();
27692
- moveSelection(
27693
- currentRowIndex,
27694
- event.shiftKey ? Math.max(currentColIndex - 1, 0) : Math.min(currentColIndex + 1, visibleCols.length - 1),
27695
- false
27696
- );
27697
- break;
27698
- case "Enter":
27699
- event.preventDefault();
27700
- if (event.metaKey || event.ctrlKey || event.altKey) {
27701
- break;
27702
- }
27703
- if (event.shiftKey) {
27704
- moveSelection(Math.max(currentRowIndex - 1, 0), currentColIndex, false);
27705
- break;
27706
- }
27707
- moveSelection(Math.min(currentRowIndex + 1, visibleRows.length - 1), currentColIndex, false);
27708
- break;
27709
- case "Backspace":
27710
- case "Delete":
27711
- if (!readOnly) {
27712
- event.preventDefault();
27713
- clearSelectedCells();
27714
- }
27715
- break;
27716
- case "F2":
27717
- if (!readOnly) {
27718
- event.preventDefault();
27719
- startEditing(currentCell);
27720
- }
27721
- break;
27722
- default:
27723
- break;
27724
- }
28090
+ gridKeyboardActiveRef.current = true;
28091
+ focusGridElement(event.currentTarget);
28092
+ },
28093
+ onFocus: () => {
28094
+ gridKeyboardActiveRef.current = true;
27725
28095
  },
28096
+ onKeyDown: handleGridKeyDown,
27726
28097
  onPaste: (event) => {
27727
28098
  if (editingCell || readOnly) {
27728
28099
  return;
@@ -27972,7 +28343,7 @@ function XlsxGrid({
27972
28343
  {
27973
28344
  ref: editingInputRef,
27974
28345
  autoFocus: true,
27975
- onBlur: commitEditing,
28346
+ onBlur: handleEditingBlur,
27976
28347
  onChange: (event) => setEditingValue(event.target.value),
27977
28348
  onKeyDown: (event) => {
27978
28349
  event.stopPropagation();
@@ -27984,6 +28355,11 @@ function XlsxGrid({
27984
28355
  if (event.key === "Escape") {
27985
28356
  event.preventDefault();
27986
28357
  cancelEditing();
28358
+ return;
28359
+ }
28360
+ if (event.key === "ArrowDown" || event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp") {
28361
+ event.preventDefault();
28362
+ handleEditingNavigate(event.key, event.currentTarget.value);
27987
28363
  }
27988
28364
  },
27989
28365
  style: {
@@ -28138,8 +28514,10 @@ function XlsxGrid({
28138
28514
  onCellClick: handleCellClick,
28139
28515
  onCellDoubleClick: handleCellDoubleClick,
28140
28516
  onCellPointerDown: handleCellPointerDown,
28517
+ onEditingBlur: handleEditingBlur,
28141
28518
  onEditingCancel: cancelEditing,
28142
28519
  onEditingCommit: commitEditing,
28520
+ onEditingNavigate: handleEditingNavigate,
28143
28521
  onEditingValueChange: setEditingValue,
28144
28522
  headerLabelLiveScale,
28145
28523
  onRowHeaderRef: setRowHeaderRef,