File : toolbars/baseToolbar/BaseToolbar.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 ToolbarItemsContainer from './ToolbarItemsContainer.js';
27
import ButtonHTMLElementClickEL from './ButtonHTMLElementClickEL.js';
28
import ButtonHTMLElementTouchEL from './ButtonHTMLElementTouchEL.js';
29
import ButtonsHTMLElementTouchEL from './ButtonsHTMLElementTouchEL.js';
30
import ButtonsHTMLElementWheelEL from './ButtonsHTMLElementWheelEL.js';
31
import theConfig from '../../data/Config.js';
32
import { ZERO } from '../../main/Constants.js';
33
34
/* ------------------------------------------------------------------------------------------------------------------------- */
35
/**
36
A base class for realisation of toolbars
37
*/
38
/* ------------------------------------------------------------------------------------------------------------------------- */
39
40
class BaseToolbar {
41
42
    /**
43
    The main HTMLElement of the toolbar
44
    @type {HTMLElement}
45
    */
46
47
    #toolbarHTMLElement;
48
49
    /**
50
    The HTML element that contains the buttons
51
    @type {HTMLElement}
52
    */
53
54
    #buttonsHTMLElement;
55
56
    /**
57
    Timer id for the mouse leave event
58
    @type {Number}
59
    */
60
61
    #timerId;
62
63
    /**
64
    The wheel event listener for the buttons container
65
    @type {ButtonsHTMLElementWheelEL}
66
    */
67
68
    #buttonsHTMLElementWheelEL;
69
70
    /**
71
    The touch event listener for the buttons container
72
    @type {ButtonsHTMLElementTouchEL}
73
    */
74
75
    #buttonsHTMLElementTouchEL;
76
77
    /**
78
    The click event listener for the buttons
79
    @type {ButtonHTMLElementClickEL}
80
    */
81
82
    #buttonHTMLElementClickEL;
83
84
    /**
85
    The touch event listener for the buttons
86
    @type {ButtonHTMLElementTouchEL}
87
    */
88
89
    #buttonHTMLElementTouchEL;
90
91
    /**
92
    A boolean saving the the current state of the toolbar
93
    @type {boolean}
94
     */
95
96
    #isShow;
97
98
    /**
99
    An object with the ToolbarItems array. Needed to have an object to share with EL.
100
    @type {ToolbarItemsContainer}
101
    */
102
103
    #toolbarItemsContainer;
104
105
    /**
106
    The max delay between a mouseenter and a click event to consider the two events as a single event
107
    @type {Number}
108
    */
109
110
    // eslint-disable-next-line no-magic-numbers
111
    static get #MOUSE_EVENT_MAX_DELAY ( ) { return 100; }
112
113
    /**
114
    The timestamp of the last mouseenter or click event
115
    @type {Number}
116
    */
117
118
    #lastMouseEventTimestamp;
119
120
    /**
121
    Add a button to the toolbar
122
    @param {ToolbarItem} toolbarItem The toolbar item for witch the button will be created
123
    @param {Number} index The position of the toolbar item in the #toolbarItemsContainer.toolbarItemsArray
124
    */
125
126
    #addButton ( toolbarItem, index ) {
127
        const buttonHTMLElement = theHTMLElementsFactory.create (
128
            'div',
129
            {
130
                className : 'TravelNotes-BaseToolbar-ButtonHTMLElement',
131
                textContent : toolbarItem.textContent,
132
                title : toolbarItem.title,
133
                dataset : { ItemId : index }
134
            },
135
            this.#buttonsHTMLElement
136
        );
137
        buttonHTMLElement.addEventListener ( 'click', this.#buttonHTMLElementClickEL, false );
138
        buttonHTMLElement.addEventListener ( 'touchstart', this.#buttonHTMLElementTouchEL, false );
139
        buttonHTMLElement.addEventListener ( 'touchend', this.#buttonHTMLElementTouchEL, false );
140
    }
141
142
    /**
143
    Show the toolbar
144
    */
145
146
    #show ( ) {
147
148
        // container for the buttons
149
        this.#buttonsHTMLElement = theHTMLElementsFactory.create (
150
            'div',
151
            {
152
                className : 'TravelNotes-BaseToolbar-ButtonsHTMLElement'
153
            },
154
            this.#toolbarHTMLElement
155
        );
156
157
        // Calling the addToolbarItems ( ) method of the derived classes
158
        this.#toolbarItemsContainer.toolbarItemsArray = [ ];
159
        this.addToolbarItems ( );
160
161
        // adding buttons
162
        this.#toolbarItemsContainer.toolbarItemsArray.forEach (
163
            ( toolbarItem, index ) => {
164
                this.#addButton ( toolbarItem, index );
165
            }
166
        );
167
168
        // adding wheel event
169
        this.#buttonsHTMLElement.addEventListener ( 'wheel', this.#buttonsHTMLElementWheelEL, { passive : true } );
170
171
        // adding touch event listeners
172
        this.#buttonsHTMLElement.addEventListener ( 'touchstart', this.#buttonsHTMLElementTouchEL, false );
173
        this.#buttonsHTMLElement.addEventListener ( 'touchmove', this.#buttonsHTMLElementTouchEL, false );
174
        this.#buttonsHTMLElement.addEventListener ( 'touchend', this.#buttonsHTMLElementTouchEL, false );
175
        this.#isShow = true;
176
    }
177
178
    /**
179
    Click on the toolbar event listener. It's needed for touch devices where the mouseenter EL don't work.
180
    Remember that toolbars are global objects never deleted, so we can have EL as simple member methods
181
    @param {Event} mouseEvent the trigered event
182
    */
183
184
    #toolbarHTMLElementClickEL ( mouseEvent ) {
185
186
        // When the delay is lower than #MOUSE_EVENT_MAX_DELAY     we consider that the click event and the
187
        // mouse enter event are trigered by the same user action on touch devices
188
        // and the click event is cancelled
189
        if ( BaseToolbar.#MOUSE_EVENT_MAX_DELAY > mouseEvent.timeStamp - this.#lastMouseEventTimestamp ) {
190
            return;
191
        }
192
193
        this.#toolbarHTMLElementMouseEnterEL ( mouseEvent );
194
    }
195
196
    /**
197
    Mouse enter on the toolbar event listener
198
    Remember that toolbars are global objects never deleted, so we can have EL as simple member methods
199
    @param {Event} mouseEvent the trigered event
200
    */
201
202
    #toolbarHTMLElementMouseEnterEL ( mouseEvent ) {
203
204
        // Saving the time stamp
205
        this.#lastMouseEventTimestamp = mouseEvent.timeStamp;
206
207
        if ( this.#isShow ) {
208
            if ( this.#timerId ) {
209
                clearTimeout ( this.#timerId );
210
                this.#timerId = null;
211
                return;
212
            }
213
214
            // Hiding the toolbar if already show. Needed for touch devices for closing the toolbar by clicking on it
215
            this.hide ( );
216
        }
217
        else {
218
            this.#show ( );
219
        }
220
    }
221
222
    /**
223
    Mouse leave the toolbar event listener
224
    Remember that toolbars are global objects never deleted, so we can have EL as simple member methods
225
    */
226
227
    #toolbarHTMLElementMouseLeaveEL ( ) {
228
        if ( this.#isShow ) {
229
            this.#timerId = setTimeout ( ( ) => this.hide ( ), theConfig.toolbars.timeOut );
230
        }
231
    }
232
233
    /**
234
    The constructor
235
    */
236
237
    constructor ( ) {
238
        Object.freeze ( this );
239
    }
240
241
    /**
242
    Hide the toolbar
243
    */
244
245
    hide ( ) {
246
247
        // cleaning the timer
248
        if ( this.#timerId ) {
249
            clearTimeout ( this.#timerId );
250
            this.#timerId = null;
251
        }
252
253
        // removing buttons
254
        while ( this.#buttonsHTMLElement.firstChild ) {
255
            const buttonHTMLElement = this.#buttonsHTMLElement.firstChild;
256
            buttonHTMLElement.removeEventListener ( 'click', this.#buttonHTMLElementClickEL, false );
257
            buttonHTMLElement.removeEventListener ( 'touchstart', this.#buttonHTMLElementTouchEL, false );
258
            buttonHTMLElement.removeEventListener ( 'touchend', this.#buttonHTMLElementTouchEL, false );
259
            this.#buttonsHTMLElement.removeChild ( buttonHTMLElement );
260
        }
261
262
        // removing the buttons container
263
        this.#buttonsHTMLElement.removeEventListener ( 'wheel', this.#buttonsHTMLElementWheelEL, { passive : true } );
264
        this.#buttonsHTMLElement.removeEventListener ( 'touchstart', this.#buttonsHTMLElementTouchEL, false );
265
        this.#buttonsHTMLElement.removeEventListener ( 'touchmove', this.#buttonsHTMLElementTouchEL, false );
266
        this.#buttonsHTMLElement.removeEventListener ( 'touchend', this.#buttonsHTMLElementTouchEL, false );
267
        this.#toolbarHTMLElement.removeChild ( this.#buttonsHTMLElement );
268
        this.#buttonsHTMLElement = null;
269
270
        this.#isShow = false;
271
    }
272
273
    /**
274
    create the toolbar container and header
275
    @param {String} headerText The text to display on the header of the toolbar
276
    @param {TOOLBAR_POSITION} position The position of the toolbar on the screen
277
    */
278
279
    createUI ( headerText, position ) {
280
281
        // Toolbar already created... return
282
        if ( this.#toolbarHTMLElement ) {
283
            return false;
284
        }
285
286
        // init of members
287
        this.#timerId = null;
288
        this.#isShow = false;
289
        this.#lastMouseEventTimestamp = ZERO;
290
        this.#toolbarItemsContainer = new ToolbarItemsContainer ( );
291
292
        // EL creation
293
        this.#buttonsHTMLElementWheelEL = new ButtonsHTMLElementWheelEL ( );
294
        this.#buttonsHTMLElementTouchEL = new ButtonsHTMLElementTouchEL ( );
295
        this.#buttonHTMLElementClickEL = new ButtonHTMLElementClickEL ( this.#toolbarItemsContainer );
296
        this.#buttonHTMLElementTouchEL = new ButtonHTMLElementTouchEL ( this, this.#toolbarItemsContainer );
297
298
        // Toolbar container creation
299
        this.#toolbarHTMLElement =
300
            theHTMLElementsFactory.create (
301
                'div',
302
                {
303
                    className : 'TravelNotes-BaseToolbar-ToolbarHTMLElement ' + position
304
                },
305
                document.body
306
            );
307
        this.#toolbarHTMLElement.addEventListener (
308
            'click',
309
            mouseEvent => this.#toolbarHTMLElementClickEL ( mouseEvent ),
310
            false
311
        );
312
        this.#toolbarHTMLElement.addEventListener (
313
            'mouseenter',
314
            mouseEvent => this.#toolbarHTMLElementMouseEnterEL ( mouseEvent ),
315
            false
316
        );
317
        this.#toolbarHTMLElement.addEventListener (
318
            'mouseleave',
319
            mouseEvent => this.#toolbarHTMLElementMouseLeaveEL ( mouseEvent ),
320
            false
321
        );
322
323
        // Header text creation
324
        theHTMLElementsFactory.create (
325
            'div',
326
            {
327
                className : 'TravelNotes-BaseToolbar-HeaderTextHTMLElement',
328
                textContent : headerText
329
            },
330
            theHTMLElementsFactory.create (
331
                'div',
332
                {
333
                    className : 'TravelNotes-BaseToolbar-HeaderHTMLElement'
334
                },
335
                this.#toolbarHTMLElement
336
            )
337
        );
338
    }
339
340
    /**
341
    Add a ToolbarItem on the collection of ToolbarItems
342
    @param {ToolbarItem} toolbarItem An object with data to configure the button
343
    */
344
345
    addToolbarItem ( toolbarItem ) {
346
        this.#toolbarItemsContainer.toolbarItemsArray.push ( toolbarItem );
347
    }
348
349
    /**
350
    Add a css class to the #toolbarHTMLElement, so some css settings can be overloaded for a specific toolbar
351
    @param {String} cssClass The css class to add
352
    */
353
354
    addCssClass ( cssClass ) {
355
        this.#toolbarHTMLElement.classList.add ( cssClass );
356
    }
357
}
358
359
export default BaseToolbar;
360
361
/* --- End of file --------------------------------------------------------------------------------------------------------- */
362