@@ -6,20 +6,18 @@ enum SnackbarType {
66 Progress ,
77}
88
9- class Snackbar {
9+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
10+ class Snackbar extends EventTarget {
1011 #message: string = "" ;
1112 readonly #type: SnackbarType ;
1213 #snackbarElement: HTMLElement ;
13- readonly #hidden: Promise < void > ;
14- #hiddenResolve: ( ) => void ;
1514
1615 constructor ( message : string , type : SnackbarType ) {
16+ super ( ) ;
17+
1718 this . #message = message ;
1819 this . #type = type ;
1920
20- this . #hidden = new Promise < void > ( ( resolve ) => {
21- this . #hiddenResolve = resolve ;
22- } ) ;
2321 this . #render( ) ;
2422 }
2523
@@ -58,14 +56,17 @@ class Snackbar {
5856
5957 const message = document . createElement ( "div" ) ;
6058 message . classList . add ( "snackbar__message" ) ;
59+ if ( this . isProgress ( ) ) {
60+ message . setAttribute ( "aria-live" , "polite" ) ;
61+ }
6162 message . append ( this . message ) ;
6263
6364 const dismissButton = document . createElement ( "button" ) ;
6465 dismissButton . type = "button" ;
6566 dismissButton . classList . add ( "snackbar__dismissButton" ) ;
6667 dismissButton . setAttribute ( "aria-label" , getPhrase ( "wcf.global.button.close" ) ) ;
6768 dismissButton . addEventListener ( "click" , ( ) => {
68- this . hide ( ) ;
69+ this . close ( ) ;
6970 } ) ;
7071
7172 const dismissIcon = document . createElement ( "fa-icon" ) ;
@@ -95,20 +96,40 @@ class Snackbar {
9596 return this . #type == SnackbarType . Progress ;
9697 }
9798
98- hide ( ) : void {
99- this . #snackbarElement. remove ( ) ;
100- this . #hiddenResolve( ) ;
99+ isVisible ( ) : boolean {
100+ return this . #snackbarElement. parentElement !== null ;
101101 }
102102
103- get hidden ( ) : Promise < void > {
104- return this . #hidden;
103+ close ( ) : void {
104+ if ( ! this . isVisible ( ) ) {
105+ return ;
106+ }
107+
108+ this . #snackbarElement. remove ( ) ;
109+
110+ this . dispatchEvent ( new CustomEvent ( "close" ) ) ;
105111 }
106112
107113 get element ( ) : HTMLElement {
108114 return this . #snackbarElement;
109115 }
110116}
111117
118+ interface SnackbarEventMap {
119+ close : CustomEvent < void > ;
120+ }
121+
122+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
123+ interface Snackbar extends EventTarget {
124+ addEventListener : {
125+ < T extends keyof SnackbarEventMap > (
126+ type : T ,
127+ listener : ( this : Snackbar , ev : SnackbarEventMap [ T ] ) => any ,
128+ options ?: boolean | AddEventListenerOptions ,
129+ ) : void ;
130+ } & HTMLElement [ "addEventListener" ] ;
131+ }
132+
112133let snackbarContainer : SnackbarContainer ;
113134
114135function getSnackbarContainer ( ) : SnackbarContainer {
@@ -132,13 +153,13 @@ class SnackbarContainer {
132153 addSnackbar ( snackbar : Snackbar ) : void {
133154 if ( this . #snackbars. length > 2 ) {
134155 const oldSnackbar = this . #snackbars. shift ( ) ;
135- oldSnackbar ?. hide ( ) ;
156+ oldSnackbar ?. close ( ) ;
136157 }
137158
138159 this . #snackbars. push ( snackbar ) ;
139160 this . #element. prepend ( snackbar . element ) ;
140161
141- void snackbar . hidden . then ( ( ) => {
162+ void snackbar . addEventListener ( "close" , ( ) => {
142163 const i = this . #snackbars. indexOf ( snackbar ) ;
143164 if ( i !== - 1 ) {
144165 this . #snackbars = this . #snackbars. splice ( i , 1 ) ;
0 commit comments