UI 基础组件——实地指南
如果你曾看着一个现代应用心想”那个东西到底叫什么?“——这个页面就是为你准备的。
你使用的每一个界面,都是由数量惊人地少的一组可复用积木搭建而成的,它们被称为 UI 基础组件(UI primitives)。它们有自己的名字。有时候名字还挺奇怪。但一旦你看懂了背后的隐喻,就再也无法视而不见了。
En Parlant~ 使用 Mantine,一个 React 组件库,开箱即用地提供了所有这些组件。以下是你的实地指南。
听起来像家具的那些
Section titled “听起来像家具的那些”Toast(吐司通知)
Section titled “Toast(吐司通知)”Toast 是一种从屏幕边缘弹出的小通知,停留几秒钟,然后滑走。就像一片面包从烤面包机里弹出来——只不过重力方向反了,它最终会自己缩回去。
在 En Parlant~ 中,toast 出现在右下角(通过 Mantine 的 @mantine/notifications),用于以下场景:
- “数据库加载成功”——弹
- “TTS 语音未配置”——弹
- “无法连接到 Lichess”——弹,然后你会有点小难过
你从来没有主动召唤过它们。它们自己出现,传达信息,然后离开。它们是 UI 世界里的信鸽。
Card(卡片)
Section titled “Card(卡片)”Card 是一个带边框或阴影的矩形容器,将相关内容聚合在一起。想象一张放在桌上的索引卡——它有明确的边界,承载一件事,你可以拿起来移动它。
在 En Parlant~ 中,你会看到卡片用于:
- 对局预览,在数据库浏览器中(
GameCard)——棋手姓名、结果、日期 - 棋盘位置编辑器(
EditingCard)——设置自定义局面时翻开的那张卡片 - 数据库条目,在数据库页面——每个数据库在网格中都有自己的一张卡片
Card 有一个近亲叫 Paper,基本上就是一张上过艺术学校的卡片。相同的理念——一个带边框或阴影的容器——但语义含义更少。Paper 只是说”我是一个平面。“它作为分析面板和数据库信息展示的背景出现。
Drawer / Sheet(抽屉 / 底部面板)
Section titled “Drawer / Sheet(抽屉 / 底部面板)”En Parlant~ 目前没有使用,但值得了解:drawer 是一个从屏幕边缘滑入的面板,就像一个书桌抽屉。(就是这样。这就是全部的隐喻。UI 设计师那天非常直白。)
挡住你去路的那些
Section titled “挡住你去路的那些”Modal(模态框)
Section titled “Modal(模态框)”Modal 是一个弹出对话框,强制要求你的注意力。它会将背景灰化,让你在做其他任何事之前必须先处理它。它是 UI 模式中的小孩子——“现在立刻看我!”
En Parlant~ 在真正需要你关注的时刻使用模态框:
- “确定要清除所有数据吗?“(
ClearDataModal)——带有复选框,让你选择要摧毁哪些数据 - “有更新可用”(
UpdateModal)——下载时带有进度条 - “导入一盘对局”(
ImportModal)——粘贴 PGN、输入 FEN,或拖入链接 - “创建新开局库”(
CreateRepertoireModal)——命名、配置、开始 - “关于 En Parlant~“(
About)——版本信息、致谢,通常的后台通行证
模态框的意思是:“这是一次对话。我们现在就要进行。“
Dialog(对话框)
Section titled “Dialog(对话框)”Dialog 是模态框的礼貌表亲。在 Mantine 中,它们本质上是同一个组件,但在 UI 设计理论中,对话框更小、更聚焦——一个问题和一个回答。“保存更改?“是 / 否。完成。
En Parlant~ 使用 ConfirmModal 来处理这类快速的是非选择时刻,比如确认你是否要放弃未保存的更改。
来去匆匆的那些
Section titled “来去匆匆的那些”Tooltip(提示气泡)
Section titled “Tooltip(提示气泡)”Tooltip 是当你将鼠标悬停在某个东西上时出现的小标签。它就是那个在你一脸困惑时低声告诉你答案的热心陌生人。
Tooltip 在 En Parlant~ 中无处不在。几乎悬停在任何图标按钮上你都会看到一个:
- 悬停在 ↻ 按钮上 → “重新加载”
- 悬停在被截断的文件路径上 → 显示完整路径
- 悬停在标注符号上 → 该符号的含义
- 悬停在分析面板中的一步棋上 → 弹出一个棋盘缩略预览
Tooltip 是最原始的”无需阅读手册”。
Popover(弹出层)
Section titled “Popover(弹出层)”Popover 是读过研究生的 tooltip。它不只是显示一个文本标签,而是可以包含任何内容——按钮、图片,甚至一个迷你棋盘。
在 En Parlant~ 中,当你悬停在引擎分析中的某一步棋上时,popover 会渲染该位置的棋盘预览。它是一个 tooltip,但自带了视觉辅助工具。
Menu(菜单)
Section titled “Menu(菜单)”Menu 是当你点击(或右键点击)某个东西时出现的操作列表。从计算机诞生之初你就在使用它们——文件、编辑、视图。你懂的。
En Parlant~ 有两种风味:
- 顶部栏菜单——经典的 文件 / 编辑 / 视图 菜单栏,使用 Mantine 的
Menu组件构建。快捷键列在右侧。一种老到已经可以领退休金的设计模式。 - 上下文菜单——在对局树中右键点击一步棋,你会看到”提升变着”、“从此处删除”和”复制走法序列”等选项。这些使用
mantine-contextmenu库来增加额外的精致感。
菜单有自己的子词汇:Menu.Target(你点击的东西)、Menu.Dropdown(出现的列表)、Menu.Item(每个选项)和 Menu.Divider(那条表示”现在是不同区域了”的细线)。
组织空间的那些
Section titled “组织空间的那些”Stack 与 Group
Section titled “Stack 与 Group”它们是布局界的花生酱与果冻——天生一对。
- Stack 将元素垂直排列——一个叠在另一个上面。就像摞书。
- Group 将元素水平排列——并排放置。就像在架子上排列棋子。
它们各自出现在 100 多个文件中。它们是整个应用中最常用的组件。每当东西排列整齐时,背后都有 Stack 或 Group 在负责。
Flex 是 Stack 和 Group 的瑞士军刀表亲。当元素需要换行、拉伸、收缩、反转或其他不那么规矩的行为时,Flex 会以对 CSS Flexbox 模型的完全控制介入。
用于分析面板的走法列表——那些像文本一样换行流动的走法序列?那就是 Flex 在做重活。
Box 是一个戴着高礼帽的 <div>。它本身什么都不做——只是一个通用容器。但它接受 Mantine 的样式属性,这使它成为”我需要在这个东西外面包一层东西”时极其趁手的包装器。
Box 出现在 150 多个文件中。它是组件库的万能胶带。
Center
Section titled “Center”功能完全如其名。将某个东西放在正中央。水平居中。垂直居中。两者兼有。搞定。
出现在空状态中——当没有关联账号、没有加载数据库、还没有数据时。一个大图标,一条消息,Center 确保它们完美居中。没人喜欢偏心的空状态。
SimpleGrid
Section titled “SimpleGrid”一个简单的网格。你告诉它要几列,它就把你的卡片排成整齐的网格,并随窗口大小自动重排。数据库页面用它在响应式网格中排列数据库卡片。
ScrollArea
Section titled “ScrollArea”一个在内容溢出时可以滚动的容器。你可能觉得这应该是免费的——用 CSS overflow: auto 确实某种程度上是——但 ScrollArea 添加了样式化的滚动条、惯性滚动和溢出检测。它包裹着对局树面板、分析行,以及任何内容可能超出其容器高度的地方。
AppShell
Section titled “AppShell”AppShell 是总布局——整个应用的骨架。它定义了头部在哪里、侧边栏在哪里、主要内容区域在哪里。其他一切都嵌套在它里面。
你只使用它一次。它是房子的地基。
Portal
Section titled “Portal”Portal 是一个神奇的虫洞。它将一个组件渲染到 DOM 树中其正常位置之外的地方——通常是文档的根部。这就是弹出层和棋盘预览如何避免被带有 overflow: hidden 的父容器裁切的。
你永远看不到 Portal 本身。你只能看到它的效果。它是 UI 组件中的幕后工作人员。
你与之交互的那些
Section titled “你与之交互的那些”Button(按钮)
Section titled “Button(按钮)”一个可点击的矩形,里面有文字。这个你认识。在 Mantine 中,按钮有多种变体(variants):
- filled——纯色填充,高强调。“点我,我很重要。”
- outline——只有边框。“我是一个选项,但没有压力。”
- subtle——若隐若现。“如果你需要我,我就在这里。”
- default——中性的中间地带。
En Parlant~ 在 150 多个文件中使用了所有这些变体。最常见的模式:模态框底部的一个 Group,放着两个按钮——“取消”(subtle)和”确认”(filled)。经典。
ActionIcon
Section titled “ActionIcon”ActionIcon 是一个放弃了文字标签、全身心投入图标生活方式的按钮。它紧凑、方形,依靠图标(加上一个 tooltip)来传达其用途。
工具栏按钮、面板控件、关闭按钮——全都是 ActionIcon。它们是提供操作的最节省空间的方式。
Switch(开关)
Section titled “Switch(开关)”Switch 是一个看起来像小灯开关的切换控件。往左拨,往右拨。开。关。
在设置中,开关控制二元选择:
- 声音 开/关
- TTS 旁白 启用/禁用
- 深色模式 / 浅色模式
这个物理隐喻好到连你的祖父母都能理解。
Slider(滑块)
Section titled “Slider(滑块)”Slider 是一条带有可拖动手柄的轨道,用来选择一个值。就像一个被压扁的音量旋钮。
En Parlant~ 使用滑块用于:
- 字体大小——拖动调整
- 音量——拖动调整(当然)
- CPU 核心数——引擎应使用多少核心?
- 哈希内存——引擎的哈希表应使用多少内存?
还有一个 RangeSlider,带有两个手柄用于选择范围——用于按 Elo 等级分筛选对局(例如 2000–2200)以及时间段筛选。
SegmentedControl(分段控件)
Section titled “SegmentedControl(分段控件)”一行按钮,同一时间只能选中一个。就像汽车收音机的预设按钮——按下一个,其他的就弹出来。
用于在以下选项之间切换:
- 分析图表上的厘兵评估 vs. 胜/和/负百分比
- 不同的走法记谱格式
- 引擎分析行数
Checkbox 与 Select(复选框与下拉选择)
Section titled “Checkbox 与 Select(复选框与下拉选择)”这些你都认识。Checkbox 用于”勾选所有适用的”,Select 下拉菜单用于”从列表中选一个”。它们出现在整个应用的设置、筛选和表单中。清除数据模态框有一组特别令人满足的复选框,你可以在其中选择自己的毁灭方式。
Autocomplete(自动补全)
Section titled “Autocomplete(自动补全)”一个在你输入时建议补全内容的文本输入框。数据库浏览器中的棋手搜索使用了它——开始输入名字,匹配的棋手就会出现在下方。
FileInput 与 DateInput(文件输入与日期输入)
Section titled “FileInput 与 DateInput(文件输入与日期输入)”FileInput 在点击时打开文件选择器——用于导入模态框中加载 PGN 文件。DateInput 弹出一个日历用于选择日期——用于按日期筛选对局。还有 MonthPickerInput 和 YearPickerInput,用于不需要那么精确的场景(Lichess 数据库按月筛选,Masters 数据库按年筛选)。
展示进度的那些
Section titled “展示进度的那些”Progress Bar(进度条)
Section titled “Progress Bar(进度条)”一个从左到右填充的水平条,显示某件事进行到哪了。用于应用更新(下载进度),以及——更具创意地——在分析面板中显示胜/和/负概率。三个彩色段(白、灰、黑)按比例填充进度条。它是一个伪装成进度指示器的迷你条形图。
Loader(加载器)
Section titled “Loader(加载器)”Loader 是一个旋转动画,意思是”我正在处理,等我一下。“在数据库加载、棋手统计数据获取中,或任何异步操作进行时出现。
Skeleton(骨架屏)
Section titled “Skeleton(骨架屏)”Skeleton 是一个灰色占位符,模拟尚未加载完成的内容形状。不是空白屏幕或转圈,而是你看到卡片和文字将要出现位置的幽灵轮廓。它是加载状态的建筑蓝图——“真正的家具正在路上。”
数据库页面在卡片网格加载时使用骨架屏。比单独一个转圈优雅得多。
LoadingOverlay(加载遮罩)
Section titled “LoadingOverlay(加载遮罩)”一层半透明的覆盖层,在组件加载时铺在上面。分析图表使用了它——你仍然可以看到遮罩后面的图表,但它变暗了,上面还有一个转圈。“我还在,只是在更新。“
标注事物的那些
Section titled “标注事物的那些”Badge(徽章)
Section titled “Badge(徽章)”Badge 是一个小的彩色标签——就像会议上的名牌。在 En Parlant~ 中,SpeedBadge 组件使用徽章来显示带颜色编码的时间控制:
- 🩷 粉色 — UltraBullet(超超快棋)
- 🔴 红色 — Bullet(超快棋)
- 🟠 橙色 — Blitz(快棋)
- 🟢 绿色 — Rapid(快速)
- 🔵 蓝色 — Classical(慢棋)
- 🟣 紫色 — Correspondence(通信棋)
每个徽章都小巧、多彩,让人一眼就能看出对局速度。
keyboard 的缩写。以那种标志性的凸起按键样式显示一个键盘按键。在快捷键设置中用来展示类似 Ctrl + Z 的内容。它是用 CSS 重现的你键盘上的实体按键。令人愉悦的拟物设计。
分隔与折叠的那些
Section titled “分隔与折叠的那些”Divider(分隔线)
Section titled “Divider(分隔线)”一条水平线,用来分隔不同区域。就是这样。它是一条线。但它是一条有样式的线,还可以在中间带一个文本标签——比如导入模态框中的”— OR —“分隔线,将”粘贴 PGN”和”输入 FEN”分开。
30 多个文件使用了 Divider。事实证明,很多东西需要被分隔。
Collapse(折叠)
Section titled “Collapse(折叠)”Collapse 是一个可以平滑展开和收起的区域。点击展开,点击隐藏。数据库筛选选项使用了它——你并不总是需要看到每个筛选条件,所以它们藏在 Collapse 后面,等你需要时再展开。
RichTextEditor(富文本编辑器)
Section titled “RichTextEditor(富文本编辑器)”一个完整的所见即所得文本编辑器,基于 TipTap 构建。加粗、斜体、列表,应有尽有。用于撰写对局注释——你为解释某步棋为何精妙或糟糕而添加的评论笔记。
这是唯一一个更像复合有机体而非单个细胞的基础组件。底层是 TipTap + ProseMirror + Mantine 样式。但从用户的角度看,它只是一个格式功能能正常工作的文本框。
AreaChart(面积图)
Section titled “AreaChart(面积图)”来自 @mantine/charts(它封装了 Recharts),AreaChart 驱动着评估曲线图——分析面板底部那条起伏的波形线。中线以上的白色区域表示白方占优;下方的黑色区域表示黑方占优。
你可以点击图表上的任意一点跳转到对应的那步棋。交互式数据可视化遇上了国际象棋分析。它甚至有自定义 tooltip 和标记对局阶段的参考线(开局 → 中局 → 残局)。
DataTable(数据表格)
Section titled “DataTable(数据表格)”通过 mantine-datatable 库——一个功能齐全的数据表格,支持排序、分页、行选择和筛选。驱动着数据库浏览器的对局列表、棋手列表和锦标赛列表。它是一个知道自己身在国际象棋应用中的电子表格。
有些基础组件本身不渲染任何可见内容,但让其他一切得以运作:
- MantineProvider — 包裹整个应用,提供主题(颜色、深色模式、组件默认值)
- Portal — 将渲染内容传送到 DOM 的另一个位置
- useHotkeys — 一个 hook(不是组件),用于绑定键盘快捷键
- useForm — 管理表单状态、验证和错误处理
- useDebouncedValue — 等你停止输入后再触发搜索。一个有礼貌的 hook。
作为参考,以下是 En Parlant~ 中使用的每一类基础组件,按功能分组:
| 类别 | 基础组件 |
|---|---|
| 布局 | AppShell, Box, Center, Flex, Group, Stack, SimpleGrid, ScrollArea, Portal |
| 表面 | Card, Paper |
| 覆盖层 | Modal, Tooltip, Popover, Menu, ContextMenu, LoadingOverlay |
| 通知 | Toast(通过 @mantine/notifications) |
| 按钮 | Button, ActionIcon, CloseButton, ThemeIcon |
| 输入 | TextInput, Textarea, NumberInput, Select, Autocomplete, Checkbox, Switch, Slider, RangeSlider, SegmentedControl, FileInput, DateInput, MonthPickerInput, YearPickerInput |
| 数据展示 | Table, DataTable, Badge, Code, Kbd, Rating, Image |
| 排版 | Text, Title, Anchor |
| 进度 | Progress, Loader, Skeleton |
| 结构 | Divider, Collapse |
| 富内容 | RichTextEditor (TipTap), AreaChart (Recharts) |
| 主题 | MantineProvider, createTheme, useColorScheme |
| Hooks | useForm, useHotkeys, useToggle, useDebouncedValue, useClickOutside, useElementSize |
所有组件均来自 Mantine v8 及其生态系统包:@mantine/core、@mantine/notifications、@mantine/dates、@mantine/charts、@mantine/form、@mantine/tiptap、mantine-datatable 和 mantine-contextmenu。
现在你知道了
Section titled “现在你知道了”下次你看到一条小消息从屏幕角落滑上来——那是 toast。围绕对局预览的那个矩形——那是 card。内容加载前闪烁的灰色色块——那些是 skeleton。
一旦你学会了这些名字,你就会开始到处看到它们。每个应用。每个网站。同样的二十来个基础组件,以一千种不同的方式重新排列组合。
一路到底,全是乐高。