TreeView
서로 속해 있는 항목들을 접었다 펼 수 있는 행으로 보여줍니다. 폴더 목록, 사이드바 내비게이션, 접어 둘 수 있어야 하는 그룹 목록에 씁니다.
import { TreeItem, TreeView } from 'neba';
<TreeView label="Project files" lines="folder" defaultExpanded={['src']}>
<TreeItem value="src" label="src">
<TreeItem value="index" label="index.ts" />
</TreeItem>
<TreeItem value="readme" label="README.md" />
</TreeView>;Props
TreeView
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| variant공통 | 'solid' | 'outline' | 'text' | 'outline' | 표면의 무게. 채움 / 하이라인 / 없음 |
| size공통 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | 행 높이, 타입 스케일, 시트의 반경, 그리고 한 단계 들여쓰는 폭 |
| color공통 | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | 의미론적 색 역할. 임의 색상값은 받지 않습니다 |
| density공통 | 'default' | 'compact' | 'default' | 행의 좌우 여백만 바꿉니다. 행 높이도 들여쓰기도 그대로입니다 |
| elevation공통 | 0 | 1 | 2 | 3 | 0 | 그림자 깊이. 0은 그림자 없음 |
| lines | 'none' | 'simple' | 'folder' | 'simple' | 계층을 어떻게 그릴지. none은 들여쓰기만, simple은 레벨마다 세로선 하나, folder는 거기에 각 행으로 꺾여 들어가는 선까지, 마지막 자식의 세로선은 그 행에서 끊깁니다 |
| expanded | (string | number)[] | — | 열려 있는 가지들. onExpandedChange와 함께 쓰면 controlled가 됩니다 |
| defaultExpanded | (string | number)[] | — | 처음에 열려 있을 가지들 |
| onExpandedChange | (expanded: (string | number)[]) => void | — | 열림 상태가 바뀔 때 |
| selected | (string | number)[] | — | 선택된 행들. multiple이 꺼져 있어도 배열입니다. Accordion의 value와 같은 모양이라, multiple을 켜도 값의 타입은 바뀌지 않습니다 |
| defaultSelected | (string | number)[] | — | 처음에 선택되어 있을 행들 |
| onSelectedChange | (selected: (string | number)[]) => void | — | 선택이 바뀔 때 |
| multiple | boolean | false | 여러 행을 동시에 선택할 수 있는지. 켜면 aria-multiselectable도 함께 붙습니다 |
| disabled | boolean | false | 사용 불가. 모든 행이 반응하지 않습니다 |
| label | string | — | 트리가 읽히는 이름. tree에 aria-label로 붙습니다 |
| children | ReactNode | — | 최상위 TreeItem들 |
expanded와 onExpandedChange로 열림 상태가 controlled가 되고, defaultExpanded로는 uncontrolled가 됩니다. 선택은 selected와 onSelectedChange가 같은 방식으로 다루며, 두 값 모두 행의 value 배열입니다.
나머지 <ul> 속성은 그대로 트리에 전달됩니다. 공용 축은 prop 규약에 있습니다.
TreeItem
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| value | string | number | — | expanded와 selected에서 이 행을 가리키는 식별자. 생략하면 하나가 생성되므로, 코드로 다룰 일이 없는 트리라면 없어도 됩니다 |
| label | ReactNode | — | 행의 텍스트. children이 아닌 별도 prop인 이유는, 트리에서 children은 그 아래 행들이기 때문입니다 |
| startIcon | ReactNode | — | 라벨 앞의 내용, 폴더 글리프, 파일 종류, 상태 점 |
| endIcon | ReactNode | — | 라벨 뒤의 내용, 개수, 배지 |
| action | ReactNode | — | 행 끝에 고정되는 컨트롤. 누를 수 있는 영역 바깥에 놓입니다. 열리기도 하고 메뉴 버튼도 가진 행은 누를 것이 두 개입니다 |
| href | string | — | 행을 링크로 렌더링합니다. 트리가 내비게이션일 때 |
| onClick | MouseEventHandler<HTMLElement> | — | 행을 누를 때, 열리거나 선택되기 전에 호출됩니다. preventDefault를 부르면 둘 다 일어나지 않습니다 |
| expandable | boolean | — | 아직 children이 없는 행에도 펼침 화살표를 그립니다. 처음 열 때 가져오는 가지 |
| disabled | boolean | false | 사용 불가. 열려 있는 가지는 계속 동작합니다 |
| children | ReactNode | — | 이 행 아래의 TreeItem들 |
나머지 <li> 속성은 그대로 행에 전달됩니다.
예시
lines
lines는 계층을 어떻게 그릴지 정합니다. none은 들여쓰기만 하고, simple은 레벨마다 세로선 하나를 내리며, folder는 거기에 각 행으로 꺾여 들어가는 선을 더하고 가지의 마지막 자식 아래에서 세로선을 끊습니다.
variant
sheet는 색으로 채워지지 않습니다. 이미 표면이 있는 Card나 사이드바 안에서는 text를 쓰세요.
행 선택
행을 누르면 선택되고, 자식이 있으면 함께 열립니다. multiple을 켜면 여러 행을 동시에 선택할 수 있고, 끄면 새 행을 고를 때마다 앞의 선택이 교체됩니다.
펼침 화살표는 별도의 과녁입니다. 행을 선택하지 않고 가지만 엽니다.
href
href가 있는 행은 링크로 렌더링되며, 내비게이션 트리는 이것으로 만듭니다. 별도의 tab stop이 되지는 않습니다. 트리 전체가 하나이고, 행에는 방향키로 갑니다.
expandable
닫힌 가지는 DOM에 없으므로, 행을 처음 열 때 자식을 가져오는 트리에는 아직 그릴 것이 없습니다. expandable은 그래도 화살표를 그립니다. onExpandedChange에서 가져온 뒤, 도착하면 행을 렌더링하세요.
<TreeView expanded={expanded} onExpandedChange={load}>
<TreeItem value="remote" label="Remote" expandable>
{children.map((child) => (
<TreeItem key={child.id} value={child.id} label={child.name} />
))}
</TreeItem>
</TreeView>접근성
- 트리는
tree, 각 행은treeitem, 가지의 자식들은group입니다. - 트리 전체가 하나의 tab stop입니다. 안에 들어오면 ArrowUp·ArrowDown이 보이는 행들을 오르내리고, ArrowRight는 닫힌 가지를 열고 열린 가지에서는 그 안으로 들어가며, ArrowLeft는 가지를 닫고 잎에서는 부모로 올라갑니다. Home·End는 양 끝으로, Enter는 focus된 행을 선택합니다. 방향키는 선택을 바꾸지 않습니다.
- RTL에서는 ArrowLeft와 ArrowRight가 바뀌므로, 전진 방향의 화살표는 언제나 "더 안쪽"을 뜻합니다.
label을 넘겨 트리에 이름을 주세요. 없으면 screen reader가 이름 없는 트리로 읽습니다.multiple은 트리에aria-multiselectable을 붙입니다.