7.1 关键概念
- Portal(传送门)
- React 把浮层 DOM 渲染到
<body>末尾而非组件原位,避开父级overflow:hidden与z-index堆叠陷阱。Radix 浮层默认走 Portal。 - Focus Trap(焦点陷阱)
- 模态打开时把 Tab 焦点限制在浮层内,关闭后归还给触发元素。Radix Dialog 自动处理,无障碍必需。
- 模态 vs 非模态
- 模态(Dialog/AlertDialog)会加遮罩、锁滚动、拦截外部交互;非模态(Popover/Tooltip)不锁背景,点外部即关。
- Dialog
- 通用模态弹窗,可关闭(Esc/点遮罩/关闭按钮),用于表单、详情、确认等。
- AlertDialog
- 强确认对话框,不能点遮罩关闭,必须选"取消"或"确认",用于删除等破坏性操作。
- Popover / Tooltip
- Popover 是可交互的悬浮面板(可放表单);Tooltip 是只读悬浮提示,靠 hover/focus 触发,需
TooltipProvider包裹。 - DropdownMenu / ContextMenu
- DropdownMenu 由点击触发(按钮菜单);ContextMenu 由右键触发(上下文菜单)。两者支持子菜单、快捷键、勾选项、单选组。
7.2 安装
$ npx shadcn@latest add dialog alert-dialog popover \
tooltip dropdown-menu context-menu
7.3 Dialog:模态弹窗
import {
Dialog, DialogContent, DialogDescription, DialogFooter,
DialogHeader, DialogTitle, DialogTrigger, DialogClose,
} from "@/components/ui/dialog";
<Dialog>
<DialogTrigger asChild><Button>编辑资料</Button></DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>编辑资料</DialogTitle>
<DialogDescription>修改后点保存。</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<Input placeholder="昵称" />
</div>
<DialogFooter>
<DialogClose asChild><Button variant="outline">取消</Button></DialogClose>
<Button type="submit">保存</Button>
</DialogFooter>
</DialogContent>
</Dialog>
DialogTitle 不能省
即使视觉上不想显示标题,也要提供 DialogTitle(可用 sr-only 隐藏),否则屏幕阅读器无法宣告弹窗用途,Radix 也会在控制台警告。
7.4 AlertDialog:破坏性确认
import {
AlertDialog, AlertDialogAction, AlertDialogCancel,
AlertDialogContent, AlertDialogDescription, AlertDialogFooter,
AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
<AlertDialog>
<AlertDialogTrigger asChild><Button variant="destructive">删除账户</Button></AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>确定删除?</AlertDialogTitle>
<AlertDialogDescription>此操作不可撤销。</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>取消</AlertDialogCancel>
<AlertDialogAction onClick={handleDelete}>确认删除</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
7.5 Popover:可交互浮层
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
<Popover>
<PopoverTrigger asChild><Button variant="outline">设置宽度</Button></PopoverTrigger>
<PopoverContent className="w-80">
<div className="grid gap-2">
<Label>宽度</Label><Input defaultValue="100%" />
</div>
</PopoverContent>
</Popover>
7.6 Tooltip:只读提示
import {
Tooltip, TooltipContent, TooltipTrigger, TooltipProvider,
} from "@/components/ui/tooltip";
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild><Button variant="ghost" size="icon"><Info /></Button></TooltipTrigger>
<TooltipContent>更多信息</TooltipContent>
</Tooltip>
</TooltipProvider>
Tooltip 不承载关键信息
触屏无 hover、键盘用户可能错过,Tooltip 只放"锦上添花"的辅助说明。关键操作说明应直接可见,或改用 Popover。
7.7 DropdownMenu:按钮菜单
import {
DropdownMenu, DropdownMenuContent, DropdownMenuItem,
DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger,
DropdownMenuCheckboxItem,
} from "@/components/ui/dropdown-menu";
<DropdownMenu>
<DropdownMenuTrigger asChild><Button variant="outline">操作</Button></DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>我的账户</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>个人资料</DropdownMenuItem>
<DropdownMenuCheckboxItem checked>显示邮箱</DropdownMenuCheckboxItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="text-destructive">退出登录</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
7.8 ContextMenu:右键菜单
import {
ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger,
} from "@/components/ui/context-menu";
<ContextMenu>
<ContextMenuTrigger className="flex h-40 items-center justify-center border rounded-md">
右键点这里
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem>复制</ContextMenuItem>
<ContextMenuItem>粘贴</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
7.9 受控与嵌套注意
// 受控:自己管理 open 状态(如提交成功后关闭)
const [open, setOpen] = useState(false);
<Dialog open={open} onOpenChange={setOpen}> ... </Dialog>
// 提交成功后 setOpen(false)
| 组件 | 触发 | 模态 | 可交互内容 | 典型场景 |
|---|---|---|---|---|
| Dialog | 点击 | 是 | 是 | 表单/详情 |
| AlertDialog | 点击 | 是(强) | 仅按钮 | 删除确认 |
| Popover | 点击 | 否 | 是 | 小面板 |
| Tooltip | hover/focus | 否 | 否 | 提示 |
| DropdownMenu | 点击 | 否 | 菜单项 | 操作菜单 |
| ContextMenu | 右键 | 否 | 菜单项 | 上下文 |
7.10 小结与下一步
- 所有浮层走 Portal,避开 overflow/z-index 陷阱;模态自动焦点陷阱 + Esc 关闭。
- 破坏性操作用 AlertDialog(不可点遮罩关闭);Tooltip 只放辅助信息。
- DropdownMenu(点击)与 ContextMenu(右键)共享菜单原语,支持勾选/单选/子菜单。
- 下一章做数据展示:Table + TanStack Table 打造可排序筛选分页的 DataTable。