UNPKG

balm-ui

Version:

A modular and customizable UI library based on Material Design and Vue 3

261 lines (226 loc) 5.99 kB
import { contrastTone } from '../utils/color'; // Define theme constants const THEME_STYLES = [ 'primary', 'secondary', 'background', 'surface', 'error', 'on-primary', 'on-secondary', 'on-surface', 'on-error' ]; const ALL_THEME_STYLES = [...THEME_STYLES, 'primary-bg', 'secondary-bg']; const TEXT_STYLES = ['primary', 'secondary', 'hint', 'disabled', 'icon']; const THEME_TONES = ['background', 'light', 'dark']; const MESSAGE = { THEME_STYLE: 'Please choose a theme style: ' + JSON.stringify(THEME_STYLES), ALL_THEME_STYLES: 'Please choose a theme style: ' + JSON.stringify(ALL_THEME_STYLES), TEXT_STYLE: 'Please choose a text style: ' + JSON.stringify(TEXT_STYLES), THEME_TONE: 'Please choose a theme tone: ' + JSON.stringify(THEME_TONES) }; const themeColors = new Map(); const textColors = new Map(); class ThemeStyle { get themeColors() { return themeColors; } get textColors() { return textColors; } getTheme(style, isClass = false) { let result = ''; if (ALL_THEME_STYLES.includes(style)) { result = isClass ? `mdc-theme--${style}` : themeColors.get(style); } else { throw new Error( `[$theme]: Theme ${isClass ? 'Class' : 'Color'}]: ${ MESSAGE.ALL_THEME_STYLES }` ); } return result; } setTheme(style, value) { document.documentElement.style.setProperty(`--mdc-theme-${style}`, value); themeColors.set(style, value); } themeText(style, tone, fn) { let result = ''; if (TEXT_STYLES.includes(style)) { if (THEME_TONES.includes(tone)) { result = fn(); } else { throw new Error(`[$theme]: ${MESSAGE.THEME_TONE}`); } } else { throw new Error(`[$theme]: ${MESSAGE.TEXT_STYLE}`); } return result; } getText(style, tone, isClass = false) { return this.themeText(style, tone, () => { return isClass ? `mdc-theme--text-${style}-on-${tone}` : textColors.get(`${style}-on-${tone}`); }); } setText(style, tone, value) { this.themeText(style, tone, () => { document.documentElement.style.setProperty( `--mdc-theme-text-${style}-on-${tone}`, value ); textColors.set(`${style}-on-${tone}`, value); }); } } class Theme extends ThemeStyle { get primary() { return themeColors.get('primary'); } set primary(value) { this.setTheme('primary', value); this.setTheme( 'on-primary', contrastTone(value) === 'dark' ? '#000' : '#fff' ); } get secondary() { return themeColors.get('secondary'); } set secondary(value) { this.setTheme('secondary', value); this.setTheme( 'on-secondary', contrastTone(value) === 'dark' ? '#000' : '#fff' ); } get background() { return themeColors.get('background'); } set background(value) { this.setTheme('background', value); } get surface() { return themeColors.get('surface'); } set surface(value) { this.setTheme('surface', value); this.setTheme( 'on-surface', contrastTone(value) === 'dark' ? '#000' : '#fff' ); } get error() { return themeColors.get('error'); } set error(value) { this.setTheme('error', value); this.setTheme('on-error', contrastTone(value) === 'dark' ? '#000' : '#fff'); } get onPrimary() { return themeColors.get('on-primary'); } set onPrimary(value) { this.setTheme('on-primary', value); } get onSecondary() { return themeColors.get('on-secondary'); } set onSecondary(value) { this.setTheme('on-secondary', value); } get onSurface() { return themeColors.get('on-surface'); } set onSurface(value) { this.setTheme('on-surface', value); } get onError() { return themeColors.get('on-error'); } set onError(value) { this.setTheme('on-error', value); } set colors(colors) { const themeStyles = Object.keys(colors); if (themeStyles.every((style) => THEME_STYLES.includes(style))) { themeStyles.forEach((style) => this.setTheme(style, colors[style])); ['primary', 'secondary', 'surface', 'error'].forEach((style) => { if ( themeStyles.includes(style) && !themeStyles.includes(`on-${style}`) ) { this.setTheme( `on-${style}`, contrastTone(colors[style]) === 'dark' ? '#000' : '#fff' ); } }); } else { throw new Error(`[$theme]: ${MESSAGE.THEME_STYLE}`); } } getThemeColor(style) { return this.getTheme(style); } getThemeClass(style) { let result = ''; if (Array.isArray(style)) { result = style.map((themeStyle) => this.getTheme(themeStyle, true)); } else { result = this.getTheme(style, true); } return result; } getTextColor(style, tone = 'background') { return this.getText(style, tone); } getTextClass(style, tone = 'background') { return this.getText(style, tone, true); } setTextColor(style, value) { this.setText(style, 'background', value); } getTextColorOnLight(style) { return this.getTextColor(style, 'light'); } getTextClassOnLight(style) { return this.getTextClass(style, 'light'); } setTextColorOnLight(style, value) { this.setText(style, 'light', value); } getTextColorOnDark(style) { return this.getTextColor(style, 'dark'); } getTextClassOnDark(style) { return this.getTextClass(style, 'dark'); } setTextColorOnDark(style, value) { this.setText(style, 'dark', value); } } const theme = new Theme(); let isColored = false; function install(app, colors = {}) { app.mixin({ mounted() { if (!isColored) { isColored = true; theme.colors = colors; } } }); app.config.globalProperties.$theme = theme; app.provide('theme', theme); } const $theme = { install }; const useTheme = () => theme; export default $theme; export { install, useTheme };