typestyle
Version:
194 lines (193 loc) • 7.42 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var FreeStyle = require("free-style");
var formatting_1 = require("./formatting");
var utilities_1 = require("./utilities");
/**
* Creates an instance of free style with our options
*/
var createFreeStyle = function () { return FreeStyle.create(); };
/**
* Maintains a single stylesheet and keeps it in sync with requested styles
*/
var TypeStyle = /** @class */ (function () {
function TypeStyle(_a) {
var _this = this;
var autoGenerateTag = _a.autoGenerateTag;
/**
* Insert `raw` CSS as a string. This is useful for e.g.
* - third party CSS that you are customizing with template strings
* - generating raw CSS in JavaScript
* - reset libraries like normalize.css that you can use without loaders
*/
this.cssRaw = function (mustBeValidCSS) {
if (!mustBeValidCSS) {
return;
}
_this._raw += mustBeValidCSS || '';
_this._pendingRawChange = true;
_this._styleUpdated();
};
/**
* Takes CSSProperties and registers it to a global selector (body, html, etc.)
*/
this.cssRule = function (selector) {
var objects = [];
for (var _i = 1; _i < arguments.length; _i++) {
objects[_i - 1] = arguments[_i];
}
var styles = formatting_1.convertToStyles(utilities_1.extend.apply(void 0, objects));
_this._freeStyle.registerRule(selector, styles);
_this._styleUpdated();
return;
};
/**
* Renders styles to the singleton tag imediately
* NOTE: You should only call it on initial render to prevent any non CSS flash.
* After that it is kept sync using `requestAnimationFrame` and we haven't noticed any bad flashes.
**/
this.forceRenderStyles = function () {
var target = _this._getTag();
if (!target) {
return;
}
target.textContent = _this.getStyles();
};
/**
* Utility function to register an @font-face
*/
this.fontFace = function () {
var fontFace = [];
for (var _i = 0; _i < arguments.length; _i++) {
fontFace[_i] = arguments[_i];
}
var freeStyle = _this._freeStyle;
for (var _a = 0, _b = fontFace; _a < _b.length; _a++) {
var face = _b[_a];
freeStyle.registerRule('@font-face', face);
}
_this._styleUpdated();
return;
};
/**
* Allows use to use the stylesheet in a node.js environment
*/
this.getStyles = function () {
return (_this._raw || '') + _this._freeStyle.getStyles();
};
/**
* Takes keyframes and returns a generated animationName
*/
this.keyframes = function (frames) {
var keyframes = formatting_1.convertToKeyframes(frames);
// TODO: replace $debugName with display name
var animationName = _this._freeStyle.registerKeyframes(keyframes);
_this._styleUpdated();
return animationName;
};
/**
* Helps with testing. Reinitializes FreeStyle + raw
*/
this.reinit = function () {
/** reinit freestyle */
var freeStyle = createFreeStyle();
_this._freeStyle = freeStyle;
_this._lastFreeStyleChangeId = freeStyle.changeId;
/** reinit raw */
_this._raw = '';
_this._pendingRawChange = false;
/** Clear any styles that were flushed */
var target = _this._getTag();
if (target) {
target.textContent = '';
}
};
/** Sets the target tag where we write the css on style updates */
this.setStylesTarget = function (tag) {
/** Clear any data in any previous tag */
if (_this._tag) {
_this._tag.textContent = '';
}
_this._tag = tag;
/** This special time buffer immediately */
_this.forceRenderStyles();
};
/**
* Takes an object where property names are ideal class names and property values are CSSProperties, and
* returns an object where property names are the same ideal class names and the property values are
* the actual generated class names using the ideal class name as the $debugName
*/
this.stylesheet = function (classes) {
var classNames = Object.getOwnPropertyNames(classes);
var result = {};
for (var _i = 0, classNames_1 = classNames; _i < classNames_1.length; _i++) {
var className = classNames_1[_i];
var classDef = classes[className];
if (classDef) {
classDef.$debugName = className;
result[className] = _this.style(classDef);
}
}
return result;
};
var freeStyle = createFreeStyle();
this._autoGenerateTag = autoGenerateTag;
this._freeStyle = freeStyle;
this._lastFreeStyleChangeId = freeStyle.changeId;
this._pending = 0;
this._pendingRawChange = false;
this._raw = '';
this._tag = undefined;
// rebind prototype to TypeStyle. It might be better to do a function() { return this.style.apply(this, arguments)}
this.style = this.style.bind(this);
}
/**
* Only calls cb all sync operations settle
*/
TypeStyle.prototype._afterAllSync = function (cb) {
var _this = this;
this._pending++;
var pending = this._pending;
utilities_1.raf(function () {
if (pending !== _this._pending) {
return;
}
cb();
});
};
TypeStyle.prototype._getTag = function () {
if (this._tag) {
return this._tag;
}
if (this._autoGenerateTag) {
var tag = typeof window === 'undefined'
? { textContent: '' }
: document.createElement('style');
if (typeof document !== 'undefined') {
document.head.appendChild(tag);
}
this._tag = tag;
return tag;
}
return undefined;
};
/** Checks if the style tag needs updating and if so queues up the change */
TypeStyle.prototype._styleUpdated = function () {
var _this = this;
var changeId = this._freeStyle.changeId;
var lastChangeId = this._lastFreeStyleChangeId;
if (!this._pendingRawChange && changeId === lastChangeId) {
return;
}
this._lastFreeStyleChangeId = changeId;
this._pendingRawChange = false;
this._afterAllSync(function () { return _this.forceRenderStyles(); });
};
TypeStyle.prototype.style = function () {
var className = this._freeStyle.registerStyle(formatting_1.convertToStyles(utilities_1.extend.apply(undefined, arguments)));
this._styleUpdated();
return className;
};
return TypeStyle;
}());
exports.TypeStyle = TypeStyle;