-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathVirtualList.tsx
121 lines (96 loc) · 3.07 KB
/
VirtualList.tsx
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import React, { Component, CSSProperties, createRef } from 'react';
import VirtualListRow from './VirtualListRow';
import { ColumnProps } from './Column';
import TreeState from '../model/tree-state';
import { RowModel } from '../model/row';
export type VirtualListProps<TData> = {
data: Readonly<TreeState<TData>>;
columns: Array<ColumnProps<TData>>;
height: number;
onChange: (value: Readonly<TreeState<TData>>) => void;
onScroll: (scrollTop: number) => void;
}
export type VirtualListState = {
topOffset: number,
overscanHeight: number,
}
export default class VirtualList<TData> extends Component<VirtualListProps<TData>, VirtualListState> {
state = {
topOffset: 0,
overscanHeight: 100,
};
private containerRef = createRef<HTMLDivElement>();
render() {
const { data, columns, height, onChange } = this.props;
const { topOffset, overscanHeight } = this.state;
const startYMax = Math.max(0, data.height - height - (overscanHeight * 2));
const startY = Math.min(startYMax, Math.max(0, topOffset - overscanHeight));
let startIndex = data.indexAtYPos(startY);
const endY = Math.min(data.height, topOffset + height + overscanHeight);
let endIndex = data.indexAtYPos(endY);
const contentTopOffset = data.yPosAtIndex(startIndex);
let visibleRowsData: Array<RowModel<TData>> = [];
TreeState.sliceRows(data, startIndex, endIndex).forEach((rowModel: RowModel<TData>) => {
if (rowModel.$state.isVisible) {
visibleRowsData.push(rowModel);
}
});
const visibleVLRows = visibleRowsData.map((rowModel: RowModel<TData>, relIndex: number) => {
return (
<VirtualListRow key={relIndex}
data={data}
model={rowModel}
columns={columns}
onChange={onChange}
index={rowModel.metadata.index}
relIndex={relIndex} />
);
});
return (
<div className="cp_tree-table_viewport"
style={{ ...STYLE_LIST, height: `${height}px` }}
ref={this.containerRef}
onScroll={this.handleScroll}>
<div style={{ ...STYLE_WRAPPER, height: `${data.height}px` }}>
<div style={{ ...STYLE_CONTENT, top: `${contentTopOffset}px` }}
className="cp_tree-table_mover">
{visibleVLRows}
</div>
</div>
</div>
);
}
private handleScroll = () => {
if (this.containerRef.current != null) {
const { scrollTop } = this.containerRef.current;
const { onScroll } = this.props;
onScroll(scrollTop);
this.setState({
topOffset: scrollTop,
});
}
}
scrollTo = (posY: number) => {
if (this.containerRef.current != null) {
this.containerRef.current.scrollTop = posY;
}
}
}
const STYLE_LIST: CSSProperties = {
overflow: 'auto',
WebkitOverflowScrolling: 'touch',
};
const STYLE_WRAPPER: CSSProperties = {
position: 'relative',
overflow: 'hidden',
width: '100%',
minHeight: '100%',
};
const STYLE_CONTENT: CSSProperties = {
position: 'absolute',
overflow: 'visible',
height: '100%',
width: '100%',
top: '0px',
left: '0px',
};