UNPKG

react-images

Version:

A mobile-friendly, highly customizable, carousel component for displaying media in ReactJS

1,610 lines (1,351 loc) 51.8 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react'), require('react-dom'), require('glam'), require('raf-schd'), require('react-view-pager'), require('html-react-parser'), require('react-focus-on'), require('react-full-screen'), require('a11y-focus-store'), require('react-transition-group')) : typeof define === 'function' && define.amd ? define(['react', 'react-dom', 'glam', 'raf-schd', 'react-view-pager', 'html-react-parser', 'react-focus-on', 'react-full-screen', 'a11y-focus-store', 'react-transition-group'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Images = factory(global.React, global.ReactDOM, global.glam, global.rafScheduler, global.PageView, global.ParseHtml, global.FocusOn, global.Fullscreen, global.focusStore, global.Transition)); }(this, (function (React, reactDom, glam, rafScheduler, reactViewPager, ParseHtml, reactFocusOn, reactFullScreen, focusStore, reactTransitionGroup) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var glam__default = /*#__PURE__*/_interopDefaultLegacy(glam); var rafScheduler__default = /*#__PURE__*/_interopDefaultLegacy(rafScheduler); var ParseHtml__default = /*#__PURE__*/_interopDefaultLegacy(ParseHtml); var focusStore__default = /*#__PURE__*/_interopDefaultLegacy(focusStore); var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; 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 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; }; 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 inherits = function (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 objectWithoutProperties = function (obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }; var possibleConstructorReturn = function (self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }; var Base = function Base(_ref) { var css = _ref.css, innerRef = _ref.innerRef, Tag = _ref.tag, props = objectWithoutProperties(_ref, ['css', 'innerRef', 'tag']); return glam__default['default'](Tag, _extends({ ref: innerRef, css: _extends({ boxSizing: 'border-box' }, css) }, props)); }; var Button = function Button(props) { return glam__default['default'](Base, _extends({ tag: 'button' }, props)); }; var Div = function Div(props) { return glam__default['default'](Base, _extends({ tag: 'div' }, props)); }; var Img = function Img(props) { return glam__default['default'](Base, _extends({ tag: 'img' }, props)); }; var Nav = function Nav(props) { return glam__default['default'](Base, _extends({ tag: 'nav' }, props)); }; var Span = function Span(props) { return glam__default['default'](Base, _extends({ tag: 'span' }, props)); }; // ============================== // Class Name Prefixer // ============================== var CLASS_PREFIX = 'react-images'; /** String representation of component state for styling with class names. Expects an array of strings OR a string/object pair: - className(['comp', 'comp-arg', 'comp-arg-2']) @returns 'react-images__comp react-images__comp-arg react-images__comp-arg-2' - className('comp', { some: true, state: false }) @returns 'react-images__comp react-images__comp--some' */ function className(name, state) { var arr = Array.isArray(name) ? name : [name]; // loop through state object, remove falsey values and combine with name if (state && typeof name === 'string') { for (var _key in state) { if (state.hasOwnProperty(_key) && state[_key]) { arr.push(name + '--' + _key); } } } // prefix everything and return a string return arr.map(function (cn) { return CLASS_PREFIX + '__' + cn; }).join(' '); } // ============================== // Touch Capability Detector // ============================== function isTouch() { try { document.createEvent('TouchEvent'); return true; } catch (e) { return false; } } var containerCSS = function containerCSS(_ref) { var isFullscreen = _ref.isFullscreen; return { backgroundColor: isFullscreen ? 'black' : null, display: 'flex ', flexDirection: 'column', height: '100%' }; }; var Container = function Container(props) { var children = props.children, getStyles = props.getStyles, isFullscreen = props.isFullscreen, isModal = props.isModal, innerProps = props.innerProps; return glam__default['default']( Div, _extends({ css: getStyles('container', props), className: className('container', { isFullscreen: isFullscreen, isModal: isModal }) }, innerProps), children ); }; var smallDevice = '@media (max-width: 769px)'; /** * Used to get the HTML class to select specific components. * We call `className()` in utils with each of these to get the full className, * with prefixes. */ var componentBaseClassNames = { Header: 'header', Footer: 'footer', View: 'view', Track: 'track', Positioner: 'positioner' }; var footerCSS = function footerCSS(_ref) { var isModal = _ref.isModal, interactionIsIdle = _ref.interactionIsIdle; return defineProperty({ alignItems: 'top', bottom: isModal ? 0 : null, color: isModal ? 'rgba(255, 255, 255, 0.9)' : '#666', display: 'flex ', flex: '0 0 auto', fontSize: 13, justifyContent: 'space-between', left: isModal ? 0 : null, opacity: interactionIsIdle && isModal ? 0 : 1, padding: isModal ? '30px 20px 20px' : '10px 0', position: isModal ? 'absolute' : null, right: isModal ? 0 : null, transform: isModal ? 'translateY(' + (interactionIsIdle ? 10 : 0) + 'px)' : null, transition: 'opacity 300ms, transform 300ms', zIndex: isModal ? 1 : null, '& *:focus': { outline: '1.5px solid orange' } }, smallDevice, { padding: isModal ? '20px 15px 15px' : '5px 0' }); }; var footerBaseClassName = componentBaseClassNames.Footer; var Footer = function Footer(props) { var components = props.components, getStyles = props.getStyles, innerProps = props.innerProps, isFullscreen = props.isFullscreen, isModal = props.isModal; var style = isModal ? { background: 'linear-gradient(rgba(0,0,0,0), rgba(0,0,0,0.33))' } : null; var state = { isFullscreen: isFullscreen, isModal: isModal }; var cn = { container: className(footerBaseClassName, state), caption: className('footer__caption', state), count: className('footer__count', state) }; var css = { container: getStyles(footerBaseClassName, props), caption: getStyles('footerCaption', props), count: getStyles('footerCount', props) }; var Caption = components.Caption, Count = components.Count; return glam__default['default']( Div, _extends({ css: css.container, className: cn.container // TODO glam prefixer fails on gradients // https://github.com/threepointone/glam/issues/35 , style: style }, innerProps), glam__default['default'](Caption, props), glam__default['default'](Count, props) ); }; // ============================== // Inner Elements // ============================== var footerCaptionCSS = function footerCaptionCSS() { return {}; }; var FooterCaption = function FooterCaption(props) { var currentView = props.currentView, getStyles = props.getStyles, isFullscreen = props.isFullscreen, isModal = props.isModal; var caption = currentView.caption; var state = { isFullscreen: isFullscreen, isModal: isModal }; return glam__default['default']( Span, { css: getStyles('footerCaption', props), className: className('footer__caption', state) }, typeof caption === 'string' ? ParseHtml__default['default']('<span>' + caption + '</span>') : caption ); }; var footerCountCSS = function footerCountCSS() { return { flexShrink: 0, marginLeft: '1em' }; }; var FooterCount = function FooterCount(props) { var currentIndex = props.currentIndex, getStyles = props.getStyles, isFullscreen = props.isFullscreen, isModal = props.isModal, views = props.views; var state = { isFullscreen: isFullscreen, isModal: isModal }; var activeView = currentIndex + 1; var totalViews = views.length; if (!activeView || !totalViews) return null; return glam__default['default']( Span, { css: getStyles('footerCount', props), className: className('footer__count', state) }, activeView, ' of ', totalViews ); }; var Svg = function Svg(_ref) { var size = _ref.size, props = objectWithoutProperties(_ref, ['size']); return glam__default['default']('svg', _extends({ role: 'presentation', viewBox: '0 0 24 24', css: { display: 'inline-block', fill: 'currentColor', height: size, stroke: 'currentColor', strokeWidth: 0, width: size } }, props)); }; var ChevronLeft = function ChevronLeft(_ref2) { var _ref2$size = _ref2.size, size = _ref2$size === undefined ? 32 : _ref2$size, props = objectWithoutProperties(_ref2, ['size']); return glam__default['default']( Svg, _extends({ size: size }, props), glam__default['default']('path', { d: 'M15.422 16.078l-1.406 1.406-6-6 6-6 1.406 1.406-4.594 4.594z' }) ); }; var ChevronRight = function ChevronRight(_ref3) { var _ref3$size = _ref3.size, size = _ref3$size === undefined ? 32 : _ref3$size, props = objectWithoutProperties(_ref3, ['size']); return glam__default['default']( Svg, _extends({ size: size }, props), glam__default['default']('path', { d: 'M9.984 6l6 6-6 6-1.406-1.406 4.594-4.594-4.594-4.594z' }) ); }; var Close = function Close(_ref4) { var _ref4$size = _ref4.size, size = _ref4$size === undefined ? 32 : _ref4$size, props = objectWithoutProperties(_ref4, ['size']); return glam__default['default']( Svg, _extends({ size: size }, props), glam__default['default']('path', { d: 'M18.984 6.422l-5.578 5.578 5.578 5.578-1.406 1.406-5.578-5.578-5.578 5.578-1.406-1.406 5.578-5.578-5.578-5.578 1.406-1.406 5.578 5.578 5.578-5.578z' }) ); }; var FullscreenEnter = function FullscreenEnter(_ref5) { var _ref5$size = _ref5.size, size = _ref5$size === undefined ? 32 : _ref5$size, props = objectWithoutProperties(_ref5, ['size']); return glam__default['default']( Svg, _extends({ size: size }, props), glam__default['default']('path', { d: 'M14.016 5.016h4.969v4.969h-1.969v-3h-3v-1.969zM17.016 17.016v-3h1.969v4.969h-4.969v-1.969h3zM5.016 9.984v-4.969h4.969v1.969h-3v3h-1.969zM6.984 14.016v3h3v1.969h-4.969v-4.969h1.969z' }) ); }; var FullscreenExit = function FullscreenExit(_ref6) { var _ref6$size = _ref6.size, size = _ref6$size === undefined ? 32 : _ref6$size, props = objectWithoutProperties(_ref6, ['size']); return glam__default['default']( Svg, _extends({ size: size }, props), glam__default['default']('path', { d: 'M15.984 8.016h3v1.969h-4.969v-4.969h1.969v3zM14.016 18.984v-4.969h4.969v1.969h-3v3h-1.969zM8.016 8.016v-3h1.969v4.969h-4.969v-1.969h3zM5.016 15.984v-1.969h4.969v4.969h-1.969v-3h-3z' }) ); }; var headerCSS = function headerCSS(_ref) { var interactionIsIdle = _ref.interactionIsIdle; return { alignItems: 'center', display: 'flex ', flex: '0 0 auto', justifyContent: 'space-between', opacity: interactionIsIdle ? 0 : 1, padding: 10, paddingBottom: 20, position: 'absolute', transform: 'translateY(' + (interactionIsIdle ? -10 : 0) + 'px)', transition: 'opacity 300ms, transform 300ms', top: 0, left: 0, right: 0, zIndex: 1, '& *:focus': { outline: '1.5px solid orange' } }; }; var headerBaseClassName = componentBaseClassNames.Header; var Header = function Header(props) { var components = props.components, getStyles = props.getStyles, getCloseLabel = props.getCloseLabel, getFullscreenLabel = props.getFullscreenLabel, innerProps = props.innerProps, isModal = props.isModal, modalProps = props.modalProps; if (!isModal) return null; var allowFullscreen = modalProps.allowFullscreen, isFullscreen = modalProps.isFullscreen, onClose = modalProps.onClose, toggleFullscreen = modalProps.toggleFullscreen; var FsIcon = isFullscreen ? FullscreenExit : FullscreenEnter; var CloseButton = components.CloseButton, FullscreenButton = components.FullscreenButton; var state = { isFullscreen: isFullscreen, isModal: isModal }; return glam__default['default']( Div, _extends({ css: getStyles(headerBaseClassName, props), className: className(headerBaseClassName, state) // TODO glam prefixer fails on gradients // https://github.com/threepointone/glam/issues/35 , style: { background: 'linear-gradient(rgba(0,0,0,0.33), rgba(0,0,0,0))' } }, innerProps), glam__default['default']('span', null), glam__default['default']( 'span', null, allowFullscreen ? glam__default['default']( FullscreenButton, { getStyles: getStyles, innerProps: { onClick: toggleFullscreen, title: getFullscreenLabel(state) } }, glam__default['default'](FsIcon, { size: 32 }) ) : null, glam__default['default']( CloseButton, { getStyles: getStyles, innerProps: { onClick: onClose, title: getCloseLabel(state) } }, glam__default['default'](Close, { size: 32 }) ) ) ); }; // ============================== // Header Buttons // ============================== var headerButtonCSS = function headerButtonCSS() { return { alignItems: 'center', background: 0, border: 0, color: 'rgba(255, 255, 255, 0.75)', cursor: 'pointer', display: 'inline-flex ', height: 44, justifyContent: 'center', outline: 0, padding: 0, position: 'relative', width: 44, '&:hover': { color: 'white' } }; }; var headerFullscreenCSS = headerButtonCSS; var HeaderFullscreen = function HeaderFullscreen(props) { var children = props.children, getStyles = props.getStyles, innerProps = props.innerProps; return glam__default['default']( Button, _extends({ css: getStyles('headerFullscreen', props), className: className(['header_button', 'header_button--fullscreen']), type: 'button' }, innerProps), children ); }; var headerCloseCSS = headerButtonCSS; var HeaderClose = function HeaderClose(props) { var children = props.children, getStyles = props.getStyles, innerProps = props.innerProps; return glam__default['default']( Button, _extends({ css: getStyles('headerClose', props), className: className(['header_button', 'header_button--close']), type: 'button' }, innerProps), children ); }; // ============================== // Navigation // ============================== var navigationCSS = function navigationCSS(_ref) { var interactionIsIdle = _ref.interactionIsIdle; return { display: 'flex ', alignItems: 'center', justifyContent: 'space-between', opacity: interactionIsIdle ? 0 : 1, transition: 'opacity 300ms', '& *:focus': { outline: '1.5px solid orange' } }; }; var Navigation = function Navigation(props) { var children = props.children, getStyles = props.getStyles, isFullscreen = props.isFullscreen, isModal = props.isModal, showNavigationOnTouchDevice = props.showNavigationOnTouchDevice; return !isTouch() || isTouch() && showNavigationOnTouchDevice ? glam__default['default']( Nav, { css: getStyles('navigation', props), className: className('navigation', { isFullscreen: isFullscreen, isModal: isModal }) }, children ) : null; }; // ============================== // Nav Item // ============================== var BUTTON_SIZE = 50; var navigationItemCSS = function navigationItemCSS(_ref2) { var _ref3; var align = _ref2.align; return _ref3 = { alignItems: 'center', background: 'rgba(255, 255, 255, 0.2)', border: 0, borderRadius: '50%', color: 'white', cursor: 'pointer', display: 'flex ', fontSize: 'inherit', height: BUTTON_SIZE, justifyContent: 'center', marginTop: -(BUTTON_SIZE / 2), outline: 0, position: 'absolute', top: '50%', transition: 'background-color 200ms', width: BUTTON_SIZE }, defineProperty(_ref3, align, 20), defineProperty(_ref3, '&:hover', { background: 'rgba(255, 255, 255, 0.3)' }), defineProperty(_ref3, '&:active', { background: 'rgba(255, 255, 255, 0.2)' }), _ref3; }; var navigationPrevCSS = navigationItemCSS; var NavigationPrev = function NavigationPrev(props) { var _props$children = props.children, children = _props$children === undefined ? glam__default['default'](ChevronLeft, { size: 48 }) : _props$children, getStyles = props.getStyles, innerProps = props.innerProps; return glam__default['default']( Button, _extends({ type: 'button', css: getStyles('navigationPrev', props) }, innerProps), children ); }; var navigationNextCSS = navigationItemCSS; var NavigationNext = function NavigationNext(props) { var _props$children2 = props.children, children = _props$children2 === undefined ? glam__default['default'](ChevronRight, { size: 48 }) : _props$children2, getStyles = props.getStyles, innerProps = props.innerProps; return glam__default['default']( Button, _extends({ type: 'button', css: getStyles('navigationNext', props) }, innerProps), children ); }; // ============================== // Blanket // ============================== var blanketCSS = function blanketCSS(_ref) { var isFullscreen = _ref.isFullscreen; return { backgroundColor: isFullscreen ? 'black' : 'rgba(0, 0, 0, 0.8)', bottom: 0, left: 0, position: 'fixed', right: 0, top: 0, zIndex: 1199 }; }; var Blanket = function Blanket(props) { var getStyles = props.getStyles, innerProps = props.innerProps, isFullscreen = props.isFullscreen; return glam__default['default'](Div, _extends({ css: getStyles('blanket', props), className: className('blanket', { isFullscreen: isFullscreen }) }, innerProps)); }; // ============================== // Positioner // ============================== var positionerCSS = function positionerCSS() { return { alignItems: 'center', bottom: 0, display: 'flex ', justifyContent: 'center', left: 0, position: 'fixed', right: 0, top: 0, zIndex: 1200 }; }; var Positioner = function Positioner(props) { var children = props.children, getStyles = props.getStyles, innerProps = props.innerProps, isFullscreen = props.isFullscreen; return glam__default['default']( Div, _extends({ css: getStyles(componentBaseClassNames.Positioner, props), className: className(componentBaseClassNames.Positioner, { isFullscreen: isFullscreen }) }, innerProps), children ); }; // ============================== // Dialog // ============================== var dialogCSS = function dialogCSS() { return { width: '100%' }; }; var Dialog = function Dialog(props) { var children = props.children, getStyles = props.getStyles, innerProps = props.innerProps, isFullscreen = props.isFullscreen, removeFocusOn = props.removeFocusOn; return removeFocusOn ? glam__default['default']( Div, _extends({ css: getStyles('dialog', props), className: className('dialog', { isFullscreen: isFullscreen }) }, innerProps), children ) : glam__default['default']( reactFocusOn.FocusOn, null, glam__default['default']( Div, _extends({ css: getStyles('dialog', props), className: className('dialog', { isFullscreen: isFullscreen }) }, innerProps), children ) ); }; function getSource(_ref) { var data = _ref.data, isFullscreen = _ref.isFullscreen; var _data$source = data.source, source = _data$source === undefined ? data.src : _data$source; if (typeof source === 'string') return source; return isFullscreen ? source.fullscreen : source.regular; } var viewCSS = function viewCSS() { return { lineHeight: 0, position: 'relative', textAlign: 'center' }; }; var viewBaseClassName = componentBaseClassNames.View; var View = function View(props) { var data = props.data, formatters = props.formatters, getStyles = props.getStyles, index = props.index, isFullscreen = props.isFullscreen, isModal = props.isModal; var innerProps = { alt: formatters.getAltText({ data: data, index: index }), src: getSource({ data: data, isFullscreen: isFullscreen }) }; return glam__default['default']( Div, { css: getStyles(viewBaseClassName, props), className: className(viewBaseClassName, { isFullscreen: isFullscreen, isModal: isModal }) }, glam__default['default'](Img, _extends({}, innerProps, { className: className('view-image', { isFullscreen: isFullscreen, isModal: isModal }), css: { height: 'auto', maxHeight: '100vh', maxWidth: '100%', userSelect: 'none' } })) ); }; var carouselComponents = { Container: Container, Footer: Footer, FooterCaption: FooterCaption, FooterCount: FooterCount, Header: Header, HeaderClose: HeaderClose, HeaderFullscreen: HeaderFullscreen, Navigation: Navigation, NavigationPrev: NavigationPrev, NavigationNext: NavigationNext, View: View }; var defaultCarouselComponents = function defaultCarouselComponents(providedComponents) { return _extends({}, carouselComponents, providedComponents); }; // ============================== // Modal // ============================== var modalComponents = { Blanket: Blanket, Positioner: Positioner, Dialog: Dialog }; var defaultModalComponents = function defaultModalComponents(providedComponents) { return _extends({}, modalComponents, providedComponents); }; var defaultCarouselStyles = { container: containerCSS, footer: footerCSS, footerCaption: footerCaptionCSS, footerCount: footerCountCSS, header: headerCSS, headerClose: headerCloseCSS, headerFullscreen: headerFullscreenCSS, navigation: navigationCSS, navigationPrev: navigationPrevCSS, navigationNext: navigationNextCSS, view: viewCSS }; var defaultModalStyles = { blanket: blanketCSS, dialog: dialogCSS, positioner: positionerCSS // Merge Utility // Allows consumers to extend a base Carousel or Modal with additional styles }; var easing = 'cubic-bezier(0.23, 1, 0.32, 1)'; // easeOutQuint var verticalOffset = 40; // ============================== // Fade // ============================== var Fade = function Fade(_ref) { var Tag = _ref.component, onEntered = _ref.onEntered, onExited = _ref.onExited, inProp = _ref.in, originalProps = _ref.innerProps, props = objectWithoutProperties(_ref, ['component', 'onEntered', 'onExited', 'in', 'innerProps']); var enter = 300; var exit = 500; var fadeStyle = { transition: 'opacity 200ms', opacity: 0 }; var fadeTransition = { entering: { opacity: 0 }, entered: { opacity: 1 }, exiting: { opacity: 0, transitionDuration: exit + 'ms' } }; return React__default['default'].createElement( reactTransitionGroup.Transition, { appear: true, mountOnEnter: true, unmountOnExit: true, onEntered: onEntered, onExited: onExited, key: 'fade', 'in': inProp, timeout: { enter: enter, exit: exit } }, function (status) { var innerProps = _extends({}, originalProps, { style: _extends({}, fadeStyle, fadeTransition[status]) }); if (status === 'exited') return null; return React__default['default'].createElement(Tag, _extends({ innerProps: innerProps }, props)); } ); }; var SlideUp = function SlideUp(_ref2) { var Tag = _ref2.component, onEntered = _ref2.onEntered, onExited = _ref2.onExited, inProp = _ref2.in, originalProps = _ref2.innerProps, props = objectWithoutProperties(_ref2, ['component', 'onEntered', 'onExited', 'in', 'innerProps']); var enter = 300; var exit = 500; var restingTransform = 'translate3d(0, 0, 0)'; var slideStyle = { transition: 'transform ' + enter + 'ms ' + easing + ', opacity ' + enter + 'ms ' + easing, transform: restingTransform }; var slideTransition = { entering: { opacity: 0, transform: 'translate3d(0, ' + verticalOffset + 'px, 0) scale(0.9)' }, entered: { opacity: 1, transform: restingTransform }, exiting: { opacity: 0, transform: 'translate3d(0, ' + verticalOffset + 'px, 0) scale(0.9)', transition: 'transform ' + exit + 'ms ' + easing + ', opacity ' + exit + 'ms ' + easing } }; return React__default['default'].createElement( reactTransitionGroup.Transition, { appear: true, 'in': inProp, mountOnEnter: true, onEntered: onEntered, onExited: onExited, timeout: { enter: enter, exit: exit }, unmountOnExit: true }, function (status) { if (status === 'exited') return null; var innerProps = _extends({}, originalProps, { style: _extends({}, slideStyle, slideTransition[status]) }); return React__default['default'].createElement(Tag, _extends({ innerProps: innerProps }, props)); } ); }; var defaultProps = { allowFullscreen: !isTouch(), closeOnBackdropClick: true, closeOnEsc: true, preventScroll: true, styles: {} /** Classes that when clicked on, close the backdrop */ };var backdropClassNames = new Set([componentBaseClassNames.View, componentBaseClassNames.Header, componentBaseClassNames.Footer, componentBaseClassNames.Track, componentBaseClassNames.Positioner].map(className)); var Modal = function (_Component) { inherits(Modal, _Component); // TODO function Modal(props) { classCallCheck(this, Modal); var _this = possibleConstructorReturn(this, (Modal.__proto__ || Object.getPrototypeOf(Modal)).call(this, props)); _initialiseProps.call(_this); _this.cacheComponents(props.components); _this.state = { isFullscreen: false, isClosing: false }; return _this; } createClass(Modal, [{ key: 'componentDidUpdate', value: function componentDidUpdate(prevProps) { if (prevProps.components !== this.props.components) { this.cacheComponents(prevProps.components); } } // emulate `componentDidMount` & `componentWillUnmount` // called on complete of enter & exit transitions respectively }, { key: 'getCommonProps', value: function getCommonProps() { var isFullscreen = this.state.isFullscreen; return { getStyles: this.getStyles, isFullscreen: isFullscreen, modalProps: this.props }; } }, { key: 'render', value: function render() { var _components = this.components, Blanket = _components.Blanket, Positioner = _components.Positioner, Dialog = _components.Dialog; var _props = this.props, allowFullscreen = _props.allowFullscreen, children = _props.children; var isFullscreen = this.state.isFullscreen; var commonProps = this.commonProps = this.getCommonProps(); // $FlowFixMe var transitionIn = this.props.in; // forward props to modal for use in internal components var modalProps = { allowFullscreen: allowFullscreen, isFullscreen: isFullscreen, onClose: this.handleClose, preventScroll: this.preventScroll, toggleFullscreen: this.toggleFullscreen // augment user carousel with modal props // $FlowFixMe };var carouselComponent = React.cloneElement(children, { isModal: true, modalProps: modalProps }); return glam__default['default']( reactFullScreen.FullScreen, { handle: { active: isFullscreen }, onChange: this.handleFullscreenChange }, glam__default['default'](Fade, _extends({}, commonProps, { component: Blanket, 'in': transitionIn })), glam__default['default']( SlideUp, _extends({}, commonProps, { component: Positioner, 'in': transitionIn, innerProps: { onClick: this.state.isClosing ? null : this.handleBackdropClick }, onEntered: this.modalDidMount, onExited: this.modalWillUnmount }), glam__default['default']( Dialog, _extends({ removeFocusOn: this.state.isClosing }, commonProps), carouselComponent ) ) ); } }]); return Modal; }(React.Component); Modal.defaultProps = defaultProps; var _initialiseProps = function _initialiseProps() { var _this2 = this; this.modalDidMount = function () { document.addEventListener('keyup', _this2.handleKeyUp); focusStore__default['default'].storeFocus(); }; this.modalWillUnmount = function () { document.removeEventListener('keyup', _this2.handleKeyUp); focusStore__default['default'].restoreFocus(); _this2.setState({ isClosing: false }); }; this.cacheComponents = function (comps) { _this2.components = defaultModalComponents(comps); }; this.handleFullscreenChange = function (isFullscreen) { _this2.setState({ isFullscreen: isFullscreen }); }; this.handleKeyUp = function (event) { var _props2 = _this2.props, allowFullscreen = _props2.allowFullscreen, closeOnEsc = _props2.closeOnEsc; var isFullscreen = _this2.state.isFullscreen; var allowClose = event.key === 'Escape' && closeOnEsc && !isFullscreen; // toggle fullscreen if (allowFullscreen && event.key === 'f') { _this2.toggleFullscreen(); } // close on escape when not fullscreen if (allowClose) _this2.handleClose(event); }; this.handleBackdropClick = function (event) { var hasBackdropClassName = false; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = event.target.classList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var targetClass = _step.value; if (backdropClassNames.has(targetClass)) { hasBackdropClassName = true; } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } if (!hasBackdropClassName || !_this2.props.closeOnBackdropClick) { return; } _this2.handleClose(event); }; this.toggleFullscreen = function () { _this2.setState(function (state) { return { isFullscreen: !state.isFullscreen }; }); }; this.handleClose = function (event) { var onClose = _this2.props.onClose; var _state = _this2.state, isFullscreen = _state.isFullscreen, isClosing = _state.isClosing; if (!isClosing) { _this2.setState({ isClosing: true }); // force exit fullscreen mode on close if (isFullscreen) { _this2.toggleFullscreen(); } // call the consumer's onClose func onClose(event); } }; this.getStyles = function (key, props) { var base = defaultModalStyles[key](props); base.boxSizing = 'border-box'; var custom = _this2.props.styles[key]; return custom ? custom(base, props) : base; }; }; // ============================== // Navigation // ============================== /* ARIA label for the next button */ // NOTE: props aren't used by default for some getters but consumers may need // them, this needs to be reflected in the flow type. /* eslint-disable no-unused-vars */ function getNextLabel(_ref) { var currentIndex = _ref.currentIndex, views = _ref.views; return 'Show slide ' + (currentIndex + 2) + ' of ' + views.length; } /* ARIA label for the previous button */ function getPrevLabel(_ref2) { var currentIndex = _ref2.currentIndex, views = _ref2.views; return 'Show slide ' + currentIndex + ' of ' + views.length; } /* HTML title for the next button */ function getNextTitle(props) { return 'Next (right arrow)'; } /* HTML title for the previous button */ function getPrevTitle(props) { return 'Previous (left arrow)'; } // ============================== // Header // ============================== /* ARIA label for the close button */ function getCloseLabel(props) { return 'Close (esc)'; } /* ARIA label for the fullscreen button */ function getFullscreenLabel(_ref3) { var isFullscreen = _ref3.isFullscreen; return isFullscreen ? 'Exit fullscreen (f)' : 'Enter fullscreen (f)'; } // ============================== // View // ============================== /* alt text for each image in the carousel */ function getAltText(_ref4) { var data = _ref4.data, index = _ref4.index; if (data.alt) { if (typeof data.alt !== 'string') { console.error('Image ' + (index + 1) + ' had a non-string alt property, which will probably render incorrectly.\nInstead of a plain string it was ', data.alt); } return data.alt; } if (data.caption) { if (typeof data.caption !== 'string') { console.warn('Image ' + (index + 1) + ' has a non-string caption, but no alt\xA0value provided. This will probably make the alt prop unintelligible for screen readers. Is this intentional?'); } return data.caption; } return 'Image ' + (index + 1); } // ============================== // Exports // ============================== var formatters = { getAltText: getAltText, getNextLabel: getNextLabel, getPrevLabel: getPrevLabel, getNextTitle: getNextTitle, getPrevTitle: getPrevTitle, getCloseLabel: getCloseLabel, getFullscreenLabel: getFullscreenLabel }; var viewPagerStyles = { flex: '1 1 auto', position: 'relative' }; var frameStyles = { outline: 0 }; var defaultProps$1 = { currentIndex: 0, formatters: formatters, hideControlsWhenIdle: 3000, showNavigationOnTouchDevice: false, styles: {}, trackProps: { instant: !isTouch(), swipe: 'touch' } }; var trackBaseClassName = componentBaseClassNames.Track; var Carousel = function (_Component) { inherits(Carousel, _Component); function Carousel(props) { classCallCheck(this, Carousel); var _this = possibleConstructorReturn(this, (Carousel.__proto__ || Object.getPrototypeOf(Carousel)).call(this, props)); _initialiseProps$1.call(_this); _this.cacheComponents(props.components); _this.state = { currentIndex: props.currentIndex, interactionIsIdle: isTouch() }; return _this; } // TODO createClass(Carousel, [{ key: 'componentDidMount', value: function componentDidMount() { var _props = this.props, hideControlsWhenIdle = _props.hideControlsWhenIdle, modalProps = _props.modalProps; var isModal = Boolean(modalProps); this.mounted = true; if (hideControlsWhenIdle && this.container) { this.container.addEventListener('mousedown', this.handleMouseActivity); this.container.addEventListener('mousemove', this.handleMouseActivity); this.container.addEventListener('touchmove', this.handleMouseActivity); } if (isModal) { this.focusViewFrame(); } } }, { key: 'componentDidUpdate', value: function componentDidUpdate(prevProps) { if (prevProps.components !== this.props.components) { this.cacheComponents(prevProps.components); } if (this.props.currentIndex !== prevProps.currentIndex) { this.setState({ currentIndex: this.props.currentIndex }); } } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { this.mounted = false; if (this.props.hideControlsWhenIdle && this.container) { this.container.removeEventListener('mousedown', this.handleMouseActivity); this.container.removeEventListener('mousemove', this.handleMouseActivity); this.container.removeEventListener('touchmove', this.handleMouseActivity); this.handleMouseActivity.cancel(); } } // ============================== // Refs // ============================== // ============================== // Utilities // ============================== // combine defaultProps with consumer props to maintain expected behaviour // combine defaultProps with consumer props to maintain expected behaviour // ============================== // Handlers // ============================== // ============================== // Renderers // ============================== }, { key: 'getCommonProps', value: function getCommonProps() { var _props2 = this.props, frameProps = _props2.frameProps, trackProps = _props2.trackProps, modalProps = _props2.modalProps, views = _props2.views, showNavigationOnTouchDevice = _props2.showNavigationOnTouchDevice; var isModal = Boolean(modalProps); var isFullscreen = Boolean(modalProps && modalProps.isFullscreen); var _state = this.state, currentIndex = _state.currentIndex, interactionIsIdle = _state.interactionIsIdle; var currentView = this.getViewData(); return { carouselProps: this.props, currentIndex: currentIndex, currentView: currentView, formatters: this.props.formatters, frameProps: frameProps, getStyles: this.getStyles, showNavigationOnTouchDevice: showNavigationOnTouchDevice, isFullscreen: isFullscreen, isModal: isModal, modalProps: modalProps, interactionIsIdle: interactionIsIdle, trackProps: trackProps, views: views }; } }, { key: 'render', value: function render() { var _components = this.components, Container = _components.Container, View = _components.View; var currentIndex = this.state.currentIndex; var _props3 = this.props, frameProps = _props3.frameProps, views = _props3.views; var commonProps = this.commonProps = this.getCommonProps(); return glam__default['default']( Container, _extends({}, commonProps, { innerProps: { innerRef: this.getContainer } }), this.renderHeader(), glam__default['default']( reactViewPager.ViewPager, { tag: 'main', style: viewPagerStyles, className: className('pager') }, glam__default['default']( reactViewPager.Frame, _extends({}, frameProps, { ref: this.getFrame, className: className('frame'), style: frameStyles, tabIndex: '-1' }), glam__default['default']( reactViewPager.Track, _extends({}, this.getTrackProps(this.props), { style: { display: 'flex', alignItems: 'center' }, currentView: currentIndex, className: className(trackBaseClassName), onViewChange: this.handleViewChange, ref: this.getTrack }), views && views.map(function (data, index) { return glam__default['default']( reactViewPager.View, { className: className('view-wrapper'), key: index }, glam__default['default'](View, _extends({}, commonProps, { data: data, index: index })) ); }) ) ), this.renderNavigation() ), this.renderFooter() ); } }]); return Carousel; }(React.Component); Carousel.defaultProps = defaultProps$1; var _initialiseProps$1 = function _initialiseProps() { var _this2 = this; this.mounted = false; this.cacheComponents = function (comps) { _this2.components = defaultCarouselComponents(comps); }; this.getContainer = function (ref) { _this2.container = ref; }; this.getFooter = function (ref) { _this2.footer = ref; }; this.getFrame = function (ref) { _this2.frame = reactDom.findDOMNode(ref); }; this.getHeader = function (ref) { _this2.header = ref; }; this.getTrack = function (ref) { _this2.track = ref; }; this.hasPreviousView = function () { var trackProps = _this2.props.trackProps; var currentIndex = _this2.state.currentIndex; return trackProps.infinite || currentIndex !== 0; }; this.hasNextView = function () { var _props4 = _this2.props, trackProps = _props4.trackProps, views = _props4.views; var currentIndex = _this2.state.currentIndex; return trackProps.infinite || currentIndex !== views.length - 1; }; this.getStyles = function (key, props) { var base = defaultCarouselStyles[key](props); base.boxSizing = 'border-box'; var custom = _this2.props.styles[key]; return custom ? custom(base, props) : base; }; this.getTrackProps = function (props) { return _extends({}, defaultProps$1.trackProps, props.trackProps); }; this.getFormatters = function () { return _extends({}, defaultProps$1.formatters, _this2.props.formatters); }; this.getViewData = function () { var views = _this2.props.views; var currentIndex = _this2.state.currentIndex; return views[currentIndex]; }; this.focusViewFrame = function () { if (_this2.frame && document.activeElement !== _this2.frame) { _this2.frame.focus(); } }; this.prev = function (event) { event.stopPropagation(); _this2.track.prev(); _this2.focusViewFrame(); }; this.next = function (event) { event.stopPropagation(); _this2.track.next(); _this2.focusViewFrame(); }; this.handleMouseActivity = rafScheduler__default['default'](function () { clearTimeout(_this2.timer); if (_this2.state.interactionIsIdle) { _this2.setState({ interactionIsIdle: false }); } _this2.timer = setTimeout(function () { if (_this2.mounted) { _this2.setState({ interactionIsIdle: true }); } }, _this2.props.hideControlsWhenIdle); }); this.handleViewChange = function (indicies) { var trackProps = _this2.props.trackProps; // simplify by enforcing number var currentIndex = indicies[0]; _this2.setState({ currentIndex: currentIndex }); // call the consumer's onViewChange fn if (trackProps && trackProps.onViewChange) { trackProps.onViewChange(currentIndex); } }; this.renderNavigation = function () { var _getFormatters = _this2.getFormatters(), getNextLabel = _getFormatters.getNextLabel, getPrevLabel = _getFormatters.getPrevLabel, getNextTitle = _getFormatters.getNextTitle, getPrevTitle = _getFormatters.getPrevTitle; var _components2 = _this2.components, Navigation = _components2.Navigation, NavigationPrev = _components2.NavigationPrev, NavigationNext = _components2.NavigationNext; var commonProps = _this2.commonProps; var showPrev = _this2.hasPreviousView(); var showNext = _this2.hasNextView(); var showNav = (showPrev || showNext) && Navigation; return showNav ? glam__default['default']( Navigation, commonProps, showPrev && glam__default['default'](NavigationPrev, _extends({}, commonProps, { align: 'left', innerProps: { 'aria-label': getPrevLabel(commonProps), onClick: _this2.prev, title: getPrevTitle(commonProps) } })), showNext && glam__default['default'](NavigationNext, _extends({}, commonProps, { align: 'right', innerProps: { 'aria-label': getNextLabel(commonProps), onClick: _this2.next, title: getNextTitle(commonProps) } })) ) : null; }; this.renderFooter = function () { var _components3 = _this2.components, Footer = _components3.Footer, FooterCaption = _components3.FooterCaption, FooterCount = _components3.FooterCount; var commonProps = _this2.commonProps; return Footer ? glam__default['default'](Footer, _extends({}, commonProps, { components: { Caption: FooterCaption, Count: FooterCount }, innerProps: { innerRef: _this2.getFooter } })) : null; }; this.renderHeader = function () { var _components4 = _this2.components, Header = _components4.Header, HeaderClose = _components4.HeaderClose, HeaderFullscreen = _components4.HeaderFullscreen; var _getFormatters2 = _this2.getFormatters(), getCloseLabel = _getFormatters2.getCloseLabel, getFullscreenLabel = _getFormatters2.