UNPKG

@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
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 };