UNPKG

@playkit-js/playkit-js-ui

Version:

[![Build Status](https://github.com/kaltura/playkit-js-ui/actions/workflows/run_canary_full_flow.yaml/badge.svg)](https://github.com/kaltura/playkit-js-ui/actions/workflows/run_canary_full_flow.yaml) [![code style: prettier](https://img.shields.io/badge/c

193 lines (174 loc) • 6.4 kB
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); } }