swiper.jsx 6.9 KB

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