4.0 KiB
4.0 KiB
L6 · 公共 UI 组件库(@aso/plugin-sdk/ui)
前置:L4 React 组件。约 15 分钟。 目标:不写一行样式代码,做出与宿主完全一致的界面;等待/失败/空态不再自造。
为什么必须用它
DESIGN.md 定了「长什么样」,组件库把它变成「直接拿来的代码」:
- token 同源:组件内部只用宿主已核验的类词汇(
text-ink-3、rounded-control…) 与var(--token)内联变量,深浅色自动跟随宿主主题 - 三态统一:等待
LoadingState/ 失败ErrorState(可重试)/ 空EmptyState, 任何插件的加载体验与宿主一致 - 零依赖:
./ui是 sdk 源码子路径,随 esbuild 一起打进 renderer.mjs, 不引任何第三方 UI 库(shadcn/ui 的思路——无运行时、复制即用——由宿主承担)
导入与构建
import { Button, LoadingState, ErrorState, EmptyState, SettingRow, Switch } from '@aso/plugin-sdk/ui'
构建命令与 L4 完全相同——ui 随 sdk 源码一起被打包,react 别名照旧:
esbuild src/renderer.tsx --bundle --format=esm --outfile=renderer.mjs \
--jsx=automatic --alias:react=./shims/react.js --alias:react/jsx-runtime=./shims/jsx-runtime.js
组件目录
| 组件 | 用途 | 要点 |
|---|---|---|
Spinner |
行内旋转指示 | size 默认 14px |
LoadingState |
区块等待态 | label 可换文案,默认「加载中…」 |
ErrorState |
区块失败态 | title/description(错误消息)、onRetry 提供即显示重试按钮 |
EmptyState |
区块空态 | title + description + action(如「去设置」按钮) |
Button |
按钮 | variant: 'primary' | 'ghost' | 'danger';loading 置灰并显示 Spinner,防重复提交 |
Input / Textarea |
输入 | 与宿主设置页同款外观 |
Select |
下拉 | options: { value, label }[],原生 select |
Switch |
开关 | 受控 checked/onCheckedChange,或非受控 defaultChecked |
Card |
卡片容器 | rounded-card + line 边框 |
SettingRow |
设置行 | label 左 / 控件右(DESIGN §4 标准模式) |
Badge |
徽标 | tone: 'neutral' | 'accent' | 'danger',版本/来源/状态元信息 |
全部组件接受 className 追加布局类(flex/间距/尺寸),不接受颜色/字号覆写——
那两条路(hex、新字号)被 L7 检验器拦截。
三态铁律
任何异步区块的状态机:loading → data | error | empty,四选一,不允许第五种 (白屏、裸文本「加载中」、无反馈的失败)。模板(plugin-template)是完整示范:
{error !== null ? (
<ErrorState title="数据加载失败" description={error} onRetry={() => setTick((t) => t + 1)} />
) : items === null ? (
<LoadingState label="正在拉取数据…" />
) : items.length === 0 ? (
<EmptyState title="暂无数据" description="检查数据源后点击重新加载" />
) : (
items.map((it) => <SettingRow key={it} label={it} />)
)}
配套铁律:异步调用必须 withTimeout/withRetry(见 L7),否则 loading 态可能永远挂着。
宿主兜底
renderer 模块的读源与 import 宿主各限时 8 秒:超时或失败时,插件槽位自动呈现宿主侧 「插件界面装载失败」ErrorState(带重试按钮);组件渲染期崩溃由 PluginErrorBoundary 兜底。也就是说装载失败不需要插件自己处理,插件只需管好自己组件内部的异步。
边界
- 仅 renderer 组件槽位可用(渲染在宿主 DOM,复用宿主样式表);iframe 插件页是物理 隔离文档,请按 DESIGN.md §4 的 HTML 模式自行实现
- 需要新形态时:先用现有组件拼;确实不够,在 DESIGN.md 约束内组合 token, 并把新形态沉淀回团队公共代码,而不是留在单个插件里