export default class JustifiedGallery {
constructor(container, options = {}) {
this.container = container;
this.options = {
margin: options.margin || 5,
columns: options.columns || 3,
breakpoints: options.breakpoints || { 768: 2, 480: 1 },
wrapLinks: options.wrapLinks || false, // active l'encapsulation
linkAttribute: options.linkAttribute || 'src', // attribut à utiliser pour href
justifyLastRow: options.justifyLastRow || false,
};
this.currentIndex = 0;
this.isOpenModal = false;
this.init();
window.addEventListener('resize', () => this.layoutGallery());
}
init() {
this.images = this.getImages();
// Encapsule les images si nécessaire
if (this.options.wrapLinks) {
this.images.forEach(img => this.wrapImage(img));
this.images = this.getImages(); // rafraîchit la liste
}
// S'assure que toutes les images sont chargées
const promises = this.images.map(img => this.waitForImageLoad(img));
Promise.all(promises).then(() => {
this.layoutGallery();
this.setupModal();
});
this.container.addEventListener('keydown', e => {
e.preventDefault();
if (e.key === 'ArrowRight') {
e.target.nextSibling.nextSibling.focus()
}
if (e.key === "ArrowLeft") {
e.target.previousSibling.previousSibling.focus()
}
if (e.key === "ArrowDown") {
const images = this.getImages();
let target = e.target;
for (let i = 0; i < this.options.columns; i++) {
if (Math.ceil(images.length / this.options.columns) <= i) {
// On récupère le dernier élément si on dépasse la dernière ligne
target = images[images.length - 1].parentNode;
break;
}
// Utiliser nextElementSibling pour éviter les text nodes
if (target.nextElementSibling) {
target = target.nextElementSibling;
}
}
if (target) {
target.focus();
// Scroll pour voir la ligne où se trouve l'image focus
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
if (e.key === "ArrowUp") {
let target = e.target
for (let i = 0; this.options.columns > i; i++) {
if (this.getImages().length < i) {
target = this.getImages()[0];
break;
}
target = target.previousSibling.previousSibling
}
if (target) {
target.focus();
// Scroll pour voir la ligne où se trouve l'image focus
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
}
if (e.key === "Enter") {
e.target.click()
}
})
}
wrapImage(img) {
const href = img.getAttribute(this.options.linkAttribute) || img.src;
const a = document.createElement('a');
a.href = href;
a.style.display = 'inline-block';
img.parentNode.insertBefore(a, img);
a.appendChild(img);
}
getImages() {
return Array.from(this.container.querySelectorAll('img'));
}
waitForImageLoad(img) {
return new Promise(resolve => {
if (img.complete) resolve();
else {
img.onload = () => resolve();
img.onerror = () => resolve();
}
});
}
getColumns() {
const width = this.container.clientWidth;
let cols = this.options.columns;
for (let bp in this.options.breakpoints) {
if (width <= bp) cols = this.options.breakpoints[bp];
}
return cols;
}
layoutGallery() {
const containerWidth = this.container.clientWidth;
const margin = this.options.margin;
const cols = this.getColumns();
let rowImages = [];
let totalRatio = 0;
this.images.forEach((img, index) => {
const ratio = img.naturalWidth / img.naturalHeight;
if (this.options.wrapLinks) {
rowImages.push({ img: img.parentNode, ratio });
} else {
rowImages.push({ img, ratio });
}
totalRatio += ratio;
const isLastRow = index === this.images.length - 1;
if (rowImages.length === cols || isLastRow) {
let rowHeight;
if (isLastRow && rowImages.length < cols && !this.options.justifyLastRow) {
// Ne pas étirer la dernière ligne
const avgRatio = totalRatio / rowImages.length;
rowHeight = Math.floor((containerWidth / cols) / avgRatio);
} else {
// Étire normalement pour remplir la ligne
rowHeight = Math.floor((containerWidth - margin * (rowImages.length - 1)) / totalRatio);
}
this.scaleRow(rowImages, rowHeight, margin);
rowImages = [];
totalRatio = 0;
}
});
}
scaleRow(row, rowHeight, margin) {
row.forEach((item, index) => {
const width = rowHeight * item.ratio;
if (this.options.wrapLinks) {
item.img.style.overflow = 'hidden';
item.img.firstChild.style.width = '100%';
}
item.img.style.width = `${width}px`;
item.img.style.height = `${rowHeight}px`;
item.img.style.marginRight = index < row.length - 1 ? `${margin}px` : '0';
item.img.style.marginBottom = `${margin}px`;
item.img.style.cursor = 'pointer';
});
}
setupModal() {
// Création du modal
this.modal = document.createElement('div');
this.modal.classList.add('jg-modal');
this.modal.innerHTML = `
×