diff --git a/src/dialog.jsx b/src/dialog.jsx index 9dcaa130e70fad..6de0e342a31e2b 100644 --- a/src/dialog.jsx +++ b/src/dialog.jsx @@ -462,7 +462,7 @@ const Dialog = React.createClass({ render() { return ( - + ); }, @@ -523,10 +523,6 @@ const Dialog = React.createClass({ }, this._onDismiss); }, - layerWillUnmount() { - if (this.props.onDismiss) this.props.onDismiss(); - }, - isOpen() { return this.state.openImmediately; }, diff --git a/src/popover/popover.jsx b/src/popover/popover.jsx index 6bf77fd86ab80c..da58ad956fba63 100644 --- a/src/popover/popover.jsx +++ b/src/popover/popover.jsx @@ -49,7 +49,7 @@ const Popover = React.createClass({ vertical: 'top', horizontal: 'left', }, - useLayerForClickAway:true, + useLayerForClickAway: true, zDepth: 1, }; }, @@ -70,6 +70,7 @@ const Popover = React.createClass({ getChildContext() { return { + muiTheme: this.state.muiTheme, }; }, @@ -80,20 +81,35 @@ const Popover = React.createClass({ componentWillReceiveProps(nextProps, nextContext) { let newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme; - if (nextProps.open !== this.state.open) { + + if (this._isAnimating === true || nextProps.open !== this.state.open) { if (nextProps.open) { this.anchorEl = nextProps.anchorEl || this.props.anchorEl; this.setState({ open: true, muiTheme: newMuiTheme, - }); - } else { - this.setState({ - open: false, - muiTheme: newMuiTheme, }, () => { - this._animateClose(); + this._animate(true); }); + } else { + if (nextProps.animated) { + this._animate(false); + this._isAnimating = true; + this._timeout = setTimeout(() => { + if (this.isMounted()) { + this._isAnimating = false; + this.setState({ + open: false, + muiTheme: newMuiTheme, + }); + } + }, 500); + } else { + this.setState({ + open: false, + muiTheme: newMuiTheme, + }); + } } } }, @@ -102,22 +118,18 @@ const Popover = React.createClass({ this.setPlacement(); }, - componentWillUnmount() { - if (this.state.open) { - this._animateClose(); - } - }, - render() { - return ; + return ( + + ); }, renderLayer() { - let { + const { animated, targetOrigin, className, @@ -125,35 +137,34 @@ const Popover = React.createClass({ } = this.props; const anchorEl = this.props.anchorEl || this.anchorEl; - let anchor = this.getAnchorPosition(anchorEl); - let horizontal = targetOrigin.horizontal.replace('middle', 'vertical'); + const anchor = this.getAnchorPosition(anchorEl); + const horizontal = targetOrigin.horizontal.replace('middle', 'vertical'); - let wrapperStyle = { + const wrapperStyle = this.mergeAndPrefix({ position: 'fixed', top: anchor.top, left: anchor.left, zIndex: this.state.muiTheme.zIndex.popover, - opacity:1, - overflow:'auto', - maxHeight:'100%', - transform:'scale(0,0)', + opacity: 1, + overflow: 'auto', + maxHeight: '100%', + transform: 'scale(0,0)', transformOrigin: `${horizontal} ${targetOrigin.vertical}`, transition: animated ? Transitions.easeOut('500ms', ['transform', 'opacity']) : null, - }; - wrapperStyle = this.mergeAndPrefix(wrapperStyle, this.props.style); + }, this.props.style); - let horizontalAnimation = { - maxHeight:'100%', - overflowY:'auto', - transform:'scaleX(0)', - opacity:1, + const horizontalAnimation = { + maxHeight: '100%', + overflowY: 'auto', + transform: 'scaleX(0)', + opacity: 1, transition: animated ? Transitions.easeOut('250ms', ['transform', 'opacity']) : null, transformOrigin: `${horizontal} ${targetOrigin.vertical}`, }; - let verticalAnimation = { - opacity:1, - transform:'scaleY(0)', + const verticalAnimation = { + opacity: 1, + transform: 'scaleY(0)', transformOrigin: `${horizontal} ${targetOrigin.vertical}`, transition: animated ? Transitions.easeOut('500ms', ['transform', 'opacity']) : null, }; @@ -177,11 +188,7 @@ const Popover = React.createClass({ } }, - componentClickAway(event) { - if (event.defaultPrevented) { - return; - } - + componentClickAway() { this.requestClose('clickAway'); }, @@ -189,16 +196,12 @@ const Popover = React.createClass({ this.setPlacement(); }, - _animateClose() { + _animate(open) { if (!this.refs.layer || !this.refs.layer.getLayer()) { return; } const el = this.refs.layer.getLayer().children[0]; - this._animate(el, false); - }, - - _animate(el) { let value = '0'; const inner = el.children[0]; const innerInner = inner.children[0]; @@ -206,7 +209,7 @@ const Popover = React.createClass({ const rootStyle = inner.style; const innerStyle = innerInner.style; - if (this.state.open) { + if (open) { value = '1'; } @@ -264,7 +267,7 @@ const Popover = React.createClass({ const targetEl = this.refs.layer.getLayer().children[0]; if (!targetEl) { - return {}; + return; } let {targetOrigin, anchorOrigin} = this.props; @@ -289,8 +292,6 @@ const Popover = React.createClass({ targetEl.style.top = targetPosition.top + 'px'; targetEl.style.left = targetPosition.left + 'px'; - - this._animate(targetEl, true); }, autoCloseWhenOffScreen(anchorPosition) { diff --git a/src/render-to-layer.js b/src/render-to-layer.js index 1b7d38cb3fc60a..3c2786ca2933f5 100644 --- a/src/render-to-layer.js +++ b/src/render-to-layer.js @@ -1,6 +1,5 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import debounce from 'lodash.debounce'; import Dom from './utils/dom'; import DefaultRawTheme from './styles/raw-themes/light-raw-theme'; import ThemeManager from './styles/theme-manager'; @@ -16,7 +15,7 @@ const RenderToLayer = React.createClass({ getDefaultProps() { return { - useLayerForClickAway:true, + useLayerForClickAway: true, }; }, @@ -40,8 +39,10 @@ const RenderToLayer = React.createClass({ //to update theme inside state whenever a new theme is passed down //from the parent / owner using context componentWillReceiveProps(nextProps, nextContext) { - let newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme; - this.setState({muiTheme: newMuiTheme}); + const newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme; + this.setState({ + muiTheme: newMuiTheme, + }); }, componentDidMount() { @@ -58,17 +59,23 @@ const RenderToLayer = React.createClass({ } }, - onClickAway(e) { - if (e.defaultPrevented) { + onClickAway(event) { + if (event.defaultPrevented) { + return; + } + + if (!this.props.componentClickAway) { + return; + } + + if (!this.props.open) { return; } const el = this._layer; - if (e.target !== el && (e.target === window) - || (document.documentElement.contains(e.target) && !Dom.isDescendant(el, e.target))) { - if (this.props.componentClickAway && this.props.open) { - this.props.componentClickAway(e); - } + if (event.target !== el && (event.target === window) + || (document.documentElement.contains(event.target) && !Dom.isDescendant(el, event.target))) { + this.props.componentClickAway(event); } }, @@ -81,74 +88,65 @@ const RenderToLayer = React.createClass({ }, _renderLayer() { - if (this.props.open) { + const { + open, + render, + } = this.props; + + if (open) { if (!this._layer) { this._layer = document.createElement('div'); document.body.appendChild(this._layer); + + if (this.props.useLayerForClickAway) { + this._layer.addEventListener('touchstart', this.onClickAway); + this._layer.addEventListener('click', this.onClickAway); + this._layer.style.position = 'fixed'; + this._layer.style.top = 0; + this._layer.style.bottom = 0; + this._layer.style.left = 0; + this._layer.style.right = 0; + this._layer.style.zIndex = this.state.muiTheme.zIndex.layer; + } else { + setTimeout(() => { + window.addEventListener('touchstart', this.onClickAway); + window.addEventListener('click', this.onClickAway); + }, 0); + } } - if (this.props.useLayerForClickAway) { - this._layer.addEventListener('touchstart', this.onClickAway); - this._layer.addEventListener('click', this.onClickAway); - this._layer.style.position = 'fixed'; - this._layer.style.top = 0; - this._layer.style.bottom = 0; - this._layer.style.left = 0; - this._layer.style.right = 0; - this._layer.style.zIndex = this.state.muiTheme.zIndex.layer; - } - else { - setTimeout(() => { - window.addEventListener('touchstart', this.onClickAway); - window.addEventListener('click', this.onClickAway); - }, 0); - } - if (this.reactUnmount) { - this.reactUnmount.cancel(); - } - } else if (this._layer) { - if (this.props.useLayerForClickAway) { - this._layer.style.position = 'relative'; - this._layer.removeEventListener('touchstart', this.onClickAway); - this._layer.removeEventListener('click', this.onClickAway); + + // By calling this method in componentDidMount() and + // componentDidUpdate(), you're effectively creating a "wormhole" that + // funnels React's hierarchical updates through to a DOM node on an + // entirely different part of the page. + + const layerElement = render(); + + if (layerElement === null) { + this.layerElement = ReactDOM.unstable_renderSubtreeIntoContainer(this, null, this._layer); } else { - window.removeEventListener('touchstart', this.onClickAway); - window.removeEventListener('click', this.onClickAway); + this.layerElement = ReactDOM.unstable_renderSubtreeIntoContainer(this, layerElement, this._layer); } - this._unrenderLayer(); } else { - return; - } + if (this._layer) { + if (this.props.useLayerForClickAway) { + this._layer.style.position = 'relative'; + this._layer.removeEventListener('touchstart', this.onClickAway); + this._layer.removeEventListener('click', this.onClickAway); + } else { + window.removeEventListener('touchstart', this.onClickAway); + window.removeEventListener('click', this.onClickAway); + } - // By calling this method in componentDidMount() and - // componentDidUpdate(), you're effectively creating a "wormhole" that - // funnels React's hierarchical updates through to a DOM node on an - // entirely different part of the page. - - const layerElement = this.props.render(); - // Renders can return null, but React.render() doesn't like being asked - // to render null. If we get null back from renderLayer(), just render - // a noscript element, like React does when an element's render returns - // null. - if (layerElement === null) { - this.layerElement = ReactDOM.unstable_renderSubtreeIntoContainer(this,