import React, { useEffect, useState } from 'react' import { useRouter } from 'next/router' import { useTranslation } from 'next-i18next' import classNames from 'classnames' import { axData } from '~utils/axData' import './index.scss' interface ErrorMap { [index: string]: string axValue1: string axValue2: string } interface Props { axType: number currentSkills?: SimpleAxSkill[] sendValidity: (isValid: boolean) => void sendValues: ( primaryAxModifier: number, primaryAxValue: number, secondaryAxModifier: number, secondaryAxValue: number ) => void } const AXSelect = (props: Props) => { const router = useRouter() const locale = router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' const { t } = useTranslation('common') // Set up form states and error handling const [errors, setErrors] = useState({ axValue1: '', axValue2: '', }) const primaryErrorClasses = classNames({ errors: true, visible: errors.axValue1.length > 0, }) const secondaryErrorClasses = classNames({ errors: true, visible: errors.axValue2.length > 0, }) // Refs const primaryAxModifierSelect = React.createRef() const primaryAxValueInput = React.createRef() const secondaryAxModifierSelect = React.createRef() const secondaryAxValueInput = React.createRef() // States const [primaryAxModifier, setPrimaryAxModifier] = useState(-1) const [secondaryAxModifier, setSecondaryAxModifier] = useState(-1) const [primaryAxValue, setPrimaryAxValue] = useState(0.0) const [secondaryAxValue, setSecondaryAxValue] = useState(0.0) useEffect(() => { if (props.currentSkills && props.currentSkills[0]) { if (props.currentSkills[0].modifier != null) setPrimaryAxModifier(props.currentSkills[0].modifier) setPrimaryAxValue(props.currentSkills[0].strength) } if (props.currentSkills && props.currentSkills[1]) { if (props.currentSkills[1].modifier != null) setSecondaryAxModifier(props.currentSkills[1].modifier) setSecondaryAxValue(props.currentSkills[1].strength) } }, [props.currentSkills]) useEffect(() => { props.sendValues( primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue ) }, [ props, primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue, ]) useEffect(() => { props.sendValidity( primaryAxValue > 0 && errors.axValue1 === '' && errors.axValue2 === '' ) }, [props, primaryAxValue, errors]) // Classes const secondarySetClasses = classNames({ AXSet: true, hidden: primaryAxModifier < 0, }) function generateOptions(modifierSet: number) { const axOptions = axData[props.axType - 1] let axOptionElements: React.ReactNode[] = [] if (modifierSet == 0) { axOptionElements = axOptions.map((ax, i) => { return ( ) }) } else { // If we are loading data from the server, state doesn't set before render, // so our defaultValue is undefined. let modifier = -1 if (primaryAxModifier >= 0) modifier = primaryAxModifier else if (props.currentSkills) modifier = props.currentSkills[0].modifier if (modifier >= 0 && axOptions[modifier]) { const primarySkill = axOptions[modifier] if (primarySkill.secondary) { const secondaryAxOptions = primarySkill.secondary axOptionElements = secondaryAxOptions.map((ax, i) => { return ( ) }) } } } axOptionElements?.unshift( ) return axOptionElements } function handleSelectChange(event: React.ChangeEvent) { const value = parseInt(event.target.value) if (primaryAxModifierSelect.current == event.target) { setPrimaryAxModifier(value) if ( primaryAxValueInput.current && secondaryAxModifierSelect.current && secondaryAxValueInput.current ) { setupInput(axData[props.axType - 1][value], primaryAxValueInput.current) secondaryAxModifierSelect.current.value = '-1' secondaryAxValueInput.current.value = '' } } else { setSecondaryAxModifier(value) const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] const currentAxSkill = primaryAxSkill.secondary ? primaryAxSkill.secondary.find((skill) => skill.id == value) : undefined if (secondaryAxValueInput.current) setupInput(currentAxSkill, secondaryAxValueInput.current) } } function handleInputChange(event: React.ChangeEvent) { const value = parseFloat(event.target.value) let newErrors = { ...errors } if (primaryAxValueInput.current == event.target) { if (handlePrimaryErrors(value)) setPrimaryAxValue(value) } else { if (handleSecondaryErrors(value)) setSecondaryAxValue(value) } } function handlePrimaryErrors(value: number) { const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] let newErrors = { ...errors } if (value < primaryAxSkill.minValue) { newErrors.axValue1 = t('ax.errors.value_too_low', { name: primaryAxSkill.name[locale], minValue: primaryAxSkill.minValue, suffix: primaryAxSkill.suffix ? primaryAxSkill.suffix : '', }) } else if (value > primaryAxSkill.maxValue) { newErrors.axValue1 = t('ax.errors.value_too_high', { name: primaryAxSkill.name[locale], maxValue: primaryAxSkill.minValue, suffix: primaryAxSkill.suffix ? primaryAxSkill.suffix : '', }) } else if (!value || value <= 0) { newErrors.axValue1 = t('ax.errors.value_empty', { name: primaryAxSkill.name[locale], }) } else { newErrors.axValue1 = '' } setErrors(newErrors) return newErrors.axValue1.length === 0 } function handleSecondaryErrors(value: number) { const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] let newErrors = { ...errors } if (primaryAxSkill.secondary) { const secondaryAxSkill = primaryAxSkill.secondary.find( (skill) => skill.id == secondaryAxModifier ) if (secondaryAxSkill) { if (value < secondaryAxSkill.minValue) { newErrors.axValue2 = t('ax.errors.value_too_low', { name: secondaryAxSkill.name[locale], minValue: secondaryAxSkill.minValue, suffix: secondaryAxSkill.suffix ? secondaryAxSkill.suffix : '', }) } else if (value > secondaryAxSkill.maxValue) { newErrors.axValue2 = t('ax.errors.value_too_high', { name: secondaryAxSkill.name[locale], maxValue: secondaryAxSkill.minValue, suffix: secondaryAxSkill.suffix ? secondaryAxSkill.suffix : '', }) } else if (!secondaryAxSkill.suffix && value % 1 !== 0) { newErrors.axValue2 = t('ax.errors.value_not_whole', { name: secondaryAxSkill.name[locale], }) } else if (primaryAxValue <= 0) { newErrors.axValue1 = t('ax.errors.value_empty', { name: primaryAxSkill.name[locale], }) } else { newErrors.axValue2 = '' } } } setErrors(newErrors) return newErrors.axValue2.length === 0 } function setupInput(ax: AxSkill | undefined, element: HTMLInputElement) { if (ax) { const rangeString = `${ax.minValue}~${ax.maxValue}${ax.suffix || ''}` element.disabled = false element.placeholder = rangeString element.min = `${ax.minValue}` element.max = `${ax.maxValue}` element.step = ax.suffix ? '0.5' : '1' } else { if (primaryAxValueInput.current && secondaryAxValueInput.current) { if (primaryAxValueInput.current == element) { primaryAxValueInput.current.disabled = true primaryAxValueInput.current.placeholder = '' } secondaryAxValueInput.current.disabled = true secondaryAxValueInput.current.placeholder = '' } } } return (

{errors.axValue1}

{errors.axValue2}

) } export default AXSelect