@zl-ui-kit/theme 0.5.1 → 0.6.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 +7 -7
- package/dist/index.js +27 -10
- package/package.json +3 -3
- package/src/runtime/api.ts +27 -17
- package/src/runtime/colors.ts +10 -4
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ ZL UI 主题引擎(决策 D7):内置 5 套主题(light/dark/red-light/re
|
|
|
8
8
|
## 快速上手
|
|
9
9
|
|
|
10
10
|
```ts
|
|
11
|
-
import '@zl-ui-kit/theme/themes.css'
|
|
11
|
+
import '@zl-ui-kit/theme/themes.css' // 或按需 '@zl-ui-kit/theme/light'
|
|
12
12
|
import { setTheme, defineTheme, useTheme } from '@zl-ui-kit/theme'
|
|
13
13
|
|
|
14
14
|
setTheme('dark')
|
|
@@ -20,12 +20,12 @@ defineTheme({ name: 'brand', base: 'light', colors: { primary: '#722ED1' }, appl
|
|
|
20
20
|
|
|
21
21
|
## 入口
|
|
22
22
|
|
|
23
|
-
| 入口
|
|
24
|
-
|
|
|
25
|
-
| `.`
|
|
26
|
-
| `/light` `/dark` `/red-light` `/red-dark` `/blue` `/themes.css` | 每主题/合集预编译 CSS(变量 + reset + overrides)
|
|
27
|
-
| `/scss`(及 `/scss/*.scss`)
|
|
28
|
-
| `/assets/*`
|
|
23
|
+
| 入口 | 内容 |
|
|
24
|
+
| --------------------------------------------------------------- | ---------------------------------------------------------------------- |
|
|
25
|
+
| `.` | 运行时 API(defineTheme/setTheme/useTheme/mix/generateElShades)+ 常量 |
|
|
26
|
+
| `/light` `/dark` `/red-light` `/red-dark` `/blue` `/themes.css` | 每主题/合集预编译 CSS(变量 + reset + overrides) |
|
|
27
|
+
| `/scss`(及 `/scss/*.scss`) | SCSS 工具(colorForEach/colorGradient/theme mixin/mapGet) |
|
|
28
|
+
| `/assets/*` | 主题背景图 |
|
|
29
29
|
|
|
30
30
|
## 本地调试
|
|
31
31
|
|
package/dist/index.js
CHANGED
|
@@ -1132,7 +1132,11 @@ function parseColor(str) {
|
|
|
1132
1132
|
let hex = s.slice(1);
|
|
1133
1133
|
if (hex.length === 3)
|
|
1134
1134
|
hex = hex.split("").map((c) => c + c).join("");
|
|
1135
|
-
return [
|
|
1135
|
+
return [
|
|
1136
|
+
Number.parseInt(hex.slice(0, 2), 16),
|
|
1137
|
+
Number.parseInt(hex.slice(2, 4), 16),
|
|
1138
|
+
Number.parseInt(hex.slice(4, 6), 16)
|
|
1139
|
+
];
|
|
1136
1140
|
}
|
|
1137
1141
|
const m = s.match(/rgba?\(([^)]+)\)/);
|
|
1138
1142
|
if (m) {
|
|
@@ -1151,8 +1155,7 @@ function toHex([r, g, b]) {
|
|
|
1151
1155
|
function mix(color1, color2, weight) {
|
|
1152
1156
|
const c1 = parseColor(color1);
|
|
1153
1157
|
const c2 = parseColor(color2);
|
|
1154
|
-
if (!c1 || !c2)
|
|
1155
|
-
return color1;
|
|
1158
|
+
if (!c1 || !c2) return color1;
|
|
1156
1159
|
const w = weight / 100;
|
|
1157
1160
|
return toHex([c1[0] * (1 - w) + c2[0] * w, c1[1] * (1 - w) + c2[1] * w, c1[2] * (1 - w) + c2[2] * w]);
|
|
1158
1161
|
}
|
|
@@ -1207,13 +1210,28 @@ function buildCssVars(resolved, themeName) {
|
|
|
1207
1210
|
decls.push(`--el-color-${name}-${shade}: ${value};`);
|
|
1208
1211
|
}
|
|
1209
1212
|
}
|
|
1210
|
-
const textMap = {
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1213
|
+
const textMap = {
|
|
1214
|
+
primary: "text-1",
|
|
1215
|
+
regular: "text-2",
|
|
1216
|
+
secondary: "text-3",
|
|
1217
|
+
placeholder: "text-4",
|
|
1218
|
+
disabled: "text-5"
|
|
1219
|
+
};
|
|
1220
|
+
for (const [el, token] of Object.entries(textMap)) decls.push(`--el-text-color-${el}: ${get(token)};`);
|
|
1221
|
+
const borderMap = [
|
|
1222
|
+
["extra-light", "border-1"],
|
|
1223
|
+
["lighter", "border-2"],
|
|
1224
|
+
["light", "border-3"],
|
|
1225
|
+
["", "border-4"]
|
|
1226
|
+
];
|
|
1214
1227
|
for (const [suffix, token] of borderMap)
|
|
1215
1228
|
decls.push(`--el-border-color${suffix ? `-${suffix}` : ""}: ${get(token)};`);
|
|
1216
|
-
const fillMap = [
|
|
1229
|
+
const fillMap = [
|
|
1230
|
+
["extra-light", "fill-1"],
|
|
1231
|
+
["lighter", "fill-2"],
|
|
1232
|
+
["light", "fill-3"],
|
|
1233
|
+
["", "fill-4"]
|
|
1234
|
+
];
|
|
1217
1235
|
for (const [suffix, token] of fillMap)
|
|
1218
1236
|
decls.push(`--el-fill-color${suffix ? `-${suffix}` : ""}: ${get(token)};`);
|
|
1219
1237
|
decls.push(`--el-fill-color-blank: ${isDark ? get("bg-2") : "#ffffff"};`);
|
|
@@ -1257,8 +1275,7 @@ function defineTheme(options) {
|
|
|
1257
1275
|
resolved[`color-${palette}-${level}`] = hex;
|
|
1258
1276
|
const seedLevel = Number(level);
|
|
1259
1277
|
for (let i = 1; i <= 10; i++) {
|
|
1260
|
-
if (i === seedLevel)
|
|
1261
|
-
continue;
|
|
1278
|
+
if (i === seedLevel) continue;
|
|
1262
1279
|
const weight = Math.abs(i - seedLevel) / 5 * 50;
|
|
1263
1280
|
resolved[`color-${palette}-${i}`] = i < seedLevel ? mix("#ffffff", hex, 100 - weight) : mix("#000000", hex, 100 - weight);
|
|
1264
1281
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zl-ui-kit/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "ZL UI 主题引擎:内置 5 套主题(light/dark/red-light/red-dark/blue),--zl-* 业务变量 + --el-* Element Plus 变量三层体系,支持运行时自定义主题(defineTheme/setTheme)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"cross-env": "^7.0.3",
|
|
42
42
|
"tsup": "^8.3.5",
|
|
43
43
|
"typescript": "^5.7.3",
|
|
44
|
-
"@jichuang/
|
|
45
|
-
"@jichuang/
|
|
44
|
+
"@jichuang/eslint-config": "1.0.0",
|
|
45
|
+
"@jichuang/tsconfig": "1.0.0"
|
|
46
46
|
},
|
|
47
47
|
"repository": {
|
|
48
48
|
"type": "git",
|
package/src/runtime/api.ts
CHANGED
|
@@ -50,8 +50,7 @@ const changeListeners = new Set<(theme: string) => void>()
|
|
|
50
50
|
function initThemeFromStorage(): string {
|
|
51
51
|
try {
|
|
52
52
|
return localStorage.getItem(THEME_STORAGE_KEY) || 'light'
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
53
|
+
} catch {
|
|
55
54
|
return 'light'
|
|
56
55
|
}
|
|
57
56
|
}
|
|
@@ -88,15 +87,30 @@ function buildCssVars(resolved: Record<string, string>, themeName: string): stri
|
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
const textMap: Record<string, string> = {
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
const textMap: Record<string, string> = {
|
|
91
|
+
primary: 'text-1',
|
|
92
|
+
regular: 'text-2',
|
|
93
|
+
secondary: 'text-3',
|
|
94
|
+
placeholder: 'text-4',
|
|
95
|
+
disabled: 'text-5',
|
|
96
|
+
}
|
|
97
|
+
for (const [el, token] of Object.entries(textMap)) decls.push(`--el-text-color-${el}: ${get(token)};`)
|
|
94
98
|
|
|
95
|
-
const borderMap: Array<[string, string]> = [
|
|
99
|
+
const borderMap: Array<[string, string]> = [
|
|
100
|
+
['extra-light', 'border-1'],
|
|
101
|
+
['lighter', 'border-2'],
|
|
102
|
+
['light', 'border-3'],
|
|
103
|
+
['', 'border-4'],
|
|
104
|
+
]
|
|
96
105
|
for (const [suffix, token] of borderMap)
|
|
97
106
|
decls.push(`--el-border-color${suffix ? `-${suffix}` : ''}: ${get(token)};`)
|
|
98
107
|
|
|
99
|
-
const fillMap: Array<[string, string]> = [
|
|
108
|
+
const fillMap: Array<[string, string]> = [
|
|
109
|
+
['extra-light', 'fill-1'],
|
|
110
|
+
['lighter', 'fill-2'],
|
|
111
|
+
['light', 'fill-3'],
|
|
112
|
+
['', 'fill-4'],
|
|
113
|
+
]
|
|
100
114
|
for (const [suffix, token] of fillMap)
|
|
101
115
|
decls.push(`--el-fill-color${suffix ? `-${suffix}` : ''}: ${get(token)};`)
|
|
102
116
|
decls.push(`--el-fill-color-blank: ${isDark ? get('bg-2') : '#ffffff'};`)
|
|
@@ -152,12 +166,10 @@ export function defineTheme(options: DefineThemeOptions): void {
|
|
|
152
166
|
// 主色同时派生整组色板(浅→白、深→黑,各 10 级),保证 --zl-{palette}-N 可用
|
|
153
167
|
const seedLevel = Number(level)
|
|
154
168
|
for (let i = 1; i <= 10; i++) {
|
|
155
|
-
if (i === seedLevel)
|
|
156
|
-
continue
|
|
169
|
+
if (i === seedLevel) continue
|
|
157
170
|
const weight = (Math.abs(i - seedLevel) / 5) * 50
|
|
158
|
-
resolved[`color-${palette}-${i}`] =
|
|
159
|
-
? mix('#ffffff', hex, 100 - weight)
|
|
160
|
-
: mix('#000000', hex, 100 - weight)
|
|
171
|
+
resolved[`color-${palette}-${i}`] =
|
|
172
|
+
i < seedLevel ? mix('#ffffff', hex, 100 - weight) : mix('#000000', hex, 100 - weight)
|
|
161
173
|
}
|
|
162
174
|
}
|
|
163
175
|
}
|
|
@@ -189,21 +201,19 @@ export function setTheme(name: string, options: SetThemeOptions = {}): void {
|
|
|
189
201
|
currentTheme.value = name
|
|
190
202
|
if (name === 'light') {
|
|
191
203
|
document.body.removeAttribute('data-theme')
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
204
|
+
} else {
|
|
194
205
|
document.body.setAttribute('data-theme', name)
|
|
195
206
|
}
|
|
196
207
|
|
|
197
208
|
if (persistenceEnabled.value || options.persist) {
|
|
198
209
|
try {
|
|
199
210
|
localStorage.setItem(THEME_STORAGE_KEY, name)
|
|
200
|
-
}
|
|
201
|
-
catch {
|
|
211
|
+
} catch {
|
|
202
212
|
// storage 不可用时忽略
|
|
203
213
|
}
|
|
204
214
|
}
|
|
205
215
|
|
|
206
|
-
changeListeners.forEach(fn => fn(name))
|
|
216
|
+
changeListeners.forEach((fn) => fn(name))
|
|
207
217
|
}
|
|
208
218
|
|
|
209
219
|
/** 当前主题名 */
|
package/src/runtime/colors.ts
CHANGED
|
@@ -8,8 +8,15 @@ export function parseColor(str: string): [number, number, number] | null {
|
|
|
8
8
|
if (s.startsWith('#')) {
|
|
9
9
|
let hex = s.slice(1)
|
|
10
10
|
if (hex.length === 3)
|
|
11
|
-
hex = hex
|
|
12
|
-
|
|
11
|
+
hex = hex
|
|
12
|
+
.split('')
|
|
13
|
+
.map((c) => c + c)
|
|
14
|
+
.join('')
|
|
15
|
+
return [
|
|
16
|
+
Number.parseInt(hex.slice(0, 2), 16),
|
|
17
|
+
Number.parseInt(hex.slice(2, 4), 16),
|
|
18
|
+
Number.parseInt(hex.slice(4, 6), 16),
|
|
19
|
+
]
|
|
13
20
|
}
|
|
14
21
|
const m = s.match(/rgba?\(([^)]+)\)/)
|
|
15
22
|
if (m) {
|
|
@@ -31,8 +38,7 @@ export function toHex([r, g, b]: [number, number, number]): string {
|
|
|
31
38
|
export function mix(color1: string, color2: string, weight: number): string {
|
|
32
39
|
const c1 = parseColor(color1)
|
|
33
40
|
const c2 = parseColor(color2)
|
|
34
|
-
if (!c1 || !c2)
|
|
35
|
-
return color1
|
|
41
|
+
if (!c1 || !c2) return color1
|
|
36
42
|
const w = weight / 100
|
|
37
43
|
return toHex([c1[0] * (1 - w) + c2[0] * w, c1[1] * (1 - w) + c2[1] * w, c1[2] * (1 - w) + c2[2] * w])
|
|
38
44
|
}
|