Skip to content
This repository was archived by the owner on Feb 22, 2024. It is now read-only.

Commit 0ba32ce

Browse files
authored
Merge pull request #1 from ruskakimov/data_table
update data_table component
2 parents 4d8d411 + d0662ea commit 0ba32ce

File tree

2 files changed

+96
-185
lines changed

2 files changed

+96
-185
lines changed
Lines changed: 46 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -1,156 +1,27 @@
11
import React from 'react';
22

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>&lt;</a>
79-
</li>
80-
{renderItems()}
81-
<li
82-
className={`pagination-next ${page === calcPagesCount() ? 'pagination-disabled' : ''}`}
83-
onClick={handleNext}
84-
>
85-
<a>&gt;</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;
9311
};
9412

95-
9613
/* 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
10016
*/
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);
13317

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`;
15425
}
15526

15627
renderRow(transaction, id) {
@@ -166,40 +37,54 @@ class DataTable extends React.Component {
16637
}
16738

16839
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));
17042
}
17143

17244
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>);
17446
}
17547

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+
}
17868

69+
render() {
17970
return (
180-
<div className='table-container'>
71+
<div className='table-container' ref={this.props.has_fixed_header && this.fixHeaderInPlace}>
18172
<table className='table'>
182-
<thead className='table-thead'>
73+
<thead className='table-head'>
18374
<tr className='table-row'>
18475
{this.renderHeaders()}
18576
</tr>
18677
</thead>
18778

188-
<tbody className='table-tbody'>
79+
<tbody className='table-body'>
18980
{this.renderBodyRows()}
19081
</tbody>
19182
</table>
19283

193-
{pagination && this.renderPagination()}
84+
{this.props.has_fixed_header && this.renderTableClone()}
19485
</div>
19586
);
19687
}
19788
}
19889

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;

src/sass/app_2/_common/components/table.scss

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,52 +3,78 @@
33
color: #333333; /* stylelint-disable-line color-no-hex */
44
font-size: 16px;
55
border-collapse: collapse;
6+
table-layout: fixed;
7+
width: 100vw;
68

79
&-row {
810
background-color: $COLOR_WHITE;
911
border-bottom: 1px solid #d2d3da; /* stylelint-disable-line color-no-hex */
1012
}
11-
&-thead {
13+
&-head {
1214
min-height: 40px;
15+
background-color: #fafafb; /* stylelint-disable-line color-no-hex */
1316

14-
& th {
17+
th {
1518
padding: 12px;
1619
font-weight: 600;
1720
text-align: left;
1821
border: none;
1922
background-color: #f4f4f6; /* stylelint-disable-line color-no-hex */
2023
}
21-
& th:first-child {
22-
padding-left: 16px;
23-
border-top-left-radius: 4px;
24-
}
25-
& th:last-child {
26-
padding-right: 16px;
27-
border-top-right-radius: 4px;
28-
}
2924
}
30-
&-tbody {
31-
& .table-row:hover {
32-
background-color: #fdf4ec; /* stylelint-disable-line color-no-hex */
25+
th:first-child, td:first-child {
26+
padding-left: 32px;
27+
}
28+
th:last-child, td:last-child {
29+
padding-right: 32px;
30+
}
31+
&-body {
32+
.table-row:hover {
33+
background-color: #e4e6f1; /* stylelint-disable-line color-no-hex */
3334
}
34-
& td {
35+
td {
3536
padding: 12px;
3637
border: none;
3738
}
38-
& td:first-child {
39-
padding-left: 16px;
39+
.profit {
40+
color: #4caf50; /* stylelint-disable-line color-no-hex */
4041
}
41-
& td:last-child {
42-
padding-right: 16px;
42+
.loss {
43+
color: #f44336; /* stylelint-disable-line color-no-hex */
4344
}
44-
& .profit {
45-
color: #2e8836; /* stylelint-disable-line color-no-hex */
45+
.amount {
46+
font-weight: bold;
4647
}
47-
& .loss {
48-
color: #c03; /* stylelint-disable-line color-no-hex */
48+
}
49+
// column widths
50+
.date, .ref {
51+
width: 6.5em;
52+
}
53+
.action {
54+
width: 7em;
55+
text-align: right;
56+
}
57+
.payout, .amount, .balance {
58+
width: 6em;
59+
text-align: right;
60+
}
61+
// styles for fixed table header
62+
&.table-clone {
63+
position: fixed;
64+
z-index: 100;
65+
pointer-events: none;
66+
67+
.table-head {
68+
pointer-events: auto;
69+
visibility: visible;
70+
}
71+
.table-body {
72+
visibility: hidden;
4973
}
5074
}
51-
&-pagination {
52-
margin-top: 16px;
75+
@media screen and (max-width: 980px) {
76+
.desc {
77+
display: none;
78+
}
5379
}
5480
}

0 commit comments

Comments
 (0)