Add form validation for AxSelect
We're not done yet, there's still some weird behaviors and a case we haven't properly checked (if second AX skill has a value but first AX skill doesn't)
This commit is contained in:
parent
9b39299a3a
commit
e9546293dc
3 changed files with 117 additions and 30 deletions
|
|
@ -1,30 +1,48 @@
|
||||||
.AXSet {
|
.AXSelect {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
gap: $unit;
|
gap: $unit;
|
||||||
|
|
||||||
&.hidden {
|
.AXSet {
|
||||||
display: none;
|
&.hidden {
|
||||||
}
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
.errors {
|
||||||
flex-grow: 1;
|
color: $error;
|
||||||
}
|
display: none;
|
||||||
|
padding: $unit 0;
|
||||||
|
|
||||||
.Input {
|
&.visible {
|
||||||
-webkit-font-smoothing: antialiased;
|
display: block;
|
||||||
border: none;
|
}
|
||||||
background-color: $grey-90;
|
}
|
||||||
border-radius: 6px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: $grey-00;
|
|
||||||
height: $unit * 6;
|
|
||||||
display: block;
|
|
||||||
font-size: $font-regular;
|
|
||||||
padding: $unit;
|
|
||||||
text-align: right;
|
|
||||||
min-width: 100px;
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
.fields {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: $unit;
|
||||||
|
|
||||||
|
select {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Input {
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
border: none;
|
||||||
|
background-color: $grey-90;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: $grey-00;
|
||||||
|
height: $unit * 6;
|
||||||
|
display: block;
|
||||||
|
font-size: $font-regular;
|
||||||
|
padding: $unit;
|
||||||
|
text-align: right;
|
||||||
|
min-width: 100px;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,13 +5,36 @@ import { axData } from '~utils/axData'
|
||||||
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
|
interface ErrorMap {
|
||||||
|
[index: string]: string
|
||||||
|
axValue1: string
|
||||||
|
axValue2: string
|
||||||
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
axType: number
|
axType: number
|
||||||
currentSkills?: SimpleAxSkill[],
|
currentSkills?: SimpleAxSkill[],
|
||||||
|
sendValidity: (isValid: boolean) => void
|
||||||
sendValues: (primaryAxModifier: number, primaryAxValue: number, secondaryAxModifier: number, secondaryAxValue: number) => void
|
sendValues: (primaryAxModifier: number, primaryAxValue: number, secondaryAxModifier: number, secondaryAxValue: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const AXSelect = (props: Props) => {
|
const AXSelect = (props: Props) => {
|
||||||
|
// Set up form states and error handling
|
||||||
|
const [errors, setErrors] = useState<ErrorMap>({
|
||||||
|
axValue1: '',
|
||||||
|
axValue2: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const primaryErrorClasses = classNames({
|
||||||
|
'errors': true,
|
||||||
|
'visible': errors.axValue1.length > 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const secondaryErrorClasses = classNames({
|
||||||
|
'errors': true,
|
||||||
|
'visible': errors.axValue2.length > 0
|
||||||
|
})
|
||||||
|
|
||||||
// Refs
|
// Refs
|
||||||
const primaryAxModifierSelect = React.createRef<HTMLSelectElement>()
|
const primaryAxModifierSelect = React.createRef<HTMLSelectElement>()
|
||||||
const primaryAxValueInput = React.createRef<HTMLInputElement>()
|
const primaryAxValueInput = React.createRef<HTMLInputElement>()
|
||||||
|
|
@ -112,11 +135,44 @@ const AXSelect = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
|
function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
let newErrors = {...errors}
|
||||||
|
|
||||||
if (primaryAxValueInput.current == event.target) {
|
if (primaryAxValueInput.current == event.target) {
|
||||||
setPrimaryAxValue(parseFloat(event.target.value))
|
const primaryAxSkill = axData[props.axType - 1][primaryAxModifier]
|
||||||
|
const value = parseFloat(event.target.value)
|
||||||
|
|
||||||
|
if (value < primaryAxSkill.minValue) {
|
||||||
|
newErrors.axValue1 = `${primaryAxSkill.name.en} must be at least ${primaryAxSkill.minValue}${ (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''}`
|
||||||
|
} else if (value > primaryAxSkill.maxValue) {
|
||||||
|
newErrors.axValue1 = `${primaryAxSkill.name.en} cannot be greater than ${primaryAxSkill.maxValue}${ (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''}`
|
||||||
|
} else {
|
||||||
|
newErrors.axValue1 = ''
|
||||||
|
setPrimaryAxValue(parseFloat(event.target.value))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setSecondaryAxValue(parseFloat(event.target.value))
|
const primaryAxSkill = axData[props.axType - 1][primaryAxModifier]
|
||||||
|
const value = parseFloat(event.target.value)
|
||||||
|
|
||||||
|
if (primaryAxSkill.secondary) {
|
||||||
|
const secondaryAxSkill = primaryAxSkill.secondary.find(skill => skill.id == secondaryAxModifier)
|
||||||
|
|
||||||
|
if (secondaryAxSkill) {
|
||||||
|
if (value < secondaryAxSkill.minValue) {
|
||||||
|
newErrors.axValue2 = `${secondaryAxSkill.name.en} must be at least ${secondaryAxSkill.minValue}${ (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''}`
|
||||||
|
} else if (value > secondaryAxSkill.maxValue) {
|
||||||
|
newErrors.axValue2 = `${secondaryAxSkill.name.en} cannot be greater than ${secondaryAxSkill.maxValue}${ (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''}`
|
||||||
|
} else if (!secondaryAxSkill.suffix && value % 1 !== 0) {
|
||||||
|
newErrors.axValue2 = `${secondaryAxSkill.name.en} must be a whole number`
|
||||||
|
} else {
|
||||||
|
newErrors.axValue2 = ''
|
||||||
|
setSecondaryAxValue(parseFloat(event.target.value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
props.sendValidity(newErrors.axValue1 === '' && newErrors.axValue2 === '')
|
||||||
|
setErrors(newErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupInput(ax: AxSkill | undefined, element: HTMLInputElement) {
|
function setupInput(ax: AxSkill | undefined, element: HTMLInputElement) {
|
||||||
|
|
@ -144,13 +200,19 @@ const AXSelect = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<div className="AXSelect">
|
<div className="AXSelect">
|
||||||
<div className="AXSet">
|
<div className="AXSet">
|
||||||
<select key="ax1" defaultValue={ (props.currentSkills && props.currentSkills[0]) ? props.currentSkills[0].modifier : -1 } onChange={handleSelectChange} ref={primaryAxModifierSelect}>{ generateOptions(0) }</select>
|
<div className="fields">
|
||||||
<input defaultValue={ (props.currentSkills && props.currentSkills[0]) ? props.currentSkills[0].strength : 0 } className="Input" type="number" onChange={handleInputChange} ref={primaryAxValueInput} disabled />
|
<select key="ax1" defaultValue={ (props.currentSkills && props.currentSkills[0]) ? props.currentSkills[0].modifier : -1 } onChange={handleSelectChange} ref={primaryAxModifierSelect}>{ generateOptions(0) }</select>
|
||||||
|
<input defaultValue={ (props.currentSkills && props.currentSkills[0]) ? props.currentSkills[0].strength : 0 } className="Input" type="number" onChange={handleInputChange} ref={primaryAxValueInput} disabled />
|
||||||
|
</div>
|
||||||
|
<p className={primaryErrorClasses}>{errors.axValue1}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={secondarySetClasses}>
|
<div className={secondarySetClasses}>
|
||||||
<select key="ax2" defaultValue={ (props.currentSkills && props.currentSkills[1]) ? props.currentSkills[1].modifier : -1 } onChange={handleSelectChange} ref={secondaryAxModifierSelect}>{ generateOptions(1) }</select>
|
<div className="fields">
|
||||||
<input defaultValue={ (props.currentSkills && props.currentSkills[1]) ? props.currentSkills[1].strength : 0 } className="Input" type="number" onChange={handleInputChange} ref={secondaryAxValueInput} disabled />
|
<select key="ax2" defaultValue={ (props.currentSkills && props.currentSkills[1]) ? props.currentSkills[1].modifier : -1 } onChange={handleSelectChange} ref={secondaryAxModifierSelect}>{ generateOptions(1) }</select>
|
||||||
|
<input defaultValue={ (props.currentSkills && props.currentSkills[1]) ? props.currentSkills[1].strength : 0 } className="Input" type="number" onChange={handleInputChange} ref={secondaryAxValueInput} disabled />
|
||||||
|
</div>
|
||||||
|
<p className={secondaryErrorClasses}>{errors.axValue2}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ const WeaponModal = (props: Props) => {
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
const [formValid, setFormValid] = useState(false)
|
||||||
|
|
||||||
const [element, setElement] = useState(-1)
|
const [element, setElement] = useState(-1)
|
||||||
const [primaryAxModifier, setPrimaryAxModifier] = useState(-1)
|
const [primaryAxModifier, setPrimaryAxModifier] = useState(-1)
|
||||||
|
|
@ -64,6 +65,10 @@ const WeaponModal = (props: Props) => {
|
||||||
setSecondaryAxValue(secondaryAxValue)
|
setSecondaryAxValue(secondaryAxValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function receiveAxValidity(isValid: boolean) {
|
||||||
|
setFormValid(isValid)
|
||||||
|
}
|
||||||
|
|
||||||
function receiveElementValue(element: string) {
|
function receiveElementValue(element: string) {
|
||||||
setElement(parseInt(element))
|
setElement(parseInt(element))
|
||||||
}
|
}
|
||||||
|
|
@ -164,6 +169,7 @@ const WeaponModal = (props: Props) => {
|
||||||
<AXSelect
|
<AXSelect
|
||||||
axType={props.gridWeapon.object.ax}
|
axType={props.gridWeapon.object.ax}
|
||||||
currentSkills={props.gridWeapon.ax}
|
currentSkills={props.gridWeapon.ax}
|
||||||
|
sendValidity={receiveAxValidity}
|
||||||
sendValues={receiveAxValues}
|
sendValues={receiveAxValues}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -171,6 +177,7 @@ const WeaponModal = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function openChange(open: boolean) {
|
function openChange(open: boolean) {
|
||||||
|
setFormValid(false)
|
||||||
setOpen(open)
|
setOpen(open)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,7 +204,7 @@ const WeaponModal = (props: Props) => {
|
||||||
{ (props.gridWeapon.object.element == 0) ? elementSelect() : '' }
|
{ (props.gridWeapon.object.element == 0) ? elementSelect() : '' }
|
||||||
{ ([2, 3, 17, 24].includes(props.gridWeapon.object.series)) ? keySelect() : '' }
|
{ ([2, 3, 17, 24].includes(props.gridWeapon.object.series)) ? keySelect() : '' }
|
||||||
{ (props.gridWeapon.object.ax > 0) ? axSelect() : '' }
|
{ (props.gridWeapon.object.ax > 0) ? axSelect() : '' }
|
||||||
<Button click={updateWeapon}>Save Weapon</Button>
|
<Button click={updateWeapon} disabled={!formValid}>Save Weapon</Button>
|
||||||
</div>
|
</div>
|
||||||
</Dialog.Content>
|
</Dialog.Content>
|
||||||
<Dialog.Overlay className="Overlay" />
|
<Dialog.Overlay className="Overlay" />
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue