diff --git a/src/ts/app/components/PokemonExplorer/IvForm.tsx b/src/ts/app/components/PokemonExplorer/IvForm.tsx new file mode 100644 index 0000000..9798152 --- /dev/null +++ b/src/ts/app/components/PokemonExplorer/IvForm.tsx @@ -0,0 +1,186 @@ +import React from 'react'; + +import classNames from 'classnames'; + +import { IIndividualValues, IndividualValueKey } from './types'; + +import * as styles from './styles/IvForm.scss'; + +export interface IIvFormProps { + ivs : IIndividualValues; + placeholder : IIndividualValues; + + handleChangeIndividualValue : (stat : IndividualValueKey, value : number | null) => void; + handleMaximizeLevel : () => void; +} + +interface IState { + form : { + level : string; + }; +} + +export class IvForm extends React.Component { + + private readonly MIN_LEVEL = 1; + private readonly MAX_LEVEL = 40; + private readonly MIN_IV = 0; + private readonly MAX_IV = 15; + + private handleChangeHp : (event : React.ChangeEvent) => void; + private handleChangeAtk : (event : React.ChangeEvent) => void; + private handleChangeDef : (event : React.ChangeEvent) => void; + + constructor(props : IIvFormProps) { + super(props); + + this.state = { + form: { + level: '', + } + }; + + this.handleChangeHp = this.handleChangeIvFactory('hp'); + this.handleChangeAtk = this.handleChangeIvFactory('atk'); + this.handleChangeDef = this.handleChangeIvFactory('def'); + } + + public render() { + const { + ivs, + placeholder, + } = this.props; + + const idIvLevelInput = 'iv-level-input'; + const idIvHpInput = 'iv-hp-input'; + const idIvAtkInput = 'iv-atk-input'; + const idIvDefInput = 'iv-def-input'; + + const fieldCss = classNames( + 'nes-field', + ); + const inlineFieldCss = classNames( + fieldCss, + 'is-inline', + styles.fieldRow, + ); + const inputTextCss = classNames( + 'nes-input', + styles.ivInput, + ); + const inputTextLevelCss = classNames( + inputTextCss, + styles.levelInput, + ); + const maxButtonCss = classNames( + 'nes-btn', + { + 'is-primary': ivs.ivHp !== null && ivs.ivAtk !== null && ivs.ivDef !== null, + 'is-disabled': ivs.ivHp === null || ivs.ivAtk === null || ivs.ivDef === null, + }, + ); + + const ivLevel = this.state.form.level !== '' ? parseInt(this.state.form.level, 10) : ivs.level; + const showPlaceholder = ivs.level === null && ivs.ivHp === null && ivs.ivAtk === null && ivs.ivDef === null; + + return [( +
+ + + + + + +
+ ), ( +
+
+ + +
+ +
+ )]; + } + + private readonly handleChangeLevel = (event : React.ChangeEvent) => { + const raw = event.currentTarget.value; + const value = parseFloat(raw); + + this.setState({ form: { level: '' } }); + if (raw === '' + value && value >= this.MIN_LEVEL && value <= this.MAX_LEVEL && value % 0.5 === 0) { + this.props.handleChangeIndividualValue('level', value); + } else if (raw === '') { + this.props.handleChangeIndividualValue('level', null); + } else if (raw.charAt(raw.length) === '.') { + this.setState({ form: { level: raw } }); + } + } + + private readonly handleClickMaximizeLevel = () => { + this.props.handleMaximizeLevel(); + } + + private readonly handleChangeIvFactory = (type : IndividualValueKey) => { + return (event : React.ChangeEvent) => { + 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); + } + }; + } +} diff --git a/src/ts/app/components/PokemonExplorer/LeagueStatsList.tsx b/src/ts/app/components/PokemonExplorer/LeagueStatsList.tsx index 58b17d3..a7da9a2 100644 --- a/src/ts/app/components/PokemonExplorer/LeagueStatsList.tsx +++ b/src/ts/app/components/PokemonExplorer/LeagueStatsList.tsx @@ -57,15 +57,15 @@ export class LeagueStatsList extends React.Component { - if (activeIvs.hp === stats.ivHp && - activeIvs.atk === stats.ivAtk && - activeIvs.def === stats.ivDef + if (activeIvs.ivHp === stats.ivHp && + activeIvs.ivAtk === stats.ivAtk && + activeIvs.ivDef === stats.ivDef ) { this.setState({ activeIndex: index }); activeIndex = index; @@ -77,7 +77,7 @@ export class LeagueStatsList extends React.Component -1) { const stateStats = this.props.leagueStatsList[activeIndex]; - if (activeIvs.hp === stateStats.ivHp && activeIvs.atk === stateStats.ivAtk && activeIvs.def === stateStats.ivDef) { + if (activeIvs.ivHp === stateStats.ivHp && activeIvs.ivAtk === stateStats.ivAtk && activeIvs.ivDef === stateStats.ivDef) { // the current IVs belong to the stats at the active index, so scroll to active stats if (this.listRef.current !== null) { this.listRef.current.scrollToItem(activeIndex, 'center'); @@ -142,9 +142,9 @@ export class LeagueStatsList extends React.Component) => void; - private handleChangeAtk : (event : React.ChangeEvent) => void; - private handleChangeDef : (event : React.ChangeEvent) => void; - constructor(props : IPokemonExplorerProps) { super(props); @@ -75,51 +67,40 @@ export class PokemonExplorer extends React.Component { - if (individualValueLevel === stats.level && - individualValues.hp === stats.ivHp && - individualValues.atk === stats.ivAtk && - individualValues.def === stats.ivDef + if (individualValues.level === stats.level && + individualValues.ivHp === stats.ivHp && + individualValues.ivAtk === stats.ivAtk && + individualValues.ivDef === stats.ivDef ) { rankedPokemon = stats; return true; @@ -130,14 +111,14 @@ export class PokemonExplorer extends React.Component
IVs
-
- - - - - - -
-
-
- - -
- -
+
Rank
-

{ rankedGrade }

Rank
+ { rankedPokemon === null || rankedPokemon.cp > MaxCpByLeague[activeLeague] && +

N/A

+ } + { rankedPokemon !== null && rankedPokemon.cp <= MaxCpByLeague[activeLeague] && +

{ rankedGrade }

Rank
+ }
CP

{ rankedCp }

@@ -416,36 +315,6 @@ export class PokemonExplorer extends React.Component) => { - const raw = event.currentTarget.value; - const value = parseFloat(raw); - - this.setState({ form: { level: '' } }); - if (raw === '' + value && value >= this.MIN_LEVEL && value <= this.MAX_LEVEL && value % 0.5 === 0) { - this.props.handleChangeIndividualValue('level', value); - } else if (raw === '') { - this.props.handleChangeIndividualValue('level', null); - } else if (raw.charAt(raw.length) === '.') { - this.setState({ form: { level: raw } }); - } - } - - private readonly handleClickMaximizeLevel = () => { - this.props.handleMaximizeLevel(); - } - - private readonly handleChangeIvFactory = (type : IndividualValueKey) => { - return (event : React.ChangeEvent) => { - 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); - } - }; - } - private readonly handleActivateLeagueStats = (stats : IStats) => { const { handleChangeIndividualValue } = this.props; diff --git a/src/ts/app/components/PokemonExplorer/actions.ts b/src/ts/app/components/PokemonExplorer/actions.ts index 8212532..ed62ae9 100644 --- a/src/ts/app/components/PokemonExplorer/actions.ts +++ b/src/ts/app/components/PokemonExplorer/actions.ts @@ -16,11 +16,11 @@ export const setLeaguePokemon = (leaguePokemon : ILeaguePokemon | null) => actio 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 setIvHp = (ivHp : number | null) => action(PokemonExplorerActionTypes.SET_IV_HP, { ivHp }); -export const setIvAtk = (atk : number | null) => action(PokemonExplorerActionTypes.SET_IV_ATK, { atk }); +export const setIvAtk = (ivAtk : number | null) => action(PokemonExplorerActionTypes.SET_IV_ATK, { ivAtk }); -export const setIvDef = (def : number | null) => action(PokemonExplorerActionTypes.SET_IV_DEF, { def }); +export const setIvDef = (ivDef : number | null) => action(PokemonExplorerActionTypes.SET_IV_DEF, { ivDef }); export const setActiveLeague = (league : League) => action(PokemonExplorerActionTypes.SET_ACTIVE_LEAGUE, { league }); @@ -37,17 +37,17 @@ export const maximizeLevel = ( return async (dispatch, getState, extraArguments) => { const pokemonExplorerState = getState().pokemonExplorerState; const { - hp, - atk, - def, + ivHp, + ivAtk, + ivDef, } = pokemonExplorerState.individualValues; if (pokemonExplorerState.leaguePokemon !== null) { const pokemonLeagueValues = pokemonExplorerState.leaguePokemon.pvp[pokemonExplorerState.league]; const statsSet = pokemonLeagueValues.some((stats) => { - if (((hp === null) || (stats.ivHp === hp)) && - ((atk === null) || (stats.ivAtk === atk)) && - ((def === null) || (stats.ivDef === def)) + if (((ivHp === null) || (stats.ivHp === ivHp)) && + ((ivAtk === null) || (stats.ivAtk === ivAtk)) && + ((ivDef === null) || (stats.ivDef === ivDef)) ) { dispatch(setIvHp(stats.ivHp)); dispatch(setIvAtk(stats.ivAtk)); @@ -57,8 +57,8 @@ export const maximizeLevel = ( } return false; }); - if (!statsSet && hp !== null && atk !== null && def !== null) { - dispatch(setIvLevel(calculateMaxLevelForLeague(pokemonExplorerState.leaguePokemon.stats, hp, atk, def, pokemonExplorerState.league))); + if (!statsSet && ivHp !== null && ivAtk !== null && ivDef !== null) { + dispatch(setIvLevel(calculateMaxLevelForLeague(pokemonExplorerState.leaguePokemon.stats, ivHp, ivAtk, ivDef, pokemonExplorerState.league))); } } }; diff --git a/src/ts/app/components/PokemonExplorer/reducers.ts b/src/ts/app/components/PokemonExplorer/reducers.ts index b8b71d1..5947d31 100644 --- a/src/ts/app/components/PokemonExplorer/reducers.ts +++ b/src/ts/app/components/PokemonExplorer/reducers.ts @@ -14,9 +14,9 @@ export const initialState : IPokemonExplorerState = { }, individualValues: { level: null, - hp: null, - atk: null, - def: null, + ivHp: null, + ivAtk: null, + ivDef: null, }, league: 'great', }; @@ -65,7 +65,7 @@ const reduceSetIvHp = ( ...state, individualValues: { ...state.individualValues, - hp: action.payload.hp, + ivHp: action.payload.ivHp, } }); @@ -76,7 +76,7 @@ const reduceSetIvAtk = ( ...state, individualValues: { ...state.individualValues, - atk: action.payload.atk, + ivAtk: action.payload.ivAtk, } }); @@ -87,7 +87,7 @@ const reduceSetIvDef = ( ...state, individualValues: { ...state.individualValues, - def: action.payload.def, + ivDef: action.payload.ivDef, } }); diff --git a/src/ts/app/components/PokemonExplorer/styles/IvForm.scss b/src/ts/app/components/PokemonExplorer/styles/IvForm.scss new file mode 100644 index 0000000..39182e2 --- /dev/null +++ b/src/ts/app/components/PokemonExplorer/styles/IvForm.scss @@ -0,0 +1,22 @@ +:global(.nes-field.is-inline) .ivInput { + width: 4.25em; + padding-left: 0.7em; + padding-right: 0.5em; + + &.levelInput { + width: 6.5em; + } +} + +:global(.nes-field.is-inline).fieldRow { + justify-content: space-between; + + label { + flex-grow: 0; + margin-left: 1em; + + &:first-child { + margin-left: 0; + } + } +} diff --git a/src/ts/app/components/PokemonExplorer/styles/IvForm.scss.d.ts b/src/ts/app/components/PokemonExplorer/styles/IvForm.scss.d.ts new file mode 100644 index 0000000..93f0d58 --- /dev/null +++ b/src/ts/app/components/PokemonExplorer/styles/IvForm.scss.d.ts @@ -0,0 +1,5 @@ +// This file is automatically generated. +// Please do not change this file! +export const fieldRow: string; +export const ivInput: string; +export const levelInput: string; diff --git a/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss b/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss index 125cbf1..8811797 100644 --- a/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss +++ b/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss @@ -106,26 +106,3 @@ align-items: start; align-self: stretch; } - -:global(.nes-field.is-inline) .ivInput { - width: 4.25em; - padding-left: 0.7em; - padding-right: 0.5em; - - &.levelInput { - width: 6.5em; - } -} - -:global(.nes-field.is-inline).fieldRow { - justify-content: space-between; - - label { - flex-grow: 0; - margin-left: 1em; - - &:first-child { - margin-left: 0; - } - } -} diff --git a/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss.d.ts b/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss.d.ts index cedb10f..1c13e5c 100644 --- a/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss.d.ts +++ b/src/ts/app/components/PokemonExplorer/styles/PokemonExplorer.scss.d.ts @@ -1,11 +1,8 @@ // This file is automatically generated. // Please do not change this file! export const dexHeader: string; -export const fieldRow: string; export const formHeader: string; -export const ivInput: string; export const leaguePokemonRank: string; -export const levelInput: string; export const pokemonBaseStats: string; export const pokemonInfoLeftColumn: string; export const pokemonInfoRightColumn: string; diff --git a/src/ts/app/components/PokemonExplorer/types.ts b/src/ts/app/components/PokemonExplorer/types.ts index 649528c..4876cee 100644 --- a/src/ts/app/components/PokemonExplorer/types.ts +++ b/src/ts/app/components/PokemonExplorer/types.ts @@ -4,9 +4,9 @@ import { IMaxStats } 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; + ivHp : number | null; + ivAtk : number | null; + ivDef : number | null; } export interface IPokemonExplorerState {