angular-material-css-vars
Version:
Little library to use css variables for @angular/material
502 lines (493 loc) • 21.2 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, inject, DOCUMENT, RendererFactory2, RendererStyleFlags2, isDevMode, Injectable, NgModule, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';
import { TinyColor } from '@ctrl/tinycolor';
import { CommonModule } from '@angular/common';
var MatCssPalettePrefix;
(function (MatCssPalettePrefix) {
MatCssPalettePrefix["Primary"] = "--palette-primary-";
MatCssPalettePrefix["Accent"] = "--palette-accent-";
MatCssPalettePrefix["Warn"] = "--palette-warn-";
})(MatCssPalettePrefix || (MatCssPalettePrefix = {}));
var MaterialCssVariables;
(function (MaterialCssVariables) {
MaterialCssVariables["Primary50"] = "--palette-primary-50";
MaterialCssVariables["Primary100"] = "--palette-primary-100";
MaterialCssVariables["Primary200"] = "--palette-primary-200";
MaterialCssVariables["Primary300"] = "--palette-primary-300";
MaterialCssVariables["Primary400"] = "--palette-primary-400";
MaterialCssVariables["Primary500"] = "--palette-primary-500";
MaterialCssVariables["Primary600"] = "--palette-primary-600";
MaterialCssVariables["Primary700"] = "--palette-primary-700";
MaterialCssVariables["Primary800"] = "--palette-primary-800";
MaterialCssVariables["Primary900"] = "--palette-primary-900";
MaterialCssVariables["PrimaryA100"] = "--palette-primary-A100";
MaterialCssVariables["PrimaryA200"] = "--palette-primary-A200";
MaterialCssVariables["PrimaryA400"] = "--palette-primary-A400";
MaterialCssVariables["PrimaryA700"] = "--palette-primary-A700";
MaterialCssVariables["PrimaryContrast50"] = "--palette-primary-contrast-50";
MaterialCssVariables["PrimaryContrast100"] = "--palette-primary-contrast-100";
MaterialCssVariables["PrimaryContrast200"] = "--palette-primary-contrast-200";
MaterialCssVariables["PrimaryContrast300"] = "--palette-primary-contrast-300";
MaterialCssVariables["PrimaryContrast400"] = "--palette-primary-contrast-400";
MaterialCssVariables["PrimaryContrast500"] = "--palette-primary-contrast-500";
MaterialCssVariables["PrimaryContrast600"] = "--palette-primary-contrast-600";
MaterialCssVariables["PrimaryContrast700"] = "--palette-primary-contrast-700";
MaterialCssVariables["PrimaryContrast800"] = "--palette-primary-contrast-800";
MaterialCssVariables["PrimaryContrast900"] = "--palette-primary-contrast-900";
MaterialCssVariables["PrimaryContrastA100"] = "--palette-primary-contrast-A100";
MaterialCssVariables["PrimaryContrastA200"] = "--palette-primary-contrast-A200";
MaterialCssVariables["PrimaryContrastA400"] = "--palette-primary-contrast-A400";
MaterialCssVariables["PrimaryContrastA700"] = "--palette-primary-contrast-A700";
// ACCENT
MaterialCssVariables["Accent50"] = "--palette-accent-50";
MaterialCssVariables["Accent100"] = "--palette-accent-100";
MaterialCssVariables["Accent200"] = "--palette-accent-200";
MaterialCssVariables["Accent300"] = "--palette-accent-300";
MaterialCssVariables["Accent400"] = "--palette-accent-400";
MaterialCssVariables["Accent500"] = "--palette-accent-500";
MaterialCssVariables["Accent600"] = "--palette-accent-600";
MaterialCssVariables["Accent700"] = "--palette-accent-700";
MaterialCssVariables["Accent800"] = "--palette-accent-800";
MaterialCssVariables["Accent900"] = "--palette-accent-900";
MaterialCssVariables["AccentA100"] = "--palette-accent-A100";
MaterialCssVariables["AccentA200"] = "--palette-accent-A200";
MaterialCssVariables["AccentA400"] = "--palette-accent-A400";
MaterialCssVariables["AccentA700"] = "--palette-accent-A700";
MaterialCssVariables["DarkAccentText"] = "--dark-accent-text";
MaterialCssVariables["LightAccentText"] = "--light-accent-text";
// WARN
MaterialCssVariables["Warn50"] = "--palette-warn-50";
MaterialCssVariables["Warn100"] = "--palette-warn-100";
MaterialCssVariables["Warn200"] = "--palette-warn-200";
MaterialCssVariables["Warn300"] = "--palette-warn-300";
MaterialCssVariables["Warn400"] = "--palette-warn-400";
MaterialCssVariables["Warn500"] = "--palette-warn-500";
MaterialCssVariables["Warn600"] = "--palette-warn-600";
MaterialCssVariables["Warn700"] = "--palette-warn-700";
MaterialCssVariables["Warn800"] = "--palette-warn-800";
MaterialCssVariables["Warn900"] = "--palette-warn-900";
MaterialCssVariables["WarnA100"] = "--palette-warn-A100";
MaterialCssVariables["WarnA200"] = "--palette-warn-A200";
MaterialCssVariables["WarnA400"] = "--palette-warn-A400";
MaterialCssVariables["WarnA700"] = "--palette-warn-A700";
MaterialCssVariables["DarkWarnText"] = "--dark-warn-text";
MaterialCssVariables["LightWarnText"] = "--light-warn-text";
})(MaterialCssVariables || (MaterialCssVariables = {}));
const DEFAULT_MAT_CSS_CFG = {
isAutoContrast: true,
isAlternativeColorAlgorithm: false,
darkThemeClass: "isDarkTheme",
lightThemeClass: "isLightTheme",
colorMap: [
{ name: "50", map: [52, 0, 0] },
{ name: "100", map: [37, 0, 0] },
{ name: "200", map: [26, 0, 0] },
{ name: "300", map: [12, 0, 0] },
{ name: "400", map: [6, 0, 0] },
{ name: "500", map: [0, 0, 0] },
{ name: "600", map: [0, 6, 0] },
{ name: "700", map: [0, 12, 0] },
{ name: "800", map: [0, 18, 0] },
{ name: "900", map: [0, 24, 0] },
{ name: "A100", map: [50, 0, 30] },
{ name: "A200", map: [30, 0, 30] },
{ name: "A400", map: [10, 0, 15] },
{ name: "A700", map: [5, 0, 5] },
],
sortedHues: [
"50",
"100",
"200",
"300",
"400",
"500",
"600",
"700",
"800",
"900",
],
};
const MATERIAL_CSS_VARS_CFG = new InjectionToken("Mat Css Config");
// @see: https://github.com/angular/angular/issues/20351
/** @dynamic */
class MaterialCssVarsService {
static { this.CONTRAST_PREFIX = "contrast-"; }
static { this.DARK_TEXT_VAR = "--dark-text-contrast"; }
static { this.LIGHT_TEXT_VAR = "--light-text-contrast"; }
constructor() {
this.primary = "#03a9f4";
this.accent = "#e91e63";
this.warn = "#f44336";
this.contrastColorThresholdPrimary = "400";
this.contrastColorThresholdAccent = "400";
this.contrastColorThresholdWarn = "400";
this.isAutoContrast = false;
this._black = new TinyColor("#000000");
this._white = new TinyColor("#ffffff");
this.document = inject(DOCUMENT);
this.renderer = inject(RendererFactory2).createRenderer(null, null);
const cfg = inject(MATERIAL_CSS_VARS_CFG);
this.cfg = {
...DEFAULT_MAT_CSS_CFG,
...cfg,
};
this.isAutoContrast = this.cfg.isAutoContrast;
this.ROOT = this._getRootElement(this.cfg.rootSelector);
if (typeof this.cfg.isDarkTheme === "boolean") {
this.setDarkTheme(this.cfg.isDarkTheme);
}
if (this.cfg.primary) {
this.setPrimaryColor(this.cfg.primary);
}
if (this.cfg.accent) {
this.setAccentColor(this.cfg.accent);
}
if (this.cfg.warn) {
this.setWarnColor(this.cfg.warn);
}
}
setPrimaryColor(hex) {
this.primary = hex;
const varPrefix = MatCssPalettePrefix.Primary;
const stylePrimary = this._computePaletteColors(varPrefix, this.primary);
this._setStyle(stylePrimary);
if (this.isAutoContrast) {
this._recalculateAndSetContrastColor(varPrefix);
}
}
setAccentColor(hex) {
this.accent = hex;
const varPrefix = MatCssPalettePrefix.Accent;
const styleAccent = this._computePaletteColors(varPrefix, this.accent);
this._setStyle(styleAccent);
if (this.isAutoContrast) {
this._recalculateAndSetContrastColor(varPrefix);
}
}
setWarnColor(hex) {
this.warn = hex;
const varPrefix = MatCssPalettePrefix.Warn;
const styleWarn = this._computePaletteColors(varPrefix, this.warn);
this._setStyle(styleWarn);
if (this.isAutoContrast) {
this._recalculateAndSetContrastColor(varPrefix);
}
}
setVariable(cssVarName, value) {
this._setStyle([
{
name: cssVarName,
val: value,
},
]);
}
setDarkTheme(isDark) {
if (isDark) {
this.document.body.classList.remove(this.cfg.lightThemeClass);
this.document.body.classList.add(this.cfg.darkThemeClass);
}
else {
this.document.body.classList.remove(this.cfg.darkThemeClass);
this.document.body.classList.add(this.cfg.lightThemeClass);
}
this.isDarkTheme = isDark;
}
setAutoContrastEnabled(val) {
this.isAutoContrast = val;
if (val) {
this._recalculateAndSetContrastColor(MatCssPalettePrefix.Primary);
this._recalculateAndSetContrastColor(MatCssPalettePrefix.Accent);
this._recalculateAndSetContrastColor(MatCssPalettePrefix.Warn);
}
else {
this.setContrastColorThresholdPrimary(this.contrastColorThresholdPrimary);
this.setContrastColorThresholdAccent(this.contrastColorThresholdAccent);
this.setContrastColorThresholdWarn(this.contrastColorThresholdWarn);
}
}
setContrastColorThresholdPrimary(threshold) {
this.contrastColorThresholdPrimary = threshold;
this.setContrastColorThreshold(threshold, MatCssPalettePrefix.Primary);
}
setContrastColorThresholdAccent(threshold) {
this.contrastColorThresholdAccent = threshold;
this.setContrastColorThreshold(threshold, MatCssPalettePrefix.Accent);
}
setContrastColorThresholdWarn(threshold) {
this.contrastColorThresholdWarn = threshold;
this.setContrastColorThreshold(threshold, MatCssPalettePrefix.Warn);
}
setContrastColorThreshold(threshold, palettePrefix) {
if (this.isAutoContrast) {
return;
}
let color = MaterialCssVarsService.DARK_TEXT_VAR;
const updates = this.cfg.sortedHues.map((hue) => {
if (hue === threshold) {
color = MaterialCssVarsService.LIGHT_TEXT_VAR;
}
return {
val: `var(${color})`, //val: this._getCssVarValue(color),
name: `${palettePrefix + MaterialCssVarsService.CONTRAST_PREFIX}${hue}`,
};
});
this._setStyle(updates);
}
/**
* Generate palette color based on traditional values
*/
setAlternativeColorAlgorithm(traditional) {
this.cfg.isAlternativeColorAlgorithm = traditional;
this.setPrimaryColor(this.primary);
this.setAccentColor(this.accent);
this.setWarnColor(this.warn);
}
getPaletteForColor(hex) {
if (this.cfg.isAlternativeColorAlgorithm) {
return this.getTraditionalPaletteForColor(hex);
}
else {
return this.getConstantinPaletteForColor(hex);
}
}
getPaletteWithContrastForColor(hex) {
const lightText = this._getCssVarValue(MaterialCssVarsService.LIGHT_TEXT_VAR);
const darkText = this._getCssVarValue(MaterialCssVarsService.DARK_TEXT_VAR);
const palette = this.getPaletteForColor(hex);
// TODO handle non auto case
return palette.map((item) => {
const contrastStr = item.isLight ? lightText : darkText;
const sLight = this._replaceNoRgbValue("", contrastStr)
.split(",")
.map((v) => +v);
const cco = { r: sLight[0], g: sLight[1], b: sLight[2], a: 1 };
return {
...item,
contrast: {
...cco,
str: `${cco.r},${cco.g},${cco.b}`,
},
};
});
}
getTraditionalPaletteForColor(hex) {
return this.cfg.colorMap.map((item) => {
const mappedColor = new TinyColor(hex)
.lighten(item.map[0])
.darken(item.map[1])
.saturate(item.map[2]);
const c = new TinyColor(mappedColor);
return {
hue: item.name,
isLight: c.isLight(),
color: {
...c.toRgb(),
str: `rgb(${c.toRgb().r},${c.toRgb().g},${c.toRgb().b})`,
},
};
});
}
getConstantinPaletteForColor(hex) {
return this.cfg.colorMap.map((item) => {
const c = this.computePalletTriad(hex, item.name);
return {
hue: item.name,
isLight: c.isLight,
color: {
...c.rgb,
str: `rgb(${c.rgb.r},${c.rgb.g},${c.rgb.b})`,
},
};
});
}
_computePaletteColors(prefix, hex) {
return this.getPaletteForColor(hex).map((item) => {
const c = item.color;
return {
name: `${prefix}${item.hue}`,
val: `rgb(${c.r}, ${c.g}, ${c.b})`,
};
});
}
_recalculateAndSetContrastColor(palettePrefix) {
const updates = this._calculateContrastColorsForCurrentValues(palettePrefix).map(({ contrastColorVar, hue }) => {
return {
val: `var(${contrastColorVar})`, //this._getCssVarValue(contrastColorVar),
name: `${palettePrefix + MaterialCssVarsService.CONTRAST_PREFIX}${hue}`,
};
});
this._setStyle(updates);
}
_calculateContrastColorsForCurrentValues(palettePrefix) {
return this.cfg.sortedHues.map((hue) => {
const hueVarVal = this._getCssVarValue(`${palettePrefix}${hue}`);
const c = new TinyColor(`rgb(${hueVarVal})`);
const contrastColorVar = this._getContrastColorVar(c);
return {
contrastColorVar,
hue,
};
});
}
_setStyle(vars) {
vars.forEach((s) => {
this.renderer.setStyle(this.ROOT, s.name, s.val, RendererStyleFlags2.DashCase);
this.renderer.setStyle(this.ROOT, s.name + "-no-rgb", this._replaceNoRgbValue(s.name, s.val), RendererStyleFlags2.DashCase);
});
}
/**
* Replace variables that are formatted as rgba(var(rgb(xxx))) to be var(xxx) to allow proper formatting
* in variable overrides.
* @param value
* @returns
*/
_replaceNoRgbValue(name, value) {
const isContrast = name.includes(MaterialCssVarsService.CONTRAST_PREFIX);
let noRgb = "";
if (isContrast) {
noRgb = value.replace(")", "-no-rgb)");
}
else {
noRgb = value.replace("rgba(", "").replace("rgb(", "").replace(")", "");
if (noRgb.startsWith("var(")) {
noRgb = noRgb.concat(")");
}
}
return noRgb;
}
_getCssVarValue(v) {
return getComputedStyle(this.ROOT).getPropertyValue(v);
}
/**
* Compute pallet colors based on a Triad (Constantin)
* see: https://github.com/mbitson/mcg
*/
computePalletTriad(hex, hue) {
const baseLight = new TinyColor("#ffffff");
const baseDark = this.multiply(new TinyColor(hex).toRgb(), new TinyColor(hex).toRgb());
const baseTriad = new TinyColor(hex).tetrad();
let color;
switch (hue) {
case "50":
color = this.getColorObject(baseLight.mix(hex, 12));
break;
case "100":
color = this.getColorObject(baseLight.mix(hex, 30));
break;
case "200":
color = this.getColorObject(baseLight.mix(hex, 50));
break;
case "300":
color = this.getColorObject(baseLight.mix(hex, 70));
break;
case "400":
color = this.getColorObject(baseLight.mix(hex, 85));
break;
case "500":
color = this.getColorObject(baseLight.mix(hex, 100));
break;
case "600":
color = this.getColorObject(baseDark.mix(hex, 87));
break;
case "700":
color = this.getColorObject(baseDark.mix(hex, 70));
break;
case "800":
color = this.getColorObject(baseDark.mix(hex, 54));
break;
case "900":
color = this.getColorObject(baseDark.mix(hex, 25));
break;
case "A100":
color = this.getColorObject(baseDark.mix(baseTriad[4], 15).saturate(80).lighten(65));
break;
case "A200":
color = this.getColorObject(baseDark.mix(baseTriad[4], 15).saturate(80).lighten(55));
break;
case "A400":
color = this.getColorObject(baseDark.mix(baseTriad[4], 15).saturate(100).lighten(45));
break;
case "A700":
color = this.getColorObject(baseDark.mix(baseTriad[4], 15).saturate(100).lighten(40));
break;
}
return color;
}
multiply(rgb1, rgb2) {
rgb1.b = Math.floor((rgb1.b * rgb2.b) / 255);
rgb1.g = Math.floor((rgb1.g * rgb2.g) / 255);
rgb1.r = Math.floor((rgb1.r * rgb2.r) / 255);
return new TinyColor("rgb " + rgb1.r + " " + rgb1.g + " " + rgb1.b);
}
getColorObject(value) {
const c = new TinyColor(value);
return { rgb: c.toRgb(), isLight: c.isLight() };
}
_getContrastColorVar(color) {
const contrastDark = this._getContrast(color, this._black);
const contrastLight = this._getContrast(color, this._white);
return contrastLight > contrastDark
? MaterialCssVarsService.LIGHT_TEXT_VAR
: MaterialCssVarsService.DARK_TEXT_VAR;
}
_getContrast(color1, color2) {
const luminance1 = color1.getLuminance();
const luminance2 = color2.getLuminance();
const brightest = Math.max(luminance1, luminance2);
const darkest = Math.min(luminance1, luminance2);
return (brightest + 0.05) / (darkest + 0.05);
}
_getRootElement(rootSelector) {
if (typeof rootSelector !== "string") {
return this.document.documentElement;
}
const rootElement = document.querySelector(rootSelector);
if (rootElement) {
return rootElement;
}
if (isDevMode()) {
console.warn(`[MaterialCssVars] Could not find root element: ${rootSelector}. Falling back to HTML element.`);
}
return this.document.documentElement;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: MaterialCssVarsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: MaterialCssVarsService, providedIn: "root" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: MaterialCssVarsService, decorators: [{
type: Injectable,
args: [{
providedIn: "root",
}]
}], ctorParameters: () => [] });
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
class MaterialCssVarsModule {
static forRoot(config) {
return {
ngModule: MaterialCssVarsModule,
providers: [provideMaterialCssVars(config)],
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: MaterialCssVarsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.0", ngImport: i0, type: MaterialCssVarsModule, imports: [CommonModule] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: MaterialCssVarsModule, imports: [CommonModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: MaterialCssVarsModule, decorators: [{
type: NgModule,
args: [{
imports: [CommonModule],
}]
}] });
function provideMaterialCssVars(config) {
return makeEnvironmentProviders([
{ provide: MATERIAL_CSS_VARS_CFG, useValue: config },
provideEnvironmentInitializer(() => inject(MaterialCssVarsService)),
]);
}
/*
* Public API Surface of material-css-vars
*/
/**
* Generated bundle index. Do not edit.
*/
export { MatCssPalettePrefix, MaterialCssVariables, MaterialCssVarsModule, MaterialCssVarsService, provideMaterialCssVars };
//# sourceMappingURL=angular-material-css-vars.mjs.map