React навигация по кастомному селекту с поиском

Сделал кастомынй селектор с поиском при помощи input и ul. Теперь стоит проблема: как сделать выбор не только при клике мышкой, но и стрелочками + enter. Может можно переделать имеющийся вариант со списка на другой элемент?


export default function SearchingSelect(): JSX.Element {
    const values = ['avstria', 'brasile', 'russia', 'USA', 'china']
    const [searchValue, setSearchValue] = useState('')
    const [selectValues, setSelectValues] = useState(values)

    function handleClick(e: React.MouseEvent<HTMLLIElement>) {
        setSearchValue(e.currentTarget.innerText)
        setSelectValues(values)
    }

    function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
        setSearchValue(e.target.value)
        const newValue = values.filter((el) => (
            el.includes(e.target.value)
        ))
        setSelectValues(newValue)
    }

    return (
        <div className='searching-select'>
            <input value={searchValue} type="search" className='searching-select__search' onChange={handleChange} />
            <ul className="searching-select__values">
                {selectValues.map((el) => <li onClick={handleClick}>{el}</li>)}
            </ul>
        </div>
    )
} ```

Ответы (1 шт):

Автор решения: Yuriy Sidorov

import { useCallback, useEffect, useState } from "react";
import "./styles.scss";

export default function SearchingSelect(): JSX.Element {
  const values = ["avstria", "brasile", "russia", "USA", "china"];
  const [searchValue, setSearchValue] = useState("");
  const [selectValues, setSelectValues] = useState(values);
  const [selectValueIndex, setSelectValueIndex] = useState(0);

  function handleClick(e: React.MouseEvent<HTMLLIElement>) {
    setSearchValue(e.currentTarget.innerText);
    setSelectValues(values);
  }

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    setSearchValue(e.target.value);
    const newValue = values.filter((el) => el.includes(e.target.value));
    setSelectValues(newValue);
  }

  const handleKeyPressArrow = useCallback(
    (event: KeyboardEvent) => {
      if (event.key === "ArrowDown") {
        setSelectValueIndex((prev) =>
          prev < selectValues.length - 1 ? prev + 1 : prev
        );
      } else if (event.key === "ArrowUp") {
        setSelectValueIndex((prev) => (!!prev ? prev - 1 : prev));
      }
    },
    [selectValues.length]
  );

  const handleKeyPressEnter = useCallback(
    (event: KeyboardEvent) => {
      if (event.key === "Enter" && selectValues.length) {
        setSearchValue(selectValues[selectValueIndex]);
      }
    },
    [selectValues, selectValueIndex]
  );

  useEffect(() => {
    if (selectValues.length) {
      window.addEventListener("keydown", handleKeyPressArrow);
      window.addEventListener("keydown", handleKeyPressEnter);
    }
    return () => {
      window.removeEventListener("keydown", handleKeyPressArrow);
      window.removeEventListener("keydown", handleKeyPressEnter);
    };
  }, [selectValues.length, handleKeyPressArrow, handleKeyPressEnter]);

  return (
    <div className="searching-select">
      <input
        value={searchValue}
        type="search"
        className="searching-select__search"
        onChange={handleChange}
      />
      <ul className="searching-select__values">
        {selectValues.map((el, key) => (
          <li
            key={el}
            className={
              key === selectValueIndex
                ? "searching-select__values_value-selected"
                : ""
            }
            onClick={handleClick}
          >
            {el}
          </li>
        ))}
      </ul>
    </div>
  );
}

→ Ссылка