Drawer
任意の端からスライドインし、指に追従するパネルです。スナップポイント、実際に使えるハンドル、重なっていく入れ子のドロワーに対応します。
pnpm dlx shadcn@latest add https://hextaui.com/r/drawer.jsonコンポーネント、HextaUIのテーマトークン、依存するHextaUIコンポーネントを追加します。
まだ追加していない場合は、グローバルCSSにテーマトークンを追加してください。
依存関係をインストールします。
pnpm add @base-ui/react @tabler/icons-react cn次のコードをコピーしてプロジェクトに貼り付けてください。
components/ui/drawer.tsx components/ui/sheet.tsx components/ui/button.tsx インポートパスは、お使いのプロジェクト構成に合わせて更新してください。
方向
<Drawer /> に swipeDirection を設定して端を選びます。drawer はその端から開き、その端に向かってスワイプして戻ります。上下の drawer には、デフォルトでハンドルが表示されます。
スナップポイント
snapPoints を渡すと、下部の drawer をあらかじめ決めた高さで止められます。0 から 1 の数値はビューポートに対する割合、それより大きい数値はピクセル、文字列は px または rem を受け付けます。表示される部分は常にコンテンツに収まるため、画面の下に隠れるものはありません。
スクロール可能なコンテンツ
<DrawerBody /> は単独でスクロールするため、ヘッダーとフッターはその場に保たれます。スワイプは、本体が一番上までスクロールされて初めて開始します。
フォームとキーボード
下部の drawer にテキスト入力欄がある場合は、<DrawerContent /> を <DrawerVirtualKeyboardProvider /> で囲みます。スマートフォンでは、フォーカスされたフィールドがソフトウェアキーボードの陰に隠れず、その上に表示されるようスクロールされます。ヘッダーとフッターを固定しておくために、フィールドは <DrawerBody /> に入れておきます。
入れ子
同じ端にある drawer から開いた drawer は、手前に積み重なります。後ろのものは縮小して上にのぞき、一番上のものをスワイプで閉じる指に追従します。
drawer からの確認
ダイアログ、alert dialog、シート、および別の端にある drawer は、積み重なるのではなく、上に重なります。drawer は奥に下がり、より薄い背景がそれを覆います。
レスポンシブ
メディアクエリで swipeDirection を切り替えると、デスクトップではサイドパネル、スマートフォンではボトムシートにできます。
非モーダル
modal={false} では背景がなく、ページはスクロールし続け、フォーカスは drawer の外に出られます。
制御
open と onOpenChange を渡すと、トリガーなしでどこからでも開けます。
分離したトリガー
createDrawerHandle で、1 つの drawer を複数のトリガーで共有できます。各トリガーは payload を渡し、drawer は関数の子要素を通じてそれをレンダリングします。
右から左
<DrawerContent /> に dir="rtl" を渡すと、コンテンツが反転します。swipeDirection は物理的な端を指すため、"left" は左のまま、ハンドルは内側の端に残ります。
| キー | アクション |
|---|---|
| EnterSpace | トリガーでは、drawer を開き、その内側にフォーカスを移動します。 |
| TabShift + Tab | フォーカス可能な要素の間を移動します。モーダルな drawer では、フォーカスは内側にとどまります。 |
| Esc | 最上位の drawer を閉じ、そのトリガーにフォーカスを戻します。 |
- drawer はダイアログです。
<DrawerTitle />がラベルを付け、<DrawerDescription />が説明を補うため、必ずタイトルを含めてください。 - スワイプだけが閉じる手段になることはありません。Escape、背景、
<DrawerClose />ボタンでも閉じます。 - ハンドルは装飾であり、支援技術からは隠されます。マウスでは、drawer をドラッグせずに内側のテキストを選択できます。
- モーションの低減が有効な場合、drawer はスライドではなくフェードで表示・非表示になります。ドラッグは引き続きポインターに追従します。
Base UI の drawer をベースにしています。各パーツは、ラップしているプリミティブの props を受け付けます。
| プロパティ | 型 | デフォルト |
|---|---|---|
swipeDirection開く端と、閉じるスワイプの方向。 | "up" | "down" | "left" | "right" | "down" |
showSwipeHandleハンドルを表示します。デフォルトは、上下では true、左右では false です。 | boolean | – |
snapPoints縦向きの drawer が止まれる高さ。0〜1 はビューポートに対する割合、1 より大きい値はピクセル、文字列は px または 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 | – |
onOpenChangeComplete開閉アニメーションの終了後に呼ばれます。 | (open: boolean) => void | – |
modaltrue のときだけ、背景をレンダリングします。 | boolean | "trap-focus" | true |
disablePointerDismissal背景がクリックされても開いたままにします。 | boolean | false |
handle | DrawerHandle<Payload> | – |
children | ReactNode | ({ payload }) => ReactNode | – |
| プロパティ | 型 | デフォルト |
|---|---|---|
handle | DrawerHandle<Payload> | – |
payload | Payload | – |
render | ReactElement | (props, state) => ReactElement | <button> |
| 属性 | 説明 |
|---|---|
data-slot="drawer-trigger" | CSSでトリガーを指定します。 |
data-popup-open | drawer が開いている間、付与されます。 |
ポータル、背景、ビューポート、ポップアップに加えて、ハンドルをレンダリングします。縦向きの drawer は、ビューポートの高さから 4rem を引いた値までコンテンツに合わせます。サイドの drawer は幅 75% で、sm ブレークポイントからは最大 24rem です。h-* や w-* で上書きするか、data-[swipe-axis=y]: で 1 つの軸に限定します。
| プロパティ | 型 | デフォルト |
|---|---|---|
initialFocus | boolean | RefObject | (type) => HTMLElement | boolean | – |
finalFocus | boolean | RefObject | (type) => HTMLElement | boolean | – |
className | string | (state) => string | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| 属性 | 説明 |
|---|---|
data-slot="drawer-popup" | drawer のパネル。 |
data-slot="drawer-content" | children を囲む内側のラッパー。他にスクロールするものがない場合にスクロールします。 |
data-swipe-direction | up、right、down、left のいずれか。 |
data-swipe-axis | x または y。 |
data-open | drawer が開いている間、付与されます。 |
data-starting-style | 表示アニメーションの間存在します。 |
data-ending-style | 非表示アニメーションの間存在します。 |
data-swiping | ドラッグされている間、付与されます。 |
data-snap-points | drawer にスナップポイントがあるときに付与されます。 |
data-expanded | 全高のスナップポイントで付与されます。 |
data-nested-drawer-open | 別の drawer が上で開いている間、付与されます。 |
data-stack | 同じ端にある別の drawer が上に積まれている間、付与されます。 |
--drawer-inset | drawer をビューポートの端から浮かせます。デフォルトは 0px です。 |
--drawer-bleed-background | 端を越えてドラッグしたときに現れる領域を塗ります。デフォルトはポップオーバーの色です。 |
--drawer-swipe-movement-x | 水平方向のドラッグ距離。-y の変数もあります。 |
--drawer-snap-point-offset | 現在のスナップポイントが上端からどれだけ下にあるか。 |
--nested-drawers | 上に開いている drawer の数。 |
modal が true のとき、<DrawerContent /> によってレンダリングされます。スワイプに合わせてフェードし、スナップポイントがある場合は少なくとも半分は表示されたままです。シートや別の端にある drawer の上に重なった drawer では、より薄い背景になります。
| 属性 | 説明 |
|---|---|
data-slot="drawer-overlay" | バックドロップ。 |
data-nested | 重なった drawer の、より薄い背景に付与されます。 |
--drawer-overlay-min-opacity | スワイプ中にフェードする最小の不透明度。0、スナップポイントがある場合は 0.5。 |
showSwipeHandle がオンのとき、<DrawerContent /> によって内側の端にレンダリングされます。drawer 全体をドラッグできるため、ハンドルは視覚的な手がかりです。
| 属性 | 説明 |
|---|---|
data-slot="drawer-swipe-handle" | ハンドル。 |
drawer のレイアウトを構成するプレーンな <div> 要素。ヘッダーは小さい画面の縦向きの drawer でテキストを中央揃えにし、本体はスクロールして残りの空間を占め、フッターはアクションを縦に積みます。
| 属性 | 説明 |
|---|---|
data-slot="drawer-header" | タイトルと説明。 |
data-slot="drawer-body" | スクロール可能なコンテンツ。 |
data-slot="drawer-footer" | アクション。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <h2> |
| 属性 | 説明 |
|---|---|
data-slot="drawer-title" | drawer にラベルを付けます。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <p> |
| 属性 | 説明 |
|---|---|
data-slot="drawer-description" | drawer を説明します。 |
要素をレンダリングしません。<Drawer /> の内側で、<DrawerContent /> を囲むように配置します。ソフトウェアキーボードが開いている間、drawer のスクロールコンテナの下に余白を追加し、フォーカスされたフィールドを表示位置までスクロールし、iOS ではフィールドのタップでキーボードが開くようにします。これがない drawer には影響しません。
| プロパティ | 型 | デフォルト |
|---|---|---|
children | ReactNode | – |
| 属性 | 説明 |
|---|---|
--drawer-keyboard-inset | キーボードが開いている間、ビューポートに設定されます: キーボードがページと重なっている量です。0px のフォールバックとともに使います。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <button> |
| 属性 | 説明 |
|---|---|
data-slot="drawer-close" | 押すと drawer を閉じます。 |
createDrawerHandle<Payload>() は、別の場所にレンダリングされたトリガーと <Drawer /> をつなぐハンドルを返します。コンポーネントの外で一度だけ作成してください。
- Sheet任意の端からスライドインするパネルです。スワイプで閉じる操作、スクロールロック、重なる入れ子に対応します。
- Alert dialog破壊的または重要な操作のための確認ダイアログです。非同期処理を待ち、スマートフォンではボトムシートになります。
- Commandインラインまたは⌘Kパレットとして使える、検索可能な操作リストです。ページ、ショートカット、一致箇所のハイライトに対応します。
- Context menu右クリックまたは長押しで開く操作メニューです。サブメニュー、チェックボックス項目、ラジオ項目に対応し、タッチでは長押しのフィードバックを返します。
- Dialogフォームや集中して行う作業のための、ページ上のウィンドウです。固定のヘッダーとフッター、入れ子に対応し、スマートフォンではスワイプできるボトムシートになります。
- Dropdown menuボタンの背後に、操作とオプションをまとめたメニューです。グループ、サブメニュー、チェックボックス項目、ラジオ項目、ショートカットに対応します。
使用しているブロック
Drawer の上に構築されるブロック。