├── examples ├── label.html ├── simple.html ├── simple.js └── label.js ├── tests ├── runner.html └── index.spec.js ├── index.js ├── src ├── index.js └── Pager.jsx ├── HISTORY.md ├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── package.json ├── assets └── bootstrap.less └── README.md /examples/label.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/simple.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/runner.html: -------------------------------------------------------------------------------- 1 | stub 2 | 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./src/'); 4 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./Pager'); 4 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # History 2 | ---- 3 | 4 | ## 2.2.0 / 2015-06-01 5 | 6 | `new` [https://github.com/react-component/pager/pull/3](https://github.com/react-component/pager/pull/3) add previousLabel and nextlabel -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*.{js,css}] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.log 3 | .idea/ 4 | .ipr 5 | .iws 6 | *~ 7 | ~* 8 | *.diff 9 | *.patch 10 | *.bak 11 | .DS_Store 12 | Thumbs.db 13 | .project 14 | .*proj 15 | .svn/ 16 | *.swp 17 | *.swo 18 | *.pyc 19 | *.pyo 20 | .build 21 | node_modules 22 | .cache 23 | dist 24 | assets/**/*.css 25 | build 26 | lib -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.cfg 3 | nohup.out 4 | *.iml 5 | .idea/ 6 | .ipr 7 | .iws 8 | *~ 9 | ~* 10 | *.diff 11 | *.log 12 | *.patch 13 | *.bak 14 | .DS_Store 15 | Thumbs.db 16 | .project 17 | .*proj 18 | .svn/ 19 | *.swp 20 | out/ 21 | .build 22 | node_modules 23 | .cache 24 | examples 25 | tests 26 | src 27 | /index.js 28 | .* 29 | assets/**/*.less -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | notifications: 4 | email: 5 | - yiminghe@gmail.com 6 | 7 | node_js: 8 | - 0.12 9 | 10 | before_install: 11 | - | 12 | if ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qvE '(\.md$)|(^(docs|examples))/' 13 | then 14 | echo "Only docs were updated, stopping build process." 15 | exit 16 | fi 17 | npm install mocha-phantomjs -g 18 | phantomjs --version 19 | 20 | script: 21 | - | 22 | if [ "$TEST_TYPE" = test ]; then 23 | npm test 24 | else 25 | npm run $TEST_TYPE 26 | fi 27 | 28 | env: 29 | matrix: 30 | - TEST_TYPE=lint 31 | - TEST_TYPE=browser-test 32 | - TEST_TYPE=browser-test-cover 33 | - TEST_TYPE=saucelabs -------------------------------------------------------------------------------- /examples/simple.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | var Pager = require('rc-pager'); 5 | 6 | require('rc-pager/assets/bootstrap.css'); 7 | 8 | class MyControl extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | current: 0 13 | }; 14 | } 15 | 16 | handleSkip(page) { 17 | this.setState({ 18 | current: page 19 | }); 20 | 21 | if (this.props.onSkipTo) { 22 | this.props.onSkipTo(page); 23 | } 24 | } 25 | 26 | render() { 27 | return ( 28 |
29 | 30 |
31 | ); 32 | } 33 | } 34 | 35 | React.render( 36 |
37 |

simple pager

38 | 39 |
, 40 | document.getElementById('__react-content') 41 | ); 42 | -------------------------------------------------------------------------------- /examples/label.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | var Pager = require('rc-pager'); 5 | 6 | require('rc-pager/assets/bootstrap.css'); 7 | 8 | class MyControl extends React.Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | current: 0 13 | }; 14 | } 15 | 16 | handleSkip(page) { 17 | this.setState({ 18 | current: page 19 | }); 20 | 21 | if (this.props.onSkipTo) { 22 | this.props.onSkipTo(page); 23 | } 24 | } 25 | 26 | render() { 27 | return ( 28 |
29 | 34 |
35 | ); 36 | } 37 | } 38 | 39 | React.render( 40 |
41 |

simple pager

42 | 43 |
, 44 | document.getElementById('__react-content') 45 | ); 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rc-pager", 3 | "version": "2.2.0", 4 | "description": "pager ui component for react", 5 | "keywords": [ 6 | "react", 7 | "react-pager" 8 | ], 9 | "main": "./lib/index", 10 | "homepage": "http://github.com/react-component/pager", 11 | "maintainers": [ 12 | "yiminghe@gmail.com", 13 | "dxq613@gmail.com" 14 | ], 15 | "repository": { 16 | "type": "git", 17 | "url": "git@github.com:react-component/pager.git" 18 | }, 19 | "bugs": { 20 | "url": "http://github.com/react-component/pager/issues" 21 | }, 22 | "licenses": "MIT", 23 | "config": { 24 | "port": 8000 25 | }, 26 | "scripts": { 27 | "build": "rc-tools run build", 28 | "precommit": "rc-tools run precommit", 29 | "less": "rc-tools run less", 30 | "gh-pages": "rc-tools run gh-pages", 31 | "history": "rc-tools run history", 32 | "start": "node --harmony node_modules/.bin/rc-server", 33 | "publish": "rc-tools run tag", 34 | "lint": "rc-tools run lint", 35 | "saucelabs": "node --harmony node_modules/.bin/rc-tools run saucelabs", 36 | "browser-test": "node --harmony node_modules/.bin/rc-tools run browser-test", 37 | "browser-test-cover": "node --harmony node_modules/.bin/rc-tools run browser-test-cover" 38 | }, 39 | "devDependencies": { 40 | "expect.js": "~0.3.1", 41 | "jquery": "^1.11.2", 42 | "precommit-hook": "^1.0.7", 43 | "rc-server": "3.x", 44 | "rc-tools": "3.x", 45 | "react": "~0.13.0", 46 | "sinon": "1.x" 47 | }, 48 | "precommit": [ 49 | "precommit" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /assets/bootstrap.less: -------------------------------------------------------------------------------- 1 | .rc-pager { 2 | display: inline-block; 3 | padding-left: 0; 4 | margin: 20px 0; 5 | border-radius: 4px; 6 | } 7 | .rc-pager > li { 8 | display: inline; 9 | } 10 | .rc-pager > li > a, 11 | .rc-pager > li > span { 12 | position: relative; 13 | float: left; 14 | padding: 6px 12px; 15 | margin-left: -1px; 16 | line-height: 1.42857143; 17 | color: #337ab7; 18 | text-decoration: none; 19 | background-color: #fff; 20 | border: 1px solid #ddd; 21 | } 22 | .rc-pager > li:first-child > a, 23 | .rc-pager > li:first-child > span { 24 | margin-left: 0; 25 | border-top-left-radius: 4px; 26 | border-bottom-left-radius: 4px; 27 | } 28 | .rc-pager > li:last-child > a, 29 | .rc-pager > li:last-child > span { 30 | border-top-right-radius: 4px; 31 | border-bottom-right-radius: 4px; 32 | } 33 | .rc-pager > li > a:hover, 34 | .rc-pager > li > span:hover, 35 | .rc-pager > li > a:focus, 36 | .rc-pager > li > span:focus { 37 | color: #23527c; 38 | background-color: #eee; 39 | border-color: #ddd; 40 | } 41 | .rc-pager > .rc-pager-item-active > a, 42 | .rc-pager > .rc-pager-item-active > span, 43 | .rc-pager > .rc-pager-item-active > a:hover, 44 | .rc-pager > .rc-pager-item-active > span:hover, 45 | .rc-pager > .rc-pager-item-active > a:focus, 46 | .rc-pager > .rc-pager-item-active > span:focus { 47 | z-index: 2; 48 | color: #fff; 49 | cursor: default; 50 | background-color: #337ab7; 51 | border-color: #337ab7; 52 | } 53 | .rc-pager > .rc-pager-item-disabled > span, 54 | .rc-pager > .rc-pager-item-disabled > span:hover, 55 | .rc-pager > .rc-pager-item-disabled > span:focus, 56 | .rc-pager > .rc-pager-item-disabled > a, 57 | .rc-pager > .rc-pager-item-disabled > a:hover, 58 | .rc-pager > .rc-pager-item-disabled > a:focus { 59 | color: #777; 60 | cursor: not-allowed; 61 | background-color: #fff; 62 | border-color: #ddd; 63 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rc-pager 2 | 3 | react pager component 4 | 5 | [![NPM version][npm-image]][npm-url] 6 | [![build status][travis-image]][travis-url] 7 | [![Test coverage][coveralls-image]][coveralls-url] 8 | [![gemnasium deps][gemnasium-image]][gemnasium-url] 9 | [![node version][node-image]][node-url] 10 | [![npm download][download-image]][download-url] 11 | 12 | [npm-image]: http://img.shields.io/npm/v/rc-pager.svg?style=flat-square 13 | [npm-url]: http://npmjs.org/package/rc-pager 14 | [travis-image]: https://img.shields.io/travis/react-component/pager.svg?style=flat-square 15 | [travis-url]: https://travis-ci.org/react-component/pager 16 | [coveralls-image]: https://img.shields.io/coveralls/react-component/pager.svg?style=flat-square 17 | [coveralls-url]: https://coveralls.io/r/react-component/pager?branch=master 18 | [gemnasium-image]: http://img.shields.io/gemnasium/react-component/pager.svg?style=flat-square 19 | [gemnasium-url]: https://gemnasium.com/react-component/pager 20 | [node-image]: https://img.shields.io/badge/node.js-%3E=_0.10-green.svg?style=flat-square 21 | [node-url]: http://nodejs.org/download/ 22 | [download-image]: https://img.shields.io/npm/dm/rc-pager.svg?style=flat-square 23 | [download-url]: https://npmjs.org/package/rc-pager 24 | 25 | ## install 26 | 27 | [![rc-pager](https://nodei.co/npm/rc-pager.png)](https://npmjs.org/package/rc-pager) 28 | 29 | ## Usage 30 | 31 | ```js 32 | var Pager = require('rc-pager'); 33 | React.renderComponent( 34 | , 35 | document.getElementById('p1') 36 | ); 37 | ``` 38 | 39 | ## Development 40 | 41 | ``` 42 | npm install 43 | npm start 44 | ``` 45 | 46 | ## Example 47 | 48 | http://localhost:8000/examples/ 49 | 50 | online example: 51 | 52 | http://react-component.github.io/pager/build/examples/index.html 53 | 54 | ## API 55 | 56 | #### total `Number` 57 | 58 | * total page 59 | 60 | #### current `Number` 61 | 62 | * current page 63 | 64 | #### previousLabel `String|Element` 65 | 66 | * previous page label, also can be a React Component 67 | 68 | #### nextLabel `String|Element` 69 | 70 | * next page label, also can be a React Component 71 | 72 | #### onSkipTo(page) `Function` 73 | 74 | * The callback when the page skip 75 | 76 | ## Test Case 77 | 78 | http://localhost:8000/tests/runner.html?coverage 79 | 80 | ## Coverage 81 | 82 | http://localhost:8000/node_modules/rc-server/node_modules/node-jscover/lib/front-end/jscoverage.html?w=http://localhost:8000/tests/runner.html?coverage 83 | 84 | ## License 85 | 86 | rc-pager is released under the MIT license. 87 | -------------------------------------------------------------------------------- /src/Pager.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | 5 | class PagerItem extends React.Component { 6 | handleClick(ev) { 7 | ev.preventDefault(); 8 | if (!this.props.active && !this.props.disabled) { 9 | this.props.skipTo(this.props.page); 10 | } 11 | } 12 | 13 | render() { 14 | var status = this.props.active ? 'rc-pager-item-active' : ''; 15 | if (this.props.disabled) { 16 | status += ' rc-pager-item-disabled'; 17 | } 18 | return (
  • 19 | {this.props.text} 20 |
  • ); 21 | } 22 | } 23 | 24 | /** 25 | * @private 26 | * 命令子项 27 | */ 28 | class CmdItem extends React.Component { 29 | handleClick(ev) { 30 | ev.preventDefault(); 31 | if (!this.props.disabled) { 32 | this.props.skipTo(this.props.page); 33 | } 34 | } 35 | 36 | render() { 37 | var disabled = this.props.disabled ? 'rc-pager-item-disabled' : ''; 38 | return (
  • 39 | 40 | 41 | 42 |
  • ); 43 | } 44 | } 45 | 46 | //分页栏 47 | class Pager extends React.Component { 48 | constructor(props) { 49 | super(props); 50 | this.skipTo = this.skipTo.bind(this); 51 | } 52 | 53 | //获取首页按钮 54 | _getFirstItem() { 55 | var self = this, 56 | props = self.props, 57 | current = props.current, 58 | disabled = current === 0, 59 | label = props.previousLabel || '«'; 60 | 61 | return ; 62 | } 63 | 64 | //获取最后一页按钮 65 | _getLastItem() { 66 | var self = this, 67 | total = self.props.total, 68 | current = self.props.current, 69 | disabled = current === total - 1, 70 | label = self.props.nextLabel || '»'; 71 | 72 | return ; 73 | } 74 | 75 | //获取所有数字按钮 76 | _getItems() { 77 | var self = this, 78 | total = self.props.total, 79 | current = self.props.current, 80 | rst = [], 81 | from = 0, 82 | active, 83 | skip = 2, 84 | to = total - 1; 85 | 86 | if (current > skip) { 87 | from = current - skip; 88 | } 89 | if (total - current > skip) { 90 | to = current + skip; 91 | } 92 | if (from !== 0) { 93 | rst.push(); 94 | if (from > 1) { 95 | rst.push(); 96 | } 97 | } 98 | 99 | for (var i = from; i <= to; i++) { 100 | active = current === i; 101 | rst.push(); 102 | } 103 | 104 | if (to < total - 1) { 105 | active = current === total - 1; 106 | if (to < total - 2) { 107 | rst.push(); 108 | } 109 | rst.push(); 110 | } 111 | return rst; 112 | } 113 | 114 | /** 115 | * 跳转到对应的节点 116 | * @param {Number} page 页码 117 | */ 118 | skipTo(page) { 119 | var handler = this.props.onSkipTo; 120 | if (handler) { 121 | handler(page); 122 | } 123 | } 124 | 125 | render() { 126 | var self = this, 127 | first = self._getFirstItem(), 128 | last = self._getLastItem(), 129 | items = self._getItems(); 130 | 131 | var className = 'rc-pager'; 132 | if (this.props.className) { 133 | className += ' ' + this.props.className; 134 | } 135 | 136 | return ( 137 |
      138 | {first} 139 | {items} 140 | {last} 141 |
    142 | ); 143 | } 144 | } 145 | 146 | module.exports = Pager; 147 | -------------------------------------------------------------------------------- /tests/index.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expect = require('expect.js'); 4 | var Pager = require('../index'); 5 | var React = require('react/addons'); 6 | var TestUtils = React.addons.TestUtils; 7 | var Simulate = TestUtils.Simulate; 8 | var sinon = require('sinon'); 9 | var $ = require('jquery'); 10 | 11 | require('../assets/bootstrap.css'); 12 | 13 | $('
    ').appendTo('body'); 14 | $('
    ').appendTo('body'); 15 | $('
    ').appendTo('body'); 16 | $('
    ').appendTo('body'); 17 | $('
    ').appendTo('body'); 18 | $('
    ').appendTo('body'); 19 | $('
    ').appendTo('body'); 20 | $('
    ').appendTo('body'); 21 | 22 | var total = 10; 23 | describe('rc-pager', function () { 24 | var current = 3; 25 | 26 | var pager = React.render( 27 | , 28 | document.getElementById('t1') 29 | ); 30 | 31 | var node = $('#t1'); 32 | 33 | it('create', function () { 34 | expect(node.children().length).not.to.be(0); 35 | }); 36 | 37 | it('previousLabel and nextLabel support', function() { 38 | expect(node.find('li:first-child').text()).to.be('prev'); 39 | expect(node.find('li:last-child').text()).to.be('next'); 40 | }); 41 | 42 | it('page struct', function () { 43 | expect(node.find('.rc-pager').length).to.be(1); 44 | expect(node.find('li').length).not.to.be(0); 45 | }); 46 | 47 | it('page current', function () { 48 | expect(pager.props.current).to.be(current); 49 | expect(pager.props.total).to.be(total); 50 | expect(node.find('.rc-pager-item-active').text()).to.be((current + 1).toString()); 51 | }); 52 | 53 | describe('rc-pager chang init', function () { 54 | it('init current last', function () { 55 | var current = 9, 56 | node = $('#t2'); 57 | React.render( 58 | , 59 | document.getElementById('t2') 60 | ); 61 | expect(node.find('.rc-pager-item-active').text()).to.be((current + 1).toString()); 62 | expect(node.find('li').last().hasClass('rc-pager-item-disabled')).to.be(true); 63 | }); 64 | 65 | it('current first', function () { 66 | var current = 0, 67 | node = $('#t3'); 68 | React.render( 69 | , 70 | document.getElementById('t3') 71 | ); 72 | expect(node.find('.rc-pager-item-active').text()).to.be((current + 1).toString()); 73 | expect(node.find('li').first().hasClass('rc-pager-item-disabled')).to.be(true); 74 | }); 75 | 76 | it('skip to middle', function () { 77 | var current = 5, 78 | node = $('#t4'); 79 | React.render( 80 | , 81 | document.getElementById('t4') 82 | ); 83 | expect(node.find('.rc-pager-item-active').text()).to.be((current + 1).toString()); 84 | expect(node.find('li').first().hasClass('rc-pager-item-disabled')).to.be(false); 85 | expect(node.find('li').last().hasClass('rc-pager-item-disabled')).to.be(false); 86 | }); 87 | 88 | it('skip to second', function () { 89 | var current = 1, 90 | node = $('#t6'); 91 | React.render( 92 | , 93 | document.getElementById('t6') 94 | ); 95 | expect(node.find('.rc-pager-item-active').text()).to.be((current + 1).toString()); 96 | expect(node.find('li').first().hasClass('rc-pager-item-disabled')).to.be(false); 97 | expect(node.find('li').last().hasClass('rc-pager-item-disabled')).to.be(false); 98 | }); 99 | 100 | it('skip to last - 1', function () { 101 | var current = 7, 102 | node = $('#t7'); 103 | React.render( 104 | , 105 | document.getElementById('t7') 106 | ); 107 | expect(node.find('.rc-pager-item-active').text()).to.be((current + 1).toString()); 108 | expect(node.find('li').first().hasClass('rc-pager-item-disabled')).to.be(false); 109 | expect(node.find('li').last().hasClass('rc-pager-item-disabled')).to.be(false); 110 | }); 111 | }); 112 | 113 | describe('test event', function () { 114 | var node = $('#t5'); 115 | var callback = sinon.spy(); 116 | React.render( 117 | , 118 | document.getElementById('t5') 119 | ); 120 | 121 | it('test event', function () { 122 | var last = node.find('li').last()[0]; 123 | Simulate.click(last); 124 | expect(callback.called).to.be(true); 125 | }); 126 | 127 | it('test first ', function () { 128 | callback.called = false; 129 | var first = node.find('li').first()[0]; 130 | Simulate.click(first); 131 | expect(callback.called).to.be(false); 132 | var second = node.find('li')[1]; 133 | Simulate.click(first); 134 | expect(callback.called).to.be(false); 135 | var ellipsis = node.find('li')[4]; 136 | Simulate.click(ellipsis); 137 | expect(callback.called).to.be(false); 138 | }); 139 | }) 140 | }); 141 | --------------------------------------------------------------------------------