@playkit-js/playkit-js-ui
Version:
[](https://github.com/kaltura/playkit-js-ui/actions/workflows/run_canary_full_flow.yaml) [ • 6.4 kB
text/typescript
import {hexToHsl, hslToHex} from './color-format-convertors';
import style from '../styles/style.scss';
import {UserTheme} from '../types';
const PREFIX = 'playkit';
const ACTUAL_USED_CSS_VAR = `--${PREFIX}-{name}-color`;
const HSL_HUE_CSS_VAR = `--${PREFIX}-{name}-hsl-hue`;
const HSL_SATURATION_CSS_VAR = `--${PREFIX}-{name}-hsl-saturation`;
const HSL_LIGHTNESS_CSS_VAR = `--${PREFIX}-{name}-hsl-lightness`;
const cssVarNames = {
colors: {
live: '--playkit-live-color',
playerBackground: '--playkit-player-background-color',
paperSurface: '--playkit-paper-color',
elevatedSurface: '--playkit-elevated-color',
protectionSurface: '--playkit-protection-color',
tone1: '--playkit-tone-1-color',
tone2: '--playkit-tone-2-color',
tone3: '--playkit-tone-3-color',
tone4: '--playkit-tone-4-color',
tone5: '--playkit-tone-5-color',
tone6: '--playkit-tone-6-color',
tone7: '--playkit-tone-7-color',
tone8: '--playkit-tone-8-color',
tone1RGB: '--playkit-tone-1-color-rgb',
tone2RGB: '--playkit-tone-2-color-rgb',
tone3RGB: '--playkit-tone-3-color-rgb',
tone4RGB: '--playkit-tone-4-color-rgb',
tone5RGB: '--playkit-tone-5-color-rgb',
tone6RGB: '--playkit-tone-6-color-rgb',
tone7RGB: '--playkit-tone-7-color-rgb',
tone8RGB: '--playkit-tone-8-color-rgb'
}
};
const ACCENT_AND_ACKNOWLEDGEMENT_COLORS = ['primary', 'secondary', 'success', 'danger', 'warning'];
const dynamicColoredIconsSvgUrlVars = [
'--playkit-icon-data-url',
'--playkit-icon-chromecast-url',
'--playkit-icon-quality-HD-active-url',
'--playkit-icon-quality-4K-active-url',
'--playkit-icon-quality-8K-active-url'
];
const TEXT_CONTRAST_SUFFIX = 'text-contrast';
// eslint-disable-next-line require-jsdoc
export class ThemesManager {
private player: any;
private readonly config?: UserTheme;
private playerContainerElement: HTMLElement;
// eslint-disable-next-line require-jsdoc
constructor(player: any, config: UserTheme | undefined, targetId: string) {
this.player = player;
this.config = config;
this.playerContainerElement = document.querySelector<HTMLDivElement>(`#${targetId}`)!;
}
/**
* Apply the user theme from config (should be called only after the UI was build and the css vars are exist in the DOM).
* @returns {void}
*/
public applyUserTheme(): void {
if (this.config) {
this.setColors(this.config);
}
}
/**
* Makes color value for text.
* @param {string} inputColor - css color in HEX format
* @returns {string} hex - css color in HEX format
*/
private makeTextContrastColor(inputColor: string): string {
const hsl = hexToHsl(inputColor);
const hue = hsl[0];
const saturation = hsl[1];
let lightness = hsl[2];
if (hue < 30 || hue > 205) {
if (lightness < 70) {
lightness = 100;
} else {
lightness = 0;
}
}
if (hue > 30 && hue < 205) {
if (lightness < 40) {
lightness = 100;
} else {
lightness = 0;
}
}
return hslToHex(hue, saturation, lightness);
}
/**
* Override the colors from config.
* @param {UserTheme} config -
* @returns {void}
*/
private setColors(config: UserTheme): void {
if (config.colors.primary) {
this.setSvgFillColor(config.colors.primary);
}
for (const color in config.colors) {
if (ACCENT_AND_ACKNOWLEDGEMENT_COLORS.includes(color)) {
const colorValue = config.colors[color];
const textContrastColor = this.makeTextContrastColor(colorValue);
this.setAccentOrAcknowledgementColor(`${color}-${TEXT_CONTRAST_SUFFIX}`, textContrastColor);
this.setAccentOrAcknowledgementColor(color, colorValue);
if (color === 'primary') {
this.setSvgFillColor(textContrastColor, true);
}
} else {
this.setColor(cssVarNames.colors[color], config.colors[color]);
}
}
}
/**
* Override the specified css var value.
* @param {string} colorTitle -
* @param {string} color -
* @returns {void}
*/
private setAccentOrAcknowledgementColor(colorTitle: string, color: string): void {
const [hue, saturation, lightness] = hexToHsl(color);
this.setCSSVariable(ACTUAL_USED_CSS_VAR.replace('{name}', colorTitle), color);
this.setCSSVariable(HSL_HUE_CSS_VAR.replace('{name}', colorTitle), `${Math.round(hue)}deg`);
this.setCSSVariable(HSL_SATURATION_CSS_VAR.replace('{name}', colorTitle), `${Math.round(saturation)}%`);
this.setCSSVariable(HSL_LIGHTNESS_CSS_VAR.replace('{name}', colorTitle), `${Math.round(lightness)}%`);
}
/**
* Override the specified css var value.
* @param {string} cssVarName -
* @param {string} color -
* @returns {void}
*/
private setColor(cssVarName: string, color: string): void {
this.setCSSVariable(cssVarName, color);
}
/**
* Update the SVG url (of dynamic SVGs) with the new primary color and contrast text color.
* @param {string} color -
* @returns {void}
*/
private setSvgFillColor(color: string, contrastColor: boolean = false): void {
for (const varName of dynamicColoredIconsSvgUrlVars) {
// $FlowFixMe
const svgUrl = this.getCSSVariable(varName);
const newColor = color.replace('#', '%23');
let regex = /fill='%23([a-f0-9]{3}){1,2}\b'/g;
if (contrastColor) {
regex = /fill='textColor'/g;
}
this.setCSSVariable(varName, svgUrl.replaceAll(regex, `fill='${newColor}'`));
}
}
/**
* Return a css variable value
* @param {string} variableName - CSS variable name
* @returns {string} CSS variable value
*/
public getCSSVariable(variableName: string): string {
return (
getComputedStyle(this.playerContainerElement.querySelector<HTMLDivElement>(`.${style.player}`) as HTMLDivElement).getPropertyValue(
variableName
) || ''
);
}
/**
* Return a css variable name
* @param {string} variableName - CSS variable name
* @param {string} value - CSS variable value
* @returns {void}
*/
public setCSSVariable(variableName: string, value: string): void {
const playkitPlayerElement = this.playerContainerElement.querySelector<HTMLDivElement>(`.${style.player}`)!;
const playkitPlayerElementStyle = playkitPlayerElement.style;
playkitPlayerElementStyle.setProperty(variableName, value);
}
}