@ecl/gallery 5.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/gallery.js ADDED
@@ -0,0 +1,779 @@
1
+ import { queryOne, queryAll } from '@ecl/dom-utils';
2
+ import { createFocusTrap } from 'focus-trap';
3
+
4
+ /**
5
+ * @param {HTMLElement} element DOM element for component instantiation and scope
6
+ * @param {Object} options
7
+ * @param {String} options.galleryItemSelector Selector for gallery element
8
+ * @param {String} options.descriptionSelector Selector for gallery description element
9
+ * @param {String} options.titleSelector Selector for gallery title element
10
+ * @param {String} options.closeButtonSelector Selector for close button element
11
+ * @param {String} options.allButtonSelector Selector for view all button element
12
+ * @param {String} options.overlaySelector Selector for gallery overlay element
13
+ * @param {String} options.overlayHeaderSelector Selector for gallery overlay header element
14
+ * @param {String} options.overlayFooterSelector Selector for gallery overlay footer element
15
+ * @param {String} options.overlayMediaSelector Selector for gallery overlay media element
16
+ * @param {String} options.overlayCounterCurrentSelector Selector for gallery overlay current number element
17
+ * @param {String} options.overlayCounterMaxSelector Selector for display of number of elements in the gallery overlay
18
+ * @param {String} options.overlayDownloadSelector Selector for gallery overlay download element
19
+ * @param {String} options.overlayShareSelector Selector for gallery overlay share element
20
+ * @param {String} options.overlayDescriptionSelector Selector for gallery overlay description element
21
+ * @param {String} options.overlayPreviousSelector Selector for gallery overlay previous link element
22
+ * @param {String} options.overlayNextSelector Selector for gallery overlay next link element
23
+ * @param {String} options.videoTitleSelector Selector for video title
24
+ * @param {Boolean} options.attachClickListener Whether or not to bind click events
25
+ * @param {Boolean} options.attachKeyListener Whether or not to bind keyup events
26
+ */
27
+ export class Gallery {
28
+ /**
29
+ * @static
30
+ * Shorthand for instance creation and initialisation.
31
+ *
32
+ * @param {HTMLElement} root DOM element for component instantiation and scope
33
+ *
34
+ * @return {Gallery} An instance of Gallery.
35
+ */
36
+ static autoInit(root, { GALLERY: defaultOptions = {} } = {}) {
37
+ const gallery = new Gallery(root, defaultOptions);
38
+ gallery.init();
39
+ root.ECLGallery = gallery;
40
+ return gallery;
41
+ }
42
+
43
+ constructor(
44
+ element,
45
+ {
46
+ expandableSelector = 'data-ecl-gallery-not-expandable',
47
+ galleryItemSelector = '[data-ecl-gallery-item]',
48
+ descriptionSelector = '[data-ecl-gallery-description]',
49
+ titleSelector = '[data-ecl-gallery-title]',
50
+ noOverlaySelector = 'data-ecl-gallery-no-overlay',
51
+ itemsLimitSelector = 'data-ecl-gallery-visible-items',
52
+ closeButtonSelector = '[data-ecl-gallery-close]',
53
+ viewAllSelector = '[data-ecl-gallery-all]',
54
+ viewAllLabelSelector = 'data-ecl-gallery-collapsed-label',
55
+ viewAllExpandedLabelSelector = 'data-ecl-gallery-expanded-label',
56
+ countSelector = '[data-ecl-gallery-count]',
57
+ overlaySelector = '[data-ecl-gallery-overlay]',
58
+ overlayHeaderSelector = '[data-ecl-gallery-overlay-header]',
59
+ overlayFooterSelector = '[data-ecl-gallery-overlay-footer]',
60
+ overlayMediaSelector = '[data-ecl-gallery-overlay-media]',
61
+ overlayCounterCurrentSelector = '[data-ecl-gallery-overlay-counter-current]',
62
+ overlayCounterMaxSelector = '[data-ecl-gallery-overlay-counter-max]',
63
+ overlayDownloadSelector = '[data-ecl-gallery-overlay-download]',
64
+ overlayShareSelector = '[data-ecl-gallery-overlay-share]',
65
+ overlayDescriptionSelector = '[data-ecl-gallery-overlay-description]',
66
+ overlayPreviousSelector = '[data-ecl-gallery-overlay-previous]',
67
+ overlayNextSelector = '[data-ecl-gallery-overlay-next]',
68
+ videoTitleSelector = 'data-ecl-gallery-item-video-title',
69
+ attachClickListener = true,
70
+ attachKeyListener = true,
71
+ attachResizeListener = true,
72
+ } = {},
73
+ ) {
74
+ // Check element
75
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) {
76
+ throw new TypeError(
77
+ 'DOM element should be given to initialize this widget.',
78
+ );
79
+ }
80
+
81
+ this.element = element;
82
+
83
+ // Options
84
+ this.galleryItemSelector = galleryItemSelector;
85
+ this.descriptionSelector = descriptionSelector;
86
+ this.titleSelector = titleSelector;
87
+ this.closeButtonSelector = closeButtonSelector;
88
+ this.viewAllSelector = viewAllSelector;
89
+ this.countSelector = countSelector;
90
+ this.itemsLimitSelector = itemsLimitSelector;
91
+ this.overlaySelector = overlaySelector;
92
+ this.noOverlaySelector = noOverlaySelector;
93
+ this.overlayHeaderSelector = overlayHeaderSelector;
94
+ this.overlayFooterSelector = overlayFooterSelector;
95
+ this.overlayMediaSelector = overlayMediaSelector;
96
+ this.overlayCounterCurrentSelector = overlayCounterCurrentSelector;
97
+ this.overlayCounterMaxSelector = overlayCounterMaxSelector;
98
+ this.overlayDownloadSelector = overlayDownloadSelector;
99
+ this.overlayShareSelector = overlayShareSelector;
100
+ this.overlayDescriptionSelector = overlayDescriptionSelector;
101
+ this.overlayPreviousSelector = overlayPreviousSelector;
102
+ this.overlayNextSelector = overlayNextSelector;
103
+ this.attachClickListener = attachClickListener;
104
+ this.attachKeyListener = attachKeyListener;
105
+ this.attachResizeListener = attachResizeListener;
106
+ this.viewAllLabelSelector = viewAllLabelSelector;
107
+ this.viewAllExpandedLabelSelector = viewAllExpandedLabelSelector;
108
+ this.expandableSelector = expandableSelector;
109
+ this.videoTitleSelector = videoTitleSelector;
110
+
111
+ // Private variables
112
+ this.galleryItems = null;
113
+ this.closeButton = null;
114
+ this.viewAll = null;
115
+ this.count = null;
116
+ this.overlay = null;
117
+ this.overlayHeader = null;
118
+ this.overlayFooter = null;
119
+ this.overlayMedia = null;
120
+ this.overlayCounterCurrent = null;
121
+ this.overlayCounterMax = null;
122
+ this.overlayDownload = null;
123
+ this.overlayShare = null;
124
+ this.overlayDescription = null;
125
+ this.overlayPrevious = null;
126
+ this.overlayNext = null;
127
+ this.selectedItem = null;
128
+ this.focusTrap = null;
129
+ this.isDesktop = false;
130
+ this.resizeTimer = null;
131
+ this.visibleItems = 0;
132
+ this.breakpointMd = 768;
133
+ this.breakpointLg = 996;
134
+ this.imageHeight = 185;
135
+ this.imageHeightBig = 260;
136
+
137
+ // Bind `this` for use in callbacks
138
+ this.iframeResize = this.iframeResize.bind(this);
139
+ this.handleClickOnCloseButton = this.handleClickOnCloseButton.bind(this);
140
+ this.handleClickOnViewAll = this.handleClickOnViewAll.bind(this);
141
+ this.handleClickOnItem = this.handleClickOnItem.bind(this);
142
+ this.preventClickOnItem = this.preventClickOnItem.bind(this);
143
+ this.handleKeyPressOnItem = this.handleKeyPressOnItem.bind(this);
144
+ this.handleClickOnPreviousButton =
145
+ this.handleClickOnPreviousButton.bind(this);
146
+ this.handleClickOnNextButton = this.handleClickOnNextButton.bind(this);
147
+ this.handleKeyboard = this.handleKeyboard.bind(this);
148
+ this.handleResize = this.handleResize.bind(this);
149
+ }
150
+
151
+ /**
152
+ * Initialise component.
153
+ */
154
+ init() {
155
+ if (!ECL) {
156
+ throw new TypeError('Called init but ECL is not present');
157
+ }
158
+ ECL.components = ECL.components || new Map();
159
+ // Query elements
160
+ this.expandable = !this.element.hasAttribute(this.expandableSelector);
161
+ this.visibleItems = this.element.getAttribute(this.itemsLimitSelector);
162
+ this.galleryItems = queryAll(this.galleryItemSelector, this.element);
163
+ this.closeButton = queryOne(this.closeButtonSelector, this.element);
164
+ this.noOverlay = this.element.hasAttribute(this.noOverlaySelector);
165
+ if (this.expandable) {
166
+ this.viewAll = queryOne(this.viewAllSelector, this.element);
167
+ this.viewAllLabel =
168
+ this.viewAll.getAttribute(this.viewAllLabelSelector) ||
169
+ this.viewAll.innerText;
170
+ this.viewAllLabelExpanded =
171
+ this.viewAll.getAttribute(this.viewAllExpandedLabelSelector) ||
172
+ this.viewAllLabel;
173
+ }
174
+ this.count = queryOne(this.countSelector, this.element);
175
+
176
+ // Bind click event on view all (open first item)
177
+ if (this.attachClickListener && this.viewAll) {
178
+ this.viewAll.addEventListener('click', this.handleClickOnViewAll);
179
+ }
180
+ if (!this.noOverlay) {
181
+ this.overlay = queryOne(this.overlaySelector, this.element);
182
+ this.overlayHeader = queryOne(this.overlayHeaderSelector, this.overlay);
183
+ this.overlayFooter = queryOne(this.overlayFooterSelector, this.overlay);
184
+ this.overlayMedia = queryOne(this.overlayMediaSelector, this.overlay);
185
+ this.overlayCounterCurrent = queryOne(
186
+ this.overlayCounterCurrentSelector,
187
+ this.overlay,
188
+ );
189
+ this.overlayCounterMax = queryOne(
190
+ this.overlayCounterMaxSelector,
191
+ this.overlay,
192
+ );
193
+ this.overlayDownload = queryOne(
194
+ this.overlayDownloadSelector,
195
+ this.overlay,
196
+ );
197
+ this.overlayShare = queryOne(this.overlayShareSelector, this.overlay);
198
+ this.overlayDescription = queryOne(
199
+ this.overlayDescriptionSelector,
200
+ this.overlay,
201
+ );
202
+ this.overlayPrevious = queryOne(
203
+ this.overlayPreviousSelector,
204
+ this.overlay,
205
+ );
206
+ this.overlayNext = queryOne(this.overlayNextSelector, this.overlay);
207
+
208
+ // Create focus trap
209
+ this.focusTrap = createFocusTrap(this.overlay, {
210
+ escapeDeactivates: false,
211
+ returnFocusOnDeactivate: false,
212
+ });
213
+
214
+ // Polyfill to support <dialog>
215
+ this.isDialogSupported = true;
216
+ if (!window.HTMLDialogElement) {
217
+ this.isDialogSupported = false;
218
+ }
219
+
220
+ // Bind click event on close button
221
+ if (this.attachClickListener && this.closeButton) {
222
+ this.closeButton.addEventListener(
223
+ 'click',
224
+ this.handleClickOnCloseButton,
225
+ );
226
+ }
227
+
228
+ // Bind click event on gallery items
229
+ if (this.attachClickListener && this.galleryItems) {
230
+ this.galleryItems.forEach((galleryItem) => {
231
+ if (this.attachClickListener) {
232
+ galleryItem.addEventListener('click', this.handleClickOnItem);
233
+ }
234
+ if (this.attachKeyListener) {
235
+ galleryItem.addEventListener('keyup', this.handleKeyPressOnItem);
236
+ }
237
+ });
238
+ }
239
+
240
+ // Bind click event on previous button
241
+ if (this.attachClickListener && this.overlayPrevious) {
242
+ this.overlayPrevious.addEventListener(
243
+ 'click',
244
+ this.handleClickOnPreviousButton,
245
+ );
246
+ }
247
+
248
+ // Bind click event on next button
249
+ if (this.attachClickListener && this.overlayNext) {
250
+ this.overlayNext.addEventListener(
251
+ 'click',
252
+ this.handleClickOnNextButton,
253
+ );
254
+ }
255
+
256
+ // Bind other close event
257
+ if (!this.isDialogSupported && this.attachKeyListener && this.overlay) {
258
+ this.overlay.addEventListener('keyup', this.handleKeyboard);
259
+ }
260
+ if (this.isDialogSupported && this.overlay) {
261
+ this.overlay.addEventListener('close', this.handleClickOnCloseButton);
262
+ }
263
+ } else {
264
+ this.galleryItems.forEach((galleryItem) => {
265
+ galleryItem.classList.add('ecl-gallery__item__link--frozen');
266
+ galleryItem.addEventListener('click', this.preventClickOnItem);
267
+ });
268
+ }
269
+
270
+ // Bind resize events
271
+ if (this.attachResizeListener) {
272
+ window.addEventListener('resize', this.handleResize);
273
+ }
274
+
275
+ // Init display of gallery items
276
+ if (this.expandable) {
277
+ this.checkScreen();
278
+ this.hideItems();
279
+ }
280
+
281
+ // Add number to gallery items
282
+ this.galleryItems.forEach((galleryItem, key) => {
283
+ galleryItem.setAttribute('data-ecl-gallery-item-id', key);
284
+ });
285
+
286
+ // Update counter
287
+ if (this.count) {
288
+ this.count.innerHTML = this.galleryItems.length;
289
+ }
290
+
291
+ // Set ecl initialized attribute
292
+ this.element.setAttribute('data-ecl-auto-initialized', 'true');
293
+ ECL.components.set(this.element, this);
294
+ }
295
+
296
+ /**
297
+ * Destroy component.
298
+ */
299
+ destroy() {
300
+ if (this.attachClickListener && this.closeButton) {
301
+ this.closeButton.removeEventListener(
302
+ 'click',
303
+ this.handleClickOnCloseButton,
304
+ );
305
+ }
306
+
307
+ if (this.attachClickListener && this.viewAll) {
308
+ this.viewAll.removeEventListener('click', this.handleClickOnViewAll);
309
+ }
310
+
311
+ if (this.attachClickListener && this.galleryItems) {
312
+ this.galleryItems.forEach((galleryItem) => {
313
+ galleryItem.removeEventListener('click', this.handleClickOnItem);
314
+ });
315
+ }
316
+
317
+ if (this.attachClickListener && this.overlayPrevious) {
318
+ this.overlayPrevious.removeEventListener(
319
+ 'click',
320
+ this.handleClickOnPreviousButton,
321
+ );
322
+ }
323
+
324
+ if (this.attachClickListener && this.overlayNext) {
325
+ this.overlayNext.removeEventListener(
326
+ 'click',
327
+ this.handleClickOnNextButton,
328
+ );
329
+ }
330
+
331
+ if (!this.isDialogSupported && this.attachKeyListener && this.overlay) {
332
+ this.overlay.removeEventListener('keyup', this.handleKeyboard);
333
+ }
334
+ if (this.isDialogSupported && this.overlay) {
335
+ this.overlay.removeEventListener('close', this.handleClickOnCloseButton);
336
+ }
337
+
338
+ if (this.attachResizeListener) {
339
+ window.removeEventListener('resize', this.handleResize);
340
+ }
341
+
342
+ if (this.element) {
343
+ this.element.removeAttribute('data-ecl-auto-initialized');
344
+ ECL.components.delete(this.element);
345
+ }
346
+ }
347
+
348
+ /**
349
+ * Check if current display is desktop or mobile
350
+ */
351
+ checkScreen() {
352
+ if (window.innerWidth > this.breakpointMd) {
353
+ this.isDesktop = true;
354
+ } else {
355
+ this.isDesktop = false;
356
+ }
357
+ if (window.innerWidth > this.breakpointLg) {
358
+ this.isLarge = true;
359
+ }
360
+ }
361
+
362
+ iframeResize(iframe) {
363
+ if (!iframe && this.overlay) {
364
+ iframe = queryOne('iframe', this.overlay);
365
+ }
366
+
367
+ if (iframe) {
368
+ const width = window.innerWidth;
369
+
370
+ setTimeout(() => {
371
+ const height =
372
+ this.overlay.clientHeight -
373
+ this.overlayHeader.clientHeight -
374
+ this.overlayFooter.clientHeight;
375
+
376
+ if (width > height) {
377
+ iframe.setAttribute('height', `${height}px`);
378
+
379
+ if ((height * 16) / 9 > width) {
380
+ iframe.setAttribute('width', `${width - 0.05 * width}px`);
381
+ } else {
382
+ iframe.setAttribute('width', `${(height * 16) / 9}px`);
383
+ }
384
+ } else {
385
+ iframe.setAttribute('width', `${width}px`);
386
+ if ((width * 4) / 3 > height) {
387
+ iframe.setAttribute('height', `${height - 0.05 * height}px`);
388
+ } else {
389
+ iframe.setAttribute('height', `${(width * 4) / 3}px`);
390
+ }
391
+ }
392
+ }, 0);
393
+ }
394
+ }
395
+
396
+ /**
397
+ * @param {Int} rows/item number
398
+ *
399
+ * Hide several gallery items by default
400
+ * - 2 "lines" of items on desktop
401
+ * - only 3 items on mobile or the desired rows or items
402
+ * when using the view more button.
403
+ */
404
+ hideItems(plus = 0) {
405
+ if (!this.viewAll || this.viewAll.expanded) return;
406
+
407
+ if (this.isDesktop) {
408
+ let hiddenItemIds = [];
409
+ // We should browse the list first to mark the items to be hidden, and hide them later
410
+ // otherwise, it will interfer with the calculus
411
+ this.galleryItems.forEach((galleryItem, key) => {
412
+ galleryItem.parentNode.classList.remove('ecl-gallery__item--hidden');
413
+ if (key >= Number(this.visibleItems) + Number(plus)) {
414
+ hiddenItemIds = [...hiddenItemIds, key];
415
+ }
416
+ });
417
+ hiddenItemIds.forEach((id) => {
418
+ this.galleryItems[id].parentNode.classList.add(
419
+ 'ecl-gallery__item--hidden',
420
+ );
421
+ });
422
+ return;
423
+ }
424
+
425
+ this.galleryItems.forEach((galleryItem, key) => {
426
+ if (key > 2 + Number(plus)) {
427
+ galleryItem.parentNode.classList.add('ecl-gallery__item--hidden');
428
+ } else {
429
+ galleryItem.parentNode.classList.remove('ecl-gallery__item--hidden');
430
+ }
431
+ });
432
+ }
433
+
434
+ /**
435
+ * Trigger events on resize
436
+ * Uses a debounce, for performance
437
+ */
438
+ handleResize() {
439
+ clearTimeout(this.resizeTimer);
440
+ this.resizeTimer = setTimeout(() => {
441
+ this.checkScreen();
442
+ this.hideItems();
443
+ this.iframeResize();
444
+ }, 200);
445
+ }
446
+
447
+ /**
448
+ * @param {HTMLElement} selectedItem Media element
449
+ */
450
+ updateOverlay(selectedItem) {
451
+ this.selectedItem = selectedItem;
452
+ const embeddedVideo = selectedItem.getAttribute(
453
+ 'data-ecl-gallery-item-embed-src',
454
+ );
455
+ const embeddedVideoAudio = selectedItem.getAttribute(
456
+ 'data-ecl-gallery-item-embed-audio',
457
+ );
458
+ const video = queryOne('video', selectedItem);
459
+ let mediaElement = null;
460
+
461
+ // Update media
462
+ if (embeddedVideo != null) {
463
+ // Media is a embedded video
464
+ mediaElement = document.createElement('div');
465
+ mediaElement.classList.add('ecl-gallery__slider-embed');
466
+
467
+ const mediaAudio = document.createElement('div');
468
+ mediaAudio.classList.add('ecl-gallery__slider-embed-audio');
469
+ if (embeddedVideoAudio) {
470
+ mediaAudio.innerHTML = embeddedVideoAudio;
471
+ }
472
+
473
+ const iframeUrl = new URL(embeddedVideo);
474
+
475
+ const mediaIframe = document.createElement('iframe');
476
+ mediaIframe.setAttribute('src', iframeUrl);
477
+ mediaIframe.setAttribute('frameBorder', '0');
478
+
479
+ // Update iframe title
480
+ const videoTitle = selectedItem.getAttribute(this.videoTitleSelector);
481
+ if (videoTitle) {
482
+ mediaIframe.setAttribute('title', videoTitle);
483
+ }
484
+
485
+ if (this.overlayMedia) {
486
+ if (embeddedVideoAudio) {
487
+ mediaElement.appendChild(mediaAudio);
488
+ }
489
+ mediaElement.appendChild(mediaIframe);
490
+ this.overlayMedia.innerHTML = '';
491
+ this.overlayMedia.appendChild(mediaElement);
492
+ }
493
+ this.iframeResize(mediaIframe);
494
+ } else if (video != null) {
495
+ // Media is a video
496
+ mediaElement = document.createElement('video');
497
+ mediaElement.setAttribute('poster', video.poster);
498
+ mediaElement.setAttribute('controls', 'controls');
499
+ mediaElement.classList.add('ecl-gallery__slider-video');
500
+
501
+ // Update video title
502
+ const videoTitle = selectedItem.getAttribute(this.videoTitleSelector);
503
+ if (videoTitle) {
504
+ mediaElement.setAttribute('aria-label', videoTitle);
505
+ }
506
+
507
+ if (this.overlayMedia) {
508
+ this.overlayMedia.innerHTML = '';
509
+ this.overlayMedia.appendChild(mediaElement);
510
+ }
511
+
512
+ // Get sources
513
+ const sources = queryAll('source', video);
514
+ sources.forEach((source) => {
515
+ const sourceTag = document.createElement('source');
516
+ sourceTag.setAttribute('src', source.getAttribute('src'));
517
+ sourceTag.setAttribute('type', source.getAttribute('type'));
518
+ mediaElement.appendChild(sourceTag);
519
+ });
520
+
521
+ // Get tracks
522
+ const tracks = queryAll('track', video);
523
+ tracks.forEach((track) => {
524
+ const trackTag = document.createElement('track');
525
+ trackTag.setAttribute('src', track.getAttribute('src'));
526
+ trackTag.setAttribute('kind', track.getAttribute('kind'));
527
+ trackTag.setAttribute('srclang', track.getAttribute('srcLang'));
528
+ trackTag.setAttribute('label', track.getAttribute('label'));
529
+ mediaElement.appendChild(trackTag);
530
+ });
531
+
532
+ mediaElement.load();
533
+ } else {
534
+ // Media is an image
535
+ const picture = queryOne('.ecl-gallery__picture', selectedItem);
536
+ const image = queryOne('img', picture);
537
+ if (picture) {
538
+ image.classList.remove('ecl-gallery__image');
539
+ mediaElement = picture.cloneNode(true);
540
+ } else {
541
+ // backward compatibility
542
+ mediaElement = document.createElement('img');
543
+ mediaElement.setAttribute('src', image.getAttribute('src'));
544
+ mediaElement.setAttribute('alt', image.getAttribute('alt'));
545
+ }
546
+ mediaElement.classList.add('ecl-gallery__slider-image');
547
+
548
+ if (this.overlayMedia) {
549
+ this.overlayMedia.innerHTML = '';
550
+ this.overlayMedia.appendChild(mediaElement);
551
+ }
552
+ }
553
+
554
+ // Get id
555
+ const id = selectedItem.getAttribute('id');
556
+
557
+ // Update counter
558
+ this.overlayCounterCurrent.innerHTML =
559
+ +selectedItem.getAttribute('data-ecl-gallery-item-id') + 1;
560
+ this.overlayCounterMax.innerHTML = this.galleryItems.length;
561
+
562
+ // Prepare display of links for mobile
563
+ const actionMobile = document.createElement('div');
564
+ actionMobile.classList.add('ecl-gallery__detail-actions-mobile');
565
+
566
+ // Update download link
567
+ if (this.overlayDownload !== null && embeddedVideo === null) {
568
+ this.overlayDownload.href = this.selectedItem.href;
569
+ if (id) {
570
+ this.overlayDownload.setAttribute('aria-describedby', `${id}-title`);
571
+ }
572
+ this.overlayDownload.hidden = false;
573
+ actionMobile.appendChild(this.overlayDownload.cloneNode(true));
574
+ } else if (this.overlayDownload !== null) {
575
+ this.overlayDownload.hidden = true;
576
+ }
577
+
578
+ // Update share link
579
+ const shareHref = this.selectedItem.getAttribute(
580
+ 'data-ecl-gallery-item-share',
581
+ );
582
+ if (shareHref != null) {
583
+ this.overlayShare.href = shareHref;
584
+ if (id) {
585
+ this.overlayShare.setAttribute('aria-describedby', `${id}-title`);
586
+ }
587
+ this.overlayShare.hidden = false;
588
+ actionMobile.appendChild(this.overlayShare.cloneNode(true));
589
+ } else {
590
+ this.overlayShare.hidden = true;
591
+ }
592
+
593
+ // Update description
594
+ const description = queryOne(this.descriptionSelector, selectedItem);
595
+ if (description) {
596
+ this.overlayDescription.innerHTML = description.innerHTML;
597
+ }
598
+ if (actionMobile.childNodes.length > 0) {
599
+ this.overlayDescription.prepend(actionMobile);
600
+ }
601
+ }
602
+
603
+ /**
604
+ * Handles keyboard events such as Escape and navigation.
605
+ *
606
+ * @param {Event} e
607
+ */
608
+ handleKeyboard(e) {
609
+ // Detect press on Escape
610
+ // Only used if the browser do not support <dialog>
611
+ if (e.key === 'Escape' || e.key === 'Esc') {
612
+ this.handleClickOnCloseButton();
613
+ }
614
+ }
615
+
616
+ /**
617
+ * Invoke listeners for close events.
618
+ */
619
+ handleClickOnCloseButton() {
620
+ if (this.isDialogSupported) {
621
+ this.overlay.close();
622
+ } else {
623
+ this.overlay.removeAttribute('open');
624
+ }
625
+
626
+ // Remove iframe
627
+ const embeddedVideo = queryOne('iframe', this.overlayMedia);
628
+ if (embeddedVideo) embeddedVideo.remove();
629
+
630
+ // Stop video
631
+ const video = queryOne('video', this.overlayMedia);
632
+ if (video) video.pause();
633
+
634
+ // Untrap focus
635
+ this.focusTrap.deactivate();
636
+
637
+ // Restore css class on items
638
+ this.galleryItems.forEach((galleryItem) => {
639
+ const image = queryOne('img', galleryItem);
640
+ if (image) {
641
+ image.classList.add('ecl-gallery__image');
642
+ }
643
+ });
644
+
645
+ // Focus item
646
+ this.selectedItem.focus();
647
+
648
+ // Enable scroll on body
649
+ document.body.classList.remove('ecl-u-disablescroll');
650
+ }
651
+
652
+ /**
653
+ * Invoke listeners for on pressing the spacebar button.
654
+ *
655
+ * @param {Event} e
656
+ */
657
+ handleKeyPressOnItem(e) {
658
+ if (e.keyCode === 32) {
659
+ // If spacebar trigger the modal
660
+ this.handleClickOnItem(e);
661
+ }
662
+ }
663
+
664
+ /**
665
+ * Invoke listeners for on click events on view all.
666
+ *
667
+ * @param {Event} e
668
+ */
669
+ handleClickOnViewAll(e) {
670
+ e.preventDefault();
671
+ if (!this.viewAll) return;
672
+
673
+ if (this.viewAll.expanded) {
674
+ delete this.viewAll.expanded;
675
+ this.checkScreen();
676
+ this.hideItems();
677
+ this.viewAll.textContent = this.viewAllLabel;
678
+ } else {
679
+ this.viewAll.expanded = true;
680
+ this.viewAll.textContent = this.viewAllLabelExpanded;
681
+
682
+ const hidden = this.galleryItems.filter((item) =>
683
+ item.parentNode.classList.contains('ecl-gallery__item--hidden'),
684
+ );
685
+ if (hidden.length > 0) {
686
+ hidden.forEach((item) => {
687
+ item.parentNode.classList.remove('ecl-gallery__item--hidden');
688
+ });
689
+ hidden[0].focus();
690
+ }
691
+ }
692
+ }
693
+
694
+ /**
695
+ * Invoke listeners for on click events on the given gallery item.
696
+ *
697
+ * @param {Event} e
698
+ */
699
+ handleClickOnItem(e) {
700
+ e.preventDefault();
701
+
702
+ // Disable scroll on body
703
+ document.body.classList.add('ecl-u-disablescroll');
704
+
705
+ // Display overlay
706
+ if (this.isDialogSupported) {
707
+ this.overlay.showModal();
708
+ } else {
709
+ this.overlay.setAttribute('open', '');
710
+ }
711
+
712
+ // Update overlay
713
+ this.updateOverlay(e.currentTarget);
714
+
715
+ // Trap focus
716
+ this.focusTrap.activate();
717
+ }
718
+
719
+ /**
720
+ * handle click event on gallery items when no overlay.
721
+ *
722
+ * @param {Event} e
723
+ */
724
+ // eslint-disable-next-line class-methods-use-this
725
+ preventClickOnItem(e) {
726
+ e.preventDefault();
727
+ e.stopPropagation();
728
+ }
729
+
730
+ /**
731
+ * Invoke listeners for on click events on previous navigation link.
732
+ */
733
+ handleClickOnPreviousButton() {
734
+ // Get current id
735
+ const currentId = this.selectedItem.getAttribute(
736
+ 'data-ecl-gallery-item-id',
737
+ );
738
+
739
+ // Get previous id
740
+ let previousId = +currentId - 1;
741
+ if (previousId < 0) previousId = this.galleryItems.length - 1;
742
+
743
+ // Stop video
744
+ const video = queryOne('video', this.selectedItem);
745
+ if (video) video.pause();
746
+
747
+ // Update overlay
748
+ this.updateOverlay(this.galleryItems[previousId]);
749
+ this.selectedItem = this.galleryItems[previousId];
750
+
751
+ return this;
752
+ }
753
+
754
+ /**
755
+ * Invoke listeners for on click events on next navigation link.
756
+ */
757
+ handleClickOnNextButton() {
758
+ // Get current id
759
+ const currentId = this.selectedItem.getAttribute(
760
+ 'data-ecl-gallery-item-id',
761
+ );
762
+
763
+ // Get next id
764
+ let nextId = +currentId + 1;
765
+ if (nextId >= this.galleryItems.length) nextId = 0;
766
+
767
+ // Stop video
768
+ const video = queryOne('video', this.selectedItem);
769
+ if (video) video.pause();
770
+
771
+ // Update overlay
772
+ this.updateOverlay(this.galleryItems[nextId]);
773
+ this.selectedItem = this.galleryItems[nextId];
774
+
775
+ return this;
776
+ }
777
+ }
778
+
779
+ export default Gallery;