UNPKG

swiper

Version:

Most modern mobile touch slider and framework with hardware accelerated transitions

248 lines (219 loc) 7.03 kB
import { createEffect, createMemo, createSignal, onCleanup, onMount, Show, splitProps, children, } from 'solid-js'; import SwiperCore from 'swiper'; import { SwiperContext } from './context.js'; import { getChangedParams } from '../components-shared/get-changed-params.js'; import { getChildren } from './get-children.js'; import { getParams } from '../components-shared/get-params.js'; import { calcLoopedSlides, renderLoop } from './loop.js'; import { mountSwiper } from '../components-shared/mount-swiper.js'; import { updateSwiper } from '../components-shared/update-swiper.js'; import { extend, needsNavigation, needsPagination, needsScrollbar, uniqueClasses, } from '../components-shared/utils.js'; import { renderVirtual } from './virtual.js'; import { updateOnVirtualData } from '../components-shared/update-on-virtual-data.js'; const Swiper = (props) => { let eventsAssigned = false; const [containerClasses, setContainerClasses] = createSignal('swiper'); const [virtualData, setVirtualData] = createSignal(null); const [, setBreakpointChanged] = createSignal(false); // The variables bellow are mofied by SolidJS and can't be const let initializedRef = false; // eslint-disable-line prefer-const let swiperElRef = null; // eslint-disable-line prefer-const let swiperRef = null; // eslint-disable-line prefer-const let oldPassedParamsRef = null; // eslint-disable-line prefer-const let oldSlides = null; // eslint-disable-line prefer-const let nextElRef = null; // eslint-disable-line prefer-const let prevElRef = null; // eslint-disable-line prefer-const let paginationElRef = null; // eslint-disable-line prefer-const let scrollbarElRef = null; // eslint-disable-line prefer-const const [local, rest] = splitProps(props, [ 'children', 'class', 'onSwiper', 'ref', 'tag', 'wrapperTag', ]); const params = createMemo(() => getParams(rest)); const slidesSlots = children(() => getChildren(local.children)); const onBeforeBreakpoint = () => { setBreakpointChanged((state) => !state); }; Object.assign(params().params.on, { _containerClasses(swiper, classes) { setContainerClasses(classes); }, }); const initSwiper = () => { // init swiper Object.assign(params().params.on, params().events); eventsAssigned = true; swiperRef = new SwiperCore(params().params); swiperRef.loopCreate = () => {}; swiperRef.loopDestroy = () => {}; if (params().params.loop) { swiperRef.loopedSlides = calcLoopedSlides(slidesSlots().slides, params().params); } if (swiperRef.virtual && swiperRef.params.virtual.enabled) { swiperRef.virtual.slides = slidesSlots().slides; const extendWith = { cache: false, slides: slidesSlots().slides, renderExternal: (data) => { setVirtualData(data); }, renderExternalUpdate: true, }; extend(swiperRef.params.virtual, extendWith); extend(swiperRef.originalParams.virtual, extendWith); } }; if (!swiperElRef) { initSwiper(); } // Listen for breakpoints change if (swiperRef) { swiperRef.on('_beforeBreakpoint', onBeforeBreakpoint); } const attachEvents = () => { if (eventsAssigned || !params().events || !swiperRef) return; Object.keys(params().events).forEach((eventName) => { swiperRef.on(eventName, params().events[eventName]); }); }; const detachEvents = () => { if (!params().events || !swiperRef) return; Object.keys(params().events).forEach((eventName) => { swiperRef.off(eventName, params().events[eventName]); }); }; onCleanup(() => { if (swiperRef) swiperRef.off('_beforeBreakpoint', onBeforeBreakpoint); }); // set initialized flag createEffect(() => { if (!initializedRef && swiperRef) { swiperRef.emitSlidesClasses(); initializedRef = true; } }); // mount swiper onMount(() => { if (local.ref) { if (typeof local.ref === 'function') { local.ref(swiperElRef); } else { local.ref = swiperElRef; } } if (!swiperElRef) return; if (swiperRef.destroyed) { initSwiper(); } mountSwiper( { el: swiperElRef, nextEl: nextElRef, prevEl: prevElRef, paginationEl: paginationElRef, scrollbarEl: scrollbarElRef, swiper: swiperRef, }, params().params, ); if (local.onSwiper) local.onSwiper(swiperRef); }); onCleanup(() => { if (swiperRef && !swiperRef.destroyed) { swiperRef.destroy(true, false); } }); // watch for params change createEffect(() => { attachEvents(); const { passedParams } = params(); const changedParams = getChangedParams( passedParams, oldPassedParamsRef, slidesSlots().slides, oldSlides, (c) => c.key, ); oldPassedParamsRef = passedParams; oldSlides = slidesSlots().slides; if (changedParams.length && swiperRef && !swiperRef.destroyed) { updateSwiper({ swiper: swiperRef, slides: slidesSlots().slides, passedParams, changedParams, nextEl: nextElRef, prevEl: prevElRef, scrollbarEl: scrollbarElRef, paginationEl: paginationElRef, }); } onCleanup(detachEvents); }); // update on virtual update createEffect(() => { updateOnVirtualData(swiperRef); setTimeout(() => { updateOnVirtualData(swiperRef); }); }); // bypass swiper instance to slides function renderSlides() { if (params().params.virtual) { return renderVirtual(swiperRef, slidesSlots().slides, virtualData()); } if (!params().params.loop || (swiperRef && swiperRef.destroyed)) { return slidesSlots().slides; } return renderLoop(swiperRef, slidesSlots().slides, params().params); } /* eslint-disable react/react-in-jsx-scope */ /* eslint-disable react/no-unknown-property */ return ( <div ref={swiperElRef} class={uniqueClasses(`${containerClasses()}${local.class ? ` ${local.class}` : ''}`)} {...params().rest} > <SwiperContext.Provider value={swiperRef}> {slidesSlots().slots['container-start']} <div class="swiper-wrapper"> {slidesSlots().slots['wrapper-start']} {renderSlides()} {slidesSlots().slots['wrapper-end']} </div> <Show when={needsNavigation(params().params)}> <div ref={prevElRef} class="swiper-button-prev" /> <div ref={nextElRef} class="swiper-button-next" /> </Show> <Show when={needsScrollbar(params().params)}> <div ref={scrollbarElRef} class="swiper-scrollbar" /> </Show> <Show when={needsPagination(params().params)}> <div ref={paginationElRef} class="swiper-pagination" /> </Show> {slidesSlots().slots['container-end']} </SwiperContext.Provider> </div> ); }; export { Swiper };