175 lines
5.6 KiB
TypeScript
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);
|
|
}
|
|
});
|
|
}
|
|
}
|