Comparing version 1.0.0-alpha.5 to 1.0.0-alpha.6
847
dist/fela.js
@@ -38,2 +38,17 @@ (function (global, factory) { | ||
babelHelpers.defineProperty = function (obj, key, value) { | ||
if (key in obj) { | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: true, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} else { | ||
obj[key] = value; | ||
} | ||
return obj; | ||
}; | ||
babelHelpers.extends = Object.assign || function (target) { | ||
@@ -55,13 +70,738 @@ for (var i = 1; i < arguments.length; i++) { | ||
var Selector = function () { | ||
function __commonjs(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } | ||
var emptyFunction = __commonjs(function (module) { | ||
"use strict"; | ||
/** | ||
* Copyright (c) 2013-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
* | ||
* | ||
*/ | ||
function makeEmptyFunction(arg) { | ||
return function () { | ||
return arg; | ||
}; | ||
} | ||
/** | ||
* This function accepts and discards inputs; it has no side effects. This is | ||
* primarily useful idiomatically for overridable function endpoints which | ||
* always need to be callable, since JS lacks a null-call idiom ala Cocoa. | ||
*/ | ||
var emptyFunction = function emptyFunction() {}; | ||
emptyFunction.thatReturns = makeEmptyFunction; | ||
emptyFunction.thatReturnsFalse = makeEmptyFunction(false); | ||
emptyFunction.thatReturnsTrue = makeEmptyFunction(true); | ||
emptyFunction.thatReturnsNull = makeEmptyFunction(null); | ||
emptyFunction.thatReturnsThis = function () { | ||
return this; | ||
}; | ||
emptyFunction.thatReturnsArgument = function (arg) { | ||
return arg; | ||
}; | ||
module.exports = emptyFunction; | ||
}); | ||
var require$$0 = (emptyFunction && typeof emptyFunction === 'object' && 'default' in emptyFunction ? emptyFunction['default'] : emptyFunction); | ||
var warning = __commonjs(function (module) { | ||
/** | ||
* Copyright 2014-2015, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
* | ||
*/ | ||
'use strict'; | ||
var emptyFunction = require$$0; | ||
/** | ||
* Similar to invariant but only logs a warning if the condition is not met. | ||
* This can be used to log issues in development environments in critical | ||
* paths. Removing the logging code for production environments will keep the | ||
* same logic and follow the same code paths. | ||
*/ | ||
var warning = emptyFunction; | ||
if (true) { | ||
warning = function warning(condition, format) { | ||
for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { | ||
args[_key - 2] = arguments[_key]; | ||
} | ||
if (format === undefined) { | ||
throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument'); | ||
} | ||
if (format.indexOf('Failed Composite propType: ') === 0) { | ||
return; // Ignore CompositeComponent proptype check. | ||
} | ||
if (!condition) { | ||
var argIndex = 0; | ||
var message = 'Warning: ' + format.replace(/%s/g, function () { | ||
return args[argIndex++]; | ||
}); | ||
if (typeof console !== 'undefined') { | ||
console.error(message); | ||
} | ||
try { | ||
// --- Welcome to debugging React --- | ||
// This error was thrown as a convenience so that you can use this stack | ||
// to find the callsite that caused this warning to fire. | ||
throw new Error(message); | ||
} catch (x) {} | ||
} | ||
}; | ||
} | ||
module.exports = warning; | ||
}); | ||
var warning$1 = (warning && typeof warning === 'object' && 'default' in warning ? warning['default'] : warning); | ||
/** | ||
* converts camel cased to dash cased properties | ||
* | ||
* @param {string} property - camel cased CSS property | ||
* @returns {string} dash cased CSS property | ||
*/ | ||
function camelToDashCase(property) { | ||
return property.replace(/([a-z]|^)([A-Z])/g, function (match, p1, p2) { | ||
return p1 + '-' + p2.toLowerCase(); | ||
}).replace('ms-', '-ms-'); | ||
} | ||
/** | ||
* generates a valid CSS string containing styles | ||
* | ||
* @param {Object} styles - object containing CSS declarations | ||
* @returns {string} valid CSS string with dash cased properties | ||
*/ | ||
function cssifyObject(styles) { | ||
return Object.keys(styles).reduce(function (css, prop) { | ||
// prevents the semicolon after | ||
// the last rule declaration | ||
if (css.length > 0) { | ||
css += ';'; | ||
} | ||
css += camelToDashCase(prop) + ':' + styles[prop]; | ||
return css; | ||
}, ''); | ||
} | ||
/** | ||
* generates a hashcode from a string | ||
* taken from http://stackoverflow.com/a/7616484 | ||
* | ||
* @param {string} str - str used to generate the unique hash code | ||
* @return {string} compressed content hash | ||
*/ | ||
function generateHash(str) { | ||
var hash = 0; | ||
var iterator = 0; | ||
var char = void 0; | ||
var length = str.length; | ||
// return a `s` for empty strings | ||
// to symbolize `static` | ||
if (length === 0) { | ||
return ''; | ||
} | ||
for (; iterator < length; ++iterator) { | ||
char = str.charCodeAt(iterator); | ||
hash = (hash << 5) - hash + char; | ||
hash |= 0; | ||
} | ||
return '-' + hash.toString(36); | ||
} | ||
/** | ||
* stringifies an object without any special character | ||
* uses a sort version of the obj to get same hash codes | ||
* | ||
* @param {Object} obj - obj that gets stringified | ||
* @return {string} stringyfied sorted object | ||
*/ | ||
function sortedStringify(obj) { | ||
if (obj === undefined) { | ||
return ''; | ||
} | ||
return Object.keys(obj).sort().reduce(function (str, prop) { | ||
// only concatenate property and value | ||
// without any special characters | ||
str += prop + obj[prop]; | ||
return str; | ||
}, ''); | ||
} | ||
var isPseudo = (function (property) { | ||
return property.charAt(0) === ':'; | ||
}) | ||
var isMediaQuery = (function (property) { | ||
return property.substr(0, 6) === '@media'; | ||
}) | ||
var StyleSheet = function () { | ||
/** | ||
* Selector is a dynamic style container | ||
* StyleSheet is a low-level container to cache Selectors | ||
* Keyframes and FontFaces which optimizes styles | ||
*/ | ||
function StyleSheet() { | ||
var config = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
babelHelpers.classCallCheck(this, StyleSheet); | ||
this.listeners = new Set(); | ||
this.keyframePrefixes = config.keyframePrefixes || ['-webkit-', '-moz-', '']; | ||
this.plugins = config.plugins || []; | ||
this._init(); | ||
} | ||
/** | ||
* clears the sheet's cache but keeps all listeners | ||
*/ | ||
babelHelpers.createClass(StyleSheet, [{ | ||
key: 'clear', | ||
value: function clear() { | ||
this._init(); | ||
} | ||
/** | ||
* renders all cached selector styles into a single valid CSS string | ||
* clusters media query styles into groups to reduce output size | ||
*/ | ||
}, { | ||
key: 'renderToString', | ||
value: function renderToString() { | ||
var _this = this; | ||
var css = ''; | ||
this.fontFaces.forEach(function (fontFace) { | ||
return css += fontFace; | ||
}); | ||
this.keyframes.forEach(function (variation) { | ||
variation.forEach(function (markup) { | ||
return css += markup; | ||
}); | ||
}); | ||
css += this._renderCache(this.cache); | ||
this.mediaCache.forEach(function (cache, media) { | ||
css += '@media ' + media + '{' + _this._renderCache(cache) + '}'; | ||
}); | ||
return css; | ||
} | ||
/** | ||
* Adds a new subscription to get notified on every rerender | ||
* | ||
* @param {Function} callback - callback function which will be executed | ||
* @return {Object} equivalent unsubscribe method | ||
*/ | ||
}, { | ||
key: 'subscribe', | ||
value: function subscribe(callback) { | ||
var _this2 = this; | ||
this.listeners.add(callback); | ||
return { | ||
unsubscribe: function unsubscribe() { | ||
return _this2.listeners.delete(callback); | ||
} | ||
}; | ||
} | ||
/** | ||
* calls each listener with the current CSS markup of all caches | ||
* gets only called if the markup actually changes | ||
* | ||
* @param {Function} callback - callback function which will be executed | ||
* @return {Object} equivalent unsubscribe method | ||
*/ | ||
}, { | ||
key: '_emitChange', | ||
value: function _emitChange() { | ||
var css = this.renderToString(); | ||
this.listeners.forEach(function (listener) { | ||
return listener(css); | ||
}); | ||
} | ||
/** | ||
* initializes the stylesheet by setting up the caches | ||
*/ | ||
}, { | ||
key: '_init', | ||
value: function _init() { | ||
this.cache = new Map(); | ||
this.mediaCache = new Map(); | ||
this.fontFaces = new Set(); | ||
this.keyframes = new Map(); | ||
this.ids = new Map(); | ||
this._counter = -1; | ||
} | ||
/** | ||
* renders a new font-face and caches it | ||
* | ||
* @param {FontFace} fontFace - fontFace which gets rendered | ||
* @return {string} fontFamily reference | ||
*/ | ||
}, { | ||
key: '_renderFontFace', | ||
value: function _renderFontFace(fontFace) { | ||
if (!this.fontFaces.has(fontFace)) { | ||
var renderedFontFace = '@font-face {' + cssifyObject(fontFace.render()) + '}'; | ||
this.fontFaces.add(renderedFontFace); | ||
this._emitChange(); | ||
} | ||
return fontFace.fontFamily; | ||
} | ||
/** | ||
* renders a new keyframe variation and caches the result | ||
* | ||
* @param {Keyframe} keyframe - Keyframe which gets rendered | ||
* @param {Object?} props - properties used to render | ||
* @param {Function[]?} plugins - array of plugins to process styles | ||
* @return {string} animationName to reference the rendered keyframe | ||
*/ | ||
}, { | ||
key: '_renderKeyframeVariation', | ||
value: function _renderKeyframeVariation(keyframe) { | ||
var props = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
var plugins = arguments.length <= 2 || arguments[2] === undefined ? [] : arguments[2]; | ||
// rendering a Keyframe for the first time | ||
// will create cache entries and an ID reference | ||
if (!this.keyframes.has(keyframe)) { | ||
this.keyframes.set(keyframe, new Map()); | ||
this.ids.set(keyframe, ++this._counter); | ||
} | ||
var cachedKeyframe = this.keyframes.get(keyframe); | ||
var propsReference = this._generatePropsReference(props); | ||
var animationName = 'k' + this.ids.get(keyframe) + propsReference; | ||
// only if the cached selector has not already been rendered | ||
// with a specific set of properties it actually renders | ||
if (!cachedKeyframe.has(propsReference)) { | ||
var pluginInterface = { | ||
plugins: this.plugins.concat(plugins), | ||
processStyles: this._processStyles, | ||
styles: keyframe.render(props), | ||
props: props | ||
}; | ||
var processedKeyframe = this._processStyles(pluginInterface); | ||
cachedKeyframe.set(propsReference, this._renderKeyframe(processedKeyframe, animationName)); | ||
this._emitChange(); | ||
} | ||
return animationName; | ||
} | ||
/** | ||
* renders a new selector variation and caches the result | ||
* | ||
* @param {Selector|Function} selector - Selector which gets rendered | ||
* @param {Object?} props - properties used to render | ||
* @param {Function[]?} plugins - array of plugins to process styles | ||
* @return {string} className to reference the rendered selector | ||
*/ | ||
}, { | ||
key: '_renderSelectorVariation', | ||
value: function _renderSelectorVariation(selector) { | ||
var _this3 = this; | ||
var props = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
var plugins = arguments.length <= 2 || arguments[2] === undefined ? [] : arguments[2]; | ||
// rendering a Selector for the first time | ||
// will create cache entries and an ID reference | ||
if (!this.cache.has(selector)) { | ||
this.ids.set(selector, ++this._counter); | ||
this.cache.set(selector, new Map()); | ||
// directly render the static base styles to be able | ||
// to diff future dynamic styles with those | ||
this._renderSelectorVariation(selector, {}, plugins); | ||
} | ||
var cachedSelector = this.cache.get(selector); | ||
var propsReference = this._generatePropsReference(props); | ||
// uses the reference ID and the props to generate an unique className | ||
var className = 'c' + this.ids.get(selector) + propsReference; | ||
// only if the cached selector has not already been rendered | ||
// with a specific set of properties it actually renders | ||
if (!cachedSelector.has(propsReference)) { | ||
(function () { | ||
// get the render method of either class-like selectors | ||
// or pure functional selectors without a constructor | ||
var pluginInterface = { | ||
plugins: _this3.plugins.concat(plugins), | ||
processStyles: _this3._processStyles, | ||
styles: selector(props), | ||
props: props | ||
}; | ||
var preparedStyles = _this3._prepareStyles(pluginInterface, cachedSelector.get('static'), propsReference); | ||
var clusteredStyles = _this3._clusterStyles(preparedStyles); | ||
if (Object.keys(clusteredStyles).length === 0) { | ||
cachedSelector.set(propsReference, ''); | ||
} | ||
Object.keys(clusteredStyles).forEach(function (media) { | ||
var renderedStyles = _this3._renderClusteredStyles(clusteredStyles[media], className); | ||
if (media === '') { | ||
cachedSelector.set(propsReference, renderedStyles); | ||
} else { | ||
if (!_this3.mediaCache.has(media)) { | ||
_this3.mediaCache.set(media, new Map([[selector, new Map()]])); | ||
} | ||
if (!_this3.mediaCache.get(media).has(selector)) { | ||
_this3.mediaCache.get(media).set(selector, new Map()); | ||
} | ||
_this3.mediaCache.get(media).get(selector).set(propsReference, renderedStyles); | ||
} | ||
}); | ||
// keep static styles to diff dynamic onces later on | ||
if (propsReference === '') { | ||
cachedSelector.set('static', preparedStyles); | ||
} | ||
// emit changes as the styles stack changed | ||
_this3._emitChange(); | ||
})(); | ||
} | ||
var baseClassName = 'c' + this.ids.get(selector); | ||
if (cachedSelector.get(propsReference) === '') { | ||
return baseClassName; | ||
} | ||
// returns either the base className or both the base and the dynamic part | ||
return className !== baseClassName ? baseClassName + ' ' + className : className; | ||
} | ||
/** | ||
* executes each plugin using a predefined plugin interface | ||
* | ||
* @param {Object} pluginInterface - interface containing relevant processing data | ||
* @return {Object} processed styles | ||
*/ | ||
}, { | ||
key: '_processStyles', | ||
value: function _processStyles(pluginInterface) { | ||
var plugins = pluginInterface.plugins; | ||
var styles = pluginInterface.styles; | ||
// pipes each plugin by passes the plugin interface | ||
// NOTE: as the styles are passed directly they're editable | ||
// therefore the plugin order might matter | ||
plugins.forEach(function (plugin) { | ||
return styles = plugin(pluginInterface); | ||
}); | ||
return styles; | ||
} | ||
/** | ||
* extracts all dynamic styles by diffing with the static base styles | ||
* | ||
* @param {Object} styles - dynamic styles | ||
* @param {Object} base - static base styles to diff | ||
* @return {Object} encapsulated dynamic styles | ||
*/ | ||
}, { | ||
key: '_extractDynamicStyles', | ||
value: function _extractDynamicStyles(styles, base) { | ||
var _this4 = this; | ||
Object.keys(styles).forEach(function (property) { | ||
var value = styles[property]; | ||
if (value instanceof Object && !Array.isArray(value)) { | ||
// also diff inner objects such as pseudo classes | ||
styles[property] = _this4._extractDynamicStyles(styles[property], base[property]); | ||
if (Object.keys(styles[property]).length === 0) { | ||
delete styles[property]; | ||
} | ||
// checks if the base styles has the property and if the value is equal | ||
} else if (base.hasOwnProperty(property) && base[property] === value) { | ||
delete styles[property]; | ||
} | ||
}); | ||
return styles; | ||
} | ||
/** | ||
* removes every invalid property except pseudo class objects | ||
* | ||
* @param {Object} styles - styles to be validated | ||
* @return {Object} validated styles | ||
*/ | ||
}, { | ||
key: '_validateStyles', | ||
value: function _validateStyles(styles) { | ||
var _this5 = this; | ||
Object.keys(styles).forEach(function (property) { | ||
var value = styles[property]; | ||
if (value instanceof Object && !Array.isArray(value)) { | ||
styles[property] = isPseudo(property) || isMediaQuery(property) ? _this5._validateStyles(value) : {}; | ||
if (Object.keys(styles[property]).length === 0) { | ||
delete styles[property]; | ||
} | ||
} else if (typeof value !== 'string' && typeof value !== 'number') { | ||
delete styles[property]; | ||
} | ||
}); | ||
return styles; | ||
} | ||
/** | ||
* flattens nested pseudo classes | ||
* removes all invalid properties that are not either a string or a number | ||
* | ||
* @param {Object} styles - dynamic styles | ||
* @return {Object} flat and validated styles | ||
*/ | ||
}, { | ||
key: '_clusterStyles', | ||
value: function _clusterStyles(styles) { | ||
var pseudo = arguments.length <= 1 || arguments[1] === undefined ? '' : arguments[1]; | ||
var _this6 = this; | ||
var media = arguments.length <= 2 || arguments[2] === undefined ? '' : arguments[2]; | ||
var out = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3]; | ||
Object.keys(styles).forEach(function (property) { | ||
var value = styles[property]; | ||
if (value instanceof Object && !Array.isArray(value)) { | ||
if (isPseudo(property)) { | ||
_this6._clusterStyles(value, pseudo + property, media, out); | ||
} else if (isMediaQuery(property)) { | ||
var query = property.slice(6).trim(); | ||
var nestedMedia = media !== '' ? media + ' and ' + query : query; | ||
_this6._clusterStyles(value, pseudo, nestedMedia, out); | ||
} | ||
} else { | ||
if (!out[media]) { | ||
out[media] = babelHelpers.defineProperty({}, pseudo, {}); | ||
} | ||
if (!out[media][pseudo]) { | ||
out[media][pseudo] = {}; | ||
} | ||
out[media][pseudo][property] = value; | ||
} | ||
}); | ||
return out; | ||
} | ||
/** | ||
* processes, flattens, normalizes and diffs styles | ||
* | ||
* @param {Object} pluginInterface - plugin interface to process styles | ||
* @param {Object} baseStyles - static base styles | ||
* @return {Object} processed styles | ||
*/ | ||
}, { | ||
key: '_prepareStyles', | ||
value: function _prepareStyles(pluginInterface, baseStyles, propsReference) { | ||
var processedStyles = this._processStyles(pluginInterface); | ||
var validatedStyles = this._validateStyles(processedStyles); | ||
// only diff and extract dynamic styles | ||
// if not actually rendering the base styles | ||
if (propsReference !== '') { | ||
return this._extractDynamicStyles(validatedStyles, baseStyles); | ||
} | ||
return validatedStyles; | ||
} | ||
/** | ||
* generates an unique reference id by content hashing props | ||
* | ||
* @param {Object} props - props that get hashed | ||
* @return {string} reference - unique props reference | ||
*/ | ||
}, { | ||
key: '_generatePropsReference', | ||
value: function _generatePropsReference(props) { | ||
return generateHash(sortedStringify(props)); | ||
} | ||
/** | ||
* renders clustered styles into a CSS string | ||
* | ||
* @param {Object} styles - prepared styles with pseudo keys | ||
* @param {string} className - className reference to render | ||
* @return {string} valid CSS string | ||
*/ | ||
}, { | ||
key: '_renderClusteredStyles', | ||
value: function _renderClusteredStyles(styles, className) { | ||
return Object.keys(styles).reduce(function (css, pseudo) { | ||
return css + '.' + className + pseudo + '{' + cssifyObject(styles[pseudo]) + '}'; | ||
}, ''); | ||
} | ||
/** | ||
* renders keyframes into a CSS string with all prefixes | ||
* | ||
* @param {Object} frames - validated frame declarations | ||
* @param {string} animationName - animation reference naming | ||
* @return {string} valid CSS string | ||
*/ | ||
}, { | ||
key: '_renderKeyframe', | ||
value: function _renderKeyframe(frames, animationName) { | ||
var _this7 = this; | ||
var keyframe = Object.keys(frames).reduce(function (css, percentage) { | ||
return css + percentage + '{' + cssifyObject(_this7._validateStyles(frames[percentage])) + '}'; | ||
}, ''); | ||
return this.keyframePrefixes.reduce(function (css, prefix) { | ||
return css + '@' + prefix + 'keyframes ' + animationName + '{' + keyframe + '}'; | ||
}, ''); | ||
} | ||
/** | ||
* renders a whole cache into a single CSS string | ||
* | ||
* @param {Map} cache - cache including all selector variations | ||
* @return {string} valid CSS string | ||
*/ | ||
}, { | ||
key: '_renderCache', | ||
value: function _renderCache(cache) { | ||
var css = ''; | ||
cache.forEach(function (variation) { | ||
variation.forEach(function (markup, propsReference) { | ||
if (propsReference !== 'static') { | ||
css += markup; | ||
} | ||
}); | ||
}); | ||
return css; | ||
} | ||
}]); | ||
return StyleSheet; | ||
}(); | ||
var formats = { | ||
'.woff': 'woff', | ||
'.eof': 'eof', | ||
'.ttf': 'truetype', | ||
'.svg': 'svg' | ||
}; | ||
// Returns the font format for a specific font source | ||
function getFontFormat(src) { | ||
return Object.keys(formats).reduce(function (format, extension) { | ||
if (src.indexOf(extension) > -1) { | ||
format = formats[extension]; | ||
} | ||
return format; // eslint-disable-line | ||
}, undefined); | ||
} | ||
var FontFace = function () { | ||
function FontFace(family, files) { | ||
var properties = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; | ||
babelHelpers.classCallCheck(this, FontFace); | ||
this.family = family; | ||
this.files = files; | ||
this.properties = properties; | ||
} | ||
babelHelpers.createClass(FontFace, [{ | ||
key: 'render', | ||
value: function render() { | ||
var _this = this; | ||
var font = { | ||
fontFamily: '\'' + this.family + '\'', | ||
src: this.files.map(function (src) { | ||
return 'url(\'' + src + '\') format(\'' + getFontFormat(src) + '\')'; | ||
}).join(',') | ||
}; | ||
var fontProperties = ['fontWeight', 'fontStretch', 'fontStyle', 'unicodeRange']; | ||
Object.keys(this.properties).filter(function (prop) { | ||
return fontProperties.indexOf(prop) > -1; | ||
}).forEach(function (fontProp) { | ||
return font[fontProp] = _this.properties[fontProp]; | ||
}); | ||
return font; | ||
} | ||
}]); | ||
return FontFace; | ||
}(); | ||
var Keyframe = function () { | ||
/** | ||
* Keyframe is a dynamic keyframe animation container | ||
* | ||
* @param {Function} composer - values to resolve dynamic styles | ||
* @param {Function} keyframeComposer - composer function | ||
*/ | ||
function Selector(composer) { | ||
babelHelpers.classCallCheck(this, Selector); | ||
function Keyframe(keyframeComposer) { | ||
babelHelpers.classCallCheck(this, Keyframe); | ||
this.composer = composer; | ||
this.keyframeComposer = keyframeComposer; | ||
} | ||
@@ -77,3 +817,3 @@ | ||
babelHelpers.createClass(Selector, [{ | ||
babelHelpers.createClass(Keyframe, [{ | ||
key: "render", | ||
@@ -83,32 +823,81 @@ value: function render() { | ||
return this.composer(props); | ||
return this.keyframeComposer(props); | ||
} | ||
}]); | ||
return Selector; | ||
return Keyframe; | ||
}(); | ||
/** | ||
* Enhances a Renderer to automatically render with a set of plugins | ||
* @param {FelaRenderer} renderer - renderer that gets enhanced | ||
* @param {function[]} plugins - array of plugin functions | ||
* @return enhanced renderer | ||
*/ | ||
function enhanceWithPlugins(renderer, plugins) { | ||
// cache the initial render function to later refer to | ||
// it would else get overwritten directly | ||
var existingRenderFunction = renderer.render.bind(renderer); | ||
renderer.render = function (selector, props) { | ||
var additionalPlugins = arguments.length <= 2 || arguments[2] === undefined ? [] : arguments[2]; | ||
var NODE_TYPE = 1; | ||
var NODE_NAME = 'STYLE'; | ||
// concat enhancing plugins with additional plugins to allow multiple | ||
// enhancing cycles without loosing the ability to render with additional plugins | ||
return existingRenderFunction(selector, props, plugins.concat(additionalPlugins)); | ||
}; | ||
var Renderer = function () { | ||
function Renderer(node, config) { | ||
var _this = this; | ||
return renderer; | ||
} | ||
babelHelpers.classCallCheck(this, Renderer); | ||
// Check if the passed node is a valid element node which allows | ||
// setting the `textContent` property to update the node's content | ||
if (node.nodeType !== NODE_TYPE || node.textContent === undefined || node.setAttribute instanceof Function === false) { | ||
throw new Error('You need to specify a valid element node (nodeType = 1) to render into.'); | ||
} | ||
warning$1(node.nodeName === NODE_NAME, 'You are using a node other than `<style>`. Your styles might not get applied correctly.'); | ||
warning$1(!node.hasAttribute('data-fela-stylesheet'), 'This node is already used by another renderer. Rendering might overwrite other styles.'); | ||
node.setAttribute('data-fela-stylesheet', ''); | ||
this.node = node; | ||
this.stylesheet = new StyleSheet(config); | ||
this.stylesheet.subscribe(function (css) { | ||
return _this.node.textContent = css; | ||
}); | ||
} | ||
/** | ||
* renders a Selector variation of props into a DOM node | ||
* | ||
* @param {Selector} selector - Selector instance that is rendered | ||
* @param {Object?} props - list of props to render | ||
* @param {Function[]?} plugins - array of plugins to process styles | ||
* @return {string} className reference of the rendered selector | ||
*/ | ||
babelHelpers.createClass(Renderer, [{ | ||
key: 'render', | ||
value: function render(selector, props, plugins) { | ||
if (selector instanceof FontFace) { | ||
return this.stylesheet._renderFontFace(selector); | ||
} | ||
if (selector instanceof Keyframe) { | ||
return this.stylesheet._renderKeyframeVariation(selector, props, plugins); | ||
} | ||
// renders the passed selector variation into the stylesheet which | ||
// adds the variation to the cache and updates the DOM automatically | ||
// if the variation has already been added it will do nothing but return | ||
// the cached className to reference the mounted CSS selector | ||
return this.stylesheet._renderSelectorVariation(selector, props, plugins); | ||
} | ||
/** | ||
* clears the stylesheet associated with a DOM node | ||
*/ | ||
}, { | ||
key: 'clear', | ||
value: function clear() { | ||
this.stylesheet.clear(); | ||
this.node.textContent = ''; | ||
} | ||
}]); | ||
return Renderer; | ||
}(); | ||
var fela = { | ||
Selector: Selector, | ||
enhanceWithPlugins: enhanceWithPlugins | ||
Renderer: Renderer, | ||
Keyframe: Keyframe, | ||
FontFace: FontFace | ||
}; | ||
@@ -115,0 +904,0 @@ |
@@ -1,1 +0,1 @@ | ||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):e.Fela=n()}(this,function(){"use strict";function e(e,n){var t=e.render.bind(e);return e.render=function(e,r){var o=arguments.length<=2||void 0===arguments[2]?[]:arguments[2];return t(e,r,n.concat(o))},e}var n={};n["typeof"]="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},n.classCallCheck=function(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")},n.createClass=function(){function e(e,n){for(var t=0;t<n.length;t++){var r=n[t];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(n,t,r){return t&&e(n.prototype,t),r&&e(n,r),n}}(),n["extends"]=Object.assign||function(e){for(var n=1;n<arguments.length;n++){var t=arguments[n];for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])}return e};var t=function(){function e(t){n.classCallCheck(this,e),this.composer=t}return n.createClass(e,[{key:"render",value:function(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];return this.composer(e)}}]),e}(),r={Selector:t,enhanceWithPlugins:e};return r}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Fela=t()}(this,function(){"use strict";function e(e,t){return t={exports:{}},e(t,t.exports),t.exports}function t(e){return e.replace(/([a-z]|^)([A-Z])/g,function(e,t,n){return t+"-"+n.toLowerCase()}).replace("ms-","-ms-")}function n(e){return Object.keys(e).reduce(function(n,r){return n.length>0&&(n+=";"),n+=t(r)+":"+e[r]},"")}function r(e){var t=0,n=0,r=void 0,i=e.length;if(0===i)return"";for(;i>n;++n)r=e.charCodeAt(n),t=(t<<5)-t+r,t|=0;return"-"+t.toString(36)}function i(e){return void 0===e?"":Object.keys(e).sort().reduce(function(t,n){return t+=n+e[n]},"")}function s(e){return Object.keys(h).reduce(function(t,n){return e.indexOf(n)>-1&&(t=h[n]),t},void 0)}var a={};a["typeof"]="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol?"symbol":typeof e},a.classCallCheck=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},a.createClass=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),a.defineProperty=function(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e},a["extends"]=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e};var o=e(function(e){function t(e){return function(){return e}}var n=function(){};n.thatReturns=t,n.thatReturnsFalse=t(!1),n.thatReturnsTrue=t(!0),n.thatReturnsNull=t(null),n.thatReturnsThis=function(){return this},n.thatReturnsArgument=function(e){return e},e.exports=n}),c=o&&"object"==typeof o&&"default"in o?o["default"]:o,u=(e(function(e){var t=c,n=t;e.exports=n}),function(e){return":"===e.charAt(0)}),f=function(e){return"@media"===e.substr(0,6)},l=function(){function e(){var t=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];a.classCallCheck(this,e),this.listeners=new Set,this.keyframePrefixes=t.keyframePrefixes||["-webkit-","-moz-",""],this.plugins=t.plugins||[],this._init()}return a.createClass(e,[{key:"clear",value:function(){this._init()}},{key:"renderToString",value:function(){var e=this,t="";return this.fontFaces.forEach(function(e){return t+=e}),this.keyframes.forEach(function(e){e.forEach(function(e){return t+=e})}),t+=this._renderCache(this.cache),this.mediaCache.forEach(function(n,r){t+="@media "+r+"{"+e._renderCache(n)+"}"}),t}},{key:"subscribe",value:function(e){var t=this;return this.listeners.add(e),{unsubscribe:function(){return t.listeners["delete"](e)}}}},{key:"_emitChange",value:function(){var e=this.renderToString();this.listeners.forEach(function(t){return t(e)})}},{key:"_init",value:function(){this.cache=new Map,this.mediaCache=new Map,this.fontFaces=new Set,this.keyframes=new Map,this.ids=new Map,this._counter=-1}},{key:"_renderFontFace",value:function(e){if(!this.fontFaces.has(e)){var t="@font-face {"+n(e.render())+"}";this.fontFaces.add(t),this._emitChange()}return e.fontFamily}},{key:"_renderKeyframeVariation",value:function(e){var t=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],n=arguments.length<=2||void 0===arguments[2]?[]:arguments[2];this.keyframes.has(e)||(this.keyframes.set(e,new Map),this.ids.set(e,++this._counter));var r=this.keyframes.get(e),i=this._generatePropsReference(t),s="k"+this.ids.get(e)+i;if(!r.has(i)){var a={plugins:this.plugins.concat(n),processStyles:this._processStyles,styles:e.render(t),props:t},o=this._processStyles(a);r.set(i,this._renderKeyframe(o,s)),this._emitChange()}return s}},{key:"_renderSelectorVariation",value:function(e){var t=this,n=arguments.length<=1||void 0===arguments[1]?{}:arguments[1],r=arguments.length<=2||void 0===arguments[2]?[]:arguments[2];this.cache.has(e)||(this.ids.set(e,++this._counter),this.cache.set(e,new Map),this._renderSelectorVariation(e,{},r));var i=this.cache.get(e),s=this._generatePropsReference(n),a="c"+this.ids.get(e)+s;i.has(s)||!function(){var o={plugins:t.plugins.concat(r),processStyles:t._processStyles,styles:e(n),props:n},c=t._prepareStyles(o,i.get("static"),s),u=t._clusterStyles(c);0===Object.keys(u).length&&i.set(s,""),Object.keys(u).forEach(function(n){var r=t._renderClusteredStyles(u[n],a);""===n?i.set(s,r):(t.mediaCache.has(n)||t.mediaCache.set(n,new Map([[e,new Map]])),t.mediaCache.get(n).has(e)||t.mediaCache.get(n).set(e,new Map),t.mediaCache.get(n).get(e).set(s,r))}),""===s&&i.set("static",c),t._emitChange()}();var o="c"+this.ids.get(e);return""===i.get(s)?o:a!==o?o+" "+a:a}},{key:"_processStyles",value:function(e){var t=e.plugins,n=e.styles;return t.forEach(function(t){return n=t(e)}),n}},{key:"_extractDynamicStyles",value:function(e,t){var n=this;return Object.keys(e).forEach(function(r){var i=e[r];i instanceof Object&&!Array.isArray(i)?(e[r]=n._extractDynamicStyles(e[r],t[r]),0===Object.keys(e[r]).length&&delete e[r]):t.hasOwnProperty(r)&&t[r]===i&&delete e[r]}),e}},{key:"_validateStyles",value:function(e){var t=this;return Object.keys(e).forEach(function(n){var r=e[n];r instanceof Object&&!Array.isArray(r)?(e[n]=u(n)||f(n)?t._validateStyles(r):{},0===Object.keys(e[n]).length&&delete e[n]):"string"!=typeof r&&"number"!=typeof r&&delete e[n]}),e}},{key:"_clusterStyles",value:function(e){var t=arguments.length<=1||void 0===arguments[1]?"":arguments[1],n=this,r=arguments.length<=2||void 0===arguments[2]?"":arguments[2],i=arguments.length<=3||void 0===arguments[3]?{}:arguments[3];return Object.keys(e).forEach(function(s){var o=e[s];if(o instanceof Object&&!Array.isArray(o)){if(u(s))n._clusterStyles(o,t+s,r,i);else if(f(s)){var c=s.slice(6).trim(),l=""!==r?r+" and "+c:c;n._clusterStyles(o,t,l,i)}}else i[r]||(i[r]=a.defineProperty({},t,{})),i[r][t]||(i[r][t]={}),i[r][t][s]=o}),i}},{key:"_prepareStyles",value:function(e,t,n){var r=this._processStyles(e),i=this._validateStyles(r);return""!==n?this._extractDynamicStyles(i,t):i}},{key:"_generatePropsReference",value:function(e){return r(i(e))}},{key:"_renderClusteredStyles",value:function(e,t){return Object.keys(e).reduce(function(r,i){return r+"."+t+i+"{"+n(e[i])+"}"},"")}},{key:"_renderKeyframe",value:function(e,t){var r=this,i=Object.keys(e).reduce(function(t,i){return t+i+"{"+n(r._validateStyles(e[i]))+"}"},"");return this.keyframePrefixes.reduce(function(e,n){return e+"@"+n+"keyframes "+t+"{"+i+"}"},"")}},{key:"_renderCache",value:function(e){var t="";return e.forEach(function(e){e.forEach(function(e,n){"static"!==n&&(t+=e)})}),t}}]),e}(),h={".woff":"woff",".eof":"eof",".ttf":"truetype",".svg":"svg"},y=function(){function e(t,n){var r=arguments.length<=2||void 0===arguments[2]?{}:arguments[2];a.classCallCheck(this,e),this.family=t,this.files=n,this.properties=r}return a.createClass(e,[{key:"render",value:function(){var e=this,t={fontFamily:"'"+this.family+"'",src:this.files.map(function(e){return"url('"+e+"') format('"+s(e)+"')"}).join(",")},n=["fontWeight","fontStretch","fontStyle","unicodeRange"];return Object.keys(this.properties).filter(function(e){return n.indexOf(e)>-1}).forEach(function(n){return t[n]=e.properties[n]}),t}}]),e}(),d=function(){function e(t){a.classCallCheck(this,e),this.keyframeComposer=t}return a.createClass(e,[{key:"render",value:function(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];return this.keyframeComposer(e)}}]),e}(),v=1,p=function(){function e(t,n){var r=this;if(a.classCallCheck(this,e),t.nodeType!==v||void 0===t.textContent||t.setAttribute instanceof Function==!1)throw new Error("You need to specify a valid element node (nodeType = 1) to render into.");t.setAttribute("data-fela-stylesheet",""),this.node=t,this.stylesheet=new l(n),this.stylesheet.subscribe(function(e){return r.node.textContent=e})}return a.createClass(e,[{key:"render",value:function(e,t,n){return e instanceof y?this.stylesheet._renderFontFace(e):e instanceof d?this.stylesheet._renderKeyframeVariation(e,t,n):this.stylesheet._renderSelectorVariation(e,t,n)}},{key:"clear",value:function(){this.stylesheet.clear(),this.node.textContent=""}}]),e}(),m={Renderer:p,Keyframe:d,FontFace:y};return m}); |
@@ -7,16 +7,21 @@ 'use strict'; | ||
var _Selector = require('./components/shared/Selector'); | ||
var _DOMRenderer = require('./renderers/dom/DOMRenderer'); | ||
var _Selector2 = _interopRequireDefault(_Selector); | ||
var _DOMRenderer2 = _interopRequireDefault(_DOMRenderer); | ||
var _enhanceWithPlugins = require('./helpers/enhanceWithPlugins'); | ||
var _Keyframe = require('./components/dom/Keyframe'); | ||
var _enhanceWithPlugins2 = _interopRequireDefault(_enhanceWithPlugins); | ||
var _Keyframe2 = _interopRequireDefault(_Keyframe); | ||
var _FontFace = require('./components/dom/FontFace'); | ||
var _FontFace2 = _interopRequireDefault(_FontFace); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
exports.default = { | ||
Selector: _Selector2.default, | ||
enhanceWithPlugins: _enhanceWithPlugins2.default | ||
Renderer: _DOMRenderer2.default, | ||
Keyframe: _Keyframe2.default, | ||
FontFace: _FontFace2.default | ||
}; | ||
module.exports = exports['default']; |
@@ -13,2 +13,6 @@ 'use strict'; | ||
var _warning = require('fbjs/lib/warning'); | ||
var _warning2 = _interopRequireDefault(_warning); | ||
var _unitlessCssProperty = require('unitless-css-property'); | ||
@@ -33,2 +37,4 @@ | ||
process.env.NODE_ENV !== "production" ? (0, _warning2.default)(unit.match(/ch|em|ex|rem|vh|vw|vmin|vmax|px|cm|mm|in|pc|pt|mozmm|%/) === null, 'You are using an invalid unit `' + unit + '`. Consider using one of the following ch, em, ex, rem, vh, vw, vmin, vmax, px, cm, mm, in, pc, pt, mozmm or %.') : void 0; | ||
return function (pluginInterface) { | ||
@@ -35,0 +41,0 @@ var styles = pluginInterface.styles; |
@@ -9,2 +9,6 @@ 'use strict'; | ||
var _warning = require('fbjs/lib/warning'); | ||
var _warning2 = _interopRequireDefault(_warning); | ||
var _StyleSheet = require('./StyleSheet'); | ||
@@ -14,2 +18,10 @@ | ||
var _FontFace = require('../../components/dom/FontFace'); | ||
var _FontFace2 = _interopRequireDefault(_FontFace); | ||
var _Keyframe = require('../../components/dom/Keyframe'); | ||
var _Keyframe2 = _interopRequireDefault(_Keyframe); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -23,3 +35,3 @@ | ||
var Renderer = function () { | ||
function Renderer(node) { | ||
function Renderer(node, config) { | ||
var _this = this; | ||
@@ -31,23 +43,13 @@ | ||
// setting the `textContent` property to update the node's content | ||
if (node.nodeType !== NODE_TYPE || node.textContent === undefined) { | ||
console.error('You need to specify a valid element node (nodeType = 1) to render into.'); // eslint-disable-line | ||
return false; | ||
if (node.nodeType !== NODE_TYPE || node.textContent === undefined || node.setAttribute instanceof Function === false) { | ||
throw new Error('You need to specify a valid element node (nodeType = 1) to render into.'); | ||
} | ||
// TODO: DEV-MODE | ||
// In dev-mode we should allow using elements other than <style> as | ||
// one might want to render the CSS markup into a visible node to be able to | ||
// validate and observe the styles on runtime | ||
if (node.nodeName !== NODE_NAME) { | ||
console.warn('You are using a node other than `<style>`. Your styles might not get applied correctly.'); // eslint-disable-line | ||
} | ||
process.env.NODE_ENV !== "production" ? (0, _warning2.default)(node.nodeName === NODE_NAME, 'You are using a node other than `<style>`. Your styles might not get applied correctly.') : void 0; | ||
process.env.NODE_ENV !== "production" ? (0, _warning2.default)(!node.hasAttribute('data-fela-stylesheet'), 'This node is already used by another renderer. Rendering might overwrite other styles.') : void 0; | ||
if (node.hasAttribute('data-fela-stylesheet')) { | ||
console.warn('This node is already used by another renderer. Rendering might overwrite other styles.'); // eslint-disable-line | ||
} | ||
node.setAttribute('data-fela-stylesheet', ''); | ||
this.node = node; | ||
this.stylesheet = new _StyleSheet2.default(); | ||
this.stylesheet = new _StyleSheet2.default(config); | ||
this.stylesheet.subscribe(function (css) { | ||
@@ -57,2 +59,3 @@ return _this.node.textContent = css; | ||
} | ||
/** | ||
@@ -71,2 +74,10 @@ * renders a Selector variation of props into a DOM node | ||
value: function render(selector, props, plugins) { | ||
if (selector instanceof _FontFace2.default) { | ||
return this.stylesheet._renderFontFace(selector); | ||
} | ||
if (selector instanceof _Keyframe2.default) { | ||
return this.stylesheet._renderKeyframeVariation(selector, props, plugins); | ||
} | ||
// renders the passed selector variation into the stylesheet which | ||
@@ -73,0 +84,0 @@ // adds the variation to the cache and updates the DOM automatically |
@@ -13,2 +13,10 @@ 'use strict'; | ||
var _FontFace = require('../../components/dom/FontFace'); | ||
var _FontFace2 = _interopRequireDefault(_FontFace); | ||
var _Keyframe = require('../../components/dom/Keyframe'); | ||
var _Keyframe2 = _interopRequireDefault(_Keyframe); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -19,7 +27,8 @@ | ||
var Renderer = function () { | ||
function Renderer() { | ||
function Renderer(config) { | ||
_classCallCheck(this, Renderer); | ||
this.stylesheet = new _StyleSheet2.default(); | ||
this.stylesheet = new _StyleSheet2.default(config); | ||
} | ||
/** | ||
@@ -38,2 +47,10 @@ * renders a Selector variation of props into a DOM node | ||
value: function render(selector, props, plugins) { | ||
if (selector instanceof _FontFace2.default) { | ||
return this.stylesheet._renderFontFace(selector); | ||
} | ||
if (selector instanceof _Keyframe2.default) { | ||
return this.stylesheet._renderKeyframeVariation(selector, props, plugins); | ||
} | ||
// renders the passed selector variation into the stylesheet which | ||
@@ -50,2 +67,3 @@ // adds the variation to the cache and updates the DOM automatically | ||
} | ||
/** | ||
@@ -52,0 +70,0 @@ * clears the stylesheet |
@@ -9,16 +9,26 @@ 'use strict'; | ||
var _cssifyObject = require('./utils/cssifyObject'); | ||
var _cssifyObject = require('../../utils/cssifyObject'); | ||
var _cssifyObject2 = _interopRequireDefault(_cssifyObject); | ||
var _generateContentHash = require('./utils/generateContentHash'); | ||
var _generateContentHash = require('../../utils/generateContentHash'); | ||
var _generateContentHash2 = _interopRequireDefault(_generateContentHash); | ||
var _sortedStringify = require('./utils/sortedStringify'); | ||
var _sortedStringify = require('../../utils/sortedStringify'); | ||
var _sortedStringify2 = _interopRequireDefault(_sortedStringify); | ||
var _isPseudo = require('../../utils/isPseudo'); | ||
var _isPseudo2 = _interopRequireDefault(_isPseudo); | ||
var _isMediaQuery = require('../../utils/isMediaQuery'); | ||
var _isMediaQuery2 = _interopRequireDefault(_isMediaQuery); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -28,9 +38,14 @@ | ||
/** | ||
* StyleSheet is a low-level Selector container | ||
* StyleSheet is a low-level container to cache Selectors | ||
* Keyframes and FontFaces which optimizes styles | ||
*/ | ||
function StyleSheet() { | ||
var config = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
_classCallCheck(this, StyleSheet); | ||
this.listeners = new Set(); | ||
this.keyframePrefixes = config.keyframePrefixes || ['-webkit-', '-moz-', '']; | ||
this.plugins = config.plugins || []; | ||
this._init(); | ||
@@ -60,6 +75,16 @@ } | ||
var css = this._renderCache(this.cache); | ||
var css = ''; | ||
this.fontFaces.forEach(function (fontFace) { | ||
return css += fontFace; | ||
}); | ||
this.keyframes.forEach(function (variation) { | ||
variation.forEach(function (markup) { | ||
return css += markup; | ||
}); | ||
}); | ||
css += this._renderCache(this.cache); | ||
this.mediaCache.forEach(function (cache, media) { | ||
css += _this._renderMediaQuery(media, _this._renderCache(cache)); | ||
css += '@media ' + media + '{' + _this._renderCache(cache) + '}'; | ||
}); | ||
@@ -83,3 +108,2 @@ | ||
this.listeners.add(callback); | ||
return { | ||
@@ -118,2 +142,4 @@ unsubscribe: function unsubscribe() { | ||
this.mediaCache = new Map(); | ||
this.fontFaces = new Set(); | ||
this.keyframes = new Map(); | ||
this.ids = new Map(); | ||
@@ -124,2 +150,65 @@ this._counter = -1; | ||
/** | ||
* renders a new font-face and caches it | ||
* | ||
* @param {FontFace} fontFace - fontFace which gets rendered | ||
* @return {string} fontFamily reference | ||
*/ | ||
}, { | ||
key: '_renderFontFace', | ||
value: function _renderFontFace(fontFace) { | ||
if (!this.fontFaces.has(fontFace)) { | ||
var renderedFontFace = '@font-face {' + (0, _cssifyObject2.default)(fontFace.render()) + '}'; | ||
this.fontFaces.add(renderedFontFace); | ||
this._emitChange(); | ||
} | ||
return fontFace.fontFamily; | ||
} | ||
/** | ||
* renders a new keyframe variation and caches the result | ||
* | ||
* @param {Keyframe} keyframe - Keyframe which gets rendered | ||
* @param {Object?} props - properties used to render | ||
* @param {Function[]?} plugins - array of plugins to process styles | ||
* @return {string} animationName to reference the rendered keyframe | ||
*/ | ||
}, { | ||
key: '_renderKeyframeVariation', | ||
value: function _renderKeyframeVariation(keyframe) { | ||
var props = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
var plugins = arguments.length <= 2 || arguments[2] === undefined ? [] : arguments[2]; | ||
// rendering a Keyframe for the first time | ||
// will create cache entries and an ID reference | ||
if (!this.keyframes.has(keyframe)) { | ||
this.keyframes.set(keyframe, new Map()); | ||
this.ids.set(keyframe, ++this._counter); | ||
} | ||
var cachedKeyframe = this.keyframes.get(keyframe); | ||
var propsReference = this._generatePropsReference(props); | ||
var animationName = 'k' + this.ids.get(keyframe) + propsReference; | ||
// only if the cached selector has not already been rendered | ||
// with a specific set of properties it actually renders | ||
if (!cachedKeyframe.has(propsReference)) { | ||
var pluginInterface = { | ||
plugins: this.plugins.concat(plugins), | ||
processStyles: this._processStyles, | ||
styles: keyframe.render(props), | ||
props: props | ||
}; | ||
var processedKeyframe = this._processStyles(pluginInterface); | ||
cachedKeyframe.set(propsReference, this._renderKeyframe(processedKeyframe, animationName)); | ||
this._emitChange(); | ||
} | ||
return animationName; | ||
} | ||
/** | ||
* renders a new selector variation and caches the result | ||
@@ -141,5 +230,2 @@ * | ||
var isFunctionalSelector = selector instanceof Function; | ||
var isMediaSelector = !isFunctionalSelector && selector.renderMedia instanceof Function; | ||
// rendering a Selector for the first time | ||
@@ -151,12 +237,5 @@ // will create cache entries and an ID reference | ||
// iterate all used media strings to create | ||
// selector caches for each media as well | ||
if (isMediaSelector) { | ||
selector.mediaStrings.forEach(function (media) { | ||
if (!_this3.mediaCache.has(media)) { | ||
_this3.mediaCache.set(media, new Map()); | ||
} | ||
_this3.mediaCache.get(media).set(selector, new Map()); | ||
}); | ||
} | ||
// directly render the static base styles to be able | ||
// to diff future dynamic styles with those | ||
this._renderSelectorVariation(selector, {}, plugins); | ||
} | ||
@@ -166,5 +245,4 @@ | ||
var propsReference = this._generatePropsReference(props); | ||
// uses the reference ID and the props to generate an unique className | ||
var className = this._renderClassName(this.ids.get(selector), propsReference); | ||
var className = 'c' + this.ids.get(selector) + propsReference; | ||
@@ -178,19 +256,34 @@ // only if the cached selector has not already been rendered | ||
var pluginInterface = { | ||
plugins: plugins, | ||
plugins: _this3.plugins.concat(plugins), | ||
processStyles: _this3._processStyles, | ||
styles: isFunctionalSelector ? selector(props) : selector.render(props), | ||
className: className, | ||
styles: selector(props), | ||
props: props | ||
}; | ||
cachedSelector.set(propsReference, _this3._processStyles(pluginInterface)); | ||
var preparedStyles = _this3._prepareStyles(pluginInterface, cachedSelector.get('static'), propsReference); | ||
var clusteredStyles = _this3._clusterStyles(preparedStyles); | ||
if (isMediaSelector) { | ||
selector.mediaStrings.forEach(function (media) { | ||
pluginInterface.styles = selector.renderMedia(props, media); | ||
pluginInterface.media = media; | ||
if (Object.keys(clusteredStyles).length === 0) { | ||
cachedSelector.set(propsReference, ''); | ||
} | ||
var processedStyles = _this3._processStyles(pluginInterface); | ||
_this3.mediaCache.get(media).get(selector).set(propsReference, processedStyles); | ||
}); | ||
Object.keys(clusteredStyles).forEach(function (media) { | ||
var renderedStyles = _this3._renderClusteredStyles(clusteredStyles[media], className); | ||
if (media === '') { | ||
cachedSelector.set(propsReference, renderedStyles); | ||
} else { | ||
if (!_this3.mediaCache.has(media)) { | ||
_this3.mediaCache.set(media, new Map([[selector, new Map()]])); | ||
} | ||
if (!_this3.mediaCache.get(media).has(selector)) { | ||
_this3.mediaCache.get(media).set(selector, new Map()); | ||
} | ||
_this3.mediaCache.get(media).get(selector).set(propsReference, renderedStyles); | ||
} | ||
}); | ||
// keep static styles to diff dynamic onces later on | ||
if (propsReference === '') { | ||
cachedSelector.set('static', preparedStyles); | ||
} | ||
@@ -203,3 +296,9 @@ | ||
return className; | ||
var baseClassName = 'c' + this.ids.get(selector); | ||
if (cachedSelector.get(propsReference) === '') { | ||
return baseClassName; | ||
} | ||
// returns either the base className or both the base and the dynamic part | ||
return className !== baseClassName ? baseClassName + ' ' + className : className; | ||
} | ||
@@ -211,3 +310,3 @@ | ||
* @param {Object} pluginInterface - interface containing relevant processing data | ||
* @return {Object} processed and validated styles | ||
* @return {Object} processed styles | ||
*/ | ||
@@ -220,3 +319,2 @@ | ||
var styles = pluginInterface.styles; | ||
// pipes each plugin by passes the plugin interface | ||
@@ -227,5 +325,4 @@ // NOTE: as the styles are passed directly they're editable | ||
plugins.forEach(function (plugin) { | ||
return plugin(pluginInterface); | ||
return styles = plugin(pluginInterface); | ||
}); | ||
return styles; | ||
@@ -235,45 +332,94 @@ } | ||
/** | ||
* generates an unique reference id by content hashing props | ||
* extracts all dynamic styles by diffing with the static base styles | ||
* | ||
* @param {Object} props - props that get hashed | ||
* @return {string} reference - unique props reference | ||
* @param {Object} styles - dynamic styles | ||
* @param {Object} base - static base styles to diff | ||
* @return {Object} encapsulated dynamic styles | ||
*/ | ||
}, { | ||
key: '_generatePropsReference', | ||
value: function _generatePropsReference(props) { | ||
return (0, _generateContentHash2.default)((0, _sortedStringify2.default)(props)); | ||
key: '_extractDynamicStyles', | ||
value: function _extractDynamicStyles(styles, base) { | ||
var _this4 = this; | ||
Object.keys(styles).forEach(function (property) { | ||
var value = styles[property]; | ||
if (value instanceof Object && !Array.isArray(value)) { | ||
// also diff inner objects such as pseudo classes | ||
styles[property] = _this4._extractDynamicStyles(styles[property], base[property]); | ||
if (Object.keys(styles[property]).length === 0) { | ||
delete styles[property]; | ||
} | ||
// checks if the base styles has the property and if the value is equal | ||
} else if (base.hasOwnProperty(property) && base[property] === value) { | ||
delete styles[property]; | ||
} | ||
}); | ||
return styles; | ||
} | ||
/** | ||
* generates an unique className using a Selectors reference ID | ||
* as well as a content hash of the passed props | ||
* removes every invalid property except pseudo class objects | ||
* | ||
* @param {number} id - Selectors reference ID stored within the stylesheet | ||
* @param {strng} reference - generated props reference | ||
* @return {string} className - unique className reference | ||
* @param {Object} styles - styles to be validated | ||
* @return {Object} validated styles | ||
*/ | ||
}, { | ||
key: '_renderClassName', | ||
value: function _renderClassName(id, reference) { | ||
return 'c' + id + '-' + reference; | ||
key: '_validateStyles', | ||
value: function _validateStyles(styles) { | ||
var _this5 = this; | ||
Object.keys(styles).forEach(function (property) { | ||
var value = styles[property]; | ||
if (value instanceof Object && !Array.isArray(value)) { | ||
styles[property] = (0, _isPseudo2.default)(property) || (0, _isMediaQuery2.default)(property) ? _this5._validateStyles(value) : {}; | ||
if (Object.keys(styles[property]).length === 0) { | ||
delete styles[property]; | ||
} | ||
} else if (typeof value !== 'string' && typeof value !== 'number') { | ||
delete styles[property]; | ||
} | ||
}); | ||
return styles; | ||
} | ||
/** | ||
* flattens nested pseudo classes | ||
* removes all invalid properties that are not either a string or a number | ||
* | ||
* @param {Object} styles - dynamic styles | ||
* @return {Object} flat and validated styles | ||
*/ | ||
}, { | ||
key: '_splitPseudoClasses', | ||
value: function _splitPseudoClasses(styles) { | ||
var _this4 = this; | ||
key: '_clusterStyles', | ||
value: function _clusterStyles(styles) { | ||
var pseudo = arguments.length <= 1 || arguments[1] === undefined ? '' : arguments[1]; | ||
var out = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; | ||
var _this6 = this; | ||
var media = arguments.length <= 2 || arguments[2] === undefined ? '' : arguments[2]; | ||
var out = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3]; | ||
Object.keys(styles).forEach(function (property) { | ||
var value = styles[property]; | ||
if (value instanceof Object) { | ||
_this4._splitPseudoClasses(value, pseudo + property, out); | ||
if (value instanceof Object && !Array.isArray(value)) { | ||
if ((0, _isPseudo2.default)(property)) { | ||
_this6._clusterStyles(value, pseudo + property, media, out); | ||
} else if ((0, _isMediaQuery2.default)(property)) { | ||
var query = property.slice(6).trim(); | ||
var nestedMedia = media !== '' ? media + ' and ' + query : query; | ||
_this6._clusterStyles(value, pseudo, nestedMedia, out); | ||
} | ||
} else { | ||
if (!out[pseudo]) { | ||
out[pseudo] = {}; | ||
if (!out[media]) { | ||
out[media] = _defineProperty({}, pseudo, {}); | ||
} | ||
out[pseudo][property] = value; | ||
if (!out[media][pseudo]) { | ||
out[media][pseudo] = {}; | ||
} | ||
out[media][pseudo][property] = value; | ||
} | ||
@@ -286,34 +432,42 @@ }); | ||
/** | ||
* renders a single ruleset into a CSS string | ||
* processes, flattens, normalizes and diffs styles | ||
* | ||
* @param {string} className - rendered selector | ||
* @param {Object} styles - style declarations | ||
* @return {string} valid selector CSS string | ||
* @param {Object} pluginInterface - plugin interface to process styles | ||
* @param {Object} baseStyles - static base styles | ||
* @return {Object} processed styles | ||
*/ | ||
}, { | ||
key: '_renderSelector', | ||
value: function _renderSelector(className, styles) { | ||
return '.' + className + '{' + (0, _cssifyObject2.default)(styles) + '}'; | ||
key: '_prepareStyles', | ||
value: function _prepareStyles(pluginInterface, baseStyles, propsReference) { | ||
var processedStyles = this._processStyles(pluginInterface); | ||
var validatedStyles = this._validateStyles(processedStyles); | ||
// only diff and extract dynamic styles | ||
// if not actually rendering the base styles | ||
if (propsReference !== '') { | ||
return this._extractDynamicStyles(validatedStyles, baseStyles); | ||
} | ||
return validatedStyles; | ||
} | ||
/** | ||
* renders a set of media styles into a CSS string | ||
* generates an unique reference id by content hashing props | ||
* | ||
* @param {string} media - media string | ||
* @param {string} selectors - CSS string of all selectors | ||
* @return {string} valid media query CSS string | ||
* @param {Object} props - props that get hashed | ||
* @return {string} reference - unique props reference | ||
*/ | ||
}, { | ||
key: '_renderMediaQuery', | ||
value: function _renderMediaQuery(media, selectors) { | ||
return '@media(' + media + '){' + selectors + '}'; | ||
key: '_generatePropsReference', | ||
value: function _generatePropsReference(props) { | ||
return (0, _generateContentHash2.default)((0, _sortedStringify2.default)(props)); | ||
} | ||
/** | ||
* renders a whole cache into a CSS string | ||
* clusters media queries into single @media groups | ||
* renders clustered styles into a CSS string | ||
* | ||
* @param {Map} cache - cache including styles and media styles | ||
* @param {Object} styles - prepared styles with pseudo keys | ||
* @param {string} className - className reference to render | ||
* @return {string} valid CSS string | ||
@@ -323,18 +477,48 @@ */ | ||
}, { | ||
key: '_renderClusteredStyles', | ||
value: function _renderClusteredStyles(styles, className) { | ||
return Object.keys(styles).reduce(function (css, pseudo) { | ||
return css + '.' + className + pseudo + '{' + (0, _cssifyObject2.default)(styles[pseudo]) + '}'; | ||
}, ''); | ||
} | ||
/** | ||
* renders keyframes into a CSS string with all prefixes | ||
* | ||
* @param {Object} frames - validated frame declarations | ||
* @param {string} animationName - animation reference naming | ||
* @return {string} valid CSS string | ||
*/ | ||
}, { | ||
key: '_renderKeyframe', | ||
value: function _renderKeyframe(frames, animationName) { | ||
var _this7 = this; | ||
var keyframe = Object.keys(frames).reduce(function (css, percentage) { | ||
return css + percentage + '{' + (0, _cssifyObject2.default)(_this7._validateStyles(frames[percentage])) + '}'; | ||
}, ''); | ||
return this.keyframePrefixes.reduce(function (css, prefix) { | ||
return css + '@' + prefix + 'keyframes ' + animationName + '{' + keyframe + '}'; | ||
}, ''); | ||
} | ||
/** | ||
* renders a whole cache into a single CSS string | ||
* | ||
* @param {Map} cache - cache including all selector variations | ||
* @return {string} valid CSS string | ||
*/ | ||
}, { | ||
key: '_renderCache', | ||
value: function _renderCache(cache) { | ||
var _this5 = this; | ||
var css = ''; | ||
cache.forEach(function (variation, selector) { | ||
var id = _this5.ids.get(selector); | ||
variation.forEach(function (styles, propsReference) { | ||
var className = _this5._renderClassName(id, propsReference); | ||
var splitPseudos = _this5._splitPseudoClasses(styles); | ||
Object.keys(splitPseudos).forEach(function (pseudo) { | ||
css += _this5._renderSelector(className + pseudo, splitPseudos[pseudo]); | ||
}); | ||
cache.forEach(function (variation) { | ||
variation.forEach(function (markup, propsReference) { | ||
if (propsReference !== 'static') { | ||
css += markup; | ||
} | ||
}); | ||
@@ -341,0 +525,0 @@ }); |
{ | ||
"name": "fela", | ||
"version": "1.0.0-alpha.5", | ||
"version": "1.0.0-alpha.6", | ||
"description": "Fast, tiny & dynamic low-level API to handle Styling in JavaScript", | ||
"main": "lib/fela.js", | ||
"main": "index.js", | ||
"files": [ | ||
@@ -7,0 +7,0 @@ "LICENSE", |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
180709
30
2356
4
1