UNPKG

@ussebastian/kitdigital

Version:

Kit Digital de la Universidad San Sebastián

230 lines (181 loc) 7.96 kB
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); } } }