├── .babelrc ├── index.js ├── example.gif ├── demo.html ├── .gitignore ├── src ├── arrow.jsx ├── touch-handler.js ├── pagination.jsx └── react-shift.jsx ├── .npmignore ├── dist ├── touch-handler.js ├── arrow.js ├── react-shift.js.map └── react-shift.js ├── .eslintrc ├── karma.conf.js ├── LICENSE ├── demo ├── demo.css └── demo.jsx ├── test └── react-shift.js ├── gulpfile.js ├── package.json └── README.md /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/react-shift'); 2 | -------------------------------------------------------------------------------- /example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sowiecki/react-shift/HEAD/example.gif -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # demo 2 | demo/demo.js 3 | 4 | # Logs 5 | logs 6 | *.log 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # node-waf configuration 17 | .lock-wscript 18 | 19 | # Dependency directory 20 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 21 | node_modules 22 | 23 | .DS_Store 24 | .fuse_hidden* 25 | -------------------------------------------------------------------------------- /src/arrow.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Arrow = ({ fakeLink, onClick, label, className, style }) => ( 5 | 10 | {label} 11 | 12 | ); 13 | 14 | Arrow.propTypes = { 15 | fakeLink: PropTypes.bool, 16 | onClick: PropTypes.func, 17 | label: PropTypes.string, 18 | className: PropTypes.string, 19 | style: PropTypes.object 20 | }; 21 | 22 | export default Arrow; 23 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /src/touch-handler.js: -------------------------------------------------------------------------------- 1 | const position = { 2 | right: 0, 3 | left: 0, 4 | direction: null, 5 | clear() { 6 | this.right = 0; 7 | this.left = 0; 8 | } 9 | }; 10 | 11 | export default (e, left, right) => { 12 | e = Math.round(e); 13 | 14 | position.direction = null; 15 | 16 | if (e < 450) { 17 | position.left += 1; 18 | position.right = 0; 19 | } else if (e > 550) { 20 | position.right += 1; 21 | position.left = 0; 22 | } 23 | 24 | if (position.left > 4) { 25 | position.clear(); 26 | left(); 27 | return; 28 | } 29 | 30 | if (position.right > 4) { 31 | position.clear(); 32 | right(); 33 | return; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /dist/touch-handler.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | var position = { 7 | right: 0, 8 | left: 0, 9 | direction: null, 10 | clear: function clear() { 11 | this.right = 0; 12 | this.left = 0; 13 | } 14 | }; 15 | 16 | exports.default = function (e, left, right) { 17 | e = Math.round(e); 18 | 19 | position.direction = null; 20 | 21 | if (e < 450) { 22 | position.left += 1; 23 | position.right = 0; 24 | } else if (e > 550) { 25 | position.right += 1; 26 | position.left = 0; 27 | } 28 | 29 | if (position.left > 4) { 30 | position.clear(); 31 | left(); 32 | return; 33 | } 34 | 35 | if (position.right > 4) { 36 | position.clear(); 37 | right(); 38 | return; 39 | } 40 | }; -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "filenames", 4 | "react" 5 | ], 6 | "extends": [ 7 | "airbnb" 8 | ], 9 | "parser": "babel-eslint", 10 | "ecmaFeatures": { 11 | "jsx": true, 12 | "modules": true 13 | }, 14 | "env": { 15 | "es6": true 16 | }, 17 | "rules": { 18 | "strict": 0, 19 | "quotes": [1, "single"], 20 | "jsx-quotes": 0, 21 | "indent": [2, 2, {"SwitchCase": 1}], 22 | "no-invalid-this": 0, 23 | "react/jsx-closing-bracket-location": [2, "after-props"], 24 | "react/jsx-boolean-value": 0, 25 | "react/jsx-space-before-closing": 0, 26 | "react/jsx-no-bind": 0, 27 | "react/sort-comp": 0, 28 | "valid-jsdoc": 0, 29 | "prefer-spread": 0, 30 | "comma-dangle": 0, 31 | "arrow-body-style": 0, 32 | "no-param-reassign": 0 33 | }, 34 | "globals": { 35 | "require": true 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /dist/arrow.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _react = require('react'); 8 | 9 | var _react2 = _interopRequireDefault(_react); 10 | 11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 12 | 13 | var Arrow = function Arrow(_ref) { 14 | var fakeLink = _ref.fakeLink; 15 | var onClick = _ref.onClick; 16 | var label = _ref.label; 17 | var className = _ref.className; 18 | var style = _ref.style; 19 | return _react2.default.createElement( 20 | 'a', 21 | { 22 | className: className, 23 | style: style, 24 | href: fakeLink ? '#' : null, 25 | onClick: onClick }, 26 | label 27 | ); 28 | }; 29 | 30 | Arrow.propTypes = { 31 | onClick: _react.PropTypes.func, 32 | label: _react.PropTypes.string, 33 | className: _react.PropTypes.string, 34 | style: _react.PropTypes.object 35 | }; 36 | 37 | exports.default = Arrow; -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | frameworks: ['browserify', 'mocha','chai', 'phantomjs-shim'], 4 | 'plugins': [ 5 | 'karma-browserify', 6 | 'karma-mocha', 7 | 'karma-chai', 8 | 'karma-phantomjs-launcher', 9 | 'karma-babel-preprocessor', 10 | 'karma-phantomjs-shim', 11 | 'karma-spec-reporter' 12 | ], 13 | reporters: ['spec'], 14 | singleRun: true, 15 | files: [ 16 | 'src/**/*', 17 | 'test/*.js' 18 | ], 19 | preprocessors: { 20 | 'src/**/*': ['browserify'], 21 | 'test/**/*': ['browserify'] 22 | }, 23 | babelPreprocessor: { 24 | options: { 25 | presets: ['es2015', 'react'], 26 | sourceMap: 'inline' 27 | } 28 | }, 29 | browserify: { 30 | debug: true, 31 | transform: ['babelify'] 32 | }, 33 | browsers: ['PhantomJS'], 34 | phantomjsLauncher: { 35 | exitOnResourceError: true 36 | } 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sean 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 | -------------------------------------------------------------------------------- /src/pagination.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Pagination = (props) => { 5 | const { classes, styles, fakeLinks, page, pageCount, onClick } = props; 6 | 7 | const paginationArray = Array 8 | .apply(null, { length: pageCount + 1 }) 9 | .map(Number.call, Number); 10 | 11 | const renderPage = (n) => { 12 | return n === page ? ( 13 | 19 | {n + 1} 20 | 21 | ) : ( 22 | 28 | {n + 1} 29 | 30 | ); 31 | }; 32 | 33 | return ( 34 | 38 | {paginationArray.map(renderPage)} 39 | 40 | ); 41 | }; 42 | 43 | export default Pagination; 44 | 45 | Pagination.propTypes = { 46 | classes: PropTypes.object, 47 | styles: PropTypes.object, 48 | fakeLinks: PropTypes.bool, 49 | page: PropTypes.number, 50 | pageCount: PropTypes.number, 51 | onClick: PropTypes.func 52 | }; 53 | -------------------------------------------------------------------------------- /demo/demo.css: -------------------------------------------------------------------------------- 1 | html { 2 | width: 100%; 3 | } 4 | 5 | .react-shift-page-enter { 6 | position: absolute; 7 | right: 0; left: 0; 8 | opacity: 0.01; 9 | transition: opacity .25s ease-in; 10 | } 11 | .react-shift-page-enter .react-shift-page-enter-active { 12 | opacity: 1; 13 | } 14 | .react-shift-page-leave { 15 | position: absolute; 16 | right: 0; left: 0; 17 | opacity: 1; 18 | transition: opacity .25s ease-in; 19 | } 20 | .react-shift-page-leave .react-shift-page-leave-active { 21 | position: absolute; 22 | opacity: 0.01; 23 | transition: opacity .25s ease-in; 24 | } 25 | 26 | .react-shift-wrapper { 27 | height: 100%; 28 | width: 100%; 29 | font-family: Helvetica; 30 | } 31 | 32 | .react-shift-page { 33 | box-sizing: border-box; 34 | position: absolute; 35 | margin: 0 auto auto; 36 | padding: 10px; 37 | left: 0; right: 0; 38 | width: 100%; 39 | /*border: 2px solid #3f3f3f;*/ 40 | border-radius: 10px; 41 | } 42 | 43 | .react-shift-navigation { 44 | box-sizing: border-box; 45 | position: fixed; 46 | bottom: 100px; left: 0; right: 0; 47 | margin: 10px auto auto; 48 | padding: 2px; 49 | width: 600px; 50 | height: 60px; 51 | text-align: center; 52 | /*border: 2px solid #3f3f3f;*/ 53 | border-radius: 10px; 54 | } 55 | 56 | 57 | .react-shift-nav-arrow { 58 | display: inline-block; 59 | width: 60px; 60 | } 61 | 62 | a { 63 | font-size: 12px; 64 | transition: all .25s; 65 | 66 | } 67 | a.react-shift-current-page { 68 | font-size: 26px; 69 | 70 | } 71 | 72 | a.react-shift-fast-link { 73 | margin: 0 4px 0 4px; 74 | } 75 | -------------------------------------------------------------------------------- /test/react-shift.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TestUtils from 'react-addons-test-utils'; 3 | import shallowRender from 'react-shallow-render'; 4 | import { findClass, findType } from 'react-shallow-renderer-helpers'; 5 | 6 | import Shift from '../src/react-shift.jsx'; 7 | import Arrow from '../src/arrow.jsx'; 8 | 9 | const shallowRenderer = TestUtils.createRenderer(); 10 | 11 | describe('Shift', () => { 12 | let component, children; 13 | 14 | const props = { 15 | classes: { 16 | page: 'test-page', 17 | navigation: 'test-navigation', 18 | navArrow: 'test-nav-arrow', 19 | nextPage: 'test-next-page', 20 | previousPage: 'test-previous-page' 21 | } 22 | }; 23 | 24 | beforeEach(() => { 25 | children = [ 26 |
Example child #1
, 27 |
Example child #2
, 28 |
Example child #3
29 | ]; 30 | 31 | shallowRenderer.render(); 32 | 33 | component = shallowRenderer.getRenderOutput(); 34 | }); 35 | 36 | it('renders', () => { 37 | expect(component).to.be.defined; 38 | }); 39 | 40 | it('renders children in the correct order', () => { 41 | shallowRenderer.render(); 42 | component = shallowRenderer.getRenderOutput(); 43 | 44 | const pageWrapper = findClass(component, 'test-page'); 45 | const navigation = findClass(component, 'test-navigation'); 46 | 47 | const renderedPageContent = pageWrapper.props.children.props.children; 48 | expect(renderedPageContent).to.equal(children[0].props.children); 49 | }); 50 | }) 51 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const source = require('vinyl-source-stream'); 3 | const buffer = require('vinyl-buffer'); 4 | const sourcemaps = require('gulp-sourcemaps'); 5 | const babel = require('gulp-babel'); 6 | const babelify = require('babelify'); 7 | const browserify = require('browserify'); 8 | const concat = require('gulp-concat'); 9 | const eslint = require('gulp-eslint'); 10 | 11 | const path = 'src/**/*'; 12 | 13 | const babelPresets = { presets: ['es2015', 'react'] }; 14 | 15 | // Development 16 | gulp.task('dev', () => { 17 | return gulp.src(path) 18 | .pipe(sourcemaps.init()) 19 | .pipe(babel(babelPresets)) 20 | .pipe(concat('react-shift.js')) 21 | .pipe(sourcemaps.write('.')) 22 | .pipe(gulp.dest('dist')); 23 | }); 24 | 25 | gulp.task('watch', ['dev'], () => { 26 | gulp.watch(path, ['dev']); 27 | }); 28 | 29 | gulp.task('lint', () => { 30 | return gulp.src([path]) 31 | .pipe(eslint()) 32 | .pipe(eslint.format()) 33 | .pipe(eslint.failOnError()); 34 | }); 35 | 36 | // Production 37 | gulp.task('prod', ['lint', 'dev']); 38 | 39 | // Demo 40 | gulp.task('compileDemo', () => { 41 | browserify({ 42 | entries: './demo/demo.jsx', 43 | debug: true 44 | }) 45 | .on('error', (err) => { 46 | console.log(err.toString()); 47 | this.emit('end'); 48 | }) 49 | .transform(babelify, babelPresets) 50 | .bundle() 51 | .pipe(source('demo.js')) 52 | .pipe(buffer()) 53 | .pipe(gulp.dest('./demo')); 54 | }); 55 | 56 | gulp.task('demo', ['compileDemo'], () => { 57 | gulp.watch([path, 'demo/demo.jsx'], ['compileDemo']); 58 | }); 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-shift", 3 | "version": "2.2.0", 4 | "description": "React carousel/slider with pagination.", 5 | "main": "index.jsx", 6 | "scripts": { 7 | "dev": "gulp watch", 8 | "prod": "gulp prod", 9 | "lint": "gulp lint", 10 | "demo": "gulp demo", 11 | "karma": "./node_modules/.bin/karma start karma.conf.js", 12 | "test": "npm run lint && npm run karma", 13 | "build": "gulp dev && npm run test" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/Nase00/react-shift.git" 18 | }, 19 | "keywords": [ 20 | "react", 21 | "slider", 22 | "carousel" 23 | ], 24 | "author": "Sean Owiecki ", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/Nase00/react-shift/issues" 28 | }, 29 | "homepage": "https://github.com/Nase00/react-shift", 30 | "dependencies": { 31 | "prop-types": "^15.5.10", 32 | "react": "^15.0.0", 33 | "react-addons-css-transition-group": "^15.0.1" 34 | }, 35 | "devDependencies": { 36 | "babel-cli": "^6.7.5", 37 | "babel-eslint": "^6.0.2", 38 | "babel-plugin-syntax-jsx": "^6.5.0", 39 | "babel-preset-es2015": "^6.6.0", 40 | "babel-preset-react": "^6.5.0", 41 | "babel-runtime": "^6.6.1", 42 | "babelify": "^7.2.0", 43 | "browserify": "^13.0.0", 44 | "chai": "^3.4.1", 45 | "eslint": "^2.7.0", 46 | "eslint-config-airbnb": "^6.2.0", 47 | "eslint-plugin-filenames": "^0.2.0", 48 | "eslint-plugin-react": "^4.3.0", 49 | "gulp": "^3.9.0", 50 | "gulp-babel": "^6.1.1", 51 | "gulp-concat": "^2.6.0", 52 | "gulp-eslint": "^2.0.0", 53 | "gulp-sourcemaps": "^1.6.0", 54 | "karma": "^0.13.22", 55 | "karma-babel-preprocessor": "^6.0.1", 56 | "karma-browserify": "^5.0.3", 57 | "karma-chai": "^0.1.0", 58 | "karma-chai-plugins": "^0.6.1", 59 | "karma-mocha": "^0.2.1", 60 | "karma-phantomjs-launcher": "^1.0.0", 61 | "karma-phantomjs-shim": "^1.1.2", 62 | "karma-spec-reporter": "0.0.26", 63 | "matchdep": "^0.3.0", 64 | "mocha": "^2.3.4", 65 | "phantomjs-prebuilt": "^2.1.7", 66 | "react-addons-test-utils": "^15.0.1", 67 | "react-dom": "^15.0.0", 68 | "react-shallow-render": "^1.0.1", 69 | "react-shallow-renderer-helpers": "^2.0.2", 70 | "vinyl-buffer": "^1.0.0", 71 | "vinyl-source-stream": "^1.1.0", 72 | "watchify": "^3.7.0" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](./example.gif) 2 | 3 | # react-shift 4 | A paginated "carousel-like" component for [JSX](https://facebook.github.io/react/docs/jsx-in-depth.html) elements. Can be used to serve pages of content or dropped as a small component into a larger stack. 5 | 6 | Currently hard-swaps content, so smooth scrolling/sliding can only be simulated using `ReactCSSTransitionGroup`. 7 | 8 | ## Installation 9 | ``` 10 | npm install react-shift --save 11 | ``` 12 | 13 | ## Getting started 14 | ```js 15 | render( 16 | 17 |
First page
18 |
Second page
19 |
Third page
20 |
, 21 | node 22 | ); 23 | ``` 24 | 25 | ## Props 26 | The component can be passed objects as props to customize the navigation and page elements. 27 | 28 | Name | Type | Properties | Description | Default 29 | ------------- | ------------- | ------------- | ------------- | ------------- 30 | `arrowLabels` | *object* | `next`, `previous` | Specifies custom next and previous page link labels. | `{ next: 'Next page', previous: 'Previous page' }` 31 | `fastLinks` | *object* | Custom key/value pairs each defining a fast links. | Creates a shortcut link to a page, where the key is the link name and the value is the page index to link to. | `null` 32 | `fakeLinks` | *boolean* | *N/A* | Toggles psuedohyperlinking on naviation elements. | `true` 33 | `transitions` | *object* | `active`, `name` | Used to activate and define [ReactCSSTransitionGroup](https://facebook.github.io/react/docs/animation.html) on the page subcomponent. | `null` 34 | `classes` | *object* | `wrapper`, `navigation`, `page`, `pagination`, `pageNumber`1, `currentPage`, `fastLinks`, `navArrow`, `nextPage`, `previousPage`, `arrowFiller`2 | Passes class names to subcomponent `className` properties. | `null` 35 | `styles` | *object* | `wrapper`, `navigation`, `page`, `pagination`, `pageNumber`3, `currentPage`, `fastLinks`, `navArrow`, `nextPage`, `previousPage`, `arrowFiller`2 | Passes styles to subcomponent `style` properties. | `null` 36 | `scrollable`**4** | *boolean* | *N/A* | Specifies if mouse wheel scrolling events on the page subcomponent triggers page changes. | `false` 37 | 38 | 1 `pageNumber` applies that generic className *every* page number element, but also creates a unique class name on each page number using `${yourDefinedClass}-${pageIndex}`. 39 | 40 | 2 `arrowFiller` can be used to override navArrow styling normally applied to the empty space reserved for page arrows. 41 | 42 | 3 Unlike the `className` version, this prop does **not** currently offer unique styling for each page index number. 43 | 44 | 4 This feature is **highly experimental** and **not recommended** for use. Seriously, don't use it; it makes the component nearly unusable on laptop touchpads and mobile devices. 45 | 46 | ## Advanced example: 47 | ```js 48 | const arrowLabels = { 49 | next: '>>>', 50 | previous: '<<<' 51 | }; 52 | 53 | const fastLinks = { 54 | 'Third page': 2, 55 | 'Fifth page': 4 56 | }; 57 | 58 | const classes = { 59 | navigation: 'react-shift-navigation', 60 | page: 'react-shift-page', 61 | pagination: 'react-shift-pagination', 62 | pageNumber: 'react-shift-page-number', 63 | currentPage: 'react-shift-current-page', 64 | fastLinks: 'react-shift-fast-link', 65 | navArrow: 'react-shift-nav-arrow', 66 | nextPage: 'react-shift-next-page', 67 | previousPage: 'react-shift-previous-page' 68 | }; 69 | 70 | ReactDOM.render( 71 | First page 77 |
Second page
78 |
Third page
79 |
, 80 | document.getElementById('react-shift-anchor') 81 | ); 82 | ``` 83 | 84 | ## Demo 85 | 86 | `npm run demo` 87 | 88 | Open `demo.html` in a browser. 89 | -------------------------------------------------------------------------------- /demo/demo.jsx: -------------------------------------------------------------------------------- 1 | // Example usage of package 2 | 3 | import React from 'react'; 4 | import { render } from 'react-dom'; 5 | 6 | import Shift from '../src/react-shift.jsx'; 7 | 8 | const arrowLabels = { 9 | next: '>>>', 10 | previous: '<<<' 11 | }; 12 | 13 | const fastLinks = { 14 | 'Third page': 2, 15 | 'Fifth page': 4 16 | }; 17 | 18 | const transitions = { 19 | active: true, 20 | name: 'react-shift-page' 21 | }; 22 | 23 | const classes = { 24 | wrapper: 'react-shift-wrapper', 25 | navigation: 'react-shift-navigation', 26 | page: 'react-shift-page', 27 | pagination: 'react-shift-pagination', 28 | pageNumber: 'react-shift-page-number', 29 | currentPage: 'react-shift-current-page', 30 | fastLinks: 'react-shift-fast-link', 31 | navArrow: 'react-shift-nav-arrow', 32 | nextPage: 'react-shift-next-page', 33 | previousPage: 'react-shift-previous-page', 34 | arrowFiller: 'react-shift-previous-page' 35 | } 36 | 37 | const styles = { 38 | wrapper: { 39 | borderRadius: '10px', 40 | backgroundColor: '#D3D3D3' 41 | } 42 | }; 43 | 44 | render( 45 | 52 |
53 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. 54 | Proin lectus justo, varius eget tellus at, auctor suscipit tellus. 55 | Vestibulum ullamcorper urna non purus tempor, eget fermentum eros porta. 56 | Proin nulla enim, sagittis nec sagittis eu, faucibus eu erat. 57 | Etiam luctus molestie nisi aliquet malesuada. 58 | Quisque pellentesque sodales augue, in luctus enim posuere ac. 59 | Mauris posuere magna ac condimentum blandit. Proin hendrerit turpis ac vestibulum hendrerit. 60 | Quisque non interdum mi. 61 |
62 | 63 | Interdum et malesuada fames ac ante ipsum primis in faucibus. 64 | Aliquam nec sem quis dolor malesuada aliquam in at ipsum. 65 | Etiam blandit cursus sapien in molestie. Suspendisse pharetra ante elit, ut vehicula nisi faucibus sit amet. 66 | Donec faucibus eu nisi rhoncus finibus. Nunc ac rutrum sapien, in aliquet nunc. 67 | Sed at magna et enim facilisis hendrerit. Suspendisse tristique in quam in aliquam. 68 | In hac habitasse platea dictumst. Sed at elementum nulla. 69 | 70 |
71 | Nam quis tincidunt turpis. Ut egestas luctus lectus et tincidunt. 72 | Sed eget tellus ut lectus tempus iaculis. Phasellus porttitor ultricies mi ut aliquam. 73 | Etiam sollicitudin finibus nibh, vitae finibus sapien tempor scelerisque. 74 | Phasellus laoreet turpis sed lobortis facilisis. 75 | Integer venenatis lobortis ipsum, eget viverra ipsum sodales vitae. 76 | Interdum et malesuada fames ac ante ipsum primis in faucibus. 77 | Nam vitae orci feugiat, laoreet arcu a, hendrerit eros. Vivamus accumsan ante justo. 78 | In hac habitasse platea dictumst. 79 | Pellentesque fringilla, leo ut cursus viverra, ex nulla tempus diam, maximus dapibus augue magna ac nisi. 80 | Ut venenatis, diam sollicitudin euismod sollicitudin, sapien neque egestas nulla, quis venenatis dui metus id libero. 81 | Nulla pharetra, odio nec gravida dapibus, erat lectus aliquam tellus, eget consectetur risus nibh et lorem. 82 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut augue sem, laoreet a rhoncus ornare, dapibus consequat magna. 83 |
84 |
85 | Fusce id laoreet neque. Etiam tincidunt sem tortor, eu pharetra lorem tincidunt ut. 86 | Nulla tincidunt mattis felis convallis iaculis. Nunc viverra elit vel posuere mollis. 87 | Vivamus et ullamcorper orci, sagittis vestibulum nibh. Phasellus vitae neque aliquam, dignissim urna et, facilisis est. 88 | Nullam mollis lacinia quam, eu faucibus elit blandit vitae. Etiam tempus tempus fermentum. 89 | In eget urna rutrum, finibus purus non, interdum magna. Sed elementum a metus a elementum. 90 |
91 |
92 | Vestibulum interdum dapibus iaculis. Pellentesque a condimentum erat, non tempus erat. 93 | Sed pellentesque, arcu eget tristique facilisis, sapien ligula pharetra tellus, at malesuada nisl diam nec purus. 94 | Morbi aliquam ante erat, nec hendrerit enim malesuada vel. Sed in lorem quis enim aliquam consectetur eu nec leo. 95 | Nulla facilisi. Aenean malesuada risus sed tortor aliquam maximus. 96 |
97 |
, 98 | document.getElementById('react-shift-anchor') 99 | ); 100 | -------------------------------------------------------------------------------- /src/react-shift.jsx: -------------------------------------------------------------------------------- 1 | /* eslint react/no-did-mount-set-state:0 */ 2 | import React, { Component } from 'react'; 3 | import PropTypes from 'prop-types'; 4 | import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; 5 | 6 | import Arrow from './arrow.jsx'; 7 | import Pagination from './pagination.jsx'; 8 | 9 | import touchHandler from './touch-handler'; 10 | 11 | class ReactShift extends Component { 12 | constructor(props) { 13 | super(props); 14 | 15 | this.state = { 16 | page: 0, 17 | pageCount: 0 18 | }; 19 | 20 | this.next = this.next.bind(this); 21 | this.previous = this.previous.bind(this); 22 | this.setPage = this.setPage.bind(this); 23 | this.handleWheel = this.handleWheel.bind(this); 24 | this.handleTouch = this.handleTouch.bind(this); 25 | } 26 | 27 | componentWillMount() { 28 | // TODO move this out of componentDidMount 29 | const { children, scrollable } = this.props; 30 | 31 | this.setState({ 32 | pageCount: children.length - 1, 33 | scrollable 34 | }); 35 | } 36 | 37 | next() { 38 | const { page, pageCount } = this.state; 39 | 40 | if (page !== pageCount) { 41 | this.setState({ page: page + 1 }); 42 | } 43 | } 44 | 45 | previous() { 46 | const { page } = this.state; 47 | 48 | if (page !== 0) { 49 | this.setState({ page: page - 1 }); 50 | } 51 | } 52 | 53 | setPage(n) { 54 | this.setState({ page: n }); 55 | } 56 | 57 | handleWheel(e) { 58 | const { scrollable } = this.props; 59 | 60 | if (scrollable) { 61 | if (e.deltaY > 0) { 62 | this.next(); 63 | } else { 64 | this.previous(); 65 | } 66 | } 67 | } 68 | 69 | handleTouch(e) { 70 | const { next, previous } = this; 71 | 72 | touchHandler( 73 | e.changedTouches[0].pageX, 74 | next, 75 | previous 76 | ); 77 | } 78 | 79 | render() { 80 | const { fastLinks, 81 | fakeLinks, 82 | arrowLabels, 83 | transitions, 84 | children, 85 | classes, 86 | styles } = this.props; 87 | const { page, pageCount } = this.state; 88 | 89 | const filler = ( 90 | 93 | {String.fromCharCode('\u00a0')} 94 | 95 | ); 96 | 97 | const leftArrow = page === 0 ? filler : ( 98 | 104 | ); 105 | 106 | const rightArrow = page === pageCount ? filler : ( 107 | 113 | ); 114 | 115 | const pagination = ( 116 | 121 | ); 122 | 123 | const fastLinksList = fastLinks ? ( 124 |
125 | {Object.keys(fastLinks).map((i, v) => { 126 | return ( 127 | 133 | {Object.keys(fastLinks)[v]} 134 | 135 | ); 136 | })} 137 |
138 | ) : null; 139 | 140 | return ( 141 |
147 |
150 | {transitions ? 151 | 155 | {children[page]} 156 | 157 | : children[page]} 158 |
159 | 165 |
166 | ); 167 | } 168 | } 169 | 170 | ReactShift.propTypes = { 171 | children: PropTypes.node.isRequired, 172 | classes: PropTypes.shape({ 173 | wrapper: PropTypes.string, 174 | navigation: PropTypes.string, 175 | page: PropTypes.string, 176 | pagination: PropTypes.string, 177 | pageNumber: PropTypes.string, 178 | currentPage: PropTypes.string, 179 | fastLinks: PropTypes.string, 180 | navArrow: PropTypes.string, 181 | nextPage: PropTypes.string, 182 | previousPage: PropTypes.string, 183 | arrowFiller: PropTypes.string 184 | }), 185 | styles: PropTypes.shape({ 186 | wrapper: PropTypes.object, 187 | navigation: PropTypes.object, 188 | page: PropTypes.object, 189 | pagination: PropTypes.object, 190 | pageNumber: PropTypes.object, 191 | currentPage: PropTypes.object, 192 | fastLinks: PropTypes.object, 193 | navArrow: PropTypes.object, 194 | nextPage: PropTypes.object, 195 | previousPage: PropTypes.object, 196 | arrowFiller: PropTypes.object 197 | }), 198 | arrowLabels: PropTypes.shape({ 199 | className: PropTypes.string, 200 | next: PropTypes.string, 201 | previous: PropTypes.string 202 | }), 203 | fastLinks: PropTypes.object, 204 | fakeLinks: PropTypes.bool, 205 | transitions: PropTypes.shape({ 206 | active: PropTypes.bool, 207 | name: PropTypes.string 208 | }), 209 | scrollable: PropTypes.bool 210 | }; 211 | 212 | ReactShift.defaultProps = { 213 | classes: {}, 214 | styles: {}, 215 | arrowLabels: { 216 | next: 'Next page', 217 | previous: 'Previous page' 218 | }, 219 | fakeLinks: true, 220 | // TODO: Fix scrolling problems on mobile devices 221 | scrollable: false 222 | }; 223 | 224 | export default ReactShift; 225 | -------------------------------------------------------------------------------- /dist/react-shift.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["arrow.jsx","pagination.jsx","react-shift.jsx","touch-handler.js"],"names":[],"mappings":";;;;;;AAAA;;;;AACA;;;;;;AAEA,IAAM,QAAQ,SAAR,KAAQ;MAAG;MAAU;MAAS;MAAO;MAAW;SACpD;;;AACE,iBAAW,SAAX;AACA,aAAO,KAAP;AACA,YAAM,WAAW,GAAX,GAAiB,IAAjB;AACN,eAAS,OAAT,EAJF;IAKK,KALL;;CADY;;AAUd,MAAM,SAAN,GAAkB;AAChB,YAAU,oBAAU,IAAV;AACV,WAAS,oBAAU,IAAV;AACT,SAAO,oBAAU,MAAV;AACP,aAAW,oBAAU,MAAV;AACX,SAAO,oBAAU,MAAV;CALT;;kBAQe;;;;;;;ACrBf;;;;AACA;;;;;;AAEA,IAAM,aAAa,SAAb,UAAa,CAAC,KAAD,EAAW;MACpB,UAAyD,MAAzD,QADoB;MACX,SAAgD,MAAhD,OADW;MACH,YAAwC,MAAxC,UADG;MACQ,OAA6B,MAA7B,KADR;MACc,YAAuB,MAAvB,UADd;MACyB,UAAY,MAAZ,QADzB;;;AAG5B,MAAM,kBAAkB,MACrB,KADqB,CACf,IADe,EACT,EAAE,QAAQ,YAAY,CAAZ,EADD,EAErB,GAFqB,CAEjB,OAAO,IAAP,EAAa,MAFI,CAAlB,CAHsB;;AAO5B,MAAM,aAAa,SAAb,UAAa,CAAC,CAAD,EAAO;AACxB,WAAO,MAAM,IAAN,GACL;;;AACE,8BAAoB,IAApB;AACA,mBAAc,QAAQ,UAAR,SAAsB,UAAK,QAAQ,WAAR;;UAEzC,OAAO,OAAO,WAAP;AACP,cAAM,YAAY,GAAZ,GAAkB,IAAlB,EALR;MAMG,IAAI,CAAJ;KAPE,GAUL;;;AACE,uBAAa,CAAb;AACA,mBAAW,QAAQ,UAAR;AACX,eAAO,OAAO,UAAP;AACP,cAAM,YAAY,GAAZ,GAAkB,IAAlB;AACN,iBAAS,QAAQ,IAAR,CAAa,IAAb,EAAmB,CAAnB,CAAT,EALF;MAMG,IAAI,CAAJ;KAhBE,CADiB;GAAP,CAPS;;AA6B5B,SACE;;;AACE,WAAI,wBAAJ;AACA,iBAAW,QAAQ,UAAR;AACX,aAAO,OAAO,UAAP,EAHT;IAIG,gBAAgB,GAAhB,CAAoB,UAApB,CAJH;GADF,CA7B4B;CAAX;;kBAuCJ;;;AAEf,WAAW,SAAX,GAAuB;AACrB,WAAS,oBAAU,MAAV;AACT,UAAQ,oBAAU,MAAV;AACR,aAAW,oBAAU,IAAV;AACX,QAAM,oBAAU,MAAV;AACN,aAAW,oBAAU,MAAV;AACX,WAAS,oBAAU,IAAV;CANX;;;;;;;;;;;AC3CA;;;;AACA;;;;AACA;;;;AAEA;;;;AACA;;;;AAEA;;;;;;;;;;;;;IAEM;;;AACJ,WADI,UACJ,CAAY,KAAZ,EAAmB;0BADf,YACe;;uEADf,uBAEI,QADW;;AAGjB,UAAK,KAAL,GAAa;AACX,YAAM,CAAN;AACA,iBAAW,CAAX;KAFF,CAHiB;;AAQjB,UAAK,IAAL,GAAY,MAAK,IAAL,CAAU,IAAV,OAAZ,CARiB;AASjB,UAAK,QAAL,GAAgB,MAAK,QAAL,CAAc,IAAd,OAAhB,CATiB;AAUjB,UAAK,OAAL,GAAe,MAAK,OAAL,CAAa,IAAb,OAAf,CAViB;AAWjB,UAAK,WAAL,GAAmB,MAAK,WAAL,CAAiB,IAAjB,OAAnB,CAXiB;AAYjB,UAAK,WAAL,GAAmB,MAAK,WAAL,CAAiB,IAAjB,OAAnB,CAZiB;;GAAnB;;eADI;;yCAgBiB;;mBAEc,KAAK,KAAL,CAFd;UAEX,2BAFW;UAED,+BAFC;;;AAInB,WAAK,QAAL,CAAc;AACZ,mBAAW,SAAS,MAAT,GAAkB,CAAlB;AACX,8BAFY;OAAd,EAJmB;;;;2BAUd;mBACuB,KAAK,KAAL,CADvB;UACG,mBADH;UACS,6BADT;;;AAGL,UAAI,SAAS,SAAT,EAAoB;AACtB,aAAK,QAAL,CAAc,EAAE,MAAM,OAAO,CAAP,EAAtB,EADsB;OAAxB;;;;+BAKS;UACD,OAAS,KAAK,KAAL,CAAT,KADC;;;AAGT,UAAI,SAAS,CAAT,EAAY;AACd,aAAK,QAAL,CAAc,EAAE,MAAM,OAAO,CAAP,EAAtB,EADc;OAAhB;;;;4BAKM,GAAG;AACT,WAAK,QAAL,CAAc,EAAE,MAAM,CAAN,EAAhB,EADS;;;;gCAIC,GAAG;UACL,aAAe,KAAK,KAAL,CAAf,WADK;;;AAGb,UAAI,UAAJ,EAAgB;AACd,YAAI,EAAE,MAAF,GAAW,CAAX,EAAc;AAChB,eAAK,IAAL,GADgB;SAAlB,MAEO;AACL,eAAK,QAAL,GADK;SAFP;OADF;;;;gCASU,GAAG;UACL,OAAmB,KAAnB,KADK;UACC,WAAa,KAAb,SADD;;;AAGb,kCACE,EAAE,cAAF,CAAiB,CAAjB,EAAoB,KAApB,EACA,IAFF,EAGE,QAHF,EAHa;;;;6BAUN;;;oBAOY,KAAK,KAAL,CAPZ;UACC,8BADD;UAEC,8BAFD;UAGC,kCAHD;UAIC,kCAJD;UAKC,4BALD;UAMC,0BAND;UAOC,wBAPD;oBAQqB,KAAK,KAAL,CARrB;UAQC,oBARD;UAQO,8BARP;;;AAUP,UAAM,SACJ;;;AACE,qBAAW,QAAQ,WAAR,IAAuB,QAAQ,QAAR;AAClC,iBAAO,OAAO,WAAP,IAAsB,OAAO,QAAP,EAF/B;QAGG,OAAO,YAAP,CAAoB,GAApB,CAHH;OADI,CAVC;;AAkBP,UAAM,YAAY,SAAS,CAAT,GAAa,MAAb,GAChB;AACE,mBAAW,QAAQ,YAAR;AACX,eAAO,OAAO,YAAP;AACP,eAAO,YAAY,QAAZ;AACP,kBAAU,SAAV;AACA,iBAAS,KAAK,QAAL,EALX,CADgB,CAlBX;;AA2BP,UAAM,aAAa,SAAS,SAAT,GAAqB,MAArB,GACjB;AACE,mBAAW,QAAQ,QAAR;AACX,eAAO,OAAO,QAAP;AACP,eAAO,YAAY,IAAZ;AACP,kBAAU,SAAV;AACA,iBAAS,KAAK,IAAL,EALX,CADiB,CA3BZ;;AAoCP,UAAM,aACJ;AACE,iBAAS,KAAK,OAAL;AACT,cAAM,IAAN;AACA,mBAAW,SAAX;SACI,KAAK,KAAL,CAJN,CADI,CApCC;;AA4CP,UAAM,gBAAgB,YACpB;;;QACG,OAAO,IAAP,CAAY,SAAZ,EAAuB,GAAvB,CAA2B,UAAC,CAAD,EAAI,CAAJ,EAAU;AACpC,iBACE;;;AACE,iCAAiB,CAAjB;AACA,yBAAW,QAAQ,SAAR;AACX,qBAAO,OAAO,SAAP;AACP,oBAAM,YAAY,GAAZ,GAAkB,IAAlB;AACN,uBAAS,OAAK,OAAL,CAAa,IAAb,CAAkB,IAAlB,EAAwB,UAAU,CAAV,CAAxB,CAAT,EALF;YAMK,OAAO,IAAP,CAAY,SAAZ,EAAuB,CAAvB,CANL;WADF,CADoC;SAAV,CAD9B;OADoB,GAelB,IAfkB,CA5Cf;;AA6DP,aACE;;;AACE,eAAI,aAAJ;AACA,qBAAW,QAAQ,OAAR;AACX,iBAAO,OAAO,OAAP;AACP,0BAAgB,KAAK,WAAL;AAChB,uBAAa,KAAK,aAAL,EALf;QAME;;;AACE,uBAAW,QAAQ,IAAR;AACX,mBAAO,OAAO,IAAP,EAFT;UAGK,cACC;;;AACE,sCAAwB,GAAxB;AACA,sCAAwB,GAAxB;AACA,8BAAgB,YAAY,IAAZ,EAHlB;YAIK,SAAS,IAAT,CAJL;WADD,GAOC,SAAS,IAAT,CAPD;SATP;QAkBE;;;AACE,uBAAW,QAAQ,UAAR;AACX,mBAAO,OAAO,UAAP,EAFT;UAGK,aAHL;UAIK,SAJL;;UAIiB,UAJjB;;UAI8B,UAJ9B;SAlBF;OADF,CA7DO;;;;SApEL;;;AA+JN,WAAW,SAAX,GAAuB;AACrB,YAAU,oBAAU,IAAV,CAAe,UAAf;AACV,WAAS,oBAAU,KAAV,CAAgB;AACvB,aAAS,oBAAU,MAAV;AACT,gBAAY,oBAAU,MAAV;AACZ,UAAM,oBAAU,MAAV;AACN,gBAAY,oBAAU,MAAV;AACZ,gBAAY,oBAAU,MAAV;AACZ,iBAAa,oBAAU,MAAV;AACb,eAAW,oBAAU,MAAV;AACX,cAAU,oBAAU,MAAV;AACV,cAAU,oBAAU,MAAV;AACV,kBAAc,oBAAU,MAAV;AACd,iBAAa,oBAAU,MAAV;GAXN,CAAT;AAaA,UAAQ,oBAAU,KAAV,CAAgB;AACtB,aAAS,oBAAU,MAAV;AACT,gBAAY,oBAAU,MAAV;AACZ,UAAM,oBAAU,MAAV;AACN,gBAAY,oBAAU,MAAV;AACZ,gBAAY,oBAAU,MAAV;AACZ,iBAAa,oBAAU,MAAV;AACb,eAAW,oBAAU,MAAV;AACX,cAAU,oBAAU,MAAV;AACV,cAAU,oBAAU,MAAV;AACV,kBAAc,oBAAU,MAAV;AACd,iBAAa,oBAAU,MAAV;GAXP,CAAR;AAaA,eAAa,oBAAU,KAAV,CAAgB;AAC3B,eAAW,oBAAU,MAAV;AACX,UAAM,oBAAU,MAAV;AACN,cAAU,oBAAU,MAAV;GAHC,CAAb;AAKA,aAAW,oBAAU,MAAV;AACX,aAAW,oBAAU,IAAV;AACX,eAAa,oBAAU,KAAV,CAAgB;AAC3B,YAAQ,oBAAU,IAAV;AACR,UAAM,oBAAU,MAAV;GAFK,CAAb;AAIA,cAAY,oBAAU,IAAV;CAvCd;;AA0CA,WAAW,YAAX,GAA0B;AACxB,WAAS,EAAT;AACA,UAAQ,EAAR;AACA,eAAa;AACX,UAAM,WAAN;AACA,cAAU,eAAV;GAFF;AAIA,aAAW,IAAX;;AAEA,cAAY,KAAZ;CATF;;kBAYe;;;;;;AC/Nf,IAAM,WAAW;AACf,SAAO,CAAP;AACA,QAAM,CAAN;AACA,aAAW,IAAX;AACA,0BAAQ;AACN,SAAK,KAAL,GAAa,CAAb,CADM;AAEN,SAAK,IAAL,GAAY,CAAZ,CAFM;GAJO;CAAX;;kBAUS,UAAC,CAAD,EAAI,IAAJ,EAAU,KAAV,EAAoB;AACjC,MAAI,KAAK,KAAL,CAAW,CAAX,CAAJ,CADiC;;AAGjC,WAAS,SAAT,GAAqB,IAArB,CAHiC;;AAKjC,MAAI,IAAI,GAAJ,EAAS;AACX,aAAS,IAAT,IAAiB,CAAjB,CADW;AAEX,aAAS,KAAT,GAAiB,CAAjB,CAFW;GAAb,MAGO,IAAI,IAAI,GAAJ,EAAS;AAClB,aAAS,KAAT,IAAkB,CAAlB,CADkB;AAElB,aAAS,IAAT,GAAgB,CAAhB,CAFkB;GAAb;;AAKP,MAAI,SAAS,IAAT,GAAgB,CAAhB,EAAmB;AACrB,aAAS,KAAT,GADqB;AAErB,WAFqB;AAGrB,WAHqB;GAAvB;;AAMA,MAAI,SAAS,KAAT,GAAiB,CAAjB,EAAoB;AACtB,aAAS,KAAT,GADsB;AAEtB,YAFsB;AAGtB,WAHsB;GAAxB;CAnBa","file":"react-shift.js","sourcesContent":["import React from 'react';\nimport PropTypes from 'prop-types';\n\nconst Arrow = ({ fakeLink, onClick, label, className, style }) => (\n \n {label}\n \n);\n\nArrow.propTypes = {\n fakeLink: PropTypes.bool,\n onClick: PropTypes.func,\n label: PropTypes.string,\n className: PropTypes.string,\n style: PropTypes.object\n};\n\nexport default Arrow;\n","import React from 'react';\nimport PropTypes from 'prop-types';\n\nconst Pagination = (props) => {\n const { classes, styles, fakeLinks, page, pageCount, onClick } = props;\n\n const paginationArray = Array\n .apply(null, { length: pageCount + 1 })\n .map(Number.call, Number);\n\n const renderPage = (n) => {\n return n === page ? (\n \n {n + 1}\n \n ) : (\n \n {n + 1}\n \n );\n };\n\n return (\n \n {paginationArray.map(renderPage)}\n \n );\n};\n\nexport default Pagination;\n\nPagination.propTypes = {\n classes: PropTypes.object,\n styles: PropTypes.object,\n fakeLinks: PropTypes.bool,\n page: PropTypes.number,\n pageCount: PropTypes.number,\n onClick: PropTypes.func\n};\n","/* eslint react/no-did-mount-set-state:0 */\nimport React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport ReactCSSTransitionGroup from 'react-addons-css-transition-group';\n\nimport Arrow from './arrow.jsx';\nimport Pagination from './pagination.jsx';\n\nimport touchHandler from './touch-handler';\n\nclass ReactShift extends Component {\n constructor(props) {\n super(props);\n\n this.state = {\n page: 0,\n pageCount: 0\n };\n\n this.next = this.next.bind(this);\n this.previous = this.previous.bind(this);\n this.setPage = this.setPage.bind(this);\n this.handleWheel = this.handleWheel.bind(this);\n this.handleTouch = this.handleTouch.bind(this);\n }\n\n componentWillMount() {\n // TODO move this out of componentDidMount\n const { children, scrollable } = this.props;\n\n this.setState({\n pageCount: children.length - 1,\n scrollable\n });\n }\n\n next() {\n const { page, pageCount } = this.state;\n\n if (page !== pageCount) {\n this.setState({ page: page + 1 });\n }\n }\n\n previous() {\n const { page } = this.state;\n\n if (page !== 0) {\n this.setState({ page: page - 1 });\n }\n }\n\n setPage(n) {\n this.setState({ page: n });\n }\n\n handleWheel(e) {\n const { scrollable } = this.props;\n\n if (scrollable) {\n if (e.deltaY > 0) {\n this.next();\n } else {\n this.previous();\n }\n }\n }\n\n handleTouch(e) {\n const { next, previous } = this;\n\n touchHandler(\n e.changedTouches[0].pageX,\n next,\n previous\n );\n }\n\n render() {\n const { fastLinks,\n fakeLinks,\n arrowLabels,\n transitions,\n children,\n classes,\n styles } = this.props;\n const { page, pageCount } = this.state;\n\n const filler = (\n \n {String.fromCharCode('\\u00a0')}\n \n );\n\n const leftArrow = page === 0 ? filler : (\n \n );\n\n const rightArrow = page === pageCount ? filler : (\n \n );\n\n const pagination = (\n \n );\n\n const fastLinksList = fastLinks ? (\n
\n {Object.keys(fastLinks).map((i, v) => {\n return (\n \n {Object.keys(fastLinks)[v]}\n \n );\n })}\n
\n ) : null;\n\n return (\n \n \n {transitions ?\n \n {children[page]}\n \n : children[page]}\n \n \n {fastLinksList}\n {leftArrow} {pagination} {rightArrow}\n \n \n );\n }\n}\n\nReactShift.propTypes = {\n children: PropTypes.node.isRequired,\n classes: PropTypes.shape({\n wrapper: PropTypes.string,\n navigation: PropTypes.string,\n page: PropTypes.string,\n pagination: PropTypes.string,\n pageNumber: PropTypes.string,\n currentPage: PropTypes.string,\n fastLinks: PropTypes.string,\n navArrow: PropTypes.string,\n nextPage: PropTypes.string,\n previousPage: PropTypes.string,\n arrowFiller: PropTypes.string\n }),\n styles: PropTypes.shape({\n wrapper: PropTypes.object,\n navigation: PropTypes.object,\n page: PropTypes.object,\n pagination: PropTypes.object,\n pageNumber: PropTypes.object,\n currentPage: PropTypes.object,\n fastLinks: PropTypes.object,\n navArrow: PropTypes.object,\n nextPage: PropTypes.object,\n previousPage: PropTypes.object,\n arrowFiller: PropTypes.object\n }),\n arrowLabels: PropTypes.shape({\n className: PropTypes.string,\n next: PropTypes.string,\n previous: PropTypes.string\n }),\n fastLinks: PropTypes.object,\n fakeLinks: PropTypes.bool,\n transitions: PropTypes.shape({\n active: PropTypes.bool,\n name: PropTypes.string\n }),\n scrollable: PropTypes.bool\n};\n\nReactShift.defaultProps = {\n classes: {},\n styles: {},\n arrowLabels: {\n next: 'Next page',\n previous: 'Previous page'\n },\n fakeLinks: true,\n // TODO: Fix scrolling problems on mobile devices\n scrollable: false\n};\n\nexport default ReactShift;\n","const position = {\n right: 0,\n left: 0,\n direction: null,\n clear() {\n this.right = 0;\n this.left = 0;\n }\n};\n\nexport default (e, left, right) => {\n e = Math.round(e);\n\n position.direction = null;\n\n if (e < 450) {\n position.left += 1;\n position.right = 0;\n } else if (e > 550) {\n position.right += 1;\n position.left = 0;\n }\n\n if (position.left > 4) {\n position.clear();\n left();\n return;\n }\n\n if (position.right > 4) {\n position.clear();\n right();\n return;\n }\n};\n"],"sourceRoot":"/source/"} -------------------------------------------------------------------------------- /dist/react-shift.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _react = require('react'); 8 | 9 | var _react2 = _interopRequireDefault(_react); 10 | 11 | var _propTypes = require('prop-types'); 12 | 13 | var _propTypes2 = _interopRequireDefault(_propTypes); 14 | 15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 16 | 17 | var Arrow = function Arrow(_ref) { 18 | var fakeLink = _ref.fakeLink; 19 | var onClick = _ref.onClick; 20 | var label = _ref.label; 21 | var className = _ref.className; 22 | var style = _ref.style; 23 | return _react2.default.createElement( 24 | 'a', 25 | { 26 | className: className, 27 | style: style, 28 | href: fakeLink ? '#' : null, 29 | onClick: onClick }, 30 | label 31 | ); 32 | }; 33 | 34 | Arrow.propTypes = { 35 | fakeLink: _propTypes2.default.bool, 36 | onClick: _propTypes2.default.func, 37 | label: _propTypes2.default.string, 38 | className: _propTypes2.default.string, 39 | style: _propTypes2.default.object 40 | }; 41 | 42 | exports.default = Arrow; 43 | 'use strict'; 44 | 45 | Object.defineProperty(exports, "__esModule", { 46 | value: true 47 | }); 48 | 49 | var _react = require('react'); 50 | 51 | var _react2 = _interopRequireDefault(_react); 52 | 53 | var _propTypes = require('prop-types'); 54 | 55 | var _propTypes2 = _interopRequireDefault(_propTypes); 56 | 57 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 58 | 59 | var Pagination = function Pagination(props) { 60 | var classes = props.classes; 61 | var styles = props.styles; 62 | var fakeLinks = props.fakeLinks; 63 | var page = props.page; 64 | var pageCount = props.pageCount; 65 | var onClick = props.onClick; 66 | 67 | 68 | var paginationArray = Array.apply(null, { length: pageCount + 1 }).map(Number.call, Number); 69 | 70 | var renderPage = function renderPage(n) { 71 | return n === page ? _react2.default.createElement( 72 | 'a', 73 | { 74 | key: 'currentPage-' + page, 75 | className: classes.pageNumber + '-' + n + ' ' + classes.currentPage 76 | // TODO Implement unique style prop for each page number element 77 | , style: styles.currentPage, 78 | href: fakeLinks ? '#' : null }, 79 | n + 1 80 | ) : _react2.default.createElement( 81 | 'a', 82 | { 83 | key: 'page-' + n, 84 | className: classes.pageNumber, 85 | style: styles.pageNumber, 86 | href: fakeLinks ? '#' : null, 87 | onClick: onClick.bind(null, n) }, 88 | n + 1 89 | ); 90 | }; 91 | 92 | return _react2.default.createElement( 93 | 'span', 94 | { 95 | key: 'react-shift-pagination', 96 | className: classes.pagination, 97 | style: styles.pagination }, 98 | paginationArray.map(renderPage) 99 | ); 100 | }; 101 | 102 | exports.default = Pagination; 103 | 104 | 105 | Pagination.propTypes = { 106 | classes: _propTypes2.default.object, 107 | styles: _propTypes2.default.object, 108 | fakeLinks: _propTypes2.default.bool, 109 | page: _propTypes2.default.number, 110 | pageCount: _propTypes2.default.number, 111 | onClick: _propTypes2.default.func 112 | }; 113 | 'use strict'; 114 | 115 | Object.defineProperty(exports, "__esModule", { 116 | value: true 117 | }); 118 | 119 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 120 | 121 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 122 | 123 | var _react = require('react'); 124 | 125 | var _react2 = _interopRequireDefault(_react); 126 | 127 | var _propTypes = require('prop-types'); 128 | 129 | var _propTypes2 = _interopRequireDefault(_propTypes); 130 | 131 | var _reactAddonsCssTransitionGroup = require('react-addons-css-transition-group'); 132 | 133 | var _reactAddonsCssTransitionGroup2 = _interopRequireDefault(_reactAddonsCssTransitionGroup); 134 | 135 | var _arrow = require('./arrow.jsx'); 136 | 137 | var _arrow2 = _interopRequireDefault(_arrow); 138 | 139 | var _pagination = require('./pagination.jsx'); 140 | 141 | var _pagination2 = _interopRequireDefault(_pagination); 142 | 143 | var _touchHandler = require('./touch-handler'); 144 | 145 | var _touchHandler2 = _interopRequireDefault(_touchHandler); 146 | 147 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 148 | 149 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 150 | 151 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 152 | 153 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* eslint react/no-did-mount-set-state:0 */ 154 | 155 | 156 | var ReactShift = function (_Component) { 157 | _inherits(ReactShift, _Component); 158 | 159 | function ReactShift(props) { 160 | _classCallCheck(this, ReactShift); 161 | 162 | var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(ReactShift).call(this, props)); 163 | 164 | _this.state = { 165 | page: 0, 166 | pageCount: 0 167 | }; 168 | 169 | _this.next = _this.next.bind(_this); 170 | _this.previous = _this.previous.bind(_this); 171 | _this.setPage = _this.setPage.bind(_this); 172 | _this.handleWheel = _this.handleWheel.bind(_this); 173 | _this.handleTouch = _this.handleTouch.bind(_this); 174 | return _this; 175 | } 176 | 177 | _createClass(ReactShift, [{ 178 | key: 'componentWillMount', 179 | value: function componentWillMount() { 180 | // TODO move this out of componentDidMount 181 | var _props = this.props; 182 | var children = _props.children; 183 | var scrollable = _props.scrollable; 184 | 185 | 186 | this.setState({ 187 | pageCount: children.length - 1, 188 | scrollable: scrollable 189 | }); 190 | } 191 | }, { 192 | key: 'next', 193 | value: function next() { 194 | var _state = this.state; 195 | var page = _state.page; 196 | var pageCount = _state.pageCount; 197 | 198 | 199 | if (page !== pageCount) { 200 | this.setState({ page: page + 1 }); 201 | } 202 | } 203 | }, { 204 | key: 'previous', 205 | value: function previous() { 206 | var page = this.state.page; 207 | 208 | 209 | if (page !== 0) { 210 | this.setState({ page: page - 1 }); 211 | } 212 | } 213 | }, { 214 | key: 'setPage', 215 | value: function setPage(n) { 216 | this.setState({ page: n }); 217 | } 218 | }, { 219 | key: 'handleWheel', 220 | value: function handleWheel(e) { 221 | var scrollable = this.props.scrollable; 222 | 223 | 224 | if (scrollable) { 225 | if (e.deltaY > 0) { 226 | this.next(); 227 | } else { 228 | this.previous(); 229 | } 230 | } 231 | } 232 | }, { 233 | key: 'handleTouch', 234 | value: function handleTouch(e) { 235 | var next = this.next; 236 | var previous = this.previous; 237 | 238 | 239 | (0, _touchHandler2.default)(e.changedTouches[0].pageX, next, previous); 240 | } 241 | }, { 242 | key: 'render', 243 | value: function render() { 244 | var _this2 = this; 245 | 246 | var _props2 = this.props; 247 | var fastLinks = _props2.fastLinks; 248 | var fakeLinks = _props2.fakeLinks; 249 | var arrowLabels = _props2.arrowLabels; 250 | var transitions = _props2.transitions; 251 | var children = _props2.children; 252 | var classes = _props2.classes; 253 | var styles = _props2.styles; 254 | var _state2 = this.state; 255 | var page = _state2.page; 256 | var pageCount = _state2.pageCount; 257 | 258 | 259 | var filler = _react2.default.createElement( 260 | 'a', 261 | { 262 | className: classes.arrowFiller || classes.navArrow, 263 | style: styles.arrowFiller || styles.navArrow }, 264 | String.fromCharCode(' ') 265 | ); 266 | 267 | var leftArrow = page === 0 ? filler : _react2.default.createElement(_arrow2.default, { 268 | className: classes.previousPage, 269 | style: styles.previousPage, 270 | label: arrowLabels.previous, 271 | fakeLink: fakeLinks, 272 | onClick: this.previous }); 273 | 274 | var rightArrow = page === pageCount ? filler : _react2.default.createElement(_arrow2.default, { 275 | className: classes.nextPage, 276 | style: styles.nextPage, 277 | label: arrowLabels.next, 278 | fakeLink: fakeLinks, 279 | onClick: this.next }); 280 | 281 | var pagination = _react2.default.createElement(_pagination2.default, _extends({ 282 | onClick: this.setPage, 283 | page: page, 284 | pageCount: pageCount 285 | }, this.props)); 286 | 287 | var fastLinksList = fastLinks ? _react2.default.createElement( 288 | 'div', 289 | null, 290 | Object.keys(fastLinks).map(function (i, v) { 291 | return _react2.default.createElement( 292 | 'a', 293 | { 294 | key: 'fastLink-' + i, 295 | className: classes.fastLinks, 296 | style: styles.faskLinks, 297 | href: fakeLinks ? '#' : null, 298 | onClick: _this2.setPage.bind(null, fastLinks[i]) }, 299 | Object.keys(fastLinks)[v] 300 | ); 301 | }) 302 | ) : null; 303 | 304 | return _react2.default.createElement( 305 | 'div', 306 | { 307 | key: 'react-shift', 308 | className: classes.wrapper, 309 | style: styles.wrapper, 310 | onWheelCapture: this.handleWheel, 311 | onTouchMove: this.handconstouch }, 312 | _react2.default.createElement( 313 | 'div', 314 | { 315 | className: classes.page, 316 | style: styles.page }, 317 | transitions ? _react2.default.createElement( 318 | _reactAddonsCssTransitionGroup2.default, 319 | { 320 | transitionEnterTimeout: 300, 321 | transitionLeaveTimeout: 300, 322 | transitionName: transitions.name }, 323 | children[page] 324 | ) : children[page] 325 | ), 326 | _react2.default.createElement( 327 | 'nav', 328 | { 329 | className: classes.navigation, 330 | style: styles.navigation }, 331 | fastLinksList, 332 | leftArrow, 333 | ' ', 334 | pagination, 335 | ' ', 336 | rightArrow 337 | ) 338 | ); 339 | } 340 | }]); 341 | 342 | return ReactShift; 343 | }(_react.Component); 344 | 345 | ReactShift.propTypes = { 346 | children: _propTypes2.default.node.isRequired, 347 | classes: _propTypes2.default.shape({ 348 | wrapper: _propTypes2.default.string, 349 | navigation: _propTypes2.default.string, 350 | page: _propTypes2.default.string, 351 | pagination: _propTypes2.default.string, 352 | pageNumber: _propTypes2.default.string, 353 | currentPage: _propTypes2.default.string, 354 | fastLinks: _propTypes2.default.string, 355 | navArrow: _propTypes2.default.string, 356 | nextPage: _propTypes2.default.string, 357 | previousPage: _propTypes2.default.string, 358 | arrowFiller: _propTypes2.default.string 359 | }), 360 | styles: _propTypes2.default.shape({ 361 | wrapper: _propTypes2.default.object, 362 | navigation: _propTypes2.default.object, 363 | page: _propTypes2.default.object, 364 | pagination: _propTypes2.default.object, 365 | pageNumber: _propTypes2.default.object, 366 | currentPage: _propTypes2.default.object, 367 | fastLinks: _propTypes2.default.object, 368 | navArrow: _propTypes2.default.object, 369 | nextPage: _propTypes2.default.object, 370 | previousPage: _propTypes2.default.object, 371 | arrowFiller: _propTypes2.default.object 372 | }), 373 | arrowLabels: _propTypes2.default.shape({ 374 | className: _propTypes2.default.string, 375 | next: _propTypes2.default.string, 376 | previous: _propTypes2.default.string 377 | }), 378 | fastLinks: _propTypes2.default.object, 379 | fakeLinks: _propTypes2.default.bool, 380 | transitions: _propTypes2.default.shape({ 381 | active: _propTypes2.default.bool, 382 | name: _propTypes2.default.string 383 | }), 384 | scrollable: _propTypes2.default.bool 385 | }; 386 | 387 | ReactShift.defaultProps = { 388 | classes: {}, 389 | styles: {}, 390 | arrowLabels: { 391 | next: 'Next page', 392 | previous: 'Previous page' 393 | }, 394 | fakeLinks: true, 395 | // TODO: Fix scrolling problems on mobile devices 396 | scrollable: false 397 | }; 398 | 399 | exports.default = ReactShift; 400 | "use strict"; 401 | 402 | Object.defineProperty(exports, "__esModule", { 403 | value: true 404 | }); 405 | var position = { 406 | right: 0, 407 | left: 0, 408 | direction: null, 409 | clear: function clear() { 410 | this.right = 0; 411 | this.left = 0; 412 | } 413 | }; 414 | 415 | exports.default = function (e, left, right) { 416 | e = Math.round(e); 417 | 418 | position.direction = null; 419 | 420 | if (e < 450) { 421 | position.left += 1; 422 | position.right = 0; 423 | } else if (e > 550) { 424 | position.right += 1; 425 | position.left = 0; 426 | } 427 | 428 | if (position.left > 4) { 429 | position.clear(); 430 | left(); 431 | return; 432 | } 433 | 434 | if (position.right > 4) { 435 | position.clear(); 436 | right(); 437 | return; 438 | } 439 | }; 440 | //# sourceMappingURL=react-shift.js.map 441 | --------------------------------------------------------------------------------