]> cat aescling's git repositories - mastodon.git/blob - app/javascript/mastodon/features/ui/components/modal_root.js
Replace tutorial modal with welcome e-mail (#6273)
[mastodon.git] / app / javascript / mastodon / features / ui / components / modal_root.js
1 import React from 'react';
2 import PropTypes from 'prop-types';
3 import BundleContainer from '../containers/bundle_container';
4 import BundleModalError from './bundle_modal_error';
5 import ModalLoading from './modal_loading';
6 import ActionsModal from './actions_modal';
7 import MediaModal from './media_modal';
8 import VideoModal from './video_modal';
9 import BoostModal from './boost_modal';
10 import ConfirmationModal from './confirmation_modal';
11 import {
12 MuteModal,
13 ReportModal,
14 EmbedModal,
15 ListEditor,
16 } from '../../../features/ui/util/async-components';
17
18 const MODAL_COMPONENTS = {
19 'MEDIA': () => Promise.resolve({ default: MediaModal }),
20 'VIDEO': () => Promise.resolve({ default: VideoModal }),
21 'BOOST': () => Promise.resolve({ default: BoostModal }),
22 'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }),
23 'MUTE': MuteModal,
24 'REPORT': ReportModal,
25 'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
26 'EMBED': EmbedModal,
27 'LIST_EDITOR': ListEditor,
28 };
29
30 export default class ModalRoot extends React.PureComponent {
31
32 static propTypes = {
33 type: PropTypes.string,
34 props: PropTypes.object,
35 onClose: PropTypes.func.isRequired,
36 };
37
38 state = {
39 revealed: false,
40 };
41
42 handleKeyUp = (e) => {
43 if ((e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27)
44 && !!this.props.type) {
45 this.props.onClose();
46 }
47 }
48
49 componentDidMount () {
50 window.addEventListener('keyup', this.handleKeyUp, false);
51 }
52
53 componentWillReceiveProps (nextProps) {
54 if (!!nextProps.type && !this.props.type) {
55 this.activeElement = document.activeElement;
56
57 this.getSiblings().forEach(sibling => sibling.setAttribute('inert', true));
58 } else if (!nextProps.type) {
59 this.setState({ revealed: false });
60 }
61 }
62
63 componentDidUpdate (prevProps) {
64 if (!this.props.type && !!prevProps.type) {
65 this.getSiblings().forEach(sibling => sibling.removeAttribute('inert'));
66 this.activeElement.focus();
67 this.activeElement = null;
68 }
69 if (this.props.type) {
70 requestAnimationFrame(() => {
71 this.setState({ revealed: true });
72 });
73 }
74 }
75
76 componentWillUnmount () {
77 window.removeEventListener('keyup', this.handleKeyUp);
78 }
79
80 getSiblings = () => {
81 return Array(...this.node.parentElement.childNodes).filter(node => node !== this.node);
82 }
83
84 setRef = ref => {
85 this.node = ref;
86 }
87
88 renderLoading = modalId => () => {
89 return ['MEDIA', 'VIDEO', 'BOOST', 'CONFIRM', 'ACTIONS'].indexOf(modalId) === -1 ? <ModalLoading /> : null;
90 }
91
92 renderError = (props) => {
93 const { onClose } = this.props;
94
95 return <BundleModalError {...props} onClose={onClose} />;
96 }
97
98 render () {
99 const { type, props, onClose } = this.props;
100 const { revealed } = this.state;
101 const visible = !!type;
102
103 if (!visible) {
104 return (
105 <div className='modal-root' ref={this.setRef} style={{ opacity: 0 }} />
106 );
107 }
108
109 return (
110 <div className='modal-root' ref={this.setRef} style={{ opacity: revealed ? 1 : 0 }}>
111 <div style={{ pointerEvents: visible ? 'auto' : 'none' }}>
112 <div role='presentation' className='modal-root__overlay' onClick={onClose} />
113 <div role='dialog' className='modal-root__container'>
114 {visible && (
115 <BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
116 {(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />}
117 </BundleContainer>
118 )}
119 </div>
120 </div>
121 </div>
122 );
123 }
124
125 }
This page took 0.10305 seconds and 4 git commands to generate.