UNPKG

4.1 kBJavaScriptView Raw
1import React, {PureComponent} from 'react';
2import PropTypes from 'prop-types';
3import classNames from 'classnames';
4import {Waypoint} from 'react-waypoint';
5
6import Checkbox from '../checkbox/checkbox';
7
8import style from './table.css';
9import HeaderCell from './header-cell';
10
11export 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}