import { inject } from 'aurelia-dependency-injection'; import { ObserverLocator } from 'aurelia-binding'; import { UxTheme } from './ux-theme'; import { GlobalStyleEngine } from './global-style-engine'; import { AureliaUX } from '../aurelia-ux'; @inject(ObserverLocator, GlobalStyleEngine, AureliaUX) export class StyleController { public themes: UxTheme[] = []; constructor( private observerLocator: ObserverLocator, private globalStyleEngine: GlobalStyleEngine, private ux: AureliaUX) { } public updateTheme(theme: UxTheme, element?: HTMLElement) { const baseTheme: UxTheme = { themeKey: 'base-theme' }; if (theme.themeKey == null) { throw new Error('Provided theme has no themeKey property.'); } if (theme.themeKey === 'design') { for (const key in theme) { if (key !== 'themeKey') { (this.ux.design as any)[key] = (theme as any)[key]; } } } else if (element != null) { for (const key in theme) { if (theme.hasOwnProperty(key) && baseTheme.hasOwnProperty(key) === false) { if ((theme as any)[key]) { element.style.setProperty(this.generateCssVariableName(theme.themeKey, key), (theme as any)[key]); } elseĀ { element.style.removeProperty(this.generateCssVariableName(theme.themeKey, key)); } } } } else { const uxTheme: any = this.themes.splice(this.themes.indexOf(this.themes[theme.themeKey as any]))[0]; if (uxTheme != null) { this.removeWatches(uxTheme); } this.globalStyleEngine.addOrUpdateGlobalStyle( `aurelia-ux theme ${theme.themeKey}`, this.processInnerHtml(theme), ':root'); this.setWatches(theme); this.themes[theme.themeKey as any] = theme; } } public getThemeKeys(theme: UxTheme): string[] { const baseTheme: UxTheme = { themeKey: 'base-theme' }; const themeKeys: string[] = []; for (const key in theme) { if (theme.hasOwnProperty(key) && baseTheme.hasOwnProperty(key) === false) { themeKeys.push(key); } } return themeKeys; } public generateCssVariableName(themeKey: string, propertyKey: string) { return `--aurelia-ux--${themeKey}-${kebabCase(propertyKey)}`; } public generateCssVariable(themeKey: string, propertyKey: string, value: string | number) { if (value === undefined || value === 'undefined') { return ''; } return `--aurelia-ux--${themeKey}-${kebabCase(propertyKey)}: ${value};`; } private setWatches(theme: UxTheme) { for (const key of this.getThemeKeys(theme)) { this.observerLocator.getObserver(theme, key).subscribe(() => this.themePropertyChanged(theme)); } } private removeWatches(theme: UxTheme) { for (const key of this.getThemeKeys(theme)) { this.observerLocator.getObserver(theme, key).unsubscribe(() => this.themePropertyChanged(theme)); } } private themePropertyChanged(theme: UxTheme) { this.globalStyleEngine.addOrUpdateGlobalStyle( `aurelia-ux theme ${theme.themeKey}`, this.processInnerHtml(theme), ':root'); } private processInnerHtml(theme: UxTheme) { let designInnerHtml = ''; for (const key of this.getThemeKeys(theme)) { if ((theme as any)[key]) { designInnerHtml += ` ${this.generateCssVariable(theme.themeKey, key, (theme as any)[key])}\r\n`; } } return designInnerHtml; } } function kebabCase(value: string) { value = value.charAt(0).toLowerCase() + value.slice(1); return value.replace(/([A-Z])/g, (match) => `-${match[0].toLowerCase()}`); }