This commit is contained in:
Jeff Colombo 2019-01-19 20:34:46 -05:00
parent f887b3e13e
commit 5ba1d48c7a
6 changed files with 254 additions and 8 deletions

View File

@ -7,6 +7,8 @@ import * as ActionsPokemonExplorer from './components/PokemonExplorer/actions';
import * as ActionsPokemonSelectList from './components/PokemonSelectList/actions';
import { ThunkDispatchPokemonSelectList } from './types';
import { IndividualValueKey } from './components/PokemonExplorer/types';
import { PokemonExplorer } from './components/PokemonExplorer/PokemonExplorer';
import { PokemonSelectList } from './components/PokemonSelectList/PokemonSelectList';
@ -32,6 +34,7 @@ class PokemonApp extends React.Component<IConnectedPokemonAppProps> {
pokemonList,
} = this.props.pokemonSelectListState;
const {
individualValues,
leaguePokemon,
} = this.props.pokemonExplorerState;
@ -41,19 +44,21 @@ class PokemonApp extends React.Component<IConnectedPokemonAppProps> {
isLoading={ this.props.pokemonSelectListState.isLoading }
activePokemonIndex={ activePokemonIndex }
pokemonList={ pokemonList }
onActivatePokemon={ this.onActivatePokemon }
handleActivatePokemon={ this.handleActivatePokemon }
/>
{ leaguePokemon !== null &&
<PokemonExplorer
isLoading={ this.props.pokemonExplorerState.isLoading }
leaguePokemon={ leaguePokemon }
individualValues={ individualValues }
handleChangeIndividualValue={ this.handleChangeIndividualValue }
/>
}
</div>
);
}
private readonly onActivatePokemon = (pokemonIndex : number) => {
private readonly handleActivatePokemon = (pokemonIndex : number) => {
const { dispatch, pokemonSelectListState } = this.props;
const pokemonId = pokemonSelectListState.pokemonList[pokemonIndex].id;
@ -69,6 +74,27 @@ class PokemonApp extends React.Component<IConnectedPokemonAppProps> {
})
.then(() => dispatch(ActionsPokemonExplorer.setIsLoading(false)));
}
private readonly handleChangeIndividualValue = (stat : IndividualValueKey, value : number | null) => {
const { dispatch } = this.props;
switch (stat) {
case 'level':
dispatch(ActionsPokemonExplorer.setIvLevel(value));
break;
case 'hp':
dispatch(ActionsPokemonExplorer.setIvHp(value));
break;
case 'atk':
dispatch(ActionsPokemonExplorer.setIvAtk(value));
break;
case 'def':
dispatch(ActionsPokemonExplorer.setIvDef(value));
break;
default:
break;
}
}
}
const mapStateToProps = (state : ReturnType<typeof appReducers>) : PokemonAppProps => {

View File

@ -2,16 +2,157 @@ import React from 'react';
import { ILeaguePokemon } from 'app/models/Pokemon';
export interface IPokemonSelectListProps {
import { IIndividualValues, IndividualValueKey } from './types';
export interface IPokemonExplorerProps {
isLoading : boolean;
leaguePokemon : ILeaguePokemon;
individualValues : IIndividualValues;
handleChangeIndividualValue : (stat : IndividualValueKey, value : number | null) => void;
}
interface IState {
}
export class PokemonExplorer extends React.Component<IPokemonSelectListProps, IState> {
export class PokemonExplorer extends React.Component<IPokemonExplorerProps, IState> {
private readonly MIN_LEVEL = 1;
private readonly MAX_LEVEL = 40;
private readonly MIN_IV = 0;
private readonly MAX_IV = 15;
private onChangeHp : (event : React.ChangeEvent<HTMLInputElement>) => void;
private onChangeAtk : (event : React.ChangeEvent<HTMLInputElement>) => void;
private onChangeDef : (event : React.ChangeEvent<HTMLInputElement>) => void;
constructor(props : IPokemonExplorerProps) {
super(props);
this.onChangeHp = this.onChangeIvFactory('hp');
this.onChangeAtk = this.onChangeIvFactory('atk');
this.onChangeDef = this.onChangeIvFactory('def');
}
public render() {
return (<div>{ this.props.leaguePokemon.name }</div>);
const {
individualValues,
leaguePokemon
} = this.props;
const placeholderLevel = '40';
const placeholderHp = '15';
const placeholderAtk = '15';
const placeholderDef = '15';
return (
<div id="pokemon-explorer">
<h2 className="pokemon-info">
<span>{ this.formatDexNumber(leaguePokemon.dex) }</span>
{ leaguePokemon.name }
</h2>
<div className="pokemon-base-stats">
<div>Base Stats</div>
<div className="pokemon-type type-1">{ }</div>
<div className="pokemon-type type-2">{ }</div>
<div>{ leaguePokemon.stats.baseStamina }</div>
<div>{ leaguePokemon.stats.baseAttack }</div>
<div>{ leaguePokemon.stats.baseDefense }</div>
</div>
<div className="pokemon-individual-stats">
<div>
<h5>Level</h5>
<input
name="level"
type="number"
min={ this.MIN_LEVEL }
max={ this.MAX_LEVEL }
maxLength={ 2 }
onChange={ this.onChangeLevel }
value={ individualValues.level || '' }
placeholder={ placeholderLevel }
/>
</div>
<div>
<h5>IVs</h5>
<label>
<input
name="hp"
type="number"
min={ this.MIN_IV }
max={ this.MAX_IV }
maxLength={ 2 }
onChange={ this.onChangeHp }
value={ individualValues.hp || '' }
placeholder={ placeholderHp }
/> HP
</label>
<label>
<input
name="atk"
type="number"
min={ this.MIN_IV }
max={ this.MAX_IV }
maxLength={ 2 }
onChange={ this.onChangeAtk }
value={ individualValues.atk || '' }
placeholder={ placeholderAtk }
/> ATK
</label>
<label>
<input
name="def"
type="number"
min={ this.MIN_IV }
max={ this.MAX_IV }
maxLength={ 2 }
onChange={ this.onChangeDef }
value={ individualValues.def || '' }
placeholder={ placeholderDef }
/> DEF
</label>
</div>
</div>
<div className="league-pokemon-rank">
<div className="league-pokemon-stat pokemon-rank"><span>{ }</span> Rank</div>
<div className="league-pokemon-stat pokemon-cp">CP <span>{ }</span></div>
<div className="league-pokemon-stat"><span>{ }</span> HP</div>
<div className="league-pokemon-stat"><span>{ }</span> ATK</div>
<div className="league-pokemon-stat"><span>{ }</span> DEF</div>
</div>
</div>
);
}
private formatDexNumber(dex : number) {
let prefix : string = '#';
if (dex < 100) {
prefix += '0';
}
if (dex < 10) {
prefix += '0';
}
return prefix + dex;
}
private readonly onChangeLevel = (event : React.ChangeEvent<HTMLInputElement>) => {
const raw = event.currentTarget.value;
const value = parseInt(raw, 10);
if (raw === '' + value && value >= this.MIN_LEVEL && value <= this.MAX_LEVEL) {
this.props.handleChangeIndividualValue('level', value);
} else if (raw === '') {
this.props.handleChangeIndividualValue('level', null);
}
}
private readonly onChangeIvFactory = (type : IndividualValueKey) => {
return (event : React.ChangeEvent<HTMLInputElement>) => {
const raw = event.currentTarget.value;
const value = parseInt(raw, 10);
if (raw === '' + value && value >= this.MIN_IV && value <= this.MAX_IV) {
this.props.handleChangeIndividualValue(type, value);
} else if (raw === '') {
this.props.handleChangeIndividualValue(type, null);
}
};
}
}

View File

@ -7,3 +7,11 @@ import { ILeaguePokemon } from 'app/models/Pokemon';
export const setIsLoading = (isLoading : boolean) => action(PokemonExplorerActionTypes.SET_IS_LOADING, { isLoading });
export const setLeaguePokemon = (leaguePokemon : ILeaguePokemon | null) => action(PokemonExplorerActionTypes.SET_LEAGUE_POKEMON, { leaguePokemon });
export const setIvLevel = (level : number | null) => action(PokemonExplorerActionTypes.SET_IV_LEVEL, { level });
export const setIvHp = (hp : number | null) => action(PokemonExplorerActionTypes.SET_IV_HP, { hp });
export const setIvAtk = (atk : number | null) => action(PokemonExplorerActionTypes.SET_IV_ATK, { atk });
export const setIvDef = (def : number | null) => action(PokemonExplorerActionTypes.SET_IV_DEF, { def });

View File

@ -6,6 +6,12 @@ import { IPokemonExplorerState, PokemonExplorerActionTypes } from './types';
export const initialState : IPokemonExplorerState = {
isLoading: false,
leaguePokemon: null,
individualValues: {
level: null,
hp: null,
atk: null,
def: null,
},
};
const reduceSetIsLoading = (
@ -24,6 +30,50 @@ const reduceSetLeaguePokemon = (
leaguePokemon: action.payload.leaguePokemon,
});
const reduceSetIvLevel = (
state : IPokemonExplorerState,
action : ReturnType<typeof Actions.setIvLevel>
) : IPokemonExplorerState => ({
...state,
individualValues: {
...state.individualValues,
level: action.payload.level,
}
});
const reduceSetIvHp = (
state : IPokemonExplorerState,
action : ReturnType<typeof Actions.setIvHp>
) : IPokemonExplorerState => ({
...state,
individualValues: {
...state.individualValues,
hp: action.payload.hp,
}
});
const reduceSetIvAtk = (
state : IPokemonExplorerState,
action : ReturnType<typeof Actions.setIvAtk>
) : IPokemonExplorerState => ({
...state,
individualValues: {
...state.individualValues,
atk: action.payload.atk,
}
});
const reduceSetIvDef = (
state : IPokemonExplorerState,
action : ReturnType<typeof Actions.setIvDef>
) : IPokemonExplorerState => ({
...state,
individualValues: {
...state.individualValues,
def: action.payload.def,
}
});
export const PokemonExplorerReducers : Reducer<IPokemonExplorerState> = (
state : IPokemonExplorerState = initialState,
action,
@ -33,6 +83,14 @@ export const PokemonExplorerReducers : Reducer<IPokemonExplorerState> = (
return reduceSetIsLoading(state, action as ReturnType<typeof Actions.setIsLoading>);
case PokemonExplorerActionTypes.SET_LEAGUE_POKEMON:
return reduceSetLeaguePokemon(state, action as ReturnType<typeof Actions.setLeaguePokemon>);
case PokemonExplorerActionTypes.SET_IV_LEVEL:
return reduceSetIvLevel(state, action as ReturnType<typeof Actions.setIvLevel>);
case PokemonExplorerActionTypes.SET_IV_HP:
return reduceSetIvHp(state, action as ReturnType<typeof Actions.setIvHp>);
case PokemonExplorerActionTypes.SET_IV_ATK:
return reduceSetIvAtk(state, action as ReturnType<typeof Actions.setIvAtk>);
case PokemonExplorerActionTypes.SET_IV_DEF:
return reduceSetIvDef(state, action as ReturnType<typeof Actions.setIvDef>);
default:
return state;
}

View File

@ -1,11 +1,24 @@
import { ILeaguePokemon } from 'app/models/Pokemon';
export type IndividualValueKey = 'level' | 'hp' | 'atk' | 'def';
export interface IIndividualValues {
level : number | null;
hp : number | null;
atk : number | null;
def : number | null;
}
export interface IPokemonExplorerState {
isLoading : boolean;
leaguePokemon : ILeaguePokemon | null;
individualValues : IIndividualValues;
}
export const PokemonExplorerActionTypes = {
SET_IS_LOADING: 'POKEMON_EXPLORER/SET_IS_LOADING',
SET_LEAGUE_POKEMON: 'POKEMON_EXPLORER/SET_LEAGUE_POKEMON',
SET_IV_LEVEL: 'POKEMON_EXPLORER/SET_IV_LEVEL',
SET_IV_HP: 'POKEMON_EXPLORER/SET_IV_HP',
SET_IV_ATK: 'POKEMON_EXPLORER/SET_IV_ATK',
SET_IV_DEF: 'POKEMON_EXPLORER/SET_IV_DEF',
};

View File

@ -11,7 +11,7 @@ export interface IPokemonSelectListProps {
activePokemonIndex : number | null;
pokemonList : Array<IPokemon>;
onActivatePokemon : (index : number) => void;
handleActivatePokemon : (index : number) => void;
}
interface IState {
@ -51,7 +51,7 @@ export class PokemonSelectList extends React.Component<IPokemonSelectListProps,
});
return (
<div className={ classes } style={ { height: '400px' } }>
<div id="pokemon-select-list" className={ classes } style={ { height: '400px' } }>
<Measure
bounds={ true }
onResize={ onResize }
@ -80,7 +80,7 @@ export class PokemonSelectList extends React.Component<IPokemonSelectListProps,
const css = classNames({
active: this.props.activePokemonIndex === index
});
const onClick = () => this.props.onActivatePokemon(index);
const onClick = () => this.props.handleActivatePokemon(index);
return <div key={ index } style={ style } className={ css } onClick={ onClick }>{ pokemon.name }</div>;
}
}