AlertDialog

Dialog の variant。取り消しのきかない操作 (削除・送信など) で、ユーザーに最終確認を求めるモーダルダイアログ。Radix AlertDialog がベースで、Esc やオーバーレイクリックでは閉じない。

プレビュー — 標準

トリガーを押すと最終確認ダイアログが開きます。Cancel / Action の 2 ボタン構成が基本。

実装例 — 標準

import { Button } from "@/components/Button";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog";

export function Example() {
  return (
    <AlertDialog>
      <AlertDialogTrigger asChild>
        <Button variant="danger" size="m">
          削除
        </Button>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>この顧客を削除しますか?</AlertDialogTitle>
          <AlertDialogDescription>
            会話履歴や紐づくメモも一緒に削除されます。元に戻せません。
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel variant="secondary" size="m">
            キャンセル
          </AlertDialogCancel>
          <AlertDialogAction variant="danger" size="m">
            削除する
          </AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  );
}

プレビュー — 入力必須の確認

破壊性が特に高い操作向け。ダイアログ内の入力欄に「削除」と入力しないと Action ボタンが有効化されない。誤タップ・惰性クリックによる事故を防ぐ用途で使う。

実装例 — 入力必須の確認

"use client";

import * as React from "react";
import { Button } from "@/components/Button";
import { Input } from "@/components/ui/input";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/ui/alert-dialog";

const CONFIRM_TEXT = "削除";

export function Example() {
  const [open, setOpen] = React.useState(false);
  const [value, setValue] = React.useState("");
  const [loading, setLoading] = React.useState(false);

  const canConfirm = value === CONFIRM_TEXT;

  function handleOpenChange(next: boolean) {
    if (loading) return;
    setOpen(next);
    if (!next) setValue("");
  }

  function handleConfirm(e: React.MouseEvent) {
    // Radix の auto-close を止めて loading を見せる
    e.preventDefault();
    setLoading(true);
    window.setTimeout(() => {
      setLoading(false);
      setValue("");
      setOpen(false);
    }, 2000);
  }

  return (
    <AlertDialog open={open} onOpenChange={handleOpenChange}>
      <AlertDialogTrigger asChild>
        <Button variant="danger" size="m">顧客を削除</Button>
      </AlertDialogTrigger>
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>チェックした顧客を削除しますか?</AlertDialogTitle>
          <AlertDialogDescription>
            会話履歴や紐づくメモも一緒に削除されます。元に戻せません。
          </AlertDialogDescription>
          <Input
            value={value}
            onChange={(e) => setValue(e.target.value)}
            placeholder="「削除」と入力してください"
            autoComplete="off"
            disabled={loading}
            aria-label="削除確認テキスト"
          />
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel variant="secondary" size="s" disabled={loading}>
            キャンセル
          </AlertDialogCancel>
          <AlertDialogAction
            variant="danger"
            size="s"
            loading={loading}
            disabled={!canConfirm}
            onClick={handleConfirm}
          >
            削除
          </AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  );
}

削除操作での使い方

AlertDialog の主な用途は削除確認。以下のルールで文言・構成を統一する。

  • タイトルは「〇〇を削除しますか?」 — 削除対象を必ず明示する。「削除しますか?」だけでは何が消えるか分からない。
  • 説明文には副作用を書く — 「会話履歴や紐づくメモも一緒に削除されます。元に戻せません。」のように、削除によって起きることを具体的に示す。副作用がない場合は省略してよい。
  • ボタン配置はキャンセル左・削除右 — グーテンベルク図に従い、主要 CTA(削除)を右下に置く。キャンセルは左。
  • 削除ボタンは variant="danger" — 破壊的操作であることを色で伝える。primary は使わない。
  • Esc・オーバーレイクリックで閉じない — AlertDialog の仕様通り。誤タップで確認をスキップできないようにする。
  • 入力必須型はデータ喪失が大きい操作に限定 — 対象が複数件、または関連データ(会話履歴・メモ等)が同時に消える場合など、影響範囲が大きい削除にのみ使う。通常の 1 件削除では過剰なフリクションになるため標準型を使う。
  • 入力必須型では Action ボタン内にスピナーを表示する — Action ボタンにスピナー + disabled を出し、Cancel / Input も disabled にして操作を止める。ラベル文言は変えない("削除" のまま)。

仕様

  • primitive: src/components/ui/alert-dialog.tsx(Radix AlertDialog ベース)
  • 構成: AlertDialog / AlertDialogTrigger / AlertDialogContent / AlertDialogHeader / AlertDialogTitle / AlertDialogDescription / AlertDialogFooter / AlertDialogCancel / AlertDialogAction
  • 幅: --width-dialog-s(424px) / 角丸: --radius-l(12px) / 影: --shadow-layer-4 / Header padding: --spacing-xs(16px) / Footer 上線: --border-default
  • タイトル: .tds-text-heading-l(20px / Bold / 125%)、 説明文: .tds-text-body-s(14px / Regular / 150%、--text-secondary)
  • ボタンは Button primitive を流用。AlertDialogCancel の default は secondary / s、AlertDialogAction の default は primary / s。 破壊的操作のときは variant="danger" を明示する。