Skip to content

Commit 5734ec3

Browse files
roadmanroadmanfong
roadman
authored andcommitted
[added] Pagination component
1 parent 12d6ddb commit 5734ec3

11 files changed

+402
-14
lines changed

docs/examples/.eslintrc

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"PageHeader",
4040
"PageItem",
4141
"Pager",
42+
"Pagination",
4243
"Panel",
4344
"PanelGroup",
4445
"Popover",

docs/examples/PaginationAdvanced.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const PaginationAdvanced = React.createClass({
2+
getInitialState() {
3+
return {
4+
activePage: 1
5+
};
6+
},
7+
8+
handleSelect(event, selectedEvent) {
9+
this.setState({
10+
activePage: selectedEvent.eventKey
11+
});
12+
},
13+
14+
render() {
15+
return (
16+
<Pagination
17+
prev={true}
18+
next={true}
19+
first={true}
20+
last={true}
21+
ellipsis={true}
22+
items={20}
23+
maxButtons={5}
24+
activePage={this.state.activePage}
25+
onSelect={this.handleSelect} />
26+
);
27+
}
28+
});
29+
30+
React.render(<PaginationAdvanced />, mountNode);

docs/examples/PaginationBasic.js

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
const PaginationBasic = React.createClass({
2+
getInitialState() {
3+
return {
4+
activePage: 1
5+
};
6+
},
7+
8+
handleSelect(event, selectedEvent){
9+
this.setState({
10+
activePage: selectedEvent.eventKey
11+
});
12+
},
13+
14+
render() {
15+
return (
16+
<div>
17+
<Pagination
18+
bsSize='large'
19+
items={10}
20+
activePage={this.state.activePage}
21+
onSelect={this.handleSelect} />
22+
<br />
23+
24+
<Pagination
25+
bsSize='medium'
26+
items={10}
27+
activePage={this.state.activePage}
28+
onSelect={this.handleSelect} />
29+
<br />
30+
31+
<Pagination
32+
bsSize='small'
33+
items={10}
34+
activePage={this.state.activePage}
35+
onSelect={this.handleSelect} />
36+
</div>
37+
);
38+
}
39+
});
40+
41+
React.render(<PaginationBasic />, mountNode);

docs/src/ComponentsPage.js

+27-13
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,19 @@ const ComponentsPage = React.createClass({
445445
<ReactPlayground codeText={Samples.PagerDisabled} />
446446
</div>
447447

448+
{/* Pagination */}
449+
<div className='bs-docs-section'>
450+
<h1 id='pagination' className='page-header'>Pagination <small>Pagination</small></h1>
451+
<h2 id='pagination-examples'>Example pagination</h2>
452+
453+
<p>Provide pagination links for your site or app with the multi-page pagination component. Set <code>items</code> to the number of pages. <code>activePage</code> prop dictates which page is active</p>
454+
<ReactPlayground codeText={Samples.PaginationBasic} />
455+
456+
<p>More options such as <code>first</code>, <code>last</code>, <code>previous</code>, <code>next</code> and <code>ellipsis</code>.</p>
457+
<ReactPlayground codeText={Samples.PaginationAdvanced} />
458+
459+
</div>
460+
448461
{/* Alerts */}
449462
<div className='bs-docs-section'>
450463
<h1 id='alerts' className='page-header'>Alert messages <small>Alert</small></h1>
@@ -654,19 +667,20 @@ const ComponentsPage = React.createClass({
654667
<NavItem href='#navbars' key={10}>Navbars</NavItem>
655668
<NavItem href='#tabs' key={11}>Togglable tabs</NavItem>
656669
<NavItem href='#pager' key={12}>Pager</NavItem>
657-
<NavItem href='#alerts' key={13}>Alerts</NavItem>
658-
<NavItem href='#carousels' key={14}>Carousels</NavItem>
659-
<NavItem href='#grids' key={15}>Grids</NavItem>
660-
<NavItem href='#thumbnail' key={16}>Thumbnail</NavItem>
661-
<NavItem href='#listgroup' key={17}>List group</NavItem>
662-
<NavItem href='#labels' key={18}>Labels</NavItem>
663-
<NavItem href='#badges' key={19}>Badges</NavItem>
664-
<NavItem href='#jumbotron' key={20}>Jumbotron</NavItem>
665-
<NavItem href='#page-header' key={21}>Page Header</NavItem>
666-
<NavItem href='#wells' key={22}>Wells</NavItem>
667-
<NavItem href='#glyphicons' key={23}>Glyphicons</NavItem>
668-
<NavItem href='#tables' key={24}>Tables</NavItem>
669-
<NavItem href='#input' key={25}>Input</NavItem>
670+
<NavItem href='#pagination' key={13}>Pagination</NavItem>
671+
<NavItem href='#alerts' key={14}>Alerts</NavItem>
672+
<NavItem href='#carousels' key={15}>Carousels</NavItem>
673+
<NavItem href='#grids' key={16}>Grids</NavItem>
674+
<NavItem href='#thumbnail' key={17}>Thumbnail</NavItem>
675+
<NavItem href='#listgroup' key={18}>List group</NavItem>
676+
<NavItem href='#labels' key={19}>Labels</NavItem>
677+
<NavItem href='#badges' key={20}>Badges</NavItem>
678+
<NavItem href='#jumbotron' key={21}>Jumbotron</NavItem>
679+
<NavItem href='#page-header' key={22}>Page Header</NavItem>
680+
<NavItem href='#wells' key={23}>Wells</NavItem>
681+
<NavItem href='#glyphicons' key={24}>Glyphicons</NavItem>
682+
<NavItem href='#tables' key={25}>Tables</NavItem>
683+
<NavItem href='#input' key={26}>Input</NavItem>
670684
</Nav>
671685
<a className='back-to-top' href='#top'>
672686
Back to top

docs/src/ReactPlayground.js

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import * as modOverlayMixin from '../../src/OverlayMixin';
3232
import * as modPageHeader from '../../src/PageHeader';
3333
import * as modPageItem from '../../src/PageItem';
3434
import * as modPager from '../../src/Pager';
35+
import * as modPagination from '../../src/Pagination';
3536
import * as modPanel from '../../src/Panel';
3637
import * as modPanelGroup from '../../src/PanelGroup';
3738
import * as modPopover from '../../src/Popover';
@@ -83,6 +84,7 @@ const OverlayTrigger = modOverlayTrigger.default;
8384
const OverlayMixin = modOverlayMixin.default;
8485
const PageHeader = modPageHeader.default;
8586
const PageItem = modPageItem.default;
87+
const Pagination = modPagination.default;
8688
const Pager = modPager.default;
8789
const Panel = modPanel.default;
8890
const PanelGroup = modPanelGroup.default;

docs/src/Samples.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export default {
1414
ButtonGroupNested: require('fs').readFileSync(__dirname + '/../examples/ButtonGroupNested.js', 'utf8'),
1515
ButtonGroupVertical: require('fs').readFileSync(__dirname + '/../examples/ButtonGroupVertical.js', 'utf8'),
1616
ButtonGroupJustified: require('fs').readFileSync(__dirname + '/../examples/ButtonGroupJustified.js', 'utf8'),
17-
ButtonGroupBlock: require('fs').readFileSync(__dirname + '/../examples/ButtonGroupBlock.js', 'utf8'),
17+
ButtonGroupBlock: require('fs').readFileSync(__dirname + '/../examples/ButtonGroupBlock.js', 'utf8'),
1818
DropdownButtonBasic: require('fs').readFileSync(__dirname + '/../examples/DropdownButtonBasic.js', 'utf8'),
1919
SplitButtonBasic: require('fs').readFileSync(__dirname + '/../examples/SplitButtonBasic.js', 'utf8'),
2020
DropdownButtonSizes: require('fs').readFileSync(__dirname + '/../examples/DropdownButtonSizes.js', 'utf8'),
@@ -65,6 +65,8 @@ export default {
6565
PagerDefault: require('fs').readFileSync(__dirname + '/../examples/PagerDefault.js', 'utf8'),
6666
PagerAligned: require('fs').readFileSync(__dirname + '/../examples/PagerAligned.js', 'utf8'),
6767
PagerDisabled: require('fs').readFileSync(__dirname + '/../examples/PagerDisabled.js', 'utf8'),
68+
PaginationBasic: require('fs').readFileSync(__dirname + '/../examples/PaginationBasic.js', 'utf8'),
69+
PaginationAdvanced: require('fs').readFileSync(__dirname + '/../examples/PaginationAdvanced.js', 'utf8'),
6870
AlertBasic: require('fs').readFileSync(__dirname + '/../examples/AlertBasic.js', 'utf8'),
6971
AlertDismissable: require('fs').readFileSync(__dirname + '/../examples/AlertDismissable.js', 'utf8'),
7072
AlertAutoDismissable: require('fs').readFileSync(__dirname + '/../examples/AlertAutoDismissable.js', 'utf8'),

src/Pagination.js

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import React from 'react';
2+
import classNames from 'classnames';
3+
import BootstrapMixin from './BootstrapMixin';
4+
import PaginationButton from './PaginationButton';
5+
6+
const Pagination = React.createClass({
7+
mixins: [BootstrapMixin],
8+
9+
propTypes: {
10+
activePage: React.PropTypes.number,
11+
items: React.PropTypes.number,
12+
maxButtons: React.PropTypes.number,
13+
ellipsis: React.PropTypes.bool,
14+
first: React.PropTypes.bool,
15+
last: React.PropTypes.bool,
16+
prev: React.PropTypes.bool,
17+
next: React.PropTypes.bool,
18+
onSelect: React.PropTypes.func
19+
},
20+
21+
getDefaultProps() {
22+
return {
23+
activePage: 1,
24+
items: 1,
25+
maxButtons: 0,
26+
first: false,
27+
last: false,
28+
prev: false,
29+
next: false,
30+
ellipsis: true,
31+
bsClass: 'pagination'
32+
};
33+
},
34+
35+
renderPageButtons() {
36+
let pageButtons = [];
37+
let startPage, endPage, hasHiddenPagesBefore, hasHiddenPagesAfter;
38+
let {
39+
maxButtons,
40+
activePage,
41+
items,
42+
onSelect,
43+
ellipsis
44+
} = this.props;
45+
46+
if(maxButtons){
47+
let hiddenPagesBefore = activePage - parseInt(maxButtons / 2);
48+
startPage = hiddenPagesBefore > 1 ? hiddenPagesBefore : 1;
49+
hasHiddenPagesAfter = startPage + maxButtons <= items;
50+
51+
if(!hasHiddenPagesAfter){
52+
endPage = items;
53+
startPage = items - maxButtons + 1;
54+
} else {
55+
endPage = startPage + maxButtons - 1;
56+
}
57+
} else {
58+
startPage = 1;
59+
endPage = items;
60+
}
61+
62+
for(let pagenumber = startPage; pagenumber <= endPage; pagenumber++){
63+
pageButtons.push(
64+
<PaginationButton
65+
key={pagenumber}
66+
eventKey={pagenumber}
67+
active={pagenumber === activePage}
68+
onSelect={onSelect}>
69+
{pagenumber}
70+
</PaginationButton>
71+
);
72+
}
73+
74+
if(maxButtons && hasHiddenPagesAfter && ellipsis){
75+
pageButtons.push(
76+
<PaginationButton
77+
key='ellipsis'
78+
disabled>
79+
<span aria-label='More'>...</span>
80+
</PaginationButton>
81+
);
82+
}
83+
84+
return pageButtons;
85+
},
86+
87+
renderPrev() {
88+
if(!this.props.prev){
89+
return null;
90+
}
91+
92+
return (
93+
<PaginationButton
94+
key='prev'
95+
eventKey={this.props.activePage - 1}
96+
disabled={this.props.activePage === 1}
97+
onSelect={this.props.onSelect}>
98+
<span aria-label='Previous'>&lsaquo;</span>
99+
</PaginationButton>
100+
);
101+
},
102+
103+
renderNext() {
104+
if(!this.props.next){
105+
return null;
106+
}
107+
108+
return (
109+
<PaginationButton
110+
key='next'
111+
eventKey={this.props.activePage + 1}
112+
disabled={this.props.activePage === this.props.items}
113+
onSelect={this.props.onSelect}>
114+
<span aria-label='Next'>&rsaquo;</span>
115+
</PaginationButton>
116+
);
117+
},
118+
119+
renderFirst() {
120+
if(!this.props.first){
121+
return null;
122+
}
123+
124+
return (
125+
<PaginationButton
126+
key='first'
127+
eventKey={1}
128+
disabled={this.props.activePage === 1 }
129+
onSelect={this.props.onSelect}>
130+
<span aria-label='First'>&laquo;</span>
131+
</PaginationButton>
132+
);
133+
},
134+
135+
renderLast() {
136+
if(!this.props.last){
137+
return null;
138+
}
139+
140+
return (
141+
<PaginationButton
142+
key='last'
143+
eventKey={this.props.items}
144+
disabled={this.props.activePage === this.props.items}
145+
onSelect={this.props.onSelect}>
146+
<span aria-label='Last'>&raquo;</span>
147+
</PaginationButton>
148+
);
149+
},
150+
151+
render() {
152+
let classes = this.getBsClassSet();
153+
return (
154+
<ul
155+
{...this.props}
156+
className={classNames(this.props.className, classes)}>
157+
{this.renderFirst()}
158+
{this.renderPrev()}
159+
{this.renderPageButtons()}
160+
{this.renderNext()}
161+
{this.renderLast()}
162+
</ul>
163+
);
164+
}
165+
});
166+
167+
export default Pagination;

src/PaginationButton.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from 'react';
2+
import classNames from 'classnames';
3+
import BootstrapMixin from './BootstrapMixin';
4+
import createSelectedEvent from './utils/createSelectedEvent';
5+
6+
const PaginationButton = React.createClass({
7+
mixins: [BootstrapMixin],
8+
9+
propTypes: {
10+
className: React.PropTypes.string,
11+
eventKey: React.PropTypes.oneOfType([
12+
React.PropTypes.string,
13+
React.PropTypes.number
14+
]),
15+
onSelect: React.PropTypes.func,
16+
disabled: React.PropTypes.bool,
17+
active: React.PropTypes.bool
18+
},
19+
20+
getDefaultProps() {
21+
return {
22+
active: false,
23+
disabled: false
24+
};
25+
},
26+
27+
handleClick(event) {
28+
// This would go away once SafeAnchor is available
29+
event.preventDefault();
30+
31+
if (this.props.onSelect) {
32+
let selectedEvent = createSelectedEvent(this.props.eventKey);
33+
this.props.onSelect(event, selectedEvent);
34+
}
35+
},
36+
37+
render() {
38+
let classes = this.getBsClassSet();
39+
40+
classes.active = this.props.active;
41+
classes.disabled = this.props.disabled;
42+
43+
return (
44+
<li className={classNames(this.props.className, classes)}>
45+
<a href='#' onClick={this.handleClick}>{this.props.children}</a>
46+
</li>
47+
);
48+
}
49+
});
50+
51+
export default PaginationButton;

0 commit comments

Comments
 (0)