UNPKG

typestyle

Version:
192 lines (191 loc) 7.28 kB
import * as FreeStyle from "free-style"; import { convertToStyles, convertToKeyframes } from './formatting'; import { extend, raf } from './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 = convertToStyles(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 = 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; 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(convertToStyles(extend.apply(undefined, arguments))); this._styleUpdated(); return className; }; return TypeStyle; }()); export { TypeStyle };