@bcodes/ngx-theme-service
Version:
Configurable theme switching service for use with CSS variables
213 lines (208 loc) • 6.29 kB
JavaScript
import { DOCUMENT } from '@angular/common';
import { InjectionToken, Injectable, Inject, ɵɵdefineInjectable, ɵɵinject } from '@angular/core';
import { Subject, BehaviorSubject, timer } from 'rxjs';
import { tap, switchMap, takeUntil } from 'rxjs/operators';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Apply a CSS class to the `<html>` element when switching themes
* @record
*/
function ThemeTransitionConfig() { }
if (false) {
/** @type {?} */
ThemeTransitionConfig.prototype.className;
/**
* remove class after duration in milliseconds
* @type {?}
*/
ThemeTransitionConfig.prototype.duration;
}
/**
* @record
*/
function ThemeServiceConfig() { }
if (false) {
/** @type {?} */
ThemeServiceConfig.prototype.themes;
/**
* theme that should always be on the target element if using explicit default theme
* @type {?|undefined}
*/
ThemeServiceConfig.prototype.defaultTheme;
/**
* optional transition configuration
* @type {?|undefined}
*/
ThemeServiceConfig.prototype.transitionConfig;
/**
* themes applied to <html> by default. Supply CSS selector to change
* @type {?|undefined}
*/
ThemeServiceConfig.prototype.targetElementSelector;
}
/** @type {?} */
const THEME_CONFIG = new InjectionToken('ThemeService: Config');
// https://angular.io/guide/angular-compiler-options#strictmetadataemit
// @dynamic
class ThemeService {
/**
* @param {?} config
* @param {?} document
*/
constructor(config, document) {
this.config = config;
this.document = document;
this.stopListening$ = new Subject();
this.selectedTheme = new BehaviorSubject(this.config.defaultTheme || '');
this.selectedTheme$ = this.selectedTheme.asObservable();
this.setupSubscription();
}
/**
* @param {?} className
* @return {?}
*/
switchTheme(className) {
this.selectedTheme.next(className);
}
/**
* @private
* @return {?}
*/
setupSubscription() {
/** @type {?} */
const transitionConfig = this.config.transitionConfig;
/** @type {?} */
const nonDefaultThemes = this.config.themes.filter((/**
* @param {?} c
* @return {?}
*/
c => c !== this.config.defaultTheme));
this.selectedTheme
.pipe(tap((/**
* @param {?} theme
* @return {?}
*/
theme => {
this.removeClasses(nonDefaultThemes);
// Conditional literal entries:
// https://2ality.com/2017/04/conditional-literal-entries.html
/** @type {?} */
const toAdd = [
...(theme ? [theme] : []),
...(transitionConfig
? [transitionConfig.className]
: []),
];
this.addClasses(toAdd);
})), transitionConfig
? switchMap((/**
* @param {?} value
* @return {?}
*/
value => {
return timer(transitionConfig.duration).pipe(tap((/**
* @param {?} x
* @return {?}
*/
x => {
this.removeClasses([
transitionConfig.className,
]);
})));
}))
: tap((/**
* @param {?} x
* @return {?}
*/
(x) => { })), takeUntil(this.stopListening$))
.subscribe();
}
/**
* @private
* @param {?} arr
* @return {?}
*/
removeClasses(arr) {
this.targetElement.classList.remove(...arr);
}
/**
* @private
* @param {?} arr
* @return {?}
*/
addClasses(arr) {
this.targetElement.classList.add(...arr);
}
/**
* @private
* @return {?}
*/
get targetElement() {
/** @type {?} */
let elem;
if (this.config.targetElementSelector) {
elem = this.document.querySelector(this.config.targetElementSelector);
if (!elem) {
console.warn(`${this.config.targetElementSelector} not found, defaulting to <html>`);
}
}
if (!elem) {
elem = this.document.documentElement;
}
return elem;
}
/**
* @return {?}
*/
ngOnDestroy() {
this.stopListening$.next(true);
}
}
ThemeService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root',
},] }
];
/** @nocollapse */
ThemeService.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [THEME_CONFIG,] }] },
{ type: Document, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
];
/** @nocollapse */ ThemeService.ngInjectableDef = ɵɵdefineInjectable({ factory: function ThemeService_Factory() { return new ThemeService(ɵɵinject(THEME_CONFIG), ɵɵinject(DOCUMENT)); }, token: ThemeService, providedIn: "root" });
if (false) {
/**
* @type {?}
* @private
*/
ThemeService.prototype.stopListening$;
/**
* @type {?}
* @private
*/
ThemeService.prototype.selectedTheme;
/** @type {?} */
ThemeService.prototype.selectedTheme$;
/**
* @type {?}
* @private
*/
ThemeService.prototype.config;
/**
* @type {?}
* @private
*/
ThemeService.prototype.document;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
export { THEME_CONFIG, ThemeService };
//# sourceMappingURL=bcodes-ngx-theme-service.js.map