85 lines
1.9 KiB
JavaScript
85 lines
1.9 KiB
JavaScript
|
|
import * as css from 'css';
|
|
import React from 'react';
|
|
|
|
let newId = 0;
|
|
const map = new WeakMap();
|
|
let sheet = null;
|
|
|
|
// Ensure styles exist for a component.
|
|
const ensureComponentStyles = (constructor, styles) => {
|
|
|
|
if (!window || !styles || map.has(constructor)) {
|
|
return;
|
|
}
|
|
newId += 1;
|
|
const id = newId.toString(36);
|
|
map.set(constructor, id);
|
|
|
|
// Ensure sheet exists.
|
|
if (!sheet) {
|
|
sheet = window.document.createElement('style');
|
|
sheet.id = 'stylist-styles';
|
|
window.document.getElementsByTagName('head')[0].appendChild(sheet);
|
|
}
|
|
|
|
// All rules made component-specific.
|
|
for (const style of styles) {
|
|
|
|
const ast = css.parse(style);
|
|
ast.stylesheet.rules = mapStyleRulesTree(ast.stylesheet.rules, id);
|
|
sheet.innerHTML += css.stringify(ast);
|
|
}
|
|
}
|
|
|
|
const mapStyleRulesTree = (rules, id) => rules.map((rule) => {
|
|
|
|
// Recur for mediaqueries.
|
|
if ('media' === rule.type) {
|
|
rule.rules = mapStyleRulesTree(rule.rules, id);
|
|
}
|
|
|
|
if (!rule.selectors) {
|
|
return rule;
|
|
}
|
|
|
|
return {
|
|
...rule,
|
|
...{
|
|
selectors: rule.selectors.map((rule) =>
|
|
`[data-component-style="${id}"] ${rule}`
|
|
),
|
|
},
|
|
};
|
|
});
|
|
|
|
export default function contempo(styles) { return function (Component) {
|
|
if (!Array.isArray(styles)) {
|
|
styles = [styles];
|
|
}
|
|
|
|
const createRenderFunction = (constructor, markupFn) => function() {
|
|
ensureComponentStyles(constructor, styles);
|
|
|
|
const markup = markupFn.apply(this, arguments);
|
|
const id = map.get(constructor);
|
|
if (!id) {
|
|
return markup;
|
|
}
|
|
return <ins data-component-style={id}>{markup}</ins>;
|
|
}
|
|
|
|
if (Component.prototype.render) {
|
|
class ContempoComponent extends Component {
|
|
}
|
|
ContempoComponent.prototype.render = createRenderFunction(
|
|
Component, Component.prototype.render
|
|
);
|
|
return ContempoComponent;
|
|
}
|
|
else {
|
|
return createRenderFunction(Component, Component);
|
|
}
|
|
}}
|
|
|