├── .babelrc ├── .editorconfig ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .prettierignore ├── .travis.yml ├── LICENSE ├── README.md ├── demo ├── bundle.js ├── index.css ├── index.html └── index.js ├── package-lock.json ├── package.json ├── src └── index.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | "plugins": [ 7 | "add-module-exports", 8 | "@babel/plugin-syntax-dynamic-import", 9 | "@babel/plugin-syntax-import-meta", 10 | "@babel/plugin-proposal-class-properties", 11 | "@babel/plugin-proposal-json-strings", 12 | [ 13 | "@babel/plugin-proposal-decorators", 14 | { 15 | "legacy": true 16 | } 17 | ], 18 | "@babel/plugin-proposal-function-sent", 19 | "@babel/plugin-proposal-export-namespace-from", 20 | "@babel/plugin-proposal-numeric-separator", 21 | "@babel/plugin-proposal-throw-expressions", 22 | "@babel/plugin-proposal-export-default-from", 23 | "@babel/plugin-proposal-logical-assignment-operators", 24 | "@babel/plugin-proposal-optional-chaining", 25 | [ 26 | "@babel/plugin-proposal-pipeline-operator", 27 | { 28 | "proposal": "minimal" 29 | } 30 | ], 31 | "@babel/plugin-proposal-nullish-coalescing-operator", 32 | "@babel/plugin-proposal-do-expressions", 33 | "@babel/plugin-proposal-function-bind" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | ## Demo link 4 | 5 | * you can use this link as a boilerplate - 6 | http://esnextb.in/?gist=8d9bf6bf17a4a3f779485e4baa6c3f26 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.log 4 | 5 | dist 6 | lib 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .github 2 | src 3 | demo 4 | test 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | demo/bundle.js 2 | demo/bundle.min.js 3 | dist 4 | lib 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10 4 | - 8 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Dmitri Voronianski 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-swipe 2 | 3 | [![build status](http://img.shields.io/travis/voronianski/react-swipe.svg?style=flat)](https://travis-ci.org/voronianski/react-swipe) 4 | [![npm version](http://badge.fury.io/js/react-swipe.svg)](http://badge.fury.io/js/react-swipe) 5 | [![Download Count](http://img.shields.io/npm/dm/react-swipe.svg?style=flat)](http://www.npmjs.com/package/react-swipe) 6 | Buy Me A Coffee 7 | 8 | > [Brad Birdsall](https://github.com/thebird)'s [Swipe.js](https://github.com/voronianski/swipe-js-iso) as a [React](http://facebook.github.io/react) component. 9 | 10 | ## Demo 11 | 12 | Check out the [demo](http://voronianski.github.io/react-swipe/demo/) from a mobile device (real or emulated). 13 | 14 | 15 | 16 | ## Install 17 | 18 | ```bash 19 | npm install react swipe-js-iso react-swipe --save 20 | ``` 21 | 22 | ## Usage 23 | 24 | ### Examples 25 | 26 | ```javascript 27 | import React from 'react'; 28 | import ReactDOM from 'react-dom'; 29 | import ReactSwipe from 'react-swipe'; 30 | 31 | const Carousel = () => { 32 | let reactSwipeEl; 33 | 34 | return ( 35 |
36 | (reactSwipeEl = el)} 40 | > 41 |
PANE 1
42 |
PANE 2
43 |
PANE 3
44 |
45 | 46 | 47 |
48 | ); 49 | }; 50 | 51 | ReactDOM.render(, document.getElementById('app')); 52 | ``` 53 | 54 | ### Props 55 | 56 | - `swipeOptions: ?Object` - supports all original options from [Swipe.js config](https://github.com/voronianski/swipe-js-iso#config-options). If passed object differs from the previous one `react-swipe` will re-initiate underlying Swipe.js instance with fresh options 57 | 58 | - `style: ?Object` - object with 3 keys (see [defaults](https://github.com/voronianski/react-swipe/blob/gh-pages/src/index.js#L28)): 59 | 60 | - `container: ?Object` 61 | - `wrapper: ?Object` 62 | - `child: ?Object` 63 | 64 | - regular props as `className`, `id` for root component are also supported 65 | 66 | - `childCount: ?Number` - use it to explicitely tell `react-swipe` that it needs to re-initiate underlying Swipe.js instance. For example, by setting the `childCount` prop to the `length` of the images array that you pass into `react-swipe`, re-rendering will take place when the `images.length` differs from the previous `render` pass: 67 | 68 | ```js 69 | {images} 70 | ``` 71 | 72 | ## Methods 73 | 74 | Component proxies all [Swipe.js instance methods](https://github.com/voronianski/swipe-js-iso/#swipe-api). 75 | 76 | ### Playground 77 | 78 | Configure the ReactSwipe component in a sandbox environment at [CodeSandbox](https://codesandbox.io/s/q86m8n9qnj). 79 | 80 | --- 81 | 82 | **MIT Licensed** 83 | -------------------------------------------------------------------------------- /demo/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | div, 4 | span, 5 | object, 6 | iframe, 7 | h1, 8 | h2, 9 | h3, 10 | h4, 11 | h5, 12 | h6, 13 | p, 14 | del, 15 | dfn, 16 | em, 17 | img, 18 | ins, 19 | kbd, 20 | q, 21 | samp, 22 | small, 23 | strong, 24 | b, 25 | i, 26 | dl, 27 | dt, 28 | dd, 29 | ol, 30 | ul, 31 | li, 32 | fieldset, 33 | form, 34 | label, 35 | table, 36 | tbody, 37 | tfoot, 38 | thead, 39 | tr, 40 | th, 41 | td, 42 | article, 43 | aside, 44 | footer, 45 | header, 46 | nav, 47 | section { 48 | margin: 0; 49 | padding: 0; 50 | border: 0; 51 | outline: 0; 52 | font-size: 100%; 53 | vertical-align: baseline; 54 | background: transparent; 55 | } 56 | 57 | body { 58 | background: #f3f3f3; 59 | font-family: 'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif; 60 | -webkit-text-size-adjust: none; 61 | } 62 | 63 | a { 64 | color: #14ade5; 65 | } 66 | a:hover { 67 | text-decoration: none; 68 | } 69 | 70 | .center { 71 | text-align: center; 72 | } 73 | 74 | h1 { 75 | font-size: 34px; 76 | margin: 50px auto 20px; 77 | color: #212121; 78 | } 79 | 80 | h2 { 81 | font-size: 14px; 82 | color: #3c3c3c; 83 | margin: 10px; 84 | } 85 | 86 | .mySwipe { 87 | margin: 0 auto; 88 | } 89 | .mySwipe .item { 90 | font-weight: bold; 91 | color: #14ade5; 92 | font-size: 20px; 93 | text-align: center; 94 | margin: 10px; 95 | padding: 100px 10px; 96 | box-shadow: 0 1px #ebebeb; 97 | background: #fff; 98 | border-radius: 5px; 99 | border: 1px solid; 100 | border-color: #e5e5e5 #d3d3d3 #b9c1c6; 101 | } 102 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ReactSwipe 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 19 | 23 | 29 | 30 | 31 |
32 | 33 | Fork me on GitHub 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import querystring from 'querystring'; 4 | import ReactSwipe from '../src'; 5 | 6 | const query = querystring.parse(window.location.search.slice(1)); 7 | 8 | // generate slide panes 9 | const numberOfSlides = parseInt(query.slidesNum, 10) || 20; 10 | const paneNodes = Array.apply(null, Array(numberOfSlides)).map((_, i) => { 11 | return ( 12 |
13 |
{i}
14 |
15 | ); 16 | }); 17 | 18 | // change Swipe.js options by query params 19 | const startSlide = parseInt(query.startSlide, 10) || 0; 20 | const swipeOptions = { 21 | startSlide: startSlide < paneNodes.length && startSlide >= 0 ? startSlide : 0, 22 | auto: parseInt(query.auto, 10) || 0, 23 | speed: parseInt(query.speed, 10) || 300, 24 | disableScroll: query.disableScroll === 'true', 25 | continuous: query.continuous === 'true', 26 | widthOfSiblingSlidePreview: 27 | parseInt(query.widthOfSiblingSlidePreview, 10) || 0, 28 | callback() { 29 | console.log('slide changed'); 30 | }, 31 | transitionEnd() { 32 | console.log('ended transition'); 33 | } 34 | }; 35 | 36 | const Page = () => { 37 | let reactSwipeEl; 38 | 39 | return ( 40 |
41 |

ReactSwipe.js

42 |

Open this page from a mobile device (real or emulated).

43 |

44 | You can pass{' '} 45 | 46 | Swipe.js options 47 | {' '} 48 | as query params. 49 |

50 | 51 | (reactSwipeEl = el)} 55 | > 56 | {paneNodes} 57 | 58 | 59 | 60 |
61 | ); 62 | }; 63 | 64 | ReactDOM.render(, document.getElementById('app')); 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-swipe", 3 | "version": "6.0.4", 4 | "description": "Brad Birdsall's Swipe.js as a React component", 5 | "homepage": "https://github.com/voronianski/react-swipe", 6 | "keywords": [ 7 | "react", 8 | "component", 9 | "carousel", 10 | "swipe", 11 | "react-component" 12 | ], 13 | "author": "Dmitri Voronianski ", 14 | "contributors": [ 15 | "Jed Schmidt " 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "git://github.com/voronianski/react-swipe.git" 20 | }, 21 | "main": "./lib/index.js", 22 | "license": "MIT", 23 | "engines": { 24 | "node": ">=8.0.0", 25 | "npm": ">=5.5.1" 26 | }, 27 | "prettier": { 28 | "singleQuote": true 29 | }, 30 | "husky": { 31 | "hooks": { 32 | "pre-commit": "pretty-quick --staged --ignore-path ./public" 33 | } 34 | }, 35 | "browserslist": [ 36 | "> 0.2%", 37 | "last 2 versions", 38 | "not ie <= 8", 39 | "not op_mini all" 40 | ], 41 | "dependencies": { 42 | "lodash.isequal": "^4.5.0", 43 | "prop-types": "^15.6.0", 44 | "swipe-js-iso": "^2.1.5" 45 | }, 46 | "devDependencies": { 47 | "@babel/cli": "^7.0.0", 48 | "@babel/core": "^7.0.0", 49 | "@babel/plugin-proposal-class-properties": "^7.0.0", 50 | "@babel/plugin-proposal-decorators": "^7.0.0", 51 | "@babel/plugin-proposal-do-expressions": "^7.0.0", 52 | "@babel/plugin-proposal-export-default-from": "^7.0.0", 53 | "@babel/plugin-proposal-export-namespace-from": "^7.0.0", 54 | "@babel/plugin-proposal-function-bind": "^7.0.0", 55 | "@babel/plugin-proposal-function-sent": "^7.0.0", 56 | "@babel/plugin-proposal-json-strings": "^7.0.0", 57 | "@babel/plugin-proposal-logical-assignment-operators": "^7.0.0", 58 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0", 59 | "@babel/plugin-proposal-numeric-separator": "^7.0.0", 60 | "@babel/plugin-proposal-optional-chaining": "^7.0.0", 61 | "@babel/plugin-proposal-pipeline-operator": "^7.0.0", 62 | "@babel/plugin-proposal-throw-expressions": "^7.0.0", 63 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 64 | "@babel/plugin-syntax-import-meta": "^7.0.0", 65 | "@babel/plugin-transform-object-assign": "^7.0.0", 66 | "@babel/preset-env": "^7.0.0", 67 | "@babel/preset-react": "^7.0.0", 68 | "@babel/register": "^7.0.0", 69 | "babel-loader": "^8.0.0", 70 | "babel-plugin-add-module-exports": "^0.2.1", 71 | "babelify": "^10.0.0", 72 | "browserify": "^16.2.3", 73 | "husky": "^1.2.0", 74 | "prettier": "^1.15.2", 75 | "pretty-quick": "^1.8.0", 76 | "react": "^16.6.3", 77 | "react-dom": "^16.6.3", 78 | "uglifyjs-webpack-plugin": "^2.0.1", 79 | "webpack": "^4.26.0", 80 | "webpack-cli": "^3.1.2", 81 | "webpack-umd-external": "^1.0.2" 82 | }, 83 | "scripts": { 84 | "build": "npm run build-babel && npm run build-dist-dev && npm run build-dist-prod && npm run build-demo", 85 | "build-babel": "babel ./src/index.js --out-file ./lib/index.js", 86 | "build-dist-dev": "NODE_ENV=development webpack --config webpack.config --colors", 87 | "build-dist-prod": "NODE_ENV=production webpack --config webpack.config --progress --colors -p", 88 | "build-demo": "browserify ./demo/index.js -t [ babelify --presets [ @babel/preset-env @babel/preset-react ] ] -o ./demo/bundle.js", 89 | "test": "echo \"Comming soon ;)\" && exit 0", 90 | "prepublishOnly": "npm run build" 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types'; 2 | import React, { Component } from 'react'; 3 | import Swipe from 'swipe-js-iso'; 4 | import isEqual from 'lodash.isequal'; 5 | 6 | class ReactSwipe extends Component { 7 | static propTypes = { 8 | swipeOptions: PropTypes.shape({ 9 | startSlide: PropTypes.number, 10 | speed: PropTypes.number, 11 | auto: PropTypes.number, 12 | continuous: PropTypes.bool, 13 | disableScroll: PropTypes.bool, 14 | stopPropagation: PropTypes.bool, 15 | swiping: PropTypes.func, 16 | callback: PropTypes.func, 17 | transitionEnd: PropTypes.func 18 | }), 19 | style: PropTypes.shape({ 20 | container: PropTypes.object, 21 | wrapper: PropTypes.object, 22 | child: PropTypes.object 23 | }), 24 | id: PropTypes.string, 25 | className: PropTypes.string, 26 | childCount: PropTypes.number 27 | }; 28 | 29 | static defaultProps = { 30 | swipeOptions: {}, 31 | style: { 32 | container: { 33 | overflow: 'hidden', 34 | visibility: 'hidden', 35 | position: 'relative' 36 | }, 37 | wrapper: { 38 | overflow: 'hidden', 39 | position: 'relative' 40 | }, 41 | child: { 42 | float: 'left', 43 | width: '100%', 44 | position: 'relative', 45 | transitionProperty: 'transform' 46 | } 47 | }, 48 | className: '', 49 | childCount: 0 50 | }; 51 | 52 | componentDidMount() { 53 | this.swipe = Swipe(this.containerEl, this.props.swipeOptions); 54 | } 55 | 56 | componentDidUpdate(prevProps) { 57 | const { childCount, swipeOptions } = this.props; 58 | const shouldUpdateSwipeInstance = 59 | prevProps.childCount !== childCount || 60 | !isEqual(prevProps.swipeOptions, swipeOptions); 61 | 62 | if (shouldUpdateSwipeInstance) { 63 | this.swipe.kill(); 64 | this.swipe = Swipe(this.containerEl, this.props.swipeOptions); 65 | } 66 | } 67 | 68 | componentWillUnmount() { 69 | this.swipe.kill(); 70 | this.swipe = void 0; 71 | } 72 | 73 | next() { 74 | this.swipe.next(); 75 | } 76 | 77 | prev() { 78 | this.swipe.prev(); 79 | } 80 | 81 | slide(...args) { 82 | this.swipe.slide(...args); 83 | } 84 | 85 | getPos() { 86 | return this.swipe.getPos(); 87 | } 88 | 89 | getNumSlides() { 90 | return this.swipe.getNumSlides(); 91 | } 92 | 93 | render() { 94 | const { id, className, style, children } = this.props; 95 | 96 | return ( 97 |
(this.containerEl = el)} 100 | className={`react-swipe-container ${className}`} 101 | style={style.container} 102 | > 103 |
104 | {React.Children.map(children, child => { 105 | if (!child) { 106 | return null; 107 | } 108 | 109 | const childStyle = child.props.style 110 | ? { ...style.child, ...child.props.style } 111 | : style.child; 112 | 113 | return React.cloneElement(child, { style: childStyle }); 114 | })} 115 |
116 |
117 | ); 118 | } 119 | } 120 | 121 | export default ReactSwipe; 122 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 4 | const webpackUMDExternal = require('webpack-umd-external'); 5 | 6 | const env = process.env.NODE_ENV || 'development'; 7 | const isProduction = env === 'production'; 8 | const outputFileName = isProduction ? 'react-swipe.min.js' : 'react-swipe.js'; 9 | 10 | const config = { 11 | mode: isProduction ? 'production' : 'development', 12 | 13 | devtool: false, 14 | 15 | target: 'web', 16 | 17 | entry: './src/index.js', 18 | 19 | output: { 20 | path: path.join(__dirname, './dist'), 21 | filename: outputFileName, 22 | library: 'ReactSwipe', 23 | libraryTarget: 'umd', 24 | umdNamedDefine: true 25 | }, 26 | 27 | optimization: { 28 | minimizer: [ 29 | new UglifyJsPlugin({ 30 | parallel: true, 31 | uglifyOptions: { 32 | compress: { warnings: false }, 33 | output: { comments: false } 34 | } 35 | }) 36 | ] 37 | }, 38 | 39 | externals: webpackUMDExternal({ 40 | react: 'React', 41 | 'swipe-js-iso': 'Swipe' 42 | }), 43 | 44 | resolve: { 45 | extensions: ['.js', '.jsx'] 46 | }, 47 | 48 | plugins: [ 49 | new webpack.DefinePlugin({ 50 | 'process.env': { 51 | NODE_ENV: JSON.stringify(env) 52 | } 53 | }) 54 | ], 55 | 56 | module: { 57 | rules: [ 58 | { 59 | test: /\.jsx?$/, 60 | exclude: /node_modules/, 61 | loader: 'babel-loader' 62 | } 63 | ] 64 | } 65 | }; 66 | 67 | module.exports = config; 68 | --------------------------------------------------------------------------------