'use client' import React, { useEffect, useState } from 'react' import { getCookie } from 'cookies-next' import { useTranslations } from 'next-intl' import Input from '~components/common/Input' import Select from '~components/common/Select' import SelectItem from '~components/common/SelectItem' import classNames from 'classnames' import ax from '~data/ax' import styles from './index.module.scss' interface ErrorMap { [index: string]: string axValue1: string axValue2: string } interface Props { axType: number currentSkills?: SimpleAxSkill[] onOpenChange: (index: 1 | 2, open: boolean) => void sendValidity: (isValid: boolean) => void sendValues: ( primaryAxModifier: number, primaryAxValue: number, secondaryAxModifier: number, secondaryAxValue: number ) => void } const AXSelect = (props: Props) => { const locale = (getCookie('NEXT_LOCALE') as string) || 'en' const t = useTranslations('common') const [openAX1, setOpenAX1] = useState(false) const [openAX2, setOpenAX2] = useState(false) // Set up form states and error handling const [errors, setErrors] = useState({ axValue1: '', axValue2: '', }) const inputClasses = classNames({ fullHeight: true, range: true, }) const primaryErrorClasses = classNames({ [styles.errors]: true, [styles.visible]: errors.axValue1.length > 0, }) const secondaryErrorClasses = classNames({ [styles.errors]: true, [styles.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( props.currentSkills ? props.currentSkills[0].strength : 0.0 ) const [secondaryAxValue, setSecondaryAxValue] = useState( props.currentSkills ? props.currentSkills[1].strength : 0.0 ) useEffect(() => { setupAx1() setupAx2() }, [props.currentSkills]) useEffect(() => { props.sendValues( primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue ) }, [ props, primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue, ]) useEffect(() => { if ( props.currentSkills && props.currentSkills[0].modifier != null && props.currentSkills[0].modifier >= 0 ) { setPrimaryAxModifier(props.currentSkills[0].modifier) setPrimaryAxValue(props.currentSkills[0].strength) } else setPrimaryAxModifier(-1) }, [props.currentSkills, setPrimaryAxModifier]) useEffect(() => { if (props.currentSkills && props.currentSkills[1].modifier) { setSecondaryAxModifier(props.currentSkills[1].modifier) setSecondaryAxValue(props.currentSkills[1].strength) } else { setSecondaryAxModifier(-1) } }, [props.currentSkills, setSecondaryAxModifier]) useEffect(() => { let noErrors = false if (errors.axValue1 === '' && errors.axValue2 === '') { if (primaryAxModifier === -1 && secondaryAxModifier === -1) noErrors = true else if ( primaryAxModifier >= 0 && primaryAxValue > 0 && secondaryAxModifier === -1 ) noErrors = true else if ( primaryAxModifier >= 0 && primaryAxValue > 0 && secondaryAxModifier >= 0 && secondaryAxValue > 0 ) noErrors = true } props.sendValidity(noErrors) }, [ primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue, errors, ]) // Classes const secondarySetClasses = classNames({ [styles.set]: true, [styles.hidden]: primaryAxModifier < 0 || primaryAxModifier === 18 || primaryAxModifier === 19, }) function setupAx1() { if ( props.currentSkills && props.currentSkills[0] && // Should this be > -1 or != null props.currentSkills[0].modifier != null ) { setPrimaryAxModifier(props.currentSkills[0].modifier) setPrimaryAxValue(props.currentSkills[0].strength) if (props.currentSkills[0].modifier > -1 && primaryAxValueInput.current) { const modifier = props.currentSkills[0].modifier const axSkill = ax[props.axType - 1][modifier] setupInput(axSkill, primaryAxValueInput.current) } } } function setupAx2() { if ( props.currentSkills && props.currentSkills[1] && // Should this be > -1 or != null props.currentSkills[1].modifier != null ) { const firstSkill = props.currentSkills[0] const primaryAxSkill = ax[props.axType - 1][firstSkill.modifier] const secondaryAxSkill = findSecondaryAxSkill( primaryAxSkill, props.currentSkills[1] ) if ( props.currentSkills[1].modifier > -1 && secondaryAxValueInput.current ) { setupInput(secondaryAxSkill, secondaryAxValueInput.current) } } } function findSecondaryAxSkill( axSkill: ItemSkill | undefined, skillAtIndex: SimpleAxSkill ) { if (axSkill) return axSkill.secondary ? axSkill.secondary.find((skill) => skill.id === skillAtIndex.modifier) : undefined } function openSelect(index: 1 | 2) { if (index === 1) { setOpenAX1(!openAX1) setOpenAX2(false) props.onOpenChange(1, !openAX1) props.onOpenChange(2, false) } else if (index === 2) { setOpenAX2(!openAX2) setOpenAX1(false) props.onOpenChange(2, !openAX2) props.onOpenChange(1, false) } } function onClose(index: 1 | 2) { props.onOpenChange(index, false) } function generateOptions(modifierSet: number) { const axOptions = ax[props.axType - 1] let axOptionElements: React.ReactNode[] = [] if (modifierSet == 0) { axOptionElements = axOptions.map((ax, i) => { return ( {ax.name[locale]} ) }) } 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 ( {ax.name[locale]} ) }) } } } axOptionElements?.unshift( {t('ax.no_skill')} ) return axOptionElements } function handleAX1SelectChange(rawValue: string) { const value = parseInt(rawValue) setPrimaryAxModifier(value) if ( primaryAxValueInput.current && secondaryAxModifierSelect.current && secondaryAxValueInput.current ) { setupInput( ax[props.axType - 1].find((ax) => ax.id === value), primaryAxValueInput.current ) setPrimaryAxValue(0) // Reset the secondary AX modifier, reset the AX value and hide the input setSecondaryAxModifier(-1) setSecondaryAxValue(0) // secondaryAxValueInput.current.className = 'Input Contained' secondaryAxValueInput.current.value = '' } } function handleAX2SelectChange(rawValue: string) { const value = parseInt(rawValue) setSecondaryAxModifier(value) const primaryAxSkill = ax[props.axType - 1][primaryAxModifier] const currentAxSkill = primaryAxSkill.secondary ? primaryAxSkill.secondary.find((skill) => skill.id == value) : undefined if (secondaryAxValueInput.current) { setupInput(currentAxSkill, secondaryAxValueInput.current) setSecondaryAxValue(0) secondaryAxValueInput.current.value = '' } } 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 = ax[props.axType - 1].find( (ax) => ax.id === primaryAxModifier ) let newErrors = { ...errors } if (primaryAxSkill && 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 (primaryAxSkill && value > primaryAxSkill.maxValue) { newErrors.axValue1 = t('ax.errors.value_too_high', { name: primaryAxSkill.name[locale], maxValue: primaryAxSkill.maxValue, 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 = ax[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.maxValue, 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: ItemSkill | undefined, element: HTMLInputElement) { console.log(ax) if (ax) { const rangeString = `${ax.minValue}~${ax.maxValue}${ax.suffix || ''}` // element.className = 'Input Bound Visible' 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.className = 'Input Contained' primaryAxValueInput.current.disabled = true primaryAxValueInput.current.placeholder = '' } // secondaryAxValueInput.current.className = 'Input Contained' secondaryAxValueInput.current.disabled = true secondaryAxValueInput.current.placeholder = '' } } } return (

{errors.axValue1}

{errors.axValue2}

) } export default AXSelect