@rfkit/theme
Version:
A flexible light/dark theme switching library supporting custom theme configuration, dynamic switching, and responsive design
209 lines (208 loc) • 7.32 kB
JavaScript
var types_ThemeType = /*#__PURE__*/ function(ThemeType) {
ThemeType["Dark"] = "dark";
ThemeType["Light"] = "light";
return ThemeType;
}({});
const DEFAULT_THEME_CONFIGS = [
{
name: '--theme-color-primary',
dark: '#4371F8',
light: '#2A51F7'
},
{
name: '--theme-color-warn',
dark: '#FF9626',
light: '#FF7D00'
},
{
name: '--theme-color-error',
dark: '#F76965',
light: '#F53F3F'
},
{
name: '--theme-bg-base',
dark: '#202020',
light: '#FFFFFF'
},
{
name: '--theme-bg-second',
dark: '#29292B',
light: '#F0F1F3'
},
{
name: '--theme-bg-third',
dark: '#2F2F2F',
light: '#E1E2E8'
},
{
name: '--theme-color-base',
dark: '#FFFFFF',
light: '#1D2129'
},
{
name: '--theme-border-base',
dark: '#313131',
light: '#F0F0F0'
},
{
name: '--theme-box-shadow-base',
dark: '0 1px 2px 0 rgba(255, 255, 255, 0.03),0 1px 6px -1px rgba(2552, 2552, 2552, 0.02),0 2px 4px 0 rgba(255, 255, 255, 0.02)',
light: '0 1px 2px 0 rgba(0, 0, 0, 0.03),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)'
}
];
const THEME_KEY = 'theme';
const THEME_MODE_KEY = 'theme-follow-OS';
const DEFAULT_THEME = types_ThemeType.Light;
const initializeCSS = (configs, key, modeKey)=>{
const target = `${key}-style-element-target`;
if (document.querySelector(`style[${target}]`)) return;
const css = generateCSS(configs, key, modeKey);
const styleElement = document.createElement('style');
styleElement.textContent = css;
styleElement.setAttribute(target, target);
document.head.appendChild(styleElement);
};
const generateCSS = (configs, key, modeKey)=>{
const cssBlocks = [
':root,'
];
const [firstConfig] = configs;
const types = Object.keys(firstConfig).filter((f)=>'name' !== f);
for (const type of types){
const cssVariables = configs.map((config)=>` ${config.name}: ${config[type]};`).join('\n');
const base = `:root[${key}='${type}'] {\n${cssVariables}\n}`;
const media = `@media (prefers-color-scheme: ${type}) {\n :root[${modeKey}='true'] {\n${cssVariables}\n }\n}`;
cssBlocks.push(`${base}\n${modeKey ? media : ''}`);
}
return cssBlocks.join('\n');
};
const setThemeAttribute = (theme, mode)=>{
document.documentElement.setAttribute(THEME_KEY, theme);
document.documentElement.setAttribute(THEME_MODE_KEY, JSON.stringify(mode));
};
const getThemeMedia = ()=>{
if ('undefined' != typeof window) return window.matchMedia('(prefers-color-scheme: dark)');
};
class ThemeChangeObserver {
mediaQuery;
callbacks;
boundChangeHandler;
constructor(){
this.mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
this.callbacks = new Set();
this.boundChangeHandler = this.handleThemeChange.bind(this);
this.attachEventListener();
}
subscribe(callback) {
if ('function' != typeof callback) throw new Error('回调函数必须是一个函数');
this.callbacks.add(callback);
callback(this.mediaQuery.matches);
return ()=>this.unsubscribe(callback);
}
unsubscribe(callback) {
this.callbacks.delete(callback);
}
isDarkMode() {
return this.mediaQuery.matches;
}
dispose() {
this.detachEventListener();
this.callbacks.clear();
}
attachEventListener() {
this.mediaQuery.addEventListener('change', this.boundChangeHandler);
}
detachEventListener() {
this.mediaQuery.removeEventListener('change', this.boundChangeHandler);
}
handleThemeChange(event) {
const isDarkMode = event.matches;
const currentCallbacks = Array.from(this.callbacks);
for (const callback of currentCallbacks)try {
callback(isDarkMode);
} catch (error) {
console.error('主题变化回调执行错误:', error);
}
}
}
const themeObserver = new ThemeChangeObserver();
const core_media = window.matchMedia('(prefers-color-scheme: dark)');
const useMediaTheme = ()=>core_media?.matches ? types_ThemeType.Dark : types_ThemeType.Light;
const getCurrentThemeMode = ()=>JSON.parse('false' !== document.documentElement.getAttribute(THEME_MODE_KEY) ? 'true' : 'false');
const getCurrentTheme = (mode)=>mode ?? getCurrentThemeMode() ? useMediaTheme() : document.documentElement.getAttribute(THEME_KEY) ?? DEFAULT_THEME;
function initTheme({ subscribeThemeChange, config = DEFAULT_THEME_CONFIGS, theme = getCurrentTheme(), mode = getCurrentThemeMode() }) {
const newTheme = mode ? useMediaTheme() : theme;
setThemeAttribute(newTheme, mode);
initializeCSS(config, THEME_KEY, THEME_MODE_KEY);
subscribeThemeChange && initMediaThemeChange(subscribeThemeChange);
return newTheme;
}
const toggleTheme = ()=>{
const currentTheme = getCurrentTheme();
const newTheme = currentTheme !== types_ThemeType.Light ? types_ThemeType.Light : types_ThemeType.Dark;
setThemeAttribute(newTheme, false);
return newTheme;
};
const toggleThemeMode = (mode)=>{
const newThemeMode = mode ?? !getCurrentThemeMode();
const newTheme = getCurrentTheme(newThemeMode);
setThemeAttribute(newTheme, newThemeMode);
return newThemeMode;
};
const core_themeObserver = new ThemeChangeObserver();
const initMediaThemeChange = (subscribeThemeChange)=>core_themeObserver.subscribe(()=>{
const mode = getCurrentThemeMode();
const newTheme = getCurrentTheme();
setThemeAttribute(newTheme, mode);
subscribeThemeChange({
theme: newTheme,
mode
});
});
var types_SizeType = /*#__PURE__*/ function(SizeType) {
SizeType["Middle"] = "middle";
SizeType["Small"] = "small";
return SizeType;
}({});
const DEFAULT_SIZE_CONFIGS = [
{
name: '--size-font-max',
middle: '16px',
small: '14px'
},
{
name: '--size-font-base',
middle: '14px',
small: '12px'
},
{
name: '--size-font-second',
middle: '12px',
small: '10px'
},
{
name: '--size-gap-base',
middle: '8px',
small: '4px'
},
{
name: '--size-border-radius-base',
middle: '8px',
small: '4px'
},
{
name: '--size-border-radius-second',
middle: '4px',
small: '2px'
}
];
const SIZE_KEY = 'size';
const DEFAULT_SIZE = types_SizeType.Middle;
const getCurrentSize = ()=>document.documentElement.getAttribute(SIZE_KEY) ?? DEFAULT_SIZE;
function initSize({ configs = DEFAULT_SIZE_CONFIGS }) {
initializeCSS(configs, SIZE_KEY);
}
const toggleSize = (newSize)=>{
document.documentElement.setAttribute(SIZE_KEY, newSize);
};
export { DEFAULT_SIZE, DEFAULT_SIZE_CONFIGS, DEFAULT_THEME, DEFAULT_THEME_CONFIGS, SIZE_KEY, types_SizeType as SizeType, THEME_KEY, THEME_MODE_KEY, types_ThemeType as ThemeType, getCurrentSize, getCurrentTheme, getCurrentThemeMode, getThemeMedia, initMediaThemeChange, initSize, initTheme, initializeCSS, core_media as media, themeObserver, toggleSize, toggleTheme, toggleThemeMode, useMediaTheme };