Files
flipper/src/ui/components/VirtualList.js
Daniel Büchele 086ab0188b rename fill attribute
Summary:
We were using `fill={true}` as an attribute to make flexbox containers fill the entire available space.

However, `fill` is an HTML attribute (see: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill) This caused warnings printed to the console.

This diff renames the attribute to `grow` with is also more in line with the Flexbox terminology.

Reviewed By: priteshrnandgaonkar

Differential Revision: D10488389

fbshipit-source-id: ed8553c6203cdf6df94d26c731164ecec4c9fbd2
2018-10-22 09:54:59 -07:00

138 lines
3.0 KiB
JavaScript

/**
* 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';
import styled from '../styled/index.js';
const Inner = styled(FlexColumn)(({height}) => ({
alignItems: 'flex-start',
height,
minHeight: '100%',
minWidth: '100%',
overflow: 'visible',
width: '100%',
}));
const Content = styled(FlexColumn)(({top}) => ({
alignItems: 'flex-start',
height: '100%',
marginTop: top,
minWidth: '100%',
overflow: 'visible',
}));
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
grow={true}
onScroll={this.handleScroll}
innerRef={this.setRef}
scrollable={true}>
{inner}
</View>
);
}
}