组件 · 浮层
Drawer
A panel that slides in from the right for details and settings people look at for a while, and on a phone a bottom sheet for picking a place to go or reading a short explanation.
import { Drawer } from "@botiverse/raft-ui";A drawer slides in from the right with details and settings that people look at for a while, scroll and edit, such as the details and members of #launch-week. The page stays in place behind it, under a scrim. On a phone the same component rises from the bottom as a bottom sheet: to pick a place to go, or to read a short explanation with one button.
When to use
| Situation | Use |
|---|---|
| Details or settings people read, scroll and edit | Drawer, from the right |
| A large form filled in one pass: registering or editing an app | The wide Dialog, not a wide drawer (Forms) |
| Switching server, on a phone | Not a drawer: a popover under the top bar (Navigation). Working now is a popover, on a phone too |
| A sentence or two and one button, on a phone | A bottom sheet |
| Acting on one thing | A menu (DropdownMenu, ContextMenu), never a bottom sheet |
| Picking a value, such as sort order or status | The list of a Select, or radio items in a menu |
| Confirming | AlertDialog |
| A glance on hover | PreviewCard |
| A thread or a profile kept open beside the conversation | A side panel (ThreadPanel, Panel), which is not a drawer and has no scrim |
Anatomy
Drawer: the root. ItsswipeDirectionsets the edge it comes from. Required.DrawerTrigger: the button that opens it.DrawerContent: the surface, portaled, with the scrim (DrawerOverlay) behind it when the drawer is modal. Required. It islayer-popoverwith aline-seamedge andshadow-float, like every other popup, not an ink border and a hard shadow: every floating surface uses the same shadow (Elevation).DrawerHeader: the title row. In a drawer from the right it is 56px tall, with aline-seamunder it andDrawerCloseat its end.DrawerTitle: the title. Required, unless the content has anaria-label.DrawerDescription: a sentence under the title.DrawerBody: the content. It scrolls.DrawerFooter: the bottom bar. In a drawer from the right it has aline-seamover it.DrawerActions: the buttons in the footer, in a row at the end in a drawer and stacked full width in a bottom sheet.DrawerClose: closes the drawer. Without children it is the × icon button; withrenderit turns another button into a close button.DrawerPortalandDrawerOverlay: lower-level parts.
Variants
| Variant | Use for |
|---|---|
swipeDirection="right" | The drawer: details and settings on desktop, at most 420px wide and the full window height |
swipeDirection="down" (default) | The bottom sheet, on a phone |
snapPoints | A bottom sheet that opens to a summary and expands to its full height. It shows the grab handle |
Behavior
- A drawer slides in from its edge with
ease-slide. It is the only overlay that moves, because the slide shows where it came from (Motion). - A modal drawer puts the same scrim as a dialog over the rest of the page: the page can't be used while the drawer is open, and the scrim says so. A click on the scrim closes it. A bottom sheet has the scrim too.
- Swiping toward its edge dismisses a drawer.
- A drawer opened from a drawer stacks on top of it, and the one below steps back.
- On a phone, a drawer from the right fills the screen and works as a page: no ×, and Back at the top left.
- A bottom sheet rises to at most 82% of the window height, with
radius-dialogon its top corners, and keeps clear of the home indicator.
Content
- Title it with the words of the button that opened it (Writing).
- In a bottom sheet, write the explanation in one or two sentences, in
foreground-muted, and its button as one action in Title Case: Got It.
Accessibility
- Drawer wraps the Base UI Drawer. It provides the
dialogsemantics, the focus trap of a modal drawer, Esc, swipe to dismiss, and focus return to the trigger. - Give every drawer and bottom sheet a title: a visible
DrawerTitle, or at least anaria-labelonDrawerContent. - The × of
DrawerCloseis labeled "Close". On a phone, give the Back button that replaces it anaria-label("Back"). - The grab handle is hidden from assistive technology. Never make swiping the only way to close: keep ×, Back or a button in the sheet.
Rules
- Keep a drawer to 420px at most. A form that needs more room is the wide Dialog.
- Make the × the same icon button as in a panel header, with the same icon size, and put it last in
DrawerHeader: its icon box ends on the margin, and a Back that replaces it on a phone starts on it (Alignment). - Put the buttons in the footer, never in the header. They are
size="lg"(44px on a phone, 32px on a desktop): Cancel first, Save last (Forms). - Split a long form in a drawer into FormSection parts, the same sections as in the wide dialog, and save them all together with the footer's Save.
- Under each yes-or-no checkbox in a drawer form, add a line that says what happens when it is unchecked. It is a checkbox, not a switch, because the form is saved (Forms).
- Give every bottom sheet a grab handle at its top edge.
- In a bottom sheet list, make each row 52px tall, with a 20px icon and the label in
text-body, medium. Mark the current row with a check mark and bold text.
API
Drawer
| Prop | 类型 | 默认值 |
|---|---|---|
| A ref to imperative actions. - `unmount`: Manually unmounts the drawer. Call this after any externally controlled closing animation finishes. - `close`: Closes the drawer imperatively when called. | RefObject<DrawerRootActions | null> | |
| The content of the drawer. | ReactNode | PayloadChildRenderFunction<unknown> | |
| Whether the drawer is initially open. | boolean | false |
| The initial snap point value when uncontrolled. | DrawerSnapPoint | |
| ID of the trigger that the drawer is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open drawer. | string | |
| Whether to prevent the drawer from closing on outside presses. For non-modal drawers, this also prevents the drawer from closing when focus moves outside of it. | boolean | false |
| A handle to associate the drawer with a trigger. If specified, allows detached triggers to control the drawer's open state. Can be created with the Drawer.createHandle() method. | DrawerPrimitive.Handle<unknown> | |
| Determines if the drawer enters a modal state when open. - `true`: user interaction is limited to just the drawer: focus is trapped, document page scroll is locked, and pointer interactions on outside elements are disabled. - `false`: user interaction with the rest of the document is allowed. - `'trap-focus'`: focus is trapped inside the drawer, but document page scroll is not locked and pointer interactions outside of it remain enabled. | boolean | "trap-focus" | true |
| Event handler called when the drawer is opened or closed. | (open: boolean, eventDetails: DrawerPrimitive.Root.ChangeEventDetails) => void | |
| Event handler called after any animations complete when the drawer is opened or closed. | (open: boolean) => void | |
| Callback fired when the snap point changes. | (snapPoint: DrawerSnapPoint | null, eventDetails: DrawerPrimitive.Root.SnapPointChangeEventDetails) => void | |
| Whether the drawer is currently open. | boolean | |
| The currently active snap point. Use with `onSnapPointChange` to control the snap point. | DrawerSnapPoint | |
| Snap points used to position the drawer. Use numbers between 0 and 1 to represent fractions of the viewport height, numbers greater than 1 as pixel values, or strings in `px`/`rem` units (for example, `'148px'` or `'30rem'`). | DrawerSnapPoint[] | |
| Disables velocity-based snap skipping so drag distance determines the next snap point. | boolean | false |
| The swipe direction used to dismiss the drawer. | SwipeDirection | "down" |
| ID of the trigger that the drawer is associated with. This is useful in conjunction with the `open` prop to create a controlled drawer. There's no need to specify this prop when the drawer is uncontrolled (that is, when the `open` prop is not set). | string |
DrawerActions, DrawerBody
| Prop | 类型 | 默认值 |
|---|---|---|
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
DrawerClose
| Prop | 类型 | 默认值 |
|---|---|---|
| `md` (32px) and `icon-md` for rows, cards, banners, toolbars and panel headers; `lg` and `icon-lg` (the control height) for dialog footers, forms and beside a field; `xl` for a full-width call to action. The size follows the place: see Button › Size by place. | "md" | "lg" | "xl" | "icon-md" | "icon-lg" | DeprecatedButtonSize | |
| "primary" | "accent" | "warning" | "danger" | "default" | "outline" | "information" | "muted" | "success" | "danger-secondary" | "danger-outline" | "ghost" | "on-frame" | ||
| Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | boolean | true |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
DrawerContent
| Prop | 类型 | 默认值 |
|---|---|---|
| DrawerPortalProps | ||
| Determines the element to focus when the drawer is closed. | boolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | HTMLElement | null | void) | |
| Determines the element to focus when the drawer is opened. | boolean | RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | HTMLElement | null | void) | |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
DrawerDescription, DrawerFooter, DrawerHeader
| Prop | 类型 | 默认值 |
|---|---|---|
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
DrawerOverlay
| Prop | 类型 | 默认值 |
|---|---|---|
| Whether the backdrop is forced to render even when nested. | boolean | false |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
DrawerPortal
| Prop | 类型 | 默认值 |
|---|---|---|
| A parent element to render the portal element into. | HTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | |
| Whether to keep the portal mounted in the DOM while the popup is hidden. | boolean | false |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
DrawerTitle
| Prop | 类型 | 默认值 |
|---|---|---|
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
DrawerTrigger
| Prop | 类型 | 默认值 |
|---|---|---|
| A handle to associate the trigger with a drawer. Can be created with the Drawer.createHandle() method. | DrawerPrimitive.Handle<unknown> | |
| ID of the trigger. In addition to being forwarded to the rendered element, it is also used to specify the active trigger for drawers in controlled mode (with the Drawer.Root `triggerId` prop). | string | |
| Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | boolean | true |
| A payload to pass to the drawer when it is opened. | {} | |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
相关组件
改动
- 0.6.0 · Breaking ChangesProps that no longer change anything are removed:
Badgeuppercase;Checkboxsize,variant,color;Statuspulse,attention(status never animates);Switch/SwitchThumbsize;SidebarItemvariant;SidebarItemCount/SidebarItemIndicatorvariantandappearance(the count is always the pink sticker, the indicator the yellow @);NotificationCenterActionButtonvariant;DrawerContentwidth(one-go forms use the wideDialog);AppRailItemIndicatorattention;AppRailItemAttentionchildren;Stepssize,track,renderIndicator,renderTraveler;FilePreviewBadgetype,color. Remove them at call sites.