Multiselect

Базовый мультиселект, в основе текстовое поле. Basic multiselect component, based on a text field.

Import Permalink to "Import"

js
import ProximaMultiselect from 'proxima-vue/field/multiselect';

Playground Permalink to "Playground"

0 items selected:
value: '[]'
Props
Label position
View
Round
Shadow
Actions visibility
js
import { ref } from 'vue';
import ProximaMultiselect from 'proxima-vue/field/multiselect';

const value = ref([]);

const options = ['Ivan III', 'Vasili III', 'Ivan IV Vasilyevich', 'Feodor I', 'Boris Godunov', 'Feodor II', 'False Dmitry I', 'Vasili IV', 'Fedor Mstislavsky', 'Michael I', 'Alexei Mikhailovich', 'Feodor III', 'Ivan V', 'Sophia Alekseyevna', 'Peter the Great', 'Catherine I', 'Peter II', 'Anna Ioannovna', 'Ivan VI', 'Elizaveta Petrovna', 'Peter III', 'Catherine the Great', 'Paul I', 'Alexander I', 'Nicholas I', 'Alexander II', 'Alexander III', 'Nicholas II'];
html
<ProximaMultiselect
  label="Select person"
  placeholder="Favorite tsar(evna) of Russia"
  :options="options"
  v-model="value"
/>

Options Permalink to "Options"

В компонент можно передать простой массив строк или объектов c указанием ключа для фильтра, по умолчанию это поле You can pass a simple array of strings or objects to the component, indicating the key for the filter; by default, this is the name:

0 items selected:
value: []
html
<ProximaMultiselect
  label="Select characters"
  placeholder="Who is your fav character?"
  :options="candidates"
  filter-key="fullname"
  track-key="fullname"
  v-model="value"
/>
js
import { ref } from 'vue';
import ProximaMultiselect from 'proxima-vue/field/multiselect';

const value = ref([]);
const candidates = [
  { fullname: 'Rick Sanchez' },
  { fullname: 'Morty Smith' },
  { fullname: 'Jerry Smith' },
  { fullname: 'Summer Smith' },
  { fullname: 'Beth Smith' },
  { fullname: 'Jessica' },
  { fullname: 'Mr. Goldenfold' },
  { fullname: 'Squanchy' },
  { fullname: 'Birdperson' },
];

Track key Permalink to "Track key"

Для отслеживания используется поле name, но вы можете указать другой ключ через пропс track‑key: The name field is used for tracking, but you can specify a different key via the track‑key prop:

0 items selected:
value: []
html
<ProximaMultiselect
  label="Select person"
  placeholder="Favorite Rick"
  :options="candidates"
  track-key="reality"
  v-model="value"
>
  <template #option="{ option }">
    <span v-text="option.name" />
    <span v-text="option.reality" class="text text_dim" />
  </template>
</ProximaMultiselect>
js
import { ref } from 'vue';
import ProximaMultiselect from 'proxima-vue/field/multiselect';

const value = ref([]);
const candidates = [
  { name: 'Rick Sanchez', reality: 'C-137' },
  { name: 'Rick Sanchez', reality: 'C-132' },
  { name: 'Rick Sanchez', reality: 'C-123' },
  { name: 'Rick Sanchez', reality: 'C-131' },
];

Max visible Permalink to "Max visible"

По умолчанию отображается 15 вариантов и текстовая подсказка об общем количестве, вы можете изменить это число, а также скрыть подсказку: By default, 15 options are displayed along with a text hint about the total quantity. You can change this number and also hide the count label:

html
<ProximaMultiselect
  :max-visible-options="20"
  :has-count-label="false"
/>

Chips Permalink to "Chips"

Компонент может содержать список выбранных элементов в виде чипов/тегов, для активации используйте пропс has‑chips: The component can contain a list of selected elements in the form of chips/tags; to activate, use the has‑chips prop:

2 items selected: Bitcoin, Toncoin
html
<ProximaMultiselect
  has-chips
/>

Search Permalink to "Search"

В компонент можно передать свою функцию для поиска элемента: You can pass your own function to search for an element:

0 items selected:
value: []
html
<ProximaMultiselect
  label="Select coin"
  placeholder="Favorite cryptocurrency"
  :search-filter="findCoinByNameAndCode"
  :options="coins"
  v-model="value"
/>
js
import { ref } from 'vue';
import ProximaMultiselect from 'proxima-vue/field/multiselect';

const findCoinByNameAndCode = (option, query) => {
  const str = [option.name, option.code].join(' ').toLowerCase();
  return str.includes(query.toLowerCase());
};

const value = ref([]);
const coins = [
  { name: 'Tether (TRC-20)', code: 'usdt' },
  { name: 'Bitcoin', code: 'btc' },
  { name: 'Toncoin', code: 'ton' },
  { name: 'Dogecoin', code: 'doge' },
  { name: 'Ethereum', code: 'eth' },
  { name: 'Litecoin', code: 'ltc' },
  { name: 'Dash', code: 'dash' },
  { name: 'Chia', code: 'xch' },
];

Pin Permalink to "Pin"

Вы можете передать функцию фильтр для закрепления определенных вариантов вверху списка: You can pass a filter function to pin certain options to the top of the list:

0 items selected:
value: []
html
<ProximaMultiselect
  label="Select person"
  placeholder="Favorite tsar(evna) of Russia"
  :pin-filter="pinGreatPersons"
  :options="options"
  v-model="value"
/>
js
import { ref } from 'vue';
import ProximaMultiselect from 'proxima-vue/field/multiselect';

const pinGreatPersons = (name) => name.toLowerCase().includes('great');

const value = ref([]);
const options = ['Ivan III', 'Vasili III', 'Ivan IV Vasilyevich', 'Feodor I', 'Boris Godunov', 'Feodor II', 'False Dmitry I', 'Vasili IV', 'Fedor Mstislavsky', 'Michael I', 'Alexei Mikhailovich', 'Feodor III', 'Ivan V', 'Sophia Alekseyevna', 'Peter the Great', 'Catherine I', 'Peter II', 'Anna Ioannovna', 'Ivan VI', 'Elizaveta Petrovna', 'Peter III', 'Catherine the Great', 'Paul I', 'Alexander I', 'Nicholas I', 'Alexander II', 'Alexander III', 'Nicholas II'];

Disable Permalink to "Disable"

Вы можете передать функцию фильтр для блокировки определенных вариантов: You can pass a filter function to disable certain options:

0 items selected:
value: []
html
<ProximaMultiselect
  label="Select person"
  placeholder="Favorite tsar(evna) of Russia"
  :disable-filter="disablePersons"
  :options="options"
  v-model="value"
/>
js
import { ref } from 'vue';
import ProximaMultiselect from 'proxima-vue/field/multiselect';

const disablePersons = (name) => name.toLowerCase().includes('ivan');

const value = ref([]);
const options = ['Ivan III', 'Vasili III', 'Ivan IV Vasilyevich', 'Feodor I', 'Boris Godunov', 'Feodor II', 'False Dmitry I', 'Vasili IV', 'Fedor Mstislavsky', 'Michael I', 'Alexei Mikhailovich', 'Feodor III', 'Ivan V', 'Sophia Alekseyevna', 'Peter the Great', 'Catherine I', 'Peter II', 'Anna Ioannovna', 'Ivan VI', 'Elizaveta Petrovna', 'Peter III', 'Catherine the Great', 'Paul I', 'Alexander I', 'Nicholas I', 'Alexander II', 'Alexander III', 'Nicholas II'];

Create Permalink to "Create"

Попап может содержать кнопку создания нового значения: The field popup may contain a button to create a new value:

Press Enter to create and select “”
0 items selected:
cities: []
list: [ "Saint Petersburg", "Barcelona", "New York" ]
html
<ProximaMultiselect
  label="The most beautiful city"
  placeholder="You can add your city"
  :options="list"
  has-create-button
  @create="createAndSelect"
  v-model="cities"
/>
js
import { ref, unref } from 'vue';
import ProximaMultiselect from 'proxima-vue/field/multiselect';

const cities = ref([]);
const list = ref(['Saint Petersburg', 'Barcelona', 'New York']);

const createAndSelect = (name) => {
  unref(list).push(name);
  unref(cities).push(name);
};

Fly search Permalink to "Fly search"

Для активации поиска на лету добавьте mode="fetch" и используйте функцию useProximaFlySearch, которая принимает в качестве аргумента метод поиска и возвращает необходимые параметры: To enable on‑the‑fly search, add mode="fetch" and use the useProximaFlySearch function, which takes a search method as an argument and returns the necessary parameters:

Анимация загрузки списка требуетList loading animation requires ghost.css

0 items selected:
countries: []
html
<ProximaMultiselect
  mode="fetch"
  label="Country"
  placeholder="Find country ..."
  :options="flySearchResults"
  :is-list-pending="isFlySearchPending"
  @update:filterValue="onFlySearchUpdate"
  v-model="countries"
/>
js
import { ref } from 'vue';
import ProximaMultiselect from 'proxima-vue/field/multiselect';
import useProximaFlySearch from 'proxima-vue/composables/flySearch';

const countries = ref([]);

const findCountry = async (query) => {
  const response = await fetch(`https://mocky.yoobe.ru/countries/find?name=${query}`);
  const results = await response.json();
  return results;
};

const { isFlySearchPending, onFlySearchUpdate, flySearchResults } =
  useProximaFlySearch(findCountry);

Accessibility Permalink to "Accessibility"

Поле имеет role="combobox" и атрибуты aria‑autocomplete="list", aria‑haspopup="listbox", aria‑multiselectable="true", aria‑controls, aria‑expanded, вспомогательные технологии обозначают активный элемент списка при навигации с клавиатуры благодаря атрибуту aria‑activedescendant. The field has role="combobox" and attributes aria‑autocomplete="list", aria‑haspopup="listbox", aria‑multiselectable="true", aria‑controls, aria‑expanded. Assistive technologies indicate the active list item during keyboard navigation with aria‑activedescendant attribute.

BEM Permalink to "BEM"

Компонент имеет свои модификаторы (создаются автоматически на основе пропсов): The component has its own modifiers (automatically created based on props):

CSS Permalink to "CSS"

css
--field-chips-gap: 0.25rem;

--field-popup-background: var(--field-background);
--field-popup-color: var(--field-color);

--field-popup-overflow-y: auto;
--field-popup-scroll-padding-y-start: 0.25rem;
--field-popup-scroll-padding-y-end: 6rem;

--field-popup-footer-max-width: 24em;
--field-popup-footer-border-size: 1px;
--field-popup-footer-border-color: #eee;

--field-popup-margin-y-start: -0.25rem;

--field-popup-font-size-xxs: 0.75rem;
--field-popup-font-size-xs: 0.8125rem;
--field-popup-font-size-s: 0.875rem;
--field-popup-font-size: 0.875rem;
--field-popup-font-size-m: 0.875rem;
--field-popup-font-size-l: 1rem;
--field-popup-font-size-xl: 1.125rem;
--field-popup-font-size-xxl: 1.25rem;

/*
  Options
*/

--field-options-padding-x: 0.25rem;
--field-options-padding-y: 0.25rem;
--field-options-z-index: 1;

--field-option-padding-x: 0.875em;
--field-option-padding-y: 0.75em;

--field-option-gap: 1em;
--field-option-border-radius: 0.375rem;

--field-option-selected-color: var(--field-accent-color);
--field-option-hover-background: #f6f6f6;
--field-option-disabled-opacity: 0.4;

/* Create */
--field-option-create-padding-x: 1.25em;
--field-option-create-color: var(--field-accent-color);

/* Check icon */
--field-option-tick-order: 1;
--field-option-tick-size: 1.125em;
--field-option-tick-stroke-width: 2;
--field-option-tick-margin-x-start: auto;

/* Plus icon */
--field-option-plus-size: 1em;
--field-option-plus-stroke-width: 1.25;
--field-option-plus-margin-x-start: -0.375em;
Locale Permalink to "Locale"

Компонент использует токены локализации, вы можете передать другие токены или обычный текст через пропсы: The component uses localization tokens, and you can pass other tokens or plain text via props:

  • notfoundLabel
  • countLabel
  • createLabel
  • createDescription
  • selectedLabel

js
{
  fieldCreateLabel: "Create “{query}”",
  fieldCreateDescription: "Press Enter to create and select “{query}”",
  fieldNotfoundLabel: "Nothing was found",
  fieldFilterCountLabel: "Showing {visibleCount} of {totalCount} items, refine your request to see the rest",
  fieldSelectedLabel: "{n} item selected | {n} items selected",
}
Props Permalink to "Props"
ts
interface ProximaMultiselectProps<Option> {
  mode?: 'local' | 'fetch'
  filterKey?: string
  trackKey?: string
  modelValue?: Option[]
  options?: Option[]
  describedby?: string
  required?: boolean
  minlength?: number
  maxlength?: number
  maxVisibleOptions?: number
  notfoundLabel?: string
  countLabel?: string
  createLabel?: string
  createDescription?: string
  selectedLabel?: string
  pinFilter?: (option: Option) => boolean
  searchFilter?: (option: Option, query: string) => boolean
  disableFilter?: (option: Option, modelValue: Option[]) => boolean
  selectChecker?: (option: Option, modelValue: Option[]) => boolean
  popupProps?: ProximaDynamicProps
  hasCreateButton?: boolean
  hasCountLabel?: boolean
  hasChips?: boolean
  isListPending?: boolean
}
Slots Permalink to "Slots"
html
<ProximaMultiselect>
  <template #popup-prepend="slotProps">
    you code (prepend to popup)
  </template>

  <template #popup="slotProps">
    you code (append to popup)
  </template>

  <template #popup-footer="{ id, ...slotProps }">
    you code (replaced popup footer)
  </template>

  <template #option="{ option, ...slotProps }">
    you code (replaced popup option)
  </template>
</ProximaMultiselect>
ts
type SlotProps = {
  maxVisibleOptions: number
  filterValue: string
  pinnedOptions: Option[]
  unpinnedOptions: Option[]
  filteredOptions: Option[]
  visibleOptions: Option[]
  optionIndex: number
  notfoundLabel: string
  countLabel: string
  selectedLabel: string
  hasValue: boolean
  isNotfoundVisible: boolean
  isFilterCountVisible: boolean
  isPopupOpened: boolean
  isFlySearch: boolean
}
Expose Permalink to "Expose"

Поле поддерживает все методы текстового поля и свои собственные: The field supports all text field methods and some own:

html
<ProximaMultiselect
  ref="multiselect"
  :options="options"
  v-model="value"
/>
ts
import { ref, unref, onMounted } from 'vue';
import ProximaMultiselect from 'proxima-vue/field/multiselect';

const multiselect = ref({} as InstanceType<typeof ProximaMultiselect>);
const value = ref([]);
const options = ['Bitcoin', 'Toncoin', 'Dogecoin'];

onMounted(() => {
  unref(multiselect).checkOptionSelected('Bitcoin'); // false
  unref(multiselect).pickOption('Bitcoin');
  unref(multiselect).checkOptionSelected('Bitcoin'); // true
});
ts
type ProximaMultiselectInstance = {
  checkOptionPinned: (option: any) => boolean
  checkOptionSelected: (option: any) => boolean
  checkOptionDisabled: (option: any) => boolean
  pickOption: (option: any) => void
  pickOptionByIndex: () => void
  scrollToOption: () => void
  setOptionIndex: (number) => void
  setPrevIndex: () => void
  setNextIndex: () => void
  setFirstIndex: () => void
  setFilterValue: (value: string) => void
  showPopup: () => void
  hidePopup: () => void
}

Extends Permalink to "Extends"

Данный компонент основан на текстовом поле и поддерживает его пропсы, события, слоты, БЭМ модификаторы и кастомные свойства. This component is based on a text field and supports its props, events, slots, BEM modifiers and custom properties.

View source on GitHub