playable
Version:
Video player based on HTML5Video
108 lines (87 loc) • 3.1 kB
text/typescript
import camelToKebab from './utils/camelToKebab';
import generateClassNames from './utils/generateClassNames';
import { IStyles } from '../types';
import { ICSSRuleFunction, ICSSRules, ICSSRule } from './types';
export class StyleSheet {
private _rulesByModule: Map<object, ICSSRules> = new Map();
private _classNamesByModule: Map<object, IStyles> = new Map();
private _data: any = {};
private _styleNode: Element;
attach() {
this._styleNode = this._styleNode || document.createElement('style');
const discoveredStyles: string[] = [];
this._rulesByModule.forEach((_, module) => {
discoveredStyles.push(this._getModuleCSS(module));
});
this._styleNode.innerHTML = discoveredStyles.join(' ');
document.getElementsByTagName('head')[0].appendChild(this._styleNode);
}
update(data: any) {
this._data = {
...this._data,
...data,
};
if (this._styleNode) {
this.attach();
}
}
registerModuleTheme(module: object, rules: ICSSRules) {
//todo maybe we would like to update overrides for module? Or at least show warning instead of Error
if (this._rulesByModule.get(module)) {
throw new Error('can`t register multiple themes for one module');
}
this._rulesByModule.set(module, rules);
this._classNamesByModule.set(module, generateClassNames(rules));
}
getModuleClassNames(module: any): IStyles {
return this._classNamesByModule.get(module);
}
private _getModuleCSS(module: any): string {
const moduleRules = this._rulesByModule.get(module);
const moduleClassNames = this._classNamesByModule.get(module);
if (!moduleRules || !moduleClassNames) {
return '';
}
return Object.keys(moduleRules)
.map(classImportName =>
this._getRuleCSS(
moduleRules[classImportName],
moduleClassNames[classImportName],
),
)
.join(' ');
}
private _getRuleCSS(rule: ICSSRule, ruleClassName: string): string {
if (!rule || !ruleClassName) {
return '';
}
const complexRuleNames = Object.keys(rule)
.filter(ruleName => typeof rule[ruleName] === 'object')
.map(ruleName =>
ruleName.indexOf('&') !== -1 ? ruleName : `& ${ruleName}`,
);
const complexRules = complexRuleNames
.map(ruleName => {
const selector = ruleName.replace(/&/g, `.${ruleClassName}`);
//don't want to allow deep nesting now, maybe later
return `${selector} {${this._getRuleStyles(rule[ruleName])}}`;
})
.join(' ');
return `.${ruleClassName} {${this._getRuleStyles(rule)}} ${complexRules}`;
}
private _getRuleStyles(rule: any) {
const simpleRuleNames: Array<string> = Object.keys(rule).filter(
ruleName => typeof rule[ruleName] !== 'object',
);
return simpleRuleNames
.map(
ruleName =>
`${camelToKebab(ruleName)}: ${
typeof rule[ruleName] === 'function'
? (rule[ruleName] as ICSSRuleFunction)(this._data)
: rule[ruleName]
}`,
)
.join('; ');
}
}