2019-02-19 19:29:32 -05:00

215 lines
7.2 KiB
TypeScript

import POGOProtos from 'pogo-protos';
import React from 'react';
import { ContentRect, default as Measure } from 'react-measure';
import { Link } from 'react-router-dom';
import { VariableSizeList } from 'react-window';
import classNames from 'classnames';
import { formatDexNumber, formatForm } from 'app/utils/formatter';
import { appendQueryString } from 'app/utils/navigation';
import { DEFAULT_POKEMON_NAME, IPokemon } from 'app/models/Pokemon';
import * as styles from './styles/PokemonSelectList.scss';
export interface IPokemonSelectListProps {
isLoading : boolean;
activePokemonId : POGOProtos.Enums.PokemonId | null;
activePokemonForm : POGOProtos.Enums.Form | null;
pokemonList : Array<IPokemon>;
filterTerm : string;
handleActivatePokemon : (pokemonId : POGOProtos.Enums.PokemonId, form : POGOProtos.Enums.Form) => 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 }>
<div className={ styles.filterWrapper }>
<input
name="filter"
type="text"
className={ inputTextCss }
onChange={ this.handleChangeFilter }
value={ this.props.filterTerm }
placeholder="Search"
/>
{ this.props.filterTerm !== '' &&
<i
className="nes-icon close is-small"
onClick={ this.handleClickClearFilter }
/>
}
</div>
<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 && this.props.filterTerm !== '' &&
<div className={ styles.emptyState }>
<i className="pokemon-missing-no" />
<h3>{ DEFAULT_POKEMON_NAME }</h3>
</div>
}
</div>
</div>
);
}
private readonly getListItemKey = (index : number) => {
const { pokemonList } = this.props;
const pokemon = pokemonList[index];
return `${index}-${pokemon.id}-${pokemon.form}`;
}
private readonly calculateRowHeight = (index : number) => {
return this.props.pokemonList[index].form === POGOProtos.Enums.Form.FORM_UNSET ? 25 : 40;
}
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 && this.props.activePokemonForm === pokemon.form
}
);
const menuIconCss = classNames(
styles.menuIcon,
'menu',
`pokemon-${dex}`
);
const dexCss = classNames(
'de-emphasize',
styles.dex
);
const formCss = classNames(
'de-emphasize',
styles.form
);
const onClick = () => this.props.handleActivatePokemon(pokemon.id, pokemon.form);
const linkTo = {
// pathname: '/courses',
search: appendQueryString(location, {
id: pokemon.id.toString(),
form: pokemon.form.toString(),
}),
// hash: '#the-hash',
// state: { fromDashboard: true }
};
return (
<Link
to={ linkTo }
key={ this.getListItemKey(index) }
style={ style }
className={ anchorCss }
onClick={ onClick }
>
<span>{ pokemon.name }</span>
<span className={ dexCss }>#{ dex }</span>
<i className={ menuIconCss } />
{ pokemon.form !== POGOProtos.Enums.Form.FORM_UNSET &&
<span className={ formCss }>{ formatForm(pokemon.form) } Form</span>
}
</Link>
);
}
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);
}
});
}
private readonly handleClickClearFilter = () => {
this.props.handleChangeFilter('')
.then(() => {
if (this.listRef.current !== null) {
this.listRef.current.resetAfterIndex(0, true);
}
});
}
}