Введение Струтура компонента Состояние компонента Реализация Ссылки

Multiselect Input

Multiselect Input это вид компонента select, который позволяет пользователю выбрать несколько пунктов из option list.

В основном multiselect input используют в различных формах входа или заполнения данных, когда нужно сделать множественный выбор при поиске по тегам или фильтрации.

Структура компонента

Multiselect input состоит из input с предзаполненным placeholder, option item который необходимо выбрать и, в выбранном состоянии, chip. Подробнее про основную структуру select.

Структура чипса

Chip

Основа отличия multiselect input от обычного select input в том, что выбранные строки из option list выводятся в input в виде chip. Эти элементы отображают выбранный контент и имеют иконку удаления.

Структура чипса

Состояние компонента

Исходя из структуры, мы можем выявить несколько состояний компонента с данными и без них. Заполненный input отображает не строку, а отдельный компонент или несколько компонентов в виде chip. Multiselect input изменяется следующим образом:

Структура чипса

Затем в выбранном состоянии появляется chip. Так как выбираемая строка может быть слишком длинной, чтобы поместиться в input целиком, мы ограничиваем максимальную длину chip и срезаем выбранный текст.

Структура чипса

Количество строк в опции может быть большим, соответственно и выбранных chip будет много. Проблема размещения этих компонентов в input решается таким образом.

Структура чипса

Реализация

Компонент должен принимать параметры: placeholder, current selection, option list. Также мы прописываем его функциональность при выборе элемента списка и когда уже из input мы удаляем выбранный chip. Особенность этого компонента в том, что при выборе строки из массива option list мы переносим ее в массив с chip.

import React, { PureComponent } from 'react'
import A_Input from '../../atoms/A_Input/A_Input.jsx'
import A_Chip from '../../atoms/A_Chip/A_Chip.jsx'
import A_OptionItem from '../../atoms/A_OptionItem/A_OptionItem.jsx'
import './O_MultiselectInput.css'

export default class O_MultiselectInput extends PureComponent {
  constructor(props) {
    super(props)

    this.state = {
      selectedOptions: [],
      disabled: props.disabled,
      options: props.options,
      showOptions: false
    }
  }

  handleSelectOption = (id) => {
    const selectedOption = this.state.options.find((elem) => elem.id === id)

    if (!selectedOption) {
      return
    }

    const updatedOptions = this.state.options.filter(
      (option) => option.id !== id
    )

    this.setState((prevState) => ({
      ...prevState,
      selectedOptions: [...prevState.selectedOptions, selectedOption],
      options: updatedOptions
    }))
  }

  handleUnselectOption = (id) => {
    const unselectedOption = this.state.selectedOptions.find(
      (elem) => elem.id === id
    )

    if (!unselectedOption) {
      return
    }

    const newSelectedOptions = this.state.selectedOptions.filter(
      (option) => option.id !== id
    )

    this.setState((prevState) => ({
      ...prevState,
      options: [...prevState.options, unselectedOption],
      selectedOptions: newSelectedOptions
    }))
  }

  setVisibleOptions = (visible) => {
    if (this.state.disabled) {
      return
    }

    this.setState((preState) => ({
      ...preState,
      showOptions: visible
    }))
  }

  render() {
    const { selectedOptions, options, showOptions } = this.state
    const { placeholder } = this.props

    const optionElements = options.map((option) => (
      <A_OptionItem
        key={option.id}
        id={option.id}
        value={option.value}
        onSelect={this.handleSelectOption}
      />
    ))

    const chips = selectedOptions.map((option) => (
      <A_Chip
        key={option.id}
        text={option.value}
        handleClick={() => {
          this.handleUnselectOption(option.id)
        }}
      />
    ))

    const optionCollection = (
      <div className="C_OptionsList">{optionElements}</div>
    )

    return (
      <div className="O_MultiselectInput">
        <A_Input
          onFocus={() => this.setVisibleOptions(true)}
          disabled={options.length === 0}
          placeholder={placeholder}
        >
          {chips}
        </A_Input>

        {showOptions && optionCollection}
      </div>
    )
  }
}

Затем прописываем стили для multiselect input и задаем среду для остальных компонентов, которые используем. Подробнее про проектирование в изоляции можешь прочитать тут.

.O_MultiselectInput {
   box-shadow: 9px 9px 35px rgba(0, 0, 0, 0.09);
  margin: 20% auto;
  width: 610px;
  border-radius: 10px;
}

.O_MultiselectInput .C_OptionsList {
  margin-top: -15px;
  background-color: #fff f;
  padding: 20px 20px 20px 20px;
}

.O_MultiselectInput .A_Chip {
  margin: 4px 4px;
}

.O_MultiselectInput .A_InputContainer {
  box-shadow: none;
  flex-wrap: nowrap;
  flex-direction: row;
}

Мы создали компонент, но описали только его функционал, теперь его нужно отрендерить. Попробуй теперь этот компонент вывести согласно своей структуре проекта. Ты можешь увидеть как это делаем мы, а также посмотреть получившийся multiselect input.

import React from 'react'
import ReactDOM from 'react-dom'
import O_MultiselectInput from '../../components/organisms/O_MultiselectInput/O_MultiselectInput.jsx'

const options = [
  {
    id: '1',
    value: 'Айва'
  },
  {
    id: '2',
    value: 'Абрикос'
  },
  {
    id: '3',
    value: 'Ананас'
  },
  {
    id: '4',
    value: 'Манго'
  },
  {
    id: '5',
    value: 'Азимина трехлопастная'
  },
  {
    id: '6',
    value: 'Айва бенгальская'
  }
]

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <O_MultiselectInput placeholder="Выбери фрукт" options={options} />,
    document.body
  )
})

Получаем следующий результат в виде input, на который можно нажать и провзаимодействовать, выбрать chips или удалить его из input.

Готовый компонент 1 Готовый компонент 2

Также для тебя ссылки на Figma с дизайн системой и GitHub с библиотекой компонентов.

Составляющие

В этот компонент часто выводятся следующие компоненты.

Этот компонент часто выводится в следующие компоненты.