Repository: nolimits4web/swiper Branch: master Commit: daa64671f4c8 Files: 309 Total size: 1.1 MB Directory structure: gitextract_z4g623hj/ ├── .all-contributorsrc ├── .browserslistrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.cjs ├── .gitattributes ├── .github/ │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── config.yml │ │ ├── feature-request.yml │ │ ├── swiper_core_issue.yml │ │ ├── swiper_element_issue.yml │ │ ├── swiper_react_issue.yml │ │ └── vue_issue.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ ├── lock.yml │ ├── stale.yml │ └── workflows/ │ ├── build.yml │ ├── formatting.yml │ ├── issue-close-require.yml │ ├── issue-labeled.yml │ └── lint.yml ├── .gitignore ├── .lintstagedrc ├── .npmignore ├── .prettierignore ├── .prettierrc ├── .vscode/ │ └── settings.json ├── BACKERS.md ├── CHANGELOG.md ├── CODE_CONTRIBUTORS.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── TODO.md ├── babel.config.json ├── demos/ │ ├── 010-default.html │ ├── 020-navigation.html │ ├── 030-pagination.html │ ├── 040-pagination-dynamic.html │ ├── 050-pagination-progress.html │ ├── 060-pagination-fraction.html │ ├── 070-pagination-custom.html │ ├── 080-scrollbar.html │ ├── 090-vertical.html │ ├── 100-space-between.html │ ├── 110-slides-per-view.html │ ├── 120-slides-per-view-auto.html │ ├── 125-snap-to-slide-edge.html │ ├── 130-centered.html │ ├── 140-centered-auto.html │ ├── 145-css-mode.html │ ├── 150-freemode.html │ ├── 160-scroll-container.html │ ├── 170-slides-per-column.html │ ├── 180-nested.html │ ├── 190-grab-cursor.html │ ├── 200-infinite-loop.html │ ├── 210-infinite-loop-with-slides-per-group.html │ ├── 211-slides-per-group-skip.html │ ├── 220-effect-fade.html │ ├── 230-effect-cube.html │ ├── 240-effect-coverflow.html │ ├── 250-effect-flip.html │ ├── 255-effect-cards.html │ ├── 257-effect-creative.html │ ├── 260-keyboard-control.html │ ├── 270-mousewheel-control.html │ ├── 280-autoplay.html │ ├── 290-dynamic-slides.html │ ├── 300-thumbs-gallery.html │ ├── 310-thumbs-gallery-loop.html │ ├── 320-multiple-swipers.html │ ├── 330-hash-navigation.html │ ├── 340-history.html │ ├── 350-rtl.html │ ├── 360-parallax.html │ ├── 370-lazy-load-images.html │ ├── 380-responsive-breakpoints.html │ ├── 381-ratio-breakpoints.html │ ├── 390-autoheight.html │ ├── 400-zoom.html │ ├── 410-virtual-slides.html │ ├── 420-custom-plugin.html │ ├── 430-slideable-menu.html │ ├── 440-change-direction.html │ ├── 450-watchSlidesVisibility.html │ ├── index.html │ └── vite.config.js ├── package.json ├── playground/ │ ├── core/ │ │ ├── index.html │ │ └── vite.config.js │ ├── element/ │ │ ├── index.html │ │ └── vite.config.js │ ├── react/ │ │ ├── App.jsx │ │ ├── index.html │ │ ├── main.js │ │ └── vite.config.js │ └── vue/ │ ├── App.vue │ ├── index.html │ ├── innerComp.vue │ ├── main.js │ └── vite.config.js ├── scripts/ │ ├── build-config.js │ ├── build-modules.js │ ├── build-sponsors.js │ ├── build-styles.js │ ├── build-types.js │ ├── build.js │ ├── release.js │ ├── utils/ │ │ ├── autoprefixer.js │ │ ├── banner.js │ │ ├── get-element-styles.js │ │ ├── helper.js │ │ ├── isProd.js │ │ ├── less.js │ │ ├── minify-css.js │ │ ├── minify.js │ │ ├── output-dir.js │ │ └── unwrap-css.js │ └── watch.js └── src/ ├── components-shared/ │ ├── get-changed-params.mjs │ ├── get-element-params.mjs │ ├── get-params.mjs │ ├── mount-swiper.mjs │ ├── params-list.mjs │ ├── update-on-virtual-data.mjs │ ├── update-swiper.mjs │ └── utils.mjs ├── copy/ │ ├── LICENSE │ ├── README.md │ └── package.json ├── core/ │ ├── breakpoints/ │ │ ├── getBreakpoint.mjs │ │ ├── index.mjs │ │ └── setBreakpoint.mjs │ ├── check-overflow/ │ │ └── index.mjs │ ├── classes/ │ │ ├── addClasses.mjs │ │ ├── index.mjs │ │ └── removeClasses.mjs │ ├── core.mjs │ ├── defaults.mjs │ ├── events/ │ │ ├── index.mjs │ │ ├── onClick.mjs │ │ ├── onDocumentTouchStart.mjs │ │ ├── onLoad.mjs │ │ ├── onResize.mjs │ │ ├── onScroll.mjs │ │ ├── onTouchEnd.mjs │ │ ├── onTouchMove.mjs │ │ └── onTouchStart.mjs │ ├── events-emitter.mjs │ ├── grab-cursor/ │ │ ├── index.mjs │ │ ├── setGrabCursor.mjs │ │ └── unsetGrabCursor.mjs │ ├── loop/ │ │ ├── index.mjs │ │ ├── loopCreate.mjs │ │ ├── loopDestroy.mjs │ │ └── loopFix.mjs │ ├── moduleExtendParams.mjs │ ├── modules/ │ │ ├── observer/ │ │ │ └── observer.mjs │ │ └── resize/ │ │ └── resize.mjs │ ├── slide/ │ │ ├── index.mjs │ │ ├── slideNext.mjs │ │ ├── slidePrev.mjs │ │ ├── slideReset.mjs │ │ ├── slideTo.mjs │ │ ├── slideToClickedSlide.mjs │ │ ├── slideToClosest.mjs │ │ └── slideToLoop.mjs │ ├── transition/ │ │ ├── index.mjs │ │ ├── setTransition.mjs │ │ ├── transitionEmit.mjs │ │ ├── transitionEnd.mjs │ │ └── transitionStart.mjs │ ├── translate/ │ │ ├── getTranslate.mjs │ │ ├── index.mjs │ │ ├── maxTranslate.mjs │ │ ├── minTranslate.mjs │ │ ├── setTranslate.mjs │ │ └── translateTo.mjs │ └── update/ │ ├── index.mjs │ ├── updateActiveIndex.mjs │ ├── updateAutoHeight.mjs │ ├── updateClickedSlide.mjs │ ├── updateProgress.mjs │ ├── updateSize.mjs │ ├── updateSlides.mjs │ ├── updateSlidesClasses.mjs │ ├── updateSlidesOffset.mjs │ └── updateSlidesProgress.mjs ├── modules/ │ ├── a11y/ │ │ ├── a11y.css │ │ └── a11y.mjs │ ├── autoplay/ │ │ ├── autoplay.css │ │ └── autoplay.mjs │ ├── controller/ │ │ ├── controller.css │ │ └── controller.mjs │ ├── effect-cards/ │ │ ├── effect-cards.css │ │ └── effect-cards.mjs │ ├── effect-coverflow/ │ │ ├── effect-coverflow.css │ │ └── effect-coverflow.mjs │ ├── effect-creative/ │ │ ├── effect-creative.css │ │ └── effect-creative.mjs │ ├── effect-cube/ │ │ ├── effect-cube.css │ │ └── effect-cube.mjs │ ├── effect-fade/ │ │ ├── effect-fade.css │ │ └── effect-fade.mjs │ ├── effect-flip/ │ │ ├── effect-flip.css │ │ └── effect-flip.mjs │ ├── free-mode/ │ │ ├── free-mode.css │ │ └── free-mode.mjs │ ├── grid/ │ │ ├── grid.css │ │ └── grid.mjs │ ├── hash-navigation/ │ │ ├── hash-navigation.css │ │ └── hash-navigation.mjs │ ├── history/ │ │ ├── history.css │ │ └── history.mjs │ ├── keyboard/ │ │ ├── keyboard.css │ │ └── keyboard.mjs │ ├── manipulation/ │ │ ├── manipulation.css │ │ ├── manipulation.mjs │ │ └── methods/ │ │ ├── addSlide.mjs │ │ ├── appendSlide.mjs │ │ ├── prependSlide.mjs │ │ ├── removeAllSlides.mjs │ │ └── removeSlide.mjs │ ├── mousewheel/ │ │ ├── mousewheel.css │ │ └── mousewheel.mjs │ ├── navigation/ │ │ ├── navigation.css │ │ └── navigation.mjs │ ├── pagination/ │ │ ├── pagination.css │ │ └── pagination.mjs │ ├── parallax/ │ │ ├── parallax.css │ │ └── parallax.mjs │ ├── scrollbar/ │ │ ├── scrollbar.css │ │ └── scrollbar.mjs │ ├── thumbs/ │ │ ├── thumbs.css │ │ └── thumbs.mjs │ ├── virtual/ │ │ ├── virtual.css │ │ └── virtual.mjs │ └── zoom/ │ ├── zoom.css │ └── zoom.mjs ├── react/ │ ├── context.mjs │ ├── get-children.mjs │ ├── swiper-slide.mjs │ ├── swiper.mjs │ ├── use-isomorphic-layout-effect.mjs │ └── virtual.mjs ├── shared/ │ ├── classes-to-selector.mjs │ ├── classes-to-tokens.mjs │ ├── create-element-if-not-defined.mjs │ ├── create-shadow.mjs │ ├── effect-init.mjs │ ├── effect-target.mjs │ ├── effect-virtual-transition-end.mjs │ ├── get-browser.mjs │ ├── get-device.mjs │ ├── get-support.mjs │ ├── process-lazy-preloader.mjs │ └── utils.mjs ├── swiper-bundle.mjs ├── swiper-effect-utils.d.ts ├── swiper-effect-utils.mjs ├── swiper-element.d.ts ├── swiper-element.mjs ├── swiper-react.d.ts ├── swiper-react.mjs ├── swiper-vars.less ├── swiper-vue.d.ts ├── swiper-vue.mjs ├── swiper.css ├── swiper.d.ts ├── swiper.mjs ├── types/ │ ├── index.d.ts │ ├── modules/ │ │ ├── a11y.d.ts │ │ ├── autoplay.d.ts │ │ ├── controller.d.ts │ │ ├── effect-cards.d.ts │ │ ├── effect-coverflow.d.ts │ │ ├── effect-creative.d.ts │ │ ├── effect-cube.d.ts │ │ ├── effect-fade.d.ts │ │ ├── effect-flip.d.ts │ │ ├── free-mode.d.ts │ │ ├── grid.d.ts │ │ ├── hash-navigation.d.ts │ │ ├── history.d.ts │ │ ├── index.d.ts │ │ ├── keyboard.d.ts │ │ ├── manipulation.d.ts │ │ ├── mousewheel.d.ts │ │ ├── navigation.d.ts │ │ ├── pagination.d.ts │ │ ├── parallax.d.ts │ │ ├── public-api.d.ts │ │ ├── scrollbar.d.ts │ │ ├── thumbs.d.ts │ │ ├── virtual.d.ts │ │ └── zoom.d.ts │ ├── shared.d.ts │ ├── swiper-class.d.ts │ ├── swiper-events.d.ts │ └── swiper-options.d.ts └── vue/ ├── context.mjs ├── get-children.mjs ├── swiper-slide.mjs ├── swiper.mjs └── virtual.mjs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .all-contributorsrc ================================================ { "projectName": "swiper", "projectOwner": "nolimits4web", "repoType": "github", "repoHost": "https://github.com", "files": [ "CODE_CONTRIBUTORS.md" ], "imageSize": 100, "commit": false, "contributors": [ { "login": "nolimits4web", "name": "Vladimir Kharlampidi", "avatar_url": "https://avatars.githubusercontent.com/u/999588?v=4", "profile": "https://github.com/nolimits4web", "contributions": [ "code", "maintenance", "infra", "question", "doc", "ideas" ] }, { "login": "vltansky", "name": "Vlad Tansky", "avatar_url": "https://avatars.githubusercontent.com/u/5851280?v=4", "profile": "https://github.com/vltansky", "contributions": [ "code" ] }, { "login": "DAnn2012", "name": "DAnn2012", "avatar_url": "https://avatars.githubusercontent.com/u/1197819?v=4", "profile": "https://github.com/DAnn2012", "contributions": [ "code" ] }, { "login": "stephanebachelier", "name": "Stéphane Bachelier", "avatar_url": "https://avatars.githubusercontent.com/u/172615?v=4", "profile": "https://github.com/stephanebachelier", "contributions": [ "code" ] }, { "login": "robpop", "name": "Robert F. Popeleski", "avatar_url": "https://avatars.githubusercontent.com/u/13895584?v=4", "profile": "https://github.com/robpop", "contributions": [ "code" ] }, { "login": "ygj6", "name": "yuangongji", "avatar_url": "https://avatars.githubusercontent.com/u/7699524?v=4", "profile": "https://github.com/ygj6", "contributions": [ "code" ] }, { "login": "imjohansunden", "name": "Johan Sundén", "avatar_url": "https://avatars.githubusercontent.com/u/19605741?v=4", "profile": "https://github.com/imjohansunden", "contributions": [ "code" ] }, { "login": "netcitylife", "name": "Dmitry", "avatar_url": "https://avatars.githubusercontent.com/u/2089893?v=4", "profile": "https://github.com/netcitylife", "contributions": [ "code" ] }, { "login": "andyburke", "name": "Andy Burke", "avatar_url": "https://avatars.githubusercontent.com/u/273857?v=4", "profile": "https://github.com/andyburke", "contributions": [ "code" ] }, { "login": "tzvikaf", "name": "Tzvika Farber", "avatar_url": "https://avatars.githubusercontent.com/u/13975372?v=4", "profile": "https://github.com/tzvikaf", "contributions": [ "code" ] }, { "login": "callumacrae", "name": "Callum Macrae", "avatar_url": "https://avatars.githubusercontent.com/u/472830?v=4", "profile": "http://macr.ae/", "contributions": [ "code" ] }, { "login": "jasonlav", "name": "jasonlav", "avatar_url": "https://avatars.githubusercontent.com/u/7593912?v=4", "profile": "https://github.com/jasonlav", "contributions": [ "code" ] }, { "login": "shashank1010", "name": "Shashank Agarwal", "avatar_url": "https://avatars.githubusercontent.com/u/3106368?v=4", "profile": "https://plus.google.com/u/0/114487431636655655146", "contributions": [ "code" ] }, { "login": "kochizufan", "name": "Kohei Otsuka", "avatar_url": "https://avatars.githubusercontent.com/u/2221431?v=4", "profile": "https://github.com/kochizufan", "contributions": [ "code" ] }, { "login": "Triloworld", "name": "Patryk Padus", "avatar_url": "https://avatars.githubusercontent.com/u/2671726?v=4", "profile": "https://www.webo.agency/", "contributions": [ "code" ] }, { "login": "icek", "name": "Grzegorz Michlicki", "avatar_url": "https://avatars.githubusercontent.com/u/150243?v=4", "profile": "http://fatcode.pl/", "contributions": [ "code" ] }, { "login": "justingrant", "name": "Justin Grant", "avatar_url": "https://avatars.githubusercontent.com/u/277214?v=4", "profile": "http://cantaloupesys.com/", "contributions": [ "code" ] }, { "login": "OrbintSoft", "name": "Stefano Balzarotti", "avatar_url": "https://avatars.githubusercontent.com/u/6378748?v=4", "profile": "https://github.com/OrbintSoft", "contributions": [ "code" ] }, { "login": "fenyagg", "name": "Dmitry Chernyshev", "avatar_url": "https://avatars.githubusercontent.com/u/2457209?v=4", "profile": "https://github.com/fenyagg", "contributions": [ "code" ] }, { "login": "ghaiat", "name": "Gilles Haiat", "avatar_url": "https://avatars.githubusercontent.com/u/5446909?v=4", "profile": "http://www.yoobic.com/", "contributions": [ "code" ] }, { "login": "weiz18", "name": "weiz18", "avatar_url": "https://avatars.githubusercontent.com/u/42139732?v=4", "profile": "https://github.com/weiz18", "contributions": [ "code" ] }, { "login": "kireerik", "name": "Erik Engi", "avatar_url": "https://avatars.githubusercontent.com/u/3628043?v=4", "profile": "https://oengi.com/", "contributions": [ "code" ] }, { "login": "GalCohen", "name": "Gal Cohen", "avatar_url": "https://avatars.githubusercontent.com/u/1293368?v=4", "profile": "https://github.com/GalCohen", "contributions": [ "code" ] }, { "login": "mrtnbroder", "name": "Martin Broder", "avatar_url": "https://avatars.githubusercontent.com/u/1118826?v=4", "profile": "https://martinbroder.com/", "contributions": [ "code" ] }, { "login": "Rikk", "name": "Ricardo", "avatar_url": "https://avatars.githubusercontent.com/u/2916485?v=4", "profile": "https://github.com/Rikk", "contributions": [ "code" ] }, { "login": "roman01la", "name": "Roman Liutikov", "avatar_url": "https://avatars.githubusercontent.com/u/1355501?v=4", "profile": "https://romanliutikov.com/", "contributions": [ "code" ] }, { "login": "baxang", "name": "Sanghyun Park", "avatar_url": "https://avatars.githubusercontent.com/u/196302?v=4", "profile": "http://baxang.com/", "contributions": [ "code" ] }, { "login": "girliemac", "name": "Tomomi ❤ Imura", "avatar_url": "https://avatars.githubusercontent.com/u/107763?v=4", "profile": "https://girliemac.com/", "contributions": [ "code" ] }, { "login": "jenemde", "name": "jenemde", "avatar_url": "https://avatars.githubusercontent.com/u/53625399?v=4", "profile": "https://github.com/jenemde", "contributions": [ "code", "a11y" ] }, { "login": "navyxie", "name": "navy", "avatar_url": "https://avatars.githubusercontent.com/u/1982716?v=4", "profile": "http://www.navyxie.com/", "contributions": [ "code" ] }, { "login": "1sergiogarciap", "name": "1sergiogarciap", "avatar_url": "https://avatars.githubusercontent.com/u/64509552?v=4", "profile": "https://github.com/1sergiogarciap", "contributions": [ "code" ] }, { "login": "azaslavsky", "name": "Alex Zaslavsky", "avatar_url": "https://avatars.githubusercontent.com/u/3709945?v=4", "profile": "https://github.com/azaslavsky", "contributions": [ "code" ] }, { "login": "andreybs11", "name": "Andrey Bolaños Sandoval", "avatar_url": "https://avatars.githubusercontent.com/u/1462803?v=4", "profile": "https://github.com/andreybs11", "contributions": [ "code" ] }, { "login": "exodusanto", "name": "Antonio Dal Sie", "avatar_url": "https://avatars.githubusercontent.com/u/13484843?v=4", "profile": "https://antoniodalsie.com/", "contributions": [ "code" ] }, { "login": "tremby", "name": "Bart Nagel", "avatar_url": "https://avatars.githubusercontent.com/u/199635?v=4", "profile": "https://bartnagel.ca/", "contributions": [ "code" ] }, { "login": "Garbanas", "name": "Christoph Dörfel", "avatar_url": "https://avatars.githubusercontent.com/u/2815411?v=4", "profile": "https://github.com/Garbanas", "contributions": [ "code" ] }, { "login": "DKvistgaard", "name": "Daniel Kvistgaard", "avatar_url": "https://avatars.githubusercontent.com/u/1705203?v=4", "profile": "https://github.com/DKvistgaard", "contributions": [ "code" ] }, { "login": "danijelGombac", "name": "Danijel Gombač", "avatar_url": "https://avatars.githubusercontent.com/u/3872726?v=4", "profile": "http://www.gombac.si/", "contributions": [ "code" ] }, { "login": "dy", "name": "Dmitry IV.", "avatar_url": "https://avatars.githubusercontent.com/u/300067?v=4", "profile": "https://twitter.com/dimayv", "contributions": [ "code" ] }, { "login": "eklingen", "name": "Elco Klingen", "avatar_url": "https://avatars.githubusercontent.com/u/716683?v=4", "profile": "https://elcoklingen.nl/", "contributions": [ "code" ] }, { "login": "OxyDesign", "name": "Nicolas Escoffier", "avatar_url": "https://avatars.githubusercontent.com/u/2378180?v=4", "profile": "http://www.oxydesign.fr/", "contributions": [ "code" ] }, { "login": "Evidica", "name": "Patrick Mallahan", "avatar_url": "https://avatars.githubusercontent.com/u/6105827?v=4", "profile": "https://github.com/Evidica", "contributions": [ "code" ] }, { "login": "zhiru", "name": "Felipe", "avatar_url": "https://avatars.githubusercontent.com/u/4226997?v=4", "profile": "https://aireset.com.br/", "contributions": [ "code" ] }, { "login": "garrettmaring", "name": "Garrett Maring", "avatar_url": "https://avatars.githubusercontent.com/u/8270120?v=4", "profile": "https://www.linkedin.com/in/garrettmaringdev", "contributions": [ "code" ] }, { "login": "joejoseph00", "name": "Joseph Olstad", "avatar_url": "https://avatars.githubusercontent.com/u/1028526?v=4", "profile": "https://github.com/joejoseph00", "contributions": [ "code" ] }, { "login": "kostyabushuev", "name": "Kostya Bushuev", "avatar_url": "https://avatars.githubusercontent.com/u/31274471?v=4", "profile": "https://github.com/kostyabushuev", "contributions": [ "code" ] }, { "login": "marwej", "name": "Marcus Wejderot", "avatar_url": "https://avatars.githubusercontent.com/u/5296840?v=4", "profile": "https://mevisio.com/", "contributions": [ "code" ] }, { "login": "cfxd", "name": "CFX", "avatar_url": "https://avatars.githubusercontent.com/u/1671933?v=4", "profile": "https://github.com/cfxd", "contributions": [ "code" ] }, { "login": "Mrliuchanghao", "name": "HoH-World", "avatar_url": "https://avatars.githubusercontent.com/u/30208835?v=4", "profile": "https://github.com/Mrliuchanghao", "contributions": [ "code" ] }, { "login": "websirnik", "name": "Nikita Korotaev", "avatar_url": "https://avatars.githubusercontent.com/u/282871?v=4", "profile": "https://relayto.com/", "contributions": [ "code" ] }, { "login": "katerlouis", "name": "René Eschke", "avatar_url": "https://avatars.githubusercontent.com/u/1983382?v=4", "profile": "https://github.com/katerlouis", "contributions": [ "code" ] }, { "login": "ryanzec", "name": "Ryan Zec", "avatar_url": "https://avatars.githubusercontent.com/u/444206?v=4", "profile": "http://www.ryanzec.com/", "contributions": [ "code" ] }, { "login": "SablinSergey", "name": "Sergey Sablin", "avatar_url": "https://avatars.githubusercontent.com/u/12068136?v=4", "profile": "https://github.com/SablinSergey", "contributions": [ "code" ] }, { "login": "tobiasstrebitzer", "name": "Tobias Strebitzer", "avatar_url": "https://avatars.githubusercontent.com/u/222509?v=4", "profile": "http://www.magloft.com/", "contributions": [ "code" ] }, { "login": "X4arms", "name": "X4arms", "avatar_url": "https://avatars.githubusercontent.com/u/1517616?v=4", "profile": "https://github.com/X4arms", "contributions": [ "code" ] }, { "login": "a-barbieri", "name": "Alessandro", "avatar_url": "https://avatars.githubusercontent.com/u/1528468?v=4", "profile": "http://lacolonia.studio/", "contributions": [ "code" ] }, { "login": "amnausman", "name": "amnausman", "avatar_url": "https://avatars.githubusercontent.com/u/20682461?v=4", "profile": "https://github.com/amnausman", "contributions": [ "code" ] }, { "login": "crutch12", "name": "Konstantin Barabanov", "avatar_url": "https://avatars.githubusercontent.com/u/19373212?v=4", "profile": "https://github.com/crutch12", "contributions": [ "code" ] }, { "login": "michaelWoe", "name": "michaelWoe", "avatar_url": "https://avatars.githubusercontent.com/u/49037485?v=4", "profile": "https://github.com/michaelWoe", "contributions": [ "code" ] }, { "login": "timothyhague", "name": "timothyhague", "avatar_url": "https://avatars.githubusercontent.com/u/9559835?v=4", "profile": "https://github.com/timothyhague", "contributions": [ "code" ] }, { "login": "hsxfjames", "name": "Yaojun Zheng", "avatar_url": "https://avatars.githubusercontent.com/u/8982188?v=4", "profile": "http://www.cnblogs.com/blackstorm", "contributions": [ "code" ] }, { "login": "raahede", "name": "Frej Raahede Nielsen", "avatar_url": "https://avatars.githubusercontent.com/u/391833?v=4", "profile": "https://github.com/raahede", "contributions": [ "code" ] }, { "login": "mzkmzk", "name": "404_K", "avatar_url": "https://avatars.githubusercontent.com/u/4627170?v=4", "profile": "https://github.com/mzkmzk", "contributions": [ "code" ] }, { "login": "voxtex", "name": "Adam", "avatar_url": "https://avatars.githubusercontent.com/u/735455?v=4", "profile": "https://github.com/voxtex", "contributions": [ "code" ] }, { "login": "promoinvideo", "name": "Alberto Cobre", "avatar_url": "https://avatars.githubusercontent.com/u/10633232?v=4", "profile": "https://github.com/promoinvideo", "contributions": [ "code" ] }, { "login": "alejandroiglesias", "name": "Alejandro García Iglesias", "avatar_url": "https://avatars.githubusercontent.com/u/480640?v=4", "profile": "https://alejandroiglesias.github.io/cv/", "contributions": [ "code" ] }, { "login": "anneke", "name": "Anneke Sinnema", "avatar_url": "https://avatars.githubusercontent.com/u/7202272?v=4", "profile": "https://annekesinnema.nl/", "contributions": [ "code" ] }, { "login": "kosminen", "name": "Anssi Hautamäki", "avatar_url": "https://avatars.githubusercontent.com/u/1532140?v=4", "profile": "https://github.com/kosminen", "contributions": [ "code" ] }, { "login": "anteprimorac", "name": "Ante Primorac", "avatar_url": "https://avatars.githubusercontent.com/u/972083?v=4", "profile": "http://anteprimorac.com.hr/", "contributions": [ "code" ] }, { "login": "hanoii", "name": "Ariel Barreiro", "avatar_url": "https://avatars.githubusercontent.com/u/677879?v=4", "profile": "https://github.com/hanoii", "contributions": [ "code" ] }, { "login": "arthurpf", "name": "Arthur Franco", "avatar_url": "https://avatars.githubusercontent.com/u/2388720?v=4", "profile": "https://arthurpf.github.io/", "contributions": [ "code" ] }, { "login": "austinknight", "name": "Austin Knight", "avatar_url": "https://avatars.githubusercontent.com/u/1007248?v=4", "profile": "http://www.austinknight.net/", "contributions": [ "code" ] }, { "login": "justb81", "name": "Bastian Rang", "avatar_url": "https://avatars.githubusercontent.com/u/3680539?v=4", "profile": "https://github.com/justb81", "contributions": [ "code" ] }, { "login": "bitdeli-chef", "name": "Bitdeli Chef", "avatar_url": "https://avatars.githubusercontent.com/u/3092978?v=4", "profile": "https://bitdeli.com/free", "contributions": [ "code" ] }, { "login": "Brightcells", "name": "Kimi.Huang", "avatar_url": "https://avatars.githubusercontent.com/u/5810063?v=4", "profile": "https://pypi.org/user/Hackathon/", "contributions": [ "code" ] }, { "login": "brunolm", "name": "BrunoLM", "avatar_url": "https://avatars.githubusercontent.com/u/706078?v=4", "profile": "https://brunolm.com/blog", "contributions": [ "code" ] }, { "login": "Chepheus", "name": "Cepheus", "avatar_url": "https://avatars.githubusercontent.com/u/9037206?v=4", "profile": "https://github.com/Chepheus", "contributions": [ "code" ] }, { "login": "Chun-Yang", "name": "yang2007chun", "avatar_url": "https://avatars.githubusercontent.com/u/2827867?v=4", "profile": "https://trentyang.com/", "contributions": [ "code" ] }, { "login": "DanielRuf", "name": "Daniel Ruf", "avatar_url": "https://avatars.githubusercontent.com/u/827205?v=4", "profile": "https://daniel-ruf.de/", "contributions": [ "code" ] }, { "login": "dfvalero", "name": "David Fernandez", "avatar_url": "https://avatars.githubusercontent.com/u/337955?v=4", "profile": "https://github.com/dfvalero", "contributions": [ "code" ] }, { "login": "davgothic", "name": "David Hancock", "avatar_url": "https://avatars.githubusercontent.com/u/158586?v=4", "profile": "http://davidhancock.co/", "contributions": [ "code" ] }, { "login": "cartok", "name": "cartok", "avatar_url": "https://avatars.githubusercontent.com/u/22181589?v=4", "profile": "https://github.com/cartok", "contributions": [ "code" ] }, { "login": "izifortune", "name": "Fabrizio Fortunato", "avatar_url": "https://avatars.githubusercontent.com/u/3394606?v=4", "profile": "https://izifortune.com/", "contributions": [ "code" ] }, { "login": "FishErr", "name": "FishErr", "avatar_url": "https://avatars.githubusercontent.com/u/742467?v=4", "profile": "https://github.com/FishErr", "contributions": [ "code" ] }, { "login": "fritzmg", "name": "Fritz Michael Gschwantner", "avatar_url": "https://avatars.githubusercontent.com/u/4970961?v=4", "profile": "https://github.com/fritzmg", "contributions": [ "code" ] }, { "login": "glebmachine", "name": "Gleb Mikheev", "avatar_url": "https://avatars.githubusercontent.com/u/1610882?v=4", "profile": "http://betaagency.ru/", "contributions": [ "code" ] }, { "login": "TatumCreative", "name": "TatumCreative", "avatar_url": "https://avatars.githubusercontent.com/u/22155328?v=4", "profile": "https://github.com/gregtatum", "contributions": [ "code" ] }, { "login": "hanjukim", "name": "Paul Kim", "avatar_url": "https://avatars.githubusercontent.com/u/1264116?v=4", "profile": "https://github.com/hanjukim", "contributions": [ "code" ] }, { "login": "HeadFox", "name": "Lucien PESLIER", "avatar_url": "https://avatars.githubusercontent.com/u/6277284?v=4", "profile": "https://github.com/HeadFox", "contributions": [ "code" ] }, { "login": "HerringtonDarkholme", "name": "Herrington Darkholme", "avatar_url": "https://avatars.githubusercontent.com/u/2883231?v=4", "profile": "https://github.com/HerringtonDarkholme", "contributions": [ "code" ] }, { "login": "IronKinoko", "name": "IronKinoko", "avatar_url": "https://avatars.githubusercontent.com/u/32838658?v=4", "profile": "https://github.com/IronKinoko", "contributions": [ "code" ] }, { "login": "stonebranch", "name": "Jakob Stenqvist", "avatar_url": "https://avatars.githubusercontent.com/u/87466?v=4", "profile": "https://jakobstenqvist.com/", "contributions": [ "code" ] }, { "login": "sconix", "name": "Janne Julkunen", "avatar_url": "https://avatars.githubusercontent.com/u/921515?v=4", "profile": "https://github.com/sconix", "contributions": [ "code" ] }, { "login": "jasonkuhrt", "name": "Jason Kuhrt", "avatar_url": "https://avatars.githubusercontent.com/u/284476?v=4", "profile": "https://github.com/jasonkuhrt", "contributions": [ "code" ] }, { "login": "Manduro", "name": "Job", "avatar_url": "https://avatars.githubusercontent.com/u/2545042?v=4", "profile": "https://github.com/Manduro", "contributions": [ "code" ] }, { "login": "johnferrie", "name": "John Ferrie", "avatar_url": "https://avatars.githubusercontent.com/u/1991460?v=4", "profile": "https://github.com/johnferrie", "contributions": [ "code" ] }, { "login": "sakuraineed", "name": "Kenji Sakurai", "avatar_url": "https://avatars.githubusercontent.com/u/4065624?v=4", "profile": "https://github.com/sakuraineed", "contributions": [ "code" ] }, { "login": "careykevin", "name": "Kevin Carey", "avatar_url": "https://avatars.githubusercontent.com/u/9685146?v=4", "profile": "http://www.linkedin.com/in/careykevin", "contributions": [ "code" ] }, { "login": "klojniewski-pagepro", "name": "Chris (Krzysztof) Lojniewski", "avatar_url": "https://avatars.githubusercontent.com/u/245971?v=4", "profile": "http://pagepro.pl/", "contributions": [ "code" ] }, { "login": "ljanecek", "name": "Ladislav Janeček", "avatar_url": "https://avatars.githubusercontent.com/u/2696545?v=4", "profile": "https://twitter.com/ladislavjanecek", "contributions": [ "code" ] }, { "login": "luads", "name": "Luã de Souza", "avatar_url": "https://avatars.githubusercontent.com/u/194708?v=4", "profile": "http://www.lsouza.pro.br/", "contributions": [ "code" ] }, { "login": "luis-kaufmann-silva", "name": "Luis", "avatar_url": "https://avatars.githubusercontent.com/u/1151616?v=4", "profile": "https://wiserdev.com/", "contributions": [ "code" ] }, { "login": "blikblum", "name": "Luiz Américo", "avatar_url": "https://avatars.githubusercontent.com/u/3047126?v=4", "profile": "https://github.com/blikblum", "contributions": [ "code" ] }, { "login": "LukasDrgon", "name": "Lukas Drgon", "avatar_url": "https://avatars.githubusercontent.com/u/15611832?v=4", "profile": "https://github.com/LukasDrgon", "contributions": [ "code" ] }, { "login": "waffle-iron", "name": "Making GitHub Delicious.", "avatar_url": "https://avatars.githubusercontent.com/u/6912981?v=4", "profile": "https://github.com/waffle-iron", "contributions": [ "code" ] }, { "login": "silvenon", "name": "Matija Marohnić", "avatar_url": "https://avatars.githubusercontent.com/u/471278?v=4", "profile": "https://silvenon.com/", "contributions": [ "code" ] }, { "login": "damrbaby", "name": "Matthew Windwer", "avatar_url": "https://avatars.githubusercontent.com/u/237580?v=4", "profile": "https://github.com/damrbaby", "contributions": [ "code" ] }, { "login": "mehernosh", "name": "Mehernosh Bhathena", "avatar_url": "https://avatars.githubusercontent.com/u/1410232?v=4", "profile": "https://github.com/mehernosh", "contributions": [ "code" ] }, { "login": "mems", "name": "Memmie Lenglet", "avatar_url": "https://avatars.githubusercontent.com/u/729275?v=4", "profile": "https://memmie.lenglet.name/", "contributions": [ "code" ] }, { "login": "mibo-fdc", "name": "Michael Bohn", "avatar_url": "https://avatars.githubusercontent.com/u/70259891?v=4", "profile": "https://github.com/mibo-fdc", "contributions": [ "code" ] }, { "login": "wodka", "name": "Michael Schramm", "avatar_url": "https://avatars.githubusercontent.com/u/385731?v=4", "profile": "https://github.com/wodka", "contributions": [ "code" ] }, { "login": "mhartington", "name": "Mike Hartington", "avatar_url": "https://avatars.githubusercontent.com/u/2835826?v=4", "profile": "https://twitter.com/mhartington", "contributions": [ "code" ] }, { "login": "mitchheddles", "name": "Mitchell Heddles", "avatar_url": "https://avatars.githubusercontent.com/u/20656128?v=4", "profile": "https://mitch.app/", "contributions": [ "code" ] }, { "login": "NLNicoo", "name": "Nico", "avatar_url": "https://avatars.githubusercontent.com/u/6526666?v=4", "profile": "https://github.com/NLNicoo", "contributions": [ "code" ] }, { "login": "nil-ni-ck", "name": "Nil", "avatar_url": "https://avatars.githubusercontent.com/u/8202448?v=4", "profile": "https://github.com/nil-ni-ck", "contributions": [ "code" ] }, { "login": "pawl", "name": "Paul Brown", "avatar_url": "https://avatars.githubusercontent.com/u/992533?v=4", "profile": "http://paulsprogrammingnotes.com/", "contributions": [ "code" ] }, { "login": "pbalmasov", "name": "Pavel Balmasov", "avatar_url": "https://avatars.githubusercontent.com/u/3986749?v=4", "profile": "https://github.com/pbalmasov", "contributions": [ "code" ] }, { "login": "czajkowski", "name": "Piotr Czajkowski", "avatar_url": "https://avatars.githubusercontent.com/u/197684?v=4", "profile": "https://github.com/czajkowski", "contributions": [ "code" ] }, { "login": "PythonLinks", "name": "PythonLinks", "avatar_url": "https://avatars.githubusercontent.com/u/34622952?v=4", "profile": "https://github.com/PythonLinks", "contributions": [ "code" ] }, { "login": "rayvincent-bsd", "name": "rayvincent-bsd", "avatar_url": "https://avatars.githubusercontent.com/u/41829998?v=4", "profile": "https://github.com/rayvincent-bsd", "contributions": [ "code" ] }, { "login": "RomanYazvinsky", "name": "RomanYazvinsky", "avatar_url": "https://avatars.githubusercontent.com/u/32144682?v=4", "profile": "https://github.com/RomanYazvinsky", "contributions": [ "code" ] }, { "login": "rrelmy", "name": "Rémy", "avatar_url": "https://avatars.githubusercontent.com/u/442683?v=4", "profile": "https://remyboehler.ch/", "contributions": [ "code" ] }, { "login": "KSH-code", "name": "Seonghoon Kim", "avatar_url": "https://avatars.githubusercontent.com/u/29705162?v=4", "profile": "http://ksh-code.github.io/", "contributions": [ "code" ] }, { "login": "isairz", "name": "Seongjun Kim", "avatar_url": "https://avatars.githubusercontent.com/u/4544327?v=4", "profile": "https://github.com/isairz", "contributions": [ "code" ] }, { "login": "dippas", "name": "Serpa", "avatar_url": "https://avatars.githubusercontent.com/u/10220287?v=4", "profile": "http://stackoverflow.com/users/3448527/dippas", "contributions": [ "code" ] }, { "login": "staszek998", "name": "Stanisław Gregor", "avatar_url": "https://avatars.githubusercontent.com/u/34459296?v=4", "profile": "http://staszek.codes/", "contributions": [ "code" ] }, { "login": "steve3d", "name": "Steve Yin", "avatar_url": "https://avatars.githubusercontent.com/u/670687?v=4", "profile": "https://github.com/steve3d", "contributions": [ "code" ] }, { "login": "seastland", "name": "Steven Eastland", "avatar_url": "https://avatars.githubusercontent.com/u/3535053?v=4", "profile": "http://www.grassmonk.net/", "contributions": [ "code" ] }, { "login": "soenkekluth", "name": "Sönke Kluth", "avatar_url": "https://avatars.githubusercontent.com/u/201338?v=4", "profile": "https://soenkekluth.com/", "contributions": [ "code" ] }, { "login": "gitter-badger", "name": "The Gitter Badger", "avatar_url": "https://avatars.githubusercontent.com/u/8518239?v=4", "profile": "https://gitter.im/", "contributions": [ "code" ] }, { "login": "dogoku", "name": "Theo", "avatar_url": "https://avatars.githubusercontent.com/u/761999?v=4", "profile": "https://github.com/dogoku", "contributions": [ "code" ] }, { "login": "timgates42", "name": "Tim Gates", "avatar_url": "https://avatars.githubusercontent.com/u/47873678?v=4", "profile": "https://github.com/timgates42", "contributions": [ "code" ] }, { "login": "tom-sherman", "name": "Tom Sherman", "avatar_url": "https://avatars.githubusercontent.com/u/9257001?v=4", "profile": "https://tom-sherman.com/", "contributions": [ "code" ] }, { "login": "WarriorRocker", "name": "WarriorRocker", "avatar_url": "https://avatars.githubusercontent.com/u/2924540?v=4", "profile": "https://github.com/WarriorRocker", "contributions": [ "code" ] }, { "login": "omgovich", "name": "Vlad Shilov", "avatar_url": "https://avatars.githubusercontent.com/u/206567?v=4", "profile": "https://github.com/omgovich", "contributions": [ "code" ] }, { "login": "vladgurovich", "name": "Vladimir Gurovich", "avatar_url": "https://avatars.githubusercontent.com/u/444833?v=4", "profile": "http://vlad.io/", "contributions": [ "code" ] }, { "login": "VladimirIvanin", "name": "Vladimir Ivanin", "avatar_url": "https://avatars.githubusercontent.com/u/15268753?v=4", "profile": "https://github.com/VladimirIvanin", "contributions": [ "code" ] }, { "login": "TreVld", "name": "TreVld", "avatar_url": "https://avatars.githubusercontent.com/u/42523020?v=4", "profile": "https://github.com/TreVld", "contributions": [ "code" ] }, { "login": "waghcwb", "name": "Wagner Souza", "avatar_url": "https://avatars.githubusercontent.com/u/6169950?v=4", "profile": "https://www.linkedin.com/in/waghcwb", "contributions": [ "code" ] }, { "login": "codesignist", "name": "Yunus EŞ", "avatar_url": "https://avatars.githubusercontent.com/u/1472881?v=4", "profile": "http://yunuses.com/", "contributions": [ "code" ] }, { "login": "axten", "name": "axten", "avatar_url": "https://avatars.githubusercontent.com/u/1412778?v=4", "profile": "https://github.com/axten", "contributions": [ "code" ] }, { "login": "ccebrand", "name": "Cyrille", "avatar_url": "https://avatars.githubusercontent.com/u/818237?v=4", "profile": "http://www.moustic.biz/", "contributions": [ "code" ] }, { "login": "daleknauss", "name": "Dale Knauss", "avatar_url": "https://avatars.githubusercontent.com/u/2430232?v=4", "profile": "http://daleknauss.com/", "contributions": [ "code" ] }, { "login": "danielkalen", "name": "Daniel Kalen", "avatar_url": "https://avatars.githubusercontent.com/u/10832620?v=4", "profile": "https://github.com/danielkalen", "contributions": [ "code" ] }, { "login": "dependabot[bot]", "name": "dependabot[bot]", "avatar_url": "https://avatars.githubusercontent.com/in/29110?v=4", "profile": "https://github.com/apps/dependabot", "contributions": [ "code" ] }, { "login": "devmi", "name": "devmi", "avatar_url": "https://avatars.githubusercontent.com/u/19313261?v=4", "profile": "https://github.com/devmi", "contributions": [ "code" ] }, { "login": "fabrizim", "name": "fabrizim", "avatar_url": "https://avatars.githubusercontent.com/u/79165?v=4", "profile": "https://owlwatch.com/", "contributions": [ "code" ] }, { "login": "hiroqn", "name": "hiroqn", "avatar_url": "https://avatars.githubusercontent.com/u/909385?v=4", "profile": "https://github.com/hiroqn", "contributions": [ "code" ] }, { "login": "jamesryanbell", "name": "James Bell", "avatar_url": "https://avatars.githubusercontent.com/u/1842481?v=4", "profile": "https://james-bell.co.uk/", "contributions": [ "code" ] }, { "login": "knusperpixel", "name": "knusperpixel", "avatar_url": "https://avatars.githubusercontent.com/u/1455404?v=4", "profile": "https://github.com/knusperpixel", "contributions": [ "code" ] }, { "login": "langjun", "name": "langjun", "avatar_url": "https://avatars.githubusercontent.com/u/7370806?v=4", "profile": "https://langjun.github.io/", "contributions": [ "code" ] }, { "login": "lenny0702", "name": "Lenny", "avatar_url": "https://avatars.githubusercontent.com/u/2850927?v=4", "profile": "https://github.com/lenny0702", "contributions": [ "code" ] }, { "login": "mattdemps", "name": "Matt", "avatar_url": "https://avatars.githubusercontent.com/u/2694295?v=4", "profile": "https://github.com/mattdemps", "contributions": [ "code" ] }, { "login": "n3gotium", "name": "n3gotium", "avatar_url": "https://avatars.githubusercontent.com/u/3309521?v=4", "profile": "https://github.com/n3gotium", "contributions": [ "code" ] }, { "login": "naedx", "name": "naedx", "avatar_url": "https://avatars.githubusercontent.com/u/1711099?v=4", "profile": "https://github.com/naedx", "contributions": [ "code" ] }, { "login": "nealnote", "name": "nealnote", "avatar_url": "https://avatars.githubusercontent.com/u/3173785?v=4", "profile": "https://github.com/nealnote", "contributions": [ "code" ] }, { "login": "osamaalsabbagh", "name": "osamaalsabbagh", "avatar_url": "https://avatars.githubusercontent.com/u/2883742?v=4", "profile": "https://github.com/osamaalsabbagh", "contributions": [ "code" ] }, { "login": "pantcaser", "name": "pantcaser", "avatar_url": "https://avatars.githubusercontent.com/u/33579079?v=4", "profile": "https://github.com/pantcaser", "contributions": [ "code" ] }, { "login": "ph1p", "name": "Phil", "avatar_url": "https://avatars.githubusercontent.com/u/15351728?v=4", "profile": "https://ph1p.dev/", "contributions": [ "code" ] }, { "login": "pimlie", "name": "Pim", "avatar_url": "https://avatars.githubusercontent.com/u/1067403?v=4", "profile": "https://github.com/pimlie", "contributions": [ "code" ] }, { "login": "andrey-glotov", "name": "Andrey A. Glotov", "avatar_url": "https://avatars.githubusercontent.com/u/1656509?v=4", "profile": "https://github.com/andrey-glotov", "contributions": [ "code" ] }, { "login": "rdsn", "name": "rdsn", "avatar_url": "https://avatars.githubusercontent.com/u/3916181?v=4", "profile": "https://github.com/rdsn", "contributions": [ "code" ] }, { "login": "shahjehanali1", "name": "Shahjehan Ali", "avatar_url": "https://avatars.githubusercontent.com/u/8455072?v=4", "profile": "https://github.com/shahjehanali1", "contributions": [ "code" ] }, { "login": "JoveX", "name": "清泉古雾", "avatar_url": "https://avatars.githubusercontent.com/u/1560179?v=4", "profile": "http://jser.io/", "contributions": [ "code" ] }, { "login": "kashtian", "name": "kashtian", "avatar_url": "https://avatars.githubusercontent.com/u/17918091?v=4", "profile": "https://github.com/kashtian", "contributions": [ "code" ] }, { "login": "tienbuide", "name": "tienbuide", "avatar_url": "https://avatars.githubusercontent.com/u/2574700?v=4", "profile": "https://github.com/tienbuide", "contributions": [ "code" ] }, { "login": "unicod3", "name": "Sinan Ülker", "avatar_url": "https://avatars.githubusercontent.com/u/2614110?v=4", "profile": "https://github.com/unicod3", "contributions": [ "code" ] }, { "login": "vaxul", "name": "vaxul", "avatar_url": "https://avatars.githubusercontent.com/u/3798226?v=4", "profile": "https://github.com/vaxul", "contributions": [ "code" ] }, { "login": "wolffan", "name": "Raimon Lapuente", "avatar_url": "https://avatars.githubusercontent.com/u/1092080?v=4", "profile": "http://raimonlapuente.com/", "contributions": [ "code" ] }, { "login": "yukulele", "name": "Clément P", "avatar_url": "https://avatars.githubusercontent.com/u/347244?v=4", "profile": "https://github.com/yukulele", "contributions": [ "code" ] }, { "login": "aPalenov", "name": "Андрей", "avatar_url": "https://avatars.githubusercontent.com/u/20544542?v=4", "profile": "https://github.com/aPalenov", "contributions": [ "code" ] }, { "login": "beomy", "name": "이효범", "avatar_url": "https://avatars.githubusercontent.com/u/6986479?v=4", "profile": "https://github.com/beomy", "contributions": [ "code" ] }, { "login": "philwolstenholme", "name": "Phil Wolstenholme", "avatar_url": "https://avatars.githubusercontent.com/u/6339853?v=4", "profile": "https://wolstenhol.me/", "contributions": [ "a11y" ] }, { "login": "shahjehan-wpbrigade", "name": "Shahjehan Ali", "avatar_url": "https://avatars.githubusercontent.com/u/65647117?v=4", "profile": "https://github.com/shahjehan-wpbrigade", "contributions": [ "doc" ] } ], "contributorsPerLine": 7, "commitConvention": "none", "skipCi": true } ================================================ FILE: .browserslistrc ================================================ # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. # For additional information regarding the format and rule options, please see: # https://github.com/browserslist/browserslist#queries # You can see what browsers were selected by your queries by running: # npx browserslist IOS >= 17 Safari >= 17 last 5 Chrome versions last 5 Firefox versions ================================================ FILE: .editorconfig ================================================ root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true ================================================ FILE: .eslintignore ================================================ build playground/index.html node_modules dist src/swiper.js src/swiper-bundle.js ================================================ FILE: .eslintrc.cjs ================================================ const rules = { 'no-nested-ternary': 'off', 'no-lonely-if': 'off', 'no-param-reassign': 'off', 'no-underscore-dangle': 'off', 'prefer-object-spread': 'off', 'prefer-destructuring': 'off', 'default-param-last': 'off', 'import/prefer-default-export': 'off', 'guard-for-in': 'off', 'no-restricted-syntax': 'off', 'import/no-extraneous-dependencies': 'off', 'no-console': 'off', 'no-restricted-globals': 'off', 'max-classes-per-file': 'off', 'react/jsx-filename-extension': 'off', 'jsx-a11y/label-has-associated-control': 'off', 'react/no-array-index-key': 'off', 'react/prop-types': 'off', 'react/jsx-props-no-spreading': 'off', 'import/no-unresolved': [2, { ignore: ['ssr-window', 'dom7', 'swiper'] }], }; module.exports = { root: true, env: { browser: true, es6: true, node: true, }, globals: { Atomics: 'readonly', SharedArrayBuffer: 'readonly', }, parserOptions: { ecmaFeatures: { jsx: true, }, ecmaVersion: 2021, sourceType: 'module', }, overrides: [ { files: ['*.js'], extends: ['plugin:react/recommended', 'airbnb-base', 'plugin:prettier/recommended'], rules, }, { files: ['**/*.jsx', 'src/react/*.js'], plugins: ['react'], rules, }, { files: ['src/**/*.*'], rules: { ...rules, 'import/extensions': [2, 'ignorePackages', { js: 'always' }], }, }, { files: ['scripts/**/*.*'], parserOptions: { ecmaVersion: 2020, sourceType: 'module', }, rules: { ...rules, 'import/extensions': [2, 'ignorePackages', { js: 'always' }], }, }, ], }; ================================================ FILE: .gitattributes ================================================ * text=LF ================================================ FILE: .github/CONTRIBUTING.md ================================================ # How to contribute Swiper loves to welcome your contributions. There are several ways to help out: - Create an [issue](https://github.com/nolimits4web/swiper/issues) on GitHub, if you have found a bug - Write test cases or provide examples for open bug issues - Write patches for open bug/feature issues There are a few guidelines that we need contributors to follow so that we have a chance of keeping on top of things. ## Getting Started - Make sure you have a [GitHub account](https://github.com/signup/free). - Submit an [issue](https://github.com/nolimits4web/swiper/issues), assuming one does not already exist. - Clearly describe the issue including steps to reproduce when it is a bug. - Make sure you fill in the earliest version that you know has the issue. - Fork the repository on GitHub. ## Making Changes - Create a topic branch from where you want to base your work. - This is usually the master branch. - Only target release branches if you are certain your fix must be on that branch. - To quickly create a topic branch based on master; `git branch master/my_contribution master` then checkout the new branch with `git checkout master/my_contribution`. Better avoid working directly on the `master` branch, to avoid conflicts if you pull in updates from origin. - Make commits of logical units. - Check for unnecessary whitespace with `git diff --check` before committing. - Use descriptive commit messages and reference the #issue number. ## Submitting Changes - Push your changes to a topic branch in your fork of the repository. - Submit a pull request to the repository ## Editor Config The project uses .editorconfig to define the coding style of each file. We recommend that you install the Editor Config extension for your preferred IDE. ================================================ FILE: .github/FUNDING.yml ================================================ patreon: swiperjs open_collective: swiper github: nolimits4web ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ contact_links: - name: 🙏 Open Collective url: https://opencollective.com/swiper/ about: Love Swiper? Please consider supporting us via Open Collective. - name: 📃 Documentation Issue url: https://github.com/nolimits4web/swiper-website/issues/new about: Issues with Swiper website ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request.yml ================================================ name: "\U0001F680 New feature proposal" description: Propose a new feature to be added to Swiper labels: ['feature request'] body: - type: markdown attributes: value: | Thanks for your interest in the project and taking the time to fill out this feature report! - type: textarea id: feature-description attributes: label: Clear and concise description of the problem description: As a developer using Swiper I want [goal / wish] so that [benefit] validations: required: true - type: textarea id: suggested-solution attributes: label: Suggested solution description: 'In module [xy] we could provide following implementation...' validations: required: true - type: textarea id: alternative attributes: label: Alternative description: Clear and concise description of any alternative solutions or features you've considered. - type: textarea id: additional-context attributes: label: Additional context description: Any other context or screenshots about the feature request here. - type: checkboxes id: checkboxes attributes: label: Validations description: Before submitting the issue, please make sure you do the following options: - label: Follow our [Code of Conduct](https://github.com/nolimits4web/swiper/blob/master/CODE_OF_CONDUCT.md) required: true - label: Read the [docs](https://swiperjs.com/swiper-api). required: true - label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate. required: true - type: checkboxes id: pr attributes: label: Would you like to open a PR for this feature? description: Before starting to work on PR it is recommended to get maintainers approval. options: - label: I'm willing to open a PR required: false ================================================ FILE: .github/ISSUE_TEMPLATE/swiper_core_issue.yml ================================================ name: '🐞 Swiper Core Issue' description: Report an issue with Swiper labels: [] body: - type: markdown attributes: value: | Thanks for taking the time to fill out this bug report! - type: checkboxes id: qa attributes: label: Check that this is really a bug description: For Q&A open a [GitHub Discussion](https://github.com/nolimits4web/swiper/discussions) options: - label: I confirm required: true - type: input id: reproduction attributes: label: Reproduction link description: Please provide a link to a repo that can reproduce the problem you ran into. You can fork one of our [demos](https://swiperjs.com/demos) in codesandbox to get start. A reproduction is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "missing demo" label. If no reproduction is provided after 3 days, it will be auto-closed. placeholder: 'https://codesandbox.io/..' validations: required: true - type: textarea id: descr attributes: label: Bug description description: A clear and concise description of what the bug is validations: required: true - type: textarea id: expected attributes: label: Expected Behavior description: A concise description of what you expected to happen validations: required: false - type: textarea id: actual attributes: label: Actual Behavior description: A concise description of what you're experiencing validations: required: false - type: input id: swiper attributes: label: Swiper version description: Exact release version or commit hash placeholder: e.g 7.0.0 validations: required: true - type: input id: browser attributes: label: Platform/Target and Browser Versions description: Platform client you are targeting such as macOS, Windows, Cordova, iOS, Android, Chrome, etc. placeholder: e.g macOS Safari 14.1 validations: required: true - type: checkboxes id: checkboxes attributes: label: Validations description: Before submitting the issue, please make sure you do the following options: - label: Follow our [Code of Conduct](https://github.com/nolimits4web/swiper/blob/master/CODE_OF_CONDUCT.md) required: true - label: Read the [docs](https://swiperjs.com/swiper-api). required: true - label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate. required: true - label: Make sure this is a Swiper issue and not a framework-specific issue required: true - type: checkboxes id: pr attributes: label: Would you like to open a PR for this bug? description: Before starting to work on PR it is recommended to get maintainers approval options: - label: I'm willing to open a PR required: false ================================================ FILE: .github/ISSUE_TEMPLATE/swiper_element_issue.yml ================================================ name: '🐞 Swiper Element Issue' description: Report an issue with Swiper Element labels: ['Element'] body: - type: markdown attributes: value: | Thanks for taking the time to fill out this bug report! - type: checkboxes id: qa attributes: label: Check that this is really a bug description: For Q&A open a [GitHub Discussion](https://github.com/nolimits4web/swiper/discussions) options: - label: I confirm required: true - type: input id: reproduction attributes: label: Reproduction link description: Please provide a link to a repo that can reproduce the problem you ran into. You can fork one of our [demos](https://swiperjs.com/demos) in codesandbox to get start. A reproduction is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "missing demo" label. If no reproduction is provided after 3 days, it will be auto-closed. placeholder: 'https://codesandbox.io/..' validations: required: true - type: textarea id: descr attributes: label: Bug description description: A clear and concise description of what the bug is validations: required: true - type: textarea id: expected attributes: label: Expected Behavior description: A concise description of what you expected to happen validations: required: false - type: textarea id: actual attributes: label: Actual Behavior description: A concise description of what you're experiencing validations: required: false - type: input id: swiper attributes: label: Swiper version description: Exact release version or commit hash placeholder: e.g 7.0.0 validations: required: true - type: input id: browser attributes: label: Platform/Target and Browser Versions description: Platform client you are targeting such as macOS, Windows, Cordova, iOS, Android, Chrome, etc. placeholder: e.g macOS Safari 14.1 validations: required: true - type: checkboxes id: checkboxes attributes: label: Validations description: Before submitting the issue, please make sure you do the following options: - label: Follow our [Code of Conduct](https://github.com/nolimits4web/swiper/blob/master/CODE_OF_CONDUCT.md) required: true - label: Read the [docs](https://swiperjs.com/swiper-api). required: true - label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate. required: true - label: Make sure this is a Swiper issue and not a framework-specific issue required: true - type: checkboxes id: pr attributes: label: Would you like to open a PR for this bug? description: Before starting to work on PR it is recommended to get maintainers approval options: - label: I'm willing to open a PR required: false ================================================ FILE: .github/ISSUE_TEMPLATE/swiper_react_issue.yml ================================================ name: '🐞 Swiper React Issue' description: Create a report for Swiper React components labels: 'React' body: - type: markdown attributes: value: | Thanks for taking the time to fill out this bug report! - type: checkboxes id: qa attributes: label: Check that this is really a bug description: For Q&A open a [GitHub Discussion](https://github.com/nolimits4web/swiper/discussions) options: - label: I confirm required: true - type: input id: reproduction attributes: label: Reproduction link description: Please provide a link to a repo that can reproduce the problem you ran into. You can fork one of our [demos](https://swiperjs.com/demos) in codesandbox to get start. A reproduction is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "missing demo" label. If no reproduction is provided after 3 days, it will be auto-closed. placeholder: 'https://codesandbox.io/..' validations: required: true - type: textarea id: descr attributes: label: Bug description description: A clear and concise description of what the bug is validations: required: true - type: textarea id: expected attributes: label: Expected Behavior description: A concise description of what you expected to happen validations: required: false - type: textarea id: actual attributes: label: Actual Behavior description: A concise description of what you're experiencing validations: required: false - type: input id: swiper attributes: label: Swiper version description: Exact release version or commit hash placeholder: e.g 7.0.0 validations: required: true - type: input id: browser attributes: label: Platform/Target and Browser Versions description: Platform client you are targeting such as macOS, Windows, Cordova, iOS, Android, Chrome, etc. placeholder: e.g macOS Safari 14.1 validations: required: true - type: checkboxes id: checkboxes attributes: label: Validations description: Before submitting the issue, please make sure you do the following options: - label: Follow our [Code of Conduct](https://github.com/nolimits4web/swiper/blob/master/CODE_OF_CONDUCT.md) required: true - label: Read the [docs](https://swiperjs.com/swiper-api). required: true - label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate. required: true - label: Make sure this is a Swiper issue and not a framework-specific issue required: true - type: checkboxes id: pr attributes: label: Would you like to open a PR for this bug? description: Before starting to work on PR it is recommended to get maintainers approval options: - label: I'm willing to open a PR required: false ================================================ FILE: .github/ISSUE_TEMPLATE/vue_issue.yml ================================================ name: 🐞 Swiper Vue Issue description: Create a report for Swiper Vue components labels: 'Vue' body: - type: markdown attributes: value: | Thanks for taking the time to fill out this bug report! - type: checkboxes id: qa attributes: label: Check that this is really a bug description: For Q&A open a [GitHub Discussion](https://github.com/nolimits4web/swiper/discussions) options: - label: I confirm required: true - type: input id: reproduction attributes: label: Reproduction link description: Please provide a link to a repo that can reproduce the problem you ran into. You can fork one of our [demos](https://swiperjs.com/demos) in codesandbox to get start. A reproduction is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "missing demo" label. If no reproduction is provided after 3 days, it will be auto-closed. placeholder: 'https://codesandbox.io/..' validations: required: true - type: textarea id: descr attributes: label: Bug description description: A clear and concise description of what the bug is validations: required: true - type: textarea id: expected attributes: label: Expected Behavior description: A concise description of what you expected to happen validations: required: false - type: textarea id: actual attributes: label: Actual Behavior description: A concise description of what you're experiencing validations: required: false - type: input id: swiper attributes: label: Swiper version description: Exact release version or commit hash placeholder: e.g 6.7.1 validations: required: true - type: input id: browser attributes: label: Platform/Target and Browser Versions description: Platform client you are targeting such as macOS, Windows, Cordova, iOS, Android, Chrome, etc. placeholder: e.g macOS Safari 14.1 validations: required: true - type: checkboxes id: checkboxes attributes: label: Validations description: Before submitting the issue, please make sure you do the following options: - label: Follow our [Code of Conduct](https://github.com/nolimits4web/swiper/blob/master/CODE_OF_CONDUCT.md) required: true - label: Read the [docs](https://swiperjs.com/swiper-api). required: true - label: Check that there isn't already an issue that request the same feature to avoid creating a duplicate. required: true - label: Make sure this is a Swiper issue and not a framework-specific issue required: true - type: checkboxes id: pr attributes: label: Would you like to open a PR for this bug? description: Before starting to work on PR it is recommended to get maintainers approval options: - label: I'm willing to open a PR required: false ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. The best way to propose a feature is to open an issue first and discuss your ideas there before implementing them. Always follow the [contribution guidelines](https://github.com/nolimits4web/swiper/blob/master/CONTRIBUTING.md) when submitting a pull request. ================================================ FILE: .github/dependabot.yml ================================================ - package-ecosystem: npm directory: "/" schedule: interval: monthly versioning-strategy: increase - package-ecosystem: github-actions directory: "/" schedule: interval: monthly ================================================ FILE: .github/lock.yml ================================================ daysUntilLock: 240 exemptLabels: - feature request - in progress lockLabel: outdated lockComment: false ================================================ FILE: .github/stale.yml ================================================ daysUntilStale: 180 daysUntilClose: 60 exemptLabels: - feature request - probably bug - bug confirmed - in progress staleLabel: stale markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. closeComment: > This issue has been automatically closed due to inactivity. If this issue is still actual, please, create the new one. ================================================ FILE: .github/workflows/build.yml ================================================ name: Build on: push: branches: [master, Swiper6, Swiper7, Swiper8] pull_request: branches: [master, Swiper6, Swiper7, Swiper8] jobs: test: runs-on: ubuntu-latest steps: - name: Checkout the repository uses: actions/checkout@v2 - name: Install Node.js uses: actions/setup-node@v2 with: node-version: 18 - name: Install dependencies run: npm install - name: Run build run: npm run build:prod ================================================ FILE: .github/workflows/formatting.yml ================================================ name: Formatting on: push: branches: [master, Swiper6, Swiper7, Swiper8] pull_request: branches: [master, Swiper6, Swiper7, Swiper8] jobs: test: runs-on: ubuntu-latest steps: - name: Checkout the repository uses: actions/checkout@v2 - name: Install Node.js uses: actions/setup-node@v2 with: node-version: 18 - name: Install dependencies run: npm install - name: Run prettier check run: npm run check-format ================================================ FILE: .github/workflows/issue-close-require.yml ================================================ name: Issue Close Require on: schedule: - cron: '0 0 * * *' jobs: close-issues: runs-on: ubuntu-latest steps: - name: missing demo uses: actions-cool/issues-helper@v2.2.1 with: actions: 'close-issues' token: ${{ secrets.GITHUB_TOKEN }} labels: 'missing demo' inactive-day: 3 ================================================ FILE: .github/workflows/issue-labeled.yml ================================================ name: Issue Labeled on: issues: types: [labeled] jobs: reply-labeled: runs-on: ubuntu-latest steps: - name: missing demo if: github.event.label.name == 'missing demo' uses: actions-cool/issues-helper@v2.2.1 with: actions: 'create-comment, remove-labels' token: ${{ secrets.GITHUB_TOKEN }} issue-number: ${{ github.event.issue.number }} body: | Hello @${{ github.event.issue.user.login }}. Please provide a online reproduction by [codesandbox](https://codesandbox.io/) or a minimal GitHub repository. You can fork one of our [demos](https://swiperjs.com/demos) in codesandbox to get start. Issues labeled by `missing demo` will be closed if no activities in 3 days. ================================================ FILE: .github/workflows/lint.yml ================================================ name: Lint on: push: branches: [master, Swiper6, Swiper7, Swiper8] pull_request: branches: [master, Swiper6, Swiper7, Swiper8] jobs: test: runs-on: ubuntu-latest steps: - name: Checkout the repository uses: actions/checkout@v2 - name: Install Node.js uses: actions/setup-node@v2 with: node-version: 18 - name: Install dependencies run: npm install - name: Run eslint run: npm run lint ================================================ FILE: .gitignore ================================================ .DS_Store node_modules custom *.log .versions .idea dist # local env files .env.local .env.development.local .env.test.local .env.production.local .eslintcache src/swiper-element-bundle.mjs # Agents .claude .agents .cursor .gemini .codex .opencode ================================================ FILE: .lintstagedrc ================================================ { "*.+(js)": [ "eslint" ], "*.+(js|json|scss|css|less|ts)": [ "prettier --write" ] } ================================================ FILE: .npmignore ================================================ .git .github build demos node_modules playground scripts ================================================ FILE: .prettierignore ================================================ build node_modules dist .nova package.json .claude .agents .cursor .gemini .codex .opencode ================================================ FILE: .prettierrc ================================================ { "arrowParens": "always", "bracketSpacing": true, "htmlWhitespaceSensitivity": "css", "insertPragma": false, "jsxSingleQuote": false, "printWidth": 100, "proseWrap": "preserve", "quoteProps": "as-needed", "requirePragma": false, "endOfLine": "auto", "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "all", "useTabs": false, "vueIndentScriptAndStyle": false } ================================================ FILE: .vscode/settings.json ================================================ { "editor.formatOnSave": true, "eslint.enable": true, "editor.defaultFormatter": "esbenp.prettier-vscode" } ================================================ FILE: BACKERS.md ================================================ # Backers Support Swiper development by [pledging on Open Collective](http://opencollective.com/swiper)!
Uudet Nettikasinot VanguardNGR France Marsbahis: Marsbahis Giriş - Marsbahis Güncel Adres ve Üyelik Casinos Sin Licencia En España Skilled Writers for In-Depth Academic Papers Masterpapers - Qualified writers delivering excellence in every word! Best Online Casinos Canada Grademiners - Professional writers, original content, quality you can trust! Nettikasinot České Online Casino: Hrajte Bezpečně a Výhodně v 2024 Najlepšie Online Casino Slovensko 2024 | Október 2024 SS Market: Social Media Services Market
Automatenspieler Uudet nettikasinot 2024 » Listaamme Suomen uudet kasinot Nettikasinot 2022 | Löydä Luotettava & Turvallinen Nettikasino! NonGamStopOdds casino sites Online Casino Test 2022 » 90+ Casinos von Experten geprüft! Online sports betting and casino at Parimatch India Casinos not on GamStop | Casino-Wise.com Casinos not on GamStop UK 🏆 NonGamStopWager.com Online Casinos Cyprus - CasinoAuditor Best Online Casinos in Canada Buy TikTok Followers - STL Today Online Pokies in Australia
No Deposit Casinos Brecktic UK fortune365 CryptoCasino Guide Buy Facebook Likes for Post & Page SMM Panel Yahoo Finance - Super Clone Watches superclonewatch AI Detector & AI Checker for ChatGPT, Gemini, Claude, & More Buy Instagram Followers - IDS News gmkoutsi.com Interac-casino.com - Canada
Releaf - Medizinischer Cannabis Shop Legit Casino EuroCommpr SnapInsta Brand New Online Casinos USA No Deposit Bonus fx取引とは Bonustly: Best Crypto Casino Bonuses Migliori Casino non AAMS in Italia 2025 fx取引とは online casino australia JokaCasino Swipey AI - NSFW Chat Remove negative information from the Internet | NonDetected.com
king Johnnie buy YouTube subscribers buy instagram followers Luotettavat nettikasinot 2025 Best New Online Casinos in 2025 Buy Backlinks - High Quality SEO Backlink Services King Johnnie Casino Australia Nomaspin Casino Nederland Buy TikTok Followers Buy TikTok Followers & Get Fame SoftOrbits - Easiest Photo Editing Software for Beginners casinos sin licencia en España
someone to take my online class service AI ETFs Super Clone Watches - Trusted Dealer For 1:1 Replica Watches ゴールド取引 Buy Instagram Followers Super Clone Watches For Sale: Best Website for 1:1 Replica Watches humanize ai unaimytext Casino online - Vi jämför casinon på nätet i Sverige Parhaat kasinot - Valitse turvallinen nettikasino New Casinos Not on Gamstop - Best Non GamStop sites in 2025 Casino not on GamStop The Best Social Media Promotion Service Provider
Trusted Source for Online Casino Info , Games, Guides , Reviews Buy Instagram Followers with Instant Delivery buy tiktok followers Buy YouTube Subscribers & Views UK Miramtech Uudet Nettikasinot Huhtikuu 2025 | Parhaat Uudet Kasinot Goldstar Social Pistolo Casino TikTok Likes Buy Tiktok Followers Online Casino Ausland: Beste ausländische Online Casinos Kasyno online w Polsce
Free Instagram Likes Casino online Favbet Buy X Followers from TweSocial (Instant & Cheap) SidesMedia: Buy Followers, Views, Likes & More Mejores Casinos Internacionales Online de España 2025 CasinoKennis casino online ellada БК Favbet casinos not on Gamstop Online Casinos mit deutscher Lizenz Casinofy CasinoAllianz
casinò online Italia Top UK Casinos Not on GamStop in 2025 nogamstopcasinos.org.uk My Social Following DashTickets New Zealand gambling magazine best tether casinos Buy TikTok Followers Онлайн казино та БК (ставки на спорт) в Україні O Καλύτερος Οδηγός Online Καζίνο Top online casino's van Nederland Polskie Sloty RoboCat Casino
Zamsino.com 🎖️ Global Online Gambling Guide Betwinner Partner Online Casinos Deutschland Casinos online em Portugal Betting Site Online Casino Nederland UK Casinos not on GamStop 2025 - nongamstopcasinos.net Guidebook.BetWinner ZonderCruks - Online Gokken Zonder CRUKS Mejores Casinos Sin Licencia en España SANCTIONS LAW FIRM Interpol Law Firm
try bookmakers not on GamStop Buy Instagram Followers & Likes Accelerating Your Software Products | Teravision Technologies Casino Magyar Ta καλύτερα διαδικτυακά καζίνο στην Ελλάδα το 2024 Bulkoid Gokken Online Greece Casinos Casino Bonussen Nederland Casino No deposit Bonus 2024 Buy Instagram Likes - Real Likes & Instant Delivery! Vedonlyontiyhtiot.com - Parhaat Vedolyöntiyhtiöt & Bonukset
Purchase TikTok followers, likes and views Buy Youtube Views UpGrow: #1 AI-Powered Instagram Growth | Real IG Followers Best Bitcoin Casinos Topnoaccountcasinos casino zonder registratie Prointernet Nexus Smoke Premium E-Liquid and Luxury Vape Products Overlyzer » football betting analyzer & soccer predictions Casino utan Svensk Licens | Bästa Casinon utan Spelpaus 🎖️ BetFans - Alles over online wedden; Bookmakers Vergelijken Top USA Online Casinos September 2023 | Online United States Casinos Best Online Casino in Philippines using GCash | 2023 Rank
Technology, Security, Innovation, The Cyber World Now | Cyberogism Buy Instagram Reels Views Onlinecasinosspelen.com site is dé nummer één gids, waardoor je gemakkelijk alle informatie van de top 10 online casino sites. Rotativka.com - Най-добрите онлайн казина в България Under 1 Hour Withdrawal Casinos in Australia - 2022 PA Online Casino - List of Best Casinos in Pennsylvania Casino Zonder Registratie 2022 | CZR's Top No Account Casino's Ranglijst Nieuwe Online Casino's December 2022 | Overzicht van de top nieuwe casinos! cricket betting app WiserGamblers | Best Online Gambling Guide betting sider The Casino Wizard » Best Casinos & (No) Deposit Bonuses 2022
Fastest Payout Casinos in Canada [2022] Clique Studios - Creative Digital Transformation Correct Casinos | The Ultimate Guide to the Legit Online Casinos IB extended essay writing service Online Slots - UK Slot Games - 500 FREE Spins at Wizard Slots Fortune Games® | Free Spins No Deposit Slot Games | Online Slots LÅN PENGE NU | Hurtige Online lån 2021 | Klik her og Ansøg i dag VPN for Chrome to Make Web Surfing 100% Safe CasinoExpo casino utan registrering Ranking Bukmacherów Legalnych 2020. Bukmacher nr 1 to... The Best Online Casinos in the UK » Gambling Sites by Casinosters Deposit £10 Play with 30, 40, 50, 60, 70, or 80 Pounds✔️ GambLizard
Instagram likes
### \$500 Platinum Sponsor [Currently vacant. It could be you!](https://opencollective.com/swiper/contribute/platinum-sponsor-24468/checkout) --- ### \$250 Gold Sponsor [Currently vacant. It could be you!](https://opencollective.com/swiper/contribute/gold-sponsor-24466/checkout) --- ### \$100 Silver Sponsor - [Uudet Nettikasinot](https://fi.parhaatuudetkasinot.com/) - [VanguardNGR France](https://www.vanguardngr.com/casino/fr/) - [Marsbahis: Marsbahis Giriş - Marsbahis Güncel Adres ve Üyelik](https://marsbahis.com/tr/) - [Casinos Sin Licencia En España](https://heinekenjazzaldia.eus/) - [Skilled Writers for In-Depth Academic Papers](https://royalwriter.co.uk/) - [Masterpapers - Qualified writers delivering excellence in every word!](https://www.masterpapers.com/) - [Best Online Casinos Canada](https://casinosfest.com/) - [Grademiners - Professional writers, original content, quality you can trust!](https://grademiners.com/) - [Nettikasinot](https://www.nettikasinot.org/) - [České Online Casino: Hrajte Bezpečně a Výhodně v 2024](https://nejlepsiceskacasina.com/) - [Najlepšie Online Casino Slovensko 2024 | Október 2024](https://slovenskeonlinecasino.com/) - [SS Market: Social Media Services Market](https://ssmarket.net/) - [Automatenspieler](https://automatenspieler.net/) - [Uudet nettikasinot 2024 » Listaamme Suomen uudet kasinot](https://uusimmatkasinot.com/) - [Nettikasinot 2022 | Löydä Luotettava & Turvallinen Nettikasino!](https://www.kasinohai.com/nettikasinot) - [NonGamStopOdds casino sites](https://www.nongamstopodds.com/casinos-not-on-gamstop/) - [Online Casino Test 2022 » 90+ Casinos von Experten geprüft!](https://www.casinotest.de) - [Online sports betting and casino at Parimatch India](https://parimatch.in/en/football/live) - [Casinos not on GamStop | Casino-Wise.com](https://casino-wise.com/casinos-not-on-gamstop/) - [Casinos not on GamStop UK 🏆 NonGamStopWager.com](https://www.nongamstopwager.com/casinos-not-on-gamstop/) - [Online Casinos Cyprus - CasinoAuditor](https://casinoauditor.com/online-casinos-cyprus/) - [Best Online Casinos in Canada](https://casinoshunter.com/online-casinos/) [Join here!](https://opencollective.com/swiper/contribute/silver-sponsor-24464/checkout) --- ### \$50+ Sponsor - [Buy TikTok Followers - STL Today](https://www.stltoday.com/exclusive/3-top-rated-platforms-to-buy-tiktok-followers-for-fast-growth/article_74fb98eb-6019-5b74-9cda-803a7d4ac4a2.html) - [Online Pokies in Australia](https://au.trustpilot.com/review/pokiesgambler.com) - [No Deposit Casinos](https://topl.co) - [Brecktic UK](https://brecktic.uk/) - [fortune365](https://www.fortune365.com/) - [CryptoCasino Guide](https://cryptocasino-guide.com/) - [Buy Facebook Likes for Post & Page](https://www.fbpostlikes.com/) - [SMM Panel](https://www.reddit.com/r/BusinessVault/comments/1nr32xk/whats_the_best_smm_panel_looking_for_suggestions/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button) - [Yahoo Finance - Super Clone Watches](https://finance.yahoo.com/news/best-website-super-clone-watches-073500706.html) - [superclonewatch](https://superclonereps.com/) - [AI Detector & AI Checker for ChatGPT, Gemini, Claude, & More](https://detecting-ai.com/) - [Buy Instagram Followers - IDS News](https://www.idsnews.com/article/2025/09/buy-instagram-followers) - [gmkoutsi.com](https://gmkoutsi.com/) - [Interac-casino.com - Canada](https://interac-casino.com/en-ca/) - [Releaf - Medizinischer Cannabis Shop](https://releaf.com/de/) - [Legit Casino](https://legitcasino.vip/) - [EuroCommpr](https://www.eurocommpr.at/) - [SnapInsta](https://snapinsta.vin/) - [Brand New Online Casinos USA No Deposit Bonus](https://gpc.fm/) - [fx取引とは](https://www.hfm.com/int/jp/trading-education/what-is-forex) - [Bonustly: Best Crypto Casino Bonuses](https://bonustly.com/) - [Migliori Casino non AAMS in Italia 2025](https://casinononaamsit.com/) - [fx取引とは](https://www.hfm.com/int/jp/trading-education/what-is-forex) - [online casino australia JokaCasino](https://opencollective.com/jokacasino) - [Swipey AI - NSFW Chat](https://swipey.ai/) - [Remove negative information from the Internet | NonDetected.com](https://nondetected.com/) - [king Johnnie](https://opencollective.com/king-johnnie1) - [buy YouTube subscribers](https://collegian.com/sponsored/2025/03/buy-youtube-subscribers/) - [buy instagram followers](https://www.reddit.com/r/InstagramEmpire/comments/1o8v7zx/whats_the_best_site_to_buy_instagram_followers/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button) - [Luotettavat nettikasinot 2025](https://www.kasinoranking.com/) - [Best New Online Casinos in 2025](https://newcasinos.vip/) - [Buy Backlinks - High Quality SEO Backlink Services](https://www.backlinksrocket.com/) - [King Johnnie Casino Australia](https://kingjohnnie-au.casino/) - [Nomaspin Casino Nederland](https://nomaspinn.com/) - [Buy TikTok Followers](https://buytiktokfollowers.co/) - [Buy TikTok Followers & Get Fame](https://tiktokfame.co/buy-tiktok-followers/) - [SoftOrbits - Easiest Photo Editing Software for Beginners](https://www.softorbits.net/) - [casinos sin licencia en España](https://casinossinlicencia.eu/) - [someone to take my online class service](https://homeworkguy.org/someone-to-take-my-online-class) - [AI ETFs](https://www.bestetf.net/list/artificial-intelligence/) - [Super Clone Watches - Trusted Dealer For 1:1 Replica Watches](https://superluxuryreps.com/) - [ゴールド取引](https://www.hfm.com/int/jp/trading-education/how-to-trade-gold) - [Buy Instagram Followers ](https://www.reddit.com/r/MarketingMentor/comments/1cut7x5/where_to_buy_instagram_followers_likes/) - [Super Clone Watches For Sale: Best Website for 1:1 Replica Watches](https://prestigewatches.co/) - [humanize ai unaimytext](https://unaimytext.com/) - [Casino online - Vi jämför casinon på nätet i Sverige](https://www.casinotopplistan.com/) - [Parhaat kasinot - Valitse turvallinen nettikasino](https://www.kasinonetti.com/) - [New Casinos Not on Gamstop - Best Non GamStop sites in 2025](https://nogamstopcasinos.uk/) - [Casino not on GamStop](https://casinonotongamstop.uk/) - [The Best Social Media Promotion Service Provider](https://socialfollowers.io/) - [Trusted Source for Online Casino Info , Games, Guides , Reviews](https://casinotreasure.com/) - [Buy Instagram Followers with Instant Delivery](https://www.mixx.com/buy-instagram-followers) - [buy tiktok followers](https://expressfollowers.com/buy-tiktok-followers) - [Buy YouTube Subscribers & Views UK](https://boostlikes.uk/buy-youtube-subscribers-views/) - [Miramtech](https://miramtech.com/) - [Uudet Nettikasinot Huhtikuu 2025 | Parhaat Uudet Kasinot](https://www.uudetkasinot.com/) - [Goldstar Social](https://goldstarsocial.com/) - [Pistolo Casino](https://pistolocasino.com/) - [TikTok Likes](https://follower-boerse.de/product/tiktok-likes-kaufen/) - [Buy Tiktok Followers](https://www.socialfollowers.uk/buy-tiktok-followers/) - [Online Casino Ausland: Beste ausländische Online Casinos](https://www.palace-luzern.ch/) - [Kasyno online w Polsce](https://kasynoplonline.com/) - [Free Instagram Likes](https://buylikesservices.com/free-instagram-likes/) - [Casino online Favbet](https://www.favbet.ro/ro/casino/) - [Buy X Followers from TweSocial (Instant & Cheap)](https://twesocial.com/) - [SidesMedia: Buy Followers, Views, Likes & More](https://sidesmedia.com/) - [Mejores Casinos Internacionales Online de España 2025](https://casinosinternacionalesonline.com/) - [CasinoKennis](https://www.casinokennis.com/) - [casino online ellada](https://casinoonlineellada.com/) - [БК Favbet](https://www.favbet.ua/uk/) - [casinos not on Gamstop](https://www.vso.org.uk/) - [Online Casinos mit deutscher Lizenz](https://bonusoid.com/) - [Casinofy](https://www.casinofy.com/) - [CasinoAllianz](https://casinoallianz.com/) - [casinò online Italia](https://casinos.it.com/) - [Top UK Casinos Not on GamStop in 2025](https://www.stjamestheatre.co.uk/) - [nogamstopcasinos.org.uk](https://nogamstopcasinos.org.uk/) - [My Social Following](https://mysocialfollowing.com/) - [DashTickets New Zealand gambling magazine](https://dashtickets.nz/) - [best tether casinos](https://tethercasinos.top/) - [Buy TikTok Followers](https://www.socialboosting.com/buy-tiktok-followers) - [Онлайн казино та БК (ставки на спорт) в Україні](https://betking.com.ua/) - [O Καλύτερος Οδηγός Online Καζίνο](https://tychebets.gr/) - [Top online casino's van Nederland](https://www.top-casino.nl/) - [Polskie Sloty](https://polskiesloty.com/) - [RoboCat Casino](https://robocat.casino/) - [Zamsino.com 🎖️ Global Online Gambling Guide](https://zamsino.com/) - [Betwinner Partner](https://betwinnerpartner.com/) - [Online Casinos Deutschland](https://www.casinosdeutschlandonline.com/) - [Casinos online em Portugal](https://casinosportugal.com/) - [Betting Site](https://bettingsite.cc/) - [Online Casino Nederland](https://www.onlinecasinolegends.com/) - [UK Casinos not on GamStop 2025 - nongamstopcasinos.net](https://nongamstopcasinos.net/gb/) - [Guidebook.BetWinner](https://guidebook.betwinner.com/) - [ZonderCruks - Online Gokken Zonder CRUKS](https://zondercruks.net/) - [Mejores Casinos Sin Licencia en España](https://casinosinlicenciaespana.com/) - [SANCTIONS LAW FIRM](https://sanctionslawyers.net/) - [Interpol Law Firm](https://interpollawfirm.com/) - [try bookmakers not on GamStop](https://nongamstopbookiesuk.com/) - [Buy Instagram Followers & Likes](https://leofame.com/) - [Accelerating Your Software Products | Teravision Technologies](https://www.teravisiontech.com/) - [Casino Magyar](https://onlinecasinosgr.com/) - [Ta καλύτερα διαδικτυακά καζίνο στην Ελλάδα το 2024](https://onlinecasinosgr.com/) - [Bulkoid](https://bulkoid.com/) - [Gokken Online](https://www.gokken-online.com/) - [Greece Casinos](https://greece-casinos.com/) - [Casino Bonussen Nederland](https://www.hellobonuses.com/nl/) - [Casino No deposit Bonus 2024](https://www.ownedcore.com/casino/) - [Buy Instagram Likes - Real Likes & Instant Delivery!](https://blastup.com/buy-instagram-likes) - [Vedonlyontiyhtiot.com - Parhaat Vedolyöntiyhtiöt & Bonukset](https://vedonlyontiyhtiot.com/) - [Purchase TikTok followers, likes and views](https://celebian.com/) - [Buy Youtube Views](https://views4you.com/buy-youtube-views/) - [UpGrow: #1 AI-Powered Instagram Growth | Real IG Followers](https://www.upgrow.com/) - [Best Bitcoin Casinos](https://www.doublethebitcoin.net/) - [Topnoaccountcasinos casino zonder registratie](https://topnoaccountcasinos.com/nl/) - [Prointernet](https://www.prointernet.in.ua/) - [Nexus Smoke Premium E-Liquid and Luxury Vape Products](https://nexussmoke.com/) - [Overlyzer » football betting analyzer & soccer predictions](https://www.overlyzer.com/) - [Casino utan Svensk Licens | Bästa Casinon utan Spelpaus 🎖️](https://spelpressen.se/casino-reportage/casino-utan-svensk-licens) - [BetFans - Alles over online wedden; Bookmakers Vergelijken](https://betfans.nl/) - [Top USA Online Casinos September 2023 | Online United States Casinos](https://www.onlineunitedstatescasinos.com/) - [Best Online Casino in Philippines using GCash | 2023 Rank](https://philippinescasinos.ph/gcash/) - [Technology, Security, Innovation, The Cyber World Now | Cyberogism](https://cyberogism.com/) - [Buy Instagram Reels Views](https://buycheapestfollowers.com/buy-instagram-reels-views) - [Onlinecasinosspelen.com site is dé nummer één gids, waardoor je gemakkelijk alle informatie van de top 10 online casino sites.](https://onlinecasinosspelen.com/) - [Rotativka.com - Най-добрите онлайн казина в България](https://rotativka.com/) - [Under 1 Hour Withdrawal Casinos in Australia - 2022](https://www.casinoaustraliaonline.com/under-1-hour-withdrawal-casinos/) - [PA Online Casino - List of Best Casinos in Pennsylvania](https://betbetter-pa.com/) - [Casino Zonder Registratie 2022 | CZR's Top No Account Casino's Ranglijst](https://casinozonderregistratie.net/) - [Nieuwe Online Casino's December 2022 | Overzicht van de top nieuwe casinos!](https://nieuwe-casinos.net/) - [cricket betting app](https://4rabet.com/app) - [WiserGamblers | Best Online Gambling Guide](https://www.wisergamblers.com/de/casino-bonus-ohne-einzahlung/) - [betting sider](https://betting-sider.net/) - [The Casino Wizard » Best Casinos & (No) Deposit Bonuses 2022](https://thecasinowizard.com/) - [Fastest Payout Casinos in Canada [2022]](https://www.fast.bet/ca/) - [Clique Studios - Creative Digital Transformation](https://cliquestudios.com) - [Correct Casinos | The Ultimate Guide to the Legit Online Casinos](https://correctcasinos.com) - [IB extended essay writing service](https://writingmetier.com/extended-essay-writing-service/) - [Online Slots - UK Slot Games - 500 FREE Spins at Wizard Slots](https://www.wizardslots.com) - [Fortune Games® | Free Spins No Deposit Slot Games | Online Slots](https://www.fortunegames.com) - [LÅN PENGE NU | Hurtige Online lån 2021 | Klik her og Ansøg i dag](https://tankpenge.dk) - [VPN for Chrome to Make Web Surfing 100% Safe](https://veepn.com/vpn-apps/vpn-for-chrome/) - [CasinoExpo casino utan registrering](https://casinoexpo.se/casino-utan-registrering/) - [Ranking Bukmacherów Legalnych 2020. Bukmacher nr 1 to...](https://najlepsibukmacherzy.pl/ranking-legalnych-bukmacherow/) - [The Best Online Casinos in the UK » Gambling Sites by Casinosters](https://casinosters.com) - [Deposit £10 Play with 30, 40, 50, 60, 70, or 80 Pounds✔️ GambLizard](https://gamblizard.com/deposit-bonuses/deposit-10-pound/) - [Instagram likes](https://goread.io/buy-instagram-likes) [Join here!](https://opencollective.com/swiper/contribute/sponsor-24467/checkout) --- ### \$25+ Top Supporter [easy-views.org](https://easy-views.org) - High Retention Youtube Views
[Join here!](https://opencollective.com/swiper/contribute/top-supporter-24465/checkout) --- ### \$10+ Supporter [Quicko](https://opencollective.com/quicko)
[Instagram Stories Viewer](https://opencollective.com/instagram-stories-viewer)
[Will Myers](https://opencollective.com/will-myers)
[Join here!](https://opencollective.com/swiper/contribute/supporter-23766/checkout) --- ### \$5+ Thank You [Fresh Engagements](https://opencollective.com/fresh-engagements)
[Instagram Services](https://www.patreon.com/user?u=67523502)
================================================ FILE: CHANGELOG.md ================================================ # Changelog # [12.1.1](https://github.com/nolimits4web/Swiper/compare/v12.1.0...v12.1.1) (2026-02-13) ### Bug Fixes - **a11y:** fix focus in virtual mode enabled ([3055008](https://github.com/nolimits4web/Swiper/commit/30550088fd089600aec2d7f8924b88cff13abbe9)), closes [#8147](https://github.com/nolimits4web/Swiper/issues/8147) - **core:** avoid double-subtracting offsets in centerInsufficientSlides ([#8158](https://github.com/nolimits4web/Swiper/issues/8158)) ([60b0052](https://github.com/nolimits4web/Swiper/commit/60b005222a801029a4a00d319517028afba7af18)) - **core:** prevent duplicate module initialization in constructor ([#8155](https://github.com/nolimits4web/Swiper/issues/8155)) ([#8156](https://github.com/nolimits4web/Swiper/issues/8156)) ([07738a2](https://github.com/nolimits4web/Swiper/commit/07738a233b70535c36126c5b579f2bb40049da6c)) - **types:** support boolean as a11y value ([#8157](https://github.com/nolimits4web/Swiper/issues/8157)) ([6bf76d5](https://github.com/nolimits4web/Swiper/commit/6bf76d573196c61db1328350c11e2c44f5d3ec08)) # [12.1.0](https://github.com/nolimits4web/Swiper/compare/v12.0.3...v12.1.0) (2026-01-28) ### Bug Fixes - **autoplay:** broken custom delay percentages with pause/resume ([#8133](https://github.com/nolimits4web/Swiper/issues/8133)) ([0afecde](https://github.com/nolimits4web/Swiper/commit/0afecde9781268a60a92f752ea3a3a92420e2dcf)) - **core:** Don't use `data-swiper-slide-index` for `realIndex` when virtual module is enabled ([#8142](https://github.com/nolimits4web/Swiper/issues/8142)) ([bd957f8](https://github.com/nolimits4web/Swiper/commit/bd957f8396f711b83dc1bc6b3d42a59e6d6539d2)) - **core:** Escape all CSS selector special characters ([d35f41a](https://github.com/nolimits4web/Swiper/commit/d35f41a85bd1793de58358d06300440e09187a6d)), closes [#8135](https://github.com/nolimits4web/Swiper/issues/8135) - **core:** support slidesOffsetBefore and slidesOffsetAfert in cssMode ([45b98d0](https://github.com/nolimits4web/Swiper/commit/45b98d02b2235b0c425f8bd60ebdc04d7b1a4fbd)), closes [#7926](https://github.com/nolimits4web/Swiper/issues/7926) - fix lazy preloader removal error in react in vue ([332f5c7](https://github.com/nolimits4web/Swiper/commit/332f5c77005921c8a260f199cdfe6d3aa5d209a1)), closes [#8149](https://github.com/nolimits4web/Swiper/issues/8149) - **thumbs:** update slide classes on virtual swiper update ([#8141](https://github.com/nolimits4web/Swiper/issues/8141)) ([9752771](https://github.com/nolimits4web/Swiper/commit/975277111b73f389043cb0ed19feee0244a80f57)) - **types:** Add `autoScroll` to `thumbs.update` type signature ([#8146](https://github.com/nolimits4web/Swiper/issues/8146)) ([5d91e6e](https://github.com/nolimits4web/Swiper/commit/5d91e6edb4ce35d70019616b51f1e380feb9a082)) - **zoom:** initialize gesture state after programmatic zoom ([#8112](https://github.com/nolimits4web/Swiper/issues/8112)) ([71e9511](https://github.com/nolimits4web/Swiper/commit/71e9511802c34482bc8b66abda19a1a518d88d36)) ### Features - **keyboard:** add support for custom speed parameter in keyboard navigation ([#8148](https://github.com/nolimits4web/Swiper/issues/8148)) ([7a4a0e5](https://github.com/nolimits4web/Swiper/commit/7a4a0e5fc3c85710a37ab021328e083fc3b14e16)) - new snapToSlideEdge parameter ([de3131f](https://github.com/nolimits4web/Swiper/commit/de3131fbf72cccbf1d1473f787ddf15c74612584)), closes [#8021](https://github.com/nolimits4web/Swiper/issues/8021) [#4780](https://github.com/nolimits4web/Swiper/issues/4780) # [12.0.3](https://github.com/nolimits4web/Swiper/compare/v12.0.2...v12.0.3) (2025-10-21) ### Bug Fixes - **element:** fixed reference to nav arrows SVG ([0b17ecf](https://github.com/nolimits4web/Swiper/commit/0b17ecf56bf941e4a5da2a2c171d5e16a9e4552b)), closes [#8115](https://github.com/nolimits4web/Swiper/issues/8115) ### Features - **core:** add 'getRotateFix' export to effect utils ([c97ae5d](https://github.com/nolimits4web/Swiper/commit/c97ae5d0069cf3d5c745efb63ced9924a64d2453)), closes [#8114](https://github.com/nolimits4web/Swiper/issues/8114) # [12.0.2](https://github.com/nolimits4web/Swiper/compare/v12.0.1...v12.0.2) (2025-09-18) ### Features - **navigation:** add styles for when buttons set before slider ([4588c57](https://github.com/nolimits4web/Swiper/commit/4588c5719d4d828548c34f456de099f621f4c709)), closes [#8085](https://github.com/nolimits4web/Swiper/issues/8085) - **navigation:** new `addIcons` parameter to add SVG icons to nav buttons ([b955b0c](https://github.com/nolimits4web/Swiper/commit/b955b0c15c3b813bbda7a68cdd250f8a822015df)), closes [#8088](https://github.com/nolimits4web/Swiper/issues/8088) [#8087](https://github.com/nolimits4web/Swiper/issues/8087) # [12.0.1](https://github.com/nolimits4web/Swiper/compare/v12.0.0...v12.0.1) (2025-09-11) ### Bug Fixes - **navigation:** tweak nav styles when adjacent ([98440d9](https://github.com/nolimits4web/Swiper/commit/98440d9621c2b06c1c45edf8f4103ce5125e8231)) # [12.0.0](https://github.com/nolimits4web/Swiper/compare/v11.2.10...v12.0.0) (2025-09-11) ### Bug Fixes - **core:** fixes issues when slidesOffsetBefore & slidesOffsetAfter are combinated with centeredSlides, slidesPerView & loop ([#8038](https://github.com/nolimits4web/Swiper/issues/8038)) ([74cc297](https://github.com/nolimits4web/Swiper/commit/74cc29713508bb48939ef9147a869979a8120cc4)), closes [#7298](https://github.com/nolimits4web/Swiper/issues/7298) [#7956](https://github.com/nolimits4web/Swiper/issues/7956) [#6916](https://github.com/nolimits4web/Swiper/issues/6916) - **effect-cards:** fix offset in vertical direction when rotate is unset ([a248ca1](https://github.com/nolimits4web/Swiper/commit/a248ca1fc4fd070e02f65ca7afc55789c02f15c7)), closes [#8075](https://github.com/nolimits4web/Swiper/issues/8075) - **get-device:** regexp tweak ([60bb79b](https://github.com/nolimits4web/Swiper/commit/60bb79b599895350da71212b05e3f89a0dabc447)), closes [#8057](https://github.com/nolimits4web/Swiper/issues/8057) - **types:** correct types for getSlideTransformEl ([b34bdce](https://github.com/nolimits4web/Swiper/commit/b34bdce988a681a41778c95795e19787a19e754d)), closes [#8054](https://github.com/nolimits4web/Swiper/issues/8054) ### Features - **a11y:** add wrapperLiveRegion param to disable wrapper live region in a11y module ([#8061](https://github.com/nolimits4web/Swiper/issues/8061)) ([d03044e](https://github.com/nolimits4web/Swiper/commit/d03044e8f8648946dfecf9b669df987a5a5e1925)) - move to SVG icons for navigation ([264603c](https://github.com/nolimits4web/Swiper/commit/264603cc36a54e7a4f39507b2fbdab7e38bb046e)), closes [#6652](https://github.com/nolimits4web/Swiper/issues/6652) [#4990](https://github.com/nolimits4web/Swiper/issues/4990) - remove LESS and SCSS styles in favor of CSS ([118ec66](https://github.com/nolimits4web/Swiper/commit/118ec6616da1249b589f9c468076ec036234b36d)) - **virtual:** add slidesPerViewAutoSlideSize parameter for fixed slide dimensions with slidesPerView auto ([d472144](https://github.com/nolimits4web/Swiper/commit/d47214480e7b9155ce2203a9a21209e56c5c303b)), closes [#8041](https://github.com/nolimits4web/Swiper/issues/8041) [#7796](https://github.com/nolimits4web/Swiper/issues/7796) # [11.2.10](https://github.com/nolimits4web/Swiper/compare/v11.2.9...v11.2.10) (2025-06-28) ### Bug Fixes - **core:** fix clickedIndex with grid ([863d8bd](https://github.com/nolimits4web/Swiper/commit/863d8bdd750b5a5f5b6df8b1129656e8050bd615)) # [11.2.9](https://github.com/nolimits4web/Swiper/compare/v11.2.8...v11.2.9) (2025-06-27) ### Bug Fixes - **core:** clear blank slides within loopCreate() ([#8033](https://github.com/nolimits4web/Swiper/issues/8033)) ([377d53c](https://github.com/nolimits4web/Swiper/commit/377d53cbcf7dfbaeab76e8bfa88e94795693b157)) - **core:** escape brackets when transforming classes to selector ([#8014](https://github.com/nolimits4web/Swiper/issues/8014)) ([0c53ee8](https://github.com/nolimits4web/Swiper/commit/0c53ee8bd9d606934e0e29d78b7088a58d9fba64)) - **core:** slideTo function does not work when grid.rows > 1 ([#8030](https://github.com/nolimits4web/Swiper/issues/8030)) ([1fde9d3](https://github.com/nolimits4web/Swiper/commit/1fde9d38090a858487ae9e000a8c164fecf81320)) - **core:** using loop and slideToClickedSlide / slideToLoop with centeredSlides ([b6692e2](https://github.com/nolimits4web/Swiper/commit/b6692e21de281685c6434368e5cac2180f9320fa)), closes [#8031](https://github.com/nolimits4web/Swiper/issues/8031) - **element:** don't set subprops on boolean values ([090caa1](https://github.com/nolimits4web/Swiper/commit/090caa19764f16e44e89b8478e5737f1783eefa3)), closes [#8003](https://github.com/nolimits4web/Swiper/issues/8003) - **keyboard:** detect contenteditable element in keyboard module ([#8006](https://github.com/nolimits4web/Swiper/issues/8006)) ([fcd434a](https://github.com/nolimits4web/Swiper/commit/fcd434aa53e957b516417865bddafdcb5533e863)) - **vue:** add missing event ([839cafb](https://github.com/nolimits4web/Swiper/commit/839cafb60fb68d8f4ef8b5a2ccf6648e895e8fe9)), closes [#8002](https://github.com/nolimits4web/Swiper/issues/8002) # [11.2.8](https://github.com/nolimits4web/Swiper/compare/v11.2.7...v11.2.8) (2025-05-23) ### Bug Fixes - **virtual:** fix error in Virtual slides ([7a53208](https://github.com/nolimits4web/Swiper/commit/7a53208899cbcc6efb3ec655a9de3a0b4871bbb8)), closes [#7997](https://github.com/nolimits4web/Swiper/issues/7997) # [11.2.7](https://github.com/nolimits4web/Swiper/compare/v11.2.6...v11.2.7) (2025-05-19) ### Bug Fixes - **core:** fix for slideResetTransition events ([#7989](https://github.com/nolimits4web/Swiper/issues/7989)) ([473ec25](https://github.com/nolimits4web/Swiper/commit/473ec25a74a9dfbf4366445034bd22aabc3f1889)) - **core:** set innerHTML using trusted type ([8a46954](https://github.com/nolimits4web/Swiper/commit/8a46954e43c976ea5dc0c6e3edf351f3a99a0d42)), closes [#7961](https://github.com/nolimits4web/Swiper/issues/7961) - **react,vue:** fix not rendered slides in effects when Virtual is enabled ([e4e1680](https://github.com/nolimits4web/Swiper/commit/e4e1680cda676992ff2ff7b14d7c45db9aec304b)), closes [#7730](https://github.com/nolimits4web/Swiper/issues/7730) - **types:** add enabled property on swiper-class.d.ts ([#7973](https://github.com/nolimits4web/Swiper/issues/7973)) ([ccd96dc](https://github.com/nolimits4web/Swiper/commit/ccd96dca508de2a72adb11cba3fce96d24596453)) - **types:** coverflow effect can be number or string with percentage ([#7931](https://github.com/nolimits4web/Swiper/issues/7931)) ([4d988d8](https://github.com/nolimits4web/Swiper/commit/4d988d8b0f5f646d881895c2934d82042fa1b120)) - **types:** fix `effect` param type ([#7945](https://github.com/nolimits4web/Swiper/issues/7945)) ([42eec07](https://github.com/nolimits4web/Swiper/commit/42eec0722c7a1d6dd943245282f4048ce7d4459d)) - **vue:** added missing props ([#7966](https://github.com/nolimits4web/Swiper/issues/7966)) ([6aa8d05](https://github.com/nolimits4web/Swiper/commit/6aa8d0585d29ff10abb9a4328ce6add240d4e55d)) # [11.2.6](https://github.com/nolimits4web/Swiper/compare/v11.2.5...v11.2.6) (2025-03-19) ### Bug Fixes - **cards/loop:** improve loop behavior with cards effect ([9a258d4](https://github.com/nolimits4web/Swiper/commit/9a258d42324cce36026bd3e3367b069a64e9aa99)), closes [#7917](https://github.com/nolimits4web/Swiper/issues/7917) - **core:** fix initialSlide in loop mode when it there are not enough slides to fill ([16818e2](https://github.com/nolimits4web/Swiper/commit/16818e2629d6afbbc1ef3778b3289d575e86d2ff)), closes [#7780](https://github.com/nolimits4web/Swiper/issues/7780) - **core:** fix touchReleaseOnEdges in RTL ([d841428](https://github.com/nolimits4web/Swiper/commit/d841428af537f127f16584cf437ba4c6a912a82f)), closes [#7179](https://github.com/nolimits4web/Swiper/issues/7179) # [11.2.5](https://github.com/nolimits4web/Swiper/compare/v11.2.4...v11.2.5) (2025-03-04) ### Bug Fixes - **docs:** correct structure of pagination render functions ([b259723](https://github.com/nolimits4web/Swiper/commit/b25972397926ad0abb3bfa808e8621127a0f0e06)), closes [#7897](https://github.com/nolimits4web/Swiper/issues/7897) [#7896](https://github.com/nolimits4web/Swiper/issues/7896) - **thumbs:** correct reinit thumbs ([1cf24d4](https://github.com/nolimits4web/Swiper/commit/1cf24d46472f1e0bbd73fa1ea6064fc5f793b7e6)), closes [#7880](https://github.com/nolimits4web/Swiper/issues/7880) - **zoom:** fix transform origin of `zoom.in()` function ([#7904](https://github.com/nolimits4web/Swiper/issues/7904)) ([f7febe1](https://github.com/nolimits4web/Swiper/commit/f7febe10be5d38954a7b030234037a852b47b12d)) ### Features - check if slidesEl is defined in loopDestroy ([#7906](https://github.com/nolimits4web/Swiper/issues/7906)) ([ced30cb](https://github.com/nolimits4web/Swiper/commit/ced30cbd567700582aef5076c5e22dc3b4b571e1)) # [11.2.4](https://github.com/nolimits4web/Swiper/compare/v11.2.3...v11.2.4) (2025-02-14) ### Bug Fixes - restore compatibility with ES2019 [#7891](https://github.com/nolimits4web/Swiper/issues/7891) ([#7892](https://github.com/nolimits4web/Swiper/issues/7892)) ([b4fbbb3](https://github.com/nolimits4web/Swiper/commit/b4fbbb3ceb1af1cecd5295a32f1e600876802e87)) # [11.2.3](https://github.com/nolimits4web/Swiper/compare/v11.2.2...v11.2.3) (2025-02-12) ### Bug Fixes - **element:** remove infinite preloader in loop ([#7886](https://github.com/nolimits4web/Swiper/issues/7886)) ([c0d3ece](https://github.com/nolimits4web/Swiper/commit/c0d3ece2147e95f37154ee019402286f5b213712)) ### Features - **core:** export swiper-vars.scss for overwriteability ([#7883](https://github.com/nolimits4web/Swiper/issues/7883)) ([0b688a1](https://github.com/nolimits4web/Swiper/commit/0b688a1a40d9365ff0a696abf290bea6baf5a8f0)), closes [#7882](https://github.com/nolimits4web/Swiper/issues/7882) - update ssr-window to latest ([c521888](https://github.com/nolimits4web/Swiper/commit/c521888d98863ededd3286543dac7e040bcf1ddc)) # [11.2.2](https://github.com/nolimits4web/Swiper/compare/v11.2.1...v11.2.2) (2025-01-31) ### Bug Fixes - **cards:** fix slides swap issue in loop mode ([e012e34](https://github.com/nolimits4web/Swiper/commit/e012e34f6f926dcec6415f11912d23190ed85242)), closes [#7823](https://github.com/nolimits4web/Swiper/issues/7823) - **core:** fix slidePrev issue in free-mode ([a3fee36](https://github.com/nolimits4web/Swiper/commit/a3fee3615eaf3daf3fad54445c3bdba29d9cd302)), closes [#7869](https://github.com/nolimits4web/Swiper/issues/7869) - **virtual:** fix when Element not swiping in Safari ([5abdbfd](https://github.com/nolimits4web/Swiper/commit/5abdbfde1a4e796a6ed981105d02ca7b4b13d6ed)), closes [#7679](https://github.com/nolimits4web/Swiper/issues/7679) # [11.2.1](https://github.com/nolimits4web/Swiper/compare/v11.2.0...v11.2.1) (2025-01-10) ### Bug Fixes - **core:** add check for HTMLSlotElement support ([#7840](https://github.com/nolimits4web/Swiper/issues/7840)) ([56700e5](https://github.com/nolimits4web/Swiper/commit/56700e588e63da8a07ec25f096910cbef64e7dd1)) - **core:** typo in autoplay param ([#7848](https://github.com/nolimits4web/Swiper/issues/7848)) ([69113c9](https://github.com/nolimits4web/Swiper/commit/69113c99478921a52532daf2ba1ab994ed46b701)) # [11.2.0](https://github.com/nolimits4web/Swiper/compare/v11.1.15...v11.2.0) (2025-01-02) ### Bug Fixes - **core:** Fixed elementIsChildOf returning false for nested web components ([#7762](https://github.com/nolimits4web/Swiper/issues/7762)) ([8136607](https://github.com/nolimits4web/Swiper/commit/8136607e0782646cd095b6e3ff8ece63033df390)), closes [#7761](https://github.com/nolimits4web/Swiper/issues/7761) - **zoom:** Ensure the zoom module's array is cleared onTouchEnd to fix [#7304](https://github.com/nolimits4web/Swiper/issues/7304) ([#7830](https://github.com/nolimits4web/Swiper/issues/7830)) ([21610bd](https://github.com/nolimits4web/Swiper/commit/21610bde1d6537102fdcec62b86f2f2e0794540c)) ### Features - **core:** Allow using a CSS selector as breakpointsBase ([#7818](https://github.com/nolimits4web/Swiper/issues/7818)) ([44d3443](https://github.com/nolimits4web/Swiper/commit/44d3443f66d93c1dc6720bddb05780bea7406982)) - **zoom:** Add ability to pan around an image on mouse move ([#7831](https://github.com/nolimits4web/Swiper/issues/7831)) ([c4619bb](https://github.com/nolimits4web/Swiper/commit/c4619bb01dd929b29397afc7697958ca23c82c90)), closes [#7306](https://github.com/nolimits4web/Swiper/issues/7306) # [11.1.15](https://github.com/nolimits4web/Swiper/compare/v11.1.14...v11.1.15) (2024-11-18) ### Bug Fixes - **react:** react strict mode double initialization errors for thumbs ([#7789](https://github.com/nolimits4web/Swiper/issues/7789)) ([9dece7d](https://github.com/nolimits4web/Swiper/commit/9dece7dae34e6a7a0291cd0a05f8c804bfdd7b59)) - **scss:** Dart Sass 3.0.0 compatibility ([fbd2ba4](https://github.com/nolimits4web/Swiper/commit/fbd2ba4b9bc7c5ae6a3c8796e4ea790a2c5507b0)), closes [#7772](https://github.com/nolimits4web/Swiper/issues/7772) [#7771](https://github.com/nolimits4web/Swiper/issues/7771) - **types:** remove 'Lazy' declaration ([#7739](https://github.com/nolimits4web/Swiper/issues/7739)) ([40a705e](https://github.com/nolimits4web/Swiper/commit/40a705e5bcadf2ee2ee90591ff9ed95c1aaf9026)) - **types:** type definitions for `slidesEl`, `slidesGrid`, and `slidesSizesGrid` ([#7768](https://github.com/nolimits4web/Swiper/issues/7768)) ([fb59a41](https://github.com/nolimits4web/Swiper/commit/fb59a41801629a25dc7f0738961e3aeeb60a9cc8)) # [11.1.14](https://github.com/nolimits4web/Swiper/compare/v11.1.12...v11.1.14) (2024-09-12) ### Bug Fixes - **core:** fix mobile input blur on touchstart on different input ([66c5dd1](https://github.com/nolimits4web/Swiper/commit/66c5dd10dc3b75a645b17c5b06b193483c026a89)), closes [#7728](https://github.com/nolimits4web/Swiper/issues/7728) - **element:** Query the swiper.hostEl for the navigation buttons ([#7714](https://github.com/nolimits4web/Swiper/issues/7714)) ([#7716](https://github.com/nolimits4web/Swiper/issues/7716)) ([d0b6abd](https://github.com/nolimits4web/Swiper/commit/d0b6abd74805398ac3cbf41aeebf141a805ec64b)) - **zoom:** add sanity check before allowTouchMove call in timeout ([#7723](https://github.com/nolimits4web/Swiper/issues/7723)) ([869bb84](https://github.com/nolimits4web/Swiper/commit/869bb843735718c3c77551644cfd717bbf9e264e)) # [11.1.12](https://github.com/nolimits4web/Swiper/compare/v11.1.11...v11.1.12) (2024-09-01) ### Bug Fixes - **cards:** fix cards effect styles leak ([0be4c6a](https://github.com/nolimits4web/Swiper/commit/0be4c6aa785d5be34a8f33fde91b67ec25d5752a)), closes [#7712](https://github.com/nolimits4web/Swiper/issues/7712) ### Features - **a11y:** add `containerRole` parameter ([#7708](https://github.com/nolimits4web/Swiper/issues/7708)) ([1542c01](https://github.com/nolimits4web/Swiper/commit/1542c01823fd233aa6159c13aec717360cb3c01c)) # [11.1.11](https://github.com/nolimits4web/Swiper/compare/v11.1.10...v11.1.11) (2024-08-28) ### Bug Fixes - centeredSlides with centeredSlidesBounds don't work correct when slidesPerView: 'auto' and width of the swiper-container bigger then width of slides ([#7696](https://github.com/nolimits4web/Swiper/issues/7696)) ([c11172a](https://github.com/nolimits4web/Swiper/commit/c11172a50ae67d79a625ad35c9460feceff478c6)) - **element:** fix element styles to have correct order override ([f26036f](https://github.com/nolimits4web/Swiper/commit/f26036f3261bc9dad9f83a8c7145578b8ccf6ecc)), closes [#7704](https://github.com/nolimits4web/Swiper/issues/7704) - **virtual:** fix bypassing initial translate check if Virtual is enabled ([df957bb](https://github.com/nolimits4web/Swiper/commit/df957bbdb81ffeb7ef752598f740d4e155f9840a)), closes [#7699](https://github.com/nolimits4web/Swiper/issues/7699) # [11.1.10](https://github.com/nolimits4web/Swiper/compare/v11.1.9...v11.1.10) (2024-08-21) ### Bug Fixes - **pagination:** fixed swiper Infinite loop scroll jumping ([#7690](https://github.com/nolimits4web/Swiper/issues/7690)) - **zoom:** fix zoomIn after currentScale is set to 1 ([#7663](https://github.com/nolimits4web/Swiper/issues/7663)) ([94173da](https://github.com/nolimits4web/Swiper/commit/94173dae058d18e3839d8f98584bce460eb49996)) # [11.1.9](https://github.com/nolimits4web/Swiper/compare/v11.1.8...v11.1.9) (2024-07-31) ### Bug Fixes - **core:** fix loop missing slides with centeredSlides ([4847fcb](https://github.com/nolimits4web/Swiper/commit/4847fcba5dbe34caa91ea23b0bb72c8d40da0e63)), closes [#7584](https://github.com/nolimits4web/Swiper/issues/7584) - **core:** fix types of swiper-effect-utils.d.ts ([#7655](https://github.com/nolimits4web/Swiper/issues/7655)) ([4875f26](https://github.com/nolimits4web/Swiper/commit/4875f26163c016469e599abd3d5e0a05b3838959)) - **mousewheel:** fix issue with event handling after Swiper was destroyed ([82ae434](https://github.com/nolimits4web/Swiper/commit/82ae4346800b96bdb435cb97bed34beebbb1f359)), closes [#7654](https://github.com/nolimits4web/Swiper/issues/7654) ### Features - add types for minTranslate and maxTranslate functions ([#7647](https://github.com/nolimits4web/Swiper/issues/7647)) ([450c57a](https://github.com/nolimits4web/Swiper/commit/450c57a4c6d651207e4f79ffe7ac823ab2cfcb63)) # [11.1.8](https://github.com/nolimits4web/Swiper/compare/v11.1.7...v11.1.8) (2024-07-26) ### Bug Fixes - **core:** fixed typo in utils/elementIsChildOf ([#7649](https://github.com/nolimits4web/Swiper/issues/7649)) ([575e715](https://github.com/nolimits4web/Swiper/commit/575e71593bac73f8db64730cce2323f660006489)) # [11.1.7](https://github.com/nolimits4web/Swiper/compare/v11.1.6...v11.1.7) (2024-07-24) ### Bug Fixes - **core:** fix element child detection ([7ec975c](https://github.com/nolimits4web/Swiper/commit/7ec975c8550bfebacc7ecd032dc540e720f45175)), closes [#7636](https://github.com/nolimits4web/Swiper/issues/7636) # [11.1.6](https://github.com/nolimits4web/Swiper/compare/v11.1.5...v11.1.6) (2024-07-23) ### Bug Fixes - **controller:** fix 2 way control in element ([6eec16b](https://github.com/nolimits4web/Swiper/commit/6eec16bfe1f103bd19bad6dec0a7aa3fb3ee2bb1)), closes [#7628](https://github.com/nolimits4web/Swiper/issues/7628) ### Features - **a11y:** added new prop for a11y module - `scrollOnFocus` ([#7632](https://github.com/nolimits4web/Swiper/issues/7632)) ([f4f7da0](https://github.com/nolimits4web/Swiper/commit/f4f7da0260f1b03ab5f168b84a3b74ef8e2a7a26)) - **element:** added support for using slots as swiper wrappers ([#7624](https://github.com/nolimits4web/Swiper/issues/7624)) ([e374e06](https://github.com/nolimits4web/Swiper/commit/e374e06a930bef74658614900bb1e11c5a54b189)) # [11.1.5](https://github.com/nolimits4web/Swiper/compare/v11.1.4...v11.1.5) (2024-07-15) ### Bug Fixes - **element:** fix observer to watch for slides ([7cffede](https://github.com/nolimits4web/Swiper/commit/7cffedef190c6e75bf84adfa6dfddd64e1bfa0e6)), closes [#7598](https://github.com/nolimits4web/Swiper/issues/7598) - improved 3d rotate fix in Safari ([cb83879](https://github.com/nolimits4web/Swiper/commit/cb83879894fca633844b5db76dfe1d9d82c816ea)), closes [#7532](https://github.com/nolimits4web/Swiper/issues/7532) - update navigation.scss to remove SASS Deprecation Warning ([#7612](https://github.com/nolimits4web/Swiper/issues/7612)) ([a3e0bf8](https://github.com/nolimits4web/Swiper/commit/a3e0bf893da2af150a64b99c8d59b81249929a5a)) - **vue:** add breakpointsBase type ([4adb85b](https://github.com/nolimits4web/Swiper/commit/4adb85b081a0ed3e657da79d8f711e610335edba)), closes [#7607](https://github.com/nolimits4web/Swiper/issues/7607) # [11.1.4](https://github.com/nolimits4web/Swiper/compare/v11.1.3...v11.1.4) (2024-05-30) ### Bug Fixes - **a11y:** fix slide on focus when loop mode is enabled ([fc8ed1a](https://github.com/nolimits4web/Swiper/commit/fc8ed1a2de8f05b017e371763b1236e1eb2670c4)), closes [#7540](https://github.com/nolimits4web/Swiper/issues/7540) - **core:** check for swiper.el in destroy queue ([39a3e53](https://github.com/nolimits4web/Swiper/commit/39a3e53ba7df00a0479e0748fff874703578c7df)), closes [#7530](https://github.com/nolimits4web/Swiper/issues/7530) - **navigation:** prevent hide on click when clicking on navigation buttons ([c0f7bb6](https://github.com/nolimits4web/Swiper/commit/c0f7bb6f3612f42664eab60bbcfb7969bfeb16bf)), closes [#7559](https://github.com/nolimits4web/Swiper/issues/7559) - **vue:** avoid rendering same slide vnode twice for small amount of slides in loop + virtual mode ([#7556](https://github.com/nolimits4web/Swiper/issues/7556)) ([5737f03](https://github.com/nolimits4web/Swiper/commit/5737f03d202ce0f22d390b2e9f94b8573f59472b)) - **zoom:** fix issue when slide change possible during zoom out ([f67308c](https://github.com/nolimits4web/Swiper/commit/f67308c8f4367817d9c60980276c922af9fcb654)) # [11.1.3](https://github.com/nolimits4web/Swiper/compare/v11.1.2...v11.1.3) (2024-05-13) ### Bug Fixes - fix types import ([57923db](https://github.com/nolimits4web/Swiper/commit/57923db8f2cc7dae9f39011531147d68f26f834d)), closes [#7529](https://github.com/nolimits4web/Swiper/issues/7529) # [11.1.2](https://github.com/nolimits4web/Swiper/compare/v11.1.1...v11.1.2) (2024-05-13) ### Bug Fixes - **autoplay:** keep 0 transition on touchmove with 0 timeout delay ([8ccb08e](https://github.com/nolimits4web/Swiper/commit/8ccb08ef1c1adcb4ff53f91bf494a0bd221d5735)), closes [#7515](https://github.com/nolimits4web/Swiper/issues/7515) - **core:** centerInsuffientSlides takes offsets into account ([#7437](https://github.com/nolimits4web/Swiper/issues/7437)) ([5a271ff](https://github.com/nolimits4web/Swiper/commit/5a271ff050376820cfe42e04730cd097f38496a1)) - **types:** fixed SwiperModule, slideTo, effectInit types ([#7428](https://github.com/nolimits4web/Swiper/issues/7428)) ([bc61bce](https://github.com/nolimits4web/Swiper/commit/bc61bce297bce8c38299671e9fe18f6a5b0fd77e)) ### Performance Improvements - do not remove and re-add visibility classes for unchanged slides to prevents unnecessary style recalculations (This performance difference is mostly noticable when moving a slide with a mouse or touchmove because updateSlidesProgress is triggered for every keyboard-/touchevent) ([#7505](https://github.com/nolimits4web/Swiper/issues/7505)) ([2c08227](https://github.com/nolimits4web/Swiper/commit/2c08227d3e81db744e2d6e1c9d87c9ff6236533d)) # [11.1.1](https://github.com/nolimits4web/Swiper/compare/v11.1.0...v11.1.1) (2024-04-09) ### Bug Fixes - **zoom:** fix zoom pan not preventing slide changes using touch ([f73cc2a](https://github.com/nolimits4web/Swiper/commit/f73cc2a7a0bc4b9d094c33853e629785d1b5ddeb)), closes [#7308](https://github.com/nolimits4web/Swiper/issues/7308) # [11.1.0](https://github.com/nolimits4web/Swiper/compare/v11.0.7...v11.1.0) (2024-03-28) ### Bug Fixes - **a11y:** fixed issue with not working "enter" navigation on arrows ([aac2dcf](https://github.com/nolimits4web/Swiper/commit/aac2dcfc4cf6ddcef93933c75d3eab984f149a18)), closes [#7423](https://github.com/nolimits4web/Swiper/issues/7423) - **a11y:** prevent falsy focus handlers ([a7c260a](https://github.com/nolimits4web/Swiper/commit/a7c260a9d93405a0adce304b4ccf4f5309dfedda)), closes [#7406](https://github.com/nolimits4web/Swiper/issues/7406) - **core:** add/remove slide classes only when changed ([3312fba](https://github.com/nolimits4web/Swiper/commit/3312fba06de686e48cf0c138ea7bba20fc7842e5)), closes [#7356](https://github.com/nolimits4web/Swiper/issues/7356) - **core:** don't fix the loop on simple resize ([641793f](https://github.com/nolimits4web/Swiper/commit/641793ff459527c4c1efc9f72b447b87c8939f5f)), closes [#7325](https://github.com/nolimits4web/Swiper/issues/7325) - **core:** fixed thrown error on calling slideTo methods on destroyed swiper ([8c6a3c6](https://github.com/nolimits4web/Swiper/commit/8c6a3c6f63915db82415e2d7829ece6c624ace1e)), closes [#7416](https://github.com/nolimits4web/Swiper/issues/7416) - **core:** handle grabCursor within breakpoints ([e853908](https://github.com/nolimits4web/Swiper/commit/e853908c2ff93dc944ba045c9c0fce83efe46288)), closes [#7364](https://github.com/nolimits4web/Swiper/issues/7364) - **core:** reset animating flag on translateTo call ([7da50bf](https://github.com/nolimits4web/Swiper/commit/7da50bf1bcfe86f5af646e7d53c7d0414211da60)), closes [#7403](https://github.com/nolimits4web/Swiper/issues/7403) - **scrollbar:** fix warning ([e5371f7](https://github.com/nolimits4web/Swiper/commit/e5371f7656c4e828d87e7497edc73f9b6311f91d)), closes [#7415](https://github.com/nolimits4web/Swiper/issues/7415) - **thumbs:** fix thumbs .swiper type prop to accept string ([5b0fa84](https://github.com/nolimits4web/Swiper/commit/5b0fa84ac14022dd1ca39d387267a8d9125cb13a)), closes [#7421](https://github.com/nolimits4web/Swiper/issues/7421) - **virtual:** don't render first slides when initialSlide set ([bab9230](https://github.com/nolimits4web/Swiper/commit/bab92305c5a69b9b9f33615fa5df61fbfda39700)), closes [#7353](https://github.com/nolimits4web/Swiper/issues/7353) - **zoom:** fix panning on SVG elements ([eed8a5b](https://github.com/nolimits4web/Swiper/commit/eed8a5bf727163cf2da343490c09fc248b658e5e)), closes [#7352](https://github.com/nolimits4web/Swiper/issues/7352) # [11.0.7](https://github.com/nolimits4web/Swiper/compare/v11.0.6...v11.0.7) (2024-02-27) ### Bug Fixes - **core:** fix initial slide index shift with centeredSlides and slidesPerView auto ([#7319](https://github.com/nolimits4web/Swiper/issues/7319)) ([cae9c2d](https://github.com/nolimits4web/Swiper/commit/cae9c2dc192dafc0f35cb03e919dd1151dca88da)) - **history:** fix setting history in virtual slides ([d4de17b](https://github.com/nolimits4web/Swiper/commit/d4de17b278b57760cfc9953d1da7892b44f0fc22)), closes [#7327](https://github.com/nolimits4web/Swiper/issues/7327) - **react:** make sure the key is unique in virtual mode ([829a253](https://github.com/nolimits4web/Swiper/commit/829a2536b5d4f4ed86cb31d9c73d4d9b3be656c3)), closes [#7329](https://github.com/nolimits4web/Swiper/issues/7329) ### Features - add swiper-effect-utils ([df5f873](https://github.com/nolimits4web/Swiper/commit/df5f8731b795419534420bf4d567c75bfd475656)), closes [#7336](https://github.com/nolimits4web/Swiper/issues/7336) - **zoom:** add ability to constrain max zoom to 100% of original image size ([#7311](https://github.com/nolimits4web/Swiper/issues/7311)) ([645f266](https://github.com/nolimits4web/Swiper/commit/645f266c6b8dea5d43e14cace925ad86236ecc75)) # [11.0.6](https://github.com/nolimits4web/Swiper/compare/v11.0.5...v11.0.6) (2024-02-05) ### Bug Fixes - add optional swiperElementNodeName param to allow more flexible web component usage ([#7284](https://github.com/nolimits4web/Swiper/issues/7284)) ([178511f](https://github.com/nolimits4web/Swiper/commit/178511fe8ac37b590b92ff081379074e340436fa)) - fixed behavior where the combination of 'initialSlide:0' and 'slidesPerView:auto' would shift the first slide position ([e5c04c3](https://github.com/nolimits4web/Swiper/commit/e5c04c38e4261f4afb195bc15ea7417e7c1190dd)), closes [#7216](https://github.com/nolimits4web/Swiper/issues/7216) - Safari 3D fix for webview ([d42ce05](https://github.com/nolimits4web/Swiper/commit/d42ce05380c4a8f1848ad409b2ac8ed848723399)), closes [#7167](https://github.com/nolimits4web/Swiper/issues/7167) - **scrollbar:** correctly update scrollbar on changeDirection ([6bbb73d](https://github.com/nolimits4web/Swiper/commit/6bbb73d84f684e711250664d1eb7017b1b02ec3a)), closes [#7263](https://github.com/nolimits4web/Swiper/issues/7263) - SwiperOptions documentation for loopAddBlankSlides ([#7289](https://github.com/nolimits4web/Swiper/issues/7289)) ([cbc3dba](https://github.com/nolimits4web/Swiper/commit/cbc3dba7b4869c82f87c920e519f334fe9dc9880)) ### Features - **core:** prevent running .slideTo methods when Swiper is destroyed ([05f9c64](https://github.com/nolimits4web/Swiper/commit/05f9c6493081666f0458a67715271fc728827731)), closes [#7265](https://github.com/nolimits4web/Swiper/issues/7265) # [11.0.5](https://github.com/nolimits4web/Swiper/compare/v11.0.4...v11.0.5) (2023-11-22) ### Bug Fixes - **core:** swipe to last with slidesPerView: auto ([#7183](https://github.com/nolimits4web/Swiper/issues/7183)) ([2e3f47d](https://github.com/nolimits4web/Swiper/commit/2e3f47d5fcd37707be18180f35c9955c0bcc915e)) - **modules/a11y:** filter out falsy pagination elems ([#7201](https://github.com/nolimits4web/Swiper/issues/7201)) ([a044626](https://github.com/nolimits4web/Swiper/commit/a0446261bdbd9d538e31c02d3248d3bec0328c55)) ### Features - **core:** new `slidesUpdated` event ([8a0c7c4](https://github.com/nolimits4web/Swiper/commit/8a0c7c43ad5931d22a20a9b33152f6befc5bcb59)) # [11.0.4](https://github.com/nolimits4web/Swiper/compare/v11.0.3...v11.0.4) (2023-11-09) ### Bug Fixes - **effectx:** fix Safari issue with rotates even to 90deg ([e005b69](https://github.com/nolimits4web/Swiper/commit/e005b699e7dbd7c343a56f19fca384b28c37cb97)), closes [#7167](https://github.com/nolimits4web/Swiper/issues/7167) # [11.0.3](https://github.com/nolimits4web/Swiper/compare/v11.0.2...v11.0.3) (2023-10-26) ### Bug Fixes - **core:** fixed legacy condition preventing touch move when zoom enabled ([2f64043](https://github.com/nolimits4web/Swiper/commit/2f64043bc2abfe13a6b3a2a24b082c9627c20ee7)), closes [#7137](https://github.com/nolimits4web/Swiper/issues/7137) - **core:** prevent observer updates on loop fix ([7a5eacc](https://github.com/nolimits4web/Swiper/commit/7a5eaccb5e6ee1a867a3c2f30e9a44400e6c341c)), closes [#7135](https://github.com/nolimits4web/Swiper/issues/7135) # [11.0.2](https://github.com/nolimits4web/Swiper/compare/v11.0.1...v11.0.2) (2023-10-25) ### Bug Fixes - **core:** correctly handle loopAdditionalSlides parameter ([3f5e05d](https://github.com/nolimits4web/Swiper/commit/3f5e05d59a776e2cfc3a709e4b230ff23191266c)) # [11.0.1](https://github.com/nolimits4web/Swiper/compare/v11.0.0...v11.0.1) (2023-10-24) ### Bug Fixes - **types:** fix eventsPrefix type ([fd0f601](https://github.com/nolimits4web/Swiper/commit/fd0f601fb08af7a72d3c582cf88e06e6a8bed9f8)) # [11.0.0](https://github.com/nolimits4web/Swiper/compare/v10.3.1...v11.0.0) (2023-09-24) ### Bug Fixes - **autoplay:** fix negative autoplay values after stop/start, fix autoplay with free mode ([8bef84d](https://github.com/nolimits4web/Swiper/commit/8bef84d68c44b93757585cb037f7448cc99f7c71)), closes [#7084](https://github.com/nolimits4web/Swiper/issues/7084) - **autoplay:** fix pauseOnPointerEnter if hovered during transition ([5080d95](https://github.com/nolimits4web/Swiper/commit/5080d9569fc1ac77c0cb01812567f8035cc780bb)), closes [#7107](https://github.com/nolimits4web/Swiper/issues/7107) - **core:** remove grid class on rows change ([2f65e89](https://github.com/nolimits4web/Swiper/commit/2f65e89cb5758b44ceced212d4be905e0ed0f4c3)), closes [#7053](https://github.com/nolimits4web/Swiper/issues/7053) - **element:** correctly respond to object params assignment ([f23c742](https://github.com/nolimits4web/Swiper/commit/f23c74250c7d4fd56ea4d5abfe2e031504c86d91)) - **scrollbar:** allow multiple classes in scrollbar parameters ([89a6f71](https://github.com/nolimits4web/Swiper/commit/89a6f7192f7da120487c224dc1b1a7668422321a)), closes [#7096](https://github.com/nolimits4web/Swiper/issues/7096) ### Features - **core:** add fully visible slides classes ([902a4c4](https://github.com/nolimits4web/Swiper/commit/902a4c4adbff3af1188427d6cfef50d537c1bcef)), closes [#6773](https://github.com/nolimits4web/Swiper/issues/6773) - **core:** add handling for native touch events ([74bb1cc](https://github.com/nolimits4web/Swiper/commit/74bb1ccf4c6f31ffdb7419d5a58a4f592aa6006b)), closes [#6478](https://github.com/nolimits4web/Swiper/issues/6478) [#6381](https://github.com/nolimits4web/Swiper/issues/6381) [#6897](https://github.com/nolimits4web/Swiper/issues/6897) - **core:** loop support for grid, new `loopAddBlankSlides` parameter ([b5db223](https://github.com/nolimits4web/Swiper/commit/b5db22392f6c6609de292c00d163f9230d0925ee)) - **core:** remove `loopedSlides` parameter, add `loopAdditionalSlides` parameter ([d647985](https://github.com/nolimits4web/Swiper/commit/d647985faa9c8e3d551270e96692b2bd135bc92a)) - **core:** reworked loop mode ([2a99dbd](https://github.com/nolimits4web/Swiper/commit/2a99dbd383ca6880dd60ce35c70d7337bb1f38c8)) - **core:** update loop mode logic and lowered requirements ([703ede6](https://github.com/nolimits4web/Swiper/commit/703ede6ea1e490ee0200edf18c6857f534101827)) - **element:** make`eventPrefix` parameter default to `swiper` ([88d463a](https://github.com/nolimits4web/Swiper/commit/88d463aef9b74e6de637470c4c7c024e5f3ca6b9)) - **core:** move default container overflow back to `hidden` ([88941a8](https://github.com/nolimits4web/Swiper/commit/88941a82491b289faed24508db8b90e3c5506ba1)) # [10.3.1](https://github.com/nolimits4web/Swiper/compare/v10.3.0...v10.3.1) (2023-09-28) ### Bug Fixes - **autoplay:** fix autoplay stop when disableOnInteraction is active ([ecfb3fb](https://github.com/nolimits4web/Swiper/commit/ecfb3fb06b7214e87332e41a6b96dc7a721a8313)), closes [#7060](https://github.com/nolimits4web/Swiper/issues/7060) [#7059](https://github.com/nolimits4web/Swiper/issues/7059) - **types:** detection of custom html tags ([#7055](https://github.com/nolimits4web/Swiper/issues/7055)) ([c55f76d](https://github.com/nolimits4web/Swiper/commit/c55f76d51cadb3b9835b6c45537da30d2c3b298e)) # [10.3.0](https://github.com/nolimits4web/Swiper/compare/v10.2.0...v10.3.0) (2023-09-21) ### Bug Fixes - **core:** correctly destroyor create loop on breakpoints ([12a44fb](https://github.com/nolimits4web/Swiper/commit/12a44fb995313c58cde37395a654493e2d42c694)), closes [#6967](https://github.com/nolimits4web/Swiper/issues/6967) - **core:** don't call `realIndexChange` on initialIndex if runCallbacksOnInit is disalbed ([48c4e0a](https://github.com/nolimits4web/Swiper/commit/48c4e0a76736ac124a0d6e878f840208f31b8b09)), closes [#6976](https://github.com/nolimits4web/Swiper/issues/6976) - **core:** fix `slideToClickedSlide` when using Element slide slots ([af0519c](https://github.com/nolimits4web/Swiper/commit/af0519c22513ec4a7bad2c80896d421fe07012f8)), closes [#6958](https://github.com/nolimits4web/Swiper/issues/6958) - **core:** fix lazy preloader in later initialized slides ([e4fddc0](https://github.com/nolimits4web/Swiper/commit/e4fddc076f69bdf267559c70a489474bc311c02c)), closes [#6946](https://github.com/nolimits4web/Swiper/issues/6946) - **core:** fix loop on centeredSlides slide to beginning ([c496835](https://github.com/nolimits4web/Swiper/commit/c496835a2ce49065a9b282bbafe5629797bb3da4)), closes [#7011](https://github.com/nolimits4web/Swiper/issues/7011) - **core:** fix loopFix in loop and cssMode ([8180a52](https://github.com/nolimits4web/Swiper/commit/8180a52f386301147dae286994119693f6231202)), closes [#6919](https://github.com/nolimits4web/Swiper/issues/6919) - **core:** fixed ignored allowSlidePrev/Next in loop mode ([1b74619](https://github.com/nolimits4web/Swiper/commit/1b74619f5d74073cf387201c5715c29c8115f770)), closes [#6987](https://github.com/nolimits4web/Swiper/issues/6987) - **core:** remove grid class on rows change ([908becc](https://github.com/nolimits4web/Swiper/commit/908becc6ffbf5b5a050e2fde522867289073cf4a)), closes [#7053](https://github.com/nolimits4web/Swiper/issues/7053) - **element:** correctly respond to object params assignment ([2ef1ff5](https://github.com/nolimits4web/Swiper/commit/2ef1ff5d42cea985556c54f79b8a52305294d21e)) - **element:** do not bubble `hashchange` event ([106a3d7](https://github.com/nolimits4web/Swiper/commit/106a3d7da4967bbf1ba705c1bb8a0547307bb2f9)), closes [#6943](https://github.com/nolimits4web/Swiper/issues/6943) - **element:** fix issue updating with boolean module params ([1cc359e](https://github.com/nolimits4web/Swiper/commit/1cc359e45d637c209ce97ebffb917ae8587fdcf2)), closes [#6947](https://github.com/nolimits4web/Swiper/issues/6947) - **navigation:** fix lock class on enable ([ea39c33](https://github.com/nolimits4web/Swiper/commit/ea39c3353ccd74d37444692cf01f38724aaff6b7)), closes [#7009](https://github.com/nolimits4web/Swiper/issues/7009) - **react:** add breakpointsBase param ([0eb4122](https://github.com/nolimits4web/Swiper/commit/0eb4122e45558efe9209f3b72ae9c30524424183)), closes [#7014](https://github.com/nolimits4web/Swiper/issues/7014) - **react:** fix react components props type ([1cd412e](https://github.com/nolimits4web/Swiper/commit/1cd412ecf22a38ff6c3c63bb7dc49fc1f92ca16f)), closes [#7000](https://github.com/nolimits4web/Swiper/issues/7000) - **scrollbar:** add 'touch-action: none' to swiper-scrollbar ([#7024](https://github.com/nolimits4web/Swiper/issues/7024)) ([9542d09](https://github.com/nolimits4web/Swiper/commit/9542d094faa61d9a4837a6ec40c331e8172324ab)) - **virtual:** fix issue with loop mode and initialSlide enabled ([f4afd9d](https://github.com/nolimits4web/Swiper/commit/f4afd9d0fec3be48c17ab75c462c23e69e98a571)), closes [#6945](https://github.com/nolimits4web/Swiper/issues/6945) - **virtual:** fix removing nested slides ([c3321e1](https://github.com/nolimits4web/Swiper/commit/c3321e1645015e2ea053c72edbe60b6351030bcb)), closes [#7005](https://github.com/nolimits4web/Swiper/issues/7005) - **virtual:** recalc cache on removeSlide ([96e5166](https://github.com/nolimits4web/Swiper/commit/96e5166c9e01a8ca3e18ee236adee0566ad0969e)), closes [#7020](https://github.com/nolimits4web/Swiper/issues/7020) - **vue:** add breakpointsBase param ([6800dbb](https://github.com/nolimits4web/Swiper/commit/6800dbba2f006d1ff2206cd7ac2068cfc6429089)) - **zoom:** fix scale origin when document is scrolled ([2cf3fc2](https://github.com/nolimits4web/Swiper/commit/2cf3fc26c2a2d9e5dc29b02427c73e8559ab896d)), closes [#6950](https://github.com/nolimits4web/Swiper/issues/6950) [#6955](https://github.com/nolimits4web/Swiper/issues/6955) ### Features - **core:** allow createElements to process object params with `{enabled: true}` ([abf8405](https://github.com/nolimits4web/Swiper/commit/abf840506aa4c4f2c60cadb9dcdd4223fa071352)) - **core:** make slidesPerViewDynamic public ([ae434b0](https://github.com/nolimits4web/Swiper/commit/ae434b0a5d76fd0d2659cd2dffc27d19c1f14a9e)), closes [#7036](https://github.com/nolimits4web/Swiper/issues/7036) # [10.2.0](https://github.com/nolimits4web/Swiper/compare/v10.1.0...v10.2.0) (2023-08-17) ### Bug Fixes - **autoplay:** fix autoplay pause during transition ([db9b17f](https://github.com/nolimits4web/Swiper/commit/db9b17ffc627bafaa912b31e6336b4c366be3021)), closes [#6896](https://github.com/nolimits4web/Swiper/issues/6896) - **controller:** fix issues with loop mode ([fbb84fe](https://github.com/nolimits4web/Swiper/commit/fbb84fed425f8341c0bd927e5a658962b130abdf)), closes [#6659](https://github.com/nolimits4web/Swiper/issues/6659) - **core:** fix touch move and loop behavior when transition-delay enabled on swiper-wrapper ([ac27d02](https://github.com/nolimits4web/Swiper/commit/ac27d0204dfa3e6cc2059e19d5f6ff836d77c4d0)) - **core:** handle `contextmenu` event ([721ccaf](https://github.com/nolimits4web/Swiper/commit/721ccaf292de538e479c33668abf30ae86278d2b)), closes [#6692](https://github.com/nolimits4web/Swiper/issues/6692) - **element:** fix missing elements `part` when added dynamically ([db5b5d6](https://github.com/nolimits4web/Swiper/commit/db5b5d6c337b4e8615b7e6ec2acb1694213a2311)), closes [#6899](https://github.com/nolimits4web/Swiper/issues/6899) - **element:** fix parallax on elements passed to component root ([265e466](https://github.com/nolimits4web/Swiper/commit/265e466a043efc750964c397d262736923e58040)) - **element:** fixed issue with incorrect lookup for lazy prelader and images ([64513ac](https://github.com/nolimits4web/Swiper/commit/64513ac899c843b745e33813126ab6c52af650a4)), closes [#6901](https://github.com/nolimits4web/Swiper/issues/6901) - **element:** fixed issue with pointer-events:none in fade effect ([2dcb802](https://github.com/nolimits4web/Swiper/commit/2dcb802ea5192c1f4b47fed4b20242983e06c6cb)), closes [#6908](https://github.com/nolimits4web/Swiper/issues/6908) - **pagination:** fixed issue in loop mode when sometimes it switches slides with transiton ([3d7dc58](https://github.com/nolimits4web/Swiper/commit/3d7dc5834f12a8ed518c9a79a58fe13777f9298f)), closes [#6856](https://github.com/nolimits4web/Swiper/issues/6856) ### Features - **pagination:** allow multiple `clickableClass` ([703d13b](https://github.com/nolimits4web/Swiper/commit/703d13b1aeb234d0f72ab0a61572af6a8bb94ab7)), closes [#6741](https://github.com/nolimits4web/Swiper/issues/6741) # [10.1.0](https://github.com/nolimits4web/Swiper/compare/v10.0.4...v10.1.0) (2023-08-01) ### Bug Fixes - **core:** fix flickering in loop mode edge positions ([bf29843](https://github.com/nolimits4web/Swiper/commit/bf298437039168611853cc7b7f92624f5f2726c6)), closes [#6673](https://github.com/nolimits4web/Swiper/issues/6673) - **core:** set `isElement` on swiper-containers only ([#6870](https://github.com/nolimits4web/Swiper/issues/6870)) ([2f61fda](https://github.com/nolimits4web/Swiper/commit/2f61fda721232f78b1b8ed16f57953442d70384b)) - **package:** fix source maps for ES modules ([#6866](https://github.com/nolimits4web/Swiper/issues/6866)) ([5e88c4b](https://github.com/nolimits4web/Swiper/commit/5e88c4b2a88fb2158bc89ebb3f30ad738078530a)), closes [#6863](https://github.com/nolimits4web/Swiper/issues/6863) - **modules:** filter out falsy elements ([#6823](https://github.com/nolimits4web/Swiper/issues/6823)) ([e1b7254](https://github.com/nolimits4web/Swiper/commit/e1b725476eb90e2bbf719a344e57a92530f779ce)) - **mousewheel:** fix `releaseOnEdges` for freeMode ([8a83360](https://github.com/nolimits4web/Swiper/commit/8a83360ea780d63dd5a95d9f62a364c5f4ab0f3f)), closes [#6770](https://github.com/nolimits4web/Swiper/issues/6770) [#6799](https://github.com/nolimits4web/Swiper/issues/6799) - **zoom:** fix image move in element ([89d9aa5](https://github.com/nolimits4web/Swiper/commit/89d9aa57531c4675611842378519025a926de34f)), closes [#6847](https://github.com/nolimits4web/Swiper/issues/6847) ### Features - **element:** support slides as slots ([697b028](https://github.com/nolimits4web/Swiper/commit/697b02843f5feaf1968aefa07ec7e389dcd478cc)) - **types:** make VirtualOptions generic ([#6852](https://github.com/nolimits4web/Swiper/issues/6852)) ([068ee68](https://github.com/nolimits4web/Swiper/commit/068ee68c55d8f62167af015fc2d57db8af439003)) # [10.0.4](https://github.com/nolimits4web/Swiper/compare/v10.0.3...v10.0.4) (2023-07-08) ### Bug Fixes - **element:** fix in Safari < 16.4 ([e53fc07](https://github.com/nolimits4web/Swiper/commit/e53fc07dffca2a2ef3e929e120d4af6b41540cd7)), closes [#6831](https://github.com/nolimits4web/Swiper/issues/6831) # [v10.0.3](https://github.com/nolimits4web/Swiper/compare/v10.0.2...v10.0.3) (2023-07-03) ### Features - added overflow:hidden for fallback if clip is not supported in target… ([#6807](https://github.com/nolimits4web/Swiper/issues/6807)) ([5d8d6f9](https://github.com/nolimits4web/Swiper/commit/5d8d6f933146c8c80792c4892c54cb769b21a005)) - **element:** use usual ` ================================================ FILE: demos/020-navigation.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/030-pagination.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/040-pagination-dynamic.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/050-pagination-progress.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/060-pagination-fraction.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/070-pagination-custom.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/080-scrollbar.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/090-vertical.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/100-space-between.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/110-slides-per-view.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/120-slides-per-view-auto.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/125-snap-to-slide-edge.html ================================================ Swiper demo - Snap To Slide Edge

Default behavior (snapToSlideEdge: false)

Notice how at the end, the last slide aligns to the right edge

Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8

With snapToSlideEdge: true

Slides always stay aligned to their original position

Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
================================================ FILE: demos/130-centered.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/140-centered-auto.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/145-css-mode.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/150-freemode.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/160-scroll-container.html ================================================ Swiper demo

Scroll Container

Lorem ipsum dolor sit amet, consectetur adipiscing elit. In luctus, ex eu sagittis faucibus, ligula ipsum sagittis magna, et imperdiet dolor lectus eu libero. Vestibulum venenatis eget turpis sed faucibus. Maecenas in ullamcorper orci, eu ullamcorper sem. Etiam elit ante, luctus non ante sit amet, sodales vulputate odio. Aenean tristique nisl tellus, sit amet fringilla nisl volutpat cursus. Quisque dignissim lectus ac nunc consectetur mattis. Proin vel hendrerit ipsum, et lobortis dolor. Vestibulum convallis, nibh et tincidunt tristique, nisl risus facilisis lectus, ut interdum orci nisl ac nunc. Cras et aliquam felis. Quisque vel ipsum at elit sodales posuere eget non est. Fusce convallis vestibulum dolor non volutpat. Vivamus vestibulum quam ut ultricies pretium.

Suspendisse rhoncus fringilla nisl. Mauris eget lorem ac urna consectetur convallis non vel mi. Donec libero dolor, volutpat ut urna sit amet, aliquet molestie purus. Phasellus faucibus, leo vel scelerisque lobortis, ipsum leo sollicitudin metus, eget sagittis ante orci eu ipsum. Nulla ac mauris eu risus sagittis scelerisque iaculis bibendum mauris. Cras ut egestas orci. Cras odio risus, sagittis ut nunc vitae, aliquam consectetur purus. Vivamus ornare nunc vel tellus facilisis, quis dictum elit tincidunt. Donec accumsan nisi at laoreet sodales. Cras at ullamcorper massa. Maecenas at facilisis ex. Nam mollis dignissim purus id efficitur.

Curabitur eget aliquam erat. Curabitur a neque vitae purus volutpat elementum. Vivamus quis vestibulum leo, efficitur ullamcorper velit. Integer tincidunt finibus metus vel porta. Mauris sed mauris congue, pretium est nec, malesuada purus. Nulla hendrerit consectetur arcu et lacinia. Suspendisse augue justo, convallis eget arcu in, pretium tempor ligula. Nullam vulputate tincidunt est ut ullamcorper.

Curabitur sed sodales leo. Nulla facilisi. Etiam condimentum, nisi id tempor vulputate, nisi justo cursus justo, pellentesque condimentum diam arcu sit amet leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In placerat tellus a posuere vehicula. Donec diam massa, efficitur vitae mattis et, pretium in augue. Fusce iaculis mi quis ante venenatis, sit amet pellentesque orci aliquam. Vestibulum elementum posuere vehicula.

Sed tincidunt diam a massa pharetra faucibus. Praesent condimentum id arcu nec fringilla. Maecenas faucibus, ante et venenatis interdum, erat mi eleifend dui, at convallis nisl est nec arcu. Duis vitae arcu rhoncus, faucibus magna ut, tempus metus. Cras in nibh sed ipsum consequat rhoncus. Proin fringilla nulla ut augue tempor fermentum. Nunc hendrerit non nisi vitae finibus. Donec eget ornare libero. Aliquam auctor erat enim, a semper risus semper at. In ut dui in metus tincidunt euismod eget et lacus. Aenean et dictum urna, sed rhoncus lorem. Duis pharetra sagittis odio. Etiam a libero ut nisi feugiat tincidunt vel vitae turpis. Maecenas vel orci sit amet lorem hendrerit venenatis sollicitudin ut dui. Quisque rhoncus nibh in massa pretium scelerisque.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. In luctus, ex eu sagittis faucibus, ligula ipsum sagittis magna, et imperdiet dolor lectus eu libero. Vestibulum venenatis eget turpis sed faucibus. Maecenas in ullamcorper orci, eu ullamcorper sem. Etiam elit ante, luctus non ante sit amet, sodales vulputate odio. Aenean tristique nisl tellus, sit amet fringilla nisl volutpat cursus. Quisque dignissim lectus ac nunc consectetur mattis. Proin vel hendrerit ipsum, et lobortis dolor. Vestibulum convallis, nibh et tincidunt tristique, nisl risus facilisis lectus, ut interdum orci nisl ac nunc. Cras et aliquam felis. Quisque vel ipsum at elit sodales posuere eget non est. Fusce convallis vestibulum dolor non volutpat. Vivamus vestibulum quam ut ultricies pretium.

Suspendisse rhoncus fringilla nisl. Mauris eget lorem ac urna consectetur convallis non vel mi. Donec libero dolor, volutpat ut urna sit amet, aliquet molestie purus. Phasellus faucibus, leo vel scelerisque lobortis, ipsum leo sollicitudin metus, eget sagittis ante orci eu ipsum. Nulla ac mauris eu risus sagittis scelerisque iaculis bibendum mauris. Cras ut egestas orci. Cras odio risus, sagittis ut nunc vitae, aliquam consectetur purus. Vivamus ornare nunc vel tellus facilisis, quis dictum elit tincidunt. Donec accumsan nisi at laoreet sodales. Cras at ullamcorper massa. Maecenas at facilisis ex. Nam mollis dignissim purus id efficitur.

Curabitur eget aliquam erat. Curabitur a neque vitae purus volutpat elementum. Vivamus quis vestibulum leo, efficitur ullamcorper velit. Integer tincidunt finibus metus vel porta. Mauris sed mauris congue, pretium est nec, malesuada purus. Nulla hendrerit consectetur arcu et lacinia. Suspendisse augue justo, convallis eget arcu in, pretium tempor ligula. Nullam vulputate tincidunt est ut ullamcorper.

Curabitur sed sodales leo. Nulla facilisi. Etiam condimentum, nisi id tempor vulputate, nisi justo cursus justo, pellentesque condimentum diam arcu sit amet leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In placerat tellus a posuere vehicula. Donec diam massa, efficitur vitae mattis et, pretium in augue. Fusce iaculis mi quis ante venenatis, sit amet pellentesque orci aliquam. Vestibulum elementum posuere vehicula.

Sed tincidunt diam a massa pharetra faucibus. Praesent condimentum id arcu nec fringilla. Maecenas faucibus, ante et venenatis interdum, erat mi eleifend dui, at convallis nisl est nec arcu. Duis vitae arcu rhoncus, faucibus magna ut, tempus metus. Cras in nibh sed ipsum consequat rhoncus. Proin fringilla nulla ut augue tempor fermentum. Nunc hendrerit non nisi vitae finibus. Donec eget ornare libero. Aliquam auctor erat enim, a semper risus semper at. In ut dui in metus tincidunt euismod eget et lacus. Aenean et dictum urna, sed rhoncus lorem. Duis pharetra sagittis odio. Etiam a libero ut nisi feugiat tincidunt vel vitae turpis. Maecenas vel orci sit amet lorem hendrerit venenatis sollicitudin ut dui. Quisque rhoncus nibh in massa pretium scelerisque.

================================================ FILE: demos/170-slides-per-column.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/180-nested.html ================================================ Swiper demo
Horizontal Slide 1
Vertical Slide 1
Vertical Slide 2
Vertical Slide 3
Vertical Slide 4
Vertical Slide 5
Horizontal Slide 3
Horizontal Slide 4
================================================ FILE: demos/190-grab-cursor.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/200-infinite-loop.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/210-infinite-loop-with-slides-per-group.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/211-slides-per-group-skip.html ================================================ Swiper demo
================================================ FILE: demos/220-effect-fade.html ================================================ Swiper demo
================================================ FILE: demos/230-effect-cube.html ================================================ Swiper demo
================================================ FILE: demos/240-effect-coverflow.html ================================================ Swiper demo
================================================ FILE: demos/250-effect-flip.html ================================================ Swiper demo
================================================ FILE: demos/255-effect-cards.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/257-effect-creative.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/260-keyboard-control.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/270-mousewheel-control.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/280-autoplay.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/290-dynamic-slides.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4

Prepend 2 Slides Prepend Slide Append Slide Append 2 Slides

================================================ FILE: demos/300-thumbs-gallery.html ================================================ Swiper demo ================================================ FILE: demos/310-thumbs-gallery-loop.html ================================================ Swiper demo ================================================ FILE: demos/320-multiple-swipers.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/330-hash-navigation.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/340-history.html ================================================ Swiper Playground
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/350-rtl.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/360-parallax.html ================================================ Swiper demo
Slide 1
Subtitle

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dictum mattis velit, sit amet faucibus felis iaculis nec. Nulla laoreet justo vitae porttitor porttitor. Suspendisse in sem justo. Integer laoreet magna nec elit suscipit, ac laoreet nibh euismod. Aliquam hendrerit lorem at elit facilisis rutrum. Ut at ullamcorper velit. Nulla ligula nisi, imperdiet ut lacinia nec, tincidunt ut libero. Aenean feugiat non eros quis feugiat.

Slide 2
Subtitle

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dictum mattis velit, sit amet faucibus felis iaculis nec. Nulla laoreet justo vitae porttitor porttitor. Suspendisse in sem justo. Integer laoreet magna nec elit suscipit, ac laoreet nibh euismod. Aliquam hendrerit lorem at elit facilisis rutrum. Ut at ullamcorper velit. Nulla ligula nisi, imperdiet ut lacinia nec, tincidunt ut libero. Aenean feugiat non eros quis feugiat.

Slide 3
Subtitle

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam dictum mattis velit, sit amet faucibus felis iaculis nec. Nulla laoreet justo vitae porttitor porttitor. Suspendisse in sem justo. Integer laoreet magna nec elit suscipit, ac laoreet nibh euismod. Aliquam hendrerit lorem at elit facilisis rutrum. Ut at ullamcorper velit. Nulla ligula nisi, imperdiet ut lacinia nec, tincidunt ut libero. Aenean feugiat non eros quis feugiat.

================================================ FILE: demos/370-lazy-load-images.html ================================================ Swiper demo
================================================ FILE: demos/380-responsive-breakpoints.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/381-ratio-breakpoints.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/390-autoheight.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/400-zoom.html ================================================ Swiper demo
================================================ FILE: demos/410-virtual-slides.html ================================================ Swiper demo

Prepend 2 Slides Slide 1 Slide 250 Slide 500 Append Slide

================================================ FILE: demos/420-custom-plugin.html ================================================ Swiper demo
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
================================================ FILE: demos/430-slideable-menu.html ================================================ Swiper demo
Content slide
================================================ FILE: demos/440-change-direction.html ================================================ Swiper demo
Resize me!
Resize me!
Resize me!
Resize me!
Resize me!
Resize me!
Resize me!
Resize me!
Resize me!
Resize me!
================================================ FILE: demos/450-watchSlidesVisibility.html ================================================ Swiper demo

Slider5 is visible when you slide to 2,3, or 4, and slider5 has "swiper-slide-visible" className


slider1
slider2
slider3
slider4
slider5
slider6
slider7
slider8
================================================ FILE: demos/index.html ================================================ Swiper demos

Swiper demos

================================================ FILE: demos/vite.config.js ================================================ import path from 'path'; export default { server: { host: '0.0.0.0', }, resolve: { alias: { swiper: path.resolve(__dirname, '../dist/'), }, }, }; ================================================ FILE: package.json ================================================ { "name": "swiper-src", "version": "12.1.2", "description": "Most modern mobile touch slider and framework with hardware accelerated transitions", "type": "module", "scripts": { "build": "node scripts/build", "build:prod": "node scripts/build --prod", "build-icons-font": "python ./scripts/icon-font/generate.py", "watch": "node scripts/watch", "demos": "npm run build && concurrently --kill-others \"vite ./demos/\" \"npm run watch\" ", "core": "npm run build && concurrently --kill-others \"vite ./playground/core\" \"npm run watch\" ", "element": "npm run build && concurrently --kill-others \"vite ./playground/element\" \"npm run watch\" ", "react": "npm run build && concurrently --kill-others \"vite ./playground/react\" \"npm run watch\"", "vue": "npm run build && concurrently --kill-others \"vite ./playground/vue\" \"npm run watch\"", "prettier": "prettier \"**/*.+(js|json|scss|css|less|ts|jsx|mjs)\"", "format": "npm run prettier -- --write", "check-format": "npm run prettier -- --list-different", "lint": "eslint --ext .js,.jsx .", "validate": "npm-run-all --parallel check-format lint", "release": "npm run validate && node ./scripts/release", "test": "npm run validate && npm run build:prod", "changelog": "npx conventional-changelog -p angular -i CHANGELOG.md -u -s", "build-sponsors": "node scripts/build-sponsors.js" }, "repository": { "type": "git", "url": "https://github.com/nolimits4web/Swiper.git" }, "keywords": [ "swiper", "swipe", "slider", "touch", "ios", "mobile", "cordova", "phonegap", "app", "framework", "framework7", "carousel", "gallery", "plugin", "vue", "slideshow" ], "author": "Vladimir Kharlampidi", "license": "MIT", "bugs": { "url": "https://github.com/nolimits4web/swiper/issues" }, "homepage": "https://swiperjs.com", "funding": [ { "type": "patreon", "url": "https://www.patreon.com/vladimirkharlampidi" }, { "type": "open_collective", "url": "http://opencollective.com/swiper" } ], "engines": { "node": ">= 4.7.0" }, "devDependencies": { "@babel/cli": "^7.23.0", "@babel/core": "^7.23.2", "@babel/preset-env": "^7.23.2", "@babel/preset-react": "^7.22.15", "@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^5.0.2", "@types/react": "^18.2.31", "@vitejs/plugin-vue": "^4.4.0", "autoprefixer": "^10.4.21", "chalk": "^5.3.0", "clean-css": "^5.3.3", "concurrently": "^8.2.2", "conventional-changelog-cli": "^2.2.2", "cross-env": "^7.0.3", "cssnano": "^7.1.1", "elapsed-time-logger": "^1.1.7", "eslint": "^8.52.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-import": "^2.29.0", "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "exec-sh": "^0.4.0", "fs-extra": "^11.1.1", "globby": "^13.2.2", "inquirer": "^9.2.11", "less": "^4.2.0", "npm-run-all": "^4.1.5", "postcss": "^8.5.6", "postcss-nested": "^7.0.2", "prettier": "^3.0.3", "react": "^18.2.0", "react-dom": "^18.2.0", "rimraf": "^5.0.5", "rollup": "^4.1.4", "ssr-window": "^5.0.1", "terser": "^5.44.0", "vite": "^4.5.0", "vue": "^3.3.6" } } ================================================ FILE: playground/core/index.html ================================================ Swiper Playground
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
================================================ FILE: playground/core/vite.config.js ================================================ import path from 'path'; export default { server: { host: '0.0.0.0', }, resolve: { alias: { swiper: path.resolve(__dirname, '../../dist/'), }, }, }; ================================================ FILE: playground/element/index.html ================================================ Swiper Playground Slide 1 Slide 2 Slide 3 Slide 4 Slide 5 ================================================ FILE: playground/element/vite.config.js ================================================ import path from 'path'; export default { server: { host: '0.0.0.0', }, resolve: { alias: { 'swiper/element/bundle': path.resolve(__dirname, '../../dist/swiper-element-bundle.mjs'), 'swiper/element': path.resolve(__dirname, '../../dist/swiper-element.mjs'), 'swiper/bundle': path.resolve(__dirname, '../../dist/swiper-bundle.mjs'), swiper: path.resolve(__dirname, '../../dist/'), }, }, }; ================================================ FILE: playground/react/App.jsx ================================================ /* eslint-disable no-restricted-globals */ import React from 'react'; // eslint-disable-next-line import { A11y, Navigation, Pagination, Scrollbar, Mousewheel } from 'swiper/modules'; // eslint-disable-next-line import { Swiper, SwiperSlide } from 'swiper/swiper-react'; const App = () => { return (
(window.swiper = swiper)} slidesPerView={3.3} threshold={2} spaceBetween={10} navigation={true} scrollbar mousewheel={{ forceToAxis: true, sensitivity: 0.1, releaseOnEdges: true }} pagination={{ clickable: true }} > Slide 1 Slide 2 Slide 3 Slide 4 Slide 5
); }; export default App; ================================================ FILE: playground/react/index.html ================================================ Swiper React
================================================ FILE: playground/react/main.js ================================================ import React from 'react'; import { createRoot } from 'react-dom/client'; // eslint-disable-next-line import 'swiper/swiper-bundle.css'; import App from './App.jsx'; // eslint-disable-next-line const root = createRoot(document.getElementById('app')); root.render(React.createElement(App)); ================================================ FILE: playground/react/vite.config.js ================================================ import path from 'path'; export default { resolve: { alias: { swiper: path.resolve(__dirname, '../../dist/'), }, }, }; ================================================ FILE: playground/vue/App.vue ================================================ ================================================ FILE: playground/vue/index.html ================================================ Swiper Vue
================================================ FILE: playground/vue/innerComp.vue ================================================ ================================================ FILE: playground/vue/main.js ================================================ // eslint-disable-next-line import 'swiper/swiper-bundle.css'; import { createApp } from 'vue'; import App from './App.vue'; const app = createApp(App); app.mount('#app'); ================================================ FILE: playground/vue/vite.config.js ================================================ import path from 'path'; import vue from '@vitejs/plugin-vue'; export default { plugins: [vue()], resolve: { alias: { swiper: path.resolve(__dirname, '../../dist/'), }, }, }; ================================================ FILE: scripts/build-config.js ================================================ import { parseSwiperBuildModulesEnv } from './utils/helper.js'; const envBuildModules = parseSwiperBuildModulesEnv(); export const modules = envBuildModules || [ 'virtual', 'keyboard', 'mousewheel', 'navigation', 'pagination', 'scrollbar', 'parallax', 'zoom', 'controller', 'a11y', 'history', 'hash-navigation', 'autoplay', 'thumbs', 'free-mode', 'grid', 'manipulation', 'effect-fade', 'effect-cube', 'effect-flip', 'effect-coverflow', 'effect-creative', 'effect-cards', ]; export default { modules, }; ================================================ FILE: scripts/build-modules.js ================================================ /* eslint-disable no-shadow */ import fs from 'fs'; import { rollup } from 'rollup'; import { nodeResolve } from '@rollup/plugin-node-resolve'; import replace from '@rollup/plugin-replace'; import { babel } from '@rollup/plugin-babel'; import elapsed from 'elapsed-time-logger'; import chalk from 'chalk'; import getElementStyles from './utils/get-element-styles.js'; import { modules as configModules } from './build-config.js'; import { capitalizeString } from './utils/helper.js'; import minify from './utils/minify.js'; import { banner } from './utils/banner.js'; import isProd from './utils/isProd.js'; export default async function buildModules() { elapsed.start('modules'); const modules = []; configModules.forEach((name) => { const capitalized = capitalizeString(name); const jsFilePath = `./src/modules/${name}/${name}.mjs`; if (fs.existsSync(jsFilePath)) { modules.push({ name, capitalized }); } }); // eslint-disable-next-line const modulesPaths = configModules.map((name) => { return `./src/modules/${name}/${name}.mjs`; }); // Create element bundle const coreElementContent = fs .readFileSync('./src/swiper-element.mjs', 'utf-8') .replace(`import Swiper from './swiper.mjs';`, `import Swiper from './swiper-bundle.mjs';`); fs.writeFileSync('./src/swiper-element-bundle.mjs', coreElementContent); const output = await rollup({ external: ['react', 'vue'], input: [ './src/swiper.mjs', './src/swiper-bundle.mjs', './src/swiper-element.mjs', './src/swiper-element-bundle.mjs', './src/swiper-vue.mjs', './src/swiper-react.mjs', ...modulesPaths, './src/swiper-effect-utils.mjs', ], plugins: [ replace({ delimiters: ['', ''], '//IMPORT_MODULES': modules .map((mod) => `import ${mod.capitalized} from './modules/${mod.name}/${mod.name}.mjs';`) .join('\n'), '//INSTALL_MODULES': modules.map((mod) => `${mod.capitalized}`).join(',\n '), '//EXPORT': 'export default Swiper; export { Swiper }', }), nodeResolve({ mainFields: ['module', 'main', 'jsnext'], rootDir: './src' }), babel({ babelHelpers: 'bundled' }), ], onwarn() {}, }); await output.write({ dir: `./dist/tmp`, format: 'esm', entryFileNames: '[name].mjs', hoistTransitiveImports: false, chunkFileNames: (i) => { if (i.name === 'swiper') return `swiper-core.mjs`; return `[name].mjs`; }, }); // REARRANGE FILES const files = fs.readdirSync(`./dist/tmp`); if (!fs.existsSync(`./dist/modules`)) { fs.mkdirSync(`./dist/modules`); } files.forEach((fileName) => { const folderName = fileName.split('.mjs')[0]; if (fs.existsSync(`./src/modules/${folderName}`)) { fs.copyFileSync(`./dist/tmp/${fileName}`, `./dist/modules/${fileName}`); fs.unlinkSync(`./dist/tmp/${fileName}`); } else if ( (fileName.indexOf('swiper-') !== 0 && fileName !== 'swiper.mjs') || fileName === 'swiper-core.mjs' ) { if (!fs.existsSync('./dist/shared')) { fs.mkdirSync('./dist/shared'); } fs.copyFileSync(`./dist/tmp/${fileName}`, `./dist/shared/${fileName}`); fs.unlinkSync(`./dist/tmp/${fileName}`); } else { fs.copyFileSync(`./dist/tmp/${fileName}`, `./dist/${fileName}`); fs.unlinkSync(`./dist/tmp/${fileName}`); } }); if (fs.existsSync('./dist/tmp')) { fs.rmdirSync('./dist/tmp'); } // FIX IMPORTS fs.readdirSync('./dist/modules') .filter((f) => f.includes('.mjs')) .forEach((modName) => { const content = fs .readFileSync(`./dist/modules/${modName}`, 'utf-8') .replace(/from '\.\//g, `from '../shared/`); fs.writeFileSync(`./dist/modules/${modName}`, content); }); const { core, bundle, slide } = await getElementStyles(); fs.readdirSync('./dist/') .filter((f) => f.includes('.mjs')) .forEach((f) => { let content = fs.readFileSync(`./dist/${f}`, 'utf-8'); if (f === 'swiper-bundle.mjs') { content = content .replace(/from '\.\/swiper-core/g, `from './shared/swiper-core`) .replace( /import ([0-9A-Za-z]*) from '\.\/([0-9a-z-]*).mjs'/g, `import $1 from './modules/$2.mjs'`, ); } else { content = content.replace(/from '\.\//g, `from './shared/`); } // ADD ELEMENT STYLES if (f === 'swiper-element.mjs') { content = content .replace('//SWIPER_STYLES', `const SwiperCSS = \`${core}\``) .replace('//SWIPER_SLIDE_STYLES', `const SwiperSlideCSS = \`${slide}\``); } if (f === 'swiper-element-bundle.mjs') { content = content .replace('/swiper-bundle.js', `/swiper-bundle.mjs`) .replace('//SWIPER_STYLES', `const SwiperCSS = \`${bundle}\``) .replace('//SWIPER_SLIDE_STYLES', `const SwiperSlideCSS = \`${slide}\``); } // ADD BANNER const bannerName = f.includes('react') ? 'React' : f.includes('vue') ? 'Vue' : f.includes('element') ? 'Custom Element' : ''; fs.writeFileSync(`./dist/${f}`, `${banner(bannerName)}\n${content}`); }); // MODULES_INDEX fs.writeFileSync( './dist/modules/index.mjs', modules .map((mod) => `export {default as ${mod.capitalized}} from './${mod.name}.mjs';`) .join('\n'), ); // IIFE const replaceExports = {}; ['swiper-bundle.mjs', 'swiper.mjs'].forEach((f) => { const content = fs.readFileSync(`./dist/${f}`, 'utf-8'); const before = content.match(/export { ([^,]*), ([^}]*) }/)[0]; const after = before.replace(/export { ([^,]*), ([^}]*) }/, `export {$2}`); replaceExports[f] = { before, after, }; fs.writeFileSync( `./dist/${f}`, content.replace(replaceExports[f].before, replaceExports[f].after), ); }); await Promise.all( ['swiper-bundle.mjs', 'swiper.mjs', 'swiper-element.mjs', 'swiper-element-bundle.mjs'].map( async (f) => { const output = await rollup({ input: `./dist/${f}`, plugins: [ replace({ preventAssignment: true, delimiters: ['', ''], 'export { SwiperContainer, SwiperSlide, register };': 'register();', }), ], }); await output.write({ file: `./dist/${f.replace('.mjs', '.js')}`, format: 'iife', name: f === 'swiper-bundle.mjs' || f === 'swiper.mjs' ? 'Swiper' : '', banner: banner(f.includes('element') ? 'Custom Element' : ''), }); }, ), ); ['swiper-bundle.mjs', 'swiper.mjs'].forEach((f) => { const content = fs.readFileSync(`./dist/${f}`, 'utf-8'); fs.writeFileSync( `./dist/${f}`, content.replace(replaceExports[f].after, replaceExports[f].before), ); }); // REMOVE ELEMENT BUNDLE if (isProd) { fs.unlinkSync('./src/swiper-element-bundle.mjs'); } if (!isProd) { elapsed.end('modules', chalk.green('Modules build completed!')); return; } // MINIFY await Promise.all([ // MINIFY SHARED ...fs .readdirSync('./dist/shared') .filter((f) => f.endsWith('.mjs') && !f.includes('.min')) .map((f) => minify(f, `./dist/shared/${f}`)), // MINIFY MODULES ...fs .readdirSync('./dist/modules') .filter((f) => f.endsWith('.mjs') && !f.includes('.min')) .map((f) => minify(f, `./dist/modules/${f}`)), // MINIFY ROOT ...fs .readdirSync('./dist/') .filter( (f) => f.endsWith('.mjs') && !f.includes('.min') && !f.includes('react') && !f.includes('vue'), ) .map((f) => { const bannerName = f.includes('react') ? 'React' : f.includes('vue') ? 'Vue' : f.includes('element') ? 'Custom Element' : ''; return minify(f, `./dist/${f}`, bannerName); }), // IIFE ...['swiper-bundle.js', 'swiper.js', 'swiper-element.js', 'swiper-element-bundle.js'].map( (f) => { const bannerName = f.includes('element') ? 'Custom Element' : ''; return minify(f, `./dist/${f}`, bannerName); }, ), ]); elapsed.end('modules', chalk.green('Modules build completed!')); } ================================================ FILE: scripts/build-sponsors.js ================================================ import fs from 'fs'; import path from 'path'; import https from 'https'; import * as url from 'url'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); const getSponsors = () => { return new Promise((resolve, reject) => { https .get('https://swiperjs.com/sponsors-list.json', (resp) => { let data = ''; // A chunk of data has been received. resp.on('data', (chunk) => { data += chunk; }); // The whole response has been received. Print out the result. resp.on('end', () => { resolve(JSON.parse(data)); }); }) .on('error', (err) => { reject(err); }); }); }; const buildTables = (sponsors) => { const tableSponsors = [ ...(sponsors['Platinum Sponsor'] || []), ...(sponsors['Gold Sponsor'] || []), ...(sponsors['Silver Sponsor'] || []), ...(sponsors['Sponsor'] || []), // eslint-disable-line ]; let tableContent = ''; if (tableSponsors.length > 0) { const rows = []; const perRow = 12; let rowIndex = 0; tableSponsors.forEach((item, index) => { const colIndex = index - perRow * rowIndex; if (colIndex > perRow - 1) rowIndex += 1; if (!rows[rowIndex]) rows[rowIndex] = []; rows[rowIndex].push(item); }); if (rows.length > 0 && rows[rows.length - 1].length < perRow) { rows[rows.length - 1].push(...Array.from({ length: perRow - rows[rows.length - 1].length })); } tableContent = `\n\n${rows .map((items) => [ ` `, items .map((item) => !item ? ' ' : [ ` `, ].join('\n'), ) .join('\n'), ` `, ].join('\n'), ) .join('\n')}\n
`, ` `, ` ${item.title}`, ` `, `
\n`; } const backersContent = fs .readFileSync(path.resolve(__dirname, '../BACKERS.md'), 'utf-8') .split(''); backersContent[1] = tableContent; const readmeContent = fs .readFileSync(path.resolve(__dirname, '../README.md'), 'utf-8') .split(''); readmeContent[1] = tableContent; fs.writeFileSync( path.resolve(__dirname, '../BACKERS.md'), backersContent.join(''), ); fs.writeFileSync( path.resolve(__dirname, '../README.md'), readmeContent.join(''), ); }; const buildSponsorsList = async (sponsors) => { const silverSponsorsContent = sponsors['Silver Sponsor'].map((item) => ` - [${item.title}](${item.link}) `.trim(), ); const sponsorsContent = sponsors.Sponsor.map((item) => ` - [${item.title}](${item.link}) `.trim(), ); let backersContent = fs.readFileSync(path.resolve(__dirname, '../BACKERS.md'), 'utf-8'); backersContent = backersContent.split(''); backersContent[1] = `\n${silverSponsorsContent.join('\n')}\n`; backersContent = backersContent.join(''); backersContent = backersContent.split(''); backersContent[1] = `\n${sponsorsContent.join('\n')}\n`; backersContent = backersContent.join(''); fs.writeFileSync(path.resolve(__dirname, '../BACKERS.md'), backersContent); }; const buildSponsors = async () => { const entries = await getSponsors(); const sponsors = {}; if (entries) { const items = [...entries]; items.sort((a, b) => { return new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1; }); items.forEach((item) => { if (!sponsors[item.plan]) sponsors[item.plan] = []; sponsors[item.plan].push(item); }); } buildTables(sponsors); buildSponsorsList(sponsors); }; buildSponsors(); ================================================ FILE: scripts/build-styles.js ================================================ import fs from 'fs-extra'; import path from 'path'; import { globby } from 'globby'; import * as url from 'url'; import chalk from 'chalk'; import elapsed from 'elapsed-time-logger'; import autoprefixer from './utils/autoprefixer.js'; import minifyCSS from './utils/minify-css.js'; import { banner } from './utils/banner.js'; import config from './build-config.js'; import { outputDir } from './utils/output-dir.js'; import isProd from './utils/isProd.js'; import { getSplittedCSS, proceedReplacements } from './utils/get-element-styles.js'; import unwrapCss from './utils/unwrap-css.js'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); const buildCSS = async ({ isBundle, modules, minified }) => { const coreContent = await fs.readFile(path.resolve(__dirname, '../src/swiper.css'), 'utf8'); const modulesContent = []; for (const mod of modules) { modulesContent.push( // eslint-disable-next-line no-await-in-loop await fs.readFile(path.resolve(__dirname, `../src/modules/${mod}/${mod}.css`), 'utf8'), ); } const swiperCSS = await autoprefixer(coreContent); const swiperBundleCSS = await autoprefixer([coreContent, ...modulesContent].join('\n')); const fileName = isBundle ? 'swiper-bundle' : 'swiper'; // Write file await fs.ensureDir(`./${outputDir}`); await fs.writeFile( `./${outputDir}/${fileName}.css`, `${banner()}\n${isBundle ? swiperBundleCSS : swiperCSS}`, ); if (minified) { const minifiedContent = await minifyCSS(isBundle ? swiperBundleCSS : swiperCSS); await fs.writeFile(`./${outputDir}/${fileName}.min.css`, `${banner()}\n${minifiedContent}`); } }; export default async function buildStyles() { elapsed.start('styles'); // eslint-disable-next-line import/no-named-as-default-member const modules = config.modules.filter((name) => { const cssFilePath = `./src/modules/${name}/${name}.css`; return fs.existsSync(cssFilePath); }); buildCSS({ isBundle: true, modules, minified: isProd }); buildCSS({ isBundle: false, modules, minified: isProd }); if (isProd) { // Copy css const files = await globby(['**/**.css'], { cwd: path.resolve(__dirname, '../src'), }); await Promise.all( files.map(async (file) => { let distFilePath = path.resolve(__dirname, `../${outputDir}`, file); const srcFilePath = path.resolve(__dirname, '../src', file); let distFileContent = fs.readFileSync(srcFilePath, 'utf-8'); if (file === 'swiper.css') { distFileContent = `${banner()}\n${distFileContent}`; } if (distFilePath.includes('/modules/') || distFilePath.includes('\\modules\\')) { distFilePath = distFilePath .replace(/modules\/([a-zA-Z0-9-]*)/, 'modules') .replace(/modules\\([a-zA-Z0-9-]*)/, 'modules'); } await fs.ensureDir(path.dirname(distFilePath)); await fs.writeFile(distFilePath, distFileContent); }), ); const modulesCSSFiles = await globby(['**/**.css'], { cwd: path.resolve(__dirname, '../dist/modules'), absolute: true, }); await Promise.all( modulesCSSFiles.map(async (filePath) => { const cssContent = await fs.readFile(filePath, 'utf-8'); const resultCSS = await autoprefixer(cssContent); const unwrappedCSS = await unwrapCss(resultCSS); const resultCSSElement = proceedReplacements(getSplittedCSS(unwrappedCSS).container); const resultFilePath = filePath.replace(/\.css$/, ''); const minifiedCSS = await minifyCSS(resultCSS); const minifiedCSSElement = await minifyCSS(resultCSSElement); await fs.writeFile(`${resultFilePath}.css`, resultCSS); await fs.writeFile(`${resultFilePath}-element.css`, resultCSSElement); await fs.writeFile(`${resultFilePath}.min.css`, minifiedCSS); await fs.writeFile(`${resultFilePath}-element.min.css`, minifiedCSSElement); }), ); } elapsed.end('styles', chalk.green('Styles build completed!')); } ================================================ FILE: scripts/build-types.js ================================================ import path from 'path'; import { globby } from 'globby'; import elapsed from 'elapsed-time-logger'; import chalk from 'chalk'; import fs from 'fs-extra'; import * as url from 'url'; import { outputDir } from './utils/output-dir.js'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); export default async function buildTypes() { elapsed.start('types'); let coreEventsReact = ''; let coreEventsVue = ''; let coreEventsElement = ''; let modulesEventsReact = ''; let modulesEventsVue = ''; let modulesEventsElement = ''; const replaceInstances = (content) => { return content .replace(/this: Swiper, /g, '') .replace(/this: Swiper/g, '') .replace(/swiper: Swiper/g, 'swiper: SwiperClass'); }; const getCoreEventsContent = async () => { let coreEventsContent = await fs.readFile( path.resolve(__dirname, '../src/types/swiper-events.d.ts'), 'utf-8', ); coreEventsContent = coreEventsContent .split('// CORE_EVENTS_START')[1] .split('// CORE_EVENTS_END')[0]; coreEventsElement = coreEventsContent.replace( / ([a-zA-Z_?]*): ([^;]*);/g, (string, name, args) => { if ( name.includes('_') || name.toLowerCase() === 'classnames' || name.toLowerCase() === 'index' ) { return ''; } args = args .replace('(', '') .replace(')', '') .split('=>')[0] .replace('SwiperClass', 'Swiper') .trim(); return ` ${name.toLowerCase()}: CustomEvent<[${args}]>;`; }, ); coreEventsReact = replaceInstances( coreEventsContent.replace(/ ([a-zA-Z]*): \(/g, (string, name) => { return ` on${name[0].toUpperCase()}${name.substr(1)}?: (`; }), ); coreEventsVue = replaceInstances( coreEventsContent.replace(/ ([a-zA-Z_?]*): \(/g, (string, name) => { return ` ${name.replace('?', '')}: (`; }), ); }; const getModulesEventsContent = async () => { const eventsFiles = await globby('src/types/modules/*.d.ts'); await Promise.all( eventsFiles.map(async (eventsFile) => { if (eventsFile.indexOf('public-api') > -1 || eventsFile.indexOf('index') > -1) { return; } let eventsContent = await fs.readFile(eventsFile, 'utf-8'); eventsContent = eventsContent.split('Events {')[1].split('}')[0].trim(); if (eventsContent.length) { modulesEventsElement += eventsContent.replace( / ([a-zA-Z]*): ([^;]*);/g, (string, name, args) => { args = args .replace('(', '') .replace(')', '') .split('=>')[0] .replace('SwiperClass', 'Swiper') .trim(); return ` ${name.toLowerCase()}: CustomEvent<[${args}]>;`; }, ); modulesEventsReact += replaceInstances( eventsContent.replace(/ ([a-zA-Z]*): \(/g, (string, name) => { return ` on${name[0].toUpperCase()}${name.substr(1)}?: (`; }), ); modulesEventsVue += replaceInstances( eventsContent.replace(/ ([a-zA-Z_?]*): \(/g, (string, name) => { return ` ${name.replace('?', '')}: (`; }), ); } }), ); }; let files; await Promise.all([ getCoreEventsContent(), getModulesEventsContent(), (async () => { files = await globby('**/*.d.ts', { cwd: path.resolve(__dirname, '../src') }); })(), ]); await Promise.all( files.map(async (file) => { const fileContent = await fs.readFile(path.resolve(__dirname, '../src', file), 'utf-8'); const destPath = path.resolve(__dirname, `../${outputDir}`, file); await fs.ensureDir(path.dirname(destPath)); const processTypingFile = async (eventsCode, modulesCode) => { const content = fileContent .replace('// MODULES_EVENTS', eventsCode) .replace('// CORE_EVENTS', modulesCode); return fs.writeFile(destPath, content); }; if (file.includes('swiper-element.d.ts')) { return processTypingFile(coreEventsElement, modulesEventsElement); } if (file.includes('swiper-react.d.ts')) { return processTypingFile(coreEventsReact, modulesEventsReact); } if (file.includes('swiper-vue.d.ts')) { return processTypingFile(coreEventsVue, modulesEventsVue); } return fs.writeFile(destPath, fileContent); }), ); elapsed.end('types', chalk.green('Types build completed!')); } ================================================ FILE: scripts/build.js ================================================ import chalk from 'chalk'; import fs from 'fs-extra'; import elapsed from 'elapsed-time-logger'; import buildTypes from './build-types.js'; import buildStyles from './build-styles.js'; import buildModules from './build-modules.js'; import { outputDir } from './utils/output-dir.js'; import isProd from './utils/isProd.js'; class Build { constructor() { this.tasks = []; // eslint-disable-next-line no-constructor-return return this; } add(flag, buildFn) { this.tasks.push(() => buildFn()); return this; } async run() { if (isProd) { await fs.remove(`./${outputDir}`); await fs.ensureDir(`./${outputDir}`); } await fs.copy('./src/copy/', `./${outputDir}`); try { for (const buildFn of this.tasks) { await buildFn(); // eslint-disable-line } } catch (err) { console.error(err); process.exit(1); } return true; } } (async () => { elapsed.start('build'); const build = new Build(); await build .add('modules', buildModules) .add('types', buildTypes) .add('styles', buildStyles) .run(); elapsed.end('build', chalk.bold.green('Build completed')); })(); ================================================ FILE: scripts/release.js ================================================ import exec from 'exec-sh'; import inquirer from 'inquirer'; import fs from 'fs'; import path from 'path'; import { rimraf } from 'rimraf'; import * as url from 'url'; const pkg = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url))); const childPkg = JSON.parse(fs.readFileSync(new URL('../src/copy/package.json', import.meta.url))); const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); async function release() { const date = new Date(); const formatter = new Intl.DateTimeFormat('en', { day: 'numeric', year: 'numeric', month: 'long', }); const releaseDate = formatter.format(date); const options = await inquirer.prompt([ { type: 'input', name: 'version', message: 'Version:', default: pkg.version, }, { type: 'list', name: 'alpha', message: 'Alpha?', when: (opts) => opts.version.indexOf('alpha') >= 0, choices: [ { name: 'YES', value: true, }, { name: 'NO', value: false, }, ], }, { type: 'list', name: 'beta', message: 'Beta?', when: (opts) => opts.version.indexOf('beta') >= 0, choices: [ { name: 'YES', value: true, }, { name: 'NO', value: false, }, ], }, { type: 'list', name: 'next', message: 'Next?', when: (opts) => opts.version.indexOf('next') >= 0, choices: [ { name: 'YES', value: true, }, { name: 'NO', value: false, }, ], }, ]); // Set version pkg.version = options.version; childPkg.version = options.version; childPkg.releaseDate = releaseDate; // Copy dependencies childPkg.dependencies = pkg.dependencies; fs.writeFileSync(path.resolve(__dirname, '../package.json'), `${JSON.stringify(pkg, null, 2)}\n`); fs.writeFileSync( path.resolve(__dirname, '../src/copy/package.json'), `${JSON.stringify(childPkg, null, 2)}\n`, ); await exec.promise('git pull'); await exec.promise('npm i'); rimraf.sync(path.resolve(__dirname, 'dir')); fs.mkdirSync(path.resolve(__dirname, 'dir')); await exec.promise(`npm run build:prod`); await exec.promise('git add .'); await exec.promise(`git commit -m ${pkg.version} --no-verify`); await exec.promise('git push'); await exec.promise(`git tag v${pkg.version}`); await exec.promise('git push origin --tags'); if (options.beta) { await exec.promise('cd ./dist && npm publish --tag beta'); } else if (options.alpha || options.next) { await exec.promise('cd ./dist && npm publish --tag next'); } else { await exec.promise('cd ./dist && npm publish'); } } release(); ================================================ FILE: scripts/utils/autoprefixer.js ================================================ import postcss from 'postcss'; import autoprefixer from 'autoprefixer'; export default async (content, { from = undefined, to = undefined } = {}) => new Promise((resolve, reject) => { postcss([autoprefixer]) .process(content, { from, to }) .then((result) => { result.warnings().forEach((warn) => { console.warn(warn.toString()); }); resolve(result.css); }) .catch((err) => { reject(err); throw err; }); }); ================================================ FILE: scripts/utils/banner.js ================================================ import fs from 'fs-extra'; const pkg = JSON.parse(fs.readFileSync(new URL('../../package.json', import.meta.url))); const date = { day: new Date().getDate(), month: 'January February March April May June July August September October November December'.split( ' ', )[new Date().getMonth()], year: new Date().getFullYear(), }; function banner(name = null) { return `${` /** * Swiper ${name ? `${name} ` : ''}${pkg.version} * ${pkg.description} * ${pkg.homepage} * * Copyright 2014-${date.year} ${pkg.author} * * Released under the ${pkg.license} License * * Released on: ${date.month} ${date.day}, ${date.year} */ `.trim()}\n`; } async function addBannerToFile(file, name) { const content = await fs.readFile(file, 'utf-8'); await fs.writeFile(file, `${banner(name)}\n${content}`); } export { banner }; export { addBannerToFile }; export default { banner, addBannerToFile, }; ================================================ FILE: scripts/utils/get-element-styles.js ================================================ import fs from 'fs'; import path from 'path'; import * as url from 'url'; import autoprefixer from './autoprefixer.js'; import minifyCss from './minify-css.js'; import config from '../build-config.js'; import unwrapCss from './unwrap-css.js'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); export const getSplittedCSS = (content) => { const cssStylesSlideCore = (content.split(`/* Slide styles start */`)[1] || '').split( `/* Slide styles end */`, )[0]; const cssStylesSlideCube = (content.split(`/* Cube slide shadows start */`)[1] || '').split( `/* Cube slide shadows end */`, )[0]; const cssStylesSlideFlip = (content.split(`/* Flip slide shadows start */`)[1] || '').split( `/* Flip slide shadows end */`, )[0]; const cssStylesSlideZoom = (content.split(`/* Zoom container styles start */`)[1] || '').split( `/* Zoom container styles end */`, )[0]; content = content .replace(cssStylesSlideCore, '') .replace(cssStylesSlideCube, '') .replace(cssStylesSlideFlip, '') .replace(cssStylesSlideZoom, ''); return { slides: [ cssStylesSlideCore || '', cssStylesSlideCube || '', cssStylesSlideFlip || '', cssStylesSlideZoom || '', ].join('\n'), container: content, }; }; export const proceedReplacements = (content = '') => { // eslint-disable-next-line const replace = ['invisible-blank', 'visible', 'zoomed', 'active', 'next', 'prev']; content = content .replace(/:root/g, ':host') .split('\n') .map((line) => { if (line.includes('.swiper {')) { return line.replace('.swiper {', '.swiper {width: 100%; height: 100%;'); } if (line.includes('> .swiper-wrapper > .swiper-slide')) { return line.replace('> .swiper-wrapper > .swiper-slide', '::slotted(swiper-slide)'); } let replaced = ''; if (line.includes('.swiper-slide ')) { replaced = line.replaceAll(/\.swiper-slide /g, '::slotted(swiper-slide) '); } if (line.includes('.swiper-slide,')) { replaced = line.replaceAll(/\.swiper-slide,/g, '::slotted(swiper-slide),'); } if (line.includes('.swiper-slide:')) { replaced = line.replaceAll(/\.swiper-slide:/g, '::slotted(swiper-slide):'); } replace.forEach((key) => { const l = replaced || line; if (l.includes(`.swiper-slide-${key}`)) { replaced = l.replaceAll(`.swiper-slide-${key}`, `::slotted(.swiper-slide-${key})`); } }); if (replaced) { return replaced; } return line; }) .join('\n'); return content; }; const proceedSlideReplacements = (content) => { content = content .split('\n') .map((line) => { if (line === '.swiper-lazy-preloader {') { return line.replace( '.swiper-lazy-preloader {', '.swiper-lazy-preloader {animation: swiper-preloader-spin 1s infinite linear;', ); } if (line.includes('animation: swiper-preloader-spin 1s infinite linear;')) { return ''; } if (line.includes('.swiper-zoom-container')) { return line.replace('.swiper-zoom-container', '::slotted(.swiper-zoom-container)'); } if (line.includes('--swiper-preloader-color:')) return ''; if (line.includes('.swiper-3d .swiper-slide-shadow')) { return line.replace('.swiper-3d ', '::slotted(').replace(',', '),').replace(' {', ') {'); } if (line.includes('.swiper-cube .swiper-slide-shadow')) { return line.replace('.swiper-cube ', '::slotted(').replace(',', '),').replace(' {', ') {'); } if (line.includes('.swiper-flip .swiper-slide-shadow')) { return line.replace('.swiper-flip ', '::slotted(').replace(',', '),').replace(' {', ') {'); } return line; }) .join('\n'); return content; }; export default async function getElementStyles() { // eslint-disable-next-line const modules = config.modules.filter((name) => { const cssFilePath = `./src/modules/${name}/${name}.css`; return fs.existsSync(cssFilePath); }); const cssContentBundle = await fs.readFileSync( path.resolve(__dirname, '../../src/swiper.css'), 'utf8', ); const modulesCSSContent = []; for (const mod of modules) { modulesCSSContent.push( // eslint-disable-next-line no-await-in-loop await fs.readFileSync(path.resolve(__dirname, `../../src/modules/${mod}/${mod}.css`), 'utf8'), ); } const cssContentCore = await fs.readFileSync( path.resolve(__dirname, '../../src/swiper.css'), 'utf8', ); let cssStylesBundle = await unwrapCss( await autoprefixer([cssContentBundle, ...modulesCSSContent].join('\n')), ); let cssStylesCore = await unwrapCss(await autoprefixer(cssContentCore)); // eslint-disable-next-line let cssStylesSlide = getSplittedCSS(cssStylesBundle).slides; cssStylesBundle = getSplittedCSS(cssStylesBundle).container; cssStylesCore = getSplittedCSS(cssStylesCore).container; cssStylesBundle = proceedReplacements(cssStylesBundle); cssStylesCore = proceedReplacements(cssStylesCore); cssStylesSlide = proceedSlideReplacements(cssStylesSlide); cssStylesBundle = await minifyCss(cssStylesBundle); cssStylesCore = await minifyCss(cssStylesCore); cssStylesSlide = await minifyCss(cssStylesSlide); return { core: cssStylesCore, bundle: cssStylesBundle, slide: cssStylesSlide, }; } ================================================ FILE: scripts/utils/helper.js ================================================ export function capitalizeString(str) { return str.replace(/(?:^|-)([a-z])/g, (m, char) => char.toUpperCase()); } /** * Parse build modules from env `SWIPER_BUILD_MODULES`.
* Will return null if env is not set or empty * @returns {string[]|null} */ export function parseSwiperBuildModulesEnv() { if (process.env.SWIPER_BUILD_MODULES) { const buildModules = process.env.SWIPER_BUILD_MODULES.split(/[,\s]+/).filter( (moduleName) => moduleName, // ensure to empty will be removed ); if (buildModules.length) return buildModules; } return null; } ================================================ FILE: scripts/utils/isProd.js ================================================ function isProd() { const argv = process.argv.slice(2).map((v) => v.toLowerCase()); return argv.includes('--prod'); } export default isProd(); ================================================ FILE: scripts/utils/less.js ================================================ import less from 'less'; import path from 'path'; import * as url from 'url'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); export default (content, resolvePath = path.resolve(__dirname, '../../src/core')) => new Promise((resolve, reject) => { less .render(content, { paths: [resolvePath] }) .then((result) => { resolve(result.css); }) .catch((err) => { reject(err); throw err; }); }); ================================================ FILE: scripts/utils/minify-css.js ================================================ import postcss from 'postcss'; import cssnano from 'cssnano'; export default (content) => { return new Promise((resolve, reject) => { if (content instanceof Promise) { content .then((c) => { postcss([cssnano()]) .process(c, { from: undefined, to: undefined }) .then((result) => resolve(result.css)); }) .catch((err) => { reject(err); throw err; }); return; } postcss([cssnano()]) .process(content, { from: undefined, to: undefined }) .then((res) => { resolve(res.css); }); }); }; ================================================ FILE: scripts/utils/minify.js ================================================ import { minify } from 'terser'; import fs from 'fs'; import { banner } from './banner.js'; export default async (fileName, filePath, bannerName) => { const content = fs .readFileSync(filePath, 'utf-8') .replace(/from '([A-Za-z0-9./-]*).mjs';/g, 'from "$1.min.mjs";'); const fileExt = fileName.includes('.mjs') ? '.mjs' : '.js'; const { code, map } = await minify(content, { sourceMap: { filename: `${fileName}${fileExt}`, url: `${fileName.replace(fileExt, `.min${fileExt}`)}.map`, }, output: { preamble: typeof bannerName !== 'undefined' ? banner(bannerName) : '', }, }).catch((err) => { console.error(`Terser failed on file ${fileName}: ${err.toString()}`); }); fs.writeFileSync(filePath.replace(fileExt, `.min${fileExt}`), code); fs.writeFileSync(filePath.replace(`${fileExt}`, `.min${fileExt}.map`), map); }; ================================================ FILE: scripts/utils/output-dir.js ================================================ export const outputDir = 'dist'; ================================================ FILE: scripts/utils/unwrap-css.js ================================================ import postcss from 'postcss'; import nested from 'postcss-nested'; export default (content) => { return new Promise((resolve, reject) => { if (content instanceof Promise) { content .then((c) => { postcss([nested()]) .process(c, { from: undefined, to: undefined }) .then((result) => resolve(result.css)); }) .catch((err) => { reject(err); throw err; }); return; } postcss([nested()]) .process(content, { from: undefined, to: undefined }) .then((res) => { resolve(res.css); }); }); }; ================================================ FILE: scripts/watch.js ================================================ import fs from 'fs'; import path from 'path'; import chalk from 'chalk'; import * as url from 'url'; import buildTypes from './build-types.js'; import buildStyles from './build-styles.js'; import buildModules from './build-modules.js'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); console.log(chalk.cyan('Watching file changes ...')); const watchFunction = async (fileName) => { if (fileName.includes('swiper-element-bundle.mjs')) return; if (fileName.includes('.css')) { console.log('Building styles'); await buildStyles(); console.log('Building styles DONE'); return; } if (fileName.includes('.d.ts')) { console.log('Building Types'); await buildTypes(); return; } if (fileName.includes('.mjs') || fileName.includes('.js')) { console.log('Building JS'); await buildModules(); return; } console.log('something wrong...'); }; let watchTimeout; fs.watch(path.resolve(__dirname, '../src'), { recursive: true }, (eventType, fileName) => { clearTimeout(watchTimeout); watchTimeout = setTimeout(() => { watchFunction(fileName); }, 100); }); ================================================ FILE: src/components-shared/get-changed-params.mjs ================================================ import { paramsList } from './params-list.mjs'; import { isObject } from './utils.mjs'; function getChangedParams(swiperParams, oldParams, children, oldChildren, getKey) { const keys = []; if (!oldParams) return keys; const addKey = (key) => { if (keys.indexOf(key) < 0) keys.push(key); }; if (children && oldChildren) { const oldChildrenKeys = oldChildren.map(getKey); const childrenKeys = children.map(getKey); if (oldChildrenKeys.join('') !== childrenKeys.join('')) addKey('children'); if (oldChildren.length !== children.length) addKey('children'); } const watchParams = paramsList.filter((key) => key[0] === '_').map((key) => key.replace(/_/, '')); watchParams.forEach((key) => { if (key in swiperParams && key in oldParams) { if (isObject(swiperParams[key]) && isObject(oldParams[key])) { const newKeys = Object.keys(swiperParams[key]); const oldKeys = Object.keys(oldParams[key]); if (newKeys.length !== oldKeys.length) { addKey(key); } else { newKeys.forEach((newKey) => { if (swiperParams[key][newKey] !== oldParams[key][newKey]) { addKey(key); } }); oldKeys.forEach((oldKey) => { if (swiperParams[key][oldKey] !== oldParams[key][oldKey]) addKey(key); }); } } else if (swiperParams[key] !== oldParams[key]) { addKey(key); } } }); return keys; } export { getChangedParams }; ================================================ FILE: src/components-shared/get-element-params.mjs ================================================ import { attrToProp, extend, isObject } from './utils.mjs'; import { paramsList } from './params-list.mjs'; import defaults from '../core/defaults.mjs'; const formatValue = (val) => { if (parseFloat(val) === Number(val)) return Number(val); if (val === 'true') return true; if (val === '') return true; if (val === 'false') return false; if (val === 'null') return null; if (val === 'undefined') return undefined; if (typeof val === 'string' && val.includes('{') && val.includes('}') && val.includes('"')) { let v; try { v = JSON.parse(val); } catch (err) { v = val; } return v; } return val; }; const modulesParamsList = [ 'a11y', 'autoplay', 'controller', 'cards-effect', 'coverflow-effect', 'creative-effect', 'cube-effect', 'fade-effect', 'flip-effect', 'free-mode', 'grid', 'hash-navigation', 'history', 'keyboard', 'mousewheel', 'navigation', 'pagination', 'parallax', 'scrollbar', 'thumbs', 'virtual', 'zoom', ]; function getParams(element, propName, propValue) { const params = {}; const passedParams = {}; extend(params, defaults); const localParamsList = [...paramsList, 'on']; const allowedParams = localParamsList.map((key) => key.replace(/_/, '')); // First check props localParamsList.forEach((paramName) => { paramName = paramName.replace('_', ''); if (typeof element[paramName] !== 'undefined') { passedParams[paramName] = element[paramName]; } }); // Attributes const attrsList = [...element.attributes]; if (typeof propName === 'string' && typeof propValue !== 'undefined') { attrsList.push({ name: propName, value: isObject(propValue) ? { ...propValue } : propValue }); } attrsList.forEach((attr) => { const moduleParam = modulesParamsList.find((mParam) => attr.name.startsWith(`${mParam}-`)); if (moduleParam) { const parentObjName = attrToProp(moduleParam); const subObjName = attrToProp(attr.name.split(`${moduleParam}-`)[1]); if (typeof passedParams[parentObjName] === 'undefined') { passedParams[parentObjName] = {}; } if (passedParams[parentObjName] === true) { passedParams[parentObjName] = { enabled: true }; } if (passedParams[parentObjName] === false) { passedParams[parentObjName] = { enabled: false }; } passedParams[parentObjName][subObjName] = formatValue(attr.value); } else { const name = attrToProp(attr.name); if (!allowedParams.includes(name)) return; const value = formatValue(attr.value); if (passedParams[name] && modulesParamsList.includes(attr.name) && !isObject(value)) { if (passedParams[name].constructor !== Object) { passedParams[name] = {}; } passedParams[name].enabled = !!value; } else { passedParams[name] = value; } } }); extend(params, passedParams); if (params.navigation) { params.navigation = { prevEl: '.swiper-button-prev', nextEl: '.swiper-button-next', ...(params.navigation !== true ? params.navigation : {}), }; } else if (params.navigation === false) { delete params.navigation; } if (params.scrollbar) { params.scrollbar = { el: '.swiper-scrollbar', ...(params.scrollbar !== true ? params.scrollbar : {}), }; } else if (params.scrollbar === false) { delete params.scrollbar; } if (params.pagination) { params.pagination = { el: '.swiper-pagination', ...(params.pagination !== true ? params.pagination : {}), }; } else if (params.pagination === false) { delete params.pagination; } return { params, passedParams }; } export { getParams }; ================================================ FILE: src/components-shared/get-params.mjs ================================================ import { isObject, extend } from './utils.mjs'; import { paramsList } from './params-list.mjs'; import defaults from '../core/defaults.mjs'; function getParams(obj = {}, splitEvents = true) { const params = { on: {}, }; const events = {}; const passedParams = {}; extend(params, defaults); params._emitClasses = true; params.init = false; const rest = {}; const allowedParams = paramsList.map((key) => key.replace(/_/, '')); const plainObj = Object.assign({}, obj); Object.keys(plainObj).forEach((key) => { if (typeof obj[key] === 'undefined') return; if (allowedParams.indexOf(key) >= 0) { if (isObject(obj[key])) { params[key] = {}; passedParams[key] = {}; extend(params[key], obj[key]); extend(passedParams[key], obj[key]); } else { params[key] = obj[key]; passedParams[key] = obj[key]; } } else if (key.search(/on[A-Z]/) === 0 && typeof obj[key] === 'function') { if (splitEvents) { events[`${key[2].toLowerCase()}${key.substr(3)}`] = obj[key]; } else { params.on[`${key[2].toLowerCase()}${key.substr(3)}`] = obj[key]; } } else { rest[key] = obj[key]; } }); ['navigation', 'pagination', 'scrollbar'].forEach((key) => { if (params[key] === true) params[key] = {}; if (params[key] === false) delete params[key]; }); return { params, passedParams, rest, events }; } export { getParams }; ================================================ FILE: src/components-shared/mount-swiper.mjs ================================================ import { needsNavigation, needsPagination, needsScrollbar } from './utils.mjs'; function mountSwiper({ el, nextEl, prevEl, paginationEl, scrollbarEl, swiper }, swiperParams) { if (needsNavigation(swiperParams) && nextEl && prevEl) { swiper.params.navigation.nextEl = nextEl; swiper.originalParams.navigation.nextEl = nextEl; swiper.params.navigation.prevEl = prevEl; swiper.originalParams.navigation.prevEl = prevEl; } if (needsPagination(swiperParams) && paginationEl) { swiper.params.pagination.el = paginationEl; swiper.originalParams.pagination.el = paginationEl; } if (needsScrollbar(swiperParams) && scrollbarEl) { swiper.params.scrollbar.el = scrollbarEl; swiper.originalParams.scrollbar.el = scrollbarEl; } swiper.init(el); } export { mountSwiper }; ================================================ FILE: src/components-shared/params-list.mjs ================================================ /* underscore in name -> watch for changes */ const paramsList = [ 'eventsPrefix', 'injectStyles', 'injectStylesUrls', 'modules', 'init', '_direction', 'oneWayMovement', 'swiperElementNodeName', 'touchEventsTarget', 'initialSlide', '_speed', 'cssMode', 'updateOnWindowResize', 'resizeObserver', 'nested', 'focusableElements', '_enabled', '_width', '_height', 'preventInteractionOnTransition', 'userAgent', 'url', '_edgeSwipeDetection', '_edgeSwipeThreshold', '_freeMode', '_autoHeight', 'setWrapperSize', 'virtualTranslate', '_effect', 'breakpoints', 'breakpointsBase', '_spaceBetween', '_slidesPerView', 'maxBackfaceHiddenSlides', '_grid', '_slidesPerGroup', '_slidesPerGroupSkip', '_slidesPerGroupAuto', '_centeredSlides', '_centeredSlidesBounds', '_slidesOffsetBefore', '_slidesOffsetAfter', 'normalizeSlideIndex', '_centerInsufficientSlides', '_snapToSlideEdge', '_watchOverflow', 'roundLengths', 'touchRatio', 'touchAngle', 'simulateTouch', '_shortSwipes', '_longSwipes', 'longSwipesRatio', 'longSwipesMs', '_followFinger', 'allowTouchMove', '_threshold', 'touchMoveStopPropagation', 'touchStartPreventDefault', 'touchStartForcePreventDefault', 'touchReleaseOnEdges', 'uniqueNavElements', '_resistance', '_resistanceRatio', '_watchSlidesProgress', '_grabCursor', 'preventClicks', 'preventClicksPropagation', '_slideToClickedSlide', '_loop', 'loopAdditionalSlides', 'loopAddBlankSlides', 'loopPreventsSliding', '_rewind', '_allowSlidePrev', '_allowSlideNext', '_swipeHandler', '_noSwiping', 'noSwipingClass', 'noSwipingSelector', 'passiveListeners', 'containerModifierClass', 'slideClass', 'slideActiveClass', 'slideVisibleClass', 'slideFullyVisibleClass', 'slideNextClass', 'slidePrevClass', 'slideBlankClass', 'wrapperClass', 'lazyPreloaderClass', 'lazyPreloadPrevNext', 'runCallbacksOnInit', 'observer', 'observeParents', 'observeSlideChildren', // modules 'a11y', '_autoplay', '_controller', 'coverflowEffect', 'cubeEffect', 'fadeEffect', 'flipEffect', 'creativeEffect', 'cardsEffect', 'hashNavigation', 'history', 'keyboard', 'mousewheel', '_navigation', '_pagination', 'parallax', '_scrollbar', '_thumbs', 'virtual', 'zoom', 'control', ]; export { paramsList }; ================================================ FILE: src/components-shared/update-on-virtual-data.mjs ================================================ export const updateOnVirtualData = (swiper) => { if ( !swiper || swiper.destroyed || !swiper.params.virtual || (swiper.params.virtual && !swiper.params.virtual.enabled) ) return; swiper.updateSlides(); swiper.updateProgress(); swiper.updateSlidesClasses(); swiper.emit('_virtualUpdated'); if (swiper.parallax && swiper.params.parallax && swiper.params.parallax.enabled) { swiper.parallax.setTranslate(); } }; ================================================ FILE: src/components-shared/update-swiper.mjs ================================================ import { setInnerHTML } from '../shared/utils.mjs'; import { isObject } from './utils.mjs'; function updateSwiper({ swiper, slides, passedParams, changedParams, nextEl, prevEl, scrollbarEl, paginationEl, }) { const updateParams = changedParams.filter( (key) => key !== 'children' && key !== 'direction' && key !== 'wrapperClass', ); const { params: currentParams, pagination, navigation, scrollbar, virtual, thumbs } = swiper; let needThumbsInit; let needControllerInit; let needPaginationInit; let needScrollbarInit; let needNavigationInit; let loopNeedDestroy; let loopNeedEnable; let loopNeedReloop; if ( changedParams.includes('thumbs') && passedParams.thumbs && passedParams.thumbs.swiper && !passedParams.thumbs.swiper.destroyed && currentParams.thumbs && (!currentParams.thumbs.swiper || currentParams.thumbs.swiper.destroyed) ) { needThumbsInit = true; } if ( changedParams.includes('controller') && passedParams.controller && passedParams.controller.control && currentParams.controller && !currentParams.controller.control ) { needControllerInit = true; } if ( changedParams.includes('pagination') && passedParams.pagination && (passedParams.pagination.el || paginationEl) && (currentParams.pagination || currentParams.pagination === false) && pagination && !pagination.el ) { needPaginationInit = true; } if ( changedParams.includes('scrollbar') && passedParams.scrollbar && (passedParams.scrollbar.el || scrollbarEl) && (currentParams.scrollbar || currentParams.scrollbar === false) && scrollbar && !scrollbar.el ) { needScrollbarInit = true; } if ( changedParams.includes('navigation') && passedParams.navigation && (passedParams.navigation.prevEl || prevEl) && (passedParams.navigation.nextEl || nextEl) && (currentParams.navigation || currentParams.navigation === false) && navigation && !navigation.prevEl && !navigation.nextEl ) { needNavigationInit = true; } const destroyModule = (mod) => { if (!swiper[mod]) return; swiper[mod].destroy(); if (mod === 'navigation') { if (swiper.isElement) { swiper[mod].prevEl.remove(); swiper[mod].nextEl.remove(); } currentParams[mod].prevEl = undefined; currentParams[mod].nextEl = undefined; swiper[mod].prevEl = undefined; swiper[mod].nextEl = undefined; } else { if (swiper.isElement) { swiper[mod].el.remove(); } currentParams[mod].el = undefined; swiper[mod].el = undefined; } }; if (changedParams.includes('loop') && swiper.isElement) { if (currentParams.loop && !passedParams.loop) { loopNeedDestroy = true; } else if (!currentParams.loop && passedParams.loop) { loopNeedEnable = true; } else { loopNeedReloop = true; } } updateParams.forEach((key) => { if (isObject(currentParams[key]) && isObject(passedParams[key])) { Object.assign(currentParams[key], passedParams[key]); if ( (key === 'navigation' || key === 'pagination' || key === 'scrollbar') && 'enabled' in passedParams[key] && !passedParams[key].enabled ) { destroyModule(key); } } else { const newValue = passedParams[key]; if ( (newValue === true || newValue === false) && (key === 'navigation' || key === 'pagination' || key === 'scrollbar') ) { if (newValue === false) { destroyModule(key); } } else { currentParams[key] = passedParams[key]; } } }); if ( updateParams.includes('controller') && !needControllerInit && swiper.controller && swiper.controller.control && currentParams.controller && currentParams.controller.control ) { swiper.controller.control = currentParams.controller.control; } if (changedParams.includes('children') && slides && virtual && currentParams.virtual.enabled) { virtual.slides = slides; virtual.update(true); } else if (changedParams.includes('virtual') && virtual && currentParams.virtual.enabled) { if (slides) virtual.slides = slides; virtual.update(true); } if (changedParams.includes('children') && slides && currentParams.loop) { loopNeedReloop = true; } if (needThumbsInit) { const initialized = thumbs.init(); if (initialized) thumbs.update(true); } if (needControllerInit) { swiper.controller.control = currentParams.controller.control; } if (needPaginationInit) { if (swiper.isElement && (!paginationEl || typeof paginationEl === 'string')) { paginationEl = document.createElement('div'); paginationEl.classList.add('swiper-pagination'); paginationEl.part.add('pagination'); swiper.el.appendChild(paginationEl); } if (paginationEl) currentParams.pagination.el = paginationEl; pagination.init(); pagination.render(); pagination.update(); } if (needScrollbarInit) { if (swiper.isElement && (!scrollbarEl || typeof scrollbarEl === 'string')) { scrollbarEl = document.createElement('div'); scrollbarEl.classList.add('swiper-scrollbar'); scrollbarEl.part.add('scrollbar'); swiper.el.appendChild(scrollbarEl); } if (scrollbarEl) currentParams.scrollbar.el = scrollbarEl; scrollbar.init(); scrollbar.updateSize(); scrollbar.setTranslate(); } if (needNavigationInit) { if (swiper.isElement) { if (!nextEl || typeof nextEl === 'string') { nextEl = document.createElement('div'); nextEl.classList.add('swiper-button-next'); setInnerHTML(nextEl, swiper.navigation.arrowSvg); nextEl.part.add('button-next'); swiper.el.appendChild(nextEl); } if (!prevEl || typeof prevEl === 'string') { prevEl = document.createElement('div'); prevEl.classList.add('swiper-button-prev'); setInnerHTML(prevEl, swiper.navigation.arrowSvg); prevEl.part.add('button-prev'); swiper.el.appendChild(prevEl); } } if (nextEl) currentParams.navigation.nextEl = nextEl; if (prevEl) currentParams.navigation.prevEl = prevEl; navigation.init(); navigation.update(); } if (changedParams.includes('allowSlideNext')) { swiper.allowSlideNext = passedParams.allowSlideNext; } if (changedParams.includes('allowSlidePrev')) { swiper.allowSlidePrev = passedParams.allowSlidePrev; } if (changedParams.includes('direction')) { swiper.changeDirection(passedParams.direction, false); } if (loopNeedDestroy || loopNeedReloop) { swiper.loopDestroy(); } if (loopNeedEnable || loopNeedReloop) { swiper.loopCreate(); } swiper.update(); } export { updateSwiper }; ================================================ FILE: src/components-shared/utils.mjs ================================================ function isObject(o) { return ( typeof o === 'object' && o !== null && o.constructor && Object.prototype.toString.call(o).slice(8, -1) === 'Object' && !o.__swiper__ ); } function extend(target, src) { const noExtend = ['__proto__', 'constructor', 'prototype']; Object.keys(src) .filter((key) => noExtend.indexOf(key) < 0) .forEach((key) => { if (typeof target[key] === 'undefined') target[key] = src[key]; else if (isObject(src[key]) && isObject(target[key]) && Object.keys(src[key]).length > 0) { if (src[key].__swiper__) target[key] = src[key]; else extend(target[key], src[key]); } else { target[key] = src[key]; } }); } function needsNavigation(params = {}) { return ( params.navigation && typeof params.navigation.nextEl === 'undefined' && typeof params.navigation.prevEl === 'undefined' ); } function needsPagination(params = {}) { return params.pagination && typeof params.pagination.el === 'undefined'; } function needsScrollbar(params = {}) { return params.scrollbar && typeof params.scrollbar.el === 'undefined'; } function uniqueClasses(classNames = '') { const classes = classNames .split(' ') .map((c) => c.trim()) .filter((c) => !!c); const unique = []; classes.forEach((c) => { if (unique.indexOf(c) < 0) unique.push(c); }); return unique.join(' '); } function attrToProp(attrName = '') { return attrName.replace(/-[a-z]/g, (l) => l.toUpperCase().replace('-', '')); } function wrapperClass(className = '') { if (!className) return 'swiper-wrapper'; if (!className.includes('swiper-wrapper')) return `swiper-wrapper ${className}`; return className; } export { isObject, extend, needsNavigation, needsPagination, needsScrollbar, uniqueClasses, attrToProp, wrapperClass, }; ================================================ FILE: src/copy/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2019 Vladimir Kharlampidi Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: src/copy/README.md ================================================ Swiper ========== Swiper - is the free and most modern mobile touch slider with hardware accelerated transitions and amazing native behavior. It is intended to be used in mobile websites, mobile web apps, and mobile native/hybrid apps. Swiper is not compatible with all platforms, it is a modern touch slider which is focused only on modern apps/platforms to bring the best experience and simplicity. # Getting Started * [Getting Started Guide](https://swiperjs.com/get-started/) * [API](https://swiperjs.com/swiper-api/) * [Demos](https://swiperjs.com/demos/) ================================================ FILE: src/copy/package.json ================================================ { "name": "swiper", "version": "12.1.2", "description": "Most modern mobile touch slider and framework with hardware accelerated transitions", "typings": "swiper.d.ts", "type": "module", "main": "./swiper.mjs", "module": "./swiper.mjs", "exports": { ".": { "types": "./swiper.d.ts", "default": "./swiper.mjs" }, "./effect-utils": { "types": "./swiper-effect-utils.d.ts", "default": "./swiper-effect-utils.mjs" }, "./core": { "types": "./swiper.d.ts", "default": "./swiper.mjs" }, "./bundle": { "types": "./swiper.d.ts", "default": "./swiper-bundle.mjs" }, "./css": "./swiper.css", "./swiper.css": "./swiper.css", "./css/bundle": "./swiper-bundle.css", "./swiper-bundle.css": "./swiper-bundle.css", "./css/a11y": "./modules/a11y.css", "./css/autoplay": "./modules/autoplay.css", "./css/controller": "./modules/controller.css", "./css/effect-coverflow": "./modules/effect-coverflow.css", "./css/effect-cube": "./modules/effect-cube.css", "./css/effect-fade": "./modules/effect-fade.css", "./css/effect-flip": "./modules/effect-flip.css", "./css/effect-creative": "./modules/effect-creative.css", "./css/effect-cards": "./modules/effect-cards.css", "./css/free-mode": "./modules/free-mode.css", "./css/grid": "./modules/grid.css", "./css/hash-navigation": "./modules/hash-navigation.css", "./css/history": "./modules/history.css", "./css/keyboard": "./modules/keyboard.css", "./css/manipulation": "./modules/manipulation.css", "./css/mousewheel": "./modules/mousewheel.css", "./css/navigation": "./modules/navigation.css", "./css/pagination": "./modules/pagination.css", "./css/parallax": "./modules/parallax.css", "./css/scrollbar": "./modules/scrollbar.css", "./css/thumbs": "./modules/thumbs.css", "./css/virtual": "./modules/virtual.css", "./css/zoom": "./modules/zoom.css", "./element": { "types": "./swiper-element.d.ts", "default": "./swiper-element.mjs" }, "./element/bundle": { "types": "./swiper-element.d.ts", "default": "./swiper-element-bundle.mjs" }, "./element-bundle": { "types": "./swiper-element.d.ts", "default": "./swiper-element-bundle.mjs" }, "./element/css/a11y": "./modules/a11y-element.css", "./element/css/autoplay": "./modules/autoplay-element.css", "./element/css/controller": "./modules/controller-element.css", "./element/css/effect-coverflow": "./modules/effect-coverflow-element.css", "./element/css/effect-cube": "./modules/effect-cube-element.css", "./element/css/effect-fade": "./modules/effect-fade-element.css", "./element/css/effect-flip": "./modules/effect-flip-element.css", "./element/css/effect-creative": "./modules/effect-creative-element.css", "./element/css/effect-cards": "./modules/effect-cards-element.css", "./element/css/free-mode": "./modules/free-mode-element.css", "./element/css/grid": "./modules/grid-element.css", "./element/css/hash-navigation": "./modules/hash-navigation-element.css", "./element/css/history": "./modules/history-element.css", "./element/css/keyboard": "./modules/keyboard-element.css", "./element/css/manipulation": "./modules/manipulation-element.css", "./element/css/mousewheel": "./modules/mousewheel-element.css", "./element/css/navigation": "./modules/navigation-element.css", "./element/css/pagination": "./modules/pagination-element.css", "./element/css/parallax": "./modules/parallax-element.css", "./element/css/scrollbar": "./modules/scrollbar-element.css", "./element/css/thumbs": "./modules/thumbs-element.css", "./element/css/virtual": "./modules/virtual-element.css", "./element/css/zoom": "./modules/zoom-element.css", "./react": { "types": "./swiper-react.d.ts", "default": "./swiper-react.mjs" }, "./vue": { "types": "./swiper-vue.d.ts", "default": "./swiper-vue.mjs" }, "./modules": { "types": "./types/modules/index.d.ts", "default": "./modules/index.mjs" }, "./types": "./types/index.d.ts", "./package.json": "./package.json" }, "typesVersions": { "*": { "modules": [ "./types/modules/index.d.ts" ], "element": [ "./swiper-element.d.ts" ], "element/bundle": [ "./swiper-element.d.ts" ], "react": [ "./swiper-react.d.ts" ], "vue": [ "./swiper-vue.d.ts" ] } }, "repository": { "type": "git", "url": "https://github.com/nolimits4web/Swiper.git" }, "keywords": [ "swiper", "swipe", "slider", "touch", "ios", "mobile", "cordova", "phonegap", "app", "framework", "framework7", "carousel", "gallery", "plugin", "react", "vue", "slideshow" ], "author": "Vladimir Kharlampidi", "license": "MIT", "bugs": { "url": "https://github.com/nolimits4web/swiper/issues" }, "homepage": "https://swiperjs.com", "funding": [ { "type": "patreon", "url": "https://www.patreon.com/swiperjs" }, { "type": "open_collective", "url": "http://opencollective.com/swiper" } ], "engines": { "node": ">= 4.7.0" }, "releaseDate": "February 18, 2026" } ================================================ FILE: src/core/breakpoints/getBreakpoint.mjs ================================================ import { getWindow } from 'ssr-window'; export default function getBreakpoint(breakpoints, base = 'window', containerEl) { if (!breakpoints || (base === 'container' && !containerEl)) return undefined; let breakpoint = false; const window = getWindow(); const currentHeight = base === 'window' ? window.innerHeight : containerEl.clientHeight; const points = Object.keys(breakpoints).map((point) => { if (typeof point === 'string' && point.indexOf('@') === 0) { const minRatio = parseFloat(point.substr(1)); const value = currentHeight * minRatio; return { value, point }; } return { value: point, point }; }); points.sort((a, b) => parseInt(a.value, 10) - parseInt(b.value, 10)); for (let i = 0; i < points.length; i += 1) { const { point, value } = points[i]; if (base === 'window') { if (window.matchMedia(`(min-width: ${value}px)`).matches) { breakpoint = point; } } else if (value <= containerEl.clientWidth) { breakpoint = point; } } return breakpoint || 'max'; } ================================================ FILE: src/core/breakpoints/index.mjs ================================================ import setBreakpoint from './setBreakpoint.mjs'; import getBreakpoint from './getBreakpoint.mjs'; export default { setBreakpoint, getBreakpoint }; ================================================ FILE: src/core/breakpoints/setBreakpoint.mjs ================================================ import { getDocument } from 'ssr-window'; import { extend } from '../../shared/utils.mjs'; const isGridEnabled = (swiper, params) => { return swiper.grid && params.grid && params.grid.rows > 1; }; export default function setBreakpoint() { const swiper = this; const { realIndex, initialized, params, el } = swiper; const breakpoints = params.breakpoints; if (!breakpoints || (breakpoints && Object.keys(breakpoints).length === 0)) return; const document = getDocument(); // Get breakpoint for window/container width and update parameters const breakpointsBase = params.breakpointsBase === 'window' || !params.breakpointsBase ? params.breakpointsBase : 'container'; const breakpointContainer = ['window', 'container'].includes(params.breakpointsBase) || !params.breakpointsBase ? swiper.el : document.querySelector(params.breakpointsBase); const breakpoint = swiper.getBreakpoint(breakpoints, breakpointsBase, breakpointContainer); if (!breakpoint || swiper.currentBreakpoint === breakpoint) return; const breakpointOnlyParams = breakpoint in breakpoints ? breakpoints[breakpoint] : undefined; const breakpointParams = breakpointOnlyParams || swiper.originalParams; const wasMultiRow = isGridEnabled(swiper, params); const isMultiRow = isGridEnabled(swiper, breakpointParams); const wasGrabCursor = swiper.params.grabCursor; const isGrabCursor = breakpointParams.grabCursor; const wasEnabled = params.enabled; if (wasMultiRow && !isMultiRow) { el.classList.remove( `${params.containerModifierClass}grid`, `${params.containerModifierClass}grid-column`, ); swiper.emitContainerClasses(); } else if (!wasMultiRow && isMultiRow) { el.classList.add(`${params.containerModifierClass}grid`); if ( (breakpointParams.grid.fill && breakpointParams.grid.fill === 'column') || (!breakpointParams.grid.fill && params.grid.fill === 'column') ) { el.classList.add(`${params.containerModifierClass}grid-column`); } swiper.emitContainerClasses(); } if (wasGrabCursor && !isGrabCursor) { swiper.unsetGrabCursor(); } else if (!wasGrabCursor && isGrabCursor) { swiper.setGrabCursor(); } // Toggle navigation, pagination, scrollbar ['navigation', 'pagination', 'scrollbar'].forEach((prop) => { if (typeof breakpointParams[prop] === 'undefined') return; const wasModuleEnabled = params[prop] && params[prop].enabled; const isModuleEnabled = breakpointParams[prop] && breakpointParams[prop].enabled; if (wasModuleEnabled && !isModuleEnabled) { swiper[prop].disable(); } if (!wasModuleEnabled && isModuleEnabled) { swiper[prop].enable(); } }); const directionChanged = breakpointParams.direction && breakpointParams.direction !== params.direction; const needsReLoop = params.loop && (breakpointParams.slidesPerView !== params.slidesPerView || directionChanged); const wasLoop = params.loop; if (directionChanged && initialized) { swiper.changeDirection(); } extend(swiper.params, breakpointParams); const isEnabled = swiper.params.enabled; const hasLoop = swiper.params.loop; Object.assign(swiper, { allowTouchMove: swiper.params.allowTouchMove, allowSlideNext: swiper.params.allowSlideNext, allowSlidePrev: swiper.params.allowSlidePrev, }); if (wasEnabled && !isEnabled) { swiper.disable(); } else if (!wasEnabled && isEnabled) { swiper.enable(); } swiper.currentBreakpoint = breakpoint; swiper.emit('_beforeBreakpoint', breakpointParams); if (initialized) { if (needsReLoop) { swiper.loopDestroy(); swiper.loopCreate(realIndex); swiper.updateSlides(); } else if (!wasLoop && hasLoop) { swiper.loopCreate(realIndex); swiper.updateSlides(); } else if (wasLoop && !hasLoop) { swiper.loopDestroy(); } } swiper.emit('breakpoint', breakpointParams); } ================================================ FILE: src/core/check-overflow/index.mjs ================================================ function checkOverflow() { const swiper = this; const { isLocked: wasLocked, params } = swiper; const { slidesOffsetBefore } = params; if (slidesOffsetBefore) { const lastSlideIndex = swiper.slides.length - 1; const lastSlideRightEdge = swiper.slidesGrid[lastSlideIndex] + swiper.slidesSizesGrid[lastSlideIndex] + slidesOffsetBefore * 2; swiper.isLocked = swiper.size > lastSlideRightEdge; } else { swiper.isLocked = swiper.snapGrid.length === 1; } if (params.allowSlideNext === true) { swiper.allowSlideNext = !swiper.isLocked; } if (params.allowSlidePrev === true) { swiper.allowSlidePrev = !swiper.isLocked; } if (wasLocked && wasLocked !== swiper.isLocked) { swiper.isEnd = false; } if (wasLocked !== swiper.isLocked) { swiper.emit(swiper.isLocked ? 'lock' : 'unlock'); } } export default { checkOverflow }; ================================================ FILE: src/core/classes/addClasses.mjs ================================================ function prepareClasses(entries, prefix) { const resultClasses = []; entries.forEach((item) => { if (typeof item === 'object') { Object.keys(item).forEach((classNames) => { if (item[classNames]) { resultClasses.push(prefix + classNames); } }); } else if (typeof item === 'string') { resultClasses.push(prefix + item); } }); return resultClasses; } export default function addClasses() { const swiper = this; const { classNames, params, rtl, el, device } = swiper; // prettier-ignore const suffixes = prepareClasses([ 'initialized', params.direction, { 'free-mode': swiper.params.freeMode && params.freeMode.enabled }, { 'autoheight': params.autoHeight }, { 'rtl': rtl }, { 'grid': params.grid && params.grid.rows > 1 }, { 'grid-column': params.grid && params.grid.rows > 1 && params.grid.fill === 'column' }, { 'android': device.android }, { 'ios': device.ios }, { 'css-mode': params.cssMode }, { 'centered': params.cssMode && params.centeredSlides }, { 'watch-progress': params.watchSlidesProgress }, ], params.containerModifierClass); classNames.push(...suffixes); el.classList.add(...classNames); swiper.emitContainerClasses(); } ================================================ FILE: src/core/classes/index.mjs ================================================ import addClasses from './addClasses.mjs'; import removeClasses from './removeClasses.mjs'; export default { addClasses, removeClasses }; ================================================ FILE: src/core/classes/removeClasses.mjs ================================================ export default function removeClasses() { const swiper = this; const { el, classNames } = swiper; if (!el || typeof el === 'string') return; el.classList.remove(...classNames); swiper.emitContainerClasses(); } ================================================ FILE: src/core/core.mjs ================================================ /* eslint no-param-reassign: "off" */ import { getDocument } from 'ssr-window'; import { extend, deleteProps, createElement, elementChildren, elementStyle, elementIndex, } from '../shared/utils.mjs'; import { getSupport } from '../shared/get-support.mjs'; import { getDevice } from '../shared/get-device.mjs'; import { getBrowser } from '../shared/get-browser.mjs'; import Resize from './modules/resize/resize.mjs'; import Observer from './modules/observer/observer.mjs'; import eventsEmitter from './events-emitter.mjs'; import update from './update/index.mjs'; import translate from './translate/index.mjs'; import transition from './transition/index.mjs'; import slide from './slide/index.mjs'; import loop from './loop/index.mjs'; import grabCursor from './grab-cursor/index.mjs'; import events from './events/index.mjs'; import breakpoints from './breakpoints/index.mjs'; import classes from './classes/index.mjs'; import checkOverflow from './check-overflow/index.mjs'; import defaults from './defaults.mjs'; import moduleExtendParams from './moduleExtendParams.mjs'; import { processLazyPreloader, preload } from '../shared/process-lazy-preloader.mjs'; const prototypes = { eventsEmitter, update, translate, transition, slide, loop, grabCursor, events, breakpoints, checkOverflow, classes, }; const extendedDefaults = {}; class Swiper { constructor(...args) { let el; let params; if ( args.length === 1 && args[0].constructor && Object.prototype.toString.call(args[0]).slice(8, -1) === 'Object' ) { params = args[0]; } else { [el, params] = args; } if (!params) params = {}; params = extend({}, params); if (el && !params.el) params.el = el; const document = getDocument(); if ( params.el && typeof params.el === 'string' && document.querySelectorAll(params.el).length > 1 ) { const swipers = []; document.querySelectorAll(params.el).forEach((containerEl) => { const newParams = extend({}, params, { el: containerEl }); swipers.push(new Swiper(newParams)); }); // eslint-disable-next-line no-constructor-return return swipers; } // Swiper Instance const swiper = this; swiper.__swiper__ = true; swiper.support = getSupport(); swiper.device = getDevice({ userAgent: params.userAgent }); swiper.browser = getBrowser(); swiper.eventsListeners = {}; swiper.eventsAnyListeners = []; swiper.modules = [...swiper.__modules__]; if (params.modules && Array.isArray(params.modules)) { params.modules.forEach((mod) => { if (typeof mod === 'function' && swiper.modules.indexOf(mod) < 0) { swiper.modules.push(mod); } }); } const allModulesParams = {}; swiper.modules.forEach((mod) => { mod({ params, swiper, extendParams: moduleExtendParams(params, allModulesParams), on: swiper.on.bind(swiper), once: swiper.once.bind(swiper), off: swiper.off.bind(swiper), emit: swiper.emit.bind(swiper), }); }); // Extend defaults with modules params const swiperParams = extend({}, defaults, allModulesParams); // Extend defaults with passed params swiper.params = extend({}, swiperParams, extendedDefaults, params); swiper.originalParams = extend({}, swiper.params); swiper.passedParams = extend({}, params); // add event listeners if (swiper.params && swiper.params.on) { Object.keys(swiper.params.on).forEach((eventName) => { swiper.on(eventName, swiper.params.on[eventName]); }); } if (swiper.params && swiper.params.onAny) { swiper.onAny(swiper.params.onAny); } // Extend Swiper Object.assign(swiper, { enabled: swiper.params.enabled, el, // Classes classNames: [], // Slides slides: [], slidesGrid: [], snapGrid: [], slidesSizesGrid: [], // isDirection isHorizontal() { return swiper.params.direction === 'horizontal'; }, isVertical() { return swiper.params.direction === 'vertical'; }, // Indexes activeIndex: 0, realIndex: 0, // isBeginning: true, isEnd: false, // Props translate: 0, previousTranslate: 0, progress: 0, velocity: 0, animating: false, cssOverflowAdjustment() { // Returns 0 unless `translate` is > 2**23 // Should be subtracted from css values to prevent overflow return Math.trunc(this.translate / 2 ** 23) * 2 ** 23; }, // Locks allowSlideNext: swiper.params.allowSlideNext, allowSlidePrev: swiper.params.allowSlidePrev, // Touch Events touchEventsData: { isTouched: undefined, isMoved: undefined, allowTouchCallbacks: undefined, touchStartTime: undefined, isScrolling: undefined, currentTranslate: undefined, startTranslate: undefined, allowThresholdMove: undefined, // Form elements to match focusableElements: swiper.params.focusableElements, // Last click time lastClickTime: 0, clickTimeout: undefined, // Velocities velocities: [], allowMomentumBounce: undefined, startMoving: undefined, pointerId: null, touchId: null, }, // Clicks allowClick: true, // Touches allowTouchMove: swiper.params.allowTouchMove, touches: { startX: 0, startY: 0, currentX: 0, currentY: 0, diff: 0, }, // Images imagesToLoad: [], imagesLoaded: 0, }); swiper.emit('_swiper'); // Init if (swiper.params.init) { swiper.init(); } // Return app instance // eslint-disable-next-line no-constructor-return return swiper; } getDirectionLabel(property) { if (this.isHorizontal()) { return property; } // prettier-ignore return { 'width': 'height', 'margin-top': 'margin-left', 'margin-bottom ': 'margin-right', 'margin-left': 'margin-top', 'margin-right': 'margin-bottom', 'padding-left': 'padding-top', 'padding-right': 'padding-bottom', 'marginRight': 'marginBottom', }[property]; } getSlideIndex(slideEl) { const { slidesEl, params } = this; const slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`); const firstSlideIndex = elementIndex(slides[0]); return elementIndex(slideEl) - firstSlideIndex; } getSlideIndexByData(index) { return this.getSlideIndex( this.slides.find((slideEl) => slideEl.getAttribute('data-swiper-slide-index') * 1 === index), ); } getSlideIndexWhenGrid(index) { if (this.grid && this.params.grid && this.params.grid.rows > 1) { if (this.params.grid.fill === 'column') { index = Math.floor(index / this.params.grid.rows); } else if (this.params.grid.fill === 'row') { index = index % Math.ceil(this.slides.length / this.params.grid.rows); } } return index; } recalcSlides() { const swiper = this; const { slidesEl, params } = swiper; swiper.slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`); } enable() { const swiper = this; if (swiper.enabled) return; swiper.enabled = true; if (swiper.params.grabCursor) { swiper.setGrabCursor(); } swiper.emit('enable'); } disable() { const swiper = this; if (!swiper.enabled) return; swiper.enabled = false; if (swiper.params.grabCursor) { swiper.unsetGrabCursor(); } swiper.emit('disable'); } setProgress(progress, speed) { const swiper = this; progress = Math.min(Math.max(progress, 0), 1); const min = swiper.minTranslate(); const max = swiper.maxTranslate(); const current = (max - min) * progress + min; swiper.translateTo(current, typeof speed === 'undefined' ? 0 : speed); swiper.updateActiveIndex(); swiper.updateSlidesClasses(); } emitContainerClasses() { const swiper = this; if (!swiper.params._emitClasses || !swiper.el) return; const cls = swiper.el.className.split(' ').filter((className) => { return ( className.indexOf('swiper') === 0 || className.indexOf(swiper.params.containerModifierClass) === 0 ); }); swiper.emit('_containerClasses', cls.join(' ')); } getSlideClasses(slideEl) { const swiper = this; if (swiper.destroyed) return ''; return slideEl.className .split(' ') .filter((className) => { return ( className.indexOf('swiper-slide') === 0 || className.indexOf(swiper.params.slideClass) === 0 ); }) .join(' '); } emitSlidesClasses() { const swiper = this; if (!swiper.params._emitClasses || !swiper.el) return; const updates = []; swiper.slides.forEach((slideEl) => { const classNames = swiper.getSlideClasses(slideEl); updates.push({ slideEl, classNames }); swiper.emit('_slideClass', slideEl, classNames); }); swiper.emit('_slideClasses', updates); } slidesPerViewDynamic(view = 'current', exact = false) { const swiper = this; const { params, slides, slidesGrid, slidesSizesGrid, size: swiperSize, activeIndex } = swiper; let spv = 1; if (typeof params.slidesPerView === 'number') return params.slidesPerView; if (params.centeredSlides) { let slideSize = slides[activeIndex] ? Math.ceil(slides[activeIndex].swiperSlideSize) : 0; let breakLoop; for (let i = activeIndex + 1; i < slides.length; i += 1) { if (slides[i] && !breakLoop) { slideSize += Math.ceil(slides[i].swiperSlideSize); spv += 1; if (slideSize > swiperSize) breakLoop = true; } } for (let i = activeIndex - 1; i >= 0; i -= 1) { if (slides[i] && !breakLoop) { slideSize += slides[i].swiperSlideSize; spv += 1; if (slideSize > swiperSize) breakLoop = true; } } } else { // eslint-disable-next-line if (view === 'current') { for (let i = activeIndex + 1; i < slides.length; i += 1) { const slideInView = exact ? slidesGrid[i] + slidesSizesGrid[i] - slidesGrid[activeIndex] < swiperSize : slidesGrid[i] - slidesGrid[activeIndex] < swiperSize; if (slideInView) { spv += 1; } } } else { // previous for (let i = activeIndex - 1; i >= 0; i -= 1) { const slideInView = slidesGrid[activeIndex] - slidesGrid[i] < swiperSize; if (slideInView) { spv += 1; } } } } return spv; } update() { const swiper = this; if (!swiper || swiper.destroyed) return; const { snapGrid, params } = swiper; // Breakpoints if (params.breakpoints) { swiper.setBreakpoint(); } [...swiper.el.querySelectorAll('[loading="lazy"]')].forEach((imageEl) => { if (imageEl.complete) { processLazyPreloader(swiper, imageEl); } }); swiper.updateSize(); swiper.updateSlides(); swiper.updateProgress(); swiper.updateSlidesClasses(); function setTranslate() { const translateValue = swiper.rtlTranslate ? swiper.translate * -1 : swiper.translate; const newTranslate = Math.min( Math.max(translateValue, swiper.maxTranslate()), swiper.minTranslate(), ); swiper.setTranslate(newTranslate); swiper.updateActiveIndex(); swiper.updateSlidesClasses(); } let translated; if (params.freeMode && params.freeMode.enabled && !params.cssMode) { setTranslate(); if (params.autoHeight) { swiper.updateAutoHeight(); } } else { if ( (params.slidesPerView === 'auto' || params.slidesPerView > 1) && swiper.isEnd && !params.centeredSlides ) { const slides = swiper.virtual && params.virtual.enabled ? swiper.virtual.slides : swiper.slides; translated = swiper.slideTo(slides.length - 1, 0, false, true); } else { translated = swiper.slideTo(swiper.activeIndex, 0, false, true); } if (!translated) { setTranslate(); } } if (params.watchOverflow && snapGrid !== swiper.snapGrid) { swiper.checkOverflow(); } swiper.emit('update'); } changeDirection(newDirection, needUpdate = true) { const swiper = this; const currentDirection = swiper.params.direction; if (!newDirection) { // eslint-disable-next-line newDirection = currentDirection === 'horizontal' ? 'vertical' : 'horizontal'; } if ( newDirection === currentDirection || (newDirection !== 'horizontal' && newDirection !== 'vertical') ) { return swiper; } swiper.el.classList.remove(`${swiper.params.containerModifierClass}${currentDirection}`); swiper.el.classList.add(`${swiper.params.containerModifierClass}${newDirection}`); swiper.emitContainerClasses(); swiper.params.direction = newDirection; swiper.slides.forEach((slideEl) => { if (newDirection === 'vertical') { slideEl.style.width = ''; } else { slideEl.style.height = ''; } }); swiper.emit('changeDirection'); if (needUpdate) swiper.update(); return swiper; } changeLanguageDirection(direction) { const swiper = this; if ((swiper.rtl && direction === 'rtl') || (!swiper.rtl && direction === 'ltr')) return; swiper.rtl = direction === 'rtl'; swiper.rtlTranslate = swiper.params.direction === 'horizontal' && swiper.rtl; if (swiper.rtl) { swiper.el.classList.add(`${swiper.params.containerModifierClass}rtl`); swiper.el.dir = 'rtl'; } else { swiper.el.classList.remove(`${swiper.params.containerModifierClass}rtl`); swiper.el.dir = 'ltr'; } swiper.update(); } mount(element) { const swiper = this; if (swiper.mounted) return true; // Find el let el = element || swiper.params.el; if (typeof el === 'string') { el = document.querySelector(el); } if (!el) { return false; } el.swiper = swiper; if ( el.parentNode && el.parentNode.host && el.parentNode.host.nodeName === swiper.params.swiperElementNodeName.toUpperCase() ) { swiper.isElement = true; } const getWrapperSelector = () => { return `.${(swiper.params.wrapperClass || '').trim().split(' ').join('.')}`; }; const getWrapper = () => { if (el && el.shadowRoot && el.shadowRoot.querySelector) { const res = el.shadowRoot.querySelector(getWrapperSelector()); // Children needs to return slot items return res; } return elementChildren(el, getWrapperSelector())[0]; }; // Find Wrapper let wrapperEl = getWrapper(); if (!wrapperEl && swiper.params.createElements) { wrapperEl = createElement('div', swiper.params.wrapperClass); el.append(wrapperEl); elementChildren(el, `.${swiper.params.slideClass}`).forEach((slideEl) => { wrapperEl.append(slideEl); }); } Object.assign(swiper, { el, wrapperEl, slidesEl: swiper.isElement && !el.parentNode.host.slideSlots ? el.parentNode.host : wrapperEl, hostEl: swiper.isElement ? el.parentNode.host : el, mounted: true, // RTL rtl: el.dir.toLowerCase() === 'rtl' || elementStyle(el, 'direction') === 'rtl', rtlTranslate: swiper.params.direction === 'horizontal' && (el.dir.toLowerCase() === 'rtl' || elementStyle(el, 'direction') === 'rtl'), wrongRTL: elementStyle(wrapperEl, 'display') === '-webkit-box', }); return true; } init(el) { const swiper = this; if (swiper.initialized) return swiper; const mounted = swiper.mount(el); if (mounted === false) return swiper; swiper.emit('beforeInit'); // Set breakpoint if (swiper.params.breakpoints) { swiper.setBreakpoint(); } // Add Classes swiper.addClasses(); // Update size swiper.updateSize(); // Update slides swiper.updateSlides(); if (swiper.params.watchOverflow) { swiper.checkOverflow(); } // Set Grab Cursor if (swiper.params.grabCursor && swiper.enabled) { swiper.setGrabCursor(); } // Slide To Initial Slide if (swiper.params.loop && swiper.virtual && swiper.params.virtual.enabled) { swiper.slideTo( swiper.params.initialSlide + swiper.virtual.slidesBefore, 0, swiper.params.runCallbacksOnInit, false, true, ); } else { swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit, false, true); } // Create loop if (swiper.params.loop) { swiper.loopCreate(undefined, true); } // Attach events swiper.attachEvents(); const lazyElements = [...swiper.el.querySelectorAll('[loading="lazy"]')]; if (swiper.isElement) { lazyElements.push(...swiper.hostEl.querySelectorAll('[loading="lazy"]')); } lazyElements.forEach((imageEl) => { if (imageEl.complete) { processLazyPreloader(swiper, imageEl); } else { imageEl.addEventListener('load', (e) => { processLazyPreloader(swiper, e.target); }); } }); preload(swiper); // Init Flag swiper.initialized = true; preload(swiper); // Emit swiper.emit('init'); swiper.emit('afterInit'); return swiper; } destroy(deleteInstance = true, cleanStyles = true) { const swiper = this; const { params, el, wrapperEl, slides } = swiper; if (typeof swiper.params === 'undefined' || swiper.destroyed) { return null; } swiper.emit('beforeDestroy'); // Init Flag swiper.initialized = false; // Detach events swiper.detachEvents(); // Destroy loop if (params.loop) { swiper.loopDestroy(); } // Cleanup styles if (cleanStyles) { swiper.removeClasses(); if (el && typeof el !== 'string') { el.removeAttribute('style'); } if (wrapperEl) { wrapperEl.removeAttribute('style'); } if (slides && slides.length) { slides.forEach((slideEl) => { slideEl.classList.remove( params.slideVisibleClass, params.slideFullyVisibleClass, params.slideActiveClass, params.slideNextClass, params.slidePrevClass, ); slideEl.removeAttribute('style'); slideEl.removeAttribute('data-swiper-slide-index'); }); } } swiper.emit('destroy'); // Detach emitter events Object.keys(swiper.eventsListeners).forEach((eventName) => { swiper.off(eventName); }); if (deleteInstance !== false) { if (swiper.el && typeof swiper.el !== 'string') { swiper.el.swiper = null; } deleteProps(swiper); } swiper.destroyed = true; return null; } static extendDefaults(newDefaults) { extend(extendedDefaults, newDefaults); } static get extendedDefaults() { return extendedDefaults; } static get defaults() { return defaults; } static installModule(mod) { if (!Swiper.prototype.__modules__) Swiper.prototype.__modules__ = []; const modules = Swiper.prototype.__modules__; if (typeof mod === 'function' && modules.indexOf(mod) < 0) { modules.push(mod); } } static use(module) { if (Array.isArray(module)) { module.forEach((m) => Swiper.installModule(m)); return Swiper; } Swiper.installModule(module); return Swiper; } } Object.keys(prototypes).forEach((prototypeGroup) => { Object.keys(prototypes[prototypeGroup]).forEach((protoMethod) => { Swiper.prototype[protoMethod] = prototypes[prototypeGroup][protoMethod]; }); }); Swiper.use([Resize, Observer]); export default Swiper; ================================================ FILE: src/core/defaults.mjs ================================================ export default { init: true, direction: 'horizontal', oneWayMovement: false, swiperElementNodeName: 'SWIPER-CONTAINER', touchEventsTarget: 'wrapper', initialSlide: 0, speed: 300, cssMode: false, updateOnWindowResize: true, resizeObserver: true, nested: false, createElements: false, eventsPrefix: 'swiper', enabled: true, focusableElements: 'input, select, option, textarea, button, video, label', // Overrides width: null, height: null, // preventInteractionOnTransition: false, // ssr userAgent: null, url: null, // To support iOS's swipe-to-go-back gesture (when being used in-app). edgeSwipeDetection: false, edgeSwipeThreshold: 20, // Autoheight autoHeight: false, // Set wrapper width setWrapperSize: false, // Virtual Translate virtualTranslate: false, // Effects effect: 'slide', // 'slide' or 'fade' or 'cube' or 'coverflow' or 'flip' // Breakpoints breakpoints: undefined, breakpointsBase: 'window', // Slides grid spaceBetween: 0, slidesPerView: 1, slidesPerGroup: 1, slidesPerGroupSkip: 0, slidesPerGroupAuto: false, centeredSlides: false, centeredSlidesBounds: false, slidesOffsetBefore: 0, // in px slidesOffsetAfter: 0, // in px normalizeSlideIndex: true, centerInsufficientSlides: false, snapToSlideEdge: false, // Disable swiper and hide navigation when container not overflow watchOverflow: true, // Round length roundLengths: false, // Touches touchRatio: 1, touchAngle: 45, simulateTouch: true, shortSwipes: true, longSwipes: true, longSwipesRatio: 0.5, longSwipesMs: 300, followFinger: true, allowTouchMove: true, threshold: 5, touchMoveStopPropagation: false, touchStartPreventDefault: true, touchStartForcePreventDefault: false, touchReleaseOnEdges: false, // Unique Navigation Elements uniqueNavElements: true, // Resistance resistance: true, resistanceRatio: 0.85, // Progress watchSlidesProgress: false, // Cursor grabCursor: false, // Clicks preventClicks: true, preventClicksPropagation: true, slideToClickedSlide: false, // loop loop: false, loopAddBlankSlides: true, loopAdditionalSlides: 0, loopPreventsSliding: true, // rewind rewind: false, // Swiping/no swiping allowSlidePrev: true, allowSlideNext: true, swipeHandler: null, // '.swipe-handler', noSwiping: true, noSwipingClass: 'swiper-no-swiping', noSwipingSelector: null, // Passive Listeners passiveListeners: true, maxBackfaceHiddenSlides: 10, // NS containerModifierClass: 'swiper-', // NEW slideClass: 'swiper-slide', slideBlankClass: 'swiper-slide-blank', slideActiveClass: 'swiper-slide-active', slideVisibleClass: 'swiper-slide-visible', slideFullyVisibleClass: 'swiper-slide-fully-visible', slideNextClass: 'swiper-slide-next', slidePrevClass: 'swiper-slide-prev', wrapperClass: 'swiper-wrapper', lazyPreloaderClass: 'swiper-lazy-preloader', lazyPreloadPrevNext: 0, // Callbacks runCallbacksOnInit: true, // Internals _emitClasses: false, }; ================================================ FILE: src/core/events/index.mjs ================================================ import { getDocument } from 'ssr-window'; import onTouchStart from './onTouchStart.mjs'; import onTouchMove from './onTouchMove.mjs'; import onTouchEnd from './onTouchEnd.mjs'; import onResize from './onResize.mjs'; import onClick from './onClick.mjs'; import onScroll from './onScroll.mjs'; import onLoad from './onLoad.mjs'; import onDocumentTouchStart from './onDocumentTouchStart.mjs'; const events = (swiper, method) => { const document = getDocument(); const { params, el, wrapperEl, device } = swiper; const capture = !!params.nested; const domMethod = method === 'on' ? 'addEventListener' : 'removeEventListener'; const swiperMethod = method; if (!el || typeof el === 'string') return; // Touch Events document[domMethod]('touchstart', swiper.onDocumentTouchStart, { passive: false, capture }); el[domMethod]('touchstart', swiper.onTouchStart, { passive: false }); el[domMethod]('pointerdown', swiper.onTouchStart, { passive: false }); document[domMethod]('touchmove', swiper.onTouchMove, { passive: false, capture }); document[domMethod]('pointermove', swiper.onTouchMove, { passive: false, capture }); document[domMethod]('touchend', swiper.onTouchEnd, { passive: true }); document[domMethod]('pointerup', swiper.onTouchEnd, { passive: true }); document[domMethod]('pointercancel', swiper.onTouchEnd, { passive: true }); document[domMethod]('touchcancel', swiper.onTouchEnd, { passive: true }); document[domMethod]('pointerout', swiper.onTouchEnd, { passive: true }); document[domMethod]('pointerleave', swiper.onTouchEnd, { passive: true }); document[domMethod]('contextmenu', swiper.onTouchEnd, { passive: true }); // Prevent Links Clicks if (params.preventClicks || params.preventClicksPropagation) { el[domMethod]('click', swiper.onClick, true); } if (params.cssMode) { wrapperEl[domMethod]('scroll', swiper.onScroll); } // Resize handler if (params.updateOnWindowResize) { swiper[swiperMethod]( device.ios || device.android ? 'resize orientationchange observerUpdate' : 'resize observerUpdate', onResize, true, ); } else { swiper[swiperMethod]('observerUpdate', onResize, true); } // Images loader el[domMethod]('load', swiper.onLoad, { capture: true }); }; function attachEvents() { const swiper = this; const { params } = swiper; swiper.onTouchStart = onTouchStart.bind(swiper); swiper.onTouchMove = onTouchMove.bind(swiper); swiper.onTouchEnd = onTouchEnd.bind(swiper); swiper.onDocumentTouchStart = onDocumentTouchStart.bind(swiper); if (params.cssMode) { swiper.onScroll = onScroll.bind(swiper); } swiper.onClick = onClick.bind(swiper); swiper.onLoad = onLoad.bind(swiper); events(swiper, 'on'); } function detachEvents() { const swiper = this; events(swiper, 'off'); } export default { attachEvents, detachEvents, }; ================================================ FILE: src/core/events/onClick.mjs ================================================ export default function onClick(e) { const swiper = this; if (!swiper.enabled) return; if (!swiper.allowClick) { if (swiper.params.preventClicks) e.preventDefault(); if (swiper.params.preventClicksPropagation && swiper.animating) { e.stopPropagation(); e.stopImmediatePropagation(); } } } ================================================ FILE: src/core/events/onDocumentTouchStart.mjs ================================================ export default function onDocumentTouchStart() { const swiper = this; if (swiper.documentTouchHandlerProceeded) return; swiper.documentTouchHandlerProceeded = true; if (swiper.params.touchReleaseOnEdges) { swiper.el.style.touchAction = 'auto'; } } ================================================ FILE: src/core/events/onLoad.mjs ================================================ import { processLazyPreloader } from '../../shared/process-lazy-preloader.mjs'; export default function onLoad(e) { const swiper = this; processLazyPreloader(swiper, e.target); if ( swiper.params.cssMode || (swiper.params.slidesPerView !== 'auto' && !swiper.params.autoHeight) ) { return; } swiper.update(); } ================================================ FILE: src/core/events/onResize.mjs ================================================ export default function onResize() { const swiper = this; const { params, el } = swiper; if (el && el.offsetWidth === 0) return; // Breakpoints if (params.breakpoints) { swiper.setBreakpoint(); } // Save locks const { allowSlideNext, allowSlidePrev, snapGrid } = swiper; const isVirtual = swiper.virtual && swiper.params.virtual.enabled; // Disable locks on resize swiper.allowSlideNext = true; swiper.allowSlidePrev = true; swiper.updateSize(); swiper.updateSlides(); swiper.updateSlidesClasses(); const isVirtualLoop = isVirtual && params.loop; if ( (params.slidesPerView === 'auto' || params.slidesPerView > 1) && swiper.isEnd && !swiper.isBeginning && !swiper.params.centeredSlides && !isVirtualLoop ) { const slides = isVirtual ? swiper.virtual.slides : swiper.slides; swiper.slideTo(slides.length - 1, 0, false, true); } else { if (swiper.params.loop && !isVirtual) { swiper.slideToLoop(swiper.realIndex, 0, false, true); } else { swiper.slideTo(swiper.activeIndex, 0, false, true); } } if (swiper.autoplay && swiper.autoplay.running && swiper.autoplay.paused) { clearTimeout(swiper.autoplay.resizeTimeout); swiper.autoplay.resizeTimeout = setTimeout(() => { if (swiper.autoplay && swiper.autoplay.running && swiper.autoplay.paused) { swiper.autoplay.resume(); } }, 500); } // Return locks after resize swiper.allowSlidePrev = allowSlidePrev; swiper.allowSlideNext = allowSlideNext; if (swiper.params.watchOverflow && snapGrid !== swiper.snapGrid) { swiper.checkOverflow(); } } ================================================ FILE: src/core/events/onScroll.mjs ================================================ export default function onScroll() { const swiper = this; const { wrapperEl, rtlTranslate, enabled } = swiper; if (!enabled) return; swiper.previousTranslate = swiper.translate; if (swiper.isHorizontal()) { swiper.translate = -wrapperEl.scrollLeft; } else { swiper.translate = -wrapperEl.scrollTop; } // eslint-disable-next-line if (swiper.translate === 0) swiper.translate = 0; swiper.updateActiveIndex(); swiper.updateSlidesClasses(); let newProgress; const translatesDiff = swiper.maxTranslate() - swiper.minTranslate(); if (translatesDiff === 0) { newProgress = 0; } else { newProgress = (swiper.translate - swiper.minTranslate()) / translatesDiff; } if (newProgress !== swiper.progress) { swiper.updateProgress(rtlTranslate ? -swiper.translate : swiper.translate); } swiper.emit('setTranslate', swiper.translate, false); } ================================================ FILE: src/core/events/onTouchEnd.mjs ================================================ import { now, nextTick } from '../../shared/utils.mjs'; export default function onTouchEnd(event) { const swiper = this; const data = swiper.touchEventsData; let e = event; if (e.originalEvent) e = e.originalEvent; let targetTouch; const isTouchEvent = e.type === 'touchend' || e.type === 'touchcancel'; if (!isTouchEvent) { if (data.touchId !== null) return; // return from pointer if we use touch if (e.pointerId !== data.pointerId) return; targetTouch = e; } else { targetTouch = [...e.changedTouches].find((t) => t.identifier === data.touchId); if (!targetTouch || targetTouch.identifier !== data.touchId) return; } if (['pointercancel', 'pointerout', 'pointerleave', 'contextmenu'].includes(e.type)) { const proceed = ['pointercancel', 'contextmenu'].includes(e.type) && (swiper.browser.isSafari || swiper.browser.isWebView); if (!proceed) { return; } } data.pointerId = null; data.touchId = null; const { params, touches, rtlTranslate: rtl, slidesGrid, enabled } = swiper; if (!enabled) return; if (!params.simulateTouch && e.pointerType === 'mouse') return; if (data.allowTouchCallbacks) { swiper.emit('touchEnd', e); } data.allowTouchCallbacks = false; if (!data.isTouched) { if (data.isMoved && params.grabCursor) { swiper.setGrabCursor(false); } data.isMoved = false; data.startMoving = false; return; } // Return Grab Cursor if ( params.grabCursor && data.isMoved && data.isTouched && (swiper.allowSlideNext === true || swiper.allowSlidePrev === true) ) { swiper.setGrabCursor(false); } // Time diff const touchEndTime = now(); const timeDiff = touchEndTime - data.touchStartTime; // Tap, doubleTap, Click if (swiper.allowClick) { const pathTree = e.path || (e.composedPath && e.composedPath()); swiper.updateClickedSlide((pathTree && pathTree[0]) || e.target, pathTree); swiper.emit('tap click', e); if (timeDiff < 300 && touchEndTime - data.lastClickTime < 300) { swiper.emit('doubleTap doubleClick', e); } } data.lastClickTime = now(); nextTick(() => { if (!swiper.destroyed) swiper.allowClick = true; }); if ( !data.isTouched || !data.isMoved || !swiper.swipeDirection || (touches.diff === 0 && !data.loopSwapReset) || (data.currentTranslate === data.startTranslate && !data.loopSwapReset) ) { data.isTouched = false; data.isMoved = false; data.startMoving = false; return; } data.isTouched = false; data.isMoved = false; data.startMoving = false; let currentPos; if (params.followFinger) { currentPos = rtl ? swiper.translate : -swiper.translate; } else { currentPos = -data.currentTranslate; } if (params.cssMode) { return; } if (params.freeMode && params.freeMode.enabled) { swiper.freeMode.onTouchEnd({ currentPos }); return; } // Find current slide const swipeToLast = currentPos >= -swiper.maxTranslate() && !swiper.params.loop; let stopIndex = 0; let groupSize = swiper.slidesSizesGrid[0]; for ( let i = 0; i < slidesGrid.length; i += i < params.slidesPerGroupSkip ? 1 : params.slidesPerGroup ) { const increment = i < params.slidesPerGroupSkip - 1 ? 1 : params.slidesPerGroup; if (typeof slidesGrid[i + increment] !== 'undefined') { if (swipeToLast || (currentPos >= slidesGrid[i] && currentPos < slidesGrid[i + increment])) { stopIndex = i; groupSize = slidesGrid[i + increment] - slidesGrid[i]; } } else if (swipeToLast || currentPos >= slidesGrid[i]) { stopIndex = i; groupSize = slidesGrid[slidesGrid.length - 1] - slidesGrid[slidesGrid.length - 2]; } } let rewindFirstIndex = null; let rewindLastIndex = null; if (params.rewind) { if (swiper.isBeginning) { rewindLastIndex = params.virtual && params.virtual.enabled && swiper.virtual ? swiper.virtual.slides.length - 1 : swiper.slides.length - 1; } else if (swiper.isEnd) { rewindFirstIndex = 0; } } // Find current slide size const ratio = (currentPos - slidesGrid[stopIndex]) / groupSize; const increment = stopIndex < params.slidesPerGroupSkip - 1 ? 1 : params.slidesPerGroup; if (timeDiff > params.longSwipesMs) { // Long touches if (!params.longSwipes) { swiper.slideTo(swiper.activeIndex); return; } if (swiper.swipeDirection === 'next') { if (ratio >= params.longSwipesRatio) swiper.slideTo(params.rewind && swiper.isEnd ? rewindFirstIndex : stopIndex + increment); else swiper.slideTo(stopIndex); } if (swiper.swipeDirection === 'prev') { if (ratio > 1 - params.longSwipesRatio) { swiper.slideTo(stopIndex + increment); } else if ( rewindLastIndex !== null && ratio < 0 && Math.abs(ratio) > params.longSwipesRatio ) { swiper.slideTo(rewindLastIndex); } else { swiper.slideTo(stopIndex); } } } else { // Short swipes if (!params.shortSwipes) { swiper.slideTo(swiper.activeIndex); return; } const isNavButtonTarget = swiper.navigation && (e.target === swiper.navigation.nextEl || e.target === swiper.navigation.prevEl); if (!isNavButtonTarget) { if (swiper.swipeDirection === 'next') { swiper.slideTo(rewindFirstIndex !== null ? rewindFirstIndex : stopIndex + increment); } if (swiper.swipeDirection === 'prev') { swiper.slideTo(rewindLastIndex !== null ? rewindLastIndex : stopIndex); } } else if (e.target === swiper.navigation.nextEl) { swiper.slideTo(stopIndex + increment); } else { swiper.slideTo(stopIndex); } } } ================================================ FILE: src/core/events/onTouchMove.mjs ================================================ import { getDocument } from 'ssr-window'; import { now } from '../../shared/utils.mjs'; export default function onTouchMove(event) { const document = getDocument(); const swiper = this; const data = swiper.touchEventsData; const { params, touches, rtlTranslate: rtl, enabled } = swiper; if (!enabled) return; if (!params.simulateTouch && event.pointerType === 'mouse') return; let e = event; if (e.originalEvent) e = e.originalEvent; if (e.type === 'pointermove') { if (data.touchId !== null) return; // return from pointer if we use touch const id = e.pointerId; if (id !== data.pointerId) return; } let targetTouch; if (e.type === 'touchmove') { targetTouch = [...e.changedTouches].find((t) => t.identifier === data.touchId); if (!targetTouch || targetTouch.identifier !== data.touchId) return; } else { targetTouch = e; } if (!data.isTouched) { if (data.startMoving && data.isScrolling) { swiper.emit('touchMoveOpposite', e); } return; } const pageX = targetTouch.pageX; const pageY = targetTouch.pageY; if (e.preventedByNestedSwiper) { touches.startX = pageX; touches.startY = pageY; return; } if (!swiper.allowTouchMove) { if (!e.target.matches(data.focusableElements)) { swiper.allowClick = false; } if (data.isTouched) { Object.assign(touches, { startX: pageX, startY: pageY, currentX: pageX, currentY: pageY, }); data.touchStartTime = now(); } return; } if (params.touchReleaseOnEdges && !params.loop) { if (swiper.isVertical()) { // Vertical if ( (pageY < touches.startY && swiper.translate <= swiper.maxTranslate()) || (pageY > touches.startY && swiper.translate >= swiper.minTranslate()) ) { data.isTouched = false; data.isMoved = false; return; } } else if ( rtl && ((pageX > touches.startX && -swiper.translate <= swiper.maxTranslate()) || (pageX < touches.startX && -swiper.translate >= swiper.minTranslate())) ) { return; } else if ( !rtl && ((pageX < touches.startX && swiper.translate <= swiper.maxTranslate()) || (pageX > touches.startX && swiper.translate >= swiper.minTranslate())) ) { return; } } if ( document.activeElement && document.activeElement.matches(data.focusableElements) && document.activeElement !== e.target && e.pointerType !== 'mouse' ) { document.activeElement.blur(); } if (document.activeElement) { if (e.target === document.activeElement && e.target.matches(data.focusableElements)) { data.isMoved = true; swiper.allowClick = false; return; } } if (data.allowTouchCallbacks) { swiper.emit('touchMove', e); } touches.previousX = touches.currentX; touches.previousY = touches.currentY; touches.currentX = pageX; touches.currentY = pageY; const diffX = touches.currentX - touches.startX; const diffY = touches.currentY - touches.startY; if (swiper.params.threshold && Math.sqrt(diffX ** 2 + diffY ** 2) < swiper.params.threshold) return; if (typeof data.isScrolling === 'undefined') { let touchAngle; if ( (swiper.isHorizontal() && touches.currentY === touches.startY) || (swiper.isVertical() && touches.currentX === touches.startX) ) { data.isScrolling = false; } else { // eslint-disable-next-line if (diffX * diffX + diffY * diffY >= 25) { touchAngle = (Math.atan2(Math.abs(diffY), Math.abs(diffX)) * 180) / Math.PI; data.isScrolling = swiper.isHorizontal() ? touchAngle > params.touchAngle : 90 - touchAngle > params.touchAngle; } } } if (data.isScrolling) { swiper.emit('touchMoveOpposite', e); } if (typeof data.startMoving === 'undefined') { if (touches.currentX !== touches.startX || touches.currentY !== touches.startY) { data.startMoving = true; } } if (data.isScrolling || (e.type === 'touchmove' && data.preventTouchMoveFromPointerMove)) { data.isTouched = false; return; } if (!data.startMoving) { return; } swiper.allowClick = false; if (!params.cssMode && e.cancelable) { e.preventDefault(); } if (params.touchMoveStopPropagation && !params.nested) { e.stopPropagation(); } let diff = swiper.isHorizontal() ? diffX : diffY; let touchesDiff = swiper.isHorizontal() ? touches.currentX - touches.previousX : touches.currentY - touches.previousY; if (params.oneWayMovement) { diff = Math.abs(diff) * (rtl ? 1 : -1); touchesDiff = Math.abs(touchesDiff) * (rtl ? 1 : -1); } touches.diff = diff; diff *= params.touchRatio; if (rtl) { diff = -diff; touchesDiff = -touchesDiff; } const prevTouchesDirection = swiper.touchesDirection; swiper.swipeDirection = diff > 0 ? 'prev' : 'next'; swiper.touchesDirection = touchesDiff > 0 ? 'prev' : 'next'; const isLoop = swiper.params.loop && !params.cssMode; const allowLoopFix = (swiper.touchesDirection === 'next' && swiper.allowSlideNext) || (swiper.touchesDirection === 'prev' && swiper.allowSlidePrev); if (!data.isMoved) { if (isLoop && allowLoopFix) { swiper.loopFix({ direction: swiper.swipeDirection }); } data.startTranslate = swiper.getTranslate(); swiper.setTransition(0); if (swiper.animating) { const evt = new window.CustomEvent('transitionend', { bubbles: true, cancelable: true, detail: { bySwiperTouchMove: true, }, }); swiper.wrapperEl.dispatchEvent(evt); } data.allowMomentumBounce = false; // Grab Cursor if (params.grabCursor && (swiper.allowSlideNext === true || swiper.allowSlidePrev === true)) { swiper.setGrabCursor(true); } swiper.emit('sliderFirstMove', e); } let loopFixed; let time = new Date().getTime(); if ( params._loopSwapReset !== false && data.isMoved && data.allowThresholdMove && prevTouchesDirection !== swiper.touchesDirection && isLoop && allowLoopFix && Math.abs(diff) >= 1 ) { Object.assign(touches, { startX: pageX, startY: pageY, currentX: pageX, currentY: pageY, startTranslate: data.currentTranslate, }); data.loopSwapReset = true; data.startTranslate = data.currentTranslate; return; } swiper.emit('sliderMove', e); data.isMoved = true; data.currentTranslate = diff + data.startTranslate; let disableParentSwiper = true; let resistanceRatio = params.resistanceRatio; if (params.touchReleaseOnEdges) { resistanceRatio = 0; } if (diff > 0) { if ( isLoop && allowLoopFix && !loopFixed && data.allowThresholdMove && data.currentTranslate > (params.centeredSlides ? swiper.minTranslate() - swiper.slidesSizesGrid[swiper.activeIndex + 1] - (params.slidesPerView !== 'auto' && swiper.slides.length - params.slidesPerView >= 2 ? swiper.slidesSizesGrid[swiper.activeIndex + 1] + swiper.params.spaceBetween : 0) - swiper.params.spaceBetween : swiper.minTranslate()) ) { swiper.loopFix({ direction: 'prev', setTranslate: true, activeSlideIndex: 0 }); } if (data.currentTranslate > swiper.minTranslate()) { disableParentSwiper = false; if (params.resistance) { data.currentTranslate = swiper.minTranslate() - 1 + (-swiper.minTranslate() + data.startTranslate + diff) ** resistanceRatio; } } } else if (diff < 0) { if ( isLoop && allowLoopFix && !loopFixed && data.allowThresholdMove && data.currentTranslate < (params.centeredSlides ? swiper.maxTranslate() + swiper.slidesSizesGrid[swiper.slidesSizesGrid.length - 1] + swiper.params.spaceBetween + (params.slidesPerView !== 'auto' && swiper.slides.length - params.slidesPerView >= 2 ? swiper.slidesSizesGrid[swiper.slidesSizesGrid.length - 1] + swiper.params.spaceBetween : 0) : swiper.maxTranslate()) ) { swiper.loopFix({ direction: 'next', setTranslate: true, activeSlideIndex: swiper.slides.length - (params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : Math.ceil(parseFloat(params.slidesPerView, 10))), }); } if (data.currentTranslate < swiper.maxTranslate()) { disableParentSwiper = false; if (params.resistance) { data.currentTranslate = swiper.maxTranslate() + 1 - (swiper.maxTranslate() - data.startTranslate - diff) ** resistanceRatio; } } } if (disableParentSwiper) { e.preventedByNestedSwiper = true; } // Directions locks if ( !swiper.allowSlideNext && swiper.swipeDirection === 'next' && data.currentTranslate < data.startTranslate ) { data.currentTranslate = data.startTranslate; } if ( !swiper.allowSlidePrev && swiper.swipeDirection === 'prev' && data.currentTranslate > data.startTranslate ) { data.currentTranslate = data.startTranslate; } if (!swiper.allowSlidePrev && !swiper.allowSlideNext) { data.currentTranslate = data.startTranslate; } // Threshold if (params.threshold > 0) { if (Math.abs(diff) > params.threshold || data.allowThresholdMove) { if (!data.allowThresholdMove) { data.allowThresholdMove = true; touches.startX = touches.currentX; touches.startY = touches.currentY; data.currentTranslate = data.startTranslate; touches.diff = swiper.isHorizontal() ? touches.currentX - touches.startX : touches.currentY - touches.startY; return; } } else { data.currentTranslate = data.startTranslate; return; } } if (!params.followFinger || params.cssMode) return; // Update active index in free mode if ( (params.freeMode && params.freeMode.enabled && swiper.freeMode) || params.watchSlidesProgress ) { swiper.updateActiveIndex(); swiper.updateSlidesClasses(); } if (params.freeMode && params.freeMode.enabled && swiper.freeMode) { swiper.freeMode.onTouchMove(); } // Update progress swiper.updateProgress(data.currentTranslate); // Update translate swiper.setTranslate(data.currentTranslate); } ================================================ FILE: src/core/events/onTouchStart.mjs ================================================ import { getWindow, getDocument } from 'ssr-window'; import { now, elementIsChildOf } from '../../shared/utils.mjs'; // Modified from https://stackoverflow.com/questions/54520554/custom-element-getrootnode-closest-function-crossing-multiple-parent-shadowd function closestElement(selector, base = this) { function __closestFrom(el) { if (!el || el === getDocument() || el === getWindow()) return null; if (el.assignedSlot) el = el.assignedSlot; const found = el.closest(selector); if (!found && !el.getRootNode) { return null; } return found || __closestFrom(el.getRootNode().host); } return __closestFrom(base); } function preventEdgeSwipe(swiper, event, startX) { const window = getWindow(); const { params } = swiper; const edgeSwipeDetection = params.edgeSwipeDetection; const edgeSwipeThreshold = params.edgeSwipeThreshold; if ( edgeSwipeDetection && (startX <= edgeSwipeThreshold || startX >= window.innerWidth - edgeSwipeThreshold) ) { if (edgeSwipeDetection === 'prevent') { event.preventDefault(); return true; } return false; } return true; } export default function onTouchStart(event) { const swiper = this; const document = getDocument(); let e = event; if (e.originalEvent) e = e.originalEvent; const data = swiper.touchEventsData; if (e.type === 'pointerdown') { if (data.pointerId !== null && data.pointerId !== e.pointerId) { return; } data.pointerId = e.pointerId; } else if (e.type === 'touchstart' && e.targetTouches.length === 1) { data.touchId = e.targetTouches[0].identifier; } if (e.type === 'touchstart') { // don't proceed touch event preventEdgeSwipe(swiper, e, e.targetTouches[0].pageX); return; } const { params, touches, enabled } = swiper; if (!enabled) return; if (!params.simulateTouch && e.pointerType === 'mouse') return; if (swiper.animating && params.preventInteractionOnTransition) { return; } if (!swiper.animating && params.cssMode && params.loop) { swiper.loopFix(); } let targetEl = e.target; if (params.touchEventsTarget === 'wrapper') { if (!elementIsChildOf(targetEl, swiper.wrapperEl)) return; } if ('which' in e && e.which === 3) return; if ('button' in e && e.button > 0) return; if (data.isTouched && data.isMoved) return; // change target el for shadow root component const swipingClassHasValue = !!params.noSwipingClass && params.noSwipingClass !== ''; // eslint-disable-next-line const eventPath = e.composedPath ? e.composedPath() : e.path; if (swipingClassHasValue && e.target && e.target.shadowRoot && eventPath) { targetEl = eventPath[0]; } const noSwipingSelector = params.noSwipingSelector ? params.noSwipingSelector : `.${params.noSwipingClass}`; const isTargetShadow = !!(e.target && e.target.shadowRoot); // use closestElement for shadow root element to get the actual closest for nested shadow root element if ( params.noSwiping && (isTargetShadow ? closestElement(noSwipingSelector, targetEl) : targetEl.closest(noSwipingSelector)) ) { swiper.allowClick = true; return; } if (params.swipeHandler) { if (!targetEl.closest(params.swipeHandler)) return; } touches.currentX = e.pageX; touches.currentY = e.pageY; const startX = touches.currentX; const startY = touches.currentY; // Do NOT start if iOS edge swipe is detected. Otherwise iOS app cannot swipe-to-go-back anymore if (!preventEdgeSwipe(swiper, e, startX)) { return; } Object.assign(data, { isTouched: true, isMoved: false, allowTouchCallbacks: true, isScrolling: undefined, startMoving: undefined, }); touches.startX = startX; touches.startY = startY; data.touchStartTime = now(); swiper.allowClick = true; swiper.updateSize(); swiper.swipeDirection = undefined; if (params.threshold > 0) data.allowThresholdMove = false; let preventDefault = true; if (targetEl.matches(data.focusableElements)) { preventDefault = false; if (targetEl.nodeName === 'SELECT') { data.isTouched = false; } } if ( document.activeElement && document.activeElement.matches(data.focusableElements) && document.activeElement !== targetEl && (e.pointerType === 'mouse' || (e.pointerType !== 'mouse' && !targetEl.matches(data.focusableElements))) ) { document.activeElement.blur(); } const shouldPreventDefault = preventDefault && swiper.allowTouchMove && params.touchStartPreventDefault; if ( (params.touchStartForcePreventDefault || shouldPreventDefault) && !targetEl.isContentEditable ) { e.preventDefault(); } if ( params.freeMode && params.freeMode.enabled && swiper.freeMode && swiper.animating && !params.cssMode ) { swiper.freeMode.onTouchStart(); } swiper.emit('touchStart', e); } ================================================ FILE: src/core/events-emitter.mjs ================================================ /* eslint-disable no-underscore-dangle */ export default { on(events, handler, priority) { const self = this; if (!self.eventsListeners || self.destroyed) return self; if (typeof handler !== 'function') return self; const method = priority ? 'unshift' : 'push'; events.split(' ').forEach((event) => { if (!self.eventsListeners[event]) self.eventsListeners[event] = []; self.eventsListeners[event][method](handler); }); return self; }, once(events, handler, priority) { const self = this; if (!self.eventsListeners || self.destroyed) return self; if (typeof handler !== 'function') return self; function onceHandler(...args) { self.off(events, onceHandler); if (onceHandler.__emitterProxy) { delete onceHandler.__emitterProxy; } handler.apply(self, args); } onceHandler.__emitterProxy = handler; return self.on(events, onceHandler, priority); }, onAny(handler, priority) { const self = this; if (!self.eventsListeners || self.destroyed) return self; if (typeof handler !== 'function') return self; const method = priority ? 'unshift' : 'push'; if (self.eventsAnyListeners.indexOf(handler) < 0) { self.eventsAnyListeners[method](handler); } return self; }, offAny(handler) { const self = this; if (!self.eventsListeners || self.destroyed) return self; if (!self.eventsAnyListeners) return self; const index = self.eventsAnyListeners.indexOf(handler); if (index >= 0) { self.eventsAnyListeners.splice(index, 1); } return self; }, off(events, handler) { const self = this; if (!self.eventsListeners || self.destroyed) return self; if (!self.eventsListeners) return self; events.split(' ').forEach((event) => { if (typeof handler === 'undefined') { self.eventsListeners[event] = []; } else if (self.eventsListeners[event]) { self.eventsListeners[event].forEach((eventHandler, index) => { if ( eventHandler === handler || (eventHandler.__emitterProxy && eventHandler.__emitterProxy === handler) ) { self.eventsListeners[event].splice(index, 1); } }); } }); return self; }, emit(...args) { const self = this; if (!self.eventsListeners || self.destroyed) return self; if (!self.eventsListeners) return self; let events; let data; let context; if (typeof args[0] === 'string' || Array.isArray(args[0])) { events = args[0]; data = args.slice(1, args.length); context = self; } else { events = args[0].events; data = args[0].data; context = args[0].context || self; } data.unshift(context); const eventsArray = Array.isArray(events) ? events : events.split(' '); eventsArray.forEach((event) => { if (self.eventsAnyListeners && self.eventsAnyListeners.length) { self.eventsAnyListeners.forEach((eventHandler) => { eventHandler.apply(context, [event, ...data]); }); } if (self.eventsListeners && self.eventsListeners[event]) { self.eventsListeners[event].forEach((eventHandler) => { eventHandler.apply(context, data); }); } }); return self; }, }; ================================================ FILE: src/core/grab-cursor/index.mjs ================================================ import setGrabCursor from './setGrabCursor.mjs'; import unsetGrabCursor from './unsetGrabCursor.mjs'; export default { setGrabCursor, unsetGrabCursor, }; ================================================ FILE: src/core/grab-cursor/setGrabCursor.mjs ================================================ export default function setGrabCursor(moving) { const swiper = this; if ( !swiper.params.simulateTouch || (swiper.params.watchOverflow && swiper.isLocked) || swiper.params.cssMode ) return; const el = swiper.params.touchEventsTarget === 'container' ? swiper.el : swiper.wrapperEl; if (swiper.isElement) { swiper.__preventObserver__ = true; } el.style.cursor = 'move'; el.style.cursor = moving ? 'grabbing' : 'grab'; if (swiper.isElement) { requestAnimationFrame(() => { swiper.__preventObserver__ = false; }); } } ================================================ FILE: src/core/grab-cursor/unsetGrabCursor.mjs ================================================ export default function unsetGrabCursor() { const swiper = this; if ((swiper.params.watchOverflow && swiper.isLocked) || swiper.params.cssMode) { return; } if (swiper.isElement) { swiper.__preventObserver__ = true; } swiper[swiper.params.touchEventsTarget === 'container' ? 'el' : 'wrapperEl'].style.cursor = ''; if (swiper.isElement) { requestAnimationFrame(() => { swiper.__preventObserver__ = false; }); } } ================================================ FILE: src/core/loop/index.mjs ================================================ import loopCreate from './loopCreate.mjs'; import loopFix from './loopFix.mjs'; import loopDestroy from './loopDestroy.mjs'; export default { loopCreate, loopFix, loopDestroy, }; ================================================ FILE: src/core/loop/loopCreate.mjs ================================================ import { createElement, elementChildren, showWarning } from '../../shared/utils.mjs'; export default function loopCreate(slideRealIndex, initial) { const swiper = this; const { params, slidesEl } = swiper; if (!params.loop || (swiper.virtual && swiper.params.virtual.enabled)) return; const initSlides = () => { const slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`); slides.forEach((el, index) => { el.setAttribute('data-swiper-slide-index', index); }); }; const clearBlankSlides = () => { const slides = elementChildren(slidesEl, `.${params.slideBlankClass}`); slides.forEach((el) => { el.remove(); }); if (slides.length > 0) { swiper.recalcSlides(); swiper.updateSlides(); } }; const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1; if (params.loopAddBlankSlides && (params.slidesPerGroup > 1 || gridEnabled)) { clearBlankSlides(); } const slidesPerGroup = params.slidesPerGroup * (gridEnabled ? params.grid.rows : 1); const shouldFillGroup = swiper.slides.length % slidesPerGroup !== 0; const shouldFillGrid = gridEnabled && swiper.slides.length % params.grid.rows !== 0; const addBlankSlides = (amountOfSlides) => { for (let i = 0; i < amountOfSlides; i += 1) { const slideEl = swiper.isElement ? createElement('swiper-slide', [params.slideBlankClass]) : createElement('div', [params.slideClass, params.slideBlankClass]); swiper.slidesEl.append(slideEl); } }; if (shouldFillGroup) { if (params.loopAddBlankSlides) { const slidesToAdd = slidesPerGroup - (swiper.slides.length % slidesPerGroup); addBlankSlides(slidesToAdd); swiper.recalcSlides(); swiper.updateSlides(); } else { showWarning( 'Swiper Loop Warning: The number of slides is not even to slidesPerGroup, loop mode may not function properly. You need to add more slides (or make duplicates, or empty slides)', ); } initSlides(); } else if (shouldFillGrid) { if (params.loopAddBlankSlides) { const slidesToAdd = params.grid.rows - (swiper.slides.length % params.grid.rows); addBlankSlides(slidesToAdd); swiper.recalcSlides(); swiper.updateSlides(); } else { showWarning( 'Swiper Loop Warning: The number of slides is not even to grid.rows, loop mode may not function properly. You need to add more slides (or make duplicates, or empty slides)', ); } initSlides(); } else { initSlides(); } const bothDirections = params.centeredSlides || !!params.slidesOffsetBefore || !!params.slidesOffsetAfter; swiper.loopFix({ slideRealIndex, direction: bothDirections ? undefined : 'next', initial, }); } ================================================ FILE: src/core/loop/loopDestroy.mjs ================================================ export default function loopDestroy() { const swiper = this; const { params, slidesEl } = swiper; if (!params.loop || !slidesEl || (swiper.virtual && swiper.params.virtual.enabled)) return; swiper.recalcSlides(); const newSlidesOrder = []; swiper.slides.forEach((slideEl) => { const index = typeof slideEl.swiperSlideIndex === 'undefined' ? slideEl.getAttribute('data-swiper-slide-index') * 1 : slideEl.swiperSlideIndex; newSlidesOrder[index] = slideEl; }); swiper.slides.forEach((slideEl) => { slideEl.removeAttribute('data-swiper-slide-index'); }); newSlidesOrder.forEach((slideEl) => { slidesEl.append(slideEl); }); swiper.recalcSlides(); swiper.slideTo(swiper.realIndex, 0); } ================================================ FILE: src/core/loop/loopFix.mjs ================================================ import { showWarning } from '../../shared/utils.mjs'; export default function loopFix({ slideRealIndex, slideTo = true, direction, setTranslate, activeSlideIndex, initial, byController, byMousewheel, } = {}) { const swiper = this; if (!swiper.params.loop) return; swiper.emit('beforeLoopFix'); const { slides, allowSlidePrev, allowSlideNext, slidesEl, params } = swiper; const { centeredSlides, slidesOffsetBefore, slidesOffsetAfter, initialSlide } = params; const bothDirections = centeredSlides || !!slidesOffsetBefore || !!slidesOffsetAfter; swiper.allowSlidePrev = true; swiper.allowSlideNext = true; if (swiper.virtual && params.virtual.enabled) { if (slideTo) { if (!bothDirections && swiper.snapIndex === 0) { swiper.slideTo(swiper.virtual.slides.length, 0, false, true); } else if (bothDirections && swiper.snapIndex < params.slidesPerView) { swiper.slideTo(swiper.virtual.slides.length + swiper.snapIndex, 0, false, true); } else if (swiper.snapIndex === swiper.snapGrid.length - 1) { swiper.slideTo(swiper.virtual.slidesBefore, 0, false, true); } } swiper.allowSlidePrev = allowSlidePrev; swiper.allowSlideNext = allowSlideNext; swiper.emit('loopFix'); return; } let slidesPerView = params.slidesPerView; if (slidesPerView === 'auto') { slidesPerView = swiper.slidesPerViewDynamic(); } else { slidesPerView = Math.ceil(parseFloat(params.slidesPerView, 10)); if (bothDirections && slidesPerView % 2 === 0) { slidesPerView = slidesPerView + 1; } } const slidesPerGroup = params.slidesPerGroupAuto ? slidesPerView : params.slidesPerGroup; let loopedSlides = bothDirections ? Math.max(slidesPerGroup, Math.ceil(slidesPerView / 2)) : slidesPerGroup; if (loopedSlides % slidesPerGroup !== 0) { loopedSlides += slidesPerGroup - (loopedSlides % slidesPerGroup); } loopedSlides += params.loopAdditionalSlides; swiper.loopedSlides = loopedSlides; const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1; if ( slides.length < slidesPerView + loopedSlides || (swiper.params.effect === 'cards' && slides.length < slidesPerView + loopedSlides * 2) ) { showWarning( 'Swiper Loop Warning: The number of slides is not enough for loop mode, it will be disabled or not function properly. You need to add more slides (or make duplicates) or lower the values of slidesPerView and slidesPerGroup parameters', ); } else if (gridEnabled && params.grid.fill === 'row') { showWarning('Swiper Loop Warning: Loop mode is not compatible with grid.fill = `row`'); } const prependSlidesIndexes = []; const appendSlidesIndexes = []; const cols = gridEnabled ? Math.ceil(slides.length / params.grid.rows) : slides.length; const isInitialOverflow = initial && cols - initialSlide < slidesPerView && !bothDirections; let activeIndex = isInitialOverflow ? initialSlide : swiper.activeIndex; if (typeof activeSlideIndex === 'undefined') { activeSlideIndex = swiper.getSlideIndex( slides.find((el) => el.classList.contains(params.slideActiveClass)), ); } else { activeIndex = activeSlideIndex; } const isNext = direction === 'next' || !direction; const isPrev = direction === 'prev' || !direction; let slidesPrepended = 0; let slidesAppended = 0; const activeColIndex = gridEnabled ? slides[activeSlideIndex].column : activeSlideIndex; const activeColIndexWithShift = activeColIndex + (bothDirections && typeof setTranslate === 'undefined' ? -slidesPerView / 2 + 0.5 : 0); // prepend last slides before start if (activeColIndexWithShift < loopedSlides) { slidesPrepended = Math.max(loopedSlides - activeColIndexWithShift, slidesPerGroup); for (let i = 0; i < loopedSlides - activeColIndexWithShift; i += 1) { const index = i - Math.floor(i / cols) * cols; if (gridEnabled) { const colIndexToPrepend = cols - index - 1; for (let i = slides.length - 1; i >= 0; i -= 1) { if (slides[i].column === colIndexToPrepend) prependSlidesIndexes.push(i); } // slides.forEach((slide, slideIndex) => { // if (slide.column === colIndexToPrepend) prependSlidesIndexes.push(slideIndex); // }); } else { prependSlidesIndexes.push(cols - index - 1); } } } else if (activeColIndexWithShift + slidesPerView > cols - loopedSlides) { slidesAppended = Math.max(activeColIndexWithShift - (cols - loopedSlides * 2), slidesPerGroup); if (isInitialOverflow) { slidesAppended = Math.max(slidesAppended, slidesPerView - cols + initialSlide + 1); } for (let i = 0; i < slidesAppended; i += 1) { const index = i - Math.floor(i / cols) * cols; if (gridEnabled) { slides.forEach((slide, slideIndex) => { if (slide.column === index) appendSlidesIndexes.push(slideIndex); }); } else { appendSlidesIndexes.push(index); } } } swiper.__preventObserver__ = true; requestAnimationFrame(() => { swiper.__preventObserver__ = false; }); if (swiper.params.effect === 'cards' && slides.length < slidesPerView + loopedSlides * 2) { if (appendSlidesIndexes.includes(activeSlideIndex)) { appendSlidesIndexes.splice(appendSlidesIndexes.indexOf(activeSlideIndex), 1); } if (prependSlidesIndexes.includes(activeSlideIndex)) { prependSlidesIndexes.splice(prependSlidesIndexes.indexOf(activeSlideIndex), 1); } } if (isPrev) { prependSlidesIndexes.forEach((index) => { slides[index].swiperLoopMoveDOM = true; slidesEl.prepend(slides[index]); slides[index].swiperLoopMoveDOM = false; }); } if (isNext) { appendSlidesIndexes.forEach((index) => { slides[index].swiperLoopMoveDOM = true; slidesEl.append(slides[index]); slides[index].swiperLoopMoveDOM = false; }); } swiper.recalcSlides(); if (params.slidesPerView === 'auto') { swiper.updateSlides(); } else if ( gridEnabled && ((prependSlidesIndexes.length > 0 && isPrev) || (appendSlidesIndexes.length > 0 && isNext)) ) { swiper.slides.forEach((slide, slideIndex) => { swiper.grid.updateSlide(slideIndex, slide, swiper.slides); }); } if (params.watchSlidesProgress) { swiper.updateSlidesOffset(); } if (slideTo) { if (prependSlidesIndexes.length > 0 && isPrev) { if (typeof slideRealIndex === 'undefined') { const currentSlideTranslate = swiper.slidesGrid[activeIndex]; const newSlideTranslate = swiper.slidesGrid[activeIndex + slidesPrepended]; const diff = newSlideTranslate - currentSlideTranslate; if (byMousewheel) { swiper.setTranslate(swiper.translate - diff); } else { swiper.slideTo(activeIndex + Math.ceil(slidesPrepended), 0, false, true); if (setTranslate) { swiper.touchEventsData.startTranslate = swiper.touchEventsData.startTranslate - diff; swiper.touchEventsData.currentTranslate = swiper.touchEventsData.currentTranslate - diff; } } } else { if (setTranslate) { const shift = gridEnabled ? prependSlidesIndexes.length / params.grid.rows : prependSlidesIndexes.length; swiper.slideTo(swiper.activeIndex + shift, 0, false, true); swiper.touchEventsData.currentTranslate = swiper.translate; } } } else if (appendSlidesIndexes.length > 0 && isNext) { if (typeof slideRealIndex === 'undefined') { const currentSlideTranslate = swiper.slidesGrid[activeIndex]; const newSlideTranslate = swiper.slidesGrid[activeIndex - slidesAppended]; const diff = newSlideTranslate - currentSlideTranslate; if (byMousewheel) { swiper.setTranslate(swiper.translate - diff); } else { swiper.slideTo(activeIndex - slidesAppended, 0, false, true); if (setTranslate) { swiper.touchEventsData.startTranslate = swiper.touchEventsData.startTranslate - diff; swiper.touchEventsData.currentTranslate = swiper.touchEventsData.currentTranslate - diff; } } } else { const shift = gridEnabled ? appendSlidesIndexes.length / params.grid.rows : appendSlidesIndexes.length; swiper.slideTo(swiper.activeIndex - shift, 0, false, true); } } } swiper.allowSlidePrev = allowSlidePrev; swiper.allowSlideNext = allowSlideNext; if (swiper.controller && swiper.controller.control && !byController) { const loopParams = { slideRealIndex, direction, setTranslate, activeSlideIndex, byController: true, }; if (Array.isArray(swiper.controller.control)) { swiper.controller.control.forEach((c) => { if (!c.destroyed && c.params.loop) c.loopFix({ ...loopParams, slideTo: c.params.slidesPerView === params.slidesPerView ? slideTo : false, }); }); } else if ( swiper.controller.control instanceof swiper.constructor && swiper.controller.control.params.loop ) { swiper.controller.control.loopFix({ ...loopParams, slideTo: swiper.controller.control.params.slidesPerView === params.slidesPerView ? slideTo : false, }); } } swiper.emit('loopFix'); } ================================================ FILE: src/core/moduleExtendParams.mjs ================================================ import { extend } from '../shared/utils.mjs'; export default function moduleExtendParams(params, allModulesParams) { return function extendParams(obj = {}) { const moduleParamName = Object.keys(obj)[0]; const moduleParams = obj[moduleParamName]; if (typeof moduleParams !== 'object' || moduleParams === null) { extend(allModulesParams, obj); return; } if (params[moduleParamName] === true) { params[moduleParamName] = { enabled: true }; } if ( moduleParamName === 'navigation' && params[moduleParamName] && params[moduleParamName].enabled && !params[moduleParamName].prevEl && !params[moduleParamName].nextEl ) { params[moduleParamName].auto = true; } if ( ['pagination', 'scrollbar'].indexOf(moduleParamName) >= 0 && params[moduleParamName] && params[moduleParamName].enabled && !params[moduleParamName].el ) { params[moduleParamName].auto = true; } if (!(moduleParamName in params && 'enabled' in moduleParams)) { extend(allModulesParams, obj); return; } if (typeof params[moduleParamName] === 'object' && !('enabled' in params[moduleParamName])) { params[moduleParamName].enabled = true; } if (!params[moduleParamName]) params[moduleParamName] = { enabled: false }; extend(allModulesParams, obj); }; } ================================================ FILE: src/core/modules/observer/observer.mjs ================================================ import { getWindow } from 'ssr-window'; import { elementParents } from '../../../shared/utils.mjs'; export default function Observer({ swiper, extendParams, on, emit }) { const observers = []; const window = getWindow(); const attach = (target, options = {}) => { const ObserverFunc = window.MutationObserver || window.WebkitMutationObserver; const observer = new ObserverFunc((mutations) => { // The observerUpdate event should only be triggered // once despite the number of mutations. Additional // triggers are redundant and are very costly if (swiper.__preventObserver__) return; if (mutations.length === 1) { emit('observerUpdate', mutations[0]); return; } const observerUpdate = function observerUpdate() { emit('observerUpdate', mutations[0]); }; if (window.requestAnimationFrame) { window.requestAnimationFrame(observerUpdate); } else { window.setTimeout(observerUpdate, 0); } }); observer.observe(target, { attributes: typeof options.attributes === 'undefined' ? true : options.attributes, childList: swiper.isElement || (typeof options.childList === 'undefined' ? true : options).childList, characterData: typeof options.characterData === 'undefined' ? true : options.characterData, }); observers.push(observer); }; const init = () => { if (!swiper.params.observer) return; if (swiper.params.observeParents) { const containerParents = elementParents(swiper.hostEl); for (let i = 0; i < containerParents.length; i += 1) { attach(containerParents[i]); } } // Observe container attach(swiper.hostEl, { childList: swiper.params.observeSlideChildren, }); // Observe wrapper attach(swiper.wrapperEl, { attributes: false }); }; const destroy = () => { observers.forEach((observer) => { observer.disconnect(); }); observers.splice(0, observers.length); }; extendParams({ observer: false, observeParents: false, observeSlideChildren: false, }); on('init', init); on('destroy', destroy); } ================================================ FILE: src/core/modules/resize/resize.mjs ================================================ import { getWindow } from 'ssr-window'; export default function Resize({ swiper, on, emit }) { const window = getWindow(); let observer = null; let animationFrame = null; const resizeHandler = () => { if (!swiper || swiper.destroyed || !swiper.initialized) return; emit('beforeResize'); emit('resize'); }; const createObserver = () => { if (!swiper || swiper.destroyed || !swiper.initialized) return; observer = new ResizeObserver((entries) => { animationFrame = window.requestAnimationFrame(() => { const { width, height } = swiper; let newWidth = width; let newHeight = height; entries.forEach(({ contentBoxSize, contentRect, target }) => { if (target && target !== swiper.el) return; newWidth = contentRect ? contentRect.width : (contentBoxSize[0] || contentBoxSize).inlineSize; newHeight = contentRect ? contentRect.height : (contentBoxSize[0] || contentBoxSize).blockSize; }); if (newWidth !== width || newHeight !== height) { resizeHandler(); } }); }); observer.observe(swiper.el); }; const removeObserver = () => { if (animationFrame) { window.cancelAnimationFrame(animationFrame); } if (observer && observer.unobserve && swiper.el) { observer.unobserve(swiper.el); observer = null; } }; const orientationChangeHandler = () => { if (!swiper || swiper.destroyed || !swiper.initialized) return; emit('orientationchange'); }; on('init', () => { if (swiper.params.resizeObserver && typeof window.ResizeObserver !== 'undefined') { createObserver(); return; } window.addEventListener('resize', resizeHandler); window.addEventListener('orientationchange', orientationChangeHandler); }); on('destroy', () => { removeObserver(); window.removeEventListener('resize', resizeHandler); window.removeEventListener('orientationchange', orientationChangeHandler); }); } ================================================ FILE: src/core/slide/index.mjs ================================================ import slideTo from './slideTo.mjs'; import slideToLoop from './slideToLoop.mjs'; import slideNext from './slideNext.mjs'; import slidePrev from './slidePrev.mjs'; import slideReset from './slideReset.mjs'; import slideToClosest from './slideToClosest.mjs'; import slideToClickedSlide from './slideToClickedSlide.mjs'; export default { slideTo, slideToLoop, slideNext, slidePrev, slideReset, slideToClosest, slideToClickedSlide, }; ================================================ FILE: src/core/slide/slideNext.mjs ================================================ /* eslint no-unused-vars: "off" */ export default function slideNext(speed, runCallbacks = true, internal) { const swiper = this; const { enabled, params, animating } = swiper; if (!enabled || swiper.destroyed) return swiper; if (typeof speed === 'undefined') { speed = swiper.params.speed; } let perGroup = params.slidesPerGroup; if (params.slidesPerView === 'auto' && params.slidesPerGroup === 1 && params.slidesPerGroupAuto) { perGroup = Math.max(swiper.slidesPerViewDynamic('current', true), 1); } const increment = swiper.activeIndex < params.slidesPerGroupSkip ? 1 : perGroup; const isVirtual = swiper.virtual && params.virtual.enabled; if (params.loop) { if (animating && !isVirtual && params.loopPreventsSliding) return false; swiper.loopFix({ direction: 'next' }); // eslint-disable-next-line swiper._clientLeft = swiper.wrapperEl.clientLeft; if (swiper.activeIndex === swiper.slides.length - 1 && params.cssMode) { requestAnimationFrame(() => { swiper.slideTo(swiper.activeIndex + increment, speed, runCallbacks, internal); }); return true; } } if (params.rewind && swiper.isEnd) { return swiper.slideTo(0, speed, runCallbacks, internal); } return swiper.slideTo(swiper.activeIndex + increment, speed, runCallbacks, internal); } ================================================ FILE: src/core/slide/slidePrev.mjs ================================================ /* eslint no-unused-vars: "off" */ export default function slidePrev(speed, runCallbacks = true, internal) { const swiper = this; const { params, snapGrid, slidesGrid, rtlTranslate, enabled, animating } = swiper; if (!enabled || swiper.destroyed) return swiper; if (typeof speed === 'undefined') { speed = swiper.params.speed; } const isVirtual = swiper.virtual && params.virtual.enabled; if (params.loop) { if (animating && !isVirtual && params.loopPreventsSliding) return false; swiper.loopFix({ direction: 'prev' }); // eslint-disable-next-line swiper._clientLeft = swiper.wrapperEl.clientLeft; } const translate = rtlTranslate ? swiper.translate : -swiper.translate; function normalize(val) { if (val < 0) return -Math.floor(Math.abs(val)); return Math.floor(val); } const normalizedTranslate = normalize(translate); const normalizedSnapGrid = snapGrid.map((val) => normalize(val)); const isFreeMode = params.freeMode && params.freeMode.enabled; let prevSnap = snapGrid[normalizedSnapGrid.indexOf(normalizedTranslate) - 1]; if (typeof prevSnap === 'undefined' && (params.cssMode || isFreeMode)) { let prevSnapIndex; snapGrid.forEach((snap, snapIndex) => { if (normalizedTranslate >= snap) { // prevSnap = snap; prevSnapIndex = snapIndex; } }); if (typeof prevSnapIndex !== 'undefined') { prevSnap = isFreeMode ? snapGrid[prevSnapIndex] : snapGrid[prevSnapIndex > 0 ? prevSnapIndex - 1 : prevSnapIndex]; } } let prevIndex = 0; if (typeof prevSnap !== 'undefined') { prevIndex = slidesGrid.indexOf(prevSnap); if (prevIndex < 0) prevIndex = swiper.activeIndex - 1; if ( params.slidesPerView === 'auto' && params.slidesPerGroup === 1 && params.slidesPerGroupAuto ) { prevIndex = prevIndex - swiper.slidesPerViewDynamic('previous', true) + 1; prevIndex = Math.max(prevIndex, 0); } } if (params.rewind && swiper.isBeginning) { const lastIndex = swiper.params.virtual && swiper.params.virtual.enabled && swiper.virtual ? swiper.virtual.slides.length - 1 : swiper.slides.length - 1; return swiper.slideTo(lastIndex, speed, runCallbacks, internal); } else if (params.loop && swiper.activeIndex === 0 && params.cssMode) { requestAnimationFrame(() => { swiper.slideTo(prevIndex, speed, runCallbacks, internal); }); return true; } return swiper.slideTo(prevIndex, speed, runCallbacks, internal); } ================================================ FILE: src/core/slide/slideReset.mjs ================================================ /* eslint no-unused-vars: "off" */ export default function slideReset(speed, runCallbacks = true, internal) { const swiper = this; if (swiper.destroyed) return; if (typeof speed === 'undefined') { speed = swiper.params.speed; } return swiper.slideTo(swiper.activeIndex, speed, runCallbacks, internal); } ================================================ FILE: src/core/slide/slideTo.mjs ================================================ import { getBrowser } from '../../shared/get-browser.mjs'; import { animateCSSModeScroll } from '../../shared/utils.mjs'; export default function slideTo(index = 0, speed, runCallbacks = true, internal, initial) { if (typeof index === 'string') { index = parseInt(index, 10); } const swiper = this; let slideIndex = index; if (slideIndex < 0) slideIndex = 0; const { params, snapGrid, slidesGrid, previousIndex, activeIndex, rtlTranslate: rtl, wrapperEl, enabled, } = swiper; if ( (!enabled && !internal && !initial) || swiper.destroyed || (swiper.animating && params.preventInteractionOnTransition) ) { return false; } if (typeof speed === 'undefined') { speed = swiper.params.speed; } const skip = Math.min(swiper.params.slidesPerGroupSkip, slideIndex); let snapIndex = skip + Math.floor((slideIndex - skip) / swiper.params.slidesPerGroup); if (snapIndex >= snapGrid.length) snapIndex = snapGrid.length - 1; const translate = -snapGrid[snapIndex]; // Normalize slideIndex if (params.normalizeSlideIndex) { for (let i = 0; i < slidesGrid.length; i += 1) { const normalizedTranslate = -Math.floor(translate * 100); const normalizedGrid = Math.floor(slidesGrid[i] * 100); const normalizedGridNext = Math.floor(slidesGrid[i + 1] * 100); if (typeof slidesGrid[i + 1] !== 'undefined') { if ( normalizedTranslate >= normalizedGrid && normalizedTranslate < normalizedGridNext - (normalizedGridNext - normalizedGrid) / 2 ) { slideIndex = i; } else if ( normalizedTranslate >= normalizedGrid && normalizedTranslate < normalizedGridNext ) { slideIndex = i + 1; } } else if (normalizedTranslate >= normalizedGrid) { slideIndex = i; } } } // Directions locks if (swiper.initialized && slideIndex !== activeIndex) { if ( !swiper.allowSlideNext && (rtl ? translate > swiper.translate && translate > swiper.minTranslate() : translate < swiper.translate && translate < swiper.minTranslate()) ) { return false; } if ( !swiper.allowSlidePrev && translate > swiper.translate && translate > swiper.maxTranslate() ) { if ((activeIndex || 0) !== slideIndex) { return false; } } } if (slideIndex !== (previousIndex || 0) && runCallbacks) { swiper.emit('beforeSlideChangeStart'); } // Update progress swiper.updateProgress(translate); let direction; if (slideIndex > activeIndex) direction = 'next'; else if (slideIndex < activeIndex) direction = 'prev'; else direction = 'reset'; // initial virtual const isVirtual = swiper.virtual && swiper.params.virtual.enabled; const isInitialVirtual = isVirtual && initial; // Update Index if ( !isInitialVirtual && ((rtl && -translate === swiper.translate) || (!rtl && translate === swiper.translate)) ) { swiper.updateActiveIndex(slideIndex); // Update Height if (params.autoHeight) { swiper.updateAutoHeight(); } swiper.updateSlidesClasses(); if (params.effect !== 'slide') { swiper.setTranslate(translate); } if (direction !== 'reset') { swiper.transitionStart(runCallbacks, direction); swiper.transitionEnd(runCallbacks, direction); } return false; } if (params.cssMode) { const isH = swiper.isHorizontal(); const t = rtl ? translate : -translate; if (speed === 0) { if (isVirtual) { swiper.wrapperEl.style.scrollSnapType = 'none'; swiper._immediateVirtual = true; } if (isVirtual && !swiper._cssModeVirtualInitialSet && swiper.params.initialSlide > 0) { swiper._cssModeVirtualInitialSet = true; requestAnimationFrame(() => { wrapperEl[isH ? 'scrollLeft' : 'scrollTop'] = t; }); } else { wrapperEl[isH ? 'scrollLeft' : 'scrollTop'] = t; } if (isVirtual) { requestAnimationFrame(() => { swiper.wrapperEl.style.scrollSnapType = ''; swiper._immediateVirtual = false; }); } } else { if (!swiper.support.smoothScroll) { animateCSSModeScroll({ swiper, targetPosition: t, side: isH ? 'left' : 'top' }); return true; } wrapperEl.scrollTo({ [isH ? 'left' : 'top']: t, behavior: 'smooth', }); } return true; } const browser = getBrowser(); const isSafari = browser.isSafari; if (isVirtual && !initial && isSafari && swiper.isElement) { swiper.virtual.update(false, false, slideIndex); } swiper.setTransition(speed); swiper.setTranslate(translate); swiper.updateActiveIndex(slideIndex); swiper.updateSlidesClasses(); swiper.emit('beforeTransitionStart', speed, internal); swiper.transitionStart(runCallbacks, direction); if (speed === 0) { swiper.transitionEnd(runCallbacks, direction); } else if (!swiper.animating) { swiper.animating = true; if (!swiper.onSlideToWrapperTransitionEnd) { swiper.onSlideToWrapperTransitionEnd = function transitionEnd(e) { if (!swiper || swiper.destroyed) return; if (e.target !== this) return; swiper.wrapperEl.removeEventListener('transitionend', swiper.onSlideToWrapperTransitionEnd); swiper.onSlideToWrapperTransitionEnd = null; delete swiper.onSlideToWrapperTransitionEnd; swiper.transitionEnd(runCallbacks, direction); }; } swiper.wrapperEl.addEventListener('transitionend', swiper.onSlideToWrapperTransitionEnd); } return true; } ================================================ FILE: src/core/slide/slideToClickedSlide.mjs ================================================ import { elementChildren, nextTick } from '../../shared/utils.mjs'; export default function slideToClickedSlide() { const swiper = this; if (swiper.destroyed) return; const { params, slidesEl } = swiper; const slidesPerView = params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : params.slidesPerView; let slideToIndex = swiper.getSlideIndexWhenGrid(swiper.clickedIndex); let realIndex; const slideSelector = swiper.isElement ? `swiper-slide` : `.${params.slideClass}`; const isGrid = swiper.grid && swiper.params.grid && swiper.params.grid.rows > 1; if (params.loop) { if (swiper.animating) return; realIndex = parseInt(swiper.clickedSlide.getAttribute('data-swiper-slide-index'), 10); if (params.centeredSlides) { swiper.slideToLoop(realIndex); } else if ( slideToIndex > (isGrid ? (swiper.slides.length - slidesPerView) / 2 - (swiper.params.grid.rows - 1) : swiper.slides.length - slidesPerView) ) { swiper.loopFix(); slideToIndex = swiper.getSlideIndex( elementChildren(slidesEl, `${slideSelector}[data-swiper-slide-index="${realIndex}"]`)[0], ); nextTick(() => { swiper.slideTo(slideToIndex); }); } else { swiper.slideTo(slideToIndex); } } else { swiper.slideTo(slideToIndex); } } ================================================ FILE: src/core/slide/slideToClosest.mjs ================================================ /* eslint no-unused-vars: "off" */ export default function slideToClosest(speed, runCallbacks = true, internal, threshold = 0.5) { const swiper = this; if (swiper.destroyed) return; if (typeof speed === 'undefined') { speed = swiper.params.speed; } let index = swiper.activeIndex; const skip = Math.min(swiper.params.slidesPerGroupSkip, index); const snapIndex = skip + Math.floor((index - skip) / swiper.params.slidesPerGroup); const translate = swiper.rtlTranslate ? swiper.translate : -swiper.translate; if (translate >= swiper.snapGrid[snapIndex]) { // The current translate is on or after the current snap index, so the choice // is between the current index and the one after it. const currentSnap = swiper.snapGrid[snapIndex]; const nextSnap = swiper.snapGrid[snapIndex + 1]; if (translate - currentSnap > (nextSnap - currentSnap) * threshold) { index += swiper.params.slidesPerGroup; } } else { // The current translate is before the current snap index, so the choice // is between the current index and the one before it. const prevSnap = swiper.snapGrid[snapIndex - 1]; const currentSnap = swiper.snapGrid[snapIndex]; if (translate - prevSnap <= (currentSnap - prevSnap) * threshold) { index -= swiper.params.slidesPerGroup; } } index = Math.max(index, 0); index = Math.min(index, swiper.slidesGrid.length - 1); return swiper.slideTo(index, speed, runCallbacks, internal); } ================================================ FILE: src/core/slide/slideToLoop.mjs ================================================ export default function slideToLoop(index = 0, speed, runCallbacks = true, internal) { if (typeof index === 'string') { const indexAsNumber = parseInt(index, 10); index = indexAsNumber; } const swiper = this; if (swiper.destroyed) return; if (typeof speed === 'undefined') { speed = swiper.params.speed; } const gridEnabled = swiper.grid && swiper.params.grid && swiper.params.grid.rows > 1; let newIndex = index; if (swiper.params.loop) { if (swiper.virtual && swiper.params.virtual.enabled) { // eslint-disable-next-line newIndex = newIndex + swiper.virtual.slidesBefore; } else { let targetSlideIndex; if (gridEnabled) { const slideIndex = newIndex * swiper.params.grid.rows; targetSlideIndex = swiper.slides.find( (slideEl) => slideEl.getAttribute('data-swiper-slide-index') * 1 === slideIndex, ).column; } else { targetSlideIndex = swiper.getSlideIndexByData(newIndex); } const cols = gridEnabled ? Math.ceil(swiper.slides.length / swiper.params.grid.rows) : swiper.slides.length; const { centeredSlides, slidesOffsetBefore, slidesOffsetAfter } = swiper.params; const bothDirections = centeredSlides || !!slidesOffsetBefore || !!slidesOffsetAfter; let slidesPerView = swiper.params.slidesPerView; if (slidesPerView === 'auto') { slidesPerView = swiper.slidesPerViewDynamic(); } else { slidesPerView = Math.ceil(parseFloat(swiper.params.slidesPerView, 10)); if (bothDirections && slidesPerView % 2 === 0) { slidesPerView = slidesPerView + 1; } } let needLoopFix = cols - targetSlideIndex < slidesPerView; if (bothDirections) { needLoopFix = needLoopFix || targetSlideIndex < Math.ceil(slidesPerView / 2); } if (internal && bothDirections && swiper.params.slidesPerView !== 'auto' && !gridEnabled) { needLoopFix = false; } if (needLoopFix) { const direction = bothDirections ? targetSlideIndex < swiper.activeIndex ? 'prev' : 'next' : targetSlideIndex - swiper.activeIndex - 1 < swiper.params.slidesPerView ? 'next' : 'prev'; swiper.loopFix({ direction, slideTo: true, activeSlideIndex: direction === 'next' ? targetSlideIndex + 1 : targetSlideIndex - cols + 1, slideRealIndex: direction === 'next' ? swiper.realIndex : undefined, }); } if (gridEnabled) { const slideIndex = newIndex * swiper.params.grid.rows; newIndex = swiper.slides.find( (slideEl) => slideEl.getAttribute('data-swiper-slide-index') * 1 === slideIndex, ).column; } else { newIndex = swiper.getSlideIndexByData(newIndex); } } } requestAnimationFrame(() => { swiper.slideTo(newIndex, speed, runCallbacks, internal); }); return swiper; } ================================================ FILE: src/core/transition/index.mjs ================================================ import setTransition from './setTransition.mjs'; import transitionStart from './transitionStart.mjs'; import transitionEnd from './transitionEnd.mjs'; export default { setTransition, transitionStart, transitionEnd, }; ================================================ FILE: src/core/transition/setTransition.mjs ================================================ export default function setTransition(duration, byController) { const swiper = this; if (!swiper.params.cssMode) { swiper.wrapperEl.style.transitionDuration = `${duration}ms`; swiper.wrapperEl.style.transitionDelay = duration === 0 ? `0ms` : ''; } swiper.emit('setTransition', duration, byController); } ================================================ FILE: src/core/transition/transitionEmit.mjs ================================================ export default function transitionEmit({ swiper, runCallbacks, direction, step }) { const { activeIndex, previousIndex } = swiper; let dir = direction; if (!dir) { if (activeIndex > previousIndex) dir = 'next'; else if (activeIndex < previousIndex) dir = 'prev'; else dir = 'reset'; } swiper.emit(`transition${step}`); if (runCallbacks && dir === 'reset') { swiper.emit(`slideResetTransition${step}`); } else if (runCallbacks && activeIndex !== previousIndex) { swiper.emit(`slideChangeTransition${step}`); if (dir === 'next') { swiper.emit(`slideNextTransition${step}`); } else { swiper.emit(`slidePrevTransition${step}`); } } } ================================================ FILE: src/core/transition/transitionEnd.mjs ================================================ import transitionEmit from './transitionEmit.mjs'; export default function transitionEnd(runCallbacks = true, direction) { const swiper = this; const { params } = swiper; swiper.animating = false; if (params.cssMode) return; swiper.setTransition(0); transitionEmit({ swiper, runCallbacks, direction, step: 'End' }); } ================================================ FILE: src/core/transition/transitionStart.mjs ================================================ import transitionEmit from './transitionEmit.mjs'; export default function transitionStart(runCallbacks = true, direction) { const swiper = this; const { params } = swiper; if (params.cssMode) return; if (params.autoHeight) { swiper.updateAutoHeight(); } transitionEmit({ swiper, runCallbacks, direction, step: 'Start' }); } ================================================ FILE: src/core/translate/getTranslate.mjs ================================================ import { getTranslate } from '../../shared/utils.mjs'; export default function getSwiperTranslate(axis = this.isHorizontal() ? 'x' : 'y') { const swiper = this; const { params, rtlTranslate: rtl, translate, wrapperEl } = swiper; if (params.virtualTranslate) { return rtl ? -translate : translate; } if (params.cssMode) { return translate; } let currentTranslate = getTranslate(wrapperEl, axis); currentTranslate += swiper.cssOverflowAdjustment(); if (rtl) currentTranslate = -currentTranslate; return currentTranslate || 0; } ================================================ FILE: src/core/translate/index.mjs ================================================ import getTranslate from './getTranslate.mjs'; import setTranslate from './setTranslate.mjs'; import minTranslate from './minTranslate.mjs'; import maxTranslate from './maxTranslate.mjs'; import translateTo from './translateTo.mjs'; export default { getTranslate, setTranslate, minTranslate, maxTranslate, translateTo, }; ================================================ FILE: src/core/translate/maxTranslate.mjs ================================================ export default function maxTranslate() { return -this.snapGrid[this.snapGrid.length - 1]; } ================================================ FILE: src/core/translate/minTranslate.mjs ================================================ export default function minTranslate() { return -this.snapGrid[0]; } ================================================ FILE: src/core/translate/setTranslate.mjs ================================================ export default function setTranslate(translate, byController) { const swiper = this; const { rtlTranslate: rtl, params, wrapperEl, progress } = swiper; let x = 0; let y = 0; const z = 0; if (swiper.isHorizontal()) { x = rtl ? -translate : translate; } else { y = translate; } if (params.roundLengths) { x = Math.floor(x); y = Math.floor(y); } swiper.previousTranslate = swiper.translate; swiper.translate = swiper.isHorizontal() ? x : y; if (params.cssMode) { wrapperEl[swiper.isHorizontal() ? 'scrollLeft' : 'scrollTop'] = swiper.isHorizontal() ? -x : -y; } else if (!params.virtualTranslate) { if (swiper.isHorizontal()) { x -= swiper.cssOverflowAdjustment(); } else { y -= swiper.cssOverflowAdjustment(); } wrapperEl.style.transform = `translate3d(${x}px, ${y}px, ${z}px)`; } // Check if we need to update progress let newProgress; const translatesDiff = swiper.maxTranslate() - swiper.minTranslate(); if (translatesDiff === 0) { newProgress = 0; } else { newProgress = (translate - swiper.minTranslate()) / translatesDiff; } if (newProgress !== progress) { swiper.updateProgress(translate); } swiper.emit('setTranslate', swiper.translate, byController); } ================================================ FILE: src/core/translate/translateTo.mjs ================================================ import { animateCSSModeScroll } from '../../shared/utils.mjs'; export default function translateTo( translate = 0, speed = this.params.speed, runCallbacks = true, translateBounds = true, internal, ) { const swiper = this; const { params, wrapperEl } = swiper; if (swiper.animating && params.preventInteractionOnTransition) { return false; } const minTranslate = swiper.minTranslate(); const maxTranslate = swiper.maxTranslate(); let newTranslate; if (translateBounds && translate > minTranslate) newTranslate = minTranslate; else if (translateBounds && translate < maxTranslate) newTranslate = maxTranslate; else newTranslate = translate; // Update progress swiper.updateProgress(newTranslate); if (params.cssMode) { const isH = swiper.isHorizontal(); if (speed === 0) { wrapperEl[isH ? 'scrollLeft' : 'scrollTop'] = -newTranslate; } else { if (!swiper.support.smoothScroll) { animateCSSModeScroll({ swiper, targetPosition: -newTranslate, side: isH ? 'left' : 'top' }); return true; } wrapperEl.scrollTo({ [isH ? 'left' : 'top']: -newTranslate, behavior: 'smooth', }); } return true; } if (speed === 0) { swiper.setTransition(0); swiper.setTranslate(newTranslate); if (runCallbacks) { swiper.emit('beforeTransitionStart', speed, internal); swiper.emit('transitionEnd'); } } else { swiper.setTransition(speed); swiper.setTranslate(newTranslate); if (runCallbacks) { swiper.emit('beforeTransitionStart', speed, internal); swiper.emit('transitionStart'); } if (!swiper.animating) { swiper.animating = true; if (!swiper.onTranslateToWrapperTransitionEnd) { swiper.onTranslateToWrapperTransitionEnd = function transitionEnd(e) { if (!swiper || swiper.destroyed) return; if (e.target !== this) return; swiper.wrapperEl.removeEventListener( 'transitionend', swiper.onTranslateToWrapperTransitionEnd, ); swiper.onTranslateToWrapperTransitionEnd = null; delete swiper.onTranslateToWrapperTransitionEnd; swiper.animating = false; if (runCallbacks) { swiper.emit('transitionEnd'); } }; } swiper.wrapperEl.addEventListener('transitionend', swiper.onTranslateToWrapperTransitionEnd); } } return true; } ================================================ FILE: src/core/update/index.mjs ================================================ import updateSize from './updateSize.mjs'; import updateSlides from './updateSlides.mjs'; import updateAutoHeight from './updateAutoHeight.mjs'; import updateSlidesOffset from './updateSlidesOffset.mjs'; import updateSlidesProgress from './updateSlidesProgress.mjs'; import updateProgress from './updateProgress.mjs'; import updateSlidesClasses from './updateSlidesClasses.mjs'; import updateActiveIndex from './updateActiveIndex.mjs'; import updateClickedSlide from './updateClickedSlide.mjs'; export default { updateSize, updateSlides, updateAutoHeight, updateSlidesOffset, updateSlidesProgress, updateProgress, updateSlidesClasses, updateActiveIndex, updateClickedSlide, }; ================================================ FILE: src/core/update/updateActiveIndex.mjs ================================================ import { preload } from '../../shared/process-lazy-preloader.mjs'; export function getActiveIndexByTranslate(swiper) { const { slidesGrid, params } = swiper; const translate = swiper.rtlTranslate ? swiper.translate : -swiper.translate; let activeIndex; for (let i = 0; i < slidesGrid.length; i += 1) { if (typeof slidesGrid[i + 1] !== 'undefined') { if ( translate >= slidesGrid[i] && translate < slidesGrid[i + 1] - (slidesGrid[i + 1] - slidesGrid[i]) / 2 ) { activeIndex = i; } else if (translate >= slidesGrid[i] && translate < slidesGrid[i + 1]) { activeIndex = i + 1; } } else if (translate >= slidesGrid[i]) { activeIndex = i; } } // Normalize slideIndex if (params.normalizeSlideIndex) { if (activeIndex < 0 || typeof activeIndex === 'undefined') activeIndex = 0; } return activeIndex; } export default function updateActiveIndex(newActiveIndex) { const swiper = this; const translate = swiper.rtlTranslate ? swiper.translate : -swiper.translate; const { snapGrid, params, activeIndex: previousIndex, realIndex: previousRealIndex, snapIndex: previousSnapIndex, } = swiper; let activeIndex = newActiveIndex; let snapIndex; const getVirtualRealIndex = (aIndex) => { let realIndex = aIndex - swiper.virtual.slidesBefore; if (realIndex < 0) { realIndex = swiper.virtual.slides.length + realIndex; } if (realIndex >= swiper.virtual.slides.length) { realIndex -= swiper.virtual.slides.length; } return realIndex; }; if (typeof activeIndex === 'undefined') { activeIndex = getActiveIndexByTranslate(swiper); } if (snapGrid.indexOf(translate) >= 0) { snapIndex = snapGrid.indexOf(translate); } else { const skip = Math.min(params.slidesPerGroupSkip, activeIndex); snapIndex = skip + Math.floor((activeIndex - skip) / params.slidesPerGroup); } if (snapIndex >= snapGrid.length) snapIndex = snapGrid.length - 1; if (activeIndex === previousIndex && !swiper.params.loop) { if (snapIndex !== previousSnapIndex) { swiper.snapIndex = snapIndex; swiper.emit('snapIndexChange'); } return; } if ( activeIndex === previousIndex && swiper.params.loop && swiper.virtual && swiper.params.virtual.enabled ) { swiper.realIndex = getVirtualRealIndex(activeIndex); return; } const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1; const normalizeSlideIndexToColumn = (index) => { if (!gridEnabled) return index; return Math.floor(index / params.grid.rows); }; // Get real index let realIndex; if (swiper.virtual && params.virtual.enabled) { if (params.loop) { realIndex = getVirtualRealIndex(activeIndex); } else { realIndex = activeIndex; } } else if (gridEnabled) { const firstSlideInColumn = swiper.slides.find((slideEl) => slideEl.column === activeIndex); let activeSlideIndex = parseInt(firstSlideInColumn.getAttribute('data-swiper-slide-index'), 10); if (Number.isNaN(activeSlideIndex)) { activeSlideIndex = Math.max(swiper.slides.indexOf(firstSlideInColumn), 0); } realIndex = Math.floor(activeSlideIndex / params.grid.rows); } else if (swiper.slides[activeIndex]) { const slideIndex = swiper.slides[activeIndex].getAttribute('data-swiper-slide-index'); if (slideIndex) { realIndex = parseInt(slideIndex, 10); } else { realIndex = activeIndex; } } else { realIndex = activeIndex; } Object.assign(swiper, { previousSnapIndex, snapIndex, previousRealIndex, realIndex, previousIndex, activeIndex, }); if (swiper.initialized) { preload(swiper); } swiper.emit('activeIndexChange'); swiper.emit('snapIndexChange'); if (swiper.initialized || swiper.params.runCallbacksOnInit) { if (previousRealIndex !== realIndex) { swiper.emit('realIndexChange'); } swiper.emit('slideChange'); } } ================================================ FILE: src/core/update/updateAutoHeight.mjs ================================================ export default function updateAutoHeight(speed) { const swiper = this; const activeSlides = []; const isVirtual = swiper.virtual && swiper.params.virtual.enabled; let newHeight = 0; let i; if (typeof speed === 'number') { swiper.setTransition(speed); } else if (speed === true) { swiper.setTransition(swiper.params.speed); } const getSlideByIndex = (index) => { if (isVirtual) { return swiper.slides[swiper.getSlideIndexByData(index)]; } return swiper.slides[index]; }; // Find slides currently in view if (swiper.params.slidesPerView !== 'auto' && swiper.params.slidesPerView > 1) { if (swiper.params.centeredSlides) { (swiper.visibleSlides || []).forEach((slide) => { activeSlides.push(slide); }); } else { for (i = 0; i < Math.ceil(swiper.params.slidesPerView); i += 1) { const index = swiper.activeIndex + i; if (index > swiper.slides.length && !isVirtual) break; activeSlides.push(getSlideByIndex(index)); } } } else { activeSlides.push(getSlideByIndex(swiper.activeIndex)); } // Find new height from highest slide in view for (i = 0; i < activeSlides.length; i += 1) { if (typeof activeSlides[i] !== 'undefined') { const height = activeSlides[i].offsetHeight; newHeight = height > newHeight ? height : newHeight; } } // Update Height if (newHeight || newHeight === 0) swiper.wrapperEl.style.height = `${newHeight}px`; } ================================================ FILE: src/core/update/updateClickedSlide.mjs ================================================ export default function updateClickedSlide(el, path) { const swiper = this; const params = swiper.params; let slide = el.closest(`.${params.slideClass}, swiper-slide`); if (!slide && swiper.isElement && path && path.length > 1 && path.includes(el)) { [...path.slice(path.indexOf(el) + 1, path.length)].forEach((pathEl) => { if (!slide && pathEl.matches && pathEl.matches(`.${params.slideClass}, swiper-slide`)) { slide = pathEl; } }); } let slideFound = false; let slideIndex; if (slide) { for (let i = 0; i < swiper.slides.length; i += 1) { if (swiper.slides[i] === slide) { slideFound = true; slideIndex = i; break; } } } if (slide && slideFound) { swiper.clickedSlide = slide; if (swiper.virtual && swiper.params.virtual.enabled) { swiper.clickedIndex = parseInt(slide.getAttribute('data-swiper-slide-index'), 10); } else { swiper.clickedIndex = slideIndex; } } else { swiper.clickedSlide = undefined; swiper.clickedIndex = undefined; return; } if ( params.slideToClickedSlide && swiper.clickedIndex !== undefined && swiper.clickedIndex !== swiper.activeIndex ) { swiper.slideToClickedSlide(); } } ================================================ FILE: src/core/update/updateProgress.mjs ================================================ export default function updateProgress(translate) { const swiper = this; if (typeof translate === 'undefined') { const multiplier = swiper.rtlTranslate ? -1 : 1; // eslint-disable-next-line translate = (swiper && swiper.translate && swiper.translate * multiplier) || 0; } const params = swiper.params; const translatesDiff = swiper.maxTranslate() - swiper.minTranslate(); let { progress, isBeginning, isEnd, progressLoop } = swiper; const wasBeginning = isBeginning; const wasEnd = isEnd; if (translatesDiff === 0) { progress = 0; isBeginning = true; isEnd = true; } else { progress = (translate - swiper.minTranslate()) / translatesDiff; const isBeginningRounded = Math.abs(translate - swiper.minTranslate()) < 1; const isEndRounded = Math.abs(translate - swiper.maxTranslate()) < 1; isBeginning = isBeginningRounded || progress <= 0; isEnd = isEndRounded || progress >= 1; if (isBeginningRounded) progress = 0; if (isEndRounded) progress = 1; } if (params.loop) { const firstSlideIndex = swiper.getSlideIndexByData(0); const lastSlideIndex = swiper.getSlideIndexByData(swiper.slides.length - 1); const firstSlideTranslate = swiper.slidesGrid[firstSlideIndex]; const lastSlideTranslate = swiper.slidesGrid[lastSlideIndex]; const translateMax = swiper.slidesGrid[swiper.slidesGrid.length - 1]; const translateAbs = Math.abs(translate); if (translateAbs >= firstSlideTranslate) { progressLoop = (translateAbs - firstSlideTranslate) / translateMax; } else { progressLoop = (translateAbs + translateMax - lastSlideTranslate) / translateMax; } if (progressLoop > 1) progressLoop -= 1; } Object.assign(swiper, { progress, progressLoop, isBeginning, isEnd, }); if (params.watchSlidesProgress || (params.centeredSlides && params.autoHeight)) swiper.updateSlidesProgress(translate); if (isBeginning && !wasBeginning) { swiper.emit('reachBeginning toEdge'); } if (isEnd && !wasEnd) { swiper.emit('reachEnd toEdge'); } if ((wasBeginning && !isBeginning) || (wasEnd && !isEnd)) { swiper.emit('fromEdge'); } swiper.emit('progress', progress); } ================================================ FILE: src/core/update/updateSize.mjs ================================================ import { elementStyle } from '../../shared/utils.mjs'; export default function updateSize() { const swiper = this; let width; let height; const el = swiper.el; if (typeof swiper.params.width !== 'undefined' && swiper.params.width !== null) { width = swiper.params.width; } else { width = el.clientWidth; } if (typeof swiper.params.height !== 'undefined' && swiper.params.height !== null) { height = swiper.params.height; } else { height = el.clientHeight; } if ((width === 0 && swiper.isHorizontal()) || (height === 0 && swiper.isVertical())) { return; } // Subtract paddings width = width - parseInt(elementStyle(el, 'padding-left') || 0, 10) - parseInt(elementStyle(el, 'padding-right') || 0, 10); height = height - parseInt(elementStyle(el, 'padding-top') || 0, 10) - parseInt(elementStyle(el, 'padding-bottom') || 0, 10); if (Number.isNaN(width)) width = 0; if (Number.isNaN(height)) height = 0; Object.assign(swiper, { width, height, size: swiper.isHorizontal() ? width : height, }); } ================================================ FILE: src/core/update/updateSlides.mjs ================================================ import { elementChildren, elementOuterSize, elementStyle, setCSSProperty, } from '../../shared/utils.mjs'; export default function updateSlides() { const swiper = this; function getDirectionPropertyValue(node, label) { return parseFloat(node.getPropertyValue(swiper.getDirectionLabel(label)) || 0); } const params = swiper.params; const { wrapperEl, slidesEl, rtlTranslate: rtl, wrongRTL } = swiper; const isVirtual = swiper.virtual && params.virtual.enabled; const previousSlidesLength = isVirtual ? swiper.virtual.slides.length : swiper.slides.length; const slides = elementChildren(slidesEl, `.${swiper.params.slideClass}, swiper-slide`); const slidesLength = isVirtual ? swiper.virtual.slides.length : slides.length; let snapGrid = []; const slidesGrid = []; const slidesSizesGrid = []; let offsetBefore = params.slidesOffsetBefore; if (typeof offsetBefore === 'function') { offsetBefore = params.slidesOffsetBefore.call(swiper); } let offsetAfter = params.slidesOffsetAfter; if (typeof offsetAfter === 'function') { offsetAfter = params.slidesOffsetAfter.call(swiper); } const previousSnapGridLength = swiper.snapGrid.length; const previousSlidesGridLength = swiper.slidesGrid.length; const swiperSize = swiper.size - offsetBefore - offsetAfter; let spaceBetween = params.spaceBetween; let slidePosition = -offsetBefore; let prevSlideSize = 0; let index = 0; if (typeof swiperSize === 'undefined') { return; } if (typeof spaceBetween === 'string' && spaceBetween.indexOf('%') >= 0) { spaceBetween = (parseFloat(spaceBetween.replace('%', '')) / 100) * swiperSize; } else if (typeof spaceBetween === 'string') { spaceBetween = parseFloat(spaceBetween); } swiper.virtualSize = -spaceBetween - offsetBefore - offsetAfter; // reset margins slides.forEach((slideEl) => { if (rtl) { slideEl.style.marginLeft = ''; } else { slideEl.style.marginRight = ''; } slideEl.style.marginBottom = ''; slideEl.style.marginTop = ''; }); // reset cssMode offsets if (params.centeredSlides && params.cssMode) { setCSSProperty(wrapperEl, '--swiper-centered-offset-before', ''); setCSSProperty(wrapperEl, '--swiper-centered-offset-after', ''); } // set cssMode offsets if (params.cssMode) { setCSSProperty(wrapperEl, '--swiper-slides-offset-before', `${offsetBefore}px`); setCSSProperty(wrapperEl, '--swiper-slides-offset-after', `${offsetAfter}px`); } const gridEnabled = params.grid && params.grid.rows > 1 && swiper.grid; if (gridEnabled) { swiper.grid.initSlides(slides); } else if (swiper.grid) { swiper.grid.unsetSlides(); } // Calc slides let slideSize; const shouldResetSlideSize = params.slidesPerView === 'auto' && params.breakpoints && Object.keys(params.breakpoints).filter((key) => { return typeof params.breakpoints[key].slidesPerView !== 'undefined'; }).length > 0; for (let i = 0; i < slidesLength; i += 1) { slideSize = 0; const slide = slides[i]; if (slide) { if (gridEnabled) { swiper.grid.updateSlide(i, slide, slides); } if (elementStyle(slide, 'display') === 'none') continue; // eslint-disable-line } if (isVirtual && params.slidesPerView === 'auto') { if (params.virtual.slidesPerViewAutoSlideSize) { slideSize = params.virtual.slidesPerViewAutoSlideSize; } if (slideSize && slide) { if (params.roundLengths) slideSize = Math.floor(slideSize); slide.style[swiper.getDirectionLabel('width')] = `${slideSize}px`; } } else if (params.slidesPerView === 'auto') { if (shouldResetSlideSize) { slide.style[swiper.getDirectionLabel('width')] = ``; } const slideStyles = getComputedStyle(slide); const currentTransform = slide.style.transform; const currentWebKitTransform = slide.style.webkitTransform; if (currentTransform) { slide.style.transform = 'none'; } if (currentWebKitTransform) { slide.style.webkitTransform = 'none'; } if (params.roundLengths) { slideSize = swiper.isHorizontal() ? elementOuterSize(slide, 'width', true) : elementOuterSize(slide, 'height', true); } else { // eslint-disable-next-line const width = getDirectionPropertyValue(slideStyles, 'width'); const paddingLeft = getDirectionPropertyValue(slideStyles, 'padding-left'); const paddingRight = getDirectionPropertyValue(slideStyles, 'padding-right'); const marginLeft = getDirectionPropertyValue(slideStyles, 'margin-left'); const marginRight = getDirectionPropertyValue(slideStyles, 'margin-right'); const boxSizing = slideStyles.getPropertyValue('box-sizing'); if (boxSizing && boxSizing === 'border-box') { slideSize = width + marginLeft + marginRight; } else { const { clientWidth, offsetWidth } = slide; slideSize = width + paddingLeft + paddingRight + marginLeft + marginRight + (offsetWidth - clientWidth); } } if (currentTransform) { slide.style.transform = currentTransform; } if (currentWebKitTransform) { slide.style.webkitTransform = currentWebKitTransform; } if (params.roundLengths) slideSize = Math.floor(slideSize); } else { slideSize = (swiperSize - (params.slidesPerView - 1) * spaceBetween) / params.slidesPerView; if (params.roundLengths) slideSize = Math.floor(slideSize); if (slide) { slide.style[swiper.getDirectionLabel('width')] = `${slideSize}px`; } } if (slide) { slide.swiperSlideSize = slideSize; } slidesSizesGrid.push(slideSize); if (params.centeredSlides) { slidePosition = slidePosition + slideSize / 2 + prevSlideSize / 2 + spaceBetween; if (prevSlideSize === 0 && i !== 0) slidePosition = slidePosition - swiperSize / 2 - spaceBetween; if (i === 0) slidePosition = slidePosition - swiperSize / 2 - spaceBetween; if (Math.abs(slidePosition) < 1 / 1000) slidePosition = 0; if (params.roundLengths) slidePosition = Math.floor(slidePosition); if (index % params.slidesPerGroup === 0) snapGrid.push(slidePosition); slidesGrid.push(slidePosition); } else { if (params.roundLengths) slidePosition = Math.floor(slidePosition); if ( (index - Math.min(swiper.params.slidesPerGroupSkip, index)) % swiper.params.slidesPerGroup === 0 ) snapGrid.push(slidePosition); slidesGrid.push(slidePosition); slidePosition = slidePosition + slideSize + spaceBetween; } swiper.virtualSize += slideSize + spaceBetween; prevSlideSize = slideSize; index += 1; } swiper.virtualSize = Math.max(swiper.virtualSize, swiperSize) + offsetAfter; if (rtl && wrongRTL && (params.effect === 'slide' || params.effect === 'coverflow')) { wrapperEl.style.width = `${swiper.virtualSize + spaceBetween}px`; } if (params.setWrapperSize) { wrapperEl.style[swiper.getDirectionLabel('width')] = `${swiper.virtualSize + spaceBetween}px`; } if (gridEnabled) { swiper.grid.updateWrapperSize(slideSize, snapGrid); } // Remove last grid elements depending on width if (!params.centeredSlides) { // Check if snapToSlideEdge should be applied const isFractionalSlidesPerView = params.slidesPerView !== 'auto' && params.slidesPerView % 1 !== 0; const shouldSnapToSlideEdge = params.snapToSlideEdge && !params.loop && (params.slidesPerView === 'auto' || isFractionalSlidesPerView); // Calculate the last allowed snap index when snapToSlideEdge is enabled // This ensures minimum slides are visible at the end let lastAllowedSnapIndex = snapGrid.length; if (shouldSnapToSlideEdge) { let minVisibleSlides; if (params.slidesPerView === 'auto') { // For 'auto' mode, calculate how many slides fit based on actual sizes minVisibleSlides = 1; let accumulatedSize = 0; for (let i = slidesSizesGrid.length - 1; i >= 0; i -= 1) { accumulatedSize += slidesSizesGrid[i] + (i < slidesSizesGrid.length - 1 ? spaceBetween : 0); if (accumulatedSize <= swiperSize) { minVisibleSlides = slidesSizesGrid.length - i; } else { break; } } } else { minVisibleSlides = Math.floor(params.slidesPerView); } lastAllowedSnapIndex = Math.max(slidesLength - minVisibleSlides, 0); } const newSlidesGrid = []; for (let i = 0; i < snapGrid.length; i += 1) { let slidesGridItem = snapGrid[i]; if (params.roundLengths) slidesGridItem = Math.floor(slidesGridItem); if (shouldSnapToSlideEdge) { // When snapToSlideEdge is enabled, only keep snaps up to lastAllowedSnapIndex if (i <= lastAllowedSnapIndex) { newSlidesGrid.push(slidesGridItem); } } else if (snapGrid[i] <= swiper.virtualSize - swiperSize) { // When snapToSlideEdge is disabled, keep snaps that fit within scrollable area newSlidesGrid.push(slidesGridItem); } } snapGrid = newSlidesGrid; if ( Math.floor(swiper.virtualSize - swiperSize) - Math.floor(snapGrid[snapGrid.length - 1]) > 1 ) { // Only add edge-aligned snap if snapToSlideEdge is not enabled if (!shouldSnapToSlideEdge) { snapGrid.push(swiper.virtualSize - swiperSize); } } } if (isVirtual && params.loop) { const size = slidesSizesGrid[0] + spaceBetween; if (params.slidesPerGroup > 1) { const groups = Math.ceil( (swiper.virtual.slidesBefore + swiper.virtual.slidesAfter) / params.slidesPerGroup, ); const groupSize = size * params.slidesPerGroup; for (let i = 0; i < groups; i += 1) { snapGrid.push(snapGrid[snapGrid.length - 1] + groupSize); } } for (let i = 0; i < swiper.virtual.slidesBefore + swiper.virtual.slidesAfter; i += 1) { if (params.slidesPerGroup === 1) { snapGrid.push(snapGrid[snapGrid.length - 1] + size); } slidesGrid.push(slidesGrid[slidesGrid.length - 1] + size); swiper.virtualSize += size; } } if (snapGrid.length === 0) snapGrid = [0]; if (spaceBetween !== 0) { const key = swiper.isHorizontal() && rtl ? 'marginLeft' : swiper.getDirectionLabel('marginRight'); slides .filter((_, slideIndex) => { if (!params.cssMode || params.loop) return true; if (slideIndex === slides.length - 1) { return false; } return true; }) .forEach((slideEl) => { slideEl.style[key] = `${spaceBetween}px`; }); } if (params.centeredSlides && params.centeredSlidesBounds) { let allSlidesSize = 0; slidesSizesGrid.forEach((slideSizeValue) => { allSlidesSize += slideSizeValue + (spaceBetween || 0); }); allSlidesSize -= spaceBetween; const maxSnap = allSlidesSize > swiperSize ? allSlidesSize - swiperSize : 0; snapGrid = snapGrid.map((snap) => { if (snap <= 0) return -offsetBefore; if (snap > maxSnap) return maxSnap + offsetAfter; return snap; }); } if (params.centerInsufficientSlides) { let allSlidesSize = 0; slidesSizesGrid.forEach((slideSizeValue) => { allSlidesSize += slideSizeValue + (spaceBetween || 0); }); allSlidesSize -= spaceBetween; if (allSlidesSize < swiperSize) { const allSlidesOffset = (swiperSize - allSlidesSize) / 2; snapGrid.forEach((snap, snapIndex) => { snapGrid[snapIndex] = snap - allSlidesOffset; }); slidesGrid.forEach((snap, snapIndex) => { slidesGrid[snapIndex] = snap + allSlidesOffset; }); } } Object.assign(swiper, { slides, snapGrid, slidesGrid, slidesSizesGrid, }); if (params.centeredSlides && params.cssMode && !params.centeredSlidesBounds) { setCSSProperty(wrapperEl, '--swiper-centered-offset-before', `${-snapGrid[0]}px`); setCSSProperty( wrapperEl, '--swiper-centered-offset-after', `${swiper.size / 2 - slidesSizesGrid[slidesSizesGrid.length - 1] / 2}px`, ); const addToSnapGrid = -swiper.snapGrid[0]; const addToSlidesGrid = -swiper.slidesGrid[0]; swiper.snapGrid = swiper.snapGrid.map((v) => v + addToSnapGrid); swiper.slidesGrid = swiper.slidesGrid.map((v) => v + addToSlidesGrid); } if (slidesLength !== previousSlidesLength) { swiper.emit('slidesLengthChange'); } if (snapGrid.length !== previousSnapGridLength) { if (swiper.params.watchOverflow) swiper.checkOverflow(); swiper.emit('snapGridLengthChange'); } if (slidesGrid.length !== previousSlidesGridLength) { swiper.emit('slidesGridLengthChange'); } if (params.watchSlidesProgress) { swiper.updateSlidesOffset(); } swiper.emit('slidesUpdated'); if (!isVirtual && !params.cssMode && (params.effect === 'slide' || params.effect === 'fade')) { const backFaceHiddenClass = `${params.containerModifierClass}backface-hidden`; const hasClassBackfaceClassAdded = swiper.el.classList.contains(backFaceHiddenClass); if (slidesLength <= params.maxBackfaceHiddenSlides) { if (!hasClassBackfaceClassAdded) swiper.el.classList.add(backFaceHiddenClass); } else if (hasClassBackfaceClassAdded) { swiper.el.classList.remove(backFaceHiddenClass); } } } ================================================ FILE: src/core/update/updateSlidesClasses.mjs ================================================ import { elementChildren, elementNextAll, elementPrevAll } from '../../shared/utils.mjs'; const toggleSlideClasses = (slideEl, condition, className) => { if (condition && !slideEl.classList.contains(className)) { slideEl.classList.add(className); } else if (!condition && slideEl.classList.contains(className)) { slideEl.classList.remove(className); } }; export default function updateSlidesClasses() { const swiper = this; const { slides, params, slidesEl, activeIndex } = swiper; const isVirtual = swiper.virtual && params.virtual.enabled; const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1; const getFilteredSlide = (selector) => { return elementChildren( slidesEl, `.${params.slideClass}${selector}, swiper-slide${selector}`, )[0]; }; let activeSlide; let prevSlide; let nextSlide; if (isVirtual) { if (params.loop) { let slideIndex = activeIndex - swiper.virtual.slidesBefore; if (slideIndex < 0) slideIndex = swiper.virtual.slides.length + slideIndex; if (slideIndex >= swiper.virtual.slides.length) slideIndex -= swiper.virtual.slides.length; activeSlide = getFilteredSlide(`[data-swiper-slide-index="${slideIndex}"]`); } else { activeSlide = getFilteredSlide(`[data-swiper-slide-index="${activeIndex}"]`); } } else { if (gridEnabled) { activeSlide = slides.find((slideEl) => slideEl.column === activeIndex); nextSlide = slides.find((slideEl) => slideEl.column === activeIndex + 1); prevSlide = slides.find((slideEl) => slideEl.column === activeIndex - 1); } else { activeSlide = slides[activeIndex]; } } if (activeSlide) { if (!gridEnabled) { // Next Slide nextSlide = elementNextAll(activeSlide, `.${params.slideClass}, swiper-slide`)[0]; if (params.loop && !nextSlide) { nextSlide = slides[0]; } // Prev Slide prevSlide = elementPrevAll(activeSlide, `.${params.slideClass}, swiper-slide`)[0]; if (params.loop && !prevSlide === 0) { prevSlide = slides[slides.length - 1]; } } } slides.forEach((slideEl) => { toggleSlideClasses(slideEl, slideEl === activeSlide, params.slideActiveClass); toggleSlideClasses(slideEl, slideEl === nextSlide, params.slideNextClass); toggleSlideClasses(slideEl, slideEl === prevSlide, params.slidePrevClass); }); swiper.emitSlidesClasses(); } ================================================ FILE: src/core/update/updateSlidesOffset.mjs ================================================ export default function updateSlidesOffset() { const swiper = this; const slides = swiper.slides; // eslint-disable-next-line const minusOffset = swiper.isElement ? swiper.isHorizontal() ? swiper.wrapperEl.offsetLeft : swiper.wrapperEl.offsetTop : 0; for (let i = 0; i < slides.length; i += 1) { slides[i].swiperSlideOffset = (swiper.isHorizontal() ? slides[i].offsetLeft : slides[i].offsetTop) - minusOffset - swiper.cssOverflowAdjustment(); } } ================================================ FILE: src/core/update/updateSlidesProgress.mjs ================================================ const toggleSlideClasses = (slideEl, condition, className) => { if (condition && !slideEl.classList.contains(className)) { slideEl.classList.add(className); } else if (!condition && slideEl.classList.contains(className)) { slideEl.classList.remove(className); } }; export default function updateSlidesProgress(translate = (this && this.translate) || 0) { const swiper = this; const params = swiper.params; const { slides, rtlTranslate: rtl, snapGrid } = swiper; if (slides.length === 0) return; if (typeof slides[0].swiperSlideOffset === 'undefined') swiper.updateSlidesOffset(); let offsetCenter = -translate; if (rtl) offsetCenter = translate; swiper.visibleSlidesIndexes = []; swiper.visibleSlides = []; let spaceBetween = params.spaceBetween; if (typeof spaceBetween === 'string' && spaceBetween.indexOf('%') >= 0) { spaceBetween = (parseFloat(spaceBetween.replace('%', '')) / 100) * swiper.size; } else if (typeof spaceBetween === 'string') { spaceBetween = parseFloat(spaceBetween); } for (let i = 0; i < slides.length; i += 1) { const slide = slides[i]; let slideOffset = slide.swiperSlideOffset; if (params.cssMode && params.centeredSlides) { slideOffset -= slides[0].swiperSlideOffset; } const slideProgress = (offsetCenter + (params.centeredSlides ? swiper.minTranslate() : 0) - slideOffset) / (slide.swiperSlideSize + spaceBetween); const originalSlideProgress = (offsetCenter - snapGrid[0] + (params.centeredSlides ? swiper.minTranslate() : 0) - slideOffset) / (slide.swiperSlideSize + spaceBetween); const slideBefore = -(offsetCenter - slideOffset); const slideAfter = slideBefore + swiper.slidesSizesGrid[i]; const isFullyVisible = slideBefore >= 0 && slideBefore <= swiper.size - swiper.slidesSizesGrid[i]; const isVisible = (slideBefore >= 0 && slideBefore < swiper.size - 1) || (slideAfter > 1 && slideAfter <= swiper.size) || (slideBefore <= 0 && slideAfter >= swiper.size); if (isVisible) { swiper.visibleSlides.push(slide); swiper.visibleSlidesIndexes.push(i); } toggleSlideClasses(slide, isVisible, params.slideVisibleClass); toggleSlideClasses(slide, isFullyVisible, params.slideFullyVisibleClass); slide.progress = rtl ? -slideProgress : slideProgress; slide.originalProgress = rtl ? -originalSlideProgress : originalSlideProgress; } } ================================================ FILE: src/modules/a11y/a11y.css ================================================ /* a11y */ .swiper .swiper-notification { position: absolute; left: 0; top: 0; pointer-events: none; opacity: 0; z-index: -1000; } ================================================ FILE: src/modules/a11y/a11y.mjs ================================================ import { getDocument } from 'ssr-window'; import classesToSelector from '../../shared/classes-to-selector.mjs'; import { createElement, elementIndex, makeElementsArray, setInnerHTML, } from '../../shared/utils.mjs'; export default function A11y({ swiper, extendParams, on }) { extendParams({ a11y: { enabled: true, notificationClass: 'swiper-notification', prevSlideMessage: 'Previous slide', nextSlideMessage: 'Next slide', firstSlideMessage: 'This is the first slide', lastSlideMessage: 'This is the last slide', paginationBulletMessage: 'Go to slide {{index}}', slideLabelMessage: '{{index}} / {{slidesLength}}', containerMessage: null, containerRoleDescriptionMessage: null, containerRole: null, itemRoleDescriptionMessage: null, slideRole: 'group', id: null, scrollOnFocus: true, wrapperLiveRegion: true, }, }); swiper.a11y = { clicked: false, }; let liveRegion = null; let preventFocusHandler; let focusTargetSlideEl; let visibilityChangedTimestamp = new Date().getTime(); function notify(message) { const notification = liveRegion; if (notification.length === 0) return; setInnerHTML(notification, message); } function getRandomNumber(size = 16) { const randomChar = () => Math.round(16 * Math.random()).toString(16); return 'x'.repeat(size).replace(/x/g, randomChar); } function makeElFocusable(el) { el = makeElementsArray(el); el.forEach((subEl) => { subEl.setAttribute('tabIndex', '0'); }); } function makeElNotFocusable(el) { el = makeElementsArray(el); el.forEach((subEl) => { subEl.setAttribute('tabIndex', '-1'); }); } function addElRole(el, role) { el = makeElementsArray(el); el.forEach((subEl) => { subEl.setAttribute('role', role); }); } function addElRoleDescription(el, description) { el = makeElementsArray(el); el.forEach((subEl) => { subEl.setAttribute('aria-roledescription', description); }); } function addElControls(el, controls) { el = makeElementsArray(el); el.forEach((subEl) => { subEl.setAttribute('aria-controls', controls); }); } function addElLabel(el, label) { el = makeElementsArray(el); el.forEach((subEl) => { subEl.setAttribute('aria-label', label); }); } function addElId(el, id) { el = makeElementsArray(el); el.forEach((subEl) => { subEl.setAttribute('id', id); }); } function addElLive(el, live) { el = makeElementsArray(el); el.forEach((subEl) => { subEl.setAttribute('aria-live', live); }); } function disableEl(el) { el = makeElementsArray(el); el.forEach((subEl) => { subEl.setAttribute('aria-disabled', true); }); } function enableEl(el) { el = makeElementsArray(el); el.forEach((subEl) => { subEl.setAttribute('aria-disabled', false); }); } function onEnterOrSpaceKey(e) { if (e.keyCode !== 13 && e.keyCode !== 32) return; const params = swiper.params.a11y; const targetEl = e.target; if ( swiper.pagination && swiper.pagination.el && (targetEl === swiper.pagination.el || swiper.pagination.el.contains(e.target)) ) { if (!e.target.matches(classesToSelector(swiper.params.pagination.bulletClass))) return; } if (swiper.navigation && swiper.navigation.prevEl && swiper.navigation.nextEl) { const prevEls = makeElementsArray(swiper.navigation.prevEl); const nextEls = makeElementsArray(swiper.navigation.nextEl); if (nextEls.includes(targetEl)) { if (!(swiper.isEnd && !swiper.params.loop)) { swiper.slideNext(); } if (swiper.isEnd) { notify(params.lastSlideMessage); } else { notify(params.nextSlideMessage); } } if (prevEls.includes(targetEl)) { if (!(swiper.isBeginning && !swiper.params.loop)) { swiper.slidePrev(); } if (swiper.isBeginning) { notify(params.firstSlideMessage); } else { notify(params.prevSlideMessage); } } } if ( swiper.pagination && targetEl.matches(classesToSelector(swiper.params.pagination.bulletClass)) ) { targetEl.click(); } } function updateNavigation() { if (swiper.params.loop || swiper.params.rewind || !swiper.navigation) return; const { nextEl, prevEl } = swiper.navigation; if (prevEl) { if (swiper.isBeginning) { disableEl(prevEl); makeElNotFocusable(prevEl); } else { enableEl(prevEl); makeElFocusable(prevEl); } } if (nextEl) { if (swiper.isEnd) { disableEl(nextEl); makeElNotFocusable(nextEl); } else { enableEl(nextEl); makeElFocusable(nextEl); } } } function hasPagination() { return swiper.pagination && swiper.pagination.bullets && swiper.pagination.bullets.length; } function hasClickablePagination() { return hasPagination() && swiper.params.pagination.clickable; } function updatePagination() { const params = swiper.params.a11y; if (!hasPagination()) return; swiper.pagination.bullets.forEach((bulletEl) => { if (swiper.params.pagination.clickable) { makeElFocusable(bulletEl); if (!swiper.params.pagination.renderBullet) { addElRole(bulletEl, 'button'); addElLabel( bulletEl, params.paginationBulletMessage.replace(/\{\{index\}\}/, elementIndex(bulletEl) + 1), ); } } if (bulletEl.matches(classesToSelector(swiper.params.pagination.bulletActiveClass))) { bulletEl.setAttribute('aria-current', 'true'); } else { bulletEl.removeAttribute('aria-current'); } }); } const initNavEl = (el, wrapperId, message) => { makeElFocusable(el); if (el.tagName !== 'BUTTON') { addElRole(el, 'button'); el.addEventListener('keydown', onEnterOrSpaceKey); } addElLabel(el, message); addElControls(el, wrapperId); }; const handlePointerDown = (e) => { if ( focusTargetSlideEl && focusTargetSlideEl !== e.target && !focusTargetSlideEl.contains(e.target) ) { preventFocusHandler = true; } swiper.a11y.clicked = true; }; const handlePointerUp = () => { preventFocusHandler = false; requestAnimationFrame(() => { requestAnimationFrame(() => { if (!swiper.destroyed) { swiper.a11y.clicked = false; } }); }); }; const onVisibilityChange = (e) => { visibilityChangedTimestamp = new Date().getTime(); }; const handleFocus = (e) => { if (swiper.a11y.clicked || !swiper.params.a11y.scrollOnFocus) return; if (new Date().getTime() - visibilityChangedTimestamp < 100) return; const slideEl = e.target.closest(`.${swiper.params.slideClass}, swiper-slide`); if (!slideEl || !swiper.slides.includes(slideEl)) return; focusTargetSlideEl = slideEl; const isVirtual = swiper.virtual && swiper.params.virtual.enabled; const isActive = (isVirtual ? parseInt(slideEl.getAttribute('data-swiper-slide-index'), 10) : swiper.slides.indexOf(slideEl)) === swiper.activeIndex; const isVisible = swiper.params.watchSlidesProgress && swiper.visibleSlides && swiper.visibleSlides.includes(slideEl); if (isActive || isVisible) return; if (e.sourceCapabilities && e.sourceCapabilities.firesTouchEvents) return; if (swiper.isHorizontal()) { swiper.el.scrollLeft = 0; } else { swiper.el.scrollTop = 0; } requestAnimationFrame(() => { if (preventFocusHandler) return; if (swiper.params.loop) { swiper.slideToLoop( swiper.getSlideIndexWhenGrid(parseInt(slideEl.getAttribute('data-swiper-slide-index'))), 0, ); } else if (isVirtual) { swiper.slideTo( swiper.getSlideIndexWhenGrid( parseInt(slideEl.getAttribute('data-swiper-slide-index'), 10), ), 0, ); } else { swiper.slideTo(swiper.getSlideIndexWhenGrid(swiper.slides.indexOf(slideEl)), 0); } preventFocusHandler = false; }); }; const initSlides = () => { const params = swiper.params.a11y; if (params.itemRoleDescriptionMessage) { addElRoleDescription(swiper.slides, params.itemRoleDescriptionMessage); } if (params.slideRole) { addElRole(swiper.slides, params.slideRole); } const slidesLength = swiper.slides.length; if (params.slideLabelMessage) { swiper.slides.forEach((slideEl, index) => { const slideIndex = swiper.params.loop ? parseInt(slideEl.getAttribute('data-swiper-slide-index'), 10) : index; const ariaLabelMessage = params.slideLabelMessage .replace(/\{\{index\}\}/, slideIndex + 1) .replace(/\{\{slidesLength\}\}/, slidesLength); addElLabel(slideEl, ariaLabelMessage); }); } }; const init = () => { const params = swiper.params.a11y; swiper.el.append(liveRegion); // Container const containerEl = swiper.el; if (params.containerRoleDescriptionMessage) { addElRoleDescription(containerEl, params.containerRoleDescriptionMessage); } if (params.containerMessage) { addElLabel(containerEl, params.containerMessage); } if (params.containerRole) { addElRole(containerEl, params.containerRole); } // Wrapper const wrapperEl = swiper.wrapperEl; const wrapperId = params.id || wrapperEl.getAttribute('id') || `swiper-wrapper-${getRandomNumber(16)}`; addElId(wrapperEl, wrapperId); if (params.wrapperLiveRegion) { const live = swiper.params.autoplay && swiper.params.autoplay.enabled ? 'off' : 'polite'; addElLive(wrapperEl, live); } // Slide initSlides(); // Navigation let { nextEl, prevEl } = swiper.navigation ? swiper.navigation : {}; nextEl = makeElementsArray(nextEl); prevEl = makeElementsArray(prevEl); if (nextEl) { nextEl.forEach((el) => initNavEl(el, wrapperId, params.nextSlideMessage)); } if (prevEl) { prevEl.forEach((el) => initNavEl(el, wrapperId, params.prevSlideMessage)); } // Pagination if (hasClickablePagination()) { const paginationEl = makeElementsArray(swiper.pagination.el); paginationEl.forEach((el) => { el.addEventListener('keydown', onEnterOrSpaceKey); }); } // Tab focus const document = getDocument(); document.addEventListener('visibilitychange', onVisibilityChange); swiper.el.addEventListener('focus', handleFocus, true); swiper.el.addEventListener('pointerdown', handlePointerDown, true); swiper.el.addEventListener('pointerup', handlePointerUp, true); }; function destroy() { if (liveRegion) liveRegion.remove(); let { nextEl, prevEl } = swiper.navigation ? swiper.navigation : {}; nextEl = makeElementsArray(nextEl); prevEl = makeElementsArray(prevEl); if (nextEl) { nextEl.forEach((el) => el.removeEventListener('keydown', onEnterOrSpaceKey)); } if (prevEl) { prevEl.forEach((el) => el.removeEventListener('keydown', onEnterOrSpaceKey)); } // Pagination if (hasClickablePagination()) { const paginationEl = makeElementsArray(swiper.pagination.el); paginationEl.forEach((el) => { el.removeEventListener('keydown', onEnterOrSpaceKey); }); } const document = getDocument(); document.removeEventListener('visibilitychange', onVisibilityChange); // Tab focus if (swiper.el && typeof swiper.el !== 'string') { swiper.el.removeEventListener('focus', handleFocus, true); swiper.el.removeEventListener('pointerdown', handlePointerDown, true); swiper.el.removeEventListener('pointerup', handlePointerUp, true); } } on('beforeInit', () => { liveRegion = createElement('span', swiper.params.a11y.notificationClass); liveRegion.setAttribute('aria-live', 'assertive'); liveRegion.setAttribute('aria-atomic', 'true'); }); on('afterInit', () => { if (!swiper.params.a11y.enabled) return; init(); }); on('slidesLengthChange snapGridLengthChange slidesGridLengthChange', () => { if (!swiper.params.a11y.enabled) return; initSlides(); }); on('fromEdge toEdge afterInit lock unlock', () => { if (!swiper.params.a11y.enabled) return; updateNavigation(); }); on('paginationUpdate', () => { if (!swiper.params.a11y.enabled) return; updatePagination(); }); on('destroy', () => { if (!swiper.params.a11y.enabled) return; destroy(); }); } ================================================ FILE: src/modules/autoplay/autoplay.css ================================================ ================================================ FILE: src/modules/autoplay/autoplay.mjs ================================================ /* eslint no-underscore-dangle: "off" */ /* eslint no-use-before-define: "off" */ import { getDocument } from 'ssr-window'; export default function Autoplay({ swiper, extendParams, on, emit, params }) { swiper.autoplay = { running: false, paused: false, timeLeft: 0, }; extendParams({ autoplay: { enabled: false, delay: 3000, waitForTransition: true, disableOnInteraction: false, stopOnLastSlide: false, reverseDirection: false, pauseOnMouseEnter: false, }, }); let timeout; let raf; let autoplayDelayTotal = params && params.autoplay ? params.autoplay.delay : 3000; let autoplayDelayCurrent = params && params.autoplay ? params.autoplay.delay : 3000; let autoplayTimeLeft; let autoplayStartTime = new Date().getTime(); let wasPaused; let isTouched; let pausedByTouch; let touchStartTimeout; let slideChanged; let pausedByInteraction; let pausedByPointerEnter; function onTransitionEnd(e) { if (!swiper || swiper.destroyed || !swiper.wrapperEl) return; if (e.target !== swiper.wrapperEl) return; swiper.wrapperEl.removeEventListener('transitionend', onTransitionEnd); if (pausedByPointerEnter || (e.detail && e.detail.bySwiperTouchMove)) { return; } resume(); } const calcTimeLeft = () => { if (swiper.destroyed || !swiper.autoplay.running) return; if (swiper.autoplay.paused) { wasPaused = true; } else if (wasPaused) { autoplayDelayCurrent = autoplayTimeLeft; wasPaused = false; } const timeLeft = swiper.autoplay.paused ? autoplayTimeLeft : autoplayStartTime + autoplayDelayCurrent - new Date().getTime(); swiper.autoplay.timeLeft = timeLeft; emit('autoplayTimeLeft', timeLeft, timeLeft / autoplayDelayTotal); raf = requestAnimationFrame(() => { calcTimeLeft(); }); }; const getSlideDelay = () => { let activeSlideEl; if (swiper.virtual && swiper.params.virtual.enabled) { activeSlideEl = swiper.slides.find((slideEl) => slideEl.classList.contains('swiper-slide-active'), ); } else { activeSlideEl = swiper.slides[swiper.activeIndex]; } if (!activeSlideEl) return undefined; const currentSlideDelay = parseInt(activeSlideEl.getAttribute('data-swiper-autoplay'), 10); return currentSlideDelay; }; const getTotalDelay = () => { let totalDelay = swiper.params.autoplay.delay; const currentSlideDelay = getSlideDelay(); if (!Number.isNaN(currentSlideDelay) && currentSlideDelay > 0) { totalDelay = currentSlideDelay; } return totalDelay; }; const run = (delayForce) => { if (swiper.destroyed || !swiper.autoplay.running) return; cancelAnimationFrame(raf); calcTimeLeft(); let delay = delayForce; if (typeof delay === 'undefined') { delay = getTotalDelay(); autoplayDelayTotal = delay; autoplayDelayCurrent = delay; } autoplayTimeLeft = delay; const speed = swiper.params.speed; const proceed = () => { if (!swiper || swiper.destroyed) return; if (swiper.params.autoplay.reverseDirection) { if (!swiper.isBeginning || swiper.params.loop || swiper.params.rewind) { swiper.slidePrev(speed, true, true); emit('autoplay'); } else if (!swiper.params.autoplay.stopOnLastSlide) { swiper.slideTo(swiper.slides.length - 1, speed, true, true); emit('autoplay'); } } else { if (!swiper.isEnd || swiper.params.loop || swiper.params.rewind) { swiper.slideNext(speed, true, true); emit('autoplay'); } else if (!swiper.params.autoplay.stopOnLastSlide) { swiper.slideTo(0, speed, true, true); emit('autoplay'); } } if (swiper.params.cssMode) { autoplayStartTime = new Date().getTime(); requestAnimationFrame(() => { run(); }); } }; if (delay > 0) { clearTimeout(timeout); timeout = setTimeout(() => { proceed(); }, delay); } else { requestAnimationFrame(() => { proceed(); }); } // eslint-disable-next-line return delay; }; const start = () => { autoplayStartTime = new Date().getTime(); swiper.autoplay.running = true; run(); emit('autoplayStart'); }; const stop = () => { swiper.autoplay.running = false; clearTimeout(timeout); cancelAnimationFrame(raf); emit('autoplayStop'); }; const pause = (internal, reset) => { if (swiper.destroyed || !swiper.autoplay.running) return; clearTimeout(timeout); if (!internal) { pausedByInteraction = true; } const proceed = () => { emit('autoplayPause'); if (swiper.params.autoplay.waitForTransition) { swiper.wrapperEl.addEventListener('transitionend', onTransitionEnd); } else { resume(); } }; swiper.autoplay.paused = true; if (reset) { slideChanged = false; proceed(); return; } const delay = autoplayTimeLeft || swiper.params.autoplay.delay; autoplayTimeLeft = delay - (new Date().getTime() - autoplayStartTime); if (swiper.isEnd && autoplayTimeLeft < 0 && !swiper.params.loop) return; if (autoplayTimeLeft < 0) autoplayTimeLeft = 0; proceed(); }; const resume = () => { if ( (swiper.isEnd && autoplayTimeLeft < 0 && !swiper.params.loop) || swiper.destroyed || !swiper.autoplay.running ) return; autoplayStartTime = new Date().getTime(); if (pausedByInteraction) { pausedByInteraction = false; run(autoplayTimeLeft); } else { run(); } swiper.autoplay.paused = false; emit('autoplayResume'); }; const onVisibilityChange = () => { if (swiper.destroyed || !swiper.autoplay.running) return; const document = getDocument(); if (document.visibilityState === 'hidden') { pausedByInteraction = true; pause(true); } if (document.visibilityState === 'visible') { resume(); } }; const onPointerEnter = (e) => { if (e.pointerType !== 'mouse') return; pausedByInteraction = true; pausedByPointerEnter = true; if (swiper.animating || swiper.autoplay.paused) return; pause(true); }; const onPointerLeave = (e) => { if (e.pointerType !== 'mouse') return; pausedByPointerEnter = false; if (swiper.autoplay.paused) { resume(); } }; const attachMouseEvents = () => { if (swiper.params.autoplay.pauseOnMouseEnter) { swiper.el.addEventListener('pointerenter', onPointerEnter); swiper.el.addEventListener('pointerleave', onPointerLeave); } }; const detachMouseEvents = () => { if (swiper.el && typeof swiper.el !== 'string') { swiper.el.removeEventListener('pointerenter', onPointerEnter); swiper.el.removeEventListener('pointerleave', onPointerLeave); } }; const attachDocumentEvents = () => { const document = getDocument(); document.addEventListener('visibilitychange', onVisibilityChange); }; const detachDocumentEvents = () => { const document = getDocument(); document.removeEventListener('visibilitychange', onVisibilityChange); }; on('init', () => { if (swiper.params.autoplay.enabled) { attachMouseEvents(); attachDocumentEvents(); start(); } }); on('destroy', () => { detachMouseEvents(); detachDocumentEvents(); if (swiper.autoplay.running) { stop(); } }); on('_freeModeStaticRelease', () => { if (pausedByTouch || pausedByInteraction) { resume(); } }); on('_freeModeNoMomentumRelease', () => { if (!swiper.params.autoplay.disableOnInteraction) { pause(true, true); } else { stop(); } }); on('beforeTransitionStart', (_s, speed, internal) => { if (swiper.destroyed || !swiper.autoplay.running) return; if (internal || !swiper.params.autoplay.disableOnInteraction) { pause(true, true); } else { stop(); } }); on('sliderFirstMove', () => { if (swiper.destroyed || !swiper.autoplay.running) return; if (swiper.params.autoplay.disableOnInteraction) { stop(); return; } isTouched = true; pausedByTouch = false; pausedByInteraction = false; touchStartTimeout = setTimeout(() => { pausedByInteraction = true; pausedByTouch = true; pause(true); }, 200); }); on('touchEnd', () => { if (swiper.destroyed || !swiper.autoplay.running || !isTouched) return; clearTimeout(touchStartTimeout); clearTimeout(timeout); if (swiper.params.autoplay.disableOnInteraction) { pausedByTouch = false; isTouched = false; return; } if (pausedByTouch && swiper.params.cssMode) resume(); pausedByTouch = false; isTouched = false; }); on('slideChange', () => { if (swiper.destroyed || !swiper.autoplay.running) return; slideChanged = true; if (swiper.autoplay.paused) { autoplayTimeLeft = getTotalDelay(); autoplayDelayTotal = getTotalDelay(); } }); Object.assign(swiper.autoplay, { start, stop, pause, resume, }); } ================================================ FILE: src/modules/controller/controller.css ================================================ ================================================ FILE: src/modules/controller/controller.mjs ================================================ /* eslint no-bitwise: ["error", { "allow": [">>"] }] */ import { elementTransitionEnd, nextTick } from '../../shared/utils.mjs'; export default function Controller({ swiper, extendParams, on }) { extendParams({ controller: { control: undefined, inverse: false, by: 'slide', // or 'container' }, }); swiper.controller = { control: undefined, }; function LinearSpline(x, y) { const binarySearch = (function search() { let maxIndex; let minIndex; let guess; return (array, val) => { minIndex = -1; maxIndex = array.length; while (maxIndex - minIndex > 1) { guess = (maxIndex + minIndex) >> 1; if (array[guess] <= val) { minIndex = guess; } else { maxIndex = guess; } } return maxIndex; }; })(); this.x = x; this.y = y; this.lastIndex = x.length - 1; // Given an x value (x2), return the expected y2 value: // (x1,y1) is the known point before given value, // (x3,y3) is the known point after given value. let i1; let i3; this.interpolate = function interpolate(x2) { if (!x2) return 0; // Get the indexes of x1 and x3 (the array indexes before and after given x2): i3 = binarySearch(this.x, x2); i1 = i3 - 1; // We have our indexes i1 & i3, so we can calculate already: // y2 := ((x2−x1) × (y3−y1)) ÷ (x3−x1) + y1 return ( ((x2 - this.x[i1]) * (this.y[i3] - this.y[i1])) / (this.x[i3] - this.x[i1]) + this.y[i1] ); }; return this; } function getInterpolateFunction(c) { swiper.controller.spline = swiper.params.loop ? new LinearSpline(swiper.slidesGrid, c.slidesGrid) : new LinearSpline(swiper.snapGrid, c.snapGrid); } function setTranslate(_t, byController) { const controlled = swiper.controller.control; let multiplier; let controlledTranslate; const Swiper = swiper.constructor; function setControlledTranslate(c) { if (c.destroyed) return; // this will create an Interpolate function based on the snapGrids // x is the Grid of the scrolled scroller and y will be the controlled scroller // it makes sense to create this only once and recall it for the interpolation // the function does a lot of value caching for performance const translate = swiper.rtlTranslate ? -swiper.translate : swiper.translate; if (swiper.params.controller.by === 'slide') { getInterpolateFunction(c); // i am not sure why the values have to be multiplicated this way, tried to invert the snapGrid // but it did not work out controlledTranslate = -swiper.controller.spline.interpolate(-translate); } if (!controlledTranslate || swiper.params.controller.by === 'container') { multiplier = (c.maxTranslate() - c.minTranslate()) / (swiper.maxTranslate() - swiper.minTranslate()); if (Number.isNaN(multiplier) || !Number.isFinite(multiplier)) { multiplier = 1; } controlledTranslate = (translate - swiper.minTranslate()) * multiplier + c.minTranslate(); } if (swiper.params.controller.inverse) { controlledTranslate = c.maxTranslate() - controlledTranslate; } c.updateProgress(controlledTranslate); c.setTranslate(controlledTranslate, swiper); c.updateActiveIndex(); c.updateSlidesClasses(); } if (Array.isArray(controlled)) { for (let i = 0; i < controlled.length; i += 1) { if (controlled[i] !== byController && controlled[i] instanceof Swiper) { setControlledTranslate(controlled[i]); } } } else if (controlled instanceof Swiper && byController !== controlled) { setControlledTranslate(controlled); } } function setTransition(duration, byController) { const Swiper = swiper.constructor; const controlled = swiper.controller.control; let i; function setControlledTransition(c) { if (c.destroyed) return; c.setTransition(duration, swiper); if (duration !== 0) { c.transitionStart(); if (c.params.autoHeight) { nextTick(() => { c.updateAutoHeight(); }); } elementTransitionEnd(c.wrapperEl, () => { if (!controlled) return; c.transitionEnd(); }); } } if (Array.isArray(controlled)) { for (i = 0; i < controlled.length; i += 1) { if (controlled[i] !== byController && controlled[i] instanceof Swiper) { setControlledTransition(controlled[i]); } } } else if (controlled instanceof Swiper && byController !== controlled) { setControlledTransition(controlled); } } function removeSpline() { if (!swiper.controller.control) return; if (swiper.controller.spline) { swiper.controller.spline = undefined; delete swiper.controller.spline; } } on('beforeInit', () => { if ( typeof window !== 'undefined' && // eslint-disable-line (typeof swiper.params.controller.control === 'string' || swiper.params.controller.control instanceof HTMLElement) ) { const controlElements = typeof swiper.params.controller.control === 'string' ? [...document.querySelectorAll(swiper.params.controller.control)] : [swiper.params.controller.control]; controlElements.forEach((controlElement) => { if (!swiper.controller.control) swiper.controller.control = []; if (controlElement && controlElement.swiper) { swiper.controller.control.push(controlElement.swiper); } else if (controlElement) { const eventName = `${swiper.params.eventsPrefix}init`; const onControllerSwiper = (e) => { swiper.controller.control.push(e.detail[0]); swiper.update(); controlElement.removeEventListener(eventName, onControllerSwiper); }; controlElement.addEventListener(eventName, onControllerSwiper); } }); return; } swiper.controller.control = swiper.params.controller.control; }); on('update', () => { removeSpline(); }); on('resize', () => { removeSpline(); }); on('observerUpdate', () => { removeSpline(); }); on('setTranslate', (_s, translate, byController) => { if (!swiper.controller.control || swiper.controller.control.destroyed) return; swiper.controller.setTranslate(translate, byController); }); on('setTransition', (_s, duration, byController) => { if (!swiper.controller.control || swiper.controller.control.destroyed) return; swiper.controller.setTransition(duration, byController); }); Object.assign(swiper.controller, { setTranslate, setTransition, }); } ================================================ FILE: src/modules/effect-cards/effect-cards.css ================================================ .swiper.swiper-cards { overflow: visible; } .swiper-cards { .swiper-slide { transform-origin: center bottom; backface-visibility: hidden; overflow: hidden; } } ================================================ FILE: src/modules/effect-cards/effect-cards.mjs ================================================ import createShadow from '../../shared/create-shadow.mjs'; import effectInit from '../../shared/effect-init.mjs'; import effectTarget from '../../shared/effect-target.mjs'; import effectVirtualTransitionEnd from '../../shared/effect-virtual-transition-end.mjs'; import { getSlideTransformEl } from '../../shared/utils.mjs'; export default function EffectCards({ swiper, extendParams, on }) { extendParams({ cardsEffect: { slideShadows: true, rotate: true, perSlideRotate: 2, perSlideOffset: 8, }, }); const setTranslate = () => { const { slides, activeIndex, rtlTranslate: rtl } = swiper; const params = swiper.params.cardsEffect; const { startTranslate, isTouched } = swiper.touchEventsData; const currentTranslate = rtl ? -swiper.translate : swiper.translate; for (let i = 0; i < slides.length; i += 1) { const slideEl = slides[i]; const slideProgress = slideEl.progress; const progress = Math.min(Math.max(slideProgress, -4), 4); let offset = slideEl.swiperSlideOffset; if (swiper.params.centeredSlides && !swiper.params.cssMode) { swiper.wrapperEl.style.transform = `translateX(${swiper.minTranslate()}px)`; } if (swiper.params.centeredSlides && swiper.params.cssMode) { offset -= slides[0].swiperSlideOffset; } let tX = swiper.params.cssMode ? -offset - swiper.translate : -offset; let tY = 0; const tZ = -100 * Math.abs(progress); let scale = 1; let rotate = -params.perSlideRotate * progress; let tXAdd = params.perSlideOffset - Math.abs(progress) * 0.75; const slideIndex = swiper.virtual && swiper.params.virtual.enabled ? swiper.virtual.from + i : i; const isSwipeToNext = (slideIndex === activeIndex || slideIndex === activeIndex - 1) && progress > 0 && progress < 1 && (isTouched || swiper.params.cssMode) && currentTranslate < startTranslate; const isSwipeToPrev = (slideIndex === activeIndex || slideIndex === activeIndex + 1) && progress < 0 && progress > -1 && (isTouched || swiper.params.cssMode) && currentTranslate > startTranslate; if (isSwipeToNext || isSwipeToPrev) { const subProgress = (1 - Math.abs((Math.abs(progress) - 0.5) / 0.5)) ** 0.5; rotate += -28 * progress * subProgress; scale += -0.5 * subProgress; tXAdd += 96 * subProgress; tY = `${ (params.rotate || swiper.isHorizontal() ? -25 : 0) * subProgress * Math.abs(progress) }%`; } if (progress < 0) { // next tX = `calc(${tX}px ${rtl ? '-' : '+'} (${tXAdd * Math.abs(progress)}%))`; } else if (progress > 0) { // prev tX = `calc(${tX}px ${rtl ? '-' : '+'} (-${tXAdd * Math.abs(progress)}%))`; } else { tX = `${tX}px`; } if (!swiper.isHorizontal()) { const prevY = tY; tY = tX; tX = prevY; } const scaleString = progress < 0 ? `${1 + (1 - scale) * progress}` : `${1 - (1 - scale) * progress}`; /* eslint-disable */ const transform = ` translate3d(${tX}, ${tY}, ${tZ}px) rotateZ(${params.rotate ? (rtl ? -rotate : rotate) : 0}deg) scale(${scaleString}) `; /* eslint-enable */ if (params.slideShadows) { // Set shadows let shadowEl = slideEl.querySelector('.swiper-slide-shadow'); if (!shadowEl) { shadowEl = createShadow('cards', slideEl); } if (shadowEl) shadowEl.style.opacity = Math.min(Math.max((Math.abs(progress) - 0.5) / 0.5, 0), 1); } slideEl.style.zIndex = -Math.abs(Math.round(slideProgress)) + slides.length; const targetEl = effectTarget(params, slideEl); targetEl.style.transform = transform; } }; const setTransition = (duration) => { const transformElements = swiper.slides.map((slideEl) => getSlideTransformEl(slideEl)); transformElements.forEach((el) => { el.style.transitionDuration = `${duration}ms`; el.querySelectorAll('.swiper-slide-shadow').forEach((shadowEl) => { shadowEl.style.transitionDuration = `${duration}ms`; }); }); effectVirtualTransitionEnd({ swiper, duration, transformElements }); }; effectInit({ effect: 'cards', swiper, on, setTranslate, setTransition, perspective: () => true, overwriteParams: () => ({ _loopSwapReset: false, watchSlidesProgress: true, loopAdditionalSlides: swiper.params.cardsEffect.rotate ? 3 : 2, centeredSlides: true, virtualTranslate: !swiper.params.cssMode, }), }); } ================================================ FILE: src/modules/effect-coverflow/effect-coverflow.css ================================================ .swiper-coverflow { } ================================================ FILE: src/modules/effect-coverflow/effect-coverflow.mjs ================================================ import createShadow from '../../shared/create-shadow.mjs'; import effectInit from '../../shared/effect-init.mjs'; import effectTarget from '../../shared/effect-target.mjs'; import { getRotateFix, getSlideTransformEl } from '../../shared/utils.mjs'; export default function EffectCoverflow({ swiper, extendParams, on }) { extendParams({ coverflowEffect: { rotate: 50, stretch: 0, depth: 100, scale: 1, modifier: 1, slideShadows: true, }, }); const setTranslate = () => { const { width: swiperWidth, height: swiperHeight, slides, slidesSizesGrid } = swiper; const params = swiper.params.coverflowEffect; const isHorizontal = swiper.isHorizontal(); const transform = swiper.translate; const center = isHorizontal ? -transform + swiperWidth / 2 : -transform + swiperHeight / 2; const rotate = isHorizontal ? params.rotate : -params.rotate; const translate = params.depth; const r = getRotateFix(swiper); // Each slide offset from center for (let i = 0, length = slides.length; i < length; i += 1) { const slideEl = slides[i]; const slideSize = slidesSizesGrid[i]; const slideOffset = slideEl.swiperSlideOffset; const centerOffset = (center - slideOffset - slideSize / 2) / slideSize; const offsetMultiplier = typeof params.modifier === 'function' ? params.modifier(centerOffset) : centerOffset * params.modifier; let rotateY = isHorizontal ? rotate * offsetMultiplier : 0; let rotateX = isHorizontal ? 0 : rotate * offsetMultiplier; // var rotateZ = 0 let translateZ = -translate * Math.abs(offsetMultiplier); let stretch = params.stretch; // Allow percentage to make a relative stretch for responsive sliders if (typeof stretch === 'string' && stretch.indexOf('%') !== -1) { stretch = (parseFloat(params.stretch) / 100) * slideSize; } let translateY = isHorizontal ? 0 : stretch * offsetMultiplier; let translateX = isHorizontal ? stretch * offsetMultiplier : 0; let scale = 1 - (1 - params.scale) * Math.abs(offsetMultiplier); // Fix for ultra small values if (Math.abs(translateX) < 0.001) translateX = 0; if (Math.abs(translateY) < 0.001) translateY = 0; if (Math.abs(translateZ) < 0.001) translateZ = 0; if (Math.abs(rotateY) < 0.001) rotateY = 0; if (Math.abs(rotateX) < 0.001) rotateX = 0; if (Math.abs(scale) < 0.001) scale = 0; const slideTransform = `translate3d(${translateX}px,${translateY}px,${translateZ}px) rotateX(${r( rotateX, )}deg) rotateY(${r(rotateY)}deg) scale(${scale})`; const targetEl = effectTarget(params, slideEl); targetEl.style.transform = slideTransform; slideEl.style.zIndex = -Math.abs(Math.round(offsetMultiplier)) + 1; if (params.slideShadows) { // Set shadows let shadowBeforeEl = isHorizontal ? slideEl.querySelector('.swiper-slide-shadow-left') : slideEl.querySelector('.swiper-slide-shadow-top'); let shadowAfterEl = isHorizontal ? slideEl.querySelector('.swiper-slide-shadow-right') : slideEl.querySelector('.swiper-slide-shadow-bottom'); if (!shadowBeforeEl) { shadowBeforeEl = createShadow('coverflow', slideEl, isHorizontal ? 'left' : 'top'); } if (!shadowAfterEl) { shadowAfterEl = createShadow('coverflow', slideEl, isHorizontal ? 'right' : 'bottom'); } if (shadowBeforeEl) shadowBeforeEl.style.opacity = offsetMultiplier > 0 ? offsetMultiplier : 0; if (shadowAfterEl) shadowAfterEl.style.opacity = -offsetMultiplier > 0 ? -offsetMultiplier : 0; } } }; const setTransition = (duration) => { const transformElements = swiper.slides.map((slideEl) => getSlideTransformEl(slideEl)); transformElements.forEach((el) => { el.style.transitionDuration = `${duration}ms`; el.querySelectorAll( '.swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left', ).forEach((shadowEl) => { shadowEl.style.transitionDuration = `${duration}ms`; }); }); }; effectInit({ effect: 'coverflow', swiper, on, setTranslate, setTransition, perspective: () => true, overwriteParams: () => ({ watchSlidesProgress: true, }), }); } ================================================ FILE: src/modules/effect-creative/effect-creative.css ================================================ .swiper-creative { .swiper-slide { backface-visibility: hidden; overflow: hidden; transition-property: transform, opacity, height; } } ================================================ FILE: src/modules/effect-creative/effect-creative.mjs ================================================ import createShadow from '../../shared/create-shadow.mjs'; import effectInit from '../../shared/effect-init.mjs'; import effectTarget from '../../shared/effect-target.mjs'; import effectVirtualTransitionEnd from '../../shared/effect-virtual-transition-end.mjs'; import { getRotateFix, getSlideTransformEl } from '../../shared/utils.mjs'; export default function EffectCreative({ swiper, extendParams, on }) { extendParams({ creativeEffect: { limitProgress: 1, shadowPerProgress: false, progressMultiplier: 1, perspective: true, prev: { translate: [0, 0, 0], rotate: [0, 0, 0], opacity: 1, scale: 1, }, next: { translate: [0, 0, 0], rotate: [0, 0, 0], opacity: 1, scale: 1, }, }, }); const getTranslateValue = (value) => { if (typeof value === 'string') return value; return `${value}px`; }; const setTranslate = () => { const { slides, wrapperEl, slidesSizesGrid } = swiper; const params = swiper.params.creativeEffect; const { progressMultiplier: multiplier } = params; const isCenteredSlides = swiper.params.centeredSlides; const rotateFix = getRotateFix(swiper); if (isCenteredSlides) { const margin = slidesSizesGrid[0] / 2 - swiper.params.slidesOffsetBefore || 0; wrapperEl.style.transform = `translateX(calc(50% - ${margin}px))`; } for (let i = 0; i < slides.length; i += 1) { const slideEl = slides[i]; const slideProgress = slideEl.progress; const progress = Math.min( Math.max(slideEl.progress, -params.limitProgress), params.limitProgress, ); let originalProgress = progress; if (!isCenteredSlides) { originalProgress = Math.min( Math.max(slideEl.originalProgress, -params.limitProgress), params.limitProgress, ); } const offset = slideEl.swiperSlideOffset; const t = [swiper.params.cssMode ? -offset - swiper.translate : -offset, 0, 0]; const r = [0, 0, 0]; let custom = false; if (!swiper.isHorizontal()) { t[1] = t[0]; t[0] = 0; } let data = { translate: [0, 0, 0], rotate: [0, 0, 0], scale: 1, opacity: 1, }; if (progress < 0) { data = params.next; custom = true; } else if (progress > 0) { data = params.prev; custom = true; } // set translate t.forEach((value, index) => { t[index] = `calc(${value}px + (${getTranslateValue(data.translate[index])} * ${Math.abs( progress * multiplier, )}))`; }); // set rotates r.forEach((value, index) => { let val = data.rotate[index] * Math.abs(progress * multiplier); r[index] = val; }); slideEl.style.zIndex = -Math.abs(Math.round(slideProgress)) + slides.length; const translateString = t.join(', '); const rotateString = `rotateX(${rotateFix(r[0])}deg) rotateY(${rotateFix( r[1], )}deg) rotateZ(${rotateFix(r[2])}deg)`; const scaleString = originalProgress < 0 ? `scale(${1 + (1 - data.scale) * originalProgress * multiplier})` : `scale(${1 - (1 - data.scale) * originalProgress * multiplier})`; const opacityString = originalProgress < 0 ? 1 + (1 - data.opacity) * originalProgress * multiplier : 1 - (1 - data.opacity) * originalProgress * multiplier; const transform = `translate3d(${translateString}) ${rotateString} ${scaleString}`; // Set shadows if ((custom && data.shadow) || !custom) { let shadowEl = slideEl.querySelector('.swiper-slide-shadow'); if (!shadowEl && data.shadow) { shadowEl = createShadow('creative', slideEl); } if (shadowEl) { const shadowOpacity = params.shadowPerProgress ? progress * (1 / params.limitProgress) : progress; shadowEl.style.opacity = Math.min(Math.max(Math.abs(shadowOpacity), 0), 1); } } const targetEl = effectTarget(params, slideEl); targetEl.style.transform = transform; targetEl.style.opacity = opacityString; if (data.origin) { targetEl.style.transformOrigin = data.origin; } } }; const setTransition = (duration) => { const transformElements = swiper.slides.map((slideEl) => getSlideTransformEl(slideEl)); transformElements.forEach((el) => { el.style.transitionDuration = `${duration}ms`; el.querySelectorAll('.swiper-slide-shadow').forEach((shadowEl) => { shadowEl.style.transitionDuration = `${duration}ms`; }); }); effectVirtualTransitionEnd({ swiper, duration, transformElements, allSlides: true }); }; effectInit({ effect: 'creative', swiper, on, setTranslate, setTransition, perspective: () => swiper.params.creativeEffect.perspective, overwriteParams: () => ({ watchSlidesProgress: true, virtualTranslate: !swiper.params.cssMode, }), }); } ================================================ FILE: src/modules/effect-cube/effect-cube.css ================================================ .swiper.swiper-cube { overflow: visible; } .swiper-cube { .swiper-slide { pointer-events: none; backface-visibility: hidden; z-index: 1; visibility: hidden; transform-origin: 0 0; width: 100%; height: 100%; .swiper-slide { pointer-events: none; } } &.swiper-rtl .swiper-slide { transform-origin: 100% 0; } .swiper-slide-active { &, & .swiper-slide-active { pointer-events: auto; } } .swiper-slide-active, .swiper-slide-next, .swiper-slide-prev { pointer-events: auto; visibility: visible; } .swiper-cube-shadow { position: absolute; left: 0; bottom: 0px; width: 100%; height: 100%; opacity: 0.6; z-index: 0; &:before { content: ''; background: #000; position: absolute; left: 0; top: 0; bottom: 0; right: 0; -webkit-filter: blur(50px); filter: blur(50px); } } } .swiper-cube { .swiper-slide-next + .swiper-slide { pointer-events: auto; visibility: visible; } } /* Cube slide shadows start */ .swiper-cube { .swiper-slide-shadow-cube.swiper-slide-shadow-top, .swiper-slide-shadow-cube.swiper-slide-shadow-bottom, .swiper-slide-shadow-cube.swiper-slide-shadow-left, .swiper-slide-shadow-cube.swiper-slide-shadow-right { z-index: 0; backface-visibility: hidden; } } /* Cube slide shadows end */ ================================================ FILE: src/modules/effect-cube/effect-cube.mjs ================================================ import effectInit from '../../shared/effect-init.mjs'; import { createElement, getRotateFix } from '../../shared/utils.mjs'; export default function EffectCube({ swiper, extendParams, on }) { extendParams({ cubeEffect: { slideShadows: true, shadow: true, shadowOffset: 20, shadowScale: 0.94, }, }); const createSlideShadows = (slideEl, progress, isHorizontal) => { let shadowBefore = isHorizontal ? slideEl.querySelector('.swiper-slide-shadow-left') : slideEl.querySelector('.swiper-slide-shadow-top'); let shadowAfter = isHorizontal ? slideEl.querySelector('.swiper-slide-shadow-right') : slideEl.querySelector('.swiper-slide-shadow-bottom'); if (!shadowBefore) { shadowBefore = createElement( 'div', `swiper-slide-shadow-cube swiper-slide-shadow-${isHorizontal ? 'left' : 'top'}`.split(' '), ); slideEl.append(shadowBefore); } if (!shadowAfter) { shadowAfter = createElement( 'div', `swiper-slide-shadow-cube swiper-slide-shadow-${isHorizontal ? 'right' : 'bottom'}`.split( ' ', ), ); slideEl.append(shadowAfter); } if (shadowBefore) shadowBefore.style.opacity = Math.max(-progress, 0); if (shadowAfter) shadowAfter.style.opacity = Math.max(progress, 0); }; const recreateShadows = () => { // create new ones const isHorizontal = swiper.isHorizontal(); swiper.slides.forEach((slideEl) => { const progress = Math.max(Math.min(slideEl.progress, 1), -1); createSlideShadows(slideEl, progress, isHorizontal); }); }; const setTranslate = () => { const { el, wrapperEl, slides, width: swiperWidth, height: swiperHeight, rtlTranslate: rtl, size: swiperSize, browser, } = swiper; const r = getRotateFix(swiper); const params = swiper.params.cubeEffect; const isHorizontal = swiper.isHorizontal(); const isVirtual = swiper.virtual && swiper.params.virtual.enabled; let wrapperRotate = 0; let cubeShadowEl; if (params.shadow) { if (isHorizontal) { cubeShadowEl = swiper.wrapperEl.querySelector('.swiper-cube-shadow'); if (!cubeShadowEl) { cubeShadowEl = createElement('div', 'swiper-cube-shadow'); swiper.wrapperEl.append(cubeShadowEl); } cubeShadowEl.style.height = `${swiperWidth}px`; } else { cubeShadowEl = el.querySelector('.swiper-cube-shadow'); if (!cubeShadowEl) { cubeShadowEl = createElement('div', 'swiper-cube-shadow'); el.append(cubeShadowEl); } } } for (let i = 0; i < slides.length; i += 1) { const slideEl = slides[i]; let slideIndex = i; if (isVirtual) { slideIndex = parseInt(slideEl.getAttribute('data-swiper-slide-index'), 10); } let slideAngle = slideIndex * 90; let round = Math.floor(slideAngle / 360); if (rtl) { slideAngle = -slideAngle; round = Math.floor(-slideAngle / 360); } const progress = Math.max(Math.min(slideEl.progress, 1), -1); let tx = 0; let ty = 0; let tz = 0; if (slideIndex % 4 === 0) { tx = -round * 4 * swiperSize; tz = 0; } else if ((slideIndex - 1) % 4 === 0) { tx = 0; tz = -round * 4 * swiperSize; } else if ((slideIndex - 2) % 4 === 0) { tx = swiperSize + round * 4 * swiperSize; tz = swiperSize; } else if ((slideIndex - 3) % 4 === 0) { tx = -swiperSize; tz = 3 * swiperSize + swiperSize * 4 * round; } if (rtl) { tx = -tx; } if (!isHorizontal) { ty = tx; tx = 0; } const transform = `rotateX(${r(isHorizontal ? 0 : -slideAngle)}deg) rotateY(${r( isHorizontal ? slideAngle : 0, )}deg) translate3d(${tx}px, ${ty}px, ${tz}px)`; if (progress <= 1 && progress > -1) { wrapperRotate = slideIndex * 90 + progress * 90; if (rtl) wrapperRotate = -slideIndex * 90 - progress * 90; } slideEl.style.transform = transform; if (params.slideShadows) { createSlideShadows(slideEl, progress, isHorizontal); } } wrapperEl.style.transformOrigin = `50% 50% -${swiperSize / 2}px`; wrapperEl.style['-webkit-transform-origin'] = `50% 50% -${swiperSize / 2}px`; if (params.shadow) { if (isHorizontal) { cubeShadowEl.style.transform = `translate3d(0px, ${ swiperWidth / 2 + params.shadowOffset }px, ${-swiperWidth / 2}px) rotateX(89.99deg) rotateZ(0deg) scale(${params.shadowScale})`; } else { const shadowAngle = Math.abs(wrapperRotate) - Math.floor(Math.abs(wrapperRotate) / 90) * 90; const multiplier = 1.5 - (Math.sin((shadowAngle * 2 * Math.PI) / 360) / 2 + Math.cos((shadowAngle * 2 * Math.PI) / 360) / 2); const scale1 = params.shadowScale; const scale2 = params.shadowScale / multiplier; const offset = params.shadowOffset; cubeShadowEl.style.transform = `scale3d(${scale1}, 1, ${scale2}) translate3d(0px, ${ swiperHeight / 2 + offset }px, ${-swiperHeight / 2 / scale2}px) rotateX(-89.99deg)`; } } const zFactor = (browser.isSafari || browser.isWebView) && browser.needPerspectiveFix ? -swiperSize / 2 : 0; wrapperEl.style.transform = `translate3d(0px,0,${zFactor}px) rotateX(${r( swiper.isHorizontal() ? 0 : wrapperRotate, )}deg) rotateY(${r(swiper.isHorizontal() ? -wrapperRotate : 0)}deg)`; wrapperEl.style.setProperty('--swiper-cube-translate-z', `${zFactor}px`); }; const setTransition = (duration) => { const { el, slides } = swiper; slides.forEach((slideEl) => { slideEl.style.transitionDuration = `${duration}ms`; slideEl .querySelectorAll( '.swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left', ) .forEach((subEl) => { subEl.style.transitionDuration = `${duration}ms`; }); }); if (swiper.params.cubeEffect.shadow && !swiper.isHorizontal()) { const shadowEl = el.querySelector('.swiper-cube-shadow'); if (shadowEl) shadowEl.style.transitionDuration = `${duration}ms`; } }; effectInit({ effect: 'cube', swiper, on, setTranslate, setTransition, recreateShadows, getEffectParams: () => swiper.params.cubeEffect, perspective: () => true, overwriteParams: () => ({ slidesPerView: 1, slidesPerGroup: 1, watchSlidesProgress: true, resistanceRatio: 0, spaceBetween: 0, centeredSlides: false, virtualTranslate: true, }), }); } ================================================ FILE: src/modules/effect-fade/effect-fade.css ================================================ .swiper-fade { &.swiper-free-mode { .swiper-slide { transition-timing-function: ease-out; } } .swiper-slide { pointer-events: none; transition-property: opacity; .swiper-slide { pointer-events: none; } } .swiper-slide-active { pointer-events: auto; & .swiper-slide-active { pointer-events: auto; } } } ================================================ FILE: src/modules/effect-fade/effect-fade.mjs ================================================ import effectInit from '../../shared/effect-init.mjs'; import effectTarget from '../../shared/effect-target.mjs'; import effectVirtualTransitionEnd from '../../shared/effect-virtual-transition-end.mjs'; import { getSlideTransformEl } from '../../shared/utils.mjs'; export default function EffectFade({ swiper, extendParams, on }) { extendParams({ fadeEffect: { crossFade: false, }, }); const setTranslate = () => { const { slides } = swiper; const params = swiper.params.fadeEffect; for (let i = 0; i < slides.length; i += 1) { const slideEl = swiper.slides[i]; const offset = slideEl.swiperSlideOffset; let tx = -offset; if (!swiper.params.virtualTranslate) tx -= swiper.translate; let ty = 0; if (!swiper.isHorizontal()) { ty = tx; tx = 0; } const slideOpacity = swiper.params.fadeEffect.crossFade ? Math.max(1 - Math.abs(slideEl.progress), 0) : 1 + Math.min(Math.max(slideEl.progress, -1), 0); const targetEl = effectTarget(params, slideEl); targetEl.style.opacity = slideOpacity; targetEl.style.transform = `translate3d(${tx}px, ${ty}px, 0px)`; } }; const setTransition = (duration) => { const transformElements = swiper.slides.map((slideEl) => getSlideTransformEl(slideEl)); transformElements.forEach((el) => { el.style.transitionDuration = `${duration}ms`; }); effectVirtualTransitionEnd({ swiper, duration, transformElements, allSlides: true }); }; effectInit({ effect: 'fade', swiper, on, setTranslate, setTransition, overwriteParams: () => ({ slidesPerView: 1, slidesPerGroup: 1, watchSlidesProgress: true, spaceBetween: 0, virtualTranslate: !swiper.params.cssMode, }), }); } ================================================ FILE: src/modules/effect-flip/effect-flip.css ================================================ .swiper.swiper-flip { overflow: visible; } .swiper-flip { .swiper-slide { pointer-events: none; backface-visibility: hidden; z-index: 1; .swiper-slide { pointer-events: none; } } .swiper-slide-active { &, & .swiper-slide-active { pointer-events: auto; } } } /* Flip slide shadows start */ .swiper-flip { .swiper-slide-shadow-flip.swiper-slide-shadow-top, .swiper-slide-shadow-flip.swiper-slide-shadow-bottom, .swiper-slide-shadow-flip.swiper-slide-shadow-left, .swiper-slide-shadow-flip.swiper-slide-shadow-right { z-index: 0; backface-visibility: hidden; } } /* Flip slide shadows end */ ================================================ FILE: src/modules/effect-flip/effect-flip.mjs ================================================ import createShadow from '../../shared/create-shadow.mjs'; import effectInit from '../../shared/effect-init.mjs'; import effectTarget from '../../shared/effect-target.mjs'; import effectVirtualTransitionEnd from '../../shared/effect-virtual-transition-end.mjs'; import { getRotateFix, getSlideTransformEl } from '../../shared/utils.mjs'; export default function EffectFlip({ swiper, extendParams, on }) { extendParams({ flipEffect: { slideShadows: true, limitRotation: true, }, }); const createSlideShadows = (slideEl, progress) => { let shadowBefore = swiper.isHorizontal() ? slideEl.querySelector('.swiper-slide-shadow-left') : slideEl.querySelector('.swiper-slide-shadow-top'); let shadowAfter = swiper.isHorizontal() ? slideEl.querySelector('.swiper-slide-shadow-right') : slideEl.querySelector('.swiper-slide-shadow-bottom'); if (!shadowBefore) { shadowBefore = createShadow('flip', slideEl, swiper.isHorizontal() ? 'left' : 'top'); } if (!shadowAfter) { shadowAfter = createShadow('flip', slideEl, swiper.isHorizontal() ? 'right' : 'bottom'); } if (shadowBefore) shadowBefore.style.opacity = Math.max(-progress, 0); if (shadowAfter) shadowAfter.style.opacity = Math.max(progress, 0); }; const recreateShadows = () => { // Set shadows const params = swiper.params.flipEffect; swiper.slides.forEach((slideEl) => { let progress = slideEl.progress; if (swiper.params.flipEffect.limitRotation) { progress = Math.max(Math.min(slideEl.progress, 1), -1); } createSlideShadows(slideEl, progress, params); }); }; const setTranslate = () => { const { slides, rtlTranslate: rtl } = swiper; const params = swiper.params.flipEffect; const rotateFix = getRotateFix(swiper); for (let i = 0; i < slides.length; i += 1) { const slideEl = slides[i]; let progress = slideEl.progress; if (swiper.params.flipEffect.limitRotation) { progress = Math.max(Math.min(slideEl.progress, 1), -1); } const offset = slideEl.swiperSlideOffset; const rotate = -180 * progress; let rotateY = rotate; let rotateX = 0; let tx = swiper.params.cssMode ? -offset - swiper.translate : -offset; let ty = 0; if (!swiper.isHorizontal()) { ty = tx; tx = 0; rotateX = -rotateY; rotateY = 0; } else if (rtl) { rotateY = -rotateY; } slideEl.style.zIndex = -Math.abs(Math.round(progress)) + slides.length; if (params.slideShadows) { createSlideShadows(slideEl, progress, params); } const transform = `translate3d(${tx}px, ${ty}px, 0px) rotateX(${rotateFix( rotateX, )}deg) rotateY(${rotateFix(rotateY)}deg)`; const targetEl = effectTarget(params, slideEl); targetEl.style.transform = transform; } }; const setTransition = (duration) => { const transformElements = swiper.slides.map((slideEl) => getSlideTransformEl(slideEl)); transformElements.forEach((el) => { el.style.transitionDuration = `${duration}ms`; el.querySelectorAll( '.swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left', ).forEach((shadowEl) => { shadowEl.style.transitionDuration = `${duration}ms`; }); }); effectVirtualTransitionEnd({ swiper, duration, transformElements }); }; effectInit({ effect: 'flip', swiper, on, setTranslate, setTransition, recreateShadows, getEffectParams: () => swiper.params.flipEffect, perspective: () => true, overwriteParams: () => ({ slidesPerView: 1, slidesPerGroup: 1, watchSlidesProgress: true, spaceBetween: 0, virtualTranslate: !swiper.params.cssMode, }), }); } ================================================ FILE: src/modules/free-mode/free-mode.css ================================================ .swiper-free-mode > .swiper-wrapper { transition-timing-function: ease-out; margin: 0 auto; } ================================================ FILE: src/modules/free-mode/free-mode.mjs ================================================ import { elementTransitionEnd, now } from '../../shared/utils.mjs'; export default function freeMode({ swiper, extendParams, emit, once }) { extendParams({ freeMode: { enabled: false, momentum: true, momentumRatio: 1, momentumBounce: true, momentumBounceRatio: 1, momentumVelocityRatio: 1, sticky: false, minimumVelocity: 0.02, }, }); function onTouchStart() { if (swiper.params.cssMode) return; const translate = swiper.getTranslate(); swiper.setTranslate(translate); swiper.setTransition(0); swiper.touchEventsData.velocities.length = 0; swiper.freeMode.onTouchEnd({ currentPos: swiper.rtl ? swiper.translate : -swiper.translate }); } function onTouchMove() { if (swiper.params.cssMode) return; const { touchEventsData: data, touches } = swiper; // Velocity if (data.velocities.length === 0) { data.velocities.push({ position: touches[swiper.isHorizontal() ? 'startX' : 'startY'], time: data.touchStartTime, }); } data.velocities.push({ position: touches[swiper.isHorizontal() ? 'currentX' : 'currentY'], time: now(), }); } function onTouchEnd({ currentPos }) { if (swiper.params.cssMode) return; const { params, wrapperEl, rtlTranslate: rtl, snapGrid, touchEventsData: data } = swiper; // Time diff const touchEndTime = now(); const timeDiff = touchEndTime - data.touchStartTime; if (currentPos < -swiper.minTranslate()) { swiper.slideTo(swiper.activeIndex); return; } if (currentPos > -swiper.maxTranslate()) { if (swiper.slides.length < snapGrid.length) { swiper.slideTo(snapGrid.length - 1); } else { swiper.slideTo(swiper.slides.length - 1); } return; } if (params.freeMode.momentum) { if (data.velocities.length > 1) { const lastMoveEvent = data.velocities.pop(); const velocityEvent = data.velocities.pop(); const distance = lastMoveEvent.position - velocityEvent.position; const time = lastMoveEvent.time - velocityEvent.time; swiper.velocity = distance / time; swiper.velocity /= 2; if (Math.abs(swiper.velocity) < params.freeMode.minimumVelocity) { swiper.velocity = 0; } // this implies that the user stopped moving a finger then released. // There would be no events with distance zero, so the last event is stale. if (time > 150 || now() - lastMoveEvent.time > 300) { swiper.velocity = 0; } } else { swiper.velocity = 0; } swiper.velocity *= params.freeMode.momentumVelocityRatio; data.velocities.length = 0; let momentumDuration = 1000 * params.freeMode.momentumRatio; const momentumDistance = swiper.velocity * momentumDuration; let newPosition = swiper.translate + momentumDistance; if (rtl) newPosition = -newPosition; let doBounce = false; let afterBouncePosition; const bounceAmount = Math.abs(swiper.velocity) * 20 * params.freeMode.momentumBounceRatio; let needsLoopFix; if (newPosition < swiper.maxTranslate()) { if (params.freeMode.momentumBounce) { if (newPosition + swiper.maxTranslate() < -bounceAmount) { newPosition = swiper.maxTranslate() - bounceAmount; } afterBouncePosition = swiper.maxTranslate(); doBounce = true; data.allowMomentumBounce = true; } else { newPosition = swiper.maxTranslate(); } if (params.loop && params.centeredSlides) needsLoopFix = true; } else if (newPosition > swiper.minTranslate()) { if (params.freeMode.momentumBounce) { if (newPosition - swiper.minTranslate() > bounceAmount) { newPosition = swiper.minTranslate() + bounceAmount; } afterBouncePosition = swiper.minTranslate(); doBounce = true; data.allowMomentumBounce = true; } else { newPosition = swiper.minTranslate(); } if (params.loop && params.centeredSlides) needsLoopFix = true; } else if (params.freeMode.sticky) { let nextSlide; for (let j = 0; j < snapGrid.length; j += 1) { if (snapGrid[j] > -newPosition) { nextSlide = j; break; } } if ( Math.abs(snapGrid[nextSlide] - newPosition) < Math.abs(snapGrid[nextSlide - 1] - newPosition) || swiper.swipeDirection === 'next' ) { newPosition = snapGrid[nextSlide]; } else { newPosition = snapGrid[nextSlide - 1]; } newPosition = -newPosition; } if (needsLoopFix) { once('transitionEnd', () => { swiper.loopFix(); }); } // Fix duration if (swiper.velocity !== 0) { if (rtl) { momentumDuration = Math.abs((-newPosition - swiper.translate) / swiper.velocity); } else { momentumDuration = Math.abs((newPosition - swiper.translate) / swiper.velocity); } if (params.freeMode.sticky) { // If freeMode.sticky is active and the user ends a swipe with a slow-velocity // event, then durations can be 20+ seconds to slide one (or zero!) slides. // It's easy to see this when simulating touch with mouse events. To fix this, // limit single-slide swipes to the default slide duration. This also has the // nice side effect of matching slide speed if the user stopped moving before // lifting finger or mouse vs. moving slowly before lifting the finger/mouse. // For faster swipes, also apply limits (albeit higher ones). const moveDistance = Math.abs((rtl ? -newPosition : newPosition) - swiper.translate); const currentSlideSize = swiper.slidesSizesGrid[swiper.activeIndex]; if (moveDistance < currentSlideSize) { momentumDuration = params.speed; } else if (moveDistance < 2 * currentSlideSize) { momentumDuration = params.speed * 1.5; } else { momentumDuration = params.speed * 2.5; } } } else if (params.freeMode.sticky) { swiper.slideToClosest(); return; } if (params.freeMode.momentumBounce && doBounce) { swiper.updateProgress(afterBouncePosition); swiper.setTransition(momentumDuration); swiper.setTranslate(newPosition); swiper.transitionStart(true, swiper.swipeDirection); swiper.animating = true; elementTransitionEnd(wrapperEl, () => { if (!swiper || swiper.destroyed || !data.allowMomentumBounce) return; emit('momentumBounce'); swiper.setTransition(params.speed); setTimeout(() => { swiper.setTranslate(afterBouncePosition); elementTransitionEnd(wrapperEl, () => { if (!swiper || swiper.destroyed) return; swiper.transitionEnd(); }); }, 0); }); } else if (swiper.velocity) { emit('_freeModeNoMomentumRelease'); swiper.updateProgress(newPosition); swiper.setTransition(momentumDuration); swiper.setTranslate(newPosition); swiper.transitionStart(true, swiper.swipeDirection); if (!swiper.animating) { swiper.animating = true; elementTransitionEnd(wrapperEl, () => { if (!swiper || swiper.destroyed) return; swiper.transitionEnd(); }); } } else { swiper.updateProgress(newPosition); } swiper.updateActiveIndex(); swiper.updateSlidesClasses(); } else if (params.freeMode.sticky) { swiper.slideToClosest(); return; } else if (params.freeMode) { emit('_freeModeNoMomentumRelease'); } if (!params.freeMode.momentum || timeDiff >= params.longSwipesMs) { emit('_freeModeStaticRelease'); swiper.updateProgress(); swiper.updateActiveIndex(); swiper.updateSlidesClasses(); } } Object.assign(swiper, { freeMode: { onTouchStart, onTouchMove, onTouchEnd, }, }); } ================================================ FILE: src/modules/grid/grid.css ================================================ .swiper-grid > .swiper-wrapper { flex-wrap: wrap; } .swiper-grid-column > .swiper-wrapper { flex-wrap: wrap; flex-direction: column; } ================================================ FILE: src/modules/grid/grid.mjs ================================================ export default function Grid({ swiper, extendParams, on }) { extendParams({ grid: { rows: 1, fill: 'column', }, }); let slidesNumberEvenToRows; let slidesPerRow; let numFullColumns; let wasMultiRow; const getSpaceBetween = () => { let spaceBetween = swiper.params.spaceBetween; if (typeof spaceBetween === 'string' && spaceBetween.indexOf('%') >= 0) { spaceBetween = (parseFloat(spaceBetween.replace('%', '')) / 100) * swiper.size; } else if (typeof spaceBetween === 'string') { spaceBetween = parseFloat(spaceBetween); } return spaceBetween; }; const initSlides = (slides) => { const { slidesPerView } = swiper.params; const { rows, fill } = swiper.params.grid; const slidesLength = swiper.virtual && swiper.params.virtual.enabled ? swiper.virtual.slides.length : slides.length; numFullColumns = Math.floor(slidesLength / rows); if (Math.floor(slidesLength / rows) === slidesLength / rows) { slidesNumberEvenToRows = slidesLength; } else { slidesNumberEvenToRows = Math.ceil(slidesLength / rows) * rows; } if (slidesPerView !== 'auto' && fill === 'row') { slidesNumberEvenToRows = Math.max(slidesNumberEvenToRows, slidesPerView * rows); } slidesPerRow = slidesNumberEvenToRows / rows; }; const unsetSlides = () => { if (swiper.slides) { swiper.slides.forEach((slide) => { if (slide.swiperSlideGridSet) { slide.style.height = ''; slide.style[swiper.getDirectionLabel('margin-top')] = ''; } }); } }; const updateSlide = (i, slide, slides) => { const { slidesPerGroup } = swiper.params; const spaceBetween = getSpaceBetween(); const { rows, fill } = swiper.params.grid; const slidesLength = swiper.virtual && swiper.params.virtual.enabled ? swiper.virtual.slides.length : slides.length; // Set slides order let newSlideOrderIndex; let column; let row; if (fill === 'row' && slidesPerGroup > 1) { const groupIndex = Math.floor(i / (slidesPerGroup * rows)); const slideIndexInGroup = i - rows * slidesPerGroup * groupIndex; const columnsInGroup = groupIndex === 0 ? slidesPerGroup : Math.min( Math.ceil((slidesLength - groupIndex * rows * slidesPerGroup) / rows), slidesPerGroup, ); row = Math.floor(slideIndexInGroup / columnsInGroup); column = slideIndexInGroup - row * columnsInGroup + groupIndex * slidesPerGroup; newSlideOrderIndex = column + (row * slidesNumberEvenToRows) / rows; slide.style.order = newSlideOrderIndex; } else if (fill === 'column') { column = Math.floor(i / rows); row = i - column * rows; if (column > numFullColumns || (column === numFullColumns && row === rows - 1)) { row += 1; if (row >= rows) { row = 0; column += 1; } } } else { row = Math.floor(i / slidesPerRow); column = i - row * slidesPerRow; } slide.row = row; slide.column = column; slide.style.height = `calc((100% - ${(rows - 1) * spaceBetween}px) / ${rows})`; slide.style[swiper.getDirectionLabel('margin-top')] = row !== 0 ? spaceBetween && `${spaceBetween}px` : ''; slide.swiperSlideGridSet = true; }; const updateWrapperSize = (slideSize, snapGrid) => { const { centeredSlides, roundLengths } = swiper.params; const spaceBetween = getSpaceBetween(); const { rows } = swiper.params.grid; swiper.virtualSize = (slideSize + spaceBetween) * slidesNumberEvenToRows; swiper.virtualSize = Math.ceil(swiper.virtualSize / rows) - spaceBetween; if (!swiper.params.cssMode) { swiper.wrapperEl.style[swiper.getDirectionLabel('width')] = `${ swiper.virtualSize + spaceBetween }px`; } if (centeredSlides) { const newSlidesGrid = []; for (let i = 0; i < snapGrid.length; i += 1) { let slidesGridItem = snapGrid[i]; if (roundLengths) slidesGridItem = Math.floor(slidesGridItem); if (snapGrid[i] < swiper.virtualSize + snapGrid[0]) newSlidesGrid.push(slidesGridItem); } snapGrid.splice(0, snapGrid.length); snapGrid.push(...newSlidesGrid); } }; const onInit = () => { wasMultiRow = swiper.params.grid && swiper.params.grid.rows > 1; }; const onUpdate = () => { const { params, el } = swiper; const isMultiRow = params.grid && params.grid.rows > 1; if (wasMultiRow && !isMultiRow) { el.classList.remove( `${params.containerModifierClass}grid`, `${params.containerModifierClass}grid-column`, ); numFullColumns = 1; swiper.emitContainerClasses(); } else if (!wasMultiRow && isMultiRow) { el.classList.add(`${params.containerModifierClass}grid`); if (params.grid.fill === 'column') { el.classList.add(`${params.containerModifierClass}grid-column`); } swiper.emitContainerClasses(); } wasMultiRow = isMultiRow; }; on('init', onInit); on('update', onUpdate); swiper.grid = { initSlides, unsetSlides, updateSlide, updateWrapperSize, }; } ================================================ FILE: src/modules/hash-navigation/hash-navigation.css ================================================ ================================================ FILE: src/modules/hash-navigation/hash-navigation.mjs ================================================ import { getWindow, getDocument } from 'ssr-window'; import { elementChildren } from '../../shared/utils.mjs'; export default function HashNavigation({ swiper, extendParams, emit, on }) { let initialized = false; const document = getDocument(); const window = getWindow(); extendParams({ hashNavigation: { enabled: false, replaceState: false, watchState: false, getSlideIndex(_s, hash) { if (swiper.virtual && swiper.params.virtual.enabled) { const slideWithHash = swiper.slides.find( (slideEl) => slideEl.getAttribute('data-hash') === hash, ); if (!slideWithHash) return 0; const index = parseInt(slideWithHash.getAttribute('data-swiper-slide-index'), 10); return index; } return swiper.getSlideIndex( elementChildren( swiper.slidesEl, `.${swiper.params.slideClass}[data-hash="${hash}"], swiper-slide[data-hash="${hash}"]`, )[0], ); }, }, }); const onHashChange = () => { emit('hashChange'); const newHash = document.location.hash.replace('#', ''); const activeSlideEl = swiper.virtual && swiper.params.virtual.enabled ? swiper.slidesEl.querySelector(`[data-swiper-slide-index="${swiper.activeIndex}"]`) : swiper.slides[swiper.activeIndex]; const activeSlideHash = activeSlideEl ? activeSlideEl.getAttribute('data-hash') : ''; if (newHash !== activeSlideHash) { const newIndex = swiper.params.hashNavigation.getSlideIndex(swiper, newHash); if (typeof newIndex === 'undefined' || Number.isNaN(newIndex)) return; swiper.slideTo(newIndex); } }; const setHash = () => { if (!initialized || !swiper.params.hashNavigation.enabled) return; const activeSlideEl = swiper.virtual && swiper.params.virtual.enabled ? swiper.slidesEl.querySelector(`[data-swiper-slide-index="${swiper.activeIndex}"]`) : swiper.slides[swiper.activeIndex]; const activeSlideHash = activeSlideEl ? activeSlideEl.getAttribute('data-hash') || activeSlideEl.getAttribute('data-history') : ''; if ( swiper.params.hashNavigation.replaceState && window.history && window.history.replaceState ) { window.history.replaceState(null, null, `#${activeSlideHash}` || ''); emit('hashSet'); } else { document.location.hash = activeSlideHash || ''; emit('hashSet'); } }; const init = () => { if ( !swiper.params.hashNavigation.enabled || (swiper.params.history && swiper.params.history.enabled) ) return; initialized = true; const hash = document.location.hash.replace('#', ''); if (hash) { const speed = 0; const index = swiper.params.hashNavigation.getSlideIndex(swiper, hash); swiper.slideTo(index || 0, speed, swiper.params.runCallbacksOnInit, true); } if (swiper.params.hashNavigation.watchState) { window.addEventListener('hashchange', onHashChange); } }; const destroy = () => { if (swiper.params.hashNavigation.watchState) { window.removeEventListener('hashchange', onHashChange); } }; on('init', () => { if (swiper.params.hashNavigation.enabled) { init(); } }); on('destroy', () => { if (swiper.params.hashNavigation.enabled) { destroy(); } }); on('transitionEnd _freeModeNoMomentumRelease', () => { if (initialized) { setHash(); } }); on('slideChange', () => { if (initialized && swiper.params.cssMode) { setHash(); } }); } ================================================ FILE: src/modules/history/history.css ================================================ ================================================ FILE: src/modules/history/history.mjs ================================================ import { getWindow } from 'ssr-window'; export default function History({ swiper, extendParams, on }) { extendParams({ history: { enabled: false, root: '', replaceState: false, key: 'slides', keepQuery: false, }, }); let initialized = false; let paths = {}; const slugify = (text) => { return text .toString() .replace(/\s+/g, '-') .replace(/[^\w-]+/g, '') .replace(/--+/g, '-') .replace(/^-+/, '') .replace(/-+$/, ''); }; const getPathValues = (urlOverride) => { const window = getWindow(); let location; if (urlOverride) { location = new URL(urlOverride); } else { location = window.location; } const pathArray = location.pathname .slice(1) .split('/') .filter((part) => part !== ''); const total = pathArray.length; const key = pathArray[total - 2]; const value = pathArray[total - 1]; return { key, value }; }; const setHistory = (key, index) => { const window = getWindow(); if (!initialized || !swiper.params.history.enabled) return; let location; if (swiper.params.url) { location = new URL(swiper.params.url); } else { location = window.location; } const slide = swiper.virtual && swiper.params.virtual.enabled ? swiper.slidesEl.querySelector(`[data-swiper-slide-index="${index}"]`) : swiper.slides[index]; let value = slugify(slide.getAttribute('data-history')); if (swiper.params.history.root.length > 0) { let root = swiper.params.history.root; if (root[root.length - 1] === '/') root = root.slice(0, root.length - 1); value = `${root}/${key ? `${key}/` : ''}${value}`; } else if (!location.pathname.includes(key)) { value = `${key ? `${key}/` : ''}${value}`; } if (swiper.params.history.keepQuery) { value += location.search; } const currentState = window.history.state; if (currentState && currentState.value === value) { return; } if (swiper.params.history.replaceState) { window.history.replaceState({ value }, null, value); } else { window.history.pushState({ value }, null, value); } }; const scrollToSlide = (speed, value, runCallbacks) => { if (value) { for (let i = 0, length = swiper.slides.length; i < length; i += 1) { const slide = swiper.slides[i]; const slideHistory = slugify(slide.getAttribute('data-history')); if (slideHistory === value) { const index = swiper.getSlideIndex(slide); swiper.slideTo(index, speed, runCallbacks); } } } else { swiper.slideTo(0, speed, runCallbacks); } }; const setHistoryPopState = () => { paths = getPathValues(swiper.params.url); scrollToSlide(swiper.params.speed, paths.value, false); }; const init = () => { const window = getWindow(); if (!swiper.params.history) return; if (!window.history || !window.history.pushState) { swiper.params.history.enabled = false; swiper.params.hashNavigation.enabled = true; return; } initialized = true; paths = getPathValues(swiper.params.url); if (!paths.key && !paths.value) { if (!swiper.params.history.replaceState) { window.addEventListener('popstate', setHistoryPopState); } return; } scrollToSlide(0, paths.value, swiper.params.runCallbacksOnInit); if (!swiper.params.history.replaceState) { window.addEventListener('popstate', setHistoryPopState); } }; const destroy = () => { const window = getWindow(); if (!swiper.params.history.replaceState) { window.removeEventListener('popstate', setHistoryPopState); } }; on('init', () => { if (swiper.params.history.enabled) { init(); } }); on('destroy', () => { if (swiper.params.history.enabled) { destroy(); } }); on('transitionEnd _freeModeNoMomentumRelease', () => { if (initialized) { setHistory(swiper.params.history.key, swiper.activeIndex); } }); on('slideChange', () => { if (initialized && swiper.params.cssMode) { setHistory(swiper.params.history.key, swiper.activeIndex); } }); } ================================================ FILE: src/modules/keyboard/keyboard.css ================================================ ================================================ FILE: src/modules/keyboard/keyboard.mjs ================================================ /* eslint-disable consistent-return */ import { getWindow, getDocument } from 'ssr-window'; import { elementOffset, elementParents } from '../../shared/utils.mjs'; export default function Keyboard({ swiper, extendParams, on, emit }) { const document = getDocument(); const window = getWindow(); swiper.keyboard = { enabled: false, }; extendParams({ keyboard: { enabled: false, onlyInViewport: true, pageUpDown: true, speed: undefined, }, }); function handle(event) { if (!swiper.enabled) return; const { rtlTranslate: rtl } = swiper; let e = event; if (e.originalEvent) e = e.originalEvent; // jquery fix const kc = e.keyCode || e.charCode; const pageUpDown = swiper.params.keyboard.pageUpDown; const isPageUp = pageUpDown && kc === 33; const isPageDown = pageUpDown && kc === 34; const isArrowLeft = kc === 37; const isArrowRight = kc === 39; const isArrowUp = kc === 38; const isArrowDown = kc === 40; // Directions locks if ( !swiper.allowSlideNext && ((swiper.isHorizontal() && isArrowRight) || (swiper.isVertical() && isArrowDown) || isPageDown) ) { return false; } if ( !swiper.allowSlidePrev && ((swiper.isHorizontal() && isArrowLeft) || (swiper.isVertical() && isArrowUp) || isPageUp) ) { return false; } if (e.shiftKey || e.altKey || e.ctrlKey || e.metaKey) { return undefined; } if ( document.activeElement && (document.activeElement.isContentEditable || (document.activeElement.nodeName && (document.activeElement.nodeName.toLowerCase() === 'input' || document.activeElement.nodeName.toLowerCase() === 'textarea'))) ) { return undefined; } if ( swiper.params.keyboard.onlyInViewport && (isPageUp || isPageDown || isArrowLeft || isArrowRight || isArrowUp || isArrowDown) ) { let inView = false; // Check that swiper should be inside of visible area of window if ( elementParents(swiper.el, `.${swiper.params.slideClass}, swiper-slide`).length > 0 && elementParents(swiper.el, `.${swiper.params.slideActiveClass}`).length === 0 ) { return undefined; } const el = swiper.el; const swiperWidth = el.clientWidth; const swiperHeight = el.clientHeight; const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; const swiperOffset = elementOffset(el); if (rtl) swiperOffset.left -= el.scrollLeft; const swiperCoord = [ [swiperOffset.left, swiperOffset.top], [swiperOffset.left + swiperWidth, swiperOffset.top], [swiperOffset.left, swiperOffset.top + swiperHeight], [swiperOffset.left + swiperWidth, swiperOffset.top + swiperHeight], ]; for (let i = 0; i < swiperCoord.length; i += 1) { const point = swiperCoord[i]; if (point[0] >= 0 && point[0] <= windowWidth && point[1] >= 0 && point[1] <= windowHeight) { if (point[0] === 0 && point[1] === 0) continue; // eslint-disable-line inView = true; } } if (!inView) return undefined; } const speed = swiper.params.keyboard.speed; if (swiper.isHorizontal()) { if (isPageUp || isPageDown || isArrowLeft || isArrowRight) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; } if (((isPageDown || isArrowRight) && !rtl) || ((isPageUp || isArrowLeft) && rtl)) swiper.slideNext(speed); if (((isPageUp || isArrowLeft) && !rtl) || ((isPageDown || isArrowRight) && rtl)) swiper.slidePrev(speed); } else { if (isPageUp || isPageDown || isArrowUp || isArrowDown) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; } if (isPageDown || isArrowDown) swiper.slideNext(speed); if (isPageUp || isArrowUp) swiper.slidePrev(speed); } emit('keyPress', kc); return undefined; } function enable() { if (swiper.keyboard.enabled) return; document.addEventListener('keydown', handle); swiper.keyboard.enabled = true; } function disable() { if (!swiper.keyboard.enabled) return; document.removeEventListener('keydown', handle); swiper.keyboard.enabled = false; } on('init', () => { if (swiper.params.keyboard.enabled) { enable(); } }); on('destroy', () => { if (swiper.keyboard.enabled) { disable(); } }); Object.assign(swiper.keyboard, { enable, disable, }); } ================================================ FILE: src/modules/manipulation/manipulation.css ================================================ ================================================ FILE: src/modules/manipulation/manipulation.mjs ================================================ import appendSlide from './methods/appendSlide.mjs'; import prependSlide from './methods/prependSlide.mjs'; import addSlide from './methods/addSlide.mjs'; import removeSlide from './methods/removeSlide.mjs'; import removeAllSlides from './methods/removeAllSlides.mjs'; export default function Manipulation({ swiper }) { Object.assign(swiper, { appendSlide: appendSlide.bind(swiper), prependSlide: prependSlide.bind(swiper), addSlide: addSlide.bind(swiper), removeSlide: removeSlide.bind(swiper), removeAllSlides: removeAllSlides.bind(swiper), }); } ================================================ FILE: src/modules/manipulation/methods/addSlide.mjs ================================================ export default function addSlide(index, slides) { const swiper = this; const { params, activeIndex, slidesEl } = swiper; let activeIndexBuffer = activeIndex; if (params.loop) { activeIndexBuffer -= swiper.loopedSlides; swiper.loopDestroy(); swiper.recalcSlides(); } const baseLength = swiper.slides.length; if (index <= 0) { swiper.prependSlide(slides); return; } if (index >= baseLength) { swiper.appendSlide(slides); return; } let newActiveIndex = activeIndexBuffer > index ? activeIndexBuffer + 1 : activeIndexBuffer; const slidesBuffer = []; for (let i = baseLength - 1; i >= index; i -= 1) { const currentSlide = swiper.slides[i]; currentSlide.remove(); slidesBuffer.unshift(currentSlide); } if (typeof slides === 'object' && 'length' in slides) { for (let i = 0; i < slides.length; i += 1) { if (slides[i]) slidesEl.append(slides[i]); } newActiveIndex = activeIndexBuffer > index ? activeIndexBuffer + slides.length : activeIndexBuffer; } else { slidesEl.append(slides); } for (let i = 0; i < slidesBuffer.length; i += 1) { slidesEl.append(slidesBuffer[i]); } swiper.recalcSlides(); if (params.loop) { swiper.loopCreate(); } if (!params.observer || swiper.isElement) { swiper.update(); } if (params.loop) { swiper.slideTo(newActiveIndex + swiper.loopedSlides, 0, false); } else { swiper.slideTo(newActiveIndex, 0, false); } } ================================================ FILE: src/modules/manipulation/methods/appendSlide.mjs ================================================ import { setInnerHTML } from '../../../shared/utils.mjs'; export default function appendSlide(slides) { const swiper = this; const { params, slidesEl } = swiper; if (params.loop) { swiper.loopDestroy(); } const appendElement = (slideEl) => { if (typeof slideEl === 'string') { const tempDOM = document.createElement('div'); setInnerHTML(tempDOM, slideEl); slidesEl.append(tempDOM.children[0]); setInnerHTML(tempDOM, ''); } else { slidesEl.append(slideEl); } }; if (typeof slides === 'object' && 'length' in slides) { for (let i = 0; i < slides.length; i += 1) { if (slides[i]) appendElement(slides[i]); } } else { appendElement(slides); } swiper.recalcSlides(); if (params.loop) { swiper.loopCreate(); } if (!params.observer || swiper.isElement) { swiper.update(); } } ================================================ FILE: src/modules/manipulation/methods/prependSlide.mjs ================================================ import { setInnerHTML } from '../../../shared/utils.mjs'; export default function prependSlide(slides) { const swiper = this; const { params, activeIndex, slidesEl } = swiper; if (params.loop) { swiper.loopDestroy(); } let newActiveIndex = activeIndex + 1; const prependElement = (slideEl) => { if (typeof slideEl === 'string') { const tempDOM = document.createElement('div'); setInnerHTML(tempDOM, slideEl); slidesEl.prepend(tempDOM.children[0]); setInnerHTML(tempDOM, ''); } else { slidesEl.prepend(slideEl); } }; if (typeof slides === 'object' && 'length' in slides) { for (let i = 0; i < slides.length; i += 1) { if (slides[i]) prependElement(slides[i]); } newActiveIndex = activeIndex + slides.length; } else { prependElement(slides); } swiper.recalcSlides(); if (params.loop) { swiper.loopCreate(); } if (!params.observer || swiper.isElement) { swiper.update(); } swiper.slideTo(newActiveIndex, 0, false); } ================================================ FILE: src/modules/manipulation/methods/removeAllSlides.mjs ================================================ export default function removeAllSlides() { const swiper = this; const slidesIndexes = []; for (let i = 0; i < swiper.slides.length; i += 1) { slidesIndexes.push(i); } swiper.removeSlide(slidesIndexes); } ================================================ FILE: src/modules/manipulation/methods/removeSlide.mjs ================================================ export default function removeSlide(slidesIndexes) { const swiper = this; const { params, activeIndex } = swiper; let activeIndexBuffer = activeIndex; if (params.loop) { activeIndexBuffer -= swiper.loopedSlides; swiper.loopDestroy(); } let newActiveIndex = activeIndexBuffer; let indexToRemove; if (typeof slidesIndexes === 'object' && 'length' in slidesIndexes) { for (let i = 0; i < slidesIndexes.length; i += 1) { indexToRemove = slidesIndexes[i]; if (swiper.slides[indexToRemove]) swiper.slides[indexToRemove].remove(); if (indexToRemove < newActiveIndex) newActiveIndex -= 1; } newActiveIndex = Math.max(newActiveIndex, 0); } else { indexToRemove = slidesIndexes; if (swiper.slides[indexToRemove]) swiper.slides[indexToRemove].remove(); if (indexToRemove < newActiveIndex) newActiveIndex -= 1; newActiveIndex = Math.max(newActiveIndex, 0); } swiper.recalcSlides(); if (params.loop) { swiper.loopCreate(); } if (!params.observer || swiper.isElement) { swiper.update(); } if (params.loop) { swiper.slideTo(newActiveIndex + swiper.loopedSlides, 0, false); } else { swiper.slideTo(newActiveIndex, 0, false); } } ================================================ FILE: src/modules/mousewheel/mousewheel.css ================================================ ================================================ FILE: src/modules/mousewheel/mousewheel.mjs ================================================ /* eslint-disable consistent-return */ import { getWindow } from 'ssr-window'; import { now, nextTick } from '../../shared/utils.mjs'; export default function Mousewheel({ swiper, extendParams, on, emit }) { const window = getWindow(); extendParams({ mousewheel: { enabled: false, releaseOnEdges: false, invert: false, forceToAxis: false, sensitivity: 1, eventsTarget: 'container', thresholdDelta: null, thresholdTime: null, noMousewheelClass: 'swiper-no-mousewheel', }, }); swiper.mousewheel = { enabled: false, }; let timeout; let lastScrollTime = now(); let lastEventBeforeSnap; const recentWheelEvents = []; function normalize(e) { // Reasonable defaults const PIXEL_STEP = 10; const LINE_HEIGHT = 40; const PAGE_HEIGHT = 800; let sX = 0; let sY = 0; // spinX, spinY let pX = 0; let pY = 0; // pixelX, pixelY // Legacy if ('detail' in e) { sY = e.detail; } if ('wheelDelta' in e) { sY = -e.wheelDelta / 120; } if ('wheelDeltaY' in e) { sY = -e.wheelDeltaY / 120; } if ('wheelDeltaX' in e) { sX = -e.wheelDeltaX / 120; } // side scrolling on FF with DOMMouseScroll if ('axis' in e && e.axis === e.HORIZONTAL_AXIS) { sX = sY; sY = 0; } pX = sX * PIXEL_STEP; pY = sY * PIXEL_STEP; if ('deltaY' in e) { pY = e.deltaY; } if ('deltaX' in e) { pX = e.deltaX; } if (e.shiftKey && !pX) { // if user scrolls with shift he wants horizontal scroll pX = pY; pY = 0; } if ((pX || pY) && e.deltaMode) { if (e.deltaMode === 1) { // delta in LINE units pX *= LINE_HEIGHT; pY *= LINE_HEIGHT; } else { // delta in PAGE units pX *= PAGE_HEIGHT; pY *= PAGE_HEIGHT; } } // Fall-back if spin cannot be determined if (pX && !sX) { sX = pX < 1 ? -1 : 1; } if (pY && !sY) { sY = pY < 1 ? -1 : 1; } return { spinX: sX, spinY: sY, pixelX: pX, pixelY: pY, }; } function handleMouseEnter() { if (!swiper.enabled) return; swiper.mouseEntered = true; } function handleMouseLeave() { if (!swiper.enabled) return; swiper.mouseEntered = false; } function animateSlider(newEvent) { if ( swiper.params.mousewheel.thresholdDelta && newEvent.delta < swiper.params.mousewheel.thresholdDelta ) { // Prevent if delta of wheel scroll delta is below configured threshold return false; } if ( swiper.params.mousewheel.thresholdTime && now() - lastScrollTime < swiper.params.mousewheel.thresholdTime ) { // Prevent if time between scrolls is below configured threshold return false; } // If the movement is NOT big enough and // if the last time the user scrolled was too close to the current one (avoid continuously triggering the slider): // Don't go any further (avoid insignificant scroll movement). if (newEvent.delta >= 6 && now() - lastScrollTime < 60) { // Return false as a default return true; } // If user is scrolling towards the end: // If the slider hasn't hit the latest slide or // if the slider is a loop and // if the slider isn't moving right now: // Go to next slide and // emit a scroll event. // Else (the user is scrolling towards the beginning) and // if the slider hasn't hit the first slide or // if the slider is a loop and // if the slider isn't moving right now: // Go to prev slide and // emit a scroll event. if (newEvent.direction < 0) { if ((!swiper.isEnd || swiper.params.loop) && !swiper.animating) { swiper.slideNext(); emit('scroll', newEvent.raw); } } else if ((!swiper.isBeginning || swiper.params.loop) && !swiper.animating) { swiper.slidePrev(); emit('scroll', newEvent.raw); } // If you got here is because an animation has been triggered so store the current time lastScrollTime = new window.Date().getTime(); // Return false as a default return false; } function releaseScroll(newEvent) { const params = swiper.params.mousewheel; if (newEvent.direction < 0) { if (swiper.isEnd && !swiper.params.loop && params.releaseOnEdges) { // Return true to animate scroll on edges return true; } } else if (swiper.isBeginning && !swiper.params.loop && params.releaseOnEdges) { // Return true to animate scroll on edges return true; } return false; } function handle(event) { let e = event; let disableParentSwiper = true; if (!swiper.enabled) return; // Ignore event if the target or its parents have the swiper-no-mousewheel class if (event.target.closest(`.${swiper.params.mousewheel.noMousewheelClass}`)) return; const params = swiper.params.mousewheel; if (swiper.params.cssMode) { e.preventDefault(); } let targetEl = swiper.el; if (swiper.params.mousewheel.eventsTarget !== 'container') { targetEl = document.querySelector(swiper.params.mousewheel.eventsTarget); } const targetElContainsTarget = targetEl && targetEl.contains(e.target); if (!swiper.mouseEntered && !targetElContainsTarget && !params.releaseOnEdges) return true; if (e.originalEvent) e = e.originalEvent; // jquery fix let delta = 0; const rtlFactor = swiper.rtlTranslate ? -1 : 1; const data = normalize(e); if (params.forceToAxis) { if (swiper.isHorizontal()) { if (Math.abs(data.pixelX) > Math.abs(data.pixelY)) delta = -data.pixelX * rtlFactor; else return true; } else if (Math.abs(data.pixelY) > Math.abs(data.pixelX)) delta = -data.pixelY; else return true; } else { delta = Math.abs(data.pixelX) > Math.abs(data.pixelY) ? -data.pixelX * rtlFactor : -data.pixelY; } if (delta === 0) return true; if (params.invert) delta = -delta; // Get the scroll positions let positions = swiper.getTranslate() + delta * params.sensitivity; if (positions >= swiper.minTranslate()) positions = swiper.minTranslate(); if (positions <= swiper.maxTranslate()) positions = swiper.maxTranslate(); // When loop is true: // the disableParentSwiper will be true. // When loop is false: // if the scroll positions is not on edge, // then the disableParentSwiper will be true. // if the scroll on edge positions, // then the disableParentSwiper will be false. disableParentSwiper = swiper.params.loop ? true : !(positions === swiper.minTranslate() || positions === swiper.maxTranslate()); if (disableParentSwiper && swiper.params.nested) e.stopPropagation(); if (!swiper.params.freeMode || !swiper.params.freeMode.enabled) { // Register the new event in a variable which stores the relevant data const newEvent = { time: now(), delta: Math.abs(delta), direction: Math.sign(delta), raw: event, }; // Keep the most recent events if (recentWheelEvents.length >= 2) { recentWheelEvents.shift(); // only store the last N events } const prevEvent = recentWheelEvents.length ? recentWheelEvents[recentWheelEvents.length - 1] : undefined; recentWheelEvents.push(newEvent); // If there is at least one previous recorded event: // If direction has changed or // if the scroll is quicker than the previous one: // Animate the slider. // Else (this is the first time the wheel is moved): // Animate the slider. if (prevEvent) { if ( newEvent.direction !== prevEvent.direction || newEvent.delta > prevEvent.delta || newEvent.time > prevEvent.time + 150 ) { animateSlider(newEvent); } } else { animateSlider(newEvent); } // If it's time to release the scroll: // Return now so you don't hit the preventDefault. if (releaseScroll(newEvent)) { return true; } } else { // Freemode or scrollContainer: // If we recently snapped after a momentum scroll, then ignore wheel events // to give time for the deceleration to finish. Stop ignoring after 500 msecs // or if it's a new scroll (larger delta or inverse sign as last event before // an end-of-momentum snap). const newEvent = { time: now(), delta: Math.abs(delta), direction: Math.sign(delta), }; const ignoreWheelEvents = lastEventBeforeSnap && newEvent.time < lastEventBeforeSnap.time + 500 && newEvent.delta <= lastEventBeforeSnap.delta && newEvent.direction === lastEventBeforeSnap.direction; if (!ignoreWheelEvents) { lastEventBeforeSnap = undefined; let position = swiper.getTranslate() + delta * params.sensitivity; const wasBeginning = swiper.isBeginning; const wasEnd = swiper.isEnd; if (position >= swiper.minTranslate()) position = swiper.minTranslate(); if (position <= swiper.maxTranslate()) position = swiper.maxTranslate(); swiper.setTransition(0); swiper.setTranslate(position); swiper.updateProgress(); swiper.updateActiveIndex(); swiper.updateSlidesClasses(); if ((!wasBeginning && swiper.isBeginning) || (!wasEnd && swiper.isEnd)) { swiper.updateSlidesClasses(); } if (swiper.params.loop) { swiper.loopFix({ direction: newEvent.direction < 0 ? 'next' : 'prev', byMousewheel: true, }); } if (swiper.params.freeMode.sticky) { // When wheel scrolling starts with sticky (aka snap) enabled, then detect // the end of a momentum scroll by storing recent (N=15?) wheel events. // 1. do all N events have decreasing or same (absolute value) delta? // 2. did all N events arrive in the last M (M=500?) msecs? // 3. does the earliest event have an (absolute value) delta that's // at least P (P=1?) larger than the most recent event's delta? // 4. does the latest event have a delta that's smaller than Q (Q=6?) pixels? // If 1-4 are "yes" then we're near the end of a momentum scroll deceleration. // Snap immediately and ignore remaining wheel events in this scroll. // See comment above for "remaining wheel events in this scroll" determination. // If 1-4 aren't satisfied, then wait to snap until 500ms after the last event. clearTimeout(timeout); timeout = undefined; if (recentWheelEvents.length >= 15) { recentWheelEvents.shift(); // only store the last N events } const prevEvent = recentWheelEvents.length ? recentWheelEvents[recentWheelEvents.length - 1] : undefined; const firstEvent = recentWheelEvents[0]; recentWheelEvents.push(newEvent); if ( prevEvent && (newEvent.delta > prevEvent.delta || newEvent.direction !== prevEvent.direction) ) { // Increasing or reverse-sign delta means the user started scrolling again. Clear the wheel event log. recentWheelEvents.splice(0); } else if ( recentWheelEvents.length >= 15 && newEvent.time - firstEvent.time < 500 && firstEvent.delta - newEvent.delta >= 1 && newEvent.delta <= 6 ) { // We're at the end of the deceleration of a momentum scroll, so there's no need // to wait for more events. Snap ASAP on the next tick. // Also, because there's some remaining momentum we'll bias the snap in the // direction of the ongoing scroll because it's better UX for the scroll to snap // in the same direction as the scroll instead of reversing to snap. Therefore, // if it's already scrolled more than 20% in the current direction, keep going. const snapToThreshold = delta > 0 ? 0.8 : 0.2; lastEventBeforeSnap = newEvent; recentWheelEvents.splice(0); timeout = nextTick(() => { if (swiper.destroyed || !swiper.params) return; swiper.slideToClosest(swiper.params.speed, true, undefined, snapToThreshold); }, 0); // no delay; move on next tick } if (!timeout) { // if we get here, then we haven't detected the end of a momentum scroll, so // we'll consider a scroll "complete" when there haven't been any wheel events // for 500ms. timeout = nextTick(() => { if (swiper.destroyed || !swiper.params) return; const snapToThreshold = 0.5; lastEventBeforeSnap = newEvent; recentWheelEvents.splice(0); swiper.slideToClosest(swiper.params.speed, true, undefined, snapToThreshold); }, 500); } } // Emit event if (!ignoreWheelEvents) emit('scroll', e); // Stop autoplay if (swiper.params.autoplay && swiper.params.autoplay.disableOnInteraction) swiper.autoplay.stop(); // Return page scroll on edge positions if ( params.releaseOnEdges && (position === swiper.minTranslate() || position === swiper.maxTranslate()) ) { return true; } } } if (e.preventDefault) e.preventDefault(); else e.returnValue = false; return false; } function events(method) { let targetEl = swiper.el; if (swiper.params.mousewheel.eventsTarget !== 'container') { targetEl = document.querySelector(swiper.params.mousewheel.eventsTarget); } targetEl[method]('mouseenter', handleMouseEnter); targetEl[method]('mouseleave', handleMouseLeave); targetEl[method]('wheel', handle); } function enable() { if (swiper.params.cssMode) { swiper.wrapperEl.removeEventListener('wheel', handle); return true; } if (swiper.mousewheel.enabled) return false; events('addEventListener'); swiper.mousewheel.enabled = true; return true; } function disable() { if (swiper.params.cssMode) { swiper.wrapperEl.addEventListener(event, handle); return true; } if (!swiper.mousewheel.enabled) return false; events('removeEventListener'); swiper.mousewheel.enabled = false; return true; } on('init', () => { if (!swiper.params.mousewheel.enabled && swiper.params.cssMode) { disable(); } if (swiper.params.mousewheel.enabled) enable(); }); on('destroy', () => { if (swiper.params.cssMode) { enable(); } if (swiper.mousewheel.enabled) disable(); }); Object.assign(swiper.mousewheel, { enable, disable, }); } ================================================ FILE: src/modules/navigation/navigation.css ================================================ :root { --swiper-navigation-size: 44px; /* --swiper-navigation-top-offset: 50%; --swiper-navigation-sides-offset: 4px; --swiper-navigation-color: var(--swiper-theme-color); */ } .swiper-button-prev, .swiper-button-next { position: absolute; width: var(--swiper-navigation-size); height: var(--swiper-navigation-size); z-index: 10; cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--swiper-navigation-color, var(--swiper-theme-color)); &.swiper-button-disabled { opacity: 0.35; cursor: auto; pointer-events: none; } &.swiper-button-hidden { opacity: 0; cursor: auto; pointer-events: none; } .swiper-navigation-disabled & { display: none !important; } svg { width: 100%; height: 100%; object-fit: contain; transform-origin: center; fill: currentColor; pointer-events: none; } } .swiper-button-lock { display: none; } .swiper-button-prev, .swiper-button-next { top: var(--swiper-navigation-top-offset, 50%); margin-top: calc(0px - (var(--swiper-navigation-size) / 2)); } .swiper-button-prev { left: var(--swiper-navigation-sides-offset, 4px); right: auto; .swiper-navigation-icon { transform: rotate(180deg); } } .swiper-button-next { right: var(--swiper-navigation-sides-offset, 4px); left: auto; } .swiper-horizontal { .swiper-button-prev, .swiper-button-next, ~ .swiper-button-prev, ~ .swiper-button-next { top: var(--swiper-navigation-top-offset, 50%); margin-top: calc(0px - (var(--swiper-navigation-size) / 2)); margin-left: 0; } .swiper-button-prev, & ~ .swiper-button-prev, &.swiper-rtl .swiper-button-next, &.swiper-rtl ~ .swiper-button-next { left: var(--swiper-navigation-sides-offset, 4px); right: auto; } .swiper-button-next, & ~ .swiper-button-next, &.swiper-rtl .swiper-button-prev, &.swiper-rtl ~ .swiper-button-prev { right: var(--swiper-navigation-sides-offset, 4px); left: auto; } .swiper-button-prev, & ~ .swiper-button-prev, &.swiper-rtl .swiper-button-next, &.swiper-rtl ~ .swiper-button-next { .swiper-navigation-icon { transform: rotate(180deg); } } &.swiper-rtl .swiper-button-prev, &.swiper-rtl ~ .swiper-button-prev { .swiper-navigation-icon { transform: rotate(0deg); } } } .swiper-vertical { .swiper-button-prev, .swiper-button-next, ~ .swiper-button-prev, ~ .swiper-button-next { left: var(--swiper-navigation-top-offset, 50%); right: auto; margin-left: calc(0px - (var(--swiper-navigation-size) / 2)); margin-top: 0; } .swiper-button-prev, ~ .swiper-button-prev { top: var(--swiper-navigation-sides-offset, 4px); bottom: auto; .swiper-navigation-icon { transform: rotate(-90deg); } } .swiper-button-next, ~ .swiper-button-next { bottom: var(--swiper-navigation-sides-offset, 4px); top: auto; .swiper-navigation-icon { transform: rotate(90deg); } } } ================================================ FILE: src/modules/navigation/navigation.mjs ================================================ import createElementIfNotDefined from '../../shared/create-element-if-not-defined.mjs'; import { makeElementsArray, setInnerHTML } from '../../shared/utils.mjs'; const arrowSvg = ``; export default function Navigation({ swiper, extendParams, on, emit }) { extendParams({ navigation: { nextEl: null, prevEl: null, addIcons: true, hideOnClick: false, disabledClass: 'swiper-button-disabled', hiddenClass: 'swiper-button-hidden', lockClass: 'swiper-button-lock', navigationDisabledClass: 'swiper-navigation-disabled', }, }); swiper.navigation = { nextEl: null, prevEl: null, arrowSvg, }; function getEl(el) { let res; if (el && typeof el === 'string' && swiper.isElement) { res = swiper.el.querySelector(el) || swiper.hostEl.querySelector(el); if (res) return res; } if (el) { if (typeof el === 'string') res = [...document.querySelectorAll(el)]; if ( swiper.params.uniqueNavElements && typeof el === 'string' && res && res.length > 1 && swiper.el.querySelectorAll(el).length === 1 ) { res = swiper.el.querySelector(el); } else if (res && res.length === 1) { res = res[0]; } } if (el && !res) return el; // if (Array.isArray(res) && res.length === 1) res = res[0]; return res; } function toggleEl(el, disabled) { const params = swiper.params.navigation; el = makeElementsArray(el); el.forEach((subEl) => { if (subEl) { subEl.classList[disabled ? 'add' : 'remove'](...params.disabledClass.split(' ')); if (subEl.tagName === 'BUTTON') subEl.disabled = disabled; if (swiper.params.watchOverflow && swiper.enabled) { subEl.classList[swiper.isLocked ? 'add' : 'remove'](params.lockClass); } } }); } function update() { // Update Navigation Buttons const { nextEl, prevEl } = swiper.navigation; if (swiper.params.loop) { toggleEl(prevEl, false); toggleEl(nextEl, false); return; } toggleEl(prevEl, swiper.isBeginning && !swiper.params.rewind); toggleEl(nextEl, swiper.isEnd && !swiper.params.rewind); } function onPrevClick(e) { e.preventDefault(); if (swiper.isBeginning && !swiper.params.loop && !swiper.params.rewind) return; swiper.slidePrev(); emit('navigationPrev'); } function onNextClick(e) { e.preventDefault(); if (swiper.isEnd && !swiper.params.loop && !swiper.params.rewind) return; swiper.slideNext(); emit('navigationNext'); } function init() { const params = swiper.params.navigation; swiper.params.navigation = createElementIfNotDefined( swiper, swiper.originalParams.navigation, swiper.params.navigation, { nextEl: 'swiper-button-next', prevEl: 'swiper-button-prev', }, ); if (!(params.nextEl || params.prevEl)) return; let nextEl = getEl(params.nextEl); let prevEl = getEl(params.prevEl); Object.assign(swiper.navigation, { nextEl, prevEl, }); nextEl = makeElementsArray(nextEl); prevEl = makeElementsArray(prevEl); const initButton = (el, dir) => { if (el) { if ( params.addIcons && el.matches('.swiper-button-next,.swiper-button-prev') && !el.querySelector('svg') ) { const tempEl = document.createElement('div'); setInnerHTML(tempEl, arrowSvg); el.appendChild(tempEl.querySelector('svg')); tempEl.remove(); } el.addEventListener('click', dir === 'next' ? onNextClick : onPrevClick); } if (!swiper.enabled && el) { el.classList.add(...params.lockClass.split(' ')); } }; nextEl.forEach((el) => initButton(el, 'next')); prevEl.forEach((el) => initButton(el, 'prev')); } function destroy() { let { nextEl, prevEl } = swiper.navigation; nextEl = makeElementsArray(nextEl); prevEl = makeElementsArray(prevEl); const destroyButton = (el, dir) => { el.removeEventListener('click', dir === 'next' ? onNextClick : onPrevClick); el.classList.remove(...swiper.params.navigation.disabledClass.split(' ')); }; nextEl.forEach((el) => destroyButton(el, 'next')); prevEl.forEach((el) => destroyButton(el, 'prev')); } on('init', () => { if (swiper.params.navigation.enabled === false) { // eslint-disable-next-line disable(); } else { init(); update(); } }); on('toEdge fromEdge lock unlock', () => { update(); }); on('destroy', () => { destroy(); }); on('enable disable', () => { let { nextEl, prevEl } = swiper.navigation; nextEl = makeElementsArray(nextEl); prevEl = makeElementsArray(prevEl); if (swiper.enabled) { update(); return; } [...nextEl, ...prevEl] .filter((el) => !!el) .forEach((el) => el.classList.add(swiper.params.navigation.lockClass)); }); on('click', (_s, e) => { let { nextEl, prevEl } = swiper.navigation; nextEl = makeElementsArray(nextEl); prevEl = makeElementsArray(prevEl); const targetEl = e.target; let targetIsButton = prevEl.includes(targetEl) || nextEl.includes(targetEl); if (swiper.isElement && !targetIsButton) { const path = e.path || (e.composedPath && e.composedPath()); if (path) { targetIsButton = path.find((pathEl) => nextEl.includes(pathEl) || prevEl.includes(pathEl)); } } if (swiper.params.navigation.hideOnClick && !targetIsButton) { if ( swiper.pagination && swiper.params.pagination && swiper.params.pagination.clickable && (swiper.pagination.el === targetEl || swiper.pagination.el.contains(targetEl)) ) return; let isHidden; if (nextEl.length) { isHidden = nextEl[0].classList.contains(swiper.params.navigation.hiddenClass); } else if (prevEl.length) { isHidden = prevEl[0].classList.contains(swiper.params.navigation.hiddenClass); } if (isHidden === true) { emit('navigationShow'); } else { emit('navigationHide'); } [...nextEl, ...prevEl] .filter((el) => !!el) .forEach((el) => el.classList.toggle(swiper.params.navigation.hiddenClass)); } }); const enable = () => { swiper.el.classList.remove(...swiper.params.navigation.navigationDisabledClass.split(' ')); init(); update(); }; const disable = () => { swiper.el.classList.add(...swiper.params.navigation.navigationDisabledClass.split(' ')); destroy(); }; Object.assign(swiper.navigation, { enable, disable, update, init, destroy, }); } ================================================ FILE: src/modules/pagination/pagination.css ================================================ :root { /* --swiper-pagination-color: var(--swiper-theme-color); --swiper-pagination-left: auto; --swiper-pagination-right: 8px; --swiper-pagination-bottom: 8px; --swiper-pagination-top: auto; --swiper-pagination-fraction-color: inherit; --swiper-pagination-progressbar-bg-color: rgba(0,0,0,0.25); --swiper-pagination-progressbar-size: 4px; --swiper-pagination-bullet-size: 8px; --swiper-pagination-bullet-width: 8px; --swiper-pagination-bullet-height: 8px; --swiper-pagination-bullet-border-radius: 50%; --swiper-pagination-bullet-inactive-color: #000; --swiper-pagination-bullet-inactive-opacity: 0.2; --swiper-pagination-bullet-opacity: 1; --swiper-pagination-bullet-horizontal-gap: 4px; --swiper-pagination-bullet-vertical-gap: 6px; */ } .swiper-pagination { position: absolute; text-align: center; transition: 300ms opacity; transform: translate3d(0, 0, 0); z-index: 10; &.swiper-pagination-hidden { opacity: 0; } .swiper-pagination-disabled > &, &.swiper-pagination-disabled { display: none !important; } } /* Common Styles */ .swiper-pagination-fraction, .swiper-pagination-custom, .swiper-horizontal > .swiper-pagination-bullets, .swiper-pagination-bullets.swiper-pagination-horizontal { bottom: var(--swiper-pagination-bottom, 8px); top: var(--swiper-pagination-top, auto); left: 0; width: 100%; } /* Bullets */ .swiper-pagination-bullets-dynamic { overflow: hidden; font-size: 0; .swiper-pagination-bullet { transform: scale(0.33); position: relative; } .swiper-pagination-bullet-active { transform: scale(1); } .swiper-pagination-bullet-active-main { transform: scale(1); } .swiper-pagination-bullet-active-prev { transform: scale(0.66); } .swiper-pagination-bullet-active-prev-prev { transform: scale(0.33); } .swiper-pagination-bullet-active-next { transform: scale(0.66); } .swiper-pagination-bullet-active-next-next { transform: scale(0.33); } } .swiper-pagination-bullet { width: var(--swiper-pagination-bullet-width, var(--swiper-pagination-bullet-size, 8px)); height: var(--swiper-pagination-bullet-height, var(--swiper-pagination-bullet-size, 8px)); display: inline-block; border-radius: var(--swiper-pagination-bullet-border-radius, 50%); background: var(--swiper-pagination-bullet-inactive-color, #000); opacity: var(--swiper-pagination-bullet-inactive-opacity, 0.2); button& { border: none; margin: 0; padding: 0; box-shadow: none; appearance: none; } .swiper-pagination-clickable & { cursor: pointer; } &:only-child { display: none !important; } } .swiper-pagination-bullet-active { opacity: var(--swiper-pagination-bullet-opacity, 1); background: var(--swiper-pagination-color, var(--swiper-theme-color)); } .swiper-vertical > .swiper-pagination-bullets, .swiper-pagination-vertical.swiper-pagination-bullets { right: var(--swiper-pagination-right, 8px); left: var(--swiper-pagination-left, auto); top: 50%; transform: translate3d(0px, -50%, 0); .swiper-pagination-bullet { margin: var(--swiper-pagination-bullet-vertical-gap, 6px) 0; display: block; } &.swiper-pagination-bullets-dynamic { top: 50%; transform: translateY(-50%); width: 8px; .swiper-pagination-bullet { display: inline-block; transition: 200ms transform, 200ms top; } } } .swiper-horizontal > .swiper-pagination-bullets, .swiper-pagination-horizontal.swiper-pagination-bullets { .swiper-pagination-bullet { margin: 0 var(--swiper-pagination-bullet-horizontal-gap, 4px); } &.swiper-pagination-bullets-dynamic { left: 50%; transform: translateX(-50%); white-space: nowrap; .swiper-pagination-bullet { transition: 200ms transform, 200ms left; } } } .swiper-horizontal.swiper-rtl > .swiper-pagination-bullets-dynamic .swiper-pagination-bullet { transition: 200ms transform, 200ms right; } /* Fraction */ .swiper-pagination-fraction { color: var(--swiper-pagination-fraction-color, inherit); } /* Progress */ .swiper-pagination-progressbar { background: var(--swiper-pagination-progressbar-bg-color, rgba(0, 0, 0, 0.25)); position: absolute; .swiper-pagination-progressbar-fill { background: var(--swiper-pagination-color, var(--swiper-theme-color)); position: absolute; left: 0; top: 0; width: 100%; height: 100%; transform: scale(0); transform-origin: left top; } .swiper-rtl & .swiper-pagination-progressbar-fill { transform-origin: right top; } .swiper-horizontal > &, &.swiper-pagination-horizontal, .swiper-vertical > &.swiper-pagination-progressbar-opposite, &.swiper-pagination-vertical.swiper-pagination-progressbar-opposite { width: 100%; height: var(--swiper-pagination-progressbar-size, 4px); left: 0; top: 0; } .swiper-vertical > &, &.swiper-pagination-vertical, .swiper-horizontal > &.swiper-pagination-progressbar-opposite, &.swiper-pagination-horizontal.swiper-pagination-progressbar-opposite { width: var(--swiper-pagination-progressbar-size, 4px); height: 100%; left: 0; top: 0; } } .swiper-pagination-lock { display: none; } ================================================ FILE: src/modules/pagination/pagination.mjs ================================================ import classesToSelector from '../../shared/classes-to-selector.mjs'; import createElementIfNotDefined from '../../shared/create-element-if-not-defined.mjs'; import { elementIndex, elementOuterSize, elementParents, makeElementsArray, setInnerHTML, } from '../../shared/utils.mjs'; 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; 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 getMoveDirection(prevIndex, nextIndex, length) { prevIndex = prevIndex % length; nextIndex = nextIndex % length; if (nextIndex === prevIndex + 1) { return 'next'; } else if (nextIndex === prevIndex - 1) { return 'previous'; } return; } 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 moveDirection = getMoveDirection(swiper.realIndex, index, swiper.slides.length); if (moveDirection === 'next') { swiper.slideNext(); } else if (moveDirection === 'previous') { swiper.slidePrev(); } else { 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) { setInnerHTML(subEl, 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.grid && swiper.params.grid.rows > 1 ? swiper.slides.length / Math.ceil(swiper.params.grid.rows) : 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}">`; } } } if (params.type === 'fraction') { if (params.renderFraction) { paginationHTML = params.renderFraction.call(swiper, params.currentClass, params.totalClass); } else { paginationHTML = `` + ' / ' + ``; } } if (params.type === 'progressbar') { if (params.renderProgressbar) { paginationHTML = params.renderProgressbar.call(swiper, params.progressbarFillClass); } else { paginationHTML = ``; } } swiper.pagination.bullets = []; el.forEach((subEl) => { if (params.type !== 'custom') { setInnerHTML(subEl, 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.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.find((subEl) => { if (elementParents(subEl, '.swiper')[0] !== swiper.el) return false; return true; }); } } 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 || '').split(' ')); } 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.classList.remove(...(params.clickableClass || '').split(' ')); 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; const el = makeElementsArray(swiper.pagination.el); 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, }); } ================================================ FILE: src/modules/parallax/parallax.css ================================================ ================================================ FILE: src/modules/parallax/parallax.mjs ================================================ import { elementChildren } from '../../shared/utils.mjs'; export default function Parallax({ swiper, extendParams, on }) { extendParams({ parallax: { enabled: false, }, }); const elementsSelector = '[data-swiper-parallax], [data-swiper-parallax-x], [data-swiper-parallax-y], [data-swiper-parallax-opacity], [data-swiper-parallax-scale]'; const setTransform = (el, progress) => { const { rtl } = swiper; const rtlFactor = rtl ? -1 : 1; const p = el.getAttribute('data-swiper-parallax') || '0'; let x = el.getAttribute('data-swiper-parallax-x'); let y = el.getAttribute('data-swiper-parallax-y'); const scale = el.getAttribute('data-swiper-parallax-scale'); const opacity = el.getAttribute('data-swiper-parallax-opacity'); const rotate = el.getAttribute('data-swiper-parallax-rotate'); if (x || y) { x = x || '0'; y = y || '0'; } else if (swiper.isHorizontal()) { x = p; y = '0'; } else { y = p; x = '0'; } if (x.indexOf('%') >= 0) { x = `${parseInt(x, 10) * progress * rtlFactor}%`; } else { x = `${x * progress * rtlFactor}px`; } if (y.indexOf('%') >= 0) { y = `${parseInt(y, 10) * progress}%`; } else { y = `${y * progress}px`; } if (typeof opacity !== 'undefined' && opacity !== null) { const currentOpacity = opacity - (opacity - 1) * (1 - Math.abs(progress)); el.style.opacity = currentOpacity; } let transform = `translate3d(${x}, ${y}, 0px)`; if (typeof scale !== 'undefined' && scale !== null) { const currentScale = scale - (scale - 1) * (1 - Math.abs(progress)); transform += ` scale(${currentScale})`; } if (rotate && typeof rotate !== 'undefined' && rotate !== null) { const currentRotate = rotate * progress * -1; transform += ` rotate(${currentRotate}deg)`; } el.style.transform = transform; }; const setTranslate = () => { const { el, slides, progress, snapGrid, isElement } = swiper; const elements = elementChildren(el, elementsSelector); if (swiper.isElement) { elements.push(...elementChildren(swiper.hostEl, elementsSelector)); } elements.forEach((subEl) => { setTransform(subEl, progress); }); slides.forEach((slideEl, slideIndex) => { let slideProgress = slideEl.progress; if (swiper.params.slidesPerGroup > 1 && swiper.params.slidesPerView !== 'auto') { slideProgress += Math.ceil(slideIndex / 2) - progress * (snapGrid.length - 1); } slideProgress = Math.min(Math.max(slideProgress, -1), 1); slideEl .querySelectorAll(`${elementsSelector}, [data-swiper-parallax-rotate]`) .forEach((subEl) => { setTransform(subEl, slideProgress); }); }); }; const setTransition = (duration = swiper.params.speed) => { const { el, hostEl } = swiper; const elements = [...el.querySelectorAll(elementsSelector)]; if (swiper.isElement) { elements.push(...hostEl.querySelectorAll(elementsSelector)); } elements.forEach((parallaxEl) => { let parallaxDuration = parseInt(parallaxEl.getAttribute('data-swiper-parallax-duration'), 10) || duration; if (duration === 0) parallaxDuration = 0; parallaxEl.style.transitionDuration = `${parallaxDuration}ms`; }); }; on('beforeInit', () => { if (!swiper.params.parallax.enabled) return; swiper.params.watchSlidesProgress = true; swiper.originalParams.watchSlidesProgress = true; }); on('init', () => { if (!swiper.params.parallax.enabled) return; setTranslate(); }); on('setTranslate', () => { if (!swiper.params.parallax.enabled) return; setTranslate(); }); on('setTransition', (_swiper, duration) => { if (!swiper.params.parallax.enabled) return; setTransition(duration); }); } ================================================ FILE: src/modules/scrollbar/scrollbar.css ================================================ :root { /* --swiper-scrollbar-border-radius: 10px; --swiper-scrollbar-top: auto; --swiper-scrollbar-bottom: 4px; --swiper-scrollbar-left: auto; --swiper-scrollbar-right: 4px; --swiper-scrollbar-sides-offset: 1%; --swiper-scrollbar-bg-color: rgba(0, 0, 0, 0.1); --swiper-scrollbar-drag-bg-color: rgba(0, 0, 0, 0.5); --swiper-scrollbar-size: 4px; */ } .swiper-scrollbar { border-radius: var(--swiper-scrollbar-border-radius, 10px); position: relative; -ms-touch-action: none; touch-action: none; background: var(--swiper-scrollbar-bg-color, rgba(0, 0, 0, 0.1)); .swiper-scrollbar-disabled > &, &.swiper-scrollbar-disabled { display: none !important; } .swiper-horizontal > &, &.swiper-scrollbar-horizontal { position: absolute; left: var(--swiper-scrollbar-sides-offset, 1%); bottom: var(--swiper-scrollbar-bottom, 4px); top: var(--swiper-scrollbar-top, auto); z-index: 50; height: var(--swiper-scrollbar-size, 4px); width: calc(100% - 2 * var(--swiper-scrollbar-sides-offset, 1%)); } .swiper-vertical > &, &.swiper-scrollbar-vertical { position: absolute; left: var(--swiper-scrollbar-left, auto); right: var(--swiper-scrollbar-right, 4px); top: var(--swiper-scrollbar-sides-offset, 1%); z-index: 50; width: var(--swiper-scrollbar-size, 4px); height: calc(100% - 2 * var(--swiper-scrollbar-sides-offset, 1%)); } } .swiper-scrollbar-drag { height: 100%; width: 100%; position: relative; background: var(--swiper-scrollbar-drag-bg-color, rgba(0, 0, 0, 0.5)); border-radius: var(--swiper-scrollbar-border-radius, 10px); left: 0; top: 0; } .swiper-scrollbar-cursor-drag { cursor: move; } .swiper-scrollbar-lock { display: none; } ================================================ FILE: src/modules/scrollbar/scrollbar.mjs ================================================ import { getDocument } from 'ssr-window'; import { createElement, elementOffset, makeElementsArray, nextTick } from '../../shared/utils.mjs'; import createElementIfNotDefined from '../../shared/create-element-if-not-defined.mjs'; import classesToSelector from '../../shared/classes-to-selector.mjs'; import classesToTokens from '../../shared/classes-to-tokens.mjs'; export default function Scrollbar({ swiper, extendParams, on, emit }) { const document = getDocument(); let isTouched = false; let timeout = null; let dragTimeout = null; let dragStartPos; let dragSize; let trackSize; let divider; extendParams({ scrollbar: { el: null, dragSize: 'auto', hide: false, draggable: false, snapOnRelease: true, lockClass: 'swiper-scrollbar-lock', dragClass: 'swiper-scrollbar-drag', scrollbarDisabledClass: 'swiper-scrollbar-disabled', horizontalClass: `swiper-scrollbar-horizontal`, verticalClass: `swiper-scrollbar-vertical`, }, }); swiper.scrollbar = { el: null, dragEl: null, }; function setTranslate() { if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return; const { scrollbar, rtlTranslate: rtl } = swiper; const { dragEl, el } = scrollbar; const params = swiper.params.scrollbar; const progress = swiper.params.loop ? swiper.progressLoop : swiper.progress; let newSize = dragSize; let newPos = (trackSize - dragSize) * progress; if (rtl) { newPos = -newPos; if (newPos > 0) { newSize = dragSize - newPos; newPos = 0; } else if (-newPos + dragSize > trackSize) { newSize = trackSize + newPos; } } else if (newPos < 0) { newSize = dragSize + newPos; newPos = 0; } else if (newPos + dragSize > trackSize) { newSize = trackSize - newPos; } if (swiper.isHorizontal()) { dragEl.style.transform = `translate3d(${newPos}px, 0, 0)`; dragEl.style.width = `${newSize}px`; } else { dragEl.style.transform = `translate3d(0px, ${newPos}px, 0)`; dragEl.style.height = `${newSize}px`; } if (params.hide) { clearTimeout(timeout); el.style.opacity = 1; timeout = setTimeout(() => { el.style.opacity = 0; el.style.transitionDuration = '400ms'; }, 1000); } } function setTransition(duration) { if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return; swiper.scrollbar.dragEl.style.transitionDuration = `${duration}ms`; } function updateSize() { if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return; const { scrollbar } = swiper; const { dragEl, el } = scrollbar; dragEl.style.width = ''; dragEl.style.height = ''; trackSize = swiper.isHorizontal() ? el.offsetWidth : el.offsetHeight; divider = swiper.size / (swiper.virtualSize + swiper.params.slidesOffsetBefore - (swiper.params.centeredSlides ? swiper.snapGrid[0] : 0)); if (swiper.params.scrollbar.dragSize === 'auto') { dragSize = trackSize * divider; } else { dragSize = parseInt(swiper.params.scrollbar.dragSize, 10); } if (swiper.isHorizontal()) { dragEl.style.width = `${dragSize}px`; } else { dragEl.style.height = `${dragSize}px`; } if (divider >= 1) { el.style.display = 'none'; } else { el.style.display = ''; } if (swiper.params.scrollbar.hide) { el.style.opacity = 0; } if (swiper.params.watchOverflow && swiper.enabled) { scrollbar.el.classList[swiper.isLocked ? 'add' : 'remove'](swiper.params.scrollbar.lockClass); } } function getPointerPosition(e) { return swiper.isHorizontal() ? e.clientX : e.clientY; } function setDragPosition(e) { const { scrollbar, rtlTranslate: rtl } = swiper; const { el } = scrollbar; let positionRatio; positionRatio = (getPointerPosition(e) - elementOffset(el)[swiper.isHorizontal() ? 'left' : 'top'] - (dragStartPos !== null ? dragStartPos : dragSize / 2)) / (trackSize - dragSize); positionRatio = Math.max(Math.min(positionRatio, 1), 0); if (rtl) { positionRatio = 1 - positionRatio; } const position = swiper.minTranslate() + (swiper.maxTranslate() - swiper.minTranslate()) * positionRatio; swiper.updateProgress(position); swiper.setTranslate(position); swiper.updateActiveIndex(); swiper.updateSlidesClasses(); } function onDragStart(e) { const params = swiper.params.scrollbar; const { scrollbar, wrapperEl } = swiper; const { el, dragEl } = scrollbar; isTouched = true; dragStartPos = e.target === dragEl ? getPointerPosition(e) - e.target.getBoundingClientRect()[swiper.isHorizontal() ? 'left' : 'top'] : null; e.preventDefault(); e.stopPropagation(); wrapperEl.style.transitionDuration = '100ms'; dragEl.style.transitionDuration = '100ms'; setDragPosition(e); clearTimeout(dragTimeout); el.style.transitionDuration = '0ms'; if (params.hide) { el.style.opacity = 1; } if (swiper.params.cssMode) { swiper.wrapperEl.style['scroll-snap-type'] = 'none'; } emit('scrollbarDragStart', e); } function onDragMove(e) { const { scrollbar, wrapperEl } = swiper; const { el, dragEl } = scrollbar; if (!isTouched) return; if (e.preventDefault && e.cancelable) e.preventDefault(); else e.returnValue = false; setDragPosition(e); wrapperEl.style.transitionDuration = '0ms'; el.style.transitionDuration = '0ms'; dragEl.style.transitionDuration = '0ms'; emit('scrollbarDragMove', e); } function onDragEnd(e) { const params = swiper.params.scrollbar; const { scrollbar, wrapperEl } = swiper; const { el } = scrollbar; if (!isTouched) return; isTouched = false; if (swiper.params.cssMode) { swiper.wrapperEl.style['scroll-snap-type'] = ''; wrapperEl.style.transitionDuration = ''; } if (params.hide) { clearTimeout(dragTimeout); dragTimeout = nextTick(() => { el.style.opacity = 0; el.style.transitionDuration = '400ms'; }, 1000); } emit('scrollbarDragEnd', e); if (params.snapOnRelease) { swiper.slideToClosest(); } } function events(method) { const { scrollbar, params } = swiper; const el = scrollbar.el; if (!el) return; const target = el; const activeListener = params.passiveListeners ? { passive: false, capture: false } : false; const passiveListener = params.passiveListeners ? { passive: true, capture: false } : false; if (!target) return; const eventMethod = method === 'on' ? 'addEventListener' : 'removeEventListener'; target[eventMethod]('pointerdown', onDragStart, activeListener); document[eventMethod]('pointermove', onDragMove, activeListener); document[eventMethod]('pointerup', onDragEnd, passiveListener); } function enableDraggable() { if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return; events('on'); } function disableDraggable() { if (!swiper.params.scrollbar.el || !swiper.scrollbar.el) return; events('off'); } function init() { const { scrollbar, el: swiperEl } = swiper; swiper.params.scrollbar = createElementIfNotDefined( swiper, swiper.originalParams.scrollbar, swiper.params.scrollbar, { el: 'swiper-scrollbar' }, ); const params = swiper.params.scrollbar; if (!params.el) return; let el; if (typeof params.el === 'string' && swiper.isElement) { el = swiper.el.querySelector(params.el); } if (!el && typeof params.el === 'string') { el = document.querySelectorAll(params.el); if (!el.length) return; } else if (!el) { el = params.el; } if ( swiper.params.uniqueNavElements && typeof params.el === 'string' && el.length > 1 && swiperEl.querySelectorAll(params.el).length === 1 ) { el = swiperEl.querySelector(params.el); } if (el.length > 0) el = el[0]; el.classList.add(swiper.isHorizontal() ? params.horizontalClass : params.verticalClass); let dragEl; if (el) { dragEl = el.querySelector(classesToSelector(swiper.params.scrollbar.dragClass)); if (!dragEl) { dragEl = createElement('div', swiper.params.scrollbar.dragClass); el.append(dragEl); } } Object.assign(scrollbar, { el, dragEl, }); if (params.draggable) { enableDraggable(); } if (el) { el.classList[swiper.enabled ? 'remove' : 'add']( ...classesToTokens(swiper.params.scrollbar.lockClass), ); } } function destroy() { const params = swiper.params.scrollbar; const el = swiper.scrollbar.el; if (el) { el.classList.remove( ...classesToTokens(swiper.isHorizontal() ? params.horizontalClass : params.verticalClass), ); } disableDraggable(); } on('changeDirection', () => { if (!swiper.scrollbar || !swiper.scrollbar.el) return; const params = swiper.params.scrollbar; let { el } = swiper.scrollbar; 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.scrollbar.enabled === false) { // eslint-disable-next-line disable(); } else { init(); updateSize(); setTranslate(); } }); on('update resize observerUpdate lock unlock changeDirection', () => { updateSize(); }); on('setTranslate', () => { setTranslate(); }); on('setTransition', (_s, duration) => { setTransition(duration); }); on('enable disable', () => { const { el } = swiper.scrollbar; if (el) { el.classList[swiper.enabled ? 'remove' : 'add']( ...classesToTokens(swiper.params.scrollbar.lockClass), ); } }); on('destroy', () => { destroy(); }); const enable = () => { swiper.el.classList.remove(...classesToTokens(swiper.params.scrollbar.scrollbarDisabledClass)); if (swiper.scrollbar.el) { swiper.scrollbar.el.classList.remove( ...classesToTokens(swiper.params.scrollbar.scrollbarDisabledClass), ); } init(); updateSize(); setTranslate(); }; const disable = () => { swiper.el.classList.add(...classesToTokens(swiper.params.scrollbar.scrollbarDisabledClass)); if (swiper.scrollbar.el) { swiper.scrollbar.el.classList.add( ...classesToTokens(swiper.params.scrollbar.scrollbarDisabledClass), ); } destroy(); }; Object.assign(swiper.scrollbar, { enable, disable, updateSize, setTranslate, init, destroy, }); } ================================================ FILE: src/modules/thumbs/thumbs.css ================================================ .swiper-thumbs { .swiper-slide-thumb-active { /* Styles for active thumb slide */ } } ================================================ FILE: src/modules/thumbs/thumbs.mjs ================================================ import { getDocument } from 'ssr-window'; import { elementChildren, isObject } from '../../shared/utils.mjs'; export default function Thumb({ swiper, extendParams, on }) { extendParams({ thumbs: { swiper: null, multipleActiveThumbs: true, autoScrollOffset: 0, slideThumbActiveClass: 'swiper-slide-thumb-active', thumbsContainerClass: 'swiper-thumbs', }, }); let initialized = false; let swiperCreated = false; swiper.thumbs = { swiper: null, }; function isVirtualEnabled() { const thumbsSwiper = swiper.thumbs.swiper; if (!thumbsSwiper || thumbsSwiper.destroyed) return false; return thumbsSwiper.params.virtual && thumbsSwiper.params.virtual.enabled; } function onThumbClick() { const thumbsSwiper = swiper.thumbs.swiper; if (!thumbsSwiper || thumbsSwiper.destroyed) return; const clickedIndex = thumbsSwiper.clickedIndex; const clickedSlide = thumbsSwiper.clickedSlide; if (clickedSlide && clickedSlide.classList.contains(swiper.params.thumbs.slideThumbActiveClass)) return; if (typeof clickedIndex === 'undefined' || clickedIndex === null) return; let slideToIndex; if (thumbsSwiper.params.loop) { slideToIndex = parseInt( thumbsSwiper.clickedSlide.getAttribute('data-swiper-slide-index'), 10, ); } else { slideToIndex = clickedIndex; } if (swiper.params.loop) { swiper.slideToLoop(slideToIndex); } else { swiper.slideTo(slideToIndex); } } function init() { const { thumbs: thumbsParams } = swiper.params; if (initialized) return false; initialized = true; const SwiperClass = swiper.constructor; if (thumbsParams.swiper instanceof SwiperClass) { if (thumbsParams.swiper.destroyed) { initialized = false; return false; } swiper.thumbs.swiper = thumbsParams.swiper; Object.assign(swiper.thumbs.swiper.originalParams, { watchSlidesProgress: true, slideToClickedSlide: false, }); Object.assign(swiper.thumbs.swiper.params, { watchSlidesProgress: true, slideToClickedSlide: false, }); swiper.thumbs.swiper.update(); } else if (isObject(thumbsParams.swiper)) { const thumbsSwiperParams = Object.assign({}, thumbsParams.swiper); Object.assign(thumbsSwiperParams, { watchSlidesProgress: true, slideToClickedSlide: false, }); swiper.thumbs.swiper = new SwiperClass(thumbsSwiperParams); swiperCreated = true; } swiper.thumbs.swiper.el.classList.add(swiper.params.thumbs.thumbsContainerClass); swiper.thumbs.swiper.on('tap', onThumbClick); if (isVirtualEnabled()) { swiper.thumbs.swiper.on('virtualUpdate', () => { update(false, { autoScroll: false }); }); } return true; } function update(initial, p) { const thumbsSwiper = swiper.thumbs.swiper; if (!thumbsSwiper || thumbsSwiper.destroyed) return; // Activate thumbs let thumbsToActivate = 1; const thumbActiveClass = swiper.params.thumbs.slideThumbActiveClass; if (swiper.params.slidesPerView > 1 && !swiper.params.centeredSlides) { thumbsToActivate = swiper.params.slidesPerView; } if (!swiper.params.thumbs.multipleActiveThumbs) { thumbsToActivate = 1; } thumbsToActivate = Math.floor(thumbsToActivate); thumbsSwiper.slides.forEach((slideEl) => slideEl.classList.remove(thumbActiveClass)); if (thumbsSwiper.params.loop || isVirtualEnabled()) { for (let i = 0; i < thumbsToActivate; i += 1) { elementChildren( thumbsSwiper.slidesEl, `[data-swiper-slide-index="${swiper.realIndex + i}"]`, ).forEach((slideEl) => { slideEl.classList.add(thumbActiveClass); }); } } else { for (let i = 0; i < thumbsToActivate; i += 1) { if (thumbsSwiper.slides[swiper.realIndex + i]) { thumbsSwiper.slides[swiper.realIndex + i].classList.add(thumbActiveClass); } } } if (p?.autoScroll ?? true) { autoScroll(initial ? 0 : undefined); } } function autoScroll(slideSpeed) { const thumbsSwiper = swiper.thumbs.swiper; if (!thumbsSwiper || thumbsSwiper.destroyed) return; const slidesPerView = thumbsSwiper.params.slidesPerView === 'auto' ? thumbsSwiper.slidesPerViewDynamic() : thumbsSwiper.params.slidesPerView; const autoScrollOffset = swiper.params.thumbs.autoScrollOffset; const useOffset = autoScrollOffset && !thumbsSwiper.params.loop; if (swiper.realIndex !== thumbsSwiper.realIndex || useOffset) { const currentThumbsIndex = thumbsSwiper.activeIndex; let newThumbsIndex; let direction; if (thumbsSwiper.params.loop) { const newThumbsSlide = thumbsSwiper.slides.find( (slideEl) => slideEl.getAttribute('data-swiper-slide-index') === `${swiper.realIndex}`, ); newThumbsIndex = thumbsSwiper.slides.indexOf(newThumbsSlide); direction = swiper.activeIndex > swiper.previousIndex ? 'next' : 'prev'; } else { newThumbsIndex = swiper.realIndex; direction = newThumbsIndex > swiper.previousIndex ? 'next' : 'prev'; } if (useOffset) { newThumbsIndex += direction === 'next' ? autoScrollOffset : -1 * autoScrollOffset; } if ( thumbsSwiper.visibleSlidesIndexes && thumbsSwiper.visibleSlidesIndexes.indexOf(newThumbsIndex) < 0 ) { if (thumbsSwiper.params.centeredSlides) { if (newThumbsIndex > currentThumbsIndex) { newThumbsIndex = newThumbsIndex - Math.floor(slidesPerView / 2) + 1; } else { newThumbsIndex = newThumbsIndex + Math.floor(slidesPerView / 2) - 1; } } else if ( newThumbsIndex > currentThumbsIndex && thumbsSwiper.params.slidesPerGroup === 1 ) { // newThumbsIndex = newThumbsIndex - slidesPerView + 1; } thumbsSwiper.slideTo(newThumbsIndex, slideSpeed); } } } on('beforeInit', () => { const { thumbs } = swiper.params; if (!thumbs || !thumbs.swiper) return; if (typeof thumbs.swiper === 'string' || thumbs.swiper instanceof HTMLElement) { const document = getDocument(); const getThumbsElementAndInit = () => { const thumbsElement = typeof thumbs.swiper === 'string' ? document.querySelector(thumbs.swiper) : thumbs.swiper; if (thumbsElement && thumbsElement.swiper) { thumbs.swiper = thumbsElement.swiper; init(); update(true); } else if (thumbsElement) { const eventName = `${swiper.params.eventsPrefix}init`; const onThumbsSwiper = (e) => { thumbs.swiper = e.detail[0]; thumbsElement.removeEventListener(eventName, onThumbsSwiper); init(); update(true); thumbs.swiper.update(); swiper.update(); }; thumbsElement.addEventListener(eventName, onThumbsSwiper); } return thumbsElement; }; const watchForThumbsToAppear = () => { if (swiper.destroyed) return; const thumbsElement = getThumbsElementAndInit(); if (!thumbsElement) { requestAnimationFrame(watchForThumbsToAppear); } }; requestAnimationFrame(watchForThumbsToAppear); } else { init(); update(true); } }); on('slideChange update resize observerUpdate', () => { update(); }); on('setTransition', (_s, duration) => { const thumbsSwiper = swiper.thumbs.swiper; if (!thumbsSwiper || thumbsSwiper.destroyed) return; thumbsSwiper.setTransition(duration); }); on('beforeDestroy', () => { const thumbsSwiper = swiper.thumbs.swiper; if (!thumbsSwiper || thumbsSwiper.destroyed) return; if (swiperCreated) { thumbsSwiper.destroy(); } }); Object.assign(swiper.thumbs, { init, update, }); } ================================================ FILE: src/modules/virtual/virtual.css ================================================ .swiper-virtual .swiper-slide { -webkit-backface-visibility: hidden; transform: translateZ(0); } .swiper-virtual.swiper-css-mode { .swiper-wrapper::after { content: ''; position: absolute; left: 0; top: 0; pointer-events: none; } } .swiper-virtual.swiper-css-mode.swiper-horizontal { .swiper-wrapper::after { height: 1px; width: var(--swiper-virtual-size); } } .swiper-virtual.swiper-css-mode.swiper-vertical { .swiper-wrapper::after { width: 1px; height: var(--swiper-virtual-size); } } ================================================ FILE: src/modules/virtual/virtual.mjs ================================================ import { getDocument } from 'ssr-window'; import { createElement, elementChildren, setCSSProperty, setInnerHTML, } from '../../shared/utils.mjs'; export default function Virtual({ swiper, extendParams, on, emit }) { extendParams({ virtual: { enabled: false, slides: [], cache: true, slidesPerViewAutoSlideSize: 320, renderSlide: null, renderExternal: null, renderExternalUpdate: true, addSlidesBefore: 0, addSlidesAfter: 0, }, }); let cssModeTimeout; const document = getDocument(); swiper.virtual = { cache: {}, from: undefined, to: undefined, slides: [], offset: 0, slidesGrid: [], }; const tempDOM = document.createElement('div'); function renderSlide(slide, index) { const params = swiper.params.virtual; if (params.cache && swiper.virtual.cache[index]) { return swiper.virtual.cache[index]; } // eslint-disable-next-line let slideEl; if (params.renderSlide) { slideEl = params.renderSlide.call(swiper, slide, index); if (typeof slideEl === 'string') { setInnerHTML(tempDOM, slideEl); slideEl = tempDOM.children[0]; } } else if (swiper.isElement) { slideEl = createElement('swiper-slide'); } else { slideEl = createElement('div', swiper.params.slideClass); } slideEl.setAttribute('data-swiper-slide-index', index); if (!params.renderSlide) { setInnerHTML(slideEl, slide); } if (params.cache) { swiper.virtual.cache[index] = slideEl; } return slideEl; } function update(force, beforeInit, forceActiveIndex) { const { slidesPerGroup, centeredSlides, slidesPerView, loop: isLoop, initialSlide, } = swiper.params; if (beforeInit && !isLoop && initialSlide > 0) { return; } const { addSlidesBefore, addSlidesAfter, slidesPerViewAutoSlideSize } = swiper.params.virtual; const { from: previousFrom, to: previousTo, slides, slidesGrid: previousSlidesGrid, offset: previousOffset, } = swiper.virtual; if (!swiper.params.cssMode) { swiper.updateActiveIndex(); } const activeIndex = typeof forceActiveIndex === 'undefined' ? swiper.activeIndex || 0 : forceActiveIndex; let offsetProp; if (swiper.rtlTranslate) offsetProp = 'right'; else offsetProp = swiper.isHorizontal() ? 'left' : 'top'; let slidesPerViewNumeric; if (slidesPerView === 'auto') { if (slidesPerViewAutoSlideSize) { let swiperSize = swiper.size; if (!swiperSize) { swiperSize = swiper.isHorizontal() ? swiper.el.getBoundingClientRect().width : swiper.el.getBoundingClientRect().height; } slidesPerViewNumeric = Math.max(1, Math.ceil(swiperSize / slidesPerViewAutoSlideSize)); } else { slidesPerViewNumeric = 1; } } else { slidesPerViewNumeric = slidesPerView; } let slidesAfter; let slidesBefore; if (centeredSlides) { slidesAfter = Math.floor(slidesPerViewNumeric / 2) + slidesPerGroup + addSlidesAfter; slidesBefore = Math.floor(slidesPerViewNumeric / 2) + slidesPerGroup + addSlidesBefore; } else { slidesAfter = slidesPerViewNumeric + (slidesPerGroup - 1) + addSlidesAfter; slidesBefore = (isLoop ? slidesPerViewNumeric : slidesPerGroup) + addSlidesBefore; } let from = activeIndex - slidesBefore; let to = activeIndex + slidesAfter; if (!isLoop) { from = Math.max(from, 0); to = Math.min(to, slides.length - 1); } let offset = (swiper.slidesGrid[from] || 0) - (swiper.slidesGrid[0] || 0); if (isLoop && activeIndex >= slidesBefore) { from -= slidesBefore; if (!centeredSlides) offset += swiper.slidesGrid[0]; } else if (isLoop && activeIndex < slidesBefore) { from = -slidesBefore; if (centeredSlides) offset += swiper.slidesGrid[0]; } Object.assign(swiper.virtual, { from, to, offset, slidesGrid: swiper.slidesGrid, slidesBefore, slidesAfter, }); function onRendered() { swiper.updateSlides(); swiper.updateProgress(); swiper.updateSlidesClasses(); emit('virtualUpdate'); } if (previousFrom === from && previousTo === to && !force) { if (swiper.slidesGrid !== previousSlidesGrid && offset !== previousOffset) { swiper.slides.forEach((slideEl) => { slideEl.style[offsetProp] = `${offset - Math.abs(swiper.cssOverflowAdjustment())}px`; }); } swiper.updateProgress(); emit('virtualUpdate'); return; } if (swiper.params.virtual.renderExternal) { swiper.params.virtual.renderExternal.call(swiper, { offset, from, to, slides: (function getSlides() { const slidesToRender = []; for (let i = from; i <= to; i += 1) { slidesToRender.push(slides[i]); } return slidesToRender; })(), }); if (swiper.params.virtual.renderExternalUpdate) { onRendered(); } else { emit('virtualUpdate'); } return; } const prependIndexes = []; const appendIndexes = []; const getSlideIndex = (index) => { let slideIndex = index; if (index < 0) { slideIndex = slides.length + index; } else if (slideIndex >= slides.length) { // eslint-disable-next-line slideIndex = slideIndex - slides.length; } return slideIndex; }; if (force) { swiper.slides .filter((el) => el.matches(`.${swiper.params.slideClass}, swiper-slide`)) .forEach((slideEl) => { slideEl.remove(); }); } else { for (let i = previousFrom; i <= previousTo; i += 1) { if (i < from || i > to) { const slideIndex = getSlideIndex(i); swiper.slides .filter((el) => el.matches( `.${swiper.params.slideClass}[data-swiper-slide-index="${slideIndex}"], swiper-slide[data-swiper-slide-index="${slideIndex}"]`, ), ) .forEach((slideEl) => { slideEl.remove(); }); } } } const loopFrom = isLoop ? -slides.length : 0; const loopTo = isLoop ? slides.length * 2 : slides.length; for (let i = loopFrom; i < loopTo; i += 1) { if (i >= from && i <= to) { const slideIndex = getSlideIndex(i); if (typeof previousTo === 'undefined' || force) { appendIndexes.push(slideIndex); } else { if (i > previousTo) appendIndexes.push(slideIndex); if (i < previousFrom) prependIndexes.push(slideIndex); } } } appendIndexes.forEach((index) => { swiper.slidesEl.append(renderSlide(slides[index], index)); }); if (isLoop) { for (let i = prependIndexes.length - 1; i >= 0; i -= 1) { const index = prependIndexes[i]; swiper.slidesEl.prepend(renderSlide(slides[index], index)); } } else { prependIndexes.sort((a, b) => b - a); prependIndexes.forEach((index) => { swiper.slidesEl.prepend(renderSlide(slides[index], index)); }); } elementChildren(swiper.slidesEl, '.swiper-slide, swiper-slide').forEach((slideEl) => { slideEl.style[offsetProp] = `${offset - Math.abs(swiper.cssOverflowAdjustment())}px`; }); onRendered(); } function appendSlide(slides) { if (typeof slides === 'object' && 'length' in slides) { for (let i = 0; i < slides.length; i += 1) { if (slides[i]) swiper.virtual.slides.push(slides[i]); } } else { swiper.virtual.slides.push(slides); } update(true); } function prependSlide(slides) { const activeIndex = swiper.activeIndex; let newActiveIndex = activeIndex + 1; let numberOfNewSlides = 1; if (Array.isArray(slides)) { for (let i = 0; i < slides.length; i += 1) { if (slides[i]) swiper.virtual.slides.unshift(slides[i]); } newActiveIndex = activeIndex + slides.length; numberOfNewSlides = slides.length; } else { swiper.virtual.slides.unshift(slides); } if (swiper.params.virtual.cache) { const cache = swiper.virtual.cache; const newCache = {}; Object.keys(cache).forEach((cachedIndex) => { const cachedEl = cache[cachedIndex]; const cachedElIndex = cachedEl.getAttribute('data-swiper-slide-index'); if (cachedElIndex) { cachedEl.setAttribute( 'data-swiper-slide-index', parseInt(cachedElIndex, 10) + numberOfNewSlides, ); } newCache[parseInt(cachedIndex, 10) + numberOfNewSlides] = cachedEl; }); swiper.virtual.cache = newCache; } update(true); swiper.slideTo(newActiveIndex, 0); } function removeSlide(slidesIndexes) { if (typeof slidesIndexes === 'undefined' || slidesIndexes === null) return; let activeIndex = swiper.activeIndex; if (Array.isArray(slidesIndexes)) { for (let i = slidesIndexes.length - 1; i >= 0; i -= 1) { if (swiper.params.virtual.cache) { delete swiper.virtual.cache[slidesIndexes[i]]; // shift cache indexes Object.keys(swiper.virtual.cache).forEach((key) => { if (key > slidesIndexes) { swiper.virtual.cache[key - 1] = swiper.virtual.cache[key]; swiper.virtual.cache[key - 1].setAttribute('data-swiper-slide-index', key - 1); delete swiper.virtual.cache[key]; } }); } swiper.virtual.slides.splice(slidesIndexes[i], 1); if (slidesIndexes[i] < activeIndex) activeIndex -= 1; activeIndex = Math.max(activeIndex, 0); } } else { if (swiper.params.virtual.cache) { delete swiper.virtual.cache[slidesIndexes]; // shift cache indexes Object.keys(swiper.virtual.cache).forEach((key) => { if (key > slidesIndexes) { swiper.virtual.cache[key - 1] = swiper.virtual.cache[key]; swiper.virtual.cache[key - 1].setAttribute('data-swiper-slide-index', key - 1); delete swiper.virtual.cache[key]; } }); } swiper.virtual.slides.splice(slidesIndexes, 1); if (slidesIndexes < activeIndex) activeIndex -= 1; activeIndex = Math.max(activeIndex, 0); } update(true); swiper.slideTo(activeIndex, 0); } function removeAllSlides() { swiper.virtual.slides = []; if (swiper.params.virtual.cache) { swiper.virtual.cache = {}; } update(true); swiper.slideTo(0, 0); } on('beforeInit', () => { if (!swiper.params.virtual.enabled) return; let domSlidesAssigned; if (typeof swiper.passedParams.virtual.slides === 'undefined') { const slides = [...swiper.slidesEl.children].filter((el) => el.matches(`.${swiper.params.slideClass}, swiper-slide`), ); if (slides && slides.length) { swiper.virtual.slides = [...slides]; domSlidesAssigned = true; slides.forEach((slideEl, slideIndex) => { slideEl.setAttribute('data-swiper-slide-index', slideIndex); swiper.virtual.cache[slideIndex] = slideEl; slideEl.remove(); }); } } if (!domSlidesAssigned) { swiper.virtual.slides = swiper.params.virtual.slides; } swiper.classNames.push(`${swiper.params.containerModifierClass}virtual`); swiper.params.watchSlidesProgress = true; swiper.originalParams.watchSlidesProgress = true; update(false, true); }); on('setTranslate', () => { if (!swiper.params.virtual.enabled) return; if (swiper.params.cssMode && !swiper._immediateVirtual) { clearTimeout(cssModeTimeout); cssModeTimeout = setTimeout(() => { update(); }, 100); } else { update(); } }); on('init update resize', () => { if (!swiper.params.virtual.enabled) return; if (swiper.params.cssMode) { setCSSProperty(swiper.wrapperEl, '--swiper-virtual-size', `${swiper.virtualSize}px`); } }); Object.assign(swiper.virtual, { appendSlide, prependSlide, removeSlide, removeAllSlides, update, }); } ================================================ FILE: src/modules/zoom/zoom.css ================================================ /* Zoom container styles start */ .swiper-zoom-container { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; text-align: center; > img, > svg, > canvas { max-width: 100%; max-height: 100%; object-fit: contain; } } /* Zoom container styles end */ .swiper-slide-zoomed { cursor: move; touch-action: none; } ================================================ FILE: src/modules/zoom/zoom.mjs ================================================ import { getWindow } from 'ssr-window'; import { elementChildren, elementOffset, elementParents, getTranslate, } from '../../shared/utils.mjs'; export default function Zoom({ swiper, extendParams, on, emit }) { const window = getWindow(); extendParams({ zoom: { enabled: false, limitToOriginalSize: false, maxRatio: 3, minRatio: 1, panOnMouseMove: false, toggle: true, containerClass: 'swiper-zoom-container', zoomedSlideClass: 'swiper-slide-zoomed', }, }); swiper.zoom = { enabled: false, }; let currentScale = 1; let isScaling = false; let isPanningWithMouse = false; let mousePanStart = { x: 0, y: 0 }; const mousePanSensitivity = -3; // Negative to invert pan direction let fakeGestureTouched; let fakeGestureMoved; const evCache = []; const gesture = { originX: 0, originY: 0, slideEl: undefined, slideWidth: undefined, slideHeight: undefined, imageEl: undefined, imageWrapEl: undefined, maxRatio: 3, }; const image = { isTouched: undefined, isMoved: undefined, currentX: undefined, currentY: undefined, minX: undefined, minY: undefined, maxX: undefined, maxY: undefined, width: undefined, height: undefined, startX: undefined, startY: undefined, touchesStart: {}, touchesCurrent: {}, }; const velocity = { x: undefined, y: undefined, prevPositionX: undefined, prevPositionY: undefined, prevTime: undefined, }; let scale = 1; Object.defineProperty(swiper.zoom, 'scale', { get() { return scale; }, set(value) { if (scale !== value) { const imageEl = gesture.imageEl; const slideEl = gesture.slideEl; emit('zoomChange', value, imageEl, slideEl); } scale = value; }, }); function getDistanceBetweenTouches() { if (evCache.length < 2) return 1; const x1 = evCache[0].pageX; const y1 = evCache[0].pageY; const x2 = evCache[1].pageX; const y2 = evCache[1].pageY; const distance = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2); return distance; } function getMaxRatio() { const params = swiper.params.zoom; const maxRatio = gesture.imageWrapEl.getAttribute('data-swiper-zoom') || params.maxRatio; if (params.limitToOriginalSize && gesture.imageEl && gesture.imageEl.naturalWidth) { const imageMaxRatio = gesture.imageEl.naturalWidth / gesture.imageEl.offsetWidth; return Math.min(imageMaxRatio, maxRatio); } return maxRatio; } function getScaleOrigin() { if (evCache.length < 2) return { x: null, y: null }; const box = gesture.imageEl.getBoundingClientRect(); return [ (evCache[0].pageX + (evCache[1].pageX - evCache[0].pageX) / 2 - box.x - window.scrollX) / currentScale, (evCache[0].pageY + (evCache[1].pageY - evCache[0].pageY) / 2 - box.y - window.scrollY) / currentScale, ]; } function getSlideSelector() { return swiper.isElement ? `swiper-slide` : `.${swiper.params.slideClass}`; } function eventWithinSlide(e) { const slideSelector = getSlideSelector(); if (e.target.matches(slideSelector)) return true; if (swiper.slides.filter((slideEl) => slideEl.contains(e.target)).length > 0) return true; return false; } function eventWithinZoomContainer(e) { const selector = `.${swiper.params.zoom.containerClass}`; if (e.target.matches(selector)) return true; if ( [...swiper.hostEl.querySelectorAll(selector)].filter((containerEl) => containerEl.contains(e.target), ).length > 0 ) return true; return false; } // Events function onGestureStart(e) { if (e.pointerType === 'mouse') { evCache.splice(0, evCache.length); } if (!eventWithinSlide(e)) return; const params = swiper.params.zoom; fakeGestureTouched = false; fakeGestureMoved = false; evCache.push(e); if (evCache.length < 2) { return; } fakeGestureTouched = true; gesture.scaleStart = getDistanceBetweenTouches(); if (!gesture.slideEl) { gesture.slideEl = e.target.closest(`.${swiper.params.slideClass}, swiper-slide`); if (!gesture.slideEl) gesture.slideEl = swiper.slides[swiper.activeIndex]; let imageEl = gesture.slideEl.querySelector(`.${params.containerClass}`); if (imageEl) { imageEl = imageEl.querySelectorAll('picture, img, svg, canvas, .swiper-zoom-target')[0]; } gesture.imageEl = imageEl; if (imageEl) { gesture.imageWrapEl = elementParents(gesture.imageEl, `.${params.containerClass}`)[0]; } else { gesture.imageWrapEl = undefined; } if (!gesture.imageWrapEl) { gesture.imageEl = undefined; return; } gesture.maxRatio = getMaxRatio(); } if (gesture.imageEl) { const [originX, originY] = getScaleOrigin(); gesture.originX = originX; gesture.originY = originY; gesture.imageEl.style.transitionDuration = '0ms'; } isScaling = true; } function onGestureChange(e) { if (!eventWithinSlide(e)) return; const params = swiper.params.zoom; const zoom = swiper.zoom; const pointerIndex = evCache.findIndex((cachedEv) => cachedEv.pointerId === e.pointerId); if (pointerIndex >= 0) evCache[pointerIndex] = e; if (evCache.length < 2) { return; } fakeGestureMoved = true; gesture.scaleMove = getDistanceBetweenTouches(); if (!gesture.imageEl) { return; } zoom.scale = (gesture.scaleMove / gesture.scaleStart) * currentScale; if (zoom.scale > gesture.maxRatio) { zoom.scale = gesture.maxRatio - 1 + (zoom.scale - gesture.maxRatio + 1) ** 0.5; } if (zoom.scale < params.minRatio) { zoom.scale = params.minRatio + 1 - (params.minRatio - zoom.scale + 1) ** 0.5; } gesture.imageEl.style.transform = `translate3d(0,0,0) scale(${zoom.scale})`; } function onGestureEnd(e) { if (!eventWithinSlide(e)) return; if (e.pointerType === 'mouse' && e.type === 'pointerout') return; const params = swiper.params.zoom; const zoom = swiper.zoom; const pointerIndex = evCache.findIndex((cachedEv) => cachedEv.pointerId === e.pointerId); if (pointerIndex >= 0) evCache.splice(pointerIndex, 1); if (!fakeGestureTouched || !fakeGestureMoved) { return; } fakeGestureTouched = false; fakeGestureMoved = false; if (!gesture.imageEl) return; zoom.scale = Math.max(Math.min(zoom.scale, gesture.maxRatio), params.minRatio); gesture.imageEl.style.transitionDuration = `${swiper.params.speed}ms`; gesture.imageEl.style.transform = `translate3d(0,0,0) scale(${zoom.scale})`; currentScale = zoom.scale; isScaling = false; if (zoom.scale > 1 && gesture.slideEl) { gesture.slideEl.classList.add(`${params.zoomedSlideClass}`); } else if (zoom.scale <= 1 && gesture.slideEl) { gesture.slideEl.classList.remove(`${params.zoomedSlideClass}`); } if (zoom.scale === 1) { gesture.originX = 0; gesture.originY = 0; gesture.slideEl = undefined; } } let allowTouchMoveTimeout; function allowTouchMove() { swiper.touchEventsData.preventTouchMoveFromPointerMove = false; } function preventTouchMove() { clearTimeout(allowTouchMoveTimeout); swiper.touchEventsData.preventTouchMoveFromPointerMove = true; allowTouchMoveTimeout = setTimeout(() => { if (swiper.destroyed) return; allowTouchMove(); }); } function onTouchStart(e) { const device = swiper.device; if (!gesture.imageEl) return; if (image.isTouched) return; if (device.android && e.cancelable) e.preventDefault(); image.isTouched = true; const event = evCache.length > 0 ? evCache[0] : e; image.touchesStart.x = event.pageX; image.touchesStart.y = event.pageY; } function onTouchMove(e) { const isMouseEvent = e.pointerType === 'mouse'; const isMousePan = isMouseEvent && swiper.params.zoom.panOnMouseMove; if (!eventWithinSlide(e) || !eventWithinZoomContainer(e)) { return; } const zoom = swiper.zoom; if (!gesture.imageEl) { return; } if (!image.isTouched || !gesture.slideEl) { if (isMousePan) onMouseMove(e); return; } if (isMousePan) { onMouseMove(e); return; } if (!image.isMoved) { image.width = gesture.imageEl.offsetWidth || gesture.imageEl.clientWidth; image.height = gesture.imageEl.offsetHeight || gesture.imageEl.clientHeight; image.startX = getTranslate(gesture.imageWrapEl, 'x') || 0; image.startY = getTranslate(gesture.imageWrapEl, 'y') || 0; gesture.slideWidth = gesture.slideEl.offsetWidth; gesture.slideHeight = gesture.slideEl.offsetHeight; gesture.imageWrapEl.style.transitionDuration = '0ms'; } // Define if we need image drag const scaledWidth = image.width * zoom.scale; const scaledHeight = image.height * zoom.scale; image.minX = Math.min(gesture.slideWidth / 2 - scaledWidth / 2, 0); image.maxX = -image.minX; image.minY = Math.min(gesture.slideHeight / 2 - scaledHeight / 2, 0); image.maxY = -image.minY; image.touchesCurrent.x = evCache.length > 0 ? evCache[0].pageX : e.pageX; image.touchesCurrent.y = evCache.length > 0 ? evCache[0].pageY : e.pageY; const touchesDiff = Math.max( Math.abs(image.touchesCurrent.x - image.touchesStart.x), Math.abs(image.touchesCurrent.y - image.touchesStart.y), ); if (touchesDiff > 5) { swiper.allowClick = false; } if (!image.isMoved && !isScaling) { if ( swiper.isHorizontal() && ((Math.floor(image.minX) === Math.floor(image.startX) && image.touchesCurrent.x < image.touchesStart.x) || (Math.floor(image.maxX) === Math.floor(image.startX) && image.touchesCurrent.x > image.touchesStart.x)) ) { image.isTouched = false; allowTouchMove(); return; } if ( !swiper.isHorizontal() && ((Math.floor(image.minY) === Math.floor(image.startY) && image.touchesCurrent.y < image.touchesStart.y) || (Math.floor(image.maxY) === Math.floor(image.startY) && image.touchesCurrent.y > image.touchesStart.y)) ) { image.isTouched = false; allowTouchMove(); return; } } if (e.cancelable) { e.preventDefault(); } e.stopPropagation(); preventTouchMove(); image.isMoved = true; const scaleRatio = (zoom.scale - currentScale) / (gesture.maxRatio - swiper.params.zoom.minRatio); const { originX, originY } = gesture; image.currentX = image.touchesCurrent.x - image.touchesStart.x + image.startX + scaleRatio * (image.width - originX * 2); image.currentY = image.touchesCurrent.y - image.touchesStart.y + image.startY + scaleRatio * (image.height - originY * 2); if (image.currentX < image.minX) { image.currentX = image.minX + 1 - (image.minX - image.currentX + 1) ** 0.8; } if (image.currentX > image.maxX) { image.currentX = image.maxX - 1 + (image.currentX - image.maxX + 1) ** 0.8; } if (image.currentY < image.minY) { image.currentY = image.minY + 1 - (image.minY - image.currentY + 1) ** 0.8; } if (image.currentY > image.maxY) { image.currentY = image.maxY - 1 + (image.currentY - image.maxY + 1) ** 0.8; } // Velocity if (!velocity.prevPositionX) velocity.prevPositionX = image.touchesCurrent.x; if (!velocity.prevPositionY) velocity.prevPositionY = image.touchesCurrent.y; if (!velocity.prevTime) velocity.prevTime = Date.now(); velocity.x = (image.touchesCurrent.x - velocity.prevPositionX) / (Date.now() - velocity.prevTime) / 2; velocity.y = (image.touchesCurrent.y - velocity.prevPositionY) / (Date.now() - velocity.prevTime) / 2; if (Math.abs(image.touchesCurrent.x - velocity.prevPositionX) < 2) velocity.x = 0; if (Math.abs(image.touchesCurrent.y - velocity.prevPositionY) < 2) velocity.y = 0; velocity.prevPositionX = image.touchesCurrent.x; velocity.prevPositionY = image.touchesCurrent.y; velocity.prevTime = Date.now(); gesture.imageWrapEl.style.transform = `translate3d(${image.currentX}px, ${image.currentY}px,0)`; } function onTouchEnd() { const zoom = swiper.zoom; evCache.length = 0; if (!gesture.imageEl) return; if (!image.isTouched || !image.isMoved) { image.isTouched = false; image.isMoved = false; return; } image.isTouched = false; image.isMoved = false; let momentumDurationX = 300; let momentumDurationY = 300; const momentumDistanceX = velocity.x * momentumDurationX; const newPositionX = image.currentX + momentumDistanceX; const momentumDistanceY = velocity.y * momentumDurationY; const newPositionY = image.currentY + momentumDistanceY; // Fix duration if (velocity.x !== 0) momentumDurationX = Math.abs((newPositionX - image.currentX) / velocity.x); if (velocity.y !== 0) momentumDurationY = Math.abs((newPositionY - image.currentY) / velocity.y); const momentumDuration = Math.max(momentumDurationX, momentumDurationY); image.currentX = newPositionX; image.currentY = newPositionY; // Define if we need image drag const scaledWidth = image.width * zoom.scale; const scaledHeight = image.height * zoom.scale; image.minX = Math.min(gesture.slideWidth / 2 - scaledWidth / 2, 0); image.maxX = -image.minX; image.minY = Math.min(gesture.slideHeight / 2 - scaledHeight / 2, 0); image.maxY = -image.minY; image.currentX = Math.max(Math.min(image.currentX, image.maxX), image.minX); image.currentY = Math.max(Math.min(image.currentY, image.maxY), image.minY); gesture.imageWrapEl.style.transitionDuration = `${momentumDuration}ms`; gesture.imageWrapEl.style.transform = `translate3d(${image.currentX}px, ${image.currentY}px,0)`; } function onTransitionEnd() { const zoom = swiper.zoom; if (gesture.slideEl && swiper.activeIndex !== swiper.slides.indexOf(gesture.slideEl)) { if (gesture.imageEl) { gesture.imageEl.style.transform = 'translate3d(0,0,0) scale(1)'; } if (gesture.imageWrapEl) { gesture.imageWrapEl.style.transform = 'translate3d(0,0,0)'; } gesture.slideEl.classList.remove(`${swiper.params.zoom.zoomedSlideClass}`); zoom.scale = 1; currentScale = 1; gesture.slideEl = undefined; gesture.imageEl = undefined; gesture.imageWrapEl = undefined; gesture.originX = 0; gesture.originY = 0; } } function onMouseMove(e) { // Only pan if zoomed in and mouse panning is enabled if (currentScale <= 1 || !gesture.imageWrapEl) return; if (!eventWithinSlide(e) || !eventWithinZoomContainer(e)) return; const currentTransform = window.getComputedStyle(gesture.imageWrapEl).transform; const matrix = new window.DOMMatrix(currentTransform); if (!isPanningWithMouse) { isPanningWithMouse = true; mousePanStart.x = e.clientX; mousePanStart.y = e.clientY; image.startX = matrix.e; image.startY = matrix.f; image.width = gesture.imageEl.offsetWidth || gesture.imageEl.clientWidth; image.height = gesture.imageEl.offsetHeight || gesture.imageEl.clientHeight; gesture.slideWidth = gesture.slideEl.offsetWidth; gesture.slideHeight = gesture.slideEl.offsetHeight; return; } const deltaX = (e.clientX - mousePanStart.x) * mousePanSensitivity; const deltaY = (e.clientY - mousePanStart.y) * mousePanSensitivity; const scaledWidth = image.width * currentScale; const scaledHeight = image.height * currentScale; const slideWidth = gesture.slideWidth; const slideHeight = gesture.slideHeight; const minX = Math.min(slideWidth / 2 - scaledWidth / 2, 0); const maxX = -minX; const minY = Math.min(slideHeight / 2 - scaledHeight / 2, 0); const maxY = -minY; const newX = Math.max(Math.min(image.startX + deltaX, maxX), minX); const newY = Math.max(Math.min(image.startY + deltaY, maxY), minY); gesture.imageWrapEl.style.transitionDuration = '0ms'; gesture.imageWrapEl.style.transform = `translate3d(${newX}px, ${newY}px, 0)`; mousePanStart.x = e.clientX; mousePanStart.y = e.clientY; image.startX = newX; image.startY = newY; image.currentX = newX; image.currentY = newY; } function zoomIn(e) { const zoom = swiper.zoom; const params = swiper.params.zoom; if (!gesture.slideEl) { if (e && e.target) { gesture.slideEl = e.target.closest(`.${swiper.params.slideClass}, swiper-slide`); } if (!gesture.slideEl) { if (swiper.params.virtual && swiper.params.virtual.enabled && swiper.virtual) { gesture.slideEl = elementChildren( swiper.slidesEl, `.${swiper.params.slideActiveClass}`, )[0]; } else { gesture.slideEl = swiper.slides[swiper.activeIndex]; } } let imageEl = gesture.slideEl.querySelector(`.${params.containerClass}`); if (imageEl) { imageEl = imageEl.querySelectorAll('picture, img, svg, canvas, .swiper-zoom-target')[0]; } gesture.imageEl = imageEl; if (imageEl) { gesture.imageWrapEl = elementParents(gesture.imageEl, `.${params.containerClass}`)[0]; } else { gesture.imageWrapEl = undefined; } } if (!gesture.imageEl || !gesture.imageWrapEl) return; gesture.maxRatio = getMaxRatio(); if (swiper.params.cssMode) { swiper.wrapperEl.style.overflow = 'hidden'; swiper.wrapperEl.style.touchAction = 'none'; } gesture.slideEl.classList.add(`${params.zoomedSlideClass}`); let touchX; let touchY; let offsetX; let offsetY; let diffX; let diffY; let translateX; let translateY; let imageWidth; let imageHeight; let scaledWidth; let scaledHeight; let translateMinX; let translateMinY; let translateMaxX; let translateMaxY; let slideWidth; let slideHeight; if (typeof image.touchesStart.x === 'undefined' && e) { touchX = e.pageX; touchY = e.pageY; } else { touchX = image.touchesStart.x; touchY = image.touchesStart.y; } const prevScale = currentScale; const forceZoomRatio = typeof e === 'number' ? e : null; if (currentScale === 1 && forceZoomRatio) { touchX = undefined; touchY = undefined; image.touchesStart.x = undefined; image.touchesStart.y = undefined; } const maxRatio = getMaxRatio(); zoom.scale = forceZoomRatio || maxRatio; currentScale = forceZoomRatio || maxRatio; if (e && !(currentScale === 1 && forceZoomRatio)) { slideWidth = gesture.slideEl.offsetWidth; slideHeight = gesture.slideEl.offsetHeight; offsetX = elementOffset(gesture.slideEl).left + window.scrollX; offsetY = elementOffset(gesture.slideEl).top + window.scrollY; diffX = offsetX + slideWidth / 2 - touchX; diffY = offsetY + slideHeight / 2 - touchY; imageWidth = gesture.imageEl.offsetWidth || gesture.imageEl.clientWidth; imageHeight = gesture.imageEl.offsetHeight || gesture.imageEl.clientHeight; scaledWidth = imageWidth * zoom.scale; scaledHeight = imageHeight * zoom.scale; translateMinX = Math.min(slideWidth / 2 - scaledWidth / 2, 0); translateMinY = Math.min(slideHeight / 2 - scaledHeight / 2, 0); translateMaxX = -translateMinX; translateMaxY = -translateMinY; if ( prevScale > 0 && forceZoomRatio && typeof image.currentX === 'number' && typeof image.currentY === 'number' ) { translateX = (image.currentX * zoom.scale) / prevScale; translateY = (image.currentY * zoom.scale) / prevScale; } else { translateX = diffX * zoom.scale; translateY = diffY * zoom.scale; } if (translateX < translateMinX) { translateX = translateMinX; } if (translateX > translateMaxX) { translateX = translateMaxX; } if (translateY < translateMinY) { translateY = translateMinY; } if (translateY > translateMaxY) { translateY = translateMaxY; } } else { translateX = 0; translateY = 0; } if (forceZoomRatio && zoom.scale === 1) { gesture.originX = 0; gesture.originY = 0; } image.currentX = translateX; image.currentY = translateY; gesture.imageWrapEl.style.transitionDuration = '300ms'; gesture.imageWrapEl.style.transform = `translate3d(${translateX}px, ${translateY}px,0)`; gesture.imageEl.style.transitionDuration = '300ms'; gesture.imageEl.style.transform = `translate3d(0,0,0) scale(${zoom.scale})`; } function zoomOut() { const zoom = swiper.zoom; const params = swiper.params.zoom; if (!gesture.slideEl) { if (swiper.params.virtual && swiper.params.virtual.enabled && swiper.virtual) { gesture.slideEl = elementChildren(swiper.slidesEl, `.${swiper.params.slideActiveClass}`)[0]; } else { gesture.slideEl = swiper.slides[swiper.activeIndex]; } let imageEl = gesture.slideEl.querySelector(`.${params.containerClass}`); if (imageEl) { imageEl = imageEl.querySelectorAll('picture, img, svg, canvas, .swiper-zoom-target')[0]; } gesture.imageEl = imageEl; if (imageEl) { gesture.imageWrapEl = elementParents(gesture.imageEl, `.${params.containerClass}`)[0]; } else { gesture.imageWrapEl = undefined; } } if (!gesture.imageEl || !gesture.imageWrapEl) return; gesture.maxRatio = getMaxRatio(); if (swiper.params.cssMode) { swiper.wrapperEl.style.overflow = ''; swiper.wrapperEl.style.touchAction = ''; } zoom.scale = 1; currentScale = 1; image.currentX = undefined; image.currentY = undefined; image.touchesStart.x = undefined; image.touchesStart.y = undefined; gesture.imageWrapEl.style.transitionDuration = '300ms'; gesture.imageWrapEl.style.transform = 'translate3d(0,0,0)'; gesture.imageEl.style.transitionDuration = '300ms'; gesture.imageEl.style.transform = 'translate3d(0,0,0) scale(1)'; gesture.slideEl.classList.remove(`${params.zoomedSlideClass}`); gesture.slideEl = undefined; gesture.originX = 0; gesture.originY = 0; if (swiper.params.zoom.panOnMouseMove) { mousePanStart = { x: 0, y: 0 }; if (isPanningWithMouse) { isPanningWithMouse = false; image.startX = 0; image.startY = 0; } } } // Toggle Zoom function zoomToggle(e) { const zoom = swiper.zoom; if (zoom.scale && zoom.scale !== 1) { // Zoom Out zoomOut(); } else { // Zoom In zoomIn(e); } } function getListeners() { const passiveListener = swiper.params.passiveListeners ? { passive: true, capture: false } : false; const activeListenerWithCapture = swiper.params.passiveListeners ? { passive: false, capture: true } : true; return { passiveListener, activeListenerWithCapture }; } // Attach/Detach Events function enable() { const zoom = swiper.zoom; if (zoom.enabled) return; zoom.enabled = true; const { passiveListener, activeListenerWithCapture } = getListeners(); // Scale image swiper.wrapperEl.addEventListener('pointerdown', onGestureStart, passiveListener); swiper.wrapperEl.addEventListener('pointermove', onGestureChange, activeListenerWithCapture); ['pointerup', 'pointercancel', 'pointerout'].forEach((eventName) => { swiper.wrapperEl.addEventListener(eventName, onGestureEnd, passiveListener); }); // Move image swiper.wrapperEl.addEventListener('pointermove', onTouchMove, activeListenerWithCapture); } function disable() { const zoom = swiper.zoom; if (!zoom.enabled) return; zoom.enabled = false; const { passiveListener, activeListenerWithCapture } = getListeners(); // Scale image swiper.wrapperEl.removeEventListener('pointerdown', onGestureStart, passiveListener); swiper.wrapperEl.removeEventListener('pointermove', onGestureChange, activeListenerWithCapture); ['pointerup', 'pointercancel', 'pointerout'].forEach((eventName) => { swiper.wrapperEl.removeEventListener(eventName, onGestureEnd, passiveListener); }); // Move image swiper.wrapperEl.removeEventListener('pointermove', onTouchMove, activeListenerWithCapture); } on('init', () => { if (swiper.params.zoom.enabled) { enable(); } }); on('destroy', () => { disable(); }); on('touchStart', (_s, e) => { if (!swiper.zoom.enabled) return; onTouchStart(e); }); on('touchEnd', (_s, e) => { if (!swiper.zoom.enabled) return; onTouchEnd(e); }); on('doubleTap', (_s, e) => { if ( !swiper.animating && swiper.params.zoom.enabled && swiper.zoom.enabled && swiper.params.zoom.toggle ) { zoomToggle(e); } }); on('transitionEnd', () => { if (swiper.zoom.enabled && swiper.params.zoom.enabled) { onTransitionEnd(); } }); on('slideChange', () => { if (swiper.zoom.enabled && swiper.params.zoom.enabled && swiper.params.cssMode) { onTransitionEnd(); } }); Object.assign(swiper.zoom, { enable, disable, in: zoomIn, out: zoomOut, toggle: zoomToggle, }); } ================================================ FILE: src/react/context.mjs ================================================ import { createContext, useContext } from 'react'; export const SwiperSlideContext = createContext(null); export const useSwiperSlide = () => { return useContext(SwiperSlideContext); }; export const SwiperContext = createContext(null); export const useSwiper = () => { return useContext(SwiperContext); }; ================================================ FILE: src/react/get-children.mjs ================================================ import React from 'react'; function isChildSwiperSlide(child) { return child.type && child.type.displayName && child.type.displayName.includes('SwiperSlide'); } function processChildren(c) { const slides = []; React.Children.toArray(c).forEach((child) => { if (isChildSwiperSlide(child)) { slides.push(child); } else if (child.props && child.props.children) { processChildren(child.props.children).forEach((slide) => slides.push(slide)); } }); return slides; } function getChildren(c) { const slides = []; const slots = { 'container-start': [], 'container-end': [], 'wrapper-start': [], 'wrapper-end': [], }; React.Children.toArray(c).forEach((child) => { if (isChildSwiperSlide(child)) { slides.push(child); } else if (child.props && child.props.slot && slots[child.props.slot]) { slots[child.props.slot].push(child); } else if (child.props && child.props.children) { const foundSlides = processChildren(child.props.children); if (foundSlides.length > 0) { foundSlides.forEach((slide) => slides.push(slide)); } else { slots['container-end'].push(child); } } else { slots['container-end'].push(child); } }); return { slides, slots }; } export { getChildren }; ================================================ FILE: src/react/swiper-slide.mjs ================================================ import React, { useRef, useState, forwardRef } from 'react'; import { uniqueClasses } from '../components-shared/utils.mjs'; import { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect.mjs'; import { SwiperSlideContext } from './context.mjs'; const SwiperSlide = forwardRef( ( { tag: Tag = 'div', children, className = '', swiper, zoom, lazy, virtualIndex, swiperSlideIndex, ...rest } = {}, externalRef, ) => { const slideElRef = useRef(null); const [slideClasses, setSlideClasses] = useState('swiper-slide'); const [lazyLoaded, setLazyLoaded] = useState(false); function updateClasses(_s, el, classNames) { if (el === slideElRef.current) { setSlideClasses(classNames); } } useIsomorphicLayoutEffect(() => { if (typeof swiperSlideIndex !== 'undefined') { slideElRef.current.swiperSlideIndex = swiperSlideIndex; } if (externalRef) { externalRef.current = slideElRef.current; } if (!slideElRef.current || !swiper) { return; } if (swiper.destroyed) { if (slideClasses !== 'swiper-slide') { setSlideClasses('swiper-slide'); } return; } swiper.on('_slideClass', updateClasses); // eslint-disable-next-line return () => { if (!swiper) return; swiper.off('_slideClass', updateClasses); }; }); useIsomorphicLayoutEffect(() => { if (swiper && slideElRef.current && !swiper.destroyed) { setSlideClasses(swiper.getSlideClasses(slideElRef.current)); } }, [swiper]); const slideData = { isActive: slideClasses.indexOf('swiper-slide-active') >= 0, isVisible: slideClasses.indexOf('swiper-slide-visible') >= 0, isPrev: slideClasses.indexOf('swiper-slide-prev') >= 0, isNext: slideClasses.indexOf('swiper-slide-next') >= 0, }; const renderChildren = () => { return typeof children === 'function' ? children(slideData) : children; }; const onLoad = () => { setLazyLoaded(true); }; return ( {zoom && (
{renderChildren()} {lazy && !lazyLoaded && (
{ if (node) node.lazyPreloaderManaged = true; }} /> )}
)} {!zoom && ( {renderChildren()} {lazy && !lazyLoaded && (
{ if (node) node.lazyPreloaderManaged = true; }} /> )} )} ); }, ); SwiperSlide.displayName = 'SwiperSlide'; export { SwiperSlide }; ================================================ FILE: src/react/swiper.mjs ================================================ import React, { useRef, useState, useEffect, forwardRef } from 'react'; import SwiperCore from '../swiper.mjs'; import { getParams } from '../components-shared/get-params.mjs'; import { mountSwiper } from '../components-shared/mount-swiper.mjs'; import { needsScrollbar, needsNavigation, needsPagination, uniqueClasses, extend, wrapperClass, } from '../components-shared/utils.mjs'; import { getChangedParams } from '../components-shared/get-changed-params.mjs'; import { getChildren } from './get-children.mjs'; import { updateSwiper } from '../components-shared/update-swiper.mjs'; import { renderVirtual } from './virtual.mjs'; import { updateOnVirtualData } from '../components-shared/update-on-virtual-data.mjs'; import { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect.mjs'; import { SwiperContext } from './context.mjs'; const Swiper = forwardRef( ( { className, tag: Tag = 'div', wrapperTag: WrapperTag = 'div', children, onSwiper, ...rest } = {}, externalElRef, ) => { let eventsAssigned = false; const [containerClasses, setContainerClasses] = useState('swiper'); const [virtualData, setVirtualData] = useState(null); const [breakpointChanged, setBreakpointChanged] = useState(false); const initializedRef = useRef(false); const swiperElRef = useRef(null); const swiperRef = useRef(null); const oldPassedParamsRef = useRef(null); const oldSlides = useRef(null); const nextElRef = useRef(null); const prevElRef = useRef(null); const paginationElRef = useRef(null); const scrollbarElRef = useRef(null); const { params: swiperParams, passedParams, rest: restProps, events } = getParams(rest); const { slides, slots } = getChildren(children); const onBeforeBreakpoint = () => { setBreakpointChanged(!breakpointChanged); }; Object.assign(swiperParams.on, { _containerClasses(swiper, classes) { setContainerClasses(classes); }, }); const initSwiper = () => { // init swiper Object.assign(swiperParams.on, events); eventsAssigned = true; const passParams = { ...swiperParams }; delete passParams.wrapperClass; swiperRef.current = new SwiperCore(passParams); if (swiperRef.current.virtual && swiperRef.current.params.virtual.enabled) { swiperRef.current.virtual.slides = slides; const extendWith = { cache: false, slides, renderExternal: setVirtualData, renderExternalUpdate: false, }; extend(swiperRef.current.params.virtual, extendWith); extend(swiperRef.current.originalParams.virtual, extendWith); } }; if (!swiperElRef.current) { initSwiper(); } // Listen for breakpoints change if (swiperRef.current) { swiperRef.current.on('_beforeBreakpoint', onBeforeBreakpoint); } const attachEvents = () => { if (eventsAssigned || !events || !swiperRef.current) return; Object.keys(events).forEach((eventName) => { swiperRef.current.on(eventName, events[eventName]); }); }; const detachEvents = () => { if (!events || !swiperRef.current) return; Object.keys(events).forEach((eventName) => { swiperRef.current.off(eventName, events[eventName]); }); }; useEffect(() => { return () => { if (swiperRef.current) swiperRef.current.off('_beforeBreakpoint', onBeforeBreakpoint); }; }); // set initialized flag useEffect(() => { if (!initializedRef.current && swiperRef.current) { swiperRef.current.emitSlidesClasses(); initializedRef.current = true; } }); // mount swiper useIsomorphicLayoutEffect(() => { if (externalElRef) { externalElRef.current = swiperElRef.current; } if (!swiperElRef.current) return; if (swiperRef.current.destroyed) { initSwiper(); } mountSwiper( { el: swiperElRef.current, nextEl: nextElRef.current, prevEl: prevElRef.current, paginationEl: paginationElRef.current, scrollbarEl: scrollbarElRef.current, swiper: swiperRef.current, }, swiperParams, ); if (onSwiper && !swiperRef.current.destroyed) onSwiper(swiperRef.current); // eslint-disable-next-line return () => { if (swiperRef.current && !swiperRef.current.destroyed) { swiperRef.current.destroy(true, false); } }; }, []); // watch for params change useIsomorphicLayoutEffect(() => { attachEvents(); const changedParams = getChangedParams( passedParams, oldPassedParamsRef.current, slides, oldSlides.current, (c) => c.key, ); oldPassedParamsRef.current = passedParams; oldSlides.current = slides; if (changedParams.length && swiperRef.current && !swiperRef.current.destroyed) { updateSwiper({ swiper: swiperRef.current, slides, passedParams, changedParams, nextEl: nextElRef.current, prevEl: prevElRef.current, scrollbarEl: scrollbarElRef.current, paginationEl: paginationElRef.current, }); } return () => { detachEvents(); }; }); // update on virtual update useIsomorphicLayoutEffect(() => { updateOnVirtualData(swiperRef.current); }, [virtualData]); // bypass swiper instance to slides function renderSlides() { if (swiperParams.virtual) { return renderVirtual(swiperRef.current, slides, virtualData); } return slides.map((child, index) => { return React.cloneElement(child, { swiper: swiperRef.current, swiperSlideIndex: index }); }); } return ( {slots['container-start']} {slots['wrapper-start']} {renderSlides()} {slots['wrapper-end']} {needsNavigation(swiperParams) && ( <>
)} {needsScrollbar(swiperParams) && (
)} {needsPagination(swiperParams) && (
)} {slots['container-end']} ); }, ); Swiper.displayName = 'Swiper'; export { Swiper }; ================================================ FILE: src/react/use-isomorphic-layout-effect.mjs ================================================ import { useEffect, useLayoutEffect } from 'react'; function useIsomorphicLayoutEffect(callback, deps) { // eslint-disable-next-line if (typeof window === 'undefined') return useEffect(callback, deps); return useLayoutEffect(callback, deps); } export { useIsomorphicLayoutEffect }; ================================================ FILE: src/react/virtual.mjs ================================================ import React from 'react'; function renderVirtual(swiper, slides, virtualData) { if (!virtualData) return null; const getSlideIndex = (index) => { let slideIndex = index; if (index < 0) { slideIndex = slides.length + index; } else if (slideIndex >= slides.length) { // eslint-disable-next-line slideIndex = 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.push(slides[getSlideIndex(i)]); } } return slidesToRender.map((child, index) => { return React.cloneElement(child, { swiper, style, key: child.props.virtualIndex || child.key || `slide-${index}`, }); }); } export { renderVirtual }; ================================================ FILE: src/shared/classes-to-selector.mjs ================================================ export default function classesToSelector(classes = '') { // Escape all CSS selector special characters return `.${classes .trim() .replace(/([\.:!+\/()[\]#>~*^$|=,'"@{}\\])/g, '\\$1') // eslint-disable-line .replace(/ /g, '.')}`; } ================================================ FILE: src/shared/classes-to-tokens.mjs ================================================ export default function classesToTokens(classes = '') { return classes .trim() .split(' ') .filter((c) => !!c.trim()); } ================================================ FILE: src/shared/create-element-if-not-defined.mjs ================================================ import { createElement, elementChildren } from './utils.mjs'; export default function createElementIfNotDefined(swiper, originalParams, params, checkProps) { if (swiper.params.createElements) { Object.keys(checkProps).forEach((key) => { if (!params[key] && params.auto === true) { let element = elementChildren(swiper.el, `.${checkProps[key]}`)[0]; if (!element) { element = createElement('div', checkProps[key]); element.className = checkProps[key]; swiper.el.append(element); } params[key] = element; originalParams[key] = element; } }); } return params; } ================================================ FILE: src/shared/create-shadow.mjs ================================================ import { createElement, getSlideTransformEl } from './utils.mjs'; export default function createShadow(suffix, slideEl, side) { const shadowClass = `swiper-slide-shadow${side ? `-${side}` : ''}${ suffix ? ` swiper-slide-shadow-${suffix}` : '' }`; const shadowContainer = getSlideTransformEl(slideEl); let shadowEl = shadowContainer.querySelector(`.${shadowClass.split(' ').join('.')}`); if (!shadowEl) { shadowEl = createElement('div', shadowClass.split(' ')); shadowContainer.append(shadowEl); } return shadowEl; } ================================================ FILE: src/shared/effect-init.mjs ================================================ export default function effectInit(params) { const { effect, swiper, on, setTranslate, setTransition, overwriteParams, perspective, recreateShadows, getEffectParams, } = params; on('beforeInit', () => { if (swiper.params.effect !== effect) return; swiper.classNames.push(`${swiper.params.containerModifierClass}${effect}`); if (perspective && perspective()) { swiper.classNames.push(`${swiper.params.containerModifierClass}3d`); } const overwriteParamsResult = overwriteParams ? overwriteParams() : {}; Object.assign(swiper.params, overwriteParamsResult); Object.assign(swiper.originalParams, overwriteParamsResult); }); on('setTranslate _virtualUpdated', () => { if (swiper.params.effect !== effect) return; setTranslate(); }); on('setTransition', (_s, duration) => { if (swiper.params.effect !== effect) return; setTransition(duration); }); on('transitionEnd', () => { if (swiper.params.effect !== effect) return; if (recreateShadows) { if (!getEffectParams || !getEffectParams().slideShadows) return; // remove shadows swiper.slides.forEach((slideEl) => { slideEl .querySelectorAll( '.swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left', ) .forEach((shadowEl) => shadowEl.remove()); }); // create new one recreateShadows(); } }); let requireUpdateOnVirtual; on('virtualUpdate', () => { if (swiper.params.effect !== effect) return; if (!swiper.slides.length) { requireUpdateOnVirtual = true; } requestAnimationFrame(() => { if (requireUpdateOnVirtual && swiper.slides && swiper.slides.length) { setTranslate(); requireUpdateOnVirtual = false; } }); }); } ================================================ FILE: src/shared/effect-target.mjs ================================================ import { getSlideTransformEl } from './utils.mjs'; export default function effectTarget(effectParams, slideEl) { const transformEl = getSlideTransformEl(slideEl); if (transformEl !== slideEl) { transformEl.style.backfaceVisibility = 'hidden'; transformEl.style['-webkit-backface-visibility'] = 'hidden'; } return transformEl; } ================================================ FILE: src/shared/effect-virtual-transition-end.mjs ================================================ import { elementTransitionEnd } from './utils.mjs'; export default function effectVirtualTransitionEnd({ swiper, duration, transformElements, allSlides, }) { const { activeIndex } = swiper; const getSlide = (el) => { if (!el.parentElement) { // assume shadow root const slide = swiper.slides.find( (slideEl) => slideEl.shadowRoot && slideEl.shadowRoot === el.parentNode, ); return slide; } return el.parentElement; }; if (swiper.params.virtualTranslate && duration !== 0) { let eventTriggered = false; let transitionEndTarget; if (allSlides) { transitionEndTarget = transformElements; } else { transitionEndTarget = transformElements.filter((transformEl) => { const el = transformEl.classList.contains('swiper-slide-transform') ? getSlide(transformEl) : transformEl; return swiper.getSlideIndex(el) === activeIndex; }); } transitionEndTarget.forEach((el) => { elementTransitionEnd(el, () => { if (eventTriggered) return; if (!swiper || swiper.destroyed) return; eventTriggered = true; swiper.animating = false; const evt = new window.CustomEvent('transitionend', { bubbles: true, cancelable: true, }); swiper.wrapperEl.dispatchEvent(evt); }); }); } } ================================================ FILE: src/shared/get-browser.mjs ================================================ import { getWindow } from 'ssr-window'; import { getDevice } from './get-device.mjs'; let browser; function calcBrowser() { const window = getWindow(); const device = getDevice(); let needPerspectiveFix = false; function isSafari() { const ua = window.navigator.userAgent.toLowerCase(); return ua.indexOf('safari') >= 0 && ua.indexOf('chrome') < 0 && ua.indexOf('android') < 0; } if (isSafari()) { const ua = String(window.navigator.userAgent); if (ua.includes('Version/')) { const [major, minor] = ua .split('Version/')[1] .split(' ')[0] .split('.') .map((num) => Number(num)); needPerspectiveFix = major < 16 || (major === 16 && minor < 2); } } const isWebView = /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(window.navigator.userAgent); const isSafariBrowser = isSafari(); const need3dFix = isSafariBrowser || (isWebView && device.ios); return { isSafari: needPerspectiveFix || isSafariBrowser, needPerspectiveFix, need3dFix, isWebView, }; } function getBrowser() { if (!browser) { browser = calcBrowser(); } return browser; } export { getBrowser }; ================================================ FILE: src/shared/get-device.mjs ================================================ import { getWindow } from 'ssr-window'; import { getSupport } from './get-support.mjs'; let deviceCached; function calcDevice({ userAgent } = {}) { const support = getSupport(); const window = getWindow(); const platform = window.navigator.platform; const ua = userAgent || window.navigator.userAgent; const device = { ios: false, android: false, }; const screenWidth = window.screen.width; const screenHeight = window.screen.height; const android = ua.match(/(Android);?[\s\/]+([\d.]+)?/); // eslint-disable-line let ipad = ua.match(/(iPad)(?!\1).*OS\s([\d_]+)/); const ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/); const iphone = !ipad && ua.match(/(iPhone\sOS|iOS)\s([\d_]+)/); const windows = platform === 'Win32'; let macos = platform === 'MacIntel'; // iPadOs 13 fix const iPadScreens = [ '1024x1366', '1366x1024', '834x1194', '1194x834', '834x1112', '1112x834', '768x1024', '1024x768', '820x1180', '1180x820', '810x1080', '1080x810', ]; if ( !ipad && macos && support.touch && iPadScreens.indexOf(`${screenWidth}x${screenHeight}`) >= 0 ) { ipad = ua.match(/(Version)\/([\d.]+)/); if (!ipad) ipad = [0, 1, '13_0_0']; macos = false; } // Android if (android && !windows) { device.os = 'android'; device.android = true; } if (ipad || iphone || ipod) { device.os = 'ios'; device.ios = true; } // Export object return device; } function getDevice(overrides = {}) { if (!deviceCached) { deviceCached = calcDevice(overrides); } return deviceCached; } export { getDevice }; ================================================ FILE: src/shared/get-support.mjs ================================================ import { getWindow, getDocument } from 'ssr-window'; let support; function calcSupport() { const window = getWindow(); const document = getDocument(); return { smoothScroll: document.documentElement && document.documentElement.style && 'scrollBehavior' in document.documentElement.style, touch: !!( 'ontouchstart' in window || (window.DocumentTouch && document instanceof window.DocumentTouch) ), }; } function getSupport() { if (!support) { support = calcSupport(); } return support; } export { getSupport }; ================================================ FILE: src/shared/process-lazy-preloader.mjs ================================================ export const processLazyPreloader = (swiper, imageEl) => { if (!swiper || swiper.destroyed || !swiper.params) return; const slideSelector = () => (swiper.isElement ? `swiper-slide` : `.${swiper.params.slideClass}`); const slideEl = imageEl.closest(slideSelector()); if (slideEl) { let lazyEl = slideEl.querySelector(`.${swiper.params.lazyPreloaderClass}`); if (!lazyEl && swiper.isElement) { if (slideEl.shadowRoot) { lazyEl = slideEl.shadowRoot.querySelector(`.${swiper.params.lazyPreloaderClass}`); } else { // init later requestAnimationFrame(() => { if (slideEl.shadowRoot) { lazyEl = slideEl.shadowRoot.querySelector(`.${swiper.params.lazyPreloaderClass}`); if (lazyEl && !lazyEl.lazyPreloaderManaged) lazyEl.remove(); } }); } } // Skip removal if managed by React/Vue component if (lazyEl && !lazyEl.lazyPreloaderManaged) lazyEl.remove(); } }; const unlazy = (swiper, index) => { if (!swiper.slides[index]) return; const imageEl = swiper.slides[index].querySelector('[loading="lazy"]'); if (imageEl) imageEl.removeAttribute('loading'); }; export const preload = (swiper) => { if (!swiper || swiper.destroyed || !swiper.params) return; let amount = swiper.params.lazyPreloadPrevNext; const len = swiper.slides.length; if (!len || !amount || amount < 0) return; amount = Math.min(amount, len); const slidesPerView = swiper.params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : Math.ceil(swiper.params.slidesPerView); const activeIndex = swiper.activeIndex; if (swiper.params.grid && swiper.params.grid.rows > 1) { const activeColumn = activeIndex; const preloadColumns = [activeColumn - amount]; preloadColumns.push( ...Array.from({ length: amount }).map((_, i) => { return activeColumn + slidesPerView + i; }), ); swiper.slides.forEach((slideEl, i) => { if (preloadColumns.includes(slideEl.column)) unlazy(swiper, i); }); return; } const slideIndexLastInView = activeIndex + slidesPerView - 1; if (swiper.params.rewind || swiper.params.loop) { for (let i = activeIndex - amount; i <= slideIndexLastInView + amount; i += 1) { const realIndex = ((i % len) + len) % len; if (realIndex < activeIndex || realIndex > slideIndexLastInView) unlazy(swiper, realIndex); } } else { for ( let i = Math.max(activeIndex - amount, 0); i <= Math.min(slideIndexLastInView + amount, len - 1); i += 1 ) { if (i !== activeIndex && (i > slideIndexLastInView || i < activeIndex)) { unlazy(swiper, i); } } } }; ================================================ FILE: src/shared/utils.mjs ================================================ import { getWindow, getDocument } from 'ssr-window'; import classesToTokens from './classes-to-tokens.mjs'; function deleteProps(obj) { const object = obj; Object.keys(object).forEach((key) => { try { object[key] = null; } catch (e) { // no getter for object } try { delete object[key]; } catch (e) { // something got wrong } }); } function nextTick(callback, delay = 0) { return setTimeout(callback, delay); } function now() { return Date.now(); } function getComputedStyle(el) { const window = getWindow(); let style; if (window.getComputedStyle) { style = window.getComputedStyle(el, null); } if (!style && el.currentStyle) { style = el.currentStyle; } if (!style) { style = el.style; } return style; } function getTranslate(el, axis = 'x') { const window = getWindow(); let matrix; let curTransform; let transformMatrix; const curStyle = getComputedStyle(el, null); if (window.WebKitCSSMatrix) { curTransform = curStyle.transform || curStyle.webkitTransform; if (curTransform.split(',').length > 6) { curTransform = curTransform .split(', ') .map((a) => a.replace(',', '.')) .join(', '); } // Some old versions of Webkit choke when 'none' is passed; pass // empty string instead in this case transformMatrix = new window.WebKitCSSMatrix(curTransform === 'none' ? '' : curTransform); } else { transformMatrix = curStyle.MozTransform || curStyle.OTransform || curStyle.MsTransform || curStyle.msTransform || curStyle.transform || curStyle.getPropertyValue('transform').replace('translate(', 'matrix(1, 0, 0, 1,'); matrix = transformMatrix.toString().split(','); } if (axis === 'x') { // Latest Chrome and webkits Fix if (window.WebKitCSSMatrix) curTransform = transformMatrix.m41; // Crazy IE10 Matrix else if (matrix.length === 16) curTransform = parseFloat(matrix[12]); // Normal Browsers else curTransform = parseFloat(matrix[4]); } if (axis === 'y') { // Latest Chrome and webkits Fix if (window.WebKitCSSMatrix) curTransform = transformMatrix.m42; // Crazy IE10 Matrix else if (matrix.length === 16) curTransform = parseFloat(matrix[13]); // Normal Browsers else curTransform = parseFloat(matrix[5]); } return curTransform || 0; } function isObject(o) { return ( typeof o === 'object' && o !== null && o.constructor && Object.prototype.toString.call(o).slice(8, -1) === 'Object' ); } function isNode(node) { // eslint-disable-next-line if (typeof window !== 'undefined' && typeof window.HTMLElement !== 'undefined') { return node instanceof HTMLElement; } return node && (node.nodeType === 1 || node.nodeType === 11); } function extend(...args) { const to = Object(args[0]); for (let i = 1; i < args.length; i += 1) { const nextSource = args[i]; if (nextSource !== undefined && nextSource !== null && !isNode(nextSource)) { const keysArray = Object.keys(Object(nextSource)).filter( (key) => key !== '__proto__' && key !== 'constructor' && key !== 'prototype', ); for (let nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex += 1) { const nextKey = keysArray[nextIndex]; const desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); if (desc !== undefined && desc.enumerable) { if (isObject(to[nextKey]) && isObject(nextSource[nextKey])) { if (nextSource[nextKey].__swiper__) { to[nextKey] = nextSource[nextKey]; } else { extend(to[nextKey], nextSource[nextKey]); } } else if (!isObject(to[nextKey]) && isObject(nextSource[nextKey])) { to[nextKey] = {}; if (nextSource[nextKey].__swiper__) { to[nextKey] = nextSource[nextKey]; } else { extend(to[nextKey], nextSource[nextKey]); } } else { to[nextKey] = nextSource[nextKey]; } } } } } return to; } function setCSSProperty(el, varName, varValue) { el.style.setProperty(varName, varValue); } function animateCSSModeScroll({ swiper, targetPosition, side }) { const window = getWindow(); const startPosition = -swiper.translate; let startTime = null; let time; const duration = swiper.params.speed; swiper.wrapperEl.style.scrollSnapType = 'none'; window.cancelAnimationFrame(swiper.cssModeFrameID); const dir = targetPosition > startPosition ? 'next' : 'prev'; const isOutOfBound = (current, target) => { return (dir === 'next' && current >= target) || (dir === 'prev' && current <= target); }; const animate = () => { time = new Date().getTime(); if (startTime === null) { startTime = time; } const progress = Math.max(Math.min((time - startTime) / duration, 1), 0); const easeProgress = 0.5 - Math.cos(progress * Math.PI) / 2; let currentPosition = startPosition + easeProgress * (targetPosition - startPosition); if (isOutOfBound(currentPosition, targetPosition)) { currentPosition = targetPosition; } swiper.wrapperEl.scrollTo({ [side]: currentPosition, }); if (isOutOfBound(currentPosition, targetPosition)) { swiper.wrapperEl.style.overflow = 'hidden'; swiper.wrapperEl.style.scrollSnapType = ''; setTimeout(() => { swiper.wrapperEl.style.overflow = ''; swiper.wrapperEl.scrollTo({ [side]: currentPosition, }); }); window.cancelAnimationFrame(swiper.cssModeFrameID); return; } swiper.cssModeFrameID = window.requestAnimationFrame(animate); }; animate(); } function getSlideTransformEl(slideEl) { return ( slideEl.querySelector('.swiper-slide-transform') || (slideEl.shadowRoot && slideEl.shadowRoot.querySelector('.swiper-slide-transform')) || slideEl ); } function findElementsInElements(elements = [], selector = '') { const found = []; elements.forEach((el) => { found.push(...el.querySelectorAll(selector)); }); return found; } function elementChildren(element, selector = '') { const window = getWindow(); const children = [...element.children]; if (window.HTMLSlotElement && element instanceof HTMLSlotElement) { children.push(...element.assignedElements()); } if (!selector) { return children; } return children.filter((el) => el.matches(selector)); } function elementIsChildOfSlot(el, slot) { // Breadth-first search through all parent's children and assigned elements const elementsQueue = [slot]; while (elementsQueue.length > 0) { const elementToCheck = elementsQueue.shift(); if (el === elementToCheck) { return true; } elementsQueue.push( ...elementToCheck.children, ...(elementToCheck.shadowRoot ? elementToCheck.shadowRoot.children : []), ...(elementToCheck.assignedElements ? elementToCheck.assignedElements() : []), ); } } function elementIsChildOf(el, parent) { const window = getWindow(); let isChild = parent.contains(el); if (!isChild && window.HTMLSlotElement && parent instanceof HTMLSlotElement) { const children = [...parent.assignedElements()]; isChild = children.includes(el); if (!isChild) { isChild = elementIsChildOfSlot(el, parent); } } return isChild; } function showWarning(text) { try { console.warn(text); return; } catch (err) { // err } } function createElement(tag, classes = []) { const el = document.createElement(tag); el.classList.add(...(Array.isArray(classes) ? classes : classesToTokens(classes))); return el; } function elementOffset(el) { const window = getWindow(); const document = getDocument(); const box = el.getBoundingClientRect(); const body = document.body; const clientTop = el.clientTop || body.clientTop || 0; const clientLeft = el.clientLeft || body.clientLeft || 0; const scrollTop = el === window ? window.scrollY : el.scrollTop; const scrollLeft = el === window ? window.scrollX : el.scrollLeft; return { top: box.top + scrollTop - clientTop, left: box.left + scrollLeft - clientLeft, }; } function elementPrevAll(el, selector) { const prevEls = []; while (el.previousElementSibling) { const prev = el.previousElementSibling; // eslint-disable-line if (selector) { if (prev.matches(selector)) prevEls.push(prev); } else prevEls.push(prev); el = prev; } return prevEls; } function elementNextAll(el, selector) { const nextEls = []; while (el.nextElementSibling) { const next = el.nextElementSibling; // eslint-disable-line if (selector) { if (next.matches(selector)) nextEls.push(next); } else nextEls.push(next); el = next; } return nextEls; } function elementStyle(el, prop) { const window = getWindow(); return window.getComputedStyle(el, null).getPropertyValue(prop); } function elementIndex(el) { let child = el; let i; if (child) { i = 0; // eslint-disable-next-line while ((child = child.previousSibling) !== null) { if (child.nodeType === 1) i += 1; } return i; } return undefined; } function elementParents(el, selector) { const parents = []; // eslint-disable-line let parent = el.parentElement; // eslint-disable-line while (parent) { if (selector) { if (parent.matches(selector)) parents.push(parent); } else { parents.push(parent); } parent = parent.parentElement; } return parents; } function elementTransitionEnd(el, callback) { function fireCallBack(e) { if (e.target !== el) return; callback.call(el, e); el.removeEventListener('transitionend', fireCallBack); } if (callback) { el.addEventListener('transitionend', fireCallBack); } } function elementOuterSize(el, size, includeMargins) { const window = getWindow(); if (includeMargins) { return ( el[size === 'width' ? 'offsetWidth' : 'offsetHeight'] + parseFloat( window .getComputedStyle(el, null) .getPropertyValue(size === 'width' ? 'margin-right' : 'margin-top'), ) + parseFloat( window .getComputedStyle(el, null) .getPropertyValue(size === 'width' ? 'margin-left' : 'margin-bottom'), ) ); } return el.offsetWidth; } function makeElementsArray(el) { return (Array.isArray(el) ? el : [el]).filter((e) => !!e); } function getRotateFix(swiper) { return (v) => { if (Math.abs(v) > 0 && swiper.browser && swiper.browser.need3dFix && Math.abs(v) % 90 === 0) { return v + 0.001; } return v; }; } function setInnerHTML(el, html = '') { if (typeof trustedTypes !== 'undefined') { el.innerHTML = trustedTypes .createPolicy('html', { createHTML: (s) => s, }) .createHTML(html); } else { el.innerHTML = html; } } export { animateCSSModeScroll, deleteProps, nextTick, now, getTranslate, isObject, extend, getComputedStyle, setCSSProperty, getSlideTransformEl, showWarning, // dom findElementsInElements, createElement, elementChildren, elementIsChildOf, elementOffset, elementPrevAll, elementNextAll, elementStyle, elementIndex, elementParents, elementTransitionEnd, elementOuterSize, makeElementsArray, getRotateFix, setInnerHTML, }; ================================================ FILE: src/swiper-bundle.mjs ================================================ import Swiper from './core/core.mjs'; //IMPORT_MODULES // eslint-disable-next-line export { default as Swiper, default } from './core/core.mjs'; // Swiper Class const modules = [ //INSTALL_MODULES ]; Swiper.use(modules); ================================================ FILE: src/swiper-effect-utils.d.ts ================================================ import type { Swiper, SwiperOptions } from './types/index.d.ts'; declare const createShadow: (suffix: string, slideEl: HTMLElement, side?: string) => HTMLElement; declare const effectInit: (params: { effect: string; swiper: Swiper; on: () => void; setTranslate: () => void; setTransition: (duration: number) => void; overwriteParams?: () => SwiperOptions; perspective?: () => boolean; recreateShadows?: () => void; getEffectParams?: () => { slideShadows?: boolean }; }) => void; declare const effectTarget: (effectParams: any, slideEl: HTMLElement) => void; declare const effectVirtualTransitionEnd: (params: { swiper: Swiper; duration: number; transformElements: HTMLElement[]; allSlides?: boolean; }) => void; declare const getSlideTransformEl: (slideEl: HTMLElement) => HTMLElement; export { createShadow, effectInit, effectTarget, effectVirtualTransitionEnd, getSlideTransformEl }; ================================================ FILE: src/swiper-effect-utils.mjs ================================================ import createShadow from './shared/create-shadow.mjs'; import effectInit from './shared/effect-init.mjs'; import effectTarget from './shared/effect-target.mjs'; import effectVirtualTransitionEnd from './shared/effect-virtual-transition-end.mjs'; import { getSlideTransformEl, getRotateFix } from './shared/utils.mjs'; export { effectInit, effectTarget, effectVirtualTransitionEnd, getSlideTransformEl, createShadow, getRotateFix, }; ================================================ FILE: src/swiper-element.d.ts ================================================ // @ts-ignore import { Swiper, SwiperOptions } from './types/index.d.ts'; declare const register: () => void; // prettier-ignore interface SwiperContainerEventMap extends Omit { // CORE_EVENTS // MODULES_EVENTS } interface SwiperContainer extends HTMLElement {} interface SwiperContainer extends SwiperOptions { swiper: Swiper; initialize: () => void; injectStyles: string[]; injectStylesUrls: string[]; eventsPrefix: string; addEventListener( type: K, listener: (this: SwiperContainer, ev: SwiperContainerEventMap[K]) => any, options?: boolean | AddEventListenerOptions, ): void; addEventListener( type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions, ): void; removeEventListener( type: K, listener: (this: SwiperContainer, ev: SwiperContainerEventMap[K]) => any, options?: boolean | EventListenerOptions, ): void; removeEventListener( type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions, ): void; } interface SwiperSlide extends HTMLElement { lazy: string | boolean; } declare global { interface HTMLElementTagNameMap { 'swiper-container': SwiperContainer; 'swiper-slide': SwiperSlide; } } export { SwiperContainer, SwiperSlide, register }; ================================================ FILE: src/swiper-element.mjs ================================================ /* eslint-disable spaced-comment */ import Swiper from './swiper.mjs'; import { paramsList } from './components-shared/params-list.mjs'; import { getParams } from './components-shared/get-element-params.mjs'; import { needsScrollbar, needsNavigation, needsPagination, attrToProp, } from './components-shared/utils.mjs'; import { updateSwiper } from './components-shared/update-swiper.mjs'; import { setInnerHTML } from './shared/utils.mjs'; //SWIPER_STYLES //SWIPER_SLIDE_STYLES class DummyHTMLElement {} const ClassToExtend = typeof window === 'undefined' || typeof HTMLElement === 'undefined' ? DummyHTMLElement : HTMLElement; const addStyle = (shadowRoot, styles) => { if (typeof CSSStyleSheet !== 'undefined' && shadowRoot.adoptedStyleSheets) { const styleSheet = new CSSStyleSheet(); styleSheet.replaceSync(styles); shadowRoot.adoptedStyleSheets = [styleSheet]; } else { const style = document.createElement('style'); style.rel = 'stylesheet'; style.textContent = styles; shadowRoot.appendChild(style); } }; class SwiperContainer extends ClassToExtend { constructor() { super(); this.attachShadow({ mode: 'open' }); } cssStyles() { return [ SwiperCSS, // eslint-disable-line ...(this.injectStyles && Array.isArray(this.injectStyles) ? this.injectStyles : []), ].join('\n'); } cssLinks() { return this.injectStylesUrls || []; } calcSlideSlots() { const currentSideSlots = this.slideSlots || 0; // slide slots const slideSlotChildren = [...this.querySelectorAll(`[slot^=slide-]`)].map((child) => { return parseInt(child.getAttribute('slot').split('slide-')[1], 10); }); this.slideSlots = slideSlotChildren.length ? Math.max(...slideSlotChildren) + 1 : 0; if (!this.rendered) return; if (this.slideSlots > currentSideSlots) { for (let i = currentSideSlots; i < this.slideSlots; i += 1) { const slideEl = document.createElement('swiper-slide'); slideEl.setAttribute('part', `slide slide-${i + 1}`); const slotEl = document.createElement('slot'); slotEl.setAttribute('name', `slide-${i + 1}`); slideEl.appendChild(slotEl); this.shadowRoot.querySelector('.swiper-wrapper').appendChild(slideEl); } } else if (this.slideSlots < currentSideSlots) { const slides = this.swiper.slides; for (let i = slides.length - 1; i >= 0; i -= 1) { if (i > this.slideSlots) { slides[i].remove(); } } } } render() { if (this.rendered) return; this.calcSlideSlots(); // local styles let localStyles = this.cssStyles(); if (this.slideSlots > 0) { localStyles = localStyles.replace(/::slotted\(([a-z-0-9.]*)\)/g, '$1'); } if (localStyles.length) { addStyle(this.shadowRoot, localStyles); } this.cssLinks().forEach((url) => { const linkExists = this.shadowRoot.querySelector(`link[href="${url}"]`); if (linkExists) return; const linkEl = document.createElement('link'); linkEl.rel = 'stylesheet'; linkEl.href = url; this.shadowRoot.appendChild(linkEl); }); // prettier-ignore const el = document.createElement('div'); el.classList.add('swiper'); el.part = 'container'; // prettier-ignore setInnerHTML(el, `
${Array.from({length: this.slideSlots}).map((_, index) => ` `).join('')}
${needsNavigation(this.passedParams) ? `
` : ''} ${needsPagination(this.passedParams) ? `
` : ''} ${needsScrollbar(this.passedParams) ? `
` : ''} `); this.shadowRoot.appendChild(el); this.rendered = true; } initialize() { if (this.swiper && this.swiper.initialized) return; const { params: swiperParams, passedParams } = getParams(this); this.swiperParams = swiperParams; this.passedParams = passedParams; delete this.swiperParams.init; this.render(); // eslint-disable-next-line this.swiper = new Swiper(this.shadowRoot.querySelector('.swiper'), { ...(swiperParams.virtual ? {} : { observer: true }), ...swiperParams, touchEventsTarget: 'container', onAny: (name, ...args) => { if (name === 'observerUpdate') { this.calcSlideSlots(); } const eventName = swiperParams.eventsPrefix ? `${swiperParams.eventsPrefix}${name.toLowerCase()}` : name.toLowerCase(); const event = new CustomEvent(eventName, { detail: args, bubbles: name !== 'hashChange', cancelable: true, }); this.dispatchEvent(event); }, }); } connectedCallback() { if ( this.swiper && this.swiper.initialized && this.nested && this.closest('swiper-slide') && this.closest('swiper-slide').swiperLoopMoveDOM ) { return; } if (this.init === false || this.getAttribute('init') === 'false') { return; } this.initialize(); } disconnectedCallback() { if ( this.nested && this.closest('swiper-slide') && this.closest('swiper-slide').swiperLoopMoveDOM ) { return; } if (this.swiper && this.swiper.destroy) { this.swiper.destroy(); } } updateSwiperOnPropChange(propName, propValue) { const { params: swiperParams, passedParams } = getParams(this, propName, propValue); this.passedParams = passedParams; this.swiperParams = swiperParams; if (this.swiper && this.swiper.params[propName] === propValue) { return; } updateSwiper({ swiper: this.swiper, passedParams: this.passedParams, changedParams: [attrToProp(propName)], ...(propName === 'navigation' && passedParams[propName] ? { prevEl: '.swiper-button-prev', nextEl: '.swiper-button-next', } : {}), ...(propName === 'pagination' && passedParams[propName] ? { paginationEl: '.swiper-pagination', } : {}), ...(propName === 'scrollbar' && passedParams[propName] ? { scrollbarEl: '.swiper-scrollbar', } : {}), }); } attributeChangedCallback(attr, prevValue, newValue) { if (!(this.swiper && this.swiper.initialized)) return; if (prevValue === 'true' && newValue === null) { newValue = false; } this.updateSwiperOnPropChange(attr, newValue); } static get observedAttributes() { const attrs = paramsList .filter((param) => param.includes('_')) .map((param) => param .replace(/[A-Z]/g, (v) => `-${v}`) .replace('_', '') .toLowerCase(), ); return attrs; } } paramsList.forEach((paramName) => { if (paramName === 'init') return; paramName = paramName.replace('_', ''); Object.defineProperty(SwiperContainer.prototype, paramName, { configurable: true, get() { return (this.passedParams || {})[paramName]; }, set(value) { if (!this.passedParams) this.passedParams = {}; this.passedParams[paramName] = value; if (!(this.swiper && this.swiper.initialized)) return; this.updateSwiperOnPropChange(paramName, value); }, }); }); class SwiperSlide extends ClassToExtend { constructor() { super(); this.attachShadow({ mode: 'open' }); } render() { const lazy = this.lazy || this.getAttribute('lazy') === '' || this.getAttribute('lazy') === 'true'; addStyle(this.shadowRoot, SwiperSlideCSS); this.shadowRoot.appendChild(document.createElement('slot')); if (lazy) { const lazyDiv = document.createElement('div'); lazyDiv.classList.add('swiper-lazy-preloader'); lazyDiv.part.add('preloader'); this.shadowRoot.appendChild(lazyDiv); } } initialize() { this.render(); } connectedCallback() { if (this.swiperLoopMoveDOM) { return; } this.initialize(); } } // eslint-disable-next-line const register = () => { if (typeof window === 'undefined') return; if (!window.customElements.get('swiper-container')) window.customElements.define('swiper-container', SwiperContainer); if (!window.customElements.get('swiper-slide')) window.customElements.define('swiper-slide', SwiperSlide); }; if (typeof window !== 'undefined') { window.SwiperElementRegisterParams = (params) => { paramsList.push(...params); }; } //BROWSER_REGISTER export { SwiperContainer, SwiperSlide, register }; ================================================ FILE: src/swiper-react.d.ts ================================================ import * as React from 'react'; import type { SwiperOptions, Swiper as SwiperClass } from './types/index.d.ts'; type SwiperProps = Omit< React.HTMLAttributes, | 'onProgress' | 'onClick' | 'onTouchEnd' | 'onTouchMove' | 'onTouchStart' | 'onTransitionEnd' | 'onKeyPress' | 'onDoubleClick' | 'onScroll' | 'onResize' > & SwiperOptions & { /** * Swiper container tag * * @default 'div' */ tag?: string; /** * Swiper wrapper tag * * @default 'div' */ wrapperTag?: string; /** * Get Swiper instance */ onSwiper?: (swiper: SwiperClass) => void; // CORE_EVENTS // MODULES_EVENTS }; interface SlideData { isActive: boolean; isVisible: boolean; isPrev: boolean; isNext: boolean; } type SwiperSlideProps = Omit, 'children'> & { /** * Slide tag * * @default 'div' */ tag?: string; /** * Enables additional wrapper required for zoom mode * * @default false */ zoom?: boolean; /** * Adds lazy preloader to the slide * * @default false */ lazy?: boolean; /** * Slide's index in slides array/collection * * @default false */ virtualIndex?: number; /** * Slide's child element or render function * * @default undefined */ children?: React.ReactNode | ((slideData: SlideData) => React.ReactNode); }; interface SwiperRef extends React.HTMLAttributes { swiper: SwiperClass; } declare const Swiper: React.FunctionComponent< React.RefAttributes & React.PropsWithChildren >; declare const SwiperSlide: React.FunctionComponent; declare const useSwiper: () => SwiperClass; declare const useSwiperSlide: () => SlideData; export { Swiper, SwiperSlide, SwiperProps, SwiperSlideProps, SwiperRef, useSwiper, useSwiperSlide, SwiperClass, }; ================================================ FILE: src/swiper-react.mjs ================================================ import { Swiper } from './react/swiper.mjs'; import { SwiperSlide } from './react/swiper-slide.mjs'; export { useSwiperSlide, useSwiper } from './react/context.mjs'; export { Swiper, SwiperSlide }; ================================================ FILE: src/swiper-vars.less ================================================ @themeColor: #007aff; ================================================ FILE: src/swiper-vue.d.ts ================================================ import type { A11yOptions, AutoplayOptions, ControllerOptions, CoverflowEffectOptions, CubeEffectOptions, FadeEffectOptions, FlipEffectOptions, CreativeEffectOptions, CardsEffectOptions, HashNavigationOptions, HistoryOptions, KeyboardOptions, MousewheelOptions, NavigationOptions, PaginationOptions, ParallaxOptions, ScrollbarOptions, ThumbsOptions, VirtualOptions, ZoomOptions, FreeModeOptions, GridOptions, } from './types/index.d.ts'; import { ComponentOptionsMixin, DefineComponent, PropType, Ref } from 'vue'; import type { SwiperOptions, Swiper as SwiperClass } from './types/index.d.ts'; declare const Swiper: DefineComponent< { tag: { type: StringConstructor; default: string; }; wrapperTag: { type: StringConstructor; default: string; }; modules: { type: ArrayConstructor; default: undefined; }; init: { type: BooleanConstructor; default: undefined; }; direction: { type: PropType; default: SwiperOptions['direction']; }; oneWayMovement: { type: PropType; default: SwiperOptions['oneWayMovement']; }; swiperElementNodeName: { type: PropType; default: SwiperOptions['swiperElementNodeName']; }; touchEventsTarget: { type: PropType; default: undefined; }; initialSlide: { type: NumberConstructor; default: undefined; }; speed: { type: NumberConstructor; default: undefined }; cssMode: { type: BooleanConstructor; default: undefined }; updateOnWindowResize: { type: BooleanConstructor; default: undefined; }; resizeObserver: { type: BooleanConstructor; default: undefined; }; nested: { type: BooleanConstructor; default: undefined }; focusableElements: { type: StringConstructor; default: undefined; }; width: { type: NumberConstructor; default: undefined }; height: { type: NumberConstructor; default: undefined }; preventInteractionOnTransition: { type: BooleanConstructor; default: undefined; }; userAgent: { type: StringConstructor; default: undefined }; url: { type: StringConstructor; default: undefined }; edgeSwipeDetection: { type: BooleanConstructor | StringConstructor; default: undefined; }; edgeSwipeThreshold: { type: NumberConstructor; default: undefined; }; autoHeight: { type: BooleanConstructor; default: undefined; }; setWrapperSize: { type: BooleanConstructor; default: undefined; }; virtualTranslate: { type: BooleanConstructor; default: undefined; }; effect: { type: PropType; default: undefined; }; breakpoints: { type: PropType; default: undefined; }; spaceBetween: { type: PropType; default: undefined; }; slidesPerView: { type: PropType; default: undefined; }; maxBackfaceHiddenSlides: { type: NumberConstructor; default: undefined; }; slidesPerGroup: { type: NumberConstructor; default: undefined; }; slidesPerGroupSkip: { type: NumberConstructor; default: undefined; }; slidesPerGroupAuto: { type: BooleanConstructor; default: undefined; }; centeredSlides: { type: BooleanConstructor; default: undefined; }; centeredSlidesBounds: { type: BooleanConstructor; default: undefined; }; slidesOffsetBefore: { type: NumberConstructor; default: undefined; }; slidesOffsetAfter: { type: NumberConstructor; default: undefined; }; normalizeSlideIndex: { type: BooleanConstructor; default: undefined; }; centerInsufficientSlides: { type: BooleanConstructor; default: undefined; }; snapToSlideEdge: { type: BooleanConstructor; default: undefined; }; watchOverflow: { type: BooleanConstructor; default: undefined; }; roundLengths: { type: BooleanConstructor; default: undefined; }; touchRatio: { type: NumberConstructor; default: undefined; }; touchAngle: { type: NumberConstructor; default: undefined; }; simulateTouch: { type: BooleanConstructor; default: undefined; }; shortSwipes: { type: BooleanConstructor; default: undefined; }; longSwipes: { type: BooleanConstructor; default: undefined; }; longSwipesRatio: { type: NumberConstructor; default: undefined; }; longSwipesMs: { type: NumberConstructor; default: undefined; }; followFinger: { type: BooleanConstructor; default: undefined; }; allowTouchMove: { type: BooleanConstructor; default: undefined; }; threshold: { type: NumberConstructor; default: undefined }; touchMoveStopPropagation: { type: BooleanConstructor; default: undefined; }; touchStartPreventDefault: { type: BooleanConstructor; default: undefined; }; touchStartForcePreventDefault: { type: BooleanConstructor; default: undefined; }; touchReleaseOnEdges: { type: BooleanConstructor; default: undefined; }; uniqueNavElements: { type: BooleanConstructor; default: undefined; }; resistance: { type: BooleanConstructor; default: undefined; }; resistanceRatio: { type: NumberConstructor; default: undefined; }; watchSlidesProgress: { type: BooleanConstructor; default: undefined; }; grabCursor: { type: BooleanConstructor; default: undefined; }; preventClicks: { type: BooleanConstructor; default: undefined; }; preventClicksPropagation: { type: BooleanConstructor; default: undefined; }; slideToClickedSlide: { type: BooleanConstructor; default: undefined; }; loop: { type: BooleanConstructor; default: undefined }; loopAddBlankSlides: { type: BooleanConstructor; default: undefined }; loopAdditionalSlides: { type: NumberConstructor; default: undefined; }; loopPreventsSliding: { type: BooleanConstructor; default: undefined }; rewind: { type: BooleanConstructor; default: undefined }; allowSlidePrev: { type: BooleanConstructor; default: undefined; }; allowSlideNext: { type: BooleanConstructor; default: undefined; }; swipeHandler: { type: BooleanConstructor; default: undefined; }; noSwiping: { type: BooleanConstructor; default: undefined; }; noSwipingClass: { type: StringConstructor; default: undefined; }; noSwipingSelector: { type: StringConstructor; default: undefined; }; passiveListeners: { type: BooleanConstructor; default: undefined; }; containerModifierClass: { type: StringConstructor; default: undefined; }; slideClass: { type: StringConstructor; default: undefined; }; slideActiveClass: { type: StringConstructor; default: undefined; }; slideVisibleClass: { type: StringConstructor; default: undefined; }; slideFullyVisibleClass: { type: StringConstructor; default: undefined; }; slideBlankClass: { type: StringConstructor; default: undefined; }; slideNextClass: { type: StringConstructor; default: undefined; }; slidePrevClass: { type: StringConstructor; default: undefined; }; wrapperClass: { type: StringConstructor; default: undefined; }; lazyPreloaderClass: { type: StringConstructor; default: undefined; }; lazyPreloadPrevNext: { type: NumberConstructor; default: undefined; }; runCallbacksOnInit: { type: BooleanConstructor; default: undefined; }; observer: { type: BooleanConstructor; default: undefined }; observeParents: { type: BooleanConstructor; default: undefined; }; observeSlideChildren: { type: BooleanConstructor; default: undefined; }; a11y: { type: PropType; default: undefined; }; autoplay: { type: PropType; default: undefined; }; controller: { type: PropType; default: undefined; }; coverflowEffect: { type: PropType; default: undefined; }; cubeEffect: { type: PropType; default: undefined; }; fadeEffect: { type: PropType; default: undefined; }; flipEffect: { type: PropType; default: undefined; }; creativeEffect: { type: PropType; default: undefined; }; cardsEffect: { type: PropType; default: undefined; }; hashNavigation: { type: PropType; default: undefined; }; history: { type: PropType; default: undefined; }; keyboard: { type: PropType; default: undefined; }; mousewheel: { type: PropType; default: undefined; }; navigation: { type: PropType; default: undefined; }; pagination: { type: PropType; default: undefined; }; parallax: { type: PropType; default: undefined; }; scrollbar: { type: PropType; default: undefined; }; thumbs: { type: PropType; default: undefined }; virtual: { type: PropType; default: undefined; }; zoom: { type: PropType; default: undefined; }; freeMode: { type: PropType; default: undefined; }; grid: { type: PropType; default: undefined; }; }, () => JSX.Element, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, { swiper: (swiper: SwiperClass) => void; // CORE_EVENTS // MODULES_EVENTS } >; declare const SwiperSlide: DefineComponent<{ tag: { type: StringConstructor; default: string; }; swiperRef: { type: PropType; required: false }; lazy: { type: BooleanConstructor; default: false }; zoom: { type: BooleanConstructor; default: undefined }; virtualIndex: { type: StringConstructor | NumberConstructor; default: undefined; }; }>; declare const useSwiper: () => Ref; declare const useSwiperSlide: () => Ref<{ isActive: boolean; isVisible: boolean; isPrev: boolean; isNext: boolean; }>; export { Swiper, SwiperSlide, useSwiper, useSwiperSlide }; ================================================ FILE: src/swiper-vue.mjs ================================================ import { Swiper } from './vue/swiper.mjs'; import { SwiperSlide } from './vue/swiper-slide.mjs'; export { useSwiperSlide, useSwiper } from './vue/context.mjs'; export { Swiper, SwiperSlide }; ================================================ FILE: src/swiper.css ================================================ :root { --swiper-theme-color: #007aff; /* --swiper-preloader-color: var(--swiper-theme-color); --swiper-wrapper-transition-timing-function: initial; */ } :host { position: relative; display: block; margin-left: auto; margin-right: auto; z-index: 1; } .swiper { margin-left: auto; margin-right: auto; position: relative; overflow: hidden; list-style: none; padding: 0; /* Fix of Webkit flickering */ z-index: 1; display: block; } .swiper-vertical > .swiper-wrapper { flex-direction: column; } .swiper-wrapper { position: relative; width: 100%; height: 100%; z-index: 1; display: flex; transition-property: transform; transition-timing-function: var(--swiper-wrapper-transition-timing-function, initial); box-sizing: content-box; } .swiper-android .swiper-slide, .swiper-ios .swiper-slide, .swiper-wrapper { transform: translate3d(0px, 0, 0); } .swiper-horizontal { touch-action: pan-y; } .swiper-vertical { touch-action: pan-x; } .swiper-slide { flex-shrink: 0; width: 100%; height: 100%; position: relative; transition-property: transform; display: block; } .swiper-slide-invisible-blank { visibility: hidden; } /* Auto Height */ .swiper-autoheight, .swiper-autoheight .swiper-slide { height: auto; } .swiper-autoheight .swiper-wrapper { align-items: flex-start; transition-property: transform, height; } .swiper-backface-hidden .swiper-slide { transform: translateZ(0); backface-visibility: hidden; } /* 3D Effects */ .swiper-3d.swiper-css-mode .swiper-wrapper { perspective: 1200px; } .swiper-3d .swiper-wrapper { transform-style: preserve-3d; } .swiper-3d { perspective: 1200px; .swiper-slide, .swiper-cube-shadow { transform-style: preserve-3d; } } /* CSS Mode */ .swiper-css-mode { > .swiper-wrapper { overflow: auto; scrollbar-width: none; /* For Firefox */ -ms-overflow-style: none; /* For Internet Explorer and Edge */ &::-webkit-scrollbar { display: none; } } > .swiper-wrapper > .swiper-slide { scroll-snap-align: start start; } &.swiper-horizontal { > .swiper-wrapper { scroll-snap-type: x mandatory; } > .swiper-wrapper > .swiper-slide:first-child { margin-inline-start: var(--swiper-slides-offset-before); scroll-margin-inline-start: var(--swiper-slides-offset-before); } > .swiper-wrapper > .swiper-slide:last-child { margin-inline-end: var(--swiper-slides-offset-after); } } &.swiper-vertical { > .swiper-wrapper { scroll-snap-type: y mandatory; } > .swiper-wrapper > .swiper-slide:first-child { margin-block-start: var(--swiper-slides-offset-before); scroll-margin-block-start: var(--swiper-slides-offset-before); } > .swiper-wrapper > .swiper-slide:last-child { margin-block-end: var(--swiper-slides-offset-after); } } &.swiper-free-mode { > .swiper-wrapper { scroll-snap-type: none; } > .swiper-wrapper > .swiper-slide { scroll-snap-align: none; } } &.swiper-centered { > .swiper-wrapper::before { content: ''; flex-shrink: 0; order: 9999; } > .swiper-wrapper > .swiper-slide { scroll-snap-align: center center; scroll-snap-stop: always; } } &.swiper-centered.swiper-horizontal { > .swiper-wrapper > .swiper-slide:first-child { margin-inline-start: var(--swiper-centered-offset-before); } > .swiper-wrapper::before { height: 100%; min-height: 1px; width: var(--swiper-centered-offset-after); } } &.swiper-centered.swiper-vertical { > .swiper-wrapper > .swiper-slide:first-child { margin-block-start: var(--swiper-centered-offset-before); } > .swiper-wrapper::before { width: 100%; min-width: 1px; height: var(--swiper-centered-offset-after); } } } /* Slide styles start */ /* 3D Shadows */ .swiper-3d { .swiper-slide-shadow, .swiper-slide-shadow-left, .swiper-slide-shadow-right, .swiper-slide-shadow-top, .swiper-slide-shadow-bottom, .swiper-slide-shadow, .swiper-slide-shadow-left, .swiper-slide-shadow-right, .swiper-slide-shadow-top, .swiper-slide-shadow-bottom { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; z-index: 10; } .swiper-slide-shadow { background: rgba(0, 0, 0, 0.15); } .swiper-slide-shadow-left { background-image: linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); } .swiper-slide-shadow-right { background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); } .swiper-slide-shadow-top { background-image: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); } .swiper-slide-shadow-bottom { background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0)); } } .swiper-lazy-preloader { width: 42px; height: 42px; position: absolute; left: 50%; top: 50%; margin-left: -21px; margin-top: -21px; z-index: 10; transform-origin: 50%; box-sizing: border-box; border: 4px solid var(--swiper-preloader-color, var(--swiper-theme-color)); border-radius: 50%; border-top-color: transparent; } .swiper:not(.swiper-watch-progress), .swiper-watch-progress .swiper-slide-visible { .swiper-lazy-preloader { animation: swiper-preloader-spin 1s infinite linear; } } .swiper-lazy-preloader-white { --swiper-preloader-color: #fff; } .swiper-lazy-preloader-black { --swiper-preloader-color: #000; } @keyframes swiper-preloader-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* Slide styles end */ ================================================ FILE: src/swiper.d.ts ================================================ // @ts-ignore import Swiper from './types/swiper-class.d.ts'; export default Swiper; export { Swiper }; ================================================ FILE: src/swiper.mjs ================================================ export { default as Swiper, default } from './core/core.mjs'; ================================================ FILE: src/types/index.d.ts ================================================ // @ts-nocheck export * from './shared.d.ts'; export { default as Swiper } from './swiper-class.d.ts'; export * from './swiper-events.d.ts'; export * from './swiper-options.d.ts'; export * from './modules/public-api.d.ts'; ================================================ FILE: src/types/modules/a11y.d.ts ================================================ export interface A11yMethods {} export interface A11yEvents {} export interface A11yOptions { /** * Enables A11y * * @default true */ enabled?: boolean; /** * Message for screen readers for previous button * * @default 'Previous slide' */ prevSlideMessage?: string; /** * Message for screen readers for next button * * @default 'Next slide' */ nextSlideMessage?: string; /** * Message for screen readers for previous button when swiper is on first slide * * @default 'This is the first slide' */ firstSlideMessage?: string; /** * Message for screen readers for next button when swiper is on last slide * * @default 'This is the last slide' */ lastSlideMessage?: string; /** * Message for screen readers for single pagination bullet * * @default 'Go to slide {{index}}' */ paginationBulletMessage?: string; /** * CSS class name of A11y notification * * @default 'swiper-notification' */ notificationClass?: string; /** * Message for screen readers for outer swiper container * * @default null */ containerMessage?: string | null; /** * Message for screen readers describing the role of outer swiper container * * @default null */ containerRoleDescriptionMessage?: string | null; /** * Value of the "role" attribute to be set on the swiper container * * @default null */ containerRole?: string | null; /** * Message for screen readers describing the role of slide element * * @default null */ itemRoleDescriptionMessage?: string | null; /** * Message for screen readers describing the label of slide element * * @default '{{index}} / {{slidesLength}}' */ slideLabelMessage?: string; /** * Value of swiper slide `role` attribute * * @default 'group' */ slideRole?: string; /** * Value of `id` attribute to be set on swiper-wrapper. If `null` will be generated automatically * * @default null */ id?: string | number | null; /** * Enables scrolling to the slide that has been focused * * @default true */ scrollOnFocus?: boolean; /** * Whether or not the swiper-wrapper should have the `aria-live` attribute applied to it. * If true, the value will be `off` when autoplay is enabled, otherwise it will be `polite` * * @default true */ wrapperLiveRegion?: boolean; } ================================================ FILE: src/types/modules/autoplay.d.ts ================================================ import type Swiper from '../swiper-class.d.ts'; export interface AutoplayMethods { /** * Whether autoplay enabled and running */ running: boolean; /** * Whether autoplay is paused */ paused: boolean; /** * If autoplay is paused, it contains time left (in ms) before transition to next slide */ timeLeft: number; /** * Pause autoplay */ pause(): void; /** * Resume autoplay */ resume(): void; /** * Start autoplay */ start(): boolean; /** * Stop autoplay */ stop(): boolean; } export interface AutoplayEvents { /** * Event will be fired in when autoplay started */ autoplayStart: (swiper: Swiper) => void; /** * Event will be fired when autoplay stopped */ autoplayStop: (swiper: Swiper) => void; /** * Event will be fired on autoplay pause */ autoplayPause: (swiper: Swiper) => void; /** * Event will be fired on autoplay resume */ autoplayResume: (swiper: Swiper) => void; /** * Event triggers continuously while autoplay is enabled. It contains time left (in ms) before transition to next slide and percentage of that time related to autoplay delay */ autoplayTimeLeft: (swiper: Swiper, timeLeft: number, percentage: number) => void; /** * Event will be fired when slide changed with autoplay */ autoplay: (swiper: Swiper) => void; } /** * Object with autoplay parameters or boolean `true` to enable with default settings. * * @example * ```js * const swiper = new Swiper('.swiper', { * autoplay: { * delay: 5000, * }, * }); * ``` */ export interface AutoplayOptions { /** * Delay between transitions (in ms). If this parameter is not specified, auto play will be disabled * * If you need to specify different delay for specific slides you can do it by using * `data-swiper-autoplay` (in ms) attribute on slide. * * @example * ```html * *
* ``` * * @default 3000 */ delay?: number; /** * Enable this parameter and autoplay will be stopped when it reaches last slide (has no effect in loop mode) * * @default false */ stopOnLastSlide?: boolean; /** * Set to `false` and autoplay will not be disabled after user interactions (swipes), * it will be restarted every time after interaction * * @default true */ disableOnInteraction?: boolean; /** * Enables autoplay in reverse direction * * @default false */ reverseDirection?: boolean; /** * When enabled autoplay will wait for wrapper transition to continue. * Can be disabled in case of using Virtual Translate when your * slider may not have transition * * @default true */ waitForTransition?: boolean; /** * When enabled autoplay will be paused on pointer (mouse) enter over Swiper container. * * @default false */ pauseOnMouseEnter?: boolean; } ================================================ FILE: src/types/modules/controller.d.ts ================================================ import type Swiper from '../swiper-class.d.ts'; export interface ControllerMethods { /** * Pass here another Swiper instance or array with Swiper instances that should be controlled * by this Swiper */ control?: Swiper | Swiper[]; } export interface ControllerEvents {} export interface ControllerOptions { /** * Pass here another Swiper instance or array with Swiper instances that should be controlled * by this Swiper. Also accepts string with CSS selector of Swiper element, or HTMLElement of Swiper element */ control?: Swiper | Swiper[] | string | HTMLElement | null; /** * Set to `true` and controlling will be in inverse direction * * @default false */ inverse?: boolean; /** * Defines a way how to control another slider: slide by slide * (with respect to other slider's grid) or depending on all slides/container * (depending on total slider percentage). * * @default 'slide' */ by?: 'slide' | 'container'; } ================================================ FILE: src/types/modules/effect-cards.d.ts ================================================ export interface CardsEffectMethods {} export interface CardsEffectEvents {} export interface CardsEffectOptions { /** * Enables slides shadows * * @default true */ slideShadows?: boolean; /** * Enables cards rotation * * @default true */ rotate?: boolean; /** * Rotate angle per slide (in degrees) * * @default 2 */ perSlideRotate?: number; /** * Offset distance per slide (in px) * * @default 8 */ perSlideOffset?: number; } ================================================ FILE: src/types/modules/effect-coverflow.d.ts ================================================ export interface CoverflowEffectMethods {} export interface CoverflowEffectEvents {} export interface CoverflowEffectOptions { /** * Enables slides shadows * * @default true */ slideShadows?: boolean; /** * Slide rotate in degrees * * @default 50 */ rotate?: number; /** * Stretch space between slides * * - a number is interpreted as pixels (e.g., `20` for 20px). * - a string with a percentage (e.g., `"50%"`). * * @default 0 */ stretch?: number | `${number}%`; /** * Depth offset in px (slides translate in Z axis) * * @default 100 */ depth?: number; /** * Slide scale effect * * @default 1 */ scale?: number; /** * Effect multiplier * * @default 1 */ modifier?: number; } ================================================ FILE: src/types/modules/effect-creative.d.ts ================================================ interface CreativeEffectTransform { translate?: (string | number)[]; rotate?: number[]; opacity?: number; scale?: number; shadow?: boolean; origin?: string; } export interface CreativeEffectMethods {} export interface CreativeEffectEvents {} export interface CreativeEffectOptions { /** * Previous slide transformations. Accepts object of the following type: * * @example * ```js * { * // Array with translate X, Y and Z values * translate: (string | number)[]; * // Array with rotate X, Y and Z values (in deg) * rotate?: number[]; * // Slide opacity * opacity?: number; * // Slide scale * scale?: number; * // Enables slide shadow * shadow?: boolean; * // Transform origin, e.g. `left bottom` * origin?: string; * } * ``` * */ prev?: CreativeEffectTransform; /** * Next slide transformations. * * @example * ```js * { * // Array with translate X, Y and Z values * translate: (string | number)[]; * // Array with rotate X, Y and Z values (in deg) * rotate?: number[]; * // Slide opacity * opacity?: number; * // Slide scale * scale?: number; * // Enables slide shadow * shadow?: boolean; * // Transform origin, e.g. `left bottom` * origin?: string; * } * ``` * */ next?: CreativeEffectTransform; /** * Limit progress/offset to amount of side slides. If `1`, then slides all slides after prev/next will have same state. If `2`, then all slides after 2nd before/after active will have same state, etc. * * @default 1 */ limitProgress?: number; /** * Splits shadow "opacity" per slide based on `limitProgress` (only if transformation shadows enabled). E.g. setting `limitProgress: 2` and enabling `shadowPerProgress`, will set shadow opacity to `0.5` and `1` on two slides next to active. With this parameter disabled, all slides beside active will have shadow with `1` opacity * * @default false */ shadowPerProgress?: boolean; /** * Allows to multiply slides transformations and opacity. * * @default 1 */ progressMultiplier?: number; /** * Enable this parameter if your custom transforms require 3D transformations (`translateZ`, `rotateX`, `rotateY` ) * * @default true */ perspective?: boolean; } ================================================ FILE: src/types/modules/effect-cube.d.ts ================================================ export interface CubeEffectMethods {} export interface CubeEffectEvents {} export interface CubeEffectOptions { /** * Enables slides shadows * * @default true */ slideShadows?: boolean; /** * Enables main slider shadow * * @default true */ shadow?: boolean; /** * Main shadow offset in px * * @default 20 */ shadowOffset?: number; /** * Main shadow scale ratio * * @default 0.94 */ shadowScale?: number; } ================================================ FILE: src/types/modules/effect-fade.d.ts ================================================ export interface FadeEffectMethods {} export interface FadeEffectEvents {} export interface FadeEffectOptions { /** * Enables slides cross fade * * @default false */ crossFade?: boolean; } ================================================ FILE: src/types/modules/effect-flip.d.ts ================================================ export interface FlipEffectMethods {} export interface FlipEffectEvents {} export interface FlipEffectOptions { /** * Enables slides shadows * * @default true */ slideShadows?: boolean; /** * Limit edge slides rotation * * @default true */ limitRotation?: boolean; } ================================================ FILE: src/types/modules/free-mode.d.ts ================================================ export interface FreeModeMethods { onTouchMove(): void; onTouchEnd(): void; } export interface FreeModeEvents {} export interface FreeModeOptions { /** * Whether the free mode is enabled * * @default false */ enabled?: boolean; /** * If enabled, then slide will keep moving for a while after you release it * * @default true */ momentum?: boolean; /** * Higher value produces larger momentum distance after you release slider * * @default 1 */ momentumRatio?: number; /** * Higher value produces larger momentum velocity after you release slider * * @default 1 */ momentumVelocityRatio?: number; /** * Set to `false` if you want to disable momentum bounce in free mode * * @default true */ momentumBounce?: boolean; /** * Higher value produces larger momentum bounce effect * * @default 1 */ momentumBounceRatio?: number; /** * Minimum touchmove-velocity required to trigger free mode momentum * * @default 0.02 */ minimumVelocity?: number; /** * Set to enabled to enable snap to slides positions in free mode * * @default false */ sticky?: boolean; } ================================================ FILE: src/types/modules/grid.d.ts ================================================ export interface GridMethods {} export interface GridEvents {} export interface GridOptions { /** * Number of slides rows, for multirow layout * * @default 1 */ rows?: number; /** * Can be `'column'` or `'row'`. Defines how slides should fill rows, by column or by row * * @note if used with loop mode make sure number of slides is even specified in loop mode requirements, or enable `loopAddBlankSlides` parameter * * @default 'column' */ fill?: 'row' | 'column'; } ================================================ FILE: src/types/modules/hash-navigation.d.ts ================================================ import type Swiper from '../swiper-class.d.ts'; export interface HashNavigationMethods {} export interface HashNavigationEvents { /** * Event will be fired on window hash change */ hashChange: (swiper: Swiper) => void; /** * Event will be fired when swiper updates the hash */ hashSet: (swiper: Swiper) => void; } export interface HashNavigationOptions { /** * Set to `true` to enable also navigation through slides (when hashnav * is enabled) by browser history or by setting directly hash on document location * * @default false */ watchState?: boolean; /** * Works in addition to hashnav to replace current url state with the * new one instead of adding it to history * * @default false */ replaceState?: boolean; /** * Designed to be used with Virtual slides when it is impossible to find slide in DOM by hash (e.g. not yet rendered) * */ getSlideIndex?: (swiper: Swiper, hash: string) => number; } ================================================ FILE: src/types/modules/history.d.ts ================================================ export interface HistoryMethods {} export interface HistoryEvents {} export interface HistoryOptions { /** * Enables History Plugin. * * @default false */ enabled?: boolean; /** * Swiper page root, useful to specify when you use Swiper history mode not on root website page. * For example can be `https://my-website.com/` or `https://my-website.com/subpage/` or `/subpage/` * * * @default '' */ root?: string; /** * Works in addition to hashnav or history to replace current url state with the * new one instead of adding it to history * * @default false */ replaceState?: boolean; /** * Url key for slides * * @default 'slides' */ key?: string; /** * Keep query parameters when changing browser url. * * @default false */ keepQuery?: boolean; } ================================================ FILE: src/types/modules/index.d.ts ================================================ import type { SwiperModule } from '../shared.d.ts'; declare const A11y: SwiperModule; declare const Autoplay: SwiperModule; declare const Controller: SwiperModule; declare const EffectCoverflow: SwiperModule; declare const EffectCube: SwiperModule; declare const EffectFade: SwiperModule; declare const EffectFlip: SwiperModule; declare const EffectCreative: SwiperModule; declare const EffectCards: SwiperModule; declare const HashNavigation: SwiperModule; declare const History: SwiperModule; declare const Keyboard: SwiperModule; declare const Mousewheel: SwiperModule; declare const Navigation: SwiperModule; declare const Pagination: SwiperModule; declare const Parallax: SwiperModule; declare const Scrollbar: SwiperModule; declare const Thumbs: SwiperModule; declare const Virtual: SwiperModule; declare const Zoom: SwiperModule; declare const FreeMode: SwiperModule; declare const Grid: SwiperModule; declare const Manipulation: SwiperModule; export { A11y, Autoplay, Controller, EffectCoverflow, EffectCube, EffectFade, EffectFlip, EffectCreative, EffectCards, HashNavigation, History, Keyboard, Mousewheel, Navigation, Pagination, Parallax, Scrollbar, Thumbs, Virtual, Zoom, FreeMode, Grid, Manipulation, }; ================================================ FILE: src/types/modules/keyboard.d.ts ================================================ import type Swiper from '../swiper-class.d.ts'; export interface KeyboardMethods { /** * Whether the keyboard control is enabled */ enabled: boolean; /** * Enable keyboard control */ enable(): void; /** * Disable keyboard control */ disable(): void; } export interface KeyboardEvents { /** * Event will be fired on key press */ keyPress: (swiper: Swiper, keyCode: string) => void; } export interface KeyboardOptions { /** * Set to `true` to enable keyboard control * * @default false */ enabled?: boolean; /** * When enabled it will control sliders that are currently in viewport * * @default true */ onlyInViewport?: boolean; /** * When enabled it will enable keyboard navigation by Page Up and Page Down keys * * @default true */ pageUpDown?: boolean; /** * Set the speed of keyboard navigation transitions (in ms) * * @default undefined */ speed?: number; } ================================================ FILE: src/types/modules/manipulation.d.ts ================================================ export interface ManipulationMethods { /** * Add new slides to the end. slides could be * HTMLElement or HTML string with new slide or * array with such slides, for example: * * @example * ```js * appendSlide('
Slide 10"
') * * appendSlide([ * '
Slide 10"
', * '
Slide 11"
' * ]); * ``` */ appendSlide(slides: HTMLElement | string | string[] | HTMLElement[]): void; /** * Add new slides to the beginning. slides could be * HTMLElement or HTML string with new slide or array with such slides, for example: * * @example * ```js * prependSlide('
Slide 0"
') * * prependSlide([ * '
Slide 1"
', * '
Slide 2"
' * ]); * ``` */ prependSlide(slides: HTMLElement | string | string[] | HTMLElement[]): void; /** * Add new slides to the required index. slides could be HTMLElement or HTML string with new slide or array with such slides, for example: * * @example * ```js * addSlide(1, '
Slide 10"
') * * addSlide(1, [ * '
Slide 10"
', * '
Slide 11"
' * ]); * ``` */ addSlide(index: number, slides: HTMLElement | string | string[] | HTMLElement[]): void; /** * Remove selected slides. slideIndex could be a number with slide index to remove or array with indexes. * * @example * ```js * removeSlide(0); // remove first slide * removeSlide([0, 1]); // remove first and second slides * removeAllSlides(); // Remove all slides * ``` */ removeSlide(slideIndex: number | number[]): void; /** * Remove all slides */ removeAllSlides(): void; } export interface ManipulationEvents {} export interface ManipulationOptions {} ================================================ FILE: src/types/modules/mousewheel.d.ts ================================================ import type Swiper from '../swiper-class.d.ts'; import type { CSSSelector } from '../shared.d.ts'; export interface MousewheelMethods { /** * Whether the mousewheel control is enabled */ enabled: boolean; /** * Enable mousewheel control */ enable(): void; /** * Disable mousewheel control */ disable(): void; } export interface MousewheelEvents { /** * Event will be fired on mousewheel scroll */ scroll: (swiper: Swiper, event: WheelEvent) => void; } export interface MousewheelOptions { /** * Set to `true` to enable mousewheel control * * @default false */ enabled?: boolean; /** * Set to `true` to force mousewheel swipes to axis. So in horizontal mode mousewheel will work only with horizontal mousewheel scrolling, and only with vertical scrolling in vertical mode. * * @default false */ forceToAxis?: boolean; /** * Set to `true` and swiper will release mousewheel event and allow page scrolling when swiper is on edge positions (in the beginning or in the end) * * @default false */ releaseOnEdges?: boolean; /** * Set to `true` to invert sliding direction * * @default false */ invert?: boolean; /** * Multiplier of mousewheel data, allows to tweak mouse wheel sensitivity * * @default 1 */ sensitivity?: number; /** * String with CSS selector or HTML element of the container accepting mousewheel events. By default it is swiper * * @default 'container' */ eventsTarget?: 'container' | 'wrapper' | CSSSelector | HTMLElement; /** * Minimum mousewheel scroll delta to trigger swiper slide change * * @default null */ thresholdDelta?: number | null; /** * Minimum mousewheel scroll time delta (in ms) to trigger swiper slide change * * @default null */ thresholdTime?: number | null; /** * Scrolling on elements with this class will be ignored * * @default 'swiper-no-mousewheel' */ noMousewheelClass?: string; } ================================================ FILE: src/types/modules/navigation.d.ts ================================================ import type { CSSSelector } from '../shared.d.ts'; import type Swiper from '../swiper-class.d.ts'; export interface NavigationMethods { /** * HTMLElement of "next" navigation button */ nextEl: HTMLElement; /** * HTMLElement of "previous" navigation button */ prevEl: HTMLElement; /** * Update navigation buttons state (enabled/disabled) */ update(): void; /** * Initialize navigation */ init(): void; /** * Destroy navigation */ destroy(): void; } export interface NavigationEvents { /** * Event will be fired on navigation hide */ navigationHide: (swiper: Swiper) => void; /** * Event will be fired on navigation show */ navigationShow: (swiper: Swiper) => void; /** * Event will be fired on navigation prev button click */ navigationPrev: (swiper: Swiper) => void; /** * Event will be fired on navigation next button click */ navigationNext: (swiper: Swiper) => void; } export interface NavigationOptions { /** * Boolean property to use with breakpoints to enable/disable navigation on certain breakpoints */ enabled?: boolean; /** * String with CSS selector or HTML element of the element that will work * like "next" button after click on it * * @default null */ nextEl?: CSSSelector | HTMLElement | null; /** * String with CSS selector or HTML element of the element that will work * like "prev" button after click on it * * @default null */ prevEl?: CSSSelector | HTMLElement | null; /** * Boolean property to add SVG icons to navigation buttons * * @default true */ addIcons?: boolean; /** * Toggle navigation buttons visibility after click on Slider's container * * @default false */ hideOnClick?: boolean; /** * CSS class name added to navigation button when it becomes disabled * * @default 'swiper-button-disabled' */ disabledClass?: string; /** * CSS class name added to navigation button when it becomes hidden * * @default 'swiper-button-hidden' */ hiddenClass?: string; /** * CSS class name added to navigation button when it is disabled * * @default 'swiper-button-lock' */ lockClass?: string; /** * CSS class name added on swiper container when navigation is disabled by breakpoint * * @default 'swiper-navigation-disabled' */ navigationDisabledClass?: string; } ================================================ FILE: src/types/modules/pagination.d.ts ================================================ import type { CSSSelector } from '../shared.d.ts'; import type Swiper from '../swiper-class.d.ts'; export interface PaginationMethods { /** * HTMLElement of pagination container element */ el: HTMLElement; /** * Array of pagination bullets * HTML elements. To get specific slide HTMLElement * use `swiper.pagination.bullets[1]`. */ bullets: HTMLElement[]; /** * Render pagination layout */ render(): void; /** * Update pagination state (enabled/disabled/active) */ update(): void; /** * Initialize pagination */ init(): void; /** * Destroy pagination */ destroy(): void; } export interface PaginationEvents { /** * Event will be fired after pagination rendered */ paginationRender: (swiper: Swiper, paginationEl: HTMLElement) => void; /** * Event will be fired when pagination updated */ paginationUpdate: (swiper: Swiper, paginationEl: HTMLElement) => void; /** * Event will be fired on pagination hide */ paginationHide: (swiper: Swiper) => void; /** * Event will be fired on pagination show */ paginationShow: (swiper: Swiper) => void; } export interface PaginationOptions { /** * Boolean property to use with breakpoints to enable/disable pagination on certain breakpoints */ enabled?: boolean; /** * String with CSS selector or HTML element of the container with pagination * * @default null */ el?: CSSSelector | HTMLElement | null; /** * String with type of pagination. Can be `'bullets'`, `'fraction'`, `'progressbar'` or `'custom'` * * @default 'bullets' */ type?: 'bullets' | 'fraction' | 'progressbar' | 'custom'; /** * Defines which HTML tag will be used to represent single pagination bullet. Only for `'bullets'` pagination type. * * @default 'span' */ bulletElement?: string; /** * Good to enable if you use bullets pagination with a lot of slides. So it will keep only few bullets visible at the same time. * * @default false */ dynamicBullets?: boolean; /** * The number of main bullets visible when `dynamicBullets` enabled. * * @default 1 */ dynamicMainBullets?: number; /** * Toggle (hide/show) pagination container visibility after click on Slider's container * * @default true */ hideOnClick?: boolean; /** * If `true` then clicking on pagination button will cause transition to appropriate slide. Only for bullets pagination type * * @default false */ clickable?: boolean; /** * Makes pagination progressbar opposite to Swiper's `direction` parameter, means vertical progressbar for horizontal swiper * direction and horizontal progressbar for vertical swiper direction * * @default false */ progressbarOpposite?: boolean; /** * format fraction pagination current number. Function receives current number, * and you need to return formatted value */ formatFractionCurrent?: (number: number) => number | string; /** * format fraction pagination total number. Function receives total number, and you * need to return formatted value */ formatFractionTotal?: (number: number) => number | string; /** * This parameter allows totally customize pagination bullets, you need to pass here a function that accepts `index` number of * pagination bullet and required element class name (`className`). Only for `'bullets'` pagination type * * @default null * * @example * ```js * const swiper = new Swiper('.swiper', { * //... * pagination: { * //... * renderBullet: function (index, className) { * return '' + (index + 1) + ''; * }, * }, * }); * ``` */ renderBullet?: (index: number, className: string) => string; /** * This parameter allows to customize "fraction" pagination html. Only for `'fraction'` pagination type * * @default null * * @example * ```js * const swiper = new Swiper('.swiper', { * //... * pagination: { * //... * renderFraction: function (currentClass, totalClass) { * return '' + * ' of ' + * ''; * }, * }, * }); * ``` */ renderFraction?: (currentClass: string, totalClass: string) => string; /** * This parameter allows to customize "progress" pagination. Only for `'progress'` pagination type * * @default null * * @example * ```js * const swiper = new Swiper('.swiper', { * //... * pagination: { * //... * renderProgressbar: function (progressbarFillClass) { * return ''; * }, * }, * }); * ``` */ renderProgressbar?: (progressbarFillClass: string) => string; /** * This parameter is required for `'custom'` pagination type where you have to specify * how it should be rendered. * * @default null * * @example * ```js * const swiper = new Swiper('.swiper', { * //... * pagination: { * //... * renderCustom: function (swiper, current, total) { * return current + ' of ' + total; * }, * }, * }); * ``` */ renderCustom?: (swiper: Swiper, current: number, total: number) => string; /** * CSS class name of single pagination bullet * * @default 'swiper-pagination-bullet' */ bulletClass?: string; /** * CSS class name of currently active pagination bullet * * @default 'swiper-pagination-bullet-active' */ bulletActiveClass?: string; /** * The beginning of the modifier CSS class name that will be added to pagination depending on parameters * * @default 'swiper-pagination-' */ modifierClass?: string; /** * CSS class name of the element with currently active index in "fraction" pagination * * @default 'swiper-pagination-current' */ currentClass?: string; /** * CSS class name of the element with total number of "snaps" in "fraction" pagination * * @default 'swiper-pagination-total' */ totalClass?: string; /** * CSS class name of pagination when it becomes inactive * * @default 'swiper-pagination-hidden' */ hiddenClass?: string; /** * CSS class name of pagination progressbar fill element * * @default 'swiper-pagination-progressbar-fill' */ progressbarFillClass?: string; /** * CSS class name of pagination progressbar opposite * * @default 'swiper-pagination-progressbar-opposite' */ progressbarOppositeClass?: string; /** * CSS class name set to pagination when it is clickable * * @default 'swiper-pagination-clickable' */ clickableClass?: string; /** * CSS class name set to pagination when it is disabled * * @default 'swiper-pagination-lock' */ lockClass?: string; /** * CSS class name set to pagination in horizontal Swiper * * @default 'swiper-pagination-horizontal' */ horizontalClass?: string; /** * CSS class name set to pagination in vertical Swiper * * @default 'swiper-pagination-vertical' */ verticalClass?: string; /** * CSS class name added on swiper container and pagination element when pagination is disabled by breakpoint * * @default 'swiper-pagination-disabled' */ paginationDisabledClass?: string; } ================================================ FILE: src/types/modules/parallax.d.ts ================================================ export interface ParallaxMethods {} export interface ParallaxEvents {} export interface ParallaxOptions { /** * Enable, if you want to use "parallaxed" elements inside of slider * * @default false */ enabled?: boolean; } ================================================ FILE: src/types/modules/public-api.d.ts ================================================ export type * from './a11y.d.ts'; export type * from './autoplay.d.ts'; export type * from './controller.d.ts'; export type * from './effect-coverflow.d.ts'; export type * from './effect-cube.d.ts'; export type * from './effect-fade.d.ts'; export type * from './effect-flip.d.ts'; export type * from './effect-creative.d.ts'; export type * from './effect-cards.d.ts'; export type * from './hash-navigation.d.ts'; export type * from './history.d.ts'; export type * from './keyboard.d.ts'; export type * from './mousewheel.d.ts'; export type * from './navigation.d.ts'; export type * from './pagination.d.ts'; export type * from './parallax.d.ts'; export type * from './scrollbar.d.ts'; export type * from './thumbs.d.ts'; export type * from './virtual.d.ts'; export type * from './zoom.d.ts'; export type * from './free-mode.d.ts'; export type * from './grid.d.ts'; export type * from './manipulation.d.ts'; ================================================ FILE: src/types/modules/scrollbar.d.ts ================================================ import type { CSSSelector } from '../shared.d.ts'; import type Swiper from '../swiper-class.d.ts'; export interface ScrollbarMethods { /** * HTMLElement of Scrollbar container element */ el: HTMLElement; /** * HTMLElement of Scrollbar draggable handler element */ dragEl: HTMLElement; /** * Updates scrollbar track and handler sizes */ updateSize(): void; /** * Updates scrollbar translate */ setTranslate(): void; /** * Initialize scrollbar */ init(): void; /** * Destroy scrollbar */ destroy(): void; } export interface ScrollbarEvents { /** * Event will be fired on draggable scrollbar drag start */ scrollbarDragStart: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; /** * Event will be fired on draggable scrollbar drag move */ scrollbarDragMove: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; /** * Event will be fired on draggable scrollbar drag end */ scrollbarDragEnd: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; } /** * Object with scrollbar parameters. * * @example * ```js * const swiper = new Swiper('.swiper', { * scrollbar: { * el: '.swiper-scrollbar', * draggable: true, * }, * }); * ``` */ export interface ScrollbarOptions { /** * Boolean property to use with breakpoints to enable/disable scrollbar on certain breakpoints */ enabled?: boolean; /** * String with CSS selector or HTML element of the container with scrollbar. * * @default null */ el?: CSSSelector | HTMLElement | null; /** * Hide scrollbar automatically after user interaction * * @default true */ hide?: boolean; /** * Set to `true` to enable make scrollbar draggable that allows you to control slider position * * @default false */ draggable?: boolean; /** * Set to `true` to snap slider position to slides when you release scrollbar * * @default false */ snapOnRelease?: boolean; /** * Size of scrollbar draggable element in px * * @default 'auto' */ dragSize?: 'auto' | number; /** * Scrollbar element additional CSS class when it is disabled * * @default 'swiper-scrollbar-lock' */ lockClass?: string; /** * Scrollbar draggable element CSS class * * @default 'swiper-scrollbar-drag' */ dragClass?: string; /** * CSS class name added on swiper container and scrollbar element when scrollbar is disabled by breakpoint * * @default 'swiper-scrollbar-disabled' */ scrollbarDisabledClass?: string; /** * CSS class name set to scrollbar in horizontal Swiper * * @default 'swiper-scrollbar-horizontal' */ horizontalClass?: string; /** * CSS class name set to scrollbar in vertical Swiper * * @default 'swiper-scrollbar-vertical' */ verticalClass?: string; } ================================================ FILE: src/types/modules/thumbs.d.ts ================================================ import type Swiper from '../swiper-class.d.ts'; export interface ThumbsMethods { /** * Swiper instance of thumbs swiper */ swiper: Swiper; /** * Update thumbs */ update(initial: boolean, p?: { autoScroll?: boolean }): void; /** * Initialize thumbs */ init(): boolean; } export interface ThumbsEvents {} export interface ThumbsOptions { /** * Swiper instance of swiper used as thumbs or object with Swiper parameters to initialize thumbs swiper * * @default null */ swiper?: Swiper | string | null; /** * Additional class that will be added to activated thumbs swiper slide * * @default 'swiper-slide-thumb-active' */ slideThumbActiveClass?: string; /** * Additional class that will be added to thumbs swiper * * @default 'swiper-thumbs' */ thumbsContainerClass?: string; /** * When enabled multiple thumbnail slides may get activated * * @default true */ multipleActiveThumbs?: boolean; /** * Allows to set on which thumbs active slide from edge it should automatically move scroll thumbs. For example, if set to 1 and last visible thumb will be activated (1 from edge) it will auto scroll thumbs * * @default 0 */ autoScrollOffset?: number; } ================================================ FILE: src/types/modules/virtual.d.ts ================================================ export interface VirtualMethods { /** * Object with cached slides HTML elements */ cache: object; /** * Index of first rendered slide */ from: number; /** * Index of last rendered slide */ to: number; /** * Array with slide items passed by `virtual.slides` parameter */ slides: T[]; /* * Methods */ /** * Append slide. `slides` can be a single slide item or array with such slides. * * @note Only for Core version (in React & Vue it should be done by modifying slides array/data/source) */ appendSlide(slide: HTMLElement | string | HTMLElement[] | string[]): void; /** * Prepend slide. `slides` can be a single slide item or array with such slides. * * @note Only for Core version (in React & Vue it should be done by modifying slides array/data/source) */ prependSlide(slide: HTMLElement | string | HTMLElement[] | string[]): void; /** * Remove specific slide or slides. `slideIndexes` can be a number with slide index to remove or array with indexes. * * @note Only for Core version (in React & Vue it should be done by modifying slides array/data/source) */ removeSlide(slideIndexes: number[]): void; /** * Remove all slides * * @note Only for Core version (in React & Vue it should be done by modifying slides array/data/source) */ removeAllSlides(): void; /** * Update virtual slides state */ update(force: boolean): void; } export interface VirtualEvents {} export interface VirtualData { /** * slides left/top offset in px */ offset: number; /** * index of first slide required to be rendered */ from: number; /** * index of last slide required to be rendered */ to: number; /** * array with slide items to be rendered */ slides: T[]; } export interface VirtualOptions { /** * Whether the virtual slides are enabled * * @default false */ enabled?: boolean; /** * Array with slides * * @default [] */ slides?: T[]; /** * Slide size for slidesPerView: `auto` (in px) * * @default 320 */ slidesPerViewAutoSlideSize?: number; /** * Enables DOM cache of rendering slides html elements. Once they are rendered they will be saved to cache and reused from it. * * @default true */ cache?: boolean; /** * Increases amount of pre-rendered slides before active slide * * @default 0 */ addSlidesBefore?: number; /** * Increases amount of pre-rendered slides after active slide * * @default 0 */ addSlidesAfter?: number; /** * Function to render slide. As an argument it accepts current slide item for `slides` array and index number of the current slide. Function must return an outer HTML of the swiper slide or slide HTML element. * * @default null */ renderSlide?: (slide: T, index: any) => any | null; /** * Function for external rendering (e.g. using some other library to handle DOM manipulations and state like React.js or Vue.js). As an argument it accepts `data` object with the following properties: * * - `offset` - slides left/top offset in px * - `from` - index of first slide required to be rendered * - `to` - index of last slide required to be rendered * - `slides` - array with slide items to be rendered * * @default null */ renderExternal?: (data: VirtualData) => any | null; /** * When enabled (by default) it will update Swiper layout right after renderExternal called. Useful to disable and update swiper manually when used with render libraries that renders asynchronously * * @default true */ renderExternalUpdate?: boolean; } ================================================ FILE: src/types/modules/zoom.d.ts ================================================ import type Swiper from '../swiper-class.d.ts'; export interface ZoomMethods { /** * Whether the zoom module is enabled */ enabled: boolean; /** * Current image scale ratio */ scale: number; /** * Enable zoom module */ enable(): void; /** * Disable zoom module */ disable(): void; /** * Zoom in image of the currently active slide. Optionally accepts custom zoom ratio */ in(ratio?: number): void; /** * Zoom out image of the currently active slide */ out(): void; /** * Toggle image zoom of the currently active slide */ toggle(event?: MouseEvent | TouchEvent | PointerEvent): void; } export interface ZoomEvents { /** * Event will be fired on zoom change */ zoomChange: (swiper: Swiper, scale: number, imageEl: HTMLElement, slideEl: HTMLElement) => void; } export interface ZoomOptions { /** * When set to true, the image will not be scaled past 100% of its original size * * @default false */ limitToOriginalSize?: boolean; /** * Maximum image zoom multiplier * * @default 3 */ maxRatio?: number; /** * Minimal image zoom multiplier * * @default 1 */ minRatio?: number; /** * When set to true, a zoomed in image will automatically pan while moving the mouse over the image * * @default false */ panOnMouseMove?: boolean; /** * Enable/disable zoom-in by slide's double tap * * @default true */ toggle?: boolean; /** * CSS class name of zoom container * * @default 'swiper-zoom-container' */ containerClass?: string; /** * CSS class name of zoomed in container * * @default 'swiper-slide-zoomed' */ zoomedSlideClass?: string; } ================================================ FILE: src/types/shared.d.ts ================================================ import type { Swiper } from './index.d.ts'; export interface CSSSelector extends String {} export type SwiperModule = (options: { params: Swiper['params']; swiper: Swiper; extendParams: (obj: { [name: string]: any }) => void; on: Swiper['on']; once: Swiper['once']; off: Swiper['off']; emit: Swiper['emit']; }) => void; ================================================ FILE: src/types/swiper-class.d.ts ================================================ import type { SwiperOptions } from './swiper-options.d.ts'; import type { CSSSelector, SwiperModule } from './shared.d.ts'; import type { SwiperEvents } from './swiper-events.d.ts'; import type { A11yMethods } from './modules/a11y.d.ts'; import type { AutoplayMethods } from './modules/autoplay.d.ts'; import type { ControllerMethods } from './modules/controller.d.ts'; import type { CoverflowEffectMethods } from './modules/effect-coverflow.d.ts'; import type { CubeEffectMethods } from './modules/effect-cube.d.ts'; import type { FadeEffectMethods } from './modules/effect-fade.d.ts'; import type { FlipEffectMethods } from './modules/effect-flip.d.ts'; import type { CreativeEffectMethods } from './modules/effect-creative.d.ts'; import type { CardsEffectMethods } from './modules/effect-cards.d.ts'; import type { HashNavigationMethods } from './modules/hash-navigation.d.ts'; import type { HistoryMethods } from './modules/history.d.ts'; import type { KeyboardMethods } from './modules/keyboard.d.ts'; import type { MousewheelMethods } from './modules/mousewheel.d.ts'; import type { NavigationMethods } from './modules/navigation.d.ts'; import type { PaginationMethods } from './modules/pagination.d.ts'; import type { ParallaxMethods } from './modules/parallax.d.ts'; import type { ScrollbarMethods } from './modules/scrollbar.d.ts'; import type { ThumbsMethods } from './modules/thumbs.d.ts'; import type { VirtualMethods } from './modules/virtual.d.ts'; import type { ZoomMethods } from './modules/zoom.d.ts'; import type { FreeModeMethods } from './modules/free-mode.d.ts'; import type { ManipulationMethods } from './modules/manipulation.d.ts'; interface SwiperClass { /** Add event handler */ on(event: E, handler: Events[E]): void; /** Add event handler that will be removed after it was fired */ once(event: E, handler: Events[E]): void; /** Remove event handler */ off(event: E, handler: Events[E]): void; /** Remove all handlers for specified event */ off(event: E): void; /** Fire event on instance */ emit(event: E, ...args: any[]): void; } interface Swiper extends SwiperClass { /** * Object with passed initialization parameters */ params: SwiperOptions; /** * Object with original initialization parameters */ originalParams: SwiperOptions; /** * Slider container HTML element */ el: HTMLElement; /** * Wrapper HTML element */ wrapperEl: HTMLElement; /** * Wrapper HTML element */ slidesEl: HTMLElement; /** * Array of slides HTML elements. To get specific slide HTMLElement use `swiper.slides[1]` */ slides: HTMLElement[]; /** * !INTERNAL */ loopedSlides: number | null; /** * Width of container */ width: number; /** * Height of container */ height: number; /** * Current value of wrapper translate */ translate: number; /** * Current progress of wrapper translate (from 0 to 1) */ progress: number; /** * Index number of currently active slide * * @note Note, that in loop mode active index value will be always shifted on a number of looped slides */ activeIndex: number; /** * Index number of currently active slide considering rearranged slides in loop mode */ realIndex: number; /** * Index number of previously active slide */ previousIndex: number; /** * Index number of current snap in `snapGrid` */ snapIndex: number; /** * Slides snap grid */ snapGrid: number[]; /** * Slides grid */ slidesGrid: number[]; /** * Array of widths for slides */ slidesSizesGrid: number[]; /** * `true` if slider on most "left"/"top" position */ isBeginning: boolean; /** * `true` if slider on most "right"/"bottom" position */ isEnd: boolean; /** * `true` if slide is "locked" (by `watchOverflow`) and slides can not be, e.g. when amount of slides is less that slides per view */ isLocked: boolean; /** * `true` if swiper is in transition */ animating: boolean; /** * Object with the following touch event properties: * * - `swiper.touches.startX` * - `swiper.touches.startY` * - `swiper.touches.currentX` * - `swiper.touches.currentY` * - `swiper.touches.diff` */ touches: { startX: number; startY: number; currentX: number; currentY: number; diff: number; }; /** * Index number of last clicked slide */ clickedIndex: number; /** * Link to last clicked slide (HTMLElement) */ clickedSlide: HTMLElement; /** * Disable / enable ability to slide to the next slides by assigning `false` / `true` to this property */ allowSlideNext: boolean; /** * Disable / enable ability to slide to the previous slides by assigning `false` / `true` to this property */ allowSlidePrev: boolean; /** * Disable / enable ability move slider by grabbing it with mouse or by touching it with finger (on touch screens) by assigning `false` / `true` to this property */ allowTouchMove: boolean; /** * Direction of sliding */ swipeDirection: 'prev' | 'next'; /** * !INTERNAL */ rtlTranslate: boolean; /** * `true` if Swiper is enabled, `false` otherwise */ enabled: boolean; /** * Disable Swiper (if it was enabled). When Swiper is disabled, it will hide all navigation elements and won't respond to any events and interactions * */ disable(): void; /** * Enable Swiper (if it was disabled) * */ enable(): void; /** * Set Swiper translate progress (from 0 to 1). Where 0 - its initial position (offset) on first slide, and 1 - its maximum position (offset) on last slide * * @param progress Swiper translate progress (from 0 to 1). * @param speed Transition duration (in ms). */ setProgress(progress: number, speed?: number): void; /** * Run transition to next slide. * * @param speed Transition duration (in ms). * @param runCallbacks Set it to false (by default it is true) and transition will * not produce transition events. */ slideNext(speed?: number, runCallbacks?: boolean): boolean; /** * Run transition to previous slide. * * @param speed Transition duration (in ms). * @param runCallbacks Set it to false (by default it is true) and transition will * not produce transition events. */ slidePrev(speed?: number, runCallbacks?: boolean): boolean; /** * Run transition to the slide with index number equal to 'index' parameter for the * duration equal to 'speed' parameter. * * @param index Index number of slide. * @param speed Transition duration (in ms). * @param runCallbacks Set it to false (by default it is true) and transition will * not produce transition events. */ slideTo(index: number, speed?: number, runCallbacks?: boolean): boolean; /** * Does the same as .slideTo but for the case when used with enabled loop. So this * method will slide to slides with realIndex matching to passed index * * @param index Index number of slide. * @param speed Transition duration (in ms). * @param runCallbacks Set it to false (by default it is true) and transition will * not produce transition events. */ slideToLoop(index: number, speed?: number, runCallbacks?: boolean): Swiper; /** * Reset swiper position to currently active slide for the duration equal to 'speed' * parameter. * * @param speed Transition duration (in ms). * @param runCallbacks Set it to false (by default it is true) and transition will * not produce transition events. */ slideReset(speed?: number, runCallbacks?: boolean): boolean; /** * Reset swiper position to closest slide/snap point for the duration equal to 'speed' parameter. * * @param speed Transition duration (in ms). * @param runCallbacks Set it to false (by default it is true) and transition will * not produce transition events. */ slideToClosest(speed?: number, runCallbacks?: boolean): boolean; /** * Get dynamically calculated amount of slides per view, useful only when slidesPerView set to `auto` * */ slidesPerViewDynamic(): number; /** * Force swiper to update its height (when autoHeight enabled) for the duration equal to * 'speed' parameter * * @param speed Transition duration (in ms). */ updateAutoHeight(speed?: number): void; /** * You should call it after you add/remove slides * manually, or after you hide/show it, or do any * custom DOM modifications with Swiper * This method also includes subcall of the following * methods which you can use separately: */ update(): void; /** * recalculate size of swiper container */ updateSize(): void; /** * recalculate number of slides and their offsets. Useful after you add/remove slides with JavaScript */ updateSlides(): void; /** * recalculate swiper progress */ updateProgress(): void; /** * update active/prev/next classes on slides and bullets */ updateSlidesClasses(): void; /** * Changes slider direction from horizontal to vertical and back. * * @param direction New direction. If not specified, then will automatically changed to opposite direction * @param needUpdate Will call swiper.update(). Default true */ changeDirection(direction?: 'horizontal' | 'vertical', needUpdate?: boolean): void; /** * Changes slider language * * @param direction New direction. Should be `rtl` or `ltr` */ changeLanguageDirection(direction: 'rtl' | 'ltr'): void; /** * Detach all events listeners */ detachEvents(): void; /** * Attach all events listeners again */ attachEvents(): void; /** * !INTERNAL */ loopCreate(): void; /** * !INTERNAL */ loopDestroy(): void; /** * Initialize slider */ init(el?: HTMLElement): Swiper; /** * Destroy slider instance and detach all events listeners * * @param deleteInstance Set it to false (by default it is true) to not to delete Swiper instance * @param cleanStyles Set it to true (by default it is true) and all custom styles will be removed from slides, wrapper and container. * Useful if you need to destroy Swiper and to init again with new options or in different direction */ destroy(deleteInstance?: boolean, cleanStyles?: boolean): void; /** * Set custom css3 transform's translate value for swiper wrapper */ setTranslate(translate: any): void; /** * Get current value of swiper wrapper css3 transform translate */ getTranslate(): any; /** * Animate custom css3 transform's translate value for swiper wrapper * * @param translate Translate value (in px) * @param speed Transition duration (in ms) * @param runCallbacks Set it to false (by default it is true) and transition will not produce transition events * @param translateBounds Set it to false (by default it is true) and transition value can extend beyond min and max translate * */ translateTo( translate: number, speed: number, runCallbacks?: boolean, translateBounds?: boolean, ): any; /** * Get current minimal translate value */ minTranslate(): number; /** * Get current maximal translate value */ maxTranslate(): number; /** * Unset grab cursor */ unsetGrabCursor(): void; /** * Set grab cursor */ setGrabCursor(): void; /** * Add event listener that will be fired on all events */ onAny(handler: (eventName: string, ...args: any[]) => void): void; /** * Remove event listener that will be fired on all events */ offAny(handler: (eventName: string, ...args: any[]) => void): void; /** * !INTERNAL */ isHorizontal(): boolean; /** * !INTERNAL */ getBreakpoint(breakpoints: SwiperOptions['breakpoints']): string; /** * !INTERNAL */ setBreakpoint(): void; /** * !INTERNAL */ currentBreakpoint: any; /** * !INTERNAL */ destroyed: boolean; /** * !INTERNAL */ modules: Array; a11y: A11yMethods; autoplay: AutoplayMethods; controller: ControllerMethods; coverflowEffect: CoverflowEffectMethods; cubeEffect: CubeEffectMethods; fadeEffect: FadeEffectMethods; flipEffect: FlipEffectMethods; creativeEffect: CreativeEffectMethods; cardsEffect: CardsEffectMethods; hashNavigation: HashNavigationMethods; history: HistoryMethods; keyboard: KeyboardMethods; mousewheel: MousewheelMethods; navigation: NavigationMethods; pagination: PaginationMethods; parallax: ParallaxMethods; scrollbar: ScrollbarMethods; thumbs: ThumbsMethods; virtual: VirtualMethods; zoom: ZoomMethods; freeMode: FreeModeMethods; } interface Swiper extends ManipulationMethods {} declare class Swiper implements Swiper { /** * Constructs a new Swiper instance. * * @param container Where Swiper applies to. * @param options Instance options. */ constructor(container: CSSSelector | HTMLElement, options?: SwiperOptions); /** * Installs modules on Swiper in runtime. */ static use(modules: SwiperModule[]): void; /** * Swiper default options */ static defaults: SwiperOptions; /** * Extend global Swiper defaults */ static extendDefaults(options: SwiperOptions): void; /** * Object with global Swiper extended options */ static extendedDefaults: SwiperOptions; } export default Swiper; ================================================ FILE: src/types/swiper-events.d.ts ================================================ import type { SwiperOptions } from './swiper-options.d.ts'; import type Swiper from './swiper-class.d.ts'; import type { A11yEvents } from './modules/a11y.d.ts'; import type { AutoplayEvents } from './modules/autoplay.d.ts'; import type { ControllerEvents } from './modules/controller.d.ts'; import type { CoverflowEffectEvents } from './modules/effect-coverflow.d.ts'; import type { CubeEffectEvents } from './modules/effect-cube.d.ts'; import type { FadeEffectEvents } from './modules/effect-fade.d.ts'; import type { FlipEffectEvents } from './modules/effect-flip.d.ts'; import type { CreativeEffectEvents } from './modules/effect-creative.d.ts'; import type { CardsEffectEvents } from './modules/effect-cards.d.ts'; import type { HashNavigationEvents } from './modules/hash-navigation.d.ts'; import type { HistoryEvents } from './modules/history.d.ts'; import type { KeyboardEvents } from './modules/keyboard.d.ts'; import type { MousewheelEvents } from './modules/mousewheel.d.ts'; import type { NavigationEvents } from './modules/navigation.d.ts'; import type { PaginationEvents } from './modules/pagination.d.ts'; import type { ParallaxEvents } from './modules/parallax.d.ts'; import type { ScrollbarEvents } from './modules/scrollbar.d.ts'; import type { ThumbsEvents } from './modules/thumbs.d.ts'; import type { VirtualEvents } from './modules/virtual.d.ts'; import type { ZoomEvents } from './modules/zoom.d.ts'; import type { FreeModeEvents } from './modules/free-mode.d.ts'; export interface SwiperEvents { // CORE_EVENTS_START /** * Fired right after Swiper initialization. * @note Note that with `swiper.on('init')` syntax it will * work only in case you set `init: false` parameter. * * @example * ```js * const swiper = new Swiper('.swiper', { * init: false, * // other parameters * }); * swiper.on('init', function() { * // do something * }); * // init Swiper * swiper.init(); * ``` * * @example * ```js * // Otherwise use it as the parameter: * const swiper = new Swiper('.swiper', { * // other parameters * on: { * init: function () { * // do something * }, * } * }); * ``` */ init: (swiper: Swiper) => any; /** * Event will be fired right before Swiper destroyed */ beforeDestroy: (swiper: Swiper) => void; /** * Event will be fired after slides and their sizes are calculated and updated */ slidesUpdated: (swiper: Swiper) => void; /** * Event will be fired when currently active slide is changed */ slideChange: (swiper: Swiper) => void; /** * Event will be fired in the beginning of animation to other slide (next or previous). */ slideChangeTransitionStart: (swiper: Swiper) => void; /** * Event will be fired after animation to other slide (next or previous). */ slideChangeTransitionEnd: (swiper: Swiper) => void; /** * Same as "slideChangeTransitionStart" but for "forward" direction only */ slideNextTransitionStart: (swiper: Swiper) => void; /** * Same as "slideChangeTransitionEnd" but for "forward" direction only */ slideNextTransitionEnd: (swiper: Swiper) => void; /** * Same as "slideChangeTransitionStart" but for "backward" direction only */ slidePrevTransitionStart: (swiper: Swiper) => void; /** * Same as "slideChangeTransitionEnd" but for "backward" direction only */ slidePrevTransitionEnd: (swiper: Swiper) => void; /** * Event will be fired in the beginning of transition. */ transitionStart: (swiper: Swiper) => void; /** * Event will be fired after transition. */ transitionEnd: (swiper: Swiper) => void; /** * Event will be fired when user touch Swiper. Receives `pointerdown` event as an arguments. */ touchStart: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; /** * Event will be fired when user touch and move finger over Swiper. Receives `pointermove` event as an arguments. */ touchMove: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; /** * Event will be fired when user touch and move finger over Swiper in direction opposite to direction parameter. Receives `pointermove` event as an arguments. */ touchMoveOpposite: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; /** * Event will be fired when user touch and move finger over Swiper and move it. Receives `pointermove` event as an arguments. */ sliderMove: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; /** * Event will be fired when user release Swiper. Receives `pointerup` event as an arguments. */ touchEnd: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; /** * Event will be fired when user click/tap on Swiper. Receives `pointerup` event as an arguments. */ click: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; /** * Event will be fired when user click/tap on Swiper. Receives `pointerup` event as an arguments. */ tap: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; /** * Event will be fired when user double tap on Swiper's container. Receives `pointerup` event as an arguments */ doubleTap: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; /** * Event will be fired when Swiper progress is changed, as an arguments it receives progress that is always from 0 to 1 */ progress: (swiper: Swiper, progress: number) => void; /** * Event will be fired when Swiper reach its beginning (initial position) */ reachBeginning: (swiper: Swiper) => void; /** * Event will be fired when Swiper reach last slide */ reachEnd: (swiper: Swiper) => void; /** * Event will be fired when Swiper goes to beginning or end position */ toEdge: (swiper: Swiper) => void; /** * Event will be fired when Swiper goes from beginning or end position */ fromEdge: (swiper: Swiper) => void; /** * Event will be fired when swiper's wrapper change its position. Receives current translate value as an arguments */ setTranslate: (swiper: Swiper, translate: number) => void; /** * Event will be fired everytime when swiper starts animation. Receives current transition duration (in ms) as an arguments */ setTransition: (swiper: Swiper, transition: number) => void; /** * Event will be fired on window resize right before swiper's onresize manipulation */ resize: (swiper: Swiper) => void; /** * Event will be fired if observer is enabled and it detects DOM mutations */ observerUpdate: (swiper: Swiper) => void; /** * Event will be fired right before "loop fix" */ beforeLoopFix: (swiper: Swiper) => void; /** * Event will be fired after "loop fix" */ loopFix: (swiper: Swiper) => void; /** * Event will be fired on breakpoint change */ breakpoint: (swiper: Swiper, breakpointParams: SwiperOptions) => void; /** * !INTERNAL: Event will fired right before breakpoint change */ _beforeBreakpoint?: (swiper: Swiper, breakpointParams: SwiperOptions) => void; /** * !INTERNAL: Event will fired after setting CSS classes on swiper container element */ _containerClasses?: (swiper: Swiper, classNames: string) => void; /** * !INTERNAL: Event will fired after setting CSS classes on swiper slide element */ _slideClass?: (swiper: Swiper, slideEl: HTMLElement, classNames: string) => void; /** * !INTERNAL: Event will fired after setting CSS classes on all swiper slides */ _slideClasses?: ( swiper: Swiper, slides: { slideEl: HTMLElement; classNames: string; index: number }[], ) => void; /** * !INTERNAL: Event will fired as soon as swiper instance available (before init) */ _swiper?: (swiper: Swiper) => void; /** * !INTERNAL: Event will be fired on free mode touch end (release) and there will no be momentum */ _freeModeNoMomentumRelease?: (swiper: Swiper) => void; /** * Event will fired on active index change */ activeIndexChange: (swiper: Swiper) => void; /** * Event will fired on snap index change */ snapIndexChange: (swiper: Swiper) => void; /** * Event will fired on real index change */ realIndexChange: (swiper: Swiper) => void; /** * Event will fired right after initialization */ afterInit: (swiper: Swiper) => void; /** * Event will fired right before initialization */ beforeInit: (swiper: Swiper) => void; /** * Event will fired before resize handler */ beforeResize: (swiper: Swiper) => void; /** * Event will fired before slide change transition start */ beforeSlideChangeStart: (swiper: Swiper) => void; /** * Event will fired before transition start */ beforeTransitionStart: (swiper: Swiper, speed: number, internal: any) => void; // what is internal? /** * Event will fired on direction change */ changeDirection: (swiper: Swiper) => void; /** * Event will be fired when user double click/tap on Swiper */ doubleClick: (swiper: Swiper, event: MouseEvent | TouchEvent | PointerEvent) => void; /** * Event will be fired on swiper destroy */ destroy: (swiper: Swiper) => void; /** * Event will be fired on momentum bounce */ momentumBounce: (swiper: Swiper) => void; /** * Event will be fired on orientation change (e.g. landscape -> portrait) */ orientationchange: (swiper: Swiper) => void; /** * Event will be fired in the beginning of animation of resetting slide to current one */ slideResetTransitionStart: (swiper: Swiper) => void; /** * Event will be fired in the end of animation of resetting slide to current one */ slideResetTransitionEnd: (swiper: Swiper) => void; /** * Event will be fired with first touch/drag move */ sliderFirstMove: (swiper: Swiper, event: TouchEvent) => void; /** * Event will be fired when number of slides has changed */ slidesLengthChange: (swiper: Swiper) => void; /** * Event will be fired when slides grid has changed */ slidesGridLengthChange: (swiper: Swiper) => void; /** * Event will be fired when snap grid has changed */ snapGridLengthChange: (swiper: Swiper) => void; /** * Event will be fired after swiper.update() call */ update: (swiper: Swiper) => void; /** * Event will be fired when swiper is locked (when `watchOverflow` enabled) */ lock: (swiper: Swiper) => void; /** * Event will be fired when swiper is unlocked (when `watchOverflow` enabled) */ unlock: (swiper: Swiper) => void; // CORE_EVENTS_END } interface SwiperEvents extends A11yEvents {} interface SwiperEvents extends AutoplayEvents {} interface SwiperEvents extends ControllerEvents {} interface SwiperEvents extends CoverflowEffectEvents {} interface SwiperEvents extends CubeEffectEvents {} interface SwiperEvents extends FadeEffectEvents {} interface SwiperEvents extends FlipEffectEvents {} interface SwiperEvents extends CreativeEffectEvents {} interface SwiperEvents extends CardsEffectEvents {} interface SwiperEvents extends HashNavigationEvents {} interface SwiperEvents extends HistoryEvents {} interface SwiperEvents extends KeyboardEvents {} interface SwiperEvents extends MousewheelEvents {} interface SwiperEvents extends NavigationEvents {} interface SwiperEvents extends PaginationEvents {} interface SwiperEvents extends ParallaxEvents {} interface SwiperEvents extends ScrollbarEvents {} interface SwiperEvents extends ThumbsEvents {} interface SwiperEvents extends VirtualEvents {} interface SwiperEvents extends ZoomEvents {} interface SwiperEvents extends FreeModeEvents {} ================================================ FILE: src/types/swiper-options.d.ts ================================================ import type { A11yOptions } from './modules/a11y.d.ts'; import type { AutoplayOptions } from './modules/autoplay.d.ts'; import type { ControllerOptions } from './modules/controller.d.ts'; import type { CoverflowEffectOptions } from './modules/effect-coverflow.d.ts'; import type { CubeEffectOptions } from './modules/effect-cube.d.ts'; import type { FadeEffectOptions } from './modules/effect-fade.d.ts'; import type { FlipEffectOptions } from './modules/effect-flip.d.ts'; import type { CreativeEffectOptions } from './modules/effect-creative.d.ts'; import type { CardsEffectOptions } from './modules/effect-cards.d.ts'; import type { HashNavigationOptions } from './modules/hash-navigation.d.ts'; import type { HistoryOptions } from './modules/history.d.ts'; import type { KeyboardOptions } from './modules/keyboard.d.ts'; import type { MousewheelOptions } from './modules/mousewheel.d.ts'; import type { NavigationOptions } from './modules/navigation.d.ts'; import type { PaginationOptions } from './modules/pagination.d.ts'; import type { ParallaxOptions } from './modules/parallax.d.ts'; import type { ScrollbarOptions } from './modules/scrollbar.d.ts'; import type { ThumbsOptions } from './modules/thumbs.d.ts'; import type { VirtualOptions } from './modules/virtual.d.ts'; import type { ZoomOptions } from './modules/zoom.d.ts'; import type { FreeModeOptions } from './modules/free-mode.d.ts'; import type { GridOptions } from './modules/grid.d.ts'; import type { CSSSelector, SwiperModule } from './shared.d.ts'; import type { SwiperEvents } from './swiper-events.d.ts'; export interface SwiperOptions { /** * Array with Swiper modules * * @example * ```js * import Swiper from 'swiper'; * import { Navigation, Pagination } from 'swiper/modules'; * * const swiper = new Swiper('.swiper', { * modules: [ Navigation, Pagination ], * }); * ``` */ modules?: SwiperModule[]; /** * Inject text styles to the shadow DOM. Only for usage with Swiper Element * */ injectStyles?: string[]; /** * Inject styles ``s to the shadow DOM. Only for usage with Swiper Element * */ injectStylesUrls?: string[]; /** * Whether Swiper should be initialised automatically when you create an instance. * If disabled, then you need to init it manually by calling `swiper.init()` * * @default true */ init?: boolean; /** * Whether Swiper initially enabled. When Swiper is disabled, it will hide all navigation elements and won't respond to any events and interactions * * @default true */ enabled?: boolean; /** * Swiper will recalculate slides position on window resize (orientationchange) * * @default true */ updateOnWindowResize?: boolean; /** * When enabled it will use ResizeObserver (if supported by browser) on swiper container to detect container resize (instead of watching for window resize) * * @default true */ resizeObserver?: boolean; /** * Index number of initial slide. * * @default 0 */ initialSlide?: number; /** * Can be `'horizontal'` or `'vertical'` (for vertical slider). * * @default 'horizontal' */ direction?: 'horizontal' | 'vertical'; /** * When enabled, will swipe slides only forward (one-way) regardless of swipe direction * * @default false */ oneWayMovement?: boolean; /** * The name of the swiper element node name; used for detecting web component rendering * * @default 'SWIPER-CONTAINER' */ swiperElementNodeName?: string; /** * Duration of transition between slides (in ms) * * @default 300 */ speed?: number; /** * Enabled this option and plugin will set width/height on swiper wrapper equal to total size of all slides. * Mostly should be used as compatibility fallback option for browser that don't support flexbox layout well * * @default false */ setWrapperSize?: boolean; /** * Enabled this option and swiper will be operated as usual except it will not move, real translate values on wrapper will not be set. * Useful when you may need to create custom slide transition * * @default false */ virtualTranslate?: boolean; /** * Swiper width (in px). Parameter allows to force Swiper width. * Useful only if you initialize Swiper when it is hidden and in SSR and Test environments for correct Swiper initialization * * @default null * * @note Setting this parameter will make Swiper not responsive */ width?: number | null; /** * Swiper height (in px). Parameter allows to force Swiper height. * Useful only if you initialize Swiper when it is hidden and in SSR and Test environments for correct Swiper initialization * * @default null * * @note Setting this parameter will make Swiper not responsive */ height?: number | null; /** * Set to `true` and slider wrapper will adapt its height to the height of the currently active slide * * @default false */ autoHeight?: boolean; /** * Set to `true` to round values of slides width and height to prevent blurry texts on usual * resolution screens (if you have such) * * @default false */ roundLengths?: boolean; /** * Set to `true` on Swiper for correct touch events interception. Use only on * swipers that use same direction as the parent one * * @default false */ nested?: boolean; /** * When enabled Swiper will automatically wrap slides with swiper-wrapper element, * and will create required elements for navigation, pagination and scrollbar * they are enabled (with their respective params object or with boolean `true`)) * * @default false */ createElements?: boolean; /** * Event name prefix for all DOM events emitted by Swiper Element (web component) * * @default `swiper` */ eventsPrefix?: string; /** * CSS selector for focusable elements. Swiping will be disabled on such elements if they are "focused" * * @default 'input, select, option, textarea, button, video, label' */ focusableElements?: string; /** * If enabled (by default) and navigation elements' parameters passed as a string (like `".pagination"`) * then Swiper will look for such elements through child elements first. * Applies for pagination, prev/next buttons and scrollbar elements * * @default true */ uniqueNavElements?: boolean; /** * Transition effect. Can be `'slide'`, `'fade'`, `'cube'`, `'coverflow'`, `'flip'`, `'creative'` or `'cards'` * * @default 'slide' */ effect?: 'slide' | 'fade' | 'cube' | 'coverflow' | 'flip' | 'creative' | 'cards' | (string & {}); /** * Fire Transition/SlideChange/Start/End events on swiper initialization. * Such events will be fired on initialization in case of your initialSlide is not 0, or you use loop mode * * @default true */ runCallbacksOnInit?: boolean; /** * When enabled Swiper will be disabled and hide navigation buttons on * case there are not enough slides for sliding. * * @default true */ watchOverflow?: boolean; /** * userAgent string. Required for browser/device detection when rendered on server-side * * @default null */ userAgent?: string | null; /** * Required for active slide detection when rendered on server-side and enabled history * * @default null */ url?: string | null; /** * Register event handlers */ on?: { [event in keyof SwiperEvents]?: SwiperEvents[event]; }; /** * Add event listener that will be fired on all events * * @example * ```js * const swiper = new Swiper('.swiper', { * onAny(eventName, ...args) { * console.log('Event: ', eventName); * console.log('Event data: ', args); * } * }); * ``` */ onAny?(handler: (eventName: string, ...args: any[]) => void): void; /** * When enabled it will use modern CSS Scroll Snap API. * It doesn't support all of Swiper's features, but potentially should bring a much better performance in simple configurations. * * This is what is not supported when it is enabled: * * - Cube effect * - `speed` parameter may not have no effect * - All transition start/end related events (use `slideChange` instead) * - `slidesPerGroup` has limited support * - `simulateTouch` doesn't have effect and "dragging" with mouse doesn't work * - `resistance` doesn't have any effect * - `allowSlidePrev/Next` * - `swipeHandler` * * In case if you use it with other effects, especially 3D effects, it is required to wrap slide's content with `
` element. And if you use any custom styles on slides (like background colors, border radius, border, etc.), they should be set on `swiper-slide-transform` element instead. * * @example * ```html *
*
*
* *
* ... slide content ... *
*
* ... *
*
* * ``` * * @default false */ cssMode?: boolean; // Slides grid /** * Distance between slides in px. * * @default 0 * * @note If you use "margin" css property to the elements which go into Swiper in which you pass "spaceBetween" into, navigation might not work properly. */ spaceBetween?: number | string; /** * Number of slides per view (slides visible at the same time on slider's container). * @note `slidesPerView: 'auto'` is currently not compatible with multirow mode, when `grid.rows` > 1 * * @default 1 */ slidesPerView?: number | 'auto'; /** * If total number of slides less than specified here value, then Swiper will enable `backface-visibility: hidden` on slide elements to reduce visual "flicker" in Safari. * * @note It is not recommended to enable it on large amount of slides as it will reduce performance * * @default 10 */ maxBackfaceHiddenSlides?: number; /** * Set numbers of slides to define and enable group sliding. Useful to use with slidesPerView > 1 * * @default 1 */ slidesPerGroup?: number; /** * The parameter works in the following way: If `slidesPerGroupSkip` equals `0` (default), no slides are excluded from grouping, and the resulting behaviour is the same as without this change. * * If `slidesPerGroupSkip` is equal or greater than `1` the first X slides are treated as single groups, whereas all following slides are grouped by the `slidesPerGroup` value. * * @default 0 */ slidesPerGroupSkip?: number; /** * This param intended to be used only with `slidesPerView: 'auto'` and `slidesPerGroup: 1`. When enabled, it will skip all slides in view on `.slideNext()` & `.slidePrev()` methods calls, on Navigation "buttons" clicks and in autoplay. * * @default false */ slidesPerGroupAuto?: boolean; /** * If `true`, then active slide will be centered, not always on the left side. * * @default false */ centeredSlides?: boolean; /** * If `true`, then active slide will be centered without adding gaps at the beginning and end of slider. * Required `centeredSlides: true`. Not intended to be used with `loop` or `pagination` * * @default false */ centeredSlidesBounds?: boolean; /** * Add (in px) additional slide offset in the beginning of the container (before all slides) * * @default 0 */ slidesOffsetBefore?: number; /** * Add (in px) additional slide offset in the end of the container (after all slides) * * @default 0 */ slidesOffsetAfter?: number; /** * Normalize slide index. * * @default true */ normalizeSlideIndex?: boolean; /** * When enabled it center slides if the amount of slides less than `slidesPerView`. Not intended to be used `loop` mode and `grid.rows` * * @default false */ centerInsufficientSlides?: boolean; /** * When enabled, the swiper will always snap to slide edges rather than arbitrary positions. * This prevents partial slides from appearing misaligned at the end of the swiper. * Only applies when `slidesPerView` is fractional or `'auto'`, and is ignored in `loop` and `centeredSlides` modes. * * @default false */ snapToSlideEdge?: boolean; /** * This option may a little improve desktop usability. If `true`, user will see the "grab" cursor when hover on Swiper * * @default false */ grabCursor?: boolean; /** * Target element to listen touch events on. Can be `'container'` (to listen for touch events on swiper) or `'wrapper'` * (to listen for touch events on swiper-wrapper) * * @default 'wrapper' */ touchEventsTarget?: 'container' | 'wrapper'; /** * Touch ratio * * @default 1 */ touchRatio?: number; /** * Allowable angle (in degrees) to trigger touch move * * @default 45 */ touchAngle?: number; /** * If `true`, Swiper will accept mouse events like touch events (click and drag to change slides) * * @default true */ simulateTouch?: boolean; /** * Set to `false` if you want to disable short swipes * * @default true */ shortSwipes?: boolean; /** * Set to `false` if you want to disable long swipes * * @default true */ longSwipes?: boolean; /** * Ratio to trigger swipe to next/previous slide during long swipes * * @default 0.5 */ longSwipesRatio?: number; /** * Minimal duration (in ms) to trigger swipe to next/previous slide during long swipes * * @default 300 */ longSwipesMs?: number; /** * If disabled, then slider will be animated only when you release it, it will not move while you hold your finger on it * * @default true */ followFinger?: boolean; /** * If `false`, then the only way to switch the slide is use of external API functions like slidePrev or slideNext * * @default true */ allowTouchMove?: boolean; /** * Threshold value in px. If "touch distance" will be lower than this value then swiper will not move * * @default 5 */ threshold?: number; /** * If disabled, `pointerdown` event won't be prevented * * @default true */ touchStartPreventDefault?: boolean; /** * Force to always prevent default for `touchstart` (`pointerdown`) event * * @default false */ touchStartForcePreventDefault?: boolean; /** * If enabled, then propagation of "touchmove" will be stopped * * @default false */ touchMoveStopPropagation?: boolean; /** * Enable to release Swiper events for swipe-back work in app. If set to `'prevent'` then it will prevent system swipe-back navigation instead. This feature works only with "touch" events (and not pointer events), so it will work on iOS/Android devices and won't work on Windows devices with pointer (touch) events. * * @default false */ edgeSwipeDetection?: boolean | string; /** * Area (in px) from left edge of the screen to release touch events for swipe-back in app * * @default 20 */ edgeSwipeThreshold?: number; /** * Enable to release touch events on slider edge position (beginning, end) to allow for further page scrolling. This feature works only with "touch" events (and not pointer events), so it will work on iOS/Android devices and won't work on Windows devices with pointer events. Also `threshold` parameter must be set to `0` * * @default false */ touchReleaseOnEdges?: boolean; /** * Passive event listeners will be used by default where possible to improve scrolling performance on mobile devices. * But if you need to use `e.preventDefault` and you have conflict with it, then you should disable this parameter * * @default true */ passiveListeners?: boolean; // Touch Resistance /** * Set to `false` if you want to disable resistant bounds * * @default true */ resistance?: boolean; /** * This option allows you to control resistance ratio * * @default 0.85 */ resistanceRatio?: number; // Swiping / No swiping /** * When enabled it won't allow to change slides by swiping or navigation/pagination buttons during transition * * @default false */ preventInteractionOnTransition?: boolean; /** * Set to `false` to disable swiping to previous slide direction (to left or top) * * @default true */ allowSlidePrev?: boolean; /** * Set to `false` to disable swiping to next slide direction (to right or bottom) * * @default true */ allowSlideNext?: boolean; /** * Enable/disable swiping on elements matched to class specified in `noSwipingClass` * * @default true */ noSwiping?: boolean; /** * Specify `noSwiping`'s element css class * * @default 'swiper-no-swiping' */ noSwipingClass?: string; /** * Can be used instead of `noSwipingClass` to specify elements to disable swiping on. * For example `'input'` will disable swiping on all inputs * * @default */ noSwipingSelector?: string; /** * String with CSS selector or HTML element of the container with pagination that will work as only available handler for swiping * * @default null */ swipeHandler?: CSSSelector | HTMLElement | null; // Clicks /** * Set to `true` to prevent accidental unwanted clicks on links during swiping * * @default true */ preventClicks?: boolean; /** * Set to `true` to stop clicks event propagation on links during swiping * * @default true */ preventClicksPropagation?: boolean; /** * Set to `true` and click on any slide will produce transition to this slide * * @default false */ slideToClickedSlide?: boolean; // Progress /** * Enable this feature to calculate each slides progress and visibility (slides in viewport will have additional visible class) * * @default false */ watchSlidesProgress?: boolean; /** * Set to `true` to enable continuous loop mode * * Because of nature of how the loop mode works (it will rearrange slides), total number of slides must be: * * - more than or equal to `slidesPerView` + `slidesPerGroup` (and `+ 1` in case of `centeredSlides`) * - even to `slidesPerGroup` (or use `loopAddBlankSlides` parameter) * - even to `grid.rows` (or use `loopAddBlankSlides` parameter) * * @default false * */ loop?: boolean; /** * Automatically adds blank slides if you use Grid or `slidesPerGroup` and the total amount of slides is not even to `slidesPerGroup` or to `grid.rows` * * * @default true * */ loopAddBlankSlides?: boolean; /** * Allows to increase amount of looped slides * * @default 0 */ loopAdditionalSlides?: number; /** * If enabled then slideNext/Prev will do nothing while slider is animating in loop mode * * @default true */ loopPreventsSliding?: boolean; /** * Set to `true` to enable "rewind" mode. When enabled, clicking "next" navigation button (or calling `.slideNext()`) when on last slide will slide back to the first slide. Clicking "prev" navigation button (or calling `.slidePrev()`) when on first slide will slide forward to the last slide. * * @default false * * @note Should not be used together with `loop` mode */ rewind?: boolean; /** * Allows to set different parameter for different responsive breakpoints (screen sizes). Not all parameters can be changed in breakpoints, only those which do not require different layout and logic, like `slidesPerView`, `slidesPerGroup`, `spaceBetween`, `grid.rows`. Such parameters like `loop` and `effect` won't work * * @example * ```js * const swiper = new Swiper('.swiper', { * // Default parameters * slidesPerView: 1, * spaceBetween: 10, * // Responsive breakpoints * breakpoints: { * // when window width is >= 320px * 320: { * slidesPerView: 2, * spaceBetween: 20 * }, * // when window width is >= 480px * 480: { * slidesPerView: 3, * spaceBetween: 30 * }, * // when window width is >= 640px * 640: { * slidesPerView: 4, * spaceBetween: 40 * } * } * }) * ``` * * @example * ```js * const swiper = new Swiper('.swiper', { * slidesPerView: 1, * spaceBetween: 10, * // using "ratio" endpoints * breakpoints: { * '@0.75': { * slidesPerView: 2, * spaceBetween: 20, * }, * '@1.00': { * slidesPerView: 3, * spaceBetween: 40, * }, * '@1.50': { * slidesPerView: 4, * spaceBetween: 50, * }, * } * }); * ``` */ breakpoints?: { [width: number]: SwiperOptions; [ratio: string]: SwiperOptions; }; /** * Base for breakpoints (beta). Can be `window` or `container`. If set to `window` (by default) then breakpoint keys mean window width. If set to `container` then breakpoint keys treated as swiper container width * * @default 'window' */ breakpointsBase?: 'window' | 'container' | CSSSelector; // Observer /** * Set to `true` to enable Mutation Observer on Swiper and its elements. In this case Swiper will be updated (reinitialized) each time if you change its style (like hide/show) or modify its child elements (like adding/removing slides) * * @default false */ observer?: boolean; /** * Set to `true` if you also need to watch Mutations for Swiper slide children elements * * @default false */ observeSlideChildren?: boolean; /** * Set to `true` if you also need to watch Mutations for Swiper parent elements * * @default false */ observeParents?: boolean; // Namespace /** * The beginning of the modifier CSS class that can be added to swiper container depending on different parameters * * @default 'swiper-' */ containerModifierClass?: string; /** * CSS class name of slide * * @default 'swiper-slide' * * @note By changing classes you will also need to change Swiper's CSS to reflect changed classes * * @note Not supported in Swiper React/Vue components */ slideClass?: string; /** * CSS class name of currently active slide * * @default 'swiper-slide-active' * * @note By changing classes you will also need to change Swiper's CSS to reflect changed classes * * @note Not supported in Swiper React/Vue components */ slideActiveClass?: string; /** * CSS class name of currently/partially visible slide * * @default 'swiper-slide-visible' * * @note By changing classes you will also need to change Swiper's CSS to reflect changed classes * * @note Not supported in Swiper React/Vue */ slideVisibleClass?: string; /** * CSS class name of fully (when whole slide is in the viewport) visible slide * * @default 'swiper-slide-fully-visible' * * @note Not supported in Swiper React/Vue */ slideFullyVisibleClass?: string; /** * CSS class name of the blank slide added by the loop mode (when `loopAddBlankSlides` is enabled) * * @default 'swiper-slide-blank' * * @note Not supported in Swiper React/Vue */ slideBlankClass?: string; /** * CSS class name of slide which is right after currently active slide * * @default 'swiper-slide-next' * * @note By changing classes you will also need to change Swiper's CSS to reflect changed classes * * @note Not supported in Swiper React/Vue */ slideNextClass?: string; /** * CSS class name of slide which is right before currently active slide * * @default 'swiper-slide-prev' * * @note By changing classes you will also need to change Swiper's CSS to reflect changed classes * * @note Not supported in Swiper React/Vue */ slidePrevClass?: string; /** * CSS class name of slides' wrapper * * @default 'swiper-wrapper' * * @note By changing classes you will also need to change Swiper's CSS to reflect changed classes * * @note Not supported in Swiper React/Vue * */ wrapperClass?: string; /** * CSS class name of lazy preloader * * @default 'swiper-lazy-preloader' */ lazyPreloaderClass?: string; /** * Number of next and previous slides to preload. Only applicable if using lazy loading. * * @default 0 */ lazyPreloadPrevNext?: number; /** * Object with a11y parameters or boolean `true` to enable with default settings. * * @example * ```js * const swiper = new Swiper('.swiper', { * a11y: { * prevSlideMessage: 'Previous slide', * nextSlideMessage: 'Next slide', * }, * }); * ``` */ a11y?: A11yOptions | boolean; /** * Object with autoplay parameters or boolean `true` to enable with default settings * * @example * ```js * const swiper = new Swiper('.swiper', { * autoplay: { * delay: 5000, * }, *}); * ``` */ autoplay?: AutoplayOptions | boolean; /** * Object with controller parameters or boolean `true` to enable with default settings * * @example * ```js * const swiper = new Swiper('.swiper', { * controller: { * inverse: true, * }, * }); * ``` */ controller?: ControllerOptions; /** * Object with Coverflow-effect parameters. * * @example * ```js * const swiper = new Swiper('.swiper', { * effect: 'coverflow', * coverflowEffect: { * rotate: 30, * slideShadows: false, * }, * }); * ``` */ coverflowEffect?: CoverflowEffectOptions; /** * Object with Cube-effect parameters * * @example * ```js * const swiper = new Swiper('.swiper', { * effect: 'cube', * cubeEffect: { * slideShadows: false, * }, * }); * ``` */ cubeEffect?: CubeEffectOptions; /** * Object with Fade-effect parameters * * @example * ```js * const swiper = new Swiper('.swiper', { * effect: 'fade', * fadeEffect: { * crossFade: true * }, * }); * ``` */ fadeEffect?: FadeEffectOptions; /** * Object with Flip-effect parameters * * @example * ```js * const swiper = new Swiper('.swiper', { * effect: 'flip', * flipEffect: { * slideShadows: false, * }, * }); * ``` */ flipEffect?: FlipEffectOptions; /** * Object with Creative-effect parameters * * @example * ```js * const swiper = new Swiper('.swiper', { * effect: 'creative', * creativeEffect: { * prev: { * // will set `translateZ(-400px)` on previous slides * translate: [0, 0, -400], * }, * next: { * // will set `translateX(100%)` on next slides * translate: ['100%', 0, 0], * }, * }, * }); * ``` */ creativeEffect?: CreativeEffectOptions; /** * Object with Cards-effect parameters * * @example * ```js * const swiper = new Swiper('.swiper', { * effect: 'cards', * cardsEffect: { * // ... * }, * }); * ``` */ cardsEffect?: CardsEffectOptions; /** * Enables hash url navigation to for slides. * Object with hash navigation parameters or boolean `true` to enable with default settings * * @example * ```js * const swiper = new Swiper('.swiper', { * hashNavigation: { * replaceState: true, * }, * }); * ``` */ hashNavigation?: HashNavigationOptions | boolean; /** * Enables history push state where every slide will have its own url. In this parameter you have to specify main slides url like `"slides"` and specify every slide url using `data-history` attribute. * * Object with history navigation parameters or boolean `true` to enable with default settings * * @example * ```js * const swiper = new Swiper('.swiper', { * history: { * replaceState: true, * }, * }); * ``` * * @example * ```html * *
* ``` */ history?: HistoryOptions | boolean; /** * Enables navigation through slides using keyboard. Object with keyboard parameters or boolean `true` to enable with default settings * * @example * ```js * const swiper = new Swiper('.swiper', { * keyboard: { * enabled: true, * onlyInViewport: false, * }, * }); * ``` */ keyboard?: KeyboardOptions | boolean; /** * Enables navigation through slides using mouse wheel. Object with mousewheel parameters or boolean `true` to enable with default settings * * @example * ```js * const swiper = new Swiper('.swiper', { * mousewheel: { * invert: true, * }, * }); * ``` */ mousewheel?: MousewheelOptions | boolean; /** * Object with navigation parameters or boolean `true` to enable with default settings. * * @example * ```js * const swiper = new Swiper('.swiper', { * navigation: { * nextEl: '.swiper-button-next', * prevEl: '.swiper-button-prev', * }, * }); * ``` */ navigation?: NavigationOptions | boolean; /** * Object with pagination parameters or boolean `true` to enable with default settings. * * @example * ```js * const swiper = new Swiper('.swiper', { * pagination: { * el: '.swiper-pagination', * type: 'bullets', * }, * }); * ``` */ pagination?: PaginationOptions | boolean; /** * Object with parallax parameters or boolean `true` to enable with default settings. * * @example * ```js * const swiper = new Swiper('.swiper', { * parallax: true, * }); * ``` */ parallax?: ParallaxOptions | boolean; /** * Object with scrollbar parameters or boolean `true` to enable with default settings. * * @example * ```js * const swiper = new Swiper('.swiper', { * scrollbar: { * el: '.swiper-scrollbar', * draggable: true, * }, * }); * ``` */ scrollbar?: ScrollbarOptions | boolean; /** * Object with thumbs component parameters * * @example * ```js * const swiper = new Swiper('.swiper', { * ... * thumbs: { * swiper: thumbsSwiper * } * }); * ``` */ thumbs?: ThumbsOptions; /** * Enables virtual slides functionality. Object with virtual slides parameters or boolean `true` to enable with default settings. * * @example * ```js * const swiper = new Swiper('.swiper', { * virtual: { * slides: ['Slide 1', 'Slide 2', 'Slide 3', 'Slide 4', 'Slide 5'], * }, * }); * ``` */ virtual?: VirtualOptions | boolean; /** * Enables zooming functionality. Object with zoom parameters or boolean `true` to enable with default settings * * @example * ```js * const swiper = new Swiper('.swiper', { * zoom: { * maxRatio: 5, * }, * }); * ``` */ zoom?: ZoomOptions | boolean; /** * Enables free mode functionality. Object with free mode parameters or boolean `true` to enable with default settings. * * @example * ```js * const swiper = new Swiper('.swiper', { * freeMode: true, * }); * * const swiper = new Swiper('.swiper', { * freeMode: { * enabled: true, * sticky: true, * }, * }); * ``` */ freeMode?: FreeModeOptions | boolean; /** * Object with grid parameters to enable "multirow" slider. * * @example * ```js * const swiper = new Swiper('.swiper', { * grid: { * rows: 2, * }, * }); * ``` */ grid?: GridOptions; /** * !INTERNAL When enabled will emit "_containerClasses" and "_slideClass" events */ _emitClasses?: boolean; } ================================================ FILE: src/vue/context.mjs ================================================ import { inject } from 'vue'; export const useSwiperSlide = () => { return inject('swiperSlide'); }; export const useSwiper = () => { return inject('swiper'); }; ================================================ FILE: src/vue/get-children.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; } els.forEach((vnode) => { const isFragment = typeof vnode.type === 'symbol'; if (slotName === 'default') slotName = 'container-end'; if (isFragment && vnode.children) { getSlidesFromElements(vnode.children, slotName); } else if ( (vnode.type && (vnode.type.name === 'SwiperSlide' || vnode.type.name === 'AsyncComponentWrapper')) || (vnode.componentOptions && vnode.componentOptions.tag === 'SwiperSlide') ) { slides.push(vnode); } else if (slots[slotName]) { slots[slotName].push(vnode); } }); }; Object.keys(originalSlots).forEach((slotName) => { if (typeof originalSlots[slotName] !== 'function') return; const els = originalSlots[slotName](); getSlidesFromElements(els, slotName); }); oldSlidesRef.value = slidesRef.value; slidesRef.value = slides; return { slides, slots }; } export { getChildren }; ================================================ FILE: src/vue/swiper-slide.mjs ================================================ import { h, ref, onMounted, onUpdated, onBeforeUpdate, computed, onBeforeUnmount, provide, } from 'vue'; import { uniqueClasses } from '../components-shared/utils.mjs'; const SwiperSlide = { name: 'SwiperSlide', props: { tag: { type: String, default: 'div', }, swiperRef: { type: Object, required: false }, swiperSlideIndex: { type: Number, default: undefined, required: false }, zoom: { type: Boolean, 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; }; return () => { 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: (vnode) => { if (vnode.el) vnode.el.lazyPreloaderManaged = true; }, }), ], ) : [ slots.default && slots.default(slideData.value), props.lazy && !lazyLoaded.value && h('div', { class: 'swiper-lazy-preloader', onVnodeMounted: (vnode) => { if (vnode.el) vnode.el.lazyPreloaderManaged = true; }, }), ], ); }; }, }; export { SwiperSlide }; ================================================ FILE: src/vue/swiper.mjs ================================================ import { h, ref, onMounted, onUpdated, onBeforeUnmount, watch, nextTick, provide } from 'vue'; import SwiperCore from '../swiper.mjs'; import { getParams } from '../components-shared/get-params.mjs'; import { mountSwiper } from '../components-shared/mount-swiper.mjs'; import { needsScrollbar, needsNavigation, needsPagination, uniqueClasses, extend, wrapperClass, } from '../components-shared/utils.mjs'; import { getChangedParams } from '../components-shared/get-changed-params.mjs'; import { getChildren } from './get-children.mjs'; import { updateSwiper } from '../components-shared/update-swiper.mjs'; import { renderVirtual } from './virtual.mjs'; import { updateOnVirtualData } from '../components-shared/update-on-virtual-data.mjs'; const Swiper = { 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: [ '_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', ], 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 = ref(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 SwiperCore(passParams); if (swiperRef.value.virtual && swiperRef.value.params.virtual.enabled) { swiperRef.value.virtual.slides = slidesRef.value; const extendWith = { cache: false, slides: slidesRef.value, renderExternal: (data) => { virtualData.value = data; }, renderExternalUpdate: false, }; extend(swiperRef.value.params.virtual, extendWith); extend(swiperRef.value.originalParams.virtual, extendWith); } onUpdated(() => { // set initialized flag if (!initializedRef.value && swiperRef.value) { swiperRef.value.emitSlidesClasses(); initializedRef.value = true; } // watch for params change const { passedParams: newPassedParams } = getParams(props, false); const changedParams = getChangedParams( newPassedParams, oldPassedParamsRef.value, slidesRef.value, oldSlidesRef.value, (c) => c.props && c.props.key, ); 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); // update on virtual update watch(virtualData, () => { nextTick(() => { updateOnVirtualData(swiperRef.value); }); }); // mount swiper onMounted(() => { if (!swiperElRef.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); } }); // bypass swiper instance to slides function renderSlides(slides) { if (swiperParams.virtual) { return renderVirtual(swiperRef, slides, virtualData.value); } slides.forEach((slide, index) => { if (!slide.props) slide.props = {}; slide.props.swiperRef = swiperRef; slide.props.swiperSlideIndex = index; }); 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'], ], ); }; }, }; export { Swiper }; ================================================ FILE: src/vue/virtual.mjs ================================================ import { h } from 'vue'; function renderVirtual(swiperRef, slides, virtualData) { if (!virtualData) return null; const getSlideIndex = (index) => { let slideIndex = index; if (index < 0) { slideIndex = slides.length + index; } else if (slideIndex >= slides.length) { // eslint-disable-next-line slideIndex = slideIndex - slides.length; } return slideIndex; }; const style = swiperRef.value.isHorizontal() ? { [swiperRef.value.rtlTranslate ? 'right' : 'left']: `${virtualData.offset}px`, } : { top: `${virtualData.offset}px`, }; const { from, to } = virtualData; const loopFrom = swiperRef.value.params.loop ? -slides.length : 0; const loopTo = swiperRef.value.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) { slidesToRender.push(slides[getSlideIndex(i)]); } } return slidesToRender.map((slide) => { if (!slide.props) slide.props = {}; if (!slide.props.style) slide.props.style = {}; slide.props.swiperRef = swiperRef; slide.props.style = style; if (slide.type) { return h(slide.type, { ...slide.props }, slide.children); } else if (slide.componentOptions) { return h(slide.componentOptions.Ctor, { ...slide.props }, slide.componentOptions.children); } }); } export { renderVirtual };