@aquiferre/theme-kit 0.1.3 → 0.2.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/index.d.ts +9 -0
- package/index.ts +9 -0
- package/locale.ts +80 -0
- package/messages/en-US.ts +9 -0
- package/messages/index.ts +14 -0
- package/messages/zh-CN.ts +9 -0
- package/package.json +9 -3
- package/theme.ts +10 -9
- package/variables.css +12 -0
package/index.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
1
|
export { useThemeStore, themeList, type ThemeMode, type ThemeInfo } from './theme'
|
|
2
|
+
export {
|
|
3
|
+
useLocaleStore,
|
|
4
|
+
resolveInitialLocale,
|
|
5
|
+
SUPPORTED_LOCALES,
|
|
6
|
+
DEFAULT_LOCALE,
|
|
7
|
+
type LocaleCode,
|
|
8
|
+
type LocaleInfo,
|
|
9
|
+
} from './locale'
|
|
10
|
+
export { themeKitMessages, themeKitMessagesZhCN, themeKitMessagesEnUS } from './messages'
|
|
2
11
|
export { ThemeKitPlugin, type SystemColors } from './plugin'
|
|
3
12
|
export { useSystemColors, useSystemColor } from './composables'
|
package/index.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
1
|
export { useThemeStore, themeList, type ThemeMode, type ThemeInfo } from './theme'
|
|
2
|
+
export {
|
|
3
|
+
useLocaleStore,
|
|
4
|
+
resolveInitialLocale,
|
|
5
|
+
SUPPORTED_LOCALES,
|
|
6
|
+
DEFAULT_LOCALE,
|
|
7
|
+
type LocaleCode,
|
|
8
|
+
type LocaleInfo,
|
|
9
|
+
} from './locale'
|
|
10
|
+
export { themeKitMessages, themeKitMessagesZhCN, themeKitMessagesEnUS } from './messages'
|
|
2
11
|
export { ThemeKitPlugin, type SystemColors } from './plugin'
|
|
3
12
|
export { useSystemColors, useSystemColor } from './composables'
|
package/locale.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { ref, watch } from 'vue'
|
|
3
|
+
|
|
4
|
+
export type LocaleCode = 'zh-CN' | 'en-US'
|
|
5
|
+
|
|
6
|
+
export interface LocaleInfo {
|
|
7
|
+
code: LocaleCode
|
|
8
|
+
/** 母语名称:语言切换器选项固定用母语展示,不随界面语言翻译 */
|
|
9
|
+
label: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const DEFAULT_LOCALE: LocaleCode = 'zh-CN'
|
|
13
|
+
|
|
14
|
+
/** 支持的语言列表,新增语言需同步 front-kit 词典与后端 users.language 白名单 */
|
|
15
|
+
export const SUPPORTED_LOCALES: readonly LocaleInfo[] = [
|
|
16
|
+
{ code: 'zh-CN', label: '简体中文' },
|
|
17
|
+
{ code: 'en-US', label: 'English' },
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
const STORAGE_KEY = 'locale'
|
|
21
|
+
|
|
22
|
+
const supportedCodes = SUPPORTED_LOCALES.map((l) => l.code)
|
|
23
|
+
|
|
24
|
+
function isSupported(l: string | null): l is LocaleCode {
|
|
25
|
+
return !!l && (supportedCodes as string[]).includes(l)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function browserLocale(): LocaleCode | null {
|
|
29
|
+
if (typeof window === 'undefined' || !navigator.language) return null
|
|
30
|
+
const lang = navigator.language.toLowerCase()
|
|
31
|
+
if (lang.startsWith('zh')) return 'zh-CN'
|
|
32
|
+
if (lang.startsWith('en')) return 'en-US'
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 初始语言解析(非响应式纯函数,可在组件外/创建 i18n 实例前调用):
|
|
38
|
+
* localStorage > 浏览器语言白名单 > zh-CN。
|
|
39
|
+
*/
|
|
40
|
+
export function resolveInitialLocale(): LocaleCode {
|
|
41
|
+
if (typeof window === 'undefined') return DEFAULT_LOCALE
|
|
42
|
+
const stored = localStorage.getItem(STORAGE_KEY)
|
|
43
|
+
if (isSupported(stored)) return stored
|
|
44
|
+
return browserLocale() ?? DEFAULT_LOCALE
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 语言偏好 store:仿 useThemeStore —— localStorage 'locale' 持久化 +
|
|
49
|
+
* storage 事件跨 tab 同步 + 自动同步 <html lang>。
|
|
50
|
+
* 登录后以服务端 profile.language 为准覆盖(app 侧实现)。
|
|
51
|
+
*/
|
|
52
|
+
export const useLocaleStore = defineStore('locale', () => {
|
|
53
|
+
const locale = ref<LocaleCode>(resolveInitialLocale())
|
|
54
|
+
|
|
55
|
+
function apply(l: LocaleCode) {
|
|
56
|
+
if (typeof document !== 'undefined') {
|
|
57
|
+
document.documentElement.lang = l
|
|
58
|
+
}
|
|
59
|
+
if (typeof window !== 'undefined') {
|
|
60
|
+
localStorage.setItem(STORAGE_KEY, l)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function setLocale(l: LocaleCode) {
|
|
65
|
+
if (!isSupported(l)) return
|
|
66
|
+
locale.value = l
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
watch(locale, apply, { immediate: true })
|
|
70
|
+
|
|
71
|
+
if (typeof window !== 'undefined') {
|
|
72
|
+
window.addEventListener('storage', (event) => {
|
|
73
|
+
if (event.key === STORAGE_KEY && isSupported(event.newValue)) {
|
|
74
|
+
locale.value = event.newValue
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { locale, setLocale }
|
|
80
|
+
})
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// theme-kit 词典(en-US):扁平 key 结构,key 前缀 theme.,由 app 在 createI18n 时与自身词典合并
|
|
2
|
+
export const themeKitMessagesEnUS: Record<string, string> = {
|
|
3
|
+
'theme.light.label': 'Light',
|
|
4
|
+
'theme.light.description': 'Bright and clear interface',
|
|
5
|
+
'theme.dark.label': 'Dark',
|
|
6
|
+
'theme.dark.description': 'Eye-friendly dark theme',
|
|
7
|
+
'theme.neutral.label': 'Neutral',
|
|
8
|
+
'theme.neutral.description': 'Low-saturation soft tones',
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { themeKitMessagesZhCN } from './zh-CN'
|
|
2
|
+
import { themeKitMessagesEnUS } from './en-US'
|
|
3
|
+
|
|
4
|
+
export { themeKitMessagesZhCN } from './zh-CN'
|
|
5
|
+
export { themeKitMessagesEnUS } from './en-US'
|
|
6
|
+
|
|
7
|
+
/** 各语言词典聚合:app 侧 createI18n 时展开合并 */
|
|
8
|
+
export const themeKitMessages = {
|
|
9
|
+
'zh-CN': themeKitMessagesZhCN,
|
|
10
|
+
'en-US': themeKitMessagesEnUS,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type ThemeKitMessagesZh = typeof themeKitMessagesZhCN
|
|
14
|
+
export type ThemeKitMessagesEn = typeof themeKitMessagesEnUS
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// theme-kit 词典(zh-CN):扁平 key 结构,key 前缀 theme.,由 app 在 createI18n 时与自身词典合并
|
|
2
|
+
export const themeKitMessagesZhCN: Record<string, string> = {
|
|
3
|
+
'theme.light.label': '浅色模式',
|
|
4
|
+
'theme.light.description': '明亮清晰的界面',
|
|
5
|
+
'theme.dark.label': '深色模式',
|
|
6
|
+
'theme.dark.description': '护眼科普的暗色主题',
|
|
7
|
+
'theme.neutral.label': '中性模式',
|
|
8
|
+
'theme.neutral.description': '低饱和度的柔和色调',
|
|
9
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aquiferre/theme-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -15,9 +15,15 @@
|
|
|
15
15
|
"./components.css": "./components.css",
|
|
16
16
|
"./tailwind.preset": "./tailwind.preset.js"
|
|
17
17
|
},
|
|
18
|
-
"files": [
|
|
18
|
+
"files": [
|
|
19
|
+
"*.css",
|
|
20
|
+
"*.ts",
|
|
21
|
+
"*.js",
|
|
22
|
+
"*.d.ts",
|
|
23
|
+
"messages/*.ts"
|
|
24
|
+
],
|
|
19
25
|
"peerDependencies": {
|
|
20
|
-
"pinia": "
|
|
26
|
+
"pinia": ">=2.0.0",
|
|
21
27
|
"vue": "^3.0.0"
|
|
22
28
|
},
|
|
23
29
|
"publishConfig": {
|
package/theme.ts
CHANGED
|
@@ -5,32 +5,33 @@ export type ThemeMode = 'light' | 'dark' | 'neutral'
|
|
|
5
5
|
|
|
6
6
|
export interface ThemeInfo {
|
|
7
7
|
mode: ThemeMode
|
|
8
|
-
|
|
8
|
+
labelKey: string
|
|
9
9
|
icon: string
|
|
10
|
-
|
|
10
|
+
descriptionKey: string
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* 主题列表 (内置数据)
|
|
14
|
+
* 主题列表 (内置数据):文案经 labelKey/descriptionKey 由消费组件 t() 翻译,
|
|
15
|
+
* 词典来自 theme-kit/messages(key 前缀 theme.)。
|
|
15
16
|
*/
|
|
16
17
|
export const themeList: ThemeInfo[] = [
|
|
17
18
|
{
|
|
18
19
|
mode: 'light',
|
|
19
|
-
|
|
20
|
+
labelKey: 'theme.light.label',
|
|
20
21
|
icon: '☀️',
|
|
21
|
-
|
|
22
|
+
descriptionKey: 'theme.light.description'
|
|
22
23
|
},
|
|
23
24
|
{
|
|
24
25
|
mode: 'dark',
|
|
25
|
-
|
|
26
|
+
labelKey: 'theme.dark.label',
|
|
26
27
|
icon: '🌙',
|
|
27
|
-
|
|
28
|
+
descriptionKey: 'theme.dark.description'
|
|
28
29
|
},
|
|
29
30
|
{
|
|
30
31
|
mode: 'neutral',
|
|
31
|
-
|
|
32
|
+
labelKey: 'theme.neutral.label',
|
|
32
33
|
icon: '✨',
|
|
33
|
-
|
|
34
|
+
descriptionKey: 'theme.neutral.description'
|
|
34
35
|
}
|
|
35
36
|
]
|
|
36
37
|
|
package/variables.css
CHANGED
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
--color-border-focus: #2563eb;
|
|
29
29
|
--color-border-danger: #dc2626;
|
|
30
30
|
|
|
31
|
+
/* Toggle switch: per-theme thumb & idle ring */
|
|
32
|
+
--color-switch-thumb: #ffffff;
|
|
33
|
+
--color-switch-ring: #cbd5e1;
|
|
34
|
+
|
|
31
35
|
--color-accent-primary: #2563eb;
|
|
32
36
|
--color-accent-primary-hover: #1d4ed8;
|
|
33
37
|
--color-accent-secondary: #4f46e5;
|
|
@@ -104,6 +108,10 @@
|
|
|
104
108
|
--color-border-focus: #818cf8;
|
|
105
109
|
--color-border-danger: #f87171;
|
|
106
110
|
|
|
111
|
+
/* Toggle switch */
|
|
112
|
+
--color-switch-thumb: #f1f5f9;
|
|
113
|
+
--color-switch-ring: transparent;
|
|
114
|
+
|
|
107
115
|
--color-accent-primary: #818cf8;
|
|
108
116
|
--color-accent-primary-hover: #6366f1;
|
|
109
117
|
--color-accent-secondary: #a78bfa;
|
|
@@ -168,6 +176,10 @@
|
|
|
168
176
|
--color-border-focus: #5a6a8a;
|
|
169
177
|
--color-border-danger: #cc4444;
|
|
170
178
|
|
|
179
|
+
/* Toggle switch */
|
|
180
|
+
--color-switch-thumb: #ffffff;
|
|
181
|
+
--color-switch-ring: #b0b0b0;
|
|
182
|
+
|
|
171
183
|
--color-accent-primary: #5a6a8a;
|
|
172
184
|
--color-accent-primary-hover: #4a5a7a;
|
|
173
185
|
--color-accent-secondary: #6a7a9a;
|