remove old virtualized
Summary: Switches elements from custom virtualization to react-window, so the custom implementation is not needed anymore and therefore can be removed. Reviewed By: jknoxville Differential Revision: D9447723 fbshipit-source-id: 7abcc077f87fe634a0e9517908db03398848bce7
This commit is contained in:
committed by
Facebook Github Bot
parent
7bdb21e055
commit
624d06f2c2
@@ -19,8 +19,9 @@ import Glyph from '../Glyph.js';
|
||||
import {colors} from '../colors.js';
|
||||
import Text from '../Text.js';
|
||||
import styled from '../../styled/index.js';
|
||||
import {FixedList} from '../../virtualized/index.js';
|
||||
import {clipboard} from 'electron';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import {FixedSizeList as List} from 'react-window';
|
||||
|
||||
const ROW_HEIGHT = 23;
|
||||
|
||||
@@ -617,24 +618,23 @@ export class Elements extends PureComponent<ElementsProps, ElementsState> {
|
||||
);
|
||||
};
|
||||
|
||||
keyMapper = (index: number): string => {
|
||||
return this.state.flatElements[index].key;
|
||||
};
|
||||
|
||||
render() {
|
||||
const items = this.state.flatElements;
|
||||
|
||||
return (
|
||||
<ElementsBox>
|
||||
<ElementsContainer tabIndex="0" onKeyDown={this.onKeyDown}>
|
||||
<FixedList
|
||||
pureData={items}
|
||||
keyMapper={this.keyMapper}
|
||||
rowCount={items.length}
|
||||
rowHeight={ROW_HEIGHT}
|
||||
rowRenderer={this.buildRow}
|
||||
sideScrollable={true}
|
||||
/>
|
||||
<AutoSizer>
|
||||
{({width, height}) => (
|
||||
<List
|
||||
itemCount={items.length}
|
||||
itemSize={ROW_HEIGHT}
|
||||
width={width}
|
||||
height={height}>
|
||||
{e => this.buildRow(e)}
|
||||
</List>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</ElementsContainer>
|
||||
</ElementsBox>
|
||||
);
|
||||
|
||||
@@ -1,392 +0,0 @@
|
||||
/**
|
||||
* 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 type {RowRenderer, OnScroll, KeyMapper} from './types.js';
|
||||
import {PureComponent, Component} from 'react';
|
||||
import {ResizeSensor} from '../index.js';
|
||||
import {findDOMNode} from 'react-dom';
|
||||
|
||||
type RowMeasureProps = {
|
||||
id: string,
|
||||
onMount: (key: string, ref: ?Text | Element) => void,
|
||||
children: React$Node,
|
||||
};
|
||||
|
||||
class RowMeasure extends PureComponent<RowMeasureProps> {
|
||||
componentDidMount() {
|
||||
this.props.onMount(this.props.id, findDOMNode(this));
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
const CONTAINER_STYLE = {
|
||||
position: 'relative',
|
||||
overflow: 'auto',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
};
|
||||
|
||||
type DynamicListProps = {
|
||||
pureData: any,
|
||||
onMount?: () => void,
|
||||
getPrecalculatedDimensions: (
|
||||
index: number,
|
||||
) => ?{
|
||||
width: number | string,
|
||||
height: number,
|
||||
},
|
||||
rowCount: number,
|
||||
rowRenderer: RowRenderer,
|
||||
keyMapper: KeyMapper,
|
||||
onScroll?: OnScroll,
|
||||
sideScrollable?: boolean,
|
||||
};
|
||||
|
||||
type DynamicListState = {
|
||||
mounted: boolean,
|
||||
startIndex: number,
|
||||
endIndex: number,
|
||||
containerStyle: Object,
|
||||
innerStyle: Object,
|
||||
scrollHeight: number,
|
||||
scrollTop: number,
|
||||
height: number,
|
||||
width: number,
|
||||
};
|
||||
|
||||
export default class DynamicList extends Component<
|
||||
DynamicListProps,
|
||||
DynamicListState,
|
||||
> {
|
||||
constructor(props: DynamicListProps, context: Object) {
|
||||
super(props, context);
|
||||
|
||||
this.topPositionToIndex = new Map();
|
||||
this.positions = new Map();
|
||||
this.dimensions = new Map();
|
||||
this.measureQueue = new Map();
|
||||
|
||||
this.state = {
|
||||
mounted: false,
|
||||
startIndex: -1,
|
||||
endIndex: -1,
|
||||
containerStyle: {},
|
||||
innerStyle: {},
|
||||
scrollHeight: 0,
|
||||
scrollTop: 0,
|
||||
height: 0,
|
||||
width: 0,
|
||||
};
|
||||
}
|
||||
|
||||
containerRef: ?HTMLDivElement;
|
||||
|
||||
measureQueue: Map<string, React$Node>;
|
||||
|
||||
topPositionToIndex: Map<number, number>;
|
||||
positions: Map<
|
||||
number,
|
||||
{
|
||||
top: number,
|
||||
style: Object,
|
||||
},
|
||||
>;
|
||||
|
||||
dimensions: Map<
|
||||
string,
|
||||
{
|
||||
width: number | string,
|
||||
height: number,
|
||||
},
|
||||
>;
|
||||
|
||||
scrollToIndex = (index: number, additionalOffset: number = 0) => {
|
||||
const pos = this.positions.get(index);
|
||||
const ref = this.getContainerRef();
|
||||
if (pos != null && ref != null) {
|
||||
ref.scrollTop = pos.top - additionalOffset;
|
||||
}
|
||||
};
|
||||
|
||||
setContainerRef = (ref: ?HTMLDivElement) => {
|
||||
this.containerRef = ref;
|
||||
};
|
||||
|
||||
getContainerRef(): ?HTMLDivElement {
|
||||
return this.containerRef;
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: DynamicListProps) {
|
||||
if (
|
||||
nextProps.rowCount !== this.props.rowCount ||
|
||||
nextProps.pureData !== this.props.pureData
|
||||
) {
|
||||
this.queueMeasurements(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// perform initial measurements and container dimension calculation
|
||||
this.recalculateContainerDimensions();
|
||||
this.queueMeasurements(this.props);
|
||||
|
||||
// if onMount we didn't add any measurements then we've successfully calculated all row sizes
|
||||
if (this.measureQueue.size === 0) {
|
||||
this.onMount();
|
||||
}
|
||||
}
|
||||
|
||||
onMount() {
|
||||
this.setState(state => {
|
||||
if (state.mounted === false && this.props.onMount != null) {
|
||||
this.props.onMount();
|
||||
}
|
||||
return {mounted: true};
|
||||
});
|
||||
}
|
||||
|
||||
// called when the window is resized, we recalculate the positions and visibility of rows
|
||||
onResize = (e: UIEvent) => {
|
||||
this.dimensions.clear();
|
||||
this.queueMeasurements(this.props);
|
||||
this.recalculateContainerDimensions();
|
||||
this.recalculateVisibleRows(this.props);
|
||||
};
|
||||
|
||||
queueMeasurements(props: DynamicListProps) {
|
||||
// create measurements for new rows
|
||||
for (let i = 0; i < props.rowCount; i++) {
|
||||
const key = props.keyMapper(i);
|
||||
if (this.dimensions.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const precalculated = props.getPrecalculatedDimensions(i);
|
||||
if (precalculated) {
|
||||
this.dimensions.set(key, precalculated);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.measureQueue.set(
|
||||
key,
|
||||
props.rowRenderer({
|
||||
index: i,
|
||||
style: {
|
||||
visibility: 'hidden',
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// recalculate the visibility and positions of all rows
|
||||
this.recalculatePositions(props);
|
||||
this.recalculateVisibleRows(props);
|
||||
}
|
||||
|
||||
recalculateContainerDimensions = () => {
|
||||
const container = this.getContainerRef();
|
||||
if (container != null) {
|
||||
this.setState({
|
||||
scrollTop: container.scrollTop,
|
||||
height: container.clientHeight,
|
||||
width: container.clientWidth,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
recalculateVisibleRows = (props: DynamicListProps) => {
|
||||
this.setState(state => {
|
||||
let startTop = 0;
|
||||
|
||||
// find the start index
|
||||
let startIndex = 0;
|
||||
let scrollTop = state.scrollTop;
|
||||
do {
|
||||
const index = this.topPositionToIndex.get(scrollTop);
|
||||
if (index != null) {
|
||||
const startPos = this.positions.get(index);
|
||||
if (startPos != null) {
|
||||
startTop = startPos.top;
|
||||
startIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scrollTop--;
|
||||
} while (scrollTop > 0);
|
||||
|
||||
// find the end index
|
||||
let endIndex = startIndex;
|
||||
let scrollBottom = state.scrollTop + state.height;
|
||||
while (true) {
|
||||
// if the scrollBottom is equal to the height of the scrollable area then
|
||||
// we were unable to find the end index because we're at the bottom of the
|
||||
// list
|
||||
if (scrollBottom >= state.scrollHeight) {
|
||||
endIndex = props.rowCount - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
const index = this.topPositionToIndex.get(scrollBottom);
|
||||
if (index != null) {
|
||||
endIndex = index;
|
||||
break;
|
||||
}
|
||||
|
||||
scrollBottom++;
|
||||
}
|
||||
|
||||
if (
|
||||
startIndex === state.startIndex &&
|
||||
endIndex === state.endIndex &&
|
||||
startTop === state.containerStyle.top
|
||||
) {
|
||||
// this is to ensure that we don't create a new containerStyle object and obey reference equality for purity checks
|
||||
return {};
|
||||
}
|
||||
|
||||
const sideScrollable = props.sideScrollable || false;
|
||||
|
||||
return {
|
||||
startIndex,
|
||||
endIndex,
|
||||
containerStyle: sideScrollable
|
||||
? {
|
||||
position: 'absolute',
|
||||
top: startTop,
|
||||
left: 0,
|
||||
minWidth: '100%',
|
||||
}
|
||||
: {
|
||||
position: 'absolute',
|
||||
top: startTop,
|
||||
right: 0,
|
||||
left: 0,
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
onRowMeasured = (key: string, elem: ?Text | Element) => {
|
||||
if (elem != null && elem instanceof HTMLElement) {
|
||||
const dim = {
|
||||
height: elem.clientHeight,
|
||||
width: elem.clientWidth,
|
||||
};
|
||||
this.dimensions.set(key, dim);
|
||||
}
|
||||
|
||||
this.measureQueue.delete(key);
|
||||
|
||||
if (this.measureQueue.size === 0) {
|
||||
this.recalculatePositions(this.props);
|
||||
|
||||
if (this.state.mounted === false) {
|
||||
// we triggered measurements on componentDidMount and they're now complete!
|
||||
this.onMount();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleScroll = () => {
|
||||
// recalcualte visible rows
|
||||
const ref = this.getContainerRef();
|
||||
if (ref != null) {
|
||||
this.setState({
|
||||
scrollTop: ref.scrollTop,
|
||||
});
|
||||
this.recalculateVisibleRows(this.props);
|
||||
|
||||
this.props.onScroll &&
|
||||
this.props.onScroll({
|
||||
clientHeight: ref.clientHeight,
|
||||
scrollHeight: ref.scrollHeight,
|
||||
scrollTop: ref.scrollTop,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
recalculatePositions(props: DynamicListProps) {
|
||||
this.positions.clear();
|
||||
this.topPositionToIndex.clear();
|
||||
|
||||
let top = 0;
|
||||
|
||||
for (let i = 0; i < props.rowCount; i++) {
|
||||
const key = props.keyMapper(i);
|
||||
const dim = this.dimensions.get(key);
|
||||
if (dim == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.positions.set(i, {
|
||||
top,
|
||||
style: {
|
||||
width: dim.width,
|
||||
height: dim.height,
|
||||
},
|
||||
});
|
||||
|
||||
this.topPositionToIndex.set(top, i);
|
||||
|
||||
top += dim.height;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
scrollHeight: top,
|
||||
innerStyle: {
|
||||
height: top,
|
||||
overflow: 'visibile',
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
// add elements to be measured
|
||||
const measureChildren = [];
|
||||
for (const [key, value] of this.measureQueue) {
|
||||
measureChildren.push(
|
||||
<RowMeasure key={key} id={key} onMount={this.onRowMeasured}>
|
||||
{value}
|
||||
</RowMeasure>,
|
||||
);
|
||||
}
|
||||
|
||||
// add visible rows
|
||||
const children = [];
|
||||
for (let i = this.state.startIndex; i <= this.state.endIndex; i++) {
|
||||
const pos = this.positions.get(i);
|
||||
if (pos == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
children.push(
|
||||
this.props.rowRenderer({
|
||||
index: i,
|
||||
style: pos.style,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={this.setContainerRef}
|
||||
onScroll={this.handleScroll}
|
||||
style={CONTAINER_STYLE}>
|
||||
<ResizeSensor onResize={this.onResize} />
|
||||
<div style={this.state.innerStyle}>
|
||||
<div style={this.state.containerStyle}>{children}</div>
|
||||
</div>
|
||||
{measureChildren}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* 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 type {RowRenderer, OnScroll, KeyMapper} from './types.js';
|
||||
import DynamicList from './DynamicList.js';
|
||||
import {PureComponent} from 'react';
|
||||
|
||||
type FixedListProps = {
|
||||
pureData: any,
|
||||
rowCount: number,
|
||||
rowHeight: number,
|
||||
rowRenderer: RowRenderer,
|
||||
onScroll?: OnScroll,
|
||||
keyMapper: KeyMapper,
|
||||
innerRef?: (ref: DynamicList) => void,
|
||||
onMount?: () => void,
|
||||
sideScrollable?: boolean,
|
||||
};
|
||||
|
||||
export default class FixedList extends PureComponent<FixedListProps> {
|
||||
getPrecalculatedDimensions = () => {
|
||||
return {
|
||||
height: this.props.rowHeight,
|
||||
width: '100%',
|
||||
};
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<DynamicList
|
||||
ref={(this.props.innerRef: any)}
|
||||
onMount={this.props.onMount}
|
||||
pureData={this.props.pureData}
|
||||
rowCount={this.props.rowCount}
|
||||
rowRenderer={this.props.rowRenderer}
|
||||
keyMapper={this.props.keyMapper}
|
||||
onScroll={this.props.onScroll}
|
||||
sideScrollable={this.props.sideScrollable}
|
||||
getPrecalculatedDimensions={this.getPrecalculatedDimensions}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export {default as FixedList} from './FixedList.js';
|
||||
export {default as DynamicList} from './DynamicList.js';
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export type OnScroll = (params: {
|
||||
scrollHeight: number,
|
||||
scrollTop: number,
|
||||
clientHeight: number,
|
||||
}) => void;
|
||||
|
||||
export type KeyMapper = (index: number) => string;
|
||||
|
||||
export type RowRenderer = (params: {
|
||||
index: number,
|
||||
style: Object,
|
||||
}) => React$Node;
|
||||
Reference in New Issue
Block a user