UNPKG

material-ui

Version:

React Components that Implement Google's Material Design.

489 lines (427 loc) 16.7 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); var _simpleAssign = require('simple-assign'); var _simpleAssign2 = _interopRequireDefault(_simpleAssign); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _reactDom = require('react-dom'); var _reactDom2 = _interopRequireDefault(_reactDom); var _reactEventListener = require('react-event-listener'); var _reactEventListener2 = _interopRequireDefault(_reactEventListener); var _keycode = require('keycode'); var _keycode2 = _interopRequireDefault(_keycode); var _autoPrefix = require('../utils/autoPrefix'); var _autoPrefix2 = _interopRequireDefault(_autoPrefix); var _transitions = require('../styles/transitions'); var _transitions2 = _interopRequireDefault(_transitions); var _Overlay = require('../internal/Overlay'); var _Overlay2 = _interopRequireDefault(_Overlay); var _Paper = require('../Paper'); var _Paper2 = _interopRequireDefault(_Paper); var _propTypes3 = require('../utils/propTypes'); var _propTypes4 = _interopRequireDefault(_propTypes3); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var openNavEventHandler = null; var Drawer = function (_Component) { (0, _inherits3.default)(Drawer, _Component); function Drawer() { var _ref; var _temp, _this, _ret; (0, _classCallCheck3.default)(this, Drawer); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _ret = (_temp = (_this = (0, _possibleConstructorReturn3.default)(this, (_ref = Drawer.__proto__ || (0, _getPrototypeOf2.default)(Drawer)).call.apply(_ref, [this].concat(args))), _this), _this.handleClickOverlay = function (event) { event.preventDefault(); _this.close('clickaway'); }, _this.handleKeyUp = function (event) { if (_this.state.open && !_this.props.docked && (0, _keycode2.default)(event) === 'esc') { _this.close('escape'); } }, _this.onBodyTouchStart = function (event) { var swipeAreaWidth = _this.props.swipeAreaWidth; var touchStartX = _this.context.muiTheme.isRtl ? document.body.offsetWidth - event.touches[0].pageX : event.touches[0].pageX; var touchStartY = event.touches[0].pageY; // Open only if swiping from far left (or right) while closed if (swipeAreaWidth !== null && !_this.state.open) { if (_this.props.openSecondary) { // If openSecondary is true calculate from the far right if (touchStartX < document.body.offsetWidth - swipeAreaWidth) return; } else { // If openSecondary is false calculate from the far left if (touchStartX > swipeAreaWidth) return; } } if (!_this.state.open && (openNavEventHandler !== _this.onBodyTouchStart || _this.props.disableSwipeToOpen)) { return; } _this.maybeSwiping = true; _this.touchStartX = touchStartX; _this.touchStartY = touchStartY; document.body.addEventListener('touchmove', _this.onBodyTouchMove); document.body.addEventListener('touchend', _this.onBodyTouchEnd); document.body.addEventListener('touchcancel', _this.onBodyTouchEnd); }, _this.onBodyTouchMove = function (event) { var currentX = _this.context.muiTheme.isRtl ? document.body.offsetWidth - event.touches[0].pageX : event.touches[0].pageX; var currentY = event.touches[0].pageY; if (_this.state.swiping) { event.preventDefault(); _this.setPosition(_this.getTranslateX(currentX)); } else if (_this.maybeSwiping) { var dXAbs = Math.abs(currentX - _this.touchStartX); var dYAbs = Math.abs(currentY - _this.touchStartY); // If the user has moved his thumb ten pixels in either direction, // we can safely make an assumption about whether he was intending // to swipe or scroll. var threshold = 10; if (dXAbs > threshold && dYAbs <= threshold) { _this.swipeStartX = currentX; _this.setState({ swiping: _this.state.open ? 'closing' : 'opening' }); _this.setPosition(_this.getTranslateX(currentX)); } else if (dXAbs <= threshold && dYAbs > threshold) { _this.onBodyTouchEnd(); } } }, _this.onBodyTouchEnd = function (event) { if (_this.state.swiping) { var currentX = _this.context.muiTheme.isRtl ? document.body.offsetWidth - event.changedTouches[0].pageX : event.changedTouches[0].pageX; var translateRatio = _this.getTranslateX(currentX) / _this.getMaxTranslateX(); _this.maybeSwiping = false; var swiping = _this.state.swiping; _this.setState({ swiping: null }); // We have to open or close after setting swiping to null, // because only then CSS transition is enabled. if (translateRatio > 0.5) { if (swiping === 'opening') { _this.setPosition(_this.getMaxTranslateX()); } else { _this.close('swipe'); } } else { if (swiping === 'opening') { _this.open('swipe'); } else { _this.setPosition(0); } } } else { _this.maybeSwiping = false; } _this.removeBodyTouchListeners(); }, _temp), (0, _possibleConstructorReturn3.default)(_this, _ret); } (0, _createClass3.default)(Drawer, [{ key: 'componentWillMount', value: function componentWillMount() { this.maybeSwiping = false; this.touchStartX = null; this.touchStartY = null; this.swipeStartX = null; this.setState({ open: this.props.open !== null ? this.props.open : this.props.docked, swiping: null }); } }, { key: 'componentDidMount', value: function componentDidMount() { this.enableSwipeHandling(); } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { // If controlled then the open prop takes precedence. if (nextProps.open !== null) { this.setState({ open: nextProps.open }); // Otherwise, if docked is changed, change the open state for when uncontrolled. } else if (this.props.docked !== nextProps.docked) { this.setState({ open: nextProps.docked }); } } }, { key: 'componentDidUpdate', value: function componentDidUpdate() { this.enableSwipeHandling(); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { this.disableSwipeHandling(); this.removeBodyTouchListeners(); } }, { key: 'getStyles', value: function getStyles() { var muiTheme = this.context.muiTheme; var theme = muiTheme.drawer; var x = this.getTranslateMultiplier() * (this.state.open ? 0 : this.getMaxTranslateX()); var styles = { root: { height: '100%', width: this.getTranslatedWidth() || theme.width, position: 'fixed', zIndex: muiTheme.zIndex.drawer, left: 0, top: 0, transform: 'translate(' + x + 'px, 0)', transition: !this.state.swiping && _transitions2.default.easeOut(null, 'transform', null), backgroundColor: theme.color, overflow: 'auto', WebkitOverflowScrolling: 'touch' // iOS momentum scrolling }, overlay: { zIndex: muiTheme.zIndex.drawerOverlay, pointerEvents: this.state.open ? 'auto' : 'none' // Bypass mouse events when left nav is closing. }, rootWhenOpenRight: { left: 'auto', right: 0 } }; return styles; } }, { key: 'shouldShow', value: function shouldShow() { return this.state.open || !!this.state.swiping; // component is swiping } }, { key: 'close', value: function close(reason) { if (this.props.open === null) this.setState({ open: false }); if (this.props.onRequestChange) this.props.onRequestChange(false, reason); return this; } }, { key: 'open', value: function open(reason) { if (this.props.open === null) this.setState({ open: true }); if (this.props.onRequestChange) this.props.onRequestChange(true, reason); return this; } }, { key: 'getTranslatedWidth', value: function getTranslatedWidth() { if (typeof this.props.width === 'string') { if (!/^\d+(\.\d+)?%$/.test(this.props.width)) { throw new Error('Not a valid percentage format.'); } var width = parseFloat(this.props.width) / 100.0; // We are doing our best on the Server to render a consistent UI, hence the // default value of 10000 return typeof window !== 'undefined' ? width * window.innerWidth : 10000; } else { return this.props.width; } } }, { key: 'getMaxTranslateX', value: function getMaxTranslateX() { var width = this.getTranslatedWidth() || this.context.muiTheme.drawer.width; return width + 10; } }, { key: 'getTranslateMultiplier', value: function getTranslateMultiplier() { return this.props.openSecondary ? 1 : -1; } }, { key: 'enableSwipeHandling', value: function enableSwipeHandling() { if (!this.props.docked) { document.body.addEventListener('touchstart', this.onBodyTouchStart); if (!openNavEventHandler) { openNavEventHandler = this.onBodyTouchStart; } } else { this.disableSwipeHandling(); } } }, { key: 'disableSwipeHandling', value: function disableSwipeHandling() { document.body.removeEventListener('touchstart', this.onBodyTouchStart); if (openNavEventHandler === this.onBodyTouchStart) { openNavEventHandler = null; } } }, { key: 'removeBodyTouchListeners', value: function removeBodyTouchListeners() { document.body.removeEventListener('touchmove', this.onBodyTouchMove); document.body.removeEventListener('touchend', this.onBodyTouchEnd); document.body.removeEventListener('touchcancel', this.onBodyTouchEnd); } }, { key: 'setPosition', value: function setPosition(translateX) { var rtlTranslateMultiplier = this.context.muiTheme.isRtl ? -1 : 1; var drawer = _reactDom2.default.findDOMNode(this.refs.clickAwayableElement); var transformCSS = 'translate(' + this.getTranslateMultiplier() * rtlTranslateMultiplier * translateX + 'px, 0)'; this.refs.overlay.setOpacity(1 - translateX / this.getMaxTranslateX()); _autoPrefix2.default.set(drawer.style, 'transform', transformCSS); } }, { key: 'getTranslateX', value: function getTranslateX(currentX) { return Math.min(Math.max(this.state.swiping === 'closing' ? this.getTranslateMultiplier() * (currentX - this.swipeStartX) : this.getMaxTranslateX() - this.getTranslateMultiplier() * (this.swipeStartX - currentX), 0), this.getMaxTranslateX()); } }, { key: 'render', value: function render() { var _props = this.props, children = _props.children, className = _props.className, containerClassName = _props.containerClassName, containerStyle = _props.containerStyle, docked = _props.docked, openSecondary = _props.openSecondary, overlayClassName = _props.overlayClassName, overlayStyle = _props.overlayStyle, style = _props.style, zDepth = _props.zDepth; var styles = this.getStyles(); var overlay = void 0; if (!docked) { overlay = _react2.default.createElement(_Overlay2.default, { ref: 'overlay', show: this.shouldShow(), className: overlayClassName, style: (0, _simpleAssign2.default)(styles.overlay, overlayStyle), transitionEnabled: !this.state.swiping, onClick: this.handleClickOverlay }); } return _react2.default.createElement( 'div', { className: className, style: style }, _react2.default.createElement(_reactEventListener2.default, { target: 'window', onKeyUp: this.handleKeyUp }), overlay, _react2.default.createElement( _Paper2.default, { ref: 'clickAwayableElement', zDepth: zDepth, rounded: false, transitionEnabled: !this.state.swiping, className: containerClassName, style: (0, _simpleAssign2.default)(styles.root, openSecondary && styles.rootWhenOpenRight, containerStyle) }, children ) ); } }]); return Drawer; }(_react.Component); Drawer.defaultProps = { disableSwipeToOpen: false, docked: true, open: null, openSecondary: false, swipeAreaWidth: 30, width: null, zDepth: 2 }; Drawer.contextTypes = { muiTheme: _propTypes2.default.object.isRequired }; Drawer.propTypes = process.env.NODE_ENV !== "production" ? { /** * The contents of the `Drawer` */ children: _propTypes2.default.node, /** * The CSS class name of the root element. */ className: _propTypes2.default.string, /** * The CSS class name of the container element. */ containerClassName: _propTypes2.default.string, /** * Override the inline-styles of the container element. */ containerStyle: _propTypes2.default.object, /** * If true, swiping sideways when the `Drawer` is closed will not open it. */ disableSwipeToOpen: _propTypes2.default.bool, /** * If true, the `Drawer` will be docked. In this state, the overlay won't show and * clicking on a menu item will not close the `Drawer`. */ docked: _propTypes2.default.bool, /** * Callback function fired when the `open` state of the `Drawer` is requested to be changed. * * @param {boolean} open If true, the `Drawer` was requested to be opened. * @param {string} reason The reason for the open or close request. Possible values are * 'swipe' for open requests; 'clickaway' (on overlay clicks), * 'escape' (on escape key press), and 'swipe' for close requests. */ onRequestChange: _propTypes2.default.func, /** * If true, the `Drawer` is opened. Providing a value will turn the `Drawer` * into a controlled component. */ open: _propTypes2.default.bool, /** * If true, the `Drawer` is positioned to open from the opposite side. */ openSecondary: _propTypes2.default.bool, /** * The CSS class name to add to the `Overlay` component that is rendered behind the `Drawer`. */ overlayClassName: _propTypes2.default.string, /** * Override the inline-styles of the `Overlay` component that is rendered behind the `Drawer`. */ overlayStyle: _propTypes2.default.object, /** * Override the inline-styles of the root element. */ style: _propTypes2.default.object, /** * The width of the left most (or right most) area in pixels where the `Drawer` can be * swiped open from. Setting this to `null` spans that area to the entire page * (**CAUTION!** Setting this property to `null` might cause issues with sliders and * swipeable `Tabs`: use at your own risk). */ swipeAreaWidth: _propTypes2.default.number, /** * The width of the `Drawer` in pixels or percentage in string format ex. `50%` to fill * half of the window or `100%` and so on. Defaults to using the values from theme. */ width: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.number]), /** * The zDepth of the `Drawer`. */ zDepth: _propTypes4.default.zDepth } : {}; exports.default = Drawer;