UNPKG

7.03 kBJSXView Raw
1import {
2 createEffect,
3 createMemo,
4 createSignal,
5 onCleanup,
6 onMount,
7 Show,
8 splitProps,
9 children,
10} from 'solid-js';
11import SwiperCore from 'swiper';
12import { SwiperContext } from './context.js';
13import { getChangedParams } from '../components-shared/get-changed-params.js';
14import { getChildren } from './get-children.js';
15import { getParams } from '../components-shared/get-params.js';
16import { calcLoopedSlides, renderLoop } from './loop.js';
17import { mountSwiper } from '../components-shared/mount-swiper.js';
18import { updateSwiper } from '../components-shared/update-swiper.js';
19import {
20 extend,
21 needsNavigation,
22 needsPagination,
23 needsScrollbar,
24 uniqueClasses,
25} from '../components-shared/utils.js';
26import { renderVirtual } from './virtual.js';
27import { updateOnVirtualData } from '../components-shared/update-on-virtual-data.js';
28
29const Swiper = (props) => {
30 let eventsAssigned = false;
31 const [containerClasses, setContainerClasses] = createSignal('swiper');
32 const [virtualData, setVirtualData] = createSignal(null);
33 const [, setBreakpointChanged] = createSignal(false);
34
35 // The variables bellow are mofied by SolidJS and can't be const
36 let initializedRef = false; // eslint-disable-line prefer-const
37 let swiperElRef = null; // eslint-disable-line prefer-const
38 let swiperRef = null; // eslint-disable-line prefer-const
39 let oldPassedParamsRef = null; // eslint-disable-line prefer-const
40 let oldSlides = null; // eslint-disable-line prefer-const
41
42 let nextElRef = null; // eslint-disable-line prefer-const
43 let prevElRef = null; // eslint-disable-line prefer-const
44 let paginationElRef = null; // eslint-disable-line prefer-const
45 let scrollbarElRef = null; // eslint-disable-line prefer-const
46
47 const [local, rest] = splitProps(props, [
48 'children',
49 'class',
50 'onSwiper',
51 'ref',
52 'tag',
53 'wrapperTag',
54 ]);
55
56 const params = createMemo(() => getParams(rest));
57
58 const slidesSlots = children(() => getChildren(local.children));
59
60 const onBeforeBreakpoint = () => {
61 setBreakpointChanged((state) => !state);
62 };
63
64 Object.assign(params().params.on, {
65 _containerClasses(swiper, classes) {
66 setContainerClasses(classes);
67 },
68 });
69
70 const initSwiper = () => {
71 // init swiper
72 Object.assign(params().params.on, params().events);
73 eventsAssigned = true;
74 swiperRef = new SwiperCore(params().params);
75 swiperRef.loopCreate = () => {};
76 swiperRef.loopDestroy = () => {};
77 if (params().params.loop) {
78 swiperRef.loopedSlides = calcLoopedSlides(slidesSlots().slides, params().params);
79 }
80 if (swiperRef.virtual && swiperRef.params.virtual.enabled) {
81 swiperRef.virtual.slides = slidesSlots().slides;
82 const extendWith = {
83 cache: false,
84 slides: slidesSlots().slides,
85 renderExternal: (data) => {
86 setVirtualData(data);
87 },
88 renderExternalUpdate: true,
89 };
90 extend(swiperRef.params.virtual, extendWith);
91 extend(swiperRef.originalParams.virtual, extendWith);
92 }
93 };
94
95 if (!swiperElRef) {
96 initSwiper();
97 }
98
99 // Listen for breakpoints change
100 if (swiperRef) {
101 swiperRef.on('_beforeBreakpoint', onBeforeBreakpoint);
102 }
103
104 const attachEvents = () => {
105 if (eventsAssigned || !params().events || !swiperRef) return;
106 Object.keys(params().events).forEach((eventName) => {
107 swiperRef.on(eventName, params().events[eventName]);
108 });
109 };
110
111 const detachEvents = () => {
112 if (!params().events || !swiperRef) return;
113 Object.keys(params().events).forEach((eventName) => {
114 swiperRef.off(eventName, params().events[eventName]);
115 });
116 };
117
118 onCleanup(() => {
119 if (swiperRef) swiperRef.off('_beforeBreakpoint', onBeforeBreakpoint);
120 });
121
122 // set initialized flag
123 createEffect(() => {
124 if (!initializedRef && swiperRef) {
125 swiperRef.emitSlidesClasses();
126 initializedRef = true;
127 }
128 });
129
130 // mount swiper
131 onMount(() => {
132 if (local.ref) {
133 if (typeof local.ref === 'function') {
134 local.ref(swiperElRef);
135 } else {
136 local.ref = swiperElRef;
137 }
138 }
139 if (!swiperElRef) return;
140 if (swiperRef.destroyed) {
141 initSwiper();
142 }
143
144 mountSwiper(
145 {
146 el: swiperElRef,
147 nextEl: nextElRef,
148 prevEl: prevElRef,
149 paginationEl: paginationElRef,
150 scrollbarEl: scrollbarElRef,
151 swiper: swiperRef,
152 },
153 params().params,
154 );
155
156 if (local.onSwiper) local.onSwiper(swiperRef);
157 });
158
159 onCleanup(() => {
160 if (swiperRef && !swiperRef.destroyed) {
161 swiperRef.destroy(true, false);
162 }
163 });
164
165 // watch for params change
166 createEffect(() => {
167 attachEvents();
168 const { passedParams } = params();
169 const changedParams = getChangedParams(
170 passedParams,
171 oldPassedParamsRef,
172 slidesSlots().slides,
173 oldSlides,
174 (c) => c.key,
175 );
176 oldPassedParamsRef = passedParams;
177 oldSlides = slidesSlots().slides;
178 if (changedParams.length && swiperRef && !swiperRef.destroyed) {
179 updateSwiper({
180 swiper: swiperRef,
181 slides: slidesSlots().slides,
182 passedParams,
183 changedParams,
184 nextEl: nextElRef,
185 prevEl: prevElRef,
186 scrollbarEl: scrollbarElRef,
187 paginationEl: paginationElRef,
188 });
189 }
190 onCleanup(detachEvents);
191 });
192
193 // update on virtual update
194 createEffect(() => {
195 updateOnVirtualData(swiperRef);
196 setTimeout(() => {
197 updateOnVirtualData(swiperRef);
198 });
199 });
200
201 // bypass swiper instance to slides
202 function renderSlides() {
203 if (params().params.virtual) {
204 return renderVirtual(swiperRef, slidesSlots().slides, virtualData());
205 }
206 if (!params().params.loop || (swiperRef && swiperRef.destroyed)) {
207 return slidesSlots().slides;
208 }
209 return renderLoop(swiperRef, slidesSlots().slides, params().params);
210 }
211
212 /* eslint-disable react/react-in-jsx-scope */
213 /* eslint-disable react/no-unknown-property */
214
215 return (
216 <div
217 ref={swiperElRef}
218 class={uniqueClasses(`${containerClasses()}${local.class ? ` ${local.class}` : ''}`)}
219 {...params().rest}
220 >
221 <SwiperContext.Provider value={swiperRef}>
222 {slidesSlots().slots['container-start']}
223
224 <div class="swiper-wrapper">
225 {slidesSlots().slots['wrapper-start']}
226 {renderSlides()}
227 {slidesSlots().slots['wrapper-end']}
228 </div>
229
230 <Show when={needsNavigation(params().params)}>
231 <div ref={prevElRef} class="swiper-button-prev" />
232 <div ref={nextElRef} class="swiper-button-next" />
233 </Show>
234 <Show when={needsScrollbar(params().params)}>
235 <div ref={scrollbarElRef} class="swiper-scrollbar" />
236 </Show>
237 <Show when={needsPagination(params().params)}>
238 <div ref={paginationElRef} class="swiper-pagination" />
239 </Show>
240
241 {slidesSlots().slots['container-end']}
242 </SwiperContext.Provider>
243 </div>
244 );
245};
246
247export { Swiper };