Drawer
창의 한쪽 변에 붙는 패널입니다. 페이지 위에 떠서 열고 닫힐 수도 있고, 레이아웃의 일부로 고정된 사이드바가 될 수도 있습니다. 어느 쪽이든 같은 패널입니다.
import { Button, Drawer } from 'neba';
<Drawer trigger={<Button variant="outline">Open navigation</Button>} title="Workspace">
<List>…</List>
</Drawer>;Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| locale | string | — | BCP 47 태그. ×의 접근성 이름을 이 언어로 씁니다. 지원하지 않는 태그는 영어로 |
| side | 'top' | 'right' | 'bottom' | 'left' | 'left' | 패널이 붙는 창의 변. NebaSide가 어디서나 그렇듯 물리적입니다. 창 위쪽의 drawer는 어떤 쓰기 방향에서도 위쪽입니다 |
| mode | 'overlay' | 'inline' | 'overlay' | overlay는 스크림 위에 뜨고 focus를 가두며 닫을 수 있습니다. inline은 레이아웃의 일부이며 스크림도, portal도, 닫을 것도 없습니다 |
| size공통 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | 타입 스케일과 여백, 그리고 옆면 패널의 기본 너비까지 함께 정합니다 |
| color공통 | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | 의미론적 색 역할. 시트는 물들지 않으므로 가장자리와 포커스 링에만 나타납니다 |
| density공통 | 'default' | 'compact' | 'default' | 여백만 바꿉니다 |
| open | boolean | — | 열림 여부. onOpenChange와 함께 쓰면 제어 컴포넌트가 됩니다. inline에서는 패널이 레이아웃에 있는지를 뜻합니다 |
| defaultOpen | boolean | — | 비제어 drawer의 초기 상태. overlay에서는 false, inline에서는 true가 기본입니다. 열어야 나타나는 고정 사이드바는 고정 사이드바가 아니기 때문입니다 |
| onOpenChange | (open: boolean) => void | — | 열리거나 닫힐 때 호출 |
| trigger | ReactElement | — | drawer를 여는 요소. Base UI가 연결합니다. overlay 전용, inline drawer는 열 것이 없으므로 렌더링되지 않습니다 |
| title | ReactNode | — | 제목. drawer의 이름이 되는 heading으로 렌더링됩니다 |
| description | ReactNode | — | 제목 아래 한 줄이자 drawer의 접근성 설명 |
| actions | ReactNode | — | 패널 바닥에 고정되는 버튼 줄. 끝 정렬됩니다. DrawerClose가 그중 하나를 닫기 버튼으로 만듭니다 |
| dividers | boolean | false | 구역 사이를 여백 대신 하이라인으로 나눕니다. 본문이 스크롤되는 순간부터 켜는 편이 좋습니다 |
| showClose | boolean | — | 모서리의 ×. overlay에서는 켜짐, inline에서는 꺼짐이 기본입니다. 다시 열 방법 없이 고정 사이드바를 닫는 ×는 되돌릴 수 없는 문입니다 |
| closeLabel | string | — | × 버튼의 접근성 이름 |
| extent | number | string | — | 패널이 자기 변에서 얼마나 들어오는지, left/right에서는 너비, top/bottom에서는 높이입니다. 숫자는 픽셀입니다 |
| rounded | boolean | true | 페이지를 향한 변의 두 모서리만 깎습니다. 창 가장자리에 닿은 모서리는 언제나 각집니다 |
| modal | boolean | 'trap-focus' | true | 뒤 페이지를 가져갈지. trap-focus는 스크롤과 클릭은 남기고 focus만 가둡니다. overlay 전용 |
| dismissible | boolean | true | Escape나 스크림 클릭으로 닫히는지. overlay 전용 |
| children | ReactNode | — | 본문. 스크롤되는 유일한 부분입니다 |
<div>의 native 속성은 panel로 전달됩니다. color · title · children만 위 표와 이름이 겹쳐 제외됩니다.
DrawerClose는 Base UI의 Dialog.Close를 그대로 내보낸 것입니다. render prop을 주면 어떤 요소든 자기가 속한 drawer를 닫습니다: <DrawerClose render={<Button>Cancel</Button>} />. overlay drawer의 것이며, inline drawer는 dialog가 아닙니다.
공통 축은 prop 규칙에서 설명합니다.
예시
side
side는 패널이 붙는 변입니다. left와 right는 size 사다리에서 너비를 가져오고 높이를 채우며, top과 bottom은 너비를 채우고 내용만큼의 높이를 갖되 창의 85%를 넘지 않습니다.
mode
기본값인 overlay는 열어서 쓰는 drawer입니다. 스크림, focus 가둠, Esc, 그리고 닫힐 때 trigger로 돌아가는 focus까지 포함됩니다. inline은 같은 패널을 레이아웃에 놓습니다. 스크림도 portal도 닫을 것도 없으며, open이 패널을 흐름 안에 둘지 정합니다. 기본이 열림이므로 고정 사이드바에는 별도의 state가 필요 없습니다.
하나의 컴포넌트이므로, breakpoint에서 햄버거로 바뀌는 사이드바는 컴포넌트를 갈아 끼우는 일이 아니라 mode 하나가 바뀌는 일입니다.
rounded
rounded는 페이지를 향한 변의 두 모서리만 깎습니다. 옆면 패널이라면 위아래, 위아래 패널이라면 안쪽 두 개입니다. 창 가장자리에 닿은 모서리는 각진 채로 둡니다. 패널이 시트가 아니라 창의 연장처럼 읽혀야 한다면 끄세요.
dividers와 스크롤
스크롤되는 것은 본문뿐이므로 title, description, actions는 제자리에 있습니다. dividers는 구역 사이의 여백을 하이라인으로 바꾸는데, 헤더가 움직이지 않았다고 말해 주는 것이 바로 그 선입니다.
extent
extent는 패널이 자기 변에서 얼마나 들어오는지입니다. left와 right에서는 너비, top과 bottom에서는 높이입니다. 숫자는 픽셀, 문자열은 CSS 길이입니다. 그대로 두면 옆면 패널은 size가 정한 너비를 씁니다.
<Drawer side="right" extent={420} title="Details" />
<Drawer side="bottom" extent="50vh" title="Filters" />접근성
overlaymode에서 패널은 modal dialog입니다. focus가 안에 갇히고, 뒤 페이지는 inert가 되며, Esc로 닫히고 focus는 trigger로 돌아갑니다.title이 drawer의 이름이 되고description이 설명이 되며,aria-labelledby와aria-describedby로 연결됩니다. 둘 다 없는 drawer에는aria-label을 따로 주세요.modal="trap-focus"는 페이지의 스크롤과 클릭은 남기고 focus만 안에 붙잡습니다.dismissible={false}는 Esc와 스크림 클릭을 모두 취소합니다. 그런 drawer에는 닫는 action을 반드시 함께 주세요. 다른 출구가 없습니다.inlinedrawer는 dialog가 아닙니다. focus를 가져가지도, 가두지도, 알리지도 않습니다.title은 평범한 heading이므로 페이지의 heading 순서 안에 놓으세요.- ×의 접근성 이름은
locale이 정합니다.closeLabel로 직접 쓸 수도 있습니다.