UNPKG

twreporter-react

Version:

React-Redux site for The Reporter Foundation in Taiwan

283 lines (227 loc) 9.38 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _reactDom = require('react-dom'); var _reactDom2 = _interopRequireDefault(_reactDom); var _radium = require('radium'); var _radium2 = _interopRequireDefault(_radium); var _baseStyles = require('./baseStyles'); var _baseStyles2 = _interopRequireDefault(_baseStyles); var _BurgerIcon = require('./BurgerIcon'); var _BurgerIcon2 = _interopRequireDefault(_BurgerIcon); var _CrossIcon = require('./CrossIcon'); var _CrossIcon2 = _interopRequireDefault(_CrossIcon); exports['default'] = function (styles) { return (0, _radium2['default'])(_react2['default'].createClass({ propTypes: { customBurgerIcon: _react2['default'].PropTypes.string, customCrossIcon: _react2['default'].PropTypes.string, id: _react2['default'].PropTypes.string, isOpen: _react2['default'].PropTypes.bool, onStateChange: _react2['default'].PropTypes.func, outerContainerId: _react2['default'].PropTypes.string, pageWrapId: _react2['default'].PropTypes.string, right: _react2['default'].PropTypes.bool, styles: _react2['default'].PropTypes.object, width: _react2['default'].PropTypes.number }, toggleMenu: function toggleMenu() { // Order important: handle wrappers before setting sidebar state. this.applyWrapperStyles(); var newState = { isOpen: !this.state.isOpen }; this.setState(newState, this.props.onStateChange.bind(null, newState)); }, // Applies component-specific styles to external wrapper elements. applyWrapperStyles: function applyWrapperStyles() { if (styles.pageWrap && this.props.pageWrapId) { this.handleExternalWrapper(this.props.pageWrapId, styles.pageWrap, true); } if (styles.outerContainer && this.props.outerContainerId) { this.handleExternalWrapper(this.props.outerContainerId, styles.outerContainer, true); } }, // Removes component-specific styles applied to external wrapper elements. clearWrapperStyles: function clearWrapperStyles() { if (styles.pageWrap && this.props.pageWrapId) { this.handleExternalWrapper(this.props.pageWrapId, styles.pageWrap, false); } if (styles.outerContainer && this.props.outerContainerId) { this.handleExternalWrapper(this.props.outerContainerId, styles.outerContainer, false); } }, // Sets or unsets styles on DOM elements outside the menu component. // This is necessary for correct page interaction with some of the menus. // Throws and returns if the required external elements don't exist, // which means any external page animations won't be applied. handleExternalWrapper: function handleExternalWrapper(id, wrapperStyles, set) { var html = document.querySelector('html'); var body = document.querySelector('body'); var wrapper = document.getElementById(id); if (!wrapper) { console.error("Element with ID '" + id + "' not found"); return; } wrapperStyles = wrapperStyles(this.state.isOpen, this.props.width, this.props.right); for (var prop in wrapperStyles) { if (wrapperStyles.hasOwnProperty(prop)) { wrapper.style[prop] = set ? wrapperStyles[prop] : ''; } } // Prevent any horizontal scroll. [html, body].forEach(function (element) { element.style['overflow-x'] = set ? 'hidden' : ''; }); }, // Builds styles incrementally for a given element. getStyles: function getStyles(el, index) { var propName = 'bm' + el.replace(el.charAt(0), el.charAt(0).toUpperCase()); // Set base styles. var output = _baseStyles2['default'][el] ? [_baseStyles2['default'][el](this.state.isOpen, this.props.width, this.props.right)] : []; // Add animation-specific styles. if (styles[el]) { output.push(styles[el](this.state.isOpen, this.props.width, this.props.right, index + 1)); } // Add custom styles. if (this.props.styles[propName]) { output.push(this.props.styles[propName]); } return output; }, listenForClose: function listenForClose(e) { e = e || window.event; if (this.state.isOpen && (e.key === 'Escape' || e.keyCode === 27)) { this.toggleMenu(); } }, getDefaultProps: function getDefaultProps() { return { customBurgerIcon: '', customCrossIcon: '', id: '', isOpen: false, onStateChange: function onStateChange() {}, outerContainerId: '', pageWrapId: '', right: false, styles: {}, width: 300 }; }, getInitialState: function getInitialState() { return { isOpen: false }; }, componentWillMount: function componentWillMount() { if (!styles) { throw new Error('No styles supplied'); } // Warn if the selected menu requires external wrapper elements // but none were supplied. if (styles.pageWrap && !this.props.pageWrapId) { console.warn('No pageWrapId supplied'); } if (styles.outerContainer && !this.props.outerContainerId) { console.warn('No outerContainerId supplied'); } // Allow initial open state to be set by props. if (this.props.isOpen !== this.state.isOpen) { this.toggleMenu(); } }, componentDidMount: function componentDidMount() { window.onkeydown = this.listenForClose; }, componentWillUnmount: function componentWillUnmount() { window.onkeydown = null; this.clearWrapperStyles(); }, componentDidUpdate: function componentDidUpdate() { var _this = this; if (styles.svg && this.isMounted()) { (function () { // Snap.svg workaround for Webpack using imports-loader (https://github.com/webpack/imports-loader). var snap = undefined; try { snap = require('imports?this=>window,fix=>module.exports=0!snapsvg/dist/snap.svg.js'); } catch (e) { snap = require('snapsvg'); } var morphShape = _reactDom2['default'].findDOMNode(_this, 'bm-morph-shape'); var s = snap(morphShape); var path = s.select('path'); if (_this.state.isOpen) { // Animate SVG path. styles.svg.animate(path); } else { // Reset path (timeout ensures animation happens off screen). setTimeout(function () { path.attr('d', styles.svg.pathInitial); }, 300); } })(); } }, componentWillReceiveProps: function componentWillReceiveProps(nextProps) { // Allow open state to be controlled by props. if (nextProps.isOpen !== this.props.isOpen && nextProps.isOpen !== this.state.isOpen) { this.toggleMenu(); } }, render: function render() { var _this2 = this; var items = undefined, svg = undefined; // Add styles to user-defined menu items. if (this.props.children) { items = _react2['default'].Children.map(this.props.children, function (item, index) { var extraProps = { key: index, style: _this2.getStyles('item', index) }; return _react2['default'].cloneElement(item, extraProps); }); } // Add a morph shape for animations that use SVG. if (styles.svg) { svg = _react2['default'].createElement( 'div', { className: 'bm-morph-shape', style: this.getStyles('morphShape') }, _react2['default'].createElement( 'svg', { xmlns: 'http://www.w3.org/2000/svg', width: '100%', height: '100%', viewBox: '0 0 100 800', preserveAspectRatio: 'none' }, _react2['default'].createElement('path', { d: styles.svg.pathInitial }) ) ); } return _react2['default'].createElement( 'div', null, _react2['default'].createElement('div', { className: 'bm-overlay', onClick: this.toggleMenu, style: this.getStyles('overlay') }), _react2['default'].createElement( 'div', { id: this.props.id, className: "bm-menu-wrap", style: this.getStyles('menuWrap') }, svg, _react2['default'].createElement( 'div', { className: 'bm-menu', style: this.getStyles('menu') }, _react2['default'].createElement( 'nav', { className: 'bm-item-list', style: this.getStyles('itemList') }, items ) ), _react2['default'].createElement( 'div', { style: this.getStyles('closeButton') }, _react2['default'].createElement(_CrossIcon2['default'], { onClick: this.toggleMenu, styles: this.props.styles, image: this.props.customCrossIcon }) ) ), _react2['default'].createElement(_BurgerIcon2['default'], { onClick: this.toggleMenu, styles: this.props.styles, image: this.props.customBurgerIcon }) ); } })); }; module.exports = exports['default'];