@soybeanjs/ui 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -69,6 +69,52 @@ provideAccordionUi(ui); // headless reads this via useAccordionUi()
69
69
  - **`ConfigProvider`** — sets global `dir`, `locale`, `nonce`, and default `tooltip` config for the entire component tree, including RTL layout switching
70
70
  - **`cn()`** — Tailwind-aware class merge (`clsx` + `tailwind-merge`), used for conflict-free class composition
71
71
 
72
+ ### Locale Support
73
+
74
+ `ConfigProvider` supports the following locale bundles:
75
+
76
+ | Code | Language |
77
+ | ------- | ------------------- |
78
+ | `zh-CN` | Simplified Chinese |
79
+ | `zh-TW` | Traditional Chinese |
80
+ | `en` | English |
81
+ | `ar` | Arabic |
82
+ | `ja` | Japanese |
83
+ | `ko` | Korean |
84
+ | `de` | German |
85
+ | `fr` | French |
86
+ | `es` | Spanish |
87
+ | `pt-BR` | Portuguese (Brazil) |
88
+ | `ru` | Russian |
89
+ | `tr` | Turkish |
90
+ | `id` | Indonesian |
91
+
92
+ Only `en` and `zh-CN` are pre-registered by default. `registerLocale` supports two registration styles:
93
+
94
+ - Pass a `LocaleRegistry` object. Built-in locale files from `@soybeanjs/headless/locale/{code}` already export this shape, including `dir` metadata.
95
+ - Pass a locale key plus `LocaleMessages` for a lightweight custom locale.
96
+
97
+ The shorthand `registerLocale(key, messages)` form uses the key as the locale name and falls back to `ltr`. Use the object form when you need explicit metadata such as `rtl`.
98
+
99
+ ```ts
100
+ import { en, registerLocale } from '@soybeanjs/headless/locale';
101
+ import type { LocaleMessages } from '@soybeanjs/headless/locale';
102
+ import ar from '@soybeanjs/headless/locale/ar';
103
+
104
+ registerLocale(ar);
105
+
106
+ const customMessages: LocaleMessages = {
107
+ ...en.messages,
108
+ pagination: {
109
+ ...en.messages.pagination,
110
+ nextPage: 'Next →',
111
+ prevPage: '← Prev'
112
+ }
113
+ };
114
+
115
+ registerLocale('custom', customMessages);
116
+ ```
117
+
72
118
  ### Package Exports
73
119
 
74
120
  **@soybeanjs/headless** ships fine-grained sub-paths:
@@ -96,13 +142,13 @@ import '@soybeanjs/ui/styles.css'; // pre-built UnoCSS stylesheet
96
142
  If you contribute new public components, exports, or API descriptions, keep generated surfaces in sync through the official scripts instead of editing generated files by hand.
97
143
 
98
144
  ```bash
99
- pnpm gen:headless # sync headless component names and namespaced exports
100
- pnpm gen:ui # sync ui component names
101
- pnpm gen:api # regenerate docs api json and locale baseline data
102
- pnpm gen:api:i18n # refresh api locale template data only
103
- pnpm gen:changelog # regenerate docs changelog json and locale baseline data
104
- pnpm translate:api:i18n -- --locale zh-CN
105
- pnpm translate:changelog:i18n -- --locale zh-CN
145
+ pnpm sui headless # sync headless component names and namespaced exports
146
+ pnpm sui ui # sync ui component names
147
+ pnpm sui api # regenerate docs api json and locale baseline data
148
+ pnpm sui api-locales # refresh api locale template data only
149
+ pnpm sui changelog # regenerate docs changelog json and locale baseline data
150
+ pnpm sui api-translate -- --locale zh-CN
151
+ pnpm sui changelog-translate -- --locale zh-CN
106
152
  ```
107
153
 
108
154
  The docs site now renders component docs through `UsageCode`, `PlaygroundGallery`, and `ComponentApi`. Component detail pages and `/releases` also read generated changelog data from `docs/src/generated/changelog/` and `docs/src/generated/changelog-locales/`.
package/README.zh-CN.md CHANGED
@@ -69,6 +69,52 @@ provideAccordionUi(ui); // headless 通过 useAccordionUi() 读取
69
69
  - **`ConfigProvider`** — 全局设置 `dir`、`locale`、`nonce` 及默认 `tooltip` 配置,应用于整个组件树,并支持 RTL 布局切换
70
70
  - **`cn()`** — Tailwind 感知的类名合并工具(`clsx` + `tailwind-merge`),解决类名冲突
71
71
 
72
+ ### 语言支持
73
+
74
+ `ConfigProvider` 当前支持以下 locale 文案包:
75
+
76
+ | 代码 | 语言 |
77
+ | ------- | ------------ |
78
+ | `zh-CN` | 简体中文 |
79
+ | `zh-TW` | 繁體中文 |
80
+ | `en` | 英语 |
81
+ | `ar` | 阿拉伯语 |
82
+ | `ja` | 日语 |
83
+ | `ko` | 韩语 |
84
+ | `de` | 德语 |
85
+ | `fr` | 法语 |
86
+ | `es` | 西班牙语 |
87
+ | `pt-BR` | 巴西葡萄牙语 |
88
+ | `ru` | 俄语 |
89
+ | `tr` | 土耳其语 |
90
+ | `id` | 印度尼西亚语 |
91
+
92
+ 默认只有 `en` 和 `zh-CN` 会被预注册。`registerLocale` 支持两种注册方式:
93
+
94
+ - 直接传入 `LocaleRegistry` 对象。`@soybeanjs/headless/locale/{code}` 导出的内置语言文件已经是这种结构,并且自带 `dir` 元数据。
95
+ - 传入 locale key 和 `LocaleMessages`,用于快速注册一个轻量自定义语言。
96
+
97
+ 简写形式 `registerLocale(key, messages)` 会将 key 作为语言名,并在未显式提供方向时回退到 `ltr`。如果你需要像 `ar` 这样的 `rtl` 元数据,请优先使用对象形式。
98
+
99
+ ```ts
100
+ import { en, registerLocale } from '@soybeanjs/headless/locale';
101
+ import type { LocaleMessages } from '@soybeanjs/headless/locale';
102
+ import ar from '@soybeanjs/headless/locale/ar';
103
+
104
+ registerLocale(ar);
105
+
106
+ const customMessages: LocaleMessages = {
107
+ ...en.messages,
108
+ pagination: {
109
+ ...en.messages.pagination,
110
+ nextPage: '下一页 →',
111
+ prevPage: '← 上一页'
112
+ }
113
+ };
114
+
115
+ registerLocale('custom', customMessages);
116
+ ```
117
+
72
118
  ### 包导出
73
119
 
74
120
  **@soybeanjs/headless** 提供精细化子路径导出:
@@ -96,13 +142,13 @@ import '@soybeanjs/ui/styles.css'; // 预构建的 UnoCSS 样式表
96
142
  如果您在仓库内新增公共组件、调整导出入口或修改 API 描述,请通过官方脚本同步生成产物,而不是手动编辑生成文件。
97
143
 
98
144
  ```bash
99
- pnpm gen:headless # 同步 headless 组件名称与命名空间导出
100
- pnpm gen:ui # 同步 ui 组件名称
101
- pnpm gen:api # 重新生成 docs api json 与 locale 英文基线数据
102
- pnpm gen:api:i18n # 仅刷新 api locale 模板数据
103
- pnpm gen:changelog # 重新生成 docs changelog json 与 locale 英文基线数据
104
- pnpm translate:api:i18n -- --locale zh-CN
105
- pnpm translate:changelog:i18n -- --locale zh-CN
145
+ pnpm sui headless # 同步 headless 组件名称与命名空间导出
146
+ pnpm sui ui # 同步 ui 组件名称
147
+ pnpm sui api # 重新生成 docs api json 与 locale 英文基线数据
148
+ pnpm sui api-locales # 仅刷新 api locale 模板数据
149
+ pnpm sui changelog # 重新生成 docs changelog json 与 locale 英文基线数据
150
+ pnpm sui api-translate -- --locale zh-CN
151
+ pnpm sui changelog-translate -- --locale zh-CN
106
152
  ```
107
153
 
108
154
  当前文档站默认通过 `UsageCode`、`PlaygroundGallery` 与 `ComponentApi` 渲染组件文档;组件详情页与 `/releases` 还会消费 `docs/src/generated/changelog/` 和 `docs/src/generated/changelog-locales/` 下的版本日志生成数据。
@@ -1 +1 @@
1
- import{mergeVariants as e}from"../../theme/shared.js";import"../../theme/index.js";import{accordionVariants as t}from"./variants.js";import{computed as n,createBlock as r,createSlots as i,defineComponent as a,guardReactiveProps as o,mergeProps as s,normalizeProps as c,openBlock as l,renderList as u,renderSlot as d,toHandlers as f,unref as p,useSlots as m,withCtx as h}from"vue";import{AccordionCompact as g,provideAccordionUi as _}from"@soybeanjs/headless/accordion";import{useForwardListeners as v,useOmitProps as y}from"@soybeanjs/headless/composables";import{keysOf as b}from"@soybeanjs/utils";const x=a({name:`SAccordion`,__name:`accordion`,props:{class:{type:[Boolean,null,String,Object,Array]},size:{},ui:{},items:{},itemProps:{},headerProps:{},triggerProps:{},contentProps:{},descriptionProps:{},collapsible:{type:Boolean},dir:{},disabled:{type:Boolean},orientation:{},unmountOnHide:{type:Boolean},modelValue:{},defaultValue:{},multiple:{},clearable:{type:Boolean},selectionBehavior:{}},emits:[`update:modelValue`],setup(a,{emit:x}){let S=a,C=x,w=m(),T=y(S,[`class`,`size`,`ui`]),E=v(C),D=n(()=>b(w).filter(e=>e!==`item`));return _(n(()=>e(t({size:S.size}),S.ui,{root:S.class}))),(e,t)=>(l(),r(p(g),s(p(T),f(p(E))),i({item:h(t=>[d(e.$slots,`item`,c(o(t)))]),_:2},[u(D.value,t=>({name:t,fn:h(n=>[d(e.$slots,t,c(o(n)))])}))]),1040))}});export{x as default};
1
+ import{mergeVariants as e}from"../../theme/shared.js";import"../../theme/index.js";import{accordionVariants as t}from"./variants.js";import{computed as n,createBlock as r,createSlots as i,defineComponent as a,guardReactiveProps as o,mergeProps as s,normalizeProps as c,openBlock as l,renderList as u,renderSlot as d,toHandlers as f,unref as p,useSlots as m,withCtx as h}from"vue";import{AccordionCompact as g,provideAccordionUi as _}from"@soybeanjs/headless/accordion";import{useForwardListeners as v,useOmitProps as y}from"@soybeanjs/headless/composables";import{keysOf as b}from"@soybeanjs/utils";const x=a({name:`SAccordion`,__name:`accordion`,props:{class:{type:[Boolean,null,String,Object,Array]},size:{},ui:{},items:{},itemProps:{},headerProps:{},triggerProps:{},contentProps:{},descriptionProps:{},collapsible:{type:Boolean,default:!0},dir:{},disabled:{type:Boolean},orientation:{},unmountOnHide:{type:Boolean,default:!0},modelValue:{},defaultValue:{},multiple:{},selectionBehavior:{}},emits:[`update:modelValue`],setup(a,{emit:x}){let S=a,C=x,w=m(),T=y(S,[`class`,`size`,`ui`]),E=v(C),D=n(()=>b(w).filter(e=>e!==`item`));return _(n(()=>e(t({size:S.size}),S.ui,{root:S.class}))),(e,t)=>(l(),r(p(g),s(p(T),f(p(E))),i({item:h(t=>[d(e.$slots,`item`,c(o(t)))]),_:2},[u(D.value,t=>({name:t,fn:h(n=>[d(e.$slots,t,c(o(n)))])}))]),1040))}});export{x as default};
@@ -1 +1 @@
1
- import{themeSizes as e}from"../../constants/common.js";import t from"../dialog/dialog-provider.js";import n from"../progress/progress-provider.js";import r from"../toast/toast-provider.js";import{provideConfigProviderContext as i}from"./context.js";import a from"../icon/icon.js";import{createBlock as o,createCommentVNode as s,createTextVNode as c,createVNode as l,defineComponent as u,guardReactiveProps as d,h as f,mergeProps as p,normalizeProps as m,openBlock as h,renderSlot as g,toDisplayString as _,unref as v,watch as y,watchEffect as b,withCtx as x}from"vue";import{useOmitProps as S}from"@soybeanjs/headless/composables";import{useStorage as C}from"@vueuse/core";import{ConfigProvider as w}from"@soybeanjs/headless/config-provider";import{Primitive as T}from"@soybeanjs/headless/primitive";import{isClient as E,transformPropsToContext as D}from"@soybeanjs/headless/shared";import{createShadcnTheme as O}from"@soybeanjs/shadcn-theme";const k=u({name:`SConfigProvider`,__name:`config-provider`,props:{theme:{default:()=>({})},size:{default:`md`},iconify:{default:()=>({width:`1.25em`,height:`1.25em`})},progress:{},toast:{},customToast:{type:Boolean},dir:{default:`ltr`},locale:{},nonce:{},tooltip:{},nuxt:{type:Boolean},iconRender:{},messages:{}},setup(u){let k=u,A=S(k,[`iconRender`,`theme`,`size`,`iconify`,`progress`,`toast`,`customToast`]),j=k.iconRender??(e=>f(a,{icon:e}));i({...D(k),iconRender:j});let{getCss:M}=O(k.theme),N=()=>M(k.theme,k.theme.radius),P=C(`__SoybeanUI_themeVars`,N());function F(t){if(!E)return;document.documentElement.classList.add(`size-${t}`);let n=e.filter(e=>e!==t).map(e=>`size-${e}`);document.documentElement.classList.remove(...n)}return y(()=>k.size,e=>{F(e)},{immediate:!0,flush:`sync`}),b(()=>{P.value=N()}),(e,i)=>(h(),o(v(w),p(v(A),{"icon-render":v(j)}),{default:x(()=>[l(v(T),{id:`__SoybeanUI_themeVars`,as:`style`},{default:x(()=>[c(_(v(P)),1)]),_:1}),g(e.$slots,`default`),k.customToast?s(`v-if`,!0):(h(),o(r,m(p({key:0},k.toast)),null,16)),l(t),l(n,m(d(k.progress)),null,16)]),_:3},16,[`icon-render`]))}});export{k as default};
1
+ import{themeSizes as e}from"../../constants/common.js";import t from"../dialog/dialog-provider.js";import n from"../progress/progress-provider.js";import r from"../toast/toast-provider.js";import{provideConfigProviderContext as i}from"./context.js";import a from"../icon/icon.js";import{createBlock as o,createCommentVNode as s,createTextVNode as c,createVNode as l,defineComponent as u,guardReactiveProps as d,h as f,mergeProps as p,normalizeProps as m,openBlock as h,renderSlot as g,toDisplayString as _,unref as v,watch as y,watchEffect as b,withCtx as x}from"vue";import{useOmitProps as S}from"@soybeanjs/headless/composables";import{useStorage as C}from"@vueuse/core";import{ConfigProvider as w}from"@soybeanjs/headless/config-provider";import{Primitive as T}from"@soybeanjs/headless/primitive";import{isClient as E,transformPropsToContext as D}from"@soybeanjs/headless/shared";import{createShadcnTheme as O}from"@soybeanjs/shadcn-theme";const k=u({name:`SConfigProvider`,__name:`config-provider`,props:{theme:{default:()=>({})},size:{default:`md`},iconify:{default:()=>({width:`1.25em`,height:`1.25em`})},progress:{},toast:{},customToast:{type:Boolean},dir:{},locale:{},nonce:{},tooltip:{},nuxt:{type:Boolean},iconRender:{},messages:{}},setup(u){let k=u,A=S(k,[`iconRender`,`theme`,`size`,`iconify`,`progress`,`toast`,`customToast`]),j=k.iconRender??(e=>f(a,{icon:e}));i({...D(k),iconRender:j});let{getCss:M}=O(k.theme),N=()=>M(k.theme,k.theme.radius),P=C(`__SoybeanUI_themeVars`,N());function F(t){if(!E)return;document.documentElement.classList.add(`size-${t}`);let n=e.filter(e=>e!==t).map(e=>`size-${e}`);document.documentElement.classList.remove(...n)}return y(()=>k.size,e=>{F(e)},{immediate:!0,flush:`sync`}),b(()=>{P.value=N()}),(e,i)=>(h(),o(v(w),p(v(A),{"icon-render":v(j)}),{default:x(()=>[l(v(T),{id:`__SoybeanUI_themeVars`,as:`style`},{default:x(()=>[c(_(v(P)),1)]),_:1}),g(e.$slots,`default`),k.customToast?s(`v-if`,!0):(h(),o(r,m(p({key:0},k.toast)),null,16)),l(t),l(n,m(d(k.progress)),null,16)]),_:3},16,[`icon-render`]))}});export{k as default};
@@ -2,7 +2,6 @@ import { ThemeSize } from "../../theme/types.js";
2
2
  import { ConfigProviderProps } from "./types.js";
3
3
  import * as _$vue from "vue";
4
4
  import * as _$_soybeanjs_shadcn_theme0 from "@soybeanjs/shadcn-theme";
5
- import * as _$_soybeanjs_headless0 from "@soybeanjs/headless";
6
5
 
7
6
  //#region src/components/config-provider/config-provider.vue.d.ts
8
7
  declare var __VLS_14: {};
@@ -11,7 +10,6 @@ type __VLS_Slots = {} & {
11
10
  };
12
11
  declare const __VLS_base: _$vue.DefineComponent<ConfigProviderProps, {}, {}, {}, {}, _$vue.ComponentOptionsMixin, _$vue.ComponentOptionsMixin, {}, string, _$vue.PublicProps, Readonly<ConfigProviderProps> & Readonly<{}>, {
13
12
  size: ThemeSize;
14
- dir: _$_soybeanjs_headless0.Direction;
15
13
  theme: _$_soybeanjs_shadcn_theme0.ThemeOptions;
16
14
  iconify: IconifyOptions;
17
15
  }, {}, {}, {}, string, _$vue.ComponentProvideOptions, false, {}, any>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soybeanjs/ui",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "description": "SoybeanUI is built on top of SoybeanHeadless, providing a collection of styled components for Vue 3.",
5
5
  "homepage": "https://github.com/soybeanjs/soybean-ui",
6
6
  "bugs": {
@@ -57,25 +57,26 @@
57
57
  "tailwind-variants": "^3.2.2",
58
58
  "valibot": "^1.4.0",
59
59
  "zod": "^4.4.3",
60
- "@soybeanjs/headless": "^0.20.0"
60
+ "@soybeanjs/headless": "^0.22.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@soybeanjs/cli": "^1.7.2",
64
64
  "@soybeanjs/eslint-config-vue": "^0.0.2",
65
65
  "@soybeanjs/unocss-preset": "^0.2.0",
66
66
  "@soybeanjs/unocss-shadcn": "^0.4.0",
67
- "@types/node": "^25.6.2",
67
+ "@types/node": "^25.7.0",
68
68
  "@unocss/cli": "^66.6.8",
69
69
  "@vitejs/plugin-vue": "^6.0.6",
70
70
  "@vitejs/plugin-vue-jsx": "^5.1.5",
71
- "@vitest/coverage-v8": "^4.1.5",
71
+ "@vitest/coverage-v8": "^4.1.6",
72
72
  "@vue/test-utils": "^2.4.10",
73
73
  "axe-core": "^4.11.4",
74
+ "cac": "^7.0.0",
74
75
  "eslint": "^10.3.0",
75
76
  "happy-dom": "^20.9.0",
76
77
  "lint-staged": "^17.0.4",
77
- "oxfmt": "^0.48.0",
78
- "oxlint": "^1.63.0",
78
+ "oxfmt": "^0.49.0",
79
+ "oxlint": "^1.64.0",
79
80
  "simple-git-hooks": "^2.13.1",
80
81
  "tsdown": "0.22.0",
81
82
  "tsx": "^4.21.0",
@@ -90,7 +91,7 @@
90
91
  "vite": "^8.0.12",
91
92
  "vite-plugin-vue-devtools": "^8.1.2",
92
93
  "vite-plugin-vue-meta-layouts": "^0.6.1",
93
- "vitest": "^4.1.5",
94
+ "vitest": "^4.1.6",
94
95
  "vue": "^3.5.34",
95
96
  "vue-router": "^5.0.6",
96
97
  "vue-tsc": "^3.2.8"
@@ -103,7 +104,7 @@
103
104
  "*": "oxlint . --fix && oxfmt"
104
105
  },
105
106
  "scripts": {
106
- "build": "pnpm build:headless && pnpm build:ui && pnpm build:css",
107
+ "build": "pnpm build:headless && pnpm build:ui && pnpm build:css && pnpm sui skills",
107
108
  "build:css": "unocss build",
108
109
  "build:docs": "pnpm --filter @soybeanjs/ui-docs build",
109
110
  "build:headless": "pnpm --filter @soybeanjs/headless build",
@@ -115,24 +116,16 @@
115
116
  "dev:docs": "pnpm --filter @soybeanjs/ui-docs dev",
116
117
  "lint": "oxlint --fix && eslint --fix headless/ src/",
117
118
  "fmt": "oxfmt",
118
- "gen:api": "tsx scripts/api.ts && tsx scripts/api-i18n.ts && oxfmt docs/src/generated/api/",
119
- "gen:api:i18n": "tsx scripts/api-i18n.ts",
120
- "gen:changelog": "tsx scripts/changelog.ts && tsx scripts/changelog-i18n.ts && oxfmt docs/src/generated/changelog/ docs/src/generated/changelog-locales/",
121
- "translate:api:i18n": "tsx scripts/api-i18n-translate.ts",
122
- "translate:changelog:i18n": "tsx scripts/changelog-i18n-translate.ts",
123
- "gen:headless": "tsx scripts/headless.ts && oxfmt headless/src/constants/components.ts headless/src/namespaced/index.ts",
124
- "gen:ui": "tsx scripts/ui.ts && oxfmt src/constants/components.ts",
125
119
  "preview": "vite preview",
126
120
  "preview:docs": "pnpm --filter @soybeanjs/ui-docs preview",
127
121
  "publish-pkg": "pnpm publish -r --access public",
128
122
  "release": "soy release",
129
- "stub": "tsx scripts/stub.ts",
123
+ "sui": "tsx scripts/cli.ts",
130
124
  "test": "vitest run",
131
125
  "test:coverage": "vitest run --coverage",
132
126
  "test:ui": "vitest --ui",
133
127
  "test:watch": "vitest watch",
134
128
  "typecheck": "vue-tsc --noEmit --skipLibCheck",
135
- "unstub": "tsx scripts/stub.ts --reset",
136
129
  "upkg": "soy ncu"
137
130
  }
138
131
  }