this._handleCarouselSlide(event))
+
+ EventHandler.on(this.modal, EVENT_MODAL_HIDE, () => this._stopSlide())
}
// Getters
@@ -58,61 +58,111 @@ class Gallery extends BaseComponent {
return NAME
}
- // Public
- setSlide(event) {
- const slideFrom = SelectorEngine.findOne(CAROUSEL_ACTIVE_SELECTOR, this.carousel)
- const slideTo = event.relatedTarget
- this.carouselLazyLoad(slideTo)
+ static get Default() {
+ return Default
+ }
+
+ /**
+ * Handle the carousel "slide.bs.carousel" event
+ * @param {Event} event
+ */
+ _handleCarouselSlide(event) {
+ const previousSlide = SelectorEngine.findOne(CAROUSEL_ACTIVE_SELECTOR, this.carousel)
+ const currentSlide = event.relatedTarget
+
+ this._carouselLazyLoad(currentSlide)
+
this.carouselPager.textContent = event.to + 1
- this.stopVideo(slideFrom)
+
+ this._stopVideo(previousSlide)
}
- stopSlide() {
+ /**
+ * Stop the current carousel slide (when modal hides or component is disposed)
+ */
+ _stopSlide() {
const currentSlide = SelectorEngine.findOne(CAROUSEL_ACTIVE_SELECTOR, this.carousel)
- this.stopVideo(currentSlide)
+ this._stopVideo(currentSlide)
}
- stopVideo(slide) {
- const iframe = SelectorEngine.findOne('iframe', slide);
- const video = SelectorEngine.findOne('video', slide);
- if (iframe) {
- iframe.src = iframe.dataset.src;
+ /**
+ * Stop any video or iframe in the given slide
+ * @param {HTMLElement} slide
+ */
+ _stopVideo(slide) {
+ if (!slide) {
+ return
+ }
+ const iframe = SelectorEngine.findOne('iframe', slide)
+ const video = SelectorEngine.findOne('video', slide)
+
+ if (iframe && iframe.dataset.src) {
+ iframe.src = iframe.dataset.src
} else if (video) {
- video.pause();
+ video.pause()
}
}
- // Private
- carouselLazyLoad(slide) {
- const media = SelectorEngine.findOne('[data-src]', slide);
-
+ /**
+ * Lazy load media (img, iframe, video, etc.) by copying data-src into src
+ * @param {HTMLElement} slide
+ */
+ _carouselLazyLoad(slide) {
+ if (!slide) {
+ return
+ }
+ const media = SelectorEngine.findOne('[data-src]', slide)
if (media && !media.src) {
- media.src = media.dataset.src;
+ media.src = media.dataset.src
}
}
- addEventListeners() {
- EventHandler.on(this.carousel, CAROUSEL_EVENT, event => this.setSlide(event))
- EventHandler.on(this.modal, EVENT_MODAL_HIDE, event => this.stopSlide(event))
- }
+ /**
+ * Internal helper to open the modal and jump to a specific slide
+ * @param {HTMLElement} gallery
+ * @param {HTMLElement} targetLink
+ */
+ static _openModalAndShowSlide(gallery, targetLink) {
+ if (!gallery || !targetLink) {
+ return
+ }
- // Static
- static get Default() {
- return Default
+ const firstSlide = Number(targetLink.getAttribute('data-bs-slide-to') || 0)
+ gallery.dataset.galleryStart = firstSlide
+
+ const instance = Gallery.getOrCreateInstance(gallery)
+
+ const overlay = SelectorEngine.findOne('.bcl-gallery__item-overlay', targetLink)
+ if (overlay) {
+ const modalId = overlay.getAttribute('data-bs-target')
+ const modalElement = document.querySelector(modalId)
+ if (modalElement) {
+ const modal = bootstrap.Modal.getOrCreateInstance(modalElement)
+ modal.show()
+ }
+ }
+
+ setTimeout(() => {
+ const carousel = SelectorEngine.findOne(CAROUSEL_SELECTOR, instance._element)
+ const carouselInstance = bootstrap.Carousel.getOrCreateInstance(carousel)
+ carouselInstance.to(firstSlide)
+
+ const pager = SelectorEngine.findOne(CAROUSEL_PAGER_SELECTOR, instance._element)
+ if (pager) {
+ pager.textContent = firstSlide + 1
+ }
+ }, 50)
}
static jQueryInterface(config) {
- return this.each(function jInterface() {
+ return this.each(function () {
const data = Gallery.getOrCreateInstance(this)
-
if (typeof config !== 'string') {
return
}
-
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`)
}
-
data[config](this)
})
}
@@ -120,24 +170,30 @@ class Gallery extends BaseComponent {
/**
* ------------------------------------------------------------------------
- * Data Api implementation
+ * Data API implementation
* ------------------------------------------------------------------------
*/
+const isEnterOrSpace = (event) => {
+ return event.key === 'Enter' || event.key === ' '
+}
+
EventHandler.on(document, EVENT_CLICK_DATA_API, THUMBNAIL_SELECTOR, (event) => {
+ event.preventDefault()
+ const targetLink = event.target.closest('a')
const gallery = event.target.closest('div.bcl-gallery')
- const firstSlide = event.target.parentNode.getAttribute('data-bs-slide-to');
- gallery.dataset.galleryStart = firstSlide;
-
- Gallery.getOrCreateInstance(gallery);
+ Gallery._openModalAndShowSlide(gallery, targetLink)
})
-/**
- * ------------------------------------------------------------------------
- * jQuery
- * ------------------------------------------------------------------------
- * add .gallery to jQuery only if jQuery is present
- */
+EventHandler.on(document, EVENT_KEYDOWN_DATA_API, THUMBNAIL_SELECTOR, (event) => {
+ if (!isEnterOrSpace(event)) {
+ return
+ }
+ event.preventDefault()
+ const targetLink = event.target.closest('a')
+ const gallery = event.target.closest('div.bcl-gallery')
+ Gallery._openModalAndShowSlide(gallery, targetLink)
+})
defineJQueryPlugin(Gallery)