UNPKG

@lithiumjs/ngx-material-theming

Version:

Dynamic and customizable Angular Material theming made easy.

195 lines (189 loc) 10.6 kB
import { __decorate } from 'tslib'; import * as i0 from '@angular/core'; import { forwardRef, Input, SkipSelf, Optional, Inject, Directive } from '@angular/core'; import * as i2 from '@lithiumjs/angular'; import { createDirectiveState, AutoPush, stateTokenFor, OnInit, OnDestroy } from '@lithiumjs/angular'; import { combineLatest } from 'rxjs'; import { filter } from 'rxjs/operators'; import * as i1 from '@angular/cdk/overlay'; const DEFAULT_THEME_NAME = "default"; const STATE_PROVIDER = createDirectiveState(forwardRef(() => ThemeContainer)); class ThemeContainer { constructor(overlayContainer, cdRef, parentThemeContainer, stateRef) { this.stateRef = stateRef; this.theme = DEFAULT_THEME_NAME; this.active = true; this.manageOverlay = true; this.theme$ = this.stateRef.get("theme"); this.active$ = this.stateRef.get("active"); this.manageOverlay$ = this.stateRef.get("manageOverlay"); let wasManagingOverlay = false; AutoPush.enable(this, cdRef); // Manage the overlay automatically if this is the root theme container and `manageOverlay` hasn't been defined this.onInit$.pipe(filter(() => this.manageOverlay === undefined)).subscribe(() => this.manageOverlay = !parentThemeContainer && !overlayContainer.getContainerElement().getAttribute("li-theme")); // Remove the `theme` overlay attribute if this container is being destroyed and is still managing it this.onDestroy$.pipe(filter(() => this.manageOverlay && this.active)).subscribe(() => overlayContainer.getContainerElement().removeAttribute("li-theme")); // Update the overlay if the state changes and we're managing the overlay combineLatest(stateRef.getAll("active", "theme", "manageOverlay")).subscribe(([active, theme, manageOverlay]) => { const overlay = overlayContainer.getContainerElement(); if (manageOverlay && active) { overlay.setAttribute("li-theme", theme); wasManagingOverlay = true; } else if (wasManagingOverlay) { overlay.removeAttribute("li-theme"); wasManagingOverlay = false; } }); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ThemeContainer, deps: [{ token: i1.OverlayContainer }, { token: i0.ChangeDetectorRef }, { token: ThemeContainer, optional: true, skipSelf: true }, { token: stateTokenFor(STATE_PROVIDER) }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.0", type: ThemeContainer, isStandalone: true, selector: "li-theme-container", inputs: { theme: "theme", active: "active", manageOverlay: "manageOverlay" }, host: { properties: { "attr.li-theme": "theme", "attr.active": "active" } }, providers: [STATE_PROVIDER], ngImport: i0 }); } } __decorate([ OnInit() ], ThemeContainer.prototype, "onInit$", void 0); __decorate([ OnDestroy() ], ThemeContainer.prototype, "onDestroy$", void 0); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: ThemeContainer, decorators: [{ type: Directive, args: [{ selector: "li-theme-container", providers: [STATE_PROVIDER], host: { '[attr.li-theme]': 'theme', '[attr.active]': 'active' } }] }], ctorParameters: () => [{ type: i1.OverlayContainer }, { type: i0.ChangeDetectorRef }, { type: ThemeContainer, decorators: [{ type: SkipSelf }, { type: Optional }] }, { type: i2.DirectiveStateRef, decorators: [{ type: Inject, args: [stateTokenFor(STATE_PROVIDER)] }] }], propDecorators: { onInit$: [], onDestroy$: [], theme: [{ type: Input }], active: [{ type: Input }], manageOverlay: [{ type: Input }] } }); // @dynamic class ThemeLoader { static { this.THEME_NAME_PARSER = /\[li-theme="([^\]\s]+)"\]/; } /** * @description Loads the given compiled theme in for use. * @param compiledThemeData The pre-compiled theme data. * @param labelElement [Optional] If `true`, will attach a `data-theme-name` attribute to the `<style>` element set to the theme's name. */ static loadCompiled(compiledThemeData, labelElement) { const element = document.createElement("style"); element.type = "text/css"; if (labelElement === undefined ? true : labelElement) { const nameParse = this.THEME_NAME_PARSER.exec(compiledThemeData); if (nameParse && nameParse.length > 1) { element.setAttribute("data-theme-name", "" + nameParse?.[1]); } } element.innerHTML = compiledThemeData; this.getHeadElement().appendChild(element); return element; } /** * @description Unloads the given compiled theme from the DOM. * @param name The name of the theme to unload. */ static unloadCompiled(name) { const head = this.getHeadElement(); head.querySelectorAll(`style[data-theme-name=${name}]`) .forEach(style => head.removeChild(style)); } static getHeadElement() { const heads = document.getElementsByTagName("head"); if (!heads || heads.length === 0) { const head = document.createElement("head"); document.appendChild(head); return head; } else { return heads[0]; } } } var ThemeTemplateOptions; (function (ThemeTemplateOptions) { ThemeTemplateOptions.defaultValues = { nameMatcher: /%theme-name%/g, primaryHexMatcher: /#a099(\d{2})/g, primaryRgbaMatcher: /rgba\s?\(\s?160\s?,\s?153\s?,\s?(\d+)\s?,\s?([\d.]+)\s?\)/g, accentHexMatcher: /#b099(\d{2})/g, accentRgbaMatcher: /rgba\s?\(\s?176\s?,\s?153\s?,\s?(\d+)\s?,\s?([\d.]+)\s?\)/g, warnHexMatcher: /#c099(\d{2})/g, warnRgbaMatcher: /rgba\s?\(\s?192\s?,\s?153\s?,\s?(\d+)\s?,\s?([\d.]+)\s?\)/g, primaryContrastHexMatcher: /#ac99(\d{2})/g, primaryContrastRgbaMatcher: /rgba\s?\(\s?172\s?,\s?153\s?,\s?(\d+)\s?,\s?([\d.]+)\s?\)/g, accentContrastHexMatcher: /#bc99(\d{2})/g, accentContrastRgbaMatcher: /rgba\s?\(\s?188\s?,\s?153\s?,\s?(\d+)\s?,\s?([\d.]+)\s?\)/g, warnContrastHexMatcher: /#cc99(\d{2})/g, warnContrastRgbaMatcher: /rgba\s?\(\s?204\s?,\s?153\s?,\s?(\d+)\s?,\s?([\d.]+)\s?\)/g }; })(ThemeTemplateOptions || (ThemeTemplateOptions = {})); class ThemeCreator { /** * @description Creates a new theme from the given template theme CSS and theme palettes and loads it for use with the given name. * @param options The options to use to create the new theme. * @return The compiled theme CSS. */ static createFromTemplate(options) { // Generate the theme data from the given template data: let themeData = options.templateData; let templateOptions = Object.assign({}, ThemeTemplateOptions.defaultValues, options.templateOptions || {}); themeData = themeData.replace(templateOptions.nameMatcher, options.name); themeData = this.substituteThemeHex(themeData, templateOptions.primaryHexMatcher, options.primaryPalette); themeData = this.substituteThemeHex(themeData, templateOptions.accentHexMatcher, options.accentPalette); themeData = this.substituteThemeHex(themeData, templateOptions.warnHexMatcher, options.warnPalette); themeData = this.substituteThemeRgba(themeData, templateOptions.primaryRgbaMatcher, options.primaryPalette); themeData = this.substituteThemeRgba(themeData, templateOptions.accentRgbaMatcher, options.accentPalette); themeData = this.substituteThemeRgba(themeData, templateOptions.warnRgbaMatcher, options.warnPalette); themeData = this.substituteThemeHex(themeData, templateOptions.primaryContrastHexMatcher, options.primaryPalette.contrast); themeData = this.substituteThemeHex(themeData, templateOptions.accentContrastHexMatcher, options.accentPalette.contrast); themeData = this.substituteThemeHex(themeData, templateOptions.warnContrastHexMatcher, options.warnPalette.contrast); themeData = this.substituteThemeRgba(themeData, templateOptions.primaryContrastRgbaMatcher, options.primaryPalette.contrast); themeData = this.substituteThemeRgba(themeData, templateOptions.accentContrastRgbaMatcher, options.accentPalette.contrast); themeData = this.substituteThemeRgba(themeData, templateOptions.warnContrastRgbaMatcher, options.warnPalette.contrast); const themeElement = ThemeLoader.loadCompiled(themeData); // Store the color info for external programmatic use themeElement.setAttribute("data-color-primary", options.primaryPalette[500]); themeElement.setAttribute("data-color-accent", options.accentPalette[500]); themeElement.setAttribute("data-color-warn", options.warnPalette[500]); return themeData; } static substituteThemeHex(themeData, matcher, palette) { return themeData.replace(matcher, (_match, $1) => palette[this.hexOffset($1)]); } static substituteThemeRgba(themeData, matcher, palette) { return themeData.replace(matcher, (_match, $1, $2) => { return `rgba(${palette[this.rgbaOffset($1)]},${this.rgbaAlpha($2)})`; }); } /** @description Compute the palette offset from an associated hex color regex match. */ static hexOffset(match) { match = match.toLowerCase(); return String(Number.parseInt(`${match}${match.startsWith("a") ? "00" : "0"}`)); } /** @description Compute the palette offset from an associated rgba color regex match. */ static rgbaOffset(match) { const decOffset = Number.parseInt(match); return this.hexOffset(decOffset.toString(16)); } /** @description Compute the alpha channel from an associated rgba alpha regex match. */ static rgbaAlpha(match) { return Number.parseFloat(match); } } /** * Generated bundle index. Do not edit. */ export { DEFAULT_THEME_NAME, ThemeContainer, ThemeCreator, ThemeLoader, ThemeTemplateOptions }; //# sourceMappingURL=lithiumjs-ngx-material-theming.mjs.map