11import PropTypes from 'prop-types' ;
2- import React from 'react' ;
2+ import React , { useCallback , useRef } from 'react' ;
3+ import { useSelector } from 'react-redux' ;
34import { browserHistory } from 'react-router' ;
4- import { withTranslation } from 'react-i18next' ;
5+ import { useTranslation } from 'react-i18next' ;
6+ import useModalClose from '../../../common/useModalClose' ;
57
68import ExitIcon from '../../../images/exit.svg' ;
79
8- class Overlay extends React . Component {
9- constructor ( props ) {
10- super ( props ) ;
11- this . close = this . close . bind ( this ) ;
12- this . handleClick = this . handleClick . bind ( this ) ;
13- this . handleClickOutside = this . handleClickOutside . bind ( this ) ;
14- this . keyPressHandle = this . keyPressHandle . bind ( this ) ;
15- }
10+ const Overlay = ( {
11+ actions,
12+ ariaLabel,
13+ children,
14+ closeOverlay,
15+ isFixedHeight,
16+ title
17+ } ) => {
18+ const { t } = useTranslation ( ) ;
1619
17- componentWillMount ( ) {
18- document . addEventListener ( 'mousedown' , this . handleClick , false ) ;
19- document . addEventListener ( 'keydown' , this . keyPressHandle ) ;
20- }
20+ const previousPath = useSelector ( ( state ) => state . ide . previousPath ) ;
2121
22- componentDidMount ( ) {
23- this . node . focus ( ) ;
24- }
22+ const ref = useRef ( null ) ;
2523
26- componentWillUnmount ( ) {
27- document . removeEventListener ( 'mousedown' , this . handleClick , false ) ;
28- document . removeEventListener ( 'keydown' , this . keyPressHandle ) ;
29- }
30-
31- handleClick ( e ) {
32- if ( this . node . contains ( e . target ) ) {
33- return ;
34- }
35-
36- this . handleClickOutside ( e ) ;
37- }
38-
39- handleClickOutside ( ) {
40- this . close ( ) ;
41- }
42-
43- keyPressHandle ( e ) {
44- // escape key code = 27.
45- // So here we are checking if the key pressed was Escape key.
46- if ( e . keyCode === 27 ) {
47- this . close ( ) ;
48- }
49- }
50-
51- close ( ) {
24+ const close = useCallback ( ( ) => {
25+ const node = ref . current ;
26+ if ( ! node ) return ;
5227 // Only close if it is the last (and therefore the topmost overlay)
5328 const overlays = document . getElementsByClassName ( 'overlay' ) ;
54- if ( this . node . parentElement . parentElement !== overlays [ overlays . length - 1 ] )
29+ if ( node . parentElement . parentElement !== overlays [ overlays . length - 1 ] )
5530 return ;
5631
57- if ( ! this . props . closeOverlay ) {
58- browserHistory . push ( this . props . previousPath ) ;
32+ if ( ! closeOverlay ) {
33+ browserHistory . push ( previousPath ) ;
5934 } else {
60- this . props . closeOverlay ( ) ;
35+ closeOverlay ( ) ;
6136 }
62- }
37+ } , [ previousPath , closeOverlay , ref ] ) ;
6338
64- render ( ) {
65- const { ariaLabel, title, children, actions, isFixedHeight } = this . props ;
66- return (
67- < div
68- className = { `overlay ${ isFixedHeight ? 'overlay--is-fixed-height' : '' } ` }
69- >
70- < div className = "overlay__content" >
71- < section
72- role = "main"
73- aria-label = { ariaLabel }
74- ref = { ( node ) => {
75- this . node = node ;
76- } }
77- className = "overlay__body"
78- >
79- < header className = "overlay__header" >
80- < h2 className = "overlay__title" > { title } </ h2 >
81- < div className = "overlay__actions" >
82- { actions }
83- < button
84- className = "overlay__close-button"
85- onClick = { this . close }
86- aria-label = { this . props . t ( 'Overlay.AriaLabel' , { title } ) }
87- >
88- < ExitIcon focusable = "false" aria-hidden = "true" />
89- </ button >
90- </ div >
91- </ header >
92- { children }
93- </ section >
94- </ div >
39+ useModalClose ( close , ref ) ;
40+
41+ return (
42+ < div
43+ className = { `overlay ${ isFixedHeight ? 'overlay--is-fixed-height' : '' } ` }
44+ >
45+ < div className = "overlay__content" >
46+ < section
47+ role = "main"
48+ aria-label = { ariaLabel }
49+ ref = { ref }
50+ className = "overlay__body"
51+ >
52+ < header className = "overlay__header" >
53+ < h2 className = "overlay__title" > { title } </ h2 >
54+ < div className = "overlay__actions" >
55+ { actions }
56+ < button
57+ className = "overlay__close-button"
58+ onClick = { close }
59+ aria-label = { t ( 'Overlay.AriaLabel' , { title } ) }
60+ >
61+ < ExitIcon focusable = "false" aria-hidden = "true" />
62+ </ button >
63+ </ div >
64+ </ header >
65+ { children }
66+ </ section >
9567 </ div >
96- ) ;
97- }
98- }
68+ </ div >
69+ ) ;
70+ } ;
9971
10072Overlay . propTypes = {
10173 children : PropTypes . element ,
10274 actions : PropTypes . element ,
10375 closeOverlay : PropTypes . func ,
10476 title : PropTypes . string ,
10577 ariaLabel : PropTypes . string ,
106- previousPath : PropTypes . string ,
107- isFixedHeight : PropTypes . bool ,
108- t : PropTypes . func . isRequired
78+ isFixedHeight : PropTypes . bool
10979} ;
11080
11181Overlay . defaultProps = {
@@ -114,8 +84,7 @@ Overlay.defaultProps = {
11484 title : 'Modal' ,
11585 closeOverlay : null ,
11686 ariaLabel : 'modal' ,
117- previousPath : '/' ,
11887 isFixedHeight : false
11988} ;
12089
121- export default withTranslation ( ) ( Overlay ) ;
90+ export default Overlay ;
0 commit comments