模态框与对话框
规则
- 使用 Radix Dialog 或 shadcn
<Dialog> — 默认支持无障碍、焦点陷阱、滚动锁定
- 用于破坏性操作的确认对话框:显示“删除此项目?”并附带“取消”(次要按钮)和“删除”(危险按钮)
- 移动端使用面板/抽屉:来自 shadcn 的
<Sheet> — 从底部或侧面滑出,比居中模态框更适合触摸操作
- 焦点陷阱:Radix 自动处理 — 打开时聚焦第一个可聚焦元素,关闭时返回原焦点
- 滚动锁定:模态框打开时阻止 body 滚动 — Radix 通过
<Dialog.Overlay> 处理
- 关闭触发器:Escape 键、点击遮罩外部、显式关闭按钮 — 均由 Radix 提供
- 受控状态:
open + onOpenChange props — 对于由外部事件触发的模态框,不要依赖内部状态
- 对话框中的表单:Enter 提交,成功后关闭,失败时内联显示错误 — 验证失败时不关闭对话框
- 移动端:在小屏幕上使用
<Sheet>(底部抽屉)代替居中 <Dialog> 来执行操作
模式
import {
AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
AlertDialogDescription, AlertDialogFooter, AlertDialogHeader,
AlertDialogTitle, AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
function DeleteButton({ onDelete }: { onDelete: () => Promise<void> }) {
const [loading, setLoading] = useState(false);
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive">删除项目</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>删除此项目?</AlertDialogTitle>
<AlertDialogDescription>
此操作将永久删除该项目及其所有数据。此操作无法撤销。
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>取消</AlertDialogCancel>
<AlertDialogAction
onClick={async () => { setLoading(true); await onDelete(); setLoading(false); }}
disabled={loading}
>
{loading ? "删除中..." : "删除"}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet";
function MobileMenu() {
return (
<Sheet>
<SheetTrigger asChild><Button variant="ghost" size="icon"><MenuIcon /></Button></SheetTrigger>
<SheetContent side="bottom">
<SheetHeader><SheetTitle>操作</SheetTitle></SheetHeader>
<nav className="flex flex-col gap-2 py-4">
<Button variant="ghost">编辑</Button>
<Button variant="ghost">分享</Button>
<Button variant="destructive">删除</Button>
</nav>
</SheetContent>
</Sheet>
);
}
避免
- 使用
position: fixed 和手动焦点管理构建自定义模态框 — 请使用 Radix/shadcn Dialog
- 缺少 Escape 键和点击外部关闭功能 — Radix 已处理两者,不要覆盖它们
- 在移动端将操作菜单放在居中模态框中 — 使用底部 Sheet/抽屉以获得更好的拇指触达范围
- 表单验证失败时关闭对话框 — 保持打开状态,内联显示错误,让用户修正后重新提交
- 嵌套对话框缺少正确的堆叠上下文 — 如果需要,请使用 Radix 的嵌套 Dialog 支持,并配合正确的
z-index