Ver código fonte

Refactored Modal into a new component.

Dynamically center the modal on center of the page. Works with browser resize and content change.

Removes a lot of mostly duplicated 'modalStyle' code.
Sergiu Miclea 8 anos atrás
pai
commit
703ae20cbb

+ 5 - 1
src/components/AddCloudConnection/AddCloudConnection.js

@@ -39,7 +39,8 @@ class AddCloudConnection extends Reflux.Component {
   static defaultProps = {
     cloud: null,
     connection: null,
-    type: "new"
+    type: "new",
+    onResizeUpdate: () => {}
   }
 
   constructor(props) {
@@ -259,6 +260,7 @@ class AddCloudConnection extends Reflux.Component {
       }
     })
 
+    this.props.onResizeUpdate()
     this.setState({
       currentCloud: cloud,
       currentCloudData: currentCloudData,
@@ -270,6 +272,7 @@ class AddCloudConnection extends Reflux.Component {
    * Function that goes back from endpoint validation to edit mode
    */
   backToEdit() {
+    this.props.onResizeUpdate()
     this.setState({ validateEndpoint: null })
   }
 
@@ -277,6 +280,7 @@ class AddCloudConnection extends Reflux.Component {
    * Handles back operation when adding a new endpoint and want to switch cloud. Resets all previous cloud data.
    */
   handleBack() {
+    this.props.onResizeUpdate()
     this.setState({
       currentCloudData: null,
       currentCloud: null,

+ 3 - 0
src/components/AddCloudConnection/AddCloudConnection.scss

@@ -18,6 +18,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 @import '../variables.scss';
 
 .root {
+  width: 576px;
+  outline: none;
+
   :global(.Dropdown-control) {
     text-align: left;
   }

+ 2 - 48
src/components/CloudConnectionsView/CloudConnectionsView.js

@@ -22,7 +22,7 @@ import Header from '../Header';
 import ConnectionsActions from '../../actions/ConnectionsActions';
 import Location from '../../core/Location';
 import LoadingIcon from '../LoadingIcon';
-import Modal from 'react-modal';
+import Modal from '../NewModal';
 import AddCloudConnection from '../AddCloudConnection';
 import ConfirmationDialog from '../ConfirmationDialog'
 import ValidateEndpoint from '../ValidateEndpoint';
@@ -153,51 +153,6 @@ class CloudConnectionsView extends Component {
     let item = this.state.connection
     let title = "Edit Connection"
 
-    let modalStyle = {
-      overlay: {
-        position: "fixed",
-        top: 0,
-        left: 0,
-        right: 0,
-        bottom: 0,
-        backgroundColor: "rgba(164, 170, 181, 0.69)"
-      },
-      content: {
-        padding: "0px",
-        border: "none",
-        borderRadius: "4px",
-        bottom: "auto",
-        width: "576px",
-        height: "auto",
-        left: "50%",
-        top: "120px",
-        marginLeft: "-288px"
-      }
-    }
-
-    let validationModalStyle = {
-      overlay: {
-        position: "fixed",
-        top: 0,
-        left: 0,
-        right: 0,
-        bottom: 0,
-        backgroundColor: "rgba(164, 170, 181, 0.69)"
-      },
-      content: {
-        padding: "0px",
-        borderRadius: "4px",
-        border: "none",
-        bottom: "auto",
-        width: "400px",
-        height: "300px",
-        left: "50%",
-        top: "50%",
-        marginTop: "-200px",
-        marginLeft: "-150px"
-      }
-    }
-
     if (item) {
       return (
         <div className={s.root}>
@@ -236,7 +191,6 @@ class CloudConnectionsView extends Component {
           <Modal
             isOpen={this.state.showModal}
             contentLabel="Add new cloud connection"
-            style={modalStyle}
             onRequestClose={this.closeModal.bind(this)}
           >
             <AddCloudConnection
@@ -250,7 +204,7 @@ class CloudConnectionsView extends Component {
           <Modal
             isOpen={this.state.showValidationModal}
             contentLabel="Validate Endpoint"
-            style={validationModalStyle}
+            contentStyle={{ width: "400px" }}
             onRequestClose={this.closeValidationModal.bind(this)}
           >
             <ValidateEndpoint

+ 1 - 24
src/components/CloudItem/CloudItem.js

@@ -20,7 +20,7 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
 import s from './CloudItem.scss';
 import Dropdown from '../NewDropdown';
 import AddCloudConnection from '../AddCloudConnection';
-import Modal from 'react-modal';
+import Modal from '../NewModal';
 
 class CloudItem extends Component {
   static propTypes = {
@@ -133,28 +133,6 @@ class CloudItem extends Component {
       />)
     }
 
-    let modalStyle = {
-      overlay: {
-        position: "fixed",
-        top: 0,
-        left: 0,
-        right: 0,
-        bottom: 0,
-        backgroundColor: "rgba(164, 170, 181, 0.69)"
-      },
-      content: {
-        padding: "0px",
-        borderRadius: "4px",
-        border: "none",
-        bottom: "auto",
-        width: "576px",
-        height: "auto",
-        left: "50%",
-        top: "120px",
-        marginLeft: "-288px"
-      }
-    }
-
     return (
       <div className={s.root + " " + (this.props.selected ? s.selected : "")}>
         <div className={s.container}>
@@ -164,7 +142,6 @@ class CloudItem extends Component {
         <Modal
           isOpen={this.state.showModal}
           contentLabel="Add new cloud connection"
-          style={modalStyle}
           onRequestClose={this.closeModal.bind(this)}
         >
           <AddCloudConnection

+ 3 - 24
src/components/ConfirmationDialog/ConfirmationDialog.js

@@ -18,7 +18,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import React, { Component, PropTypes } from 'react';
 import withStyles from 'isomorphic-style-loader/lib/withStyles';
 import s from './ConfirmationDialog.scss';
-import Modal from 'react-modal';
+import Modal from '../NewModal';
 
 class ConfirmationDialog extends Component {
   static propTypes = {
@@ -47,34 +47,13 @@ class ConfirmationDialog extends Component {
   }
 
   render() {
-    let modalStyle = {
-      overlay: {
-        position: "fixed",
-        top: 0,
-        left: 0,
-        right: 0,
-        bottom: 0,
-        backgroundColor: "rgba(164, 170, 181, 0.69)"
-      },
-      content: {
-        padding: "16px",
-        borderRadius: "4px",
-        bottom: "auto",
-        border: "none",
-        width: "250px",
-        height: "auto",
-        left: "50%",
-        top: "40%",
-        marginLeft: "-75px"
-      }
-    }
     if (this.props.visible) {
       return (
         <div className={s.root}>
           <Modal
             isOpen={this.props.visible}
-            contentLabel="Add new cloud connection"
-            style={modalStyle}
+            contentLabel="Confirmation"
+            contentStyle={{ width: '250px', padding: '16px' }}
             onRequestClose={this.onCancel.bind(this)}
           >
             <p className={s.message}>{this.props.message}</p>

+ 1 - 24
src/components/EndpointList/EndpointList.js

@@ -22,7 +22,7 @@ import Location from '../../core/Location';
 import Moment from 'react-moment';
 import s from './EndpointList.scss';
 import AddCloudConnection from '../AddCloudConnection';
-import Modal from 'react-modal';
+import Modal from '../NewModal';
 import ConnectionsStore from '../../stores/ConnectionsStore';
 import ConnectionsActions from '../../actions/ConnectionsActions';
 import TextTruncate from 'react-text-truncate';
@@ -144,27 +144,6 @@ class EndpointList extends Reflux.Component {
   }
 
   render() {
-    let modalStyle = {
-      overlay: {
-        position: "fixed",
-        top: 0,
-        left: 0,
-        right: 0,
-        bottom: 0,
-        backgroundColor: "rgba(164, 170, 181, 0.69)"
-      },
-      content: {
-        padding: "0px",
-        borderRadius: "4px",
-        border: "none",
-        bottom: "auto",
-        width: "576px",
-        height: "auto",
-        left: "50%",
-        top: "120px",
-        marginLeft: "-288px"
-      }
-    }
     if ((this.state.connections && this.state.connections.length) || this.state.connections == null) {
       return (
         <div className={s.root}>
@@ -192,7 +171,6 @@ class EndpointList extends Reflux.Component {
           <Modal
             isOpen={this.state.showModal}
             contentLabel="Add new cloud connection"
-            style={modalStyle}
             onRequestClose={this.closeModal.bind(this)}
           >
             <AddCloudConnection
@@ -230,7 +208,6 @@ class EndpointList extends Reflux.Component {
           <Modal
             isOpen={this.state.showModal}
             contentLabel="Add new cloud connection"
-            style={modalStyle}
             onRequestClose={this.closeModal.bind(this)}
           >
             <AddCloudConnection

+ 129 - 0
src/components/NewModal/NewModal.js

@@ -0,0 +1,129 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import React, { PropTypes } from 'react';
+import withStyles from 'isomorphic-style-loader/lib/withStyles';
+import s from './NewModal.scss';
+import Modal from 'react-modal';
+
+class NewModal extends React.Component {
+
+  static defaultProps = {
+    topBottomMargin: 8
+  }
+
+  static propTypes = {
+    children: PropTypes.node.isRequired,
+    isOpen: PropTypes.bool.isRequired,
+    contentLabel: PropTypes.string,
+    onRequestClose: PropTypes.func,
+    contentStyle: PropTypes.object,
+    topBottomMargin: PropTypes.number
+  }
+
+  componentDidMount() {
+    window.addEventListener('resize', this.positionModal.bind(this), true)
+    setTimeout(this.positionModal.bind(this), 100)
+  }
+
+  componentWillReceiveProps() {
+    setTimeout(this.positionModal.bind(this), 100)
+  }
+
+  componentWillUnmount() {
+    window.removeEventListener('resize', this.positionModal.bind(this), true)
+  }
+
+  handleChildUpdate() {
+    setTimeout(this.positionModal.bind(this), 100)
+  }
+
+  positionModal() {
+    let pageNode = this.modalDiv && this.modalDiv.node.firstChild
+    let contentNode = pageNode && pageNode.firstChild;
+    if (!contentNode) {
+      return
+    }
+
+    contentNode.style.height = 'auto'
+    let left = pageNode.offsetWidth / 2 - contentNode.offsetWidth / 2
+    let top = pageNode.offsetHeight / 2 - contentNode.offsetHeight / 2
+    let height = 'auto';
+
+    if (top < this.props.topBottomMargin) {
+      top = this.props.topBottomMargin
+      height = pageNode.offsetHeight - this.props.topBottomMargin * 2 + 'px'
+    }
+
+    contentNode.style.left = left + 'px'
+    contentNode.style.top = top + 'px'
+    contentNode.style.height = height
+    contentNode.style.opacity = 1;
+  }
+
+  render() {
+    let modalStyle = {
+      overlay: {
+        position: 'fixed',
+        zIndex: 10000,
+        top: 0,
+        left: 0,
+        right: 0,
+        bottom: 0,
+        backgroundColor: 'rgba(164, 170, 181, 0.69)'
+      },
+      content: {
+        padding: 0,
+        overflowX: 'hidden',
+        borderRadius: '4px',
+        border: 'none',
+        bottom: 'auto',
+        top: 'auto',
+        left: 'auto',
+        right: 'auto',
+        transition: 'all 0.2s',
+        opacity: 0
+      }
+    }
+
+    modalStyle.content = {
+      ...modalStyle.content,
+      ...this.props.contentStyle
+    }
+
+    let children = React.Children.map(this.props.children,
+      child => React.cloneElement(child, {
+        onResizeUpdate: this.handleChildUpdate.bind(this)
+      })
+    );
+
+    return (
+      <Modal
+        ref={m => { this.modalDiv = m }}
+        isOpen={this.props.isOpen}
+        contentLabel={this.props.contentLabel}
+        style={modalStyle}
+        contentLabel={this.props.contentLabel}
+        onRequestClose={this.props.onRequestClose}
+      >
+        {children}
+      </Modal>
+    )
+  }
+}
+
+export default withStyles(NewModal, s);

+ 20 - 0
src/components/NewModal/NewModal.scss

@@ -0,0 +1,20 @@
+/*
+Copyright (C) 2017  Cloudbase Solutions SRL
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as
+published by the Free Software Foundation, either version 3 of the
+License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+@import '../variables.scss';
+
+

+ 6 - 0
src/components/NewModal/package.json

@@ -0,0 +1,6 @@
+{
+  "name": "NewModal",
+  "version": "0.0.0",
+  "private": true,
+  "main": "./NewModal.js"
+}

+ 2 - 15
src/components/UserOverview/UserOverview.js

@@ -21,7 +21,7 @@ import withStyles from 'isomorphic-style-loader/lib/withStyles';
 import s from './UserOverview.scss';
 import Moment from 'react-moment';
 import UserStore from '../../stores/UserStore';
-import Modal from 'react-modal';
+import Modal from '../NewModal';
 import EditProfile from '../EditProfile';
 
 const title = 'User Overview';
@@ -58,19 +58,6 @@ class UserOverview extends Reflux.Component {
   }
 
   render() {
-    let modalStyle = {
-      content: {
-        padding: "0px",
-        borderRadius: "4px",
-        bottom: "auto",
-        width: "576px",
-        height: "auto",
-        left: "50%",
-        top: "20%",
-        marginLeft: "-288px"
-      }
-    }
-
     let item = this.state.currentUser
     return (
       <div className={s.root}>
@@ -139,7 +126,7 @@ class UserOverview extends Reflux.Component {
         <Modal
           isOpen={this.state.showModal}
           contentLabel="Edit Profile"
-          style={modalStyle}
+          contentStyle={{ width: '576px' }}
           onRequestClose={this.closeModal.bind(this)}
         >
           <EditProfile