173 lines
5.0 KiB
TypeScript
173 lines
5.0 KiB
TypeScript
import React from 'react';
|
|
import { ContentRect, default as Measure } from 'react-measure';
|
|
import { FixedSizeList } from 'react-window';
|
|
|
|
import classNames from 'classnames';
|
|
|
|
import { CombatMoveStats, IPokemonMove } from 'app/models/Pokemon';
|
|
|
|
import { TypeIndicator } from './TypeIndicator';
|
|
|
|
import * as styles from 'app/styles/MovesDropdown.scss';
|
|
|
|
export interface IMovesDropdownProps {
|
|
menuLabel : string;
|
|
movesById : CombatMoveStats;
|
|
selectedMove : IPokemonMove | null;
|
|
options : Array<IPokemonMove>;
|
|
handleToggleOpen : (open : boolean) => void;
|
|
handleChangeSelectedOption : (option : IPokemonMove) => void;
|
|
}
|
|
|
|
interface IState {
|
|
isMenuOpen : boolean;
|
|
listRef : React.RefObject<FixedSizeList>;
|
|
dimensions : {
|
|
width : number;
|
|
height : number;
|
|
};
|
|
}
|
|
|
|
interface IRowFactory {
|
|
index : number;
|
|
style : React.CSSProperties;
|
|
}
|
|
|
|
export class MovesDropdown extends React.Component<IMovesDropdownProps, IState> {
|
|
|
|
constructor(props : IMovesDropdownProps) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
isMenuOpen: false,
|
|
listRef: React.createRef(),
|
|
dimensions: {
|
|
width: -1,
|
|
height: -1,
|
|
}
|
|
};
|
|
}
|
|
|
|
public render() {
|
|
const {
|
|
menuLabel,
|
|
movesById,
|
|
selectedMove,
|
|
options,
|
|
} = this.props;
|
|
const {
|
|
isMenuOpen,
|
|
} = this.state;
|
|
|
|
const { width, height } = this.state.dimensions;
|
|
const onResize = (contentRect : ContentRect) => {
|
|
if (typeof contentRect.bounds !== 'undefined') {
|
|
this.setState({ dimensions: contentRect.bounds });
|
|
}
|
|
};
|
|
|
|
const wrapperCss = classNames(
|
|
'nes-select',
|
|
'dropdown',
|
|
styles.wrapper,
|
|
);
|
|
const menuCss = classNames(
|
|
'nes-container',
|
|
styles.menu,
|
|
);
|
|
|
|
let moveName = 'Select a move';
|
|
if (selectedMove !== null) {
|
|
const moveStats = movesById.get(selectedMove.id);
|
|
if (moveStats) {
|
|
moveName = moveStats.name;
|
|
} else {
|
|
moveName = 'UNKNOWN MOVE';
|
|
}
|
|
}
|
|
return (
|
|
<React.Fragment>
|
|
<div
|
|
className={ wrapperCss }
|
|
onClick={ this.toggleMenu }
|
|
>
|
|
{ moveName }
|
|
</div>
|
|
{ isMenuOpen &&
|
|
<div className={ menuCss }>
|
|
<h3 className="title">{ menuLabel }</h3>
|
|
<Measure
|
|
bounds={ true }
|
|
onResize={ onResize }
|
|
>
|
|
{
|
|
({ measureRef }) => (
|
|
<div ref={ measureRef }>
|
|
<FixedSizeList
|
|
ref={ this.state.listRef }
|
|
height={ height }
|
|
itemCount={ options.length }
|
|
itemSize={ 35 }
|
|
width={ width }
|
|
>
|
|
{ this.rowFactory.bind(this) }
|
|
</FixedSizeList>
|
|
</div>
|
|
)
|
|
}
|
|
</Measure>
|
|
</div>
|
|
}
|
|
</React.Fragment>
|
|
);
|
|
}
|
|
|
|
private readonly toggleMenu = () => {
|
|
const isMenuOpen = !this.state.isMenuOpen;
|
|
this.setState({
|
|
isMenuOpen,
|
|
});
|
|
this.props.handleToggleOpen(isMenuOpen);
|
|
}
|
|
|
|
private rowFactory({ index, style } : IRowFactory) {
|
|
const {
|
|
movesById,
|
|
selectedMove,
|
|
options,
|
|
} = this.props;
|
|
const move = options[index];
|
|
const moveStats = movesById.get(move.id);
|
|
const css = classNames(
|
|
'list-item', // global style
|
|
styles.listItem,
|
|
{
|
|
active: selectedMove && selectedMove.id === move.id,
|
|
}
|
|
);
|
|
const typeCss = classNames({
|
|
[styles.legacy]: move.isLegacy
|
|
});
|
|
const onClick = () => {
|
|
this.props.handleChangeSelectedOption(move);
|
|
this.toggleMenu();
|
|
};
|
|
|
|
return (
|
|
<React.Fragment>
|
|
{ moveStats &&
|
|
<a
|
|
key={ index }
|
|
style={ style }
|
|
className={ css }
|
|
onClick={ onClick }
|
|
>
|
|
<span>{ moveStats.name }</span>
|
|
<TypeIndicator className={ typeCss } type={ moveStats.type } />
|
|
</a>
|
|
}
|
|
</React.Fragment>
|
|
);
|
|
}
|
|
}
|