Files
planet/docs/technical/zh/tactile-ui-components.md
linkong fbca381512
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
ci / delivery (push) Has been cancelled
release / images (push) Has been cancelled
release: bump version to 0.62.0
2026-05-21 01:37:32 +08:00

7.5 KiB
Raw Blame History

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保存、确认、创建、执行这类强意图操作可以保留文字。

导入

仓库内使用:

import { TactileButton, TactileSwitch, ControlGroup } from '@/components/tactile-ui'
import '@/components/tactile-ui/styles.css'

未来拆包后建议导入形式:

import { TactileButton, TactileSwitch } from '@planet/tactile-ui'
import '@planet/tactile-ui/styles.css'

主题 Token

核心 token 在 CSS 中以 --tui-* 暴露。业务主题只需要覆盖变量,不应直接重写组件内部 class。

: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。

TactileButton

按钮组件覆盖普通按钮、图标按钮、强意图按钮和链接式按钮。

常用 props

Prop 说明
variant neutralprimarydangersubtleghost
size smmdlgicon
shape squarepill
icon 预设图标名称或自定义 React 节点
iconOnly 图标按钮固定尺寸,并要求提供 tooltiparia-label
tooltip 通过 portal 渲染,默认避开光标位置
loading 禁用按钮并暴露 aria-disabled
tactile 覆盖宽高、圆角、阴影、背景和深色模式变量
<TactileButton icon="refresh" iconOnly tooltip="刷新" />
<TactileButton variant="primary" icon="save">保存</TactileButton>
<TactileButton variant="danger" icon="delete" iconOnly tooltip="删除" />

variant="neutral" 的默认按钮是白色触感按钮。彩色按钮仍应保留外部投影和统一高度,不应在业务 CSS 中手写新的阴影体系。

图标预设

预设图标由 tactileIconPresets 统一维护,业务页面通过语义名称调用,避免每个页面随意选择图标。

常用语义:

名称 用途
refresh 刷新数据
save 保存
delete 删除
trigger / collect 触发采集
start / play 启动
stop 停止
connect / test 连接或连通性测试
guide 凭证教程
generate AI 生成
reset 恢复默认
detail 查看详情
copy 复制
upload 上传

如果预设不足,可以直接传 React 节点:

<TactileButton icon={<MyIcon aria-hidden="true" />} iconOnly tooltip="自定义动作" />

TactileSwitch

开关组件用于二元设置。它不是 iOS 风格大开关,而是与 Docs 主题滑块一致的小型轻触感控件。

<TactileSwitch
  checked={enabled}
  onCheckedChange={setEnabled}
  label="启用 WebSearch"
  tooltip="启用 WebSearch"
/>

文案默认进入 tooltip。需要显示文字时由调用方在布局中单独放 label不应把长文案塞进 switch 内部。

ControlGroup

ControlGroup 用于把一组按钮按统一间距和对齐方式排列。默认没有灰色底座;只有显式传 withBasebaseTactile 时才显示底座。

<ControlGroup align="end" gap={8}>
  <TactileButton icon="refresh" iconOnly tooltip="刷新" />
  <TactileButton variant="primary" icon="save">保存</TactileButton>
</ControlGroup>

用于列表底部时,按钮应随着列表滚动出现,而不是固定漂浮在列表中部。用于详情页工具栏时,按钮高度和阴影应由组件默认值统一。

TactileTooltip

Tooltip 使用 createPortal 挂到 document.body,支持 toprightbottomleft 和 collision 修正。默认位置会相对触发元素向右下偏移,避免被鼠标指针挡住。

<TactileTooltip label="刷新" anchor={buttonRef.current} open={open} side="bottom" />

应用层通常不需要直接使用它,TactileButtonTactileSwitch 已经内置 tooltip。

Scrollbar

Scrollbar 包裹普通滚动容器。真实滚动仍由内部 viewport 承担Tactile UI 只提供覆盖式 track/thumb。

<Scrollbar axis="both" minThumbSize={28} autoHide>
  <LongContent />
</Scrollbar>

常用 props

Prop 说明
axis xyboth
autoHide 非 hover/拖拽时自动弱化
alwaysVisible 始终显示滚动条
trackSize / thumbSize 控制轨道和 thumb 尺寸
inset / radius 控制贴边距离和圆角
thumbColor / trackColor 局部颜色覆盖
viewportRef 由外部提供真实滚动节点

Textarea 不建议强行套 overlay 滚动条,因为浏览器 resize grip 和文本选择需要原生交互。需要统一外观时,应使用专门的 resizable textarea 样式,让原生 grip 保留可点击区域。

ScrollbarOverlay

ScrollbarOverlay 适合已经存在滚动节点的区域,例如第三方表格或自定义 viewport。它不会创建新的滚动容器只会监听目标节点并画覆盖式滚动条。

<div ref={hostRef}>
  <ThirdPartyTable />
  <ScrollbarOverlay containerRef={hostRef} targetSelector=".tui-scroll-target" />
</div>

TableScrollRegion

TableScrollRegion 是表格滚动区域的便利封装。默认目标选择器是 .tui-scroll-targetAdmin Next 会显式传自己的 table viewport selector避免组件库里出现业务命名。

<TableScrollRegion targetSelector=".table-viewport">
  <EntityTable />
</TableScrollRegion>

可访问性

  • iconOnly 按钮必须提供 tooltiparia-label
  • TactileSwitch 使用 role="switch"aria-checked
  • tooltip 只是说明不承担唯一状态表达状态仍应通过文本、badge 或 aria-* 呈现。
  • 禁用和 loading 状态会设置 aria-disabledbutton 元素会同步 disabled

Admin Next 迁移约定

Admin Next 中所有全局工具按钮、详情页工具按钮和列表底部动作应使用 Tactile UI

  • 无歧义动作:TactileButton iconOnly tooltip
  • 保存/创建/确认:TactileButton variant="primary",通常保留文字
  • 删除/停止:TactileButton variant="danger",根据风险决定是否保留文字
  • 开关:TactileSwitch
  • 长列表、表格、日志、MarkdownScrollbar / ScrollbarOverlay / TableScrollRegion

这样做的边界是组件负责触感、尺寸、阴影、tooltip 和滚动条行为;业务页面负责 API、状态机、权限和文案。