Dialog
フォームや集中して行う作業のための、ページ上のウィンドウです。固定のヘッダーとフッター、入れ子に対応し、スマートフォンではスワイプできるボトムシートになります。
pnpm dlx shadcn@latest add https://hextaui.com/r/dialog.jsonコンポーネント、HextaUIのテーマトークン、依存するHextaUIコンポーネントを追加します。
まだ追加していない場合は、グローバルCSSにテーマトークンを追加してください。
依存関係をインストールします。
pnpm add @base-ui/react @tabler/icons-react class-variance-authority cn次のコードをコピーしてプロジェクトに貼り付けてください。
components/ui/dialog.tsx components/ui/sheet.tsx components/ui/button.tsx インポートパスは、お使いのプロジェクト構成に合わせて更新してください。
Form
フォームは <DialogBody /> の中に置き、フッターの送信ボタンは form でそれに紐付けます。Enter で送信され、値が保存されると onOpenChange を通じてダイアログが閉じます。
カスタムの閉じるボタン
コンテンツの showCloseButton={false} で隅のボタンを非表示にし、フッターには showCloseButton で Close ボタンを追加します。
閉じるボタンなし
ボタンがなくても、ダイアログは Esc、外側のクリック、スマートフォンでの下スワイプで閉じます。
サイズ
size は大きな画面での最大幅を設定します: sm、default、lg。
固定フッター
<DialogBody /> 内の長いコンテンツはスクロールし、ヘッダーとフッターはその場に保たれるため、アクションに常に手が届きます。
スクロール可能なコンテンツ
フッターがない場合、本体はヘッダーの下でスクロールし、下側のパディングを保ちます。
制御
open と onOpenChange を渡すと、トリガーなしでコードから開けます。
入れ子
別のダイアログや alert dialog の内側から開いたダイアログは、手前に重なります。親は奥に下がり、より薄い背景がそれを覆います。Esc は一番上のものだけを閉じます。
分離したトリガー
createDialogHandle() でハンドルを作成すると、1 つのダイアログを複数のトリガーで共有できます。各トリガーは payload を渡し、ダイアログは render 関数を通じてそれを読み取ります。
右から左
コンテンツは RTL コンテナの外側のポータルにレンダリングされるため、コンテンツにも dir を渡してください。
| キー | アクション |
|---|---|
| EnterSpace | トリガーでは、ダイアログを開き、最初のコントロールにフォーカスを移動します。 |
| TabShift+Tab | コントロール間でフォーカスを移動します。フォーカスはダイアログ内にとどまります。 |
| Esc | 最上位のダイアログを閉じ、そのトリガーにフォーカスを戻します。 |
- コンテンツには
role="dialog"が付き、タイトルによってラベル付けされ、説明によって補足されます。必ず<DialogTitle />を含めてください。 - マウスやキーボードでは、フォーカスは最初のコントロールから始まります。タッチではダイアログ自体から始まるため、フィールドを選ぶ前に画面上のキーボードがコンテンツを覆うことはありません。変更するには
initialFocusを渡します。 - 隅の閉じるボタンには「Close」というラベルが付き、背後のページは inert になりスクロールできません。
- スマートフォンでは、下にスワイプして閉じられるボトムシートになります。モーションの低減が有効な場合は、拡大縮小やスライドではなくフェードになります。
<Sheet /> を通じて、Base UI の drawer をベースにしています。各パーツは、ラップしているプリミティブや要素の props をすべて受け付けます。
| プロパティ | 型 | デフォルト |
|---|---|---|
open | boolean | – |
defaultOpen | boolean | false |
onOpenChange | (open: boolean, details) => void | – |
onOpenChangeComplete開閉アニメーションの後に呼ばれます。 | (open: boolean) => void | – |
modal | boolean | "trap-focus" | true |
disablePointerDismissal外側のクリックでは、ダイアログを開いたままにします。 | boolean | false |
handle分離したトリガーを接続します。 | DialogHandle<Payload> | – |
actionsRefダイアログをコードから閉じる、またはアンマウントします。 | RefObject<{ close, unmount }> | – |
children | ReactNode | ({ payload }) => ReactNode | – |
| プロパティ | 型 | デフォルト |
|---|---|---|
handle | DialogHandle<Payload> | – |
payloadダイアログの render 関数に渡されます。 | Payload | – |
render | ReactElement | (props, state) => ReactElement | <button> |
| 属性 | 説明 |
|---|---|
data-slot="dialog-trigger" | CSSでトリガーを指定します。 |
data-popup-open | ダイアログが開いている間、付与されます。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
size | "sm" | "default" | "lg" | "default" |
showCloseButton隅に閉じるボタンを表示します。 | boolean | true |
initialFocus | boolean | RefObject | (openType) => HTMLElement | boolean | First control, or the dialog on touch |
finalFocus | boolean | RefObject | (closeType) => HTMLElement | boolean | The trigger |
dirダイアログを右から左の表示にしたいときに設定します。 | "ltr" | "rtl" | – |
render | ReactElement | (props, state) => ReactElement | <div> |
| 属性 | 説明 |
|---|---|
data-slot="dialog-content" | CSS でダイアログを指定します。 |
data-size | 現在のサイズ。 |
data-open | 開いている間存在します。 |
data-starting-style | ダイアログが開くアニメーションの間、付与されます。 |
data-ending-style | ダイアログが閉じるアニメーションの間、付与されます。 |
data-nested-drawer-open | ネストしたダイアログが上で開いている間、付与されます。 |
data-swiping | スマートフォンでスワイプしている間、付与されます。 |
--nested-drawers | このダイアログの上で開いているダイアログの数。 |
タイトルと説明を縦に並べる <div>。閉じるボタンの分の余白を確保します。
| 属性 | 説明 |
|---|---|
data-slot="dialog-header" | CSSでヘッダーを指定します。 |
コンテンツが画面より高い場合にスクロールする <div>。ヘッダーとフッターはその場に保たれます。
| 属性 | 説明 |
|---|---|
data-slot="dialog-body" | CSS で本体を指定します。 |
アクション用の <div>。スマートフォンではボタンが全幅で縦に積まれ、最初のボタンが一番下になります。
| プロパティ | 型 | デフォルト |
|---|---|---|
showCloseButtonchildren の後ろに、outline の Close ボタンを追加します。 | boolean | false |
| 属性 | 説明 |
|---|---|
data-slot="dialog-footer" | CSS でフッターを指定します。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <h2> |
| 属性 | 説明 |
|---|---|
data-slot="dialog-title" | CSSでタイトルを指定します。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
render複数の段落を含める場合は render={<div />} を使います。 | ReactElement | (props, state) => ReactElement | <p> |
| 属性 | 説明 |
|---|---|
data-slot="dialog-description" | CSSで説明を指定します。 |
| プロパティ | 型 | デフォルト |
|---|---|---|
render | ReactElement | (props, state) => ReactElement | <button> |
| 属性 | 説明 |
|---|---|
data-slot="dialog-close" | CSS で閉じるボタンを指定します。 |
<DialogContent /> はすでに両方をレンダリングします。カスタムのポップアップを組み立てるときにのみ使ってください。
| プロパティ | 型 | デフォルト |
|---|---|---|
keepMountedポータルに指定すると、閉じている間もダイアログを DOM に残します。 | boolean | false |
ページ上のどこにある <DialogTrigger /> 要素でも、1 つの <Dialog /> に接続するハンドルを返します。payload の型はジェネリクスで指定します: createDialogHandle<{ name: string }>()。
- Buttonすべてのバリアントとサイズのボタンです。読み込み、成功、エラーのフローを内蔵し、高速なリクエストではスピナーを省略します。
- Sheet任意の端からスライドインするパネルです。スワイプで閉じる操作、スクロールロック、重なる入れ子に対応します。
- Alert dialog破壊的または重要な操作のための確認ダイアログです。非同期処理を待ち、スマートフォンではボトムシートになります。
- Commandインラインまたは⌘Kパレットとして使える、検索可能な操作リストです。ページ、ショートカット、一致箇所のハイライトに対応します。
- Context menu右クリックまたは長押しで開く操作メニューです。サブメニュー、チェックボックス項目、ラジオ項目に対応し、タッチでは長押しのフィードバックを返します。
- Drawer任意の端からスライドインし、指に追従するパネルです。スナップポイント、実際に使えるハンドル、重なっていく入れ子のドロワーに対応します。
使用しているブロック
Dialog の上に構築されるブロック。
- Prompt Input最初は静かな1行で、入力に合わせてカードへ広がり、会話が始まると下へ移動するチャット入力欄です。Enterで送信でき、日本語や中国語の入力でも安全です。ファイルは貼り付け、ドロップ、選択でき、プレビュー、進捗、再試行に対応します。@でファイルを追加し、/でコマンドをカーソル位置のメニューから実行できます。数字キーで選べるモデルピッカー、Maxで動き出す推論量スライダー、コンテキストリング、リアルタイム波形つきの音声入力、ツールチップ、返信のストリーミング中に入力したメッセージのキュー、再読み込みしても残る下書きを備えています。
- API keysOpenAIやAnthropicのコンソールのような、AIプロダクトのAPIキーのページです。スコープ付きの権限と有効期限を持つキーを作成し、シークレットは一度だけ表示され、コピーで確認でき、取り消しは元に戻せ、その場で名前を変更でき、猶予期間つきでローテーションでき、キーごとの使用量を確認できます。
- BillingCursor、Claude、Vercelのスタイルによる、AIプロダクト向けのプランと使用量です。モデルごとに分かれ、サイクル終了時を予測してクレジット切れの前に警告する使用量メーター、ドラッグで確認できる日別チャート、メーター上でプレビューできるアラートつきの支出上限、正確な日割り計算によるプラン変更、実際のバリデーションを備えたカードフォーム、PDFでダウンロードできる請求書を備えています。
- ModelsAIプロダクトの設定にあるモデルページです。コンテキスト、速度、コストが一目でわかるデフォルトモデル、各モデルが対応する内容を把握しているデフォルトの推論量、フィルター、ピン留め、一括切り替えを備えプロバイダーごとにグループ化された検索可能なモデルリスト、実際の接続テストができるOpenAI互換サーバー、新着を知らせる更新を備えています。