# 组件组合 ## 目录 - 条目始终位于其 Group 组件内 - 标注提示使用 Alert - 空状态使用 Empty 组件 - Toast 通知使用 sonner - 如何选择覆盖层组件 - Dialog、Sheet 和 Drawer 始终需要 Title - Card 结构 - Button 没有 isPending 或 isLoading 属性 - TabsTrigger 必须位于 TabsList 内部 - Avatar 始终需要 AvatarFallback - 使用 Separator 替代原生 hr 或 border div - 使用 Skeleton 作为加载占位符 - 使用 Badge 替代自定义样式 span --- ## 条目始终位于其 Group 组件内 切勿直接在内容容器内渲染条目。 **错误写法:** ```tsx Apple Banana ``` **正确写法:** ```tsx Apple Banana ``` 这适用于所有基于 Group 的组件: | 条目 | 分组容器 | |------|---------| | `SelectItem`, `SelectLabel` | `SelectGroup` | | `DropdownMenuItem`, `DropdownMenuLabel`, `DropdownMenuSub` | `DropdownMenuGroup` | | `MenubarItem` | `MenubarGroup` | | `ContextMenuItem` | `ContextMenuGroup` | | `CommandItem` | `CommandGroup` | --- ## 标注提示使用 Alert ```tsx 警告 某些内容需要关注。 ``` --- ## 空状态使用 Empty 组件 ```tsx 暂无项目 立即创建一个新项目开始使用。 ``` --- ## Toast 通知使用 sonner ```tsx import { toast } from "sonner" toast.success("更改已保存。") toast.error("发生错误。") toast("文件已删除。", { action: { label: "撤销", onClick: () => undoDelete() }, }) ``` --- ## 如何选择覆盖层组件 | 使用场景 | 组件 | |----------|------| | 需要用户输入的专注型任务 | `Dialog` | | 破坏性操作确认 | `AlertDialog` | | 显示详情或筛选条件的侧面板 | `Sheet` | | 移动端优先的底部面板 | `Drawer` | | 悬停时快速查看信息 | `HoverCard` | | 点击后显示小型上下文内容 | `Popover` | --- ## Dialog、Sheet 和 Drawer 始终需要 Title `DialogTitle`、`SheetTitle`、`DrawerTitle` 是无障碍所必需的。如果希望视觉上隐藏,请使用 `className="sr-only"`。 ```tsx 编辑个人资料 更新您的个人资料。 ... ``` --- ## Card 结构 使用完整的组合方式——不要把所有内容都塞进 `CardContent`: ```tsx 团队成员 管理您的团队。 ... ``` --- ## Button 没有 isPending 或 isLoading 属性 配合 `Spinner` + `data-icon` + `disabled` 进行组合: ```tsx ``` --- ## TabsTrigger 必须位于 TabsList 内部 切勿在 `Tabs` 内部直接渲染 `TabsTrigger`——始终包裹在 `TabsList` 中: ```tsx 账户 密码 ... ``` --- ## Avatar 始终需要 AvatarFallback 始终包含 `AvatarFallback`,用于图片加载失败时的降级显示: ```tsx JD ``` --- ## 使用现有组件替代自定义标记 | 不要使用 | 请使用 | |---|---| | `
` 或 `
` | `` | | `
` 配合样式 div | `` | | `` | `` |