Merge pull request #155 from jedmund/perpetuity

Add indicators for Perpetuity Ring to CharacterUnit
This commit is contained in:
Justin Edmund 2023-01-22 21:34:44 -08:00 committed by GitHub
commit f8e6a4d26b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 247 additions and 69 deletions

View file

@ -41,28 +41,17 @@ import CrossIcon from '~public/icons/Cross.svg'
import './index.scss'
// Types
import { CharacterOverMastery, ExtendedMastery } from '~types'
interface GridCharacterObject {
character: {
ring1: ExtendedMastery
ring2: ExtendedMastery
ring3: ExtendedMastery
ring4: ExtendedMastery
earring: ExtendedMastery
awakening: {
type?: number
level?: number
}
transcendence_step: number
perpetuity: boolean
}
}
import {
CharacterOverMastery,
ExtendedMastery,
GridCharacterObject,
} from '~types'
interface Props {
gridCharacter: GridCharacter
open: boolean
onOpenChange: (open: boolean) => void
updateCharacter: (object: GridCharacterObject) => Promise<any>
}
const CharacterModal = ({
@ -70,6 +59,7 @@ const CharacterModal = ({
children,
open: modalOpen,
onOpenChange,
updateCharacter,
}: PropsWithChildren<Props>) => {
const router = useRouter()
const locale =
@ -133,42 +123,6 @@ const CharacterModal = ({
setPerpetuity(gridCharacter.perpetuity)
}, [gridCharacter])
// Methods: UI state management
function handleOpenChange(open: boolean) {
setOpen(open)
onOpenChange(open)
}
// Methods: Receive data from components
function receiveRingValues(overMastery: CharacterOverMastery) {
setRings(overMastery)
}
function receiveEarringValues(
earringModifier: number,
earringStrength: number
) {
setEarring({
modifier: earringModifier,
strength: earringStrength,
})
}
function handleCheckedChange(checked: boolean) {
setPerpetuity(checked)
}
function receiveAwakeningValues(type: number, level: number) {
setAwakeningType(type)
setAwakeningLevel(level)
}
function receiveValidity(isValid: boolean) {
setFormValid(isValid)
}
// Methods: Data syncing
// Prepare the GridWeaponObject to send to the server
function prepareObject() {
let object: GridCharacterObject = {
@ -205,27 +159,45 @@ const CharacterModal = ({
return object
}
// Send the GridWeaponObject to the server
async function updateCharacter() {
const updateObject = prepareObject()
return await api.endpoints.grid_characters
.update(gridCharacter.id, updateObject)
.then((response) => processResult(response))
.catch((error) => processError(error))
// Methods: UI state management
function handleOpenChange(open: boolean) {
setOpen(open)
onOpenChange(open)
}
// Save the server's response to state
function processResult(response: AxiosResponse) {
const gridCharacter: GridCharacter = response.data
appState.grid.characters[gridCharacter.position] = gridCharacter
// Methods: Receive data from components
function receiveRingValues(overMastery: CharacterOverMastery) {
setRings(overMastery)
}
function receiveEarringValues(
earringModifier: number,
earringStrength: number
) {
setEarring({
modifier: earringModifier,
strength: earringStrength,
})
}
function handleCheckedChange(checked: boolean) {
setPerpetuity(checked)
}
async function handleUpdateCharacter() {
await updateCharacter(prepareObject())
setOpen(false)
if (onOpenChange) onOpenChange(false)
}
function processError(error: any) {
console.error(error)
function receiveAwakeningValues(type: number, level: number) {
setAwakeningType(type)
setAwakeningLevel(level)
}
function receiveValidity(isValid: boolean) {
setFormValid(isValid)
}
const ringSelect = () => {
@ -322,7 +294,7 @@ const CharacterModal = ({
<div className="DialogFooter" ref={footerRef}>
<Button
contained={true}
onClick={updateCharacter}
onClick={handleUpdateCharacter}
disabled={!formValid}
text={t('modals.characters.buttons.confirm')}
/>

View file

@ -97,4 +97,34 @@
font-size: $font-tiny;
}
}
&:hover .Perpetuity.Empty {
opacity: 1;
}
.Perpetuity {
position: absolute;
background-image: url('/icons/perpetuity/filled.svg');
background-size: $unit-4x $unit-4x;
z-index: 20;
top: $unit * -1;
right: $unit-3x;
width: $unit-4x;
height: $unit-4x;
transition: $duration-zoom opacity ease-in-out;
&:hover {
background-image: url('/icons/perpetuity/empty.svg');
cursor: pointer;
}
&.Empty {
background-image: url('/icons/perpetuity/empty.svg');
opacity: 0;
&:hover {
background-image: url('/icons/perpetuity/filled.svg');
}
}
}
}

View file

@ -2,6 +2,7 @@ import React, { MouseEvent, useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { useSnapshot } from 'valtio'
import { Trans, useTranslation } from 'next-i18next'
import { AxiosResponse } from 'axios'
import classNames from 'classnames'
import Alert from '~components/Alert'
@ -17,12 +18,18 @@ import ContextMenuItem from '~components/ContextMenuItem'
import SearchModal from '~components/SearchModal'
import UncapIndicator from '~components/UncapIndicator'
import api from '~utils/api'
import { appState } from '~utils/appState'
import PlusIcon from '~public/icons/Add.svg'
import SettingsIcon from '~public/icons/Settings.svg'
import type { SearchableObject } from '~types'
// Types
import type {
GridCharacterObject,
PerpetuityObject,
SearchableObject,
} from '~types'
import './index.scss'
@ -99,6 +106,16 @@ const CharacterUnit = ({
setContextMenuOpen(!contextMenuOpen)
}
function handlePerpetuityClick() {
if (gridCharacter) {
let object: PerpetuityObject = {
character: { perpetuity: !gridCharacter.perpetuity },
}
updateCharacter(object)
}
}
// Methods: Handle open change
function handleCharacterModalOpenChange(open: boolean) {
setDetailsModalOpen(open)
@ -113,6 +130,28 @@ const CharacterUnit = ({
}
// Methods: Mutate data
// Send the GridWeaponObject to the server
async function updateCharacter(
object: GridCharacterObject | PerpetuityObject
) {
if (gridCharacter)
return await api.endpoints.grid_characters
.update(gridCharacter.id, object)
.then((response) => processResult(response))
.catch((error) => processError(error))
}
// Save the server's response to state
function processResult(response: AxiosResponse) {
const gridCharacter: GridCharacter = response.data
appState.grid.characters[gridCharacter.position] = gridCharacter
}
function processError(error: any) {
console.error(error)
}
function passUncapData(uncap: number) {
if (gridCharacter) updateUncap(gridCharacter.id, position, uncap)
}
@ -160,6 +199,7 @@ const CharacterUnit = ({
gridCharacter={gridCharacter}
open={detailsModalOpen}
onOpenChange={handleCharacterModalOpenChange}
updateCharacter={updateCharacter}
/>
)
}
@ -228,6 +268,17 @@ const CharacterUnit = ({
}
// Methods: Core element rendering
const perpetuity = () => {
if (gridCharacter) {
const classes = classNames({
Perpetuity: true,
Empty: !gridCharacter.perpetuity,
})
return <i className={classes} onClick={handlePerpetuityClick} />
}
}
const image = (
<div className="CharacterImage" onClick={openSearchModal}>
<img
@ -249,6 +300,7 @@ const CharacterUnit = ({
<>
<div className={classes}>
{contextMenu()}
{perpetuity()}
{image}
{gridCharacter && character ? (
<UncapIndicator

View file

@ -0,0 +1,49 @@
<svg viewBox="0 0 45 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_1460_2996)">
<rect x="4" y="2" width="37" height="38" rx="2" fill="white"/>
<rect x="5" y="3" width="35" height="36" rx="1" fill="url(#paint0_linear_1460_2996)"/>
<g filter="url(#filter1_i_1460_2996)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.4885 18.3682C27.4659 19.17 29.6429 21.7269 29.6429 24.7572C29.6429 28.4299 26.445 31.4072 22.5001 31.4072C18.5552 31.4072 15.3572 28.4299 15.3572 24.7572C15.3572 21.7288 17.5316 19.1732 20.5061 18.3697L18.6119 16.002C15.0204 17.4432 12.5 20.8206 12.5 24.7572C12.5 30.0039 16.9772 34.2572 22.5 34.2572C28.0228 34.2572 32.5 30.0039 32.5 24.7572C32.5 20.8187 29.9772 17.4401 26.3831 16L24.4885 18.3682Z" fill="#999999"/>
</g>
<g filter="url(#filter2_i_1460_2996)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.7158 18.0239L16.9691 12.0905C16.6896 11.7412 16.6762 11.2487 16.9363 10.8846L18.6976 8.41876C18.8853 8.15597 19.1884 8 19.5113 8H25.4821C25.805 8 26.1081 8.15597 26.2958 8.41876L28.0571 10.8846C28.3172 11.2487 28.3038 11.7412 28.0243 12.0905L23.2776 18.0239C22.8772 18.5243 22.1162 18.5243 21.7158 18.0239ZM23 9.25V15.7873C23 16.0236 23.2976 16.128 23.4452 15.9435L26.8751 11.6562C26.9481 11.5649 26.9481 11.4351 26.8751 11.3438L25.0751 9.09383C25.0276 9.03452 24.9558 9 24.8798 9H23.25C23.1119 9 23 9.11193 23 9.25Z" fill="#999999"/>
</g>
<rect x="5.5" y="3.5" width="34" height="35" rx="0.5" stroke="black" stroke-opacity="0.09"/>
<rect x="3.5" y="1.5" width="38" height="39" rx="2.5" stroke="black" stroke-opacity="0.1"/>
</g>
<defs>
<filter id="filter0_d_1460_2996" x="0" y="0" width="45" height="46" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="1.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.13 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1460_2996"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1460_2996" result="shape"/>
</filter>
<filter id="filter1_i_1460_2996" x="12.5" y="16" width="20" height="19.2572" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_1460_2996"/>
</filter>
<filter id="filter2_i_1460_2996" x="16.75" y="8" width="11.4934" height="11.3992" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.12 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_1460_2996"/>
</filter>
<linearGradient id="paint0_linear_1460_2996" x1="22.5" y1="3" x2="22.5" y2="39" gradientUnits="userSpaceOnUse">
<stop offset="0.0618126" stop-color="#C1C1C1"/>
<stop offset="0.756271" stop-color="#B0B0B0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -0,0 +1,53 @@
<svg viewBox="0 0 45 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_1460_2995)">
<rect x="4" y="2" width="37" height="38" rx="2" fill="white"/>
<rect x="5" y="3" width="35" height="36" rx="1" fill="url(#paint0_linear_1460_2995)"/>
<g filter="url(#filter1_d_1460_2995)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.4885 18.3682C27.4659 19.17 29.6429 21.7269 29.6429 24.7572C29.6429 28.4299 26.445 31.4072 22.5001 31.4072C18.5552 31.4072 15.3572 28.4299 15.3572 24.7572C15.3572 21.7288 17.5316 19.1732 20.5061 18.3697L18.6119 16.002C15.0204 17.4432 12.5 20.8206 12.5 24.7572C12.5 30.0039 16.9772 34.2572 22.5 34.2572C28.0228 34.2572 32.5 30.0039 32.5 24.7572C32.5 20.8187 29.9772 17.4401 26.3831 16L24.4885 18.3682Z" fill="white"/>
</g>
<g filter="url(#filter2_d_1460_2995)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.7158 18.0239L16.9691 12.0905C16.6896 11.7412 16.6762 11.2487 16.9363 10.8846L18.6976 8.41876C18.8853 8.15597 19.1884 8 19.5113 8H25.4821C25.805 8 26.1081 8.15597 26.2958 8.41876L28.0571 10.8846C28.3172 11.2487 28.3038 11.7412 28.0243 12.0905L23.2776 18.0239C22.8772 18.5243 22.1162 18.5243 21.7158 18.0239ZM23 9.25V15.7873C23 16.0236 23.2976 16.128 23.4452 15.9435L26.8751 11.6562C26.9481 11.5649 26.9481 11.4351 26.8751 11.3438L25.0751 9.09383C25.0276 9.03452 24.9558 9 24.8798 9H23.25C23.1119 9 23 9.11193 23 9.25Z" fill="white"/>
</g>
<rect x="5.5" y="3.5" width="34" height="35" rx="0.5" stroke="url(#paint1_linear_1460_2995)" stroke-opacity="0.5"/>
<rect x="3.5" y="1.5" width="38" height="39" rx="2.5" stroke="black" stroke-opacity="0.1"/>
</g>
<defs>
<filter id="filter0_d_1460_2995" x="0" y="0" width="45" height="46" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="1.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.13 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1460_2995"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1460_2995" result="shape"/>
</filter>
<filter id="filter1_d_1460_2995" x="10.5" y="15" width="24" height="22.2572" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.22 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1460_2995"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1460_2995" result="shape"/>
</filter>
<filter id="filter2_d_1460_2995" x="14.75" y="7" width="15.4934" height="14.3992" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="1"/>
<feGaussianBlur stdDeviation="1"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.22 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1460_2995"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1460_2995" result="shape"/>
</filter>
<linearGradient id="paint0_linear_1460_2995" x1="22.5" y1="3" x2="22.5" y2="39" gradientUnits="userSpaceOnUse">
<stop offset="0.0618126" stop-color="#6CD6FF"/>
<stop offset="0.756271" stop-color="#ACB3FE"/>
</linearGradient>
<linearGradient id="paint1_linear_1460_2995" x1="22.5" y1="3" x2="22.5" y2="39" gradientUnits="userSpaceOnUse">
<stop stop-color="#8DEFFE"/>
<stop offset="1" stop-color="#E8EDFF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4 KiB

22
types/index.d.ts vendored
View file

@ -48,3 +48,25 @@ export type CharacterOverMastery = {
3: ExtendedMastery
4: ExtendedMastery
}
interface GridCharacterObject {
character: {
ring1: ExtendedMastery
ring2: ExtendedMastery
ring3: ExtendedMastery
ring4: ExtendedMastery
earring: ExtendedMastery
awakening: {
type?: number
level?: number
}
transcendence_step: number
perpetuity: boolean
}
}
interface PerpetuityObject {
character: {
perpetuity: boolean
}
}