File : dialogs/baseDialog/ModalBaseDialog.js

1
/*
2
Copyright - 2017 2023 - wwwouaiebe - Contact: https://www.ouaie.be/
3
4
This  program is free software;
5
you can redistribute it and/or modify it under the terms of the
6
GNU General Public License as published by the Free Software Foundation;
7
either version 3 of the License, or any later version.
8
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
GNU General Public License for more details.
13
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
*/
18
/*
19
Changes:
20
    - v4.0.0:
21
        - created from v3.6.0
22
Doc reviewed 202208
23
 */
24
25
import theHTMLElementsFactory from '../../core/uiLib/HTMLElementsFactory.js';
26
import theHTMLSanitizer from '../../core/htmlSanitizer/HTMLSanitizer.js';
27
import CancelButtonClickEL from './CancelButtonClickEL.js';
28
import ModalDialogKeyboardKeydownEL from './ModalDialogKeyboardKeydownEL.js';
29
import OkButtonClickEL from './OkButtonClickEL.js';
30
import BackgroundWheelEL from './BackgroundWheelEL.js';
31
import BackgroundContextMenuEL from './BackgroundContextMenuEL.js';
32
import BackgroundDragOverEL from './BackgroundDragOverEL.js';
33
import BackgroundTouchEL from './BackgroundTouchEL.js';
34
import BackgroundMouseEL from './BackgroundMouseEL.js';
35
import BaseDialog from './BaseDialog.js';
36
37
/* ------------------------------------------------------------------------------------------------------------------------- */
38
/**
39
Base class for modal dialogs
40
*/
41
/* ------------------------------------------------------------------------------------------------------------------------- */
42
43
class ModalBaseDialog extends BaseDialog {
44
45
    /**
46
    The background HTMLElement of the dialog
47
    @type {HTMLElement}
48
    */
49
50
    #backgroundHTMLElement;
51
52
    /**
53
    The error HTMLElement of the dialog
54
    @type {HTMLElement}
55
    */
56
57
    #errorHTMLElement;
58
59
    /**
60
    The wait HTMLElement of the dialog
61
    @type {HTMLElement}
62
    */
63
64
    #waitHTMLElement;
65
66
    /**
67
    A flag to avoid all dialogs close when using the esc or enter keys
68
    @type {Boolean}
69
    */
70
71
    #keyboardELEnabled;
72
73
    /**
74
    The ok button
75
    @type {HTMLElement}
76
    */
77
78
    #okButton;
79
80
    /**
81
    The second button if any
82
    @type {?HTMLElement}
83
    */
84
85
    #secondButton;
86
87
    /**
88
    Ok button click event listener
89
    @type {OkButtonClickEL}
90
    */
91
92
    #okButtonClickEL;
93
94
    /**
95
    Cancel button click event listener
96
    @type {CancelButtonClickEL}
97
    */
98
99
    #cancelButtonClickEL;
100
101
    /**
102
    onOk promise function
103
    @type {function}
104
    */
105
106
    #onPromiseOkFunction;
107
108
    /**
109
    onError promise function
110
    @type {function}
111
    */
112
113
    #onPromiseErrorFunction;
114
115
    /**
116
    Keyboard key down event listener
117
    @type {ModalDialogKeyboardKeydownEL}
118
    */
119
120
    #modalDialogKeyboardKeydownEL;
121
122
    /**
123
    Drag over the background event listener
124
    @type {BackgroundDragOverEL}
125
    */
126
127
    #backgroundDragOverEL;
128
129
    /**
130
    Touch on the background event listener
131
    @type {BackgroundTouchEL}
132
    */
133
134
    #backgroundTouchEL;
135
136
    /**
137
    mouseup, mousedown and mousemove event listeners  on the background
138
    @type {BackgroundMouseEL}
139
    */
140
141
    #backgroundMouseEL;
142
143
    /**
144
    Wheel event listener on the background
145
    @type {BackgroundWheelEL}
146
    */
147
148
    #backgroundWheelEL;
149
150
    /**
151
    Context menu event listener on the background
152
    @type {BackgroundContextMenuEL}
153
    */
154
155
    #backgroundContextMenuEL;
156
157
    /**
158
    Create the background
159
    */
160
161
    #createBackgroundHTMLElement ( ) {
162
163
        // A new element covering the entire screen is created, with drag and drop event listeners
164
        this.#backgroundHTMLElement = theHTMLElementsFactory.create ( 'div', { className : 'TravelNotes-Background' } );
165
    }
166
167
    /**
168
    Create the background HTMLElement event listeners.
169
    */
170
171
    #createBackgroundHTMLElementEL ( ) {
172
        this.#backgroundDragOverEL = new BackgroundDragOverEL ( this.mover );
173
        this.#backgroundHTMLElement.addEventListener ( 'dragover', this.#backgroundDragOverEL, false );
174
175
        this.#backgroundWheelEL = new BackgroundWheelEL ( );
176
        this.#backgroundHTMLElement.addEventListener ( 'wheel', this.#backgroundWheelEL, { passive : true }    );
177
178
        this.#backgroundContextMenuEL = new BackgroundContextMenuEL ( );
179
        this.#backgroundHTMLElement.addEventListener ( 'contextmenu', this.#backgroundContextMenuEL, false );
180
181
        this.#backgroundTouchEL = new BackgroundTouchEL ( this );
182
        this.#backgroundHTMLElement.addEventListener ( 'touchstart', this.#backgroundTouchEL, false );
183
        this.#backgroundHTMLElement.addEventListener ( 'touchmove', this.#backgroundTouchEL, false );
184
        this.#backgroundHTMLElement.addEventListener ( 'touchend', this.#backgroundTouchEL, false );
185
        this.#backgroundHTMLElement.addEventListener ( 'touchcancel', this.#backgroundTouchEL, false );
186
187
        this.#backgroundMouseEL = new BackgroundMouseEL ( );
188
        this.#backgroundHTMLElement.addEventListener ( 'mouseup', this.#backgroundMouseEL, false );
189
        this.#backgroundHTMLElement.addEventListener ( 'mousemove', this.#backgroundMouseEL, false );
190
        this.#backgroundHTMLElement.addEventListener ( 'mousedown', this.#backgroundMouseEL, false );
191
    }
192
193
    /**
194
    Create the error HTMLElement
195
    */
196
197
    #createErrorHTMLElement ( ) {
198
        this.#errorHTMLElement = theHTMLElementsFactory.create (
199
            'div',
200
            {
201
                className : 'TravelNotes-ModalBaseDialog-ErrorHTMLElement TravelNotes-Hidden'
202
            }
203
        );
204
        this.addToDialog ( this.#errorHTMLElement );
205
    }
206
207
    /**
208
    Create the dialog wait HTMLElement and animation
209
    */
210
211
    #createWaitHTMLElement ( ) {
212
        theHTMLElementsFactory.create (
213
            'div',
214
            {
215
                className : 'TravelNotes-WaitAnimationBullet'
216
            },
217
            theHTMLElementsFactory.create (
218
                'div',
219
                {
220
                    className : 'TravelNotes-WaitAnimation'
221
                },
222
                this.#waitHTMLElement = theHTMLElementsFactory.create (
223
                    'div',
224
                    {
225
                        className : 'TravelNotes-ModalBaseDialog-WaitHTMLElement  TravelNotes-Hidden'
226
                    }                )
227
            )
228
        );
229
        this.addToDialog ( this.#waitHTMLElement );
230
    }
231
232
    /**
233
    Create the dialog footer
234
    */
235
236
    #createFooterHTMLElement ( ) {
237
        const footerDiv = theHTMLElementsFactory.create (
238
            'div',
239
            {
240
                className : 'TravelNotes-ModalBaseDialog-FooterHTMLElement'
241
            }
242
        );
243
        this.addToDialog ( footerDiv );
244
245
        this.#okButton = theHTMLElementsFactory.create (
246
            'div',
247
            {
248
                textContent : this.options.firstButtonText || '🆗',
249
                className : 'TravelNotes-BaseDialog-Button'
250
            },
251
            footerDiv
252
        );
253
        this.#okButtonClickEL = new OkButtonClickEL ( this );
254
        this.#okButton.addEventListener ( 'click', this.#okButtonClickEL, false );
255
256
        if ( this.options.secondButtonText ) {
257
            this.#secondButton = theHTMLElementsFactory.create (
258
                'div',
259
                {
260
                    textContent : this.options.secondButtonText,
261
                    className : 'TravelNotes-BaseDialog-Button'
262
                },
263
                footerDiv
264
            );
265
            this.#cancelButtonClickEL = new CancelButtonClickEL ( this );
266
            this.#secondButton.addEventListener ( 'click',    this.#cancelButtonClickEL, false    );
267
        }
268
        else {
269
            this.#secondButton = null;
270
        }
271
272
        this.footerHTMLElements.forEach (
273
            footerHTMLElement => footerDiv.appendChild ( footerHTMLElement )
274
        );
275
    }
276
277
    /**
278
    Create the HTML for the dialog
279
    */
280
281
    #createHTML ( ) {
282
        this.#createBackgroundHTMLElement ( );
283
        this.#createBackgroundHTMLElementEL ( );
284
        this.#createErrorHTMLElement ( );
285
        this.#createWaitHTMLElement ( );
286
        this.#createFooterHTMLElement ( );
287
    }
288
289
    /**
290
    The destructor. Remove and set to null the event listeners
291
    */
292
293
    #destructor ( ) {
294
        this.#backgroundHTMLElement.removeEventListener ( 'wheel', this.#backgroundWheelEL, { passive : true }    );
295
        this.#backgroundWheelEL = null;
296
297
        this.#backgroundHTMLElement.removeEventListener ( 'contextmenu', this.#backgroundContextMenuEL, false );
298
        this.#backgroundContextMenuEL = null;
299
300
        this.#backgroundHTMLElement.removeEventListener ( 'dragover', this.#backgroundDragOverEL, false );
301
        this.#backgroundDragOverEL = null;
302
303
        this.#backgroundHTMLElement.removeEventListener ( 'touchstart', this.#backgroundTouchEL, false );
304
        this.#backgroundHTMLElement.removeEventListener ( 'touchmove', this.#backgroundTouchEL, false );
305
        this.#backgroundHTMLElement.removeEventListener ( 'touchend', this.#backgroundTouchEL, false );
306
        this.#backgroundHTMLElement.removeEventListener ( 'touchcancel', this.#backgroundTouchEL, false );
307
        this.#backgroundTouchEL = null;
308
309
        this.#backgroundHTMLElement.removeEventListener ( 'mouseup', this.#backgroundMouseEL, false );
310
        this.#backgroundHTMLElement.removeEventListener ( 'mousemove', this.#backgroundMouseEL, false );
311
        this.#backgroundHTMLElement.removeEventListener ( 'mousedown', this.#backgroundMouseEL, false );
312
        this.#backgroundMouseEL = null;
313
314
        document.removeEventListener ( 'keydown', this.#modalDialogKeyboardKeydownEL, { capture : true } );
315
        this.#modalDialogKeyboardKeydownEL = null;
316
317
        this.#okButton.removeEventListener ( 'click', this.#okButtonClickEL, false );
318
        this.#okButtonClickEL = null;
319
        if ( this.options.secondButtonText ) {
320
            this.#secondButton.removeEventListener ( 'click', this.#cancelButtonClickEL, false    );
321
            this.#cancelButtonClickEL = null;
322
        }
323
324
        document.body.removeChild ( this.#backgroundHTMLElement );
325
    }
326
327
    /**
328
    Build and show the dialog
329
    @param {function} onOk The onOk Promise handler
330
    @param {function} onError The onError Promise handler
331
    */
332
333
    #show ( onOk, onError ) {
334
        this.#onPromiseOkFunction = onOk;
335
        this.#onPromiseErrorFunction = onError;
336
    }
337
338
    /**
339
    the constructor
340
    @param {BaseDialogOptions} options The options of the dialog
341
    */
342
343
    constructor ( options ) {
344
        super ( options );
345
        this.#keyboardELEnabled = true;
346
    }
347
348
    /**
349
    Show the dialog
350
    */
351
352
    show ( ) {
353
        super.show ( );
354
        this.#createHTML ( );
355
        this.addCssClass ( 'TravelNotes-ModalBaseDialog' );
356
        document.body.appendChild ( this.#backgroundHTMLElement );
357
        this.addToBackground ( this.#backgroundHTMLElement );
358
        this.#modalDialogKeyboardKeydownEL = new ModalDialogKeyboardKeydownEL ( this );
359
        document.addEventListener ( 'keydown', this.#modalDialogKeyboardKeydownEL, { capture : true } );
360
        this.mover.centerDialog ( );
361
        return new Promise ( ( onOk, onError ) => this.#show ( onOk, onError ) );
362
    }
363
364
    /**
365
    Cancel button handler. Can be overloaded in the derived classes
366
    */
367
368
    onCancel ( ) {
369
        this.#destructor ( );
370
        super.onCancel ( );
371
        this.#onPromiseErrorFunction ( 'Canceled by user' );
372
    }
373
374
    /**
375
    Called after the ok button will be clicked and before the dialog will be closed.
376
    Can be overloaded in the derived classes.
377
    @return {Boolean} true when the dialog can be closed (all data in the dialog are valid), false otherwise.
378
    */
379
380
    canClose ( ) {
381
        return true;
382
    }
383
384
    /**
385
    Ok button handler. Can be overloaded in the derived classes, but you have always to call super.onOk ( ).
386
    @param {} returnValue a value that will be returned to the onOk handler of the Promise
387
    */
388
389
    onOk ( returnValue ) {
390
        if ( this.canClose ( ) ) {
391
            this.#onPromiseOkFunction ( returnValue );
392
            this.#destructor ( );
393
            super.onOk ( );
394
            return true;
395
        }
396
        return false;
397
    }
398
399
    /**
400
    Show the wait section of the dialog and hide the okbutton
401
    */
402
403
    showWait ( ) {
404
        this.#waitHTMLElement.classList.remove ( 'TravelNotes-Hidden' );
405
        this.#okButton.classList.add ( 'TravelNotes-Hidden' );
406
    }
407
408
    /**
409
    Hide the wait section of the dialog and show the okbutton
410
    */
411
412
    hideWait ( ) {
413
        this.#waitHTMLElement.classList.add ( 'TravelNotes-Hidden' );
414
        this.#okButton.classList.remove ( 'TravelNotes-Hidden' );
415
    }
416
417
    /**
418
    Show the error section of the dialog
419
    @param {String} errorText The text to display in the error section
420
    */
421
422
    showError ( errorText ) {
423
        this.#errorHTMLElement.textContent = '';
424
        theHTMLSanitizer.sanitizeToHtmlElement ( errorText, this.#errorHTMLElement );
425
        this.#errorHTMLElement.classList.remove ( 'TravelNotes-Hidden' );
426
    }
427
428
    /**
429
    Hide the error section of the dialog
430
    */
431
432
    hideError ( ) {
433
        this.#errorHTMLElement.textContent = '';
434
        this.#errorHTMLElement.classList.add ( 'TravelNotes-Hidden' );
435
    }
436
437
    /**
438
    An array with the HTMLElements that have to be added in the footer of the dialog
439
    Can be overloaded in the derived classes
440
    @type {Array.<HTMLElement>}
441
    */
442
443
    get footerHTMLElements ( ) { return []; }
444
445
    /**
446
    A flag to avoid all dialogs close when using the esc or enter keys
447
    @type {Boolean}
448
    */
449
450
    get keyboardELEnabled ( ) { return this.#keyboardELEnabled; }
451
452
    set keyboardELEnabled ( keyboardELEnabled ) { this.#keyboardELEnabled = keyboardELEnabled; }
453
}
454
455
export default ModalBaseDialog;
456
457
/* --- End of file --------------------------------------------------------------------------------------------------------- */
458