├── .eslintrc
├── .gitattributes
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── babel.config.js
├── docs
├── 3.x-document.md
└── live-examples.md
├── package.json
├── src
├── ReactIdSwiper.custom.tsx
├── ReactIdSwiper.tsx
├── hooks.ts
├── index.ts
├── tests
│ ├── ReactISwiper.custom.test.tsx
│ ├── ReactIdSwiper.test.tsx
│ ├── __snapshots__
│ │ ├── ReactISwiper.custom.test.tsx.snap
│ │ └── ReactIdSwiper.test.tsx.snap
│ ├── setup.ts
│ └── utils.test.tsx
├── types.ts
└── utils.ts
├── tsconfig.json
├── tsconfig.standalone.json
├── webpack.config.js
└── yarn.lock
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "extends": [
4 | "eslint:recommended",
5 | "plugin:react/recommended",
6 | "plugin:@typescript-eslint/recommended",
7 | "prettier/@typescript-eslint",
8 | "plugin:prettier/recommended",
9 | "plugin:import/errors",
10 | "plugin:import/warnings",
11 | "plugin:import/typescript",
12 | "plugin:jest/recommended"
13 | ],
14 | "plugins": ["react", "@typescript-eslint", "prettier", "react-hooks"],
15 | "env": {
16 | "browser": true,
17 | "jest": true,
18 | "amd": true,
19 | "node": true,
20 | "es6": true,
21 | "jest/globals": true
22 | },
23 | "parserOptions": {
24 | "ecmaVersion": 2018,
25 | "sourceType": "module",
26 | "ecmaFeatures": {
27 | "jsx": true
28 | }
29 | },
30 | "rules": {
31 | "import/no-unresolved": "error",
32 | "import/named": "off",
33 | "import/no-named-as-default" : "off",
34 | "import/default": "off",
35 | "import/order": ["error", {
36 | "groups": ["external", "builtin", "internal", "parent", "sibling", "index" ]
37 | }],
38 | "@typescript-eslint/explicit-function-return-type": "off",
39 | "@typescript-eslint/no-explicit-any": ["off"],
40 | "@typescript-eslint/explicit-member-accessibility": "off",
41 | "@typescript-eslint/no-empty-interface": ["error", {
42 | "allowSingleExtends": true
43 | }],
44 | "@typescript-eslint/ban-types": ["error", {
45 | "types": {
46 | "{}": false,
47 | "object": false
48 | }
49 | }],
50 | "react/display-name": "off",
51 | "prettier/prettier": ["error", {
52 | "singleQuote": true,
53 | "printWidth": 100,
54 | "semi": true,
55 | "arrowParens" : "avoid",
56 | "trailingComma": "none"
57 | }]
58 | },
59 | "overrides": [{
60 | "files": ["**/*.tsx"],
61 | "rules": {
62 | "react/prop-types": "off"
63 | }
64 | }],
65 | "settings": {
66 | "react": {
67 | "version": "detect"
68 | },
69 | "import/parsers": {
70 | "@typescript-eslint/parser": [".ts", ".tsx"]
71 | },
72 | "import/resolver": {
73 | "typescript": {}
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.js text
2 | *.html text
3 | *.json text
4 | *.png binary
5 | *.jpg binary
6 |
7 | # Make GitHub ignore vendor libraries while computing language stats.
8 | # See https://github.com/github/linguist#overrides.
9 |
10 | /src/* linguist-vendored=true
11 | /examples/app/* linguist-vendored=true
12 |
13 | # Explicitly specify language for non-standard extensions used under
14 |
15 | *.css_ linguist-language=CSS
16 | *.html_ linguist-language=HTML
17 | *.js_ linguist-language=JavaScript
18 | *.json_ linguist-language=JSON
19 | *.scss_ linguist-language=CSS
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.log
3 | /lib
4 | /coverage
5 | .vscode
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | cache:
4 | directories:
5 | - node_modules
6 | node_js:
7 | - 12
8 | install:
9 | - yarn install
10 | - npm install -g codecov
11 | script:
12 | - yarn test
13 | - yarn test:coverage
14 | - yarn lint:ts
15 | after_script: "cat ./coverage/lcov.info | $(npm bin)/codecov"
16 | branches:
17 | only:
18 | - master
19 | - /^greenkeeper/.*$/
20 | notifications:
21 | email:
22 | recipients:
23 | - phucnguyenhoang1985@gmail.com
24 | on_success: change
25 | on_failure: always
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log for React-id-swiper
2 |
3 | ## [v4.0.0](https://github.com/kidjp85/react-id-swiper/compare/4.0.0...3.0.0) - Released on Jun 22th, 2020
4 | * Upgrade to support `Swiper` newest version
5 | * Remove `getSwiper` props
6 | * Allow to access `Swiper instance` from `ref`
7 | * Upgrade npm packages
8 |
9 | ## [v3.0.0](https://github.com/kidjp85/react-id-swiper/compare/3.0.0...2.4.0) - Released on Feb 29th, 2020
10 | * Upgrade to support `Swiper` version 5.3.0
11 | * Replace `createRef` with `useRef` for `swiper` node ref
12 | * Upgrade npm packages
13 |
14 |
15 | ## [v2.4.0](https://github.com/kidjp85/react-id-swiper/compare/2.4.0...2.3.2) - Released on Nov 7th, 2019
16 | * Upgrade to support `Swiper` version 5.x
17 | * Drop support for `Swiper`'s stylesheet
18 | * Upgrade npm packages
19 |
20 | ## [v2.3.2](https://github.com/kidjp85/react-id-swiper/compare/2.3.2...2.3.1) - Released on July 24th, 2019
21 | * Upgrade npm packages
22 | * Replace `tslint` with `eslint`
23 |
24 | ## [v2.3.1](https://github.com/kidjp85/react-id-swiper/compare/2.3.1...2.3.0) - Released on June 27th, 2019
25 | * Fix bug [276](https://github.com/kidjp85/react-id-swiper/issues/276) for rebuildOnUpdate and shouldSwiperUpdate not working properly.
26 |
27 | ## [v2.3.0](https://github.com/kidjp85/react-id-swiper/compare/2.3.0...2.1.2) - Released on June 27th, 2019
28 | * Use Swiper full build version as default instead of `react-id-swiper/lib/react-id-swiper.full`
29 | * Provide custom build version which allows to use `Swiper` class as props instead of importing directly from `swiper/dist/js/swiper.esm`
30 | * Stylesheet files are now can be loaded from `react-id-swiper/lib/styles`
31 |
32 | ## [v2.1.2](https://github.com/kidjp85/react-id-swiper/compare/2.1.2...2.1.1) - Released on April 16th, 2019
33 | * Fix rendering issue for styled-component element
34 |
35 | ## [v2.1.1](https://github.com/kidjp85/react-id-swiper/compare/2.1.1...2.1.0) - Released on Mar 26th, 2019
36 | * Add full version back to lib folder
37 |
38 | ## [v2.1.0](https://github.com/kidjp85/react-id-swiper/compare/2.1.0...2.0.0) - Released on Mar 15th, 2019
39 | * :bomb: **Great news** - Add new props **`modules`** that allows to import only necessary swiper modules.
40 | * Fix `activeSlideKey` does not work correctly when `loop: true`
41 |
42 | ## [v2.0.0](https://github.com/kidjp85/react-id-swiper/compare/2.0.0...2.0.0-beta) - Released on Mar 14th, 2019
43 | * Add tests
44 | * Update README
45 |
46 | ## [*v2.0.0-beta*](https://github.com/kidjp85/react-id-swiper/compare/2.0.0...1.6.9) - Released on Mar 13th, 2019
47 | * Rewrite completely new package with Typescript + React Hooks Apis
48 | * Use `swiper` as peer-dependencies
49 | * Add new prop `getSwiper` function that returns `Swiper` instance.
50 | * Drop custom build for lightweight version
51 | * Drop `swiper` from standalone umd build
52 | * Add swiper@4.5.0 stylesheet files
53 |
54 | ## [v1.6.9](https://github.com/kidjp85/react-id-swiper/compare/1.6.9...1.6.8) - Released on Feb 24th, 2019
55 | * Upgrade swiper@4.4.6
56 | * Update babel 7
57 | * [PR-227](https://github.com/kidjp85/react-id-swiper/pull/227) by [jpetitcolas](https://github.com/jpetitcolas)
58 |
59 | ## [v1.6.8](https://github.com/kidjp85/react-id-swiper/compare/1.6.8...1.6.7) - Released on Sep 20th, 2018
60 | * Upgrade swiper@4.4.1
61 | * Put `src/styles` folder back which only support `css`, `scss`
62 | * Update README
63 |
64 | ## [v1.6.7](https://github.com/kidjp85/react-id-swiper/compare/1.6.7...1.6.6) - Released on Sep 2nd, 2018
65 | * Upgrade swiper@4.3.5
66 | * Fix typo for README
67 | * Remove `src/styles` folder
68 | * Update test
69 |
70 | ## [v1.6.6](https://github.com/kidjp85/react-id-swiper/compare/1.6.6...1.6.5) - Released on June 17th, 2018
71 | * Upgrade swiper@4.3.3 for custom build
72 | * Remove support for parallax in lightweight version
73 | * Update test
74 |
75 | ## [v1.6.5](https://github.com/kidjp85/react-id-swiper/compare/1.6.5...1.6.4) - Released on June 14th, 2018
76 | * Upgrade swiper@4.3.3
77 |
78 | ## [v1.6.4](https://github.com/kidjp85/react-id-swiper/compare/1.6.4...1.6.3) - Released on May 18th, 2018
79 | * Provide lightweight version which drops those features below
80 | - Virtual
81 | - Keyboard
82 | - Mouse wheel
83 | - Zoom
84 | - Lazy load image
85 | - A11y
86 | - History
87 | - Hash-navigation
88 | - Effect-cube
89 | - Effect-flip
90 | - Effect-coverflow
91 |
92 |
93 | ## [v1.6.3](https://github.com/kidjp85/react-id-swiper/compare/1.6.3...1.6.2) - Released on May 3rd, 2018
94 | * Upgrade swiper@4.2.6
95 | * Deprecate these props:
96 | - renderCustomPrevButton
97 | - renderCustomNextButton
98 | - renderCustomScrollbar
99 | - renderCustomPagination
100 | - renderCustomParallax
101 | - prevButtonCustomizedClass,
102 | - nextButtonCustomizedClass,
103 | - paginationCustomizedClass,
104 | - scrollbarCustomizedClass
105 | * Add new render props:
106 | - renderPrevButton
107 | - renderNextButton
108 | - renderScrollbar
109 | - renderPagination
110 | - renderParallax
111 | * Clean code
112 | * Update unit test
113 |
114 | ## [v1.6.2](https://github.com/kidjp85/react-id-swiper/compare/1.6.2...1.6.1) - Released on April 1st, 2018
115 | * Upgrade swiper@4.2.2
116 | * Update unit test
117 |
118 | ## [v1.6.1](https://github.com/kidjp85/react-id-swiper/compare/1.6.1...1.5.8) - Released on February 17th, 2018
119 | * Upgrade swiper@4.1.6
120 | * Add unit test
121 |
122 | ## [v1.5.8](https://github.com/kidjp85/react-id-swiper/compare/1.5.8...1.5.7) - Released on January 24th, 2018
123 | * Upgrade swiper@4.1.0
124 | * Fix bug for UMD build
125 |
126 | ## [v1.5.7](https://github.com/kidjp85/react-id-swiper/compare/1.5.7...1.5.6) - Released on December 20th, 2017
127 | * Add umd version
128 | * Add new params
129 | - ContainerEl
130 | - WrapperEl
131 | - renderCustomPrevButton
132 | - renderCustomNextButton
133 | - renderCustomScrolbar
134 | - renderCustomPagination
135 | - renderCustomParallax
136 |
137 | ## [v1.5.6](https://github.com/kidjp85/react-id-swiper/compare/1.5.6...1.5.5) - Released on December 1st, 2017
138 | * Upgrade Swiper@4.0.7
139 | * Add jest. Expose rebuildSwiper instance method [PR 108](https://github.com/kidjp85/react-id-swiper/pull/108) by [timkindberg](https://github.com/timkindberg)
140 |
141 | ## [v1.5.5](https://github.com/kidjp85/react-id-swiper/compare/1.5.5...1.5.4) - Released on November 15, 2017
142 | * Upgrade Swiper@4.0.6
143 | * Add support for parallax
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Nguyen Hoang Phuc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://npmjs.org/package/react-id-swiper)
2 | [](https://codecov.io/gh/kidjp85/react-id-swiper)
3 | [](https://npmjs.org/package/react-id-swiper)
4 | [](https://npmjs.org/package/react-id-swiper)
5 | [](https://travis-ci.org/kidjp85/react-id-swiper)
6 |
7 | [](http://packagequality.com/#?package=react-id-swiper)
8 |
9 | react-id-swiper ( Newest version 4.0.0 )
10 | ========================================
11 | > A library to use [Swiper](http://www.idangero.us/swiper/get-started/) as a ReactJs component
12 |
13 | 
14 |
15 | What is Swiper?
16 | ===============
17 |
18 | Swiper - is the free and most modern mobile touch slider with hardware accelerated transitions and amazing native behavior.
19 |
20 | It is intended to be used in mobile websites, mobile web apps, and mobile native/hybrid apps. Designed mostly for iOS, but also works great on latest Android, Windows Phone 8 and modern Desktop browsers.
21 |
22 | 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. Swiper does work well with [Gatsby](https://www.gatsbyjs.org/).
23 |
24 | # Props
25 |
26 | | Name | Type | Default value | Description |
27 | | ------------------ | -------- | ---------------- | -------------------------------------------------|
28 | | ContainerEl | String | 'div' | Element type for container |
29 | | containerClass | String | swiper-container | Swiper container class name |
30 | | WrapperEl | String | 'div' | Element type for wrapper |
31 | | wrapperClass | String | swiper-wrapper | Swiper wrapper class name |
32 | | slideClass | String | swiper-slide | Swiper slide class name |
33 | | shouldSwiperUpdate | Boolean | false | Update swiper when component is updated |
34 | | rebuildOnUpdate | Boolean | false | Rebuild swiper when component is updated |
35 | | noSwiping | Boolean | false | Disable swiping by condition |
36 | | activeSlideKey | String | null | Initial slide index |
37 | | renderPrevButton | function | | Render props function for prev button |
38 | | renderNextButton | function | | Render props function for next button |
39 | | renderScrollbar | function | | Render props function for scrollbar |
40 | | renderPagination | function | | Render props function for pagination |
41 | | renderParallax | function | | Render props function for parallax |
42 |
43 | **If you want to use Swiper custom build to reduce bundle size, you need to use extra props below.**
44 |
45 | # Custom build extra props
46 |
47 | | Name | Type | Default value | Description |
48 | | ------------------ | -------- | ---------------- | -------------------------------------------------|
49 | | Swiper | Class | | Swiper class |
50 | | modules | array | | Array of Swiper necessary modules |
51 |
52 |
53 | **NOTE:**
54 |
55 | * You can also use Swiper's original params too. Swiper API documentation [HERE](http://idangero.us/swiper/api/)
56 | * Find more info about Swiper custom build [HERE](https://idangero.us/swiper/api/#custom-build)
57 | * [<= 3.x documentation](docs/3.x-document.md)
58 |
59 | # Documentation
60 |
61 | - [Get Started](https://react-id-swiper.ashernguyen.site/doc/get-started)
62 | - [API](https://react-id-swiper.ashernguyen.site/doc/api)
63 | - [Custom Build](https://react-id-swiper.ashernguyen.site/doc/custom-build)
64 | - [Examples](https://react-id-swiper.ashernguyen.site/example/default)
65 |
66 | # Installation and setup
67 |
68 | - From version 2.0.0, it requires **React & ReactDOM ver >=16.8.0** to use [Hooks](https://reactjs.org/docs/hooks-intro.html)
69 | - From version 2.4.0, it requires **Swiper ver >= 5.0.0**
70 |
71 | ## Npm package
72 |
73 | > By npm
74 |
75 | ```bash
76 | npm install --save react-id-swiper@latest swiper@latest
77 | ```
78 |
79 | > By Yarn
80 |
81 | ```bash
82 | yarn add react-id-swiper@latest swiper@latest
83 | ```
84 |
85 | ## CDN
86 |
87 | ```html
88 |
89 | ```
90 |
91 | ```html
92 |
93 | ```
94 |
95 | # Styling
96 |
97 | **Swiper stylesheet file is required**
98 |
99 | Use Swiper stylesheet file from CDN
100 |
101 | ```html
102 |
103 | ```
104 |
105 | ```html
106 |
107 | ```
108 |
109 | **Or from Swiper package**
110 |
111 | You should import directly from `Swiper` package which supports css, scss and less
112 |
113 | > css
114 |
115 | ```javascript
116 | import 'swiper/css/swiper.css'
117 | ```
118 |
119 | > scss
120 |
121 | ```javascript
122 | import 'swiper/swiper.scss'
123 | ```
124 |
125 | > less
126 |
127 | ```javascript
128 | import 'swiper/swiper.less'
129 | ```
130 |
131 | # Examples
132 |
133 | ## Live Examples
134 |
135 | [Codesandbox Live Examples](docs/live-examples.md)
136 |
137 | ## Default
138 |
139 | ```javascript
140 | import React from 'react';
141 | import Swiper from 'react-id-swiper';
142 | import 'swiper/css/swiper.css';
143 |
144 | const SimpleSwiper = () => (
145 |
146 | Slide 1
147 | Slide 2
148 | Slide 3
149 | Slide 4
150 | Slide 5
151 |
152 | )
153 |
154 | export default SimpleSwiper;
155 | ```
156 |
157 | ## Using params
158 |
159 | ```javascript
160 | import React from 'react';
161 | import Swiper from 'react-id-swiper';
162 |
163 | const SimpleSwiperWithParams = () => {
164 | const params = {
165 | pagination: {
166 | el: '.swiper-pagination',
167 | type: 'bullets',
168 | clickable: true
169 | },
170 | navigation: {
171 | nextEl: '.swiper-button-next',
172 | prevEl: '.swiper-button-prev'
173 | },
174 | spaceBetween: 30
175 | }
176 |
177 | return(
178 |
179 | Slide 1
180 | Slide 2
181 | Slide 3
182 | Slide 4
183 | Slide 5
184 |
185 | )
186 | }
187 |
188 | export default SimpleSwiperWithParams;
189 | ```
190 |
191 | ## Manipulating swiper from outside swiper component
192 |
193 | ```javascript
194 | import React, { useRef } from 'react';
195 | import Swiper from 'react-id-swiper';
196 |
197 | const ManipulatingSwiper = () => {
198 | const ref = useRef(null);
199 |
200 | const ref = useRef(null);
201 |
202 | const goNext = () => {
203 | if (ref.current !== null && ref.current.swiper !== null) {
204 | ref.current.swiper.slideNext();
205 | }
206 | };
207 |
208 | const goPrev = () => {
209 | if (ref.current !== null && ref.current.swiper !== null) {
210 | ref.current.swiper.slidePrev();
211 | }
212 | };
213 |
214 | return (
215 |
216 |
217 | Slide 1
218 | Slide 2
219 | Slide 3
220 | Slide 4
221 | Slide 5
222 |
223 |
224 |
225 |
226 | );
227 | };
228 |
229 | export default ManipulatingSwiper;
230 | ```
231 |
232 | ## Custom build Swiper
233 |
234 | You can find the [WORKING DEMO REPO HERE](https://github.com/kidjp85/react-id-swiper-custom-build)
235 |
236 | ```javascript
237 | import React from 'react';
238 | import ReactIdSwiperCustom from 'react-id-swiper/lib/ReactIdSwiper.custom';
239 | import { Swiper, Navigation, Pagination } from 'swiper/js/swiper.esm';
240 |
241 | const CustomBuildSwiper = () => {
242 | const params = {
243 | // Provide Swiper class as props
244 | Swiper,
245 | // Add modules you need
246 | modules: [Navigation, Pagination],
247 | pagination: {
248 | el: '.swiper-pagination',
249 | type: 'bullets',
250 | clickable: true
251 | },
252 | navigation: {
253 | nextEl: '.swiper-button-next',
254 | prevEl: '.swiper-button-prev'
255 | },
256 | spaceBetween: 30
257 | }
258 |
259 | return(
260 |
261 | Slide 1
262 | Slide 2
263 | Slide 3
264 | Slide 4
265 | Slide 5
266 |
267 | )
268 | }
269 |
270 | export default CustomBuildSwiper;
271 | ```
272 |
273 | **NOTE**:
274 |
275 | * If you use Webpack & Babel you need to setup Babel loader config in `webpack.config.js` like below:
276 |
277 | ```javascript
278 | module: {
279 | rules: [
280 | {
281 | exclude: [/node_modules\/(?!(swiper|dom7)\/).*/, /\.test\.js(x)?$/],
282 | test: /\.js(x)?$/,
283 | use: [{ loader: 'babel-loader' }],
284 | }
285 | ],
286 | }
287 | ```
288 |
289 | ## Adding customized css classes
290 |
291 | ```javascript
292 | const params = {
293 | pagination: {
294 | el: '.swiper-pagination.customized-swiper-pagination',
295 | }, // Add your class name for pagination container
296 | navigation: {
297 | nextEl: '.swiper-button-next.customized-swiper-button-next', // Add your class name for next button
298 | prevEl: '.swiper-button-prev.customized-swiper-button-prev' // Add your class name for prev button
299 | },
300 | containerClass: 'customized-swiper-container' // Replace swiper-container with customized-swiper-container
301 | }
302 | ```
303 |
304 | ## Adding customized components
305 |
306 | For customized rendering to work, you have to use same classname with params el.
307 |
308 | ```javascript
309 | const params = {
310 | navigation: {
311 | nextEl: '.swiper-button-next',
312 | prevEl: '.swiper-button-prev'
313 | },
314 | renderPrevButton: () => ,
315 | renderNextButton: () => ,
316 | }
317 | ```
318 |
319 | ## Workable slides
320 |
321 | Each slide should be wrapped by HTML element
322 |
323 | > BAD CODE :thumbsdown:
324 |
325 | ```javascript
326 |
327 | Slide content
328 |
329 | ```
330 |
331 | > GOOD CODE :thumbsup:
332 |
333 | ```javascript
334 |
335 | Slide content
336 |
337 | ```
338 |
339 | # Bug report
340 |
341 | **Please use the prepared Codesanbox below to reproduce your issue. Thank you!!**
342 |
343 | [](https://codesandbox.io/s/p8j61y7j7?fontsize=14)
344 |
345 |
346 | # Authors
347 |
348 | * **Asher Nguyen** - *Initial work* - [Asher Nguyen](https://github.com/kidjp85)
349 |
350 | See also the list of [contributors](https://github.com/kidjp85/react-id-swiper/contributors) who participated in this project.
351 |
352 |
353 | # Buy me a coffee
354 |
355 |
356 |
357 | # License
358 |
359 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
360 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 |
4 | const presets = ['@babel/preset-env'];
5 | const plugins = ['@babel/plugin-syntax-dynamic-import'];
6 |
7 | return {
8 | presets,
9 | plugins
10 | };
11 | };
12 |
--------------------------------------------------------------------------------
/docs/3.x-document.md:
--------------------------------------------------------------------------------
1 | [](https://npmjs.org/package/react-id-swiper)
2 | [](https://codecov.io/gh/kidjp85/react-id-swiper)
3 | [](https://npmjs.org/package/react-id-swiper)
4 | [](https://npmjs.org/package/react-id-swiper)
5 | [](https://travis-ci.org/kidjp85/react-id-swiper)
6 | [](https://greenkeeper.io/)
7 |
8 | [](http://packagequality.com/#?package=react-id-swiper)
9 |
10 | react-id-swiper ( Newest version 3.0.0 )
11 | ========================================
12 | > A library to use [Swiper](http://www.idangero.us/swiper/get-started/) as a ReactJs component
13 |
14 | 
15 |
16 | What is Swiper?
17 | ===============
18 |
19 | Swiper - is the free and most modern mobile touch slider with hardware accelerated transitions and amazing native behavior.
20 |
21 | It is intended to be used in mobile websites, mobile web apps, and mobile native/hybrid apps. Designed mostly for iOS, but also works great on latest Android, Windows Phone 8 and modern Desktop browsers.
22 |
23 | 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. Swiper does work well with [Gatsby](https://www.gatsbyjs.org/).
24 |
25 | # Props
26 |
27 | | Name | Type | Default value | Description |
28 | | ------------------ | -------- | ---------------- | -------------------------------------------------|
29 | | ContainerEl | String | 'div' | Element type for container |
30 | | containerClass | String | swiper-container | Swiper container class name |
31 | | WrapperEl | String | 'div' | Element type for wrapper |
32 | | wrapperClass | String | swiper-wrapper | Swiper wrapper class name |
33 | | slideClass | String | swiper-slide | Swiper slide class name |
34 | | shouldSwiperUpdate | Boolean | false | Update swiper when component is updated |
35 | | rebuildOnUpdate | Boolean | false | Rebuild swiper when component is updated |
36 | | noSwiping | Boolean | false | Disable swiping by condition |
37 | | activeSlideKey | String | null | Initial slide index |
38 | | renderPrevButton | function | | Render props function for prev button |
39 | | renderNextButton | function | | Render props function for next button |
40 | | renderScrollbar | function | | Render props function for scrollbar |
41 | | renderPagination | function | | Render props function for pagination |
42 | | renderParallax | function | | Render props function for parallax |
43 | | getSwiper | function | | Callback function that returns Swiper instance |
44 |
45 | **If you want to use Swiper custom build to reduce bundle size, you need to use extra props below.**
46 |
47 | # Custom build extra props
48 |
49 | | Name | Type | Default value | Description |
50 | | ------------------ | -------- | ---------------- | -------------------------------------------------|
51 | | Swiper | Class | | Swiper class |
52 | | modules | array | | Array of Swiper necessary modules |
53 |
54 |
55 | **NOTE:**
56 |
57 | * You can also use Swiper's original params too. Swiper API documentation [HERE](http://idangero.us/swiper/api/)
58 | * Find more info about Swiper custom build [HERE](https://idangero.us/swiper/api/#custom-build)
59 |
60 | # Documentation
61 |
62 | - [Get Started](https://react-id-swiper.ashernguyen.site/doc/get-started)
63 | - [API](https://react-id-swiper.ashernguyen.site/doc/api)
64 | - [Custom Build](https://react-id-swiper.ashernguyen.site/doc/custom-build)
65 | - [Examples](https://react-id-swiper.ashernguyen.site/example/default)
66 |
67 | # Installation and setup
68 |
69 | - From version 2.0.0, it requires **React & ReactDOM ver >=16.8.0** to use [Hooks](https://reactjs.org/docs/hooks-intro.html)
70 | - From version 2.4.0, it requires **Swiper ver >= 5.0.0**
71 |
72 | ## Npm package
73 |
74 | > By npm
75 |
76 | ```bash
77 | npm install --save react-id-swiper@latest swiper@latest
78 | ```
79 |
80 | > By Yarn
81 |
82 | ```bash
83 | yarn add react-id-swiper@latest swiper@latest
84 | ```
85 |
86 | ## CDN
87 |
88 | ```html
89 |
90 | ```
91 |
92 | ```html
93 |
94 | ```
95 |
96 | # Styling
97 |
98 | **Swiper stylesheet file is required**
99 |
100 | Use Swiper stylesheet file from CDN
101 |
102 | ```html
103 |
104 | ```
105 |
106 | ```html
107 |
108 | ```
109 |
110 | **For version <=2.3.2**
111 |
112 | You can import direct from `react-id-swiper/lib/styles/` (supporting css, scss)
113 |
114 | > css
115 |
116 | ```javascript
117 | import 'react-id-swiper/lib/styles/css/swiper.css'
118 | ```
119 |
120 | > scss
121 |
122 | ```javascript
123 | import 'react-id-swiper/lib/styles/scss/swiper.scss'
124 | ```
125 |
126 | **For version >=3.0.0**
127 |
128 | You should import directly from `Swiper` packages which supports css, scss and less
129 |
130 | > css
131 |
132 | ```javascript
133 | import 'swiper/css/swiper.css'
134 | ```
135 |
136 | > scss
137 |
138 | ```javascript
139 | import 'swiper/swiper.scss'
140 | ```
141 |
142 | > less
143 |
144 | ```javascript
145 | import 'swiper/swiper.less'
146 | ```
147 |
148 | # Examples
149 |
150 | [Numerous live examples](https://react-id-swiper.ashernguyen.site/example/default):
151 |
152 | >Navigation, Pagination, Pagination / Dynamic Bullets, Progress Pagination, Fraction Pagination, Custom Pagination, Scrollbar, Vertical slider, Space Between Slides, Mutiple Slides Per View, Auto Slides Per View / Carousel Mode, Centered Slides, Centered Slides + Auto Slides Per View, Free Mode / No Fixed Positions, Scroll Container, Multiple Row Slides Layout, Nested Swipers, Grab Cursor, Loop Mode / Infinite Loop, Loop Mode With Multiple Slides Per Group, Fade Effect, 3D Cube Effect, 3D Coverflow Effect, 3D Flip Effect, Mousewheel-control, Auto Play, Thumbs Gallery With Two-way Control, RTL Layout, Parallax, Lazyload Image, Responsive Breakpoints, Manipulating component outside Swiper, Customized Component
153 |
154 | ## Default
155 |
156 | ```javascript
157 | import React from 'react';
158 | import Swiper from 'react-id-swiper';
159 | // Version <= 2.3.2
160 | import 'react-id-swiper/lib/styles/css/swiper.css';
161 | // Version >= 2.4.0
162 | import 'swiper/css/swiper.css';
163 |
164 | const SimpleSwiper = () => (
165 |
166 | Slide 1
167 | Slide 2
168 | Slide 3
169 | Slide 4
170 | Slide 5
171 |
172 | )
173 |
174 | export default SimpleSwiper;
175 | ```
176 |
177 | ## Using params
178 |
179 | ```javascript
180 | import React from 'react';
181 | import Swiper from 'react-id-swiper';
182 |
183 | const SimpleSwiperWithParams = () => {
184 | const params = {
185 | pagination: {
186 | el: '.swiper-pagination',
187 | type: 'bullets',
188 | clickable: true
189 | },
190 | navigation: {
191 | nextEl: '.swiper-button-next',
192 | prevEl: '.swiper-button-prev'
193 | },
194 | spaceBetween: 30
195 | }
196 |
197 | return(
198 |
199 | Slide 1
200 | Slide 2
201 | Slide 3
202 | Slide 4
203 | Slide 5
204 |
205 | )
206 | }
207 |
208 | export default SimpleSwiperWithParams;
209 | ```
210 |
211 | ## Manipulating swiper from outside swiper component
212 |
213 | ```javascript
214 | import React, { useState } from 'react';
215 | import Swiper from 'react-id-swiper';
216 |
217 | const ManipulatingSwiper = () => {
218 | const [swiper, setSwiper] = useState(null);
219 |
220 | const goNext = () => {
221 | if (swiper !== null) {
222 | swiper.slideNext();
223 | }
224 | };
225 |
226 | const goPrev = () => {
227 | if (swiper !== null) {
228 | swiper.slidePrev();
229 | }
230 | };
231 |
232 | return (
233 |
234 |
235 | Slide 1
236 | Slide 2
237 | Slide 3
238 | Slide 4
239 | Slide 5
240 |
241 |
242 |
243 |
244 | );
245 | };
246 |
247 | export default ManipulatingSwiper;
248 | ```
249 |
250 | ## Custom build Swiper
251 |
252 | You can find the [WORKING DEMO REPO HERE](https://github.com/kidjp85/react-id-swiper-custom-build)
253 |
254 | ```javascript
255 | import React from 'react';
256 | import ReactIdSwiperCustom from 'react-id-swiper/lib/ReactIdSwiper.custom';
257 | // For swiper version 4.x
258 | import { Swiper, Navigation, Pagination } from 'swiper/dist/js/swiper.esm';
259 | // For swiper version 5.x
260 | import { Swiper, Navigation, Pagination } from 'swiper/js/swiper.esm';
261 |
262 | const CustomBuildSwiper = () => {
263 | const params = {
264 | // Provide Swiper class as props
265 | Swiper,
266 | // Add modules you need
267 | modules: [Navigation, Pagination],
268 | pagination: {
269 | el: '.swiper-pagination',
270 | type: 'bullets',
271 | clickable: true
272 | },
273 | navigation: {
274 | nextEl: '.swiper-button-next',
275 | prevEl: '.swiper-button-prev'
276 | },
277 | spaceBetween: 30
278 | }
279 |
280 | return(
281 |
282 | Slide 1
283 | Slide 2
284 | Slide 3
285 | Slide 4
286 | Slide 5
287 |
288 | )
289 | }
290 |
291 | export default CustomBuildSwiper;
292 | ```
293 |
294 | **NOTE**:
295 | * If you use Webpack & Babel you need to setup Babel loader config in `webpack.config.js` like below:
296 |
297 | ```javascript
298 | module: {
299 | rules: [
300 | {
301 | exclude: [/node_modules\/(?!(swiper|dom7)\/).*/, /\.test\.js(x)?$/],
302 | test: /\.js(x)?$/,
303 | use: [{ loader: 'babel-loader' }],
304 | }
305 | ],
306 | }
307 | ```
308 |
309 | ## Adding customized css classes
310 |
311 | ```javascript
312 | const params = {
313 | pagination: {
314 | el: '.swiper-pagination.customized-swiper-pagination',
315 | }, // Add your class name for pagination container
316 | navigation: {
317 | nextEl: '.swiper-button-next.customized-swiper-button-next', // Add your class name for next button
318 | prevEl: '.swiper-button-prev.customized-swiper-button-prev' // Add your class name for prev button
319 | },
320 | containerClass: 'customized-swiper-container' // Replace swiper-container with customized-swiper-container
321 | }
322 | ```
323 |
324 | ## Adding customized components
325 |
326 | For customized rendering to work, you have to use same classname with params el.
327 |
328 | ```javascript
329 | const params = {
330 | navigation: {
331 | nextEl: '.swiper-button-next',
332 | prevEl: '.swiper-button-prev'
333 | },
334 | renderPrevButton: () => ,
335 | renderNextButton: () => ,
336 | }
337 | ```
338 |
339 | ## Workable slides
340 |
341 | Each slide should be wrapped by HTML element
342 |
343 | > BAD CODE :thumbsdown:
344 |
345 | ```javascript
346 |
347 | Slide content
348 |
349 | ```
350 |
351 | > GOOD CODE :thumbsup:
352 |
353 | ```javascript
354 |
355 | Slide content
356 |
357 | ```
358 |
359 | # Bug report
360 |
361 | **Please use the prepared Codesanbox below to reproduce your issue. Thank you!!**
362 |
363 | [](https://codesandbox.io/s/p8j61y7j7?fontsize=14)
364 |
365 |
366 | # Authors
367 |
368 | * **Asher Nguyen** - *Initial work* - [Asher Nguyen](https://github.com/kidjp85)
369 |
370 | See also the list of [contributors](https://github.com/kidjp85/react-id-swiper/contributors) who participated in this project.
371 |
372 | # License
373 |
374 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
375 |
--------------------------------------------------------------------------------
/docs/live-examples.md:
--------------------------------------------------------------------------------
1 | ## Live Examples
2 |
3 | You can view the live code examples in Code Sandbox.
4 |
5 | | Name | Link |
6 | | -------------------------------------------| ------------------------------------------------------------------------------------ |
7 | | Default | https://codesandbox.io/s/default-example-ys1vc |
8 | | Navigation | https://codesandbox.io/s/navigation-example-ksmh8 |
9 | | Pagination | https://codesandbox.io/s/pagination-example-b8ge |
10 | | Pagination Dynamic Bullets | https://codesandbox.io/s/pagination-dynamic-bullets-example-gposd |
11 | | Progress Pagination | https://codesandbox.io/s/progress-pagination-example-u6u6n |
12 | | Fraction Pagination | https://codesandbox.io/s/fraction-pagination-example-0sfty |
13 | | Custom Pagination | https://codesandbox.io/s/custom-pagination-example-jtf4k |
14 | | Scrollbar | https://codesandbox.io/s/scrollbar-example-tirwz |
15 | | Vertical Slider | https://codesandbox.io/s/vertical-slider-example-x3qf0 |
16 | | Space Between Slides | https://codesandbox.io/s/space-between-slides-example-p1ult |
17 | | Multiple Slides Per View | https://codesandbox.io/s/mutiple-slides-per-view-example-5z83y |
18 | | Auto Slides Per View Carousel Mode | https://codesandbox.io/s/auto-slides-per-view-carousel-mode-example-dsgmo |
19 | | Centered Slides | https://codesandbox.io/s/centered-slides-example-h4fmo |
20 | | Centered Slides Auto Slides Per View | https://codesandbox.io/s/centered-slides-auto-slides-per-view-example-o8qn6 |
21 | | Free Mode No Fixed Positions | https://codesandbox.io/s/free-mode-no-fixed-positions-example-43t9f |
22 | | Scroll Container | https://codesandbox.io/s/scroll-container-example-gvix8 |
23 | | Multiple Row Slides Layout | https://codesandbox.io/s/multiple-row-slides-layout-example-du250 |
24 | | Nested Swipers | https://codesandbox.io/s/nested-swipers-example-7xk37 |
25 | | Grab Cursor | https://codesandbox.io/s/grab-cursor-example-o0pwx |
26 | | Loop Mode Infinite Loop | https://codesandbox.io/s/loop-mode-infinite-loop-example-422q1 |
27 | | Loop Mode With Multiple Slides Per Group | https://codesandbox.io/s/loop-mode-with-multiple-slides-per-group-example-0it50 |
28 | | Fade Effect | https://codesandbox.io/s/fade-effect-example-75qts |
29 | | 3D Cube Effect | https://codesandbox.io/s/3d-cube-effect-example-wcxj5 |
30 | | 3D Coverflow Effect | https://codesandbox.io/s/3d-coverflow-effect-example-2jc7w |
31 | | 3D Flip Effect | https://codesandbox.io/s/3d-flip-effect-example-0k301 |
32 | | Mousewheel Control | https://codesandbox.io/s/mousewheel-control-example-evnhy |
33 | | Auto Play | https://codesandbox.io/s/auto-play-example-44urp |
34 | | Thumbs Gallery With Two-way Control | https://codesandbox.io/s/thumbs-gallery-with-two-way-control-example-htrhu |
35 | | RTL Layout | https://codesandbox.io/s/rtl-layout-example-bljf5 |
36 | | Parallax | https://codesandbox.io/s/parallax-example-rmxhe |
37 | | Lazyload Image | https://codesandbox.io/s/lazyload-image-rl1bk |
38 | | Responsive Breakpoints | https://codesandbox.io/s/responsive-breakpoints-example-t2rsy |
39 | | Manipulating component outside Swiper | https://codesandbox.io/s/manipulating-swiper-component-from-outside-example-5gv2r |
40 | | Customized Component | https://codesandbox.io/s/customized-component-example-nksc2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-id-swiper",
3 | "version": "4.0.0",
4 | "description": "ReactJs component for iDangerous Swiper",
5 | "main": "lib/index",
6 | "types": "lib/index.d.ts",
7 | "files": [
8 | "lib/**/*"
9 | ],
10 | "scripts": {
11 | "build:cleanup": "rimraf lib",
12 | "build:lib": "yarn build:cleanup && tsc",
13 | "build:standalone": "cross-env BABEL_ENV=production webpack",
14 | "build": "yarn build:lib && yarn build:standalone",
15 | "lint:ts": "eslint 'src/**/*.ts?(x)'",
16 | "lint:ts:fix": "eslint --fix 'src/**/*.ts?(x)'",
17 | "test": "jest",
18 | "test:dev": "jest --watch --coverage",
19 | "test:coverage": "jest --coverage",
20 | "prepare": "yarn build",
21 | "preversion": "yarn lint:ts",
22 | "prepublishOnly": "yarn test && yarn lint:ts"
23 | },
24 | "keywords": [
25 | "iDangerous",
26 | "Swiper",
27 | "Reactjs"
28 | ],
29 | "engines": {
30 | "node": ">= 6.11.0"
31 | },
32 | "repository": {
33 | "type": "git",
34 | "url": "git://github.com/kidjp85/react-id-swiper.git"
35 | },
36 | "husky": {
37 | "hooks": {
38 | "pre-commit": "lint-staged"
39 | }
40 | },
41 | "lint-staged": {
42 | "*.{ts,tsx}": [
43 | "eslint",
44 | "git add"
45 | ]
46 | },
47 | "bugs": {
48 | "url": "https://github.com/kidjp85/react-id-swiper/issues"
49 | },
50 | "dependencies": {
51 | "object-assign": "^4.1.1"
52 | },
53 | "peerDependencies": {
54 | "react": ">=16.8.0",
55 | "react-dom": ">=16.8.0",
56 | "swiper": ">=5.0.0"
57 | },
58 | "devDependencies": {
59 | "@babel/core": "^7.10.4",
60 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
61 | "@babel/preset-env": "^7.10.4",
62 | "@babel/preset-react": "^7.10.4",
63 | "@types/enzyme": "^3.10.5",
64 | "@types/enzyme-adapter-react-16": "^1.0.6",
65 | "@types/jest": "^26.0.3",
66 | "@types/jsdom": "11.0.4",
67 | "@types/object-assign": "^4.0.30",
68 | "@types/react": "^16.9.41",
69 | "@types/react-dom": "^16.9.8",
70 | "@types/swiper": "^5.4.0",
71 | "@typescript-eslint/eslint-plugin": "^3.5.0",
72 | "@typescript-eslint/parser": "^3.5.0",
73 | "awesome-typescript-loader": "^5.2.1",
74 | "babel-jest": "^26.1.0",
75 | "babel-plugin-dynamic-import-node": "^2.3.3",
76 | "browser-resolve": "^1.11.3",
77 | "cross-env": "^7.0.2",
78 | "enzyme": "^3.11.0",
79 | "enzyme-adapter-react-16": "^1.15.2",
80 | "enzyme-to-json": "^3.5.0",
81 | "eslint": "^7.3.1",
82 | "eslint-config-prettier": "^6.11.0",
83 | "eslint-config-react": "^1.1.7",
84 | "eslint-import-resolver-typescript": "^2.0.0",
85 | "eslint-plugin-import": "^2.22.0",
86 | "eslint-plugin-jest": "^23.17.1",
87 | "eslint-plugin-prettier": "^3.1.4",
88 | "eslint-plugin-react": "^7.20.3",
89 | "eslint-plugin-react-hooks": "^4.0.5",
90 | "husky": "^4.2.5",
91 | "jest": "^26.1.0",
92 | "lint-staged": "^10.2.11",
93 | "prettier": "^2.0.5",
94 | "react": "^16.13.1",
95 | "react-dom": "^16.13.1",
96 | "rimraf": "^3.0.2",
97 | "swiper": "^5.4.5",
98 | "ts-jest": "^26.1.1",
99 | "ts-loader": "^7.0.5",
100 | "typescript": "^3.9.6",
101 | "uglifyjs-webpack-plugin": "^2.2.0",
102 | "webpack": "^4.43.0",
103 | "webpack-cli": "^3.3.12"
104 | },
105 | "jest": {
106 | "resetMocks": true,
107 | "resetModules": true,
108 | "verbose": true,
109 | "collectCoverage": true,
110 | "browser": true,
111 | "snapshotSerializers": [
112 | "enzyme-to-json/serializer"
113 | ],
114 | "transform": {
115 | "^.+\\.(js|jsx)$": "babel-jest",
116 | "^.+\\.(ts|tsx)$": "ts-jest"
117 | },
118 | "moduleFileExtensions": [
119 | "ts",
120 | "tsx",
121 | "js",
122 | "jsx"
123 | ],
124 | "roots": [
125 | "/src/"
126 | ],
127 | "moduleDirectories": [
128 | "node_modules",
129 | "/src"
130 | ],
131 | "transformIgnorePatterns": [
132 | "node_modules/(?!(swiper|dom7)/)"
133 | ],
134 | "setupFiles": [
135 | "/src/tests/setup.ts"
136 | ],
137 | "testMatch": [
138 | "/src/tests/**/*.test.(ts|tsx|js)"
139 | ]
140 | },
141 | "author": "Phuc Nguyen Hoang",
142 | "license": "MIT"
143 | }
144 |
--------------------------------------------------------------------------------
/src/ReactIdSwiper.custom.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | forwardRef,
3 | Children,
4 | useEffect,
5 | useRef,
6 | cloneElement,
7 | isValidElement,
8 | useCallback,
9 | ReactElement
10 | } from 'react';
11 | import objectAssign from 'object-assign';
12 | import { ReactIdSwiperCustomProps, SwiperInstance, SwiperRefNode } from './types';
13 | import { classNames, validateChildren, isReactElement, isModuleAvailable, setRef } from './utils';
14 | import { useForkRef } from './hooks';
15 |
16 | const ReactIdSwiperCustom = forwardRef(
17 | (props, externalRef) => {
18 | const {
19 | Swiper,
20 | activeSlideKey,
21 | ContainerEl,
22 | children,
23 | containerClass,
24 | navigation,
25 | noSwiping,
26 | pagination,
27 | parallax,
28 | parallaxEl,
29 | WrapperEl,
30 | wrapperClass,
31 | rebuildOnUpdate,
32 | renderScrollbar,
33 | renderPagination,
34 | renderPrevButton,
35 | renderNextButton,
36 | renderParallax,
37 | rtl,
38 | scrollbar,
39 | shouldSwiperUpdate,
40 | slideClass,
41 | loop,
42 | modules = []
43 | } = props;
44 |
45 | // Initialize modules to use with swiper
46 | if (Swiper) {
47 | Swiper.use(modules);
48 | }
49 |
50 | // Define swiper instance ref
51 | const swiperInstanceRef = useRef(null);
52 |
53 | // Internal ref
54 | const swiperNodeRef = useRef(null);
55 |
56 | // Forked ref
57 | const ref = useForkRef(swiperNodeRef, externalRef);
58 |
59 | // Get current active slide key
60 | const getActiveSlideIndexFromProps = useCallback(() => {
61 | if (!activeSlideKey) {
62 | return null;
63 | }
64 |
65 | let activeSlideId = 0;
66 |
67 | // In loop mode first slide index should be 1
68 | let id = loop ? 1 : 0;
69 |
70 | Children.forEach(children, child => {
71 | if (isValidElement(child)) {
72 | if (child.key === activeSlideKey) {
73 | activeSlideId = id;
74 | }
75 |
76 | id += 1;
77 | }
78 | });
79 |
80 | return activeSlideId;
81 | }, [activeSlideKey, children, loop]);
82 |
83 | // Destroy swiper
84 | const destroySwiper = useCallback(() => {
85 | if (swiperInstanceRef.current !== null) {
86 | swiperInstanceRef.current.destroy(true, true);
87 |
88 | setRef(swiperInstanceRef, null);
89 | }
90 | }, []);
91 |
92 | // Initialize swiper
93 | const buildSwiper = useCallback(() => {
94 | if (swiperNodeRef.current && swiperInstanceRef.current === null) {
95 | setRef(swiperInstanceRef, new Swiper(swiperNodeRef.current, objectAssign({}, props)));
96 | }
97 | }, [props]);
98 |
99 | // Render slides
100 | const renderContent = (e: ReactElement) => {
101 | if (!isReactElement(e)) {
102 | return null;
103 | }
104 |
105 | const slideClassNames = [slideClass, e.props.className];
106 |
107 | if (noSwiping) {
108 | slideClassNames.push('swiper-no-swiping');
109 | }
110 |
111 | return cloneElement(e, {
112 | ...e.props,
113 | className: slideClassNames.join(' ').trim()
114 | });
115 | };
116 |
117 | // Destroy Swiper instance when component is unmounted
118 | useEffect(() => {
119 | return () => destroySwiper();
120 | }, [destroySwiper]);
121 |
122 | useEffect(() => {
123 | buildSwiper();
124 |
125 | if (swiperInstanceRef.current !== null) {
126 | if (rebuildOnUpdate) {
127 | destroySwiper();
128 |
129 | buildSwiper();
130 | } else if (shouldSwiperUpdate) {
131 | swiperInstanceRef.current.update();
132 | }
133 |
134 | const numSlides = swiperInstanceRef.current.slides.length;
135 |
136 | if (numSlides <= swiperInstanceRef.current.activeIndex) {
137 | const index = Math.max(numSlides - 1, 0);
138 |
139 | swiperInstanceRef.current.slideTo(index);
140 | }
141 |
142 | const slideToIndex = getActiveSlideIndexFromProps();
143 |
144 | if (slideToIndex !== null) {
145 | swiperInstanceRef.current.slideTo(slideToIndex);
146 | }
147 | }
148 | }, [
149 | destroySwiper,
150 | getActiveSlideIndexFromProps,
151 | rebuildOnUpdate,
152 | shouldSwiperUpdate,
153 | buildSwiper
154 | ]);
155 |
156 | // Check modules are loaded before rendering contents
157 | const shouldRenderParallax = isModuleAvailable(modules, 'parallax') && parallax && parallaxEl;
158 | const shouldRenderPagination =
159 | isModuleAvailable(modules, 'pagination') && pagination && pagination.el;
160 | const shouldRenderScrollbar =
161 | isModuleAvailable(modules, 'scrollbar') && scrollbar && scrollbar.el;
162 | const isNavigationModuleAvailable = isModuleAvailable(modules, 'navigation');
163 | const shouldRenderNextButton = isNavigationModuleAvailable && navigation && navigation.nextEl;
164 | const shouldRenderPrevButton = isNavigationModuleAvailable && navigation && navigation.prevEl;
165 |
166 | // No render if wrapper elements are not provided or when modules is empty
167 | if (!Swiper || !children || !ContainerEl || !WrapperEl) {
168 | return null;
169 | }
170 |
171 | // Validate children props
172 | if (!validateChildren(children)) {
173 | console.warn('Children should be react element or an array of react element!!');
174 |
175 | return null;
176 | }
177 |
178 | return (
179 |
180 | {shouldRenderParallax && renderParallax && renderParallax(props)}
181 | {Children.map(children, renderContent)}
182 | {shouldRenderPagination && renderPagination && renderPagination(props)}
183 | {shouldRenderScrollbar && renderScrollbar && renderScrollbar(props)}
184 | {shouldRenderNextButton && renderNextButton && renderNextButton(props)}
185 | {shouldRenderPrevButton && renderPrevButton && renderPrevButton(props)}
186 |
187 | );
188 | }
189 | );
190 |
191 | // Default props
192 | ReactIdSwiperCustom.defaultProps = {
193 | containerClass: 'swiper-container',
194 | wrapperClass: 'swiper-wrapper',
195 | slideClass: 'swiper-slide',
196 | ContainerEl: 'div',
197 | WrapperEl: 'div',
198 | renderScrollbar: ({ scrollbar }) =>
199 | scrollbar ? : null,
200 | renderPagination: ({ pagination }) =>
201 | pagination ? : null,
202 | renderPrevButton: ({ navigation }) =>
203 | navigation ? : null,
204 | renderNextButton: ({ navigation }) =>
205 | navigation ? : null,
206 | renderParallax: ({ parallaxEl }) =>
207 | parallaxEl ? (
208 |
209 | ) : null,
210 | modules: []
211 | };
212 |
213 | ReactIdSwiperCustom.displayName = 'ReactIdSwiper';
214 |
215 | export default ReactIdSwiperCustom;
216 |
--------------------------------------------------------------------------------
/src/ReactIdSwiper.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | forwardRef,
3 | Children,
4 | useEffect,
5 | useRef,
6 | cloneElement,
7 | isValidElement,
8 | ReactElement,
9 | useCallback
10 | } from 'react';
11 | import Swiper from 'swiper';
12 | import objectAssign from 'object-assign';
13 | import { ReactIdSwiperProps, SwiperInstance, SwiperRefNode } from './types';
14 | import { classNames, validateChildren, isReactElement, setRef } from './utils';
15 | import { useForkRef } from './hooks';
16 |
17 | const ReactIdSwiper = forwardRef((props, externalRef) => {
18 | const {
19 | activeSlideKey,
20 | ContainerEl,
21 | children,
22 | containerClass,
23 | navigation,
24 | noSwiping,
25 | pagination,
26 | parallax,
27 | parallaxEl,
28 | WrapperEl,
29 | wrapperClass,
30 | rebuildOnUpdate,
31 | renderScrollbar,
32 | renderPagination,
33 | renderPrevButton,
34 | renderNextButton,
35 | renderParallax,
36 | rtl,
37 | scrollbar,
38 | shouldSwiperUpdate,
39 | slideClass,
40 | loop
41 | } = props;
42 |
43 | // Define swiper instance ref
44 | const swiperInstanceRef = useRef(null);
45 |
46 | // Internal ref
47 | const swiperNodeRef = useRef(null);
48 |
49 | // Forked ref
50 | const ref = useForkRef(swiperNodeRef, externalRef);
51 |
52 | // Get current active slide key
53 | const getActiveSlideIndexFromProps = useCallback(() => {
54 | if (!activeSlideKey) {
55 | return null;
56 | }
57 |
58 | let activeSlideId = 0;
59 |
60 | // In loop mode first slide index should be 1
61 | let id = loop ? 1 : 0;
62 |
63 | Children.forEach(children, child => {
64 | if (isValidElement(child)) {
65 | if (child.key === activeSlideKey) {
66 | activeSlideId = id;
67 | }
68 |
69 | id += 1;
70 | }
71 | });
72 |
73 | return activeSlideId;
74 | }, [activeSlideKey, children, loop]);
75 |
76 | // Destroy swiper
77 | const destroySwiper = useCallback(() => {
78 | if (swiperInstanceRef.current !== null) {
79 | swiperInstanceRef.current.destroy(true, true);
80 |
81 | setRef(swiperInstanceRef, null);
82 | }
83 | }, []);
84 |
85 | // Initialize swiper
86 | const buildSwiper = useCallback(() => {
87 | if (swiperNodeRef.current && swiperInstanceRef.current === null) {
88 | setRef(swiperInstanceRef, new Swiper(swiperNodeRef.current, objectAssign({}, props)));
89 | }
90 | }, [props]);
91 |
92 | // Render slides
93 | const renderContent = (e: ReactElement) => {
94 | if (!isReactElement(e)) {
95 | return null;
96 | }
97 |
98 | const slideClassNames = [slideClass, e.props.className];
99 |
100 | if (noSwiping) {
101 | slideClassNames.push('swiper-no-swiping');
102 | }
103 |
104 | return cloneElement(e, {
105 | ...e.props,
106 | className: slideClassNames.join(' ').trim()
107 | });
108 | };
109 |
110 | // Destroy Swiper instance when component is unmounted
111 | useEffect(() => {
112 | return () => destroySwiper();
113 | }, [destroySwiper]);
114 |
115 | useEffect(() => {
116 | buildSwiper();
117 |
118 | if (swiperInstanceRef.current !== null) {
119 | if (rebuildOnUpdate) {
120 | destroySwiper();
121 |
122 | buildSwiper();
123 | } else if (shouldSwiperUpdate) {
124 | swiperInstanceRef.current.update();
125 | }
126 |
127 | const numSlides = swiperInstanceRef.current.slides.length;
128 |
129 | if (numSlides <= swiperInstanceRef.current.activeIndex) {
130 | const index = Math.max(numSlides - 1, 0);
131 |
132 | swiperInstanceRef.current.slideTo(index);
133 | }
134 |
135 | const slideToIndex = getActiveSlideIndexFromProps();
136 |
137 | if (slideToIndex !== null) {
138 | swiperInstanceRef.current.slideTo(slideToIndex);
139 | }
140 | }
141 | }, [
142 | destroySwiper,
143 | getActiveSlideIndexFromProps,
144 | rebuildOnUpdate,
145 | shouldSwiperUpdate,
146 | buildSwiper
147 | ]);
148 |
149 | // No render if wrapper elements are not provided
150 | if (!children || !ContainerEl || !WrapperEl) {
151 | return null;
152 | }
153 |
154 | // Validate children props
155 | if (!validateChildren(children)) {
156 | if (process.env.NODE_ENV !== 'production') {
157 | console.warn('Children should be react element or an array of react element!!');
158 | }
159 |
160 | return null;
161 | }
162 |
163 | return (
164 |
165 | {parallax && parallaxEl && renderParallax && renderParallax(props)}
166 | {Children.map(children, renderContent)}
167 | {pagination && pagination.el && renderPagination && renderPagination(props)}
168 | {scrollbar && scrollbar.el && renderScrollbar && renderScrollbar(props)}
169 | {navigation && navigation.nextEl && renderNextButton && renderNextButton(props)}
170 | {navigation && navigation.prevEl && renderPrevButton && renderPrevButton(props)}
171 |
172 | );
173 | });
174 |
175 | // Default props
176 | ReactIdSwiper.defaultProps = {
177 | containerClass: 'swiper-container',
178 | wrapperClass: 'swiper-wrapper',
179 | slideClass: 'swiper-slide',
180 | ContainerEl: 'div',
181 | WrapperEl: 'div',
182 | renderScrollbar: ({ scrollbar }) =>
183 | scrollbar ? : null,
184 | renderPagination: ({ pagination }) =>
185 | pagination ? : null,
186 | renderPrevButton: ({ navigation }) =>
187 | navigation ? : null,
188 | renderNextButton: ({ navigation }) =>
189 | navigation ? : null,
190 | renderParallax: ({ parallaxEl }) =>
191 | parallaxEl ? (
192 |
193 | ) : null
194 | };
195 |
196 | ReactIdSwiper.displayName = 'ReactIdSwiper';
197 |
198 | export default ReactIdSwiper;
199 |
--------------------------------------------------------------------------------
/src/hooks.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from 'react';
2 | import { setRef } from './utils';
3 | import { UseForkRef } from './types';
4 |
5 | export const useForkRef: UseForkRef = (refA, refB) =>
6 | useMemo(() => {
7 | if (refA == null && refB == null) {
8 | return null;
9 | }
10 |
11 | return refValue => {
12 | setRef(refA, refValue);
13 |
14 | setRef(refB, refValue);
15 | };
16 | }, [refA, refB]);
17 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import ReactIdSwiper from './ReactIdSwiper';
2 |
3 | // Types
4 | export {
5 | ReactIdSwiperProps,
6 | ReactIdSwiperRenderProps,
7 | SelectableElement,
8 | SwiperInstance,
9 | WrappedElementType,
10 | ReactIdSwiperChildren,
11 | SwiperModuleName,
12 | SwiperRefNode
13 | } from './types';
14 |
15 | // React-id-swiper
16 | export default ReactIdSwiper;
17 |
--------------------------------------------------------------------------------
/src/tests/ReactISwiper.custom.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, mount } from 'enzyme';
3 | import { Pagination, Navigation, Scrollbar, Parallax, Swiper } from 'swiper/js/swiper.esm';
4 | import ReactIdSwiper from '../ReactIdSwiper.custom';
5 | import { ReactIdSwiperCustomProps, WrappedElementType } from '../types';
6 |
7 | const renderSwiper = (params?: ReactIdSwiperCustomProps) =>
8 | render(
9 |
10 | Slide 1
11 | Slide 2
12 |
13 | );
14 |
15 | const mountSwiper = (params?: ReactIdSwiperCustomProps) =>
16 | mount(
17 |
18 | Slide 1
19 | Slide 2
20 |
21 | );
22 |
23 | describe('ReactIdSwiperCustom', () => {
24 | describe('defaultProps', () => {
25 | const component = mountSwiper({ Swiper });
26 |
27 | test('should have default props', () => {
28 | expect(component.prop('containerClass')).toEqual('swiper-container');
29 | expect(component.prop('wrapperClass')).toEqual('swiper-wrapper');
30 | expect(component.prop('slideClass')).toEqual('swiper-slide');
31 | expect(component.prop('ContainerEl')).toEqual('div');
32 | expect(component.prop('WrapperEl')).toEqual('div');
33 | });
34 | });
35 |
36 | describe('rendering', () => {
37 | test('it should not render component with no child', () => {
38 | const wrapper = mount();
39 |
40 | expect(wrapper.html()).toEqual(null);
41 | });
42 |
43 | test('it should not render component with invalid children props', () => {
44 | const multipleChildren = mount(
45 |
46 | Slide 1
47 | {'Slide 2' as any}
48 |
49 | );
50 |
51 | const singleChildren = mount(
52 | {'Slide 2' as any}
53 | );
54 |
55 | expect(multipleChildren.html()).toEqual(null);
56 | expect(singleChildren.html()).toEqual(null);
57 | });
58 | });
59 |
60 | describe('rendering snapshot', () => {
61 | // With default props
62 | describe('Default', () => {
63 | test('it should render default swiper', () => {
64 | expect(renderSwiper()).toMatchSnapshot();
65 | });
66 | });
67 |
68 | // Render pagination
69 | describe('Pagination', () => {
70 | const params = {
71 | Swiper,
72 | modules: [Pagination],
73 | pagination: {
74 | el: '.swiper-pagination',
75 | clickable: true
76 | }
77 | };
78 |
79 | test('it should render pagination', () => {
80 | expect(renderSwiper(params)).toMatchSnapshot();
81 | });
82 |
83 | test('it should render pagination with customized class name', () => {
84 | const customizedParams = {
85 | Swiper,
86 | pagination: {
87 | el: '.swiper-pagination.customized-swiper-pagination'
88 | },
89 | containerClass: 'customized-swiper-container' // Replace swiper-container with customized-swiper-container
90 | };
91 |
92 | expect(renderSwiper(customizedParams)).toMatchSnapshot();
93 | });
94 |
95 | test('it should render pagination with customized function', () => {
96 | const customizedParams = {
97 | ...params,
98 | renderCustomPagination: () =>
99 | };
100 |
101 | expect(renderSwiper(customizedParams)).toMatchSnapshot();
102 | });
103 | });
104 |
105 | // Render navigation
106 | describe('Navigation', () => {
107 | const params = {
108 | Swiper,
109 | modules: [Navigation],
110 | navigation: {
111 | nextEl: '.swiper-button-next',
112 | prevEl: '.swiper-button-prev'
113 | }
114 | };
115 |
116 | test('it should render navigation buttons', () => {
117 | expect(renderSwiper(params)).toMatchSnapshot();
118 | });
119 |
120 | test('it should render pagination with customized class name', () => {
121 | const customizedParams = {
122 | ...params,
123 | navigation: {
124 | nextEl: '.swiper-button-next.customized-swiper-button-next',
125 | prevEl: '.swiper-button-prev.customized-swiper-button-prev'
126 | }
127 | };
128 |
129 | expect(renderSwiper(customizedParams)).toMatchSnapshot();
130 | });
131 |
132 | test('it should render pagination with customized function', () => {
133 | const customizedParams = {
134 | ...params,
135 | renderCustomNextButton: () => Customized next button,
136 | renderCustomPrevButton: () => Customized prev button
137 | };
138 |
139 | expect(renderSwiper(customizedParams)).toMatchSnapshot();
140 | });
141 | });
142 |
143 | describe('Parallax', () => {
144 | const params = {
145 | Swiper,
146 | modules: [Parallax],
147 | parallax: true,
148 | parallaxEl: {
149 | el: '.parallax-bg',
150 | value: '-23%'
151 | }
152 | };
153 |
154 | test('it should render parallax', () => {
155 | expect(renderSwiper(params)).toMatchSnapshot();
156 | });
157 | });
158 |
159 | describe('Scrollbar', () => {
160 | const params = {
161 | Swiper,
162 | modules: [Scrollbar],
163 | scrollbar: {
164 | el: '.swiper-scrollbar',
165 | hide: true
166 | }
167 | };
168 |
169 | test('it should render scrollbar', () => {
170 | expect(renderSwiper(params)).toMatchSnapshot();
171 | });
172 | });
173 |
174 | describe('No swiping', () => {
175 | const params = {
176 | Swiper,
177 | noSwiping: true
178 | };
179 |
180 | test('it should render slide with swiper-no-swiping class name', () => {
181 | expect(renderSwiper(params)).toMatchSnapshot();
182 | });
183 | });
184 |
185 | describe('RTL', () => {
186 | const params = {
187 | Swiper,
188 | rtl: 'rtl'
189 | };
190 |
191 | test('it should render with rtl', () => {
192 | expect(renderSwiper(params)).toMatchSnapshot();
193 | });
194 | });
195 |
196 | describe('Custom container & wrapper', () => {
197 | interface Params extends ReactIdSwiperCustomProps {
198 | ContainerEl: WrappedElementType;
199 | WrapperEl: WrappedElementType;
200 | }
201 |
202 | const params: Params = {
203 | Swiper,
204 | ContainerEl: 'div',
205 | WrapperEl: 'div'
206 | };
207 |
208 | test('it should render with custom container & wrapper', () => {
209 | expect(renderSwiper(params)).toMatchSnapshot();
210 | });
211 | });
212 | });
213 | });
214 |
--------------------------------------------------------------------------------
/src/tests/ReactIdSwiper.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, mount } from 'enzyme';
3 | import ReactIdSwiper from '../ReactIdSwiper';
4 | import { ReactIdSwiperProps, WrappedElementType } from '../types';
5 |
6 | const renderSwiper = (params?: ReactIdSwiperProps) =>
7 | render(
8 |
9 | Slide 1
10 | Slide 2
11 |
12 | );
13 |
14 | const mountSwiper = (params?: ReactIdSwiperProps) =>
15 | mount(
16 |
17 | Slide 1
18 | Slide 2
19 |
20 | );
21 |
22 | describe('ReactIdSwiper', () => {
23 | describe('defaultProps', () => {
24 | const component = mountSwiper();
25 |
26 | test('should have default props', () => {
27 | expect(component.prop('containerClass')).toEqual('swiper-container');
28 | expect(component.prop('wrapperClass')).toEqual('swiper-wrapper');
29 | expect(component.prop('slideClass')).toEqual('swiper-slide');
30 | expect(component.prop('ContainerEl')).toEqual('div');
31 | expect(component.prop('WrapperEl')).toEqual('div');
32 | });
33 | });
34 |
35 | describe('rendering', () => {
36 | test('it should not render component with no child', () => {
37 | const wrapper = mount();
38 |
39 | expect(wrapper.html()).toEqual(null);
40 | });
41 |
42 | test('it should not render component with invalid children props', () => {
43 | const multipleChildren = mount(
44 |
45 | Slide 1
46 | {'Slide 2' as any}
47 |
48 | );
49 |
50 | const singleChildren = mount({'Slide 2' as any});
51 |
52 | expect(multipleChildren.html()).toEqual(null);
53 | expect(singleChildren.html()).toEqual(null);
54 | });
55 | });
56 |
57 | describe('rendering snapshot', () => {
58 | // With default props
59 | describe('Default', () => {
60 | test('it should render default swiper', () => {
61 | expect(renderSwiper()).toMatchSnapshot();
62 | });
63 | });
64 |
65 | // Render pagination
66 | describe('Pagination', () => {
67 | const params = {
68 | pagination: {
69 | el: '.swiper-pagination',
70 | clickable: true
71 | }
72 | };
73 |
74 | test('it should render pagination', () => {
75 | expect(renderSwiper(params)).toMatchSnapshot();
76 | });
77 |
78 | test('it should render pagination with customized class name', () => {
79 | const customizedParams = {
80 | pagination: {
81 | el: '.swiper-pagination.customized-swiper-pagination'
82 | },
83 | containerClass: 'customized-swiper-container' // Replace swiper-container with customized-swiper-container
84 | };
85 |
86 | expect(renderSwiper(customizedParams)).toMatchSnapshot();
87 | });
88 |
89 | test('it should render pagination with customized function', () => {
90 | const customizedParams = {
91 | ...params,
92 | renderCustomPagination: () =>
93 | };
94 |
95 | expect(renderSwiper(customizedParams)).toMatchSnapshot();
96 | });
97 | });
98 |
99 | // Render navigation
100 | describe('Navigation', () => {
101 | const params = {
102 | navigation: {
103 | nextEl: '.swiper-button-next',
104 | prevEl: '.swiper-button-prev'
105 | }
106 | };
107 |
108 | test('it should render navigation buttons', () => {
109 | expect(renderSwiper(params)).toMatchSnapshot();
110 | });
111 |
112 | test('it should render pagination with customized class name', () => {
113 | const customizedParams = {
114 | ...params,
115 | navigation: {
116 | nextEl: '.swiper-button-next.customized-swiper-button-next',
117 | prevEl: '.swiper-button-prev.customized-swiper-button-prev'
118 | }
119 | };
120 |
121 | expect(renderSwiper(customizedParams)).toMatchSnapshot();
122 | });
123 |
124 | test('it should render pagination with customized function', () => {
125 | const customizedParams = {
126 | ...params,
127 | renderCustomNextButton: () => Customized next button,
128 | renderCustomPrevButton: () => Customized prev button
129 | };
130 |
131 | expect(renderSwiper(customizedParams)).toMatchSnapshot();
132 | });
133 | });
134 |
135 | describe('Parallax', () => {
136 | const params = {
137 | parallax: true,
138 | parallaxEl: {
139 | el: '.parallax-bg',
140 | value: '-23%'
141 | }
142 | };
143 |
144 | test('it should render parallax', () => {
145 | expect(renderSwiper(params)).toMatchSnapshot();
146 | });
147 | });
148 |
149 | describe('Scrollbar', () => {
150 | const params = {
151 | scrollbar: {
152 | el: '.swiper-scrollbar',
153 | hide: true
154 | }
155 | };
156 |
157 | test('it should render scrollbar', () => {
158 | expect(renderSwiper(params)).toMatchSnapshot();
159 | });
160 | });
161 |
162 | describe('No swiping', () => {
163 | const params = {
164 | noSwiping: true
165 | };
166 |
167 | test('it should render slide with swiper-no-swiping class name', () => {
168 | expect(renderSwiper(params)).toMatchSnapshot();
169 | });
170 | });
171 |
172 | describe('RTL', () => {
173 | const params = {
174 | rtl: 'rtl'
175 | };
176 |
177 | test('it should render with rtl', () => {
178 | expect(renderSwiper(params)).toMatchSnapshot();
179 | });
180 | });
181 |
182 | describe('Custom container & wrapper', () => {
183 | interface Params {
184 | ContainerEl: WrappedElementType;
185 | WrapperEl: WrappedElementType;
186 | }
187 |
188 | const params: Params = {
189 | ContainerEl: 'div',
190 | WrapperEl: 'div'
191 | };
192 |
193 | test('it should render with custom container & wrapper', () => {
194 | expect(renderSwiper(params)).toMatchSnapshot();
195 | });
196 | });
197 | });
198 | });
199 |
--------------------------------------------------------------------------------
/src/tests/__snapshots__/ReactISwiper.custom.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`ReactIdSwiperCustom rendering snapshot Custom container & wrapper it should render with custom container & wrapper 1`] = `
4 |
7 |
10 |
13 | Slide 1
14 |
15 |
18 | Slide 2
19 |
20 |
21 |
22 | `;
23 |
24 | exports[`ReactIdSwiperCustom rendering snapshot Default it should render default swiper 1`] = `
25 |
28 |
31 |
34 | Slide 1
35 |
36 |
39 | Slide 2
40 |
41 |
42 |
43 | `;
44 |
45 | exports[`ReactIdSwiperCustom rendering snapshot Navigation it should render navigation buttons 1`] = `
46 |
49 |
52 |
55 | Slide 1
56 |
57 |
60 | Slide 2
61 |
62 |
63 |
66 |
69 |
70 | `;
71 |
72 | exports[`ReactIdSwiperCustom rendering snapshot Navigation it should render pagination with customized class name 1`] = `
73 |
76 |
79 |
82 | Slide 1
83 |
84 |
87 | Slide 2
88 |
89 |
90 |
93 |
96 |
97 | `;
98 |
99 | exports[`ReactIdSwiperCustom rendering snapshot Navigation it should render pagination with customized function 1`] = `
100 |
103 |
106 |
109 | Slide 1
110 |
111 |
114 | Slide 2
115 |
116 |
117 |
120 |
123 |
124 | `;
125 |
126 | exports[`ReactIdSwiperCustom rendering snapshot No swiping it should render slide with swiper-no-swiping class name 1`] = `
127 |
130 |
133 |
136 | Slide 1
137 |
138 |
141 | Slide 2
142 |
143 |
144 |
145 | `;
146 |
147 | exports[`ReactIdSwiperCustom rendering snapshot Pagination it should render pagination 1`] = `
148 |
151 |
154 |
157 | Slide 1
158 |
159 |
162 | Slide 2
163 |
164 |
165 |
168 |
169 | `;
170 |
171 | exports[`ReactIdSwiperCustom rendering snapshot Pagination it should render pagination with customized class name 1`] = `
172 |
175 |
178 |
181 | Slide 1
182 |
183 |
186 | Slide 2
187 |
188 |
189 |
190 | `;
191 |
192 | exports[`ReactIdSwiperCustom rendering snapshot Pagination it should render pagination with customized function 1`] = `
193 |
196 |
199 |
202 | Slide 1
203 |
204 |
207 | Slide 2
208 |
209 |
210 |
213 |
214 | `;
215 |
216 | exports[`ReactIdSwiperCustom rendering snapshot Parallax it should render parallax 1`] = `
217 |
220 |
224 |
227 |
230 | Slide 1
231 |
232 |
235 | Slide 2
236 |
237 |
238 |
239 | `;
240 |
241 | exports[`ReactIdSwiperCustom rendering snapshot RTL it should render with rtl 1`] = `
242 |
246 |
249 |
252 | Slide 1
253 |
254 |
257 | Slide 2
258 |
259 |
260 |
261 | `;
262 |
263 | exports[`ReactIdSwiperCustom rendering snapshot Scrollbar it should render scrollbar 1`] = `
264 |
267 |
270 |
273 | Slide 1
274 |
275 |
278 | Slide 2
279 |
280 |
281 |
284 |
285 | `;
286 |
--------------------------------------------------------------------------------
/src/tests/__snapshots__/ReactIdSwiper.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`ReactIdSwiper rendering snapshot Custom container & wrapper it should render with custom container & wrapper 1`] = `
4 |
7 |
10 |
13 | Slide 1
14 |
15 |
18 | Slide 2
19 |
20 |
21 |
22 | `;
23 |
24 | exports[`ReactIdSwiper rendering snapshot Default it should render default swiper 1`] = `
25 |
28 |
31 |
34 | Slide 1
35 |
36 |
39 | Slide 2
40 |
41 |
42 |
43 | `;
44 |
45 | exports[`ReactIdSwiper rendering snapshot Navigation it should render navigation buttons 1`] = `
46 |
49 |
52 |
55 | Slide 1
56 |
57 |
60 | Slide 2
61 |
62 |
63 |
66 |
69 |
70 | `;
71 |
72 | exports[`ReactIdSwiper rendering snapshot Navigation it should render pagination with customized class name 1`] = `
73 |
76 |
79 |
82 | Slide 1
83 |
84 |
87 | Slide 2
88 |
89 |
90 |
93 |
96 |
97 | `;
98 |
99 | exports[`ReactIdSwiper rendering snapshot Navigation it should render pagination with customized function 1`] = `
100 |
103 |
106 |
109 | Slide 1
110 |
111 |
114 | Slide 2
115 |
116 |
117 |
120 |
123 |
124 | `;
125 |
126 | exports[`ReactIdSwiper rendering snapshot No swiping it should render slide with swiper-no-swiping class name 1`] = `
127 |
130 |
133 |
136 | Slide 1
137 |
138 |
141 | Slide 2
142 |
143 |
144 |
145 | `;
146 |
147 | exports[`ReactIdSwiper rendering snapshot Pagination it should render pagination 1`] = `
148 |
151 |
154 |
157 | Slide 1
158 |
159 |
162 | Slide 2
163 |
164 |
165 |
168 |
169 | `;
170 |
171 | exports[`ReactIdSwiper rendering snapshot Pagination it should render pagination with customized class name 1`] = `
172 |
175 |
178 |
181 | Slide 1
182 |
183 |
186 | Slide 2
187 |
188 |
189 |
192 |
193 | `;
194 |
195 | exports[`ReactIdSwiper rendering snapshot Pagination it should render pagination with customized function 1`] = `
196 |
199 |
202 |
205 | Slide 1
206 |
207 |
210 | Slide 2
211 |
212 |
213 |
216 |
217 | `;
218 |
219 | exports[`ReactIdSwiper rendering snapshot Parallax it should render parallax 1`] = `
220 |
223 |
227 |
230 |
233 | Slide 1
234 |
235 |
238 | Slide 2
239 |
240 |
241 |
242 | `;
243 |
244 | exports[`ReactIdSwiper rendering snapshot RTL it should render with rtl 1`] = `
245 |
249 |
252 |
255 | Slide 1
256 |
257 |
260 | Slide 2
261 |
262 |
263 |
264 | `;
265 |
266 | exports[`ReactIdSwiper rendering snapshot Scrollbar it should render scrollbar 1`] = `
267 |
270 |
273 |
276 | Slide 1
277 |
278 |
281 | Slide 2
282 |
283 |
284 |
287 |
288 | `;
289 |
--------------------------------------------------------------------------------
/src/tests/setup.ts:
--------------------------------------------------------------------------------
1 | import { configure } from 'enzyme';
2 | import Adapter from 'enzyme-adapter-react-16';
3 |
4 | configure({ adapter: new Adapter() });
5 |
--------------------------------------------------------------------------------
/src/tests/utils.test.tsx:
--------------------------------------------------------------------------------
1 | import React, { FunctionComponent } from 'react';
2 | import { Pagination, Navigation, Scrollbar, Parallax } from 'swiper/js/swiper.esm';
3 | import { mount } from 'enzyme';
4 | import { classNames, validateChildren, isReactElement, isModuleAvailable } from '../utils';
5 |
6 | describe('utils', () => {
7 | describe('classnames', () => {
8 | test('it should return empty string when input is undefined', () => {
9 | expect(classNames(undefined)).toEqual('');
10 | });
11 |
12 | test('it should return class string when input is string', () => {
13 | expect(classNames('.slide-container')).toEqual('slide-container');
14 | });
15 |
16 | test('it should return class string when input is html element', () => {
17 | const el = document.createElement('div');
18 | el.className = 'slide-container';
19 |
20 | expect(classNames(el)).toEqual('slide-container');
21 | });
22 | });
23 |
24 | describe('validateChildren', () => {
25 | describe('when has more than 1 children prop', () => {
26 | test('it should return true if children props are valid', () => {
27 | const wrapper = mount(
28 |
29 |
Slide 1
30 |
Slide 2
31 |
32 | );
33 |
34 | expect(validateChildren(wrapper.props().children)).toEqual(true);
35 | });
36 |
37 | test('it should return false if children props are invalid', () => {
38 | const wrapper = mount(
39 |
40 |
Slide 1
41 | Slide 2
42 |
43 | );
44 |
45 | expect(validateChildren(wrapper.props().children)).toEqual(false);
46 | });
47 | });
48 |
49 | describe('when has only 1 child prop', () => {
50 | test('it should return true if children props are valid', () => {
51 | const wrapper = mount(
52 |
55 | );
56 |
57 | expect(validateChildren(wrapper.props().children)).toEqual(true);
58 | });
59 |
60 | test('it should return false if children props are invalid', () => {
61 | const wrapper = mount(Slide 2
);
62 |
63 | expect(validateChildren(wrapper.props().children)).toEqual(false);
64 | });
65 | });
66 | });
67 |
68 | describe('isReactElement', () => {
69 | test('it should return true when element is react element', () => {
70 | const DOMTypeElement = Hello world
;
71 | const CompositeTypeElement: FunctionComponent = () => Hello world
;
72 |
73 | expect(isReactElement(DOMTypeElement)).toEqual(true);
74 | expect(isReactElement()).toEqual(true);
75 | });
76 | });
77 |
78 | describe('isModuleAvailable', () => {
79 | const swiperModules = [Pagination, Navigation, Scrollbar, Parallax];
80 |
81 | test('it should return true when module is available', () => {
82 | expect(isModuleAvailable(swiperModules, 'pagination')).toEqual(true);
83 | });
84 |
85 | test('it should return false when module is unavailable', () => {
86 | expect(isModuleAvailable(swiperModules, 'virtual')).toEqual(false);
87 | });
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import { ReactElement, MutableRefObject } from 'react';
2 | import Swiper, {
3 | SwiperOptions,
4 | SelectableElement as SwiperSelectableElement,
5 | SwiperModule
6 | } from 'swiper';
7 |
8 | type Maybe = T | null;
9 |
10 | export type ReactIdSwiperRenderProps = (props: ReactIdSwiperProps) => Maybe;
11 |
12 | export type WrappedElementType = 'div' | 'section' | 'span';
13 |
14 | export type ReactIdSwiperChildren = ReactElement | ReactElement[];
15 |
16 | export type SwiperModules = (SwiperModule & { name: string })[];
17 |
18 | export interface ReactIdSwiperProps extends SwiperOptions {
19 | ContainerEl?: WrappedElementType;
20 | WrapperEl?: WrappedElementType;
21 | containerClass?: string;
22 | wrapperClass?: string;
23 | slideClass?: string;
24 | rebuildOnUpdate?: boolean;
25 | shouldSwiperUpdate?: boolean;
26 | activeSlideKey?: string;
27 | renderScrollbar?: ReactIdSwiperRenderProps;
28 | renderPagination?: ReactIdSwiperRenderProps;
29 | renderPrevButton?: ReactIdSwiperRenderProps;
30 | renderNextButton?: ReactIdSwiperRenderProps;
31 | renderParallax?: ReactIdSwiperRenderProps;
32 | rtl?: string;
33 | children?: ReactIdSwiperChildren;
34 | parallaxEl?: {
35 | el: string;
36 | value: string;
37 | };
38 | }
39 |
40 | export interface ReactIdSwiperCustomProps extends ReactIdSwiperProps {
41 | modules?: SwiperModules;
42 | Swiper: typeof Swiper;
43 | }
44 |
45 | export interface SwiperRefNode extends HTMLDivElement {
46 | swiper?: SwiperInstance;
47 | }
48 |
49 | export type SelectableElement = SwiperSelectableElement | undefined;
50 |
51 | export type SwiperInstance = Maybe;
52 |
53 | export type RefType = Maybe<((instance: Maybe) => void) | MutableRefObject>>;
54 |
55 | export type SetRef = (ref: RefType, value: V) => void;
56 |
57 | export type UseForkRef = (refA: RefType, refB: RefType) => Maybe<(v: T) => void>;
58 |
59 | export type SwiperModuleName =
60 | | 'navigation'
61 | | 'pagination'
62 | | 'scrollbar'
63 | | 'autoplay'
64 | | 'parallax'
65 | | 'lazy'
66 | | 'effect-fade'
67 | | 'effect-coverflow'
68 | | 'effect-flip'
69 | | 'effect-cube'
70 | | 'zoom'
71 | | 'keyboard'
72 | | 'mousewheel'
73 | | 'virtual'
74 | | 'hash-navigation'
75 | | 'history'
76 | | 'controller'
77 | | 'a11y';
78 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { isValidElement, Children, ReactElement } from 'react';
2 | import {
3 | SelectableElement,
4 | SwiperModules,
5 | SwiperModuleName,
6 | ReactIdSwiperChildren,
7 | SetRef
8 | } from './types';
9 |
10 | export const classNames = (el: SelectableElement): string => {
11 | if (typeof el === 'string') {
12 | return el.split('.').join(' ').trim();
13 | } else if (el instanceof HTMLElement) {
14 | return el.className;
15 | }
16 |
17 | return '';
18 | };
19 |
20 | export const validateChildren = (children: ReactIdSwiperChildren): boolean => {
21 | let isValid = true;
22 |
23 | if (Array.isArray(children)) {
24 | Children.forEach(children, (child: string | number | ReactElement) => {
25 | if (!isValidElement(child)) {
26 | isValid = false;
27 | }
28 | });
29 | } else {
30 | isValid = isValidElement(children);
31 | }
32 |
33 | return isValid;
34 | };
35 |
36 | export const isReactElement = (element: ReactElement): boolean =>
37 | isValidElement(element) &&
38 | (typeof element.type === 'string' ||
39 | typeof element.type === 'function' ||
40 | typeof element.type === 'object');
41 |
42 | export const isModuleAvailable = (
43 | modules: SwiperModules,
44 | moduleName: SwiperModuleName
45 | ): boolean => {
46 | let moduleAvailable = false;
47 |
48 | for (let i = 0; i < modules.length; i++) {
49 | if (modules[i].name === moduleName) {
50 | moduleAvailable = true;
51 | break;
52 | }
53 | }
54 |
55 | return moduleAvailable;
56 | };
57 |
58 | export const setRef: SetRef = (ref, value) => {
59 | if (typeof ref === 'function') {
60 | ref(value);
61 | } else if (ref) {
62 | ref.current = value;
63 | }
64 | };
65 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "esModuleInterop": true,
4 | "resolveJsonModule": true,
5 | "emitDecoratorMetadata":true,
6 | "experimentalDecorators":true,
7 | "lib": [
8 | "es6",
9 | "dom",
10 | "es2015"
11 | ],
12 | "target": "es5",
13 | "module": "commonjs",
14 | "declaration": true,
15 | "outDir": "./lib",
16 | "strict": true,
17 | "jsx":"react",
18 | "moduleResolution":"node",
19 | "allowSyntheticDefaultImports":true,
20 | "noImplicitAny":true,
21 | "noImplicitThis":true,
22 | "noImplicitReturns":true,
23 | "strictNullChecks":true,
24 | "noUnusedLocals": true,
25 | "noUnusedParameters": true
26 | },
27 | "include": ["src"],
28 | "exclude": ["node_modules", "**/tests/*"]
29 | }
--------------------------------------------------------------------------------
/tsconfig.standalone.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "declaration": false
5 | }
6 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path');
2 | const webpack = require('webpack');
3 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
4 |
5 | const files = [
6 | {
7 | outputName: 'react-id-swiper',
8 | entryName: 'ReactIdSwiper'
9 | },
10 | {
11 | outputName: 'react-id-swiper.min',
12 | entryName: 'ReactIdSwiper',
13 | minimizer: true
14 | }
15 | ];
16 |
17 | const PATHS = {
18 | src: resolve(__dirname, 'src'),
19 | output: resolve(__dirname, 'lib')
20 | };
21 |
22 | module.exports = files.map(({ entryName, outputName, minimizer }) => ({
23 | entry: `${PATHS.src}/${entryName}.tsx`,
24 | output: {
25 | path: PATHS.output,
26 | filename: `${outputName}.js`,
27 | libraryTarget: 'umd',
28 | library: 'ReactIdSwiper',
29 | auxiliaryComment: ''
30 | },
31 | resolve: {
32 | extensions: ['.tsx', '.ts', '.js'],
33 | modules: ['./src', 'node_modules']
34 | },
35 | resolveLoader: {
36 | moduleExtensions: ['-loader']
37 | },
38 | module: {
39 | rules: [
40 | {
41 | test: /\.ts(x)?$/,
42 | loader: 'awesome-typescript',
43 | include: [PATHS.src],
44 | options: {
45 | configFileName: 'tsconfig.standalone.json'
46 | }
47 | }
48 | ]
49 | },
50 | externals: {
51 | react: 'React',
52 | swiper: 'Swiper'
53 | },
54 | mode: 'production',
55 | optimization: {
56 | minimizer: minimizer
57 | ? [
58 | new UglifyJsPlugin({
59 | uglifyOptions: {
60 | output: {
61 | comments: false
62 | }
63 | }
64 | })
65 | ]
66 | : []
67 | },
68 | plugins: [
69 | new webpack.DefinePlugin({
70 | 'process.env': {
71 | NODE_ENV: JSON.stringify('production')
72 | }
73 | })
74 | ]
75 | }));
76 |
--------------------------------------------------------------------------------