docs(ui): expand bilingual component guides

This commit is contained in:
Maofeng
2026-09-21 17:27:45 +08:00
parent fb985edb20
commit 4c61e79d0d
22 changed files with 1636 additions and 192 deletions
+85 -14
View File
@@ -1,30 +1,101 @@
---
title: 选择组件
description: 按职责选择基础组件,而不是导入一个庞大的统一组件入口。
title: 组件目录
description: 按职责查找组件入口,并理解本地 Base UI 的组合方式。
order: 11
toc:
- id: forms
title: 表单与输入
- id: overlays
title: 浮层与菜单
- id: content
- id: composition-model
title: 组合模型
- id: actions-and-forms
title: 操作与表单
- id: overlays-and-navigation
title: 浮层与导航
- id: content-and-feedback
title: 内容与反馈
- id: layout-and-data
title: 布局与数据
- id: icons
title: 图标
---
## 表单与输入 {#forms}
## 组合模型 {#composition-model}
普通表单可使用 `Button`、`Input`、`Textarea`、`Field` 和 `Label`。选择类输入可加入 `Checkbox`、`RadioGroup`、`Switch` 或 `Slider`;更丰富的选择场景可使用 `Select`、`Combobox`、`Command` 或 `Calendar`。
组件采用 shadcn/ui 的“复制源码并组合 primitives”模型,但本仓库选择的是 Base UI 实现。组件通常由 Root、Trigger、Content、Item 等部分组成,状态和可访问性由底层 primitive 管理,外观通过 Tailwind class 与 `data-slot` 暴露。
## 浮层与菜单 {#overlays}
Base UI 使用 `render` 组合自定义元素,而不是 Radix 版本常见的 `asChild`:
`Dialog`、`AlertDialog`、`Sheet` 和 `Drawer` 用于模态界面。`Popover`、`HoverCard` 和 `Tooltip` 用于锚定式信息展示,菜单模块则覆盖上下文菜单、下拉菜单、菜单栏和导航模式。
```tsx
import { Button } from "@workspace/ui/components/button"
import { Dialog, DialogTrigger } from "@workspace/ui/components/dialog"
## 内容与反馈 {#content}
;<Dialog>
<DialogTrigger render={<Button variant="outline" />}>编辑资料</DialogTrigger>
</Dialog>
```
卡片、列表项、表格、图表、进度条、骨架屏、加载器、空状态、消息和 Toast 构成通用展示词汇。Accordion、Collapsible、Tabs、Carousel 和 Pagination 用于组织更大规模的内容。
只在需要替换底层元素时使用 `render`。普通内容直接作为 children 传入。
## 操作与表单 {#actions-and-forms}
| 目标 | 组件入口 |
| -------------- | -------------------------------------------------- |
| 按钮与成组操作 | `button`、`button-group`、`toggle`、`toggle-group` |
| 文本输入 | `input`、`textarea`、`input-group`、`input-otp` |
| 字段结构 | `field`、`label` |
| 布尔与单选 | `checkbox`、`switch`、`radio-group` |
| 范围与进度 | `slider`、`progress` |
| 选项与命令 | `native-select`、`select`、`combobox`、`command` |
| 日期 | `calendar` |
简单、移动端优先且不需要自定义浮层的选项优先用 `NativeSelect`。需要搜索、多选或自定义项时,分别选择 `Combobox`、`Select` 或 `Command`。
## 浮层与导航 {#overlays-and-navigation}
| 目标 | 组件入口 |
| -------------- | -------------------------------------------------------- |
| 模态确认与编辑 | `alert-dialog`、`dialog`、`sheet`、`drawer` |
| 锚定内容 | `popover`、`hover-card`、`tooltip`、`anchored-surface` |
| 菜单 | `dropdown-menu`、`context-menu`、`menubar` |
| 页面导航 | `breadcrumb`、`navigation-menu`、`pagination`、`sidebar` |
Dialog、Sheet 与 Drawer 都提供受控状态和组合部件。重要破坏性操作使用 `AlertDialog`,短暂说明使用 `Tooltip`,需要交互控件的锚定面板使用 `Popover`。
## 内容与反馈 {#content-and-feedback}
| 目标 | 组件入口 |
| ---------- | ----------------------------------------------------- |
| 容器与状态 | `card`、`item`、`empty`、`alert`、`badge`、`marker` |
| 加载与通知 | `skeleton`、`spinner`、`toast` |
| 消息界面 | `message`、`bubble`、`attachment`、`message-scroller` |
| 分段内容 | `accordion`、`collapsible`、`tabs`、`carousel` |
`Message` 负责参与者、对齐和元数据;`Bubble` 负责消息正文表面;`Attachment` 表达上传生命周期;`MessageScroller` 管理自动跟随和回到底部按钮。它们都是展示原语,不拥有会话数据。
## 布局与数据 {#layout-and-data}
| 目标 | 组件入口 |
| -------- | ------------------------------------------------------- |
| 数据展示 | `table`、`chart` |
| 布局 | `separator`、`aspect-ratio`、`resizable`、`scroll-area` |
| 方向 | `direction` |
| 键盘提示 | `kbd` |
| 头像 | `avatar` |
图表模块封装 Recharts 的容器、Tooltip、Legend 与样式映射,不替应用决定数据模型。`DirectionProvider` 直接转发 Base UI 的方向上下文;页面仍应同步设置 HTML 的 `dir` 属性。
## 图标 {#icons}
从 `@workspace/ui/components/icon` 导入具名图标。该模块统一了 `@icones/react` 的图标词汇,使应用和各个包使用同一图标来源。
从本地 Icones 适配层导入具名图标:
```tsx
import { SearchIcon, SettingsIcon } from "@workspace/ui/components/icon"
```
组件会为没有显式尺寸 class 的 SVG 提供默认尺寸。图标按钮仍要提供可访问名称:
```tsx
<Button size="icon" aria-label="搜索">
<SearchIcon aria-hidden="true" />
</Button>
```
不要在新组件中重新引入 `lucide-react`;需要新增图标时,在 `components/icon.tsx` 中用 `createIcon` 统一注册。
@@ -0,0 +1,95 @@
---
title: 反馈与消息
description: 配置 Toast,并用 Message、Bubble、Attachment 和 MessageScroller 组合消息界面。
order: 13
toc:
- id: toast-provider
title: Toast Provider
- id: send-toast
title: 发送 Toast
- id: message-composition
title: 消息组合
- id: attachments
title: 附件状态
- id: scrolling
title: 消息滚动
---
## Toast Provider {#toast-provider}
在应用根节点附近挂载一次 `Toaster`:
```tsx
import { Toaster } from "@workspace/ui/components/toast"
export function App() {
return (
<>
<Routes />
<Toaster />
</>
)
}
```
默认 viewport 固定在小屏底部与大屏右下角。Toast 支持堆叠、展开、滑动退出、键盘焦点和 `success`、`info`、`warning`、`error`、`loading` 类型图标。
## 发送 Toast {#send-toast}
使用模块导出的 manager:
```tsx
import { Button } from "@workspace/ui/components/button"
import { toast } from "@workspace/ui/components/toast"
;<Button
onClick={() =>
toast.add({
title: "已保存",
description: "个人资料已经更新。",
type: "success",
})
}
>
保存
</Button>
```
需要隔离通知区域时,用 `createToastManager()` 创建 manager,并同时传给 `Toaster` 与调用方。关闭按钮的默认可访问名称来自 UI 本地化上下文。
## 消息组合 {#message-composition}
```tsx
import {
Message,
MessageContent,
MessageFooter,
MessageGroup,
} from "@workspace/ui/components/message"
import { Bubble, BubbleContent } from "@workspace/ui/components/bubble"
;<MessageGroup>
<Message align="end">
<MessageContent>
<Bubble align="end" variant="tinted">
<BubbleContent>明天下午三点可以。</BubbleContent>
</Bubble>
<MessageFooter>已读 · 14:32</MessageFooter>
</MessageContent>
</Message>
</MessageGroup>
```
把消息 ID、作者、发送状态与操作保留在业务层。UI primitives 只从 props 和 `data-*` 状态决定布局。
## 附件状态 {#attachments}
`Attachment` 支持 `idle`、`uploading`、`processing`、`error` 和 `done`。`AttachmentMedia`、`AttachmentTitle`、`AttachmentDescription` 与 `AttachmentActions` 会根据状态调整外观;上传进度和重试逻辑仍由应用负责。
横向附件使用默认 `orientation="horizontal"`,缩略图网格可使用 `orientation="vertical"`。多个附件放入 `AttachmentGroup` 后会获得横向滚动和 snap 行为。
## 消息滚动 {#scrolling}
`MessageScrollerProvider`、`MessageScroller`、`MessageScrollerViewport` 与 `MessageScrollerContent` 应保持层级。每条消息放在 `MessageScrollerItem` 中;需要作为自动跟随目标的项传 `scrollAnchor`。`MessageScrollerButton direction="end"` 会根据当前可滚动状态自动显示或隐藏。
业务层追加消息时不要直接设置 `scrollTop`。使用这些 primitives 提供的 follow 和 visibility 状态,避免用户向上阅读历史内容时被强制拉回底部。
+113
View File
@@ -0,0 +1,113 @@
---
title: 浮层与定位
description: 使用 Base UI 的 render 组合 Dialog、Popover 与本地 AnchoredSurface。
order: 12
toc:
- id: dialog
title: Dialog
- id: popover
title: Popover
- id: anchored-surface
title: 连续尾巴
- id: positioning
title: 自定义定位
- id: choosing-an-overlay
title: 选择浮层
---
## Dialog {#dialog}
```tsx
import { Button } from "@workspace/ui/components/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@workspace/ui/components/dialog"
export function ProfileDialog() {
return (
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
编辑资料
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>编辑资料</DialogTitle>
<DialogDescription>更新公开显示的信息。</DialogDescription>
</DialogHeader>
<div>表单内容</div>
<DialogFooter>
<Button type="submit">保存</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}
```
`DialogContent` 已包含 Portal、Overlay 和默认关闭按钮。标题与说明会连接到底层可访问性属性,不要只用无语义的 `div` 替代。
## Popover {#popover}
```tsx
import { Button } from "@workspace/ui/components/button"
import {
Popover,
PopoverContent,
PopoverDescription,
PopoverTitle,
PopoverTrigger,
} from "@workspace/ui/components/popover"
;<Popover>
<PopoverTrigger render={<Button variant="outline" />}>
打开设置
</PopoverTrigger>
<PopoverContent align="end">
<PopoverTitle>显示设置</PopoverTitle>
<PopoverDescription>选择列表密度。</PopoverDescription>
</PopoverContent>
</Popover>
```
本地 API 继续转发 Base UI Popup 与 Positioner 的 props。需要受控状态时给 `Popover` 传入 `open` 和 `onOpenChange`。
## 连续尾巴 {#anchored-surface}
本仓库的 `showArrow` 不是普通三角 SVG。它让 `PopoverContent` 用 `AnchoredSurface` 渲染一个圆角表面与连续曲线尾巴:
```tsx
<PopoverContent
side="bottom"
showArrow={{ cornerRadius: "2xl", tailSize: "sm" }}
className="w-80 p-0"
viewportClassName="max-h-72 p-4"
>
{content}
</PopoverContent>
```
外层表面保持 `overflow: visible`,避免尾巴被裁剪;滚动发生在内部 `data-slot="popover-viewport"`。因此要限制内容高度或设置 padding 时,优先使用 `viewportClassName`。
`AnchoredSurface` 也可以独立使用,但它只负责视觉表面,不负责测量锚点、碰撞检测、焦点或 Portal。
## 自定义定位 {#positioning}
`PopoverContent` 额外暴露 `anchor`、`positionMethod`、`arrowPadding`、`alignOffset` 和 `sideOffset`。虚拟锚点或自定义锚点应通过这些参数传给共享组件,不需要绕过它重新拼装 Base UI primitives。
需要从非 React 代码控制弹层时,可使用 `createPopoverHandle<Payload>()` 创建 handle,再按 Base UI 的 handle 模式传给 Root。
## 选择浮层 {#choosing-an-overlay}
- 必须阻止背景交互:`Dialog` 或 `AlertDialog`。
- 从屏幕边缘进入的任务面板:`Sheet`;强调触摸拖拽的移动面板:`Drawer`。
- 与一个触发点关联的交互内容:`Popover`。
- 只在悬停或聚焦时补充预览:`HoverCard`。
- 简短、非交互说明:`Tooltip`。
基础用法可与 [shadcn Popover 文档](https://ui.shadcn.com/docs/components/base/popover)对照;组合语法必须使用 Base UI 版本的 `render`。
+79 -27
View File
@@ -1,39 +1,91 @@
---
title: 项目配置
description: 导入全局样式,并从显式组件子路径开始使用。
description: 配置 workspace 依赖、全局样式、Tailwind 扫描和 shadcn CLI。
order: 10
toc:
- id: import-styles
title: 导入样式
- id: import-a-component
title: 导入组件
- id: entry-points
title: 包入口
- id: dependency
title: 添加依赖
- id: global-styles
title: 全局样式
- id: imports
title: 显式导入
- id: shadcn-cli
title: shadcn CLI
- id: browser-components
title: 浏览器组件
- id: verify
title: 验证配置
---
## 导入样式 {#import-styles}
## 添加依赖 {#dependency}
在应用入口中导入一次全局样式:
应用通过 workspace 协议引用 UI 包:
```ts
import "@workspace/ui/globals.css"
```
它提供组件所需的共享 Tailwind 层、设计变量、字体配置和工具类。
## 导入组件 {#import-a-component}
```tsx
import { Button } from "@workspace/ui/components/button"
export function SaveButton() {
return <Button>保存</Button>
```json
{
"dependencies": {
"@workspace/ui": "workspace:*"
}
}
```
## 包入口 {#entry-points}
仓库中的应用已经完成此配置。这个 package 当前是私有 workspace package,不需要从 npm 安装。
- `@workspace/ui/components/*` 导出单个组件模块。
- `@workspace/ui/hooks/*` 导出可复用的交互和媒体查询状态。
- `@workspace/ui/lib/utils` 导出共享的类名组合工具。
- `@workspace/ui/locales/*` 导出指定语言的词典。
## 全局样式 {#global-styles}
在应用的全局 CSS 中导入一次:
```css
@import "@workspace/ui/globals.css";
```
该入口会加载 Tailwind CSS、shadcn 的 Tailwind 扩展、动画工具、Inter Variable 字体、语义颜色、圆角变量和基础样式。主题通过根节点上的 `.ui:light` 与 `.ui:dark` class 切换;组件代码不要自行复制这些变量。
`globals.css` 中的 `@source` 已覆盖本仓库的 `apps` 与组件目录。如果将 package 移到另一种目录结构,应同步调整扫描路径,否则生产构建可能缺少组件使用的 utility。
## 显式导入 {#imports}
package 没有聚合式 barrel。始终从具体子路径导入,便于追踪依赖并避免意外扩大客户端 bundle:
```tsx
import { Button } from "@workspace/ui/components/button"
import { SaveIcon } from "@workspace/ui/components/icon"
import { useIsMobile } from "@workspace/ui/hooks/use-breakpoint"
import { cn } from "@workspace/ui/lib/utils"
```
公开入口包括:
- `@workspace/ui/components/*`:组件与局部类型。
- `@workspace/ui/hooks/*`:断点和波纹交互。
- `@workspace/ui/locales`:支持的 locale 与消息 ID 类型。
- `@workspace/ui/locales/en-US`、`@workspace/ui/locales/zh-Hans`:消息与日历 locale。
- `@workspace/ui/globals.css`:全局样式。
## shadcn CLI {#shadcn-cli}
仓库根目录运行:
```sh
bunx shadcn@latest add button -c apps/web
```
应用和 UI package 的 `components.json` 使用同一套 `base-vega`、neutral、CSS variables、Lucide 词汇和 RTL 配置;别名会把生成文件放到 `packages/ui/src/components`。这与 shadcn 官方的 [monorepo 工作流](https://ui.shadcn.com/docs/monorepo)一致。
CLI 生成完成后必须检查 diff。生成器不知道本仓库的 Icones、国际化、波纹和 Popover 扩展,更新已有组件时可能覆盖本地适配。`components.json` 各字段的含义可参考[官方说明](https://ui.shadcn.com/docs/components-json)。
## 浏览器组件 {#browser-components}
多数交互组件依赖事件、Portal 或浏览器 API。使用 React Server Components 的消费端应从自身的客户端边界导入它们。本仓库配置为 `rsc: false`,组件文件不会统一插入框架专用指令;少数组件因自身需要显式包含 `"use client"`。
## 验证配置 {#verify}
修改组件或配置后运行:
```sh
bun run --cwd packages/ui typecheck
bun run lint
bun run format:check
```
如果改动涉及消费方式,再运行应用或文档的构建,确认 Tailwind 能扫描到新 class,并检查浅色、深色与 RTL。