MultiSelect

MultiSelect

커스텀 검색 가능한 멀티 셀렉트 컴포넌트예요. Combobox 컴포넌트 위에 구축된 의견이 담긴(opinionated) 컴포넌트예요.

출처: 문서

본문

Combobox로 제작 (Made with Combobox)

MultiSelect은 Combobox 컴포넌트 위에 구축된 의견이 담긴 컴포넌트예요. 기본 사용 사례만 다루는 제한된 기능 세트를 가져요. 더 고급 기능이 필요하다면 Combobox로 자신만의 컴포넌트를 만들 수 있어요. 커스텀 멀티 셀렉트 컴포넌트 예시는 예시 페이지에서 찾을 수 있어요.

사용법 (Usage)

MultiSelect은 여러 값을 입력하는 방법을 제공해요. MultiSelect은 TagsInput과 비슷하지만 커스텀 값을 입력할 수는 없어요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return (
    <MultiSelect
      label="Your favorite libraries"
      placeholder="Pick value"
      data={['React', 'Angular', 'Vue', 'Svelte']}
    />
  );
}

로딩 상태 (Loading state)

loading prop을 설정하면 로딩 인디케이터가 표시돼요. 기본적으로 로더는 인풋 오른쪽에 표시돼요. loadingPosition prop을 'left' 또는 'right'로 바꿔 위치를 변경할 수 있어요. API 호출, 검색, 검증 같은 비동기 작업에 유용해요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect label="Your favorite libraries" loading data={['React', 'Angular', 'Vue']} />;
}

제어 방식 (Controlled)

MultiSelect의 값은 반드시 문자열 배열이어야 해요. 다른 타입은 지원되지 않아요. onChange 함수는 단일 인자로 문자열 배열과 함께 호출돼요.

import { useState } from 'react';
import { MultiSelect } from '@mantine/core';

function Demo() {
  const [value, setValue] = useState<string[]>([]);
  return <MultiSelect data={['React', 'Angular', 'Vue']} value={value} onChange={setValue} />;
}

지우기 가능 (Clearable)

clearable prop을 설정하면 오른쪽 섹션에 지우기 버튼이 표시돼요. 다음 경우에는 버튼이 표시되지 않아요.

  • 컴포넌트에 값이 없을 때
  • 컴포넌트가 비활성화되었을 때
  • 컴포넌트가 읽기 전용일 때
import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect label="Your favorite libraries" placeholder="Pick value" data={['React', 'Angular', 'Vue']} defaultValue={['React']} clearable />;
}

지우기 섹션 모드 (Clear section mode)

clearSectionMode prop은 지우기 버튼과 rightSection이 어떻게 렌더링되는지 결정해요.

  • 'both' (기본값) – 지우기 버튼과 rightSection 모두 렌더링
  • 'rightSection' – 사용자가 제공한 rightSection만 렌더링, 지우기 버튼 무시
  • 'clear' – 지우기 버튼만 렌더링, rightSection 무시
import { CaretDownIcon } from '@phosphor-icons/react';
import { MultiSelect, Stack } from '@mantine/core';

const data = ['React', 'Angular', 'Vue'];

function Demo() {
  const rightSection = <CaretDownIcon size={16} />;
  return (
    <Stack>
      <MultiSelect label="clearSectionMode='both' (default)" data={data} defaultValue={['React']} clearable rightSection={rightSection} />
      <MultiSelect label="clearSectionMode='rightSection'" data={data} defaultValue={['React']} clearable clearSectionMode="rightSection" rightSection={rightSection} />
      <MultiSelect label="clearSectionMode='clear'" data={data} defaultValue={['React']} clearable clearSectionMode="clear" rightSection={rightSection} />
    </Stack>
  );
}

검색 가능 (Searchable)

searchable prop으로 사용자 입력에 따라 옵션을 필터링할 수 있어요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect label="Your favorite libraries" placeholder="Pick value" data={['React', 'Angular', 'Vue', 'Svelte']} searchable />;
}

제어되는 검색 값 (Controlled search value)

searchValue와 onSearchChange prop으로 검색 값을 제어할 수 있어요.

import { useState } from 'react';
import { MultiSelect } from '@mantine/core';

function Demo() {
  const [searchValue, setSearchValue] = useState('');
  return <MultiSelect searchValue={searchValue} onSearchChange={setSearchValue} data={['React', 'Angular', 'Vue']} searchable />;
}

결과 없음 (Nothing found)

nothingFoundMessage prop으로 검색 쿼리와 일치하는 옵션이 없거나 사용 가능한 데이터가 없을 때 주어진 메시지를 표시할 수 있어요. nothingFoundMessage prop이 설정되지 않으면 MultiSelect 드롭다운이 숨겨져요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect label="Your favorite libraries" placeholder="Pick value" data={['React', 'Angular', 'Vue']} searchable nothingFoundMessage="Nothing found..." />;
}

체크 옵션 아이콘 (Checked option icon)

checkIconPosition prop을 left 또는 right로 설정해 활성 옵션의 체크 아이콘 위치를 제어할 수 있어요. 체크 아이콘을 제거하려면 withCheckIcon={false}를 설정해요. 체크되지 않은 라벨을 체크된 것과 정렬하려면 withAlignedLabels prop을 설정해요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect label="Control check icon" data={['React', 'Angular', 'Vue']} defaultValue={['React']} checkIconPosition="right" withAlignedLabels />;
}

최대 선택 값 (Max selected values)

maxValues prop으로 선택할 수 있는 값의 수를 제한할 수 있어요. 한도에 도달하면 더 이상 값을 추가할 수 없어요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect label="Your favorite libraries" placeholder="Pick value" data={['React', 'Angular', 'Vue', 'Svelte']} maxValues={2} />;
}

선택된 옵션 숨기기 (Hide selected options)

사용 가능한 옵션 목록에서 선택된 옵션을 제거하려면 hidePickedOptions prop을 설정해요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect label="Your favorite libraries" placeholder="Pick value" data={['React', 'Angular', 'Vue', 'Svelte']} hidePickedOptions searchable />;
}

데이터 형식 (Data formats)

MultiSelect의 data prop은 다음 형식 중 하나로 데이터를 받아요.

원시 값(문자열, 숫자, boolean) 배열:

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect data={['React', 'Angular', 'Vue']} />;
}

value, label과 선택적 disabled 키를 가진 객체 배열:

import { MultiSelect } from '@mantine/core';

function Demo() {
  return (
    <MultiSelect
      data={[
        { value: 'react', label: 'React' },
        { value: 'angular', label: 'Angular' },
        { value: 'vue', label: 'Vue', disabled: true },
      ]}
    />
  );
}

원시 값(문자열, 숫자, boolean) 옵션을 가진 그룹 배열:

import { MultiSelect } from '@mantine/core';

function Demo() {
  return (
    <MultiSelect
      data={[
        { group: 'Frontend', items: ['React', 'Angular', 'Vue'] },
        { group: 'Backend', items: ['Express', 'Koa', 'Django'] },
      ]}
    />
  );
}

객체 옵션을 가진 그룹 배열:

import { MultiSelect } from '@mantine/core';

function Demo() {
  return (
    <MultiSelect
      data={[
        { group: 'Frontend', items: [{ value: 'react', label: 'React' }, { value: 'angular', label: 'Angular' }] },
        { group: 'Backend', items: [{ value: 'express', label: 'Express' }, { value: 'koa', label: 'Koa' }] },
      ]}
    />
  );
}

값 타입 (Value type)

MultiSelect은 값 타입으로 원시 값(문자열, 숫자, boolean)을 지원해요. MultiSelect은 값 타입을 자동으로 추론해요. 값 타입을 명시하려면 타입 인자를 전달해요.

import { MultiSelect } from '@mantine/core';

type MultiSelectValue = 'React' | 'Angular' | 'Svelte' | number;

function Demo() {
  return <MultiSelect<MultiSelectValue> data={['React', 'Angular', 'Svelte', 100]} />;
}

옵션 필터링 (Options filtering)

기본적으로 MultiSelect은 옵션 라벨에 인풋 값이 포함되어 있는지 확인해 옵션을 필터링해요. filter prop으로 이 동작을 바꿀 수 있어요. filter 함수는 단일 인자로 다음 속성을 가진 객체를 받아요.

  • options – 옵션 또는 옵션 그룹 배열, 모든 옵션은 { value: string; label: string; disabled?: boolean } 형식
  • search – 현재 검색 쿼리
  • limit – MultiSelect에 전달된 limit prop 값

글자 순서 대신 단어로 옵션을 매칭하는 커스텀 필터 함수 예시:

import { MultiSelect, ComboboxItem, OptionsFilter } from '@mantine/core';

const optionsFilter: OptionsFilter = ({ options, search }) => {
  const splittedSearch = search.toLowerCase().trim().split(' ');
  return (options as ComboboxItem[]).filter((option) => {
    const words = option.label.toLowerCase().trim().split(' ');
    return splittedSearch.every((searchWord) => words.some((word) => word.includes(searchWord)));
  });
};

function Demo() {
  return <MultiSelect label="What countries have you visited?" data={['England', 'France', 'Germany', 'Italy', 'Spain']} searchable filter={optionsFilter} />;
}

옵션 정렬 (Sort options)

기본적으로 옵션은 데이터 배열에서의 위치에 따라 정렬돼요. filter 함수로 이 동작을 바꿀 수 있어요.

import { MultiSelect, ComboboxItem, OptionsFilter } from '@mantine/core';

const optionsFilter: OptionsFilter = ({ options, search }) => {
  const filtered = (options as ComboboxItem[]).filter((option) =>
    option.label.toLowerCase().trim().includes(search.toLowerCase().trim())
  );

  filtered.sort((a, b) => a.label.localeCompare(b.label));
  return filtered;
};

function Demo() {
  return <MultiSelect label="Your favorite libraries" data={['Vue', 'React', 'Angular', 'Svelte']} searchable filter={optionsFilter} />;
}

fuse.js로 퍼지 검색 (Fuzzy search with fuse.js)

fuse.js 라이브러리를 사용해 오타나 부분 일치가 있어도 옵션을 매칭하는 퍼지 검색을 구현할 수 있어요.

import { MultiSelect, ComboboxItem, OptionsFilter } from '@mantine/core';
import Fuse from 'fuse.js';

const optionsFilter: OptionsFilter = ({ options, search }) => {
  if (!search.trim()) {
    return options;
  }

  const fuse = new Fuse(options as ComboboxItem[], {
    keys: ['label'],
    threshold: 0.3,
    minMatchCharLength: 1,
  });

  return fuse.search(search).map((result) => result.item);
};

function Demo() {
  return <MultiSelect label="What countries have you visited?" data={['England', 'France', 'Germany', 'Italy', 'Spain']} searchable filter={optionsFilter} />;
}

대규모 데이터 세트 (Large data sets)

대규모 데이터 세트의 가장 좋은 전략은 한 번에 렌더링되는 옵션 수를 제한하는 것이에요. limit prop으로 이렇게 할 수 있어요. 커스텀 filter 함수를 사용한다면 filter에서 옵션 수를 제한하는 자체 로직을 구현해야 해요.

import { MultiSelect } from '@mantine/core';

const largeData = Array(100_000)
  .fill(0)
  .map((_, index) => `Option ${index}`);

function Demo() {
  return <MultiSelect label="100 000 options autocomplete" data={largeData} limit={5} searchable />;
}

renderOption

renderOption 콜백으로 옵션 렌더링을 커스터마이즈할 수 있어요. 옵션 객체와 체크 상태와 함께 호출돼요. 함수는 React 노드를 반환해야 해요.

import { MultiSelect, MultiSelectProps, Avatar, Group, Text } from '@mantine/core';

const usersData: Record<string, { image: string; email: string }> = {
  'Emily Johnson': { image: 'https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars/avatar-7.png', email: '[email protected]' },
  // ... more users
};

const renderMultiSelectOption: MultiSelectProps['renderOption'] = ({ option }) => (
  <Group gap="sm">
    <Avatar src={usersData[option.value].image} size={36} radius="xl" />
    <div>
      <Text fz="sm" fw={500}>{option.value}</Text>
      <Text fz="xs" opacity={0.6}>{usersData[option.value].email}</Text>
    </div>
  </Group>
);

function Demo() {
  return <MultiSelect label="Employees of the month" data={Object.keys(usersData)} renderOption={renderMultiSelectOption} />;
}

renderPill

renderPill 콜백으로 pill 렌더링을 커스터마이즈할 수 있어요. 함수는 option(data에 전달된 것), value, onRemove, disabled prop을 받아요. React 노드를 반환해야 해요.

import { MultiSelect, Pill, Avatar } from '@mantine/core';

const users = [{ value: 'Emily Johnson', label: 'Emily Johnson', image: '...' }];
const usersMap = new Map(users.map((user) => [user.value.toString(), user]));

function Demo() {
  return (
    <MultiSelect
      label="Candidates"
      data={users}
      renderPill={({ option }) => {
        const user = usersMap.get(option?.value.toString());
        return (
          <Pill withRemoveButton>
            <Group gap="xs" wrap="nowrap">
              <Avatar src={user?.image} size={16} radius="xl" />
              {option?.label}
            </Group>
          </Pill>
        );
      }}
    />
  );
}

Pill 재정렬 (Reorder pills)

withPillsReorder prop으로 pill을 재정렬할 수 있어요. 다른 pill 앞이나 뒤에 pill을 놓으면 컴포넌트 값이 그에 따라 갱신돼요. disabled 또는 readOnly가 설정되면 재정렬은 자동으로 비활성화돼요.

마우스(드래그 앤 드롭) 또는 키보드로 pill을 재정렬할 수 있어요.

  • Pills는 Tab 순서에 포함되지 않아요. 인풋에 포커스된 상태에서 (인풋의 시작에 캐럿이 있을 때) ArrowLeft를 누르면 마지막 pill로 포커스가 이동해요.
  • ArrowLeft와 ArrowRight는 pill 사이를 이동해요 (RTL 인식). 마지막 pill에서 ArrowRight를 누르면 인풋으로 포커스가 돌아가요.
  • Alt + ArrowLeft와 Alt + ArrowRight는 포커스된 pill을 재정렬해요 (RTL 인식).

포커스는 이동한 pill을 따라가므로 재포커스 없이 여러 이동을 연속할 수 있어요.

import { useState } from 'react';
import { MultiSelect } from '@mantine/core';

function Demo() {
  const [value, setValue] = useState(['React', 'Angular', 'Vue']);

  return <MultiSelect label="Drag pills to reorder" data={['React', 'Angular', 'Vue', 'Svelte']} value={value} onChange={setValue} withPillsReorder />;
}

renderPill prop으로 커스텀 pill 렌더러를 사용한다면, 재정렬이 계속 동작하도록 렌더 콜백 payload의 reorderProps를 포커스 가능한 pill 루트 요소에 스프레드해요. reorderProps는 키보드 재정렬을 구동하는 tabIndex, data-mantine-pill-index 속성, 키보드 핸들러를 담고 있으므로 사용자가 포커스할 수 있는 요소에 배치해야 해요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return (
    <MultiSelect
      data={['React', 'Angular', 'Vue']}
      withPillsReorder
      renderPill={({ option, onRemove, reorderProps }) => (
        <span {...reorderProps}>
          {option.value} ×
        </span>
      )}
    />
  );
}

스크롤 가능한 드롭다운 (Scrollable dropdown)

기본적으로 옵션 목록은 ScrollArea.Autosize로 감싸져요. 기본 설정을 변경하지 않았다면 maxDropdownHeight prop으로 드롭다운 최대 높이를 제어할 수 있어요.

네이티브 스크롤바를 사용하려면 withScrollArea={false}로 설정해요. 이 경우 Styles API로 드롭다운 스타일을 변경해야 해요.

드롭다운을 뷰포트 높이에 맞추기 (Fit dropdown to viewport height)

floatingHeight="viewport"로 설정하면 드롭다운이 뷰포트의 사용 가능한 세로 공간을 채우도록 커져요. 이 모드에서는 flip 미들웨어가 비활성화돼요. 드롭다운은 항상 설정된 방향으로 열리고 다른 쪽으로 뒤집히는 대신 뷰포트 가장자리에 제한돼요. 큰 옵션 목록을 다룰 때 유용해요.

import { MultiSelect } from '@mantine/core';

const data = Array(100).fill(0).map((_, index) => `Option ${index}`);

function Demo() {
  return <MultiSelect label="Fits viewport height" data={data} searchable floatingHeight="viewport" />;
}

옵션 그룹 (Group options)

import { MultiSelect } from '@mantine/core';

function Demo() {
  return (
    <MultiSelect
      label="Your favorite libraries"
      data={[
        { group: 'Frontend', items: ['React', 'Angular', 'Vue'] },
        { group: 'Backend', items: ['Express', 'Koa', 'Django'] },
      ]}
    />
  );
}

비활성 옵션 (Disabled options)

옵션이 비활성화되면 선택할 수 없고 키보드 네비게이션에서 무시돼요. 사용자가 비활성 옵션을 값으로 입력할 수는 여전히 있어요. 특정 값을 금지하려면 제어 컴포넌트를 사용하고 onChange 함수에서 걸러내요.

Combobox props

comboboxProps로 Combobox prop을 재정의할 수 있어요. MultiSelect에 노출되지 않는 일부 prop(예: withinPortal)을 변경해야 할 때 유용해요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect comboboxProps={{ withinPortal: false }} data={['React', 'Angular', 'Vue']} />;
}

드롭다운 z-index 변경 (Change dropdown z-index)

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect comboboxProps={{ middlewares: { shift: { crossAxis: true } } }} data={['React', 'Angular', 'Vue']} />;
}

팝오버 안에서 사용 (Inside Popover)

팝오버 안에서 MultiSelect을 사용하려면 withinPortal: false로 설정해야 해요.

import { Popover, Button, MultiSelect } from '@mantine/core';

function Demo() {
  return (
    <Popover width={400} position="bottom" withArrow shadow="md">
      <Popover.Target><Button>Toggle popover</Button></Popover.Target>
      <Popover.Dropdown>
        <MultiSelect comboboxProps={{ withinPortal: false }} data={['React', 'Angular', 'Vue']} searchable />
      </Popover.Dropdown>
    </Popover>
  );
}

드롭다운 열림 상태 제어 (Control dropdown opened state)

dropdownOpened prop으로 드롭다운 열림 상태를 제어할 수 있어요. 또한 onDropdownClose와 onDropdownOpen으로 열림 상태 변경을 들을 수 있어요.

import { MultiSelect, Button } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';

function Demo() {
  const [dropdownOpened, { toggle }] = useDisclosure();
  return (
    <>
      <Button onClick={toggle}>Toggle dropdown</Button>
      <MultiSelect data={['React', 'Angular', 'Vue']} label="Your favorite library" dropdownOpened={dropdownOpened} />
    </>
  );
}

드롭다운 위치 (Dropdown position)

기본적으로 드롭다운은 공간이 충분하면 인풋 아래에, 그렇지 않으면 인풋 위에 표시돼요. position과 middlewares prop으로 이 동작을 변경할 수 있는데, 이 prop들은 내부 Popover 컴포넌트로 전달돼요.

항상 인풋 위에 표시되는 드롭다운 예시:

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect comboboxProps={{ position: 'top' }} data={['React', 'Angular', 'Vue']} label="Your favorite libraries" />;
}

드롭다운 너비 (Dropdown width)

드롭다운 너비를 변경하려면 comboboxProps에서 width prop을 설정해요. 기본적으로 드롭다운 너비는 인풋 너비와 같아요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect comboboxProps={{ width: 400 }} data={['React', 'Angular', 'Vue']} label="Your favorite libraries" />;
}

드롭다운 오프셋 (Dropdown offset)

드롭다운 오프셋을 변경하려면 comboboxProps에서 offset prop을 설정해요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect comboboxProps={{ offset: 20 }} data={['React', 'Angular', 'Vue']} label="Your favorite library" />;
}

드롭다운 애니메이션 (Dropdown animation)

기본적으로 드롭다운 애니메이션은 비활성화돼 있어요. 활성화하려면 내부 Transition 컴포넌트로 전달될 transitionProps를 설정할 수 있어요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect comboboxProps={{ transitionProps: { transition: 'pop', duration: 200 } }} data={['React', 'Angular', 'Vue']} label="Your favorite libraries" />;
}

드롭다운 패딩 (Dropdown padding)

import { MultiSelect } from '@mantine/core';

function Demo() {
  return (
    <>
      <MultiSelect styles={{ dropdown: { padding: 0 } }} data={['React', 'Angular', 'Vue']} label="Zero padding" />
      <MultiSelect styles={{ dropdown: { padding: 10 } }} data={['React', 'Angular', 'Vue']} label="10px padding" mt="md" />
    </>
  );
}

드롭다운 그림자 (Dropdown shadow)

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect comboboxProps={{ shadow: 'xl' }} data={['React', 'Angular', 'Vue']} label="Your favorite libraries" />;
}

왼쪽·오른쪽 섹션 (Left and right sections)

MultiSelect은 leftSection과 rightSection prop을 지원해요. 이 섹션들은 인풋 래퍼 안에서 절대 위치로 렌더링돼요. 아이콘, 인풋 컨트롤 또는 다른 요소를 표시하는 데 사용할 수 있어요.

섹션 스타일과 콘텐츠를 제어하려면 다음 prop을 사용할 수 있어요.

  • rightSection/leftSection – 인풋의 해당 쪽에 렌더링할 React 노드
  • rightSectionWidth/leftSectionWidth – 오른쪽 섹션의 너비와 인풋 해당 쪽의 패딩을 제어해요. 기본적으로 컴포넌트 size prop에 의해 제어돼요.
  • rightSectionPointerEvents/leftSectionPointerEvents – 섹션의 pointer-events 속성을 제어해요. 비대화형 요소를 렌더링하고 싶다면 none으로 설정해 클릭이 인풋으로 통과하게 해요.

Input props

MultiSelect 컴포넌트는 Input 및 Input.Wrapper 컴포넌트 기능과 모든 input 요소 prop을 지원해요. MultiSelect 문서에는 컴포넌트가 지원하는 모든 기능이 포함되지는 않아요. 사용 가능한 모든 기능은 Input 문서를 참고해요.

읽기 전용 (Read only)

readOnly로 설정하면 인풋을 읽기 전용으로 만들 수 있어요. readOnly가 설정되면 MultiSelect은 제안을 표시하지 않고 onChange 함수도 호출하지 않아요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect label="Your favorite libraries" placeholder="Pick value" data={['React', 'Angular', 'Vue']} defaultValue={['React']} readOnly />;
}

비활성 (Disabled)

disabled로 설정하면 인풋을 비활성화할 수 있어요. disabled가 설정되면 사용자가 인풋과 상호작용할 수 없고 MultiSelect은 제안을 표시하지 않아요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect label="Your favorite libraries" placeholder="Pick value" data={['React', 'Angular', 'Vue']} disabled />;
}

오류 상태 (Error state)

import { MultiSelect } from '@mantine/core';

function Demo() {
  return (
    <>
      <MultiSelect label="Boolean error" error data={['React', 'Angular']} />
      <MultiSelect label="With error message" error="Invalid name" data={['React', 'Angular']} mt="md" />
    </>
  );
}

성공 상태 (Success state)

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect label="Multi Select" error="Looks good!" data={['React', 'Angular', 'Vue']} defaultValue={['React']} />;
}

Styles API

MultiSelect은 Styles API를 지원해요. classNames prop으로 컴포넌트의 내부 요소에 스타일을 추가할 수 있어요.

Styles API 셀렉터:

  • wrapper – Input의 루트 요소
  • input – 인풋 요소
  • section – 왼쪽·오른쪽 섹션
  • bottomSection – 인풋 테두리 하단 안쪽에 렌더링되는 아래쪽 섹션 요소
  • root – 루트 요소
  • label – 라벨 요소
  • required – 라벨 안에 렌더링되는 필수 별표 요소
  • description – 설명 요소
  • error – 오류 요소
  • success – 성공 요소
  • dropdown – 드롭다운 루트 요소
  • options – 옵션 래퍼
  • option – 옵션
  • empty – 결과 없음 메시지
  • group – 옵션 그룹 래퍼
  • groupLabel – 옵션 그룹 라벨
  • pill – 값 pill
  • inputField – 인풋 필드
  • pillsList – pill 목록, 인풋 필드도 포함

요소 ref 가져오기 (Get element ref)

import { useRef } from 'react';
import { MultiSelect } from '@mantine/core';

function Demo() {
  const ref = useRef<HTMLInputElement>(null);
  return <MultiSelect ref={ref} />;
}

접근성 (Accessibility)

label prop 없이 MultiSelect을 사용하면 스크린 리더가 제대로 알리지 못해요.

// Inaccessible input – screen reader will not announce it properly
import { MultiSelect } from '@mantine/core';
function Demo() { return <MultiSelect placeholder="Pick value" data={['React', 'Angular']} />; }

aria-label을 설정하면 인풋을 접근 가능하게 만들 수 있어요. 이 경우 라벨은 보이지 않지만 스크린 리더가 알려줘요.

// Accessible input – it has aria-label
import { MultiSelect } from '@mantine/core';
function Demo() { return <MultiSelect aria-label="Your favorite libraries" data={['React', 'Angular']} />; }

label prop이 설정되어 있으면 인풋은 접근 가능하며 aria-label을 설정할 필요가 없어요.

지우기 버튼의 aria-label을 설정하려면 clearButtonProps를 사용해요. clearable이 설정된 경우에만 필요해요.

import { MultiSelect } from '@mantine/core';

function Demo() {
  return <MultiSelect clearable clearButtonProps={{ 'aria-label': 'Clear selection' }} data={['React', 'Angular']} defaultValue={['React']} />;
}

Backspace 키

검색 인풋이 비어 있을 때 사용자가 Backspace 키를 누르면 마지막 선택 항목이 제거돼요. 이 동작은 내장되어 있어 비활성화할 수 없어요.

더 알아보기 (Learn more)