UNPKG

swiper

Version:

Most modern mobile touch slider and framework with hardware accelerated transitions

529 lines (526 loc) 21.7 kB
import { c as classesToSelector } from '../shared/classes-to-selector.mjs'; import { c as createElementIfNotDefined } from '../shared/create-element-if-not-defined.mjs'; import { s as makeElementsArray, j as elementParents, w as setInnerHTML, i as elementOuterSize, b as elementIndex } from '../shared/utils.mjs'; const isVirtualEnabled = (swiper) => !!swiper.virtual && !!swiper.params.virtual?.enabled; const isFreeModeEnabled = (swiper) => !!swiper.params.freeMode?.enabled; const Pagination = ({ swiper, extendParams, on, emit }) => { const pfx = 'swiper-pagination'; extendParams({ pagination: { el: null, bulletElement: 'span', clickable: false, hideOnClick: false, renderBullet: null, renderProgressbar: null, renderFraction: null, renderCustom: null, progressbarOpposite: false, type: 'bullets', // 'bullets' or 'progressbar' or 'fraction' or 'custom' dynamicBullets: false, dynamicMainBullets: 1, formatFractionCurrent: (number) => number, formatFractionTotal: (number) => number, bulletClass: `${pfx}-bullet`, bulletActiveClass: `${pfx}-bullet-active`, modifierClass: `${pfx}-`, currentClass: `${pfx}-current`, totalClass: `${pfx}-total`, hiddenClass: `${pfx}-hidden`, progressbarFillClass: `${pfx}-progressbar-fill`, progressbarOppositeClass: `${pfx}-progressbar-opposite`, clickableClass: `${pfx}-clickable`, lockClass: `${pfx}-lock`, horizontalClass: `${pfx}-horizontal`, verticalClass: `${pfx}-vertical`, paginationDisabledClass: `${pfx}-disabled`, }, }); // Initialized as a partial; remaining methods (render, update, init, // destroy, enable, disable) attach after their definitions below. swiper.pagination = { el: null, bullets: [], }; let bulletSize; let dynamicBulletIndex = 0; function getParams() { return swiper.params.pagination; } function isPaginationDisabled() { const elParam = getParams().el; return (!elParam || !swiper.pagination.el || (Array.isArray(swiper.pagination.el) && swiper.pagination.el.length === 0)); } function setSideBullets(bulletEl, position) { const { bulletActiveClass } = getParams(); if (!bulletEl) return; let current = bulletEl[`${position === 'prev' ? 'previous' : 'next'}ElementSibling`]; if (current) { current.classList.add(`${bulletActiveClass}-${position}`); current = current[`${position === 'prev' ? 'previous' : 'next'}ElementSibling`]; if (current) { current.classList.add(`${bulletActiveClass}-${position}-${position}`); } } } function getMoveDirection(prevIndex, nextIndex, length) { prevIndex = prevIndex % length; nextIndex = nextIndex % length; if (nextIndex === prevIndex + 1) { return 'next'; } else if (nextIndex === prevIndex - 1) { return 'previous'; } return undefined; } function onBulletClick(e) { const targetEl = e.target; const bulletEl = targetEl.closest(classesToSelector(getParams().bulletClass)); if (!bulletEl) { return; } e.preventDefault(); const index = (elementIndex(bulletEl) ?? 0) * (swiper.params.slidesPerGroup ?? 1); if (swiper.params.loop) { if (swiper.realIndex === index) return; const moveDirection = getMoveDirection(swiper.realIndex, index, swiper.slides.length); if (moveDirection === 'next') { swiper.slideNext(); } else if (moveDirection === 'previous') { swiper.slidePrev(); } else { swiper.slideToLoop(index); } } else { swiper.slideTo(index); } } function update() { // Render || Update Pagination bullets/items const rtl = swiper.rtl; const params = getParams(); if (isPaginationDisabled()) return; const els = makeElementsArray(swiper.pagination.el); // Current/Total let current; let previousIndex; const slidesLength = isVirtualEnabled(swiper) ? swiper.virtual.slides.length : swiper.slides.length; const total = swiper.params.loop ? Math.ceil(slidesLength / (swiper.params.slidesPerGroup ?? 1)) : swiper.snapGrid.length; if (swiper.params.loop) { previousIndex = swiper.previousRealIndex || 0; current = (swiper.params.slidesPerGroup ?? 1) > 1 ? Math.floor(swiper.realIndex / (swiper.params.slidesPerGroup ?? 1)) : swiper.realIndex; } else if (typeof swiper.snapIndex !== 'undefined') { current = swiper.snapIndex; previousIndex = swiper.previousSnapIndex; } else { previousIndex = swiper.previousIndex || 0; current = swiper.activeIndex || 0; } // Types if (params.type === 'bullets' && swiper.pagination.bullets && swiper.pagination.bullets.length > 0) { const bullets = swiper.pagination.bullets; let firstIndex = 0; let lastIndex = 0; let midIndex = 0; if (params.dynamicBullets) { bulletSize = elementOuterSize(bullets[0], swiper.isHorizontal() ? 'width' : 'height'); const dim = swiper.isHorizontal() ? 'width' : 'height'; els.forEach((subEl) => { subEl.style[dim] = `${(bulletSize ?? 0) * (params.dynamicMainBullets + 4)}px`; }); if (params.dynamicMainBullets > 1 && previousIndex !== undefined) { dynamicBulletIndex += current - (previousIndex || 0); if (dynamicBulletIndex > params.dynamicMainBullets - 1) { dynamicBulletIndex = params.dynamicMainBullets - 1; } else if (dynamicBulletIndex < 0) { dynamicBulletIndex = 0; } } firstIndex = Math.max(current - dynamicBulletIndex, 0); lastIndex = firstIndex + (Math.min(bullets.length, params.dynamicMainBullets) - 1); midIndex = (lastIndex + firstIndex) / 2; } bullets.forEach((bulletEl) => { const classesToRemove = [ '', '-next', '-next-next', '-prev', '-prev-prev', '-main', ] .map((suffix) => `${params.bulletActiveClass}${suffix}`) .flatMap((s) => (typeof s === 'string' && s.includes(' ') ? s.split(' ') : [s])); bulletEl.classList.remove(...classesToRemove); }); if (els.length > 1) { bullets.forEach((bullet) => { const bulletIndex = elementIndex(bullet); if (bulletIndex === current) { bullet.classList.add(...params.bulletActiveClass.split(' ')); } else if (swiper.isElement) { bullet.setAttribute('part', 'bullet'); } if (params.dynamicBullets && bulletIndex !== undefined) { if (bulletIndex >= firstIndex && bulletIndex <= lastIndex) { bullet.classList.add(...`${params.bulletActiveClass}-main`.split(' ')); } if (bulletIndex === firstIndex) { setSideBullets(bullet, 'prev'); } if (bulletIndex === lastIndex) { setSideBullets(bullet, 'next'); } } }); } else { const bullet = bullets[current]; if (bullet) { bullet.classList.add(...params.bulletActiveClass.split(' ')); } if (swiper.isElement) { bullets.forEach((bulletEl, bulletIndex) => { bulletEl.setAttribute('part', bulletIndex === current ? 'bullet-active' : 'bullet'); }); } if (params.dynamicBullets) { const firstDisplayedBullet = bullets[firstIndex]; const lastDisplayedBullet = bullets[lastIndex]; for (let i = firstIndex; i <= lastIndex; i += 1) { if (bullets[i]) { bullets[i].classList.add(...`${params.bulletActiveClass}-main`.split(' ')); } } setSideBullets(firstDisplayedBullet, 'prev'); setSideBullets(lastDisplayedBullet, 'next'); } } if (params.dynamicBullets) { const dynamicBulletsLength = Math.min(bullets.length, params.dynamicMainBullets + 4); const bulletsOffset = ((bulletSize ?? 0) * dynamicBulletsLength - (bulletSize ?? 0)) / 2 - midIndex * (bulletSize ?? 0); const offsetProp = rtl ? 'right' : 'left'; const positionDim = swiper.isHorizontal() ? offsetProp : 'top'; bullets.forEach((bullet) => { bullet.style[positionDim] = `${bulletsOffset}px`; }); } } els.forEach((subEl, subElIndex) => { if (params.type === 'fraction') { subEl.querySelectorAll(classesToSelector(params.currentClass)).forEach((fractionEl) => { fractionEl.textContent = String(params.formatFractionCurrent(current + 1)); }); subEl.querySelectorAll(classesToSelector(params.totalClass)).forEach((totalEl) => { totalEl.textContent = String(params.formatFractionTotal(total)); }); } if (params.type === 'progressbar') { let progressbarDirection; if (params.progressbarOpposite) { progressbarDirection = swiper.isHorizontal() ? 'vertical' : 'horizontal'; } else { progressbarDirection = swiper.isHorizontal() ? 'horizontal' : 'vertical'; } const scale = (current + 1) / total; let scaleX = 1; let scaleY = 1; if (progressbarDirection === 'horizontal') { scaleX = scale; } else { scaleY = scale; } subEl .querySelectorAll(classesToSelector(params.progressbarFillClass)) .forEach((progressEl) => { progressEl.style.transform = `translate3d(0,0,0) scaleX(${scaleX}) scaleY(${scaleY})`; progressEl.style.transitionDuration = `${swiper.params.speed}ms`; }); } if (params.type === 'custom' && params.renderCustom) { setInnerHTML(subEl, params.renderCustom(swiper, current + 1, total)); if (subElIndex === 0) emit('paginationRender', subEl); } else { if (subElIndex === 0) emit('paginationRender', subEl); emit('paginationUpdate', subEl); } if (swiper.params.watchOverflow && swiper.enabled) { subEl.classList[swiper.isLocked ? 'add' : 'remove'](params.lockClass); } }); } function render() { // Render Container const params = getParams(); if (isPaginationDisabled()) return; const gridParams = swiper.params.grid; const slidesLength = isVirtualEnabled(swiper) ? swiper.virtual.slides.length : swiper.grid && gridParams?.rows && gridParams.rows > 1 ? swiper.slides.length / Math.ceil(gridParams.rows) : swiper.slides.length; const els = makeElementsArray(swiper.pagination.el); let paginationHTML = ''; if (params.type === 'bullets') { let numberOfBullets = swiper.params.loop ? Math.ceil(slidesLength / (swiper.params.slidesPerGroup ?? 1)) : swiper.snapGrid.length; if (swiper.params.freeMode && isFreeModeEnabled(swiper) && numberOfBullets > slidesLength) { numberOfBullets = slidesLength; } for (let i = 0; i < numberOfBullets; i += 1) { if (params.renderBullet) { paginationHTML += params.renderBullet.call(swiper, i, params.bulletClass); } else { // oxfmt-ignore paginationHTML += `<${params.bulletElement} ${swiper.isElement ? 'part="bullet"' : ''} class="${params.bulletClass}"></${params.bulletElement}>`; } } } if (params.type === 'fraction') { if (params.renderFraction) { paginationHTML = params.renderFraction.call(swiper, params.currentClass, params.totalClass); } else { paginationHTML = `<span class="${params.currentClass}"></span>` + ' / ' + `<span class="${params.totalClass}"></span>`; } } if (params.type === 'progressbar') { if (params.renderProgressbar) { paginationHTML = params.renderProgressbar.call(swiper, params.progressbarFillClass); } else { paginationHTML = `<span class="${params.progressbarFillClass}"></span>`; } } swiper.pagination.bullets = []; els.forEach((subEl) => { if (params.type !== 'custom') { setInnerHTML(subEl, paginationHTML || ''); } if (params.type === 'bullets') { swiper.pagination.bullets.push(...Array.from(subEl.querySelectorAll(classesToSelector(params.bulletClass)))); } }); if (params.type !== 'custom') { emit('paginationRender', els[0]); } } function init() { swiper.params.pagination = createElementIfNotDefined(swiper, swiper.originalParams.pagination, swiper.params.pagination, { el: 'swiper-pagination' }); const params = getParams(); if (!params.el) return; let el; if (typeof params.el === 'string' && swiper.isElement) { el = swiper.el.querySelector(params.el); } if (!el && typeof params.el === 'string') { el = [...document.querySelectorAll(params.el)]; } if (!el) { el = params.el; } if (!el || (Array.isArray(el) && el.length === 0)) return; if (swiper.params.uniqueNavElements && typeof params.el === 'string' && Array.isArray(el) && el.length > 1) { el = [...swiper.el.querySelectorAll(params.el)]; // check if it belongs to another nested Swiper if (el.length > 1) { const found = el.find((subEl) => { if (elementParents(subEl, '.swiper')[0] !== swiper.el) return false; return true; }); if (found) el = found; } } if (Array.isArray(el) && el.length === 1) el = el[0]; Object.assign(swiper.pagination, { el, }); const els = makeElementsArray(el); els.forEach((subEl) => { if (params.type === 'bullets' && params.clickable) { subEl.classList.add(...(params.clickableClass || '').split(' ')); } subEl.classList.add(params.modifierClass + params.type); subEl.classList.add(swiper.isHorizontal() ? params.horizontalClass : params.verticalClass); if (params.type === 'bullets' && params.dynamicBullets) { subEl.classList.add(`${params.modifierClass}${params.type}-dynamic`); dynamicBulletIndex = 0; if (params.dynamicMainBullets < 1) { params.dynamicMainBullets = 1; } } if (params.type === 'progressbar' && params.progressbarOpposite) { subEl.classList.add(params.progressbarOppositeClass); } if (params.clickable) { subEl.addEventListener('click', onBulletClick); } if (!swiper.enabled) { subEl.classList.add(params.lockClass); } }); } function destroy() { const params = getParams(); if (isPaginationDisabled()) return; const el = swiper.pagination.el; if (el) { const els = makeElementsArray(el); els.forEach((subEl) => { subEl.classList.remove(params.hiddenClass); subEl.classList.remove(params.modifierClass + params.type); subEl.classList.remove(swiper.isHorizontal() ? params.horizontalClass : params.verticalClass); if (params.clickable) { subEl.classList.remove(...(params.clickableClass || '').split(' ')); subEl.removeEventListener('click', onBulletClick); } }); } if (swiper.pagination.bullets) swiper.pagination.bullets.forEach((subEl) => subEl.classList.remove(...params.bulletActiveClass.split(' '))); } on('changeDirection', () => { if (!swiper.pagination || !swiper.pagination.el) return; const params = getParams(); const els = makeElementsArray(swiper.pagination.el); els.forEach((subEl) => { subEl.classList.remove(params.horizontalClass, params.verticalClass); subEl.classList.add(swiper.isHorizontal() ? params.horizontalClass : params.verticalClass); }); }); on('init', () => { if (getParams().enabled === false) { disable(); } else { init(); render(); update(); } }); on('activeIndexChange', () => { if (typeof swiper.snapIndex === 'undefined') { update(); } }); on('snapIndexChange', () => { update(); }); on('snapGridLengthChange', () => { render(); update(); }); on('destroy', () => { destroy(); }); on('enable disable', () => { const { el } = swiper.pagination; if (el) { const params = getParams(); const els = makeElementsArray(el); els.forEach((subEl) => subEl.classList[swiper.enabled ? 'remove' : 'add'](params.lockClass)); } }); on('lock unlock', () => { update(); }); on('click', (_s, e) => { const targetEl = e.target; const els = makeElementsArray(swiper.pagination.el); const params = getParams(); if (params.el && params.hideOnClick && els && els.length > 0 && !targetEl.classList.contains(params.bulletClass)) { if (swiper.navigation && ((swiper.navigation.nextEl && targetEl === swiper.navigation.nextEl) || (swiper.navigation.prevEl && targetEl === swiper.navigation.prevEl))) return; const isHidden = els[0].classList.contains(params.hiddenClass); if (isHidden === true) { emit('paginationShow'); } else { emit('paginationHide'); } els.forEach((subEl) => subEl.classList.toggle(params.hiddenClass)); } }); const enable = () => { const params = getParams(); swiper.el.classList.remove(params.paginationDisabledClass); const { el } = swiper.pagination; if (el) { const els = makeElementsArray(el); els.forEach((subEl) => subEl.classList.remove(params.paginationDisabledClass)); } init(); render(); update(); }; const disable = () => { const params = getParams(); swiper.el.classList.add(params.paginationDisabledClass); const { el } = swiper.pagination; if (el) { const els = makeElementsArray(el); els.forEach((subEl) => subEl.classList.add(params.paginationDisabledClass)); } destroy(); }; Object.assign(swiper.pagination, { enable, disable, render, update, init, destroy, }); }; export { Pagination as default };