PlDrawer
창의 한 가장자리에 붙은 판입니다. 한 컴포넌트에 두 가지가 들어 있는데, 사실 같은 판이기 때문입니다: 열어서 쓰는 서랍과, 그냥 페이지의 일부인 서랍.
import { PlButton, PlDrawer, PlDrawerClose } from 'plass-ui';
<PlDrawer side="right" trigger={<PlButton>Filters</PlButton>} title="Filters">
Everything you can narrow by.
</PlDrawer>;import 'package:plass_ui/plass_ui.dart';
PlDrawer(
side: PlassSide.right,
open: filtering,
onOpenChanged: (bool next) => setState(() => filtering = next),
title: const Text('Filters'),
child: const FilterForm(),
);Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| side공통 | 'top' | 'right' | 'bottom' | 'left' | 'left' | 판이 붙는 가장자리. 논리적이 아니라 물리적입니다 |
| mode | 'overlay' | 'inline' | 'overlay' | overlay는 여는 서랍 — 스크림·포커스 트랩·Escape. inline은 레이아웃 속의 판입니다 |
| open | boolean | — | 서랍이 보이는지. onOpenChange와 함께 controlled로 씁니다 |
| defaultOpen | boolean | — | uncontrolled일 때의 시작 상태. overlay는 false, inline은 true입니다 |
| onOpenChange | (open: boolean) => void | — | 열림 상태가 바뀔 때 호출됩니다 |
| trigger | ReactElement | — | 서랍을 여는 요소. overlay 전용입니다 |
| title | ReactNode | — | 서랍의 이름이 되는 제목 |
| description | ReactNode | — | 제목 아래 한 줄이자 서랍의 접근 가능한 설명 |
| actions | ReactNode | — | 판 아래에 고정되는 줄. 끝 정렬로 배치됩니다 |
| dividers | boolean | false | 섹션 사이를 여백 대신 얇은 선으로 가릅니다 |
| showClose | boolean | — | 모서리의 ×. overlay에서는 켜지고 inline에서는 꺼집니다 |
| closeLabel | string | 'Close' | × 버튼의 접근 가능한 이름 |
| extent | number | string | — | 판이 가장자리에서 얼마나 들어오는지 — 좌우는 너비, 상하는 높이 |
| rounded | boolean | true | 페이지를 향한 두 모서리만 깎습니다. 창 가장자리 쪽은 언제나 각집니다 |
| modal | boolean | 'trap-focus' | true | 뒤의 페이지를 가져가는지. trap-focus는 페이지를 살려 둔 채 포커스만 붙잡습니다 |
| dismissible | boolean | true | Escape와 스크림 누름으로 닫히는지. overlay 전용입니다 |
| size공통 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | 판의 너비, 반경, 여백 |
| color공통 | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | 의미론적 색 역할. 안쪽의 focus ring까지만 닿습니다 |
| density공통 | 'default' | 'compact' | 'default' | 섹션이 얼마나 촘촘히 놓이는지 |
| classNames | { backdrop?: string } | — | className이 닿지 않는 부분에 붙는 class. backdrop은 표면 뒤에 깔리는 scrim입니다 |
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| open * | bool | — | 서랍이 보이는지. onOpenChange와 함께 controlled로 씁니다 |
| onOpenChanged | ValueChanged<bool>? | — | 열림 상태가 무엇이 되어야 하는지로 호출됩니다. ×와 바깥 누름은 행동하는 대신 보고합니다 |
| child | Widget? | — | 본문 — 스크롤되는 유일한 부분 |
| side공통 | PlassSide | PlassSide.left | 판이 붙는 가장자리. 논리적이 아니라 물리적입니다 |
| mode | PlDrawerMode | PlDrawerMode.overlay | overlay는 여는 서랍 — 스크림·포커스 트랩·Escape. inline은 레이아웃 속의 판입니다 |
| title | Widget? | — | 서랍의 이름이 되는 제목 |
| description | Widget? | — | 제목 아래 한 줄이자 서랍의 접근 가능한 설명 |
| actions | List<Widget>? | — | 판 아래에 고정되는 줄. 끝 정렬로 배치됩니다 |
| dividers | bool | false | 섹션 사이를 여백 대신 얇은 선으로 가릅니다 |
| showClose | bool? | — | 모서리의 ×. overlay에서는 켜지고 inline에서는 꺼집니다 |
| closeLabel | String | 'Close' | × 버튼의 접근 가능한 이름 |
| extent | double? | — | 판이 가장자리에서 얼마나 들어오는지, 논리 픽셀 — 좌우는 너비, 상하는 높이 |
| rounded | bool | true | 페이지를 향한 두 모서리만 깎습니다. 창 가장자리 쪽은 언제나 각집니다 |
| modal | bool | true | 뒤의 화면을 키보드뿐 아니라 포인터에서도 가져가는지 |
| dismissible | bool | true | Escape와 스크림 누름으로 닫히는지. overlay 전용입니다 |
| size공통 | PlassSize | PlassSize.md | 판의 너비, 반경, 여백 |
| color공통 | PlassColor | PlassColor.primary | 의미론적 색 역할. 안쪽의 focus ring까지만 닿습니다 |
| density공통 | PlassDensity | PlassDensity.standard | 섹션이 얼마나 촘촘히 놓이는지 |
나머지 <div> 속성은 모두 판으로 전달되고, className도 마찬가지입니다. overlay 모드에서 판 뒤에 깔리는 scrim은 같은 portal 안의 다른 요소이므로 classNames.backdrop으로 닿습니다. inline 모드에는 그 scrim이 없습니다.
라이브러리 전체에서 공유하는 축이 무엇을 뜻하는지는 prop 규약에 있습니다.
두 가지 mode, 하나의 판
둘을 가르는 것은 mode이고, 이것은 variant와는 별개의 축입니다. variant는 이미 라이브러리 전체에서 표면의 무게를 뜻하므로, 여기 쓰면 아무것도 아닌 것에 이름을 두 번 붙이는 셈이 됩니다.
overlay: 열리고, 스크림 위에서 페이지 위에 떠 있고, 포커스를 붙잡고, 닫힙니다. 햄버거 뒤의 내비게이션 서랍, 표 옆의 필터 판.inline: 레이아웃의 일부이고 페이지가 그 주위로 배치됩니다. 스크림도, 포커스 트랩도, 닫을 것도 없습니다. 그냥 거기 있는 사이드바.
그 외의 모든 것은 동일합니다. 그래서 사이드바가 브레이크포인트에서 햄버거가 될 때 호출하는 쪽이 컴포넌트를 갈아 끼우지 않아도 되는 것입니다.
defaultOpen도 그것을 따릅니다. overlay에서는 false, inline에서는 true입니다. 나타나기 전에 열어야 하는 고정 사이드바는 고정 사이드바가 아니기 때문입니다.
variant도 elevation도 없음
세 재질은 "이 표면이 페이지에 대해 얼마나 자기를 선언하는가"에 답하는데, 창의 가장자리를 차지한 판은 이미 답을 했습니다. overlay 서랍은 떠 있고 사다리 맨 위의 그림자를 답니다. inline 서랍은 레이아웃의 일부라 그림자가 없습니다. 어느 쪽도 선택지로 내놓을 값어치가 없습니다.
Examples
side
PlassSide가 어디서나 그렇듯 논리적이 아니라 물리적입니다. 창 위쪽을 따라 놓인 서랍은 어떤 쓰기 방향에서도 위쪽에 있습니다.
판은 창 쪽은 각지고 자유로운 쪽은 깎여 있습니다. 페이지를 향한 모서리는 하우스 필렛을 받고, 가장자리에 붙은 둘은 받지 않습니다. 보이는 끝이 없는 것에서 깎아 낸 모서리는 아무것도 깎지 않은 것이기 때문입니다. 얇은 선도 같은 규칙을 따라 자유로운 가장자리에만 그려집니다.
left나 right 판은 size가 뜻하는 너비를 가지고, top이나 bottom 판은 안에 든 것만큼 높되 창의 85%까지입니다. 세 줄이 든 바텀 시트는 세 줄 높이여야 합니다. extent가 어느 쪽이든 덮어씁니다.
아무것도 미끄러지지 않습니다
판은 페이드만 합니다. 미끄러져 들어오는 서랍은 전환이 이어지는 내내 자기 글자를 화면 위로 끌고 다니는 것이고, 판은 글자와 컨트롤 뿐입니다. 그러니 여기는 무변형 규칙의 예외가 아니라, 그 규칙이 쓰인 이유 그 자체입니다.
판이 가장자리에서 왔다고 말해 주는 것은 그것이 가장자리에 붙어 있다는 사실입니다.
dividers
헤더와 본문과 액션 사이를 여백 대신 얇은 선으로 가릅니다. 본문이 스크롤되기 시작하는 순간부터 켤 만합니다. 헤더가 제자리에 있었다고 말해 주는 것이 그 선입니다.
어느 쪽이든 스크롤되는 것은 본문뿐입니다.
Accessibility
overlay서랍은 떠 있는 동안 포커스를 붙잡고, 나갈 때 원래 자리로 돌려주며, 뒤의 화면을 가져갑니다.title이 이름을 붙이고description이 설명합니다. 둘 다 그냥 근처에 놓이는 것이 아니라 판에 연결되고, 제목은 heading으로 안내됩니다.inline서랍은 dialog가 아니고 그 어느 것도 선언하지 않습니다. 레이아웃 속의 판이고, 제목도 평범한 제목입니다.dismissible={false}는 Escape도 스크림 누름도 거절합니다. 그 둘을 거절하는 서랍에는 그것에 답할 액션을 주세요. 다른 출구가 없습니다.
- 포커스 트랩, 스크롤 잠금,
aria-labelledby/aria-describedby연결, 뒤 페이지의 inert 처리는 전부 Base UI의 것입니다.modal="trap-focus"는 포커스는 안에 붙잡아 두면서 페이지는 스크롤하고 클릭할 수 있게 남겨 둡니다. PlDrawerClose는 uncontrolled 서랍의 Cancel 버튼이 부를 것이 있도록 존재합니다.render가 그것을 진짜 Plass 버튼으로 만듭니다:<PlDrawerClose render={<PlButton variant="ghost">Cancel</PlButton>} />.
- 들어 올리기, 스크림, focus scope, Escape, 나갈 때 포커스를 되돌려주는 것은 전부
PlassPortal의 것입니다.PlModal과PlOverlay가 서 있는 것과 같은 층이라, 오버레이 위에 열린 서랍에 이음매가 보이지 않습니다.
React 빌드와 다른 점
| React | Flutter | 이유 |
|---|---|---|
open / defaultOpen / onOpenChange | open / onOpenChanged | Flutter의 컨트롤은 controlled이고, 이 패키지의 상태 있는 위젯도 전부 그렇습니다. |
trigger | — | 여기서는 트리거를 연결할 대상이 없습니다. 앱이 open을 세워 서랍을 열고, 그 일을 하는 버튼은 앱의 것입니다. |
PlDrawerClose | — | 저쪽에서는 uncontrolled 서랍의 Cancel 버튼이 부를 것이 필요해서 있습니다. 여기서는 모든 서랍이 controlled이므로 버튼은 이미 onOpenChanged를 있습니다. |
extent: number | string | extent: double | 픽셀은 픽셀 그대로입니다. 받을 CSS 길이가 없습니다. |
modal: boolean | 'trap-focus' | modal: bool | 달라지는 두 값은 "포인터를 막는다"와 "막지 않는다"입니다. Flutter에는 세 번째가 될 스크롤 잠금이 없습니다. |
className, style | — | 전달할 class 목록도 style 속성도 없습니다. |