@@ -2,6 +2,7 @@ import { h, Component, VNode, Fragment } from 'preact';
22import * as style from './style.css' ;
33import 'add-css:./style.css' ;
44import { linkRef } from 'shared/prerendered-app/util' ;
5+ import { cleanSet } from '../util/clean-modify' ;
56
67interface Props { }
78
@@ -26,66 +27,71 @@ export default class Modal extends Component<Props, State> {
2627 shown : false ,
2728 } ;
2829
29- private modal ?: HTMLDialogElement ;
30+ private dialogElement ! : HTMLDialogElement ;
31+ static modalInstance ?: Modal | undefined ;
3032
3133 componentDidMount ( ) {
3234 // Once a transition ends, check if the modal should be closed (not just hidden)
3335 // dialog.close() instantly hides the modal, so we call it AFTER fading it out i.e. on transition end
34- this . modal ? .addEventListener (
36+ this . dialogElement . addEventListener (
3537 'transitionend' ,
3638 this . _closeOnTransitionEnd . bind ( this ) ,
3739 ) ;
38- this . modal ?. setAttribute ( 'inert' , 'enabled' ) ;
40+ this . dialogElement . setAttribute ( 'inert' , 'enabled' ) ;
41+
42+ Modal . modalInstance = this ;
3943 }
4044
4145 private _closeOnTransitionEnd ( ) {
4246 // If modal does not exist
4347 // Or if it's not being closed at the moment
44- if ( ! this . modal || ! this . modal . classList . contains ( style . modalClosing ) )
48+ if (
49+ ! this . dialogElement ||
50+ ! this . dialogElement . classList . contains ( style . modalClosing )
51+ )
4552 return ;
4653
47- this . modal . close ( ) ;
48- this . modal . classList . remove ( style . modalClosing ) ;
49- this . modal . setAttribute ( 'inert' , 'enabled' ) ;
54+ this . dialogElement . close ( ) ;
55+ this . dialogElement . classList . remove ( style . modalClosing ) ;
56+ this . dialogElement . setAttribute ( 'inert' , 'enabled' ) ;
57+ }
58+
59+ static showModal ( message : ModalMessage ) {
60+ Modal . modalInstance ?. _showModal ( message ) ;
5061 }
5162
52- /**
53- * Function to set up the modal and show it
54- */
55- showModal ( message : ModalMessage ) {
56- if ( ! this . modal ) return ;
63+ static hideModal ( ) {
64+ Modal . modalInstance ?. _hideModal ( ) ;
65+ }
66+
67+ private _showModal ( message : ModalMessage ) {
68+ if ( ! this . dialogElement ) throw Error ( 'Modal missing' ) ;
5769
5870 this . setState ( {
5971 message : message ,
6072 shown : true ,
6173 } ) ;
6274
6375 // Actually show the modal
64- this . modal . removeAttribute ( 'inert' ) ;
65- this . modal . showModal ( ) ;
76+ this . dialogElement . removeAttribute ( 'inert' ) ;
77+ this . dialogElement . showModal ( ) ;
6678 }
6779
68- /**
69- * Function to hide the modal with a fade-out transition
70- * Adds the `modal--closing` class which is removed on transition end
71- */
72- hideModal ( ) {
73- if ( ! this . modal || ! this . modal . open ) return ;
80+ private _hideModal ( ) {
81+ if ( ! this . dialogElement || ! this . dialogElement . open )
82+ throw Error ( 'Modal missing / hidden' ) ;
7483
7584 // Make the modal fade out
76- this . modal . classList . add ( style . modalClosing ) ;
85+ this . dialogElement . classList . add ( style . modalClosing ) ;
7786
78- this . setState ( {
79- message : { ...this . state . message } ,
80- shown : false ,
81- } ) ;
87+ this . setState ( cleanSet ( this . state , 'shown' , false ) ) ;
8288 }
8389
8490 private _onKeyDown ( e : KeyboardEvent ) {
8591 // Default behaviour of <dialog> closes it instantly when you press Esc
8692 // So we hijack it to smoothly hide the modal
87- if ( e . key === 'Escape' || e . keyCode == 27 ) {
88- this . hideModal ( ) ;
93+ if ( e . key === 'Escape' ) {
94+ this . _hideModal ( ) ;
8995 e . preventDefault ( ) ;
9096 e . stopImmediatePropagation ( ) ;
9197 }
@@ -94,13 +100,13 @@ export default class Modal extends Component<Props, State> {
94100 render ( { } : Props , { message, shown } : State ) {
95101 return (
96102 < dialog
97- ref = { linkRef ( this , 'modal ' ) }
103+ ref = { linkRef ( this , 'dialogElement ' ) }
98104 onKeyDown = { ( e ) => this . _onKeyDown ( e ) }
99105 >
100106 < header class = { style . header } >
101107 < span class = { style . modalIcon } > { message . icon } </ span >
102108 < span class = { style . modalTitle } > { message . title } </ span >
103- < button class = { style . closeButton } onClick = { ( ) => this . hideModal ( ) } >
109+ < button class = { style . closeButton } onClick = { ( ) => this . _hideModal ( ) } >
104110 < svg viewBox = "0 0 480 480" fill = "currentColor" >
105111 < path
106112 d = "M119.356 120L361 361M360.644 120L119 361"
0 commit comments