admin-vben/packages/@core/preferences/src/preferences.ts

236 lines
6.5 KiB
TypeScript
Raw Normal View History

2024-06-09 04:53:38 +00:00
import type { DeepPartial } from '@vben-core/typings';
2024-06-01 15:15:29 +00:00
import type { InitialOptions, Preferences } from './types';
2024-06-08 11:49:06 +00:00
2024-06-09 04:53:38 +00:00
import { markRaw, reactive, readonly, watch } from 'vue';
2024-06-08 11:49:06 +00:00
import { isMacOs, merge, StorageManager } from '@vben-core/toolkit';
2024-06-01 15:15:29 +00:00
import {
breakpointsTailwind,
useBreakpoints,
useDebounceFn,
} from '@vueuse/core';
import { defaultPreferences } from './config';
import { updateCSSVariables } from './update-css-variables';
2024-06-01 15:15:29 +00:00
const STORAGE_KEY = 'preferences';
const STORAGE_KEY_LOCALE = `${STORAGE_KEY}-locale`;
const STORAGE_KEY_THEME = `${STORAGE_KEY}-theme`;
2024-06-01 15:15:29 +00:00
function isDarkTheme(theme: string) {
let dark = theme === 'dark';
if (theme === 'auto') {
dark = window.matchMedia('(prefers-color-scheme: dark)').matches;
}
return dark;
}
class PreferenceManager {
private cache: null | StorageManager = null;
2024-06-09 04:53:38 +00:00
// private flattenedState: Flatten<Preferences>;
2024-06-01 15:15:29 +00:00
private initialPreferences: Preferences = defaultPreferences;
private isInitialized: boolean = false;
private savePreferences: (preference: Preferences) => void;
private state: Preferences = reactive<Preferences>({
...this.loadPreferences(),
});
constructor() {
this.cache = new StorageManager();
this.savePreferences = useDebounceFn(
(preference: Preferences) => this._savePreferences(preference),
100,
2024-06-01 15:15:29 +00:00
);
}
/**
*
* @param {Preferences} preference -
*/
private _savePreferences(preference: Preferences) {
this.cache?.setItem(STORAGE_KEY, preference);
this.cache?.setItem(STORAGE_KEY_LOCALE, preference.app.locale);
2024-06-23 11:17:31 +00:00
this.cache?.setItem(STORAGE_KEY_THEME, preference.theme.mode);
2024-06-01 15:15:29 +00:00
}
/**
*
*
*
* @param {DeepPartial<Preferences>} updates -
*/
private handleUpdates(updates: DeepPartial<Preferences>) {
const themeUpdates = updates.theme || {};
const appUpdates = updates.app || {};
2024-06-23 11:17:31 +00:00
if (themeUpdates && Object.keys(themeUpdates).length > 0) {
updateCSSVariables(this.state);
2024-06-01 15:15:29 +00:00
}
2024-06-29 06:45:02 +00:00
if (
Reflect.has(appUpdates, 'colorGrayMode') ||
Reflect.has(appUpdates, 'colorWeakMode')
) {
2024-06-01 15:15:29 +00:00
this.updateColorMode(this.state);
}
}
private initPlatform() {
const dom = document.documentElement;
dom.dataset.platform = isMacOs() ? 'macOs' : 'window';
}
2024-06-02 07:04:37 +00:00
/**
*
*/
private loadCachedPreferences() {
return this.cache?.getItem<Preferences>(STORAGE_KEY);
2024-06-02 07:04:37 +00:00
}
2024-06-01 15:15:29 +00:00
/**
*
* @returns {Preferences}
*/
2024-06-02 12:47:50 +00:00
private loadPreferences(): Preferences {
2024-06-02 07:04:37 +00:00
return this.loadCachedPreferences() || { ...defaultPreferences };
2024-06-01 15:15:29 +00:00
}
2024-06-02 07:04:37 +00:00
2024-06-01 15:15:29 +00:00
/**
*
*/
private setupWatcher() {
if (this.isInitialized) {
return;
}
// 监听断点,判断是否移动端
const breakpoints = useBreakpoints(breakpointsTailwind);
const isMobile = breakpoints.smaller('md');
watch(
() => isMobile.value,
(val) => {
this.updatePreferences({
app: { isMobile: val },
});
},
{ immediate: true },
);
// 监听系统主题偏好设置变化
window
.matchMedia('(prefers-color-scheme: dark)')
.addEventListener('change', ({ matches: isDark }) => {
this.updatePreferences({
2024-06-23 11:17:31 +00:00
theme: { mode: isDark ? 'dark' : 'light' },
2024-06-01 15:15:29 +00:00
});
updateCSSVariables(this.state);
2024-06-01 15:15:29 +00:00
});
}
/**
*
* @param preference
*/
private updateColorMode(preference: Preferences) {
if (preference.app) {
const { colorGrayMode, colorWeakMode } = preference.app;
2024-06-30 15:15:23 +00:00
const dom = document.documentElement;
2024-06-01 15:15:29 +00:00
const COLOR_WEAK = 'invert-mode';
const COLOR_GRAY = 'grayscale-mode';
colorWeakMode
2024-06-30 15:15:23 +00:00
? dom.classList.add(COLOR_WEAK)
: dom.classList.remove(COLOR_WEAK);
2024-06-01 15:15:29 +00:00
colorGrayMode
2024-06-30 15:15:23 +00:00
? dom.classList.add(COLOR_GRAY)
: dom.classList.remove(COLOR_GRAY);
2024-06-01 15:15:29 +00:00
}
}
clearCache() {
[STORAGE_KEY, STORAGE_KEY_LOCALE, STORAGE_KEY_THEME].forEach((key) => {
this.cache?.removeItem(key);
});
}
2024-06-01 15:15:29 +00:00
public getInitialPreferences() {
return this.initialPreferences;
}
public getPreferences() {
2024-06-09 04:53:38 +00:00
return readonly(this.state);
2024-06-01 15:15:29 +00:00
}
/**
*
* overrides
* namespace
2024-06-01 15:15:29 +00:00
*/
public async initPreferences({ namespace, overrides }: InitialOptions) {
2024-06-01 15:15:29 +00:00
// 是否初始化过
if (this.isInitialized) {
return;
}
// 初始化存储管理器
this.cache = new StorageManager({ prefix: namespace });
// 合并初始偏好设置
this.initialPreferences = merge({}, overrides, defaultPreferences);
// 加载并合并当前存储的偏好设置
2024-06-16 07:45:15 +00:00
const mergedPreference = merge(
{},
overrides,
this.loadCachedPreferences() || defaultPreferences,
2024-06-16 07:45:15 +00:00
);
2024-06-01 15:15:29 +00:00
// 更新偏好设置
this.updatePreferences(mergedPreference);
this.setupWatcher();
this.initPlatform();
2024-06-01 15:15:29 +00:00
// 标记为已初始化
this.isInitialized = true;
}
/**
*
* localStorage
*
* @example
* initialPreferences { theme: 'light', language: 'en' }
* state { theme: 'dark', language: 'fr' }
* this.resetPreferences();
* state { theme: 'light', language: 'en' }
* localStorage
*/
resetPreferences() {
// 将状态重置为初始偏好设置
Object.assign(this.state, this.initialPreferences);
// 保存重置后的偏好设置
this.savePreferences(this.state);
// 从存储中移除偏好设置项
[STORAGE_KEY, STORAGE_KEY_THEME, STORAGE_KEY_LOCALE].forEach((key) => {
this.cache?.removeItem(key);
});
2024-06-30 14:58:57 +00:00
this.updatePreferences(this.state);
2024-06-01 15:15:29 +00:00
}
/**
*
* @param updates -
*/
2024-06-30 15:15:23 +00:00
public updatePreferences(updates: DeepPartial<Preferences>) {
2024-06-02 07:04:37 +00:00
const mergedState = merge({}, updates, markRaw(this.state));
2024-06-01 15:15:29 +00:00
Object.assign(this.state, mergedState);
2024-06-02 07:04:37 +00:00
2024-06-01 15:15:29 +00:00
// 根据更新的键值执行相应的操作
this.handleUpdates(updates);
this.savePreferences(this.state);
}
}
const preferencesManager = new PreferenceManager();
export { isDarkTheme, PreferenceManager, preferencesManager };