@veltra/styles 1.6.5 → 1.7.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 +22 -0
- package/dist/_mixins.scss +1 -7
- package/dist/theme/component-css-vars.js +1 -1
- package/dist/theme/component-css-vars.js.map +1 -1
- package/dist/theme/index.d.ts +10 -7
- package/dist/theme/index.js +9 -6
- package/dist/theme/load-theme.d.ts +3 -4
- package/dist/theme/load-theme.js +15 -22
- package/dist/theme/load-theme.js.map +1 -1
- package/dist/theme/presets/ancient.d.ts +3 -2
- package/dist/theme/presets/ancient.js +3 -2
- package/dist/theme/presets/ancient.js.map +1 -1
- package/dist/theme/presets/dark.js +1 -1
- package/dist/theme/presets/dark.js.map +1 -1
- package/dist/theme/presets/glass.d.ts +7 -3
- package/dist/theme/presets/glass.js +18 -47
- package/dist/theme/presets/glass.js.map +1 -1
- package/dist/theme/presets/hero.d.ts +3 -3
- package/dist/theme/presets/hero.js +3 -40
- package/dist/theme/presets/hero.js.map +1 -1
- package/dist/theme/presets/midnight.d.ts +8 -0
- package/dist/theme/presets/midnight.js +55 -0
- package/dist/theme/presets/midnight.js.map +1 -0
- package/dist/theme/presets/neon.d.ts +8 -0
- package/dist/theme/presets/neon.js +55 -0
- package/dist/theme/presets/neon.js.map +1 -0
- package/dist/theme/presets/ocean.d.ts +8 -0
- package/dist/theme/presets/ocean.js +55 -0
- package/dist/theme/presets/ocean.js.map +1 -0
- package/dist/theme/presets/sakura.d.ts +8 -0
- package/dist/theme/presets/sakura.js +57 -0
- package/dist/theme/presets/sakura.js.map +1 -0
- package/dist/theme/ui-theme.d.ts +16 -8
- package/dist/theme/ui-theme.js +11 -25
- package/dist/theme/ui-theme.js.map +1 -1
- package/package.json +3 -3
- package/src/_mixins.scss +1 -7
- package/src/theme/__test__/ui-theme.test.ts +42 -9
- package/src/theme/component-css-vars.ts +1 -1
- package/src/theme/load-theme.ts +12 -27
- package/src/theme/presets/ancient.ts +3 -1
- package/src/theme/presets/dark.ts +43 -40
- package/src/theme/presets/glass.ts +56 -67
- package/src/theme/presets/hero.ts +2 -35
- package/src/theme/presets/index.ts +7 -4
- package/src/theme/presets/midnight.ts +45 -0
- package/src/theme/presets/neon.ts +45 -0
- package/src/theme/presets/ocean.ts +35 -0
- package/src/theme/presets/sakura.ts +37 -0
- package/src/theme/ui-theme.ts +26 -41
- package/dist/theme/presets/shadcn.d.ts +0 -8
- package/dist/theme/presets/shadcn.js +0 -100
- package/dist/theme/presets/shadcn.js.map +0 -1
- package/src/theme/presets/shadcn.ts +0 -84
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"midnight.js","names":[],"sources":["../../../src/theme/presets/midnight.ts"],"sourcesContent":["import { darkTheme } from './dark'\n\n/** 午夜(靛蓝 + 深空底,宽松间距、舒缓缓动),天生适合深色 */\nexport const midnightTheme = darkTheme.new({\n color: {\n primary: '#6f78ee', // 星靛(白字对比 3.75:1)\n success: '#34d399',\n warning: '#fbbf24',\n danger: '#f87171',\n info: '#38bdf8',\n disabled: '#232d4a',\n default: '#232d4a'\n },\n bg: {\n color: {\n bottom: '#0a0f1e', // 深空\n middle: '#101731',\n top: '#182242',\n hover: '#212d54'\n }\n },\n 'text-color': {\n title: '#e8ecff', // 月光\n main: '#c3cbf0',\n second: '#8d97c6',\n placeholder: '#7d8ab8',\n assist: '#5b6895',\n disabled: '#576493'\n },\n // mutedColor 与卡片底对比 2.6:1,保证输入控件清晰可见\n border: { color: '#36457a', mutedColor: '#52619a' },\n // 更宽松的间距,配合深空的呼吸感\n gap: { small: 8, default: 10, large: 14 },\n shadow: {\n color: '#00000080',\n x: 0,\n y: 2,\n blur: 8,\n spread: 0,\n emboss: 'none',\n sm: '0 1px 2px 0 rgba(0, 0, 0, 0.5)',\n lg: '0 8px 24px 0 rgba(0, 0, 0, 0.6), 0 2px 6px 0 rgba(0, 0, 0, 0.4)'\n },\n transition: { slow: '0.4s', ease: 'cubic-bezier(0.33, 1, 0.68, 1)' }\n})\n"],"mappings":";;;AAGA,MAAa,gBAAgB,UAAU,IAAI;CACzC,OAAO;EACL,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ;EACR,MAAM;EACN,UAAU;EACV,SAAS;CACX;CACA,IAAI,EACF,OAAO;EACL,QAAQ;EACR,QAAQ;EACR,KAAK;EACL,OAAO;CACT,EACF;CACA,cAAc;EACZ,OAAO;EACP,MAAM;EACN,QAAQ;EACR,aAAa;EACb,QAAQ;EACR,UAAU;CACZ;CAEA,QAAQ;EAAE,OAAO;EAAW,YAAY;CAAU;CAElD,KAAK;EAAE,OAAO;EAAG,SAAS;EAAI,OAAO;CAAG;CACxC,QAAQ;EACN,OAAO;EACP,GAAG;EACH,GAAG;EACH,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,IAAI;EACJ,IAAI;CACN;CACA,YAAY;EAAE,MAAM;EAAQ,MAAM;CAAiC;AACrE,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { darkTheme } from "./dark.js";
|
|
2
|
+
//#region src/theme/presets/neon.ts
|
|
3
|
+
/** 霓虹(品红 + 夜紫底 + 辉光阴影,小圆角、急速缓动),天生适合深色 */
|
|
4
|
+
const neonTheme = darkTheme.new({
|
|
5
|
+
color: {
|
|
6
|
+
primary: "#c026d3",
|
|
7
|
+
success: "#4ade80",
|
|
8
|
+
warning: "#facc15",
|
|
9
|
+
danger: "#fb7185",
|
|
10
|
+
info: "#22d3ee",
|
|
11
|
+
disabled: "#241532",
|
|
12
|
+
default: "#241532"
|
|
13
|
+
},
|
|
14
|
+
bg: { color: {
|
|
15
|
+
bottom: "#0c0612",
|
|
16
|
+
middle: "#130a1d",
|
|
17
|
+
top: "#1c102a",
|
|
18
|
+
hover: "#28163d"
|
|
19
|
+
} },
|
|
20
|
+
"text-color": {
|
|
21
|
+
title: "#f6efff",
|
|
22
|
+
main: "#d9cdee",
|
|
23
|
+
second: "#a08bc4",
|
|
24
|
+
placeholder: "#8f74b8",
|
|
25
|
+
assist: "#6b4f94",
|
|
26
|
+
disabled: "#63498a"
|
|
27
|
+
},
|
|
28
|
+
border: {
|
|
29
|
+
color: "#503480",
|
|
30
|
+
mutedColor: "#684a9e"
|
|
31
|
+
},
|
|
32
|
+
radius: {
|
|
33
|
+
small: 2,
|
|
34
|
+
default: 4,
|
|
35
|
+
large: 8
|
|
36
|
+
},
|
|
37
|
+
shadow: {
|
|
38
|
+
color: "rgba(192, 38, 211, 0.3)",
|
|
39
|
+
x: 0,
|
|
40
|
+
y: 0,
|
|
41
|
+
blur: 12,
|
|
42
|
+
spread: 0,
|
|
43
|
+
emboss: "none",
|
|
44
|
+
sm: "0 1px 2px 0 rgba(0, 0, 0, 0.5)",
|
|
45
|
+
lg: "0 0 24px 0 rgba(192, 38, 211, 0.25), 0 8px 24px 0 rgba(0, 0, 0, 0.5)"
|
|
46
|
+
},
|
|
47
|
+
transition: {
|
|
48
|
+
fast: "0.1s",
|
|
49
|
+
ease: "cubic-bezier(0.85, 0, 0.15, 1)"
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
//#endregion
|
|
53
|
+
export { neonTheme };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=neon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neon.js","names":[],"sources":["../../../src/theme/presets/neon.ts"],"sourcesContent":["import { darkTheme } from './dark'\n\n/** 霓虹(品红 + 夜紫底 + 辉光阴影,小圆角、急速缓动),天生适合深色 */\nexport const neonTheme = darkTheme.new({\n color: {\n primary: '#c026d3', // 霓红\n success: '#4ade80',\n warning: '#facc15',\n danger: '#fb7185',\n info: '#22d3ee',\n disabled: '#241532',\n default: '#241532'\n },\n bg: {\n color: {\n bottom: '#0c0612', // 夜紫\n middle: '#130a1d',\n top: '#1c102a',\n hover: '#28163d'\n }\n },\n 'text-color': {\n title: '#f6efff',\n main: '#d9cdee',\n second: '#a08bc4',\n placeholder: '#8f74b8',\n assist: '#6b4f94',\n disabled: '#63498a'\n },\n // mutedColor 与卡片底对比 2.65:1,输入控件带清晰的紫色描边\n border: { color: '#503480', mutedColor: '#684a9e' },\n // 赛博感:小圆角 + 品红辉光\n radius: { small: 2, default: 4, large: 8 },\n shadow: {\n color: 'rgba(192, 38, 211, 0.3)',\n x: 0,\n y: 0,\n blur: 12,\n spread: 0,\n emboss: 'none',\n sm: '0 1px 2px 0 rgba(0, 0, 0, 0.5)',\n lg: '0 0 24px 0 rgba(192, 38, 211, 0.25), 0 8px 24px 0 rgba(0, 0, 0, 0.5)'\n },\n transition: { fast: '0.1s', ease: 'cubic-bezier(0.85, 0, 0.15, 1)' }\n})\n"],"mappings":";;;AAGA,MAAa,YAAY,UAAU,IAAI;CACrC,OAAO;EACL,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ;EACR,MAAM;EACN,UAAU;EACV,SAAS;CACX;CACA,IAAI,EACF,OAAO;EACL,QAAQ;EACR,QAAQ;EACR,KAAK;EACL,OAAO;CACT,EACF;CACA,cAAc;EACZ,OAAO;EACP,MAAM;EACN,QAAQ;EACR,aAAa;EACb,QAAQ;EACR,UAAU;CACZ;CAEA,QAAQ;EAAE,OAAO;EAAW,YAAY;CAAU;CAElD,QAAQ;EAAE,OAAO;EAAG,SAAS;EAAG,OAAO;CAAE;CACzC,QAAQ;EACN,OAAO;EACP,GAAG;EACH,GAAG;EACH,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,IAAI;EACJ,IAAI;CACN;CACA,YAAY;EAAE,MAAM;EAAQ,MAAM;CAAiC;AACrE,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { lightTheme } from "./light.js";
|
|
2
|
+
//#region src/theme/presets/ocean.ts
|
|
3
|
+
/** 海盐(松石青 + 冷白底,利落的快缓动),天生适合浅色 */
|
|
4
|
+
const oceanTheme = lightTheme.new({
|
|
5
|
+
color: {
|
|
6
|
+
primary: "#0d9488",
|
|
7
|
+
success: "#16a34a",
|
|
8
|
+
warning: "#d97706",
|
|
9
|
+
danger: "#dc2626",
|
|
10
|
+
info: "#0284c7",
|
|
11
|
+
disabled: "#eff6f5",
|
|
12
|
+
default: "#eff6f5"
|
|
13
|
+
},
|
|
14
|
+
bg: { color: {
|
|
15
|
+
bottom: "#eef6f5",
|
|
16
|
+
middle: "#f5fafa",
|
|
17
|
+
top: "#ffffff",
|
|
18
|
+
hover: "#e0efee"
|
|
19
|
+
} },
|
|
20
|
+
"text-color": {
|
|
21
|
+
title: "#16302e",
|
|
22
|
+
main: "#2f4a48",
|
|
23
|
+
second: "#5b7875",
|
|
24
|
+
assist: "#c3d6d4",
|
|
25
|
+
placeholder: "#7fa19e",
|
|
26
|
+
disabled: "#a3bab7"
|
|
27
|
+
},
|
|
28
|
+
border: {
|
|
29
|
+
width: 1,
|
|
30
|
+
color: "#c2dedb",
|
|
31
|
+
mutedColor: "#8fbcb7"
|
|
32
|
+
},
|
|
33
|
+
radius: {
|
|
34
|
+
small: 6,
|
|
35
|
+
default: 10,
|
|
36
|
+
large: 14
|
|
37
|
+
},
|
|
38
|
+
shadow: {
|
|
39
|
+
color: "rgba(21, 94, 89, 0.12)",
|
|
40
|
+
x: 0,
|
|
41
|
+
y: 2,
|
|
42
|
+
blur: 10,
|
|
43
|
+
spread: -1,
|
|
44
|
+
sm: "0 1px 2px 0 rgba(21, 94, 89, 0.08)",
|
|
45
|
+
lg: "0 8px 24px 0 rgba(21, 94, 89, 0.12), 0 2px 6px 0 rgba(21, 94, 89, 0.06)"
|
|
46
|
+
},
|
|
47
|
+
transition: {
|
|
48
|
+
fast: "0.12s",
|
|
49
|
+
ease: "cubic-bezier(0.22, 1, 0.36, 1)"
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
//#endregion
|
|
53
|
+
export { oceanTheme };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=ocean.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ocean.js","names":[],"sources":["../../../src/theme/presets/ocean.ts"],"sourcesContent":["import { lightTheme } from './light'\n\n/** 海盐(松石青 + 冷白底,利落的快缓动),天生适合浅色 */\nexport const oceanTheme = lightTheme.new({\n color: {\n primary: '#0d9488', // 松石青\n success: '#16a34a',\n warning: '#d97706',\n danger: '#dc2626',\n info: '#0284c7',\n disabled: '#eff6f5',\n default: '#eff6f5'\n },\n bg: { color: { bottom: '#eef6f5', middle: '#f5fafa', top: '#ffffff', hover: '#e0efee' } },\n 'text-color': {\n title: '#16302e', // 深礁\n main: '#2f4a48',\n second: '#5b7875',\n assist: '#c3d6d4',\n placeholder: '#7fa19e',\n disabled: '#a3bab7'\n },\n border: { width: 1, color: '#c2dedb', mutedColor: '#8fbcb7' },\n radius: { small: 6, default: 10, large: 14 },\n shadow: {\n color: 'rgba(21, 94, 89, 0.12)',\n x: 0,\n y: 2,\n blur: 10,\n spread: -1,\n sm: '0 1px 2px 0 rgba(21, 94, 89, 0.08)',\n lg: '0 8px 24px 0 rgba(21, 94, 89, 0.12), 0 2px 6px 0 rgba(21, 94, 89, 0.06)'\n },\n transition: { fast: '0.12s', ease: 'cubic-bezier(0.22, 1, 0.36, 1)' }\n})\n"],"mappings":";;;AAGA,MAAa,aAAa,WAAW,IAAI;CACvC,OAAO;EACL,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ;EACR,MAAM;EACN,UAAU;EACV,SAAS;CACX;CACA,IAAI,EAAE,OAAO;EAAE,QAAQ;EAAW,QAAQ;EAAW,KAAK;EAAW,OAAO;CAAU,EAAE;CACxF,cAAc;EACZ,OAAO;EACP,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,aAAa;EACb,UAAU;CACZ;CACA,QAAQ;EAAE,OAAO;EAAG,OAAO;EAAW,YAAY;CAAU;CAC5D,QAAQ;EAAE,OAAO;EAAG,SAAS;EAAI,OAAO;CAAG;CAC3C,QAAQ;EACN,OAAO;EACP,GAAG;EACH,GAAG;EACH,MAAM;EACN,QAAQ;EACR,IAAI;EACJ,IAAI;CACN;CACA,YAAY;EAAE,MAAM;EAAS,MAAM;CAAiC;AACtE,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { lightTheme } from "./light.js";
|
|
2
|
+
//#region src/theme/presets/sakura.ts
|
|
3
|
+
/** 樱花(柔粉 + 花瓣底,大圆角、弹性缓动),天生适合浅色 */
|
|
4
|
+
const sakuraTheme = lightTheme.new({
|
|
5
|
+
color: {
|
|
6
|
+
primary: "#e64980",
|
|
7
|
+
success: "#2f9e6e",
|
|
8
|
+
warning: "#e8890c",
|
|
9
|
+
danger: "#e03131",
|
|
10
|
+
info: "#7c6cf0",
|
|
11
|
+
disabled: "#faf0f3",
|
|
12
|
+
default: "#faf0f3"
|
|
13
|
+
},
|
|
14
|
+
bg: { color: {
|
|
15
|
+
bottom: "#fdf1f5",
|
|
16
|
+
middle: "#fef6f9",
|
|
17
|
+
top: "#ffffff",
|
|
18
|
+
hover: "#fbe4ec"
|
|
19
|
+
} },
|
|
20
|
+
"text-color": {
|
|
21
|
+
title: "#43272f",
|
|
22
|
+
main: "#5f3d46",
|
|
23
|
+
second: "#92616d",
|
|
24
|
+
assist: "#dcc0c7",
|
|
25
|
+
placeholder: "#b58694",
|
|
26
|
+
disabled: "#c4a0aa"
|
|
27
|
+
},
|
|
28
|
+
border: {
|
|
29
|
+
width: 1,
|
|
30
|
+
color: "#f0c3d4",
|
|
31
|
+
mutedColor: "#e09db6"
|
|
32
|
+
},
|
|
33
|
+
radius: {
|
|
34
|
+
small: 8,
|
|
35
|
+
default: 12,
|
|
36
|
+
large: 16
|
|
37
|
+
},
|
|
38
|
+
"form-component-height": {
|
|
39
|
+
small: 26,
|
|
40
|
+
default: 34,
|
|
41
|
+
large: 42
|
|
42
|
+
},
|
|
43
|
+
shadow: {
|
|
44
|
+
color: "rgba(173, 58, 105, 0.12)",
|
|
45
|
+
x: 0,
|
|
46
|
+
y: 4,
|
|
47
|
+
blur: 14,
|
|
48
|
+
spread: -2,
|
|
49
|
+
sm: "0 1px 2px 0 rgba(173, 58, 105, 0.08)",
|
|
50
|
+
lg: "0 8px 24px 0 rgba(173, 58, 105, 0.14), 0 2px 6px 0 rgba(173, 58, 105, 0.08)"
|
|
51
|
+
},
|
|
52
|
+
transition: { easeOut: "cubic-bezier(0.34, 1.56, 0.64, 1)" }
|
|
53
|
+
});
|
|
54
|
+
//#endregion
|
|
55
|
+
export { sakuraTheme };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=sakura.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sakura.js","names":[],"sources":["../../../src/theme/presets/sakura.ts"],"sourcesContent":["import { lightTheme } from './light'\n\n/** 樱花(柔粉 + 花瓣底,大圆角、弹性缓动),天生适合浅色 */\nexport const sakuraTheme = lightTheme.new({\n color: {\n primary: '#e64980', // 樱粉\n success: '#2f9e6e',\n warning: '#e8890c',\n danger: '#e03131',\n info: '#7c6cf0',\n disabled: '#faf0f3',\n default: '#faf0f3'\n },\n bg: { color: { bottom: '#fdf1f5', middle: '#fef6f9', top: '#ffffff', hover: '#fbe4ec' } },\n 'text-color': {\n title: '#43272f', // 樱墨\n main: '#5f3d46',\n second: '#92616d',\n assist: '#dcc0c7',\n placeholder: '#b58694',\n disabled: '#c4a0aa'\n },\n border: { width: 1, color: '#f0c3d4', mutedColor: '#e09db6' },\n radius: { small: 8, default: 12, large: 16 },\n // 更宽松的表单控件,配合大圆角的柔和感\n 'form-component-height': { small: 26, default: 34, large: 42 },\n shadow: {\n color: 'rgba(173, 58, 105, 0.12)',\n x: 0,\n y: 4,\n blur: 14,\n spread: -2,\n sm: '0 1px 2px 0 rgba(173, 58, 105, 0.08)',\n lg: '0 8px 24px 0 rgba(173, 58, 105, 0.14), 0 2px 6px 0 rgba(173, 58, 105, 0.08)'\n },\n transition: { easeOut: 'cubic-bezier(0.34, 1.56, 0.64, 1)' }\n})\n"],"mappings":";;;AAGA,MAAa,cAAc,WAAW,IAAI;CACxC,OAAO;EACL,SAAS;EACT,SAAS;EACT,SAAS;EACT,QAAQ;EACR,MAAM;EACN,UAAU;EACV,SAAS;CACX;CACA,IAAI,EAAE,OAAO;EAAE,QAAQ;EAAW,QAAQ;EAAW,KAAK;EAAW,OAAO;CAAU,EAAE;CACxF,cAAc;EACZ,OAAO;EACP,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,aAAa;EACb,UAAU;CACZ;CACA,QAAQ;EAAE,OAAO;EAAG,OAAO;EAAW,YAAY;CAAU;CAC5D,QAAQ;EAAE,OAAO;EAAG,SAAS;EAAI,OAAO;CAAG;CAE3C,yBAAyB;EAAE,OAAO;EAAI,SAAS;EAAI,OAAO;CAAG;CAC7D,QAAQ;EACN,OAAO;EACP,GAAG;EACH,GAAG;EACH,MAAM;EACN,QAAQ;EACR,IAAI;EACJ,IAAI;CACN;CACA,YAAY,EAAE,SAAS,oCAAoC;AAC7D,CAAC"}
|
package/dist/theme/ui-theme.d.ts
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { Theme } from "./type.js";
|
|
2
2
|
//#region src/theme/ui-theme.d.ts
|
|
3
3
|
type RecursivePartial<T> = { [P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P]; };
|
|
4
|
+
/** 主题系列:浅色或深色。决定组件级 token 与 html[data-theme] */
|
|
5
|
+
type ThemeSeries = 'light' | 'dark';
|
|
6
|
+
interface UIThemeOptions {
|
|
7
|
+
/** 主题变更时是否自动重渲染,默认 true */
|
|
8
|
+
reactive?: boolean;
|
|
9
|
+
/** 主题所属系列,默认 'light' */
|
|
10
|
+
series?: ThemeSeries;
|
|
11
|
+
}
|
|
4
12
|
declare class UITheme {
|
|
5
13
|
static themeID: string;
|
|
6
14
|
private static adoptedSheet;
|
|
7
15
|
readonly theme: Theme;
|
|
16
|
+
/** 主题所属系列:浅色 / 深色 */
|
|
17
|
+
series: ThemeSeries;
|
|
8
18
|
private readonly reactiveEnabled;
|
|
9
|
-
constructor(theme: Theme, options?:
|
|
10
|
-
reactive?: boolean;
|
|
11
|
-
});
|
|
12
|
-
static setTheme(mode: 'light' | 'dark' | 'auto'): void;
|
|
19
|
+
constructor(theme: Theme, options?: UIThemeOptions);
|
|
13
20
|
private renderBase;
|
|
14
21
|
private renderBorder;
|
|
15
22
|
private renderTypeColor;
|
|
@@ -25,11 +32,12 @@ declare class UITheme {
|
|
|
25
32
|
private static declarationBlock;
|
|
26
33
|
private static removeExistingStyleTag;
|
|
27
34
|
private static applyGlobalCSS;
|
|
28
|
-
/**
|
|
29
|
-
static injectBuiltInThemes(light: UITheme, dark: UITheme): void;
|
|
35
|
+
/** 注入主题:全局 token + 与主题系列匹配的组件级 token,并写入 html[data-theme] */
|
|
30
36
|
render(): void;
|
|
31
|
-
new(customTheme?: RecursivePartial<Theme
|
|
37
|
+
new(customTheme?: RecursivePartial<Theme>, options?: {
|
|
38
|
+
series?: ThemeSeries;
|
|
39
|
+
}): UITheme;
|
|
32
40
|
}
|
|
33
41
|
//#endregion
|
|
34
|
-
export { UITheme };
|
|
42
|
+
export { ThemeSeries, UITheme, UIThemeOptions };
|
|
35
43
|
//# sourceMappingURL=ui-theme.d.ts.map
|
package/dist/theme/ui-theme.js
CHANGED
|
@@ -8,18 +8,15 @@ var UITheme = class UITheme {
|
|
|
8
8
|
static themeID = "ultra-ui-theme";
|
|
9
9
|
static adoptedSheet = null;
|
|
10
10
|
theme;
|
|
11
|
+
/** 主题所属系列:浅色 / 深色 */
|
|
12
|
+
series;
|
|
11
13
|
reactiveEnabled;
|
|
12
14
|
constructor(theme, options) {
|
|
13
15
|
this.reactiveEnabled = options?.reactive !== false;
|
|
16
|
+
this.series = options?.series ?? "light";
|
|
14
17
|
this.theme = reactive(theme);
|
|
15
18
|
if (this.reactiveEnabled) watch(this.theme, () => this.render(), { deep: true });
|
|
16
19
|
}
|
|
17
|
-
static setTheme(mode) {
|
|
18
|
-
if (typeof document === "undefined") return;
|
|
19
|
-
const el = document.documentElement;
|
|
20
|
-
if (mode === "auto") delete el.dataset.theme;
|
|
21
|
-
else el.dataset.theme = mode;
|
|
22
|
-
}
|
|
23
20
|
renderBase(theme, themeRules = [], parentKey = "--u") {
|
|
24
21
|
Object.keys(theme).forEach((key) => {
|
|
25
22
|
const value = theme[key];
|
|
@@ -185,29 +182,15 @@ var UITheme = class UITheme {
|
|
|
185
182
|
style.textContent = css;
|
|
186
183
|
}
|
|
187
184
|
}
|
|
188
|
-
/**
|
|
189
|
-
static injectBuiltInThemes(light, dark) {
|
|
190
|
-
const lightDecls = [...light.themeToDeclarationList(toRaw(light.theme)), ...componentCssVarsLightDecls];
|
|
191
|
-
const darkDecls = [...dark.themeToDeclarationList(toRaw(dark.theme)), ...componentCssVarsDarkDecls];
|
|
192
|
-
const lightBlock = UITheme.declarationBlock(lightDecls);
|
|
193
|
-
const darkBlock = UITheme.declarationBlock(darkDecls);
|
|
194
|
-
const css = [
|
|
195
|
-
`html { ${lightBlock} }`,
|
|
196
|
-
`@media (prefers-color-scheme: dark) {`,
|
|
197
|
-
` html:not([data-theme="light"]) { ${darkBlock} }`,
|
|
198
|
-
`}`,
|
|
199
|
-
`html[data-theme="light"] { ${lightBlock} }`,
|
|
200
|
-
`html[data-theme="dark"] { ${darkBlock} }`
|
|
201
|
-
].join("\n");
|
|
202
|
-
UITheme.applyGlobalCSS(css);
|
|
203
|
-
}
|
|
185
|
+
/** 注入主题:全局 token + 与主题系列匹配的组件级 token,并写入 html[data-theme] */
|
|
204
186
|
render() {
|
|
205
|
-
const componentDecls =
|
|
187
|
+
const componentDecls = this.series === "dark" ? componentCssVarsDarkDecls : componentCssVarsLightDecls;
|
|
206
188
|
const decls = [...this.themeToDeclarationList(toRaw(this.theme)), ...componentDecls];
|
|
207
189
|
const css = `html { ${UITheme.declarationBlock(decls)} }`;
|
|
208
190
|
UITheme.applyGlobalCSS(css);
|
|
191
|
+
if (typeof document !== "undefined") document.documentElement.dataset.theme = this.series;
|
|
209
192
|
}
|
|
210
|
-
new(customTheme = {}) {
|
|
193
|
+
new(customTheme = {}, options) {
|
|
211
194
|
function delEmpty(obj) {
|
|
212
195
|
Object.keys(obj).forEach((key) => {
|
|
213
196
|
const value = obj[key];
|
|
@@ -228,7 +211,10 @@ var UITheme = class UITheme {
|
|
|
228
211
|
});
|
|
229
212
|
}
|
|
230
213
|
deepMerge(base, customTheme);
|
|
231
|
-
return new UITheme(base, {
|
|
214
|
+
return new UITheme(base, {
|
|
215
|
+
reactive: this.reactiveEnabled,
|
|
216
|
+
series: options?.series ?? this.series
|
|
217
|
+
});
|
|
232
218
|
}
|
|
233
219
|
};
|
|
234
220
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-theme.js","names":[],"sources":["../../src/theme/ui-theme.ts"],"sourcesContent":["import { isObj, str } from '@cat-kit/core'\nimport { withUnit } from '@veltra/utils'\nimport { reactive, toRaw, watch } from 'vue'\n\nimport { componentCssVarsDarkDecls, componentCssVarsLightDecls } from './component-css-vars'\nimport { hexRgbOnly, hexWithAlpha, isHexColor, mixColor } from './helper'\nimport type { Theme } from './type'\n\ntype RecursivePartial<T> = {\n [P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P]\n}\n\nexport class UITheme {\n static themeID = 'ultra-ui-theme'\n\n private static adoptedSheet: CSSStyleSheet | null = null\n\n readonly theme: Theme\n\n private readonly reactiveEnabled: boolean\n\n constructor(theme: Theme, options?: { reactive?: boolean }) {\n this.reactiveEnabled = options?.reactive !== false\n this.theme = reactive(theme) as Theme\n if (this.reactiveEnabled) {\n watch(this.theme, () => this.render(), { deep: true })\n }\n }\n\n static setTheme(mode: 'light' | 'dark' | 'auto'): void {\n if (typeof document === 'undefined') return\n const el = document.documentElement\n if (mode === 'auto') {\n delete el.dataset.theme\n } else {\n el.dataset.theme = mode\n }\n }\n\n private renderBase(\n theme: Record<string, unknown>,\n themeRules: string[] = [],\n parentKey = '--u'\n ): string[] {\n Object.keys(theme).forEach((key) => {\n const value = theme[key]\n const varKey = `${parentKey.startsWith('-') ? parentKey : str(parentKey).kebabCase()}-${str(key).kebabCase()}`\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n this.renderBase(value as Record<string, unknown>, themeRules, varKey)\n } else {\n if (value || value === 0) {\n const v = withUnit(value as number | string, 'px')\n if (v !== undefined) {\n themeRules.push(`${varKey}: ${v}`)\n }\n }\n }\n })\n\n return themeRules\n }\n\n private renderBorder(theme: Theme): string {\n const shorthandKeys = (Object.keys(theme.border) as Array<keyof typeof theme.border>).filter(\n (key) => key !== 'mutedColor'\n )\n const border = shorthandKeys.map((key) => `var(--u-border-${key})`).join(' ')\n\n return [\n `--u-border: ${border}`,\n `--u-border-muted: var(--u-border-muted-color) var(--u-border-width) var(--u-border-style)`\n ].join(';')\n }\n\n private renderTypeColor(theme: Theme): string {\n const { color } = theme\n\n const types = Object.keys(color)\n const rates = [1, 3, 5, 7, 9]\n\n return types\n .map((type) => {\n const colorValue = color[type as keyof typeof color]! as `#${string}`\n\n return rates\n .map((rate) => {\n const light = `--u-color-${type}-light-${rate}: ${mixColor(\n colorValue,\n '#fff',\n rate / 10\n )}`\n const dark = `--u-color-${type}-dark-${rate}: ${mixColor(\n colorValue,\n '#000',\n rate / 10\n )}`\n return `${light};${dark}`\n })\n .join(';')\n })\n .join(';')\n }\n\n private renderShadow(_theme: Theme): string {\n const shadow = ['x', 'y', 'blur', 'spread', 'color']\n .map((k) => `var(--u-shadow-${k})`)\n .join(' ')\n\n return `--u-shadow: ${shadow}`\n }\n\n private renderBGColorAlpha(theme: Theme): string {\n const { color } = theme.bg\n const alphas = [70]\n const lines: string[] = []\n for (const type of Object.keys(color)) {\n const value = color[type as keyof typeof color]\n // 非 hex 颜色(如 glass 主题的 rgba())无法推导 alpha token,跳过避免生成 NaN 声明\n if (typeof value !== 'string' || !isHexColor(value)) continue\n const hex = value as `#${string}`\n lines.push(`--u-bg-color-${type}-alpha: ${hex}aa`)\n for (const a of alphas) {\n lines.push(`--u-bg-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n return lines.join(';')\n }\n\n /** 语义色 / 边框 / 阴影 / 文本色的 alpha token(替代 color-mix + transparent) */\n private renderAlphaTokens(theme: Theme): string {\n const alphas = [4, 5, 8, 10, 11, 16, 22, 28, 35, 40, 50, 52, 60, 70, 86]\n const lines: string[] = []\n\n for (const type of Object.keys(theme.color)) {\n const value = theme.color[type as keyof typeof theme.color]\n if (typeof value !== 'string' || !isHexColor(value)) continue\n const hex = value as `#${string}`\n for (const a of alphas) {\n lines.push(`--u-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n\n const borderHex = theme.border.color as `#${string}`\n if (isHexColor(borderHex)) {\n for (const a of alphas) {\n lines.push(`--u-border-color-a-${a}: ${hexWithAlpha(borderHex, a)}`)\n }\n }\n\n const shadowHex = hexRgbOnly(theme.shadow.color)\n // 阴影色非 hex(如 rgba())时无法推导 alpha token,跳过避免生成 NaN 声明\n if (isHexColor(shadowHex)) {\n for (const a of alphas) {\n lines.push(`--u-shadow-color-a-${a}: ${hexWithAlpha(shadowHex, a)}`)\n }\n }\n\n const textColor = theme['text-color']\n for (const type of Object.keys(textColor)) {\n const value = textColor[type as keyof typeof textColor]\n if (typeof value === 'string' && isHexColor(value)) {\n const hex = value as `#${string}`\n for (const a of alphas) {\n lines.push(`--u-text-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n }\n\n return lines.join(';')\n }\n\n /** 组件级两色混合 token(batch-edit / kbd) */\n private renderComponentMixTokens(theme: Theme): string {\n const hover = theme.bg.color.hover as `#${string}`\n const top = theme.bg.color.top as `#${string}`\n const black = theme.bg.color.black as `#${string}`\n const borderColor = theme.border.color as `#${string}`\n const shadowColor = hexRgbOnly(theme.shadow.color)\n\n // 依赖色含非 hex 值(如 glass 主题的 rgba())时整体跳过,避免生成 NaN 声明;\n // 组件侧 var() fallback 会接管,行为等同于此前的无效声明\n if (![hover, top, black, borderColor].every(isHexColor)) return ''\n\n const formHeaderBg = mixColor(top, hover, 0.04)\n\n const kbdInset = mixColor(top, black, 0.25)\n const kbdDropShadow = isHexColor(shadowColor)\n ? hexWithAlpha(shadowColor, 60)\n : 'rgba(0, 0, 0, 0.6)'\n\n const lines = [\n `--u-batch-edit-form-header-bg: ${formHeaderBg}`,\n `--u-kbd-inset-shadow: ${kbdInset}`,\n `--u-kbd-border-shadow: ${hexWithAlpha(borderColor, 50)}`,\n `--u-kbd-drop-shadow: ${kbdDropShadow}`\n ]\n\n return lines.join(';')\n }\n\n private renderBGFilter(theme: Theme): string {\n const { filter } = theme.bg\n const value = filter.blur === 'none' ? 'none' : `${filter.blur} ${filter.saturate}`\n return `--u-bg-filter: ${value}`\n }\n\n /** 主题变量声明列表,均为 `--u-` 前缀的 CSS 自定义属性 */\n themeToDeclarationList(theme: Theme): string[] {\n const raw = theme as unknown as Record<string, unknown>\n const lines: string[] = [...this.renderBase(raw)]\n const chunks = [\n this.renderTypeColor(theme),\n this.renderShadow(theme),\n this.renderBGColorAlpha(theme),\n this.renderAlphaTokens(theme),\n this.renderComponentMixTokens(theme),\n this.renderBGFilter(theme),\n this.renderBorder(theme)\n ]\n for (const c of chunks) {\n lines.push(\n ...c\n .split(';')\n .map((s) => s.trim())\n .filter(Boolean)\n )\n }\n return lines\n }\n\n private static declarationBlock(decls: string[]): string {\n return decls.join(';')\n }\n\n private static removeExistingStyleTag(doc: Document): void {\n const el = doc.getElementById(UITheme.themeID)\n if (el?.parentNode) {\n el.parentNode.removeChild(el)\n }\n }\n\n private static applyGlobalCSS(css: string): void {\n const doc = typeof document !== 'undefined' ? document : undefined\n if (!doc) return\n\n const canAdopt =\n typeof CSSStyleSheet !== 'undefined' && 'adoptedStyleSheets' in Document.prototype\n\n if (canAdopt) {\n UITheme.removeExistingStyleTag(doc)\n if (!UITheme.adoptedSheet) {\n UITheme.adoptedSheet = new CSSStyleSheet()\n doc.adoptedStyleSheets = [...doc.adoptedStyleSheets, UITheme.adoptedSheet]\n }\n UITheme.adoptedSheet.replaceSync(css)\n } else {\n if (UITheme.adoptedSheet) {\n UITheme.adoptedSheet = null\n }\n let style = doc.getElementById(UITheme.themeID)\n if (!style) {\n style = doc.createElement('style')\n style.id = UITheme.themeID\n doc.head.appendChild(style)\n }\n style.textContent = css\n }\n }\n\n /** 内置 light/dark:支持 data-theme 与 prefers-color-scheme */\n static injectBuiltInThemes(light: UITheme, dark: UITheme): void {\n const lightDecls = [\n ...light.themeToDeclarationList(toRaw(light.theme)),\n ...componentCssVarsLightDecls\n ]\n const darkDecls = [\n ...dark.themeToDeclarationList(toRaw(dark.theme)),\n ...componentCssVarsDarkDecls\n ]\n const lightBlock = UITheme.declarationBlock(lightDecls)\n const darkBlock = UITheme.declarationBlock(darkDecls)\n\n const css = [\n `html { ${lightBlock} }`,\n `@media (prefers-color-scheme: dark) {`,\n ` html:not([data-theme=\"light\"]) { ${darkBlock} }`,\n `}`,\n `html[data-theme=\"light\"] { ${lightBlock} }`,\n `html[data-theme=\"dark\"] { ${darkBlock} }`\n ].join('\\n')\n\n UITheme.applyGlobalCSS(css)\n }\n\n render(): void {\n const isDark =\n typeof document !== 'undefined' && document.documentElement.dataset.theme === 'dark'\n const componentDecls = isDark ? componentCssVarsDarkDecls : componentCssVarsLightDecls\n const decls = [...this.themeToDeclarationList(toRaw(this.theme)), ...componentDecls]\n const block = UITheme.declarationBlock(decls)\n const css = `html { ${block} }`\n UITheme.applyGlobalCSS(css)\n }\n\n new(customTheme: RecursivePartial<Theme> = {}): UITheme {\n function delEmpty(obj: Record<string, unknown>) {\n Object.keys(obj).forEach((key) => {\n const value = obj[key]\n if (isObj(value)) {\n return delEmpty(value as Record<string, unknown>)\n }\n if (!value && value !== 0) {\n delete obj[key]\n }\n })\n }\n\n delEmpty(customTheme as Record<string, unknown>)\n\n const base = JSON.parse(JSON.stringify(toRaw(this.theme))) as Record<string, any>\n\n function deepMerge(target: any, source: any) {\n if (typeof source !== 'object' || source === null) return\n Object.keys(source).forEach((key) => {\n const sourceVal = source[key]\n if (typeof sourceVal === 'object' && sourceVal !== null && !Array.isArray(sourceVal)) {\n if (typeof target[key] !== 'object' || target[key] === null) {\n target[key] = {}\n }\n deepMerge(target[key], sourceVal)\n } else {\n target[key] = sourceVal\n }\n })\n }\n\n deepMerge(base, customTheme)\n return new UITheme(base as Theme, { reactive: this.reactiveEnabled })\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ui-theme.js","names":[],"sources":["../../src/theme/ui-theme.ts"],"sourcesContent":["import { isObj, str } from '@cat-kit/core'\nimport { withUnit } from '@veltra/utils'\nimport { reactive, toRaw, watch } from 'vue'\n\nimport { componentCssVarsDarkDecls, componentCssVarsLightDecls } from './component-css-vars'\nimport { hexRgbOnly, hexWithAlpha, isHexColor, mixColor } from './helper'\nimport type { Theme } from './type'\n\ntype RecursivePartial<T> = {\n [P in keyof T]?: T[P] extends object ? RecursivePartial<T[P]> : T[P]\n}\n\n/** 主题系列:浅色或深色。决定组件级 token 与 html[data-theme] */\nexport type ThemeSeries = 'light' | 'dark'\n\nexport interface UIThemeOptions {\n /** 主题变更时是否自动重渲染,默认 true */\n reactive?: boolean\n /** 主题所属系列,默认 'light' */\n series?: ThemeSeries\n}\n\nexport class UITheme {\n static themeID = 'ultra-ui-theme'\n\n private static adoptedSheet: CSSStyleSheet | null = null\n\n readonly theme: Theme\n\n /** 主题所属系列:浅色 / 深色 */\n series: ThemeSeries\n\n private readonly reactiveEnabled: boolean\n\n constructor(theme: Theme, options?: UIThemeOptions) {\n this.reactiveEnabled = options?.reactive !== false\n this.series = options?.series ?? 'light'\n this.theme = reactive(theme) as Theme\n if (this.reactiveEnabled) {\n watch(this.theme, () => this.render(), { deep: true })\n }\n }\n\n private renderBase(\n theme: Record<string, unknown>,\n themeRules: string[] = [],\n parentKey = '--u'\n ): string[] {\n Object.keys(theme).forEach((key) => {\n const value = theme[key]\n const varKey = `${parentKey.startsWith('-') ? parentKey : str(parentKey).kebabCase()}-${str(key).kebabCase()}`\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n this.renderBase(value as Record<string, unknown>, themeRules, varKey)\n } else {\n if (value || value === 0) {\n const v = withUnit(value as number | string, 'px')\n if (v !== undefined) {\n themeRules.push(`${varKey}: ${v}`)\n }\n }\n }\n })\n\n return themeRules\n }\n\n private renderBorder(theme: Theme): string {\n const shorthandKeys = (Object.keys(theme.border) as Array<keyof typeof theme.border>).filter(\n (key) => key !== 'mutedColor'\n )\n const border = shorthandKeys.map((key) => `var(--u-border-${key})`).join(' ')\n\n return [\n `--u-border: ${border}`,\n `--u-border-muted: var(--u-border-muted-color) var(--u-border-width) var(--u-border-style)`\n ].join(';')\n }\n\n private renderTypeColor(theme: Theme): string {\n const { color } = theme\n\n const types = Object.keys(color)\n const rates = [1, 3, 5, 7, 9]\n\n return types\n .map((type) => {\n const colorValue = color[type as keyof typeof color]! as `#${string}`\n\n return rates\n .map((rate) => {\n const light = `--u-color-${type}-light-${rate}: ${mixColor(\n colorValue,\n '#fff',\n rate / 10\n )}`\n const dark = `--u-color-${type}-dark-${rate}: ${mixColor(\n colorValue,\n '#000',\n rate / 10\n )}`\n return `${light};${dark}`\n })\n .join(';')\n })\n .join(';')\n }\n\n private renderShadow(_theme: Theme): string {\n const shadow = ['x', 'y', 'blur', 'spread', 'color']\n .map((k) => `var(--u-shadow-${k})`)\n .join(' ')\n\n return `--u-shadow: ${shadow}`\n }\n\n private renderBGColorAlpha(theme: Theme): string {\n const { color } = theme.bg\n const alphas = [70]\n const lines: string[] = []\n for (const type of Object.keys(color)) {\n const value = color[type as keyof typeof color]\n // 非 hex 颜色(如 glass 主题的 rgba())无法推导 alpha token,跳过避免生成 NaN 声明\n if (typeof value !== 'string' || !isHexColor(value)) continue\n const hex = value as `#${string}`\n lines.push(`--u-bg-color-${type}-alpha: ${hex}aa`)\n for (const a of alphas) {\n lines.push(`--u-bg-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n return lines.join(';')\n }\n\n /** 语义色 / 边框 / 阴影 / 文本色的 alpha token(替代 color-mix + transparent) */\n private renderAlphaTokens(theme: Theme): string {\n const alphas = [4, 5, 8, 10, 11, 16, 22, 28, 35, 40, 50, 52, 60, 70, 86]\n const lines: string[] = []\n\n for (const type of Object.keys(theme.color)) {\n const value = theme.color[type as keyof typeof theme.color]\n if (typeof value !== 'string' || !isHexColor(value)) continue\n const hex = value as `#${string}`\n for (const a of alphas) {\n lines.push(`--u-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n\n const borderHex = theme.border.color as `#${string}`\n if (isHexColor(borderHex)) {\n for (const a of alphas) {\n lines.push(`--u-border-color-a-${a}: ${hexWithAlpha(borderHex, a)}`)\n }\n }\n\n const shadowHex = hexRgbOnly(theme.shadow.color)\n // 阴影色非 hex(如 rgba())时无法推导 alpha token,跳过避免生成 NaN 声明\n if (isHexColor(shadowHex)) {\n for (const a of alphas) {\n lines.push(`--u-shadow-color-a-${a}: ${hexWithAlpha(shadowHex, a)}`)\n }\n }\n\n const textColor = theme['text-color']\n for (const type of Object.keys(textColor)) {\n const value = textColor[type as keyof typeof textColor]\n if (typeof value === 'string' && isHexColor(value)) {\n const hex = value as `#${string}`\n for (const a of alphas) {\n lines.push(`--u-text-color-${type}-a-${a}: ${hexWithAlpha(hex, a)}`)\n }\n }\n }\n\n return lines.join(';')\n }\n\n /** 组件级两色混合 token(batch-edit / kbd) */\n private renderComponentMixTokens(theme: Theme): string {\n const hover = theme.bg.color.hover as `#${string}`\n const top = theme.bg.color.top as `#${string}`\n const black = theme.bg.color.black as `#${string}`\n const borderColor = theme.border.color as `#${string}`\n const shadowColor = hexRgbOnly(theme.shadow.color)\n\n // 依赖色含非 hex 值(如 glass 主题的 rgba())时整体跳过,避免生成 NaN 声明;\n // 组件侧 var() fallback 会接管,行为等同于此前的无效声明\n if (![hover, top, black, borderColor].every(isHexColor)) return ''\n\n const formHeaderBg = mixColor(top, hover, 0.04)\n\n const kbdInset = mixColor(top, black, 0.25)\n const kbdDropShadow = isHexColor(shadowColor)\n ? hexWithAlpha(shadowColor, 60)\n : 'rgba(0, 0, 0, 0.6)'\n\n const lines = [\n `--u-batch-edit-form-header-bg: ${formHeaderBg}`,\n `--u-kbd-inset-shadow: ${kbdInset}`,\n `--u-kbd-border-shadow: ${hexWithAlpha(borderColor, 50)}`,\n `--u-kbd-drop-shadow: ${kbdDropShadow}`\n ]\n\n return lines.join(';')\n }\n\n private renderBGFilter(theme: Theme): string {\n const { filter } = theme.bg\n const value = filter.blur === 'none' ? 'none' : `${filter.blur} ${filter.saturate}`\n return `--u-bg-filter: ${value}`\n }\n\n /** 主题变量声明列表,均为 `--u-` 前缀的 CSS 自定义属性 */\n themeToDeclarationList(theme: Theme): string[] {\n const raw = theme as unknown as Record<string, unknown>\n const lines: string[] = [...this.renderBase(raw)]\n const chunks = [\n this.renderTypeColor(theme),\n this.renderShadow(theme),\n this.renderBGColorAlpha(theme),\n this.renderAlphaTokens(theme),\n this.renderComponentMixTokens(theme),\n this.renderBGFilter(theme),\n this.renderBorder(theme)\n ]\n for (const c of chunks) {\n lines.push(\n ...c\n .split(';')\n .map((s) => s.trim())\n .filter(Boolean)\n )\n }\n return lines\n }\n\n private static declarationBlock(decls: string[]): string {\n return decls.join(';')\n }\n\n private static removeExistingStyleTag(doc: Document): void {\n const el = doc.getElementById(UITheme.themeID)\n if (el?.parentNode) {\n el.parentNode.removeChild(el)\n }\n }\n\n private static applyGlobalCSS(css: string): void {\n const doc = typeof document !== 'undefined' ? document : undefined\n if (!doc) return\n\n const canAdopt =\n typeof CSSStyleSheet !== 'undefined' && 'adoptedStyleSheets' in Document.prototype\n\n if (canAdopt) {\n UITheme.removeExistingStyleTag(doc)\n if (!UITheme.adoptedSheet) {\n UITheme.adoptedSheet = new CSSStyleSheet()\n doc.adoptedStyleSheets = [...doc.adoptedStyleSheets, UITheme.adoptedSheet]\n }\n UITheme.adoptedSheet.replaceSync(css)\n } else {\n if (UITheme.adoptedSheet) {\n UITheme.adoptedSheet = null\n }\n let style = doc.getElementById(UITheme.themeID)\n if (!style) {\n style = doc.createElement('style')\n style.id = UITheme.themeID\n doc.head.appendChild(style)\n }\n style.textContent = css\n }\n }\n\n /** 注入主题:全局 token + 与主题系列匹配的组件级 token,并写入 html[data-theme] */\n render(): void {\n const componentDecls =\n this.series === 'dark' ? componentCssVarsDarkDecls : componentCssVarsLightDecls\n const decls = [...this.themeToDeclarationList(toRaw(this.theme)), ...componentDecls]\n const block = UITheme.declarationBlock(decls)\n const css = `html { ${block} }`\n UITheme.applyGlobalCSS(css)\n if (typeof document !== 'undefined') {\n document.documentElement.dataset.theme = this.series\n }\n }\n\n new(customTheme: RecursivePartial<Theme> = {}, options?: { series?: ThemeSeries }): UITheme {\n function delEmpty(obj: Record<string, unknown>) {\n Object.keys(obj).forEach((key) => {\n const value = obj[key]\n if (isObj(value)) {\n return delEmpty(value as Record<string, unknown>)\n }\n if (!value && value !== 0) {\n delete obj[key]\n }\n })\n }\n\n delEmpty(customTheme as Record<string, unknown>)\n\n const base = JSON.parse(JSON.stringify(toRaw(this.theme))) as Record<string, any>\n\n function deepMerge(target: any, source: any) {\n if (typeof source !== 'object' || source === null) return\n Object.keys(source).forEach((key) => {\n const sourceVal = source[key]\n if (typeof sourceVal === 'object' && sourceVal !== null && !Array.isArray(sourceVal)) {\n if (typeof target[key] !== 'object' || target[key] === null) {\n target[key] = {}\n }\n deepMerge(target[key], sourceVal)\n } else {\n target[key] = sourceVal\n }\n })\n }\n\n deepMerge(base, customTheme)\n return new UITheme(base as Theme, {\n reactive: this.reactiveEnabled,\n series: options?.series ?? this.series\n })\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veltra/styles",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"homepage": "https://github.com/cabinet-fe/ultra-ui#readme",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/cabinet-fe/ultra-ui/issues"
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"@cat-kit/core": ">=1.1.8",
|
|
54
|
-
"@veltra/compositions": "^1.
|
|
55
|
-
"@veltra/utils": "^1.
|
|
54
|
+
"@veltra/compositions": "^1.7.0",
|
|
55
|
+
"@veltra/utils": "^1.7.0",
|
|
56
56
|
"vue": ">=3.5.41"
|
|
57
57
|
}
|
|
58
58
|
}
|
package/src/_mixins.scss
CHANGED
|
@@ -146,17 +146,11 @@ $namespace: 'u' !default;
|
|
|
146
146
|
@return var(--#{$namespace}-breakpoint-#{$point});
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
/**
|
|
149
|
+
/** 暗色:`UITheme.render()` 按主题系列写入 html[data-theme] */
|
|
150
150
|
@mixin dark {
|
|
151
151
|
html[data-theme='dark'] & {
|
|
152
152
|
@content;
|
|
153
153
|
}
|
|
154
|
-
|
|
155
|
-
@media (prefers-color-scheme: dark) {
|
|
156
|
-
html:not([data-theme='light']) & {
|
|
157
|
-
@content;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
154
|
}
|
|
161
155
|
|
|
162
156
|
@mixin xs {
|
|
@@ -2,11 +2,15 @@ import { describe, expect, it } from 'vitest'
|
|
|
2
2
|
|
|
3
3
|
import { componentCssVarsDark, componentCssVarsLight } from '../component-css-vars'
|
|
4
4
|
import {
|
|
5
|
+
ancientTheme,
|
|
5
6
|
darkTheme,
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
glassTheme,
|
|
8
|
+
heroTheme,
|
|
8
9
|
lightTheme,
|
|
9
|
-
|
|
10
|
+
midnightTheme,
|
|
11
|
+
neonTheme,
|
|
12
|
+
oceanTheme,
|
|
13
|
+
sakuraTheme
|
|
10
14
|
} from '../presets'
|
|
11
15
|
import { UITheme } from '../ui-theme'
|
|
12
16
|
|
|
@@ -21,15 +25,26 @@ describe('UITheme', () => {
|
|
|
21
25
|
}
|
|
22
26
|
})
|
|
23
27
|
|
|
24
|
-
it('
|
|
28
|
+
it('render writes data-theme from theme series', () => {
|
|
25
29
|
if (typeof document === 'undefined') return
|
|
26
30
|
const el = document.documentElement
|
|
27
|
-
|
|
31
|
+
darkTheme.render()
|
|
28
32
|
expect(el.dataset.theme).toBe('dark')
|
|
29
|
-
|
|
33
|
+
lightTheme.render()
|
|
30
34
|
expect(el.dataset.theme).toBe('light')
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('derived themes inherit series unless overridden', () => {
|
|
38
|
+
expect(darkTheme.series).toBe('dark')
|
|
39
|
+
expect(glassTheme.series).toBe('dark')
|
|
40
|
+
expect(heroTheme.series).toBe('light')
|
|
41
|
+
expect(ancientTheme.series).toBe('light')
|
|
42
|
+
expect(sakuraTheme.series).toBe('light')
|
|
43
|
+
expect(oceanTheme.series).toBe('light')
|
|
44
|
+
expect(midnightTheme.series).toBe('dark')
|
|
45
|
+
expect(neonTheme.series).toBe('dark')
|
|
46
|
+
expect(lightTheme.new({}).series).toBe('light')
|
|
47
|
+
expect(lightTheme.new({}, { series: 'dark' }).series).toBe('dark')
|
|
33
48
|
})
|
|
34
49
|
|
|
35
50
|
it('renderAlphaTokens emits semantic color alpha vars', () => {
|
|
@@ -46,6 +61,14 @@ describe('UITheme', () => {
|
|
|
46
61
|
expect(decls.some((d) => d.startsWith('--u-collapse-item-bg:'))).toBe(false)
|
|
47
62
|
})
|
|
48
63
|
|
|
64
|
+
it('glass declares kbd and batch-edit vars explicitly (non-hex bg skips mix tokens)', () => {
|
|
65
|
+
const decls = glassTheme.themeToDeclarationList(glassTheme.theme)
|
|
66
|
+
expect(decls.some((d) => d.startsWith('--u-kbd-inset-shadow:'))).toBe(true)
|
|
67
|
+
expect(decls.some((d) => d.startsWith('--u-kbd-border-shadow:'))).toBe(true)
|
|
68
|
+
expect(decls.some((d) => d.startsWith('--u-kbd-drop-shadow:'))).toBe(true)
|
|
69
|
+
expect(decls.some((d) => d.startsWith('--u-batch-edit-form-header-bg:'))).toBe(true)
|
|
70
|
+
})
|
|
71
|
+
|
|
49
72
|
it('emits elevation and motion tokens', () => {
|
|
50
73
|
const decls = lightTheme.themeToDeclarationList(lightTheme.theme)
|
|
51
74
|
expect(decls.some((d) => d.startsWith('--u-shadow-sm:'))).toBe(true)
|
|
@@ -60,7 +83,17 @@ describe('UITheme', () => {
|
|
|
60
83
|
})
|
|
61
84
|
|
|
62
85
|
it('no preset emits invalid NaN declarations', () => {
|
|
63
|
-
const themes = [
|
|
86
|
+
const themes = [
|
|
87
|
+
lightTheme,
|
|
88
|
+
darkTheme,
|
|
89
|
+
heroTheme,
|
|
90
|
+
glassTheme,
|
|
91
|
+
ancientTheme,
|
|
92
|
+
sakuraTheme,
|
|
93
|
+
oceanTheme,
|
|
94
|
+
midnightTheme,
|
|
95
|
+
neonTheme
|
|
96
|
+
]
|
|
64
97
|
for (const t of themes) {
|
|
65
98
|
const decls = t.themeToDeclarationList(t.theme)
|
|
66
99
|
for (const d of decls) {
|
package/src/theme/load-theme.ts
CHANGED
|
@@ -1,45 +1,30 @@
|
|
|
1
1
|
import { useConfig } from '@veltra/compositions'
|
|
2
2
|
import { shallowRef, type ShallowRef } from 'vue'
|
|
3
3
|
|
|
4
|
-
import { lightTheme
|
|
5
|
-
import { UITheme } from './ui-theme'
|
|
4
|
+
import { lightTheme } from './presets'
|
|
5
|
+
import type { UITheme } from './ui-theme'
|
|
6
6
|
|
|
7
7
|
export const currentTheme: ShallowRef<UITheme | undefined> = shallowRef<UITheme>()
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
UITheme.setTheme(mode)
|
|
11
|
-
}
|
|
9
|
+
const SIZES = ['small', 'default', 'large'] as const
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
12
|
* @description 加载主题, 如果你是 SSR 环境,
|
|
15
13
|
* 请在 `onMounted` 中调用,否则你可以在
|
|
16
14
|
* 项目的入口环境(通常是 main.ts)或其他全局环境中调用。
|
|
17
15
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* 主题自带系列(`theme.series`),应用时会注入全局 token 与
|
|
17
|
+
* 同系列的组件级 token,并把 `html[data-theme]` 置为对应系列。
|
|
20
18
|
*/
|
|
21
|
-
export function loadTheme(theme
|
|
22
|
-
currentTheme.value = theme
|
|
23
|
-
|
|
24
|
-
const { config } = useConfig()
|
|
25
|
-
if (typeof document !== 'undefined') {
|
|
26
|
-
document.documentElement.classList.add(config.size)
|
|
27
|
-
}
|
|
19
|
+
export function loadTheme(theme: UITheme = lightTheme): void {
|
|
20
|
+
currentTheme.value = theme
|
|
28
21
|
|
|
29
22
|
if (typeof document === 'undefined') return
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
currentTheme.value = theme
|
|
24
|
+
const { config } = useConfig()
|
|
25
|
+
const classList = document.documentElement.classList
|
|
26
|
+
classList.remove(...SIZES.filter((s) => s !== config.size))
|
|
27
|
+
classList.add(config.size)
|
|
38
28
|
|
|
39
|
-
|
|
40
|
-
UITheme.injectBuiltInThemes(lightTheme, darkTheme)
|
|
41
|
-
UITheme.setTheme(theme === darkTheme ? 'dark' : 'light')
|
|
42
|
-
} else {
|
|
43
|
-
theme.render()
|
|
44
|
-
}
|
|
29
|
+
theme.render()
|
|
45
30
|
}
|