Completed
Push — master ( c7affe...8bcad0 )
by Anton
02:39
created

public/js/test/pager.js   A

Complexity

Total Complexity 31
Complexity/F 1.63

Size

Lines of Code 230
Function Count 19

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 31
dl 0
loc 230
rs 9.8
c 0
b 0
f 0
cc 1
nc 2
mnd 1
bc 21
fnc 19
bpm 1.1052
cpm 1.6315
noi 7

17 Functions

Rating   Name   Duplication   Size   Complexity  
A React.createClass.isPrevDisabled 0 3 1
A React.createClass.handleMoreNextPages 0 4 1
A React.createClass.renderPages 0 13 1
A React.createClass.calcBlocks 0 15 1
A pager.js ➔ range 0 8 2
A React.createClass.getTitles 0 4 1
A React.createClass.handlePreviousPage 0 4 2
B React.createClass.render 0 41 1
A React.createClass.isNextDisabled 0 3 1
A React.createClass.handleMorePrevPages 0 4 1
A React.createClass.handlePageChanged 0 4 2
A React.createClass.isNextMoreHidden 0 5 1
A React.createClass.handleLastPage 0 4 2
A React.createClass.visibleRange 0 7 2
A React.createClass.handleNextPage 0 4 2
A React.createClass.isPrevMoreHidden 0 5 1
A React.createClass.handleFirstPage 0 4 2
1
/**
2
 * # Stateless Pager component
3
 *
4
 * ## Usage
5
 * ```
6
 * <Pager current={3}
7
 *        total={20}
8
 *        visiblePages={5}
9
 *        onPageChanged={this.handlePageChanged}
10
 *        titles={{
11
 *            first:   "First",
12
 *            prev:    "Prev",
13
 *            prevSet: "<<<",
14
 *            nextSet: ">>>",
15
 *            next:    "Next",
16
 *            last:    "Last"
17
 *        }} />
18
 * ```
19
 *
20
 * ## How it looks like
21
 * ```
22
 * First | Prev | ... | 6 | 7 | 8 | 9 | ... | Next | Last
23
 * ```
24
 *
25
 */
26
27
var React = require( 'react' );
28
29
30
/**
31
 * ## Constants
32
 */
33
var BASE_SHIFT  = 0
34
  , TITLE_SHIFT = 1
35
  , TITLES = {
36
        first:   'First',
37
        prev:    '\u00AB',
38
        prevSet: '...',
39
        nextSet: '...',
40
        next:    '\u00BB',
41
        last:    'Last'
42
    };
43
44
/**
45
 * ## Constructor
46
 */
47
var Pager = React.createClass({displayName: "Pager",
48
    propTypes: {
49
        current:               React.PropTypes.number.isRequired,
50
        total:                 React.PropTypes.number.isRequired,
51
        visiblePages:          React.PropTypes.number.isRequired,
52
        titles:                React.PropTypes.object,
53
54
        onPageChanged:         React.PropTypes.func,
55
        onPageSizeChanged:     React.PropTypes.func
56
    },
57
    
58
59
    /* ========================= HANDLERS =============================*/
60
    handleFirstPage: function () {
61
        if ( this.isPrevDisabled() ) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
62
        this.handlePageChanged( BASE_SHIFT );
63
    },
64
    
65
    handlePreviousPage: function () {
66
        if ( this.isPrevDisabled() ) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
67
        this.handlePageChanged( this.props.current - TITLE_SHIFT );
68
    },
69
70
    handleNextPage: function () {
71
        if ( this.isNextDisabled() ) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
72
        this.handlePageChanged( this.props.current + TITLE_SHIFT );
73
    },
74
75
    handleLastPage: function () {
76
        if ( this.isNextDisabled() ) return;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
77
        this.handlePageChanged( this.props.total - TITLE_SHIFT );
78
    },
79
    
80
    /**
81
     * Chooses page, that is one before min of currently visible
82
     * pages.
83
     */
84
    handleMorePrevPages: function () {
85
        var blocks = this.calcBlocks();
86
        this.handlePageChanged( blocks.current * blocks.size - TITLE_SHIFT );
87
    },
88
89
    /**
90
     * Chooses page, that is one after max of currently visible
91
     * pages.
92
     */
93
    handleMoreNextPages: function () {
94
        var blocks = this.calcBlocks();
95
        this.handlePageChanged( (blocks.current + TITLE_SHIFT) * blocks.size );
96
    },
97
98
    handlePageChanged: function ( el ) {
99
        var handler = this.props.onPageChanged;
100
        if ( handler ) handler( el );
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
101
    },
102
    
103
104
    /* ========================= HELPERS ==============================*/
105
    /**
106
     * Calculates "blocks" of buttons with page numbers.
107
     */
108
    calcBlocks: function () {
109
        var props      = this.props
110
          , total      = props.total
111
          , blockSize  = props.visiblePages
112
          , current    = props.current + TITLE_SHIFT
113
114
          , blocks     = Math.ceil( total / blockSize ) 
115
          , currBlock  = Math.ceil( current / blockSize ) - TITLE_SHIFT;
116
        
117
        return {
118
            total:    blocks,
119
            current:  currBlock,
120
            size:     blockSize
121
        };
122
    },
123
124
    isPrevDisabled: function () {
125
        return this.props.current <= BASE_SHIFT;
126
    },
127
128
    isNextDisabled: function () {
129
        return this.props.current >= ( this.props.total - TITLE_SHIFT );
130
    },
131
132
    isPrevMoreHidden: function () {
133
        var blocks = this.calcBlocks();
134
        return ( blocks.total === TITLE_SHIFT ) 
135
               || ( blocks.current === BASE_SHIFT );
136
    },
137
138
    isNextMoreHidden: function () {
139
        var blocks = this.calcBlocks();
140
        return ( blocks.total === TITLE_SHIFT ) 
141
               || ( blocks.current === (blocks.total - TITLE_SHIFT) );
142
    },
143
144
    visibleRange: function () {
145
        var blocks  = this.calcBlocks()
146
          , start   = blocks.current * blocks.size
147
          , delta   = this.props.total - start
148
          , end     = start + ( (delta > blocks.size) ? blocks.size : delta );
149
        return [ start + TITLE_SHIFT, end + TITLE_SHIFT ];
150
    },
151
152
    
153
    getTitles: function ( key ) {
154
        var pTitles = this.props.titles || {};
155
        return pTitles[ key ] || TITLES[ key ];
156
    },
157
    
158
    /* ========================= RENDERS ==============================*/
159
    render: function () {
160
        var titles = this.getTitles;
161
162
        return (
163
            React.createElement("nav", null, 
164
                React.createElement("ul", {className: "pagination"}, 
165
                    React.createElement(Page, {className: "btn-first-page", 
166
                          key: "btn-first-page", 
167
                          isDisabled: this.isPrevDisabled(), 
168
                          onClick: this.handleFirstPage}, titles('first')), 
169
170
                    React.createElement(Page, {className: "btn-prev-page", 
171
                          key: "btn-prev-page", 
172
                          isDisabled: this.isPrevDisabled(), 
173
                          onClick: this.handlePreviousPage}, titles('prev')), 
174
175
                    React.createElement(Page, {className: "btn-prev-more", 
176
                          key: "btn-prev-more", 
177
                          isHidden: this.isPrevMoreHidden(), 
178
                          onClick: this.handleMorePrevPages}, titles('prevSet')), 
179
180
                    this.renderPages( this.visibleRange()), 
181
182
                    React.createElement(Page, {className: "btn-next-more", 
183
                          key: "btn-next-more", 
184
                          isHidden: this.isNextMoreHidden(), 
185
                          onClick: this.handleMoreNextPages}, titles('nextSet')), 
186
187
                    React.createElement(Page, {className: "btn-next-page", 
188
                          key: "btn-next-page", 
189
                          isDisabled: this.isNextDisabled(), 
190
                          onClick: this.handleNextPage}, titles('next')), 
191
192
                    React.createElement(Page, {className: "btn-last-page", 
193
                          key: "btn-last-page", 
194
                          isDisabled: this.isNextDisabled(), 
195
                          onClick: this.handleLastPage}, titles('last'))
196
                )
197
            )
198
        );
199
    },
200
201
202
    /**
203
     * ### renderPages()
204
     * Renders block of pages' buttons with numbers.
205
     * @param {Number[]} range - pair of [start, from], `from` - not inclusive.
0 ignored issues
show
Documentation introduced by
The parameter range does not exist. Did you maybe forget to remove this comment?
Loading history...
206
     * @return {React.Element[]} - array of React nodes.
207
     */
208
    renderPages: function ( pair ) {
209
        var self = this;
210
        
211
        return range( pair[0], pair[1] ).map(function ( el, idx ) {
212
            var current = el - TITLE_SHIFT
213
              , onClick = self.handlePageChanged.bind(null, current)
214
              , isActive = (self.props.current === current);
215
216
            return (React.createElement(Page, {key: idx, isActive: isActive, 
217
                          className: "btn-numbered-page", 
218
                          onClick: onClick}, el));
219
        });
220
    }
221
});
222
223
224
225
var Page = React.createClass({displayName: "Page",
226
    render: function () {
227
        var props = this.props;
228
        if ( props.isHidden ) return null;
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
229
230
        var baseCss = props.className ? props.className + ' ' : ''
231
          , css     = baseCss
232
                      + (props.isActive ? 'active' : '')
233
                      + (props.isDisabled ? ' disabled' : '');
234
235
        return (
236
            React.createElement("li", {key: this.props.key, className: css}, 
237
                React.createElement("a", {onClick: this.props.onClick}, this.props.children)
238
            )
239
        );
240
    }
241
});    
242
243
244
245
function range ( start, end ) {
246
    var res = [];
247
    for ( var i = start; i < end; i++ ) {
248
        res.push( i );
249
    }
250
251
    return res; 
252
}
253
254
if ('undefined' !== typeof module) {
255
    module.exports = Pager;
256
}
257