1
1
import React from 'react' ;
2
2
3
- const Pagination = ( { page, total, page_size, onChange } ) => {
4
- const handleChange = ( new_page ) => {
5
- if ( new_page === page ) return ;
6
- onChange ( new_page , calcPagesCount ( ) ) ;
7
- } ;
8
-
9
- const calcPagesCount = ( ) => Math . ceil ( total / page_size ) ;
10
-
11
- const handleNext = ( ) => {
12
- if ( page < calcPagesCount ( ) ) {
13
- handleChange ( page + 1 ) ;
14
- }
15
- } ;
16
-
17
- const handlePrev = ( ) => {
18
- if ( page > 1 ) {
19
- handleChange ( page - 1 ) ;
20
- }
21
- } ;
22
-
23
- const renderEllipsis = ( id ) => < li className = 'pagination-item pagination-ellipsis' key = { `ellipsis-${ id } ` } /> ;
24
-
25
- const renderItem = ( page_num ) => (
26
- < li
27
- className = { `pagination-item ${ page_num === page ? 'pagination-item-active' : '' } ` }
28
- key = { page_num }
29
- onClick = { ( ) => { handleChange ( page_num ) ; } }
30
- >
31
- < a > { page_num } </ a >
32
- </ li >
33
- ) ;
34
-
35
- const renderItemRange = ( first , last ) => {
36
- const items = [ ] ;
37
-
38
- for ( let page_num = first ; page_num <= last ; page_num ++ ) {
39
- items . push ( renderItem ( page_num ) ) ;
40
- }
41
- return items ;
42
- } ;
43
-
44
- const renderItems = ( ) => {
45
- const pages_count = calcPagesCount ( ) ;
46
-
47
- if ( pages_count <= 6 ) {
48
- return renderItemRange ( 1 , pages_count ) ;
49
- }
50
- else if ( page <= 4 ) {
51
- return [
52
- ...renderItemRange ( 1 , 5 ) ,
53
- renderEllipsis ( 2 ) ,
54
- ] ;
55
- }
56
- else if ( pages_count - page < 3 ) {
57
- return [
58
- renderItem ( 1 ) ,
59
- renderEllipsis ( 1 ) ,
60
- ...renderItemRange ( pages_count - 3 , pages_count ) ,
61
- ] ;
62
- }
63
- // else
64
- return [
65
- renderItem ( 1 ) ,
66
- renderEllipsis ( 1 ) ,
67
- ...renderItemRange ( page - 1 , page + 1 ) ,
68
- renderEllipsis ( 2 ) ,
69
- ] ;
70
- } ;
71
-
72
- return (
73
- < ul className = 'pagination' >
74
- < li
75
- className = { `pagination-prev ${ page === 1 ? 'pagination-disabled' : '' } ` }
76
- onClick = { handlePrev }
77
- >
78
- < a > <</ a >
79
- </ li >
80
- { renderItems ( ) }
81
- < li
82
- className = { `pagination-next ${ page === calcPagesCount ( ) ? 'pagination-disabled' : '' } ` }
83
- onClick = { handleNext }
84
- >
85
- < a > ></ a >
86
- </ li >
87
- </ ul >
88
- ) ;
89
- } ;
90
-
91
- Pagination . defaultProps = {
92
- page : 1 ,
3
+ const offsetPageTop = ( element ) => {
4
+ let el = element ;
5
+ let offset = - el . clientTop ;
6
+ while ( el ) {
7
+ offset += el . offsetTop + el . clientTop ;
8
+ el = el . offsetParent ;
9
+ }
10
+ return offset ;
93
11
} ;
94
12
95
-
96
13
/* TODO:
97
- 1. to implement sorting by column (ASC/DESC)
98
- 2. to implement filtering per column
99
- 3. to make pagination more customisable
14
+ 1. implement sorting by column (ASC/DESC)
15
+ 2. implement filtering per column
100
16
*/
101
- class DataTable extends React . Component {
102
- constructor ( props ) {
103
- super ( props ) ;
104
- const { data_source, pagination, page_size } = props ;
105
-
106
- this . handlePageChange = this . handlePageChange . bind ( this ) ;
107
- this . renderPagination = this . renderPagination . bind ( this ) ;
108
- this . handlePageChange = this . handlePageChange . bind ( this ) ;
109
- this . updateDisplayData = this . updateDisplayData . bind ( this ) ;
110
-
111
- this . state = {
112
- display_data : pagination ? data_source . slice ( 0 , page_size ) : data_source ,
113
- page : 1 ,
114
- } ;
115
- }
116
-
117
- componentWillReceiveProps ( nextProps ) {
118
- this . updateDisplayData ( nextProps . data_source , this . state . page , nextProps . page_size ) ;
119
- }
120
-
121
- updateDisplayData ( data_source , page , page_size ) {
122
- const start_id = ( page - 1 ) * page_size ;
123
- const end_id = start_id + page_size ;
124
-
125
- this . setState ( {
126
- page,
127
- display_data : data_source . slice ( start_id , end_id ) ,
128
- } ) ;
129
- }
130
-
131
- handlePageChange ( page , pages_count ) {
132
- this . updateDisplayData ( this . props . data_source , page , this . props . page_size ) ;
133
17
134
- if ( ! pages_count ) return ;
135
-
136
- const { pages_close_to_end, onCloseToEnd } = this . props ;
137
- const pagesLeft = pages_count - page ;
138
- if ( pagesLeft <= pages_close_to_end ) {
139
- onCloseToEnd ( ) ;
140
- }
141
- }
142
-
143
- renderPagination ( ) {
144
- return (
145
- < div className = 'table-pagination' >
146
- < Pagination
147
- page = { this . state . page }
148
- total = { this . props . data_source . length }
149
- page_size = { this . props . page_size }
150
- onChange = { this . handlePageChange }
151
- />
152
- </ div >
153
- ) ;
18
+ class DataTable extends React . Component {
19
+ fixHeaderInPlace = ( el_table_container ) => {
20
+ if ( ! el_table_container ) return ;
21
+ const el_table = el_table_container . querySelector ( '.table' ) ;
22
+ el_table . querySelector ( '.table-head' ) . style . visibility = 'hidden' ;
23
+ const el_table_clone = el_table_container . querySelector ( '.table-clone' ) ;
24
+ el_table_clone . style . top = `${ offsetPageTop ( el_table ) } px` ;
154
25
}
155
26
156
27
renderRow ( transaction , id ) {
@@ -166,40 +37,54 @@ class DataTable extends React.Component {
166
37
}
167
38
168
39
renderBodyRows ( ) {
169
- return this . state . display_data . map ( ( transaction , id ) => this . renderRow ( transaction , id ) ) ;
40
+ return this . props . data_source
41
+ . map ( ( transaction , id ) => this . renderRow ( transaction , id ) ) ;
170
42
}
171
43
172
44
renderHeaders ( ) {
173
- return this . props . columns . map ( col => < th key = { col . data_index } > { col . title } </ th > ) ;
45
+ return this . props . columns . map ( col => < th className = { col . data_index } key = { col . data_index } > { col . title } </ th > ) ;
174
46
}
175
47
176
- render ( ) {
177
- const { pagination } = this . props ;
48
+ renderTableClone ( ) {
49
+ /*
50
+ cloned table with one row for fixed header
51
+ inspired by
52
+ https://stackoverflow.com/questions/4709390
53
+ */
54
+ return (
55
+ < table className = 'table table-clone' >
56
+ < thead className = 'table-head' >
57
+ < tr className = 'table-row' >
58
+ { this . renderHeaders ( ) }
59
+ </ tr >
60
+ </ thead >
61
+
62
+ < tbody className = 'table-body' >
63
+ { this . renderRow ( this . props . data_source [ 0 ] , 0 ) }
64
+ </ tbody >
65
+ </ table >
66
+ ) ;
67
+ }
178
68
69
+ render ( ) {
179
70
return (
180
- < div className = 'table-container' >
71
+ < div className = 'table-container' ref = { this . props . has_fixed_header && this . fixHeaderInPlace } >
181
72
< table className = 'table' >
182
- < thead className = 'table-thead ' >
73
+ < thead className = 'table-head ' >
183
74
< tr className = 'table-row' >
184
75
{ this . renderHeaders ( ) }
185
76
</ tr >
186
77
</ thead >
187
78
188
- < tbody className = 'table-tbody ' >
79
+ < tbody className = 'table-body ' >
189
80
{ this . renderBodyRows ( ) }
190
81
</ tbody >
191
82
</ table >
192
83
193
- { pagination && this . renderPagination ( ) }
84
+ { this . props . has_fixed_header && this . renderTableClone ( ) }
194
85
</ div >
195
86
) ;
196
87
}
197
88
}
198
89
199
- DataTable . defaultProps = {
200
- pagination : true ,
201
- page_size : 10 ,
202
- pages_close_to_end : 5 ,
203
- } ;
204
-
205
- export default DataTable ;
90
+ export default DataTable ;
0 commit comments