2019-02-02 11:38:34 -05:00

175 lines
5.6 KiB
TypeScript

import React from 'react';
import { ContentRect, default as Measure } from 'react-measure';
import { VariableSizeList } from 'react-window';
import classNames from 'classnames';
import { formatDexNumber } from 'app/utils/formatter';
import { IPokemon } from 'app/models/Pokemon';
import * as styles from './styles/PokemonSelectList.scss';
export interface IPokemonSelectListProps {
isLoading : boolean;
activePokemonId : string | null;
pokemonList : Array<IPokemon>;
filterTerm : string;
handleActivatePokemon : (pokemonId : string) => void;
handleChangeFilter : (filterTerm : string) => Promise<void>;
}
interface IState {
dimensions : {
width : number;
height : number;
};
}
interface IRowFactory {
index : number;
style : React.CSSProperties;
}
export class PokemonSelectList extends React.Component<IPokemonSelectListProps, IState> {
private listRef : React.RefObject<VariableSizeList>;
constructor(props : IPokemonSelectListProps) {
super(props);
this.state = {
dimensions: {
width: -1,
height: -1,
}
};
this.listRef = React.createRef();
}
public render() {
const { width, height } = this.state.dimensions;
const listLength = this.props.pokemonList.length;
const onResize = (contentRect : ContentRect) => {
if (typeof contentRect.bounds !== 'undefined') {
this.setState({ dimensions: contentRect.bounds });
}
};
const wrapperCss = classNames(
styles.leftPanel,
{
loading: this.props.isLoading,
}
);
const listWrapperCss = classNames(
'nes-container',
styles.listWrapper,
{
[ styles.emptyList ]: listLength === 0
}
);
const inputTextCss = classNames(
'nes-input',
styles.filterInput
);
return (
<div id="pokemon-select-list" className={ wrapperCss }>
<input
name="filter"
type="text"
className={ inputTextCss }
onChange={ this.handleChangeFilter }
value={ this.props.filterTerm }
placeholder="Pokemon Name"
/>
<div className={ listWrapperCss }>
{ listLength > 0 &&
<Measure
bounds={ true }
onResize={ onResize }
>
{
({ measureRef }) => (
<div ref={ measureRef }>
<VariableSizeList
ref={ this.listRef }
height={ height }
itemKey={ this.getListItemKey }
itemCount={ listLength }
estimatedItemSize={ 25 }
itemSize={ this.calculateRowHeight }
width={ width }
>
{ this.rowFactory.bind(this) }
</VariableSizeList>
</div>
)
}
</Measure>
}
{ listLength === 0 &&
<div className={ styles.emptyState }>
<i className="pokemon-missing-no" />
<h3>MissingNo.</h3>
</div>
}
</div>
</div>
);
}
private readonly getListItemKey = (index : number) => {
return index + this.props.pokemonList[index].id;
}
private readonly calculateRowHeight = (index : number) => {
return this.props.pokemonList[index].form !== null ? 40 : 25;
}
private rowFactory({ index, style } : IRowFactory) {
const pokemon = this.props.pokemonList[index];
const dex = formatDexNumber(pokemon.dex);
const anchorCss = classNames(
'list-item',
{
active: this.props.activePokemonId === pokemon.id
}
);
const dexCss = classNames(
'de-emphasize',
styles.dex
);
const formCss = classNames(
'de-emphasize',
styles.form
);
const onClick = () => this.props.handleActivatePokemon(pokemon.id);
return (
<a
key={ index + pokemon.id }
style={ style }
className={ anchorCss }
onClick={ onClick }
>
<span>{ pokemon.name }</span>
<span className={ dexCss }>#{ dex }</span>
{ pokemon.form &&
<span className={ formCss }>{ pokemon.form.toLowerCase().replace('_', ' ') } Forme</span>
}
</a>
);
}
private readonly handleChangeFilter = (event : React.ChangeEvent<HTMLInputElement>) => {
this.props.handleChangeFilter(event.currentTarget.value)
.then(() => {
if (this.listRef.current !== null) {
this.listRef.current.resetAfterIndex(0, true);
}
});
}
}