bootstrap-lightbox-gallery
Version:
A nice lightbox gallery component for Bootstrap 5
129 lines (120 loc) • 5.88 kB
JavaScript
/**
* Author and copyright: Stefan Haack (https://shaack.com)
* Repository: https://github.com/shaack/bootstrap-lightbox-gallery
* License: MIT, see file 'LICENSE'
*/
import {DomUtils} from "cm-web-modules/src/utils/DomUtils.js"
import "bootstrap-show-modal/src/ShowModal.js"
export class LightboxGallery {
constructor(elements, props = {}) {
this.props = {
id: "lightboxGallery", // change this if you have multiple galleries on one page. The id must be unique per gallery.
title: "Lightbox Gallery", // set the name, it will be displayed
theme: "dark", // set to "light" if you want to display the images on a light background
useArrowKeys: true // set to false if you don't want to use the arrow keys to navigate the images
}
Object.assign(this.props, props)
this.props.isDark = props.theme === "dark"
this.state = {
title: props.title,
carousel: undefined,
itemCount: 0,
currentIndex: undefined
}
this.init(elements)
}
init(elements) {
let carouselItems = ""
elements.forEach((element) => {
const img = element.querySelector("img")
const itemData = {
url: element.href,
alt: img.alt,
title: img.title,
caption: element.querySelector("figcaption").innerHTML.trim()
}
element.addEventListener("click", (event) => {
event.preventDefault()
const targetLink = event.target.closest("a")
this.open(targetLink)
})
let caption = ""
if (itemData.caption) {
caption = `<div class="rounded carousel-caption p-2 ${this.props.isDark ? "bg-dark" : "bg-light"} bg-opacity-75">
${itemData.caption}
</div>`
}
const carouselItem = `
<div class="carousel-item h-100" data-bs-index="${this.state.itemCount}">
<div class="d-flex align-items-center h-100 w-100 ${this.props.isDark ? "bg-dark" : "bg-white"}">
<img src="${itemData.url}" class="d-block mx-auto img-fluid" style="max-height: 100%" title="${itemData.title}" alt="${itemData.alt}"/>
${caption}
</div>
</div>`
carouselItems += carouselItem
this.state.itemCount++
})
this.state.carouselElement = DomUtils.createElement(`
<div id="${this.props.id}" ${this.props.isDark ? 'data-bs-theme="light"' : 'data-bs-theme="dark"' } class="carousel slide h-100 carousel-fade">
<div class="carousel-inner h-100">
${carouselItems}
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#${this.props.id}" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Vorheriges Bild</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#${this.props.id}" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Nächstes Bild</span>
</button>
</div>`)
}
open(targetLink) {
const carouselItems = this.state.carouselElement.querySelectorAll(".carousel-item")
carouselItems.forEach((carouselItem) => {
carouselItem.classList.remove("active")
})
const image = this.state.carouselElement.querySelector(`[src="${targetLink.href}"]`)
const activeItem = image.closest(".carousel-item")
activeItem.classList.add("active")
this.state.currentIndex = parseInt(activeItem.getAttribute("data-bs-index"))
if (targetLink) {
this.modal = bootstrap.showModal({
theme: this.props.isDark ? "dark" : undefined,
title: "<span class='text-body-secondary'>" + this.state.title + "</span> <small class='text-body-tertiary ms-3 pb-1 carousel-index'></small>",
headerClass: "border-0",
bodyClass: "overflow-hidden h-100", // fix for Safari
body: this.state.carouselElement.outerHTML,
modalDialogClass: "modal-fullscreen modal-bootstrap-lightbox-gallery"
})
const carouselElement = this.modal.element.querySelector(".carousel")
carouselElement.addEventListener('slide.bs.carousel', event => {
this.state.currentIndex = event.to
this.updateIndex()
})
// Add keyboard navigation
if (this.props.useArrowKeys) {
this.keyboardHandler = (event) => {
if (event.key === 'ArrowLeft') {
event.preventDefault()
const prevButton = this.modal.element.querySelector('.carousel-control-prev')
prevButton.click()
} else if (event.key === 'ArrowRight') {
event.preventDefault()
const nextButton = this.modal.element.querySelector('.carousel-control-next')
nextButton.click()
}
}
document.addEventListener('keydown', this.keyboardHandler)
// Remove keyboard listener when modal is hidden
this.modal.element.addEventListener('hidden.bs.modal', () => {
document.removeEventListener('keydown', this.keyboardHandler)
})
}
}
this.updateIndex()
}
updateIndex() {
this.modal.titleElement.querySelector(".carousel-index").innerText = `${this.state.currentIndex + 1} / ${this.state.itemCount}`
}
}