diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..5fa15fc --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +open_collective: react-slick diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index e8039ad..0000000 --- a/.github/stale.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Number of days of inactivity before an issue becomes stale -daysUntilStale: 360 -# Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 -# Issues with these labels will never be considered stale -exemptLabels: - - pinned - - security -# Label to use when marking an issue as stale -staleLabel: stale -# Comment to post when marking an issue as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. -# Comment to post when closing a stale issue. Set to `false` to disable -closeComment: false diff --git a/.gitignore b/.gitignore index d2b51d6..735cbb4 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ jquery.html docs/fonts/ docs/ajax-loader.gif package-lock.json +.DS_Store diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 24618ea..e2be242 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,15 +5,16 @@ First, thank you for considering contributing to react-slick! It's people like you that make the open source community such a great community! 😊 We welcome any type of contribution, not only code. You can help with + - **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open) - **Marketing**: writing blog posts, howto's, printing stickers, ... - **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ... -- **Code**: take a look at the [open issues](issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them. To get started you can also [sign up to triage react-slick issues on CodeTriage](https://www.codetriage.com/akiran/react-slick). +- **Code**: take a look at the [open issues](https://github.com/akiran/react-slick/issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them. To get started you can also [sign up to triage react-slick issues on CodeTriage](https://www.codetriage.com/akiran/react-slick). - **Money**: we welcome financial contributions in full transparency on our [open collective](https://opencollective.com/react-slick). ## Your First Contribution -Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). +Working on your first Pull Request? You can learn how from this _free_ series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). ## Submitting code @@ -31,7 +32,7 @@ Anyone can file an expense. If the expense makes sense for the development of th ## Questions -If you have any questions, create an [issue](issue) (protip: do a quick search first to see if someone else didn't ask the same question before!). +If you have any questions, create an [issue](https://github.com/akiran/react-slick/issues) (protip: do a quick search first to see if someone else didn't ask the same question before!). You can also reach us at hello@react-slick.opencollective.com. ## Credits @@ -41,14 +42,12 @@ You can also reach us at hello@react-slick.opencollective.com. Thank you to all the people who have already contributed to react-slick! - ### Backers Thank you to all our backers! [[Become a backer](https://opencollective.com/react-slick#backer)] - ### Sponsors Thank you to all our sponsors! (please ask your company to also support this open source project by [becoming a sponsor](https://opencollective.com/react-slick#sponsor)) diff --git a/README.md b/README.md index 1cb15c3..7aa4f98 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,14 @@ npm install react-slick --save yarn add react-slick ``` -⚠️ Also install slick-carousel for css and font +**Also install slick-carousel for css and font** ```bash npm install slick-carousel -@import "~slick-carousel/slick/slick.css"; -@import "~slick-carousel/slick/slick-theme.css"; + +// Import css files +import "slick-carousel/slick/slick.css"; +import "slick-carousel/slick/slick-theme.css"; ``` or add cdn link in your html @@ -60,38 +62,36 @@ git push origin --follow-tags master import React from "react"; import Slider from "react-slick"; -class SimpleSlider extends React.Component { - render() { - var settings = { - dots: true, - infinite: true, - speed: 500, - slidesToShow: 1, - slidesToScroll: 1 - }; - return ( - -
-

1

-
-
-

2

-
-
-

3

-
-
-

4

-
-
-

5

-
-
-

6

-
-
- ); - } +export default function SimpleSlider() { + var settings = { + dots: true, + infinite: true, + speed: 500, + slidesToShow: 1, + slidesToScroll: 1 + }; + return ( + +
+

1

+
+
+

2

+
+
+

3

+
+
+

4

+
+
+

5

+
+
+

6

+
+
+ ); } ``` @@ -115,32 +115,6 @@ npm start open http://localhost:8080 ``` - - - - -## Contributors - -This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. - - -## Backers - -Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/react-slick#backer)] - - - -## Sponsors - -Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/react-slick#sponsor)] +## Contributing - - - - - - - - - - +Please see the [contributing guidelines](./CONTRIBUTING.md) diff --git a/__tests__/afterChange.test.js b/__tests__/afterChange.test.js index 4da3f13..8e96e5b 100644 --- a/__tests__/afterChange.test.js +++ b/__tests__/afterChange.test.js @@ -10,6 +10,7 @@ class SliderWithBeforeChange extends React.Component { }; this.afterChange = this.afterChange.bind(this); } + afterChange(currentSlide) { console.log(currentSlide, "afterChange"); this.setState({ @@ -33,16 +34,34 @@ describe("After change Slider", function() { const wrapper = mount(); expect(wrapper.state()).toEqual({ currentSlide: null }); wrapper.find(".slick-next").simulate("click"); - - //TBD . fix this test - - // expect(wrapper.find('.slick-slide.slick-active').first().text()).toEqual('slide2'); - // expect(wrapper.state()).toEqual({currentSlide: 1}) - // wrapper.find('.slick-next').simulate('click') - // expect(wrapper.find('.slick-slide.slick-active').first().text()).toEqual('slide3'); - // expect(wrapper.state()).toEqual({currentSlide: 2}) - // wrapper.find('.slick-prev').simulate('click') - // expect(wrapper.find('.slick-slide.slick-active').first().text()).toEqual('slide2'); - // expect(wrapper.state()).toEqual({currentSlide: 1}) + setTimeout(() => { + expect( + wrapper + .find(".slick-slide.slick-active") + .first() + .text() + ).toEqual("slide2"); + expect(wrapper.state()).toEqual({ currentSlide: 1 }); + }, 1); + wrapper.find(".slick-next").simulate("click"); + setTimeout(() => { + expect( + wrapper + .find(".slick-slide.slick-active") + .first() + .text() + ).toEqual("slide3"); + expect(wrapper.state()).toEqual({ currentSlide: 2 }); + }, 1); + wrapper.find(".slick-prev").simulate("click"); + setTimeout(() => { + expect( + wrapper + .find(".slick-slide.slick-active") + .first() + .text() + ).toEqual("slide2"); + expect(wrapper.state()).toEqual({ currentSlide: 1 }); + }, 1); }); }); diff --git a/docs/demos.js b/docs/demos.js new file mode 100644 index 0000000..a03cf82 --- /dev/null +++ b/docs/demos.js @@ -0,0 +1,72 @@ +"use strict"; + +import React from "react"; +import Slider from "../src/slider"; + +import SimpleSlider from "../examples/SimpleSlider"; +import SlideChangeHooks from "../examples/SlideChangeHooks"; +import MultipleItems from "../examples/MultipleItems"; +import MultipleRows from "../examples/MultipleRows"; +import Responsive from "../examples/Responsive"; +import Resizable from "../examples/Resizable"; +import UnevenSetsInfinite from "../examples/UnevenSetsInfinite"; +import UnevenSetsFinite from "../examples/UnevenSetsFinite"; +import CenterMode from "../examples/CenterMode"; +import FocusOnSelect from "../examples/FocusOnSelect"; +import AutoPlay from "../examples/AutoPlay"; +import AutoPlayMethods from "../examples/AutoPlayMethods"; +import PauseOnHover from "../examples/PauseOnHover"; +import Rtl from "../examples/Rtl"; +import VariableWidth from "../examples/VariableWidth"; +import AdaptiveHeight from "../examples/AdaptiveHeight"; +import LazyLoad from "../examples/LazyLoad"; +import Fade from "../examples/Fade"; +import SlickGoTo from "../examples/SlickGoTo"; +import CustomArrows from "../examples/CustomArrows"; +import PreviousNextMethods from "../examples/PreviousNextMethods"; +import DynamicSlides from "../examples/DynamicSlides"; +import VerticalMode from "../examples/VerticalMode"; +import SwipeToSlide from "../examples/SwipeToSlide"; +import VerticalSwipeToSlide from "../examples/VerticalSwipeToSlide"; +import CustomPaging from "../examples/CustomPaging"; +import CustomSlides from "../examples/CustomSlides"; +import AsNavFor from "../examples/AsNavFor"; +import AppendDots from "../examples/AppendDots"; + +export default class App extends React.Component { + render() { + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); + } +} diff --git a/docs/demos.jsx b/docs/demos.jsx index d877043..f3b531d 100644 --- a/docs/demos.jsx +++ b/docs/demos.jsx @@ -1,8 +1,6 @@ 'use strict'; import React from 'react'; -import Slider from '../src/slider'; - import SimpleSlider from '../examples/SimpleSlider' import SlideChangeHooks from '../examples/SlideChangeHooks' import MultipleItems from '../examples/MultipleItems' diff --git a/docs/docs.js b/docs/docs.js new file mode 100644 index 0000000..9c0404e --- /dev/null +++ b/docs/docs.js @@ -0,0 +1,21 @@ +"use strict"; + +import React from "react"; +import Demos from "./demos"; + +export default class Docs extends React.Component { + render() { + return ( +
+
+ React Slick +
+
+
+ +
+
+
+ ); + } +} diff --git a/docs/docs.jsx b/docs/docs.jsx deleted file mode 100644 index f4b556e..0000000 --- a/docs/docs.jsx +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -import React from 'react' -import Demos from './demos' - -export default class Docs extends React.Component { - render() { - return ( -
-
- React Slick -
-
-
- -
-
-
- ); - } -} diff --git a/docs/index.js b/docs/index.js new file mode 100644 index 0000000..7632853 --- /dev/null +++ b/docs/index.js @@ -0,0 +1,13 @@ +"use strict"; + +import React from "react"; +import ReactDOM from "react-dom"; +import Docs from "./docs"; + +React.initializeTouchEvents && React.initializeTouchEvents(true); +ReactDOM.render( + + + , + document.getElementById("rapp") +); diff --git a/docs/index.jsx b/docs/index.jsx deleted file mode 100644 index 949549b..0000000 --- a/docs/index.jsx +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; - -import React from 'react' -import ReactDOM from 'react-dom' -import Docs from './docs' - -React.initializeTouchEvents && React.initializeTouchEvents(true); -ReactDOM.render(, document.getElementById('rapp')); diff --git a/docs/routes.js b/docs/routes.js new file mode 100644 index 0000000..2d96a13 --- /dev/null +++ b/docs/routes.js @@ -0,0 +1,12 @@ +"use strict"; + +var React = require("react"); +var Router = require("react-router"); +var Route = Router.Route; +var Docs = require("./docs"); + +var path = + process.env.NODE_ENV === "dev_docs" ? "/" : "/opensource/react-slick"; +var routes = ; + +module.exports = routes; diff --git a/docs/routes.jsx b/docs/routes.jsx deleted file mode 100644 index 4bf801d..0000000 --- a/docs/routes.jsx +++ /dev/null @@ -1,14 +0,0 @@ -'use strict'; - -var React = require('react'); -var Router = require('react-router'); -var Route = Router.Route; -var Docs = require('./docs'); - -var path = (process.env.NODE_ENV === 'dev_docs') ? '/': '/opensource/react-slick'; -var routes = ( - - -); - -module.exports = routes; diff --git a/docs/single-demo.js b/docs/single-demo.js new file mode 100644 index 0000000..48880e3 --- /dev/null +++ b/docs/single-demo.js @@ -0,0 +1,55 @@ +"use strict"; + +import React from "react"; +import ReactDOM from "react-dom"; +import Slider from "../src/slider"; +function SimpleSlider() { + const settings = { + dots: true, + infinite: true, + speed: 500, + slidesToShow: 1, + slidesToScroll: 1 + }; + return ( +
+

Single Item

+ +
+

1

+
+
+

2

+
+
+

3

+
+
+

4

+
+
+

5

+
+
+

6

+
+
+
+ ); +} + +function App() { + return ( +
+ +
+ ); +} + +React.initializeTouchEvents && React.initializeTouchEvents(true); +ReactDOM.render( + + + , + document.getElementById("rapp") +); diff --git a/examples/Fade.js b/examples/Fade.js index f8fd80e..e4ae2c9 100644 --- a/examples/Fade.js +++ b/examples/Fade.js @@ -10,7 +10,8 @@ export default class Fade extends Component { infinite: true, speed: 500, slidesToShow: 1, - slidesToScroll: 1 + slidesToScroll: 1, + waitForAnimate: false }; return (
diff --git a/examples/__tests__/Fade.js b/examples/__tests__/Fade.js new file mode 100644 index 0000000..80a1095 --- /dev/null +++ b/examples/__tests__/Fade.js @@ -0,0 +1,18 @@ +import React from "react"; +import { mount } from "enzyme"; +import Fade from "../Fade"; +import { clickNext, clickPrev } from "../../__tests__/testUtils"; + +describe("Fade", () => { + it("should change slides when clicked on next & prev buttons", () => { + const slider = mount(); + let activeSlide = slider.find("div.slick-active"); + expect(activeSlide.props()["data-index"]).toEqual(0); + clickNext(slider); + activeSlide = slider.find("div.slick-active"); + expect(activeSlide.props()["data-index"]).toEqual(1); + clickPrev(slider); + activeSlide = slider.find("div.slick-active"); + expect(activeSlide.props()["data-index"]).toEqual(0); + }); +}); diff --git a/examples/__tests__/UnevenSets.test.js b/examples/__tests__/UnevenSets.test.js index 07d984c..1c47291 100644 --- a/examples/__tests__/UnevenSets.test.js +++ b/examples/__tests__/UnevenSets.test.js @@ -49,6 +49,8 @@ describe("UnevenSets Finite", () => { activeSlides = slider.find("div.slick-active"); expect(currentSlide.props()["data-index"]).toEqual(4); expect(activeSlides.map(slide => slide.props()["data-index"])).toEqual([ + 2, + 3, 4, 5 ]); @@ -59,6 +61,8 @@ describe("UnevenSets Finite", () => { activeSlides = slider.find("div.slick-active"); expect(currentSlide.props()["data-index"]).toEqual(4); expect(activeSlides.map(slide => slide.props()["data-index"])).toEqual([ + 2, + 3, 4, 5 ]); diff --git a/examples/__tests__/__snapshots__/MultipleItems.test.js.snap b/examples/__tests__/__snapshots__/MultipleItems.test.js.snap index ab771bd..7e4aabd 100644 --- a/examples/__tests__/__snapshots__/MultipleItems.test.js.snap +++ b/examples/__tests__/__snapshots__/MultipleItems.test.js.snap @@ -989,7 +989,7 @@ exports[`Multiple Items should show slides first 3 slides when middle dot is cli

Multiple items

-
+
diff --git a/examples/__tests__/__snapshots__/UnevenSets.test.js.snap b/examples/__tests__/__snapshots__/UnevenSets.test.js.snap index 0a9ea26..b1dacdf 100644 --- a/examples/__tests__/__snapshots__/UnevenSets.test.js.snap +++ b/examples/__tests__/__snapshots__/UnevenSets.test.js.snap @@ -20,14 +20,14 @@ exports[`UnevenSets Finite Activity test 1`] = `
-
+

3

-
+

4

diff --git a/gulpfile.js b/gulpfile.js index 30ac0ea..9cdbb5f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -59,6 +59,11 @@ gulp.task( gulp.series(["watch", "copy", "sass"], function() { console.log("Start"); var myConfig = require("./webpack.config"); + if (process.env.SINGLE_DEMO) { + myConfig.entry = { + "docs.js": "./docs/single-demo.js" + }; + } myConfig.plugins = myConfig.plugins.concat( new webpack.DefinePlugin({ "process.env": { @@ -73,11 +78,11 @@ gulp.task( stats: { colors: true } - }).listen(DEV_PORT, "localhost", function(err, result) { + }).listen(DEV_PORT, "0.0.0.0", function(err, result) { if (err) { console.log(err); } else { - const server_url = `http://localhost:${DEV_PORT}`; + const server_url = `http://0.0.0.0:${DEV_PORT}`; console.log(`> Dev Server started at ${server_url}`); opn(server_url); } diff --git a/package.json b/package.json index 23eae81..a823e46 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,11 @@ ], "scripts": { "start": "gulp server", - "build": "gulp clean && gulp sass && gulp copy && webpack", - "prepublish": "babel ./src --out-dir ./lib && gulp dist", + "demo": "SINGLE_DEMO=true gulp server", + "build-dev": "gulp clean && gulp sass && gulp copy && webpack", + "lib": "babel ./src --out-dir ./lib", + "build": "npm run lib && gulp dist", + "prepublish": "npm run build", "test": "npm run lint && jest", "test:watch": "jest --watch", "update-snapshots": "jest --updateSnapshot", @@ -43,11 +46,11 @@ "autoprefixer": "^7.1.2", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^9.0.0", - "babel-jest": "^23.4.2", + "babel-jest": "^24.8.0", "babel-loader": "^8.0.4", "babel-preset-airbnb": "^2.1.1", "changelog-verify": "^1.1.2", - "css-loader": "^0.28.0", + "css-loader": "^2.1.1", "deepmerge": "^1.1.0", "del": "^2.2.2", "enzyme": "^3.2.0", @@ -62,7 +65,7 @@ "gulp-sass": "^4.0.0", "husky": "^0.14.3", "jasmine-core": "^2.5.2", - "jest": "^23.6.0", + "jest": "^24.8.0", "jquery": "^3.2.1", "js-beautify": "^1.7.5", "json-loader": "^0.5.4", @@ -95,8 +98,8 @@ "resize-observer-polyfill": "^1.5.0" }, "peerDependencies": { - "react": "^0.14.0 || ^15.0.1 || ^16.0.0", - "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0" + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0" }, "jest": { "setupFiles": [ @@ -125,7 +128,7 @@ "bugs": { "url": "https://github.com/akiran/react-slick/issues" }, - "homepage": "https://github.com/akiran/react-slick", + "homepage": "https://react-slick.neostack.com", "collective": { "type": "opencollective", "url": "https://opencollective.com/react-slick", diff --git a/src/default-props.js b/src/default-props.js index 8d828ad..c98806a 100644 --- a/src/default-props.js +++ b/src/default-props.js @@ -1,6 +1,6 @@ import React from "react"; -var defaultProps = { +let defaultProps = { accessibility: true, adaptiveHeight: false, afterChange: null, diff --git a/src/dots.js b/src/dots.js index 908a391..93520cb 100644 --- a/src/dots.js +++ b/src/dots.js @@ -2,9 +2,10 @@ import React from "react"; import classnames from "classnames"; +import { clamp } from "./utils/innerSliderUtils"; -var getDotCount = function(spec) { - var dots; +const getDotCount = spec => { + let dots; if (spec.infinite) { dots = Math.ceil(spec.slideCount / spec.slidesToScroll); @@ -25,47 +26,55 @@ export class Dots extends React.PureComponent { this.props.clickHandler(options); } render() { - var dotCount = getDotCount({ - slideCount: this.props.slideCount, - slidesToScroll: this.props.slidesToScroll, - slidesToShow: this.props.slidesToShow, - infinite: this.props.infinite + const { + onMouseEnter, + onMouseOver, + onMouseLeave, + infinite, + slidesToScroll, + slidesToShow, + slideCount, + currentSlide + } = this.props; + let dotCount = getDotCount({ + slideCount, + slidesToScroll, + slidesToShow, + infinite }); - // Apply join & split to Array to pre-fill it for IE8 - // - // Credit: http://stackoverflow.com/a/13735425/1849458 - const { onMouseEnter, onMouseOver, onMouseLeave } = this.props; const mouseEvents = { onMouseEnter, onMouseOver, onMouseLeave }; - var dots = Array.apply( - null, - Array(dotCount + 1) - .join("0") - .split("") - ).map((x, i) => { - var leftBound = i * this.props.slidesToScroll; - var rightBound = - i * this.props.slidesToScroll + (this.props.slidesToScroll - 1); - var className = classnames({ - "slick-active": - this.props.currentSlide >= leftBound && - this.props.currentSlide <= rightBound + let dots = []; + for (let i = 0; i < dotCount; i++) { + let _rightBound = (i + 1) * slidesToScroll - 1; + let rightBound = infinite + ? _rightBound + : clamp(_rightBound, 0, slideCount - 1); + let _leftBound = rightBound - (slidesToScroll - 1); + let leftBound = infinite + ? _leftBound + : clamp(_leftBound, 0, slideCount - 1); + + let className = classnames({ + "slick-active": infinite + ? currentSlide >= leftBound && currentSlide <= rightBound + : currentSlide === leftBound }); - var dotOptions = { + let dotOptions = { message: "dots", index: i, - slidesToScroll: this.props.slidesToScroll, - currentSlide: this.props.currentSlide + slidesToScroll, + currentSlide }; - var onClick = this.clickHandler.bind(this, dotOptions); - return ( + let onClick = this.clickHandler.bind(this, dotOptions); + dots = dots.concat(
  • {React.cloneElement(this.props.customPaging(i), { onClick })}
  • ); - }); + } return React.cloneElement(this.props.appendDots(dots), { className: this.props.dotsClass, diff --git a/src/initial-state.js b/src/initial-state.js index 4eaa983..5d5e157 100644 --- a/src/initial-state.js +++ b/src/initial-state.js @@ -20,7 +20,8 @@ const initialState = { swiping: false, touchObject: { startX: 0, startY: 0, curX: 0, curY: 0 }, trackStyle: {}, - trackWidth: 0 + trackWidth: 0, + targetSlide: 0 }; export default initialState; diff --git a/src/inner-slider.js b/src/inner-slider.js index 4d267f7..96a39f4 100644 --- a/src/inner-slider.js +++ b/src/inner-slider.js @@ -1,7 +1,6 @@ "use strict"; import React from "react"; -import ReactDOM from "react-dom"; import initialState from "./initial-state"; import debounce from "lodash.debounce"; import classnames from "classnames"; @@ -10,6 +9,7 @@ import { extractObject, initializedState, getHeight, + getWidth, canGoNext, slideHandler, changeSlide, @@ -36,11 +36,14 @@ export class InnerSlider extends React.Component { this.state = { ...initialState, currentSlide: this.props.initialSlide, - slideCount: React.Children.count(this.props.children) + slideCount: React.Children.count(this.props.children), + slidesToShow: this.props.slidesToShow }; this.callbackTimers = []; this.clickable = true; this.debouncedResize = null; + const ssrState = this.ssrInit(); + this.state = { ...this.state, ...ssrState }; } listRefHandler = ref => (this.list = ref); trackRefHandler = ref => (this.track = ref); @@ -52,8 +55,7 @@ export class InnerSlider extends React.Component { this.list.style.height = getHeight(elem) + "px"; } }; - componentWillMount = () => { - this.ssrInit(); + componentDidMount = () => { this.props.onInit && this.props.onInit(); if (this.props.lazyLoad) { let slidesToLoad = getOnDemandLazySlides({ @@ -69,8 +71,6 @@ export class InnerSlider extends React.Component { } } } - }; - componentDidMount = () => { let spec = { listRef: this.list, trackRef: this.track, ...this.props }; this.updateState(spec, true, () => { this.adaptHeight(); @@ -90,14 +90,6 @@ export class InnerSlider extends React.Component { } }); this.ro.observe(this.list); - Array.prototype.forEach.call( - document.querySelectorAll(".slick-slide"), - slide => { - slide.onfocus = this.props.pauseOnFocus ? this.onSlideFocus : null; - slide.onblur = this.props.pauseOnFocus ? this.onSlideBlur : null; - } - ); - if (this.props.touchMove) { // swipeMove calls preventDefault which only has an effect on non-passive events // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener @@ -105,11 +97,14 @@ export class InnerSlider extends React.Component { passive: false }); } - - // To support server-side rendering - if (!window) { - return; - } + document.querySelectorAll && + Array.prototype.forEach.call( + document.querySelectorAll(".slick-slide"), + slide => { + slide.onfocus = this.props.pauseOnFocus ? this.onSlideFocus : null; + slide.onblur = this.props.pauseOnFocus ? this.onSlideBlur : null; + } + ); if (window.addEventListener) { window.addEventListener("resize", this.onWindowResized); } else { @@ -143,47 +138,34 @@ export class InnerSlider extends React.Component { if (this.autoplayTimer) { clearInterval(this.autoplayTimer); } + this.ro.disconnect(); }; - componentWillReceiveProps = nextProps => { - let spec = { - listRef: this.list, - trackRef: this.track, - ...nextProps, - ...this.state - }; + + didPropsChange(prevProps) { let setTrackStyle = false; for (let key of Object.keys(this.props)) { - if (!nextProps.hasOwnProperty(key)) { + if (!prevProps.hasOwnProperty(key)) { setTrackStyle = true; break; } if ( - typeof nextProps[key] === "object" || - typeof nextProps[key] === "function" + typeof prevProps[key] === "object" || + typeof prevProps[key] === "function" ) { continue; } - if (nextProps[key] !== this.props[key]) { + if (prevProps[key] !== this.props[key]) { setTrackStyle = true; break; } } - this.updateState(spec, setTrackStyle, () => { - if (this.state.currentSlide >= React.Children.count(nextProps.children)) { - this.changeSlide({ - message: "index", - index: - React.Children.count(nextProps.children) - nextProps.slidesToShow, - currentSlide: this.state.currentSlide - }); - } - if (nextProps.autoplay) { - this.autoPlay("update"); - } else { - this.pause("paused"); - } - }); - }; + return ( + setTrackStyle || + React.Children.count(this.props.children) !== + React.Children.count(prevProps.children) + ); + } + componentDidUpdate = prevProps => { this.checkImagesLoad(); this.props.onReInit && this.props.onReInit(); @@ -214,14 +196,68 @@ export class InnerSlider extends React.Component { passive: false }); } + let spec = { + listRef: this.list, + trackRef: this.track, + ...this.props, + ...this.state + }; + const setTrackStyle = this.didPropsChange(prevProps); + setTrackStyle && + this.updateState(spec, setTrackStyle, () => { + if ( + this.state.currentSlide >= React.Children.count(this.props.children) + ) { + this.changeSlide({ + message: "index", + index: + React.Children.count(this.props.children) - + this.props.slidesToShow, + currentSlide: this.state.currentSlide + }); + } + if (this.props.autoplay) { + this.autoPlay("update"); + } else { + this.pause("paused"); + } + }); }; onWindowResized = setTrackStyle => { if (this.debouncedResize) this.debouncedResize.cancel(); this.debouncedResize = debounce(() => this.resizeWindow(setTrackStyle), 50); this.debouncedResize(); }; + calcRealSlideShowed = () => { + var realSlideShowed = 0; + if (!this.props.infinite && this.props.variableWidth) { + let widthUntilListRightEnd = 0; + var trackElem = ReactDOM.findDOMNode(this.track); + var listWidth = Math.ceil(getWidth(ReactDOM.findDOMNode(this.list))); + for ( + let slide = this.state.currentSlide; + slide < trackElem.childNodes.length; + slide++ + ) { + widthUntilListRightEnd += + trackElem && + trackElem.children[slide] && + trackElem.children[slide].offsetWidth; + + if (widthUntilListRightEnd >= listWidth) { + realSlideShowed = slide - 2; + break; + } + } + } + this.setState({ + slidesToShow: realSlideShowed || this.props.slidesToShow + }); + }; resizeWindow = (setTrackStyle = true) => { - if (!ReactDOM.findDOMNode(this.track)) return; + const isTrackMounted = Boolean(this.track && this.track.node); + // prevent warning: setting state on unmounted component (server side rendering) + if (!isTrackMounted) return; let spec = { listRef: this.list, trackRef: this.track, @@ -292,10 +328,9 @@ export class InnerSlider extends React.Component { let currentWidth = `${childrenWidths[this.state.currentSlide]}px`; trackStyle.left = `calc(${trackStyle.left} + (100% - ${currentWidth}) / 2 ) `; } - this.setState({ + return { trackStyle - }); - return; + }; } let childrenCount = React.Children.count(this.props.children); const spec = { ...this.props, ...this.state, slideCount: childrenCount }; @@ -314,13 +349,17 @@ export class InnerSlider extends React.Component { width: trackWidth + "%", left: trackLeft + "%" }; - this.setState({ + return { slideWidth: slideWidth + "%", trackStyle: trackStyle - }); + }; }; checkImagesLoad = () => { - let images = document.querySelectorAll(".slick-slide img"); + let images = + (this.list && + this.list.querySelectorAll && + this.list.querySelectorAll(".slick-slide img")) || + []; let imagesCount = images.length, loadedCount = 0; Array.prototype.forEach.call(images, image => { @@ -413,10 +452,17 @@ export class InnerSlider extends React.Component { value => this.state.lazyLoadedList.indexOf(value) < 0 ); onLazyLoad && slidesToLoad.length > 0 && onLazyLoad(slidesToLoad); + if (!this.props.waitForAnimate && this.animationEndCallback) { + clearTimeout(this.animationEndCallback); + afterChange && afterChange(currentSlide); + delete this.animationEndCallback; + } this.setState(state, () => { - asNavFor && - asNavFor.innerSlider.state.currentSlide !== currentSlide && + // asNavForIndex check is to avoid recursive calls of slideHandler in waitForAnimate=false mode + if (asNavFor && this.asNavForIndex !== index) { + this.asNavForIndex = index; asNavFor.innerSlider.slideHandler(index); + } if (!nextState) return; this.animationEndCallback = setTimeout(() => { this.clickable = true; @@ -440,6 +486,11 @@ export class InnerSlider extends React.Component { } else { this.slideHandler(targetSlide); } + this.props.autoplay && this.autoPlay("update"); + if (this.props.focusOnSelect) { + const nodes = this.list.querySelectorAll(".slick-current"); + nodes[0] && nodes[0].focus(); + } }; clickHandler = e => { if (this.clickable === false) { @@ -508,6 +559,10 @@ export class InnerSlider extends React.Component { this.enableBodyScroll(); } }; + touchEnd = e => { + this.swipeEnd(e); + this.clickable = true; + }; slickPrev = () => { // this and fellow methods are wrapped in setTimeout // to make sure initialize setState has happened before @@ -641,7 +696,9 @@ export class InnerSlider extends React.Component { "trackStyle", "variableWidth", "unslick", - "centerPadding" + "centerPadding", + "targetSlide", + "useCSS" ]); const { pauseOnHover } = this.props; trackProps = { @@ -649,7 +706,8 @@ export class InnerSlider extends React.Component { onMouseEnter: pauseOnHover ? this.onTrackOver : null, onMouseLeave: pauseOnHover ? this.onTrackLeave : null, onMouseOver: pauseOnHover ? this.onTrackOver : null, - focusOnSelect: this.props.focusOnSelect ? this.selectHandler : null + focusOnSelect: + this.props.focusOnSelect && this.clickable ? this.selectHandler : null }; var dots; @@ -732,14 +790,16 @@ export class InnerSlider extends React.Component { onMouseUp: touchMove ? this.swipeEnd : null, onMouseLeave: this.state.dragging && touchMove ? this.swipeEnd : null, onTouchStart: touchMove ? this.swipeStart : null, - onTouchEnd: touchMove ? this.swipeEnd : null, + onTouchMove: this.state.dragging && touchMove ? this.swipeMove : null, + onTouchEnd: touchMove ? this.touchEnd : null, onTouchCancel: this.state.dragging && touchMove ? this.swipeEnd : null, onKeyDown: this.props.accessibility ? this.keyHandler : null }; let innerSliderProps = { className: className, - dir: "ltr" + dir: "ltr", + style: this.props.style }; if (this.props.unslick) { diff --git a/src/slider.js b/src/slider.js index d4833da..65d63a4 100644 --- a/src/slider.js +++ b/src/slider.js @@ -25,7 +25,7 @@ export default class Slider extends React.Component { } // handles responsive breakpoints - componentWillMount() { + componentDidMount() { // performance monitoring //if (process.env.NODE_ENV !== 'production') { //const { whyDidYouUpdate } = require('why-did-you-update') @@ -106,9 +106,7 @@ export default class Slider extends React.Component { process.env.NODE_ENV !== "production" ) { console.warn( - `slidesToScroll should be equal to 1 in centerMode, you are using ${ - settings.slidesToScroll - }` + `slidesToScroll should be equal to 1 in centerMode, you are using ${settings.slidesToScroll}` ); } settings.slidesToScroll = 1; @@ -117,9 +115,7 @@ export default class Slider extends React.Component { if (settings.fade) { if (settings.slidesToShow > 1 && process.env.NODE_ENV !== "production") { console.warn( - `slidesToShow should be equal to 1 when fade is true, you're using ${ - settings.slidesToShow - }` + `slidesToShow should be equal to 1 when fade is true, you're using ${settings.slidesToShow}` ); } if ( @@ -127,9 +123,7 @@ export default class Slider extends React.Component { process.env.NODE_ENV !== "production" ) { console.warn( - `slidesToScroll should be equal to 1 when fade is true, you're using ${ - settings.slidesToScroll - }` + `slidesToScroll should be equal to 1 when fade is true, you're using ${settings.slidesToScroll}` ); } settings.slidesToShow = 1; @@ -203,12 +197,16 @@ export default class Slider extends React.Component { if (settings === "unslick") { const className = "regular slider " + (this.props.className || ""); - return
    {newChildren}
    ; + return
    {children}
    ; } else if (newChildren.length <= settings.slidesToShow) { settings.unslick = true; } return ( - + {newChildren} ); diff --git a/src/track.js b/src/track.js index 4b4bd31..f8924c8 100644 --- a/src/track.js +++ b/src/track.js @@ -9,9 +9,9 @@ import { } from "./utils/innerSliderUtils"; // given specifications/props for a slide, fetch all the classes that need to be applied to the slide -var getSlideClasses = spec => { - var slickActive, slickCenter, slickCloned; - var centerOffset, index; +const getSlideClasses = spec => { + let slickActive, slickCenter, slickCloned; + let centerOffset, index; if (spec.rtl) { index = spec.slideCount - 1 - spec.index; @@ -33,7 +33,16 @@ var getSlideClasses = spec => { spec.currentSlide <= index && index < spec.currentSlide + spec.slidesToShow; } - let slickCurrent = index === spec.currentSlide; + + let focusedSlide; + if (spec.targetSlide < 0) { + focusedSlide = spec.targetSlide + spec.slideCount; + } else if (spec.targetSlide >= spec.slideCount) { + focusedSlide = spec.targetSlide - spec.slideCount; + } else { + focusedSlide = spec.targetSlide; + } + let slickCurrent = index === focusedSlide; return { "slick-slide": true, "slick-active": slickActive, @@ -43,8 +52,8 @@ var getSlideClasses = spec => { }; }; -var getSlideStyle = function(spec) { - var style = {}; +const getSlideStyle = spec => { + let style = {}; if (spec.variableWidth === undefined || spec.variableWidth === false) { style.width = spec.slideWidth; @@ -58,26 +67,18 @@ var getSlideStyle = function(spec) { style.left = -spec.index * parseInt(spec.slideWidth); } style.opacity = spec.currentSlide === spec.index ? 1 : 0; - style.transition = - "opacity " + - spec.speed + - "ms " + - spec.cssEase + - ", " + - "visibility " + - spec.speed + - "ms " + - spec.cssEase; - style.WebkitTransition = - "opacity " + - spec.speed + - "ms " + - spec.cssEase + - ", " + - "visibility " + - spec.speed + - "ms " + - spec.cssEase; + if (spec.useCSS) { + style.transition = + "opacity " + + spec.speed + + "ms " + + spec.cssEase + + ", " + + "visibility " + + spec.speed + + "ms " + + spec.cssEase; + } } return style; @@ -85,18 +86,18 @@ var getSlideStyle = function(spec) { const getKey = (child, fallbackKey) => child.key || fallbackKey; -var renderSlides = function(spec) { - var key; - var slides = []; - var preCloneSlides = []; - var postCloneSlides = []; - var childrenCount = React.Children.count(spec.children); +const renderSlides = spec => { + let key; + let slides = []; + let preCloneSlides = []; + let postCloneSlides = []; + let childrenCount = React.Children.count(spec.children); let startIndex = lazyStartIndex(spec); let endIndex = lazyEndIndex(spec); React.Children.forEach(spec.children, (elem, index) => { let child; - var childOnClickOptions = { + let childOnClickOptions = { message: "children", index: index, slidesToScroll: spec.slidesToScroll, @@ -112,8 +113,8 @@ var renderSlides = function(spec) { } else { child =
    ; } - var childStyle = getSlideStyle({ ...spec, index }); - const slideClass = child.props.className || ""; + let childStyle = getSlideStyle({ ...spec, index }); + let slideClass = child.props.className || ""; let slideClasses = getSlideClasses({ ...spec, index }); // push a cloned element of the desired slide slides.push( @@ -197,12 +198,19 @@ var renderSlides = function(spec) { }; export class Track extends React.PureComponent { + node = null; + + handleRef = ref => { + this.node = ref; + }; + render() { const slides = renderSlides(this.props); const { onMouseEnter, onMouseOver, onMouseLeave } = this.props; const mouseEvents = { onMouseEnter, onMouseOver, onMouseLeave }; return (
    { + const passiveEvents = ["onTouchStart", "onTouchMove", "onWheel"]; + if (!passiveEvents.includes(event._reactName)) { + event.preventDefault(); + } +}; export const getOnDemandLazySlides = spec => { let onDemandSlides = []; @@ -24,19 +34,25 @@ export const getRequiredLazySlides = spec => { return requiredSlides; }; -const getPreloadNumber = (lazyLoad) => { - const [preloadBefore = 0, preloadAfter = 0] = Array.isArray(lazyLoad) ? lazyLoad : [0, 0]; +const getPreloadNumber = lazyLoad => { + const [preloadBefore = 0, preloadAfter = 0] = Array.isArray(lazyLoad) + ? lazyLoad + : [0, 0]; return { preloadBefore: parseInt(preloadBefore, 10), preloadAfter: parseInt(preloadAfter, 10) - } -} + }; +}; // startIndex that needs to be present export const lazyStartIndex = spec => - spec.currentSlide - lazySlidesOnLeft(spec) - getPreloadNumber(spec.lazyLoad).preloadBefore; + spec.currentSlide - + lazySlidesOnLeft(spec) - + getPreloadNumber(spec.lazyLoad).preloadBefore; export const lazyEndIndex = spec => - spec.currentSlide + lazySlidesOnRight(spec) + getPreloadNumber(spec.lazyLoad).preloadAfter; + spec.currentSlide + + lazySlidesOnRight(spec) + + getPreloadNumber(spec.lazyLoad).preloadAfter; export const lazySlidesOnLeft = spec => spec.centerMode @@ -58,7 +74,7 @@ export const getSwipeDirection = (touchObject, verticalSwiping = false) => { xDist = touchObject.startX - touchObject.curX; yDist = touchObject.startY - touchObject.curY; r = Math.atan2(yDist, xDist); - swipeAngle = Math.round(r * 180 / Math.PI); + swipeAngle = Math.round((r * 180) / Math.PI); if (swipeAngle < 0) { swipeAngle = 360 - Math.abs(swipeAngle); } @@ -109,8 +125,10 @@ export const extractObject = (spec, keys) => { export const initializedState = spec => { // spec also contains listRef, trackRef let slideCount = React.Children.count(spec.children); - let listWidth = Math.ceil(getWidth(ReactDOM.findDOMNode(spec.listRef))); - let trackWidth = Math.ceil(getWidth(ReactDOM.findDOMNode(spec.trackRef))); + const listNode = spec.listRef; + let listWidth = Math.ceil(getWidth(listNode)); + const trackNode = spec.trackRef && spec.trackRef.node; + let trackWidth = Math.ceil(getWidth(trackNode)); let slideWidth; if (!spec.vertical) { let centerPaddingAdj = spec.centerMode && parseInt(spec.centerPadding) * 2; @@ -125,10 +143,7 @@ export const initializedState = spec => { slideWidth = listWidth; } let slideHeight = - ReactDOM.findDOMNode(spec.listRef) && - getHeight( - ReactDOM.findDOMNode(spec.listRef).querySelector('[data-index="0"]') - ); + listNode && getHeight(listNode.querySelector('[data-index="0"]')); let listHeight = slideHeight * spec.slidesToShow; let currentSlide = spec.currentSlide === undefined ? spec.initialSlide : spec.currentSlide; @@ -136,11 +151,12 @@ export const initializedState = spec => { currentSlide = slideCount - 1 - spec.initialSlide; } let lazyLoadedList = spec.lazyLoadedList || []; - let slidesToLoad = getOnDemandLazySlides( - { currentSlide, lazyLoadedList }, - spec - ); - lazyLoadedList.concat(slidesToLoad); + let slidesToLoad = getOnDemandLazySlides({ + ...spec, + currentSlide, + lazyLoadedList + }); + lazyLoadedList = lazyLoadedList.concat(slidesToLoad); let state = { slideCount, @@ -168,7 +184,6 @@ export const slideHandler = spec => { infinite, index, slideCount, - lazyLoadedList, lazyLoad, currentSlide, centerMode, @@ -176,6 +191,7 @@ export const slideHandler = spec => { slidesToShow, useCSS } = spec; + let { lazyLoadedList } = spec; if (waitForAnimate && animating) return {}; let animationSlide = index, finalSlide, @@ -183,6 +199,7 @@ export const slideHandler = spec => { finalLeft; let state = {}, nextState = {}; + const targetSlide = infinite ? index : clamp(index, 0, slideCount - 1); if (fade) { if (!infinite && (index < 0 || index >= slideCount)) return {}; if (index < 0) { @@ -191,21 +208,22 @@ export const slideHandler = spec => { animationSlide = index - slideCount; } if (lazyLoad && lazyLoadedList.indexOf(animationSlide) < 0) { - lazyLoadedList.push(animationSlide); + lazyLoadedList = lazyLoadedList.concat(animationSlide); } state = { animating: true, currentSlide: animationSlide, - lazyLoadedList + lazyLoadedList, + targetSlide: animationSlide }; - nextState = { animating: false }; + nextState = { animating: false, targetSlide: animationSlide }; } else { finalSlide = animationSlide; if (animationSlide < 0) { finalSlide = animationSlide + slideCount; if (!infinite) finalSlide = 0; else if (slideCount % slidesToScroll !== 0) - finalSlide = slideCount - slideCount % slidesToScroll; + finalSlide = slideCount - (slideCount % slidesToScroll); } else if (!canGoNext(spec) && animationSlide > currentSlide) { animationSlide = finalSlide = currentSlide; } else if (centerMode && animationSlide >= slideCount) { @@ -216,34 +234,43 @@ export const slideHandler = spec => { if (!infinite) finalSlide = slideCount - slidesToShow; else if (slideCount % slidesToScroll !== 0) finalSlide = 0; } + + if (!infinite && animationSlide + slidesToShow >= slideCount) { + finalSlide = slideCount - slidesToShow; + } + animationLeft = getTrackLeft({ ...spec, slideIndex: animationSlide }); finalLeft = getTrackLeft({ ...spec, slideIndex: finalSlide }); if (!infinite) { if (animationLeft === finalLeft) animationSlide = finalSlide; animationLeft = finalLeft; } - lazyLoad && - lazyLoadedList.concat( + if (lazyLoad) { + lazyLoadedList = lazyLoadedList.concat( getOnDemandLazySlides({ ...spec, currentSlide: animationSlide }) ); + } if (!useCSS) { state = { currentSlide: finalSlide, trackStyle: getTrackCSS({ ...spec, left: finalLeft }), - lazyLoadedList + lazyLoadedList, + targetSlide }; } else { state = { animating: true, currentSlide: finalSlide, trackStyle: getTrackAnimateCSS({ ...spec, left: animationLeft }), - lazyLoadedList + lazyLoadedList, + targetSlide }; nextState = { animating: false, currentSlide: finalSlide, trackStyle: getTrackCSS({ ...spec, left: finalLeft }), - swipeLeft: null + swipeLeft: null, + targetSlide }; } } @@ -257,12 +284,12 @@ export const changeSlide = (spec, options) => { slidesToShow, slideCount, currentSlide, + targetSlide: previousTargetSlide, lazyLoad, infinite } = spec; unevenOffset = slideCount % slidesToScroll !== 0; indexOffset = unevenOffset ? 0 : (slideCount - currentSlide) % slidesToScroll; - if (options.message === "previous") { slideOffset = indexOffset === 0 ? slidesToScroll : slidesToShow - indexOffset; @@ -271,24 +298,25 @@ export const changeSlide = (spec, options) => { previousInt = currentSlide - slideOffset; targetSlide = previousInt === -1 ? slideCount - 1 : previousInt; } + if (!infinite) { + targetSlide = previousTargetSlide - slidesToScroll; + } } else if (options.message === "next") { slideOffset = indexOffset === 0 ? slidesToScroll : indexOffset; targetSlide = currentSlide + slideOffset; if (lazyLoad && !infinite) { - targetSlide = (currentSlide + slidesToScroll) % slideCount + indexOffset; + targetSlide = + ((currentSlide + slidesToScroll) % slideCount) + indexOffset; + } + if (!infinite) { + targetSlide = previousTargetSlide + slidesToScroll; } } else if (options.message === "dots") { // Click on dots targetSlide = options.index * options.slidesToScroll; - if (targetSlide === options.currentSlide) { - return null; - } } else if (options.message === "children") { // Click on the slides targetSlide = options.index; - if (targetSlide === options.currentSlide) { - return null; - } if (infinite) { let direction = siblingDirection({ ...spec, targetSlide }); if (targetSlide > options.currentSlide && direction === "left") { @@ -299,9 +327,6 @@ export const changeSlide = (spec, options) => { } } else if (options.message === "index") { targetSlide = Number(options.index); - if (targetSlide === options.currentSlide) { - return null; - } } return targetSlide; }; @@ -314,7 +339,7 @@ export const keyHandler = (e, accessibility, rtl) => { }; export const swipeStart = (e, swipe, draggable) => { - e.target.tagName === "IMG" && e.preventDefault(); + e.target.tagName === "IMG" && safePreventDefault(e); if (!swipe || (!draggable && e.type.indexOf("mouse") !== -1)) return ""; return { dragging: true, @@ -351,8 +376,8 @@ export const swipeMove = (e, spec) => { listWidth } = spec; if (scrolling || !dragging) return; - if (animating) return e.preventDefault(); - if (vertical && swipeToSlide && verticalSwiping) e.preventDefault(); + if (animating) return safePreventDefault(e); + if (vertical && swipeToSlide && verticalSwiping) safePreventDefault(e); let swipeLeft, state = {}; let curLeft = getTrackLeft(spec); @@ -378,9 +403,12 @@ export const swipeMove = (e, spec) => { let touchSwipeLength = touchObject.swipeLength; if (!infinite) { if ( - (currentSlide === 0 && swipeDirection === "right") || - (currentSlide + 1 >= dotCount && swipeDirection === "left") || - (!canGoNext(spec) && swipeDirection === "left") + (currentSlide === 0 && + (swipeDirection === "right" || swipeDirection === "down")) || + (currentSlide + 1 >= dotCount && + (swipeDirection === "left" || swipeDirection === "up")) || + (!canGoNext(spec) && + (swipeDirection === "left" || swipeDirection === "up")) ) { touchSwipeLength = touchObject.swipeLength * edgeFriction; if (edgeDragged === false && onEdge) { @@ -420,7 +448,7 @@ export const swipeMove = (e, spec) => { } if (touchObject.swipeLength > 10) { state["swiping"] = true; - e.preventDefault(); + safePreventDefault(e); } return state; }; @@ -433,13 +461,15 @@ export const swipeEnd = (e, spec) => { touchThreshold, verticalSwiping, listHeight, - currentSlide, swipeToSlide, scrolling, - onSwipe + onSwipe, + targetSlide, + currentSlide, + infinite } = spec; if (!dragging) { - if (swipe) e.preventDefault(); + if (swipe) safePreventDefault(e); return {}; } let minSwipe = verticalSwiping @@ -463,26 +493,27 @@ export const swipeEnd = (e, spec) => { return state; } if (touchObject.swipeLength > minSwipe) { - e.preventDefault(); + safePreventDefault(e); if (onSwipe) { onSwipe(swipeDirection); } let slideCount, newSlide; + let activeSlide = infinite ? currentSlide : targetSlide; switch (swipeDirection) { case "left": case "up": - newSlide = currentSlide + getSlideCount(spec); + newSlide = activeSlide + getSlideCount(spec); slideCount = swipeToSlide ? checkNavigable(spec, newSlide) : newSlide; state["currentDirection"] = 0; break; case "right": case "down": - newSlide = currentSlide - getSlideCount(spec); + newSlide = activeSlide - getSlideCount(spec); slideCount = swipeToSlide ? checkNavigable(spec, newSlide) : newSlide; state["currentDirection"] = 1; break; default: - slideCount = currentSlide; + slideCount = activeSlide; } state["triggerSlideHandler"] = slideCount; } else { @@ -526,8 +557,11 @@ export const getSlideCount = spec => { : 0; if (spec.swipeToSlide) { let swipedSlide; - const slickList = ReactDOM.findDOMNode(spec.listRef); - const slides = slickList.querySelectorAll(".slick-slide"); + const slickList = spec.listRef; + const slides = + (slickList.querySelectorAll && + slickList.querySelectorAll(".slick-slide")) || + []; Array.from(slides).every(slide => { if (!spec.vertical) { if ( @@ -578,7 +612,14 @@ export const getTrackCSS = spec => { let trackWidth, trackHeight; const trackChildren = spec.slideCount + 2 * spec.slidesToShow; if (!spec.vertical) { - trackWidth = getTotalSlides(spec) * spec.slideWidth; + if (spec.variableWidth) { + trackWidth = 0; + spec.children.forEach(function(child) { + trackWidth += child.props.style.width; + }); + } else { + trackWidth = getTotalSlides(spec) * spec.slideWidth; + } } else { trackHeight = trackChildren * spec.slideHeight; } @@ -715,7 +756,7 @@ export const getTrackLeft = spec => { slideCount % slidesToScroll !== 0 && slideIndex + slidesToScroll > slideCount ) { - slidesToOffset = slidesToShow - slideCount % slidesToScroll; + slidesToOffset = slidesToShow - (slideCount % slidesToScroll); } if (centerMode) { slidesToOffset = parseInt(slidesToShow / 2); @@ -732,10 +773,29 @@ export const getTrackLeft = spec => { if (variableWidth === true) { var targetSlideIndex; - let trackElem = ReactDOM.findDOMNode(trackRef); + const trackElem = trackRef && trackRef.node; targetSlideIndex = slideIndex + getPreClones(spec); targetSlide = trackElem && trackElem.childNodes[targetSlideIndex]; targetLeft = targetSlide ? targetSlide.offsetLeft * -1 : 0; + if (!infinite) { + let widthUntilListRightEnd = 0; + for ( + let slide = targetSlideIndex; + slide < trackElem.childNodes.length; + slide++ + ) { + widthUntilListRightEnd += + trackElem && + trackElem.children[slide] && + trackElem.children[slide].offsetWidth; + } + if (widthUntilListRightEnd < listWidth) { + targetLeft += listWidth - widthUntilListRightEnd; + if (targetLeft >= 0) { + targetLeft = 0; + } + } + } if (centerMode === true) { targetSlideIndex = infinite ? slideIndex + getPreClones(spec) diff --git a/webpack.config.js b/webpack.config.js index 407721c..e4e96d4 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,7 +5,7 @@ module.exports = { mode: "production", devtool: "source-map", entry: { - "docs.js": "./docs/index.jsx" + "docs.js": "./docs/index.js" }, output: { path: path.join(__dirname, "build"),