Iterated on DynamicSizeList
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
// @flow
|
||||
|
||||
import { Component } from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
|
||||
import type { Direction } from './createListComponent';
|
||||
import type { HandleNewMeasurements } from './DynamicSizeList';
|
||||
|
||||
declare class ResizeObserver {
|
||||
constructor(callback: Function): void;
|
||||
observe(target: HTMLElement): void;
|
||||
unobserve(target: HTMLElement): void;
|
||||
}
|
||||
|
||||
type CellMeasurerProps = {|
|
||||
direction: Direction,
|
||||
handleNewMeasurements: HandleNewMeasurements,
|
||||
index: number,
|
||||
item: React$Element<any>,
|
||||
size: number,
|
||||
|};
|
||||
|
||||
export default class CellMeasurer extends Component<CellMeasurerProps, void> {
|
||||
_node: HTMLElement = (null: any);
|
||||
_resizeObserver: ResizeObserver = (null: any);
|
||||
|
||||
componentDidMount() {
|
||||
this._node = ((findDOMNode(this): any): HTMLElement);
|
||||
|
||||
// Force sync measure for the initial mount.
|
||||
// This is necessary to support the DynamicSizeList layout logic.
|
||||
this._measureItem(true);
|
||||
|
||||
// Watch for resizes due to changed content,
|
||||
// Or changes in the size of the parent container.
|
||||
this._resizeObserver = new ResizeObserver(this._onResize);
|
||||
this._resizeObserver.observe(this._node);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._resizeObserver.unobserve(this._node);
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.item;
|
||||
}
|
||||
|
||||
_measureItem = (isCommitPhase: boolean) => {
|
||||
const {
|
||||
direction,
|
||||
handleNewMeasurements,
|
||||
index,
|
||||
size: oldSize,
|
||||
} = this.props;
|
||||
|
||||
const node = this._node;
|
||||
|
||||
if (
|
||||
node &&
|
||||
node.ownerDocument &&
|
||||
node.ownerDocument.defaultView &&
|
||||
node instanceof node.ownerDocument.defaultView.HTMLElement
|
||||
) {
|
||||
const newSize =
|
||||
direction === 'horizontal'
|
||||
? Math.ceil(node.offsetWidth)
|
||||
: Math.ceil(node.offsetHeight);
|
||||
|
||||
if (oldSize !== newSize) {
|
||||
handleNewMeasurements(index, newSize, isCommitPhase);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_onResize = () => {
|
||||
this._measureItem(false);
|
||||
};
|
||||
}
|
||||
+114
-84
@@ -1,53 +1,84 @@
|
||||
// @flow
|
||||
|
||||
import React, { createElement, Component } from 'react';
|
||||
import { findDOMNode } from 'react-dom';
|
||||
import { createElement } from 'react';
|
||||
|
||||
import createListComponent, { defaultItemKey } from './createListComponent';
|
||||
import CellMeasurer from './CellMeasurer';
|
||||
|
||||
import type { Direction, Props, ScrollToAlign } from './createListComponent';
|
||||
import type { Props, ScrollToAlign } from './createListComponent';
|
||||
|
||||
const DEFAULT_ESTIMATED_ITEM_SIZE = 50;
|
||||
|
||||
type DynanmicProps = {|
|
||||
estimatedItemSize: number,
|
||||
...Props,
|
||||
...Props<any>,
|
||||
|};
|
||||
|
||||
export type HandleNewMeasurements = (
|
||||
index: number,
|
||||
newSize: number,
|
||||
isCommitPhase: boolean
|
||||
) => void;
|
||||
|
||||
type ItemMetadata = {|
|
||||
offset: number,
|
||||
size: number,
|
||||
|};
|
||||
type InstanceProps = {|
|
||||
estimatedItemSize: number,
|
||||
itemMetadataMap: { [index: number]: ItemMetadata },
|
||||
itemOffsetMap: { [index: number]: number },
|
||||
itemSizeMap: { [index: number]: number },
|
||||
lastMeasuredIndex: number,
|
||||
lastPositionedIndex: number,
|
||||
totalMeasuredSize: number,
|
||||
|};
|
||||
|
||||
const getItemMetadata = (
|
||||
props: Props,
|
||||
props: Props<any>,
|
||||
index: number,
|
||||
instanceProps: InstanceProps
|
||||
): ItemMetadata => {
|
||||
const { estimatedItemSize, itemMetadataMap } = instanceProps;
|
||||
|
||||
let itemMetadata = itemMetadataMap[index];
|
||||
const {
|
||||
estimatedItemSize,
|
||||
itemOffsetMap,
|
||||
itemSizeMap,
|
||||
lastMeasuredIndex,
|
||||
lastPositionedIndex,
|
||||
} = instanceProps;
|
||||
|
||||
// If the specified item has not yet been measured,
|
||||
// Just return an estimated size for now.
|
||||
if (itemMetadata === undefined) {
|
||||
itemMetadata = itemMetadataMap[index] = {
|
||||
if (index > lastMeasuredIndex) {
|
||||
return {
|
||||
offset: 0,
|
||||
size: estimatedItemSize,
|
||||
};
|
||||
}
|
||||
|
||||
return itemMetadata;
|
||||
// Lazily update positions if they are stale.
|
||||
if (index > lastPositionedIndex) {
|
||||
if (lastPositionedIndex < 0) {
|
||||
itemOffsetMap[0] = 0;
|
||||
}
|
||||
|
||||
for (let i = Math.max(1, lastPositionedIndex); i <= index; i++) {
|
||||
const prevOffset = itemOffsetMap[i - 1];
|
||||
const prevSize = itemSizeMap[i - 1];
|
||||
|
||||
itemOffsetMap[i] = prevOffset + prevSize;
|
||||
}
|
||||
|
||||
instanceProps.lastPositionedIndex = index;
|
||||
}
|
||||
|
||||
let offset = itemOffsetMap[index];
|
||||
let size = itemSizeMap[index];
|
||||
|
||||
return { offset, size };
|
||||
};
|
||||
|
||||
const findNearestItemBinarySearch = (
|
||||
props: Props,
|
||||
props: Props<any>,
|
||||
instanceProps: InstanceProps,
|
||||
high: number,
|
||||
low: number,
|
||||
@@ -74,9 +105,9 @@ const findNearestItemBinarySearch = (
|
||||
};
|
||||
|
||||
const getEstimatedTotalSize = (
|
||||
{ itemCount }: Props,
|
||||
{ itemCount }: Props<any>,
|
||||
{
|
||||
itemMetadataMap,
|
||||
itemSizeMap,
|
||||
estimatedItemSize,
|
||||
lastMeasuredIndex,
|
||||
totalMeasuredSize,
|
||||
@@ -84,72 +115,29 @@ const getEstimatedTotalSize = (
|
||||
) =>
|
||||
totalMeasuredSize + (itemCount - lastMeasuredIndex - 1) * estimatedItemSize;
|
||||
|
||||
type CellMeasurerProps = {|
|
||||
direction: Direction,
|
||||
index: number,
|
||||
instance: any,
|
||||
item: React$Element<any>,
|
||||
itemMetadata: ItemMetadata,
|
||||
|};
|
||||
class CellMeasurer extends Component<CellMeasurerProps, void> {
|
||||
componentDidMount() {
|
||||
this._measureItem();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// TODO Check if item needs to be remeasured.
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.item;
|
||||
}
|
||||
|
||||
_measureItem() {
|
||||
const { direction, index, instance, itemMetadata } = this.props;
|
||||
|
||||
const node = findDOMNode(this);
|
||||
|
||||
if (
|
||||
node &&
|
||||
node.ownerDocument &&
|
||||
node.ownerDocument.defaultView &&
|
||||
node instanceof node.ownerDocument.defaultView.HTMLElement
|
||||
) {
|
||||
const size =
|
||||
direction === 'horizontal'
|
||||
? Math.ceil(node.offsetWidth)
|
||||
: Math.ceil(node.offsetHeight);
|
||||
|
||||
if (itemMetadata.size !== size) {
|
||||
instance._handleNewMeasurements(index, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DynamicSizeList = createListComponent({
|
||||
getItemOffset: (
|
||||
props: Props,
|
||||
props: Props<any>,
|
||||
index: number,
|
||||
instanceProps: InstanceProps
|
||||
): number => getItemMetadata(props, index, instanceProps).offset,
|
||||
|
||||
getItemSize: (
|
||||
props: Props,
|
||||
props: Props<any>,
|
||||
index: number,
|
||||
instanceProps: InstanceProps
|
||||
): number => {
|
||||
if (index > instanceProps.lastMeasuredIndex) {
|
||||
return instanceProps.estimatedItemSize;
|
||||
} else {
|
||||
return instanceProps.itemMetadataMap[index].size;
|
||||
return instanceProps.itemSizeMap[index];
|
||||
}
|
||||
},
|
||||
|
||||
getEstimatedTotalSize,
|
||||
|
||||
getOffsetForIndexAndAlignment: (
|
||||
props: Props,
|
||||
props: Props<any>,
|
||||
index: number,
|
||||
align: ScrollToAlign,
|
||||
scrollOffset: number,
|
||||
@@ -190,7 +178,7 @@ const DynamicSizeList = createListComponent({
|
||||
},
|
||||
|
||||
getStartIndexForOffset: (
|
||||
props: Props,
|
||||
props: Props<any>,
|
||||
offset: number,
|
||||
instanceProps: InstanceProps
|
||||
): number => {
|
||||
@@ -213,7 +201,7 @@ const DynamicSizeList = createListComponent({
|
||||
},
|
||||
|
||||
getStopIndexForStartIndex: (
|
||||
props: Props,
|
||||
props: Props<any>,
|
||||
startIndex: number,
|
||||
scrollOffset: number,
|
||||
instanceProps: InstanceProps
|
||||
@@ -235,18 +223,31 @@ const DynamicSizeList = createListComponent({
|
||||
return stopIndex;
|
||||
},
|
||||
|
||||
initInstanceProps(props: Props, instance: any): InstanceProps {
|
||||
initInstanceProps(props: Props<any>, instance: any): InstanceProps {
|
||||
const { estimatedItemSize } = ((props: any): DynanmicProps);
|
||||
|
||||
const instanceProps = {
|
||||
estimatedItemSize: estimatedItemSize || DEFAULT_ESTIMATED_ITEM_SIZE,
|
||||
itemMetadataMap: {},
|
||||
itemOffsetMap: {},
|
||||
itemSizeMap: {},
|
||||
lastMeasuredIndex: -1,
|
||||
lastPositionedIndex: -1,
|
||||
totalMeasuredSize: 0,
|
||||
};
|
||||
|
||||
let hasNewMeasurements: boolean = false;
|
||||
|
||||
// TODO Cancel pending debounce on unmount
|
||||
let debounceForceUpdateID = null;
|
||||
const debounceForceUpdate = () => {
|
||||
if (debounceForceUpdateID === null) {
|
||||
debounceForceUpdateID = setTimeout(() => {
|
||||
debounceForceUpdateID = null;
|
||||
instance.forceUpdate();
|
||||
}, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// List calls this method automatically after "mount" and "update".
|
||||
instance._commitHook = () => {
|
||||
if (hasNewMeasurements) {
|
||||
@@ -264,34 +265,45 @@ const DynamicSizeList = createListComponent({
|
||||
// This function may be called out of order!
|
||||
// It is not safe to reposition items here.
|
||||
// Be careful when comparing index and lastMeasuredIndex.
|
||||
instance._handleNewMeasurements = (index: number, size: number) => {
|
||||
const { itemMetadataMap, lastMeasuredIndex } = instanceProps;
|
||||
const handleNewMeasurements: HandleNewMeasurements = (
|
||||
index: number,
|
||||
newSize: number,
|
||||
isCommitPhase: boolean
|
||||
) => {
|
||||
const {
|
||||
itemSizeMap,
|
||||
lastMeasuredIndex,
|
||||
lastPositionedIndex,
|
||||
} = instanceProps;
|
||||
|
||||
const itemMetadata = itemMetadataMap[index];
|
||||
const oldSize = itemSizeMap[index];
|
||||
|
||||
// Adjust item position in case new measurements were recorded.
|
||||
// This method will always be called in order (lowest to highest index),
|
||||
// So it is safe to adjust positions here.
|
||||
if (index > 0) {
|
||||
const prevItemMetadata = itemMetadataMap[index - 1];
|
||||
itemMetadata.offset = prevItemMetadata.offset + prevItemMetadata.size;
|
||||
// Mark offsets after this as stale so that getItemMetadata() will lazily recalculate it.
|
||||
if (index < lastPositionedIndex) {
|
||||
instanceProps.lastPositionedIndex = index;
|
||||
}
|
||||
|
||||
if (index <= lastMeasuredIndex) {
|
||||
if (itemMetadata.size === size) {
|
||||
if (oldSize === newSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
instanceProps.totalMeasuredSize += size - itemMetadata.size;
|
||||
// Adjust total size estimate by the delta in size.
|
||||
instanceProps.totalMeasuredSize += newSize - oldSize;
|
||||
} else {
|
||||
instanceProps.lastMeasuredIndex = index;
|
||||
instanceProps.totalMeasuredSize += size;
|
||||
instanceProps.totalMeasuredSize += newSize;
|
||||
}
|
||||
|
||||
itemMetadata.size = size;
|
||||
itemSizeMap[index] = newSize;
|
||||
|
||||
hasNewMeasurements = true;
|
||||
if (isCommitPhase) {
|
||||
hasNewMeasurements = true;
|
||||
} else {
|
||||
debounceForceUpdate();
|
||||
}
|
||||
};
|
||||
instance._handleNewMeasurements = handleNewMeasurements;
|
||||
|
||||
// Override the item-rendering process to wrap items with CellMeasurer.
|
||||
// This keep the external API simpler.
|
||||
@@ -311,10 +323,18 @@ const DynamicSizeList = createListComponent({
|
||||
|
||||
const items = [];
|
||||
if (itemCount > 0) {
|
||||
const { lastMeasuredIndex } = instanceProps;
|
||||
|
||||
for (let index = startIndex; index <= stopIndex; index++) {
|
||||
let style = instance._getItemStyle(index);
|
||||
|
||||
if (index > instanceProps.lastMeasuredIndex) {
|
||||
const { offset, size } = getItemMetadata(
|
||||
instance.props,
|
||||
index,
|
||||
instanceProps
|
||||
);
|
||||
|
||||
if (index > lastMeasuredIndex) {
|
||||
// Strip hard-coded dimensions from the inline style.
|
||||
// These would interfere with the item laying itself out anyway.
|
||||
// Constrain the item to fill either the width or height of the list,
|
||||
@@ -324,6 +344,14 @@ const DynamicSizeList = createListComponent({
|
||||
height: direction === 'horizontal' ? height : undefined,
|
||||
width: direction === 'vertical' ? width : undefined,
|
||||
};
|
||||
} else {
|
||||
style = {
|
||||
...style,
|
||||
height: direction === 'horizontal' ? '100%' : undefined,
|
||||
width: direction === 'vertical' ? '100%' : undefined,
|
||||
left: direction === 'horizontal' ? offset : 0,
|
||||
top: direction === 'vertical' ? offset : 0,
|
||||
};
|
||||
}
|
||||
|
||||
const item = createElement(children, {
|
||||
@@ -338,11 +366,11 @@ const DynamicSizeList = createListComponent({
|
||||
items.push(
|
||||
createElement(CellMeasurer, {
|
||||
direction,
|
||||
handleNewMeasurements: instance._handleNewMeasurements,
|
||||
index,
|
||||
instance,
|
||||
item,
|
||||
itemMetadata: instanceProps.itemMetadataMap[index],
|
||||
key: itemKey(index),
|
||||
size,
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -357,7 +385,9 @@ const DynamicSizeList = createListComponent({
|
||||
return instanceProps;
|
||||
},
|
||||
|
||||
validateProps: ({ itemSize }: Props): void => {
|
||||
shouldResetStyleCacheOnItemSizeChange: false,
|
||||
|
||||
validateProps: ({ itemSize }: Props<any>): void => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (itemSize !== undefined) {
|
||||
throw Error('An unexpected "itemSize" prop has been provided.');
|
||||
|
||||
@@ -231,9 +231,6 @@ export default function createListComponent({
|
||||
height,
|
||||
innerRef,
|
||||
innerTagName,
|
||||
itemCount,
|
||||
itemData,
|
||||
itemKey = defaultItemKey,
|
||||
outerTagName,
|
||||
style,
|
||||
width,
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"fs-extra": "3.0.1",
|
||||
"html-webpack-plugin": "2.29.0",
|
||||
"jest": "20.0.4",
|
||||
"lorem-ipsum": "^1.0.6",
|
||||
"object-assign": "4.1.1",
|
||||
"postcss-flexbugs-fixes": "3.2.0",
|
||||
"postcss-loader": "2.0.10",
|
||||
|
||||
@@ -2,15 +2,16 @@ import { DynamicSizeList as List } from 'react-window';
|
||||
|
||||
// This example assumes 'items' is an array of strings.
|
||||
// Your application may render a more complex list of items.
|
||||
const Row = ({ index, style }) => (
|
||||
<div style={style}>items[index]</div>
|
||||
);
|
||||
|
||||
<List
|
||||
height={150}
|
||||
itemCount={1000}
|
||||
width={300}
|
||||
>
|
||||
{({ index, style }) => (
|
||||
<div style={style}>
|
||||
{items[index]}
|
||||
</div>
|
||||
)}
|
||||
</List>
|
||||
const Example = () => (
|
||||
<List
|
||||
height={150}
|
||||
itemCount={1000}
|
||||
width={300}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
);
|
||||
@@ -10,6 +10,7 @@ import styles from './ProfiledExample.module.css';
|
||||
type Props = {|
|
||||
className?: string,
|
||||
sandbox?: string,
|
||||
style?: Object,
|
||||
|};
|
||||
|
||||
const isProfilingEnabled = window.location.hash.includes('profile=true');
|
||||
@@ -21,11 +22,11 @@ export default class ProfiledExample extends PureComponent<Props, void> {
|
||||
_totalActualTime = 0;
|
||||
|
||||
render() {
|
||||
const { className, children, sandbox } = this.props;
|
||||
const { className, children, sandbox, style } = this.props;
|
||||
|
||||
if (isProfilingEnabled) {
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className={className} style={style}>
|
||||
<Profiler id={sandbox || 'Profiler'} onRender={this._onRender}>
|
||||
{children}
|
||||
</Profiler>
|
||||
@@ -46,7 +47,7 @@ export default class ProfiledExample extends PureComponent<Props, void> {
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className={className} style={style}>
|
||||
{children}
|
||||
<div className={styles.Row}>
|
||||
<CodeSandboxLink sandbox={sandbox} />
|
||||
|
||||
@@ -1,79 +1,93 @@
|
||||
import cn from 'classnames';
|
||||
import React, { Component } from 'react';
|
||||
import { DynamicSizeList } from 'react-window';
|
||||
// import randomWords from 'random-words';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { DynamicSizeList as List } from 'react-window';
|
||||
import loremIpsum from 'lorem-ipsum';
|
||||
import CodeBlock from '../../components/CodeBlock';
|
||||
import ProfiledExample from '../../components/ProfiledExample';
|
||||
|
||||
import CODE_HORIZONTAL from '../../code/DynamicSizeListHorizontal.js';
|
||||
//import CODE_HORIZONTAL from '../../code/DynamicSizeListHorizontal.js';
|
||||
import CODE_VERTICAL from '../../code/DynamicSizeListVertical.js';
|
||||
|
||||
import styles from './shared.module.css';
|
||||
|
||||
const words = ['cat', 'kitten', 'feline', 'kitty'];
|
||||
const randomWords = ({ min, max }) => {
|
||||
const target = min + Math.round(Math.random() * (max - min));
|
||||
return new Array(target)
|
||||
.fill(true)
|
||||
.map(() => words[Math.floor(Math.random() * words.length)]);
|
||||
};
|
||||
|
||||
const names = [
|
||||
'Stasia',
|
||||
'Shaunta',
|
||||
'Lavona',
|
||||
'Felica',
|
||||
'Glinda',
|
||||
'Percy',
|
||||
'Irina',
|
||||
'Noriko',
|
||||
'Evette',
|
||||
'Margene',
|
||||
'Cordia',
|
||||
'Karmen',
|
||||
'Kitty',
|
||||
'Rima',
|
||||
'Dessie',
|
||||
'Kory',
|
||||
'Oda',
|
||||
'Alesia',
|
||||
'Loura',
|
||||
'Lucius',
|
||||
var colors = [
|
||||
['#1E88E5', '#90CAF9'],
|
||||
['#6D4C41', '#D7CCC8'],
|
||||
['#212121', '#BDBDBD'],
|
||||
['#388E3C', '#A5D6A7'],
|
||||
['#E53935', '#EF9A9A'],
|
||||
['#F4511E', '#FFAB91'],
|
||||
['#8E24AA', '#E1BEE7'],
|
||||
['#FFD600', '#FFF59D'],
|
||||
];
|
||||
|
||||
const items = new Array(500).fill(true).map(() => ({
|
||||
paragraph: randomWords({ min: 3, max: 30 }).join(', '),
|
||||
name: names[Math.floor(Math.random() * names.length)],
|
||||
}));
|
||||
const items = new Array(500).fill(true).map(() => {
|
||||
const text = loremIpsum({ units: 'paragraphs' });
|
||||
return {
|
||||
colors: colors[Math.floor(Math.random() * colors.length)],
|
||||
paragraph: text,
|
||||
sentence: text.substr(0, text.indexOf('.')) + '…',
|
||||
};
|
||||
});
|
||||
|
||||
const itemRowRenderer = ({ index, style }) => (
|
||||
<div
|
||||
className={
|
||||
index % 2 ? styles.DynamicListItemOdd : styles.DynamicListItemEven
|
||||
}
|
||||
style={style}
|
||||
>
|
||||
{index}: {items[index].paragraph}
|
||||
</div>
|
||||
);
|
||||
class Row extends PureComponent {
|
||||
state = {
|
||||
isExpanded: true,
|
||||
};
|
||||
|
||||
const itemColumnRenderer = ({ index, style }) => (
|
||||
<div
|
||||
className={cn(
|
||||
index % 2 ? styles.DynamicListItemOdd : styles.DynamicListItemEven,
|
||||
styles.ListColumn
|
||||
)}
|
||||
style={style}
|
||||
>
|
||||
{index}: {items[index].name}
|
||||
</div>
|
||||
);
|
||||
toggleExpanded = () =>
|
||||
this.setState(prevState => ({
|
||||
isExpanded: !prevState.isExpanded,
|
||||
}));
|
||||
|
||||
export default class ScrollToItem extends Component {
|
||||
render() {
|
||||
const { index, style } = this.props;
|
||||
const { isExpanded } = this.state;
|
||||
|
||||
const item = items[index];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
index % 2 ? styles.DynamicListItemOdd : styles.DynamicListItemEven
|
||||
}
|
||||
style={{
|
||||
display: 'flex',
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={styles.DynamicRowAvatar}
|
||||
style={{
|
||||
backgroundColor: item.colors[1],
|
||||
color: item.colors[0],
|
||||
}}
|
||||
>
|
||||
{index}
|
||||
</div>
|
||||
<div className={styles.DynamicRowText} onClick={this.toggleExpanded}>
|
||||
{isExpanded ? item.paragraph : item.sentence}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class DynamicSizeList extends PureComponent {
|
||||
horizontalListRef = React.createRef();
|
||||
verticalListRef = React.createRef();
|
||||
|
||||
state = {
|
||||
halfSize: false,
|
||||
};
|
||||
|
||||
handleToggleResize = () =>
|
||||
this.setState(prevState => ({
|
||||
halfSize: !prevState.halfSize,
|
||||
}));
|
||||
|
||||
render() {
|
||||
const { halfSize } = this.state;
|
||||
|
||||
return (
|
||||
<div className={styles.ExampleWrapper}>
|
||||
<h1 className={styles.ExampleHeader}>Dynamic Size List</h1>
|
||||
@@ -91,23 +105,30 @@ export default class ScrollToItem extends Component {
|
||||
>
|
||||
Scroll to 15,000px
|
||||
</button>
|
||||
<DynamicSizeList
|
||||
<button
|
||||
className={styles.ExampleButton}
|
||||
onClick={this.handleToggleResize}
|
||||
>
|
||||
Resize list
|
||||
</button>
|
||||
<List
|
||||
className={styles.List}
|
||||
height={200}
|
||||
itemCount={items.length}
|
||||
ref={this.verticalListRef}
|
||||
width={300}
|
||||
width={halfSize ? '50%' : '100%'}
|
||||
>
|
||||
{itemRowRenderer}
|
||||
</DynamicSizeList>
|
||||
{Row}
|
||||
</List>
|
||||
</ProfiledExample>
|
||||
<div className={styles.ExampleCode}>
|
||||
<CodeBlock value={CODE_VERTICAL} />
|
||||
</div>
|
||||
</div>
|
||||
{/*
|
||||
<div className={styles.Example}>
|
||||
<ProfiledExample className={styles.ExampleDemo}>
|
||||
<DynamicSizeList
|
||||
<List
|
||||
className={styles.List}
|
||||
direction="horizontal"
|
||||
height={50}
|
||||
@@ -116,12 +137,13 @@ export default class ScrollToItem extends Component {
|
||||
width={300}
|
||||
>
|
||||
{itemColumnRenderer}
|
||||
</DynamicSizeList>
|
||||
</List>
|
||||
</ProfiledExample>
|
||||
<div className={styles.ExampleCode}>
|
||||
<CodeBlock value={CODE_HORIZONTAL} />
|
||||
</div>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -89,6 +89,23 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.DynamicRowAvatar {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: 50%;
|
||||
margin-right: 0.5rem;
|
||||
flex: 0 0 auto;
|
||||
line-height: 2.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.DynamicRowText {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: 37.5rem) {
|
||||
.ExampleWrapper {
|
||||
padding: 0;
|
||||
|
||||
+7
-1
@@ -6077,6 +6077,12 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3
|
||||
dependencies:
|
||||
js-tokens "^3.0.0"
|
||||
|
||||
lorem-ipsum@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/lorem-ipsum/-/lorem-ipsum-1.0.6.tgz#69e9ab02bbb0991915d71b5559fe016d526f013f"
|
||||
dependencies:
|
||||
minimist "~1.2.0"
|
||||
|
||||
loud-rejection@^1.0.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
|
||||
@@ -6294,7 +6300,7 @@ minimist@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
|
||||
minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
|
||||
minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user