Sobes.tech
Back to questions
Middle
2

Как реализовать клавиатурную навигацию (стрелки вверх/вниз) в виртуализированном списке с использованием @tanstack/virtual?

Companies where asked
Ifellow

Answer from AI

sobes.tech AI

Для реализации клавиатурной навигации (стрелки вверх/вниз) в виртуализированном списке с использованием @tanstack/virtual нужно управлять текущим выделенным индексом и прокручивать список к нужному элементу при изменении этого индекса.

Основные шаги:

  1. Хранить в состоянии индекс текущего выделенного элемента.
  2. Обрабатывать события клавиатуры (keydown) на контейнере списка.
  3. При нажатии стрелки вниз увеличивать индекс, при стрелке вверх — уменьшать, с проверкой границ.
  4. Использовать метод scrollToIndex из API @tanstack/virtual, чтобы прокрутить список к выделенному элементу.

Пример на React:

import React, { useState, useRef, useEffect } from 'react';
import { useVirtual } from '@tanstack/react-virtual';

function VirtualList({ items }) {
  const parentRef = useRef(null);
  const [highlightedIndex, setHighlightedIndex] = useState(0);

  const rowVirtualizer = useVirtual({
    size: items.length,
    parentRef,
    estimateSize: () => 35,
    overscan: 5,
  });

  useEffect(() => {
    rowVirtualizer.scrollToIndex(highlightedIndex);
  }, [highlightedIndex, rowVirtualizer]);

  const onKeyDown = (e) => {
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setHighlightedIndex(i => Math.min(i + 1, items.length - 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setHighlightedIndex(i => Math.max(i - 1, 0));
    }
  };

  return (
    <div
      ref={parentRef}
      tabIndex={0}
      onKeyDown={onKeyDown}
      style={{ height: `300px`, overflow: 'auto', outline: 'none' }}
    >
      <div
        style={{
          height: `${rowVirtualizer.totalSize}px`,
          position: 'relative',
        }}
      >
        {rowVirtualizer.virtualItems.map(virtualRow => {
          const isSelected = virtualRow.index === highlightedIndex;
          return (
            <div
              key={virtualRow.index}
              ref={virtualRow.measureRef}
              style={{
                position: 'absolute',
                top: 0,
                left: 0,
                width: '100%',
                height: `${virtualRow.size}px`,
                transform: `translateY(${virtualRow.start}px)`,
                backgroundColor: isSelected ? '#bde4ff' : 'transparent',
                padding: '8px',
                boxSizing: 'border-box',
              }}
            >
              {items[virtualRow.index]}
            </div>
          );
        })}
      </div>
    </div>
  );
}

В этом примере при нажатии стрелок меняется выделенный индекс, и список прокручивается к нему, обеспечивая удобную клавиатурную навигацию.