Last active
December 16, 2020 19:28
-
-
Save teyfix/df4b9452b7473bedf23ce5f9a6c73d04 to your computer and use it in GitHub Desktop.
a helper class to manage local styles with javascript efficiently
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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