1 | import React, {PureComponent} from 'react';
|
2 | import PropTypes from 'prop-types';
|
3 | import classNames from 'classnames';
|
4 | import {Waypoint} from 'react-waypoint';
|
5 |
|
6 | import Checkbox from '../checkbox/checkbox';
|
7 |
|
8 | import style from './table.css';
|
9 | import HeaderCell from './header-cell';
|
10 |
|
11 | export default class Header extends PureComponent {
|
12 | static propTypes = {
|
13 | caption: PropTypes.string,
|
14 | selectable: PropTypes.bool,
|
15 | draggable: PropTypes.bool,
|
16 | checked: PropTypes.bool,
|
17 | checkboxDisabled: PropTypes.bool,
|
18 | sticky: PropTypes.bool,
|
19 | topStickOffset: PropTypes.string,
|
20 | onCheckboxChange: PropTypes.func,
|
21 | columns: PropTypes.array.isRequired,
|
22 | onSort: PropTypes.func,
|
23 | sortKey: PropTypes.string,
|
24 | sortOrder: PropTypes.bool
|
25 | };
|
26 |
|
27 | static defaultProps = {
|
28 | selectable: true,
|
29 | draggable: false,
|
30 | checked: true,
|
31 | sticky: true,
|
32 | topStickOffset: '0px',
|
33 | onSort: () => {},
|
34 | onCheckboxChange: () => {},
|
35 | sortKey: 'id',
|
36 | sortOrder: true
|
37 | };
|
38 |
|
39 | state = {
|
40 | fixed: false,
|
41 | headerWidth: null,
|
42 | widths: []
|
43 | };
|
44 |
|
45 | onCheckboxFocus = event => {
|
46 | event.target.blur();
|
47 | };
|
48 |
|
49 | storeColumnsRowNode = node => {
|
50 | if (node) {
|
51 | this._columnsRowNode = node;
|
52 | this.calculateColumnsWidths(node);
|
53 | }
|
54 | };
|
55 |
|
56 | onScrollIn = () => {
|
57 | this.calculateColumnsWidths(this._columnsRowNode);
|
58 | this.setState({fixed: false});
|
59 | };
|
60 |
|
61 | onScrollOut = ({currentPosition}) => {
|
62 | if (currentPosition !== 'above') {
|
63 | return;
|
64 | }
|
65 | this.calculateColumnsWidths(this._columnsRowNode);
|
66 | this.setState({fixed: true});
|
67 | };
|
68 |
|
69 | calculateColumnsWidths(columnsRowNode) {
|
70 | this.setState({
|
71 | headerWidth: columnsRowNode.clientWidth,
|
72 | widths: [...columnsRowNode.childNodes].map(column => column.clientWidth)
|
73 | });
|
74 | }
|
75 |
|
76 | createCells(widths = []) {
|
77 | const {
|
78 | selectable, draggable, columns, checked, checkboxDisabled,
|
79 | onCheckboxChange, onSort, sortKey, sortOrder
|
80 | } = this.props;
|
81 |
|
82 | const metaColumnClasses = classNames(style.metaColumn, style.headerMetaColumn);
|
83 |
|
84 | const metaColumn = (
|
85 | <div className={metaColumnClasses}>
|
86 | {selectable &&
|
87 | (
|
88 | <Checkbox
|
89 | disabled={checkboxDisabled}
|
90 | checked={checked}
|
91 | onChange={onCheckboxChange}
|
92 | onFocus={this.onCheckboxFocus}
|
93 | />
|
94 | )}
|
95 | </div>
|
96 | );
|
97 |
|
98 | return columns.map((column, index) => {
|
99 | const columnStyle = widths[index] ? {width: widths[index]} : null;
|
100 | const props = {column, onSort, sortKey, sortOrder, style: columnStyle};
|
101 | return (
|
102 | <HeaderCell
|
103 | key={column.id}
|
104 | {...props}
|
105 | >
|
106 | {index === 0 && (draggable || selectable) && metaColumn}
|
107 | </HeaderCell>
|
108 | );
|
109 | });
|
110 | }
|
111 |
|
112 | render() {
|
113 | const {caption, sticky, topStickOffset} = this.props;
|
114 | const {fixed, widths, headerWidth} = this.state;
|
115 |
|
116 | const regularCells = this.createCells();
|
117 |
|
118 | return (
|
119 | <thead data-test="ring-table-header" className={style.tableHead}>
|
120 | {caption && (
|
121 | <tr data-test="ring-table-header-row">
|
122 | <th
|
123 | className={classNames(style.headerCell, style.caption)}
|
124 | colSpan={regularCells.length + 1}
|
125 | data-test="ring-table-header-cell"
|
126 | >{caption}</th>
|
127 | </tr>
|
128 | )}
|
129 |
|
130 | {sticky &&
|
131 | (
|
132 | <Waypoint
|
133 | topOffset={topStickOffset}
|
134 | onEnter={this.onScrollIn}
|
135 | onLeave={this.onScrollOut}
|
136 | >
|
137 | <tr data-test="ring-table-header-row"/>
|
138 | </Waypoint>
|
139 | )
|
140 | }
|
141 |
|
142 | <tr
|
143 | className={style.subHeader}
|
144 | ref={this.storeColumnsRowNode}
|
145 | data-test="ring-table-header-row"
|
146 | >{regularCells}</tr>
|
147 |
|
148 | {fixed && sticky &&
|
149 | (
|
150 | <tr
|
151 | className={style.subHeaderFixed}
|
152 | style={{width: headerWidth, top: topStickOffset}}
|
153 | data-test="ring-table-header-row"
|
154 | >
|
155 | {this.createCells(widths)}
|
156 | </tr>
|
157 | )
|
158 | }
|
159 | </thead>
|
160 | );
|
161 | }
|
162 | }
|