@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
  • Вопрос задан
  • 77 просмотров
Пригласить эксперта
Ответы на вопрос 1
Natebash
@Natebash
React, Vue, Angular, Navite JS, Python / Node JS
Основная ваша проблема что вы написали компонент, а теперь пытаетесь его подогнать под функционал который не работает.

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

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

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

Войти через центр авторизации
Похожие вопросы