UNPKG

swiper

Version:

Most modern mobile touch slider and framework with hardware accelerated transitions

626 lines (618 loc) 23.7 kB
/** * Swiper Vue 14.0.0 * Most modern mobile touch slider and framework with hardware accelerated transitions * https://swiperjs.com * * Copyright 2014-2026 Vladimir Kharlampidi * * Released under the MIT License * * Released on: June 26, 2026 */ import { h, defineComponent, ref, shallowRef, onUpdated, provide, watch, nextTick, onMounted, onBeforeUnmount, onBeforeUpdate, computed, inject } from 'vue'; import { a as getParams, g as getChangedParams, u as updateOnVirtualData, m as mountSwiper } from './shared/update-on-virtual-data.mjs'; import { e as extend, d as updateSwiper, u as uniqueClasses, w as wrapperClass, n as needsNavigation, c as needsScrollbar, b as needsPagination } from './shared/update-swiper.mjs'; import { S as Swiper$1 } from './shared/swiper-core.mjs'; function getChildren(originalSlots = {}, slidesRef, oldSlidesRef) { const slides = []; const slots = { 'container-start': [], 'container-end': [], 'wrapper-start': [], 'wrapper-end': [], }; const getSlidesFromElements = (els, slotName) => { if (!Array.isArray(els)) return; let effectiveSlot = slotName === 'default' ? 'container-end' : slotName; els.forEach((vnode) => { const isFragment = typeof vnode.type === 'symbol'; if (isFragment && vnode.children) { getSlidesFromElements(vnode.children, effectiveSlot); return; } const typeObj = vnode.type; const legacyTag = vnode.componentOptions?.tag; if ((typeObj && (typeObj.name === 'SwiperSlide' || typeObj.name === 'AsyncComponentWrapper')) || legacyTag === 'SwiperSlide') { slides.push(vnode); } else if (slots[effectiveSlot]) { slots[effectiveSlot].push(vnode); } }); }; Object.keys(originalSlots).forEach((slotName) => { const slotFn = originalSlots[slotName]; if (typeof slotFn !== 'function') return; const els = slotFn(); getSlidesFromElements(els, slotName); }); oldSlidesRef.value = slidesRef.value; slidesRef.value = slides; return { slides, slots }; } function renderVirtual(swiperRef, slides, virtualData) { if (!virtualData) return null; const swiper = swiperRef.value; if (!swiper) return null; const getSlideIndex = (index) => { let slideIndex = index; if (index < 0) { slideIndex = slides.length + index; } else if (slideIndex >= slides.length) { slideIndex -= slides.length; } return slideIndex; }; const style = swiper.isHorizontal() ? { [swiper.rtlTranslate ? 'right' : 'left']: `${virtualData.offset}px` } : { top: `${virtualData.offset}px` }; const { from, to } = virtualData; const loopFrom = swiper.params.loop ? -slides.length : 0; const loopTo = swiper.params.loop ? slides.length * 2 : slides.length; const slidesToRender = []; for (let i = loopFrom; i < loopTo; i += 1) { if (i >= from && i <= to && slidesToRender.length < slides.length) { const slide = slides[getSlideIndex(i)]; if (slide) slidesToRender.push(slide); } } return slidesToRender.map((slide) => { const props = (slide.props ?? {}); props.style = props.style ?? {}; props.swiperRef = swiperRef; props.style = style; slide.props = props; if (slide.type) { return h(slide.type, { ...props }, slide.children); } if (slide.componentOptions) { return h(slide.componentOptions.Ctor, { ...props }, slide.componentOptions.children); } return undefined; }); } const SWIPER_EVENTS = [ '_beforeBreakpoint', '_containerClasses', '_slideClass', '_slideClasses', '_swiper', '_freeModeNoMomentumRelease', '_virtualUpdated', 'activeIndexChange', 'afterInit', 'autoplay', 'autoplayStart', 'autoplayStop', 'autoplayPause', 'autoplayResume', 'autoplayTimeLeft', 'beforeDestroy', 'beforeInit', 'beforeLoopFix', 'beforeResize', 'beforeSlideChangeStart', 'beforeTransitionStart', 'breakpoint', 'changeDirection', 'click', 'disable', 'doubleTap', 'doubleClick', 'destroy', 'enable', 'fromEdge', 'hashChange', 'hashSet', 'init', 'keyPress', 'lock', 'loopFix', 'momentumBounce', 'navigationHide', 'navigationShow', 'navigationPrev', 'navigationNext', 'observerUpdate', 'orientationchange', 'paginationHide', 'paginationRender', 'paginationShow', 'paginationUpdate', 'progress', 'reachBeginning', 'reachEnd', 'realIndexChange', 'resize', 'scroll', 'scrollbarDragEnd', 'scrollbarDragMove', 'scrollbarDragStart', 'setTransition', 'setTranslate', 'slidesUpdated', 'slideChange', 'slideChangeTransitionEnd', 'slideChangeTransitionStart', 'slideNextTransitionEnd', 'slideNextTransitionStart', 'slidePrevTransitionEnd', 'slidePrevTransitionStart', 'slideResetTransitionStart', 'slideResetTransitionEnd', 'sliderMove', 'sliderFirstMove', 'slidesLengthChange', 'slidesGridLengthChange', 'snapGridLengthChange', 'snapIndexChange', 'swiper', 'tap', 'toEdge', 'touchEnd', 'touchMove', 'touchMoveOpposite', 'touchStart', 'transitionEnd', 'transitionStart', 'unlock', 'update', 'virtualUpdate', 'zoomChange', ]; const Swiper = defineComponent({ name: 'Swiper', props: { tag: { type: String, default: 'div' }, wrapperTag: { type: String, default: 'div' }, modules: { type: Array, default: undefined }, init: { type: Boolean, default: undefined }, direction: { type: String, default: undefined, }, oneWayMovement: { type: Boolean, default: undefined }, swiperElementNodeName: { type: String, default: 'SWIPER-CONTAINER' }, touchEventsTarget: { type: String, default: undefined, }, initialSlide: { type: Number, default: undefined }, speed: { type: Number, default: undefined }, cssMode: { type: Boolean, default: undefined }, updateOnWindowResize: { type: Boolean, default: undefined }, resizeObserver: { type: Boolean, default: undefined }, nested: { type: Boolean, default: undefined }, focusableElements: { type: String, default: undefined }, width: { type: Number, default: undefined }, height: { type: Number, default: undefined }, preventInteractionOnTransition: { type: Boolean, default: undefined }, userAgent: { type: String, default: undefined }, url: { type: String, default: undefined }, edgeSwipeDetection: { type: [Boolean, String], default: undefined, }, edgeSwipeThreshold: { type: Number, default: undefined }, autoHeight: { type: Boolean, default: undefined }, setWrapperSize: { type: Boolean, default: undefined }, virtualTranslate: { type: Boolean, default: undefined }, effect: { type: String, default: undefined }, breakpoints: { type: Object, default: undefined }, breakpointsBase: { type: String, default: undefined }, spaceBetween: { type: [Number, String], default: undefined, }, slidesPerView: { type: [Number, String], default: undefined, }, maxBackfaceHiddenSlides: { type: Number, default: undefined }, slidesPerGroup: { type: Number, default: undefined }, slidesPerGroupSkip: { type: Number, default: undefined }, slidesPerGroupAuto: { type: Boolean, default: undefined }, centeredSlides: { type: Boolean, default: undefined }, centeredSlidesBounds: { type: Boolean, default: undefined }, slidesOffsetBefore: { type: Number, default: undefined }, slidesOffsetAfter: { type: Number, default: undefined }, normalizeSlideIndex: { type: Boolean, default: undefined }, centerInsufficientSlides: { type: Boolean, default: undefined }, watchOverflow: { type: Boolean, default: undefined }, roundLengths: { type: Boolean, default: undefined }, touchRatio: { type: Number, default: undefined }, touchAngle: { type: Number, default: undefined }, simulateTouch: { type: Boolean, default: undefined }, shortSwipes: { type: Boolean, default: undefined }, longSwipes: { type: Boolean, default: undefined }, longSwipesRatio: { type: Number, default: undefined }, longSwipesMs: { type: Number, default: undefined }, followFinger: { type: Boolean, default: undefined }, allowTouchMove: { type: Boolean, default: undefined }, threshold: { type: Number, default: undefined }, touchMoveStopPropagation: { type: Boolean, default: undefined }, touchStartPreventDefault: { type: Boolean, default: undefined }, touchStartForcePreventDefault: { type: Boolean, default: undefined }, touchReleaseOnEdges: { type: Boolean, default: undefined }, uniqueNavElements: { type: Boolean, default: undefined }, resistance: { type: Boolean, default: undefined }, resistanceRatio: { type: Number, default: undefined }, watchSlidesProgress: { type: Boolean, default: undefined }, grabCursor: { type: Boolean, default: undefined }, preventClicks: { type: Boolean, default: undefined }, preventClicksPropagation: { type: Boolean, default: undefined }, slideToClickedSlide: { type: Boolean, default: undefined }, loop: { type: Boolean, default: undefined }, loopedSlides: { type: Number, default: undefined }, loopPreventsSliding: { type: Boolean, default: undefined }, loopAdditionalSlides: { type: Number, default: undefined }, loopAddBlankSlides: { type: Boolean, default: undefined }, rewind: { type: Boolean, default: undefined }, allowSlidePrev: { type: Boolean, default: undefined }, allowSlideNext: { type: Boolean, default: undefined }, swipeHandler: { type: Boolean, default: undefined }, noSwiping: { type: Boolean, default: undefined }, noSwipingClass: { type: String, default: undefined }, noSwipingSelector: { type: String, default: undefined }, passiveListeners: { type: Boolean, default: undefined }, containerModifierClass: { type: String, default: undefined }, slideClass: { type: String, default: undefined }, slideActiveClass: { type: String, default: undefined }, slideVisibleClass: { type: String, default: undefined }, slideFullyVisibleClass: { type: String, default: undefined }, slideBlankClass: { type: String, default: undefined }, slideNextClass: { type: String, default: undefined }, slidePrevClass: { type: String, default: undefined }, wrapperClass: { type: String, default: undefined }, lazyPreloaderClass: { type: String, default: undefined }, lazyPreloadPrevNext: { type: Number, default: undefined }, runCallbacksOnInit: { type: Boolean, default: undefined }, observer: { type: Boolean, default: undefined }, observeParents: { type: Boolean, default: undefined }, observeSlideChildren: { type: Boolean, default: undefined }, a11y: { type: [Boolean, Object], default: undefined, }, autoplay: { type: [Boolean, Object], default: undefined, }, controller: { type: Object, default: undefined, }, coverflowEffect: { type: Object, default: undefined, }, cubeEffect: { type: Object, default: undefined }, fadeEffect: { type: Object, default: undefined }, flipEffect: { type: Object, default: undefined }, creativeEffect: { type: Object, default: undefined, }, cardsEffect: { type: Object, default: undefined }, hashNavigation: { type: [Boolean, Object], default: undefined, }, history: { type: [Boolean, Object], default: undefined, }, keyboard: { type: [Boolean, Object], default: undefined, }, mousewheel: { type: [Boolean, Object], default: undefined, }, navigation: { type: [Boolean, Object], default: undefined, }, pagination: { type: [Boolean, Object], default: undefined, }, parallax: { type: [Boolean, Object], default: undefined, }, scrollbar: { type: [Boolean, Object], default: undefined, }, thumbs: { type: Object, default: undefined }, virtual: { type: [Boolean, Object], default: undefined, }, zoom: { type: [Boolean, Object], default: undefined, }, grid: { type: Object, default: undefined }, freeMode: { type: [Boolean, Object], default: undefined, }, enabled: { type: Boolean, default: undefined }, }, emits: SWIPER_EVENTS, setup(props, { slots: originalSlots, emit }) { const { tag: Tag, wrapperTag: WrapperTag } = props; const containerClasses = ref('swiper'); const virtualData = ref(null); const breakpointChanged = ref(false); const initializedRef = ref(false); const swiperElRef = ref(null); const swiperRef = shallowRef(null); const oldPassedParamsRef = ref(null); const slidesRef = { value: [] }; const oldSlidesRef = { value: [] }; const nextElRef = ref(null); const prevElRef = ref(null); const paginationElRef = ref(null); const scrollbarElRef = ref(null); const { params: swiperParams, passedParams } = getParams(props, false); getChildren(originalSlots, slidesRef, oldSlidesRef); oldPassedParamsRef.value = passedParams; oldSlidesRef.value = slidesRef.value; const onBeforeBreakpoint = () => { getChildren(originalSlots, slidesRef, oldSlidesRef); breakpointChanged.value = true; }; swiperParams.onAny = (event, ...args) => { emit(event, ...args); }; Object.assign(swiperParams.on, { _beforeBreakpoint: onBeforeBreakpoint, _containerClasses(_swiper, classes) { containerClasses.value = classes; }, }); // init Swiper const passParams = { ...swiperParams }; delete passParams.wrapperClass; swiperRef.value = new Swiper$1(passParams); const instance = swiperRef.value; if (instance && instance.virtual && instance.params.virtual?.enabled) { instance.virtual.slides = slidesRef.value; const extendWith = { cache: false, slides: slidesRef.value, renderExternal: (data) => { virtualData.value = data; }, renderExternalUpdate: false, }; extend(instance.params.virtual, extendWith); if (instance.originalParams.virtual) extend(instance.originalParams.virtual, extendWith); } onUpdated(() => { if (!initializedRef.value && swiperRef.value) { swiperRef.value.emitSlidesClasses(); initializedRef.value = true; } const { passedParams: newPassedParams } = getParams(props, false); const changedParams = getChangedParams(newPassedParams, oldPassedParamsRef.value, slidesRef.value, oldSlidesRef.value, (c) => (c.props ? c.props.key : undefined)); oldPassedParamsRef.value = newPassedParams; if ((changedParams.length || breakpointChanged.value) && swiperRef.value && !swiperRef.value.destroyed) { updateSwiper({ swiper: swiperRef.value, slides: slidesRef.value, passedParams: newPassedParams, changedParams, nextEl: nextElRef.value, prevEl: prevElRef.value, scrollbarEl: scrollbarElRef.value, paginationEl: paginationElRef.value, }); } breakpointChanged.value = false; }); provide('swiper', swiperRef); watch(virtualData, () => { nextTick(() => { updateOnVirtualData(swiperRef.value); }); }); onMounted(() => { if (!swiperElRef.value || !swiperRef.value) return; mountSwiper({ el: swiperElRef.value, nextEl: nextElRef.value, prevEl: prevElRef.value, paginationEl: paginationElRef.value, scrollbarEl: scrollbarElRef.value, swiper: swiperRef.value, }, swiperParams); emit('swiper', swiperRef.value); }); onBeforeUnmount(() => { if (swiperRef.value && !swiperRef.value.destroyed) { swiperRef.value.destroy(true, false); } }); function renderSlides(slides) { if (swiperParams.virtual) { return renderVirtual(swiperRef, slides, virtualData.value); } slides.forEach((slide, index) => { const slideProps = (slide.props ?? {}); slideProps.swiperRef = swiperRef; slideProps.swiperSlideIndex = index; slide.props = slideProps; }); return slides; } return () => { const { slides, slots } = getChildren(originalSlots, slidesRef, oldSlidesRef); return h(Tag, { ref: swiperElRef, class: uniqueClasses(containerClasses.value), }, [ slots['container-start'], h(WrapperTag, { class: wrapperClass(swiperParams.wrapperClass) }, [ slots['wrapper-start'], renderSlides(slides), slots['wrapper-end'], ]), needsNavigation(props) && [ h('div', { ref: prevElRef, class: 'swiper-button-prev' }), h('div', { ref: nextElRef, class: 'swiper-button-next' }), ], needsScrollbar(props) && h('div', { ref: scrollbarElRef, class: 'swiper-scrollbar' }), needsPagination(props) && h('div', { ref: paginationElRef, class: 'swiper-pagination' }), slots['container-end'], ]); }; }, }); const SwiperSlide = defineComponent({ name: 'SwiperSlide', props: { tag: { type: String, default: 'div', }, swiperRef: { type: Object, required: false }, swiperSlideIndex: { type: Number, default: undefined, required: false }, zoom: { type: [Boolean, Number], default: undefined, required: false, }, lazy: { type: Boolean, default: false, required: false }, virtualIndex: { type: [String, Number], default: undefined, }, }, setup(props, { slots }) { let eventAttached = false; const { swiperRef } = props; const slideElRef = ref(null); const slideClasses = ref('swiper-slide'); const lazyLoaded = ref(false); function updateClasses(_swiper, el, classNames) { if (el === slideElRef.value) { slideClasses.value = classNames; } } onMounted(() => { if (!swiperRef || !swiperRef.value) return; swiperRef.value.on('_slideClass', updateClasses); eventAttached = true; }); onBeforeUpdate(() => { if (eventAttached || !swiperRef || !swiperRef.value) return; swiperRef.value.on('_slideClass', updateClasses); eventAttached = true; }); onUpdated(() => { if (!slideElRef.value || !swiperRef || !swiperRef.value) return; if (typeof props.swiperSlideIndex !== 'undefined') { slideElRef.value.swiperSlideIndex = props.swiperSlideIndex; } if (swiperRef.value.destroyed) { if (slideClasses.value !== 'swiper-slide') { slideClasses.value = 'swiper-slide'; } } }); onBeforeUnmount(() => { if (!swiperRef || !swiperRef.value) return; swiperRef.value.off('_slideClass', updateClasses); }); const slideData = computed(() => ({ isActive: slideClasses.value.indexOf('swiper-slide-active') >= 0, isVisible: slideClasses.value.indexOf('swiper-slide-visible') >= 0, isPrev: slideClasses.value.indexOf('swiper-slide-prev') >= 0, isNext: slideClasses.value.indexOf('swiper-slide-next') >= 0, })); provide('swiperSlide', slideData); const onLoad = () => { lazyLoaded.value = true; }; const lazyPreloaderHook = (vnode) => { const el = vnode.el; if (el) el.lazyPreloaderManaged = true; }; return () => h(props.tag, { class: uniqueClasses(`${slideClasses.value}`), ref: slideElRef, 'data-swiper-slide-index': typeof props.virtualIndex === 'undefined' && swiperRef && swiperRef.value && swiperRef.value.params.loop ? props.swiperSlideIndex : props.virtualIndex, onLoadCapture: onLoad, }, props.zoom ? h('div', { class: 'swiper-zoom-container', 'data-swiper-zoom': typeof props.zoom === 'number' ? props.zoom : undefined, }, [ slots.default && slots.default(slideData.value), props.lazy && !lazyLoaded.value && h('div', { class: 'swiper-lazy-preloader', onVnodeMounted: lazyPreloaderHook, }), ]) : [ slots.default && slots.default(slideData.value), props.lazy && !lazyLoaded.value && h('div', { class: 'swiper-lazy-preloader', onVnodeMounted: lazyPreloaderHook, }), ]); }, }); const useSwiperSlide = () => inject('swiperSlide'); const useSwiper = () => inject('swiper'); export { Swiper, SwiperSlide, useSwiper, useSwiperSlide };