Drawer
The drawer component creates a flexible overlay panel that provides focused workspace for forms, detailed views, or supplementary information.
import {Drawer} from "@qualcomm-ui/react/drawer"Overview
The drawer's API is similar to the Dialog component.
Examples
Placement End
The drawer's default placement is end, which maps to the right side of the screen in LTR languages and the left side in RTL languages.
import type {ReactElement} from "react"
import {Button} from "@qualcomm-ui/react/button"
import {Drawer} from "@qualcomm-ui/react/drawer"
import {LoremIpsum} from "@qualcomm-ui/react-core/lorem-ipsum"
export function DrawerPlacementDemo(): ReactElement {
return (
<Drawer.Root>
<Drawer.Trigger>
<Button emphasis="primary" variant="fill">
Open Drawer
</Button>
</Drawer.Trigger>
<Drawer.FloatingPortal>
<Drawer.Body>
<Drawer.Heading>Drawer Title</Drawer.Heading>
<Drawer.CloseButton />
<Drawer.Description>
<LoremIpsum />
</Drawer.Description>
</Drawer.Body>
<Drawer.Footer>
<Drawer.CloseTrigger>
<Button emphasis="primary" size="sm" variant="fill">
Confirm
</Button>
</Drawer.CloseTrigger>
</Drawer.Footer>
</Drawer.FloatingPortal>
</Drawer.Root>
)
}Placement Start
Use placement start to change the placement to the left side of the screen.
<Drawer.Root placement="start">
Controlled State
Drawer visibility can be controlled using the open, onOpenChange and defaultOpen props. These props follow our controlled state pattern.
import {type ReactElement, useState} from "react"
import {Button} from "@qualcomm-ui/react/button"
import {Drawer} from "@qualcomm-ui/react/drawer"
import {LoremIpsum} from "@qualcomm-ui/react-core/lorem-ipsum"
export function DrawerControlledStateDemo(): ReactElement {
const [open, setOpen] = useState<boolean>(false)
return (
<Drawer.Root onOpenChange={setOpen} open={open}>
<Drawer.Trigger>
<Button emphasis="primary" variant="fill">
Open Drawer
</Button>
</Drawer.Trigger>
<Drawer.FloatingPortal>
<Drawer.Body>
<Drawer.Heading>Drawer Title</Drawer.Heading>
<Drawer.CloseButton />
<Drawer.Description>
<LoremIpsum />
</Drawer.Description>
</Drawer.Body>
<Drawer.Footer>
<Drawer.CloseTrigger>
<Button emphasis="primary" size="sm" variant="fill">
Confirm
</Button>
</Drawer.CloseTrigger>
</Drawer.Footer>
</Drawer.FloatingPortal>
</Drawer.Root>
)
}Custom Container
Use the container prop of the Portal component to render the drawer in a custom container. You'll need to override the positioner's default styles to position the drawer correctly.
Consider setting closeOnInteractOutside to false to prevent the drawer from closing when interacting outside the drawer.
import {type ReactElement, useRef} from "react"
import {Button} from "@qualcomm-ui/react/button"
import {Drawer} from "@qualcomm-ui/react/drawer"
import {Portal} from "@qualcomm-ui/react-core/portal"
export function DrawerCustomContainerDemo(): ReactElement {
const containerRef = useRef<HTMLDivElement | null>(null)
return (
<div className="grid gap-4">
<Drawer.Root
closeOnEscape={false}
closeOnInteractOutside={false}
preventScroll={false}
trapFocus={false}
>
<div
ref={containerRef}
className="border-neutral-03 relative box-border flex h-96 w-[600px] overflow-hidden border p-8"
>
<Drawer.Trigger>
<Button emphasis="primary" variant="fill">
Open Drawer
</Button>
</Drawer.Trigger>
</div>
<Portal container={containerRef}>
<Drawer.Positioner className="absolute z-0 h-full w-full">
<Drawer.Content>
<Drawer.Body>
<Drawer.Heading>Title</Drawer.Heading>
<Drawer.CloseButton />
<Drawer.Description>Description</Drawer.Description>
</Drawer.Body>
<Drawer.Footer>
<Drawer.CloseTrigger>
<Button emphasis="primary" size="sm" variant="fill">
Confirm
</Button>
</Drawer.CloseTrigger>
</Drawer.Footer>
</Drawer.Content>
</Drawer.Positioner>
</Portal>
</Drawer.Root>
</div>
)
}Shortcuts
<Drawer.FloatingPortal>
A helper component that combines the portal, positioner, and content components. This shortcut is equivalent to:
<Portal {...props}>
<Drawer.Backdrop {...backdropProps} />
<Drawer.Positioner {...positionerProps}>
<Drawer.Content {...contentProps}>{children}</Drawer.Content>
</Drawer.Positioner>
</Portal>API
The Drawer shares many of its props and data attributes with the Dialog component.
<Drawer.Root>
stringbooleanbooleanboolean'ltr' | 'rtl'
() => HTMLElement
() =>
| Node
| ShadowRoot
| Document
boolean() => HTMLElement
booleanboolean(
event: KeyboardEvent,
) => void
VoidFunction(
event: FocusOutsideEvent,
) => void
(
event: InteractOutsideEvent,
) => void
(
open: boolean,
) => void
- open:The next value of the open state.
(
event: PointerDownOutsideEvent,
) => void
(
event: CustomEvent<{
originalIndex: number
originalLayer: HTMLElement
targetIndex: number
targetLayer: HTMLElement
}>,
) => void
booleanArray<
() => Element
>
- should not have pointer-events disabled
- should not trigger the dismiss event
| 'start'
| 'end'
booleanbooleanboolean| 'dialog'
| 'alertdialog'
| 'inside'
| 'outside'
'sm' | 'md'
booleanbooleanboolean<Drawer.Backdrop>
<div> element by default.string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__backdrop'data-dialog-part'backdrop'data-state| 'open'
| 'closed'
hiddenboolean<Drawer.Positioner>
<div> element by
default.string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-drawer__positioner'data-dialog-part'positioner'data-placement| 'start'
| 'end'
style<Drawer.Content>
<section> element by default.<Dialog.Root>
<Dialog.Positioner>
<Dialog.Content></Dialog.Content>
</Dialog.Positioner>
</Dialog.Root>
string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-drawer__content'data-dialog-part'content'data-placement| 'start'
| 'end'
data-size'sm' | 'md'
data-state| 'open'
| 'closed'
hiddenbooleantabIndex-1
<Drawer.Body>
<div> element by
default.| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__body'data-dialog-part'body'data-size'sm' | 'md'
<Drawer.Heading>
string| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__heading'data-dialog-part'heading'data-size'sm' | 'md'
<Drawer.IndicatorIcon>
<span> element by
default.| LucideIcon
| ReactNode
| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__indicator-icon'data-emphasis| 'info'
| 'success'
| 'warning'
| 'danger'
| 'neutral'
data-size'sm' | 'md'
<Drawer.CloseButton>
<button> element by default.LucideIconstring| ReactElement
| ((
props: object,
) => ReactElement)
className'qui-dialog__close-button'data-dialog-part'close-trigger'