about summary refs log tree commit diff
path: root/app/javascript/mastodon/features/ui/components/modal_root.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/mastodon/features/ui/components/modal_root.js')
-rw-r--r--app/javascript/mastodon/features/ui/components/modal_root.js93
1 files changed, 93 insertions, 0 deletions
diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.js
new file mode 100644
index 000000000..5cde65907
--- /dev/null
+++ b/app/javascript/mastodon/features/ui/components/modal_root.js
@@ -0,0 +1,93 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import MediaModal from './media_modal';
+import OnboardingModal from './onboarding_modal';
+import VideoModal from './video_modal';
+import BoostModal from './boost_modal';
+import ConfirmationModal from './confirmation_modal';
+import { TransitionMotion, spring } from 'react-motion';
+
+const MODAL_COMPONENTS = {
+  'MEDIA': MediaModal,
+  'ONBOARDING': OnboardingModal,
+  'VIDEO': VideoModal,
+  'BOOST': BoostModal,
+  'CONFIRM': ConfirmationModal
+};
+
+class ModalRoot extends React.PureComponent {
+
+  constructor (props, context) {
+    super(props, context);
+    this.handleKeyUp = this.handleKeyUp.bind(this);
+  }
+
+  handleKeyUp (e) {
+    if ((e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27)
+         && !!this.props.type) {
+      this.props.onClose();
+    }
+  }
+
+  componentDidMount () {
+    window.addEventListener('keyup', this.handleKeyUp, false);
+  }
+
+  componentWillUnmount () {
+    window.removeEventListener('keyup', this.handleKeyUp);
+  }
+
+  willEnter () {
+    return { opacity: 0, scale: 0.98 };
+  }
+
+  willLeave () {
+    return { opacity: spring(0), scale: spring(0.98) };
+  }
+
+  render () {
+    const { type, props, onClose } = this.props;
+    const items = [];
+
+    if (!!type) {
+      items.push({
+        key: type,
+        data: { type, props },
+        style: { opacity: spring(1), scale: spring(1, { stiffness: 120, damping: 14 }) }
+      });
+    }
+
+    return (
+      <TransitionMotion
+        styles={items}
+        willEnter={this.willEnter}
+        willLeave={this.willLeave}>
+        {interpolatedStyles =>
+          <div className='modal-root'>
+            {interpolatedStyles.map(({ key, data: { type, props }, style }) => {
+              const SpecificComponent = MODAL_COMPONENTS[type];
+
+              return (
+                <div key={key}>
+                  <div role='presentation' className='modal-root__overlay' style={{ opacity: style.opacity }} onClick={onClose} />
+                  <div className='modal-root__container' style={{ opacity: style.opacity, transform: `translateZ(0px) scale(${style.scale})` }}>
+                    <SpecificComponent {...props} onClose={onClose} />
+                  </div>
+                </div>
+              );
+            })}
+          </div>
+        }
+      </TransitionMotion>
+    );
+  }
+
+}
+
+ModalRoot.propTypes = {
+  type: PropTypes.string,
+  props: PropTypes.object,
+  onClose: PropTypes.func.isRequired
+};
+
+export default ModalRoot;