454 lines
17 KiB
JavaScript
454 lines
17 KiB
JavaScript
import classesToSelector from '../../shared/classes-to-selector.js';
|
|
import createElementIfNotDefined from '../../shared/create-element-if-not-defined.js';
|
|
import { elementIndex, elementOuterSize, elementParents } from '../../shared/utils.js';
|
|
export default function 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`
|
|
}
|
|
});
|
|
swiper.pagination = {
|
|
el: null,
|
|
bullets: []
|
|
};
|
|
let bulletSize;
|
|
let dynamicBulletIndex = 0;
|
|
const makeElementsArray = el => {
|
|
if (!Array.isArray(el)) el = [el].filter(e => !!e);
|
|
return el;
|
|
};
|
|
function isPaginationDisabled() {
|
|
return !swiper.params.pagination.el || !swiper.pagination.el || Array.isArray(swiper.pagination.el) && swiper.pagination.el.length === 0;
|
|
}
|
|
function setSideBullets(bulletEl, position) {
|
|
const {
|
|
bulletActiveClass
|
|
} = swiper.params.pagination;
|
|
if (!bulletEl) return;
|
|
bulletEl = bulletEl[`${position === 'prev' ? 'previous' : 'next'}ElementSibling`];
|
|
if (bulletEl) {
|
|
bulletEl.classList.add(`${bulletActiveClass}-${position}`);
|
|
bulletEl = bulletEl[`${position === 'prev' ? 'previous' : 'next'}ElementSibling`];
|
|
if (bulletEl) {
|
|
bulletEl.classList.add(`${bulletActiveClass}-${position}-${position}`);
|
|
}
|
|
}
|
|
}
|
|
function onBulletClick(e) {
|
|
const bulletEl = e.target.closest(classesToSelector(swiper.params.pagination.bulletClass));
|
|
if (!bulletEl) {
|
|
return;
|
|
}
|
|
e.preventDefault();
|
|
const index = elementIndex(bulletEl) * swiper.params.slidesPerGroup;
|
|
if (swiper.params.loop) {
|
|
if (swiper.realIndex === index) return;
|
|
const newSlideIndex = swiper.getSlideIndexByData(index);
|
|
const currentSlideIndex = swiper.getSlideIndexByData(swiper.realIndex);
|
|
if (newSlideIndex > swiper.slides.length - swiper.loopedSlides) {
|
|
swiper.loopFix({
|
|
direction: newSlideIndex > currentSlideIndex ? 'next' : 'prev',
|
|
activeSlideIndex: newSlideIndex,
|
|
slideTo: false
|
|
});
|
|
}
|
|
swiper.slideToLoop(index);
|
|
} else {
|
|
swiper.slideTo(index);
|
|
}
|
|
}
|
|
function update() {
|
|
// Render || Update Pagination bullets/items
|
|
const rtl = swiper.rtl;
|
|
const params = swiper.params.pagination;
|
|
if (isPaginationDisabled()) return;
|
|
let el = swiper.pagination.el;
|
|
el = makeElementsArray(el);
|
|
// Current/Total
|
|
let current;
|
|
let previousIndex;
|
|
const slidesLength = swiper.virtual && swiper.params.virtual.enabled ? swiper.virtual.slides.length : swiper.slides.length;
|
|
const total = swiper.params.loop ? Math.ceil(slidesLength / swiper.params.slidesPerGroup) : swiper.snapGrid.length;
|
|
if (swiper.params.loop) {
|
|
previousIndex = swiper.previousRealIndex || 0;
|
|
current = swiper.params.slidesPerGroup > 1 ? Math.floor(swiper.realIndex / swiper.params.slidesPerGroup) : 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;
|
|
let lastIndex;
|
|
let midIndex;
|
|
if (params.dynamicBullets) {
|
|
bulletSize = elementOuterSize(bullets[0], swiper.isHorizontal() ? 'width' : 'height', true);
|
|
el.forEach(subEl => {
|
|
subEl.style[swiper.isHorizontal() ? 'width' : 'height'] = `${bulletSize * (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}`)].map(s => typeof s === 'string' && s.includes(' ') ? s.split(' ') : s).flat();
|
|
bulletEl.classList.remove(...classesToRemove);
|
|
});
|
|
if (el.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) {
|
|
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 * dynamicBulletsLength - bulletSize) / 2 - midIndex * bulletSize;
|
|
const offsetProp = rtl ? 'right' : 'left';
|
|
bullets.forEach(bullet => {
|
|
bullet.style[swiper.isHorizontal() ? offsetProp : 'top'] = `${bulletsOffset}px`;
|
|
});
|
|
}
|
|
}
|
|
el.forEach((subEl, subElIndex) => {
|
|
if (params.type === 'fraction') {
|
|
subEl.querySelectorAll(classesToSelector(params.currentClass)).forEach(fractionEl => {
|
|
fractionEl.textContent = params.formatFractionCurrent(current + 1);
|
|
});
|
|
subEl.querySelectorAll(classesToSelector(params.totalClass)).forEach(totalEl => {
|
|
totalEl.textContent = 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) {
|
|
subEl.innerHTML = 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 = swiper.params.pagination;
|
|
if (isPaginationDisabled()) return;
|
|
const slidesLength = swiper.virtual && swiper.params.virtual.enabled ? swiper.virtual.slides.length : swiper.slides.length;
|
|
let el = swiper.pagination.el;
|
|
el = makeElementsArray(el);
|
|
let paginationHTML = '';
|
|
if (params.type === 'bullets') {
|
|
let numberOfBullets = swiper.params.loop ? Math.ceil(slidesLength / swiper.params.slidesPerGroup) : swiper.snapGrid.length;
|
|
if (swiper.params.freeMode && swiper.params.freeMode.enabled && numberOfBullets > slidesLength) {
|
|
numberOfBullets = slidesLength;
|
|
}
|
|
for (let i = 0; i < numberOfBullets; i += 1) {
|
|
if (params.renderBullet) {
|
|
paginationHTML += params.renderBullet.call(swiper, i, params.bulletClass);
|
|
} else {
|
|
// prettier-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 = [];
|
|
el.forEach(subEl => {
|
|
if (params.type !== 'custom') {
|
|
subEl.innerHTML = paginationHTML || '';
|
|
}
|
|
if (params.type === 'bullets') {
|
|
swiper.pagination.bullets.push(...subEl.querySelectorAll(classesToSelector(params.bulletClass)));
|
|
}
|
|
});
|
|
if (params.type !== 'custom') {
|
|
emit('paginationRender', el[0]);
|
|
}
|
|
}
|
|
function init() {
|
|
swiper.params.pagination = createElementIfNotDefined(swiper, swiper.originalParams.pagination, swiper.params.pagination, {
|
|
el: 'swiper-pagination'
|
|
});
|
|
const params = swiper.params.pagination;
|
|
if (!params.el) return;
|
|
let el;
|
|
if (typeof params.el === 'string' && swiper.isElement) {
|
|
el = swiper.el.shadowRoot.querySelector(params.el);
|
|
}
|
|
if (!el && typeof params.el === 'string') {
|
|
el = [...document.querySelectorAll(params.el)];
|
|
}
|
|
if (!el) {
|
|
el = params.el;
|
|
}
|
|
if (!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) {
|
|
el = el.filter(subEl => {
|
|
if (elementParents(subEl, '.swiper')[0] !== swiper.el) return false;
|
|
return true;
|
|
})[0];
|
|
}
|
|
}
|
|
if (Array.isArray(el) && el.length === 1) el = el[0];
|
|
Object.assign(swiper.pagination, {
|
|
el
|
|
});
|
|
el = makeElementsArray(el);
|
|
el.forEach(subEl => {
|
|
if (params.type === 'bullets' && params.clickable) {
|
|
subEl.classList.add(params.clickableClass);
|
|
}
|
|
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 = swiper.params.pagination;
|
|
if (isPaginationDisabled()) return;
|
|
let el = swiper.pagination.el;
|
|
if (el) {
|
|
el = makeElementsArray(el);
|
|
el.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.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 = swiper.params.pagination;
|
|
let {
|
|
el
|
|
} = swiper.pagination;
|
|
el = makeElementsArray(el);
|
|
el.forEach(subEl => {
|
|
subEl.classList.remove(params.horizontalClass, params.verticalClass);
|
|
subEl.classList.add(swiper.isHorizontal() ? params.horizontalClass : params.verticalClass);
|
|
});
|
|
});
|
|
on('init', () => {
|
|
if (swiper.params.pagination.enabled === false) {
|
|
// eslint-disable-next-line
|
|
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', () => {
|
|
let {
|
|
el
|
|
} = swiper.pagination;
|
|
if (el) {
|
|
el = makeElementsArray(el);
|
|
el.forEach(subEl => subEl.classList[swiper.enabled ? 'remove' : 'add'](swiper.params.pagination.lockClass));
|
|
}
|
|
});
|
|
on('lock unlock', () => {
|
|
update();
|
|
});
|
|
on('click', (_s, e) => {
|
|
const targetEl = e.target;
|
|
let {
|
|
el
|
|
} = swiper.pagination;
|
|
if (!Array.isArray(el)) el = [el].filter(element => !!element);
|
|
if (swiper.params.pagination.el && swiper.params.pagination.hideOnClick && el && el.length > 0 && !targetEl.classList.contains(swiper.params.pagination.bulletClass)) {
|
|
if (swiper.navigation && (swiper.navigation.nextEl && targetEl === swiper.navigation.nextEl || swiper.navigation.prevEl && targetEl === swiper.navigation.prevEl)) return;
|
|
const isHidden = el[0].classList.contains(swiper.params.pagination.hiddenClass);
|
|
if (isHidden === true) {
|
|
emit('paginationShow');
|
|
} else {
|
|
emit('paginationHide');
|
|
}
|
|
el.forEach(subEl => subEl.classList.toggle(swiper.params.pagination.hiddenClass));
|
|
}
|
|
});
|
|
const enable = () => {
|
|
swiper.el.classList.remove(swiper.params.pagination.paginationDisabledClass);
|
|
let {
|
|
el
|
|
} = swiper.pagination;
|
|
if (el) {
|
|
el = makeElementsArray(el);
|
|
el.forEach(subEl => subEl.classList.remove(swiper.params.pagination.paginationDisabledClass));
|
|
}
|
|
init();
|
|
render();
|
|
update();
|
|
};
|
|
const disable = () => {
|
|
swiper.el.classList.add(swiper.params.pagination.paginationDisabledClass);
|
|
let {
|
|
el
|
|
} = swiper.pagination;
|
|
if (el) {
|
|
el = makeElementsArray(el);
|
|
el.forEach(subEl => subEl.classList.add(swiper.params.pagination.paginationDisabledClass));
|
|
}
|
|
destroy();
|
|
};
|
|
Object.assign(swiper.pagination, {
|
|
enable,
|
|
disable,
|
|
render,
|
|
update,
|
|
init,
|
|
destroy
|
|
});
|
|
} |