Initial commit 🎉

fbshipit-source-id: b6fc29740c6875d2e78953b8a7123890a67930f2
Co-authored-by: Sebastian McKenzie <sebmck@fb.com>
Co-authored-by: John Knox <jknox@fb.com>
Co-authored-by: Emil Sjölander <emilsj@fb.com>
Co-authored-by: Pritesh Nandgaonkar <prit91@fb.com>
This commit is contained in:
Daniel Büchele
2018-04-13 08:38:06 -07:00
committed by Daniel Buchele
commit fbbf8cf16b
659 changed files with 87130 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import FlexColumn from './FlexColumn.js';
import {Component} from 'react';
import View from './View.js';
const Inner = FlexColumn.extends(
{
alignItems: 'flex-start',
height: props => props.height,
minHeight: '100%',
minWidth: '100%',
overflow: 'visible',
width: '100%',
},
{
ignoreAttributes: ['height'],
},
);
const Content = FlexColumn.extends(
{
alignItems: 'flex-start',
height: '100%',
marginTop: props => props.top,
minWidth: '100%',
overflow: 'visible',
},
{
ignoreAttributes: ['top'],
},
);
type VirtualListProps = {|
data: Array<any>,
renderRow: (data: any, i: number) => any,
rowHeight: number,
overscanCount: number,
sync?: number,
wrapInner?: (data: any) => any,
|};
type VirtualListState = {|
offset: number,
height: number,
|};
export default class VirtualList extends Component<
VirtualListProps,
VirtualListState,
> {
constructor(props: VirtualListProps, context: Object) {
super(props, context);
this.state = {
height: 0,
offset: 0,
};
}
static defaultProps = {
overscanCount: 10,
};
ref: HTMLElement;
setRef = (ref: HTMLElement) => {
this.ref = ref;
};
resize = () => {
if (this.ref && this.state.height !== this.ref.offsetHeight) {
this.setState({height: this.ref.offsetHeight});
}
};
handleScroll = () => {
this.setState({offset: this.ref.scrollTop});
if (this.props.sync === true) {
this.forceUpdate();
}
};
componentDidUpdate() {
this.resize();
}
componentDidMount() {
this.resize();
window.addEventListener('resize', this.resize);
}
componentWillUnmount() {
window.removeEventListener('resize', this.resize);
}
render() {
const {data, overscanCount, renderRow, rowHeight, wrapInner} = this.props;
const {height, offset} = this.state;
// first visible row index
// eslint-disable-next-line no-bitwise
let start = (offset / rowHeight) | 0;
// actual number of visible rows (without overscan)
// eslint-disable-next-line no-bitwise
let visibleRowCount = (height / rowHeight) | 0;
// Overscan: render blocks of rows modulo an overscan row count
// This dramatically reduces DOM writes during scrolling
if (overscanCount) {
start = Math.max(0, start - start % overscanCount);
visibleRowCount += overscanCount;
}
// last visible + overscan row index
const end = start + 1 + visibleRowCount;
// data slice currently in viewport plus overscan items
const selection = data.slice(start, end);
let inner = (
<Inner height={data.length * rowHeight}>
<Content top={start * rowHeight}>{selection.map(renderRow)}</Content>
</Inner>
);
if (wrapInner) {
inner = wrapInner(inner);
}
return (
<View
fill={true}
onScroll={this.handleScroll}
innerRef={this.setRef}
scrollable={true}>
{inner}
</View>
);
}
}