Skip to content

Instantly share code, notes, and snippets.

@teyfix
Last active December 16, 2020 19:28
Show Gist options
  • Save teyfix/df4b9452b7473bedf23ce5f9a6c73d04 to your computer and use it in GitHub Desktop.
Save teyfix/df4b9452b7473bedf23ce5f9a6c73d04 to your computer and use it in GitHub Desktop.
a helper class to manage local styles with javascript efficiently
class StyleManager {
/**
* creates a native style element
*/
static createElement() {
const style = document.createElement('style');
style.setAttribute('type', 'text/css');
return style;
}
/**
* converts camel case to kebab case
* @param {string} input
*/
static toKebabCase(input) {
if ('string' === typeof input) {
input = input.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
}
return input;
}
static parseValue(input) {
if ('number' === typeof input && Number.isFinite(input)) {
if (0 === input) {
input = '0';
} else {
input += 'px';
}
}
if ('string' === typeof input) {
return input;
}
return '';
}
/**
* converts input object to plain css
*
* input:
* {
* color: '#fff',
* fontSize: 14,
* }
*
* output:
* color:#fff;fontSize:14px
*
* @param {object} input Partial<CSSStyleDeclaration>
*/
static compileCSS(input) {
let css = '';
for (let key in input) {
if (!input.hasOwnProperty(key)) {
continue;
}
const value = this.parseValue(input[key]);
if (!value) {
continue;
}
if (css) {
css += ';';
}
css += this.toKebabCase(key) + ':' + value;
}
return css;
}
/**
* converts input object to css with selectors
*
* input:
* {
* p: {
* color: '#fff',
* fontSize: 14,
* }
* }
*
* output:
* p{color:#fff;fontSize:14px}
*
* @param {object} styles Record<string, Partial<CSSStyleDeclaration>>
*/
static compileStyles(styles) {
if (null == styles || 'object' !== typeof styles) {
return '';
}
let css = '';
for (const selector in styles) {
if (!styles.hasOwnProperty(selector)) {
continue;
}
const local = this.compileCSS(styles[selector]);
if ('' === local) {
continue;
}
css += selector + '{' + local + '}';
}
return css;
}
constructor(config) {
config = config || {};
this.parent = config.parent || document.body;
this.frame = config.frame || 1e3 / 60;
this.style = config.style || '';
this.native = config.native || StyleManager.createElement();
this.parent.appendChild(this.native);
}
/**
* compiles and appends the styles to the current style stack
*
* @param {object} styles Record<string, Partial<CSSStyleDeclaration>>
*/
append(styles) {
const compiled = StyleManager.compileStyles(styles);
if (!compiled) {
return;
}
this.style += compiled;
this.tick();
}
/**
* creates a timer to update the dom
*/
tick() {
if (null != this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(this.update.bind(this), this.frame);
}
/**
* updates native elements inner html
*/
update() {
this.timer = null;
if (this.style === this.native.innerHTML) {
return;
}
this.native.innerHTML = this.style;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment