PillsInput
PillsInput
커스텀 태그 인풋과 멀티 셀렉트를 만들기 위한 기본 컴포넌트예요. 자체적으로 로직을 포함하지 않고 주어진 children을 렌더링만 하는 유틸리티 컴포넌트예요.
출처: 문서
본문
사용법 (Usage)
PillsInput은 커스텀 태그 인풋, 멀티 셀렉트 및 유사한 컴포넌트를 만드는 데 사용하는 유틸리티 컴포넌트예요. 자체적으로 로직은 포함하지 않고 주어진 children을 렌더링만 해요. 보통 PillsInput은 Pill 컴포넌트와 함께 사용돼요.
import { PillsInput, Pill } from '@mantine/core';
function Demo() {
return (
<PillsInput label="PillsInput">
<Pill.Group>
<Pill>React</Pill>
<Pill>Vue</Pill>
<Pill>Svelte</Pill>
<PillsInput.Field placeholder="Enter items" />
</Pill.Group>
</PillsInput>
);
}
로딩 상태 (Loading state)
loading prop을 설정하면 로딩 인디케이터가 표시돼요. 기본적으로 로더는 인풋 오른쪽에 표시돼요. loadingPosition prop을 'left' 또는 'right'로 바꿔 위치를 변경할 수 있어요. API 호출, 검색, 검증 같은 비동기 작업에 유용해요.
import { PillsInput, Pill } from '@mantine/core';
function Demo() {
return (
<PillsInput label="PillsInput" loading loadingPosition="right">
<Pill.Group>
<Pill>React</Pill>
<Pill>Vue</Pill>
<PillsInput.Field placeholder="Enter items" />
</Pill.Group>
</PillsInput>
);
}
인풋 props (Input props)
PillsInput 컴포넌트는 Input 및 Input.Wrapper 컴포넌트 기능과 모든 div 요소 prop을 지원해요. PillsInput 문서에는 컴포넌트가 지원하는 모든 기능이 포함되지는 않아요. 사용 가능한 모든 기능은 Input 문서를 참고해요.
import { PillsInput, Pill } from '@mantine/core';
function Demo() {
return (
<PillsInput label="PillsInput label" description="Input description">
<Pill.Group>
<Pill>React</Pill>
<Pill>Vue</Pill>
<Pill>Svelte</Pill>
<PillsInput.Field placeholder="Enter items" />
</Pill.Group>
</PillsInput>
);
}
검색 가능한 셀렉트 예시 (Searchable select example)
PillsInput을 Combobox와 결합해 검색 가능한 멀티 셀렉트를 만들 수 있어요.
import { useState } from 'react';
import { PillsInput, Pill, Combobox, CheckIcon, Group, useCombobox } from '@mantine/core';
const groceries = ['🍎 Apples', '🍌 Bananas', '🥦 Broccoli', '🥕 Carrots', '🍫 Chocolate'];
function Demo() {
const combobox = useCombobox({
onDropdownClose: () => combobox.resetSelectedOption(),
onDropdownOpen: () => combobox.updateSelectedOptionIndex('active'),
});
const [search, setSearch] = useState('');
const [value, setValue] = useState<string[]>([]);
const handleValueSelect = (val: string) =>
setValue((current) =>
current.includes(val) ? current.filter((v) => v !== val) : [...current, val]
);
const handleValueRemove = (val: string) =>
setValue((current) => current.filter((v) => v !== val));
const values = value.map((item) => (
<Pill key={item} withRemoveButton onRemove={() => handleValueRemove(item)}>
{item}
</Pill>
));
const options = groceries
.filter((item) => item.toLowerCase().includes(search.trim().toLowerCase()))
.map((item) => (
<Combobox.Option value={item} key={item} active={value.includes(item)}>
<Group gap="sm">
{value.includes(item) ? <CheckIcon size={12} /> : null}
<span>{item}</span>
</Group>
</Combobox.Option>
));
return (
<Combobox store={combobox} onOptionSubmit={handleValueSelect}>
<Combobox.Dropdown>
<Combobox.Options>
<PillsInput onClick={() => combobox.openDropdown()}>
<Pill.Group>
{values}
<Combobox.EventsTarget>
<PillsInput.Field
onFocus={() => combobox.openDropdown()}
onBlur={() => combobox.closeDropdown()}
value={search}
placeholder="Search values"
onChange={(event) => {
combobox.updateSelectedOptionIndex();
setSearch(event.currentTarget.value);
}}
onKeyDown={(event) => {
if (event.key === 'Backspace' && search.length === 0 && value.length > 0) {
event.preventDefault();
handleValueRemove(value[value.length - 1]);
}
}}
/>
</Combobox.EventsTarget>
</Pill.Group>
</PillsInput>
{options.length > 0 ? options : <Combobox.Empty>Nothing found...</Combobox.Empty>}
</Combobox.Options>
</Combobox.Dropdown>
</Combobox>
);
}
접근성 (Accessibility)
label prop 없이 PillsInput을 사용하면 스크린 리더가 제대로 알리지 못해요.
PillsInput.Field 컴포넌트에 aria-label을 설정하면 인풋을 접근 가능하게 만들 수 있어요. 이 경우 라벨은 보이지 않지만 스크린 리더가 알려줘요.
import { PillsInput } from '@mantine/core';
// Accessible input – it has aria-label
function Demo() {
return (
<PillsInput>
<Pill.Group>
<PillsInput.Field aria-label="Your tags" placeholder="Enter items" />
</Pill.Group>
</PillsInput>
);
}
label prop이 설정되어 있으면 인풋은 접근 가능하며 aria-label을 설정할 필요가 없어요.
import { PillsInput } from '@mantine/core';
// Accessible input – it has associated label element
function Demo() {
return (
<PillsInput label="Your tags">
<Pill.Group>
<PillsInput.Field placeholder="Enter items" />
</Pill.Group>
</PillsInput>
);
}