Implement AwakeningSelect
* Has modes for weapons and characters * Shows input when awakening is selected * Saves type and level to server * Redisplays type but level is broken
This commit is contained in:
parent
cf58e64caf
commit
c74ff41479
6 changed files with 253 additions and 3 deletions
31
components/AwakeningSelect/index.scss
Normal file
31
components/AwakeningSelect/index.scss
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
.AwakeningSelect .AwakeningSet {
|
||||
.errors {
|
||||
color: $error;
|
||||
display: none;
|
||||
padding: $unit 0;
|
||||
|
||||
&.visible {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.fields {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: $unit;
|
||||
width: 100%;
|
||||
|
||||
.SelectTrigger {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.Label {
|
||||
flex-grow: 0;
|
||||
|
||||
.Input {
|
||||
min-width: $unit * 12;
|
||||
width: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
183
components/AwakeningSelect/index.tsx
Normal file
183
components/AwakeningSelect/index.tsx
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
import React, { ForwardedRef, useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
import Input from '~components/Input'
|
||||
import Select from '~components/Select'
|
||||
import SelectItem from '~components/SelectItem'
|
||||
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { weaponAwakening, characterAwakening } from '~utils/awakening'
|
||||
import type { Awakening } from '~utils/awakening'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
object: 'character' | 'weapon'
|
||||
awakeningType?: number
|
||||
awakeningLevel?: number
|
||||
sendValues: (type: number, level: number) => void
|
||||
}
|
||||
|
||||
const AwakeningSelect = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const locale =
|
||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
// Refs
|
||||
const awakeningLevelInput = React.createRef<HTMLInputElement>()
|
||||
|
||||
// States
|
||||
const [awakeningType, setAwakeningType] = useState(-1)
|
||||
const [awakeningLevel, setAwakeningLevel] = useState(1)
|
||||
|
||||
const [maxValue, setMaxValue] = useState(1)
|
||||
|
||||
const [error, setError] = useState('')
|
||||
|
||||
// Classes
|
||||
const inputClasses = classNames({
|
||||
Bound: true,
|
||||
Hidden: awakeningType === -1,
|
||||
})
|
||||
|
||||
const errorClasses = classNames({
|
||||
errors: true,
|
||||
visible: error !== '',
|
||||
})
|
||||
|
||||
// Set max value based on object type
|
||||
useEffect(() => {
|
||||
if (props.object === 'character') setMaxValue(9)
|
||||
else if (props.object === 'weapon') setMaxValue(15)
|
||||
}, [props.object])
|
||||
|
||||
// Set default awakening and level based on object type
|
||||
useEffect(() => {
|
||||
let defaultAwakening = 0
|
||||
if (props.object === 'weapon') defaultAwakening = -1
|
||||
|
||||
setAwakeningType(
|
||||
props.awakeningType != undefined ? props.awakeningType : defaultAwakening
|
||||
)
|
||||
setAwakeningLevel(props.awakeningLevel ? props.awakeningLevel : 1)
|
||||
}, [props.object, props.awakeningType, props.awakeningLevel])
|
||||
|
||||
useEffect(() => {
|
||||
props.sendValues(awakeningType, awakeningLevel)
|
||||
}, [props.sendValues, awakeningType, awakeningLevel])
|
||||
|
||||
// Classes
|
||||
function changeOpen() {
|
||||
setOpen(!open)
|
||||
}
|
||||
|
||||
function generateOptions(object: 'character' | 'weapon') {
|
||||
let options: Awakening[] = []
|
||||
if (object === 'character') options = characterAwakening
|
||||
else if (object === 'weapon') options = weaponAwakening
|
||||
else return
|
||||
|
||||
let optionElements: React.ReactNode[] = options.map((awakening, i) => {
|
||||
return (
|
||||
<SelectItem key={i} value={awakening.id}>
|
||||
{awakening.name[locale]}
|
||||
</SelectItem>
|
||||
)
|
||||
})
|
||||
|
||||
if (object === 'weapon') {
|
||||
optionElements?.unshift(
|
||||
<SelectItem key={-1} value={-1}>
|
||||
{t('ax.no_skill')}
|
||||
</SelectItem>
|
||||
)
|
||||
}
|
||||
|
||||
return optionElements
|
||||
}
|
||||
|
||||
function handleSelectChange(rawValue: string) {
|
||||
const value = parseInt(rawValue)
|
||||
setAwakeningType(value)
|
||||
}
|
||||
|
||||
function handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const value = parseFloat(event.target.value)
|
||||
if (handleLevelError(value)) setAwakeningLevel(value)
|
||||
}
|
||||
|
||||
function handleLevelError(value: number) {
|
||||
let error = ''
|
||||
if (value < 1) {
|
||||
error = t('awakening.errors.value_too_low', {
|
||||
minValue: 1,
|
||||
})
|
||||
} else if (value > maxValue) {
|
||||
error = t('awakening.errors.value_too_high', {
|
||||
maxValue: maxValue,
|
||||
})
|
||||
} else if (!value || value <= 0) {
|
||||
error = t('awakening.errors.value_empty')
|
||||
} else {
|
||||
error = ''
|
||||
}
|
||||
|
||||
setError(error)
|
||||
|
||||
return error.length === 0
|
||||
}
|
||||
|
||||
const rangeString = (object: 'character' | 'weapon') => {
|
||||
let minValue = 1
|
||||
let maxValue = 1
|
||||
|
||||
if (object === 'weapon') {
|
||||
minValue = 1
|
||||
maxValue = 15
|
||||
} else if (object === 'character') {
|
||||
minValue = 1
|
||||
maxValue = 9
|
||||
} else return
|
||||
|
||||
return `${minValue}~${maxValue}`
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="AwakeningSelect">
|
||||
<div className="AwakeningSet">
|
||||
<div className="fields">
|
||||
<Select
|
||||
key="awakening_type"
|
||||
value={`${awakeningType}`}
|
||||
open={open}
|
||||
onValueChange={handleSelectChange}
|
||||
onClick={() => changeOpen()}
|
||||
triggerClass="modal"
|
||||
>
|
||||
{generateOptions(props.object)}
|
||||
</Select>
|
||||
|
||||
<Input
|
||||
value={awakeningLevel}
|
||||
className={inputClasses}
|
||||
type="number"
|
||||
placeholder={rangeString(props.object)}
|
||||
min={1}
|
||||
max={maxValue}
|
||||
step="1"
|
||||
onChange={handleInputChange}
|
||||
ref={awakeningLevelInput}
|
||||
/>
|
||||
</div>
|
||||
<p className={errorClasses}>{error}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AwakeningSelect
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
border-radius: 6px;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
padding: ($unit * 1.5) $unit-2x;
|
||||
padding: $unit-2x;
|
||||
width: 100%;
|
||||
|
||||
&.Bound {
|
||||
|
|
@ -15,6 +15,10 @@
|
|||
background-color: var(--input-bound-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&.Hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.InputError {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ const Select = (props: Props) => {
|
|||
if (props.onValueChange) props.onValueChange(newValue)
|
||||
}
|
||||
|
||||
console.log(value)
|
||||
return (
|
||||
<RadixSelect.Root
|
||||
value={value !== '' ? value : undefined}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { appState } from '~utils/appState'
|
|||
|
||||
import CrossIcon from '~public/icons/Cross.svg'
|
||||
import './index.scss'
|
||||
import AwakeningSelect from '~components/AwakeningSelect'
|
||||
|
||||
interface GridWeaponObject {
|
||||
weapon: {
|
||||
|
|
@ -27,6 +28,8 @@ interface GridWeaponObject {
|
|||
ax_modifier2?: number
|
||||
ax_strength1?: number
|
||||
ax_strength2?: number
|
||||
awakening_type?: number
|
||||
awakening_level?: Number
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,6 +59,9 @@ const WeaponModal = (props: Props) => {
|
|||
|
||||
const [element, setElement] = useState(-1)
|
||||
|
||||
const [awakeningType, setAwakeningType] = useState(-1)
|
||||
const [awakeningLevel, setAwakeningLevel] = useState(1)
|
||||
|
||||
const [primaryAxModifier, setPrimaryAxModifier] = useState(-1)
|
||||
const [secondaryAxModifier, setSecondaryAxModifier] = useState(-1)
|
||||
const [primaryAxValue, setPrimaryAxValue] = useState(0.0)
|
||||
|
|
@ -99,6 +105,11 @@ const WeaponModal = (props: Props) => {
|
|||
setFormValid(isValid)
|
||||
}
|
||||
|
||||
function receiveAwakeningValues(type: number, level: number) {
|
||||
setAwakeningType(type)
|
||||
setAwakeningLevel(level)
|
||||
}
|
||||
|
||||
function receiveElementValue(element: string) {
|
||||
setElement(parseInt(element))
|
||||
}
|
||||
|
|
@ -125,6 +136,11 @@ const WeaponModal = (props: Props) => {
|
|||
object.weapon.ax_strength2 = secondaryAxValue
|
||||
}
|
||||
|
||||
if (props.gridWeapon.object.awakening) {
|
||||
object.weapon.awakening_type = awakeningType
|
||||
object.weapon.awakening_level = awakeningLevel
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +153,8 @@ const WeaponModal = (props: Props) => {
|
|||
}
|
||||
|
||||
function processResult(response: AxiosResponse) {
|
||||
const gridWeapon: GridWeapon = response.data.grid_weapon
|
||||
console.log(response)
|
||||
const gridWeapon: GridWeapon = response.data
|
||||
|
||||
if (gridWeapon.mainhand) appState.grid.weapons.mainWeapon = gridWeapon
|
||||
else appState.grid.weapons.allWeapons[gridWeapon.position] = gridWeapon
|
||||
|
|
@ -221,6 +238,20 @@ const WeaponModal = (props: Props) => {
|
|||
)
|
||||
}
|
||||
|
||||
const awakeningSelect = () => {
|
||||
return (
|
||||
<section>
|
||||
<h3>{t('modals.weapon.subtitles.awakening')}</h3>
|
||||
<AwakeningSelect
|
||||
object="weapon"
|
||||
awakeningType={props.gridWeapon.awakening?.type}
|
||||
awakeningLevel={props.gridWeapon.awakening?.level}
|
||||
sendValues={receiveAwakeningValues}
|
||||
/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function openChange(open: boolean) {
|
||||
setFormValid(false)
|
||||
setOpen(open)
|
||||
|
|
@ -256,6 +287,7 @@ const WeaponModal = (props: Props) => {
|
|||
? keySelect()
|
||||
: ''}
|
||||
{props.gridWeapon.object.ax > 0 ? axSelect() : ''}
|
||||
{props.gridWeapon.awakening ? awakeningSelect() : ''}
|
||||
<Button
|
||||
contained={true}
|
||||
onClick={updateWeapon}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ const WeaponUnit = (props: Props) => {
|
|||
|
||||
return (
|
||||
weapon.ax > 0 ||
|
||||
weapon.awakening ||
|
||||
(weapon.series && [2, 3, 17, 22, 24].includes(weapon.series))
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue