├── .travis.yml ├── src ├── index.js ├── initial-state.js ├── default-props.js ├── dots.js ├── arrows.js ├── slider.js ├── track.js ├── mixins │ ├── trackHelper.js │ ├── event-handlers.js │ └── helpers.js └── inner-slider.js ├── CONTRIBUTING.markdown ├── docs ├── img │ └── react-slick │ │ ├── abstract01.jpg │ │ ├── abstract02.jpg │ │ ├── abstract03.jpg │ │ └── abstract04.jpg ├── index.jsx ├── docs.scss ├── routes.jsx ├── docs.jsx ├── index.html ├── _style.scss └── demos.jsx ├── .babelrc ├── .gitignore ├── examples ├── config.js ├── SimpleSlider.js ├── UnevenSetsFinite.js ├── UnevenSetsInfinite.js ├── AutoPlay.js ├── Rtl.js ├── PauseOnHover.js ├── Fade.js ├── FocusOnSelect.js ├── MultipleItems.js ├── LazyLoad.js ├── CenterMode.js ├── VariableWidth.js ├── CustomSlides.js ├── CustomPaging.js ├── SwipeToSlide.js ├── SlideChangeHooks.js ├── VerticalMode.js ├── AdaptiveHeight.js ├── VerticalSwipeToSlide.js ├── DynamicSlides.js ├── SlickGoTo.js ├── PreviousNextMethods.js ├── CustomArrows.js └── Responsive.js ├── ISSUE_TEMPLATE.md ├── test-setup.js ├── .npmignore ├── __tests__ ├── index.js ├── sample.js └── arrows.js ├── .jshintrc ├── .eslintrc ├── bower.json ├── webpack.config.dist.js ├── LICENSE ├── webpack.config.js ├── package.json ├── gulpfile.js └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./slider'); 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.markdown: -------------------------------------------------------------------------------- 1 | Submitting Pull Requests 2 | 3 | * Make sure auto generated files like dist and lib are not part of PR -------------------------------------------------------------------------------- /docs/img/react-slick/abstract01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eligolding/react-slick/master/docs/img/react-slick/abstract01.jpg -------------------------------------------------------------------------------- /docs/img/react-slick/abstract02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eligolding/react-slick/master/docs/img/react-slick/abstract02.jpg -------------------------------------------------------------------------------- /docs/img/react-slick/abstract03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eligolding/react-slick/master/docs/img/react-slick/abstract03.jpg -------------------------------------------------------------------------------- /docs/img/react-slick/abstract04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eligolding/react-slick/master/docs/img/react-slick/abstract04.jpg -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets":[ 3 | "react", 4 | ["es2015", {"loose": true}] 5 | ], 6 | "plugins": ["transform-object-assign", "transform-object-rest-spread"] 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | .sass-cache 4 | build 5 | demos/* 6 | demos1 7 | TODO.md 8 | npm-debug.log 9 | lib 10 | *.sublime-* 11 | .idea 12 | dist -------------------------------------------------------------------------------- /examples/config.js: -------------------------------------------------------------------------------- 1 | 2 | export const baseUrl = (process.env.NODE_ENV === 'production') ? 'https://s3.amazonaws.com/static.neostack.com/img/react-slick' : '/img/react-slick' 3 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Guidlines for posting new issue 2 | 3 | * Please replicate your issue with this [jsfiddle](https://jsfiddle.net/kirana/20bumb4g/) and provide a link to it along with the issue description -------------------------------------------------------------------------------- /test-setup.js: -------------------------------------------------------------------------------- 1 | window.matchMedia = window.matchMedia || function() { 2 | return { 3 | matches : false, 4 | addListener : function() {}, 5 | removeListener: function() {} 6 | }; 7 | }; -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | .sass-cache 3 | build 4 | demos 5 | demos1 6 | TODO.md 7 | test 8 | testlib 9 | bower.json 10 | gulpfile.js 11 | karma.conf.js 12 | LICENSE 13 | webpack.config.dist.js 14 | webpack.config.js 15 | ISSUE_TEMPLATE.md 16 | -------------------------------------------------------------------------------- /docs/index.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react' 4 | import ReactDOM from 'react-dom' 5 | import Docs from './docs' 6 | 7 | React.initializeTouchEvents && React.initializeTouchEvents(true); 8 | ReactDOM.render(, document.getElementById('rapp')); 9 | -------------------------------------------------------------------------------- /docs/docs.scss: -------------------------------------------------------------------------------- 1 | 2 | @import "foundation-apps/scss/foundation.scss"; 3 | $primary-color: #00558B; 4 | 5 | $slick-font-path: "/fonts/"; 6 | $slick-arrow-color: $primary-color; 7 | 8 | @import "slick-carousel/slick/slick.scss"; 9 | @import "slick-carousel/slick/slick-theme.scss"; 10 | @import "./style.scss"; 11 | -------------------------------------------------------------------------------- /__tests__/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import {shallow, mount} from 'enzyme'; 5 | import Slider from '../src/index'; 6 | 7 | describe('Slider', function() { 8 | it('should render', function() { 9 | const wrapper = shallow(
slide1
); 10 | expect(wrapper.contains(
slide1
)).toBe(true); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esnext": true, 3 | "strict": true, 4 | "globalstrict": true, 5 | "node": true, 6 | "mocha": true, 7 | "browser": true, 8 | "bitwise": true, 9 | "camelcase": true, 10 | "curly": true, 11 | "eqeqeq": true, 12 | "immed": true, 13 | "indent": 2, 14 | "latedef": true, 15 | "newcap": true, 16 | "noarg": true, 17 | "undef": true, 18 | "unused": true 19 | } -------------------------------------------------------------------------------- /docs/routes.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | var Router = require('react-router'); 5 | var Route = Router.Route; 6 | var Docs = require('./docs'); 7 | 8 | var path = (process.env.NODE_ENV === 'dev_docs') ? '/': '/opensource/react-slick'; 9 | var routes = ( 10 | 11 | 12 | ); 13 | 14 | module.exports = routes; 15 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-extra-parens": 0, 4 | "react/jsx-uses-vars": 1, 5 | "strict": 0, 6 | "quotes": [2, "single"], 7 | "no-underscore-dangle": 0, 8 | "space-infix-ops": 0, 9 | "no-alert": 0 10 | }, 11 | "ecmaFeatures": { 12 | "jsx": true, 13 | }, 14 | "env": { 15 | "node": true, 16 | "browser": true, 17 | "es6": true, 18 | "jasmine": true 19 | }, 20 | "parser": "babel-eslint", 21 | "plugins": [ 22 | "react" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /docs/docs.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react' 4 | import Demos from './demos' 5 | 6 | export default class Docs extends React.Component { 7 | render() { 8 | return ( 9 |
10 |
11 | React Slick 12 |
13 |
14 |
15 | 16 |
17 |
18 |
19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /__tests__/sample.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import React from 'react'; 3 | import {shallow, mount} from 'enzyme'; 4 | import Slider from '../src/index'; 5 | 6 | 7 | 8 | describe('basic test', function() { 9 | it('should add numbers', function() { 10 | expect(2 + 2).toBe(4); 11 | }); 12 | }); 13 | 14 | describe('sample enzyme test', function() { 15 | it('should render', function() { 16 | const wrapper = shallow(
slide1
); 17 | expect(wrapper.contains(
slide1
)).toBe(true); 18 | }); 19 | }); 20 | 21 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-slick", 3 | "main": "dist/react-slick.min.js", 4 | "version": "0.14.2", 5 | "homepage": "https://github.com/akiran/react-slick", 6 | "authors": [ 7 | "Kiran Abburi" 8 | ], 9 | "license": "MIT", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "slick-carousel": "~1.6.0" 19 | }, 20 | "devDependencies": { 21 | "react": "~0.12.1", 22 | "jquery": "~2.1.1", 23 | "should": "~4.3.0", 24 | "foundation-apps": "~1.0.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/SimpleSlider.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class SimpleSlider extends Component { 5 | render() { 6 | const settings = { 7 | dots: true, 8 | infinite: true, 9 | speed: 500, 10 | slidesToShow: 1, 11 | slidesToScroll: 1, 12 | }; 13 | return ( 14 |
15 |

Single Item

16 | 17 |

1

18 |

2

19 |

3

20 |

4

21 |

5

22 |

6

23 |
24 |
25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/UnevenSetsFinite.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class UnevenSetsFinite extends Component { 5 | render() { 6 | var settings = { 7 | dots: true, 8 | infinite: false, 9 | speed: 500, 10 | slidesToScroll: 4, 11 | slidesToShow: 4 12 | }; 13 | return ( 14 |
15 |

Uneven sets (finite)

16 | 17 |

1

18 |

2

19 |

3

20 |

4

21 |

5

22 |

6

23 |
24 |
25 | ); 26 | } 27 | } -------------------------------------------------------------------------------- /examples/UnevenSetsInfinite.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class UnevenSetsInfinite extends Component { 5 | render() { 6 | var settings = { 7 | dots: true, 8 | infinite: true, 9 | speed: 500, 10 | slidesToScroll: 4, 11 | slidesToShow: 4 12 | }; 13 | return ( 14 |
15 |

Uneven sets (infinite)

16 | 17 |

1

18 |

2

19 |

3

20 |

4

21 |

5

22 |

6

23 |
24 |
25 | ); 26 | } 27 | } -------------------------------------------------------------------------------- /examples/AutoPlay.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class AutoPlay extends Component { 5 | render() { 6 | const settings = { 7 | dots: true, 8 | infinite: true, 9 | slidesToShow: 3, 10 | slidesToScroll: 1, 11 | autoplay: true, 12 | autoplaySpeed: 2000 13 | }; 14 | return ( 15 |
16 |

Auto Play

17 | 18 |

1

19 |

2

20 |

3

21 |

4

22 |

5

23 |

6

24 |
25 |
26 | ); 27 | } 28 | } -------------------------------------------------------------------------------- /examples/Rtl.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | 5 | export default class Rtl extends Component { 6 | render() { 7 | const settings = { 8 | dots: true, 9 | infinite: true, 10 | slidesToShow: 3, 11 | slidesToScroll: 1, 12 | autoplay: true, 13 | autoplaySpeed: 2000, 14 | rtl: true 15 | }; 16 | return ( 17 |
18 |

Right to Left

19 | 20 |

1

21 |

2

22 |

3

23 |

4

24 |

5

25 |

6

26 |
27 |
28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/PauseOnHover.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class PauseOnHover extends Component { 5 | render() { 6 | var settings = { 7 | dots: true, 8 | infinite: true, 9 | slidesToShow: 3, 10 | slidesToScroll: 1, 11 | autoplay: true, 12 | autoplaySpeed: 2000, 13 | pauseOnHover: true 14 | }; 15 | return ( 16 |
17 |

Pause On Hover

18 | 19 |

1

20 |

2

21 |

3

22 |

4

23 |

5

24 |

6

25 |
26 |
27 | ); 28 | } 29 | } -------------------------------------------------------------------------------- /examples/Fade.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | import {baseUrl} from './config' 4 | 5 | export default class Fade extends Component { 6 | render() { 7 | const settings = { 8 | dots: true, 9 | fade: true, 10 | infinite: true, 11 | speed: 500, 12 | slidesToShow: 1, 13 | slidesToScroll: 1 14 | }; 15 | return ( 16 |
17 |

Fade

18 | 19 |
20 |
21 |
22 |
23 |
24 |
25 | ); 26 | } 27 | } -------------------------------------------------------------------------------- /examples/FocusOnSelect.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class FocusOnSelect extends Component { 5 | render() { 6 | const settings = { 7 | focusOnSelect: true, 8 | infinite: true, 9 | slidesToShow: 3, 10 | slidesToScroll: 1, 11 | speed: 500 12 | }; 13 | return ( 14 |
15 |

FocusOnSelect

16 |
Click on any slide to select and make it current slide
17 | 18 |

1

19 |

2

20 |

3

21 |

4

22 |

5

23 |

6

24 |
25 |
26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/MultipleItems.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class MultipleItems extends Component { 5 | render() { 6 | const settings = { 7 | dots: true, 8 | infinite: true, 9 | speed: 500, 10 | slidesToShow: 3, 11 | slidesToScroll: 3 12 | }; 13 | return ( 14 |
15 |

Multiple items

16 | 17 |

1

18 |

2

19 |

3

20 |

4

21 |

5

22 |

6

23 |

7

24 |

8

25 |

9

26 |
27 |
28 | ); 29 | } 30 | } -------------------------------------------------------------------------------- /examples/LazyLoad.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | import {baseUrl} from './config' 4 | 5 | export default class LazyLoad extends Component { 6 | render() { 7 | const settings = { 8 | dots: true, 9 | lazyLoad: true, 10 | infinite: true, 11 | speed: 500, 12 | slidesToShow: 1, 13 | slidesToScroll: 1, 14 | initialSlide: 2 15 | }; 16 | return ( 17 |
18 |

Lazy Load

19 | 20 |
21 |
22 |
23 |
24 |
25 |
26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/CenterMode.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class CenterMode extends Component { 5 | render() { 6 | const settings = { 7 | className: 'center', 8 | centerMode: true, 9 | infinite: true, 10 | centerPadding: '60px', 11 | slidesToShow: 3, 12 | speed: 500 13 | }; 14 | return ( 15 |
16 |

Center Mode

17 | 18 |

1

19 |

2

20 |

3

21 |

4

22 |

5

23 |

6

24 |

7

25 |

8

26 |

9

27 |
28 |
29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/VariableWidth.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class VariableWidth extends Component { 5 | render() { 6 | const settings = { 7 | className: 'slider variable-width', 8 | dots: true, 9 | infinite: true, 10 | centerMode: true, 11 | slidesToShow: 1, 12 | slidesToScroll: 1, 13 | variableWidth: true 14 | }; 15 | return ( 16 |
17 |

Variable width

18 | 19 |

100

20 |

200

21 |

75

22 |

300

23 |

225

24 |

175

25 |
26 |
27 | ); 28 | } 29 | } -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/CustomSlides.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | class CustomSlide extends Component { 5 | render() { 6 | const {index, ...props} = this.props 7 | return ( 8 |

{index}

9 | ) 10 | } 11 | } 12 | 13 | export default class SimpleSlider extends Component { 14 | render() { 15 | const settings = { 16 | dots: true, 17 | infinite: true, 18 | speed: 500, 19 | slidesToShow: 1, 20 | slidesToScroll: 1, 21 | }; 22 | return ( 23 |
24 |

Custom Slides

25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/CustomPaging.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | import {baseUrl} from './config' 4 | 5 | export default class CenterMode extends Component { 6 | render() { 7 | const settings = { 8 | customPaging: function(i) { 9 | return 10 | }, 11 | dots: true, 12 | dotsClass: 'slick-dots slick-thumb', 13 | infinite: true, 14 | speed: 500, 15 | slidesToShow: 1, 16 | slidesToScroll: 1 17 | }; 18 | return ( 19 |
20 |

Custom Paging

21 | 22 |
23 |
24 |
25 |
26 |
27 |
28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/SwipeToSlide.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class SwipeToSlide extends Component { 5 | render() { 6 | const settings = { 7 | className: 'center', 8 | infinite: true, 9 | centerPadding: '60px', 10 | slidesToShow: 5, 11 | swipeToSlide: true, 12 | afterChange: function (index) { 13 | console.log(`Slider Changed to: ${index + 1}, background: #222; color: #bada55`); 14 | } 15 | }; 16 | return ( 17 |
18 |

Swipe To Slide

19 | 20 |

1

21 |

2

22 |

3

23 |

4

24 |

5

25 |

6

26 |

7

27 |

8

28 |

9

29 |
30 |
31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/SlideChangeHooks.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class SlideChangeHooks extends Component { 5 | render() { 6 | const settings = { 7 | dots: true, 8 | infinite: true, 9 | speed: 500, 10 | slidesToShow: 1, 11 | slidesToScroll: 1, 12 | beforeChange: function (currentSlide, nextSlide) { 13 | console.log('before change', currentSlide, nextSlide); 14 | }, 15 | afterChange: function (currentSlide) { 16 | console.log('after change', currentSlide); 17 | }, 18 | }; 19 | return ( 20 |
21 |

beforeChange and afterChange hooks

22 | 23 |

1

24 |

2

25 |

3

26 |

4

27 |

5

28 |

6

29 |
30 |
31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/VerticalMode.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class VerticalMode extends Component { 5 | render() { 6 | const settings = { 7 | dots: true, 8 | infinite: true, 9 | slidesToShow: 3, 10 | slidesToScroll: 1, 11 | vertical: true, 12 | verticalSwiping: true, 13 | beforeChange: function (currentSlide, nextSlide) { 14 | console.log('before change', currentSlide, nextSlide); 15 | }, 16 | afterChange: function (currentSlide) { 17 | console.log('after change', currentSlide); 18 | }, 19 | }; 20 | return ( 21 |
22 |

Vertical Mode

23 | 24 |

1

25 |

2

26 |

3

27 |

4

28 |

5

29 |

6

30 |
31 |
32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/AdaptiveHeight.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class AdaptiveHeight extends Component { 5 | render() { 6 | var settings = { 7 | className: '', 8 | dots: true, 9 | infinite: true, 10 | slidesToShow: 1, 11 | slidesToScroll: 1, 12 | adaptiveHeight: true 13 | }; 14 | return ( 15 |
16 |

Adaptive height

17 | 18 |

1

19 |
20 |

2

21 |

Hello

22 |
23 |
24 |

3

25 |

See ....

26 |

Height is adaptive

27 |
28 |
29 |

4

30 |
31 |
32 |

5

33 |
34 |
35 |

6

36 |
37 |
38 |
39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/VerticalSwipeToSlide.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class VerticalSwipeToSlide extends Component { 5 | render() { 6 | const settings = { 7 | dots: true, 8 | infinite: true, 9 | slidesToShow: 3, 10 | slidesToScroll: 1, 11 | vertical: true, 12 | verticalSwiping: true, 13 | swipeToSlide: true, 14 | beforeChange: function (currentSlide, nextSlide) { 15 | console.log('before change', currentSlide, nextSlide); 16 | }, 17 | afterChange: function (currentSlide) { 18 | console.log('after change', currentSlide); 19 | }, 20 | }; 21 | return ( 22 |
23 |

Vertical Mode with Swipe To Slide

24 | 25 |

1

26 |

2

27 |

3

28 |

4

29 |

5

30 |

6

31 |
32 |
33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/DynamicSlides.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class DynamicSlides extends Component { 5 | constructor(props) { 6 | super(props) 7 | this.state = { 8 | slides: [1, 2, 3, 4, 5, 6] 9 | } 10 | this.click = this.click.bind(this) 11 | } 12 | click() { 13 | const {slides} = this.state 14 | this.setState({ 15 | slides: slides.length === 6 ? [1, 2, 3, 4, 5, 6, 7, 8, 9] : [1, 2, 3, 4, 5, 6] 16 | }) 17 | } 18 | render() { 19 | const settings = { 20 | dots: true, 21 | infinite: true, 22 | speed: 500, 23 | slidesToShow: 3, 24 | slidesToScroll: 3 25 | }; 26 | return ( 27 |
28 |

Dynamic slides

29 | 30 | 31 | {this.state.slides.map(function (slide) { 32 | return

{slide}

33 | })} 34 |
35 |
36 | ); 37 | } 38 | } -------------------------------------------------------------------------------- /src/initial-state.js: -------------------------------------------------------------------------------- 1 | var initialState = { 2 | animating: false, 3 | dragging: false, 4 | autoPlayTimer: null, 5 | currentDirection: 0, 6 | currentLeft: null, 7 | currentSlide: 0, 8 | direction: 1, 9 | listWidth: null, 10 | listHeight: null, 11 | // loadIndex: 0, 12 | slideCount: null, 13 | slideWidth: null, 14 | slideHeight: null, 15 | // sliding: false, 16 | // slideOffset: 0, 17 | swipeLeft: null, 18 | touchObject: { 19 | startX: 0, 20 | startY: 0, 21 | curX: 0, 22 | curY: 0 23 | }, 24 | 25 | lazyLoadedList: [], 26 | 27 | // added for react 28 | initialized: false, 29 | edgeDragged: false, 30 | swiped: false, // used by swipeEvent. differentites between touch and swipe. 31 | trackStyle: {}, 32 | trackWidth: 0 33 | 34 | // Removed 35 | // transformsEnabled: false, 36 | // $nextArrow: null, 37 | // $prevArrow: null, 38 | // $dots: null, 39 | // $list: null, 40 | // $slideTrack: null, 41 | // $slides: null, 42 | }; 43 | 44 | module.exports = initialState; 45 | -------------------------------------------------------------------------------- /webpack.config.dist.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | 4 | module.exports = { 5 | entry: './src/index', 6 | 7 | output: { 8 | library: 'Slider', 9 | libraryTarget: 'umd', 10 | path: path.join(__dirname, 'dist') 11 | }, 12 | 13 | module: { 14 | loaders: [ 15 | {test: /\.jsx$/, loaders: ['babel']}, 16 | {test: /\.js$/, loaders: ['babel'], exclude: /node_modules/} 17 | ] 18 | }, 19 | 20 | resolve: { 21 | extensions: ['', '.js', '.jsx'] 22 | }, 23 | 24 | externals: [ 25 | { 26 | 'react': { 27 | root: 'React', 28 | commonjs2: 'react', 29 | commonjs: 'react', 30 | amd: 'react' 31 | }, 32 | 'react-dom': { 33 | root: 'ReactDOM', 34 | commonjs2: 'react-dom', 35 | commonjs: 'react-dom', 36 | amd: 'react-dom' 37 | } 38 | } 39 | ], 40 | 41 | node: { 42 | Buffer: false 43 | }, 44 | 45 | plugins: [ 46 | new webpack.DefinePlugin({ 47 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) 48 | }) 49 | ] 50 | }; 51 | -------------------------------------------------------------------------------- /examples/SlickGoTo.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | import {baseUrl} from './config' 4 | 5 | export default class SlickGoTo extends Component { 6 | constructor(props) { 7 | super(props) 8 | this.changeHandler = this.changeHandler.bind(this) 9 | } 10 | changeHandler(e) { 11 | this.refs.slider.slickGoTo(e.target.value) 12 | } 13 | render() { 14 | const settings = { 15 | dots: false, 16 | infinite: true, 17 | speed: 500, 18 | slidesToShow: 1, 19 | slidesToScroll: 1, 20 | }; 21 | return ( 22 |
23 |

Slick Go To

24 | 25 | 26 |
27 |
28 |
29 |
30 |
31 |
32 | ); 33 | } 34 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Kiran Abburi 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 | 23 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | var autoprefixer = require('autoprefixer-core'); 4 | 5 | module.exports = { 6 | devtool: '#inline-source-map', 7 | entry: { 8 | 'docs.js': [ 9 | './docs/index.jsx', 10 | // 'webpack/hot/only-dev-server', 11 | // 'webpack-dev-server/client?http://localhost:8000' 12 | ] 13 | }, 14 | output: { 15 | path: path.join(__dirname, 'build'), 16 | filename: '[name]' 17 | }, 18 | module: { 19 | loaders: [ 20 | {test: /\.jsx$/, loaders: ['babel']}, 21 | {test: /\.js$/, loaders: ['babel'], exclude: /node_modules/}, 22 | { 23 | test: /\.scss$/, 24 | loader: 'style!css!sass?outputStyle=expanded&' + 'includePaths[]=' + 25 | (path.resolve(__dirname, './node_modules')) 26 | }, 27 | { test: /\.md$/, loader: 'html!markdown' } 28 | ] 29 | }, 30 | postcss: [ autoprefixer({ browsers: ['last 2 version'] }) ], 31 | resolve: { 32 | extensions: ['', '.js', '.jsx'] 33 | }, 34 | plugins: [ 35 | // new webpack.HotModuleReplacementPlugin(), 36 | new webpack.NoErrorsPlugin(), 37 | new webpack.IgnorePlugin(/vertx/) 38 | ] 39 | }; 40 | -------------------------------------------------------------------------------- /examples/PreviousNextMethods.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class PreviousNextMethods extends Component { 5 | constructor(props) { 6 | super(props) 7 | this.next = this.next.bind(this) 8 | this.previous = this.previous.bind(this) 9 | } 10 | next() { 11 | this.slider.slickNext() 12 | } 13 | previous() { 14 | this.slider.slickPrev() 15 | } 16 | render() { 17 | const settings = { 18 | dots: true, 19 | infinite: true, 20 | speed: 500, 21 | slidesToShow: 1, 22 | slidesToScroll: 1, 23 | }; 24 | return ( 25 |
26 |

Previous and Next methods

27 | this.slider = c } {...settings}> 28 |

1

29 |

2

30 |

3

31 |

4

32 |

5

33 |

6

34 |
35 |
36 | 37 | 38 |
39 |
40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/default-props.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | var defaultProps = { 4 | className: '', 5 | accessibility: true, 6 | adaptiveHeight: false, 7 | arrows: true, 8 | autoplay: false, 9 | autoplaySpeed: 3000, 10 | centerMode: false, 11 | centerPadding: '50px', 12 | cssEase: 'ease', 13 | customPaging: function(i) { 14 | return ; 15 | }, 16 | dots: false, 17 | dotsClass: 'slick-dots', 18 | draggable: true, 19 | easing: 'linear', 20 | edgeFriction: 0.35, 21 | fade: false, 22 | focusOnSelect: false, 23 | infinite: true, 24 | initialSlide: 0, 25 | lazyLoad: false, 26 | pauseOnHover: true, 27 | responsive: null, 28 | rtl: false, 29 | slide: 'div', 30 | slidesToShow: 1, 31 | slidesToScroll: 1, 32 | speed: 500, 33 | swipe: true, 34 | swipeToSlide: false, 35 | touchMove: true, 36 | touchThreshold: 5, 37 | useCSS: true, 38 | variableWidth: false, 39 | vertical: false, 40 | waitForAnimate: true, 41 | afterChange: null, 42 | beforeChange: null, 43 | edgeEvent: null, 44 | init: null, 45 | swipeEvent: null, 46 | // nextArrow, prevArrow are react componets 47 | nextArrow: null, 48 | prevArrow: null 49 | }; 50 | 51 | module.exports = defaultProps; 52 | -------------------------------------------------------------------------------- /examples/CustomArrows.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | function SampleNextArrow(props) { 5 | const {className, style, onClick} = props 6 | return ( 7 |
12 | ); 13 | } 14 | 15 | function SamplePrevArrow(props) { 16 | const {className, style, onClick} = props 17 | return ( 18 |
23 | ); 24 | } 25 | 26 | 27 | export default class CustomArrows extends Component { 28 | render() { 29 | const settings = { 30 | dots: true, 31 | infinite: true, 32 | slidesToShow: 3, 33 | slidesToScroll: 1, 34 | nextArrow: , 35 | prevArrow: 36 | }; 37 | return ( 38 |
39 |

Custom Arrows

40 | 41 |

1

42 |

2

43 |

3

44 |

4

45 |

5

46 |

6

47 |
48 |
49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/Responsive.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import Slider from '../src/slider' 3 | 4 | export default class Responsive extends Component { 5 | render() { 6 | var settings = { 7 | dots: true, 8 | infinite: false, 9 | speed: 500, 10 | slidesToShow: 4, 11 | slidesToScroll: 4, 12 | initialSlide: 0, 13 | responsive: [{ 14 | breakpoint: 1024, 15 | settings: { 16 | slidesToShow: 3, 17 | slidesToScroll: 3, 18 | infinite: true, 19 | dots: true 20 | } 21 | }, { 22 | breakpoint: 600, 23 | settings: { 24 | slidesToShow: 2, 25 | slidesToScroll: 2, 26 | initialSlide: 2 27 | } 28 | }, { 29 | breakpoint: 480, 30 | settings: { 31 | slidesToShow: 1, 32 | slidesToScroll: 1 33 | } 34 | }] 35 | }; 36 | return ( 37 |
38 |

Responsive

39 | 40 |

1

41 |

2

42 |

3

43 |

4

44 |

5

45 |

6

46 |

7

47 |

8

48 |
49 |
50 | ); 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /src/dots.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import classnames from 'classnames'; 5 | 6 | var getDotCount = function (spec) { 7 | var dots; 8 | dots = Math.ceil(spec.slideCount / spec.slidesToScroll); 9 | return dots; 10 | }; 11 | 12 | 13 | export class Dots extends React.Component { 14 | clickHandler(options, e) { 15 | // In Autoplay the focus stays on clicked button even after transition 16 | // to next slide. That only goes away by click somewhere outside 17 | e.preventDefault(); 18 | this.props.clickHandler(options); 19 | } 20 | render() { 21 | 22 | var dotCount = getDotCount({ 23 | slideCount: this.props.slideCount, 24 | slidesToScroll: this.props.slidesToScroll 25 | }); 26 | 27 | // Apply join & split to Array to pre-fill it for IE8 28 | // 29 | // Credit: http://stackoverflow.com/a/13735425/1849458 30 | var dots = Array.apply(null, Array(dotCount + 1).join('0').split('')).map((x, i) => { 31 | 32 | var leftBound = (i * this.props.slidesToScroll); 33 | var rightBound = (i * this.props.slidesToScroll) + (this.props.slidesToScroll - 1); 34 | var className = classnames({ 35 | 'slick-active': (this.props.currentSlide >= leftBound) && (this.props.currentSlide <= rightBound) 36 | }); 37 | 38 | var dotOptions = { 39 | message: 'dots', 40 | index: i, 41 | slidesToScroll: this.props.slidesToScroll, 42 | currentSlide: this.props.currentSlide 43 | }; 44 | 45 | var onClick = this.clickHandler.bind(this, dotOptions); 46 | 47 | return ( 48 |
  • 49 | {React.cloneElement(this.props.customPaging(i), {onClick})} 50 |
  • 51 | ); 52 | }); 53 | 54 | return ( 55 |
      56 | {dots} 57 |
    58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /docs/_style.scss: -------------------------------------------------------------------------------- 1 | h3 { 2 | background: $primary-color; 3 | color: #fff; 4 | font-size: 36px; 5 | line-height: 100px; 6 | margin: 10px; 7 | padding: 2%; 8 | position: relative; 9 | text-align: center; 10 | } 11 | .variable-width .slick-slide p { 12 | background: $primary-color; 13 | height: 100px; 14 | color: #fff; 15 | margin: 5px; 16 | line-height: 100px; 17 | text-align: center; 18 | } 19 | .center .slick-center h3 { 20 | color: #e67e22; 21 | opacity: 1; 22 | transform: scale(1.08); 23 | } 24 | .center h3{ 25 | opacity: 0.8; 26 | transition: all 300ms ease; 27 | } 28 | .content { 29 | padding: 20px; 30 | margin: auto; 31 | width: 90%; 32 | } 33 | .slick-slide .image { 34 | padding: 10px; 35 | } 36 | .slick-slide img { 37 | border: 5px solid #FFF; 38 | display: block; 39 | margin: auto; 40 | } 41 | .slick-slide img.slick-loading { 42 | border: 0 43 | } 44 | .slick-slider { 45 | margin: 30px auto 50px; 46 | } 47 | .slick-dots { 48 | margin-left: 0; 49 | } 50 | .slick-thumb { 51 | bottom: -45px; 52 | li { 53 | width: 60px; 54 | height: 45px; 55 | img { 56 | filter: grayscale(100%); 57 | } 58 | &.slick-active { 59 | img { 60 | filter: grayscale(0%); 61 | } 62 | } 63 | } 64 | } 65 | @media (max-width: 768px) { 66 | h3 { 67 | font-size:24px; 68 | } 69 | .center { 70 | margin-left: -40px; 71 | margin-right: -40px; 72 | } 73 | .center .slick-center h3 { 74 | color: #e67e22; 75 | opacity: 1; 76 | transform: scale(1); 77 | } 78 | .center h3 { 79 | opacity: 0.8; 80 | transform: scale(0.95); 81 | transition: all 300ms ease; 82 | } 83 | } 84 | // just like in the regular library, we cannot simply have a vertical 85 | // slider fill available space like in the horizontal case 86 | // 87 | // we have to therefore set the height of a slide in a vertical slider 88 | // explicitly in CSS or otherwise 89 | .slick-vertical .slick-slide { 90 | height: 180px; 91 | } 92 | -------------------------------------------------------------------------------- /__tests__/arrows.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Arrow component tests 3 | */ 4 | 5 | sinon.stub(console, 'error'); 6 | 7 | import {render, shallow} from 'enzyme'; 8 | import React from 'react'; 9 | import sinon from 'sinon'; 10 | 11 | import { NextArrow, PrevArrow } from '../src/arrows'; 12 | 13 | function CustomArrow(props) { 14 | return ( 15 | 19 | ); 20 | } 21 | 22 | describe('Previous arrows', () => { 23 | it('should render arrow', () => { 24 | const wrapper = shallow(); 25 | expect(wrapper.find('button')).toHaveLength(1); 26 | }); 27 | 28 | it('should not result in errors', () => { 29 | shallow(); 30 | 31 | expect(console.error.called).toBe(false); 32 | }); 33 | 34 | it('should pass slide data to custom arrow', () => { 35 | let elAttributes; 36 | let arr = 37 | 38 | const wrapper = render(); 39 | 40 | elAttributes = wrapper.find('.sample')[0].attribs; 41 | expect(elAttributes['data-currentslide']).toBe('3'); 42 | expect(elAttributes['data-slidecount']).toBe('5'); 43 | }); 44 | }); 45 | 46 | describe('Next arrows', () => { 47 | it('should render arrow', () => { 48 | const wrapper = shallow(); 49 | expect(wrapper.find('button')).toHaveLength(1); 50 | }); 51 | 52 | it('should not result in errors', () => { 53 | shallow(); 54 | 55 | expect(console.error.called).toBe(false); 56 | }); 57 | 58 | it('should pass slide data to custom arrow', () => { 59 | let elAttributes; 60 | let arr = 61 | 62 | const wrapper = render(); 63 | 64 | elAttributes = wrapper.find('.sample')[0].attribs; 65 | expect(elAttributes['data-currentslide']).toBe('6'); 66 | expect(elAttributes['data-slidecount']).toBe('9'); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /docs/demos.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import Slider from '../src/slider'; 5 | 6 | import SimpleSlider from '../examples/SimpleSlider' 7 | import SlideChangeHooks from '../examples/SlideChangeHooks' 8 | import MultipleItems from '../examples/MultipleItems' 9 | import Responsive from '../examples/Responsive' 10 | import UnevenSetsInfinite from '../examples/UnevenSetsInfinite' 11 | import UnevenSetsFinite from '../examples/UnevenSetsFinite' 12 | import CenterMode from '../examples/CenterMode' 13 | import FocusOnSelect from '../examples/FocusOnSelect' 14 | import AutoPlay from '../examples/AutoPlay' 15 | import PauseOnHover from '../examples/PauseOnHover' 16 | import Rtl from '../examples/Rtl' 17 | import VariableWidth from '../examples/VariableWidth' 18 | import AdaptiveHeight from '../examples/AdaptiveHeight' 19 | import LazyLoad from '../examples/LazyLoad' 20 | import Fade from '../examples/Fade' 21 | import SlickGoTo from '../examples/SlickGoTo' 22 | import CustomArrows from '../examples/CustomArrows' 23 | import PreviousNextMethods from '../examples/PreviousNextMethods' 24 | import DynamicSlides from '../examples/DynamicSlides' 25 | import VerticalMode from '../examples/VerticalMode' 26 | import SwipeToSlide from '../examples/SwipeToSlide' 27 | import VerticalSwipeToSlide from '../examples/VerticalSwipeToSlide' 28 | import CustomPaging from '../examples/CustomPaging' 29 | import CustomSlides from '../examples/CustomSlides' 30 | 31 | export default class App extends React.Component { 32 | render() { 33 | return ( 34 |
    35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
    61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/arrows.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import classnames from 'classnames'; 5 | import Helpers from './mixins/helpers'; 6 | 7 | export class PrevArrow extends React.Component { 8 | clickHandler(options, e) { 9 | if (e) { e.preventDefault(); } 10 | this.props.clickHandler(options, e); 11 | } 12 | render() { 13 | var prevClasses = {'slick-arrow': true, 'slick-prev': true}; 14 | var prevHandler = this.clickHandler.bind(this, {message: 'previous'}); 15 | 16 | if (!this.props.infinite && (this.props.currentSlide === 0 || this.props.slideCount <= this.props.slidesToShow)) { 17 | prevClasses['slick-disabled'] = true; 18 | prevHandler = null; 19 | } 20 | 21 | var prevArrowProps = { 22 | key: '0', 23 | 'data-role': 'none', 24 | className: classnames(prevClasses), 25 | style: {display: 'block'}, 26 | onClick: prevHandler 27 | }; 28 | var customProps = { 29 | currentSlide: this.props.currentSlide, 30 | slideCount: this.props.slideCount 31 | }; 32 | var prevArrow; 33 | 34 | if (this.props.prevArrow) { 35 | prevArrow = React.cloneElement(this.props.prevArrow, { ...prevArrowProps, ...customProps }); 36 | } else { 37 | prevArrow = ; 38 | } 39 | 40 | return prevArrow; 41 | } 42 | } 43 | 44 | 45 | export class NextArrow extends React.Component { 46 | clickHandler(options, e) { 47 | if (e) { e.preventDefault(); } 48 | this.props.clickHandler(options, e); 49 | } 50 | render() { 51 | var nextClasses = {'slick-arrow': true, 'slick-next': true}; 52 | var nextHandler = this.clickHandler.bind(this, {message: 'next'}); 53 | 54 | if (!Helpers.canGoNext(this.props)) { 55 | nextClasses['slick-disabled'] = true; 56 | nextHandler = null; 57 | } 58 | 59 | var nextArrowProps = { 60 | key: '1', 61 | 'data-role': 'none', 62 | className: classnames(nextClasses), 63 | style: {display: 'block'}, 64 | onClick: nextHandler 65 | }; 66 | var customProps = { 67 | currentSlide: this.props.currentSlide, 68 | slideCount: this.props.slideCount 69 | }; 70 | var nextArrow; 71 | 72 | if (this.props.nextArrow) { 73 | nextArrow = React.cloneElement(this.props.nextArrow, { ...nextArrowProps, ...customProps }); 74 | } else { 75 | nextArrow = ; 76 | } 77 | 78 | return nextArrow; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-slick", 3 | "version": "0.14.10", 4 | "description": " React port of slick carousel", 5 | "main": "./lib", 6 | "scripts": { 7 | "start": "gulp server", 8 | "prepublish": "babel ./src --out-dir ./lib && gulp dist", 9 | "test": "jest", 10 | "dev-test": "jest --watch", 11 | "lint": "eslint src" 12 | }, 13 | "author": "Kiran Abburi", 14 | "license": "MIT", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/akiran/react-slick" 18 | }, 19 | "keywords": [ 20 | "slick", 21 | "carousel", 22 | "Image slider", 23 | "orbit", 24 | "slider", 25 | "react-component" 26 | ], 27 | "devDependencies": { 28 | "autoprefixer-core": "^6.0.1", 29 | "babel-cli": "^6.16.0", 30 | "babel-core": "^6.16.0", 31 | "babel-eslint": "^7.0.0", 32 | "babel-jest": "^19.0.0", 33 | "babel-loader": "^6.2.5", 34 | "babel-plugin-transform-object-assign": "^6.8.0", 35 | "babel-polyfill": "^6.16.0", 36 | "babel-preset-airbnb": "^2.1.1", 37 | "babel-preset-es2015": "^6.16.0", 38 | "babel-preset-react": "^6.16.0", 39 | "css-loader": "^0.28.0", 40 | "deepmerge": "^1.1.0", 41 | "del": "^2.2.2", 42 | "enzyme": "^2.8.2", 43 | "es5-shim": "^4.5.9", 44 | "eslint": "^3.6.1", 45 | "eslint-plugin-react": "^6.3.0", 46 | "express": "^4.14.0", 47 | "foundation-apps": "^1.2.0", 48 | "gulp": "^3.9.1", 49 | "gulp-sass": "^3.1.0", 50 | "jasmine-core": "^2.5.2", 51 | "jest": "^19.0.2", 52 | "json-loader": "^0.5.4", 53 | "node-sass": "^4.5.2", 54 | "postcss-loader": "^1.3.3", 55 | "react": "^15.3.2", 56 | "react-addons-test-utils": "^15.3.2", 57 | "react-dom": "^15.3.2", 58 | "react-test-renderer": "^15.5.4", 59 | "run-sequence": "^1.2.2", 60 | "sass-loader": "^6.0.3", 61 | "sinon": "^2.1.0", 62 | "style-loader": "^0.16.1", 63 | "webpack": "^1.13.2", 64 | "webpack-dev-server": "^1.16.1" 65 | }, 66 | "dependencies": { 67 | "classnames": "^2.2.5", 68 | "create-react-class": "^15.5.2", 69 | "json2mq": "^0.2.0", 70 | "object-assign": "^4.1.0", 71 | "slick-carousel": "^1.6.0", 72 | "enquire.js": "^2.1.6" 73 | }, 74 | "peerDependencies": { 75 | "react": "^0.14.0 || ^15.0.1", 76 | "react-dom": "^0.14.0 || ^15.0.1" 77 | }, 78 | "jest": { 79 | "setupFiles": [ 80 | "./test-setup.js" 81 | ] 82 | }, 83 | "npmName": "react-slick", 84 | "npmFileMap": [ 85 | { 86 | "basePath": "/dist/", 87 | "files": [ 88 | "*.js" 89 | ] 90 | } 91 | ], 92 | "bugs": { 93 | "url": "https://github.com/akiran/react-slick/issues" 94 | }, 95 | "homepage": "https://github.com/akiran/react-slick" 96 | } 97 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var del = require('del'); 5 | var sass = require('gulp-sass'); 6 | var webpack = require('webpack'); 7 | var WebpackDevServer = require('webpack-dev-server'); 8 | var runSequence = require('run-sequence'); 9 | var assign = require('object-assign'); 10 | 11 | gulp.task('default', ['watch', 'server']); 12 | 13 | gulp.task('clean', function () { 14 | return del(['./build/*']); 15 | }); 16 | 17 | gulp.task('copy', function () { 18 | gulp.src('./docs/index.html') 19 | .pipe(gulp.dest('./build')); 20 | gulp.src('./docs/img/**/*') 21 | .pipe(gulp.dest('./build/img')); 22 | gulp.src('./node_modules/slick-carousel/slick/fonts/*') 23 | .pipe(gulp.dest('./build/fonts')); 24 | return gulp.src('./node_modules/slick-carousel/slick/ajax-loader.gif') 25 | .pipe(gulp.dest('./build')); 26 | }); 27 | 28 | gulp.task('sass', function () { 29 | return gulp.src(['./docs/**/*.{scss,sass}']) 30 | .pipe(sass({ includePaths: ['bower_components', 'node_modules'], errLogToConsole: true})) 31 | .pipe(gulp.dest('./build')); 32 | }); 33 | 34 | gulp.task('watch', ['copy', 'sass'], function () { 35 | gulp.watch(['./docs/**/*.{scss,sass}'], ['sass']); 36 | gulp.watch(['./docs/index.html'], ['copy']); 37 | }); 38 | 39 | gulp.task('server', ['copy', 'sass'], function (callback) { 40 | var myConfig = require('./webpack.config'); 41 | myConfig.plugins = myConfig.plugins.concat( 42 | new webpack.DefinePlugin({ 43 | 'process.env': { 44 | 'NODE_ENV': JSON.stringify('dev_docs') 45 | } 46 | }) 47 | ); 48 | 49 | new WebpackDevServer(webpack(myConfig), { 50 | contentBase: './build', 51 | hot: true, 52 | debug: true 53 | }).listen(8080, '0.0.0.0', function (err, result) { 54 | if (err) { 55 | console.log(err); 56 | } 57 | }); 58 | callback(); 59 | }); 60 | 61 | 62 | // gulp tasks for building dist files 63 | gulp.task('dist-clean', function () { 64 | return del(['./dist/*']); 65 | }); 66 | 67 | var distConfig = require('./webpack.config.dist.js'); 68 | gulp.task('dist-unmin', function (cb) { 69 | var unminConfig = assign({}, distConfig); 70 | unminConfig.output.filename = 'react-slick.js'; 71 | return webpack(unminConfig, function (err, stat) { 72 | console.error(err); 73 | cb(); 74 | }); 75 | }); 76 | 77 | 78 | gulp.task('dist-min', function (cb) { 79 | var minConfig = assign({}, distConfig); 80 | minConfig.output.filename = 'react-slick.min.js'; 81 | minConfig.plugins = minConfig.plugins.concat( 82 | new webpack.optimize.UglifyJsPlugin({ 83 | compressor: { 84 | warnings: false 85 | } 86 | }) 87 | ); 88 | return webpack(minConfig, function (err, stat) { 89 | console.error(err); 90 | cb(); 91 | }); 92 | }); 93 | 94 | gulp.task('dist', function (cb) { 95 | runSequence('dist-clean', 'dist-unmin', 'dist-min', cb); 96 | }); 97 | -------------------------------------------------------------------------------- /src/slider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import {InnerSlider} from './inner-slider'; 5 | import assign from 'object-assign'; 6 | import json2mq from 'json2mq'; 7 | import defaultProps from './default-props'; 8 | import enquire from 'enquire.js'; 9 | 10 | export default class Slider extends React.Component { 11 | constructor(props) { 12 | super(props) 13 | this.state = { 14 | breakpoint: null 15 | }; 16 | this._responsiveMediaHandlers = []; 17 | this.innerSliderRefHandler = this.innerSliderRefHandler.bind(this) 18 | } 19 | innerSliderRefHandler(ref) { 20 | this.innerSlider = ref; 21 | } 22 | media(query, handler) { 23 | enquire.register(query, handler); 24 | this._responsiveMediaHandlers.push({query, handler}); 25 | } 26 | componentWillMount() { 27 | if (this.props.responsive) { 28 | var breakpoints = this.props.responsive.map(breakpt => breakpt.breakpoint); 29 | breakpoints.sort((x, y) => x - y); 30 | 31 | breakpoints.forEach((breakpoint, index) => { 32 | var bQuery; 33 | if (index === 0) { 34 | bQuery = json2mq({minWidth: 0, maxWidth: breakpoint}); 35 | } else { 36 | bQuery = json2mq({minWidth: breakpoints[index-1], maxWidth: breakpoint}); 37 | } 38 | this.media(bQuery, () => { 39 | this.setState({breakpoint: breakpoint}); 40 | }) 41 | }); 42 | 43 | // Register media query for full screen. Need to support resize from small to large 44 | var query = json2mq({minWidth: breakpoints.slice(-1)[0]}); 45 | 46 | this.media(query, () => { 47 | this.setState({breakpoint: null}); 48 | }); 49 | } 50 | } 51 | 52 | componentWillUnmount() { 53 | this._responsiveMediaHandlers.forEach(function(obj) { 54 | enquire.unregister(obj.query, obj.handler); 55 | }); 56 | } 57 | 58 | slickPrev() { 59 | this.innerSlider.slickPrev(); 60 | } 61 | 62 | slickNext() { 63 | this.innerSlider.slickNext(); 64 | } 65 | 66 | slickGoTo(slide) { 67 | this.innerSlider.slickGoTo(slide) 68 | } 69 | 70 | render() { 71 | var settings; 72 | var newProps; 73 | if (this.state.breakpoint) { 74 | newProps = this.props.responsive.filter(resp => resp.breakpoint === this.state.breakpoint); 75 | settings = newProps[0].settings === 'unslick' ? 'unslick' : assign({}, this.props, newProps[0].settings); 76 | } else { 77 | settings = assign({}, defaultProps, this.props); 78 | } 79 | 80 | var children = this.props.children; 81 | if(!Array.isArray(children)) { 82 | children = [children] 83 | } 84 | 85 | // Children may contain false or null, so we should filter them 86 | children = children.filter(function(child){ 87 | return !!child 88 | }) 89 | 90 | if (settings === 'unslick') { 91 | // if 'unslick' responsive breakpoint setting used, just return the tag nested HTML 92 | return ( 93 |
    {children}
    94 | ); 95 | } else { 96 | return ( 97 | 98 | {children} 99 | 100 | ); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/track.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import assign from 'object-assign'; 5 | import classnames from 'classnames'; 6 | 7 | var getSlideClasses = (spec) => { 8 | var slickActive, slickCenter, slickCloned; 9 | var centerOffset, index; 10 | 11 | if (spec.rtl) { 12 | index = spec.slideCount - 1 - spec.index; 13 | } else { 14 | index = spec.index; 15 | } 16 | 17 | slickCloned = (index < 0) || (index >= spec.slideCount); 18 | if (spec.centerMode) { 19 | centerOffset = Math.floor(spec.slidesToShow / 2); 20 | slickCenter = (index - spec.currentSlide) % spec.slideCount === 0; 21 | if ((index > spec.currentSlide - centerOffset - 1) && (index <= spec.currentSlide + centerOffset)) { 22 | slickActive = true; 23 | } 24 | } else { 25 | slickActive = (spec.currentSlide <= index) && (index < spec.currentSlide + spec.slidesToShow); 26 | } 27 | return classnames({ 28 | 'slick-slide': true, 29 | 'slick-active': slickActive, 30 | 'slick-center': slickCenter, 31 | 'slick-cloned': slickCloned 32 | }); 33 | }; 34 | 35 | var getSlideStyle = function (spec) { 36 | var style = {}; 37 | 38 | if (spec.variableWidth === undefined || spec.variableWidth === false) { 39 | style.width = spec.slideWidth; 40 | } 41 | 42 | if (spec.fade) { 43 | style.position = 'relative'; 44 | style.left = -spec.index * spec.slideWidth; 45 | style.opacity = (spec.currentSlide === spec.index) ? 1 : 0; 46 | style.transition = 'opacity ' + spec.speed + 'ms ' + spec.cssEase; 47 | style.WebkitTransition = 'opacity ' + spec.speed + 'ms ' + spec.cssEase; 48 | } 49 | 50 | return style; 51 | }; 52 | 53 | var getKey = (child, fallbackKey) => { 54 | // key could be a zero 55 | return (child.key === null || child.key === undefined) ? fallbackKey : child.key; 56 | }; 57 | 58 | var renderSlides = function (spec) { 59 | var key; 60 | var slides = []; 61 | var preCloneSlides = []; 62 | var postCloneSlides = []; 63 | var count = React.Children.count(spec.children); 64 | 65 | 66 | React.Children.forEach(spec.children, (elem, index) => { 67 | let child; 68 | var childOnClickOptions = { 69 | message: 'children', 70 | index: index, 71 | slidesToScroll: spec.slidesToScroll, 72 | currentSlide: spec.currentSlide 73 | }; 74 | 75 | if (!spec.lazyLoad | (spec.lazyLoad && spec.lazyLoadedList.indexOf(index) >= 0)) { 76 | child = elem; 77 | } else { 78 | child = (
    ); 79 | } 80 | var childStyle = getSlideStyle(assign({}, spec, {index: index})); 81 | var slickClasses = getSlideClasses(assign({index: index}, spec)); 82 | var cssClasses; 83 | 84 | if (child.props.className) { 85 | cssClasses = classnames(slickClasses, child.props.className); 86 | } else { 87 | cssClasses = slickClasses; 88 | } 89 | 90 | const onClick = function(e) { 91 | child.props && child.props.onClick && child.props.onClick(e) 92 | if (spec.focusOnSelect) { 93 | spec.focusOnSelect(childOnClickOptions) 94 | } 95 | } 96 | 97 | slides.push(React.cloneElement(child, { 98 | key: 'original' + getKey(child, index), 99 | 'data-index': index, 100 | className: cssClasses, 101 | tabIndex: '-1', 102 | style: assign({outline: 'none'}, child.props.style || {}, childStyle), 103 | onClick 104 | })); 105 | 106 | // variableWidth doesn't wrap properly. 107 | if (spec.infinite && spec.fade === false) { 108 | var infiniteCount = spec.variableWidth ? spec.slidesToShow + 1 : spec.slidesToShow; 109 | 110 | if (index >= (count - infiniteCount)) { 111 | key = -(count - index); 112 | preCloneSlides.push(React.cloneElement(child, { 113 | key: 'precloned' + getKey(child, key), 114 | 'data-index': key, 115 | className: cssClasses, 116 | style: assign({}, child.props.style || {}, childStyle), 117 | onClick 118 | })); 119 | } 120 | 121 | if (index < infiniteCount) { 122 | key = count + index; 123 | postCloneSlides.push(React.cloneElement(child, { 124 | key: 'postcloned' + getKey(child, key), 125 | 'data-index': key, 126 | className: cssClasses, 127 | style: assign({}, child.props.style || {}, childStyle), 128 | onClick 129 | })); 130 | } 131 | } 132 | }); 133 | 134 | if (spec.rtl) { 135 | return preCloneSlides.concat(slides, postCloneSlides).reverse(); 136 | } else { 137 | return preCloneSlides.concat(slides, postCloneSlides); 138 | } 139 | 140 | 141 | }; 142 | 143 | export class Track extends React.Component { 144 | render() { 145 | var slides = renderSlides.call(this, this.props); 146 | return ( 147 |
    148 | { slides } 149 |
    150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/mixins/trackHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import ReactDOM from 'react-dom'; 3 | import assign from 'object-assign'; 4 | 5 | var checkSpecKeys = function (spec, keysArray) { 6 | return keysArray.reduce((value, key) => { 7 | return value && spec.hasOwnProperty(key); 8 | }, true) ? null : console.error('Keys Missing', spec); 9 | }; 10 | 11 | export var getTrackCSS = function(spec) { 12 | checkSpecKeys(spec, [ 13 | 'left', 'variableWidth', 'slideCount', 'slidesToShow', 'slideWidth' 14 | ]); 15 | 16 | var trackWidth, trackHeight; 17 | 18 | const trackChildren = (spec.slideCount + 2 * spec.slidesToShow); 19 | 20 | if (!spec.vertical) { 21 | if (spec.variableWidth) { 22 | trackWidth = (spec.slideCount + 2*spec.slidesToShow) * spec.slideWidth; 23 | } else if (spec.centerMode) { 24 | trackWidth = (spec.slideCount + 2*(spec.slidesToShow + 1)) * spec.slideWidth; 25 | } else { 26 | trackWidth = (spec.slideCount + 2*spec.slidesToShow) * spec.slideWidth; 27 | } 28 | } else { 29 | trackHeight = trackChildren * spec.slideHeight; 30 | } 31 | 32 | var style = { 33 | opacity: 1, 34 | WebkitTransform: !spec.vertical ? 'translate3d(' + spec.left + 'px, 0px, 0px)' : 'translate3d(0px, ' + spec.left + 'px, 0px)', 35 | transform: !spec.vertical ? 'translate3d(' + spec.left + 'px, 0px, 0px)' : 'translate3d(0px, ' + spec.left + 'px, 0px)', 36 | transition: '', 37 | WebkitTransition: '', 38 | msTransform: !spec.vertical ? 'translateX(' + spec.left + 'px)' : 'translateY(' + spec.left + 'px)', 39 | }; 40 | 41 | if (trackWidth) { 42 | assign(style, { width: trackWidth }); 43 | } 44 | 45 | if (trackHeight) { 46 | assign(style, { height: trackHeight }); 47 | } 48 | 49 | // Fallback for IE8 50 | if (window && !window.addEventListener && window.attachEvent) { 51 | if (!spec.vertical) { 52 | style.marginLeft = spec.left + 'px'; 53 | } else { 54 | style.marginTop = spec.left + 'px'; 55 | } 56 | } 57 | 58 | return style; 59 | }; 60 | 61 | export var getTrackAnimateCSS = function (spec) { 62 | checkSpecKeys(spec, [ 63 | 'left', 'variableWidth', 'slideCount', 'slidesToShow', 'slideWidth', 'speed', 'cssEase' 64 | ]); 65 | 66 | var style = getTrackCSS(spec); 67 | // useCSS is true by default so it can be undefined 68 | style.WebkitTransition = '-webkit-transform ' + spec.speed + 'ms ' + spec.cssEase; 69 | style.transition = 'transform ' + spec.speed + 'ms ' + spec.cssEase; 70 | return style; 71 | }; 72 | 73 | export var getTrackLeft = function (spec) { 74 | 75 | checkSpecKeys(spec, [ 76 | 'slideIndex', 'trackRef', 'infinite', 'centerMode', 'slideCount', 'slidesToShow', 77 | 'slidesToScroll', 'slideWidth', 'listWidth', 'variableWidth', 'slideHeight']); 78 | 79 | var slideOffset = 0; 80 | var targetLeft; 81 | var targetSlide; 82 | var verticalOffset = 0; 83 | 84 | if (spec.fade) { 85 | return 0; 86 | } 87 | 88 | if (spec.infinite) { 89 | if (spec.slideCount >= spec.slidesToShow) { 90 | slideOffset = (spec.slideWidth * spec.slidesToShow) * -1; 91 | verticalOffset = (spec.slideHeight * spec.slidesToShow) * -1; 92 | } 93 | if (spec.slideCount % spec.slidesToScroll !== 0) { 94 | if (spec.slideIndex + spec.slidesToScroll > spec.slideCount && spec.slideCount > spec.slidesToShow) { 95 | if(spec.slideIndex > spec.slideCount) { 96 | slideOffset = ((spec.slidesToShow - (spec.slideIndex - spec.slideCount)) * spec.slideWidth) * -1; 97 | verticalOffset = ((spec.slidesToShow - (spec.slideIndex - spec.slideCount)) * spec.slideHeight) * -1; 98 | } else { 99 | slideOffset = ((spec.slideCount % spec.slidesToScroll) * spec.slideWidth) * -1; 100 | verticalOffset = ((spec.slideCount % spec.slidesToScroll) * spec.slideHeight) * -1; 101 | } 102 | } 103 | } 104 | } else { 105 | 106 | if (spec.slideCount % spec.slidesToScroll !== 0) { 107 | if (spec.slideIndex + spec.slidesToScroll > spec.slideCount && spec.slideCount > spec.slidesToShow) { 108 | var slidesToOffset = spec.slidesToShow - (spec.slideCount % spec.slidesToScroll); 109 | slideOffset = slidesToOffset * spec.slideWidth; 110 | } 111 | } 112 | } 113 | 114 | 115 | 116 | if (spec.centerMode) { 117 | if(spec.infinite) { 118 | slideOffset += spec.slideWidth * Math.floor(spec.slidesToShow / 2); 119 | } else { 120 | slideOffset = spec.slideWidth * Math.floor(spec.slidesToShow / 2); 121 | } 122 | } 123 | 124 | if (!spec.vertical) { 125 | targetLeft = ((spec.slideIndex * spec.slideWidth) * -1) + slideOffset; 126 | } else { 127 | targetLeft = ((spec.slideIndex * spec.slideHeight) * -1) + verticalOffset; 128 | } 129 | 130 | if (spec.variableWidth === true) { 131 | var targetSlideIndex; 132 | if(spec.slideCount <= spec.slidesToShow || spec.infinite === false) { 133 | targetSlide = ReactDOM.findDOMNode(spec.trackRef).childNodes[spec.slideIndex]; 134 | } else { 135 | targetSlideIndex = (spec.slideIndex + spec.slidesToShow); 136 | targetSlide = ReactDOM.findDOMNode(spec.trackRef).childNodes[targetSlideIndex]; 137 | } 138 | targetLeft = targetSlide ? targetSlide.offsetLeft * -1 : 0; 139 | if (spec.centerMode === true) { 140 | if(spec.infinite === false) { 141 | targetSlide = ReactDOM.findDOMNode(spec.trackRef).children[spec.slideIndex]; 142 | } else { 143 | targetSlide = ReactDOM.findDOMNode(spec.trackRef).children[(spec.slideIndex + spec.slidesToShow + 1)]; 144 | } 145 | 146 | if (targetSlide) { 147 | targetLeft = targetSlide.offsetLeft * -1 + (spec.listWidth - targetSlide.offsetWidth) / 2; 148 | } 149 | } 150 | } 151 | 152 | return targetLeft; 153 | }; 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-slick 2 | 3 | [![Join the chat at https://gitter.im/akiran/react-slick](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/akiran/react-slick?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | 6 | Carousel component built with React. It is a react port of [slick carousel](http://kenwheeler.github.io/slick/) 7 | 8 | ### Important 9 | ### Breaking changes in react-slick@0.15 10 | * slickGoTo prop is deprecated in favor of slickGoTo method. Check this [slickGoTo usage example](https://github.com/akiran/react-slick/blob/master/examples/SlickGoTo.js). 11 | * dist folder will be removed from the repo to simplify PR review process. If you are using bower or relying on the dist files in github repo, use dist files from unpkg.com 12 | ``` 13 | https://unpkg.com/react-slick@0.13.6/dist/react-slick.min.js 14 | ``` 15 | 16 | ### Installation 17 | 18 | ```bash 19 | npm install react-slick 20 | ``` 21 | 22 | Also install slick-carousel for css and font 23 | 24 | ```bash 25 | npm install slick-carousel 26 | @import "~slick-carousel/slick/slick.css"; 27 | @import "~slick-carousel/slick/slick-theme.css"; 28 | ``` 29 | 30 | or add cdn link in your html 31 | 32 | ```html 33 | 34 | 35 | ``` 36 | 37 | ### [Demos](http://neostack.com/opensource/react-slick) 38 | 39 | ### [PlayGround](https://jsfiddle.net/kirana/20bumb4g/) 40 | Use [jsfiddle template](https://jsfiddle.net/kirana/20bumb4g/) to try react-slick with different settings. 41 | 42 | ### Filing issues 43 | Please replicate your issue with [jsfiddle template](https://jsfiddle.net/kirana/20bumb4g/) and post it along with issue to make it easy for me to debug. 44 | 45 | 46 | ### Starter Kit 47 | Checkout [yeoman generator](https://github.com/akiran/generator-react-slick) to quickly 48 | get started with react-slick. 49 | 50 | ### Example 51 | 52 | ```js 53 | var React = require('react'); 54 | var Slider = require('react-slick'); 55 | 56 | class SimpleSlider extends React.Component { 57 | render: function () { 58 | var settings = { 59 | dots: true, 60 | infinite: true, 61 | speed: 500, 62 | slidesToShow: 1, 63 | slidesToScroll: 1 64 | }; 65 | return ( 66 | 67 |

    1

    68 |

    2

    69 |

    3

    70 |

    4

    71 |

    5

    72 |

    6

    73 |
    74 | ); 75 | } 76 | } 77 | ``` 78 | 79 | | Property | Type | Description | Working | 80 | | ------------- | ---- | ----------- | ------- | 81 | | accessibility | bool | Enables tabbing and arrow key navigation | Yes | 82 | | className | String |Additional class name for the inner slider div | Yes | 83 | | adaptiveHeight | bool | Adjust the slide's height automatically | Yes | 84 | | arrows | bool | Should we show Left and right nav arrows | Yes | 85 | | nextArrow | React Element | Use this element for the next arrow button | Yes | 86 | | prevArrow | React Element | Use this element for the prev arrow button | Yes | 87 | | autoplay | bool | Should the scroller auto scroll? | Yes | 88 | | autoplaySpeed | int | delay between each auto scoll. in ms | Yes | 89 | | centerMode | bool | Should we centre to a single item? | Yes | 90 | | centerPadding | | | | 91 | | cssEase | | | | 92 | | customPaging | func | Custom paging templates. [Example](https://github.com/akiran/react-slick/blob/master/examples/CustomPaging.js)| Yes | 93 | | dots | bool | Should we show the dots at the bottom of the gallery | Yes | 94 | | dotsClass | string | Class applied to the dots if they are enabled | Yes | 95 | | draggable | bool | Is the gallery scrollable via dragging on desktop? | Yes | 96 | | easing | string | | | 97 | | fade | bool | Slides use fade for transition | Yes | 98 | | focusOnSelect | bool | Go to slide on click | Yes | 99 | | infinite | bool | should the gallery wrap around it's contents | Yes | 100 | | initialSlide | int | which item should be the first to be displayed | Yes | 101 | | lazyLoad | bool | Loads images or renders components on demands | Yes | 102 | | pauseOnHover | bool | prevents autoplay while hovering | Yes | 103 | | responsive | array | Array of objects in the form of `{ breakpoint: int, settings: { ... } }` The breakpoint _int_ is the `maxWidth` so the settings will be applied when resolution is below this value. Breakpoints in the array should be ordered from smalles to greatest. Use 'unslick' in place of the settings object to disable rendering the carousel at that breakpoint. Example: `[ { breakpoint: 768, settings: { slidesToShow: 3 } }, { breakpoint: 1024, settings: { slidesToShow: 5 } }, { breakpoint: 100000, settings: 'unslick' } ]`| Yes | 104 | | rtl | bool | Reverses the slide order | Yes | 105 | | slide | string ||| 106 | | slidesToShow | int | Number of slides to be visible at a time | Yes | 107 | | slidesToScroll | int | Number of slides to scroll for each navigation item 108 | | speed | int ||| 109 | | swipe | bool ||| 110 | | swipeToSlide | bool | Allow users to drag or swipe directly to a slide irrespective of slidesToScroll | Yes | 111 | | touchMove | bool ||| 112 | | touchThreshold | int ||| 113 | | variableWidth | bool ||| 114 | | useCSS | bool | Enable/Disable CSS Transitions | Yes | 115 | | vertical | bool | Vertical slide mode | Yes | 116 | | afterChange | function | callback function called after the current index changes | Yes | 117 | | beforeChange | function | callback function called before the current index changes | Yes | 118 | | slickGoTo | int | go to the specified slide number | | 119 | 120 | 121 | ### Methods 122 | * `slickNext()` - function called to change current slide on next slide ([Example](https://github.com/akiran/react-slick/blob/master/examples/PreviousNextMethods.js)) 123 | * `slickPrev()` - function called to change current slide on previous slide ([Example](https://github.com/akiran/react-slick/blob/master/examples/PreviousNextMethods.js)) 124 | * `slickGoTo(slideNumber)` - function called to change current slide to given slide number ([Example](https://github.com/akiran/react-slick/blob/master/examples/SlickGoTo.js)) 125 | 126 | ### Custom next/prev arrows 127 | 128 | To customize the next/prev arrow elements, simply create new React components and set them 129 | as the values of nextArrow and prevArrow. 130 | 131 | ```js 132 | class LeftNavButton extends React.Component { 133 | render() { 134 | return 135 | } 136 | } 137 | ``` 138 | 139 | Important: be sure that you pass your component's props to your clickable element 140 | like the example above. If you don't, your custom component won't trigger the click handler. 141 | 142 | You can also set `onClick={this.props.onClick}` if you only want to set the click handler. 143 | 144 | ### Flexbox support 145 | If you have flex property on container div of slider, add below css 146 | ```css 147 | * { 148 | min-height: 0; 149 | min-width: 0; 150 | } 151 | ``` 152 | 153 | ### Test Setup 154 | If you try to run tests with jest in a project that uses react-slick, you my run into this error 155 | ``` 156 | matchMedia not present, legacy browsers require a polyfill 157 | ``` 158 | 159 | To fix this issue add below snippet in test-setup.js 160 | ```js 161 | window.matchMedia = window.matchMedia || function() { 162 | return { 163 | matches : false, 164 | addListener : function() {}, 165 | removeListener: function() {} 166 | }; 167 | }; 168 | 169 | ``` 170 | and add below jest config in package.json 171 | ```json 172 | "jest": { 173 | "setupFiles": ["test-setup.js"] 174 | } 175 | ``` 176 | 177 | 178 | ### Development 179 | Want to run demos locally 180 | 181 | ```bash 182 | git clone https://github.com/akiran/react-slick 183 | npm install 184 | npm start 185 | open http://localhost:8080 186 | ``` 187 | 188 | ### Polyfills for old IE support 189 | `matchMedia` support from [media-match](https://github.com/weblinc/media-match) 190 | -------------------------------------------------------------------------------- /src/inner-slider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import EventHandlersMixin from './mixins/event-handlers'; 5 | import HelpersMixin from './mixins/helpers'; 6 | import initialState from './initial-state'; 7 | import defaultProps from './default-props'; 8 | import createReactClass from 'create-react-class'; 9 | import classnames from 'classnames'; 10 | import assign from 'object-assign'; 11 | 12 | import {Track} from './track'; 13 | import {Dots} from './dots'; 14 | import {PrevArrow, NextArrow} from './arrows'; 15 | 16 | export var InnerSlider = createReactClass({ 17 | mixins: [HelpersMixin, EventHandlersMixin], 18 | list: null, 19 | track: null, 20 | listRefHandler: function (ref) { 21 | this.list = ref; 22 | }, 23 | trackRefHandler: function (ref) { 24 | this.track = ref; 25 | }, 26 | getInitialState: function () { 27 | return Object.assign({}, initialState, { 28 | currentSlide: this.props.initialSlide 29 | }); 30 | }, 31 | getDefaultProps: function () { 32 | return defaultProps; 33 | }, 34 | componentWillMount: function () { 35 | if (this.props.init) { 36 | this.props.init(); 37 | } 38 | this.setState({ 39 | mounted: true 40 | }); 41 | var lazyLoadedList = []; 42 | for (var i = 0; i < React.Children.count(this.props.children); i++) { 43 | if (i >= this.state.currentSlide && i < this.state.currentSlide + this.props.slidesToShow) { 44 | lazyLoadedList.push(i); 45 | } 46 | } 47 | 48 | if (this.props.lazyLoad && this.state.lazyLoadedList.length === 0) { 49 | this.setState({ 50 | lazyLoadedList: lazyLoadedList 51 | }); 52 | } 53 | }, 54 | componentDidMount: function componentDidMount() { 55 | // Hack for autoplay -- Inspect Later 56 | this.initialize(this.props); 57 | this.adaptHeight(); 58 | 59 | // To support server-side rendering 60 | if (!window) { 61 | return 62 | } 63 | if (window.addEventListener) { 64 | window.addEventListener('resize', this.onWindowResized); 65 | } else { 66 | window.attachEvent('onresize', this.onWindowResized); 67 | } 68 | }, 69 | componentWillUnmount: function componentWillUnmount() { 70 | if (this.animationEndCallback) { 71 | clearTimeout(this.animationEndCallback); 72 | } 73 | if (window.addEventListener) { 74 | window.removeEventListener('resize', this.onWindowResized); 75 | } else { 76 | window.detachEvent('onresize', this.onWindowResized); 77 | } 78 | if (this.state.autoPlayTimer) { 79 | clearInterval(this.state.autoPlayTimer); 80 | } 81 | }, 82 | componentWillReceiveProps: function(nextProps) { 83 | if (this.props.slickGoTo != nextProps.slickGoTo) { 84 | if (process.env.NODE_ENV !== 'production') { 85 | console.warn('react-slick deprecation warning: slickGoTo prop is deprecated and it will be removed in next release. Use slickGoTo method instead') 86 | } 87 | this.changeSlide({ 88 | message: 'index', 89 | index: nextProps.slickGoTo, 90 | currentSlide: this.state.currentSlide 91 | }); 92 | } else if (this.state.currentSlide >= nextProps.children.length) { 93 | this.update(nextProps); 94 | this.changeSlide({ 95 | message: 'index', 96 | index: nextProps.children.length - nextProps.slidesToShow, 97 | currentSlide: this.state.currentSlide 98 | }); 99 | } else { 100 | this.update(nextProps); 101 | } 102 | }, 103 | componentDidUpdate: function () { 104 | this.adaptHeight(); 105 | }, 106 | onWindowResized: function () { 107 | this.update(this.props); 108 | // animating state should be cleared while resizing, otherwise autoplay stops working 109 | this.setState({ 110 | animating: false 111 | }); 112 | clearTimeout(this.animationEndCallback); 113 | delete this.animationEndCallback; 114 | }, 115 | slickPrev: function () { 116 | this.changeSlide({message: 'previous'}); 117 | }, 118 | slickNext: function () { 119 | this.changeSlide({message: 'next'}); 120 | }, 121 | slickGoTo: function (slide) { 122 | typeof slide === 'number' && this.changeSlide({ 123 | message: 'index', 124 | index: slide, 125 | currentSlide: this.state.currentSlide 126 | }); 127 | }, 128 | render: function () { 129 | var className = classnames('slick-initialized', 'slick-slider', this.props.className, { 130 | 'slick-vertical': this.props.vertical, 131 | }); 132 | 133 | var trackProps = { 134 | fade: this.props.fade, 135 | cssEase: this.props.cssEase, 136 | speed: this.props.speed, 137 | infinite: this.props.infinite, 138 | centerMode: this.props.centerMode, 139 | focusOnSelect: this.props.focusOnSelect ? this.selectHandler : null, 140 | currentSlide: this.state.currentSlide, 141 | lazyLoad: this.props.lazyLoad, 142 | lazyLoadedList: this.state.lazyLoadedList, 143 | rtl: this.props.rtl, 144 | slideWidth: this.state.slideWidth, 145 | slidesToShow: this.props.slidesToShow, 146 | slidesToScroll: this.props.slidesToScroll, 147 | slideCount: this.state.slideCount, 148 | trackStyle: this.state.trackStyle, 149 | variableWidth: this.props.variableWidth 150 | }; 151 | 152 | var dots; 153 | 154 | if (this.props.dots === true && this.state.slideCount >= this.props.slidesToShow) { 155 | var dotProps = { 156 | dotsClass: this.props.dotsClass, 157 | slideCount: this.state.slideCount, 158 | slidesToShow: this.props.slidesToShow, 159 | currentSlide: this.state.currentSlide, 160 | slidesToScroll: this.props.slidesToScroll, 161 | clickHandler: this.changeSlide, 162 | children: this.props.children, 163 | customPaging: this.props.customPaging 164 | }; 165 | 166 | dots = (); 167 | } 168 | 169 | var prevArrow, nextArrow; 170 | 171 | var arrowProps = { 172 | infinite: this.props.infinite, 173 | centerMode: this.props.centerMode, 174 | currentSlide: this.state.currentSlide, 175 | slideCount: this.state.slideCount, 176 | slidesToShow: this.props.slidesToShow, 177 | prevArrow: this.props.prevArrow, 178 | nextArrow: this.props.nextArrow, 179 | clickHandler: this.changeSlide 180 | }; 181 | 182 | if (this.props.arrows) { 183 | prevArrow = (); 184 | nextArrow = (); 185 | } 186 | 187 | var verticalHeightStyle = null; 188 | 189 | if (this.props.vertical) { 190 | verticalHeightStyle = { 191 | height: this.state.listHeight, 192 | }; 193 | } 194 | 195 | var centerPaddingStyle = null; 196 | 197 | if (this.props.vertical === false) { 198 | if (this.props.centerMode === true) { 199 | centerPaddingStyle = { 200 | padding: ('0px ' + this.props.centerPadding) 201 | }; 202 | } 203 | } else { 204 | if (this.props.centerMode === true) { 205 | centerPaddingStyle = { 206 | padding: (this.props.centerPadding + ' 0px') 207 | }; 208 | } 209 | } 210 | 211 | const listStyle = assign({}, verticalHeightStyle, centerPaddingStyle); 212 | 213 | return ( 214 |
    220 | {prevArrow} 221 |
    234 | 235 | {this.props.children} 236 | 237 |
    238 | {nextArrow} 239 | {dots} 240 |
    241 | ); 242 | } 243 | }); 244 | -------------------------------------------------------------------------------- /src/mixins/event-handlers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import {getTrackCSS, getTrackLeft, getTrackAnimateCSS} from './trackHelper'; 3 | import helpers from './helpers'; 4 | import assign from 'object-assign'; 5 | import ReactDOM from 'react-dom'; 6 | 7 | var EventHandlers = { 8 | // Event handler for previous and next 9 | changeSlide: function (options) { 10 | var indexOffset, previousInt, slideOffset, unevenOffset, targetSlide; 11 | const {slidesToScroll, slidesToShow} = this.props 12 | const {slideCount, currentSlide} = this.state 13 | unevenOffset = (slideCount % slidesToScroll !== 0); 14 | indexOffset = unevenOffset ? 0 : (slideCount - currentSlide) % slidesToScroll; 15 | 16 | if (options.message === 'previous') { 17 | slideOffset = (indexOffset === 0) ? slidesToScroll : slidesToShow - indexOffset; 18 | targetSlide = currentSlide - slideOffset; 19 | if (this.props.lazyLoad) { 20 | previousInt = currentSlide - slideOffset; 21 | targetSlide = previousInt === -1 ? slideCount -1 : previousInt; 22 | } 23 | } else if (options.message === 'next') { 24 | slideOffset = (indexOffset === 0) ? slidesToScroll : indexOffset; 25 | targetSlide = currentSlide + slideOffset; 26 | if (this.props.lazyLoad) { 27 | targetSlide = ((currentSlide + slidesToScroll) % slideCount) + indexOffset; 28 | } 29 | } else if (options.message === 'dots' || options.message === 'children') { 30 | // Click on dots 31 | targetSlide = options.index * options.slidesToScroll; 32 | if (targetSlide === options.currentSlide) { 33 | return; 34 | } 35 | } else if (options.message === 'index') { 36 | targetSlide = parseInt(options.index); 37 | if (targetSlide === options.currentSlide) { 38 | return; 39 | } 40 | } 41 | 42 | this.slideHandler(targetSlide); 43 | }, 44 | 45 | // Accessiblity handler for previous and next 46 | keyHandler: function (e) { 47 | //Dont slide if the cursor is inside the form fields and arrow keys are pressed 48 | if(!e.target.tagName.match('TEXTAREA|INPUT|SELECT')) { 49 | if (e.keyCode === 37 && this.props.accessibility === true) { 50 | this.changeSlide({ 51 | message: this.props.rtl === true ? 'next' : 'previous' 52 | }); 53 | } else if (e.keyCode === 39 && this.props.accessibility === true) { 54 | this.changeSlide({ 55 | message: this.props.rtl === true ? 'previous' : 'next' 56 | }); 57 | } 58 | } 59 | }, 60 | // Focus on selecting a slide (click handler on track) 61 | selectHandler: function (options) { 62 | this.changeSlide(options) 63 | }, 64 | swipeStart: function (e) { 65 | var touches, posX, posY; 66 | 67 | if ((this.props.swipe === false) || ('ontouchend' in document && this.props.swipe === false)) { 68 | return; 69 | } else if (this.props.draggable === false && e.type.indexOf('mouse') !== -1) { 70 | return; 71 | } 72 | posX = (e.touches !== undefined) ? e.touches[0].pageX : e.clientX; 73 | posY = (e.touches !== undefined) ? e.touches[0].pageY : e.clientY; 74 | this.setState({ 75 | dragging: true, 76 | touchObject: { 77 | startX: posX, 78 | startY: posY, 79 | curX: posX, 80 | curY: posY 81 | } 82 | }); 83 | }, 84 | swipeMove: function (e) { 85 | if (!this.state.dragging) { 86 | e.preventDefault(); 87 | return; 88 | } 89 | if (this.state.animating) { 90 | return; 91 | } 92 | if (this.props.vertical && this.props.swipeToSlide && this.props.verticalSwiping) { 93 | e.preventDefault(); 94 | } 95 | var swipeLeft; 96 | var curLeft, positionOffset; 97 | var touchObject = this.state.touchObject; 98 | 99 | curLeft = getTrackLeft(assign({ 100 | slideIndex: this.state.currentSlide, 101 | trackRef: this.track 102 | }, this.props, this.state)); 103 | touchObject.curX = (e.touches) ? e.touches[0].pageX : e.clientX; 104 | touchObject.curY = (e.touches) ? e.touches[0].pageY : e.clientY; 105 | touchObject.swipeLength = Math.round(Math.sqrt(Math.pow(touchObject.curX - touchObject.startX, 2))); 106 | 107 | if (this.props.verticalSwiping) { 108 | touchObject.swipeLength = Math.round(Math.sqrt(Math.pow(touchObject.curY - touchObject.startY, 2))); 109 | } 110 | 111 | positionOffset = (this.props.rtl === false ? 1 : -1) * (touchObject.curX > touchObject.startX ? 1 : -1); 112 | 113 | if (this.props.verticalSwiping) { 114 | positionOffset = touchObject.curY > touchObject.startY ? 1 : -1; 115 | } 116 | 117 | var currentSlide = this.state.currentSlide; 118 | var dotCount = Math.ceil(this.state.slideCount / this.props.slidesToScroll); 119 | var swipeDirection = this.swipeDirection(this.state.touchObject); 120 | var touchSwipeLength = touchObject.swipeLength; 121 | 122 | if (this.props.infinite === false) { 123 | if ((currentSlide === 0 && swipeDirection === 'right') || (currentSlide + 1 >= dotCount && swipeDirection === 'left')) { 124 | touchSwipeLength = touchObject.swipeLength * this.props.edgeFriction; 125 | 126 | if (this.state.edgeDragged === false && this.props.edgeEvent) { 127 | this.props.edgeEvent(swipeDirection); 128 | this.setState({ edgeDragged: true }); 129 | } 130 | } 131 | } 132 | 133 | if (this.state.swiped === false && this.props.swipeEvent) { 134 | this.props.swipeEvent(swipeDirection); 135 | this.setState({ swiped: true }); 136 | } 137 | 138 | if (!this.props.vertical) { 139 | swipeLeft = curLeft + touchSwipeLength * positionOffset; 140 | } else { 141 | swipeLeft = curLeft + (touchSwipeLength * (this.state.listHeight / this.state.listWidth)) * positionOffset; 142 | } 143 | 144 | if (this.props.verticalSwiping) { 145 | swipeLeft = curLeft + touchSwipeLength * positionOffset; 146 | } 147 | 148 | this.setState({ 149 | touchObject: touchObject, 150 | swipeLeft: swipeLeft, 151 | trackStyle: getTrackCSS(assign({left: swipeLeft}, this.props, this.state)) 152 | }); 153 | 154 | if (Math.abs(touchObject.curX - touchObject.startX) < Math.abs(touchObject.curY - touchObject.startY) * 0.8) 155 | { return; } 156 | if (touchObject.swipeLength > 4) { 157 | e.preventDefault(); 158 | } 159 | }, 160 | getNavigableIndexes() { 161 | let max; 162 | let breakPoint = 0; 163 | let counter = 0; 164 | let indexes = []; 165 | 166 | if (!this.props.infinite) { 167 | max = this.state.slideCount; 168 | } else { 169 | breakPoint = this.props.slidesToShow * -1; 170 | counter = this.props.slidesToShow * -1; 171 | max = this.state.slideCount * 2; 172 | } 173 | 174 | while (breakPoint < max) { 175 | indexes.push(breakPoint); 176 | breakPoint = counter + this.props.slidesToScroll; 177 | 178 | counter += this.props.slidesToScroll <= this.props.slidesToShow ? 179 | this.props.slidesToScroll : this.props.slidesToShow; 180 | } 181 | 182 | return indexes; 183 | }, 184 | checkNavigable(index) { 185 | const navigables = this.getNavigableIndexes(); 186 | let prevNavigable = 0; 187 | 188 | if (index > navigables[navigables.length - 1]) { 189 | index = navigables[navigables.length - 1]; 190 | } else { 191 | for (var n in navigables) { 192 | if (index < navigables[n]) { 193 | index = prevNavigable; 194 | break; 195 | } 196 | 197 | prevNavigable = navigables[n]; 198 | } 199 | } 200 | 201 | return index; 202 | }, 203 | getSlideCount() { 204 | const centerOffset = this.props.centerMode ? this.state.slideWidth * Math.floor(this.props.slidesToShow / 2) : 0; 205 | 206 | if (this.props.swipeToSlide) { 207 | let swipedSlide; 208 | 209 | const slickList = ReactDOM.findDOMNode(this.list); 210 | 211 | const slides = slickList.querySelectorAll('.slick-slide'); 212 | 213 | Array.from(slides).every((slide) => { 214 | if (!this.props.vertical) { 215 | if (slide.offsetLeft - centerOffset + (this.getWidth(slide) / 2) > this.state.swipeLeft * -1) { 216 | swipedSlide = slide; 217 | return false; 218 | } 219 | } else { 220 | if (slide.offsetTop + (this.getHeight(slide) / 2) > this.state.swipeLeft * -1) { 221 | swipedSlide = slide; 222 | return false; 223 | } 224 | } 225 | 226 | return true; 227 | }); 228 | 229 | const slidesTraversed = Math.abs(swipedSlide.dataset.index - this.state.currentSlide) || 1; 230 | 231 | return slidesTraversed; 232 | } else { 233 | return this.props.slidesToScroll; 234 | } 235 | }, 236 | swipeEnd: function (e) { 237 | if (!this.state.dragging) { 238 | if (this.props.swipe) { 239 | e.preventDefault(); 240 | } 241 | return; 242 | } 243 | var touchObject = this.state.touchObject; 244 | var minSwipe = this.state.listWidth/this.props.touchThreshold; 245 | var swipeDirection = this.swipeDirection(touchObject); 246 | 247 | if (this.props.verticalSwiping) { 248 | minSwipe = this.state.listHeight/this.props.touchThreshold; 249 | } 250 | 251 | // reset the state of touch related state variables. 252 | this.setState({ 253 | dragging: false, 254 | edgeDragged: false, 255 | swiped: false, 256 | swipeLeft: null, 257 | touchObject: {} 258 | }); 259 | // Fix for #13 260 | if (!touchObject.swipeLength) { 261 | return; 262 | } 263 | if (touchObject.swipeLength > minSwipe) { 264 | e.preventDefault(); 265 | 266 | let slideCount, newSlide; 267 | 268 | switch (swipeDirection) { 269 | 270 | case 'left': 271 | case 'down': 272 | newSlide = this.state.currentSlide + this.getSlideCount(); 273 | slideCount = this.props.swipeToSlide ? this.checkNavigable(newSlide) : newSlide; 274 | this.state.currentDirection = 0; 275 | break; 276 | 277 | case 'right': 278 | case 'up': 279 | newSlide = this.state.currentSlide - this.getSlideCount(); 280 | slideCount = this.props.swipeToSlide ? this.checkNavigable(newSlide) : newSlide; 281 | this.state.currentDirection = 1; 282 | break; 283 | 284 | default: 285 | slideCount = this.state.currentSlide; 286 | 287 | } 288 | 289 | this.slideHandler(slideCount); 290 | } else { 291 | // Adjust the track back to it's original position. 292 | var currentLeft = getTrackLeft(assign({ 293 | slideIndex: this.state.currentSlide, 294 | trackRef: this.track 295 | }, this.props, this.state)); 296 | 297 | this.setState({ 298 | trackStyle: getTrackAnimateCSS(assign({left: currentLeft}, this.props, this.state)) 299 | }); 300 | } 301 | }, 302 | onInnerSliderEnter: function (e) { 303 | if (this.props.autoplay && this.props.pauseOnHover) { 304 | this.pause(); 305 | } 306 | }, 307 | onInnerSliderOver: function (e) { 308 | if (this.props.autoplay && this.props.pauseOnHover) { 309 | this.pause(); 310 | } 311 | }, 312 | onInnerSliderLeave: function (e) { 313 | if (this.props.autoplay && this.props.pauseOnHover) { 314 | this.autoPlay(); 315 | } 316 | } 317 | }; 318 | 319 | export default EventHandlers; 320 | -------------------------------------------------------------------------------- /src/mixins/helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import {getTrackCSS, getTrackLeft, getTrackAnimateCSS} from './trackHelper'; 6 | import assign from 'object-assign'; 7 | 8 | var helpers = { 9 | initialize: function (props) { 10 | const slickList = ReactDOM.findDOMNode(this.list); 11 | 12 | var slideCount = React.Children.count(props.children); 13 | var listWidth = this.getWidth(slickList); 14 | var trackWidth = this.getWidth(ReactDOM.findDOMNode(this.track)); 15 | var slideWidth; 16 | 17 | if (!props.vertical) { 18 | var centerPaddingAdj = props.centerMode && (parseInt(props.centerPadding) * 2); 19 | slideWidth = (this.getWidth(ReactDOM.findDOMNode(this)) - centerPaddingAdj)/props.slidesToShow; 20 | } else { 21 | slideWidth = this.getWidth(ReactDOM.findDOMNode(this)); 22 | } 23 | 24 | const slideHeight = this.getHeight(slickList.querySelector('[data-index="0"]')); 25 | const listHeight = slideHeight * props.slidesToShow; 26 | 27 | var currentSlide = props.rtl ? slideCount - 1 - props.initialSlide : props.initialSlide; 28 | 29 | this.setState({ 30 | slideCount, 31 | slideWidth, 32 | listWidth, 33 | trackWidth, 34 | currentSlide, 35 | slideHeight, 36 | listHeight, 37 | }, function () { 38 | 39 | var targetLeft = getTrackLeft(assign({ 40 | slideIndex: this.state.currentSlide, 41 | trackRef: this.track 42 | }, props, this.state)); 43 | // getCSS function needs previously set state 44 | var trackStyle = getTrackCSS(assign({left: targetLeft}, props, this.state)); 45 | 46 | this.setState({trackStyle: trackStyle}); 47 | 48 | this.autoPlay(); // once we're set up, trigger the initial autoplay. 49 | }); 50 | }, 51 | update: function (props) { 52 | const slickList = ReactDOM.findDOMNode(this.list); 53 | // This method has mostly same code as initialize method. 54 | // Refactor it 55 | var slideCount = React.Children.count(props.children); 56 | var listWidth = this.getWidth(slickList); 57 | var trackWidth = this.getWidth(ReactDOM.findDOMNode(this.track)); 58 | var slideWidth; 59 | 60 | if (!props.vertical) { 61 | var centerPaddingAdj = props.centerMode && (parseInt(props.centerPadding) * 2); 62 | slideWidth = (this.getWidth(ReactDOM.findDOMNode(this)) - centerPaddingAdj)/props.slidesToShow; 63 | } else { 64 | slideWidth = this.getWidth(ReactDOM.findDOMNode(this)); 65 | } 66 | 67 | const slideHeight = this.getHeight(slickList.querySelector('[data-index="0"]')); 68 | const listHeight = slideHeight * props.slidesToShow; 69 | 70 | // pause slider if autoplay is set to false 71 | if(props.autoplay) { 72 | this.pause(); 73 | } else { 74 | this.autoPlay(); 75 | } 76 | 77 | this.setState({ 78 | slideCount, 79 | slideWidth, 80 | listWidth, 81 | trackWidth, 82 | slideHeight, 83 | listHeight, 84 | }, function () { 85 | 86 | var targetLeft = getTrackLeft(assign({ 87 | slideIndex: this.state.currentSlide, 88 | trackRef: this.track 89 | }, props, this.state)); 90 | // getCSS function needs previously set state 91 | var trackStyle = getTrackCSS(assign({left: targetLeft}, props, this.state)); 92 | 93 | this.setState({trackStyle: trackStyle}); 94 | }); 95 | }, 96 | getWidth: function getWidth(elem) { 97 | return elem.getBoundingClientRect().width || elem.offsetWidth || 0; 98 | }, 99 | getHeight(elem) { 100 | return elem.getBoundingClientRect().height || elem.offsetHeight || 0; 101 | }, 102 | adaptHeight: function () { 103 | if (this.props.adaptiveHeight) { 104 | var selector = '[data-index="' + this.state.currentSlide +'"]'; 105 | if (this.list) { 106 | var slickList = ReactDOM.findDOMNode(this.list); 107 | slickList.style.height = slickList.querySelector(selector).offsetHeight + 'px'; 108 | } 109 | } 110 | }, 111 | canGoNext: function (opts){ 112 | var canGo = true; 113 | if (!opts.infinite) { 114 | if (opts.centerMode) { 115 | // check if current slide is last slide 116 | if (opts.currentSlide >= (opts.slideCount - 1)) { 117 | canGo = false; 118 | } 119 | } else { 120 | // check if all slides are shown in slider 121 | if (opts.slideCount <= opts.slidesToShow || 122 | opts.currentSlide >= (opts.slideCount - opts.slidesToShow)) { 123 | canGo = false; 124 | } 125 | } 126 | } 127 | return canGo; 128 | }, 129 | slideHandler: function (index) { 130 | // Functionality of animateSlide and postSlide is merged into this function 131 | // console.log('slideHandler', index); 132 | var targetSlide, currentSlide; 133 | var targetLeft, currentLeft; 134 | var callback; 135 | 136 | if (this.props.waitForAnimate && this.state.animating) { 137 | return; 138 | } 139 | 140 | if (this.props.fade) { 141 | currentSlide = this.state.currentSlide; 142 | 143 | // Don't change slide if it's not infite and current slide is the first or last slide. 144 | if(this.props.infinite === false && 145 | (index < 0 || index >= this.state.slideCount)) { 146 | return; 147 | } 148 | 149 | // Shifting targetSlide back into the range 150 | if (index < 0) { 151 | targetSlide = index + this.state.slideCount; 152 | } else if (index >= this.state.slideCount) { 153 | targetSlide = index - this.state.slideCount; 154 | } else { 155 | targetSlide = index; 156 | } 157 | 158 | if (this.props.lazyLoad && this.state.lazyLoadedList.indexOf(targetSlide) < 0) { 159 | this.setState({ 160 | lazyLoadedList: this.state.lazyLoadedList.concat(targetSlide) 161 | }); 162 | } 163 | 164 | callback = () => { 165 | this.setState({ 166 | animating: false 167 | }); 168 | if (this.props.afterChange) { 169 | this.props.afterChange(targetSlide); 170 | } 171 | delete this.animationEndCallback; 172 | }; 173 | 174 | this.setState({ 175 | animating: true, 176 | currentSlide: targetSlide 177 | }, function () { 178 | this.animationEndCallback = setTimeout(callback, this.props.speed); 179 | }); 180 | 181 | if (this.props.beforeChange) { 182 | this.props.beforeChange(this.state.currentSlide, targetSlide); 183 | } 184 | 185 | this.autoPlay(); 186 | return; 187 | } 188 | 189 | targetSlide = index; 190 | if (targetSlide < 0) { 191 | if(this.props.infinite === false) { 192 | currentSlide = 0; 193 | } else if (this.state.slideCount % this.props.slidesToScroll !== 0) { 194 | currentSlide = this.state.slideCount - (this.state.slideCount % this.props.slidesToScroll); 195 | } else { 196 | currentSlide = this.state.slideCount + targetSlide; 197 | } 198 | } else if (targetSlide >= this.state.slideCount) { 199 | if(this.props.infinite === false) { 200 | currentSlide = this.state.slideCount - this.props.slidesToShow; 201 | } else if (this.state.slideCount % this.props.slidesToScroll !== 0) { 202 | currentSlide = 0; 203 | } else { 204 | currentSlide = targetSlide - this.state.slideCount; 205 | } 206 | } else { 207 | currentSlide = targetSlide; 208 | } 209 | 210 | targetLeft = getTrackLeft(assign({ 211 | slideIndex: targetSlide, 212 | trackRef: this.track 213 | }, this.props, this.state)); 214 | 215 | currentLeft = getTrackLeft(assign({ 216 | slideIndex: currentSlide, 217 | trackRef: this.track 218 | }, this.props, this.state)); 219 | 220 | if (this.props.infinite === false) { 221 | targetLeft = currentLeft; 222 | } 223 | 224 | if (this.props.beforeChange) { 225 | this.props.beforeChange(this.state.currentSlide, currentSlide); 226 | } 227 | 228 | if (this.props.lazyLoad) { 229 | var loaded = true; 230 | var slidesToLoad = []; 231 | for (var i = targetSlide; i < targetSlide + this.props.slidesToShow; i++ ) { 232 | loaded = loaded && (this.state.lazyLoadedList.indexOf(i) >= 0); 233 | if (!loaded) { 234 | slidesToLoad.push(i); 235 | } 236 | } 237 | if (!loaded) { 238 | this.setState({ 239 | lazyLoadedList: this.state.lazyLoadedList.concat(slidesToLoad) 240 | }); 241 | } 242 | } 243 | 244 | // Slide Transition happens here. 245 | // animated transition happens to target Slide and 246 | // non - animated transition happens to current Slide 247 | // If CSS transitions are false, directly go the current slide. 248 | 249 | if (this.props.useCSS === false) { 250 | 251 | this.setState({ 252 | currentSlide: currentSlide, 253 | trackStyle: getTrackCSS(assign({left: currentLeft}, this.props, this.state)) 254 | }, function () { 255 | if (this.props.afterChange) { 256 | this.props.afterChange(currentSlide); 257 | } 258 | }); 259 | 260 | } else { 261 | 262 | var nextStateChanges = { 263 | animating: false, 264 | currentSlide: currentSlide, 265 | trackStyle: getTrackCSS(assign({left: currentLeft}, this.props, this.state)), 266 | swipeLeft: null 267 | }; 268 | 269 | callback = () => { 270 | this.setState(nextStateChanges); 271 | if (this.props.afterChange) { 272 | this.props.afterChange(currentSlide); 273 | } 274 | delete this.animationEndCallback; 275 | }; 276 | 277 | this.setState({ 278 | animating: true, 279 | currentSlide: currentSlide, 280 | trackStyle: getTrackAnimateCSS(assign({left: targetLeft}, this.props, this.state)) 281 | }, function () { 282 | this.animationEndCallback = setTimeout(callback, this.props.speed); 283 | }); 284 | 285 | } 286 | 287 | this.autoPlay(); 288 | }, 289 | swipeDirection: function (touchObject) { 290 | var xDist, yDist, r, swipeAngle; 291 | 292 | xDist = touchObject.startX - touchObject.curX; 293 | yDist = touchObject.startY - touchObject.curY; 294 | r = Math.atan2(yDist, xDist); 295 | 296 | swipeAngle = Math.round(r * 180 / Math.PI); 297 | if (swipeAngle < 0) { 298 | swipeAngle = 360 - Math.abs(swipeAngle); 299 | } 300 | if ((swipeAngle <= 45) && (swipeAngle >= 0) || (swipeAngle <= 360) && (swipeAngle >= 315)) { 301 | return (this.props.rtl === false ? 'left' : 'right'); 302 | } 303 | if ((swipeAngle >= 135) && (swipeAngle <= 225)) { 304 | return (this.props.rtl === false ? 'right' : 'left'); 305 | } 306 | if (this.props.verticalSwiping === true) { 307 | if ((swipeAngle >= 35) && (swipeAngle <= 135)) { 308 | return 'down'; 309 | } else { 310 | return 'up'; 311 | } 312 | } 313 | 314 | return 'vertical'; 315 | }, 316 | play: function(){ 317 | var nextIndex; 318 | 319 | if (!this.state.mounted) { 320 | return false 321 | } 322 | 323 | if (this.props.rtl) { 324 | nextIndex = this.state.currentSlide - this.props.slidesToScroll; 325 | } else { 326 | if (this.canGoNext(Object.assign({}, this.props,this.state))) { 327 | nextIndex = this.state.currentSlide + this.props.slidesToScroll; 328 | } else { 329 | return false; 330 | } 331 | } 332 | 333 | this.slideHandler(nextIndex); 334 | }, 335 | autoPlay: function () { 336 | if (this.state.autoPlayTimer) { 337 | clearTimeout(this.state.autoPlayTimer); 338 | } 339 | if (this.props.autoplay) { 340 | this.setState({ 341 | autoPlayTimer: setTimeout(this.play, this.props.autoplaySpeed) 342 | }); 343 | } 344 | }, 345 | pause: function () { 346 | if (this.state.autoPlayTimer) { 347 | clearTimeout(this.state.autoPlayTimer); 348 | this.setState({ 349 | autoPlayTimer: null 350 | }); 351 | } 352 | } 353 | }; 354 | 355 | export default helpers; 356 | --------------------------------------------------------------------------------