Files
planet/docs/technical/zh/tactile-ui-components.md
rayd1o 65e6a96c0d
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
release / images (push) Has been cancelled
ci / delivery (push) Has been cancelled
ci / backend (pull_request) Has been cancelled
ci / frontend (pull_request) Has been cancelled
ci / delivery (pull_request) Has been cancelled
release: bump version to 0.65.0
2026-05-21 05:41:49 +08:00

224 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tactile UI 组件库
Tactile UI 是 Planet 内部抽出的可移植 React 控件层。它来自 Admin Next 的按钮、开关、滚动条和 tooltip 收口,但组件本身不依赖 Admin Next、AntD、Radix、Tailwind 或 `an-*` class。目标是先在仓库内稳定使用后续可以作为独立 npm 包发布。
## 设计目标
- **轻触感**:默认控件使用白色或主题表面、细边框和外部投影,接近 Docs 主题滑块的轻微立体感,不使用大色块或发光效果。
- **可移植**:组件 class 使用 `tui-*` 前缀,样式集中在 `frontend/src/components/tactile-ui/styles.css`
- **低依赖**:组件只假设 React/React DOM图标预设当前使用 `lucide-react`,调用方也可以传自定义 React 节点。
- **主题友好**:默认样式通过 CSS variables 暴露Planet 可以在 Admin Next 或其它页面按主题覆盖 token。
- **语义清楚**:无歧义操作优先 icon-only + tooltip保存、确认、创建、执行这类强意图操作可以保留文字。
## 导入
仓库内使用:
```tsx
import { TactileButton, TactileSwitch, ControlGroup } from '@/components/tactile-ui'
import '@/components/tactile-ui/styles.css'
```
未来拆包后建议导入形式:
```tsx
import { TactileButton, TactileSwitch } from '@planet/tactile-ui'
import '@planet/tactile-ui/styles.css'
```
## 主题 Token
核心 token 在 CSS 中以 `--tui-*` 暴露。业务主题只需要覆盖变量,不应直接重写组件内部 class。
```css
:root {
--tui-surface: #ffffff;
--tui-surface-raised-hover: #f8fbff;
--tui-border-soft: #d6dfeb;
--tui-border-hover: #b8c6d9;
--tui-text: #0f172a;
--tui-primary: #2563eb;
--tui-danger: #dc2626;
}
[data-theme='dark'] {
--tui-surface: #111827;
--tui-text: #e5edf8;
}
```
组件还支持通过 `tactile` prop 传入局部尺寸、圆角、背景、边框和阴影参数。局部参数适合少量特殊按钮;全局视觉应优先走 CSS variables。
## Portal 与深色主题
`TactileTooltip` 和使用 Tactile UI 的 Admin Next `Dialog``Select`、Toast 都可能通过 portal 挂到 `document.body`。这类节点不在 `.admin-next-theme-root[data-theme='dark']` 下面,不能只依赖局部祖先选择器读取深色 token。
Admin Next 的主题 provider 会把当前主题同步到 `body[data-admin-next-theme]`。共享样式必须同时支持两类选择器:
```css
[data-theme='dark'] .tui-button,
body[data-admin-next-theme='dark'] .tui-button {
--tui-surface: #172033;
--tui-text: #e5edf8;
}
```
新增 portal 控件时,先确认它是否渲染到 body。如果是就要在组件自己的样式入口补 `body[data-admin-next-theme='dark']` 分支,或复用已经覆盖过的 `--tui-*` / `--an-*` token。不要在单个弹窗里手写固定深色因为同一问题会在下拉菜单、tooltip、toast 和确认弹窗里重复出现。
## `TactileButton`
按钮组件覆盖普通按钮、图标按钮、强意图按钮和链接式按钮。
常用 props
| Prop | 说明 |
| --- | --- |
| `variant` | `neutral``primary``danger``subtle``ghost` |
| `size` | `sm``md``lg``icon` |
| `shape` | `square``pill` |
| `icon` | 预设图标名称或自定义 React 节点 |
| `iconOnly` | 图标按钮固定尺寸,并要求提供 `tooltip``aria-label` |
| `tooltip` | 通过 portal 渲染,默认避开光标位置 |
| `loading` | 禁用按钮并暴露 `aria-disabled` |
| `tactile` | 覆盖宽高、圆角、阴影、背景和深色模式变量 |
```tsx
<TactileButton icon="refresh" iconOnly tooltip="刷新" />
<TactileButton variant="primary" icon="save"></TactileButton>
<TactileButton variant="danger" icon="delete" iconOnly tooltip="删除" />
```
`variant="neutral"` 的默认按钮是白色触感按钮。彩色按钮仍应保留外部投影和统一高度,不应在业务 CSS 中手写新的阴影体系。
有色按钮的边框不能直接使用填充色本身。`primary``danger` 和后续新增的有色 variant 应使用同色系减淡边框,例如 `color-mix(in srgb, var(--tui-danger) 64%, white)`。这样边框仍然属于按钮色相,但视觉重量弱于填充面,避免红色/蓝色按钮看起来比默认按钮额外大一圈。hover 态应提亮而不是压暗,背景用当前色混入少量 white边框继续比背景更轻。
## 图标预设
预设图标由 `tactileIconPresets` 统一维护,业务页面通过语义名称调用,避免每个页面随意选择图标。
常用语义:
| 名称 | 用途 |
| --- | --- |
| `refresh` | 刷新数据 |
| `save` | 保存 |
| `delete` | 删除 |
| `trigger` / `collect` | 触发采集 |
| `start` / `play` | 启动 |
| `stop` | 停止 |
| `connect` / `test` | 连接或连通性测试 |
| `guide` | 凭证教程 |
| `generate` | AI 生成 |
| `reset` | 恢复默认 |
| `detail` | 查看详情 |
| `copy` | 复制 |
| `upload` | 上传 |
如果预设不足,可以直接传 React 节点:
```tsx
<TactileButton icon={<MyIcon aria-hidden="true" />} iconOnly tooltip="自定义动作" />
```
## `TactileSwitch`
开关组件用于二元设置。它不是 iOS 风格大开关,而是与 Docs 主题滑块一致的小型轻触感控件。
```tsx
<TactileSwitch
checked={enabled}
onCheckedChange={setEnabled}
label="启用 WebSearch"
tooltip="启用 WebSearch"
/>
```
文案默认进入 tooltip。需要显示文字时由调用方在布局中单独放 label不应把长文案塞进 switch 内部。
## `ControlGroup`
`ControlGroup` 用于把一组按钮按统一间距和对齐方式排列。默认没有灰色底座;只有显式传 `withBase``baseTactile` 时才显示底座。
```tsx
<ControlGroup align="end" gap={8}>
<TactileButton icon="refresh" iconOnly tooltip="刷新" />
<TactileButton variant="primary" icon="save"></TactileButton>
</ControlGroup>
```
用于列表底部时,按钮应随着列表滚动出现,而不是固定漂浮在列表中部。用于详情页工具栏时,按钮高度和阴影应由组件默认值统一。
## `TactileTooltip`
Tooltip 使用 `createPortal` 挂到 `document.body`,支持 `top``right``bottom``left` 和 collision 修正。默认位置会相对触发元素向右下偏移,避免被鼠标指针挡住。
```tsx
<TactileTooltip label="刷新" anchor={buttonRef.current} open={open} side="bottom" />
```
应用层通常不需要直接使用它,`TactileButton``TactileSwitch` 已经内置 tooltip。
## `Scrollbar`
`Scrollbar` 包裹普通滚动容器。真实滚动仍由内部 viewport 承担Tactile UI 只提供覆盖式 track/thumb。
```tsx
<Scrollbar axis="both" minThumbSize={28} autoHide>
<LongContent />
</Scrollbar>
```
常用 props
| Prop | 说明 |
| --- | --- |
| `axis` | `x``y``both` |
| `autoHide` | 非 hover/拖拽时自动弱化 |
| `alwaysVisible` | 始终显示滚动条 |
| `trackSize` / `thumbSize` | 控制轨道和 thumb 尺寸 |
| `inset` / `radius` | 控制贴边距离和圆角 |
| `thumbColor` / `trackColor` | 局部颜色覆盖 |
| `viewportRef` | 由外部提供真实滚动节点 |
Textarea 不建议强行套 overlay 滚动条,因为浏览器 resize grip 和文本选择需要原生交互。需要统一外观时,应使用专门的 resizable textarea 样式,让原生 grip 保留可点击区域。
## `ScrollbarOverlay`
`ScrollbarOverlay` 适合已经存在滚动节点的区域,例如第三方表格或自定义 viewport。它不会创建新的滚动容器只会监听目标节点并画覆盖式滚动条。
```tsx
<div ref={hostRef}>
<ThirdPartyTable />
<ScrollbarOverlay containerRef={hostRef} targetSelector=".tui-scroll-target" />
</div>
```
## `TableScrollRegion`
`TableScrollRegion` 是表格滚动区域的便利封装。默认目标选择器是 `.tui-scroll-target`Admin Next 会显式传自己的 table viewport selector避免组件库里出现业务命名。
```tsx
<TableScrollRegion targetSelector=".table-viewport">
<EntityTable />
</TableScrollRegion>
```
## 可访问性
- `iconOnly` 按钮必须提供 `tooltip``aria-label`
- `TactileSwitch` 使用 `role="switch"``aria-checked`
- tooltip 只是说明不承担唯一状态表达状态仍应通过文本、badge 或 `aria-*` 呈现。
- 禁用和 loading 状态会设置 `aria-disabled`button 元素会同步 `disabled`
## Admin Next 迁移约定
Admin Next 中所有全局工具按钮、详情页工具按钮和列表底部动作应使用 Tactile UI
- 无歧义动作:`TactileButton iconOnly tooltip`
- 保存/创建/确认:`TactileButton variant="primary"`,通常保留文字
- 删除/停止:`TactileButton variant="danger"`,根据风险决定是否保留文字
- 开关:`TactileSwitch`
- 长列表、表格、日志、Markdown`Scrollbar` / `ScrollbarOverlay` / `TableScrollRegion`
这样做的边界是组件负责触感、尺寸、阴影、tooltip 和滚动条行为;业务页面负责 API、状态机、权限和文案。