From d5161eb2d63ad9677ee390ca732ca92c3f65695b Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 1 Mar 2022 13:03:44 -0800 Subject: [PATCH 01/30] Update Arrow / Add Settings icons --- public/icons/Arrow.svg | 4 ++-- public/icons/Settings.svg | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 public/icons/Settings.svg diff --git a/public/icons/Arrow.svg b/public/icons/Arrow.svg index 61369665..44d30bc4 100644 --- a/public/icons/Arrow.svg +++ b/public/icons/Arrow.svg @@ -1,3 +1,3 @@ - - + + diff --git a/public/icons/Settings.svg b/public/icons/Settings.svg new file mode 100644 index 00000000..6ffc10a7 --- /dev/null +++ b/public/icons/Settings.svg @@ -0,0 +1,6 @@ + + + + + + From dc18fb1975f6212b429ad811f2c8463181649764 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 1 Mar 2022 19:52:24 -0800 Subject: [PATCH 02/30] Add series, ax to Weapon type --- types/Weapon.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/types/Weapon.d.ts b/types/Weapon.d.ts index 0f3c1ead..9ec3d57a 100644 --- a/types/Weapon.d.ts +++ b/types/Weapon.d.ts @@ -7,6 +7,8 @@ interface Weapon { proficiency: number max_level: number max_skill_level: number + series?: number + ax: number name: { en: string jp: string From 0d09c0adf8047545133291caa1a66bb4bb7a4a4b Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 1 Mar 2022 19:52:27 -0800 Subject: [PATCH 03/30] Delete hello.ts --- pages/api/hello.ts | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 pages/api/hello.ts diff --git a/pages/api/hello.ts b/pages/api/hello.ts deleted file mode 100644 index f8bcc7e5..00000000 --- a/pages/api/hello.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import type { NextApiRequest, NextApiResponse } from 'next' - -type Data = { - name: string -} - -export default function handler( - req: NextApiRequest, - res: NextApiResponse -) { - res.status(200).json({ name: 'John Doe' }) -} From bbca27d82cee4ed9275ed4057bbe8c79df9ae3fd Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 1 Mar 2022 19:52:36 -0800 Subject: [PATCH 04/30] Add Radix toggle group package --- package-lock.json | 186 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 187 insertions(+) diff --git a/package-lock.json b/package-lock.json index e8da421c..025a9370 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@radix-ui/react-hover-card": "^0.1.3", "@radix-ui/react-label": "^0.1.4", "@radix-ui/react-switch": "^0.1.4", + "@radix-ui/react-toggle-group": "^0.1.5", "@svgr/webpack": "^6.2.0", "@types/axios": "^0.14.0", "axios": "^0.25.0", @@ -2630,6 +2631,107 @@ "react": "^16.8 || ^17.0" } }, + "node_modules/@radix-ui/react-toggle": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-0.1.4.tgz", + "integrity": "sha512-gxUq6NgMc4ChV8VJnwdYqueeoblspwXHAexYo+jM9N2hFLbI1C587jLjdTHzIcUa9q68Xaw4jtiImWDOokEhRw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "0.1.0", + "@radix-ui/react-primitive": "0.1.4", + "@radix-ui/react-use-controllable-state": "0.1.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0" + } + }, + "node_modules/@radix-ui/react-toggle-group": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-0.1.5.tgz", + "integrity": "sha512-Yp14wFiqe00azF+sG5CCJz4JGOP/f5Jj+CxLlZCmMpG5qhVTWeaeG4YH6pvX4KL41fS8x9FAaLb8wW9y01o67g==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "0.1.0", + "@radix-ui/react-context": "0.1.1", + "@radix-ui/react-primitive": "0.1.4", + "@radix-ui/react-roving-focus": "0.1.5", + "@radix-ui/react-toggle": "0.1.4", + "@radix-ui/react-use-controllable-state": "0.1.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0" + } + }, + "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-collection": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-0.1.4.tgz", + "integrity": "sha512-3muGI15IdgaDFjOcO7xX8a35HQRBRF6LH9pS6UCeZeRmbslkVeHyJRQr2rzICBUoX7zgIA0kXyMDbpQnJGyJTA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "0.1.0", + "@radix-ui/react-context": "0.1.1", + "@radix-ui/react-primitive": "0.1.4", + "@radix-ui/react-slot": "0.1.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0" + } + }, + "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-id": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-0.1.5.tgz", + "integrity": "sha512-IPc4H/63bes0IZ1GJJozSEkSWcDyhNGtKFWUpJ+XtaLyQ1X3x7Mf6fWwWhDcpqlYEP+5WtAvfqcyEsyjP+ZhBQ==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "0.1.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0" + } + }, + "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-primitive": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz", + "integrity": "sha512-6gSl2IidySupIMJFjYnDIkIWRyQdbu/AHK7rbICPani+LW4b0XdxBXc46og/iZvuwW8pjCS8I2SadIerv84xYA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "0.1.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0" + } + }, + "node_modules/@radix-ui/react-toggle-group/node_modules/@radix-ui/react-roving-focus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-0.1.5.tgz", + "integrity": "sha512-ClwKPS5JZE+PaHCoW7eu1onvE61pDv4kO8W4t5Ra3qMFQiTJLZMdpBQUhksN//DaVygoLirz4Samdr5Y1x1FSA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "0.1.0", + "@radix-ui/react-collection": "0.1.4", + "@radix-ui/react-compose-refs": "0.1.0", + "@radix-ui/react-context": "0.1.1", + "@radix-ui/react-id": "0.1.5", + "@radix-ui/react-primitive": "0.1.4", + "@radix-ui/react-use-callback-ref": "0.1.0", + "@radix-ui/react-use-controllable-state": "0.1.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0" + } + }, + "node_modules/@radix-ui/react-toggle/node_modules/@radix-ui/react-primitive": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz", + "integrity": "sha512-6gSl2IidySupIMJFjYnDIkIWRyQdbu/AHK7rbICPani+LW4b0XdxBXc46og/iZvuwW8pjCS8I2SadIerv84xYA==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "0.1.2" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0" + } + }, "node_modules/@radix-ui/react-use-body-pointer-events": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-body-pointer-events/-/react-use-body-pointer-events-0.1.0.tgz", @@ -8660,6 +8762,90 @@ "@radix-ui/react-use-size": "0.1.0" } }, + "@radix-ui/react-toggle": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-0.1.4.tgz", + "integrity": "sha512-gxUq6NgMc4ChV8VJnwdYqueeoblspwXHAexYo+jM9N2hFLbI1C587jLjdTHzIcUa9q68Xaw4jtiImWDOokEhRw==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "0.1.0", + "@radix-ui/react-primitive": "0.1.4", + "@radix-ui/react-use-controllable-state": "0.1.0" + }, + "dependencies": { + "@radix-ui/react-primitive": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz", + "integrity": "sha512-6gSl2IidySupIMJFjYnDIkIWRyQdbu/AHK7rbICPani+LW4b0XdxBXc46og/iZvuwW8pjCS8I2SadIerv84xYA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "0.1.2" + } + } + } + }, + "@radix-ui/react-toggle-group": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-0.1.5.tgz", + "integrity": "sha512-Yp14wFiqe00azF+sG5CCJz4JGOP/f5Jj+CxLlZCmMpG5qhVTWeaeG4YH6pvX4KL41fS8x9FAaLb8wW9y01o67g==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "0.1.0", + "@radix-ui/react-context": "0.1.1", + "@radix-ui/react-primitive": "0.1.4", + "@radix-ui/react-roving-focus": "0.1.5", + "@radix-ui/react-toggle": "0.1.4", + "@radix-ui/react-use-controllable-state": "0.1.0" + }, + "dependencies": { + "@radix-ui/react-collection": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-0.1.4.tgz", + "integrity": "sha512-3muGI15IdgaDFjOcO7xX8a35HQRBRF6LH9pS6UCeZeRmbslkVeHyJRQr2rzICBUoX7zgIA0kXyMDbpQnJGyJTA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "0.1.0", + "@radix-ui/react-context": "0.1.1", + "@radix-ui/react-primitive": "0.1.4", + "@radix-ui/react-slot": "0.1.2" + } + }, + "@radix-ui/react-id": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-0.1.5.tgz", + "integrity": "sha512-IPc4H/63bes0IZ1GJJozSEkSWcDyhNGtKFWUpJ+XtaLyQ1X3x7Mf6fWwWhDcpqlYEP+5WtAvfqcyEsyjP+ZhBQ==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "0.1.0" + } + }, + "@radix-ui/react-primitive": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-0.1.4.tgz", + "integrity": "sha512-6gSl2IidySupIMJFjYnDIkIWRyQdbu/AHK7rbICPani+LW4b0XdxBXc46og/iZvuwW8pjCS8I2SadIerv84xYA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "0.1.2" + } + }, + "@radix-ui/react-roving-focus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-0.1.5.tgz", + "integrity": "sha512-ClwKPS5JZE+PaHCoW7eu1onvE61pDv4kO8W4t5Ra3qMFQiTJLZMdpBQUhksN//DaVygoLirz4Samdr5Y1x1FSA==", + "requires": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "0.1.0", + "@radix-ui/react-collection": "0.1.4", + "@radix-ui/react-compose-refs": "0.1.0", + "@radix-ui/react-context": "0.1.1", + "@radix-ui/react-id": "0.1.5", + "@radix-ui/react-primitive": "0.1.4", + "@radix-ui/react-use-callback-ref": "0.1.0", + "@radix-ui/react-use-controllable-state": "0.1.0" + } + } + } + }, "@radix-ui/react-use-body-pointer-events": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-body-pointer-events/-/react-use-body-pointer-events-0.1.0.tgz", diff --git a/package.json b/package.json index fa87c27b..c800aaa3 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@radix-ui/react-hover-card": "^0.1.3", "@radix-ui/react-label": "^0.1.4", "@radix-ui/react-switch": "^0.1.4", + "@radix-ui/react-toggle-group": "^0.1.5", "@svgr/webpack": "^6.2.0", "@types/axios": "^0.14.0", "axios": "^0.25.0", From 7cae5eebfe327fc9760cfce31ef07fab52b3fc64 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 1 Mar 2022 19:53:08 -0800 Subject: [PATCH 05/30] Display button on moddable weapons --- components/WeaponUnit/index.scss | 15 +++++++++++++ components/WeaponUnit/index.tsx | 37 +++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/components/WeaponUnit/index.scss b/components/WeaponUnit/index.scss index e8bb254d..ccda269a 100644 --- a/components/WeaponUnit/index.scss +++ b/components/WeaponUnit/index.scss @@ -3,11 +3,16 @@ flex-direction: column; gap: 4px; min-height: 139px; + position: relative; @media (max-width: $medium-screen) { min-height: auto; } + &:hover .Button { + display: block; + } + &.editable .WeaponImage:hover { border: $hover-stroke; box-shadow: $hover-shadow; @@ -66,6 +71,16 @@ display: none; } + .Button { + background: white; + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.14); + display: none; + position: absolute; + left: $unit; + top: $unit; + z-index: 3; + } + h3 { color: $grey-00; font-size: $font-button; diff --git a/components/WeaponUnit/index.tsx b/components/WeaponUnit/index.tsx index f8541653..c171fe8d 100644 --- a/components/WeaponUnit/index.tsx +++ b/components/WeaponUnit/index.tsx @@ -2,9 +2,13 @@ import React, { useEffect, useState } from 'react' import classnames from 'classnames' import SearchModal from '~components/SearchModal' +import WeaponModal from '~components/WeaponModal' import UncapIndicator from '~components/UncapIndicator' -import PlusIcon from '~public/icons/Add.svg' +import Button from '~components/Button' +import { ButtonType } from '~utils/enums' + +import PlusIcon from '~public/icons/Add.svg' import './index.scss' interface Props { @@ -53,6 +57,14 @@ const WeaponUnit = (props: Props) => { props.updateUncap(props.gridWeapon.id, props.position, uncap) } + function canBeModified(gridWeapon: GridWeapon) { + const weapon = gridWeapon.object + + console.log(`${weapon.name.en}: ${weapon.series} ${weapon.ax > 0 || (weapon.series) && [2, 3, 8, 22, 25].includes(weapon.series)}`) + return weapon.ax > 0 || + (weapon.series) && [2, 3, 8, 22, 25].includes(weapon.series) + } + const image = (
{weapon?.name.en} @@ -61,18 +73,27 @@ const WeaponUnit = (props: Props) => { ) const editableImage = ( - - {image} - +
+ + {image} + +
) + console.log(gridWeapon?.object) return (
+ { (props.editable && gridWeapon && canBeModified(gridWeapon)) ? + +
+
+
: '' } { (props.editable) ? editableImage : image } { (gridWeapon) ? Date: Tue, 1 Mar 2022 19:53:23 -0800 Subject: [PATCH 06/30] Added ElementToggle component A segmented control for selecting an element --- components/ElementToggle/index.scss | 59 +++++++++++++++++++++++++++++ components/ElementToggle/index.tsx | 38 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 components/ElementToggle/index.scss create mode 100644 components/ElementToggle/index.tsx diff --git a/components/ElementToggle/index.scss b/components/ElementToggle/index.scss new file mode 100644 index 00000000..c97a1d19 --- /dev/null +++ b/components/ElementToggle/index.scss @@ -0,0 +1,59 @@ +.ToggleGroup { + $height: 36px; + + border: 1px solid rgba(0, 0, 0, 0.14); + border-radius: $height; + display: flex; + height: $height; + gap: $unit / 4; + padding: $unit / 2; + + .ToggleItem { + background: white; + border: none; + border-radius: 18px; + color: $grey-40; + flex-grow: 1; + font-size: $font-regular; + padding: ($unit) $unit * 2; + + &:hover { + cursor: pointer; + } + + &:hover, &[data-state="on"] { + background:$grey-80; + color: $grey-00; + + &.fire { + background: $fire-bg-light; + color: $fire-text-dark; + } + + &.water { + background: $water-bg-light; + color: $water-text-dark; + } + + &.earth { + background: $earth-bg-light; + color: $earth-text-dark; + } + + &.wind { + background: $wind-bg-light; + color: $wind-text-dark; + } + + &.dark { + background: $dark-bg-light; + color: $dark-text-dark; + } + + &.light { + background: $light-bg-light; + color: $light-text-dark; + } + } + } +} \ No newline at end of file diff --git a/components/ElementToggle/index.tsx b/components/ElementToggle/index.tsx new file mode 100644 index 00000000..6e09920a --- /dev/null +++ b/components/ElementToggle/index.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import * as ToggleGroup from '@radix-ui/react-toggle-group' + +import './index.scss' + +interface Props { + currentElement: number +} + +const ElementToggle = (props: Props) => { + return ( + + + Null + + + Wind + + + Fire + + + Water + + + Earth + + + Dark + + + Light + + + ) +} + +export default ElementToggle \ No newline at end of file From 5d07d72073f5c4bed30e92b4b74d7900c602d6ae Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 1 Mar 2022 19:53:30 -0800 Subject: [PATCH 07/30] Update Settings.svg --- public/icons/Settings.svg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/icons/Settings.svg b/public/icons/Settings.svg index 6ffc10a7..9e8741c4 100644 --- a/public/icons/Settings.svg +++ b/public/icons/Settings.svg @@ -1,6 +1,6 @@ - - + + - + From 869547dc93f377a0c41968d821b9f26ce35d7620 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 1 Mar 2022 19:53:40 -0800 Subject: [PATCH 08/30] Add settings icon to Button --- components/Button/index.scss | 5 +++++ components/Button/index.tsx | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/components/Button/index.scss b/components/Button/index.scss index 616c9cf0..4f363ce5 100644 --- a/components/Button/index.scss +++ b/components/Button/index.scss @@ -86,6 +86,11 @@ fill: none; stroke: $grey-50; } + + &.settings svg { + height: 13px; + width: 13px; + } } &.Active { diff --git a/components/Button/index.tsx b/components/Button/index.tsx index 3fcd68f5..4d670555 100644 --- a/components/Button/index.tsx +++ b/components/Button/index.tsx @@ -9,6 +9,7 @@ import EditIcon from '~public/icons/Edit.svg' import LinkIcon from '~public/icons/Link.svg' import MenuIcon from '~public/icons/Menu.svg' import SaveIcon from '~public/icons/Save.svg' +import SettingsIcon from '~public/icons/Settings.svg' import './index.scss' @@ -68,6 +69,10 @@ class Button extends React.Component { icon = + } else if (this.props.icon === 'settings') { + icon = + + } const classes = classNames({ From b46bafa5f743404e9789bf40236bc8454a88c1a6 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 1 Mar 2022 19:53:54 -0800 Subject: [PATCH 09/30] Add a modal for modifying weapons --- components/WeaponModal/index.scss | 12 ++++++++ components/WeaponModal/index.tsx | 47 +++++++++++++++++++++++++++++++ styles/globals.scss | 14 +++++++++ 3 files changed, 73 insertions(+) create mode 100644 components/WeaponModal/index.scss create mode 100644 components/WeaponModal/index.tsx diff --git a/components/WeaponModal/index.scss b/components/WeaponModal/index.scss new file mode 100644 index 00000000..97b53507 --- /dev/null +++ b/components/WeaponModal/index.scss @@ -0,0 +1,12 @@ +.Weapon.Dialog { + section { + display: flex; + flex-direction: column; + gap: $unit / 2; + + h3 { + color: $grey-50; + font-size: $font-small; + } + } +} \ No newline at end of file diff --git a/components/WeaponModal/index.tsx b/components/WeaponModal/index.tsx new file mode 100644 index 00000000..a6df76ac --- /dev/null +++ b/components/WeaponModal/index.tsx @@ -0,0 +1,47 @@ +import React from 'react' +import * as Dialog from '@radix-ui/react-dialog' + +import ElementToggle from '~components/ElementToggle' + +import CrossIcon from '~public/icons/Cross.svg' +import './index.scss' + +interface Props { + gridWeapon: GridWeapon + children: React.ReactNode +} + +const WeaponModal = (props: Props) => { + return ( + + + { props.children } + + + event.preventDefault() }> +
+
+ Modify Weapon + {props.gridWeapon.object.name.en} +
+ + + + + +
+ +
+ { (props.gridWeapon.object.element == 0) ? + + : ''} +
+
+ +
+
+ ) +} + +export default WeaponModal \ No newline at end of file diff --git a/styles/globals.scss b/styles/globals.scss index b70a95db..4f08d3e5 100644 --- a/styles/globals.scss +++ b/styles/globals.scss @@ -110,6 +110,7 @@ select { .DialogHeader { display: flex; + align-items: center; gap: $unit; .left { @@ -150,6 +151,19 @@ select { flex-grow: 1; } + .DialogTop { + display: flex; + flex-direction: column; + flex-grow: 1; + gap: $unit / 2; + + .SubTitle { + color: $grey-50; + font-size: $font-small; + font-weight: $medium; + } + } + .DialogDescription { flex-grow: 1; } From 5256964dd92f1a35405f265c8ce8ad75eadffb0a Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:07:33 -0800 Subject: [PATCH 10/30] Add AX Data object This is our reference for what AX skills go together and what the min/max strength values are --- utils/axData.tsx | 791 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 791 insertions(+) create mode 100644 utils/axData.tsx diff --git a/utils/axData.tsx b/utils/axData.tsx new file mode 100644 index 00000000..8ea007f4 --- /dev/null +++ b/utils/axData.tsx @@ -0,0 +1,791 @@ +export const axData: AxSkill[][] = [ + [ + { + name: { + "en": "ATK", + "jp": "攻撃" + }, + id: 0, + minValue: 1, + maxValue: 3.5, + suffix: '%', + secondary: [ + { + name: { + "en": "C.A. DMG", + "jp": "奥義ダメ" + }, + id: 3, + minValue: 2, + maxValue: 4, + suffix: '%' + }, + { + name: { + "en": "Double Attack Rate", + "jp": "DA確率" + }, + id: 5, + minValue: 1, + maxValue: 2, + suffix: '%' + }, + { + name: { + "en": "Triple Attack Rate", + "jp": "TA確率" + }, + id: 6, + minValue: 1, + maxValue: 2, + suffix: '%' + }, + { + name: { + "en": "Skill DMG Cap", + "jp": "アビ上限" + }, + id: 7, + minValue: 1, + maxValue: 2, + suffix: '%' + } + ] + }, + { + name: { + "en": "DEF", + "jp": "防御" + }, + id: 1, + minValue: 1, + maxValue: 8, + suffix: '%', + secondary: [ + { + name: { + "en": "HP", + "jp": "HP" + }, + id: 2, + minValue: 1, + maxValue: 3, + suffix: '%' + }, + { + name: { + "en": "Debuff Resistance", + "jp": "弱体耐性" + }, + id: 9, + minValue: 1, + maxValue: 3, + suffix: '%' + }, + { + name: { + "en": "Healing", + "jp": "回復性能" + }, + id: 10, + minValue: 2, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "Enmity", + "jp": "背水" + }, + id: 11, + minValue: 1, + maxValue: 3 + } + ] + }, + { + name: { + "en": "HP", + "jp": "HP" + }, + id: 2, + minValue: 1, + maxValue: 11, + suffix: '%', + secondary: [ + { + name: { + "en": "DEF", + "jp": "防御" + }, + id: 1, + minValue: 1, + maxValue: 3, + suffix: '%' + }, + { + name: { + "en": "Debuff Resistance", + "jp": "弱体耐性" + }, + id: 9, + minValue: 1, + maxValue: 3, + suffix: '%' + }, + { + name: { + "en": "Healing", + "jp": "回復性能" + }, + id: 10, + minValue: 2, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "Stamina", + "jp": "渾身" + }, + id: 12, + minValue: 1, + maxValue: 3 + } + ] + }, + { + name: { + "en": "C.A. DMG", + "jp": "奥義ダメ" + }, + id: 3, + minValue: 2, + maxValue: 8.5, + suffix: '%', + secondary: [ + { + name: { + "en": "ATK", + "jp": "攻撃" + }, + id: 0, + minValue: 1, + maxValue: 1.5, + suffix: '%' + }, + { + name: { + "en": "Elemental ATK", + "jp": "全属性攻撃力" + }, + id: 13, + minValue: 1, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "C.A. DMG Cap", + "jp": "奥義上限" + }, + id: 8, + minValue: 1, + maxValue: 2, + suffix: '%' + }, + { + name: { + "en": "Stamina", + "jp": "渾身" + }, + id: 12, + minValue: 1, + maxValue: 3 + } + ] + }, + { + name: { + "en": "Multiattack Rate", + "jp": "連撃率" + }, + id: 4, + minValue: 1, + maxValue: 4, + suffix: '%', + secondary: [ + { + name: { + "en": "C.A. DMG", + "jp": "奥義ダメ" + }, + id: 3, + minValue: 2, + maxValue: 4, + suffix: '%' + }, + { + name: { + "en": "Elemental ATK", + "jp": "全属性攻撃力" + }, + id: 13, + minValue: 1, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "Double Attack Rate", + "jp": "DA確率" + }, + id: 5, + minValue: 1, + maxValue: 2, + suffix: '%' + }, + { + name: { + "en": "Triple Attack Rate", + "jp": "TA確率" + }, + id: 6, + minValue: 1, + maxValue: 2, + suffix: '%' + } + ] + } + ], [ + { + name: { + "en": "ATK", + "jp": "攻撃" + }, + id: 0, + minValue: 1, + maxValue: 3.5, + suffix: '%', + secondary: [ + { + name: { + "en": "C.A. DMG", + "jp": "奥義ダメ" + }, + id: 3, + minValue: 2, + maxValue: 8.5, + suffix: '%' + }, + { + name: { + "en": "Multiattack Rate", + "jp": "連撃確率" + }, + id: 4, + minValue: 1.5, + maxValue: 4, + suffix: '%' + }, + { + name: { + "en": "Normal ATK DMG Cap", + "jp": "通常ダメ上限" + }, + id: 14, + minValue: 0.5, + maxValue: 1.5, + suffix: '%' + }, + { + name: { + "en": "Supplemental Skill DMG", + "jp": "アビ与ダメ上昇" + }, + id: 15, + minValue: 1, + maxValue: 5 + } + ] + }, + { + name: { + "en": "DEF", + "jp": "防御" + }, + id: 1, + minValue: 1, + maxValue: 8, + suffix: '%', + secondary: [ + { + name: { + "en": "Elemental DMG Reduction", + "jp": "属性ダメ軽減" + }, + id: 17, + minValue: 1, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "Debuff Resistance", + "jp": "弱体耐性" + }, + id: 9, + minValue: 1, + maxValue: 3, + suffix: '%' + }, + { + name: { + "en": "Healing", + "jp": "回復性能" + }, + id: 10, + minValue: 2, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "Enmity", + "jp": "背水" + }, + id: 11, + minValue: 1, + maxValue: 3 + } + ] + }, + { + name: { + "en": "HP", + "jp": "HP" + }, + id: 2, + minValue: 1, + maxValue: 11, + suffix: '%', + secondary: [ + { + name: { + "en": "Elemental DMG Reduction", + "jp": "属性ダメ軽減" + }, + id: 17, + minValue: 1, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "Debuff Resistance", + "jp": "弱体耐性" + }, + id: 9, + minValue: 1, + maxValue: 3, + suffix: '%' + }, + { + name: { + "en": "Healing", + "jp": "回復性能" + }, + id: 10, + minValue: 2, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "Stamina", + "jp": "渾身" + }, + id: 12, + minValue: 1, + maxValue: 3 + } + ] + }, + { + name: { + "en": "C.A. DMG", + "jp": "奥義ダメ" + }, + id: 3, + minValue: 2, + maxValue: 8.5, + suffix: '%', + secondary: [ + { + name: { + "en": "Multiattack Rate", + "jp": "連撃率" + }, + id: 4, + minValue: 1.5, + maxValue: 4, + suffix: '%' + }, + { + name: { + "en": "Supplemental Skill DMG", + "jp": "アビ与ダメ上昇" + }, + id: 15, + minValue: 1, + maxValue: 5, + }, + { + name: { + "en": "Supplemental C.A. DMG", + "jp": "奥義与ダメ上昇" + }, + id: 16, + minValue: 1, + maxValue: 5, + }, + { + name: { + "en": "Stamina", + "jp": "渾身" + }, + id: 12, + minValue: 1, + maxValue: 3 + } + ] + }, + { + name: { + "en": "Multiattack Rate", + "jp": "連撃率" + }, + id: 4, + minValue: 1, + maxValue: 4, + suffix: '%', + secondary: [ + { + name: { + "en": "Supplemental C.A. DMG", + "jp": "奥義与ダメ上昇" + }, + id: 16, + minValue: 1, + maxValue: 5, + }, + { + name: { + "en": "Normal ATK DMG Cap", + "jp": "通常ダメ上限" + }, + id: 14, + minValue: 0.5, + maxValue: 1.5, + suffix: '%' + }, + { + name: { + "en": "Stamina", + "jp": "渾身" + }, + id: 12, + minValue: 1, + maxValue: 3, + }, + { + name: { + "en": "Enmity", + "jp": "背水" + }, + id: 11, + minValue: 1, + maxValue: 3, + } + ] + } + ], [ + { + name: { + "en": "ATK", + "jp": "攻撃" + }, + id: 0, + minValue: 1, + maxValue: 3.5, + suffix: '%', + secondary: [ + { + name: { + "en": "C.A. DMG", + "jp": "奥義ダメ" + }, + id: 3, + minValue: 2, + maxValue: 4, + suffix: '%' + }, + { + name: { + "en": "Double Attack Rate", + "jp": "DA確率" + }, + id: 5, + minValue: 1, + maxValue: 2, + suffix: '%' + }, + { + name: { + "en": "Triple Attack Rate", + "jp": "TA確率" + }, + id: 6, + minValue: 1, + maxValue: 2, + suffix: '%' + }, + { + name: { + "en": "Skill DMG Cap", + "jp": "アビ上限" + }, + id: 7, + minValue: 1, + maxValue: 2, + suffix: '%' + } + ] + }, + { + name: { + "en": "DEF", + "jp": "防御" + }, + id: 1, + minValue: 1, + maxValue: 8, + suffix: '%', + secondary: [ + { + name: { + "en": "HP", + "jp": "HP" + }, + id: 2, + minValue: 1, + maxValue: 3, + suffix: '%' + }, + { + name: { + "en": "Debuff Resistance", + "jp": "弱体耐性" + }, + id: 9, + minValue: 1, + maxValue: 3, + suffix: '%' + }, + { + name: { + "en": "Healing", + "jp": "回復性能" + }, + id: 10, + minValue: 2, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "Enmity", + "jp": "背水" + }, + id: 11, + minValue: 1, + maxValue: 3 + } + ] + }, + { + name: { + "en": "HP", + "jp": "HP" + }, + id: 2, + minValue: 1, + maxValue: 11, + suffix: '%', + secondary: [ + { + name: { + "en": "DEF", + "jp": "防御" + }, + id: 1, + minValue: 1, + maxValue: 3, + suffix: '%' + }, + { + name: { + "en": "Debuff Resistance", + "jp": "弱体耐性" + }, + id: 9, + minValue: 1, + maxValue: 3, + suffix: '%' + }, + { + name: { + "en": "Healing", + "jp": "回復性能" + }, + id: 10, + minValue: 2, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "Stamina", + "jp": "渾身" + }, + id: 12, + minValue: 1, + maxValue: 3 + } + ] + }, + { + name: { + "en": "C.A. DMG", + "jp": "奥義ダメ" + }, + id: 3, + minValue: 2, + maxValue: 8.5, + suffix: '%', + secondary: [ + { + name: { + "en": "ATK", + "jp": "攻撃" + }, + id: 0, + minValue: 1, + maxValue: 1.5, + suffix: '%' + }, + { + name: { + "en": "Elemental ATK", + "jp": "全属性攻撃力" + }, + id: 13, + minValue: 1, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "C.A. DMG Cap", + "jp": "奥義上限" + }, + id: 8, + minValue: 1, + maxValue: 2, + suffix: '%' + }, + { + name: { + "en": "Stamina", + "jp": "渾身" + }, + id: 12, + minValue: 1, + maxValue: 3 + } + ] + }, + { + name: { + "en": "Multiattack Rate", + "jp": "連撃率" + }, + id: 4, + minValue: 1, + maxValue: 4, + suffix: '%', + secondary: [ + { + name: { + "en": "C.A. DMG", + "jp": "奥義ダメ" + }, + id: 3, + minValue: 2, + maxValue: 4, + suffix: '%' + }, + { + name: { + "en": "Elemental ATK", + "jp": "全属性攻撃力" + }, + id: 13, + minValue: 1, + maxValue: 5, + suffix: '%' + }, + { + name: { + "en": "Double Attack Rate", + "jp": "DA確率" + }, + id: 5, + minValue: 1, + maxValue: 2, + suffix: '%' + }, + { + name: { + "en": "Triple Attack Rate", + "jp": "TA確率" + }, + id: 6, + minValue: 1, + maxValue: 2, + suffix: '%' + } + ] + }, + { + name: { + "en": "EXP Up", + "jp": "EXP UP" + }, + id: 18, + minValue: 5, + maxValue: 10, + suffix: '%' + }, + { + name: { + "en": "Rupies", + "jp": "獲得ルピ" + }, + id: 19, + minValue: 10, + maxValue: 20, + suffix: '%' + } + ] +] \ No newline at end of file From 66ebab74ab739600245fcbefff37971b6439befa Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:07:40 -0800 Subject: [PATCH 11/30] Create WeaponKey.d.ts --- types/WeaponKey.d.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 types/WeaponKey.d.ts diff --git a/types/WeaponKey.d.ts b/types/WeaponKey.d.ts new file mode 100644 index 00000000..d5541baa --- /dev/null +++ b/types/WeaponKey.d.ts @@ -0,0 +1,11 @@ +interface WeaponKey { + id: string + name: { + en: string, + jp: string + } + series: integer + slot: integer + group: integer + order: integer +} \ No newline at end of file From 6d0227c6b77b97968374e5b093a4b16dffef6edc Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:07:44 -0800 Subject: [PATCH 12/30] Create AxSkill.d.ts --- types/AxSkill.d.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 types/AxSkill.d.ts diff --git a/types/AxSkill.d.ts b/types/AxSkill.d.ts new file mode 100644 index 00000000..69d42b39 --- /dev/null +++ b/types/AxSkill.d.ts @@ -0,0 +1,11 @@ +interface AxSkill { + name: { + en: string, + jp: string + }, + id: number, + minValue: number, + maxValue: number, + suffix?: string, + secondary?: AxSkill[] +} \ No newline at end of file From a8544610dc30aacdcb6168f5958344d7ea990ff5 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:07:47 -0800 Subject: [PATCH 13/30] Create SimpleAxSkill.d.ts --- types/SimpleAxSkill.d.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 types/SimpleAxSkill.d.ts diff --git a/types/SimpleAxSkill.d.ts b/types/SimpleAxSkill.d.ts new file mode 100644 index 00000000..e13171e7 --- /dev/null +++ b/types/SimpleAxSkill.d.ts @@ -0,0 +1,4 @@ +interface SimpleAxSkill { + modifier: number + strength: number +} \ No newline at end of file From 97091050fda7fbc4843a56b30980f8bd7115fedb Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:08:00 -0800 Subject: [PATCH 14/30] Make series not optional in Weapon type --- types/Weapon.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/Weapon.d.ts b/types/Weapon.d.ts index 9ec3d57a..d26caa7d 100644 --- a/types/Weapon.d.ts +++ b/types/Weapon.d.ts @@ -7,7 +7,7 @@ interface Weapon { proficiency: number max_level: number max_skill_level: number - series?: number + series: number ax: number name: { en: string From 9cef2ce89f2f6e6c0062d363a351817e45b62279 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:08:11 -0800 Subject: [PATCH 15/30] Add element, ax and weapon_keys to GridWeapon type --- types/GridWeapon.d.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/types/GridWeapon.d.ts b/types/GridWeapon.d.ts index f2d7dd37..954812ac 100644 --- a/types/GridWeapon.d.ts +++ b/types/GridWeapon.d.ts @@ -4,4 +4,7 @@ interface GridWeapon { position: number object: Weapon uncap_level: number + element: number + weapon_keys?: WeaponKey[] + ax?: SimpleAxSkill[] } \ No newline at end of file From 610810b8a19f0ca9265dd58fbc6381d56d261335 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:08:16 -0800 Subject: [PATCH 16/30] Update globals.scss --- styles/globals.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/globals.scss b/styles/globals.scss index 4f08d3e5..4f26f3f7 100644 --- a/styles/globals.scss +++ b/styles/globals.scss @@ -49,7 +49,7 @@ select { background-image: url('/icons/Arrow.svg'); background-repeat: no-repeat; background-position-y: center; - background-position-x: 98%; + background-position-x: 97%; background-size: $unit * 1.5; border: none; border-radius: 6px; From a64d9c02e449ee2c54591fc9af62a378e4145428 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:08:31 -0800 Subject: [PATCH 17/30] Add darker Arrow icon We can't change SVG color when used as a CSS background image --- public/icons/ArrowDark.svg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 public/icons/ArrowDark.svg diff --git a/public/icons/ArrowDark.svg b/public/icons/ArrowDark.svg new file mode 100644 index 00000000..156139cb --- /dev/null +++ b/public/icons/ArrowDark.svg @@ -0,0 +1,3 @@ + + + From 01db78c4229f26f51350fb27b4c80cdcbcb2f29a Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:09:12 -0800 Subject: [PATCH 18/30] Add WeaponKeyDropdown component and API method --- components/WeaponKeyDropdown/index.scss | 0 components/WeaponKeyDropdown/index.tsx | 139 ++++++++++++++++++++++++ utils/api.tsx | 1 + 3 files changed, 140 insertions(+) create mode 100644 components/WeaponKeyDropdown/index.scss create mode 100644 components/WeaponKeyDropdown/index.tsx diff --git a/components/WeaponKeyDropdown/index.scss b/components/WeaponKeyDropdown/index.scss new file mode 100644 index 00000000..e69de29b diff --git a/components/WeaponKeyDropdown/index.tsx b/components/WeaponKeyDropdown/index.tsx new file mode 100644 index 00000000..59680eaf --- /dev/null +++ b/components/WeaponKeyDropdown/index.tsx @@ -0,0 +1,139 @@ +import React, { useCallback, useEffect, useState } from 'react' + +import api from '~utils/api' + +import './index.scss' + +// Props +interface Props { + currentValue?: WeaponKey + series: number + slot: number + onChange?: (event: React.ChangeEvent) => void + onBlur?: (event: React.ChangeEvent) => void +} + +const WeaponKeyDropdown = React.forwardRef(function useFieldSet(props, ref) { + const [keys, setKeys] = useState([]) + + const pendulumNames = [ + { en: 'Pendulum', jp: '' }, + { en: 'Chain', 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: '' } + ] + + const keyName = () => { + return { + en: '', + jp: '' + } + } + + const emptyKey: WeaponKey = { + id: '0', + name: { + en: `No ${keyName().en}`, + jp: `${keyName().jp}なし` + }, + series: props.series, + slot: props.slot, + group: -1 + } + + const organizeWeaponKeys = useCallback((weaponKeys: WeaponKey[]) => { + const numGroups = Math.max.apply(Math, weaponKeys.map(key => key.group)) + let groupedKeys = [] + for (let i = 0; i <= numGroups; i++) { + groupedKeys[i] = weaponKeys.filter(key => key.group == i) + } + + setKeys(groupedKeys) + }, []) + + useEffect(() => { + const filterParams = { + params: { + series: props.series, + slot: props.slot + } + } + + function fetchWeaponKeys() { + api.endpoints.weapon_keys.getAll(filterParams) + .then((response) => { + const keys = response.data.map((k: any) => k.weapon_key) + organizeWeaponKeys(keys) + }) + } + + fetchWeaponKeys() + }, [organizeWeaponKeys]) + + function weaponKeyGroup(index: number) { + ['α','β','γ','Δ'].sort((a, b) => a.localeCompare(b, 'el')) + + const sortByOrder = (a: WeaponKey, b: WeaponKey) => a.order > b.order ? 1 : -1 + + const options = keys && keys.length > 0 && keys[index].length > 0 && + keys[index].sort(sortByOrder).map((item, i) => { + return ( + + ) + }) + + let name: { [key: string]: string } = {} + 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 == 17) + name = gauphNames[props.slot] + else if (props.series == 22) + name = emblemNames[index] + + return ( + + {options} + + ) + } + + const emptyOption = () => { + let name = '' + 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 + + return `No ${name}` + } + + return ( + + ) +}) + +export default WeaponKeyDropdown diff --git a/utils/api.tsx b/utils/api.tsx index 61d49454..1e164649 100644 --- a/utils/api.tsx +++ b/utils/api.tsx @@ -107,6 +107,7 @@ api.createEntity( { name: 'characters' }) api.createEntity( { name: 'weapons' }) api.createEntity( { name: 'summons' }) api.createEntity( { name: 'raids' }) +api.createEntity( { name: 'weapon_keys' }) api.createEntity( { name: 'favorites' }) export default api \ No newline at end of file From 97fb9420dbeaa037e57e79c67546c66692ba2f7a Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:10:25 -0800 Subject: [PATCH 19/30] Add onValueChange hook to send data up --- components/ElementToggle/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/ElementToggle/index.tsx b/components/ElementToggle/index.tsx index 6e09920a..68486bdb 100644 --- a/components/ElementToggle/index.tsx +++ b/components/ElementToggle/index.tsx @@ -5,11 +5,12 @@ import './index.scss' interface Props { currentElement: number + sendValue: (value: string) => void } const ElementToggle = (props: Props) => { return ( - + Null From 1d0c24242918ebb215a3fbe8fe939e997a45ef48 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:10:37 -0800 Subject: [PATCH 20/30] Add AxSelect component and API call --- components/AxSelect/index.scss | 30 +++++++ components/AxSelect/index.tsx | 159 +++++++++++++++++++++++++++++++++ utils/api.tsx | 1 + 3 files changed, 190 insertions(+) create mode 100644 components/AxSelect/index.scss create mode 100644 components/AxSelect/index.tsx diff --git a/components/AxSelect/index.scss b/components/AxSelect/index.scss new file mode 100644 index 00000000..52aab9b0 --- /dev/null +++ b/components/AxSelect/index.scss @@ -0,0 +1,30 @@ +.AXSet { + display: flex; + flex-direction: row; + gap: $unit; + + &.hidden { + display: none; + } + + select { + flex-grow: 1; + } + + .Input { + -webkit-font-smoothing: antialiased; + border: none; + background-color: $grey-90; + border-radius: 6px; + box-sizing: border-box; + color: $grey-00; + height: $unit * 6; + display: block; + font-size: $font-regular; + padding: $unit; + text-align: right; + min-width: 100px; + width: 100px; + } + +} \ No newline at end of file diff --git a/components/AxSelect/index.tsx b/components/AxSelect/index.tsx new file mode 100644 index 00000000..b6798e74 --- /dev/null +++ b/components/AxSelect/index.tsx @@ -0,0 +1,159 @@ +import React, { useEffect, useState } from 'react' +import classNames from 'classnames' + +import { axData } from '~utils/axData' + +import './index.scss' + +interface Props { + axType: number + currentSkills?: SimpleAxSkill[], + sendValues: (primaryAxModifier: number, primaryAxValue: number, secondaryAxModifier: number, secondaryAxValue: number) => void +} + +const AXSelect = (props: Props) => { + // Refs + const primaryAxModifierSelect = React.createRef() + const primaryAxValueInput = React.createRef() + const secondaryAxModifierSelect = React.createRef() + const secondaryAxValueInput = React.createRef() + + // States + const [primaryAxModifier, setPrimaryAxModifier] = useState(-1) + const [secondaryAxModifier, setSecondaryAxModifier] = useState(-1) + const [primaryAxValue, setPrimaryAxValue] = useState(0.0) + const [secondaryAxValue, setSecondaryAxValue] = useState(0.0) + + useEffect(() => { + if (props.currentSkills && props.currentSkills[0]) { + if (props.currentSkills[0].modifier != null) + setPrimaryAxModifier(props.currentSkills[0].modifier) + + setPrimaryAxValue(props.currentSkills[0].strength) + } + + if (props.currentSkills && props.currentSkills[1]) { + if (props.currentSkills[1].modifier != null) + setSecondaryAxModifier(props.currentSkills[1].modifier) + + setSecondaryAxValue(props.currentSkills[1].strength) + } + }, [props.currentSkills]) + + useEffect(() => { + props.sendValues(primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue) + }, [primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue]) + + // Classes + const secondarySetClasses = classNames({ + 'AXSet': true, + 'hidden': primaryAxModifier < 0 + }) + + function generateOptions(modifierSet: number) { + const axOptions = axData[props.axType - 1] + + let axOptionElements: React.ReactNode[] = [] + if (modifierSet == 0) { + axOptionElements = axOptions.map((ax, i) => { + return ( + + ) + }) + } else { + // If we are loading data from the server, state doesn't set before render, + // so our defaultValue is undefined. + let modifier = -1; + if (primaryAxModifier >= 0) + modifier = primaryAxModifier + else if (props.currentSkills) + modifier = props.currentSkills[0].modifier + + if (modifier >= 0 && axOptions[modifier]) { + const primarySkill = axOptions[modifier] + + if (primarySkill.secondary) { + const secondaryAxOptions = primarySkill.secondary + axOptionElements = secondaryAxOptions.map((ax, i) => { + return ( + + ) + }) + } + } + } + + axOptionElements?.unshift() + return axOptionElements + } + + function handleSelectChange(event: React.ChangeEvent) { + const value = parseInt(event.target.value) + + if (primaryAxModifierSelect.current == event.target) { + setPrimaryAxModifier(value) + + if (primaryAxValueInput.current && secondaryAxModifierSelect.current && secondaryAxValueInput.current) { + setupInput(axData[props.axType - 1][value], primaryAxValueInput.current) + + secondaryAxModifierSelect.current.value = "-1" + secondaryAxValueInput.current.value = "" + } + } else { + setSecondaryAxModifier(value) + + const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] + const currentAxSkill = (primaryAxSkill.secondary) ? + primaryAxSkill.secondary.find(skill => skill.id == value) : undefined + + if (secondaryAxValueInput.current) + setupInput(currentAxSkill, secondaryAxValueInput.current) + } + } + + function handleInputChange(event: React.ChangeEvent) { + if (primaryAxValueInput.current == event.target) { + setPrimaryAxValue(parseFloat(event.target.value)) + } else { + setSecondaryAxValue(parseFloat(event.target.value)) + } + } + + function setupInput(ax: AxSkill | undefined, element: HTMLInputElement) { + if (ax) { + const rangeString = `${ax.minValue}~${ax.maxValue}${ax.suffix || ''}` + + element.disabled = false + element.placeholder = rangeString + element.min = `${ax.minValue}` + element.max = `${ax.maxValue}` + element.step = (ax.suffix) ? "0.5" : "1" + } else { + if (primaryAxValueInput.current && secondaryAxValueInput.current) { + if (primaryAxValueInput.current == element) { + primaryAxValueInput.current.disabled = true + primaryAxValueInput.current.placeholder = '' + } + + secondaryAxValueInput.current.disabled = true + secondaryAxValueInput.current.placeholder = '' + } + } + } + + return ( +
+
+ + +
+ +
+ + +
+
+ ) +} + +export default AXSelect \ No newline at end of file diff --git a/utils/api.tsx b/utils/api.tsx index 1e164649..3781e275 100644 --- a/utils/api.tsx +++ b/utils/api.tsx @@ -103,6 +103,7 @@ class Api { const api: Api = new Api({ url: process.env.NEXT_PUBLIC_SIERO_API_URL || 'https://localhost:3000/api/v1'}) api.createEntity( { name: 'users' }) api.createEntity( { name: 'parties' }) +api.createEntity( { name: 'grid_weapons' }) api.createEntity( { name: 'characters' }) api.createEntity( { name: 'weapons' }) api.createEntity( { name: 'summons' }) From 657026e8b50a1365be77969ce58f843a2f8a6800 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:10:56 -0800 Subject: [PATCH 21/30] Remove cookies/headers We don't make any auth'd calls here --- components/RaidDropdown/index.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/components/RaidDropdown/index.tsx b/components/RaidDropdown/index.tsx index 208af245..47ad6b26 100644 --- a/components/RaidDropdown/index.tsx +++ b/components/RaidDropdown/index.tsx @@ -1,5 +1,4 @@ import React, { useCallback, useEffect, useState } from 'react' -import { useCookies } from 'react-cookie' import { appState } from '~utils/appState' import api from '~utils/api' @@ -15,7 +14,6 @@ interface Props { } const RaidDropdown = React.forwardRef(function useFieldSet(props, ref) { - const [cookies] = useCookies(['user']) const [raids, setRaids] = useState() const raidGroups = [ @@ -50,12 +48,8 @@ const RaidDropdown = React.forwardRef(function useFiel }, [props.allOption]) useEffect(() => { - const headers = (cookies.user != null) ? { - headers: { 'Authorization': `Bearer ${cookies.user.access_token}` } - } : {} - function fetchRaids() { - api.endpoints.raids.getAll(headers) + api.endpoints.raids.getAll() .then((response) => { const raids = response.data.map((r: any) => r.raid) @@ -65,7 +59,7 @@ const RaidDropdown = React.forwardRef(function useFiel } fetchRaids() - }, [cookies.user, organizeRaids]) + }, [organizeRaids]) function raidGroup(index: number) { const options = raids && raids.length > 0 && raids[index].length > 0 && From 43cc0de23269a04fe8933edadc32373ac66a8ad4 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:11:20 -0800 Subject: [PATCH 22/30] Added WeaponModal component This uses AxSelect, ElementToggle and WeaponKeyDropdown to let users modify their weapons. --- components/WeaponModal/index.scss | 42 ++++++- components/WeaponModal/index.tsx | 178 ++++++++++++++++++++++++++++-- 2 files changed, 207 insertions(+), 13 deletions(-) diff --git a/components/WeaponModal/index.scss b/components/WeaponModal/index.scss index 97b53507..a0ec1acd 100644 --- a/components/WeaponModal/index.scss +++ b/components/WeaponModal/index.scss @@ -1,12 +1,44 @@ .Weapon.Dialog { - section { + .mods { display: flex; flex-direction: column; - gap: $unit / 2; + gap: $unit * 4; - h3 { - color: $grey-50; - font-size: $font-small; + section { + display: flex; + flex-direction: column; + gap: $unit / 2; + + h3 { + color: $grey-50; + font-size: $font-small; + margin-bottom: $unit; + } + + select { + background-color: $grey-90; + } + } + + .Button { + font-size: $font-regular; + padding: ($unit * 1.5) ($unit * 2); + width: 100%; + + &.btn-disabled { + background: $grey-90; + color: $grey-70; + cursor: not-allowed; + } + + &:not(.btn-disabled) { + background: $grey-90; + color: $grey-40; + + &:hover { + background: $grey-80; + } + } } } } \ No newline at end of file diff --git a/components/WeaponModal/index.tsx b/components/WeaponModal/index.tsx index a6df76ac..0873456d 100644 --- a/components/WeaponModal/index.tsx +++ b/components/WeaponModal/index.tsx @@ -1,19 +1,181 @@ -import React from 'react' +import React, { useState } from 'react' +import { useCookies } from 'react-cookie' +import { AxiosResponse } from 'axios' import * as Dialog from '@radix-ui/react-dialog' +import AXSelect from '~components/AxSelect' import ElementToggle from '~components/ElementToggle' +import WeaponKeyDropdown from '~components/WeaponKeyDropdown' +import Button from '~components/Button' + +import api from '~utils/api' +import { appState } from '~utils/appState' import CrossIcon from '~public/icons/Cross.svg' import './index.scss' + +interface GridWeaponObject { + weapon: { + element?: number + weapon_key1_id?: string + weapon_key2_id?: string + weapon_key3_id?: string + ax_modifier1?: number + ax_modifier2?: number + ax_strength1?: number + ax_strength2?: number + } +} + interface Props { gridWeapon: GridWeapon children: React.ReactNode } const WeaponModal = (props: Props) => { + // Cookies + const [cookies, _] = useCookies(['user']) + const headers = (cookies.user != null) ? { + headers: { + 'Authorization': `Bearer ${cookies.user.access_token}` + } + } : {} + + // Refs + const weaponKey1Select = React.createRef() + const weaponKey2Select = React.createRef() + const weaponKey3Select = React.createRef() + + // State + const [open, setOpen] = useState(false) + + const [element, setElement] = useState(-1) + const [primaryAxModifier, setPrimaryAxModifier] = useState(-1) + const [secondaryAxModifier, setSecondaryAxModifier] = useState(-1) + const [primaryAxValue, setPrimaryAxValue] = useState(0.0) + const [secondaryAxValue, setSecondaryAxValue] = useState(0.0) + + function receiveAxValues(primaryAxModifier: number, primaryAxValue: number, secondaryAxModifier: number, secondaryAxValue: number) { + setPrimaryAxModifier(primaryAxModifier) + setSecondaryAxModifier(secondaryAxModifier) + + setPrimaryAxValue(primaryAxValue) + setSecondaryAxValue(secondaryAxValue) + } + + function receiveElementValue(element: string) { + setElement(parseInt(element)) + } + + function prepareObject() { + let object: GridWeaponObject = { weapon: {} } + + 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].includes(props.gridWeapon.object.series)) + object.weapon.weapon_key2_id = weaponKey2Select.current?.value + + if (props.gridWeapon.object.series == 17) + object.weapon.weapon_key3_id = weaponKey3Select.current?.value + + if (props.gridWeapon.object.ax > 0) { + object.weapon.ax_modifier1 = primaryAxModifier + object.weapon.ax_modifier2 = secondaryAxModifier + object.weapon.ax_strength1 = primaryAxValue + object.weapon.ax_strength2 = secondaryAxValue + } + + return object + } + + async function updateWeapon() { + const updateObject = prepareObject() + return await api.endpoints.grid_weapons.update(props.gridWeapon.id, updateObject, headers) + .then(response => processResult(response)) + .catch(error => processError(error)) + } + + function processResult(response: AxiosResponse) { + const gridWeapon: GridWeapon = response.data.grid_weapon + + if (gridWeapon.mainhand) + appState.grid.weapons.mainWeapon = gridWeapon + else + appState.grid.weapons.allWeapons[gridWeapon.position] = gridWeapon + + setOpen(false) + } + + function processError(error: any) { + console.error(error) + } + + const elementSelect = () => { + return ( +
+

Element

+ +
+ ) + } + + const keySelect = () => { + return ( +
+

Weapon Keys

+ { ([2, 3, 17, 22].includes(props.gridWeapon.object.series)) ? + + : ''} + + { ([2, 3, 17].includes(props.gridWeapon.object.series)) ? + + : ''} + + { (props.gridWeapon.object.series == 17) ? + + : ''} +
+ ) + } + + const axSelect = () => { + return ( +
+ +
+ ) + } + + function openChange(open: boolean) { + setOpen(open) + } + return ( - + { props.children } @@ -31,12 +193,12 @@ const WeaponModal = (props: Props) => {
-
- { (props.gridWeapon.object.element == 0) ? - - : ''} -
+
+ { (props.gridWeapon.object.element == 0) ? elementSelect() : '' } + { ([2, 3, 17, 24].includes(props.gridWeapon.object.series)) ? keySelect() : '' } + { (props.gridWeapon.object.ax > 0) ? axSelect() : '' } + +
From 4b2c354d53a4bc2db3496366d469d3cc3451df50 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:13:20 -0800 Subject: [PATCH 23/30] Update segmented control for null weapons This uses the GridWeapon element to modify the party's segmented control color, if possible --- components/PartySegmentedControl/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/components/PartySegmentedControl/index.tsx b/components/PartySegmentedControl/index.tsx index 18cc6c55..97ca5895 100644 --- a/components/PartySegmentedControl/index.tsx +++ b/components/PartySegmentedControl/index.tsx @@ -17,10 +17,16 @@ interface Props { } const PartySegmentedControl = (props: Props) => { - const { party } = useSnapshot(appState) + const { party, grid } = useSnapshot(appState) function getElement() { - switch(party.element) { + let element: number = 0 + if (party.element == 0 && grid.weapons.mainWeapon) + element = grid.weapons.mainWeapon.element + else + element = party.element + + switch(element) { case 1: return "wind"; break case 2: return "fire"; break case 3: return "water"; break From adebca255e0cb5a2236987f17c62bd62c5900372 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:13:35 -0800 Subject: [PATCH 24/30] Remove logs --- components/WeaponUnit/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/WeaponUnit/index.tsx b/components/WeaponUnit/index.tsx index c171fe8d..78a36fd9 100644 --- a/components/WeaponUnit/index.tsx +++ b/components/WeaponUnit/index.tsx @@ -60,7 +60,6 @@ const WeaponUnit = (props: Props) => { function canBeModified(gridWeapon: GridWeapon) { const weapon = gridWeapon.object - console.log(`${weapon.name.en}: ${weapon.series} ${weapon.ax > 0 || (weapon.series) && [2, 3, 8, 22, 25].includes(weapon.series)}`) return weapon.ax > 0 || (weapon.series) && [2, 3, 8, 22, 25].includes(weapon.series) } @@ -83,7 +82,6 @@ const WeaponUnit = (props: Props) => {
) - console.log(gridWeapon?.object) return (
From a161a1b63a1d6570a8760af04184a730cc224fc3 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:13:48 -0800 Subject: [PATCH 25/30] Modify which weapon series trigger the hover button --- components/WeaponUnit/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/WeaponUnit/index.tsx b/components/WeaponUnit/index.tsx index 78a36fd9..cf3d0826 100644 --- a/components/WeaponUnit/index.tsx +++ b/components/WeaponUnit/index.tsx @@ -61,7 +61,7 @@ const WeaponUnit = (props: Props) => { const weapon = gridWeapon.object return weapon.ax > 0 || - (weapon.series) && [2, 3, 8, 22, 25].includes(weapon.series) + (weapon.series) && [2, 3, 17, 22].includes(weapon.series) } const image = ( From 47ff412c2b7cb8d0027a0216847785ab4f68bfd7 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:14:00 -0800 Subject: [PATCH 26/30] Change the weapon's image for null element weapons --- components/WeaponUnit/index.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/components/WeaponUnit/index.tsx b/components/WeaponUnit/index.tsx index cf3d0826..21df410e 100644 --- a/components/WeaponUnit/index.tsx +++ b/components/WeaponUnit/index.tsx @@ -43,10 +43,17 @@ const WeaponUnit = (props: Props) => { if (props.gridWeapon) { const weapon = props.gridWeapon.object! - if (props.unitType == 0) - imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${weapon.granblue_id}.jpg` - else - imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}.jpg` + if (props.unitType == 0) { + if (props.gridWeapon.object.element == 0 && props.gridWeapon.element) + imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${weapon.granblue_id}_${props.gridWeapon.element}.jpg` + else + imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${weapon.granblue_id}.jpg` + } else { + if (props.gridWeapon.object.element == 0 && props.gridWeapon.element) + imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}_${props.gridWeapon.element}.jpg` + else + imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}.jpg` + } } setImageUrl(imgSrc) From 9b39299a3aacff36831b9bf37fffd56785117ad7 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 03:14:03 -0800 Subject: [PATCH 27/30] Update index.scss --- components/FilterBar/index.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/FilterBar/index.scss b/components/FilterBar/index.scss index fd78e099..ba8cdf4b 100644 --- a/components/FilterBar/index.scss +++ b/components/FilterBar/index.scss @@ -28,7 +28,7 @@ background: url('/icons/Arrow.svg'), $grey-90; background-repeat: no-repeat; background-position-y: center; - background-position-x: 98%; + background-position-x: 95%; background-size: $unit * 1.5; color: $grey-50; font-size: $font-small; From e9546293dcb05bff060e95dd4308b0852d84d528 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 15:27:31 -0800 Subject: [PATCH 28/30] Add form validation for AxSelect We're not done yet, there's still some weird behaviors and a case we haven't properly checked (if second AX skill has a value but first AX skill doesn't) --- components/AxSelect/index.scss | 64 +++++++++++++++++---------- components/AxSelect/index.tsx | 74 +++++++++++++++++++++++++++++--- components/WeaponModal/index.tsx | 9 +++- 3 files changed, 117 insertions(+), 30 deletions(-) diff --git a/components/AxSelect/index.scss b/components/AxSelect/index.scss index 52aab9b0..559645d3 100644 --- a/components/AxSelect/index.scss +++ b/components/AxSelect/index.scss @@ -1,30 +1,48 @@ -.AXSet { +.AXSelect { display: flex; - flex-direction: row; + flex-direction: column; gap: $unit; - &.hidden { - display: none; - } + .AXSet { + &.hidden { + display: none; + } - select { - flex-grow: 1; - } + .errors { + color: $error; + display: none; + padding: $unit 0; - .Input { - -webkit-font-smoothing: antialiased; - border: none; - background-color: $grey-90; - border-radius: 6px; - box-sizing: border-box; - color: $grey-00; - height: $unit * 6; - display: block; - font-size: $font-regular; - padding: $unit; - text-align: right; - min-width: 100px; - width: 100px; - } + &.visible { + display: block; + } + } + .fields { + display: flex; + flex-direction: row; + gap: $unit; + + select { + flex-grow: 1; + margin: 0; + } + + .Input { + -webkit-font-smoothing: antialiased; + border: none; + background-color: $grey-90; + border-radius: 6px; + box-sizing: border-box; + color: $grey-00; + height: $unit * 6; + display: block; + font-size: $font-regular; + padding: $unit; + text-align: right; + min-width: 100px; + width: 100px; + } + } + } } \ No newline at end of file diff --git a/components/AxSelect/index.tsx b/components/AxSelect/index.tsx index b6798e74..0eeb169f 100644 --- a/components/AxSelect/index.tsx +++ b/components/AxSelect/index.tsx @@ -5,13 +5,36 @@ import { axData } from '~utils/axData' import './index.scss' +interface ErrorMap { + [index: string]: string + axValue1: string + axValue2: string +} + interface Props { axType: number currentSkills?: SimpleAxSkill[], + sendValidity: (isValid: boolean) => void sendValues: (primaryAxModifier: number, primaryAxValue: number, secondaryAxModifier: number, secondaryAxValue: number) => void } const AXSelect = (props: Props) => { + // Set up form states and error handling + const [errors, setErrors] = useState({ + axValue1: '', + axValue2: '' + }) + + const primaryErrorClasses = classNames({ + 'errors': true, + 'visible': errors.axValue1.length > 0 + }) + + const secondaryErrorClasses = classNames({ + 'errors': true, + 'visible': errors.axValue2.length > 0 + }) + // Refs const primaryAxModifierSelect = React.createRef() const primaryAxValueInput = React.createRef() @@ -112,11 +135,44 @@ const AXSelect = (props: Props) => { } function handleInputChange(event: React.ChangeEvent) { + let newErrors = {...errors} + if (primaryAxValueInput.current == event.target) { - setPrimaryAxValue(parseFloat(event.target.value)) + const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] + const value = parseFloat(event.target.value) + + if (value < primaryAxSkill.minValue) { + newErrors.axValue1 = `${primaryAxSkill.name.en} must be at least ${primaryAxSkill.minValue}${ (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''}` + } else if (value > primaryAxSkill.maxValue) { + newErrors.axValue1 = `${primaryAxSkill.name.en} cannot be greater than ${primaryAxSkill.maxValue}${ (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''}` + } else { + newErrors.axValue1 = '' + setPrimaryAxValue(parseFloat(event.target.value)) + } } else { - setSecondaryAxValue(parseFloat(event.target.value)) + const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] + const value = parseFloat(event.target.value) + + if (primaryAxSkill.secondary) { + const secondaryAxSkill = primaryAxSkill.secondary.find(skill => skill.id == secondaryAxModifier) + + if (secondaryAxSkill) { + if (value < secondaryAxSkill.minValue) { + newErrors.axValue2 = `${secondaryAxSkill.name.en} must be at least ${secondaryAxSkill.minValue}${ (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''}` + } else if (value > secondaryAxSkill.maxValue) { + newErrors.axValue2 = `${secondaryAxSkill.name.en} cannot be greater than ${secondaryAxSkill.maxValue}${ (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''}` + } else if (!secondaryAxSkill.suffix && value % 1 !== 0) { + newErrors.axValue2 = `${secondaryAxSkill.name.en} must be a whole number` + } else { + newErrors.axValue2 = '' + setSecondaryAxValue(parseFloat(event.target.value)) + } + } + } } + + props.sendValidity(newErrors.axValue1 === '' && newErrors.axValue2 === '') + setErrors(newErrors) } function setupInput(ax: AxSkill | undefined, element: HTMLInputElement) { @@ -144,13 +200,19 @@ const AXSelect = (props: Props) => { return (
- - +
+ + +
+

{errors.axValue1}

- - +
+ + +
+

{errors.axValue2}

) diff --git a/components/WeaponModal/index.tsx b/components/WeaponModal/index.tsx index 0873456d..eba61367 100644 --- a/components/WeaponModal/index.tsx +++ b/components/WeaponModal/index.tsx @@ -49,6 +49,7 @@ const WeaponModal = (props: Props) => { // State const [open, setOpen] = useState(false) + const [formValid, setFormValid] = useState(false) const [element, setElement] = useState(-1) const [primaryAxModifier, setPrimaryAxModifier] = useState(-1) @@ -64,6 +65,10 @@ const WeaponModal = (props: Props) => { setSecondaryAxValue(secondaryAxValue) } + function receiveAxValidity(isValid: boolean) { + setFormValid(isValid) + } + function receiveElementValue(element: string) { setElement(parseInt(element)) } @@ -164,6 +169,7 @@ const WeaponModal = (props: Props) => { @@ -171,6 +177,7 @@ const WeaponModal = (props: Props) => { } function openChange(open: boolean) { + setFormValid(false) setOpen(open) } @@ -197,7 +204,7 @@ const WeaponModal = (props: Props) => { { (props.gridWeapon.object.element == 0) ? elementSelect() : '' } { ([2, 3, 17, 24].includes(props.gridWeapon.object.series)) ? keySelect() : '' } { (props.gridWeapon.object.ax > 0) ? axSelect() : '' } - +
From 8fe270adb5480706a19a1a8e003e0bacc746c30b Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 16:12:28 -0800 Subject: [PATCH 29/30] Added the last bit of client validation --- components/AxSelect/index.tsx | 76 ++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/components/AxSelect/index.tsx b/components/AxSelect/index.tsx index 0eeb169f..4dfb6661 100644 --- a/components/AxSelect/index.tsx +++ b/components/AxSelect/index.tsx @@ -67,6 +67,10 @@ const AXSelect = (props: Props) => { props.sendValues(primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue) }, [primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue]) + useEffect(() => { + props.sendValidity(primaryAxValue > 0 && errors.axValue1 === '' && errors.axValue2 === '') + }, [primaryAxValue, errors]) + // Classes const secondarySetClasses = classNames({ 'AXSet': true, @@ -135,44 +139,62 @@ const AXSelect = (props: Props) => { } function handleInputChange(event: React.ChangeEvent) { + const value = parseFloat(event.target.value) let newErrors = {...errors} if (primaryAxValueInput.current == event.target) { - const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] - const value = parseFloat(event.target.value) - - if (value < primaryAxSkill.minValue) { - newErrors.axValue1 = `${primaryAxSkill.name.en} must be at least ${primaryAxSkill.minValue}${ (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''}` - } else if (value > primaryAxSkill.maxValue) { - newErrors.axValue1 = `${primaryAxSkill.name.en} cannot be greater than ${primaryAxSkill.maxValue}${ (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''}` - } else { - newErrors.axValue1 = '' - setPrimaryAxValue(parseFloat(event.target.value)) - } + if (handlePrimaryErrors(value)) + setPrimaryAxValue(value) } else { - const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] - const value = parseFloat(event.target.value) + if (handleSecondaryErrors(value)) + setSecondaryAxValue(value) + } + } - if (primaryAxSkill.secondary) { - const secondaryAxSkill = primaryAxSkill.secondary.find(skill => skill.id == secondaryAxModifier) + function handlePrimaryErrors(value: number) { + const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] + let newErrors = {...errors} - if (secondaryAxSkill) { - if (value < secondaryAxSkill.minValue) { - newErrors.axValue2 = `${secondaryAxSkill.name.en} must be at least ${secondaryAxSkill.minValue}${ (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''}` - } else if (value > secondaryAxSkill.maxValue) { - newErrors.axValue2 = `${secondaryAxSkill.name.en} cannot be greater than ${secondaryAxSkill.maxValue}${ (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''}` - } else if (!secondaryAxSkill.suffix && value % 1 !== 0) { - newErrors.axValue2 = `${secondaryAxSkill.name.en} must be a whole number` - } else { - newErrors.axValue2 = '' - setSecondaryAxValue(parseFloat(event.target.value)) - } + if (value < primaryAxSkill.minValue) { + newErrors.axValue1 = `${primaryAxSkill.name.en} must be at least ${primaryAxSkill.minValue}${ (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''}` + } else if (value > primaryAxSkill.maxValue) { + newErrors.axValue1 = `${primaryAxSkill.name.en} cannot be greater than ${primaryAxSkill.maxValue}${ (primaryAxSkill.suffix) ? primaryAxSkill.suffix : ''}` + } else if (!value || value <= 0) { + newErrors.axValue1 = `${primaryAxSkill.name.en} must have a value` + } else { + newErrors.axValue1 = '' + } + + setErrors(newErrors) + + return newErrors.axValue1.length === 0 + } + + function handleSecondaryErrors(value: number) { + const primaryAxSkill = axData[props.axType - 1][primaryAxModifier] + let newErrors = {...errors} + + if (primaryAxSkill.secondary) { + const secondaryAxSkill = primaryAxSkill.secondary.find(skill => skill.id == secondaryAxModifier) + + if (secondaryAxSkill) { + if (value < secondaryAxSkill.minValue) { + newErrors.axValue2 = `${secondaryAxSkill.name.en} must be at least ${secondaryAxSkill.minValue}${ (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''}` + } else if (value > secondaryAxSkill.maxValue) { + newErrors.axValue2 = `${secondaryAxSkill.name.en} cannot be greater than ${secondaryAxSkill.maxValue}${ (secondaryAxSkill.suffix) ? secondaryAxSkill.suffix : ''}` + } else if (!secondaryAxSkill.suffix && value % 1 !== 0) { + newErrors.axValue2 = `${secondaryAxSkill.name.en} must be a whole number` + } else if (primaryAxValue <= 0) { + newErrors.axValue1 = `${primaryAxSkill.name.en} must have a value` + } else { + newErrors.axValue2 = '' } } } - props.sendValidity(newErrors.axValue1 === '' && newErrors.axValue2 === '') setErrors(newErrors) + + return newErrors.axValue2.length === 0 } function setupInput(ax: AxSkill | undefined, element: HTMLInputElement) { From 242c61c200d77622364ab66bf21ba70868062635 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 3 Mar 2022 16:58:04 -0800 Subject: [PATCH 30/30] Fix typescript build errors --- components/AxSelect/index.tsx | 4 ++-- components/WeaponGrid/index.tsx | 6 +++--- components/WeaponKeyDropdown/index.tsx | 5 +++-- types/GridWeapon.d.ts | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/components/AxSelect/index.tsx b/components/AxSelect/index.tsx index 4dfb6661..636261ba 100644 --- a/components/AxSelect/index.tsx +++ b/components/AxSelect/index.tsx @@ -65,11 +65,11 @@ const AXSelect = (props: Props) => { useEffect(() => { props.sendValues(primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue) - }, [primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue]) + }, [props, primaryAxModifier, primaryAxValue, secondaryAxModifier, secondaryAxValue]) useEffect(() => { props.sendValidity(primaryAxValue > 0 && errors.axValue1 === '' && errors.axValue2 === '') - }, [primaryAxValue, errors]) + }, [props, primaryAxValue, errors]) // Classes const secondarySetClasses = classNames({ diff --git a/components/WeaponGrid/index.tsx b/components/WeaponGrid/index.tsx index 753cc288..efa1311a 100644 --- a/components/WeaponGrid/index.tsx +++ b/components/WeaponGrid/index.tsx @@ -232,7 +232,7 @@ const WeaponGrid = (props: Props) => { // Render: JSX components const mainhandElement = ( { return (
  • { const extraGridElement = ( (function us }, series: props.series, slot: props.slot, - group: -1 + group: -1, + order: 0 } const organizeWeaponKeys = useCallback((weaponKeys: WeaponKey[]) => { @@ -74,7 +75,7 @@ const WeaponKeyDropdown = React.forwardRef(function us } fetchWeaponKeys() - }, [organizeWeaponKeys]) + }, [props.series, props.slot, organizeWeaponKeys]) function weaponKeyGroup(index: number) { ['α','β','γ','Δ'].sort((a, b) => a.localeCompare(b, 'el')) diff --git a/types/GridWeapon.d.ts b/types/GridWeapon.d.ts index 954812ac..fe88cf27 100644 --- a/types/GridWeapon.d.ts +++ b/types/GridWeapon.d.ts @@ -5,6 +5,6 @@ interface GridWeapon { object: Weapon uncap_level: number element: number - weapon_keys?: WeaponKey[] - ax?: SimpleAxSkill[] + weapon_keys?: Array + ax?: Array } \ No newline at end of file