Merge pull request #49 from jedmund/weapon-mods

Weapon Mod updates
This commit is contained in:
Justin Edmund 2022-12-24 05:03:58 -08:00 committed by GitHub
commit 492c2b40ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 1343 additions and 214 deletions

2
.gitignore vendored
View file

@ -51,6 +51,8 @@ public/images/weapon*
public/images/summon*
public/images/chara*
public/images/job*
public/images/awakening*
public/images/ax*
# Typescript v1 declaration files
typings/

View 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;
}
}
}
}

View file

@ -0,0 +1,192 @@
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
sendValidity: (isValid: boolean) => void
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])
// Send awakening type and level when changed
useEffect(() => {
props.sendValues(awakeningType, awakeningLevel)
}, [props.sendValues, awakeningType, awakeningLevel])
// Send validity of form when awakening level changes
useEffect(() => {
props.sendValidity(awakeningLevel > 0 && error === '')
}, [props.sendValidity, awakeningLevel, error])
// 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('awakening.no_type')}
</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 % 1 != 0) {
error = t('awakening.errors.value_not_whole')
} 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

View file

@ -23,25 +23,24 @@
flex-direction: row;
gap: $unit;
select {
.SelectTrigger {
flex-grow: 1;
margin: 0;
}
.Input {
input {
-webkit-font-smoothing: antialiased;
border: none;
background-color: $grey-90;
border-radius: 6px;
border-radius: $input-corner;
box-sizing: border-box;
color: $grey-15;
height: $unit * 6;
display: block;
font-size: $font-regular;
padding: $unit;
display: none;
text-align: right;
min-width: 100px;
min-width: $unit-14x;
width: 100px;
&.Visible {
display: block;
}
}
}
}

View file

@ -1,7 +1,10 @@
import React, { useEffect, useState } from 'react'
import React, { ForwardedRef, useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import Select from '~components/Select'
import SelectItem from '~components/SelectItem'
import classNames from 'classnames'
import { axData } from '~utils/axData'
@ -32,6 +35,9 @@ const AXSelect = (props: Props) => {
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
const { t } = useTranslation('common')
const [openAX1, setOpenAX1] = useState(false)
const [openAX2, setOpenAX2] = useState(false)
// Set up form states and error handling
const [errors, setErrors] = useState<ErrorMap>({
axValue1: '',
@ -49,9 +55,9 @@ const AXSelect = (props: Props) => {
})
// Refs
const primaryAxModifierSelect = React.createRef<HTMLSelectElement>()
const primaryAxModifierSelect = React.createRef<HTMLButtonElement>()
const primaryAxValueInput = React.createRef<HTMLInputElement>()
const secondaryAxModifierSelect = React.createRef<HTMLSelectElement>()
const secondaryAxModifierSelect = React.createRef<HTMLButtonElement>()
const secondaryAxValueInput = React.createRef<HTMLInputElement>()
// States
@ -61,19 +67,8 @@ const AXSelect = (props: Props) => {
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)
}
setupAx1()
setupAx2()
}, [props.currentSkills])
useEffect(() => {
@ -92,10 +87,68 @@ const AXSelect = (props: Props) => {
])
useEffect(() => {
props.sendValidity(
primaryAxValue > 0 && errors.axValue1 === '' && errors.axValue2 === ''
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(() => {
console.log(
primaryAxModifier,
primaryAxValue,
secondaryAxModifier,
secondaryAxValue
)
}, [props, primaryAxValue, errors])
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
else
console.log(
primaryAxModifier >= 0,
primaryAxValue > 0,
secondaryAxModifier >= 0,
secondaryAxValue > 0
)
}
props.sendValidity(noErrors)
}, [
primaryAxModifier,
primaryAxValue,
secondaryAxModifier,
secondaryAxValue,
errors,
])
// Classes
const secondarySetClasses = classNames({
@ -103,6 +156,62 @@ const AXSelect = (props: Props) => {
hidden: primaryAxModifier < 0,
})
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 = axData[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 = axData[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: AxSkill | undefined,
skillAtIndex: SimpleAxSkill
) {
if (axSkill)
return axSkill.secondary
? axSkill.secondary.find((skill) => skill.id === skillAtIndex.modifier)
: undefined
}
function openSelect(ref: ForwardedRef<HTMLButtonElement>) {
if (ref === primaryAxModifierSelect) setOpenAX1(!openAX1)
if (ref === secondaryAxModifierSelect) setOpenAX2(!openAX2)
}
function generateOptions(modifierSet: number) {
const axOptions = axData[props.axType - 1]
@ -110,9 +219,9 @@ const AXSelect = (props: Props) => {
if (modifierSet == 0) {
axOptionElements = axOptions.map((ax, i) => {
return (
<option key={i} value={ax.id}>
<SelectItem key={i} value={ax.id}>
{ax.name[locale]}
</option>
</SelectItem>
)
})
} else {
@ -129,9 +238,9 @@ const AXSelect = (props: Props) => {
const secondaryAxOptions = primarySkill.secondary
axOptionElements = secondaryAxOptions.map((ax, i) => {
return (
<option key={i} value={ax.id}>
<SelectItem key={i} value={ax.id}>
{ax.name[locale]}
</option>
</SelectItem>
)
})
}
@ -139,39 +248,47 @@ const AXSelect = (props: Props) => {
}
axOptionElements?.unshift(
<option key={-1} value={-1}>
<SelectItem key={-1} value={-1}>
{t('ax.no_skill')}
</option>
</SelectItem>
)
return axOptionElements
}
function handleSelectChange(event: React.ChangeEvent<HTMLSelectElement>) {
const value = parseInt(event.target.value)
function handleAX1SelectChange(rawValue: string) {
const value = parseInt(rawValue)
setPrimaryAxModifier(value)
if (primaryAxModifierSelect.current == event.target) {
setPrimaryAxModifier(value)
if (
primaryAxValueInput.current &&
secondaryAxModifierSelect.current &&
secondaryAxValueInput.current
) {
setupInput(axData[props.axType - 1][value], primaryAxValueInput.current)
setPrimaryAxValue(0)
primaryAxValueInput.current.value = ''
if (
primaryAxValueInput.current &&
secondaryAxModifierSelect.current &&
secondaryAxValueInput.current
) {
setupInput(axData[props.axType - 1][value], primaryAxValueInput.current)
// 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 = ''
}
}
secondaryAxModifierSelect.current.value = '-1'
secondaryAxValueInput.current.value = ''
}
} else {
setSecondaryAxModifier(value)
function handleAX2SelectChange(rawValue: string) {
const value = parseInt(rawValue)
setSecondaryAxModifier(value)
const primaryAxSkill = axData[props.axType - 1][primaryAxModifier]
const currentAxSkill = primaryAxSkill.secondary
? primaryAxSkill.secondary.find((skill) => skill.id == value)
: undefined
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)
if (secondaryAxValueInput.current) {
setupInput(currentAxSkill, secondaryAxValueInput.current)
setSecondaryAxValue(0)
secondaryAxValueInput.current.value = ''
}
}
@ -199,7 +316,7 @@ const AXSelect = (props: Props) => {
} else if (value > primaryAxSkill.maxValue) {
newErrors.axValue1 = t('ax.errors.value_too_high', {
name: primaryAxSkill.name[locale],
maxValue: primaryAxSkill.minValue,
maxValue: primaryAxSkill.maxValue,
suffix: primaryAxSkill.suffix ? primaryAxSkill.suffix : '',
})
} else if (!value || value <= 0) {
@ -234,7 +351,7 @@ const AXSelect = (props: Props) => {
} else if (value > secondaryAxSkill.maxValue) {
newErrors.axValue2 = t('ax.errors.value_too_high', {
name: secondaryAxSkill.name[locale],
maxValue: secondaryAxSkill.minValue,
maxValue: secondaryAxSkill.maxValue,
suffix: secondaryAxSkill.suffix ? secondaryAxSkill.suffix : '',
})
} else if (!secondaryAxSkill.suffix && value % 1 !== 0) {
@ -260,6 +377,7 @@ const AXSelect = (props: Props) => {
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}`
@ -268,10 +386,12 @@ const AXSelect = (props: Props) => {
} 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 = ''
}
@ -282,29 +402,26 @@ const AXSelect = (props: Props) => {
<div className="AXSelect">
<div className="AXSet">
<div className="fields">
<select
<Select
key="ax1"
defaultValue={
props.currentSkills && props.currentSkills[0]
? props.currentSkills[0].modifier
: -1
}
onChange={handleSelectChange}
ref={primaryAxModifierSelect}
value={`${primaryAxModifier}`}
open={openAX1}
onValueChange={handleAX1SelectChange}
onClick={() => openSelect(primaryAxModifierSelect)}
triggerClass="modal"
>
{generateOptions(0)}
</select>
</Select>
<input
defaultValue={
props.currentSkills && props.currentSkills[0]
? props.currentSkills[0].strength
: 0
}
className="Input"
type="number"
onChange={handleInputChange}
ref={primaryAxValueInput}
disabled={primaryAxValue != 0}
/>
</div>
<p className={primaryErrorClasses}>{errors.axValue1}</p>
@ -312,29 +429,26 @@ const AXSelect = (props: Props) => {
<div className={secondarySetClasses}>
<div className="fields">
<select
<Select
key="ax2"
defaultValue={
props.currentSkills && props.currentSkills[1]
? props.currentSkills[1].modifier
: -1
}
onChange={handleSelectChange}
value={`${secondaryAxModifier}`}
open={openAX2}
onValueChange={handleAX2SelectChange}
onClick={() => openSelect(secondaryAxModifierSelect)}
triggerClass="modal"
ref={secondaryAxModifierSelect}
>
{generateOptions(1)}
</select>
</Select>
<input
defaultValue={
props.currentSkills && props.currentSkills[1]
? props.currentSkills[1].strength
: 0
}
className="Input"
type="number"
onChange={handleInputChange}
ref={secondaryAxValueInput}
disabled={secondaryAxValue != 0}
/>
</div>
<p className={secondaryErrorClasses}>{errors.axValue2}</p>

View file

@ -60,6 +60,17 @@
}
}
&:disabled {
background-color: var(--button-bg-disabled);
color: var(--button-text-disabled);
&:hover {
background-color: var(--button-bg-disabled);
color: var(--button-text-disabled);
cursor: default;
}
}
&.medium {
height: $unit * 5.5;
padding: ($unit * 1.5) $unit-2x;

View file

@ -1,7 +1,8 @@
.ToggleGroup {
$height: 36px;
border: 1px solid rgba(0, 0, 0, 0.14);
background-color: var(--toggle-bg);
border: 1px solid var(--toggle-stroke);
border-radius: $height;
display: flex;
height: $height;
@ -9,10 +10,10 @@
padding: calc($unit / 2);
.ToggleItem {
background: $grey-100;
background: var(--toggle-bg);
border: none;
border-radius: 18px;
color: $grey-50;
color: var(--input-secondary);
flex-grow: 1;
font-size: $font-regular;
padding: ($unit) $unit * 2;
@ -32,33 +33,33 @@
color: $grey-15;
&.fire {
background: $fire-bg-20;
color: $fire-text-10;
background: var(--fire-bg);
color: var(--fire-text);
}
&.water {
background: $water-bg-20;
color: $water-text-10;
background: var(--water-bg);
color: var(--water-text);
}
&.earth {
background: $earth-bg-20;
color: $earth-text-10;
background: var(--earth-bg);
color: var(--earth-text);
}
&.wind {
background: $wind-bg-20;
color: $wind-text-10;
background: var(--wind-bg);
color: var(--wind-text);
}
&.dark {
background: $dark-bg-10;
color: $dark-text-10;
background: var(--dark-bg);
color: var(--dark-text);
}
&.light {
background: $light-bg-20;
color: $light-text-10;
background: var(--light-bg);
color: var(--light-text);
}
}
}

View file

@ -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 {

View file

@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react'
import classNames from 'classnames'
import React from 'react'
import './index.scss'
interface Props
@ -11,12 +12,26 @@ interface Props
label?: string
}
const Input = React.forwardRef<HTMLInputElement, Props>(function input(
const Input = React.forwardRef<HTMLInputElement, Props>(function Input(
props: Props,
forwardedRef
) {
// States
const [inputValue, setInputValue] = useState('')
// Classes
const classes = classNames({ Input: true }, props.className)
const { value, ...inputProps } = props
const { defaultValue, ...inputProps } = props
// Change value when prop updates
useEffect(() => {
if (props.value) setInputValue(`${props.value}`)
}, [props.value])
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
setInputValue(event.target.value)
if (props.onChange) props.onChange(event)
}
return (
<label className="Label" htmlFor={props.name}>
@ -24,8 +39,9 @@ const Input = React.forwardRef<HTMLInputElement, Props>(function input(
{...inputProps}
autoComplete="off"
className={classes}
defaultValue={props.value || ''}
value={inputValue}
ref={forwardedRef}
onChange={handleChange}
formNoValidate
/>
{props.label}

View file

@ -47,8 +47,6 @@ const Party = (props: Props) => {
},
}
console.log(body)
return await api.endpoints.parties.create(body)
}

View file

@ -15,8 +15,8 @@
&.fire {
.Segment input:checked + label {
background: $fire-bg-10;
color: $fire-text-10;
background: var(--fire-bg);
color: var(--fire-text);
}
.Segment:hover label {
@ -27,8 +27,8 @@
&.water {
.Segment input:checked + label {
background: $water-bg-10;
color: $water-text-10;
background: var(--water-bg);
color: var(--water-text);
}
.Segment:hover label {
@ -39,8 +39,8 @@
&.earth {
.Segment input:checked + label {
background: $earth-bg-10;
color: $earth-text-10;
background: var(--earth-bg);
color: var(--earth-text);
}
.Segment:hover label {
@ -51,8 +51,8 @@
&.wind {
.Segment input:checked + label {
background: $wind-bg-10;
color: $wind-text-10;
background: var(--wind-bg);
color: var(--wind-text);
}
.Segment:hover label {
@ -63,8 +63,8 @@
&.light {
.Segment input:checked + label {
background: $light-bg-10;
color: $light-text-10;
background: var(--light-bg);
color: var(--light-text);
}
.Segment:hover label {
@ -75,8 +75,8 @@
&.dark {
.Segment input:checked + label {
background: $dark-bg-10;
color: $dark-text-10;
background: var(--dark-bg);
color: var(--dark-text);
}
.Segment:hover label {

View file

@ -6,9 +6,22 @@
display: flex;
padding: $unit-2x $unit-2x;
&.modal {
background-color: var(--select-modal-bg);
&:hover {
background-color: var(--select-modal-bg-hover);
}
}
&:hover {
background-color: var(--input-bg-hover);
color: var(--text-primary);
cursor: pointer;
&[data-placeholder] > span:not(.SelectIcon) {
color: var(--text-primary);
}
}
&[data-placeholder] > span:not(.SelectIcon) {

View file

@ -20,7 +20,10 @@ interface Props
triggerClass?: string
}
const Select = (props: Props) => {
const Select = React.forwardRef<HTMLButtonElement, Props>(function Select(
props: Props,
forwardedRef
) {
const [value, setValue] = useState('')
useEffect(() => {
@ -33,7 +36,6 @@ const Select = (props: Props) => {
if (props.onValueChange) props.onValueChange(newValue)
}
console.log(value)
return (
<RadixSelect.Root
value={value !== '' ? value : undefined}
@ -42,6 +44,7 @@ const Select = (props: Props) => {
<RadixSelect.Trigger
className={classNames('SelectTrigger', props.triggerClass)}
placeholder={props.placeholder}
ref={forwardedRef}
>
<RadixSelect.Value placeholder={props.placeholder} />
<RadixSelect.Icon className="SelectIcon">
@ -62,6 +65,6 @@ const Select = (props: Props) => {
</RadixSelect.Portal>
</RadixSelect.Root>
)
}
})
export default Select

View file

@ -1,7 +1,7 @@
.SelectItem {
border-radius: $item-corner;
border: 2px solid transparent;
color: var(--text-secondary);
color: var(--text-tertiary);
font-size: $font-regular;
padding: ($unit * 1.5) $unit-2x;

View file

@ -1,5 +1,8 @@
import React, { useEffect, useState } from 'react'
import Select from '~components/Select'
import SelectGroup from '~components/SelectGroup'
import SelectItem from '~components/SelectItem'
import api from '~utils/api'
import './index.scss'
@ -9,32 +12,27 @@ interface Props {
currentValue?: WeaponKey
series: number
slot: number
onChange?: (event: React.ChangeEvent<HTMLSelectElement>) => void
onBlur?: (event: React.ChangeEvent<HTMLSelectElement>) => void
onChange?: (value: string, slot: number) => void
}
const WeaponKeyDropdown = React.forwardRef<HTMLSelectElement, Props>(
const WeaponKeySelect = React.forwardRef<HTMLButtonElement, Props>(
function useFieldSet(props, ref) {
const [open, setOpen] = useState(false)
const [keys, setKeys] = useState<WeaponKey[][]>([])
const [currentKey, setCurrentKey] = useState('')
const pendulumNames = [
{ en: 'Pendulum', jp: '' },
{ en: 'Chain', jp: '' },
{ en: 'Pendulum', jp: 'ペンデュラム' },
{ en: 'Chain', jp: 'チェイン' },
]
const telumaNames = [{ en: 'Teluma', jp: '' }]
const emblemNames = [{ en: 'Emblem', jp: '' }]
const telumaNames = [{ en: 'Teluma', jp: 'テルマ' }]
const emblemNames = [{ en: 'Emblem', jp: 'エンブレム' }]
const gauphNames = [
{ en: 'Gauph Key', jp: '' },
{ en: 'Ultima Key', jp: '' },
{ en: 'Gate of Omnipotence', jp: '' },
{ en: 'Gauph Key', jp: 'ガフスキー' },
{ en: 'Ultima Key', jp: 'ガフスキーΩ' },
{ en: 'Gate of Omnipotence', jp: 'ガフスキー' },
]
useEffect(() => {
if (props.currentValue) setCurrentKey(props.currentValue.id)
}, [props.currentValue])
useEffect(() => {
const filterParams = {
params: {
@ -50,22 +48,26 @@ const WeaponKeyDropdown = React.forwardRef<HTMLSelectElement, Props>(
)
let groupedKeys = []
for (let i = 0; i <= numGroups; i++) {
groupedKeys[i] = weaponKeys.filter((key) => key.group == i)
const values = weaponKeys.filter((key) => key.group == i)
if (values.length > 0) groupedKeys[i] = values
}
setKeys(groupedKeys)
setKeys(groupedKeys.filter(() => true))
}
function fetchWeaponKeys() {
api.endpoints.weapon_keys.getAll(filterParams).then((response) => {
const keys = response.data.map((k: any) => k.weapon_key)
organizeWeaponKeys(keys)
})
api.endpoints.weapon_keys
.getAll(filterParams)
.then((response) => organizeWeaponKeys(response.data))
}
fetchWeaponKeys()
}, [props.series, props.slot])
function openSelect() {
setOpen(!open)
}
function weaponKeyGroup(index: number) {
;['α', 'β', 'γ', 'Δ'].sort((a, b) => a.localeCompare(b, 'el'))
@ -78,9 +80,9 @@ const WeaponKeyDropdown = React.forwardRef<HTMLSelectElement, Props>(
keys[index].length > 0 &&
keys[index].sort(sortByOrder).map((item, i) => {
return (
<option key={i} value={item.id}>
<SelectItem key={i} value={item.id}>
{item.name.en}
</option>
</SelectItem>
)
})
@ -88,26 +90,25 @@ const WeaponKeyDropdown = React.forwardRef<HTMLSelectElement, Props>(
if (props.series == 2 && index == 0) name = pendulumNames[0]
else if (props.series == 2 && props.slot == 1 && index == 1)
name = pendulumNames[1]
else if (props.series == 3) name = telumaNames[index]
else if (props.series == 3) name = telumaNames[0]
else if (props.series == 17) name = gauphNames[props.slot]
else if (props.series == 22) name = emblemNames[index]
else if (props.series == 24) name = emblemNames[index]
return (
<optgroup
<SelectGroup
key={index}
label={
props.series == 17 && props.slot == 2 ? name.en : `${name.en}s`
}
separator={false}
>
{options}
</optgroup>
</SelectGroup>
)
}
function handleChange(event: React.ChangeEvent<HTMLSelectElement>) {
if (props.onChange) props.onChange(event)
setCurrentKey(event.currentTarget.value)
function handleChange(value: string) {
if (props.onChange) props.onChange(value, props.slot)
}
const emptyOption = () => {
@ -115,28 +116,30 @@ const WeaponKeyDropdown = React.forwardRef<HTMLSelectElement, Props>(
if (props.series == 2) name = pendulumNames[0].en
else if (props.series == 3) name = telumaNames[0].en
else if (props.series == 17) name = gauphNames[props.slot].en
else if (props.series == 22) name = emblemNames[0].en
else if (props.series == 24) name = emblemNames[0].en
return `No ${name}`
}
return (
<select
<Select
key={`weapon-key-${props.slot}`}
value={currentKey}
onBlur={props.onBlur}
onChange={handleChange}
value={props.currentValue ? props.currentValue.id : 'no-key'}
open={open}
onValueChange={handleChange}
onClick={openSelect}
ref={ref}
triggerClass="modal"
>
<option key="-1" value="-1">
<SelectItem key="no-key" value="no-key">
{emptyOption()}
</option>
</SelectItem>
{Array.from(Array(keys?.length)).map((x, i) => {
return weaponKeyGroup(i)
})}
</select>
</Select>
)
}
)
export default WeaponKeyDropdown
export default WeaponKeySelect

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react'
import React, { useEffect, useState } from 'react'
import { getCookie } from 'cookies-next'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
@ -7,8 +7,9 @@ import { AxiosResponse } from 'axios'
import * as Dialog from '@radix-ui/react-dialog'
import AXSelect from '~components/AxSelect'
import AwakeningSelect from '~components/AwakeningSelect'
import ElementToggle from '~components/ElementToggle'
import WeaponKeyDropdown from '~components/WeaponKeyDropdown'
import WeaponKeySelect from '~components/WeaponKeySelect'
import Button from '~components/Button'
import api from '~utils/api'
@ -27,6 +28,8 @@ interface GridWeaponObject {
ax_modifier2?: number
ax_strength1?: number
ax_strength2?: number
awakening_type?: number
awakening_level?: Number
}
}
@ -50,21 +53,43 @@ const WeaponModal = (props: Props) => {
? { Authorization: `Bearer ${accountData.token}` }
: {}
// Refs
const weaponKey1Select = React.createRef<HTMLSelectElement>()
const weaponKey2Select = React.createRef<HTMLSelectElement>()
const weaponKey3Select = React.createRef<HTMLSelectElement>()
// State
const [open, setOpen] = useState(false)
const [formValid, setFormValid] = useState(false)
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)
const [secondaryAxValue, setSecondaryAxValue] = useState(0.0)
const [weaponKey1, setWeaponKey1] = useState<WeaponKey | undefined>()
const [weaponKey2, setWeaponKey2] = useState<WeaponKey | undefined>()
const [weaponKey3, setWeaponKey3] = useState<WeaponKey | undefined>()
const [weaponKey1Id, setWeaponKey1Id] = useState('')
const [weaponKey2Id, setWeaponKey2Id] = useState('')
const [weaponKey3Id, setWeaponKey3Id] = useState('')
useEffect(() => {
setElement(props.gridWeapon.element)
if (props.gridWeapon.weapon_keys) {
props.gridWeapon.weapon_keys.forEach((key) => {
if (key.slot + 1 === 1) {
setWeaponKey1(key)
} else if (key.slot + 1 === 2) {
setWeaponKey2(key)
} else if (key.slot + 1 === 3) {
setWeaponKey3(key)
}
})
}
}, [props])
function receiveAxValues(
primaryAxModifier: number,
primaryAxValue: number,
@ -78,10 +103,15 @@ const WeaponModal = (props: Props) => {
setSecondaryAxValue(secondaryAxValue)
}
function receiveAxValidity(isValid: boolean) {
function receiveValidity(isValid: boolean) {
setFormValid(isValid)
}
function receiveAwakeningValues(type: number, level: number) {
setAwakeningType(type)
setAwakeningLevel(level)
}
function receiveElementValue(element: string) {
setElement(parseInt(element))
}
@ -91,14 +121,18 @@ const WeaponModal = (props: Props) => {
if (props.gridWeapon.object.element == 0) object.weapon.element = element
if ([2, 3, 17, 24].includes(props.gridWeapon.object.series))
object.weapon.weapon_key1_id = weaponKey1Select.current?.value
if (
[2, 3, 17, 24].includes(props.gridWeapon.object.series) &&
weaponKey1Id
) {
object.weapon.weapon_key1_id = weaponKey1Id
}
if ([2, 3, 17].includes(props.gridWeapon.object.series))
object.weapon.weapon_key2_id = weaponKey2Select.current?.value
if ([2, 3, 17].includes(props.gridWeapon.object.series) && weaponKey2Id)
object.weapon.weapon_key2_id = weaponKey2Id
if (props.gridWeapon.object.series == 17)
object.weapon.weapon_key3_id = weaponKey3Select.current?.value
if (props.gridWeapon.object.series == 17 && weaponKey3Id)
object.weapon.weapon_key3_id = weaponKey3Id
if (props.gridWeapon.object.ax > 0) {
object.weapon.ax_modifier1 = primaryAxModifier
@ -107,6 +141,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
}
@ -119,7 +158,7 @@ const WeaponModal = (props: Props) => {
}
function processResult(response: AxiosResponse) {
const gridWeapon: GridWeapon = response.data.grid_weapon
const gridWeapon: GridWeapon = response.data
if (gridWeapon.mainhand) appState.grid.weapons.mainWeapon = gridWeapon
else appState.grid.weapons.allWeapons[gridWeapon.position] = gridWeapon
@ -131,12 +170,18 @@ const WeaponModal = (props: Props) => {
console.error(error)
}
function receiveWeaponKey(value: string, slot: number) {
if (slot === 0) setWeaponKey1Id(value)
if (slot === 1) setWeaponKey2Id(value)
if (slot === 2) setWeaponKey3Id(value)
}
const elementSelect = () => {
return (
<section>
<h3>{t('modals.weapon.subtitles.element')}</h3>
<ElementToggle
currentElement={props.gridWeapon.element}
currentElement={element}
sendValue={receiveElementValue}
/>
</section>
@ -148,45 +193,45 @@ const WeaponModal = (props: Props) => {
<section>
<h3>{t('modals.weapon.subtitles.weapon_keys')}</h3>
{[2, 3, 17, 22].includes(props.gridWeapon.object.series) ? (
<WeaponKeyDropdown
currentValue={
props.gridWeapon.weapon_keys
? props.gridWeapon.weapon_keys[0]
: undefined
}
<WeaponKeySelect
currentValue={weaponKey1 != null ? weaponKey1 : undefined}
series={props.gridWeapon.object.series}
slot={0}
ref={weaponKey1Select}
onChange={receiveWeaponKey}
/>
) : (
''
)}
{[2, 3, 17].includes(props.gridWeapon.object.series) ? (
<WeaponKeyDropdown
currentValue={
props.gridWeapon.weapon_keys
? props.gridWeapon.weapon_keys[1]
: undefined
}
<WeaponKeySelect
currentValue={weaponKey2 != null ? weaponKey2 : undefined}
series={props.gridWeapon.object.series}
slot={1}
ref={weaponKey2Select}
onChange={receiveWeaponKey}
/>
) : (
''
)}
{props.gridWeapon.object.series == 17 ? (
<WeaponKeyDropdown
currentValue={
props.gridWeapon.weapon_keys
? props.gridWeapon.weapon_keys[2]
: undefined
}
<WeaponKeySelect
currentValue={weaponKey3 != null ? weaponKey3 : undefined}
series={props.gridWeapon.object.series}
slot={2}
ref={weaponKey3Select}
onChange={receiveWeaponKey}
/>
) : (
''
)}
{props.gridWeapon.object.series == 24 &&
props.gridWeapon.object.uncap.ulb ? (
<WeaponKeySelect
currentValue={weaponKey1 != null ? weaponKey1 : undefined}
series={props.gridWeapon.object.series}
slot={0}
onChange={receiveWeaponKey}
/>
) : (
''
@ -202,19 +247,39 @@ const WeaponModal = (props: Props) => {
<AXSelect
axType={props.gridWeapon.object.ax}
currentSkills={props.gridWeapon.ax}
sendValidity={receiveAxValidity}
sendValidity={receiveValidity}
sendValues={receiveAxValues}
/>
</section>
)
}
const awakeningSelect = () => {
return (
<section>
<h3>{t('modals.weapon.subtitles.awakening')}</h3>
<AwakeningSelect
object="weapon"
awakeningType={props.gridWeapon.awakening?.type}
awakeningLevel={props.gridWeapon.awakening?.level}
sendValidity={receiveValidity}
sendValues={receiveAwakeningValues}
/>
</section>
)
}
function openChange(open: boolean) {
setFormValid(false)
if (props.gridWeapon.object.ax > 0 || props.gridWeapon.object.awakening) {
setFormValid(false)
} else {
setFormValid(true)
}
setOpen(open)
}
return (
// TODO: Refactor into Dialog component
<Dialog.Root open={open} onOpenChange={openChange}>
<Dialog.Trigger asChild>{props.children}</Dialog.Trigger>
<Dialog.Portal>
@ -244,9 +309,11 @@ const WeaponModal = (props: Props) => {
? keySelect()
: ''}
{props.gridWeapon.object.ax > 0 ? axSelect() : ''}
{props.gridWeapon.awakening ? awakeningSelect() : ''}
<Button
contained={true}
onClick={updateWeapon}
disabled={props.gridWeapon.object.ax > 0 && !formValid}
disabled={!formValid}
text={t('modals.weapon.buttons.confirm')}
/>
</div>

View file

@ -41,6 +41,23 @@
width: 200px;
height: auto;
.Awakening {
width: 40%;
height: auto;
top: 67%;
left: -3.5%;
}
.Modifiers > .Skills {
bottom: 12%;
right: -3.5%;
& > .Skill {
width: 25%;
height: auto;
}
}
@media (max-width: $medium-screen) {
width: 25vw;
}
@ -56,6 +73,23 @@
width: 160px;
height: auto;
.Awakening {
width: 30%;
height: auto;
top: 14%;
left: -3.5%;
}
.Modifiers > .Skills {
bottom: 12%;
left: -3.5%;
& > .Skill {
width: 20%;
height: auto;
}
}
@media (max-width: $medium-screen) {
width: 20vw;
}
@ -81,7 +115,7 @@
position: absolute;
left: $unit;
top: $unit;
z-index: 3;
z-index: 10;
}
h3 {
@ -102,12 +136,37 @@
justify-content: center;
margin-bottom: calc($unit / 4);
overflow: hidden;
position: relative;
transition: all 0.18s ease-in-out;
&:hover .icon svg {
fill: var(--icon-secondary-hover);
}
.Awakening {
width: 100%;
height: 100%;
position: absolute;
z-index: 3;
}
.Modifiers {
display: flex;
align-items: flex-end;
width: 100%;
height: 100%;
position: absolute;
z-index: 3;
.Skills {
display: flex;
gap: $unit-fourth;
justify-content: flex-end;
padding: $unit-half;
width: 100%;
}
}
img {
position: relative;
width: 100%;

View file

@ -9,9 +9,12 @@ import WeaponHovercard from '~components/WeaponHovercard'
import UncapIndicator from '~components/UncapIndicator'
import Button from '~components/Button'
import { ButtonType } from '~utils/enums'
import type { SearchableObject } from '~types'
import { appState } from '~utils/appState'
import { axData } from '~utils/axData'
import { weaponAwakening } from '~utils/awakening'
import PlusIcon from '~public/icons/Add.svg'
import SettingsIcon from '~public/icons/Settings.svg'
import './index.scss'
@ -71,6 +74,265 @@ const WeaponUnit = (props: Props) => {
setImageUrl(imgSrc)
}
function awakeningImage() {
if (
props.gridWeapon &&
props.gridWeapon.object.awakening &&
props.gridWeapon.awakening &&
props.gridWeapon.awakening.type >= 0
) {
const awakening = weaponAwakening.find(
(awakening) => awakening.id === props.gridWeapon?.awakening?.type
)
const name = awakening?.name[locale]
return (
<img
alt={`${name} Lv${props.gridWeapon.awakening.level}`}
className="Awakening"
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/awakening/weapon_${props.gridWeapon.awakening.type}.png`}
/>
)
}
}
function telumaImage(index: number) {
const baseUrl = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-keys/`
let filename = ''
let altText = ''
// If there is a grid weapon, it is a Draconic Weapon and it has keys
if (
props.gridWeapon &&
props.gridWeapon.object.series === 3 &&
props.gridWeapon.weapon_keys
) {
if (index === 0 && props.gridWeapon.weapon_keys[0]) {
altText = `${props.gridWeapon.weapon_keys[0].name[locale]}`
filename = `${props.gridWeapon.weapon_keys[0].slug}.png`
} else if (index === 1 && props.gridWeapon.weapon_keys[1]) {
altText = `${props.gridWeapon.weapon_keys[1].name[locale]}`
const element = props.gridWeapon.object.element
filename = `${props.gridWeapon.weapon_keys[1].slug}-${element}.png`
}
return (
<img
alt={altText}
key={altText}
className="Skill"
src={`${baseUrl}${filename}`}
/>
)
}
}
function telumaImages() {
let images: JSX.Element[] = []
if (
props.gridWeapon &&
props.gridWeapon.object.series === 3 &&
props.gridWeapon.weapon_keys &&
props.gridWeapon.weapon_keys.length > 0
) {
for (let i = 0; i < props.gridWeapon.weapon_keys.length; i++) {
const image = telumaImage(i)
if (image) images.push(image)
}
}
return images
}
function ultimaImage(index: number) {
const baseUrl = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-keys/`
let filename = ''
let altText = ''
// If there is a grid weapon, it is a Dark Opus Weapon and it has keys
if (
props.gridWeapon &&
props.gridWeapon.object.series === 17 &&
props.gridWeapon.weapon_keys
) {
if (
props.gridWeapon.weapon_keys[index] &&
(props.gridWeapon.weapon_keys[index].slot === 1 ||
props.gridWeapon.weapon_keys[index].slot === 2)
) {
altText = `${props.gridWeapon.weapon_keys[index].name[locale]}`
filename = `${props.gridWeapon.weapon_keys[index].slug}.png`
} else if (
props.gridWeapon.weapon_keys[index] &&
props.gridWeapon.weapon_keys[index].slot === 0
) {
altText = `${props.gridWeapon.weapon_keys[index].name[locale]}`
const weapon = props.gridWeapon.object.proficiency
const suffix = `${weapon}`
filename = `${props.gridWeapon.weapon_keys[index].slug}-${suffix}.png`
}
}
return (
<img alt={`${altText}`} className="Skill" src={`${baseUrl}${filename}`} />
)
}
function ultimaImages() {
let images: JSX.Element[] = []
if (
props.gridWeapon &&
props.gridWeapon.object.series === 17 &&
props.gridWeapon.weapon_keys &&
props.gridWeapon.weapon_keys.length > 0
) {
for (let i = 0; i < props.gridWeapon.weapon_keys.length; i++) {
const image = ultimaImage(i)
if (image) images.push(image)
}
}
return images
}
function opusImage(index: number) {
const baseUrl = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-keys/`
let filename = ''
let altText = ''
// If there is a grid weapon, it is a Dark Opus Weapon and it has keys
if (
props.gridWeapon &&
props.gridWeapon.object.series === 2 &&
props.gridWeapon.weapon_keys
) {
if (
props.gridWeapon.weapon_keys[index] &&
props.gridWeapon.weapon_keys[index].slot === 0
) {
altText = `${props.gridWeapon.weapon_keys[index].name[locale]}`
filename = `${props.gridWeapon.weapon_keys[index].slug}.png`
} else if (
props.gridWeapon.weapon_keys[index] &&
props.gridWeapon.weapon_keys[index].slot === 1
) {
altText = `${props.gridWeapon.weapon_keys[index].name[locale]}`
const element = props.gridWeapon.object.element
const mod = props.gridWeapon.object.name.en.includes('Repudiation')
? 'primal'
: 'magna'
const suffix = `${mod}-${element}`
const weaponKey = props.gridWeapon.weapon_keys[index]
if (
[
'pendulum-strength',
'pendulum-zeal',
'pendulum-strife',
'chain-temperament',
'chain-restoration',
'chain-glorification',
].includes(weaponKey.slug)
) {
filename = `${props.gridWeapon.weapon_keys[index].slug}-${suffix}.png`
} else {
filename = `${props.gridWeapon.weapon_keys[index].slug}.png`
}
}
return (
<img
alt={altText}
key={altText}
className="Skill"
src={`${baseUrl}${filename}`}
/>
)
}
}
function opusImages() {
let images: JSX.Element[] = []
if (
props.gridWeapon &&
props.gridWeapon.object.series === 2 &&
props.gridWeapon.weapon_keys &&
props.gridWeapon.weapon_keys.length > 0
) {
for (let i = 0; i < props.gridWeapon.weapon_keys.length; i++) {
const image = opusImage(i)
if (image) images.push(image)
}
}
return images
}
function axImage(index: number) {
const axSkill = getCanonicalAxSkill(index)
if (
props.gridWeapon &&
props.gridWeapon.object.ax &&
props.gridWeapon.object.ax > 0 &&
props.gridWeapon.ax &&
axSkill
) {
return (
<img
alt={`axskill`}
className="Skill"
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/ax/${axSkill.slug}.png`}
/>
)
}
}
function axImages() {
let images: JSX.Element[] = []
if (
props.gridWeapon &&
props.gridWeapon.object.ax > 0 &&
props.gridWeapon.ax &&
props.gridWeapon.ax.length > 0
) {
for (let i = 0; i < props.gridWeapon.ax.length; i++) {
const image = axImage(i)
if (image) images.push(image)
}
}
return images
}
function getCanonicalAxSkill(index: number) {
if (
props.gridWeapon &&
props.gridWeapon.object.ax &&
props.gridWeapon.object.ax > 0 &&
props.gridWeapon.ax
) {
const axOptions = axData[props.gridWeapon.object.ax - 1]
const weaponAxSkill: SimpleAxSkill = props.gridWeapon.ax[0]
let axSkill = axOptions.find((ax) => ax.id === weaponAxSkill.modifier)
if (index !== 0 && axSkill && axSkill.secondary) {
const weaponSubAxSkill: SimpleAxSkill = props.gridWeapon.ax[1]
axSkill = axSkill.secondary.find(
(ax) => ax.id === weaponSubAxSkill.modifier
)
}
return axSkill
} else return
}
function passUncapData(uncap: number) {
if (props.gridWeapon)
props.updateUncap(props.gridWeapon.id, props.position, uncap)
@ -81,12 +343,22 @@ const WeaponUnit = (props: Props) => {
return (
weapon.ax > 0 ||
weapon.awakening ||
(weapon.series && [2, 3, 17, 22, 24].includes(weapon.series))
)
}
const image = (
<div className="WeaponImage">
<div className="Modifiers">
{awakeningImage()}
<div className="Skills">
{axImages()}
{telumaImages()}
{opusImages()}
{ultimaImages()}
</div>
</div>
<img alt={weapon?.name.en} className="grid_image" src={imageUrl} />
{props.editable ? (
<span className="icon">

View file

@ -4,17 +4,20 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import Party from '~components/Party'
import { appState } from '~utils/appState'
import { groupWeaponKeys } from '~utils/groupWeaponKeys'
import organizeRaids from '~utils/organizeRaids'
import setUserToken from '~utils/setUserToken'
import api from '~utils/api'
import type { NextApiRequest, NextApiResponse } from 'next'
import type { GroupedWeaponKeys } from '~utils/groupWeaponKeys'
interface Props {
jobs: Job[]
jobSkills: JobSkill[]
raids: Raid[]
sortedRaids: Raid[][]
weaponKeys: GroupedWeaponKeys
}
const NewRoute: React.FC<Props> = (props: Props) => {
@ -31,6 +34,7 @@ const NewRoute: React.FC<Props> = (props: Props) => {
appState.raids = props.raids
appState.jobs = props.jobs
appState.jobSkills = props.jobSkills
appState.weaponKeys = props.weaponKeys
}
return <Party new={true} raids={props.sortedRaids} pushHistory={callback} />
@ -61,9 +65,11 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex
return response.data
})
let jobSkills = await api.allJobSkills().then((response) => {
return response.data
})
let jobSkills = await api.allJobSkills().then((response) => response.data)
let weaponKeys = await api.endpoints.weapon_keys
.getAll()
.then((response) => groupWeaponKeys(response.data))
return {
props: {
@ -71,6 +77,7 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex
jobSkills: jobSkills,
raids: raids,
sortedRaids: sortedRaids,
weaponKeys: weaponKeys,
...(await serverSideTranslations(locale, ['common'])),
// Will be passed to the page component as props
},

View file

@ -5,10 +5,12 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import Party from '~components/Party'
import { appState } from '~utils/appState'
import { groupWeaponKeys } from '~utils/groupWeaponKeys'
import organizeRaids from '~utils/organizeRaids'
import api from '~utils/api'
import type { NextApiRequest, NextApiResponse } from 'next'
import type { GroupedWeaponKeys } from '~utils/groupWeaponKeys'
interface Props {
party: Party
@ -16,6 +18,7 @@ interface Props {
jobSkills: JobSkill[]
raids: Raid[]
sortedRaids: Raid[][]
weaponKeys: GroupedWeaponKeys
}
const PartyRoute: React.FC<Props> = (props: Props) => {
@ -27,6 +30,7 @@ const PartyRoute: React.FC<Props> = (props: Props) => {
appState.raids = props.raids
appState.jobs = props.jobs
appState.jobSkills = props.jobSkills
appState.weaponKeys = props.weaponKeys
}
return <Party team={props.party} raids={props.sortedRaids} />
@ -65,6 +69,10 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex
})
let jobSkills = await api.allJobSkills(headers).then((response) => response.data)
let weaponKeys = await api.endpoints.weapon_keys
.getAll()
.then((response) => groupWeaponKeys(response.data))
let party: Party | null = null
if (query.party) {
@ -81,6 +89,7 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex
jobSkills: jobSkills,
raids: raids,
sortedRaids: sortedRaids,
weaponKeys: weaponKeys,
...(await serverSideTranslations(locale, ["common"])),
// Will be passed to the page component as props
},

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -8,6 +8,15 @@
"value_empty": "{{name}} must have a value"
}
},
"awakening": {
"no_type": "No awakening",
"errors": {
"value_too_low": "Awakening level must be at least {{minValue}}",
"value_too_high": "Awakening level cannot be greater than {{maxValue}}",
"value_not_whole": "Awakening level must be a whole number",
"value_empty": "Awakening must have a level"
}
},
"buttons": {
"cancel": "Cancel",
"copy": "Copy link",
@ -192,6 +201,7 @@
"subtitles": {
"element": "Element",
"ax_skills": "AX Skills",
"awakening": "Awakening",
"weapon_keys": "Weapon Keys"
}
}

View file

@ -3,11 +3,20 @@
"no_skill": "EXスキルなし",
"errors": {
"value_too_low": "{{name}}は最低{{minValue}}{{suffix}}を入力してください",
"value_too_high": "{{name}}は最大{{maxValue}}を入力してください",
"value_too_high": "{{name}}は最大{{maxValue}}{{suffix}}を入力してください",
"value_not_whole": "{{name}}は整数でなければなりません",
"value_empty": "{{name}}を入力してください"
}
},
"awakening": {
"no_type": "覚醒タイプなし",
"errors": {
"value_too_low": "覚醒レベルは最低{{minValue}}を入力してください",
"value_too_high": "覚醒レベルは最大{{maxValue}}を入力してください",
"value_not_whole": "覚醒レベルは整数でなければなりません",
"value_empty": "覚醒レベルを入力してください"
}
},
"buttons": {
"cancel": "キャンセルs",
"copy": "リンクをコピー",
@ -193,6 +202,7 @@
"subtitles": {
"element": "属性",
"ax_skills": "EXスキル",
"awakening": "覚醒",
"weapon_keys": "武器スキル"
}
}

View file

@ -38,6 +38,8 @@
--select-bg: #{$select--bg--light};
--select-contained-bg: #{$select--contained--bg--light};
--select-contained-bg-hover: #{$select--contained--bg--light--hover};
--select-modal-bg: #{$select--modal--bg--light};
--select-modal-bg-hover: #{$select--modal--bg--light--hover};
--select-separator: #{$select--separator--light};
--option-bg-hover: #{$option--bg--light--hover};
@ -72,19 +74,40 @@
--subaura-orange-secondary: #{$subaura--orange--secondary--light};
--subaura-orange-text: #{$subaura--orange--text--light};
// Light - Element Toggle
--toggle-bg: #{$toggle--bg--light};
--toggle-stroke: #{$toggle--stroke--light};
--grid-border-color: #{$grid--border--color--light};
--wind-bg: #{$wind-bg-10};
--wind-hover-bg: #{$wind-bg-20};
--wind-text: #{$wind-text-10};
--wind-hover-text: #{$wind-text-20};
--fire-bg: #{$fire-bg-10};
--fire-hover-bg: #{$fire-bg-20};
--fire-text: #{$fire-text-10};
--fire-hover-text: #{$fire-text-20};
--water-bg: #{$water-bg-10};
--water-hover-bg: #{$water-bg-20};
--water-text: #{$water-text-10};
--water-hover-text: #{$water-text-20};
--earth-bg: #{$earth-bg-10};
--earth-hover-bg: #{$earth-bg-20};
--earth-text: #{$earth-text-10};
--earth-hover-text: #{$earth-text-20};
--dark-bg: #{$dark-bg-10};
--dark-hover-bg: #{$dark-bg-20};
--dark-text: #{$dark-text-10};
--dark-hover-text: #{$dark-text-20};
--light-bg: #{$light-bg-10};
--light-hover-bg: #{$light-bg-20};
--light-text: #{$light-text-10};
--light-hover-text: #{$light-text-20};
}
@ -128,6 +151,8 @@
--select-bg: #{$select--bg--dark};
--select-contained-bg: #{$select--contained--bg--dark};
--select-contained-bg-hover: #{$select--contained--bg--dark--hover};
--select-modal-bg: #{$select--modal--bg--dark};
--select-modal-bg-hover: #{$select--modal--bg--dark--hover};
--select-separator: #{$select--separator--dark};
--option-bg-hover: #{$option--bg--dark--hover};
@ -162,16 +187,40 @@
--subaura-orange-secondary: #{$subaura--orange--secondary--dark};
--subaura-orange-text: #{$subaura--orange--text--dark};
// Dark - Element Toggle
--toggle-bg: #{$toggle--bg--dark};
--toggle-stroke: #{$toggle--stroke--dark};
--grid-border-color: #{$grid--border--color--dark};
// Element theming
--wind-bg: #{$wind-bg-10};
--wind-hover-bg: #{$wind-bg-00};
--wind-text: #{$wind-text-10};
--wind-hover-text: #{$wind-text-00};
--fire-bg: #{$fire-bg-10};
--fire-hover-bg: #{$fire-bg-00};
--fire-text: #{$fire-text-10};
--fire-hover-text: #{$fire-text-00};
--water-bg: #{$water-bg-10};
--water-hover-bg: #{$water-bg-00};
--water-text: #{$water-text-10};
--water-hover-text: #{$water-text-00};
--earth-bg: #{$earth-bg-10};
--earth-hover-bg: #{$earth-bg-00};
--earth-text: #{$earth-text-10};
--earth-hover-text: #{$earth-text-00};
--dark-bg: #{$dark-bg-10};
--dark-hover-bg: #{$dark-bg-00};
--dark-text: #{$dark-text-10};
--dark-hover-text: #{$dark-text-00};
--light-bg: #{$light-bg-10};
--light-hover-bg: #{$light-bg-00};
--light-text: #{$light-text-10};
--light-hover-text: #{$light-text-00};
}

View file

@ -19,6 +19,7 @@ $unit-6x: $unit * 6;
$unit-8x: $unit * 8;
$unit-10x: $unit * 10;
$unit-12x: $unit * 12;
$unit-14x: $unit * 14;
$unit-20x: $unit * 20;
// Colors
@ -34,6 +35,7 @@ $grey-55: #888;
$grey-60: #a9a9a9;
$grey-70: #c6c6c6;
$grey-80: #e9e9e9;
$grey-85: #efefef;
$grey-90: #f5f5f5;
$grey-95: #fafafa;
$grey-100: white;
@ -132,8 +134,8 @@ $menu--text--dark--hover: $grey-15;
// Color Definitions: Button
$button--bg--light: $grey-80;
$button--bg--light--hover: $grey-100;
$button--bg--light--disabled: $grey-50;
$button--contained--bg--light: $grey-90;
$button--bg--light--disabled: $grey-80;
$button--contained--bg--light: $grey-85;
$button--contained--bg--light--hover: $grey-80;
$button--bg--dark: $grey-00;
@ -144,10 +146,10 @@ $button--contained--bg--dark--hover: $grey-05;
$button--text--light: $grey-55;
$button--text--light--hover: $grey-40;
$button--text--light--disabled: $grey-40;
$button--text--light--disabled: $grey-70;
$button--text--dark: $grey-70;
$button--text--dark--hover: $grey-100;
$button--text--dark--disabled: $grey-50;
$button--text--dark--disabled: $grey-40;
// Color Definitions: Input
$input--bg--light: $grey-100;
@ -155,7 +157,7 @@ $input--bg--light--hover: $grey-95;
$input--bg--dark: $grey-40;
$input--bg--dark--hover: $grey-30;
$input--bound--bg--light: $grey-90;
$input--bound--bg--light: $grey-85;
$input--bound--bg--light--hover: $grey-80;
$input--bound--bg--dark: $grey-15;
$input--bound--bg--dark--hover: $grey-25;
@ -163,12 +165,20 @@ $input--bound--bg--dark--hover: $grey-25;
// Color Definitions: Select
$select--bg--light: $grey-100;
$select--bg--dark: $grey-10;
$select--contained--bg--light: $grey-90;
$select--contained--bg--dark: $grey-05;
$select--contained--bg--light--hover: $grey-80;
$select--contained--bg--dark: $grey-05;
$select--contained--bg--dark--hover: $grey-00;
$select--modal--bg--light: $grey-85;
$select--modal--bg--light--hover: $grey-80;
$select--modal--bg--dark: $grey-40;
$select--modal--bg--dark--hover: $grey-30;
$select--separator--light: $grey-90;
$select--separator--dark: $grey-05;
$option--bg--light--hover: $grey-90;
$option--bg--dark--hover: $grey-00;
@ -205,6 +215,13 @@ $subaura--orange--primary--dark: $orange-00;
$subaura--orange--secondary--dark: $orange-10;
$subaura--orange--text--dark: $orange-00;
// Color Definitions: Element Toggle
$toggle--bg--light: $grey-90;
$toggle--bg--dark: $grey-15;
$toggle--stroke--light: rgba(0, 0, 0, 0.14);
$toggle--stroke--dark: rgba(0, 0, 0, 0.8);
// Color Definitions: Text
$text--primary--color--light: $grey-40;
$text--primary--color--dark: $grey-90;

1
types/AxSkill.d.ts vendored
View file

@ -5,6 +5,7 @@ interface AxSkill {
ja: string
}
id: number
slug: string
minValue: number
maxValue: number
suffix?: string

View file

@ -3,4 +3,8 @@ interface GridCharacter {
position: number
object: Character
uncap_level: number
awakening: {
type: number
level: number
}
}

View file

@ -7,4 +7,8 @@ interface GridWeapon {
element: number
weapon_keys?: Array<WeaponKey>
ax?: Array<SimpleAxSkill>
awakening?: {
type: number
level: number
}
}

1
types/Weapon.d.ts vendored
View file

@ -9,6 +9,7 @@ interface Weapon {
max_skill_level: number
series: number
ax: number
awakening: boolean
name: {
[key: string]: string
en: string

View file

@ -5,6 +5,7 @@ interface WeaponKey {
en: string
ja: string
}
slug: string
series: integer
slot: integer
group: integer

View file

@ -1,5 +1,6 @@
import { proxy } from 'valtio'
import { JobSkillObject } from '~types'
import { GroupedWeaponKeys } from './groupWeaponKeys'
const emptyJob: Job = {
id: '-1',
@ -57,6 +58,7 @@ interface AppState {
raids: Raid[]
jobs: Job[]
jobSkills: JobSkill[]
weaponKeys: GroupedWeaponKeys
}
export const initialAppState: AppState = {
@ -103,6 +105,13 @@ export const initialAppState: AppState = {
raids: [],
jobs: [],
jobSkills: [],
weaponKeys: {
pendulum: [],
chain: [],
teluma: [],
gauph: [],
emblem: [],
},
}
export const appState = proxy(initialAppState)

62
utils/awakening.tsx Normal file
View file

@ -0,0 +1,62 @@
export type Awakening = {
id: number
name: {
[key: string]: string
en: string
ja: string
}
}
export const characterAwakening: Awakening[] = [
{
id: 0,
name: {
en: 'Balanced',
ja: 'バランス',
},
},
{
id: 1,
name: {
en: 'Attack',
ja: '攻撃',
},
},
{
id: 2,
name: {
en: 'Defense',
ja: '防御',
},
},
{
id: 3,
name: {
en: 'Multiattack',
ja: '連続攻撃',
},
},
]
export const weaponAwakening: Awakening[] = [
{
id: 0,
name: {
en: 'Attack',
ja: '攻撃',
},
},
{
id: 1,
name: {
en: 'Defense',
ja: '防御',
},
},
{
id: 2,
name: {
en: 'Special',
ja: '特殊',
},
},
]

View file

@ -6,6 +6,7 @@ export const axData: AxSkill[][] = [
ja: '攻撃',
},
id: 0,
slug: 'atk',
minValue: 1,
maxValue: 3.5,
suffix: '%',
@ -16,6 +17,7 @@ export const axData: AxSkill[][] = [
ja: '奥義ダメ',
},
id: 3,
slug: 'ca-dmg',
minValue: 2,
maxValue: 4,
suffix: '%',
@ -26,6 +28,7 @@ export const axData: AxSkill[][] = [
ja: 'DA確率',
},
id: 5,
slug: 'da',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -36,6 +39,7 @@ export const axData: AxSkill[][] = [
ja: 'TA確率',
},
id: 6,
slug: 'ta',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -46,6 +50,7 @@ export const axData: AxSkill[][] = [
ja: 'アビ上限',
},
id: 7,
slug: 'skill-cap',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -58,6 +63,7 @@ export const axData: AxSkill[][] = [
ja: '防御',
},
id: 1,
slug: 'def',
minValue: 1,
maxValue: 8,
suffix: '%',
@ -68,6 +74,7 @@ export const axData: AxSkill[][] = [
ja: 'HP',
},
id: 2,
slug: 'hp',
minValue: 1,
maxValue: 3,
suffix: '%',
@ -78,6 +85,7 @@ export const axData: AxSkill[][] = [
ja: '弱体耐性',
},
id: 9,
slug: 'debuff',
minValue: 1,
maxValue: 3,
suffix: '%',
@ -88,6 +96,7 @@ export const axData: AxSkill[][] = [
ja: '回復性能',
},
id: 10,
slug: 'healing',
minValue: 2,
maxValue: 5,
suffix: '%',
@ -98,6 +107,7 @@ export const axData: AxSkill[][] = [
ja: '背水',
},
id: 11,
slug: 'enmity',
minValue: 1,
maxValue: 3,
},
@ -109,6 +119,7 @@ export const axData: AxSkill[][] = [
ja: 'HP',
},
id: 2,
slug: 'hp',
minValue: 1,
maxValue: 11,
suffix: '%',
@ -119,6 +130,7 @@ export const axData: AxSkill[][] = [
ja: '防御',
},
id: 1,
slug: 'def',
minValue: 1,
maxValue: 3,
suffix: '%',
@ -129,6 +141,7 @@ export const axData: AxSkill[][] = [
ja: '弱体耐性',
},
id: 9,
slug: 'debuff',
minValue: 1,
maxValue: 3,
suffix: '%',
@ -139,6 +152,7 @@ export const axData: AxSkill[][] = [
ja: '回復性能',
},
id: 10,
slug: 'healing',
minValue: 2,
maxValue: 5,
suffix: '%',
@ -149,6 +163,7 @@ export const axData: AxSkill[][] = [
ja: '渾身',
},
id: 12,
slug: 'stamina',
minValue: 1,
maxValue: 3,
},
@ -160,6 +175,7 @@ export const axData: AxSkill[][] = [
ja: '奥義ダメ',
},
id: 3,
slug: 'ca-dmg',
minValue: 2,
maxValue: 8.5,
suffix: '%',
@ -170,6 +186,7 @@ export const axData: AxSkill[][] = [
ja: '攻撃',
},
id: 0,
slug: 'atk',
minValue: 1,
maxValue: 1.5,
suffix: '%',
@ -180,6 +197,7 @@ export const axData: AxSkill[][] = [
ja: '全属性攻撃力',
},
id: 13,
slug: 'ele-atk',
minValue: 1,
maxValue: 5,
suffix: '%',
@ -190,6 +208,7 @@ export const axData: AxSkill[][] = [
ja: '奥義上限',
},
id: 8,
slug: 'ca-cap',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -200,6 +219,7 @@ export const axData: AxSkill[][] = [
ja: '渾身',
},
id: 12,
slug: 'stamina',
minValue: 1,
maxValue: 3,
},
@ -211,6 +231,7 @@ export const axData: AxSkill[][] = [
ja: '連撃率',
},
id: 4,
slug: 'ta',
minValue: 1,
maxValue: 4,
suffix: '%',
@ -221,6 +242,7 @@ export const axData: AxSkill[][] = [
ja: '奥義ダメ',
},
id: 3,
slug: 'ca-dmg',
minValue: 2,
maxValue: 4,
suffix: '%',
@ -231,6 +253,7 @@ export const axData: AxSkill[][] = [
ja: '全属性攻撃力',
},
id: 13,
slug: 'ele-atk',
minValue: 1,
maxValue: 5,
suffix: '%',
@ -241,6 +264,7 @@ export const axData: AxSkill[][] = [
ja: 'DA確率',
},
id: 5,
slug: 'da',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -251,6 +275,7 @@ export const axData: AxSkill[][] = [
ja: 'TA確率',
},
id: 6,
slug: 'ta',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -265,6 +290,7 @@ export const axData: AxSkill[][] = [
ja: '攻撃',
},
id: 0,
slug: 'atk',
minValue: 1,
maxValue: 3.5,
suffix: '%',
@ -275,6 +301,7 @@ export const axData: AxSkill[][] = [
ja: '奥義ダメ',
},
id: 3,
slug: 'ca-dmg',
minValue: 2,
maxValue: 8.5,
suffix: '%',
@ -285,6 +312,7 @@ export const axData: AxSkill[][] = [
ja: '連撃確率',
},
id: 4,
slug: 'ta',
minValue: 1.5,
maxValue: 4,
suffix: '%',
@ -295,6 +323,7 @@ export const axData: AxSkill[][] = [
ja: '通常ダメ上限',
},
id: 14,
slug: 'na-dmg',
minValue: 0.5,
maxValue: 1.5,
suffix: '%',
@ -305,6 +334,7 @@ export const axData: AxSkill[][] = [
ja: 'アビ与ダメ上昇',
},
id: 15,
slug: 'skill-supp',
minValue: 1,
maxValue: 5,
},
@ -316,6 +346,7 @@ export const axData: AxSkill[][] = [
ja: '防御',
},
id: 1,
slug: 'def',
minValue: 1,
maxValue: 8,
suffix: '%',
@ -326,6 +357,7 @@ export const axData: AxSkill[][] = [
ja: '属性ダメ軽減',
},
id: 17,
slug: 'ele-def',
minValue: 1,
maxValue: 5,
suffix: '%',
@ -336,6 +368,7 @@ export const axData: AxSkill[][] = [
ja: '弱体耐性',
},
id: 9,
slug: 'debuff',
minValue: 1,
maxValue: 3,
suffix: '%',
@ -346,6 +379,7 @@ export const axData: AxSkill[][] = [
ja: '回復性能',
},
id: 10,
slug: 'healing',
minValue: 2,
maxValue: 5,
suffix: '%',
@ -356,6 +390,7 @@ export const axData: AxSkill[][] = [
ja: '背水',
},
id: 11,
slug: 'enmity',
minValue: 1,
maxValue: 3,
},
@ -367,6 +402,7 @@ export const axData: AxSkill[][] = [
ja: 'HP',
},
id: 2,
slug: 'hp',
minValue: 1,
maxValue: 11,
suffix: '%',
@ -377,6 +413,7 @@ export const axData: AxSkill[][] = [
ja: '属性ダメ軽減',
},
id: 17,
slug: 'ele-def',
minValue: 1,
maxValue: 5,
suffix: '%',
@ -387,6 +424,7 @@ export const axData: AxSkill[][] = [
ja: '弱体耐性',
},
id: 9,
slug: 'debuff',
minValue: 1,
maxValue: 3,
suffix: '%',
@ -397,6 +435,7 @@ export const axData: AxSkill[][] = [
ja: '回復性能',
},
id: 10,
slug: 'healing',
minValue: 2,
maxValue: 5,
suffix: '%',
@ -407,6 +446,7 @@ export const axData: AxSkill[][] = [
ja: '渾身',
},
id: 12,
slug: 'stamina',
minValue: 1,
maxValue: 3,
},
@ -418,6 +458,7 @@ export const axData: AxSkill[][] = [
ja: '奥義ダメ',
},
id: 3,
slug: 'ca-dmg',
minValue: 2,
maxValue: 8.5,
suffix: '%',
@ -428,6 +469,7 @@ export const axData: AxSkill[][] = [
ja: '連撃率',
},
id: 4,
slug: 'ta',
minValue: 1.5,
maxValue: 4,
suffix: '%',
@ -438,6 +480,7 @@ export const axData: AxSkill[][] = [
ja: 'アビ与ダメ上昇',
},
id: 15,
slug: 'skill-supp',
minValue: 1,
maxValue: 5,
},
@ -447,6 +490,7 @@ export const axData: AxSkill[][] = [
ja: '奥義与ダメ上昇',
},
id: 16,
slug: 'ca-supp',
minValue: 1,
maxValue: 5,
},
@ -456,6 +500,7 @@ export const axData: AxSkill[][] = [
ja: '渾身',
},
id: 12,
slug: 'stamina',
minValue: 1,
maxValue: 3,
},
@ -467,6 +512,7 @@ export const axData: AxSkill[][] = [
ja: '連撃率',
},
id: 4,
slug: 'ta',
minValue: 1,
maxValue: 4,
suffix: '%',
@ -477,6 +523,7 @@ export const axData: AxSkill[][] = [
ja: '奥義与ダメ上昇',
},
id: 16,
slug: 'ca-supp',
minValue: 1,
maxValue: 5,
},
@ -486,6 +533,7 @@ export const axData: AxSkill[][] = [
ja: '通常ダメ上限',
},
id: 14,
slug: 'na-cap',
minValue: 0.5,
maxValue: 1.5,
suffix: '%',
@ -496,6 +544,7 @@ export const axData: AxSkill[][] = [
ja: '渾身',
},
id: 12,
slug: 'stamina',
minValue: 1,
maxValue: 3,
},
@ -505,6 +554,7 @@ export const axData: AxSkill[][] = [
ja: '背水',
},
id: 11,
slug: 'enmity',
minValue: 1,
maxValue: 3,
},
@ -518,6 +568,7 @@ export const axData: AxSkill[][] = [
ja: '攻撃',
},
id: 0,
slug: 'atk',
minValue: 1,
maxValue: 3.5,
suffix: '%',
@ -528,6 +579,7 @@ export const axData: AxSkill[][] = [
ja: '奥義ダメ',
},
id: 3,
slug: 'ca-dmg',
minValue: 2,
maxValue: 4,
suffix: '%',
@ -538,6 +590,7 @@ export const axData: AxSkill[][] = [
ja: 'DA確率',
},
id: 5,
slug: 'da',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -548,6 +601,7 @@ export const axData: AxSkill[][] = [
ja: 'TA確率',
},
id: 6,
slug: 'ta',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -558,6 +612,7 @@ export const axData: AxSkill[][] = [
ja: 'アビ上限',
},
id: 7,
slug: 'skill-cap',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -570,6 +625,7 @@ export const axData: AxSkill[][] = [
ja: '防御',
},
id: 1,
slug: 'def',
minValue: 1,
maxValue: 8,
suffix: '%',
@ -580,6 +636,7 @@ export const axData: AxSkill[][] = [
ja: 'HP',
},
id: 2,
slug: 'hp',
minValue: 1,
maxValue: 3,
suffix: '%',
@ -590,6 +647,7 @@ export const axData: AxSkill[][] = [
ja: '弱体耐性',
},
id: 9,
slug: 'debuff',
minValue: 1,
maxValue: 3,
suffix: '%',
@ -600,6 +658,7 @@ export const axData: AxSkill[][] = [
ja: '回復性能',
},
id: 10,
slug: 'healing',
minValue: 2,
maxValue: 5,
suffix: '%',
@ -610,6 +669,7 @@ export const axData: AxSkill[][] = [
ja: '背水',
},
id: 11,
slug: 'enmity',
minValue: 1,
maxValue: 3,
},
@ -621,6 +681,7 @@ export const axData: AxSkill[][] = [
ja: 'HP',
},
id: 2,
slug: 'hp',
minValue: 1,
maxValue: 11,
suffix: '%',
@ -631,6 +692,7 @@ export const axData: AxSkill[][] = [
ja: '防御',
},
id: 1,
slug: 'def',
minValue: 1,
maxValue: 3,
suffix: '%',
@ -641,6 +703,7 @@ export const axData: AxSkill[][] = [
ja: '弱体耐性',
},
id: 9,
slug: 'debuff',
minValue: 1,
maxValue: 3,
suffix: '%',
@ -651,6 +714,7 @@ export const axData: AxSkill[][] = [
ja: '回復性能',
},
id: 10,
slug: 'healing',
minValue: 2,
maxValue: 5,
suffix: '%',
@ -661,6 +725,7 @@ export const axData: AxSkill[][] = [
ja: '渾身',
},
id: 12,
slug: 'stamina',
minValue: 1,
maxValue: 3,
},
@ -672,6 +737,7 @@ export const axData: AxSkill[][] = [
ja: '奥義ダメ',
},
id: 3,
slug: 'ca-dmg',
minValue: 2,
maxValue: 8.5,
suffix: '%',
@ -682,6 +748,7 @@ export const axData: AxSkill[][] = [
ja: '攻撃',
},
id: 0,
slug: 'atk',
minValue: 1,
maxValue: 1.5,
suffix: '%',
@ -692,6 +759,7 @@ export const axData: AxSkill[][] = [
ja: '全属性攻撃力',
},
id: 13,
slug: 'ele-atk',
minValue: 1,
maxValue: 5,
suffix: '%',
@ -702,6 +770,7 @@ export const axData: AxSkill[][] = [
ja: '奥義上限',
},
id: 8,
slug: 'ca-dmg',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -712,6 +781,7 @@ export const axData: AxSkill[][] = [
ja: '渾身',
},
id: 12,
slug: 'stamina',
minValue: 1,
maxValue: 3,
},
@ -723,6 +793,7 @@ export const axData: AxSkill[][] = [
ja: '連撃率',
},
id: 4,
slug: 'ta',
minValue: 1,
maxValue: 4,
suffix: '%',
@ -733,6 +804,7 @@ export const axData: AxSkill[][] = [
ja: '奥義ダメ',
},
id: 3,
slug: 'ca-dmg',
minValue: 2,
maxValue: 4,
suffix: '%',
@ -743,6 +815,7 @@ export const axData: AxSkill[][] = [
ja: '全属性攻撃力',
},
id: 13,
slug: 'ele-atk',
minValue: 1,
maxValue: 5,
suffix: '%',
@ -753,6 +826,7 @@ export const axData: AxSkill[][] = [
ja: 'DA確率',
},
id: 5,
slug: 'da',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -763,6 +837,7 @@ export const axData: AxSkill[][] = [
ja: 'TA確率',
},
id: 6,
slug: 'ta',
minValue: 1,
maxValue: 2,
suffix: '%',
@ -775,6 +850,7 @@ export const axData: AxSkill[][] = [
ja: 'EXP UP',
},
id: 18,
slug: 'exp',
minValue: 5,
maxValue: 10,
suffix: '%',
@ -785,6 +861,7 @@ export const axData: AxSkill[][] = [
ja: '獲得ルピ',
},
id: 19,
slug: 'rupie',
minValue: 10,
maxValue: 20,
suffix: '%',

34
utils/groupWeaponKeys.tsx Normal file
View file

@ -0,0 +1,34 @@
import { weaponKeyGroups } from './weaponKeyGroups'
export type GroupedWeaponKeys = {
[key: string]: WeaponKey[]
pendulum: WeaponKey[]
chain: WeaponKey[]
teluma: WeaponKey[]
gauph: WeaponKey[]
emblem: WeaponKey[]
}
export function groupWeaponKeys(keys: WeaponKey[]) {
console.log(keys)
const numGroups = Math.max.apply(
Math,
keys.map((key) => key.group)
)
let groupedKeys: GroupedWeaponKeys = {
pendulum: [],
chain: [],
teluma: [],
gauph: [],
emblem: [],
}
for (let i = 0; i <= numGroups; i++) {
groupedKeys[weaponKeyGroups[i].slug] = keys.filter((key) => key.group == i)
}
console.log(groupedKeys)
return groupedKeys
}

52
utils/weaponKeyGroups.tsx Normal file
View file

@ -0,0 +1,52 @@
interface WeaponKeyGroup {
id: number
slug: string
name: {
[key: string]: string
en: string
ja: string
}
}
export const weaponKeyGroups: WeaponKeyGroup[] = [
{
id: 0,
slug: 'pendulum',
name: {
en: 'Pendulum',
ja: 'ペンデュラム',
},
},
{
id: 1,
slug: 'chain',
name: {
en: 'Chain',
ja: 'チェイン',
},
},
{
id: 2,
slug: 'teluma',
name: {
en: 'Teluma',
ja: 'テルマ',
},
},
{
id: 3,
slug: 'gauph',
name: {
en: 'Gauph Key',
ja: 'ガフスキー',
},
},
{
id: 4,
slug: 'emblem',
name: {
en: 'Emblem',
ja: 'エンブレム',
},
},
]