TreeSelect
TreeSelect
계층적 트리 데이터에서 값을 선택하는 select 컴포넌트예요. 단일, 다중, 체크박스(부모-자식 계단식) 세 가지 선택 모드를 지원해요.
출처: 문서
본문
사용법 (Usage)
TreeSelect는 계층적 트리 데이터에서 하나 이상의 값을 선택할 수 있게 해요. 단일(single), 다중(multiple), 체크박스(checkbox, 부모-자식 계단식) 세 가지 선택 모드를 지원해요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Your favorite item" data={data} />;
}
Data prop
data prop에 전달된 데이터는 Tree 컴포넌트와 같은 규칙을 따라야 해요.
- 데이터는
TreeNodeData객체의 배열이어야 해요 - 각 노드는 고유한
value와label키를 가져야 해요 - 각 노드는 자식 노드 배열이 있는
children키를 가질 수 있어요
import { TreeNodeData } from '@mantine/core';
const data: TreeNodeData[] = [
{
value: 'fruits',
label: 'Fruits',
children: [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
],
},
{ value: 'milk', label: 'Milk' },
];
선택 모드 (Selection modes)
TreeSelect는 mode prop으로 제어되는 세 가지 선택 모드를 지원해요.
single(기본) – 단일 값 선택, 입력으로 렌더링돼요multiple– 다중 값 선택, 필(pills)로 렌더링돼요checkbox– 부모-자식 계단식 체크박스 선택, 필로 렌더링돼요
다중 모드 (Multiple mode)
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Your favorite items" data={data} mode="multiple" />;
}
체크박스 모드 (Checkbox mode)
체크박스 모드에서 부모 노드를 체크하면 모든 자식이 자동으로 체크돼요. 부모를 해제하면 모든 자식이 해제돼요. 일부 자식만 체크되면 부모는 불확정(indeterminate) 상태를 보여줘요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Select categories" data={data} mode="checkbox" />;
}
제어 컴포넌트 (Controlled)
import { useState } from 'react';
import { TreeSelect } from '@mantine/core';
// Single mode
function SingleDemo() {
const [value, setValue] = useState<string | null>(null);
return <TreeSelect data={[]} value={value} onChange={setValue} />;
}
// Multiple or checkbox mode
function MultipleDemo() {
const [value, setValue] = useState<string[]>([]);
return <TreeSelect data={[]} mode="multiple" value={value} onChange={setValue} />;
}
검색 가능 (Searchable)
searchable prop을 설정하면 사용자 입력으로 옵션을 필터링할 수 있어요. 검색할 때 매칭되는 노드와 그 조상들이 표시돼요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Your favorite item" data={data} searchable />;
}
아무것도 찾지 못함 (Nothing found)
nothingFoundMessage prop을 설정해 검색어에 일치하는 옵션이 없거나 데이터가 없을 때 주어진 메시지를 표시해요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Your favorite item" data={data} searchable nothingFoundMessage="Nothing found" />;
}
지우기 가능 (Clearable)
clearable prop을 설정하면 오른쪽 섹션에 지우기 버튼을 표시해요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Your favorite item" data={data} clearable />;
}
클릭 시 확장 (Expand on click)
expandOnClick prop을 설정하면 부모 노드를 클릭할 때도 (셰브론 외에) 확장을 토글할 수 있어요. 동작은 선택 모드에 따라 달라져요.
single과multiple– 부모를 클릭하면 확장/축소만 돼요. 잎(leaf) 노드만 선택할 수 있어요checkbox– 부모를 클릭하면 체크 상태를 토글하면서 동시에 확장해요
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Your favorite item" data={data} expandOnClick />;
}
연결선 (Connecting lines)
TreeSelect는 기본적으로 부모와 자식 노드 사이에 연결선을 렌더링해요. withLines={false}로 설정하면 비활성화할 수 있어요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Without connecting lines" data={data} withLines={false} />;
}
엄격한 체크 (Check strictly)
checkStrictly를 설정하면 체크박스 모드에서 부모-자식 계단식을 비활성화해요. 각 노드의 체크 상태가 완전히 독립적이 돼요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Select items" data={data} mode="checkbox" checkStrictly />;
}
체크 전략 (Checked strategy)
checkedStrategy prop은 체크박스 모드에서 어떤 체크된 노드가 값과 필에 나타나는지 제어해요.
child(기본) – 잎 노드만 값에 나타나요all– 모든 체크된 노드(부모와 자식)가 값에 나타나요parent– 완전히 체크된 최상위 부모만 값에 나타나요
import { useState } from 'react';
import { Stack, TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
const [childValue, setChildValue] = useState<string[]>([]);
const [allValue, setAllValue] = useState<string[]>([]);
const [parentValue, setParentValue] = useState<string[]>([]);
return (
<Stack>
<TreeSelect label="checkedStrategy: child (default)" data={data} mode="checkbox" value={childValue} onChange={setChildValue} />
<TreeSelect label="checkedStrategy: all" data={data} mode="checkbox" checkedStrategy="all" value={allValue} onChange={setAllValue} />
<TreeSelect label="checkedStrategy: parent" data={data} mode="checkbox" checkedStrategy="parent" value={parentValue} onChange={setParentValue} />
</Stack>
);
}
최대 값 (Max values)
maxValues prop을 설정하면 다중 및 체크박스 모드에서 선택할 수 있는 값의 개수를 제한할 수 있어요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Pick up to 3 items" data={data} mode="multiple" maxValues={3} />;
}
renderNode
renderNode 콜백으로 드롭다운에서 노드 렌더링을 커스터마이즈할 수 있어요. node, level, expanded, hasChildren, selected, checked, indeterminate, expand 속성을 포함하는 객체로 호출돼요.
import { FileTextIcon, FolderOpenIcon, FolderSimpleIcon } from '@phosphor-icons/react';
import { Group, Text, TreeSelect, TreeSelectProps } from '@mantine/core';
import { data } from './data';
const renderTreeNode: TreeSelectProps['renderNode'] = ({ node, hasChildren, expanded }) => (
<Group gap={5}>
{hasChildren ? (
expanded ? <FolderOpenIcon size={16} /> : <FolderSimpleIcon size={16} />
) : (
<FileTextIcon size={16} />
)}
<span>{node.label}</span>
</Group>
);
function Demo() {
return <TreeSelect label="Your favorite item" data={data} renderNode={renderTreeNode} />;
}
페이로드에는 노드의 확장 상태를 토글하는 expand 함수도 포함돼요. 확장 상태를 외부에서 제어하지 않고 renderNode 안에서 커스텀 확장 컨트롤을 만들 때 사용해요. 확장이 커스텀 컨트롤로만 일어나게 하려면 expandOnClick={false}를 설정해요.
import { CaretRightIcon } from '@phosphor-icons/react';
import { Group, TreeSelect, TreeSelectProps } from '@mantine/core';
import { data } from './data';
const renderTreeNode: TreeSelectProps['renderNode'] = ({ node, hasChildren, expanded, expand }) => (
<Group gap={5}>
{hasChildren ? (
<CaretRightIcon size={16} onClick={expand} style={{ transform: expanded ? 'rotate(90deg)' : 'none' }} />
) : (
<span />
)}
<span>{node.label}</span>
</Group>
);
스크롤 가능한 드롭다운 (Scrollable dropdown)
기본적으로 옵션 목록은 ScrollArea.Autosize로 감싸져요. maxDropdownHeight prop으로 드롭다운 최대 높이를 제어할 수 있어요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Your favorite item" data={data} maxDropdownHeight={200} />;
}
Combobox props
Combobox props를 comboboxProps로 덮어쓸 수 있어요. TreeSelect가 노출하지 않는 일부 props(예: withinPortal)를 변경해야 할 때 유용해요.
import { TreeSelect } from '@mantine/core';
function Demo() {
return <TreeSelect data={[]} comboboxProps={{ withinPortal: false }} />;
}
드롭다운 z-index 변경 (Change dropdown z-index)
import { TreeSelect } from '@mantine/core';
function Demo() {
return <TreeSelect data={[]} comboboxProps={{ zIndex: 1000 }} />;
}
드롭다운 열림 상태 제어 (Control dropdown opened state)
dropdownOpened prop으로 드롭다운 열림 상태를 제어할 수 있어요. 또한 onDropdownClose와 onDropdownOpen으로 드롭다운 열림 상태 변경을 들을 수 있어요.
import { TreeSelect, Button } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { data } from './data';
function Demo() {
const [dropdownOpened, { toggle }] = useDisclosure();
return (
<>
<Button onClick={toggle}>Toggle dropdown</Button>
<TreeSelect label="Your favorite item" data={data} dropdownOpened={dropdownOpened} />
</>
);
}
드롭다운 위치 (Dropdown position)
기본적으로 드롭다운은 공간이 충분하면 입력 아래에 표시되고, 그렇지 않으면 입력 위에 표시돼요. position과 middlewares props를 설정해 이 동작을 바꿀 수 있어요. 이 props는 기반 Popover 컴포넌트로 전달돼요.
항상 입력 위에 표시되는 드롭다운 예시:
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Your favorite item" data={data} position="top" />;
}
드롭다운 너비 (Dropdown width)
드롭다운 너비를 바꾸려면 comboboxProps에서 width prop을 설정해요. 기본적으로 드롭다운 너비는 입력 너비와 같아요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Your favorite item" data={data} comboboxProps={{ width: 400 }} />;
}
드롭다운 오프셋 (Dropdown offset)
드롭다운 오프셋을 바꾸려면 comboboxProps에서 offset prop을 설정해요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
import classes from './Demo.module.css';
function Demo() {
return <TreeSelect label="Your favorite item" data={data} comboboxProps={{ offset: 20 }} />;
}
드롭다운 애니메이션 (Dropdown animation)
기본적으로 드롭다운 애니메이션은 비활성화돼 있어요. transitionProps를 설정해 활성화할 수 있으며, 이 props는 기반 Transition 컴포넌트로 전달돼요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return (
<TreeSelect
label="Your favorite item"
data={data}
transitionProps={{ transition: 'pop', duration: 200 }}
/>
);
}
드롭다운 패딩 (Dropdown padding)
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return (
<>
<TreeSelect label="Zero padding" data={data} comboboxProps={{ dropdownPadding: 0 }} />
<TreeSelect label="10px padding" data={data} comboboxProps={{ dropdownPadding: 10 }} />
</>
);
}
드롭다운 그림자 (Dropdown shadow)
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Your favorite item" data={data} comboboxProps={{ shadow: 'md' }} />;
}
키보드 내비게이션 (Keyboard navigation)
TreeSelect는 드롭다운이 열려 있을 때 다음 키보드 상호작용을 지원해요.
ArrowRight– 강조된 부모 노드를 확장해요ArrowLeft– 강조된 부모 노드를 축소하거나 그 부모로 이동해요ArrowUp/ArrowDown– 옵션 사이를 이동해요Enter– 강조된 옵션을 선택해요
확장 상태 (Expand state)
노드의 확장 상태를 제어할 수 있어요.
import { TreeSelect } from '@mantine/core';
// 기본적으로 특정 노드 확장
// function Demo() { return <TreeSelect data={[]} defaultExpanded={['fruits']} />; }
// 기본적으로 모든 노드 확장
// function Demo() { return <TreeSelect data={[]} defaultExpanded={['__all__']} />; }
// 제어된 확장 상태
// function Demo() {
// const [expanded, setExpanded] = useState<string[]>([]);
// return <TreeSelect data={[]} expanded={expanded} onExpandedChange={setExpanded} />;
// }
왼쪽·오른쪽 섹션 (Left and right sections)
TreeSelect는 leftSection과 rightSection props를 지원해요. 이 섹션들은 입력 래퍼 안에 절대 위치로 렌더링돼요. 아이콘, 입력 컨트롤, 기타 요소를 표시하는 데 사용할 수 있어요.
rightSection/leftSection– 입력의 해당 측면에 렌더링할 React 노드rightSectionWidth/leftSectionWidth– 오른쪽 섹션의 너비와 입력 해당 측면의 패딩을 제어해요. 기본적으로 컴포넌트sizeprop으로 제어돼요.rightSectionPointerEvents/leftSectionPointerEvents– 섹션의pointer-events속성을 제어해요. 비상호작용 요소를 렌더링하려면none으로 설정해 클릭이 입력으로 전달되게 해요.
import { TreeSelect } from '@mantine/core';
import { SquaresFourIcon } from '@phosphor-icons/react';
import { data } from './data';
function Demo() {
const icon = <SquaresFourIcon size={16} />;
return (
<>
<TreeSelect data={data} label="Your favorite item" leftSection={icon} />
<TreeSelect data={data} label="Your favorite item" rightSection={icon} />
</>
);
}
Input props
TreeSelect 컴포넌트는 Input과 Input.Wrapper 컴포넌트의 기능과 모든 input 요소 props를 지원해요. TreeSelect 문서에는 컴포넌트가 지원하는 모든 기능이 포함되어 있지 않아요. 사용 가능한 모든 기능은 Input 문서에서 확인할 수 있어요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return (
<TreeSelect
label="Input label"
description="Input description"
placeholder="Pick value"
data={data}
/>
);
}
읽기 전용 (Read only)
readOnly를 설정하면 입력을 읽기 전용으로 만들어요. readOnly가 설정되면 TreeSelect는 제안을 표시하지 않고 onChange 함수를 호출하지 않아요.
비활성 (Disabled)
disabled를 설정하면 입력을 비활성화해요. disabled가 설정되면 사용자가 입력과 상호작용할 수 없고 TreeSelect는 제안을 표시하지 않아요.
오류 상태 (Error state)
불리언 오류 또는 오류 메시지와 함께 TreeSelect를 표시할 수 있어요.
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return (
<>
<TreeSelect label="Boolean error" data={data} error />
<TreeSelect label="With error message" data={data} error="Invalid value" />
</>
);
}
성공 상태 (Success state)
import { TreeSelect } from '@mantine/core';
import { data } from './data';
function Demo() {
return <TreeSelect label="Tree Select" description="Looks good!" data={data} />;
}
요소 ref 가져오기 (Get element ref)
import { useRef } from 'react';
import { TreeSelect } from '@mantine/core';
function Demo() {
const ref = useRef<HTMLInputElement>(null);
return <TreeSelect data={[]} ref={ref} />;
}
접근성 (Accessibility)
TreeSelect를 label prop 없이 사용하면 화면 판독기가 제대로 알려주지 못해요. aria-label을 설정하면 라벨이 보이지 않아도 화면 판독기가 알려줘요. label prop을 설정하면 별도로 aria-label을 지정할 필요 없이 접근성이 확보돼요.
지우기 버튼에 aria-label을 설정하려면 clearButtonProps를 사용해요. 이는 clearable이 설정된 경우에만 필요하다는 점에 주의해요.
import { TreeSelect } from '@mantine/core';
function Demo() {
return <TreeSelect data={[]} clearable clearButtonProps={{ 'aria-label': 'Clear selection' }} />;
}