Skip to content

Lampio — UI 业务级组件使用全景

本文件定期扫描全仓 src/components/ui/ 组件实际调用,按业务语义分类索引。 与 docs/ui-standards.md §13.2 实体→variant 映射表 互补:规范说"应该用什么",本文档记录"当前用在哪里 / 怎么用"。 扫描时间以 git log -1 -- docs/ui-component-inventory.md 为准(手写"最后更新"必然漂移,已移除)。


0. 目录

  1. 列表组件全景(ListItem)
  2. 空态 / 占位(EmptyState)
  3. 结构 molecule(SectionHeader / Divider / Card / SectionCard)
  4. 交互 molecule(ConfirmDialog / Tooltip / toast)
  5. 表单 molecule(Input / Textarea / Select / Checkbox / Switch)
  6. 动作 atom(Button / IconButton / ChromeButton / Toggle)
  7. 视觉 atom(Icon / Spinner / ProgressBar)
  8. 非列表特殊模式(豁免清单)
  9. 低使用率 / 候选废弃
  10. 未抽象但重复的 pattern(S7 候选)

1. 列表组件全景

1.1 <ListItem> — 14 处调用(4 variant × 自由组合 slots)

1.1.1 variant="simple" dense — 紧凑同质词条流

#位置特点业务实体
1VocabPanel Notebookactive 双态:selected=strong(bg/15) / expanded=soft(bg/10);leading = [checkbox?] + [mastery dot] 复合;trailing = [CEFR badge] + [✓掌握/🗑] 两操作词汇
2VocabPanel Excludedactive=soft 单态;leading=[checkbox or placeholder];trailing=[SYS/USER badge] + [🗑 删除]排除词汇
3VocabPanel Browse无 active;leading=占位(与 Excluded 对齐);trailing=[CEFR] + [+添加/⊘排除] 或 [✓/⊘ badge] 互斥核心词汇浏览
4SearchDropdown Related Articlesleading=[emoji 📰/🕐]搜索下拉文章项

1.1.2 variant="simple" normal — 单行数据流

#位置特点业务实体
5RssPanel 订阅源列表leading=[Favicon 或 Rss fallback];meta=[●+未读数];trailing=[ChevronRight]RSS 订阅源

1.1.3 variant="double" normal — 双行 stack

#位置特点业务实体
6HistoryPanel 资源历史leading=[TypeIcon: Globe/BookOpen];subtitle = type + count + 日期(右对齐 ml-auto);trailing=[🗑 hover-reveal]资源访问记录
7MySitesPanel 站点列表leading=[Favicon 或 TypeIcon];subtitle=domain;trailing=[X 取消收藏 hover-reveal]收藏站点
8HomePage Recently Opened与 #6 同实体,同结构资源访问记录
9HomePage Recommended 文章leading=[Favicon 方块];subtitle = CEFR badge + reason + source 内联内容推荐
10RssPanel 文章列表tone="muted" 已读灰化titleLines=2 长标题;leading=[● 未读/○ 已读];subtitle=日期;className="items-start"RSS 文章
14NotesPanel 来源页面(笔记详情,2026-07-04 改版 + 二轮)density=dense;subtitle=最近阅读相对时间(last_opened_at 兜底 created_at);meta="N 词";trailing=[ChevronRight];点行 drill-down 进来源详情页(打开页面动作移入 drill header)阅读来源页

1.1.4 variant="rich" normal — 卡片化独立实体(用 avatar slot)

#位置特点业务实体
11NotesPanel 笔记本avatar(cover / fallback=Folder/Book,tone=primary/neutral);subtitle="N 词 · N 源";trailing=[ChevronRight](S6.5 对齐 ReviewOverview 视觉)笔记本
12ReviewOverview 复习分组avatar(cover / fallback=Calendar/BookOpen);meta=[due pill badge];subtitle=缓存页数+预览词;trailing=[ChevronRight]复习会话分组

1.1.5 variant="tree" dense — 层级缩进

#位置特点业务实体
13TocPanel 目录depth(paddingLeft = 8 + depth×12);无 border;active → text-primary font-medium文档目录项

1.2 <Tile>已退役(2026-08-13)

原 4 处调用(全在 HomePage)已迁到 ListItem variant="double"ui/Tile.tsx + tileVariants 一并删除。迁移理由:同一发现页里原先并存两套列表语法——「继续阅读 / 最近打开」 是 ListItem,站点/订阅四栏是 Tile 描边卡;而 double 变体的实体映射原文就写着 「history / sites / RSS / feeds」。

现由 HomePage.DiscoverRow 统一(ListItem double + 星标 trailing):

#位置leadingsubtitle星标业务实体
D1SiteCard(我的收藏站点)SiteFavicon smdomain[· category]实心,hover 显现收藏站点
D2推荐站点SiteFavicon smdomain · category · 难度档空心,常显推荐站点
D3我的订阅Rss 图标tag/域名 · 未读数实心,hover 显现订阅源
D4推荐订阅Rss 图标tag · 难度档空心,常显推荐订阅源

⚠️ 星标显隐刻意不对称:已收藏态星标是「取消」(hover 显现,同 MySitesPanel); 可收藏态星标是「收藏」= 该栏主要动作,常显,藏起来等于藏掉整栏用途。

1.2.5 <WordRow> — 4 处(Library 4 tab + sourceWords 子列表)

ListItem variant="simple" density="dense" 的固定槽位 wrapper:leading → word → CEFR badge(auto from cefrLevel) → stateBadges → actions。所有词汇行的统一入口。

位置leadingstateBadgesactions
NotebookMasteryDot(+SelectCheckbox 批量)✓/Eye + 🗑
ExcludedBan(amber)SYS/USER badge🗑(user only)
Browse(in-notebook)Bookmark(emerald)✓ Notebook
Browse(excluded)Ban(amber)⊘ Excluded
Browse(discoverable)空 placeholder⊘ chip

1.2.6 <ListPanel> + <ListWithDetail> — 4 处(Library 4 tab)

<ListPanel>:封装 w-80 + 右 border + header + scroll body + footer,header 区放 filter pills / advanced popdown / batch toolbar(filter 紧贴 list,toolbar 极简化后的承载点)。

<ListWithDetail>:2-column 骨架(left + 右灰画布 + 内嵌白卡)。hasSelection=false 时跳过白卡只画 EmptyState。

1.3 ListItem 正交能力索引(按能力查列表)

能力prop / 模式命中列表
左图标 14-20pxleading: ReactNodeHistory / MySites / RSS feeds / RSS articles / Search / Home Recently / Home Recommended
圆角 avatar 28×28avatar: {src, fallback, tone}NotesPanel / ReviewOverview
主标题 + 副标题title + subtitle所有 double + rich
双行标题 clamptitleLines={2}RssPanel articles
已读/灰化tone="muted"RssPanel 已读
右缘 metameta: ReactNodeRSS feeds unread / ReviewOverview due / History time
右缘操作trailing: ReactNodeHistory 🗑 / MySites X / Notes 🗑 / Vocab ✓🗑 / Review ❯
hover-reveal 操作trailing + className="group"History / MySites 删除
选中 activeactive + activeToneVocabPanel / TocPanel
多选 checkboxleading={<Fragment>checkbox + dot</>}VocabPanel 批量选择
层级缩进variant='tree' + depthTocPanel
整行可点 + 右缘 actiononClick + trailing + 子按钮 stopPropagationMySites / HomePage DiscoverRow 四栏

1.4 可展开二级模式(目前仅一种)

VocabPanel Notebook 主行 + sibling 详情(mastery label + date):

tsx
<div>
  <ListItem ... onClick={toggleExpand} active={isExpanded} />
  {isExpanded && <div className="mx-3 mb-1 p-2 bg-surface-content rounded">...</div>}
</div>

非侵入式:ListItem 不管展开,调用方渲染兄弟节点。适合简短 meta 展开。复杂详情(如 TranslationTimeline 展开 grammar/vocab/grammar_points 三段)→ 独立组件。


2. 空态 / 占位

2.1 <EmptyState> — 16 处调用(三大类)

类型位置关键 slots
Loading 占位NotesPanel "加载笔记本..." / RssPanel × 2 / ReviewOverviewprimary + density="compact"
空态无 CTAHistoryPanel / DiscoveryPanel / NotesPanel "暂无来源" / VocabPanel Excluded / TranslationTimeline × 2primary [+ secondary]
空态带 CTAaction slot,S6.3 启用)NotesPanel + 新建笔记本 / RssPanel + 添加 RSS / VocabPanel 浏览核心词汇primary + secondary + action=Button variant="link"
错误态RssPanel 网络失败icon=CircleAlert + primary(红色) + secondary 降级提示

3. 结构 molecule

3.1 <SectionHeader> — 5 处调用(S6.3 从 HomePage 升级)

Uppercase caption + 可选右缘 link action + nested( 前缀缩进)。目前全在 HomePage,未来 NotesPanel / VocabPanel section headers 可复用。

tsx
<SectionHeader title="Recently Opened" action="View All" onAction={...} />
<SectionHeader title="Recommended Sites" nested />

3.2 <Divider> — 2 处调用

Section 之间的硬分隔(border-t border-border/30)。调用少——大部分区块靠 mb-* 间距 + section header 已够。

3.3 <Card> / <SectionCard> — 均 0 调用 ⚠️

Pre-S5 残留。Card 的语义已被 ListItem variant='rich' 吸收,SectionCard 的语义被 Settings 独立 row 样式覆盖。候选废弃,见 §9。

3.4 <CollapsibleSection> — 句子工作台拆解/指代子块

自包含的折叠小节:header = 图标 + 标题 + 可选数量徽 + 右侧 ChevronDowngroup-hover 时图标/标题/箭头三元素同步变色、箭头 rotate-180),body 经 useState 折叠(defaultOpen 默认 true)。视觉 idiom 抽取自 DiscoveryPanel 短语清单/词清单 header。a11y:button 可见标题即 accessible name + aria-expanded,无需额外 aria-label。当前调用:SentenceWorkbench 拆解段(主干/修饰/词汇/语法点)+ 指代段(指代/背景);NotesPanel 域名规则(低频配置项折叠收纳,defaultOpen=false + count=规则数,body 即扁平管理面:chips hover-× + 内联添加)+ 来源详情 drill-down 三节(单词/句子/跟读——句子节为按句聚合的管理卡,卡级+类型级删除,分组 key 复用 SentenceWorkbench.normalizeSentence;2026-07-06 三轮打磨)。与 DiscoveryPanel 的区别——那里 header/body 分在两个 PanelSection(面板级分隔+滚动耦合),本组件是卡内内联单元,故 DiscoveryPanel 暂未迁移。


4. 交互 molecule

4.1 <ConfirmDialog> — 1 处调用

基于 Modal(Radix Dialog)+ 固定 amber warning 或 destructive 配色。用于"删除/关闭前确认"。目前 App.tsx 只在标签驱逐触发。

4.2 <Tooltip> / <TooltipProvider> — 0 直接调用 ⚠️

S2.5 预备的 Radix Tooltip wrapper,原计划 S7 长尾把 HTML title="..." 属性批量迁过来。目前没有业务调用,但 <TooltipProvider> 已挂在 App 根节点等待使用。活跃基础设施,不算废弃

4.3 <Modal> / <Popover> — 基础设施

Radix wrapper,分别有 1 个直接调用(Modal → ReportOverlay;Popover → AccountPopover)。数量少因为 ConfirmDialog 封装了 Modal 的 90% 场景。

4.4 lib/toast.ts(sonner 包装)— 3 处调用(S6)

ts
toast.success('已复制')  // TranslationTimeline 剪贴板
toast.error('清理失败', { description: String(err) })  // StorageSettings
toast.promise(p, { loading, success, error })

<Toaster position="bottom-center" richColors closeButton /> 已挂 App 根。


5. 表单 molecule

组件调用特点
<Input>74 variant(default / search / xs / soft)+ error prop + tone="readonly"
<Textarea>0 ⚠️带 error,但无调用
<Select>0 ⚠️带 error,但无调用(Settings 用原生 <select>
<Checkbox>0 ⚠️Vocab 批量选择用内联 span + svg 而非 Checkbox
<Switch>1Settings 开关

现象:VocabPanel 的批量选择 checkbox 是内联实现而不走 ui/Checkbox——因为 ListItem 的 leading slot 接受任意 ReactNode,作者自然选了最短路径。这是 API 宽松的副作用,但收紧难度高(Checkbox 样式在 ListItem leading 里需要更小的 14×14 尺寸)。S7 低优先级。


6. 动作 atom

组件调用特点
<Button>395 variant(primary/secondary/ghost/destructive/link)× 4 size(xs/sm/md/lg)
<IconButton>193 size(xs/sm/md),纯 icon 按钮
<ChromeButton>9Chrome 专用(TitleBar / AddressBar / Toolbar 的灰黑风格按钮)
<Toggle>0 ⚠️3 variant × active 态,但无调用(都走内联 TOGGLE.* class 组合)

现象<Toggle> 是 S3 cva 化的 variant 系统,但无人用 React 组件,都直接贴 TOGGLE.navActive class 字符串。这是能用但没人用的状态——因为 TabBar / VocabPanel 内联 class 写起来比传 props 更短。


7. 视觉 atom

组件调用特点
<Icon>60lucide 包装;强制 ICON_SIZE scale(xs/sm/md/lg/xl),arch-check S17 守护
<Spinner>1加载小转圈
<ProgressBar>1复习进度条

Icon 是全仓调用最多的 ui/ 组件(60 处),说明 S2 的 lucide 迁移起到了预期效果。


8. 非列表特殊模式(明确豁免清单)

以下不应强套 ListItem

位置模式豁免原因
WordPopup definitionspos italic + def_zh 内联段落段落排版,非 row
TranslationTimeline TimelineCard可展开翻译详情(grammar+vocab+grammar_points 三段)展开后是完整详情视图
SearchDropdown words字典结果卡片(word + IPA + CEFR + mastery + def)字段过多 inline
DiscoveryPanelCEFR filter 横向 chip + 按 level 分组 3 列 gridgrid 结构
StatsPanel图表分组 + metric cards图表容器
CefrPanel文章难度 context strip(DiscoveryPanel 顶部摘要行 + 可展开 CEFR 分布条 + 个性化难度档)摘要/统计展示,非 row 列表
Settings GeneralSettingsCEFR_LEVELS / SHORTCUTS 横向 pill选项组
ReportOverlay2-col metric grid + 时段 tabdashboard 布局
TabBar / TitleBar / EpubNavBar横向 tab / 操作按钮行chrome
charts/*SimpleBarChart / SimpleLineChart图表实现
ReviewCardFace复习卡翻转单卡片

9. 低使用率 / 候选废弃

组件调用判定
Card✅ S6.4 删除(语义被 ListItem variant='rich' 吸收)
SectionCard✅ S6.4 删除(Settings 用独立 row 样式)
Textarea0保留——未来表单场景可能用
Select0保留——表单场景预备位
Checkbox0保留——VocabPanel 若 S7 收紧会迁过来
Toggle0保留——是 class 字符串的 React wrapper 备选(哲学:class string + 组件双入口)
Tooltip0 直接调用保留——S7 预备位,替代 HTML title 属性;TooltipProvider 已挂 App 根

清理策略:Textarea / Select / Checkbox / Toggle / Tooltip 五个 0 调用组件确认保留(2026-04-19 决策),各自有明确的未来预备位或哲学理由。


10. 未抽象但重复的 pattern

2026-04-19(S6.4)清理:

10.1 ✅ SiteFavicon — 已抽到 ui/SiteFavicon.tsx

两种尺寸(size='sm' = 16×16 列表行 leading / size='lg' = 28×28 wrapped,Tile 退役后 lg 暂无调用方)。自带 duckduckgo src + 首字母 fallback + useState failed React 化错误处理(替代原来两种不同的 onError 实现)。

迁移调用点:

  • HomePage.SiteCard → 内部用 HomeSiteLeading(epub 走 TypeIcon,其他委托 SiteFavicon size="lg"
  • HomePage Recommended Sites → SiteFavicon size="lg"
  • MySitesPanel 站点行 → SiteFavicon size="sm"(替代原 local Favicon 组件)

Favicon Fallback pattern(10.5)已并入 SiteFavicon,不再单独列项。

10.2 ✅ TabButton — 已移到 ui/TabButton.tsx

src/components/TabButton.tsx 移入 ui/。5 处 import 路径更新(RssPanel / VocabPanel / ReviewOverview / TranslationTimeline / HistoryPanel)。

10.3 保留 class helper 形态(不组件化)

2026-04-19 决策:以下 pattern 不抽组件,保留 class helper 函数:

  • Mastery Dot<span className="w-1.5 h-1.5 rounded-full ${MASTERY_COLORS[level]}"> — 1 行样式 + 1 个 className 引用,组件化收益不覆盖导入成本。保留 MASTERY_COLORS 常量 + 内联使用。
  • CEFR Badge:已有 cefrBadgeClass() / cefrBarClass() helper,调用处只需 <span className={cefrBadgeClass(level)}>{level}</span>。保留 helper 形态。

判断标准:组件化门槛 = 结构 > 10 行 有条件分支 跨文件逻辑重复。单行 className 引用不够门槛——过度组件化本身是另一种漂移。


附录 A:刷新本文档的时机

  • 新增 ui/ 组件 → 在对应分类下登记
  • ListItem 新调用 → 在 §1 表中加行
  • 组件 0 调用超 3 个月 → 候选废弃(§9)
  • 发现新重复 pattern → 加到 §10

附录 B:与其他文档的关系

这里有别处有关系
§1.1 ListItem 13 处详单docs/ui-standards.md §13.2 实体→variant 映射表规范规定"应该用什么",本文档记录"用在哪里"
§2 EmptyState 16 处docs/ui-standards.md §15.1同上
§8 豁免清单docs/ui-standards.md §20 违规速查同上
§10 未抽象 pattern本文档是唯一记录点,S7 长尾规划参考