- 数据库
- 后端
- 前端
- 协作
【免费下载链接】mathesar
An intuitive spreadsheet-like interface that lets users of all technical skill levels view, edit, query, and collaborate on Postgres data directly. 100% open source and self hosted, with native Postgres access control.
Mathesar 前端的全部可复用 UI 能力都沉淀在mathesar_ui/src/component-library/目录中。本文基于该目录的 README 开发指南,结合仓库源码逐项印证其三条核心约定(代码不越界导入、样式自包含、全局静态类名),并解析组件库的目录组织、公共 API 出口、设计令牌(CSS 变量)体系,以及 Storybook 被退役后遗留的组件 playbook 现状,帮助你在该组件库中新增组件时遵循既定规范、不破坏其"未来可拆分为独立包"的设计目标。
组件库定位:一个计划独立成包的设计系统
README 对目录的定义只有一句话,但信息量足够:
This directory contains the components and design system used by the Mathesar front end. This will be spun off into a package of it's own at a later point in time.
即该目录同时承担组件集合与设计系统两个角色,并且团队已明确规划将其拆分为独立发布的包(spun off into a package of it's own)。这一"未来自立门户"的目标,正是后文所有开发约定的出发点——只有做到目录自包含,拆分时才不会牵动应用主代码。
从源码结构看,组件库对外暴露三条清晰的路径:
- 值导出入口index.ts:按层次组织全部公共 API——工具函数(
common/utils、createDebounce、getPaginationPageCount)、Svelte actions(clickOffBounds、popper、portal、slider、focusTrap)、Simple Components(Button、Badge、Checkbox、Spinner等约 40 个)、Compound Components(Select、ListBox、Pagination、TabContainer等)以及 Systems(confirmation、dropdown、modal、toast、menu、form-builder等组合式系统)。 - 类型出口types.ts:通过
export *聚合common/utils、commonTypes及各个组件的*Types模块,构成完整的类型面,对应 tsconfig 中的@mathesar-component-library/types别名。 - 样式入口styles.scss:文件头注释明确写道"这是组件库样式系统的基础出口,待库移出主项目后可能自动化这一过程",目前它以
@import逐一列出common/styles/main.scss及各组件的静态.scss文件。
对应用主代码而言,使用方只需记住一个别名:@mathesar-component-library。例如 App.svelte 中import { Spinner } from '@mathesar-component-library'。
约定一:目录内的文件不得导入目录外的任何文件
README 开发指南的第一条:
Ensure that files within this directory do not import any file outside the scope of the directory.
这条规则的落点在于:库内部引用一律走@mathesar-component-library-dir/*别名,且该别名只映射到目录自身。tsconfig.json 中的路径配置为:
"paths": { "@mathesar/*": ["src/*"], "@mathesar-component-library-dir/*": ["src/component-library/*"], "@mathesar-component-library/types": ["src/component-library/types.ts"], "@mathesar-component-library": ["src/component-library/index.ts"] }其中@mathesar-component-library-dir/*前缀被刻意设计为仅指向src/component-library/——组件库内部文件互相引用时必须使用它(或相对路径),从机制上杜绝了写@mathesar/systems/...这类越界导入。而 vite.config.js 的getAlias()函数直接从 tsconfig 的paths逐条生成 Vite 的resolve.alias,保证开发服务器、构建与类型检查三者的别名完全一致:
function getAlias() { const alias = []; const { paths } = data.compilerOptions; Object.keys(paths).forEach((key) => { const find = (__dirname, key.replace('/*', '')); const replacement = path.resolve(paths[key][0].replace('/*', '')); alias.push({ find, replacement }); }); return alias; }对目录实际做导入面统计,可以验证该约定的执行状况:库内部文件(.ts/.svelte)的 import 源只有三类——
- npm 包:
svelte、svelte/store、svelte/action、@popperjs/core、flatpickr、dayjs、@fortawesome/free-solid-svg-icons、iter-tools、fast-diff等(测试文件另有vitest、@testing-library/svelte); - 库内部别名:
@mathesar-component-library-dir/common/utils、@mathesar-component-library-dir/button/Button.svelte、@mathesar-component-library-dir/icon/Icon.svelte等; - 相对路径:同目录或兄弟子目录间的引用。
没有出现任何指向src/component-library/之外的应用源码导入。这正是 README 要求的状态:一旦未来拆分发包,只需把package.json依赖从主工程同步过来,而不需要重构任何 import。
约定二:组件样式不得依赖目录外的样式
README 开发指南的第二条:
Ensure that the styles for the components do not depend on styles specified outside the scope of the directory.
样式自包含由"单一全局入口 + 全部样式文件位于目录内"两点保证:
- 全部样式源文件都在
component-library/内,公共部分集中在 common/styles/:variables.scss定义尺寸/字体等基础变量,themes/main.scss分发light/dark两套主题(颜色令牌如--color-bg-control、--color-fg-danger等),components/下还有input.scss、card.scss、canvas.scss等跨组件的基类。 - 应用侧的引入点是唯一且固定的:App.svelte 中有一行注释"The component-library style import would only ever be from App.svelte",随后
@import 'component-library/styles.scss';。也就是说组件库的样式不会分散注入,主应用代码也不会反向依赖库内某个具体 scss 文件。
约定三:全局样式 + 静态类名,而非自动生成的 scoped 类
README 开发指南的第三条:
All component styles should be global, the classes should be static and not auto-generated.
这意味着组件不使用 Svelte 的<style scoped>自动哈希类名,而是编写全局可寻址的静态类名,例如最简组件 Badge.svelte 全文仅两行——<span class="badge"><slot /></span>,其样式来自全局的badge/Badge.scss。
以 Button.scss 为典型,可以完整看到"全局静态类 + 设计令牌"的运作方式:
静态类名:基类
.btn,变体类.btn-{variant}(default、primary、secondary、outcome、plain、ghost、danger、action、tip、link),尺寸工具类.size-small/.size-medium/.size-large,布局工具类.full-width、.padding-compact等。变体名与 commonTypes.ts 中的 TypeScript 联合类型一一对应:export type Appearance = | 'default' | 'primary' | 'secondary' | 'plain' | 'ghost' | 'action' | 'outcome' | 'danger' | 'link' | 'custom' | 'tip'; export type Size = 'small' | 'medium' | 'large';状态矩阵:SCSS 用
$btn-states: (hover, focus, active)与$btn-variants两张映射表,把"变体 × 状态"展开为 CSS 变量的默认值,同时兼容伪类选择器(&:hover)与静态类选择器(&.hover)两种触发方式。CSS 变量分层:每个可配置属性生成分层变量,外部覆盖 > 内部默认:
background: var(--button-background, var(--button-internal-background)),状态层再套一层var(--button-hover-background, var(--button-background, var(--button-internal-hover-background)))。这使得调用方只需在行内 style 上设置--button-*即可定制任意变体任意状态的单个属性,而组件类名保持静态不变。主题切换零成本:因为取值全部落在
--color-*设计令牌上,common/styles/themes/下的 light/dark 主题只需换一组令牌即可整体换肤。
从源码结构看,"全局静态类名"这一约定还带来一个副作用:.stories/测试文件可以直接用字符串类名断言渲染结果(如 Button.test.ts 通过../Button.svelte相对导入被测组件),无需处理构建期生成的类名哈希。
目录组织:一个组件一个目录,元文件收敛在meta
README 没有细讲目录规范,但源码呈现出一致性很高的组织方式,以button/为例:
button/ ├── Button.svelte # 组件本体 ├── Button.scss # 该组件的全局静态样式(被 styles.scss 导入) └── __meta__/ ├── Button.stories.svelte # 遗留的组件 playbook 故事 ├── Button.test.ts # 组件测试 └── Button.mdx # 文档各组件目录(badge/、checkbox/、dropdown/、toast/、form-builder/等 60 余个)均遵循"本体 + 同名样式 +__meta__"的模式;跨组件共享的东西则统一收敛到 common/:
common/actions/:clickOffBounds.ts、focusTrap.ts、popper.ts、portal.ts、slider.ts五个 Svelte action 的通用实现;common/base-components/:BaseInput.svelte与BaseInputTypes.ts,供TextInput、TextArea、Select等输入类组件复用;common/styles/:变量、主题与跨组件基类(上文已述);common/utils/:约 30 个工具模块(CancellablePromise、ImmutableMap、storeUtils等),其中核心工具在__tests__/子目录配有 vitest 单测(如ImmutableMap.test.ts、filterUtils.test.ts)。
此外还有目录级的测试工具meta/utils/SlotTest.svelte,用于在测试中向任意 slot 注入带data-testid的标记内容,验证"插槽内容是否被正确渲染"这类与具体组件无关的通用断言。
Storybook 的退役:从 #3214 到组件 playbook 迁移
README 专设了 Storybook 小节,说明该库的组件展示方式正在切换:
- As of #3214, we no longer use Storybook for developing our components.
- Our Storybook stories will be migrated to a different component playbook, as tracked in #3215.
即自 PR #3214 起 Storybook 不再用于组件开发,原 stories 向另一套"component playbook"的迁移由 issue #3215 跟踪。仓库现状与这段描述吻合:
package.json中已无storybook相关脚本与依赖,工具链(vitest + vite.config.js 的test段)走的是 vitest/jest-dom 路线;- 但各组件
__meta__/下仍保留着 31 个*.stories.svelte遗留文件,它们仍import { Meta, Story } from '@storybook/addon-svelte-csf'(如 Button.stories.svelte)。从源码结构看,这些文件属于"已弃用、待迁移"的存量资产,可作为组件各变体的参照样例,但不再是可运行的开发入口——新组件不应再按 Storybook CSF 格式编写展示文件。
实操清单:在 component-library 中新增一个组件
综合 README 三条约定与源码现状,新增组件(例如MyControl/MyControl.svelte)应满足:
- 导入边界:只 import npm 依赖、
@mathesar-component-library-dir/*(或目录内相对路径);禁止出现@mathesar/...指向应用层代码的 import。 - 样式边界:新建
MyControl.scss,只引用common/styles/内的变量与主题令牌;类名使用静态全局名(如.my-control、.my-control-danger),不使用 scoped 自动类名。 - 样式注册:在 styles.scss 中追加
@import 'my-control/MyControl.scss';(当前该文件是手工维护的单一入口)。 - API 出口:在 index.ts 对应分区(Simple Components / Compound Components / Systems)追加导出;类型放入
MyControlTypes.ts并在 types.ts 中export type。 - 元文件:测试与展示文件放入
__meta__/,遵循库内现有__tests__的 vitest +@testing-library/svelte用法;不要再写.stories.svelte。 - 复用优先:输入类组件先评估能否基于
common/base-components/BaseInput.svelte扩展;图标用common/icons.ts聚合的 FontAwesome 图标与 Icon.svelte。
小结
Mathesar 的component-library用 README 中短短三条开发约定(不越界导入、样式自包含、全局静态类名)支撑起整个设计系统:@mathesar-component-library与@mathesar-component-library-dir双别名把导入边界固化在 tsconfig/Vite 层,styles.scss单一入口把样式边界收敛到文件清单,CSS 变量分层(外部覆盖 > 内部默认)则让"静态类名"依然保有完整的定制与换肤能力。配合按组件分目录、common/共享层与__meta__元文件的组织方式,以及 Storybook 退役后向新 playbook 迁移的过渡状态,这套约定使该目录在满足当前应用内使用之余,具备了低成本拆分为独立 npm 包的工程条件。
- 数据库
- 后端
- 前端
- 协作
【免费下载链接】mathesar
An intuitive spreadsheet-like interface that lets users of all technical skill levels view, edit, query, and collaborate on Postgres data directly. 100% open source and self hosted, with native Postgres access control.
相关推荐
MetaMask 组件库(Component Library)开发指南:基于 Box 的设计系统架构、多态 Props 与样式定制实践
MetaMask 组件库(Component Library)开发指南:基于 Box 的设计系统架构、多态 Props 与样式定制实践 导读 本文基于 Meta
区块链Web3金融科技nest-admin组件库文档:Storybook集成与组件开发规范
nest admin组件库文档:Storybook集成与组件开发规范 概述 nest admin是基于NestJs的企业级中后台管理系统解决方案,采用nestj
后端认证鉴权权限管理ljg-skills 仓库 CLAUDE.md 全解:面向 Claude Code 的自包含技能集架构、格式规范与开发维护指南
ljg skills 仓库 CLAUDE.md 全解:面向 Claude Code 的自包含技能集架构、格式规范与开发维护指南 本文以仓库根目录下的 CLAUD
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考