UNPKG

react-images-lightbox

Version:

A simple, responsive lightbox component for displaying an array of images with React.js

1,405 lines (1,219 loc) 195 kB
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 'use strict'; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _reactDom = require('react-dom'); var _componentsGallery = require('./components/Gallery'); var _componentsGallery2 = _interopRequireDefault(_componentsGallery); function makeUnsplashSrc(id) { return 'https://images.unsplash.com/photo-' + id + '?dpr=2&auto=format&w=1024&h=1024'; } function makeUnsplashSrcSet(id, size) { return 'https://images.unsplash.com/photo-' + id + '?dpr=2&auto=format&w=' + size + ' ' + size + 'w'; } function makeUnsplashThumbnail(id) { var orientation = arguments.length <= 1 || arguments[1] === undefined ? 'landscape' : arguments[1]; var dimensions = orientation === 'square' ? 'w=300&h=300' : 'w=240&h=159'; return 'https://images.unsplash.com/photo-' + id + '?dpr=2&auto=format&crop=faces&fit=crop&' + dimensions; } // Unsplash images from the "Spirit Animals" collection // https://unsplash.com/collections/158825/spirit-animals var DEFAULT_IMAGES = [{ id: '1470619549108-b85c56fe5be8', caption: 'Photo by Alan Emery', orientation: 'square', useForDemo: true }, // https://unsplash.com/photos/SYzUF6XcWBY (Flamingo) { id: '1471079502516-250c19af6928', caption: 'Photo by Jeremy Bishop', orientation: 'landscape', useForDemo: true }, // https://unsplash.com/photos/GIpGxe2_cT4 (Turtle) { id: '1454023492550-5696f8ff10e1', caption: 'Photo by Jessica Weiller', orientation: 'landscape', useForDemo: true }, // https://unsplash.com/photos/LmVSKeDy6EA (Tiger) { id: '1470854989922-5be2f7456d78', caption: 'Photo by Piotr Łaskawski', orientation: 'landscape', useForDemo: true }, // https://unsplash.com/photos/GXMr7BadXQo (Hedgehog) { id: '1470317596697-cbdeda56f999', caption: 'Photo by Michel Bosma', orientation: 'landscape', useForDemo: true }]; // https://unsplash.com/photos/XgF9e93Tkt0 (Ladybug) var THEMED_IMAGES = [{ id: '1471101173712-b9884175254e', caption: 'Photo by Pedro Lastra', orientation: 'square', useForDemo: true }, // https://unsplash.com/photos/5oRzZU5uwSM (Dragonfly) { id: '1471127432458-65206be149c9', caption: 'Photo by Ernesto Velázquez', orientation: 'landscape', useForDemo: true }, // https://unsplash.com/photos/Kpgt4pl03O0 (Deer) { id: '1470777639313-60af88918203', caption: 'Photo by Cris Saur', orientation: 'landscape', useForDemo: true }, // https://unsplash.com/photos/GNUcUx-iObg (Koala) { id: '1453550486481-aa4175b013ea', caption: 'Photo by Benjamin Pley', orientation: 'landscape', useForDemo: true }, // https://unsplash.com/photos/WiSeaZ4E6ZI (Elephant) { id: '1415904663467-dfdc16cae794', caption: 'Photo by Levi Saunders', orientation: 'landscape', useForDemo: true }]; // https://unsplash.com/photos/NUMlxTPsznM (Coyote) var THUMBNAIL_IMAGES = [{ id: '1454991727061-be514eae86f7', caption: 'Photo by Thomas Kelley', orientation: 'square', useForDemo: true }, // https://unsplash.com/photos/t20pc32VbrU (Hump Back Whale) { id: '1455717974081-0436a066bb96', caption: 'Photo by Teddy Kelley', orientation: 'landscape', useForDemo: true }, // https://unsplash.com/photos/cmKPOUgdmWc (Deer) { id: '1460899960812-f6ee1ecaf117', caption: 'Photo by Jay Ruzesky', orientation: 'landscape', useForDemo: true }, // https://unsplash.com/photos/h13Y8vyIXNU (Walrus) { id: '1456926631375-92c8ce872def', caption: 'Photo by Gwen Weustink', orientation: 'landscape', useForDemo: true }, // https://unsplash.com/photos/I3C1sSXj1i8 (Leopard) { id: '1452274381522-521513015433', caption: 'Photo by Adam Willoughby-Knox', orientation: 'landscape', useForDemo: true }, // https://unsplash.com/photos/_snqARKTgoc (Mother and Cubs) { id: '1471145653077-54c6f0aae511', caption: 'Photo by Boris Smokrovic', orientation: 'landscape' }, // https://unsplash.com/photos/n0feC_PWFdk (Dragonfly) { id: '1471005197911-88e9d4a7834d', caption: 'Photo by Gaetano Cessati', orientation: 'landscape' }, // https://unsplash.com/photos/YOX8ZMTo7hk (Baby Crocodile) { id: '1470583190240-bd6bbde8a569', caption: 'Photo by Alan Emery', orientation: 'landscape' }, // https://unsplash.com/photos/emTCWiq2txk (Beetle) { id: '1470688090067-6d429c0b2600', caption: 'Photo by Ján Jakub Naništa', orientation: 'landscape' }, // https://unsplash.com/photos/xqjO-lx39B4 (Scottish Highland Cow) { id: '1470742292565-de43c4b02b57', caption: 'Photo by Eric Knoll', orientation: 'landscape' }]; // https://unsplash.com/photos/DmOCkOnx-MQ (Cheetah) // https://unsplash.com/photos/NUMlxTPsznM coyote? var theme = { // container container: { background: 'rgba(255, 255, 255, 0.9)' }, // arrows arrow: { backgroundColor: 'rgba(255, 255, 255, 0.8)', fill: '#222', opacity: 0.6, transition: 'opacity 200ms', ':hover': { opacity: 1 } }, arrow__size__medium: { borderRadius: 40, height: 40, marginTop: -20, '@media (min-width: 768px)': { height: 70, padding: 15 } }, arrow__direction__left: { marginLeft: 10 }, arrow__direction__right: { marginRight: 10 }, close: { fill: '#D40000', opacity: 0.6, transition: 'all 200ms', ':hover': { opacity: 1 } }, // footer footer: { color: 'black' }, footerCount: { color: 'rgba(0, 0, 0, 0.6)' }, // thumbnails thumbnail: {}, thumbnail__active: { boxShadow: '0 0 0 2px #00D8FF' } }; (0, _reactDom.render)(_react2['default'].createElement( 'div', null, _react2['default'].createElement( 'div', { style: { marginBottom: 40 } }, _react2['default'].createElement( 'p', null, 'Photos courtesy of ', _react2['default'].createElement( 'a', { href: 'https://unsplash.com/', target: '_blank' }, 'Unsplash' ), '. Use your keyboard to navigate ', _react2['default'].createElement( 'kbd', null, 'left' ), ' ', _react2['default'].createElement( 'kbd', null, 'right' ), ' ', _react2['default'].createElement( 'kbd', null, 'esc' ), ' — Also, try resizing your browser window.' ) ), _react2['default'].createElement( 'h3', null, 'Default Options' ), _react2['default'].createElement(_componentsGallery2['default'], { images: DEFAULT_IMAGES.map(function (_ref) { var caption = _ref.caption; var id = _ref.id; var orientation = _ref.orientation; var useForDemo = _ref.useForDemo; return { src: makeUnsplashSrc(id), thumbnail: makeUnsplashThumbnail(id, orientation), srcset: [makeUnsplashSrcSet(id, 1024), makeUnsplashSrcSet(id, 800), makeUnsplashSrcSet(id, 500), makeUnsplashSrcSet(id, 320)], caption: caption, orientation: orientation, useForDemo: useForDemo }; }) }), _react2['default'].createElement( 'h3', null, 'With Thumbnails' ), _react2['default'].createElement(_componentsGallery2['default'], { images: THUMBNAIL_IMAGES.map(function (_ref2) { var caption = _ref2.caption; var id = _ref2.id; var orientation = _ref2.orientation; var useForDemo = _ref2.useForDemo; return { src: makeUnsplashSrc(id), thumbnail: makeUnsplashThumbnail(id, orientation), srcset: [makeUnsplashSrcSet(id, 1024), makeUnsplashSrcSet(id, 800), makeUnsplashSrcSet(id, 500), makeUnsplashSrcSet(id, 320)], caption: caption, orientation: orientation, useForDemo: useForDemo }; }), showThumbnails: true }), _react2['default'].createElement( 'h3', null, 'Themed Lightbox' ), _react2['default'].createElement(_componentsGallery2['default'], { images: THEMED_IMAGES.map(function (_ref3) { var caption = _ref3.caption; var id = _ref3.id; var orientation = _ref3.orientation; var useForDemo = _ref3.useForDemo; return { src: makeUnsplashSrc(id), thumbnail: makeUnsplashThumbnail(id, orientation), srcset: [makeUnsplashSrcSet(id, 1024), makeUnsplashSrcSet(id, 800), makeUnsplashSrcSet(id, 500), makeUnsplashSrcSet(id, 320)], caption: caption, orientation: orientation, useForDemo: useForDemo }; }), theme: theme, showThumbnails: true }) ), document.getElementById('example')); },{"./components/Gallery":2,"react":undefined,"react-dom":undefined}],2:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _aphroditeNoImportant = require('aphrodite/no-important'); var _reactImages = require('react-images'); var _reactImages2 = _interopRequireDefault(_reactImages); var Gallery = (function (_Component) { _inherits(Gallery, _Component); function Gallery() { _classCallCheck(this, Gallery); _get(Object.getPrototypeOf(Gallery.prototype), 'constructor', this).call(this); this.state = { lightboxIsOpen: false, currentImage: 0 }; this.closeLightbox = this.closeLightbox.bind(this); this.gotoNext = this.gotoNext.bind(this); this.gotoPrevious = this.gotoPrevious.bind(this); this.gotoImage = this.gotoImage.bind(this); this.handleClickImage = this.handleClickImage.bind(this); this.openLightbox = this.openLightbox.bind(this); } _createClass(Gallery, [{ key: 'openLightbox', value: function openLightbox(index, event) { event.preventDefault(); this.setState({ currentImage: index, lightboxIsOpen: true }); } }, { key: 'closeLightbox', value: function closeLightbox() { this.setState({ currentImage: 0, lightboxIsOpen: false }); } }, { key: 'gotoPrevious', value: function gotoPrevious() { this.setState({ currentImage: this.state.currentImage - 1 }); } }, { key: 'gotoNext', value: function gotoNext() { this.setState({ currentImage: this.state.currentImage + 1 }); } }, { key: 'gotoImage', value: function gotoImage(index) { this.setState({ currentImage: index }); } }, { key: 'handleClickImage', value: function handleClickImage() { if (this.state.currentImage === this.props.images.length - 1) return; this.gotoNext(); } }, { key: 'renderGallery', value: function renderGallery() { var _this = this; var images = this.props.images; if (!images) return; var gallery = images.filter(function (i) { return i.useForDemo; }).map(function (obj, i) { return _react2['default'].createElement( 'a', { href: obj.src, className: (0, _aphroditeNoImportant.css)(classes.thumbnail, classes[obj.orientation]), key: i, onClick: function (e) { return _this.openLightbox(i, e); } }, _react2['default'].createElement('img', { src: obj.thumbnail, className: (0, _aphroditeNoImportant.css)(classes.source) }) ); }); return _react2['default'].createElement( 'div', { className: (0, _aphroditeNoImportant.css)(classes.gallery) }, gallery ); } }, { key: 'render', value: function render() { return _react2['default'].createElement( 'div', { className: 'section' }, this.props.heading && _react2['default'].createElement( 'h2', null, this.props.heading ), this.props.subheading && _react2['default'].createElement( 'p', null, this.props.subheading ), this.renderGallery(), _react2['default'].createElement(_reactImages2['default'], { currentImage: this.state.currentImage, images: this.props.images, isOpen: this.state.lightboxIsOpen, onClickImage: this.handleClickImage, onClickNext: this.gotoNext, onClickPrev: this.gotoPrevious, onClickThumbnail: this.gotoImage, onClose: this.closeLightbox, showThumbnails: this.props.showThumbnails, theme: this.props.theme }) ); } }]); return Gallery; })(_react.Component); Gallery.displayName = 'Gallery'; Gallery.propTypes = { heading: _propTypes2['default'].string, images: _propTypes2['default'].array, showThumbnails: _propTypes2['default'].bool, subheading: _propTypes2['default'].string }; var gutter = { small: 2, large: 4 }; var classes = _aphroditeNoImportant.StyleSheet.create({ gallery: { marginRight: -gutter.small, overflow: 'hidden', '@media (min-width: 500px)': { marginRight: -gutter.large } }, // anchor thumbnail: { boxSizing: 'border-box', display: 'block', float: 'left', lineHeight: 0, paddingRight: gutter.small, paddingBottom: gutter.small, overflow: 'hidden', '@media (min-width: 500px)': { paddingRight: gutter.large, paddingBottom: gutter.large } }, // orientation landscape: { width: '30%' }, square: { paddingBottom: 0, width: '40%', '@media (min-width: 500px)': { paddingBottom: 0 } }, // actual <img /> source: { border: 0, display: 'block', height: 'auto', maxWidth: '100%', width: 'auto' } }); exports['default'] = Gallery; module.exports = exports['default']; },{"aphrodite/no-important":8,"prop-types":undefined,"react":undefined,"react-images":undefined}],3:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _inlineStylePrefixerStatic = require('inline-style-prefixer/static'); var _inlineStylePrefixerStatic2 = _interopRequireDefault(_inlineStylePrefixerStatic); var _util = require('./util'); /** * Generate CSS for a selector and some styles. * * This function handles the media queries, pseudo selectors, and descendant * styles that can be used in aphrodite styles. * * @param {string} selector: A base CSS selector for the styles to be generated * with. * @param {Object} styleTypes: A list of properties of the return type of * StyleSheet.create, e.g. [styles.red, styles.blue]. * @param stringHandlers: See `generateCSSRuleset` * @param useImportant: See `generateCSSRuleset` * * To actually generate the CSS special-construct-less styles are passed to * `generateCSSRuleset`. * * For instance, a call to * * generateCSSInner(".foo", { * color: "red", * "@media screen": { * height: 20, * ":hover": { * backgroundColor: "black" * } * }, * ":active": { * fontWeight: "bold", * ">>bar": { * _names: { "foo_bar": true }, * height: 10, * } * } * }); * * will make 5 calls to `generateCSSRuleset`: * * generateCSSRuleset(".foo", { color: "red" }, ...) * generateCSSRuleset(".foo:active", { fontWeight: "bold" }, ...) * generateCSSRuleset(".foo:active .foo_bar", { height: 10 }, ...) * // These 2 will be wrapped in @media screen {} * generateCSSRuleset(".foo", { height: 20 }, ...) * generateCSSRuleset(".foo:hover", { backgroundColor: "black" }, ...) */ var generateCSS = function generateCSS(selector, styleTypes, stringHandlers, useImportant) { var merged = styleTypes.reduce(_util.recursiveMerge); var declarations = {}; var mediaQueries = {}; var pseudoStyles = {}; Object.keys(merged).forEach(function (key) { if (key[0] === ':') { pseudoStyles[key] = merged[key]; } else if (key[0] === '@') { mediaQueries[key] = merged[key]; } else { declarations[key] = merged[key]; } }); return generateCSSRuleset(selector, declarations, stringHandlers, useImportant) + Object.keys(pseudoStyles).map(function (pseudoSelector) { return generateCSSRuleset(selector + pseudoSelector, pseudoStyles[pseudoSelector], stringHandlers, useImportant); }).join("") + Object.keys(mediaQueries).map(function (mediaQuery) { var ruleset = generateCSS(selector, [mediaQueries[mediaQuery]], stringHandlers, useImportant); return mediaQuery + '{' + ruleset + '}'; }).join(""); }; exports.generateCSS = generateCSS; /** * Helper method of generateCSSRuleset to facilitate custom handling of certain * CSS properties. Used for e.g. font families. * * See generateCSSRuleset for usage and documentation of paramater types. */ var runStringHandlers = function runStringHandlers(declarations, stringHandlers) { var result = {}; Object.keys(declarations).forEach(function (key) { // If a handler exists for this particular key, let it interpret // that value first before continuing if (stringHandlers && stringHandlers.hasOwnProperty(key)) { result[key] = stringHandlers[key](declarations[key]); } else { result[key] = declarations[key]; } }); return result; }; /** * Generate a CSS ruleset with the selector and containing the declarations. * * This function assumes that the given declarations don't contain any special * children (such as media queries, pseudo-selectors, or descendant styles). * * Note that this method does not deal with nesting used for e.g. * psuedo-selectors or media queries. That responsibility is left to the * `generateCSS` function. * * @param {string} selector: the selector associated with the ruleset * @param {Object} declarations: a map from camelCased CSS property name to CSS * property value. * @param {Object.<string, function>} stringHandlers: a map from camelCased CSS * property name to a function which will map the given value to the value * that is output. * @param {bool} useImportant: A boolean saying whether to append "!important" * to each of the CSS declarations. * @returns {string} A string of raw CSS. * * Examples: * * generateCSSRuleset(".blah", { color: "red" }) * -> ".blah{color: red !important;}" * generateCSSRuleset(".blah", { color: "red" }, {}, false) * -> ".blah{color: red}" * generateCSSRuleset(".blah", { color: "red" }, {color: c => c.toUpperCase}) * -> ".blah{color: RED}" * generateCSSRuleset(".blah:hover", { color: "red" }) * -> ".blah:hover{color: red}" */ var generateCSSRuleset = function generateCSSRuleset(selector, declarations, stringHandlers, useImportant) { var handledDeclarations = runStringHandlers(declarations, stringHandlers); var prefixedDeclarations = (0, _inlineStylePrefixerStatic2['default'])(handledDeclarations); var prefixedRules = (0, _util.flatten)((0, _util.objectToPairs)(prefixedDeclarations).map(function (_ref) { var _ref2 = _slicedToArray(_ref, 2); var key = _ref2[0]; var value = _ref2[1]; if (Array.isArray(value)) { var _ret = (function () { // inline-style-prefix-all returns an array when there should be // multiple rules, we will flatten to single rules var prefixedValues = []; var unprefixedValues = []; value.forEach(function (v) { if (v.indexOf('-') === 0) { prefixedValues.push(v); } else { unprefixedValues.push(v); } }); prefixedValues.sort(); unprefixedValues.sort(); return { v: prefixedValues.concat(unprefixedValues).map(function (v) { return [key, v]; }) }; })(); if (typeof _ret === 'object') return _ret.v; } return [[key, value]]; })); var rules = prefixedRules.map(function (_ref3) { var _ref32 = _slicedToArray(_ref3, 2); var key = _ref32[0]; var value = _ref32[1]; var stringValue = (0, _util.stringifyValue)(key, value); var ret = (0, _util.kebabifyStyleName)(key) + ':' + stringValue + ';'; return useImportant === false ? ret : (0, _util.importantify)(ret); }).join(""); if (rules) { return selector + '{' + rules + '}'; } else { return ""; } }; exports.generateCSSRuleset = generateCSSRuleset; },{"./util":7,"inline-style-prefixer/static":28}],4:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); var _util = require('./util'); var _inject = require('./inject'); var StyleSheet = { create: function create(sheetDefinition) { return (0, _util.mapObj)(sheetDefinition, function (_ref) { var _ref2 = _slicedToArray(_ref, 2); var key = _ref2[0]; var val = _ref2[1]; return [key, { // TODO(emily): Make a 'production' mode which doesn't prepend // the class name here, to make the generated CSS smaller. _name: key + '_' + (0, _util.hashObject)(val), _definition: val }]; }); }, rehydrate: function rehydrate() { var renderedClassNames = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0]; (0, _inject.addRenderedClassNames)(renderedClassNames); } }; /** * Utilities for using Aphrodite server-side. */ var StyleSheetServer = { renderStatic: function renderStatic(renderFunc) { (0, _inject.reset)(); (0, _inject.startBuffering)(); var html = renderFunc(); var cssContent = (0, _inject.flushToString)(); return { html: html, css: { content: cssContent, renderedClassNames: (0, _inject.getRenderedClassNames)() } }; } }; /** * Utilities for using Aphrodite in tests. * * Not meant to be used in production. */ var StyleSheetTestUtils = { /** * Prevent styles from being injected into the DOM. * * This is useful in situations where you'd like to test rendering UI * components which use Aphrodite without any of the side-effects of * Aphrodite happening. Particularly useful for testing the output of * components when you have no DOM, e.g. testing in Node without a fake DOM. * * Should be paired with a subsequent call to * clearBufferAndResumeStyleInjection. */ suppressStyleInjection: function suppressStyleInjection() { (0, _inject.reset)(); (0, _inject.startBuffering)(); }, /** * Opposite method of preventStyleInject. */ clearBufferAndResumeStyleInjection: function clearBufferAndResumeStyleInjection() { (0, _inject.reset)(); } }; var css = function css() { for (var _len = arguments.length, styleDefinitions = Array(_len), _key = 0; _key < _len; _key++) { styleDefinitions[_key] = arguments[_key]; } var useImportant = true; // Append !important to all style definitions return (0, _inject.injectAndGetClassName)(useImportant, styleDefinitions); }; exports['default'] = { StyleSheet: StyleSheet, StyleSheetServer: StyleSheetServer, StyleSheetTestUtils: StyleSheetTestUtils, css: css }; module.exports = exports['default']; },{"./inject":5,"./util":7}],5:[function(require,module,exports){ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _asap = require('asap'); var _asap2 = _interopRequireDefault(_asap); var _generate = require('./generate'); var _util = require('./util'); // The current <style> tag we are inserting into, or null if we haven't // inserted anything yet. We could find this each time using // `document.querySelector("style[data-aphrodite"])`, but holding onto it is // faster. var styleTag = null; // Inject a string of styles into a <style> tag in the head of the document. This // will automatically create a style tag and then continue to use it for // multiple injections. It will also use a style tag with the `data-aphrodite` // tag on it if that exists in the DOM. This could be used for e.g. reusing the // same style tag that server-side rendering inserts. var injectStyleTag = function injectStyleTag(cssContents) { if (styleTag == null) { // Try to find a style tag with the `data-aphrodite` attribute first. styleTag = document.querySelector("style[data-aphrodite]"); // If that doesn't work, generate a new style tag. if (styleTag == null) { // Taken from // http://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript var head = document.head || document.getElementsByTagName('head')[0]; styleTag = document.createElement('style'); styleTag.type = 'text/css'; styleTag.setAttribute("data-aphrodite", ""); head.appendChild(styleTag); } } if (styleTag.styleSheet) { styleTag.styleSheet.cssText += cssContents; } else { styleTag.appendChild(document.createTextNode(cssContents)); } }; // Custom handlers for stringifying CSS values that have side effects // (such as fontFamily, which can cause @font-face rules to be injected) var stringHandlers = { // With fontFamily we look for objects that are passed in and interpret // them as @font-face rules that we need to inject. The value of fontFamily // can either be a string (as normal), an object (a single font face), or // an array of objects and strings. fontFamily: function fontFamily(val) { if (Array.isArray(val)) { return val.map(fontFamily).join(","); } else if (typeof val === "object") { injectStyleOnce(val.fontFamily, "@font-face", [val], false); return '"' + val.fontFamily + '"'; } else { return val; } }, // With animationName we look for an object that contains keyframes and // inject them as an `@keyframes` block, returning a uniquely generated // name. The keyframes object should look like // animationName: { // from: { // left: 0, // top: 0, // }, // '50%': { // left: 15, // top: 5, // }, // to: { // left: 20, // top: 20, // } // } // TODO(emily): `stringHandlers` doesn't let us rename the key, so I have // to use `animationName` here. Improve that so we can call this // `animation` instead of `animationName`. animationName: function animationName(val) { if (typeof val !== "object") { return val; } // Generate a unique name based on the hash of the object. We can't // just use the hash because the name can't start with a number. // TODO(emily): this probably makes debugging hard, allow a custom // name? var name = 'keyframe_' + (0, _util.hashObject)(val); // Since keyframes need 3 layers of nesting, we use `generateCSS` to // build the inner layers and wrap it in `@keyframes` ourselves. var finalVal = '@keyframes ' + name + '{'; Object.keys(val).forEach(function (key) { finalVal += (0, _generate.generateCSS)(key, [val[key]], stringHandlers, false); }); finalVal += '}'; injectGeneratedCSSOnce(name, finalVal); return name; } }; // This is a map from Aphrodite's generated class names to `true` (acting as a // set of class names) var alreadyInjected = {}; // This is the buffer of styles which have not yet been flushed. var injectionBuffer = ""; // A flag to tell if we are already buffering styles. This could happen either // because we scheduled a flush call already, so newly added styles will // already be flushed, or because we are statically buffering on the server. var isBuffering = false; var injectGeneratedCSSOnce = function injectGeneratedCSSOnce(key, generatedCSS) { if (!alreadyInjected[key]) { if (!isBuffering) { // We should never be automatically buffering on the server (or any // place without a document), so guard against that. if (typeof document === "undefined") { throw new Error("Cannot automatically buffer without a document"); } // If we're not already buffering, schedule a call to flush the // current styles. isBuffering = true; (0, _asap2['default'])(flushToStyleTag); } injectionBuffer += generatedCSS; alreadyInjected[key] = true; } }; var injectStyleOnce = function injectStyleOnce(key, selector, definitions, useImportant) { if (!alreadyInjected[key]) { var generated = (0, _generate.generateCSS)(selector, definitions, stringHandlers, useImportant); injectGeneratedCSSOnce(key, generated); } }; exports.injectStyleOnce = injectStyleOnce; var reset = function reset() { injectionBuffer = ""; alreadyInjected = {}; isBuffering = false; styleTag = null; }; exports.reset = reset; var startBuffering = function startBuffering() { if (isBuffering) { throw new Error("Cannot buffer while already buffering"); } isBuffering = true; }; exports.startBuffering = startBuffering; var flushToString = function flushToString() { isBuffering = false; var ret = injectionBuffer; injectionBuffer = ""; return ret; }; exports.flushToString = flushToString; var flushToStyleTag = function flushToStyleTag() { var cssContent = flushToString(); if (cssContent.length > 0) { injectStyleTag(cssContent); } }; exports.flushToStyleTag = flushToStyleTag; var getRenderedClassNames = function getRenderedClassNames() { return Object.keys(alreadyInjected); }; exports.getRenderedClassNames = getRenderedClassNames; var addRenderedClassNames = function addRenderedClassNames(classNames) { classNames.forEach(function (className) { alreadyInjected[className] = true; }); }; exports.addRenderedClassNames = addRenderedClassNames; /** * Inject styles associated with the passed style definition objects, and return * an associated CSS class name. * * @param {boolean} useImportant If true, will append !important to generated * CSS output. e.g. {color: red} -> "color: red !important". * @param {Object[]} styleDefinitions style definition objects as returned as * properties of the return value of StyleSheet.create(). */ var injectAndGetClassName = function injectAndGetClassName(useImportant, styleDefinitions) { // Filter out falsy values from the input, to allow for // `css(a, test && c)` var validDefinitions = styleDefinitions.filter(function (def) { return def; }); // Break if there aren't any valid styles. if (validDefinitions.length === 0) { return ""; } var className = validDefinitions.map(function (s) { return s._name; }).join("-o_O-"); injectStyleOnce(className, '.' + className, validDefinitions.map(function (d) { return d._definition; }), useImportant); return className; }; exports.injectAndGetClassName = injectAndGetClassName; },{"./generate":3,"./util":7,"asap":9}],6:[function(require,module,exports){ // Module with the same interface as the core aphrodite module, // except that styles injected do not automatically have !important // appended to them. // 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _inject = require('./inject'); var _indexJs = require('./index.js'); var css = function css() { for (var _len = arguments.length, styleDefinitions = Array(_len), _key = 0; _key < _len; _key++) { styleDefinitions[_key] = arguments[_key]; } var useImportant = false; // Don't append !important to style definitions return (0, _inject.injectAndGetClassName)(useImportant, styleDefinitions); }; exports.StyleSheet = _indexJs.StyleSheet; exports.StyleSheetServer = _indexJs.StyleSheetServer; exports.StyleSheetTestUtils = _indexJs.StyleSheetTestUtils; exports.css = css; },{"./index.js":4,"./inject":5}],7:[function(require,module,exports){ // {K1: V1, K2: V2, ...} -> [[K1, V1], [K2, V2]] 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var objectToPairs = function objectToPairs(obj) { return Object.keys(obj).map(function (key) { return [key, obj[key]]; }); }; exports.objectToPairs = objectToPairs; // [[K1, V1], [K2, V2]] -> {K1: V1, K2: V2, ...} var pairsToObject = function pairsToObject(pairs) { var result = {}; pairs.forEach(function (_ref) { var _ref2 = _slicedToArray(_ref, 2); var key = _ref2[0]; var val = _ref2[1]; result[key] = val; }); return result; }; var mapObj = function mapObj(obj, fn) { return pairsToObject(objectToPairs(obj).map(fn)); }; exports.mapObj = mapObj; // Flattens an array one level // [[A], [B, C, [D]]] -> [A, B, C, [D]] var flatten = function flatten(list) { return list.reduce(function (memo, x) { return memo.concat(x); }, []); }; exports.flatten = flatten; var UPPERCASE_RE = /([A-Z])/g; var MS_RE = /^ms-/; var kebabify = function kebabify(string) { return string.replace(UPPERCASE_RE, '-$1').toLowerCase(); }; var kebabifyStyleName = function kebabifyStyleName(string) { return kebabify(string).replace(MS_RE, '-ms-'); }; exports.kebabifyStyleName = kebabifyStyleName; var recursiveMerge = function recursiveMerge(a, b) { // TODO(jlfwong): Handle malformed input where a and b are not the same // type. if (typeof a !== 'object') { return b; } var ret = _extends({}, a); Object.keys(b).forEach(function (key) { if (ret.hasOwnProperty(key)) { ret[key] = recursiveMerge(a[key], b[key]); } else { ret[key] = b[key]; } }); return ret; }; exports.recursiveMerge = recursiveMerge; /** * CSS properties which accept numbers but are not in units of "px". * Taken from React's CSSProperty.js */ var isUnitlessNumber = { animationIterationCount: true, borderImageOutset: true, borderImageSlice: true, borderImageWidth: true, boxFlex: true, boxFlexGroup: true, boxOrdinalGroup: true, columnCount: true, flex: true, flexGrow: true, flexPositive: true, flexShrink: true, flexNegative: true, flexOrder: true, gridRow: true, gridColumn: true, fontWeight: true, lineClamp: true, lineHeight: true, opacity: true, order: true, orphans: true, tabSize: true, widows: true, zIndex: true, zoom: true, // SVG-related properties fillOpacity: true, floodOpacity: true, stopOpacity: true, strokeDasharray: true, strokeDashoffset: true, strokeMiterlimit: true, strokeOpacity: true, strokeWidth: true }; /** * Taken from React's CSSProperty.js * * @param {string} prefix vendor-specific prefix, eg: Webkit * @param {string} key style name, eg: transitionDuration * @return {string} style name prefixed with `prefix`, properly camelCased, eg: * WebkitTransitionDuration */ function prefixKey(prefix, key) { return prefix + key.charAt(0).toUpperCase() + key.substring(1); } /** * Support style names that may come passed in prefixed by adding permutations * of vendor prefixes. * Taken from React's CSSProperty.js */ var prefixes = ['Webkit', 'ms', 'Moz', 'O']; // Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an // infinite loop, because it iterates over the newly added props too. // Taken from React's CSSProperty.js Object.keys(isUnitlessNumber).forEach(function (prop) { prefixes.forEach(function (prefix) { isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop]; }); }); var stringifyValue = function stringifyValue(key, prop) { if (typeof prop === "number") { if (isUnitlessNumber[key]) { return "" + prop; } else { return prop + "px"; } } else { return prop; } }; exports.stringifyValue = stringifyValue; /** * JS Implementation of MurmurHash2 * * @author <a href="mailto:gary.court@gmail.com">Gary Court</a> * @see http://github.com/garycourt/murmurhash-js * @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a> * @see http://sites.google.com/site/murmurhash/ * * @param {string} str ASCII only * @return {string} Base 36 encoded hash result */ function murmurhash2_32_gc(str) { var l = str.length; var h = l; var i = 0; var k = undefined; while (l >= 4) { k = str.charCodeAt(i) & 0xff | (str.charCodeAt(++i) & 0xff) << 8 | (str.charCodeAt(++i) & 0xff) << 16 | (str.charCodeAt(++i) & 0xff) << 24; k = (k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0x5bd1e995 & 0xffff) << 16); k ^= k >>> 24; k = (k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0x5bd1e995 & 0xffff) << 16); h = (h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0x5bd1e995 & 0xffff) << 16) ^ k; l -= 4; ++i; } switch (l) { case 3: h ^= (str.charCodeAt(i + 2) & 0xff) << 16; case 2: h ^= (str.charCodeAt(i + 1) & 0xff) << 8; case 1: h ^= str.charCodeAt(i) & 0xff; h = (h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0x5bd1e995 & 0xffff) << 16); } h ^= h >>> 13; h = (h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0x5bd1e995 & 0xffff) << 16); h ^= h >>> 15; return (h >>> 0).toString(36); } // Hash a javascript object using JSON.stringify. This is very fast, about 3 // microseconds on my computer for a sample object: // http://jsperf.com/test-hashfnv32a-hash/5 // // Note that this uses JSON.stringify to stringify the objects so in order for // this to produce consistent hashes browsers need to have a consistent // ordering of objects. Ben Alpert says that Facebook depends on this, so we // can probably depend on this too. var hashObject = function hashObject(object) { return murmurhash2_32_gc(JSON.stringify(object)); }; exports.hashObject = hashObject; var IMPORTANT_RE = /^([^:]+:.*?)( !important)?;$/; // Given a single style rule string like "a: b;", adds !important to generate // "a: b !important;". var importantify = function importantify(string) { return string.replace(IMPORTANT_RE, function (_, base, important) { return base + " !important;"; }); }; exports.importantify = importantify; },{}],8:[function(require,module,exports){ module.exports = require('./lib/no-important.js'); },{"./lib/no-important.js":6}],9:[function(require,module,exports){ "use strict"; // rawAsap provides everything we need except exception management. var rawAsap = require("./raw"); // RawTasks are recycled to reduce GC churn. var freeTasks = []; // We queue errors to ensure they are thrown in right order (FIFO). // Array-as-queue is good enough here, since we are just dealing with exceptions. var pendingErrors = []; var requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError); function throwFirstError() { if (pendingErrors.length) { throw pendingErrors.shift(); } } /** * Calls a task as soon as possible after returning, in its own event, with priority * over other events like animation, reflow, and repaint. An error thrown from an * event will not interrupt, nor even substantially slow down the processing of * other events, but will be rather postponed to a lower priority event. * @param {{call}} task A callable object, typically a function that takes no * arguments. */ module.exports = asap; function asap(task) { var rawTask; if (freeTasks.length) { rawTask = freeTasks.pop(); } else { rawTask = new RawTask(); } rawTask.task = task; rawAsap(rawTask); } // We wrap tasks with recyclable task objects. A task object implements // `call`, just like a function. function RawTask() { this.task = null; } // The sole purpose of wrapping the task is to catch the exception and recycle // the task object after its single use. RawTask.prototype.call = function () { try { this.task.call(); } catch (error) { if (asap.onerror) { // This hook exists purely for testing purposes. // Its name will be periodically randomized to break any code that // depends on its existence. asap.onerror(error); } else { // In a web browser, exceptions are not fatal. However, to avoid // slowing down the queue of pending tasks, we rethrow the error in a // lower priority turn. pendingErrors.push(error); requestErrorThrow(); } } finally { this.task = null; freeTasks[freeTasks.length] = this; } }; },{"./raw":10}],10:[function(require,module,exports){ (function (global){ "use strict"; // Use the fastest means possible to execute a task in its own turn, with // priority over other events including IO, animation, reflow, and redraw // events in browsers. // // An exception thrown by a task will permanently interrupt the processing of // subsequent tasks. The higher level `asap` function ensures that if an // exception is thrown by a task, that the task queue will continue flushing as // soon as possible, but if you use `rawAsap` directly, you are responsible to // either ensure that no exceptions are thrown from your task, or to manually // call `rawAsap.requestFlush` if an exception is thrown. module.exports = rawAsap; function rawAsap(task) { if (!queue.length) { requestFlush(); flushing = true; } // Equivalent to push, but avoids a function call. queue[queue.length] = task; } var queue = []; // Once a flush has been requested, no further calls to `requestFlush` are // necessary until the next `flush` completes. var flushing = false; // `requestFlush` is an implementation-specific method that attempts to kick // off a `flush` event as quickly as possible. `flush` will attempt to exhaust // the event queue before yielding to the browser's own event loop. var requestFlush; // The position of the next task to execute in the task queue. This is // preserved between calls to `flush` so that it can be resumed if // a task throws an exception. var index = 0; // If a task schedules additional tasks recursively, the task queue can grow // unbounded. To prevent memory exhaustion, the task queue will periodically // truncate already-completed tasks. var capacity = 1024; // The flush function processes all tasks that have been scheduled with // `rawAsap` unless and until one of those tasks throws an exception. // If a task throws an exception, `flush` ensures that its state will remain // consistent and will resume where it left off when called again. // However, `flush` does not make any arrangements to be called again if an // exception is thrown. function flush() { while (index < queue.length) { var currentIndex = index; // Advance the index before calling the task. This ensures that we will // begin flushing on the next task the task throws an error. index = index + 1; queue[currentIndex].call(); // Prevent leaking memory for long chains of recursive calls to `asap`. // If we call `asap` within tasks scheduled by `asap`, the queue will // grow, but to avoid an O(n) walk for every task we execute, we don't // shift tasks off the queue after they have been executed. // Instead, we periodically shift 1024 tasks off the queue. if (index > capacity) { // Manually shift all values starting at the index back to the // beginning of the queue. for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) { queue[scan] = queue[scan + index]; } queue.length -= index; index = 0; } } queue.length = 0; index = 0; flushing = false; } // `requestFlush` is implemented using a strategy based on data collected from // every available SauceLabs Selenium web driver worker at time of writing. // https://docs.google.com/spreadsheets/d/1mG-5UYGup5qxGdEMWkhP6BWCz053NUb2E1QoUTU16uA/edit#gid=783724593 // Safari 6 and 6.1 for desktop, iPad, and iPhone are the only browsers that // have WebKitMutationObserver but not un-prefixed MutationObserver. // Must use `global` or `self` instead of `window` to work in both frames and web // workers. `global` is a provision of Browserify, Mr, Mrs, or Mop. /* globals self */ var scope = typeof global !== "undefined" ? global : self; var BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver; // MutationObservers are desirable because they have high priority and work // reliably everywhere they are implemented. // They are implemented in all modern browsers. // // - Android 4-4.3 // - Chrome 26-34 // - Firefox 14-29 // - Internet Explorer 11 // - iPad Safari 6-7.1 // - iPhone Safari 7-7.1 // - Safari 6-7 if (typeof BrowserMutationObserver === "function") { requestFlush = makeRequestCallFromMutationObserver(flush); // MessageChannels are desirable because they give direct access to the HTML // task queue, are implemented in Internet