@ussebastian/kitdigital
Version:
Kit Digital de la Universidad San Sebastián
271 lines (222 loc) • 9.68 kB
JavaScript
import EmblaCarousel from 'embla-carousel/embla-carousel.esm';
import Autoplay from 'embla-carousel-autoplay/embla-carousel-autoplay.esm';
import { wrap } from './utils/InitUtils';
export default class ComponentCarousel {
constructor(el) {
this.rootNode = el;
// data attributes
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 = getAttr('data-uss-visible-items') || 1;
}
this.align = this.carouselType === 'cards' ? 'start' : 'center';
this.carouselItemsClass = getAttr('data-uss-items-class');
this.autoplayDelay = getAttr('data-uss-autoplay');
this.title = getAttr('data-uss-title');
this.hideControlls = hasAttr('data-uss-hide-controls');
this.EmblaOptions = {
loop: hasAttr('data-uss-loop'),
draggable: hasAttr('data-uss-draggable'),
speed: getAttr('data-uss-speed') || 10,
startIndex: getAttr('data-uss-startIndex') || 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 = !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>`;
this.pageButtonContainer.innerHTML = embla.scrollSnapList().reduce((acc) => acc + template, '');
const pageButtonsArray = Array.prototype.slice.call(this.pageButtonContainer.children);
for (let i = 0; i < pageButtonsArray.length; i += 1) {
const index = i + 1;
const indexFormatted = index.toString().padStart(2, '0');
pageButtonsArray[i].innerHTML = indexFormatted;
}
return pageButtonsArray;
};
selectPage = (embla) => () => {
const previous = embla.previousScrollSnap();
const selected = embla.selectedScrollSnap();
this.pageButtonsArray[previous].classList.remove(
`uss-carousel-${this.carouselType}__page-single-button--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;
if (this.carouselType === 'cards') {
this.rootNode.prepend(title);
} else {
controlContainer.appendChild(title);
}
}
const control = document.createElement('div');
control.classList.add(`uss-carousel-${this.carouselType}__control`);
let pageButtonContainer = null;
pageButtonContainer = document.createElement('div');
pageButtonContainer.classList.add(`uss-carousel-${this.carouselType}__page-buttons`);
this.pageButtonContainer = pageButtonContainer;
// make a div with the current and total items like so: 01/10
const counter = document.createElement('div');
counter.classList.add(`uss-carousel-${this.carouselType}__counter`);
// add number of items to the counter
counter.innerHTML = `01/${this.totalItems.toString().padStart(2, '0')}`;
const pageTotals = document.createElement('div');
pageTotals.classList.add(`uss-carousel-${this.carouselType}__page-totals`);
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');
// append this
prevButton.appendChild(prevButtonIcon);
nextButton.appendChild(nextButtonIcon);
controlButtonsContainer.appendChild(prevButton);
controlButtonsContainer.appendChild(nextButton);
control.appendChild(pageButtonContainer);
pageButtonContainer.before(counter);
this.counterNode = counter;
control.appendChild(controlButtonsContainer);
controlContainer.appendChild(control);
if (this.carouselType === 'cards') {
controlContainer.appendChild(control);
controlContainer.appendChild(counter);
}
return controlContainer;
}
handleButtonsDisableState() {
const width = document.body.clientWidth;
const selected = this.emblaInstance.selectedScrollSnap();
if (width < this.mobileBreakpoint) {
if (selected === 0) {
this.prevButtonNode.setAttribute('disabled', true);
this.nextButtonNode.removeAttribute('disabled');
} else if (selected >= this.totalItems - 1) {
this.nextButtonNode.setAttribute('disabled', true);
this.prevButtonNode.removeAttribute('disabled');
} else {
this.prevButtonNode.removeAttribute('disabled');
this.nextButtonNode.removeAttribute('disabled');
}
} else if (selected >= this.disableNextButtonAtIndex) {
this.nextButtonNode.setAttribute('disabled', true);
this.prevButtonNode.removeAttribute('disabled', false);
} else {
this.nextButtonNode.removeAttribute('disabled', false);
this.prevButtonNode.removeAttribute('disabled', false);
if (selected === 0) this.prevButtonNode.setAttribute('disabled', true);
}
}
handleCounter() {
const selected = this.emblaInstance.selectedScrollSnap();
const selectedFormatted = (selected + 1).toString().padStart(2, '0');
this.counterNode.innerHTML = `${selectedFormatted}/${this.totalItems
.toString()
.padStart(2, '0')}`;
}
init() {
if (this.carouselType === 'cards' && this.visibleItems) {
this.carouselItems.forEach((_, i) => {
this.carouselItems[i].classList.add(`uss-carousel-cards__md-col-${this.visibleItems}`);
});
this.disableNextButtonAtIndex = this.carouselItems.length - this.visibleItems;
}
let container = document.createElement('div');
container.classList.add(`uss-carousel-${this.carouselType}__container`);
const carouselItemsContainer = wrap(this.carouselItems, container);
this.rootNode.appendChild(carouselItemsContainer);
container = this.rootNode.querySelector(`.uss-carousel-${this.carouselType}__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.hideControlls) {
const carouselConstrols = this.makeControls();
if (this.carouselType === 'cards') {
this.rootNode.appendChild(carouselConstrols);
} else {
this.rootNode.appendChild(carouselConstrols);
}
}
let autoplay = null;
if (this.autoplayDelay) {
autoplay = Autoplay({ delay: this.autoplayDelay }, this.rootNode);
}
const embla = EmblaCarousel(
this.carouselViewport,
this.EmblaOptions,
autoplay ? [autoplay] : null,
);
this.emblaInstance = embla;
if (!this.hideControlls && this.carouselType !== 'cards') {
// for hero and single-content
this.pageButtonsArray = this.generatePageButtons(embla);
const setSelectedPage = this.selectPage(embla);
this.setupPageButtons(embla);
embla.on('init', setSelectedPage);
embla.on('select', setSelectedPage);
if (!this.EmblaOptions.loop) {
embla.on('init', () => {
this.handleButtonsDisableState();
});
embla.on('select', () => {
this.handleButtonsDisableState();
});
}
} else if (!this.hideControlls) {
// for cards
embla.on('init', () => {
this.handleButtonsDisableState();
});
embla.on('select', () => {
this.handleCounter();
this.handleButtonsDisableState();
});
}
if (!this.hideControlls) {
this.prevButtonNode.addEventListener('click', embla.scrollPrev, false);
this.nextButtonNode.addEventListener('click', embla.scrollNext, false);
}
}
}