Initial TrueGrowth source import

This commit is contained in:
2026-07-07 09:36:36 +08:00
commit 3b6781d695
2283 changed files with 691996 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
# Hover 提示统一经验
更新日期2026-04-30
## 背景
项目已经提取了共享 hover 提示组件,但应用 UI 中仍散落着原生 `title``data-tooltip`、局部 CSS tooltip 和直接使用 TDesign `Tooltip` 的写法。
这类分散实现会导致样式、延迟、层级、可访问性和测试行为不一致,也容易在新功能里继续扩散。
## 需求边界
本次统一的是“应用 UI 的视觉 hover 提示”,不是机械删除所有 `title`
需要统一:
- 按钮、图标、菜单、工具栏、状态徽章、素材库、AI 输入栏、属性面板等应用 UI hover 文案。
- 原生 DOM `title``data-tooltip`、局部 CSS tips。
- 组件层直接引入 `tdesign-react``Tooltip`
- `ToolButton title` 的新增使用。
暂不统一:
- Markdown/用户内容渲染出来的链接、图片语义 `title`
- TDesign 表单 `tips`,它是静态说明文案,不是 hover 提示。
## 组件规则
- 简短纯文本提示使用 `HoverTip`
- 富内容、需要鼠标停留或可交互内容的 hover 浮层使用 `HoverCard`
- `ToolButton title` 只作为兼容入口保留,新代码使用 `tooltip`
- 共享封装内部可以使用 TDesign `Tooltip`,业务组件不要直接引入。
- 测试容易 mock `../shared` 聚合导出时,优先从 `../shared/hover` 引入 `HoverTip`,减少 mock 破坏面。
## 迁移模式
### DOM title
```tsx
<HoverTip content="上传图片" showArrow={false}>
<button onClick={handleUpload}>
<ImageUploadIcon />
</button>
</HoverTip>
```
### ToolButton
```tsx
<ToolButton
type="icon"
icon={<DownloadIcon />}
aria-label="下载"
tooltip="下载"
/>
```
### 条件提示
```tsx
<HoverTip content={model.id} disabled={!showIdTooltip} showArrow={false}>
<div role="option">{model.label}</div>
</HoverTip>
```
## 守卫脚本经验
不要只靠简单正则扫描 JSX。
`onClick={() => ...}` 这类属性里包含 `>`,如果用 `<button[^>]*title=` 会在箭头函数处提前截断,漏掉后面的 `title`
更稳的做法:
- 扫描 JSX opening tag 时记录 `{}` 深度。
- 同时处理字符串引号和模板字符串。
- 找到真正的 tag 结束 `>` 后再判断属性。
- 用脚本所在 package 根目录作为路径基准,避免从仓库根和 package 目录执行时例外路径不一致。
检查脚本应覆盖:
- `src/components`
- `src/tools`
- `*.ts`
- `*.tsx`
- `*.scss`
- `*.css`
明确允许共享封装和 Markdown 用户内容例外,其他例外必须写进脚本,不要靠口头约定。
## 性能注意
高频网格路径要特别小心,例如素材库图片卡片、缩略图列表、颜色块列表。
规则:
- 不要在一个高密度列表项里叠多层 hover 浮层。
- 非关键提示优先用可见短文案或静态省略展示。
- hover 样式不要触发布局变化。
- 避免在列表 hover 上使用大阴影、`backdrop-filter` 或频繁 portal 定位。
如果产品必须在高频网格上保留提示,优先只给关键操作加 `HoverTip`,不要给每个子元素都加。
## Lint 接入经验
`drawnix` lint 原先直接 `eslint .`,会扫进 `node_modules` 里的第三方 `.eslintrc`,导致无关失败。
更合理的命令:
- ESLint 只扫项目源码目录。
- hover 守卫脚本独立执行。
- 即使 ESLint 被既有基线挡住,也要继续执行 hover 守卫脚本,避免新规则失效。
同时,由于项目把 `tdesign-react` 映射到本地样式聚合入口,需要在 Nx module boundary 规则中允许 `tdesign-react` 虚拟入口,否则 ESLint 可能尝试把它当内部路径重写并崩溃。
## 验证清单
- `openspec validate refactor-hover-tip-unification --strict`
- `pnpm --dir packages/drawnix exec node scripts/check-hover-usage.mjs`
- `pnpm --dir packages/drawnix exec vitest run src/components/shared/hover/hover-utils.test.tsx`
- `pnpm nx typecheck drawnix`
- `pnpm nx lint drawnix`
如果 `pnpm nx lint drawnix` 被既有 ESLint 基线阻塞,需要在结果里明确区分:
- hover 守卫是否通过
- typecheck 是否通过
- lint 失败是否来自本次改动
## 修复检查清单
- 应用 UI 不再直接使用原生 hover `title`
- 应用 UI 不再使用 `data-tooltip` 或局部 CSS tooltip。
- 业务组件不直接从 `tdesign-react` 引入 `Tooltip`
- `ToolButton` 新用法使用 `tooltip`,不是 `title`
- Markdown/用户内容 `title` 例外写进守卫脚本。
- TDesign 表单 `tips` 不按 hover 处理。
- 守卫脚本从仓库根和 `packages/drawnix` 目录执行都能得到一致结果。