/
Salvager.js
99 lines (88 loc) · 2.67 KB
/
Salvager.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import React, { Component } from 'react';
import clamp from 'clamp';
import isFunction from 'lodash.isfunction';
import Row from './Row';
export default class Salvager extends Component {
constructor(props) {
super(props);
this.state = {
bufferStart: 0,
isUpdating: false,
rowHeight: 0,
rowWrapperTransform: '',
visibleAreaOffsetHeight: 0
};
}
render() {
return (
<div
className={this.props.visibleAreaClassName}
onScroll={this._scrollHandler.bind(this)}
ref={(ref) => this.visibleArea = ref}
style={{ overflow: 'auto' }}>
<ol
className={this.props.rowWrapperClassName}
ref={(ref) => this.rowWrapper = ref}
style={{
listStyleType: 'none',
marginBottom: 0,
marginTop: 0,
paddingLeft: 0,
transform: this.state.rowWrapperTransform
}}>
{this._buildRows()}
</ol>
<div
className={this.props.spacerClassName}
style={{ height: this._getSpacerHeight() }}
/>
</div>
);
}
componentDidMount() {
if (!isFunction(this.row.getHeight)) {
throw new Error('Row component must define a getHeight method.');
}
this.setState({
rowHeight: this.row.getHeight(),
visibleAreaOffsetHeight: this.visibleArea.offsetHeight
});
}
shouldComponentUpdate(nextProps, nextState) {
return (
nextState.bufferStart !== this.state.bufferStart ||
nextState.rowHeight !== this.state.rowHeight
);
}
_buildRows() {
let RenderedRow = Row;
if (this.props.getRow) RenderedRow = this.props.getRow();
const rows = [];
for (let i = 0, j = this.props.bufferSize; i < j; i++) {
rows.push(
<RenderedRow
className={this.props.rowClassName}
key={i}
ref={(ref) => { if (!this.row) this.row = ref; }}>
{this.props.data[this.state.bufferStart + i]}
</RenderedRow>
);
}
return rows;
}
_getSpacerHeight() {
return (this.props.data.length - this.props.bufferSize) * this.state.rowHeight;
}
_scrollHandler() {
if (this.state.isUpdating) return;
this.setState({ isUpdating: true });
const midPoint = this.visibleArea.scrollTop + this.state.visibleAreaOffsetHeight / 2;
const bufferMidPoint = Math.floor(midPoint / this.state.rowHeight);
let bufferStart = clamp(Math.floor(bufferMidPoint - this.props.bufferSize / 2), 0, this.props.data.length - this.props.bufferSize);
this.setState({
bufferStart,
isUpdating: false,
rowWrapperTransform: `translateY(${bufferStart * this.state.rowHeight}px)`
});
}
}