Задать вопрос
@QnaTwitt

Как переписать функцию поиска React?

Я сделал свой (dropdown\select) с поиском, поиск работает у меня пока что только для заголовков, подскажите как сделать поиск для заголовков и для вложенных пунктов. ( после чего ещё буду пытаться обводить цветом текст который был найден, но это не самая важная задача... )

import React, { FC, useEffect, useState } from 'react';
import { Disclosure } from '@headlessui/react';
import Style from './Select.module.scss';
import Icon from '../icon/Icon';

interface ISelectItem {
  name: string;
  categories: [];
}

interface ISelect {
  placeholder?: string;
  lists: any;
}

const SelectItem: FC<ISelectItem> = ({ name, categories }) => {
  return (
    <Disclosure as="li" className={Style.Item}>
      {({ open }) => (
        <>
          <Disclosure.Button
            className={`${Style.ItemName} ${open ? Style.open : ''}`}
            as="div"
          >
            <span>{name}</span>
            <Icon id="angle-right" width={8} height={8} />
          </Disclosure.Button>
          <ul className={Style.ItemList}>
            {categories.map((category: any) => (
              <Disclosure.Panel key={category.id} as="li">
                <span>{category.name}</span>
                <Icon id="angle-right" width={8} height={8} />
              </Disclosure.Panel>
            ))}
          </ul>
        </>
      )}
    </Disclosure>
  );
};

const filterList = (searchText: string, list: any[]) => {
  if (!searchText) return list;
  return list.filter(({ name }) =>
    name.toLowerCase().includes(searchText.toLowerCase())
  );
};

const Select: FC<ISelect> = ({ placeholder, lists }) => {
  const [searchInput, setSearchInput] = useState('');
  const [filteredList, setFilteredList] = useState<any>();

  useEffect(() => {
    const Debounce = setTimeout(() => {
      const sortedList = filterList(searchInput, lists);
      setFilteredList(sortedList);
    }, 300);
    return () => clearTimeout(Debounce);
  }, [searchInput]);

  return (
    <div className={Style.wrapper}>
      <label className={Style.Input}>
        <input
          type="text"
          placeholder={placeholder}
          value={searchInput}
          onChange={(e) => setSearchInput(e.target.value)}
        />
        {/*<Icon id="search" width={14} height={14} />*/}
      </label>
      <ul className={Style.list}>
        {filteredList &&
          filteredList.map((item: any) => (
            <SelectItem
              key={item.id}
              name={item.name}
              categories={item.categories}
            />
          ))}
      </ul>
    </div>
  );
};

export default Select;


@use '../../styles/variables';

.wrapper {
  max-width: variables.rem(350px);
  background-color: white;
  box-shadow: 0 0 variables.rem(6px) #becadb;
  border-radius: variables.rem(4px);

  ul {
    margin: 0;
    list-style: none;
  }
}

.Input {
  display: flex;
  align-items: center;
  padding: variables.rem(15px) variables.rem(10px);

  input {
    width: 100%;
    padding: variables.rem(5px) variables.rem(10px) variables.rem(5px);
    font-size: variables.rem(14px);
    line-height: variables.rem(14px);
    letter-spacing: .02em;
    color: variables.$blue-800;
    border: none;

    &::placeholder {
      font-size: inherit;
      line-height: inherit;
      letter-spacing: inherit;
      color: inherit;
    }
  }
}

.list {
  padding: 0 variables.rem(10px) variables.rem(20px);
  max-height: variables.rem(350px);
  overflow: auto;
}

.Item {
  padding: variables.rem(16px) variables.rem(10px);
  cursor: pointer;

  span {
    font-size: variables.rem(14px);
    font-weight: 600;
    line-height: variables.rem(19px);
    letter-spacing: .01em;
    color: variables.$blue;
  }

  :global .icon-angle-right {
    transition: 200ms;
  }

  & .open :global .icon-angle-right {
    transform: rotate(90deg);
  }

  &Name {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
}

.ItemList {
  padding: 0;

  li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: variables.rem(16px) variables.rem(10px);
    transition: 200ms;
    border-radius: 4px;

    &:first-child {
      margin-top: variables.rem(16px);
    }

    :global .icon-angle-right {
      stroke-width: 0.5px;
      stroke: variables.$blue-800;
    }

    span {
      font-size: variables.rem(14px);
      font-weight: 400;
      line-height: variables.rem(19px);
      letter-spacing: .04em;
    }

    &:hover {
      background-color: #f5f6fa;
    }
  }
}


Скрины того что сейчас есть, и то как оно работает

63173b05152ed210938916.jpeg
63173b07c5bf0122910291.jpeg
63173b0ab058d205703054.jpeg
Вот в таком видео приходят данные, сначала name\id общего списка, и потом массив категорий где тоже name\id. И нужно сделать поиск который будет включать и общее названию списка и названия в самих категориях...
63173b2646aea377496841.jpeg

codesandbox
  • Вопрос задан
  • 81 просмотр
Подписаться 1 Простой 6 комментариев
Помогут разобраться в теме Все курсы
  • Яндекс Практикум
    Мидл фронтенд-разработчик
    5 месяцев
    Далее
  • Яндекс Практикум
    React-разработчик
    3 месяца
    Далее
  • Яндекс Практикум
    Фронтенд-разработчик
    10 месяцев
    Далее
Пригласить эксперта
Ответы на вопрос 1
Natebash
@Natebash
React, Vue, Angular, Navite JS, Python / Node JS
Основная ваша проблема что вы написали компонент, а теперь пытаетесь его подогнать под функционал который не работает.

Начните заново:
Входные данные, у вас один поиск внутри дропдауна? (если 2, то зачем?)
Сам дропдаун - это всего лишь список, дропдаун с вложенными элементами, это список в списке.

Научитесь искать элемент в массивах с вложенными массивами - а далее все уже зависит от того как хорошо вы верстаете, логика довольно простая.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы
ITK academy Нижний Новгород
от 80 000 до 120 000 ₽
ITK academy Воронеж
от 50 000 до 90 000 ₽