Drawer
Un panneau qui glisse depuis n’importe quel bord et suit votre doigt, avec des points d’accroche, une poignée fonctionnelle et des drawers imbriqués qui s’empilent.
pnpm dlx shadcn@latest add https://hextaui.com/r/drawer.jsonAjoute le composant, les tokens de thème HextaUI et les composants HextaUI dont il dépend.
Ajoutez les tokens de thème à votre CSS global, si ce n’est pas déjà fait.
Installez les dépendances.
pnpm add @base-ui/react @tabler/icons-react cnCopiez et collez le code suivant dans votre projet.
components/ui/drawer.tsx components/ui/sheet.tsx components/ui/button.tsx Mettez à jour les chemins d’import selon la configuration de votre projet.
Directions
Définissez swipeDirection sur <Drawer /> pour choisir le bord. Le drawer s’ouvre depuis ce bord et se balaie en retour vers lui. Les drawers du haut et du bas affichent une poignée par défaut.
Points d’accroche
Passez snapPoints pour stabiliser un drawer du bas à des hauteurs prédéfinies. Les nombres de 0 à 1 sont des fractions du viewport, les nombres plus grands des pixels, et les chaînes acceptent px ou rem. La partie visible s’adapte toujours à son contenu : rien ne se cache sous l’écran.
Contenu défilant
<DrawerBody /> défile de lui-même : l’en-tête et le pied de page restent en place. Le balayage ne démarre qu’une fois le corps revenu tout en haut.
Formulaires et clavier
Enveloppez <DrawerContent /> dans <DrawerVirtualKeyboardProvider /> lorsqu’un drawer du bas contient des champs de texte. Sur téléphone, le champ ayant le focus défile dans la vue au-dessus du clavier logiciel au lieu de se cacher derrière. Gardez les champs dans <DrawerBody /> pour que l’en-tête et le pied de page restent en place.
Imbriqué
Un drawer ouvert depuis un drawer du même bord s’empile par-dessus. Ceux de derrière rétrécissent, dépassent au-dessus et suivent votre doigt lorsque vous balayez celui du dessus pour l’écarter.
Confirmer depuis un drawer
Les boîtes de dialogue, boîtes de dialogue d’alerte, sheets et drawers d’un autre bord se superposent au lieu de s’empiler. Le drawer recule et un arrière-plan plus clair le recouvre.
Responsive
Changez swipeDirection avec une media query pour afficher un panneau latéral sur ordinateur et une bottom sheet sur téléphone.
Non modal
Avec modal={false}, il n’y a pas d’arrière-plan, la page continue de défiler et le focus peut quitter le drawer.
Contrôlé
Passez open et onOpenChange pour l’ouvrir de n’importe où, sans déclencheur.
Déclencheurs détachés
Partagez un drawer entre plusieurs déclencheurs avec createDrawerHandle. Chaque déclencheur transmet un payload que le drawer rend via un enfant fonction.
De droite à gauche
Passez dir="rtl" à <DrawerContent /> pour inverser son contenu. swipeDirection désigne un bord physique : "left" reste donc à gauche et la poignée reste sur le bord intérieur.
| Touche | Action |
|---|---|
| EnterSpace | Sur le déclencheur, ouvre le drawer et déplace le focus à l’intérieur. |
| TabShift + Tab | Passe d’un élément focusable à l’autre. Le focus reste dans un drawer modal. |
| Esc | Ferme le drawer le plus haut et rend le focus à son déclencheur. |
- Le drawer est une boîte de dialogue.
<DrawerTitle />l’étiquette et<DrawerDescription />le décrit : incluez donc toujours un titre. - Le balayage n’est jamais la seule sortie : Escape, l’arrière-plan et un bouton
<DrawerClose />le ferment aussi. - La poignée est décorative et masquée aux technologies d’assistance. À la souris, le texte dans le drawer peut être sélectionné sans le faire glisser.
- Avec la réduction des animations, le drawer apparaît et disparaît en fondu au lieu de glisser. Le glissement suit toujours le pointeur.
Construit sur le drawer de Base UI. Chaque partie accepte les props de la primitive qu’elle enveloppe.
| Prop | Type | Par défaut |
|---|---|---|
swipeDirectionLe bord depuis lequel il s’ouvre et la direction qui le ferme. | "up" | "down" | "left" | "right" | "down" |
showSwipeHandleAffiche la poignée. Par défaut true pour haut et bas, false pour gauche et droite. | boolean | – |
snapPointsHauteurs auxquelles un drawer vertical peut se stabiliser. 0 à 1 correspond à une fraction du viewport, >1 à des pixels, et les chaînes acceptent px ou rem. | (number | string)[] | – |
snapPoint | number | string | null | – |
defaultSnapPoint | number | string | null | – |
onSnapPointChange | (snapPoint, details) => void | – |
defaultOpen | boolean | false |
open | boolean | – |
onOpenChange | (open: boolean, details) => void | – |
onOpenChangeCompleteAppelé à la fin de l’animation d’ouverture ou de fermeture. | (open: boolean) => void | – |
modalL’arrière-plan n’est rendu que si true. | boolean | "trap-focus" | true |
disablePointerDismissalLe garde ouvert lors d’un clic sur l’arrière-plan. | boolean | false |
handle | DrawerHandle<Payload> | – |
children | ReactNode | ({ payload }) => ReactNode | – |
| Prop | Type | Par défaut |
|---|---|---|
handle | DrawerHandle<Payload> | – |
payload | Payload | – |
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribut | Description |
|---|---|
data-slot="drawer-trigger" | Ciblez le déclencheur en CSS. |
data-popup-open | Présent tant que son drawer est ouvert. |
Rend le portail, l’arrière-plan, le viewport et la popup, ainsi que la poignée. Les drawers verticaux s’adaptent à leur contenu jusqu’à la hauteur du viewport moins 4 rem. Les drawers latéraux font 75 % de large, jusqu’à 24 rem à partir du breakpoint sm. Remplacez avec h-* ou w-*, ou limitez-vous à un axe avec data-[swipe-axis=y]:.
| Prop | Type | Par défaut |
|---|---|---|
initialFocus | boolean | RefObject | (type) => HTMLElement | boolean | – |
finalFocus | boolean | RefObject | (type) => HTMLElement | boolean | – |
className | string | (state) => string | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribut | Description |
|---|---|
data-slot="drawer-popup" | Le panneau du drawer. |
data-slot="drawer-content" | L’enveloppe interne autour de vos enfants. Elle défile quand rien d’autre ne le fait. |
data-swipe-direction | up, right, down ou left. |
data-swipe-axis | x ou y. |
data-open | Présent tant que le drawer est ouvert. |
data-starting-style | Présent pendant l’animation d’entrée. |
data-ending-style | Présent pendant l’animation de sortie. |
data-swiping | Présent pendant qu’on le fait glisser. |
data-snap-points | Présent lorsque le drawer a des points d’accroche. |
data-expanded | Présent au point d’accroche pleine hauteur. |
data-nested-drawer-open | Présent tant qu’un autre drawer est ouvert par-dessus. |
data-stack | Présent tant qu’un drawer du même bord est empilé par-dessus. |
--drawer-inset | Détache le drawer des bords du viewport. 0px par défaut. |
--drawer-bleed-background | Remplit la zone révélée lorsqu’on tire au-delà du bord. Par défaut, la couleur du popover. |
--drawer-swipe-movement-x | Distance de glissement horizontale. Une variable -y existe aussi. |
--drawer-snap-point-offset | À quelle distance sous le haut se trouve le point d’accroche actuel. |
--nested-drawers | Nombre de drawers ouverts par-dessus. |
Rendu par <DrawerContent /> lorsque modal vaut true. Il s’estompe pendant le balayage et reste visible au moins à moitié lorsqu’il y a des points d’accroche. Un drawer superposé à une sheet ou à un drawer d’un autre bord reçoit un arrière-plan plus clair.
| Attribut | Description |
|---|---|
data-slot="drawer-overlay" | L’arrière-plan. |
data-nested | Présent sur les arrière-plans plus clairs des drawers superposés. |
--drawer-overlay-min-opacity | L’opacité minimale atteinte pendant le balayage. 0, ou 0,5 avec des points d’accroche. |
Rendu par <DrawerContent /> sur le bord intérieur lorsque showSwipeHandle est activé. Tout le drawer peut être glissé : la poignée n’est donc qu’un repère visuel.
| Attribut | Description |
|---|---|
data-slot="drawer-swipe-handle" | La poignée. |
De simples éléments <div> qui mettent en page le drawer. L’en-tête centre son texte dans les drawers verticaux sur petit écran, le corps défile et prend l’espace restant, et le pied de page empile ses actions.
| Attribut | Description |
|---|---|
data-slot="drawer-header" | Titre et description. |
data-slot="drawer-body" | Contenu défilant. |
data-slot="drawer-footer" | Actions. |
| Prop | Type | Par défaut |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <h2> |
| Attribut | Description |
|---|---|
data-slot="drawer-title" | Étiquette le drawer. |
| Prop | Type | Par défaut |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <p> |
| Attribut | Description |
|---|---|
data-slot="drawer-description" | Décrit le drawer. |
Ne rend aucun élément. Placez-le dans <Drawer />, autour de <DrawerContent />. Tant que le clavier logiciel est ouvert, il ajoute de l’espace sous le conteneur de défilement du drawer, fait défiler le champ ayant le focus dans la vue et fait en sorte qu’un appui sur un champ ouvre le clavier sur iOS. Les drawers sans lui ne sont pas affectés.
| Prop | Type | Par défaut |
|---|---|---|
children | ReactNode | – |
| Attribut | Description |
|---|---|
--drawer-keyboard-inset | Défini sur le viewport pendant que le clavier est ouvert : la part du viewport qu’il recouvre. À utiliser avec une valeur de repli de 0px. |
| Prop | Type | Par défaut |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribut | Description |
|---|---|
data-slot="drawer-close" | Ferme le drawer lorsqu’il est pressé. |
createDrawerHandle<Payload>() renvoie un handle qui relie un <Drawer /> à des déclencheurs rendus ailleurs. Créez-le une seule fois, en dehors de votre composant.
- SheetUn panneau qui glisse depuis n’importe quel bord, avec fermeture par balayage, verrouillage du défilement et imbrication empilée.
- Alert dialogUne boîte de dialogue de confirmation pour les actions destructrices ou importantes, qui attend le travail asynchrone et devient une bottom sheet sur mobile.
- CommandUne liste d’actions recherchable, intégrée ou en palette ⌘K, avec pages, raccourcis et correspondances surlignées.
- Context menuUn menu d’actions au clic droit ou à l’appui long, avec sous-menus, éléments case à cocher et radio, et retour visuel de maintien au toucher.
- DialogUne fenêtre au-dessus de la page pour les formulaires et les tâches ciblées, avec en-tête et pied de page épinglés, imbrication, et une bottom sheet balayable sur mobile.
- Dropdown menuUn menu d’actions et d’options derrière un bouton, avec groupes, sous-menus, éléments case à cocher et radio, et raccourcis.
Utilisé dans les blocks
Des blocks qui s’appuient sur Drawer.