ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Univer @univerjs/design 设计系统解析:组件、样式令牌与本地化资源的实现内幕

Univer @univerjs/design 设计系统解析:组件、样式令牌与本地化资源的实现内幕 Univer univerjs/design 设计系统解析组件、样式令牌与本地化资源的实现内幕【免费下载链接】univerUniver is a full-stack framework for creating and editing spreadsheets / word processor / presentation on both web and server.项目地址: https://gitcode.com/GitHub_Trending/un/univeruniverjs/design是 Univer 全栈文档/表格/演示套件中的共享设计系统包它为 Univer 的所有 UI 包如univerjs/ui、univerjs/sheets-ui提供 React 组件、Tailwind 样式令牌Design Tokens和多语言本地化资源。阅读本文你将理解该包的包结构、安装与引用方式、Tailwind 前缀隔离与 CSS 变量主题机制、组件变体的cva实现模式以及如何通过ConfigProvider注入 locale 与方向从而在自己的业务中正确消费或扩展 Univer 的设计系统。包概览它是什么不是什么根据 packages/design/README.md 的定义该包提供的是shared React design components, tokens, styles, and locale resources used by Univer UI packages即内容说明仓库位置React 组件Button、Dialog、ColorPicker、Tree、VirtualList 等约 40 个组件src/components样式令牌基于 CSS 变量--univer-*的 Tailwind 主题common/shared/tailwind/tailwind.config.ts全局样式Tailwind 入口global.csstailwind base/components/utilities三行指令src/global.css本地化资源18 个语言包en-US、zh-CN、ja-JP、de-DE 等src/localeUMD 全局变量打包后暴露UniverDesignpackage.jsonREADME 中的 Package Overview 表格指出该包带有 CSS 与 Locales但不暴露 Facade 入口Facade entry: No——它不是面向终端用户的编程门面而是内部 UI 包的设计基座。从 package.json 的exports字段可以看到它的模块解析策略开发态workspace 内直接指向./src发布态publishConfig则同时提供 ESlib/es、CJSlib/cjs与类型声明lib/types三套产物locale 子路径单独映射为./locale/* → src/locale/*.ts。这就是 README 用法中univerjs/design/locale/en-US这类子路径导入能够生效的原因。安装与版本约束安装方式继承自 packages/design/README.mdpnpm add univerjs/design # or npm install univerjs/designREADME 同时强调一条重要约束所有univerjs/*包必须保持同一版本。原因是这些包之间存在强耦合——从依赖关系看各 UI 包如packages/sheets-ui、packages/ui都会依赖本包的组件与样式令牌版本不一致会导致 Tailwind 类名、CSS 变量、组件 API 错位。React 兼容性以 package.json 的peerDependencies为准{ react: ^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc, react-dom: ^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc }即支持 React 16.9 至 19含 19 RC。仓库内 devDependencies 固定使用 React 18.3.1 进行开发与测试。使用方式CSS 与 locale 的引用路径README 给出的最小用法import univerjs/design/lib/index.css; import DesignEnUS from univerjs/design/locale/en-US;第一条导入lib/index.css即构建产物中的全局样式。其源文件 src/global.css 内容极简——只有tailwind base; tailwind components; tailwind utilities;三条指令实际样式由 Tailwind 在构建期扫描组件源码生成。第二条按子路径导入语言包。以 src/locale/en-US.ts 为例它导出的对象结构为locale.design下的若干组件词条const locale { design: { Accessibility: { close: Close, previous: Previous, next: Next, /* ... */ }, Confirm: { cancel: cancel, confirm: ok }, CascaderList: { empty: None }, Calendar: { year: , weekDays: [Sun, Mon, /* ... */], months: [Jan, Feb, /* ... */], ariaLabels: { previousMonth: Previous month, /* ... */ }, }, ColorPicker: { more: More Colors, cancel: cancel, confirm: ok }, GradientColorPicker: { linear: Linear, radial: Radial, /* ... */ }, }, };从源码结构看设计系统只翻译通用组件层的词条日历、确认框、取色器、无障碍 aria 标签等而业务级词条如电子表格的菜单文本由各业务包sheets-ui、docs-ui 等自行维护——这解释了为何本包的 locale 文件比packages/sheets-ui等包的 locale 文件短得多。语言覆盖包括 en-US、zh-CN、zh-TW、zh-HK、ja-JP、ko-KR、de-DE、fr-FR、ru-RU、es-ES、pt-BR、ar-SA、fa-IR、id-ID、it-IT、pl-PL、sk-SK、vi-VN 共 18 种。样式令牌体系Tailwind 前缀 CSS 变量设计系统可换肤的核心在于两层机制。1.univer-前缀隔离共享 Tailwind 预设 common/shared/tailwind/tailwind.config.ts 中设置了const config: OmitConfig, content { prefix: univer-, darkMode: selector, corePlugins: { preflight: false, // 关键不注入浏览器默认样式重置 }, /* ... */ };prefix: univer-意味着所有工具类都编译为univer-*形态如组件源码中的univer-flex、univer-text-sm。这避免了 Univer 组件库样式与宿主应用的 Tailwind/全局样式互相污染——嵌入电子表格、文档编辑器的业务页面通常自带 CSS前缀隔离是可嵌入的关键设计。preflight: false关闭了 Tailwind 的浏览器预置重置进一步减少对外部页面的副作用。darkMode: selector表示暗色模式通过在祖先节点添加类名配合dark:变体启用而非跟随系统。packages/design/tailwind.config.ts 在此基础上引入共享预设并附加tailwindcss-animate插件import preset from univerjs-infra/shared/tailwind; import animate from tailwindcss-animate; const config: Config { presets: [preset], content: [./src/**/*.{js,ts,jsx,tsx}], plugins: [animate], };值得注意的是共享预设导出的createTailwindContent函数它会自动扫描当前包dependencies中所有带src/global.css的univerjs*包把它们的源码加入 Tailwind content 扫描范围。从源码结构看这是为了让各 UI 包构建自己的样式产物时也能覆盖到依赖包中出现的类名保证按需打包场景下样式不缺失。2.--univer-*CSS 变量主题令牌共享预设的theme.extend.colors将全部色板映射为 CSS 变量colors: { primary: { 50: var(--univer-primary-50), /* 100~900 */ }, gray: { 0: var(--univer-gray-0), 50: var(--univer-gray-50), /* ... */ }, blue: { /* var(--univer-blue-*) */ }, red: { /* var(--univer-red-*) */ }, /* orange / yellow / green / jiqing / indigo / purple / pink */ }工具类univer-bg-primary-600最终解析为background-color: var(--univer-primary-600)而变量具体取什么颜色由宿主在运行时通过:root或.dark选择器注入。这意味着改主题不改代码只要在页面定义好--univer-primary-600等变量即可换肤无需重新构建组件库暗色模式同理通过dark:变体在类名中引用另一组变量值。预设同时扩展了boxShadowsm/md/lg/xl/2xl五档基于rgba(30, 40, 77, ...)的柔和阴影与tailwind-scrollbar滚动条插件。Storybook 中的 Design Token 页面见文首图片正是对这些令牌的可视化展示。组件实现模式cva 变体 前缀化 clsx以 src/components/button/Button.tsx 为典型样本可以概括设计系统组件的三个通用套路。套路一class-variance-authority定义变体矩阵。Button 用cva声明了 6 种视觉变体 × 4 种尺寸export const buttonVariants cva( univer-box-border univer-inline-flex univer-cursor-pointer /* ...基础类 */, { variants: { variant: { default: univer-border-gray-200 univer-bg-gray-0 univer-text-gray-700 /* ... */, primary: univer-border-primary-600 univer-bg-primary-600 /* ... */, danger: univer-border-red-500 univer-bg-red-500 /* ... */, text: /* 无底纹的文字按钮 */, link: /* 下划线链接样式 */, ghost: /* 透明底 hover 灰底 */, }, size: { icon: univer-size-8 !univer-p-0, small: univer-h-6 univer-rounded-md univer-px-1.5 univer-text-xs, middle: univer-h-8 univer-rounded-lg univer-px-2 univer-text-sm, large: univer-h-10 univer-rounded-lg univer-px-3 univer-text-sm, }, }, defaultVariants: { variant: default, size: middle }, } );注意变体类名中大量出现dark:!univer-bg-gray-600这类写法——感叹号表示 Tailwind!important用于在暗色选择器下覆盖浅色态类。套路二Radix UI 原语 asChild组合。组件asChild时改用 Radix 的Slot让Button asChilda href....../a/Button渲染为a而非buttonexport const Button forwardRefHTMLButtonElement, IButtonProps( ({ className, variant, size, asChild false, ...props }, ref) { const Comp asChild ? Slot : button; return ( Comp className{clsx(buttonVariants({ variant, size, className }))} ref{ref} >import { clsx as cn } from clsx; import { extendTailwindMerge } from tailwind-merge; const twMerge extendTailwindMerge({ prefix: univer- }); export function clsx(...inputs: ClassValue[]) { return twMerge(cn(inputs)); }tailwind-merge默认按无前缀类名去重冲突如p-2 p-4只保留后者。这里用extendTailwindMerge({ prefix: univer- })让它正确识别univer-p-2 univer-p-4这类前缀化类名保证组件对外暴露的className覆盖语义一致——外部传入的类在冲突时能稳定胜出。配置注入ConfigProvider 与挂载容器组件级全局配置通过 src/components/config-provider/ConfigProvider.tsx 下发export interface IConfigProviderProps { children: ReactNode; locale?: any; direction?: ltr | rtl; mountContainer: HTMLElement | null; } export function ConfigProvider(props: IConfigProviderProps) { const { children, locale, mountContainer, direction } props; const value useMemo(() ({ locale, direction, mountContainer }), [locale, direction, mountContainer]); return ( ConfigContext.Provider value{value} DirectionProvider dir{direction ?? ltr} {children} /DirectionProvider /ConfigContext.Provider ); }三个注入点各有用途locale透传上文的语言包对象组件Calendar、Confirm、ColorPicker 等通过ConfigContext读取locale.design.*词条direction借助radix-ui/react-direction的DirectionProvider支持 RTL 布局默认ltrmountContainer指定 Dialog、Tooltip、Message 等脱离组件树渲染的浮层挂载节点。默认值由ConfigContext初始化时给出isBrowser() ? document.body : null见 src/helper/is-browser.ts即 SSR/Node 环境下为null浏览器环境下挂到document.body。从源码结构看Dialogsrc/components/dialog、Toaster基于sonner、Message 等组件正是依赖这一上下文来决定 teleport 目标从而支持多实例 Univer 共存时把浮层挂到各自容器内。组件清单与测试覆盖入口文件 src/index.ts 是完整的 API 清单按功能域可归纳为功能域组件备注反馈Dialog、Confirm、Popup、Toast/Toaster、MessageConfirm 由 Dialog 派生Toaster 封装sonner表单输入Input、Textarea、InputNumber、Checkbox(Group)、Radio(Group)、Switch、Select/MultipleSelect、Segmented、TimeInput提供I*Props类型导出日期时间Calendar、DatePicker、DateRangePicker文案取自 locale 的Calendar词条色彩ColorPicker、ColorPresets、GradientColorPicker含AlphaSlider/HueSlider/ColorSpectrum子件与color-conversion.ts纯函数导航/结构Dropdown、DropdownMenu、HoverCard、CascaderList、SelectList、Tree、Accordion、PanelTree 额外导出filterLeafNode、findNodePathFromTree等树算法工具展示Avatar、Badge、KBD、Gallery、Pager、Separator、Tooltip、VirtualList、DraggableListGallery 支持缩放/轮播文案取自Accessibility词条命令面板Command、CommandInput、CommandItem等基于cmdk工具导出clsx、isBrowser、render/unmount、cva、borderClassName等边框类名常量供其他 UI 包复用质量保障方面几乎每个组件目录都含__tests__/index.spec.tsx配合testing-library/react做行为测试ColorPicker 有独立拆分的功能测试如color-conversion.spec.ts颜色格式转换纯函数、alpha-and-input.spec.tsx、hue-slider-extra.spec.tsx每个组件另配*.stories.tsx用于 Storybook 预览文首截图即 Storybook 中的 Design Token 面板包内脚本见 package.jsonpnpm test跑 vitestpnpm typecheck做tsc --noEmitpnpm build串联univer-cli build产物打包与tsc -p tsconfig.node.json类型声明。小结如何在业务中正确消费该设计系统保持版本一致与所有univerjs/*包锁定同一版本样式引用import univerjs/design/lib/index.css发布产物或在 monorepo 内走源码切勿绕过 Tailwind 前缀体系手写univer-*冲突类名——clsx的 tailwind-merge 逻辑只处理规范的前缀化类名主题定制优先定义--univer-*CSS 变量primary/gray/语义色 五档 shadow而不是覆写组件类暗色模式用dark:选择器机制本地化与方向用ConfigProvider注入locale按语言从univerjs/design/locale/xx-XX导入与direction多实例场景通过mountContainer控制浮层挂载点避免多实例间 Dialog/Tooltip 串扰。相关延伸阅读主工程入口 packages/core/src/univer.ts、UI 适配层 packages/ui-adapter-vue3 与 packages/ui-adapter-web-component可进一步理解设计系统组件如何被上层框架消费。【免费下载链接】univerUniver is a full-stack framework for creating and editing spreadsheets / word processor / presentation on both web and server.项目地址: https://gitcode.com/GitHub_Trending/un/univer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表