@ussebastian/kitdigital
Version:
Kit Digital de la Universidad San Sebastián
230 lines (181 loc) • 7.96 kB
JavaScript
import EmblaCarousel from 'embla-carousel';
import { wrap } from './WrapUtility';
export class Carousel {
constructor(el) {
this.rootNode = el;
const getAttr = (attribute) => this.rootNode.getAttribute(attribute);
const hasAttr = (attribute) => this.rootNode.hasAttribute(attribute);
this.carouselClass = getAttr('data-uss-carousel');
this.rootNode.classList.add(this.carouselClass);
this.carouselType = this.carouselClass.split('-').pop();
this.visibleItems = null;
if (this.carouselType === 'cards') {
this.visibleItems = parseInt(getAttr('data-uss-visible-items'), 10) || 1;
}
this.align = this.carouselType === 'cards' ? 'start' : 'center';
this.carouselItemsClass = getAttr('data-uss-items-class');
this.title = getAttr('data-uss-title');
this.hideControls = hasAttr('data-uss-hide-controls');
this.EmblaOptions = {
loop: hasAttr('data-uss-loop') && ['', 'true'].includes(getAttr('data-uss-loop')),
draggable: hasAttr('data-uss-draggable'),
duration: parseInt(getAttr('data-uss-speed'), 10) || 25,
startIndex: parseInt(getAttr('data-uss-startIndex'), 10) || 0,
align: this.align,
};
this.emblaInstance = null;
this.prevButtonNode = null;
this.nextButtonNode = null;
this.pageButtonContainer = null;
this.pageButtonsArray = null;
this.mobileBreakpoint = 990;
this.counterNode = null;
this.carouselItems = Array.from(
!this.carouselItemsClass
? this.rootNode.querySelectorAll(':scope > *')
: this.rootNode.querySelectorAll(`.${this.carouselItemsClass}`),
);
this.totalItems = this.carouselItems.length;
this.disableNextButtonAtIndex = this.totalItems - 1;
this.carouselViewport = null;
}
setupPageButtons(embla) {
this.pageButtonsArray.forEach((dotNode, i) => {
dotNode.addEventListener('click', () => embla.scrollTo(i), false);
});
}
generatePageButtons(embla) {
const template = `<button class="uss-carousel-${this.carouselType}__page-single-button" type="button"></button>`;
const snapCount = embla.scrollSnapList().length;
this.pageButtonContainer.innerHTML = new Array(snapCount).fill(template).join('');
const pageButtonsArray = Array.from(this.pageButtonContainer.children);
for (let index = 0; index < pageButtonsArray.length; index += 1) {
const button = pageButtonsArray[index];
const indexFormatted = String(index + 1).padStart(2, '0');
button.innerHTML = indexFormatted;
}
return pageButtonsArray;
}
selectPage(embla) {
const previous = embla.previousScrollSnap();
const selected = embla.selectedScrollSnap();
if (previous !== -1 && this.pageButtonsArray[previous]) {
this.pageButtonsArray[previous].classList.remove(
`uss-carousel-${this.carouselType}__page-single-button--selected`,
);
}
if (this.pageButtonsArray[selected]) {
this.pageButtonsArray[selected].classList.add(
`uss-carousel-${this.carouselType}__page-single-button--selected`,
);
}
}
makeControls() {
const controlContainer = document.createElement('div');
controlContainer.classList.add(`uss-carousel-${this.carouselType}__control-container`);
if (this.title) {
const title = document.createElement('h2');
title.classList.add(`uss-carousel-${this.carouselType}__title`);
title.innerHTML = this.title;
this.rootNode.insertBefore(title, this.rootNode.firstChild);
}
const control = document.createElement('div');
control.classList.add(`uss-carousel-${this.carouselType}__control`);
const counter = document.createElement('div');
counter.classList.add(`uss-carousel-${this.carouselType}__counter`);
counter.innerHTML = `01/${String(this.totalItems).padStart(2, '0')}`;
this.counterNode = counter;
const pageButtonContainer = document.createElement('div');
pageButtonContainer.classList.add(`uss-carousel-${this.carouselType}__page-buttons`);
this.pageButtonContainer = pageButtonContainer;
const controlButtonsContainer = document.createElement('div');
controlButtonsContainer.classList.add(
`uss-carousel-${this.carouselType}__control-buttons-container`,
);
const prevButton = document.createElement('button');
prevButton.classList.add('uss-btn', 'uss-btn--slide', 'uss-btn--slide-prev');
prevButton.setAttribute('data-uss-carousel-button', 'prev');
this.prevButtonNode = prevButton;
const prevButtonIcon = document.createElement('i');
prevButtonIcon.classList.add('uss-icon', 'ri-arrow-left-s-line');
const nextButton = document.createElement('button');
nextButton.classList.add('uss-btn', 'uss-btn--slide', 'uss-btn--slide-next');
nextButton.setAttribute('data-uss-carousel-button', 'next');
this.nextButtonNode = nextButton;
const nextButtonIcon = document.createElement('i');
nextButtonIcon.classList.add('uss-icon', 'ri-arrow-right-s-line');
prevButton.appendChild(prevButtonIcon);
nextButton.appendChild(nextButtonIcon);
controlButtonsContainer.appendChild(prevButton);
controlButtonsContainer.appendChild(nextButton);
control.appendChild(counter);
control.appendChild(pageButtonContainer);
control.appendChild(controlButtonsContainer);
controlContainer.appendChild(control);
return controlContainer;
}
handleButtonsDisableState() {
if (this.EmblaOptions.loop) {
this.prevButtonNode.disabled = false;
this.nextButtonNode.disabled = false;
return;
}
const canScrollPrev = this.emblaInstance.canScrollPrev();
const canScrollNext = this.emblaInstance.canScrollNext();
this.prevButtonNode.disabled = !canScrollPrev;
this.nextButtonNode.disabled = !canScrollNext;
}
handleCounter() {
const selected = this.emblaInstance.selectedScrollSnap();
const selectedFormatted = String(selected + 1).padStart(2, '0');
this.counterNode.innerHTML = `${selectedFormatted}/${String(this.totalItems).padStart(2, '0')}`;
}
mount() {
if (this.rootNode.carouselInitialized) {
return;
}
this.rootNode.carouselInitialized = true;
if (this.totalItems === 0) {
console.warn('No carousel items found.');
return;
}
if (this.carouselType === 'cards' && this.visibleItems) {
this.carouselItems.forEach((item) => {
item.classList.add(`uss-carousel-cards__md-col-${this.visibleItems}`);
});
this.disableNextButtonAtIndex = this.carouselItems.length - this.visibleItems;
}
const container = document.createElement('div');
container.classList.add(`uss-carousel-${this.carouselType}__container`);
wrap(this.carouselItems, container);
const viewport = document.createElement('div');
viewport.classList.add(`uss-carousel-${this.carouselType}__viewport`);
this.carouselViewport = wrap([container], viewport);
this.rootNode.appendChild(this.carouselViewport);
if (!this.hideControls) {
const carouselControls = this.makeControls();
this.rootNode.appendChild(carouselControls);
}
const embla = EmblaCarousel(this.carouselViewport, this.EmblaOptions);
this.emblaInstance = embla;
if (!this.hideControls) {
// Initialize controls immediately
this.handleCounter();
this.handleButtonsDisableState();
if (this.carouselType !== 'cards') {
this.pageButtonsArray = this.generatePageButtons(embla);
this.setupPageButtons(embla);
this.selectPage(embla);
}
embla.on('select', () => {
this.handleCounter();
this.handleButtonsDisableState();
if (this.carouselType !== 'cards') {
this.selectPage(embla);
}
});
this.prevButtonNode.addEventListener('click', () => embla.scrollPrev(), false);
this.nextButtonNode.addEventListener('click', () => embla.scrollNext(), false);
}
}
}