From 9e9fb632a680f1a4753b05aebe525b5f05e784b4 Mon Sep 17 00:00:00 2001 From: Chris McVittie Date: Thu, 12 Nov 2015 13:24:51 +0000 Subject: [PATCH 1/3] dialog render to layer --- src/dialog.jsx | 301 ++++++++++++++++++++++++++---------------------- src/overlay.jsx | 33 +++--- 2 files changed, 180 insertions(+), 154 deletions(-) diff --git a/src/dialog.jsx b/src/dialog.jsx index 33d2846908abfd..4f7eb7960cf454 100644 --- a/src/dialog.jsx +++ b/src/dialog.jsx @@ -7,6 +7,7 @@ const Transitions = require('./styles/transitions'); const StylePropable = require('./mixins/style-propable'); const FlatButton = require('./flat-button'); const Overlay = require('./overlay'); +const RenderToLayer = require('./render-to-layer'); const Paper = require('./paper'); const DefaultRawTheme = require('./styles/raw-themes/light-raw-theme'); const ThemeManager = require('./styles/theme-manager'); @@ -46,7 +47,7 @@ const TransitionItem = React.createClass({ this.setState({muiTheme: newMuiTheme}); }, - componentWillEnter(callback) { + componentWillAppear(callback) { let spacing = this.state.muiTheme.rawTheme.spacing; this.setState({ @@ -84,7 +85,7 @@ const TransitionItem = React.createClass({ }, }); -let Dialog = React.createClass({ +const DialogInline = React.createClass({ mixins: [WindowListenable, StylePropable], @@ -110,18 +111,10 @@ let Dialog = React.createClass({ bodyStyle: React.PropTypes.object, contentClassName: React.PropTypes.string, contentStyle: React.PropTypes.object, - openImmediately: React.PropTypes.bool, repositionOnUpdate: React.PropTypes.bool, style: React.PropTypes.object, title: React.PropTypes.node, - defaultOpen: React.PropTypes.bool, - open: React.PropTypes.bool, - modal: React.PropTypes.bool, - onDismiss: React.PropTypes.func, - onShow: React.PropTypes.func, - onRequestClose: React.PropTypes.func, - actionFocus: React.PropTypes.string, - titleStyle: React.PropTypes.object, + open: React.PropTypes.bool.isRequired, }, windowListeners: { @@ -129,58 +122,24 @@ let Dialog = React.createClass({ resize: '_handleResize', }, + getInitialState() { + return { + muiTheme: this.context.muiTheme ? this.context.muiTheme : ThemeManager.getMuiTheme(DefaultRawTheme), + }; + }, + getDefaultProps() { return { autoDetectWindowHeight: false, autoScrollBodyContent: false, actions: [], repositionOnUpdate: true, - defaultOpen: false, open: null, }; }, - getInitialState() { - if (process.env.NODE_ENV !== 'production') { - this._testDeprecations(); - } - - let open = this.props.open; - - if (open === null) { - open = (this.props.openImmediately || this.props.defaultOpen); - } - - return { - open: open, - muiTheme: this.context.muiTheme ? this.context.muiTheme : ThemeManager.getMuiTheme(DefaultRawTheme), - }; - }, - - //to update theme inside state whenever a new theme is passed down - //from the parent / owner using context - componentWillReceiveProps (nextProps, nextContext) { - const newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme; - this.setState({muiTheme: newMuiTheme}); - - if (process.env.NODE_ENV !== 'production') { - this._testDeprecations(); - } - - if (nextProps.open !== this.props.open) { - if (nextProps.open && !this.state.open) { - this._show(); - } else if (!nextProps.open && this.state.open) { - this._dismiss(); - } - } - }, - componentDidMount() { this._positionDialog(); - if (this.state.open) { - this.refs.dialogOverlay.preventScrolling(); - } }, componentDidUpdate() { @@ -230,7 +189,7 @@ let Dialog = React.createClass({ }; - if (this.state.open) { + if (this.props.open) { main = this.mergeStyles(main, { left: 0, transition: Transitions.easeOut('0ms', 'left', '0ms'), @@ -262,8 +221,8 @@ let Dialog = React.createClass({ return (
- - {this.state.open && + + {this.props.open && @@ -279,32 +238,12 @@ let Dialog = React.createClass({ }
); }, - isOpen() { - return this.state.open; - }, - - _testDeprecations() { - warning(!this.props.hasOwnProperty('openImmediately'), - 'openImmediately has been deprecated in favor of defaultOpen'); - - warning(!(typeof this.props.onShow === 'function'), - 'onShow will be removed in favor of explicitly setting open'); - - warning(!(typeof this.props.onDismiss === 'function'), - 'onDismiss will be removed in favor of explicitly setting open and can be replaced by onRequestClose'); - - warning(!this.props.hasOwnProperty('modal'), - 'modal will be removed in favor of explicitly setting open and onRequestClose'); - }, - _getAction(actionJSON, key) { let props = { key: key, @@ -372,40 +311,159 @@ let Dialog = React.createClass({ }, _positionDialog() { - if (this.state.open) { - let clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; - let container = ReactDOM.findDOMNode(this); - let dialogWindow = ReactDOM.findDOMNode(this.refs.dialogWindow); - let dialogContent = ReactDOM.findDOMNode(this.refs.dialogContent); - let minPaddingTop = 16; - - //Reset the height in case the window was resized. - dialogWindow.style.height = ''; - dialogContent.style.height = ''; - - let dialogWindowHeight = dialogWindow.offsetHeight; - let paddingTop = ((clientHeight - dialogWindowHeight) / 2) - 64; - if (paddingTop < minPaddingTop) paddingTop = minPaddingTop; - - //Vertically center the dialog window, but make sure it doesn't - //transition to that position. - if (this.props.repositionOnUpdate || !container.style.paddingTop) { - container.style.paddingTop = paddingTop + 'px'; - } + if (!this.props.open) { + return; + } + + let clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; + let container = ReactDOM.findDOMNode(this); + let dialogWindow = ReactDOM.findDOMNode(this.refs.dialogWindow); + let dialogContent = ReactDOM.findDOMNode(this.refs.dialogContent); + let minPaddingTop = 16; + + //Reset the height in case the window was resized. + dialogWindow.style.height = ''; + dialogContent.style.height = ''; + + let dialogWindowHeight = dialogWindow.offsetHeight; + let paddingTop = ((clientHeight - dialogWindowHeight) / 2) - 64; + if (paddingTop < minPaddingTop) paddingTop = minPaddingTop; + + //Vertically center the dialog window, but make sure it doesn't + //transition to that position. + if (this.props.repositionOnUpdate || !container.style.paddingTop) { + container.style.paddingTop = paddingTop + 'px'; + } + + // Force a height if the dialog is taller than clientHeight + if (this.props.autoDetectWindowHeight || this.props.autoScrollBodyContent) { + let styles = this.getStyles(); + let maxDialogContentHeight = clientHeight - 2 * (styles.body.padding + 64); + + if (this.props.title) maxDialogContentHeight -= dialogContent.previousSibling.offsetHeight; + if (this.props.actions.length) maxDialogContentHeight -= dialogContent.nextSibling.offsetHeight; + + dialogContent.style.maxHeight = maxDialogContentHeight + 'px'; + } + }, + + _requestClose(buttonClicked) { + if (!buttonClicked || this.props.modal) { + return; + } + + if (this.props.onRequestClose) { + this.props.onRequestClose(!!buttonClicked); + } + }, + + _handleOverlayTouchTap() { + this._requestClose(false); + }, + + _handleWindowKeyUp(event) { + if (event.keyCode === KeyCode.ESC) { + this._requestClose(false); + } + }, + + _handleResize() { + if (this.props.open) { + this._positionDialog(); + } + }, + +}); - // Force a height if the dialog is taller than clientHeight - if (this.props.autoDetectWindowHeight || this.props.autoScrollBodyContent) { - let styles = this.getStyles(); - let maxDialogContentHeight = clientHeight - 2 * (styles.body.padding + 64); - if (this.props.title) maxDialogContentHeight -= dialogContent.previousSibling.offsetHeight; - if (this.props.actions.length) maxDialogContentHeight -= dialogContent.nextSibling.offsetHeight; +const wrapperStyle = {position:'fixed', top:0, left:0, zIndex:20}; +const Dialog = React.createClass({ - dialogContent.style.maxHeight = maxDialogContentHeight + 'px'; + propTypes: { + actions: React.PropTypes.array, + autoDetectWindowHeight: React.PropTypes.bool, + autoScrollBodyContent: React.PropTypes.bool, + bodyStyle: React.PropTypes.object, + contentClassName: React.PropTypes.string, + contentStyle: React.PropTypes.object, + openImmediately: React.PropTypes.bool, + onClickAway: React.PropTypes.func, + repositionOnUpdate: React.PropTypes.bool, + style: React.PropTypes.object, + title: React.PropTypes.node, + defaultOpen: React.PropTypes.bool, + open: React.PropTypes.bool, + }, + + getInitialState() { + if (process.env.NODE_ENV !== 'production') { + this._testDeprecations(); + } + + let open = this.props.open; + + if (open === null) { + open = (this.props.openImmediately || this.props.defaultOpen); + } + + return { + open: open, + muiTheme: this.context.muiTheme ? this.context.muiTheme : ThemeManager.getMuiTheme(DefaultRawTheme), + }; + }, + + getDefaultProps() { + return { + open:null, + defaultOpen:false, + } + }, + + //to update theme inside state whenever a new theme is passed down + //from the parent / owner using context + componentWillReceiveProps (nextProps, nextContext) { + const newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme; + this.setState({muiTheme: newMuiTheme}); + + if (process.env.NODE_ENV !== 'production') { + this._testDeprecations(); + } + if (nextProps.open !== this.props.open) { + if (nextProps.open && !this.state.open) { + this._show(); + } else if (!nextProps.open && this.state.open) { + this._dismiss(); } } }, + render() { + return ( + + ); + }, + + renderLayer() { + return ( +
+ +
+ ); + }, + + _testDeprecations() { + warning(!this.props.hasOwnProperty('openImmediately'), + 'openImmediately has been deprecated in favor of defaultOpen'); + + warning(!(typeof this.props.onShow === 'function'), + 'onShow will be removed in favor of explicitly setting open'); + + warning(!(typeof this.props.onDismiss === 'function'), + 'onDismiss will be removed in favor of explicitly setting open and can be replaced by onRequestClose'); + + }, + + show() { warning(false, 'show has been deprecated in favor of explicitly setting the open property.'); @@ -419,7 +477,6 @@ let Dialog = React.createClass({ }, _show() { - this.refs.dialogOverlay.preventScrolling(); this.setState({ open: true, }, this._onShow); @@ -438,47 +495,17 @@ let Dialog = React.createClass({ }, _dismiss() { - CssEvent.onTransitionEnd(ReactDOM.findDOMNode(this), () => { - this.refs.dialogOverlay.allowScrolling(); - }); - this.setState({ open: false, }, this._onDismiss); }, - _requestClose(buttonClicked) { - warning(!this.props.hasOwnProperty('modal'), - 'modal will be removed in favor of explicitly setting open and onRequestClose'); - - if (!buttonClicked && this.props.modal) { - return; - } - - // Close the dialog if the open state is not explicitly set. - if (this.props.open === null) { - this._dismiss(); - } - if (this.props.onRequestClose) { - this.props.onRequestClose(!!buttonClicked); - } - }, - - _handleOverlayTouchTap() { - this._requestClose(false); - }, - - _handleWindowKeyUp(event) { - if (event.keyCode === KeyCode.ESC) { - this._requestClose(false); - } + layerWillUnmount() { + if (this.props.onDismiss) this.props.onDismiss(); }, - _handleResize() { - if (this.state.open) { - this.refs.dialogOverlay.preventScrolling(); - this._positionDialog(); - } + isOpen() { + return this.state.openImmediately; }, }); diff --git a/src/overlay.jsx b/src/overlay.jsx index 5e8b5e53fd7154..31cd88c96bf1aa 100644 --- a/src/overlay.jsx +++ b/src/overlay.jsx @@ -39,33 +39,30 @@ const Overlay = React.createClass({ componentWillReceiveProps (nextProps, nextContext) { let newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme; this.setState({muiTheme: newMuiTheme}); + if (this.props.show !== nextProps.show) { + this._applyAutoLockScrolling(); + } }, propTypes: { autoLockScrolling: React.PropTypes.bool, - show: React.PropTypes.bool, - transitionEnabled: React.PropTypes.bool, + show: React.PropTypes.bool.isRequired, style: React.PropTypes.object, + transitionEnabled: React.PropTypes.bool, }, getDefaultProps() { return { autoLockScrolling: true, transitionEnabled: true, + style:{}, }; }, componentDidMount() { this._originalBodyOverflow = document.getElementsByTagName('body')[0].style.overflow; - }, - - componentDidUpdate() { - if (this.props.autoLockScrolling) { - if (this.props.show) { - this._preventScrolling(); - } else { - this._allowScrolling(); - } + if (this.props.show) { + this._applyAutoLockScrolling(); } }, @@ -126,12 +123,14 @@ const Overlay = React.createClass({ ); }, - preventScrolling() { - if (!this.props.autoLockScrolling) this._preventScrolling(); - }, - - allowScrolling() { - if (!this.props.autoLockScrolling) this._allowScrolling(); + _applyAutoLockScrolling() { + if (this.props.autoLockScrolling) { + if (this.props.show) { + this._preventScrolling(); + } else { + this._allowScrolling(); + } + } }, _preventScrolling() { From 2de98c7053a060713239f4c15f056600bfd7b2ec Mon Sep 17 00:00:00 2001 From: Chris McVittie Date: Tue, 24 Nov 2015 19:31:31 +0000 Subject: [PATCH 2/3] fix height issue --- .../components/pages/components/dialog.jsx | 3 +++ src/dialog.jsx | 23 +++++++++++++------ src/overlay.jsx | 10 ++++---- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/docs/src/app/components/pages/components/dialog.jsx b/docs/src/app/components/pages/components/dialog.jsx index 9a35a697d79173..c5114b0816571f 100644 --- a/docs/src/app/components/pages/components/dialog.jsx +++ b/docs/src/app/components/pages/components/dialog.jsx @@ -212,6 +212,7 @@ export default class DialogPage extends React.Component { title="Dialog With Standard Actions" actions={standardActions} actionFocus="submit" + modal={this.state.modal} open={this.state.openDialogStandardActions} onRequestClose={this._handleRequestClose}> The actions in this window are created from the json that's passed in. @@ -221,6 +222,7 @@ export default class DialogPage extends React.Component { ref="customDialog" title="Dialog With Custom Actions" actions={customActions} + modal={this.state.modal} open={this.state.openDialogCustomActions} onRequestClose={this._handleRequestClose}> The actions in this window were passed in as an array of react objects. @@ -235,6 +237,7 @@ export default class DialogPage extends React.Component { ref="scrollableContentDialog" title="Dialog With Scrollable Content" actions={scrollableCustomActions} + modal={this.state.modal} autoDetectWindowHeight={true} autoScrollBodyContent={true} open={this.state.openDialogScrollable} diff --git a/src/dialog.jsx b/src/dialog.jsx index 4f7eb7960cf454..f55f9146c21be2 100644 --- a/src/dialog.jsx +++ b/src/dialog.jsx @@ -47,6 +47,10 @@ const TransitionItem = React.createClass({ this.setState({muiTheme: newMuiTheme}); }, + componentWillEnter(callback) { + this.componentWillAppear(callback); + }, + componentWillAppear(callback) { let spacing = this.state.muiTheme.rawTheme.spacing; @@ -111,10 +115,10 @@ const DialogInline = React.createClass({ bodyStyle: React.PropTypes.object, contentClassName: React.PropTypes.string, contentStyle: React.PropTypes.object, + open: React.PropTypes.bool.isRequired, repositionOnUpdate: React.PropTypes.bool, style: React.PropTypes.object, title: React.PropTypes.node, - open: React.PropTypes.bool.isRequired, }, windowListeners: { @@ -221,7 +225,9 @@ const DialogInline = React.createClass({ return (
- + {this.props.open && - +
); }, diff --git a/src/overlay.jsx b/src/overlay.jsx index 31cd88c96bf1aa..f64f9d6d0f614f 100644 --- a/src/overlay.jsx +++ b/src/overlay.jsx @@ -40,7 +40,7 @@ const Overlay = React.createClass({ let newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme; this.setState({muiTheme: newMuiTheme}); if (this.props.show !== nextProps.show) { - this._applyAutoLockScrolling(); + this._applyAutoLockScrolling(nextProps); } }, @@ -62,7 +62,7 @@ const Overlay = React.createClass({ componentDidMount() { this._originalBodyOverflow = document.getElementsByTagName('body')[0].style.overflow; if (this.props.show) { - this._applyAutoLockScrolling(); + this._applyAutoLockScrolling(this.props); } }, @@ -123,9 +123,9 @@ const Overlay = React.createClass({ ); }, - _applyAutoLockScrolling() { - if (this.props.autoLockScrolling) { - if (this.props.show) { + _applyAutoLockScrolling(props) { + if (props.autoLockScrolling) { + if (props.show) { this._preventScrolling(); } else { this._allowScrolling(); From b4b7bb9448fa34e5e99cbbba05c77b3c408738d4 Mon Sep 17 00:00:00 2001 From: Chris McVittie Date: Tue, 24 Nov 2015 19:44:55 +0000 Subject: [PATCH 3/3] manual merge ad36ba9b9a920c97b4749cb561ca5b7865fa837c --- src/dialog.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dialog.jsx b/src/dialog.jsx index f55f9146c21be2..aa8cde57645639 100644 --- a/src/dialog.jsx +++ b/src/dialog.jsx @@ -387,6 +387,7 @@ const wrapperStyle = {position:'fixed', top:0, left:0, zIndex:20}; const Dialog = React.createClass({ propTypes: { + actionFocus: React.PropTypes.string, actions: React.PropTypes.array, autoDetectWindowHeight: React.PropTypes.bool, autoScrollBodyContent: React.PropTypes.bool, @@ -395,12 +396,15 @@ const Dialog = React.createClass({ contentStyle: React.PropTypes.object, defaultOpen: React.PropTypes.bool, modal: React.PropTypes.bool, + onDismiss: React.PropTypes.func, onRequestClose: React.PropTypes.func, + onShow: React.PropTypes.func, open: React.PropTypes.bool, openImmediately: React.PropTypes.bool, repositionOnUpdate: React.PropTypes.bool, style: React.PropTypes.object, title: React.PropTypes.node, + titleStyle: React.PropTypes.object, }, getInitialState() {