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';
16 } from '../../../features/ui/util/async-components';
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
}),
24 'REPORT': ReportModal
,
25 'ACTIONS': () => Promise
.resolve({ default: ActionsModal
}),
27 'LIST_EDITOR': ListEditor
,
30 export default class ModalRoot
extends React
.PureComponent
{
33 type: PropTypes
.string
,
34 props: PropTypes
.object
,
35 onClose: PropTypes
.func
.isRequired
,
42 handleKeyUp
= (e
) => {
43 if ((e
.key
=== 'Escape' || e
.key
=== 'Esc' || e
.keyCode
=== 27)
44 && !!this.props
.type
) {
49 componentDidMount () {
50 window
.addEventListener('keyup', this.handleKeyUp
, false);
53 componentWillReceiveProps (nextProps
) {
54 if (!!nextProps
.type
&& !this.props
.type
) {
55 this.activeElement
= document
.activeElement
;
57 this.getSiblings().forEach(sibling
=> sibling
.setAttribute('inert', true));
58 } else if (!nextProps
.type
) {
59 this.setState({ revealed: false });
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;
69 if (this.props
.type
) {
70 requestAnimationFrame(() => {
71 this.setState({ revealed: true });
76 componentWillUnmount () {
77 window
.removeEventListener('keyup', this.handleKeyUp
);
81 return Array(...this.node
.parentElement
.childNodes
).filter(node
=> node
!== this.node
);
88 renderLoading
= modalId
=> () => {
89 return ['MEDIA', 'VIDEO', 'BOOST', 'CONFIRM', 'ACTIONS'].indexOf(modalId
) === -1 ? <ModalLoading
/> : null;
92 renderError
= (props
) => {
93 const { onClose
} = this.props
;
95 return <BundleModalError
{...props
} onClose
={onClose
} />;
99 const { type
, props
, onClose
} = this.props
;
100 const { revealed
} = this.state
;
101 const visible
= !!type
;
105 <div className
='modal-root' ref
={this.setRef
} style
={{ opacity: 0 }} />
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'>
115 <BundleContainer fetchComponent
={MODAL_COMPONENTS
[type
]} loading
={this.renderLoading(type
)} error
={this.renderError
} renderDelay
={200}>
116 {(SpecificComponent
) => <SpecificComponent
{...props
} onClose
={onClose
} />}