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;
|
border-radius: 6px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: block;
|
display: block;
|
||||||
padding: ($unit * 1.5) $unit-2x;
|
padding: $unit-2x;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&.Bound {
|
&.Bound {
|
||||||
|
|
@ -15,6 +15,10 @@
|
||||||
background-color: var(--input-bound-bg-hover);
|
background-color: var(--input-bound-bg-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.Hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.InputError {
|
.InputError {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ const Select = (props: Props) => {
|
||||||
if (props.onValueChange) props.onValueChange(newValue)
|
if (props.onValueChange) props.onValueChange(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(value)
|
|
||||||
return (
|
return (
|
||||||
<RadixSelect.Root
|
<RadixSelect.Root
|
||||||
value={value !== '' ? value : undefined}
|
value={value !== '' ? value : undefined}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import { appState } from '~utils/appState'
|
||||||
|
|
||||||
import CrossIcon from '~public/icons/Cross.svg'
|
import CrossIcon from '~public/icons/Cross.svg'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
import AwakeningSelect from '~components/AwakeningSelect'
|
||||||
|
|
||||||
interface GridWeaponObject {
|
interface GridWeaponObject {
|
||||||
weapon: {
|
weapon: {
|
||||||
|
|
@ -27,6 +28,8 @@ interface GridWeaponObject {
|
||||||
ax_modifier2?: number
|
ax_modifier2?: number
|
||||||
ax_strength1?: number
|
ax_strength1?: number
|
||||||
ax_strength2?: number
|
ax_strength2?: number
|
||||||
|
awakening_type?: number
|
||||||
|
awakening_level?: Number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,6 +59,9 @@ const WeaponModal = (props: Props) => {
|
||||||
|
|
||||||
const [element, setElement] = useState(-1)
|
const [element, setElement] = useState(-1)
|
||||||
|
|
||||||
|
const [awakeningType, setAwakeningType] = useState(-1)
|
||||||
|
const [awakeningLevel, setAwakeningLevel] = useState(1)
|
||||||
|
|
||||||
const [primaryAxModifier, setPrimaryAxModifier] = useState(-1)
|
const [primaryAxModifier, setPrimaryAxModifier] = useState(-1)
|
||||||
const [secondaryAxModifier, setSecondaryAxModifier] = useState(-1)
|
const [secondaryAxModifier, setSecondaryAxModifier] = useState(-1)
|
||||||
const [primaryAxValue, setPrimaryAxValue] = useState(0.0)
|
const [primaryAxValue, setPrimaryAxValue] = useState(0.0)
|
||||||
|
|
@ -99,6 +105,11 @@ const WeaponModal = (props: Props) => {
|
||||||
setFormValid(isValid)
|
setFormValid(isValid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function receiveAwakeningValues(type: number, level: number) {
|
||||||
|
setAwakeningType(type)
|
||||||
|
setAwakeningLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
function receiveElementValue(element: string) {
|
function receiveElementValue(element: string) {
|
||||||
setElement(parseInt(element))
|
setElement(parseInt(element))
|
||||||
}
|
}
|
||||||
|
|
@ -125,6 +136,11 @@ const WeaponModal = (props: Props) => {
|
||||||
object.weapon.ax_strength2 = secondaryAxValue
|
object.weapon.ax_strength2 = secondaryAxValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (props.gridWeapon.object.awakening) {
|
||||||
|
object.weapon.awakening_type = awakeningType
|
||||||
|
object.weapon.awakening_level = awakeningLevel
|
||||||
|
}
|
||||||
|
|
||||||
return object
|
return object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,7 +153,8 @@ const WeaponModal = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function processResult(response: AxiosResponse) {
|
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
|
if (gridWeapon.mainhand) appState.grid.weapons.mainWeapon = gridWeapon
|
||||||
else appState.grid.weapons.allWeapons[gridWeapon.position] = 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) {
|
function openChange(open: boolean) {
|
||||||
setFormValid(false)
|
setFormValid(false)
|
||||||
setOpen(open)
|
setOpen(open)
|
||||||
|
|
@ -256,6 +287,7 @@ const WeaponModal = (props: Props) => {
|
||||||
? keySelect()
|
? keySelect()
|
||||||
: ''}
|
: ''}
|
||||||
{props.gridWeapon.object.ax > 0 ? axSelect() : ''}
|
{props.gridWeapon.object.ax > 0 ? axSelect() : ''}
|
||||||
|
{props.gridWeapon.awakening ? awakeningSelect() : ''}
|
||||||
<Button
|
<Button
|
||||||
contained={true}
|
contained={true}
|
||||||
onClick={updateWeapon}
|
onClick={updateWeapon}
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ const WeaponUnit = (props: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
weapon.ax > 0 ||
|
weapon.ax > 0 ||
|
||||||
|
weapon.awakening ||
|
||||||
(weapon.series && [2, 3, 17, 22, 24].includes(weapon.series))
|
(weapon.series && [2, 3, 17, 22, 24].includes(weapon.series))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue