mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-26 08:36:19 +08:00
chore: init project
This commit is contained in:
198
packages/preference/src/preference.ts
Normal file
198
packages/preference/src/preference.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
import type {
|
||||
DeepPartial,
|
||||
Preference,
|
||||
PreferenceKeys,
|
||||
} from '@vben-core/typings';
|
||||
|
||||
import { convertToHslCssVar, merge } from '@vben-core/toolkit';
|
||||
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import { breakpointsTailwind, useBreakpoints, useCssVar } from '@vueuse/core';
|
||||
import { markRaw, reactive, ref, watch } from 'vue';
|
||||
|
||||
import { defaultPreference } from './config';
|
||||
|
||||
import type { PreferenceCacheType } from './cache';
|
||||
|
||||
/**
|
||||
* 当前偏好设置
|
||||
*/
|
||||
const currentPreference: Preference = reactive(defaultPreference);
|
||||
/**
|
||||
* 当前偏好设置原始值
|
||||
*/
|
||||
const initialPreference: Ref<Preference> = ref(defaultPreference);
|
||||
|
||||
let preferenceCache: PreferenceCacheType;
|
||||
|
||||
/**
|
||||
* 是否监听过系统设置变化
|
||||
*/
|
||||
let isRegisterListen = false;
|
||||
|
||||
function updatePreference(key: keyof Preference, value: boolean | string): void;
|
||||
function updatePreference(
|
||||
preference: DeepPartial<Preference>,
|
||||
value?: undefined,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* 更新偏好设置
|
||||
* @param preference - 一个部分偏好设置对象,它将被合并到当前偏好设置中。
|
||||
*/
|
||||
function updatePreference(preference: any, value: any) {
|
||||
if (typeof preference === 'string') {
|
||||
updatePreference({ [preference]: value }, value);
|
||||
} else {
|
||||
const updateKeys = Object.keys(preference) as PreferenceKeys[];
|
||||
|
||||
const mergePreference = merge(preference, markRaw(currentPreference));
|
||||
|
||||
Object.assign(currentPreference, mergePreference);
|
||||
|
||||
// 当修改到颜色变量时,更新 css 变量
|
||||
if (updateKeys.includes('colorPrimary')) {
|
||||
updateCssVar(currentPreference);
|
||||
}
|
||||
|
||||
// 更新主题
|
||||
if (updateKeys.includes('theme')) {
|
||||
updateTheme(currentPreference);
|
||||
}
|
||||
|
||||
// 更新页面颜色模式(灰色、色弱)
|
||||
if (
|
||||
updateKeys.includes('colorGrayMode') ||
|
||||
updateKeys.includes('colorWeakMode')
|
||||
) {
|
||||
updateColorMode(currentPreference);
|
||||
}
|
||||
|
||||
preferenceCache.set(currentPreference);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新 CSS 变量
|
||||
* @param preference - 当前偏好设置对象,它的颜色值将被转换成 HSL 格式并设置为 CSS 变量。
|
||||
*/
|
||||
function updateCssVar(preference: Preference) {
|
||||
for (const [key, value] of Object.entries(preference)) {
|
||||
if (['colorPrimary'].includes(key)) {
|
||||
const cssVarKey = key.replaceAll(/([A-Z])/g, '-$1').toLowerCase();
|
||||
const cssVarValue = useCssVar(`--${cssVarKey}`);
|
||||
cssVarValue.value = convertToHslCssVar(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新主题
|
||||
* @param preference - 当前偏好设置对象,它的主题值将被用来设置文档的主题。
|
||||
*/
|
||||
function updateTheme(preference: Preference) {
|
||||
// 当修改到颜色变量时,更新 css 变量
|
||||
const root = document.documentElement;
|
||||
if (root) {
|
||||
const dark = isDarkTheme(preference.theme);
|
||||
root.classList.toggle('dark', dark);
|
||||
}
|
||||
|
||||
// 只需要监听一次即可
|
||||
listenOnce(preference);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新页面颜色模式(灰色、色弱)
|
||||
* @param preference
|
||||
*/
|
||||
function updateColorMode(preference: Preference) {
|
||||
const { colorGrayMode, colorWeakMode } = preference;
|
||||
const body = document.body;
|
||||
const COLOR_WEAK = 'invert';
|
||||
const COLOR_GRAY = 'grayscale';
|
||||
colorWeakMode
|
||||
? body.classList.add(COLOR_WEAK)
|
||||
: body.classList.remove(COLOR_WEAK);
|
||||
colorGrayMode
|
||||
? body.classList.add(COLOR_GRAY)
|
||||
: body.classList.remove(COLOR_GRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. 监听系统主题偏好设置变化
|
||||
* 2. 监听断点,判断是否移动端
|
||||
* @param preference - 当前偏好设置对象,当系统主题偏好变化时,它的主题值会被更新。
|
||||
*/
|
||||
function listenOnce(preference: Preference) {
|
||||
if (isRegisterListen) {
|
||||
return;
|
||||
}
|
||||
isRegisterListen = true;
|
||||
// 监听系统主题偏好设置变化
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.addEventListener('change', ({ matches: isDark }) => {
|
||||
preference.theme = isDark ? 'dark' : 'light';
|
||||
updateTheme(preference);
|
||||
});
|
||||
|
||||
// 监听断点,判断是否移动端
|
||||
const breakpoints = useBreakpoints(breakpointsTailwind);
|
||||
const isMobile = breakpoints.smaller('md');
|
||||
watch(
|
||||
() => isMobile.value,
|
||||
(val) => {
|
||||
currentPreference.isMobile = val;
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置偏好设置
|
||||
* 偏好设置将被重置为初始值,并从 localStorage 中移除。
|
||||
*/
|
||||
function resetPreference() {
|
||||
Object.assign(currentPreference, initialPreference.value);
|
||||
updatePreference(currentPreference);
|
||||
preferenceCache.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置当前app默认的偏好配置
|
||||
* @param overrides
|
||||
*/
|
||||
function overridesPreference(
|
||||
overrides: DeepPartial<Preference>,
|
||||
cache: PreferenceCacheType,
|
||||
) {
|
||||
preferenceCache = cache;
|
||||
/**
|
||||
* 重置状态时用到的原始值
|
||||
*/
|
||||
initialPreference.value = merge(overrides, defaultPreference);
|
||||
const mergedPreference = merge(
|
||||
overrides,
|
||||
preferenceCache.get(defaultPreference),
|
||||
);
|
||||
updatePreference(mergedPreference);
|
||||
}
|
||||
|
||||
function isDarkTheme(theme: string) {
|
||||
let dark = theme === 'dark';
|
||||
if (theme === 'auto') {
|
||||
dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
return dark;
|
||||
}
|
||||
|
||||
export {
|
||||
currentPreference,
|
||||
initialPreference,
|
||||
isDarkTheme,
|
||||
overridesPreference,
|
||||
resetPreference,
|
||||
updatePreference,
|
||||
};
|
Reference in New Issue
Block a user