Merge pull request #157 from jedmund/transcendence
Add support for Transcendence
|
|
@ -57,11 +57,16 @@ const CharacterGrid = (props: Props) => {
|
||||||
})
|
})
|
||||||
const [errorMessage, setErrorMessage] = useState('')
|
const [errorMessage, setErrorMessage] = useState('')
|
||||||
|
|
||||||
// Create a temporary state to store previous character uncap values
|
// Create a temporary state to store previous weapon uncap values and transcendence stages
|
||||||
const [previousUncapValues, setPreviousUncapValues] = useState<{
|
const [previousUncapValues, setPreviousUncapValues] = useState<{
|
||||||
[key: number]: number | undefined
|
[key: number]: number | undefined
|
||||||
}>({})
|
}>({})
|
||||||
|
|
||||||
|
const [previousTranscendenceStages, setPreviousTranscendenceStages] =
|
||||||
|
useState<{
|
||||||
|
[key: number]: number | undefined
|
||||||
|
}>({})
|
||||||
|
|
||||||
// Set the editable flag only on first load
|
// Set the editable flag only on first load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// If user is logged in and matches
|
// If user is logged in and matches
|
||||||
|
|
@ -269,6 +274,7 @@ const CharacterGrid = (props: Props) => {
|
||||||
// Note: Saves, but debouncing is not working properly
|
// Note: Saves, but debouncing is not working properly
|
||||||
async function saveUncap(id: string, position: number, uncapLevel: number) {
|
async function saveUncap(id: string, position: number, uncapLevel: number) {
|
||||||
storePreviousUncapValue(position)
|
storePreviousUncapValue(position)
|
||||||
|
storePreviousTranscendenceStage(position)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (uncapLevel != previousUncapValues[position])
|
if (uncapLevel != previousUncapValues[position])
|
||||||
|
|
@ -280,11 +286,17 @@ const CharacterGrid = (props: Props) => {
|
||||||
|
|
||||||
// Revert optimistic UI
|
// Revert optimistic UI
|
||||||
updateUncapLevel(position, previousUncapValues[position])
|
updateUncapLevel(position, previousUncapValues[position])
|
||||||
|
updateTranscendenceStage(position, previousTranscendenceStages[position])
|
||||||
|
|
||||||
// Remove optimistic key
|
// Remove optimistic key
|
||||||
let newPreviousValues = { ...previousUncapValues }
|
let newPreviousTranscendenceStages = { ...previousTranscendenceStages }
|
||||||
delete newPreviousValues[position]
|
let newPreviousUncapValues = { ...previousUncapValues }
|
||||||
setPreviousUncapValues(newPreviousValues)
|
|
||||||
|
delete newPreviousTranscendenceStages[position]
|
||||||
|
delete newPreviousUncapValues[position]
|
||||||
|
|
||||||
|
setPreviousTranscendenceStages(newPreviousTranscendenceStages)
|
||||||
|
setPreviousUncapValues(newPreviousUncapValues)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,21 +310,25 @@ const CharacterGrid = (props: Props) => {
|
||||||
accountState.account.user &&
|
accountState.account.user &&
|
||||||
party.user.id === accountState.account.user.id
|
party.user.id === accountState.account.user.id
|
||||||
) {
|
) {
|
||||||
memoizeAction(id, position, uncapLevel)
|
memoizeUncapAction(id, position, uncapLevel)
|
||||||
|
|
||||||
// Optimistically update UI
|
// Optimistically update UI
|
||||||
updateUncapLevel(position, uncapLevel)
|
updateUncapLevel(position, uncapLevel)
|
||||||
|
|
||||||
|
if (uncapLevel < 6) {
|
||||||
|
updateTranscendenceStage(position, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const memoizeAction = useCallback(
|
const memoizeUncapAction = useCallback(
|
||||||
(id: string, position: number, uncapLevel: number) => {
|
(id: string, position: number, uncapLevel: number) => {
|
||||||
debouncedAction(id, position, uncapLevel)
|
debouncedUncapAction(id, position, uncapLevel)
|
||||||
},
|
},
|
||||||
[props, previousUncapValues]
|
[props, previousUncapValues]
|
||||||
)
|
)
|
||||||
|
|
||||||
const debouncedAction = useMemo(
|
const debouncedUncapAction = useMemo(
|
||||||
() =>
|
() =>
|
||||||
debounce((id, position, number) => {
|
debounce((id, position, number) => {
|
||||||
saveUncap(id, position, number)
|
saveUncap(id, position, number)
|
||||||
|
|
@ -341,6 +357,106 @@ const CharacterGrid = (props: Props) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Methods: Updating transcendence stage
|
||||||
|
// Note: Saves, but debouncing is not working properly
|
||||||
|
async function saveTranscendence(
|
||||||
|
id: string,
|
||||||
|
position: number,
|
||||||
|
stage: number
|
||||||
|
) {
|
||||||
|
storePreviousUncapValue(position)
|
||||||
|
storePreviousTranscendenceStage(position)
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
character: {
|
||||||
|
uncap_level: stage > 0 ? 6 : 5,
|
||||||
|
transcendence_step: stage,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (stage != previousTranscendenceStages[position])
|
||||||
|
await api.endpoints.grid_characters
|
||||||
|
.update(id, payload)
|
||||||
|
.then((response) => {
|
||||||
|
storeGridCharacter(response.data)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
|
||||||
|
// Revert optimistic UI
|
||||||
|
updateUncapLevel(position, previousUncapValues[position])
|
||||||
|
updateTranscendenceStage(position, previousTranscendenceStages[position])
|
||||||
|
|
||||||
|
// Remove optimistic key
|
||||||
|
let newPreviousTranscendenceStages = { ...previousTranscendenceStages }
|
||||||
|
let newPreviousUncapValues = { ...previousUncapValues }
|
||||||
|
|
||||||
|
delete newPreviousTranscendenceStages[position]
|
||||||
|
delete newPreviousUncapValues[position]
|
||||||
|
|
||||||
|
setPreviousTranscendenceStages(newPreviousTranscendenceStages)
|
||||||
|
setPreviousUncapValues(newPreviousUncapValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initiateTranscendenceUpdate(
|
||||||
|
id: string,
|
||||||
|
position: number,
|
||||||
|
stage: number
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
party.user &&
|
||||||
|
accountState.account.user &&
|
||||||
|
party.user.id === accountState.account.user.id
|
||||||
|
) {
|
||||||
|
memoizeTranscendenceAction(id, position, stage)
|
||||||
|
|
||||||
|
// Optimistically update UI
|
||||||
|
updateTranscendenceStage(position, stage)
|
||||||
|
|
||||||
|
if (stage > 0) {
|
||||||
|
updateUncapLevel(position, 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const memoizeTranscendenceAction = useCallback(
|
||||||
|
(id: string, position: number, stage: number) => {
|
||||||
|
debouncedTranscendenceAction(id, position, stage)
|
||||||
|
},
|
||||||
|
[props, previousTranscendenceStages]
|
||||||
|
)
|
||||||
|
|
||||||
|
const debouncedTranscendenceAction = useMemo(
|
||||||
|
() =>
|
||||||
|
debounce((id, position, number) => {
|
||||||
|
saveTranscendence(id, position, number)
|
||||||
|
}, 500),
|
||||||
|
[props, saveTranscendence]
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateTranscendenceStage = (
|
||||||
|
position: number,
|
||||||
|
stage: number | undefined
|
||||||
|
) => {
|
||||||
|
const character = appState.grid.characters[position]
|
||||||
|
if (character && stage !== undefined) {
|
||||||
|
character.transcendence_step = stage
|
||||||
|
appState.grid.characters[position] = character
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storePreviousTranscendenceStage(position: number) {
|
||||||
|
// Save the current value in case of an unexpected result
|
||||||
|
let newPreviousValues = { ...previousUncapValues }
|
||||||
|
|
||||||
|
if (grid.characters[position]) {
|
||||||
|
newPreviousValues[position] = grid.characters[position]?.uncap_level
|
||||||
|
setPreviousTranscendenceStages(newPreviousValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function cancelAlert() {
|
function cancelAlert() {
|
||||||
setErrorMessage('')
|
setErrorMessage('')
|
||||||
}
|
}
|
||||||
|
|
@ -380,6 +496,7 @@ const CharacterGrid = (props: Props) => {
|
||||||
position={i}
|
position={i}
|
||||||
updateObject={receiveCharacterFromSearch}
|
updateObject={receiveCharacterFromSearch}
|
||||||
updateUncap={initiateUncapUpdate}
|
updateUncap={initiateUncapUpdate}
|
||||||
|
updateTranscendence={initiateTranscendenceUpdate}
|
||||||
removeCharacter={removeCharacter}
|
removeCharacter={removeCharacter}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ interface Props {
|
||||||
removeCharacter: (id: string) => void
|
removeCharacter: (id: string) => void
|
||||||
updateObject: (object: SearchableObject, position: number) => void
|
updateObject: (object: SearchableObject, position: number) => void
|
||||||
updateUncap: (id: string, position: number, uncap: number) => void
|
updateUncap: (id: string, position: number, uncap: number) => void
|
||||||
|
updateTranscendence: (id: string, position: number, stage: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const CharacterUnit = ({
|
const CharacterUnit = ({
|
||||||
|
|
@ -49,6 +50,7 @@ const CharacterUnit = ({
|
||||||
removeCharacter: sendCharacterToRemove,
|
removeCharacter: sendCharacterToRemove,
|
||||||
updateObject,
|
updateObject,
|
||||||
updateUncap,
|
updateUncap,
|
||||||
|
updateTranscendence,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
// Translations and locale
|
// Translations and locale
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
@ -156,6 +158,10 @@ const CharacterUnit = ({
|
||||||
if (gridCharacter) updateUncap(gridCharacter.id, position, uncap)
|
if (gridCharacter) updateUncap(gridCharacter.id, position, uncap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function passTranscendenceData(stage: number) {
|
||||||
|
if (gridCharacter) updateTranscendence(gridCharacter.id, position, stage)
|
||||||
|
}
|
||||||
|
|
||||||
function removeCharacter() {
|
function removeCharacter() {
|
||||||
if (gridCharacter) sendCharacterToRemove(gridCharacter.id)
|
if (gridCharacter) sendCharacterToRemove(gridCharacter.id)
|
||||||
}
|
}
|
||||||
|
|
@ -169,8 +175,8 @@ const CharacterUnit = ({
|
||||||
|
|
||||||
// Change the image based on the uncap level
|
// Change the image based on the uncap level
|
||||||
let suffix = '01'
|
let suffix = '01'
|
||||||
if (gridCharacter.uncap_level == 6) suffix = '04'
|
if (gridCharacter.transcendence_step > 0) suffix = '04'
|
||||||
else if (gridCharacter.uncap_level == 5) suffix = '03'
|
else if (gridCharacter.uncap_level >= 5) suffix = '03'
|
||||||
else if (gridCharacter.uncap_level > 2) suffix = '02'
|
else if (gridCharacter.uncap_level > 2) suffix = '02'
|
||||||
|
|
||||||
// Special casing for Lyria (and Young Cat eventually)
|
// Special casing for Lyria (and Young Cat eventually)
|
||||||
|
|
@ -280,7 +286,11 @@ const CharacterUnit = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const image = (
|
const image = (
|
||||||
<div className="CharacterImage" onClick={openSearchModal}>
|
<div
|
||||||
|
className="CharacterImage"
|
||||||
|
onClick={openSearchModal}
|
||||||
|
tabIndex={gridCharacter ? gridCharacter.position * 7 : 0}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
alt={character?.name[locale]}
|
alt={character?.name[locale]}
|
||||||
className="grid_image"
|
className="grid_image"
|
||||||
|
|
@ -308,7 +318,11 @@ const CharacterUnit = ({
|
||||||
flb={character.uncap.flb || false}
|
flb={character.uncap.flb || false}
|
||||||
ulb={character.uncap.ulb || false}
|
ulb={character.uncap.ulb || false}
|
||||||
uncapLevel={gridCharacter.uncap_level}
|
uncapLevel={gridCharacter.uncap_level}
|
||||||
|
transcendenceStage={gridCharacter.transcendence_step}
|
||||||
|
position={gridCharacter.position}
|
||||||
|
editable={editable}
|
||||||
updateUncap={passUncapData}
|
updateUncap={passUncapData}
|
||||||
|
updateTranscendence={passTranscendenceData}
|
||||||
special={character.special}
|
special={character.special}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ interface Props {
|
||||||
removeSummon: (id: string) => void
|
removeSummon: (id: string) => void
|
||||||
updateObject: (object: SearchableObject, position: number) => void
|
updateObject: (object: SearchableObject, position: number) => void
|
||||||
updateUncap: (id: string, position: number, uncap: number) => void
|
updateUncap: (id: string, position: number, uncap: number) => void
|
||||||
|
updateTranscendence: (id: string, position: number, stage: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExtraSummons = (props: Props) => {
|
const ExtraSummons = (props: Props) => {
|
||||||
|
|
@ -36,6 +37,7 @@ const ExtraSummons = (props: Props) => {
|
||||||
gridSummon={props.grid[props.offset + i]}
|
gridSummon={props.grid[props.offset + i]}
|
||||||
updateObject={props.updateObject}
|
updateObject={props.updateObject}
|
||||||
updateUncap={props.updateUncap}
|
updateUncap={props.updateUncap}
|
||||||
|
updateTranscendence={props.updateTranscendence}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
9
components/Popover/index.scss
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
.Popover {
|
||||||
|
animation: scaleIn $duration-zoom ease-out;
|
||||||
|
background: var(--dialog-bg);
|
||||||
|
border-radius: $card-corner;
|
||||||
|
border: 0.5px solid rgba(0, 0, 0, 0.18);
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.24);
|
||||||
|
transform-origin: var(--radix-popover-content-transform-origin);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
37
components/Popover/index.tsx
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import React, { PropsWithChildren } from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
import * as PopoverPrimitive from '@radix-ui/react-popover'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
interface Props
|
||||||
|
extends React.DetailedHTMLProps<
|
||||||
|
React.DialogHTMLAttributes<HTMLDivElement>,
|
||||||
|
HTMLDivElement
|
||||||
|
> {}
|
||||||
|
|
||||||
|
export const Popover = PopoverPrimitive.Root
|
||||||
|
export const PopoverAnchor = PopoverPrimitive.Anchor
|
||||||
|
export const PopoverTrigger = PopoverPrimitive.Trigger
|
||||||
|
|
||||||
|
export const PopoverContent = React.forwardRef<HTMLDivElement, Props>(
|
||||||
|
({ children, ...props }: PropsWithChildren<Props>, forwardedRef) => {
|
||||||
|
const classes = classnames(props.className, {
|
||||||
|
Popover: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PopoverPrimitive.Portal>
|
||||||
|
<PopoverPrimitive.Content
|
||||||
|
sideOffset={5}
|
||||||
|
{...props}
|
||||||
|
className={classes}
|
||||||
|
ref={forwardedRef}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<PopoverPrimitive.Arrow />
|
||||||
|
</PopoverPrimitive.Content>
|
||||||
|
</PopoverPrimitive.Portal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
@ -42,10 +42,14 @@ const SummonGrid = (props: Props) => {
|
||||||
const { party, grid } = useSnapshot(appState)
|
const { party, grid } = useSnapshot(appState)
|
||||||
const [slug, setSlug] = useState()
|
const [slug, setSlug] = useState()
|
||||||
|
|
||||||
// Create a temporary state to store previous weapon uncap value
|
// Create a temporary state to store previous weapon uncap values and transcendence stages
|
||||||
const [previousUncapValues, setPreviousUncapValues] = useState<{
|
const [previousUncapValues, setPreviousUncapValues] = useState<{
|
||||||
[key: number]: number
|
[key: number]: number
|
||||||
}>({})
|
}>({})
|
||||||
|
const [previousTranscendenceStages, setPreviousTranscendenceStages] =
|
||||||
|
useState<{
|
||||||
|
[key: number]: number
|
||||||
|
}>({})
|
||||||
|
|
||||||
// Set the editable flag only on first load
|
// Set the editable flag only on first load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -155,6 +159,7 @@ const SummonGrid = (props: Props) => {
|
||||||
// Note: Saves, but debouncing is not working properly
|
// Note: Saves, but debouncing is not working properly
|
||||||
async function saveUncap(id: string, position: number, uncapLevel: number) {
|
async function saveUncap(id: string, position: number, uncapLevel: number) {
|
||||||
storePreviousUncapValue(position)
|
storePreviousUncapValue(position)
|
||||||
|
storePreviousTranscendenceStage(position)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (uncapLevel != previousUncapValues[position])
|
if (uncapLevel != previousUncapValues[position])
|
||||||
|
|
@ -166,11 +171,17 @@ const SummonGrid = (props: Props) => {
|
||||||
|
|
||||||
// Revert optimistic UI
|
// Revert optimistic UI
|
||||||
updateUncapLevel(position, previousUncapValues[position])
|
updateUncapLevel(position, previousUncapValues[position])
|
||||||
|
updateTranscendenceStage(position, previousTranscendenceStages[position])
|
||||||
|
|
||||||
// Remove optimistic key
|
// Remove optimistic key
|
||||||
let newPreviousValues = { ...previousUncapValues }
|
let newPreviousTranscendenceStages = { ...previousTranscendenceStages }
|
||||||
delete newPreviousValues[position]
|
let newPreviousUncapValues = { ...previousUncapValues }
|
||||||
setPreviousUncapValues(newPreviousValues)
|
|
||||||
|
delete newPreviousTranscendenceStages[position]
|
||||||
|
delete newPreviousUncapValues[position]
|
||||||
|
|
||||||
|
setPreviousTranscendenceStages(newPreviousTranscendenceStages)
|
||||||
|
setPreviousUncapValues(newPreviousUncapValues)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,21 +195,25 @@ const SummonGrid = (props: Props) => {
|
||||||
accountState.account.user &&
|
accountState.account.user &&
|
||||||
party.user.id === accountState.account.user.id
|
party.user.id === accountState.account.user.id
|
||||||
) {
|
) {
|
||||||
memoizeAction(id, position, uncapLevel)
|
memoizeUncapAction(id, position, uncapLevel)
|
||||||
|
|
||||||
// Optimistically update UI
|
// Optimistically update UI
|
||||||
updateUncapLevel(position, uncapLevel)
|
updateUncapLevel(position, uncapLevel)
|
||||||
|
|
||||||
|
if (uncapLevel < 6) {
|
||||||
|
updateTranscendenceStage(position, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const memoizeAction = useCallback(
|
const memoizeUncapAction = useCallback(
|
||||||
(id: string, position: number, uncapLevel: number) => {
|
(id: string, position: number, uncapLevel: number) => {
|
||||||
debouncedAction(id, position, uncapLevel)
|
debouncedUncapAction(id, position, uncapLevel)
|
||||||
},
|
},
|
||||||
[props, previousUncapValues]
|
[props, previousUncapValues]
|
||||||
)
|
)
|
||||||
|
|
||||||
const debouncedAction = useMemo(
|
const debouncedUncapAction = useMemo(
|
||||||
() =>
|
() =>
|
||||||
debounce((id, position, number) => {
|
debounce((id, position, number) => {
|
||||||
saveUncap(id, position, number)
|
saveUncap(id, position, number)
|
||||||
|
|
@ -237,6 +252,116 @@ const SummonGrid = (props: Props) => {
|
||||||
setPreviousUncapValues(newPreviousValues)
|
setPreviousUncapValues(newPreviousValues)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Methods: Updating transcendence stage
|
||||||
|
// Note: Saves, but debouncing is not working properly
|
||||||
|
async function saveTranscendence(
|
||||||
|
id: string,
|
||||||
|
position: number,
|
||||||
|
stage: number
|
||||||
|
) {
|
||||||
|
storePreviousUncapValue(position)
|
||||||
|
storePreviousTranscendenceStage(position)
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
summon: {
|
||||||
|
uncap_level: stage > 0 ? 6 : 5,
|
||||||
|
transcendence_step: stage,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (stage != previousTranscendenceStages[position])
|
||||||
|
await api.endpoints.grid_summons
|
||||||
|
.update(id, payload)
|
||||||
|
.then((response) => {
|
||||||
|
storeGridSummon(response.data.grid_summon)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
|
||||||
|
// Revert optimistic UI
|
||||||
|
updateUncapLevel(position, previousUncapValues[position])
|
||||||
|
updateTranscendenceStage(position, previousTranscendenceStages[position])
|
||||||
|
|
||||||
|
// Remove optimistic key
|
||||||
|
let newPreviousTranscendenceStages = { ...previousTranscendenceStages }
|
||||||
|
let newPreviousUncapValues = { ...previousUncapValues }
|
||||||
|
|
||||||
|
delete newPreviousTranscendenceStages[position]
|
||||||
|
delete newPreviousUncapValues[position]
|
||||||
|
|
||||||
|
setPreviousTranscendenceStages(newPreviousTranscendenceStages)
|
||||||
|
setPreviousUncapValues(newPreviousUncapValues)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initiateTranscendenceUpdate(
|
||||||
|
id: string,
|
||||||
|
position: number,
|
||||||
|
stage: number
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
party.user &&
|
||||||
|
accountState.account.user &&
|
||||||
|
party.user.id === accountState.account.user.id
|
||||||
|
) {
|
||||||
|
memoizeTranscendenceAction(id, position, stage)
|
||||||
|
|
||||||
|
// Optimistically update UI
|
||||||
|
updateTranscendenceStage(position, stage)
|
||||||
|
|
||||||
|
if (stage > 0) {
|
||||||
|
updateUncapLevel(position, 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const memoizeTranscendenceAction = useCallback(
|
||||||
|
(id: string, position: number, stage: number) => {
|
||||||
|
debouncedTranscendenceAction(id, position, stage)
|
||||||
|
},
|
||||||
|
[props, previousTranscendenceStages]
|
||||||
|
)
|
||||||
|
|
||||||
|
const debouncedTranscendenceAction = useMemo(
|
||||||
|
() =>
|
||||||
|
debounce((id, position, number) => {
|
||||||
|
saveTranscendence(id, position, number)
|
||||||
|
}, 500),
|
||||||
|
[props, saveTranscendence]
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateTranscendenceStage = (position: number, stage: number) => {
|
||||||
|
if (appState.grid.summons.mainSummon && position == -1)
|
||||||
|
appState.grid.summons.mainSummon.transcendence_step = stage
|
||||||
|
else if (appState.grid.summons.friendSummon && position == 6)
|
||||||
|
appState.grid.summons.friendSummon.transcendence_step = stage
|
||||||
|
else {
|
||||||
|
const summon = appState.grid.summons.allSummons[position]
|
||||||
|
if (summon) {
|
||||||
|
summon.transcendence_step = stage
|
||||||
|
appState.grid.summons.allSummons[position] = summon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storePreviousTranscendenceStage(position: number) {
|
||||||
|
// Save the current value in case of an unexpected result
|
||||||
|
let newPreviousValues = { ...previousUncapValues }
|
||||||
|
|
||||||
|
if (appState.grid.summons.mainSummon && position == -1)
|
||||||
|
newPreviousValues[position] = appState.grid.summons.mainSummon.uncap_level
|
||||||
|
else if (appState.grid.summons.friendSummon && position == 6)
|
||||||
|
newPreviousValues[position] =
|
||||||
|
appState.grid.summons.friendSummon.uncap_level
|
||||||
|
else {
|
||||||
|
const summon = appState.grid.summons.allSummons[position]
|
||||||
|
newPreviousValues[position] = summon ? summon.uncap_level : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
setPreviousUncapValues(newPreviousValues)
|
||||||
|
}
|
||||||
|
|
||||||
async function removeSummon(id: string) {
|
async function removeSummon(id: string) {
|
||||||
try {
|
try {
|
||||||
const response = await api.endpoints.grid_summons.destroy({ id: id })
|
const response = await api.endpoints.grid_summons.destroy({ id: id })
|
||||||
|
|
@ -267,6 +392,7 @@ const SummonGrid = (props: Props) => {
|
||||||
removeSummon={removeSummon}
|
removeSummon={removeSummon}
|
||||||
updateObject={receiveSummonFromSearch}
|
updateObject={receiveSummonFromSearch}
|
||||||
updateUncap={initiateUncapUpdate}
|
updateUncap={initiateUncapUpdate}
|
||||||
|
updateTranscendence={initiateTranscendenceUpdate}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -280,11 +406,14 @@ const SummonGrid = (props: Props) => {
|
||||||
key="grid_friend_summon"
|
key="grid_friend_summon"
|
||||||
position={6}
|
position={6}
|
||||||
unitType={2}
|
unitType={2}
|
||||||
|
removeSummon={removeSummon}
|
||||||
updateObject={receiveSummonFromSearch}
|
updateObject={receiveSummonFromSearch}
|
||||||
updateUncap={initiateUncapUpdate}
|
updateUncap={initiateUncapUpdate}
|
||||||
|
updateTranscendence={initiateTranscendenceUpdate}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const summonGridElement = (
|
const summonGridElement = (
|
||||||
<div id="LabeledGrid">
|
<div id="LabeledGrid">
|
||||||
<div className="Label">{t('summons.summons')}</div>
|
<div className="Label">{t('summons.summons')}</div>
|
||||||
|
|
@ -300,6 +429,7 @@ const SummonGrid = (props: Props) => {
|
||||||
removeSummon={removeSummon}
|
removeSummon={removeSummon}
|
||||||
updateObject={receiveSummonFromSearch}
|
updateObject={receiveSummonFromSearch}
|
||||||
updateUncap={initiateUncapUpdate}
|
updateUncap={initiateUncapUpdate}
|
||||||
|
updateTranscendence={initiateTranscendenceUpdate}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
)
|
)
|
||||||
|
|
@ -307,6 +437,7 @@ const SummonGrid = (props: Props) => {
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const subAuraSummonElement = (
|
const subAuraSummonElement = (
|
||||||
<ExtraSummons
|
<ExtraSummons
|
||||||
grid={grid.summons.allSummons}
|
grid={grid.summons.allSummons}
|
||||||
|
|
@ -316,8 +447,10 @@ const SummonGrid = (props: Props) => {
|
||||||
removeSummon={removeSummon}
|
removeSummon={removeSummon}
|
||||||
updateObject={receiveSummonFromSearch}
|
updateObject={receiveSummonFromSearch}
|
||||||
updateUncap={initiateUncapUpdate}
|
updateUncap={initiateUncapUpdate}
|
||||||
|
updateTranscendence={initiateTranscendenceUpdate}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div id="SummonGrid">
|
<div id="SummonGrid">
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ interface Props {
|
||||||
removeSummon: (id: string) => void
|
removeSummon: (id: string) => void
|
||||||
updateObject: (object: SearchableObject, position: number) => void
|
updateObject: (object: SearchableObject, position: number) => void
|
||||||
updateUncap: (id: string, position: number, uncap: number) => void
|
updateUncap: (id: string, position: number, uncap: number) => void
|
||||||
|
updateTranscendence: (id: string, position: number, stage: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const SummonUnit = ({
|
const SummonUnit = ({
|
||||||
|
|
@ -39,6 +40,7 @@ const SummonUnit = ({
|
||||||
removeSummon: sendSummonToRemove,
|
removeSummon: sendSummonToRemove,
|
||||||
updateObject,
|
updateObject,
|
||||||
updateUncap,
|
updateUncap,
|
||||||
|
updateTranscendence,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
// Translations and locale
|
// Translations and locale
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
|
|
@ -105,6 +107,10 @@ const SummonUnit = ({
|
||||||
if (gridSummon) updateUncap(gridSummon.id, position, uncap)
|
if (gridSummon) updateUncap(gridSummon.id, position, uncap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function passTranscendenceData(stage: number) {
|
||||||
|
if (gridSummon) updateTranscendence(gridSummon.id, position, stage)
|
||||||
|
}
|
||||||
|
|
||||||
function removeSummon() {
|
function removeSummon() {
|
||||||
if (gridSummon) sendSummonToRemove(gridSummon.id)
|
if (gridSummon) sendSummonToRemove(gridSummon.id)
|
||||||
}
|
}
|
||||||
|
|
@ -133,11 +139,14 @@ const SummonUnit = ({
|
||||||
]
|
]
|
||||||
|
|
||||||
let suffix = ''
|
let suffix = ''
|
||||||
if (
|
if (gridSummon.object.uncap.xlb && gridSummon.uncap_level == 6) {
|
||||||
|
suffix = '_03'
|
||||||
|
} else if (
|
||||||
upgradedSummons.indexOf(summon.granblue_id.toString()) != -1 &&
|
upgradedSummons.indexOf(summon.granblue_id.toString()) != -1 &&
|
||||||
gridSummon.uncap_level == 5
|
gridSummon.uncap_level == 5
|
||||||
)
|
) {
|
||||||
suffix = '_02'
|
suffix = '_02'
|
||||||
|
}
|
||||||
|
|
||||||
// Generate the correct source for the summon
|
// Generate the correct source for the summon
|
||||||
if (unitType == 0 || unitType == 2)
|
if (unitType == 0 || unitType == 2)
|
||||||
|
|
@ -243,8 +252,13 @@ const SummonUnit = ({
|
||||||
type="summon"
|
type="summon"
|
||||||
ulb={gridSummon.object.uncap.ulb || false}
|
ulb={gridSummon.object.uncap.ulb || false}
|
||||||
flb={gridSummon.object.uncap.flb || false}
|
flb={gridSummon.object.uncap.flb || false}
|
||||||
|
xlb={gridSummon.object.uncap.xlb || false}
|
||||||
|
editable={editable}
|
||||||
uncapLevel={gridSummon.uncap_level}
|
uncapLevel={gridSummon.uncap_level}
|
||||||
|
transcendenceStage={gridSummon.transcendence_step}
|
||||||
|
position={gridSummon.position}
|
||||||
updateUncap={passUncapData}
|
updateUncap={passUncapData}
|
||||||
|
updateTranscendence={passTranscendenceData}
|
||||||
special={false}
|
special={false}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
83
components/TranscendenceFragment/index.scss
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
.Fragment {
|
||||||
|
$degrees: 72deg;
|
||||||
|
|
||||||
|
$origWidth: 29px;
|
||||||
|
$origHeight: 54px;
|
||||||
|
|
||||||
|
$scaledWidth: 12px;
|
||||||
|
$scaledHeight: calc(($scaledWidth / $origWidth) * $origHeight);
|
||||||
|
|
||||||
|
$scale: 1.2;
|
||||||
|
|
||||||
|
@include hidpiImage(
|
||||||
|
'/icons/transcendence/interactive/interactive-piece',
|
||||||
|
png,
|
||||||
|
$scaledWidth,
|
||||||
|
$scaledHeight
|
||||||
|
);
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
z-index: 32;
|
||||||
|
|
||||||
|
aspect-ratio: 29 / 54;
|
||||||
|
height: $scaledHeight;
|
||||||
|
width: $scaledWidth;
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Visible {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage1 {
|
||||||
|
top: 3px;
|
||||||
|
left: 18px;
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// transform: scale($scale, $scale) translateY(-2px);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage2 {
|
||||||
|
top: 10px;
|
||||||
|
left: 27px;
|
||||||
|
transform: rotate($degrees);
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// transform: rotate($degrees) scale($scale, $scale) translateY(-2px);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage3 {
|
||||||
|
top: 21px;
|
||||||
|
left: 24px;
|
||||||
|
transform: rotate($degrees * 2);
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// transform: rotate($degrees * 2) scale($scale, $scale) translateY(-1px);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage4 {
|
||||||
|
top: 21px;
|
||||||
|
left: 12px;
|
||||||
|
transform: rotate($degrees * 3);
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// transform: rotate($degrees * 3) scale($scale, $scale) translateY(-1px);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage5 {
|
||||||
|
top: 10px;
|
||||||
|
left: 8px;
|
||||||
|
transform: rotate($degrees * 4);
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// transform: rotate($degrees * 4) scale($scale, $scale) translateY(-1px);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
49
components/TranscendenceFragment/index.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import React from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
stage: number
|
||||||
|
interactive: boolean
|
||||||
|
visible: boolean
|
||||||
|
onClick?: (index: number) => void
|
||||||
|
onHover?: (index: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const TranscendenceFragment = ({
|
||||||
|
interactive,
|
||||||
|
stage,
|
||||||
|
visible,
|
||||||
|
onClick,
|
||||||
|
onHover,
|
||||||
|
}: Props) => {
|
||||||
|
const classes = classnames({
|
||||||
|
Fragment: true,
|
||||||
|
Visible: visible,
|
||||||
|
Stage1: stage === 1,
|
||||||
|
Stage2: stage === 2,
|
||||||
|
Stage3: stage === 3,
|
||||||
|
Stage4: stage === 4,
|
||||||
|
Stage5: stage === 5,
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
if (interactive && onClick) onClick(stage)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleHover() {
|
||||||
|
if (interactive && onHover) onHover(stage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<i className={classes} onClick={handleClick} onMouseOver={handleHover} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TranscendenceFragment.defaultProps = {
|
||||||
|
interactive: false,
|
||||||
|
visible: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TranscendenceFragment
|
||||||
25
components/TranscendencePopover/index.scss
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
.Transcendence.Popover {
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $unit-half;
|
||||||
|
display: flex;
|
||||||
|
width: $unit-10x;
|
||||||
|
height: $unit-10x;
|
||||||
|
padding: $unit;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 32;
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
opacity: 1;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: $font-small;
|
||||||
|
font-weight: $medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Pending {
|
||||||
|
color: $yellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
87
components/TranscendencePopover/index.tsx
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import React, { PropsWithChildren, useEffect, useState } from 'react'
|
||||||
|
import { useTranslation } from 'next-i18next'
|
||||||
|
import classNames from 'classnames'
|
||||||
|
|
||||||
|
import { Popover, PopoverAnchor, PopoverContent } from '~components/Popover'
|
||||||
|
import TranscendenceStar from '~components/TranscendenceStar'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
interface Props
|
||||||
|
extends React.DetailedHTMLProps<
|
||||||
|
React.DialogHTMLAttributes<HTMLDivElement>,
|
||||||
|
HTMLDivElement
|
||||||
|
> {
|
||||||
|
open: boolean
|
||||||
|
stage: number
|
||||||
|
onOpenChange?: (open: boolean) => void
|
||||||
|
sendValue?: (stage: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const TranscendencePopover = ({
|
||||||
|
children,
|
||||||
|
open: popoverOpen,
|
||||||
|
stage,
|
||||||
|
tabIndex,
|
||||||
|
onOpenChange,
|
||||||
|
sendValue,
|
||||||
|
}: PropsWithChildren<Props>) => {
|
||||||
|
const { t } = useTranslation('common')
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
|
const [currentStage, setCurrentStage] = useState(0)
|
||||||
|
|
||||||
|
const popoverRef = React.createRef<HTMLDivElement>()
|
||||||
|
|
||||||
|
const classes = classNames({
|
||||||
|
Transcendence: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const levelClasses = classNames({
|
||||||
|
Pending: stage != currentStage,
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) popoverRef.current?.focus()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentStage(stage)
|
||||||
|
}, [stage])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setOpen(popoverOpen)
|
||||||
|
}, [popoverOpen])
|
||||||
|
|
||||||
|
function handleFragmentClicked(newStage: number) {
|
||||||
|
setCurrentStage(newStage)
|
||||||
|
if (sendValue) sendValue(newStage)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFragmentHovered(newStage: number) {
|
||||||
|
setCurrentStage(newStage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover open={open} onOpenChange={onOpenChange}>
|
||||||
|
<PopoverAnchor>{children}</PopoverAnchor>
|
||||||
|
<PopoverContent className={classes} ref={popoverRef} tabIndex={tabIndex}>
|
||||||
|
<TranscendenceStar
|
||||||
|
className="Interactive Base"
|
||||||
|
editable={true}
|
||||||
|
interactive={true}
|
||||||
|
stage={stage}
|
||||||
|
onFragmentClick={handleFragmentClicked}
|
||||||
|
onFragmentHover={handleFragmentHovered}
|
||||||
|
/>
|
||||||
|
<h4>
|
||||||
|
<span>{t('level')} </span>
|
||||||
|
<span className={levelClasses}>{200 + 10 * currentStage}</span>
|
||||||
|
</h4>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TranscendencePopover
|
||||||
108
components/TranscendenceStar/index.scss
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
.TranscendenceStar {
|
||||||
|
$size: 18px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Immutable {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Empty {
|
||||||
|
@include hidpiImage('/icons/transcendence/0/stage-0', png, $size, $size);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage1 {
|
||||||
|
@include hidpiImage('/icons/transcendence/1/stage-1', png, $size, $size);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage2 {
|
||||||
|
@include hidpiImage('/icons/transcendence/2/stage-2', png, $size, $size);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage3 {
|
||||||
|
@include hidpiImage('/icons/transcendence/3/stage-3', png, $size, $size);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage4 {
|
||||||
|
@include hidpiImage('/icons/transcendence/4/stage-4', png, $size, $size);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage5 {
|
||||||
|
@include hidpiImage('/icons/transcendence/5/stage-5', png, $size, $size);
|
||||||
|
}
|
||||||
|
|
||||||
|
.Figure {
|
||||||
|
$size: 18px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 54px 54px;
|
||||||
|
display: block;
|
||||||
|
height: $size;
|
||||||
|
width: $size;
|
||||||
|
|
||||||
|
&.Interactive.Base {
|
||||||
|
$size: $unit-6x;
|
||||||
|
|
||||||
|
@include hidpiImage(
|
||||||
|
'/icons/transcendence/interactive/interactive-base',
|
||||||
|
png,
|
||||||
|
$size,
|
||||||
|
$size
|
||||||
|
);
|
||||||
|
|
||||||
|
height: $size;
|
||||||
|
width: $size;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage1 {
|
||||||
|
background-image: url('/icons/transcendence/1/step-1@3x.png');
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-image: url('/icons/transcendence/1/step-1-hover@3x.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage2 {
|
||||||
|
background-image: url('/icons/transcendence/2/step-2@3x.png');
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-image: url('/icons/transcendence/2/step-2-hover@3x.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage3 {
|
||||||
|
background-image: url('/icons/transcendence/3/step-3@3x.png');
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-image: url('/icons/transcendence/3/step-3-hover@3x.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage4 {
|
||||||
|
background-image: url('/icons/transcendence/4/step-4@3x.png');
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-image: url('/icons/transcendence/4/step-4-hover@3x.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Stage5 {
|
||||||
|
background-image: url('/icons/transcendence/5/step-5@3x.png');
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-image: url('/icons/transcendence/5/step-5-hover@3x.png');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
118
components/TranscendenceStar/index.tsx
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
|
import TranscendenceFragment from '~components/TranscendenceFragment'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
interface Props
|
||||||
|
extends React.DetailedHTMLProps<
|
||||||
|
React.DialogHTMLAttributes<HTMLDivElement>,
|
||||||
|
HTMLDivElement
|
||||||
|
> {
|
||||||
|
className?: string
|
||||||
|
stage: number
|
||||||
|
editable: boolean
|
||||||
|
interactive: boolean
|
||||||
|
onStarClick?: () => void
|
||||||
|
onFragmentClick?: (newStage: number) => void
|
||||||
|
onFragmentHover?: (newStage: number) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const NUM_FRAGMENTS = 5
|
||||||
|
|
||||||
|
const TranscendenceStar = ({
|
||||||
|
className,
|
||||||
|
interactive,
|
||||||
|
stage,
|
||||||
|
editable,
|
||||||
|
tabIndex,
|
||||||
|
onStarClick,
|
||||||
|
onFragmentClick,
|
||||||
|
onFragmentHover,
|
||||||
|
}: Props) => {
|
||||||
|
const [visibleStage, setVisibleStage] = useState(0)
|
||||||
|
const [currentStage, setCurrentStage] = useState(0)
|
||||||
|
const [immutable, setImmutable] = useState(false)
|
||||||
|
|
||||||
|
// Classes
|
||||||
|
const starClasses = classnames({
|
||||||
|
TranscendenceStar: true,
|
||||||
|
Immutable: immutable,
|
||||||
|
Empty: stage === 0,
|
||||||
|
Stage1: stage === 1,
|
||||||
|
Stage2: stage === 2,
|
||||||
|
Stage3: stage === 3,
|
||||||
|
Stage4: stage === 4,
|
||||||
|
Stage5: stage === 5,
|
||||||
|
})
|
||||||
|
|
||||||
|
const baseImageClasses = classnames(className, {
|
||||||
|
Figure: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setVisibleStage(stage)
|
||||||
|
setCurrentStage(stage)
|
||||||
|
}, [stage])
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
if (onStarClick) {
|
||||||
|
onStarClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFragmentClick(index: number) {
|
||||||
|
let newStage = index
|
||||||
|
if (index === currentStage) newStage = 0
|
||||||
|
|
||||||
|
setVisibleStage(newStage)
|
||||||
|
setCurrentStage(newStage)
|
||||||
|
if (onFragmentClick) onFragmentClick(newStage)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFragmentHover(index: number) {
|
||||||
|
setVisibleStage(index)
|
||||||
|
if (onFragmentHover) onFragmentHover(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMouseLeave() {
|
||||||
|
setVisibleStage(currentStage)
|
||||||
|
if (onFragmentHover) onFragmentHover(currentStage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={starClasses}
|
||||||
|
onClick={editable ? handleClick : () => {}}
|
||||||
|
onMouseLeave={interactive ? handleMouseLeave : () => {}}
|
||||||
|
tabIndex={tabIndex}
|
||||||
|
>
|
||||||
|
<div className="Fragments">
|
||||||
|
{[...Array(NUM_FRAGMENTS)].map((e, i) => {
|
||||||
|
const loopStage = i + 1
|
||||||
|
return interactive ? (
|
||||||
|
<TranscendenceFragment
|
||||||
|
key={`fragment_${loopStage}`}
|
||||||
|
stage={loopStage}
|
||||||
|
visible={loopStage <= visibleStage}
|
||||||
|
interactive={interactive}
|
||||||
|
onClick={handleFragmentClick}
|
||||||
|
onHover={handleFragmentHover}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<i className={baseImageClasses} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TranscendenceStar.defaultProps = {
|
||||||
|
stage: 0,
|
||||||
|
editable: false,
|
||||||
|
interactive: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TranscendenceStar
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
.Uncap {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.UncapIndicator {
|
.UncapIndicator {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import React from 'react'
|
import React, { useState } from 'react'
|
||||||
import UncapStar from '~components/UncapStar'
|
import UncapStar from '~components/UncapStar'
|
||||||
|
import TranscendencePopover from '~components/TranscendencePopover'
|
||||||
|
import TranscendenceStar from '~components/TranscendenceStar'
|
||||||
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
|
|
@ -7,14 +9,22 @@ interface Props {
|
||||||
type: 'character' | 'weapon' | 'summon'
|
type: 'character' | 'weapon' | 'summon'
|
||||||
rarity?: number
|
rarity?: number
|
||||||
uncapLevel?: number
|
uncapLevel?: number
|
||||||
|
position: number
|
||||||
|
transcendenceStage?: number
|
||||||
|
editable: boolean
|
||||||
flb: boolean
|
flb: boolean
|
||||||
ulb: boolean
|
ulb: boolean
|
||||||
|
xlb?: boolean
|
||||||
special: boolean
|
special: boolean
|
||||||
updateUncap?: (index: number) => void
|
updateUncap?: (index: number) => void
|
||||||
|
updateTranscendence?: (index: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const UncapIndicator = (props: Props) => {
|
const UncapIndicator = (props: Props) => {
|
||||||
const numStars = setNumStars()
|
const numStars = setNumStars()
|
||||||
|
|
||||||
|
const [popoverOpen, setPopoverOpen] = useState(false)
|
||||||
|
|
||||||
function setNumStars() {
|
function setNumStars() {
|
||||||
let numStars
|
let numStars
|
||||||
|
|
||||||
|
|
@ -37,7 +47,9 @@ const UncapIndicator = (props: Props) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (props.ulb) {
|
if (props.xlb) {
|
||||||
|
numStars = 6
|
||||||
|
} else if (props.ulb) {
|
||||||
numStars = 5
|
numStars = 5
|
||||||
} else if (props.flb) {
|
} else if (props.flb) {
|
||||||
numStars = 4
|
numStars = 4
|
||||||
|
|
@ -56,14 +68,41 @@ const UncapIndicator = (props: Props) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function togglePopover(open: boolean) {
|
||||||
|
setPopoverOpen(open)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendTranscendenceStage(stage: number) {
|
||||||
|
if (props.updateTranscendence) props.updateTranscendence(stage)
|
||||||
|
togglePopover(false)
|
||||||
|
}
|
||||||
|
|
||||||
const transcendence = (i: number) => {
|
const transcendence = (i: number) => {
|
||||||
return (
|
const tabIndex = props.position * 7 + i + 1
|
||||||
<UncapStar
|
return props.type === 'character' || props.type === 'summon' ? (
|
||||||
ulb={true}
|
<TranscendencePopover
|
||||||
empty={props.uncapLevel ? i >= props.uncapLevel : false}
|
open={popoverOpen}
|
||||||
|
stage={props.transcendenceStage ? props.transcendenceStage : 0}
|
||||||
|
onOpenChange={togglePopover}
|
||||||
|
sendValue={sendTranscendenceStage}
|
||||||
key={`star_${i}`}
|
key={`star_${i}`}
|
||||||
index={i}
|
tabIndex={tabIndex}
|
||||||
onClick={toggleStar}
|
>
|
||||||
|
<TranscendenceStar
|
||||||
|
key={`star_${i}`}
|
||||||
|
stage={props.transcendenceStage}
|
||||||
|
editable={props.editable}
|
||||||
|
interactive={false}
|
||||||
|
onStarClick={() => togglePopover(true)}
|
||||||
|
/>
|
||||||
|
</TranscendencePopover>
|
||||||
|
) : (
|
||||||
|
<TranscendenceStar
|
||||||
|
key={`star_${i}`}
|
||||||
|
stage={props.transcendenceStage}
|
||||||
|
editable={props.editable}
|
||||||
|
interactive={false}
|
||||||
|
tabIndex={tabIndex}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -76,7 +115,8 @@ const UncapIndicator = (props: Props) => {
|
||||||
empty={props.uncapLevel != null ? i >= props.uncapLevel : false}
|
empty={props.uncapLevel != null ? i >= props.uncapLevel : false}
|
||||||
key={`star_${i}`}
|
key={`star_${i}`}
|
||||||
index={i}
|
index={i}
|
||||||
onClick={toggleStar}
|
onStarClick={toggleStar}
|
||||||
|
tabIndex={props.position * 7 + i + 1}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +129,8 @@ const UncapIndicator = (props: Props) => {
|
||||||
empty={props.uncapLevel != null ? i >= props.uncapLevel : false}
|
empty={props.uncapLevel != null ? i >= props.uncapLevel : false}
|
||||||
key={`star_${i}`}
|
key={`star_${i}`}
|
||||||
index={i}
|
index={i}
|
||||||
onClick={toggleStar}
|
onStarClick={toggleStar}
|
||||||
|
tabIndex={props.position * 7 + i + 1}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -101,17 +142,21 @@ const UncapIndicator = (props: Props) => {
|
||||||
empty={props.uncapLevel != null ? i >= props.uncapLevel : false}
|
empty={props.uncapLevel != null ? i >= props.uncapLevel : false}
|
||||||
key={`star_${i}`}
|
key={`star_${i}`}
|
||||||
index={i}
|
index={i}
|
||||||
onClick={toggleStar}
|
onStarClick={toggleStar}
|
||||||
|
tabIndex={props.position * 7 + i + 1}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className="Uncap">
|
||||||
<ul className="UncapIndicator">
|
<ul className="UncapIndicator">
|
||||||
{Array.from(Array(numStars)).map((x, i) => {
|
{Array.from(Array(numStars)).map((x, i) => {
|
||||||
if (props.type === 'character' && i > 4) {
|
if (props.type === 'character' && i > 4) {
|
||||||
if (props.special) return ulb(i)
|
if (props.special) return ulb(i)
|
||||||
else return transcendence(i)
|
else return transcendence(i)
|
||||||
|
} else if (props.type === 'summon' && i > 4) {
|
||||||
|
return transcendence(i)
|
||||||
} else if (
|
} else if (
|
||||||
(props.special && props.type === 'character' && i == 3) ||
|
(props.special && props.type === 'character' && i == 3) ||
|
||||||
(props.type === 'character' && i == 4) ||
|
(props.type === 'character' && i == 4) ||
|
||||||
|
|
@ -123,7 +168,12 @@ const UncapIndicator = (props: Props) => {
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UncapIndicator.defaultProps = {
|
||||||
|
editable: false,
|
||||||
|
}
|
||||||
|
|
||||||
export default UncapIndicator
|
export default UncapIndicator
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,17 @@ import classnames from 'classnames'
|
||||||
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
interface Props {
|
interface Props
|
||||||
|
extends React.DetailedHTMLProps<
|
||||||
|
React.DialogHTMLAttributes<HTMLDivElement>,
|
||||||
|
HTMLDivElement
|
||||||
|
> {
|
||||||
empty: boolean
|
empty: boolean
|
||||||
special: boolean
|
special: boolean
|
||||||
flb: boolean
|
flb: boolean
|
||||||
ulb: boolean
|
ulb: boolean
|
||||||
index: number
|
index: number
|
||||||
onClick: (index: number, empty: boolean) => void
|
onStarClick: (index: number, empty: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const UncapStar = (props: Props) => {
|
const UncapStar = (props: Props) => {
|
||||||
|
|
@ -23,10 +27,12 @@ const UncapStar = (props: Props) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
function clicked() {
|
function clicked() {
|
||||||
props.onClick(props.index, props.empty)
|
props.onStarClick(props.index, props.empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
return <li className={classes} onClick={clicked}></li>
|
return (
|
||||||
|
<li className={classes} tabIndex={props.tabIndex} onClick={clicked}></li>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
UncapStar.defaultProps = {
|
UncapStar.defaultProps = {
|
||||||
|
|
|
||||||
|
|
@ -548,6 +548,7 @@ const WeaponUnit = ({
|
||||||
ulb={gridWeapon.object.uncap.ulb || false}
|
ulb={gridWeapon.object.uncap.ulb || false}
|
||||||
flb={gridWeapon.object.uncap.flb || false}
|
flb={gridWeapon.object.uncap.flb || false}
|
||||||
uncapLevel={gridWeapon.uncap_level}
|
uncapLevel={gridWeapon.uncap_level}
|
||||||
|
position={gridWeapon.position}
|
||||||
updateUncap={passUncapData}
|
updateUncap={passUncapData}
|
||||||
special={false}
|
special={false}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
93
package-lock.json
generated
|
|
@ -10,6 +10,7 @@
|
||||||
"@radix-ui/react-dialog": "^1.0.2",
|
"@radix-ui/react-dialog": "^1.0.2",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.1",
|
"@radix-ui/react-dropdown-menu": "^2.0.1",
|
||||||
"@radix-ui/react-hover-card": "^1.0.2",
|
"@radix-ui/react-hover-card": "^1.0.2",
|
||||||
|
"@radix-ui/react-popover": "^1.0.3",
|
||||||
"@radix-ui/react-select": "^1.1.2",
|
"@radix-ui/react-select": "^1.1.2",
|
||||||
"@radix-ui/react-switch": "^1.0.1",
|
"@radix-ui/react-switch": "^1.0.1",
|
||||||
"@radix-ui/react-toast": "^1.1.2",
|
"@radix-ui/react-toast": "^1.1.2",
|
||||||
|
|
@ -2325,6 +2326,55 @@
|
||||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-popover": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-YwedSukfWsyJs3/yP3yXUq44k4/JBe3jqU63Z8v2i19qZZ3dsx32oma17ztgclWPNuqp3A+Xa9UiDlZHyVX8Vg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/primitive": "1.0.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.0",
|
||||||
|
"@radix-ui/react-context": "1.0.0",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.0.2",
|
||||||
|
"@radix-ui/react-focus-guards": "1.0.0",
|
||||||
|
"@radix-ui/react-focus-scope": "1.0.1",
|
||||||
|
"@radix-ui/react-id": "1.0.0",
|
||||||
|
"@radix-ui/react-popper": "1.1.0",
|
||||||
|
"@radix-ui/react-portal": "1.0.1",
|
||||||
|
"@radix-ui/react-presence": "1.0.0",
|
||||||
|
"@radix-ui/react-primitive": "1.0.1",
|
||||||
|
"@radix-ui/react-slot": "1.0.1",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.0.0",
|
||||||
|
"aria-hidden": "^1.1.1",
|
||||||
|
"react-remove-scroll": "2.5.5"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-popper": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-07U7jpI0dZcLRAxT7L9qs6HecSoPhDSJybF7mEGHJDBDv+ZoGCvIlva0s+WxMXwJEav+ckX3hAlXBtnHmuvlCQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@floating-ui/react-dom": "0.7.2",
|
||||||
|
"@radix-ui/react-arrow": "1.0.1",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.0",
|
||||||
|
"@radix-ui/react-context": "1.0.0",
|
||||||
|
"@radix-ui/react-primitive": "1.0.1",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.0.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.0.0",
|
||||||
|
"@radix-ui/react-use-rect": "1.0.0",
|
||||||
|
"@radix-ui/react-use-size": "1.0.0",
|
||||||
|
"@radix-ui/rect": "1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-popper": {
|
"node_modules/@radix-ui/react-popper": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.0.1.tgz",
|
||||||
|
|
@ -8809,6 +8859,49 @@
|
||||||
"react-remove-scroll": "2.5.5"
|
"react-remove-scroll": "2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@radix-ui/react-popover": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-YwedSukfWsyJs3/yP3yXUq44k4/JBe3jqU63Z8v2i19qZZ3dsx32oma17ztgclWPNuqp3A+Xa9UiDlZHyVX8Vg==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@radix-ui/primitive": "1.0.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.0",
|
||||||
|
"@radix-ui/react-context": "1.0.0",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.0.2",
|
||||||
|
"@radix-ui/react-focus-guards": "1.0.0",
|
||||||
|
"@radix-ui/react-focus-scope": "1.0.1",
|
||||||
|
"@radix-ui/react-id": "1.0.0",
|
||||||
|
"@radix-ui/react-popper": "1.1.0",
|
||||||
|
"@radix-ui/react-portal": "1.0.1",
|
||||||
|
"@radix-ui/react-presence": "1.0.0",
|
||||||
|
"@radix-ui/react-primitive": "1.0.1",
|
||||||
|
"@radix-ui/react-slot": "1.0.1",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.0.0",
|
||||||
|
"aria-hidden": "^1.1.1",
|
||||||
|
"react-remove-scroll": "2.5.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-popper": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-07U7jpI0dZcLRAxT7L9qs6HecSoPhDSJybF7mEGHJDBDv+ZoGCvIlva0s+WxMXwJEav+ckX3hAlXBtnHmuvlCQ==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.13.10",
|
||||||
|
"@floating-ui/react-dom": "0.7.2",
|
||||||
|
"@radix-ui/react-arrow": "1.0.1",
|
||||||
|
"@radix-ui/react-compose-refs": "1.0.0",
|
||||||
|
"@radix-ui/react-context": "1.0.0",
|
||||||
|
"@radix-ui/react-primitive": "1.0.1",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.0.0",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.0.0",
|
||||||
|
"@radix-ui/react-use-rect": "1.0.0",
|
||||||
|
"@radix-ui/react-use-size": "1.0.0",
|
||||||
|
"@radix-ui/rect": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@radix-ui/react-popper": {
|
"@radix-ui/react-popper": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.0.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
"@radix-ui/react-dialog": "^1.0.2",
|
"@radix-ui/react-dialog": "^1.0.2",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.1",
|
"@radix-ui/react-dropdown-menu": "^2.0.1",
|
||||||
"@radix-ui/react-hover-card": "^1.0.2",
|
"@radix-ui/react-hover-card": "^1.0.2",
|
||||||
|
"@radix-ui/react-popover": "^1.0.3",
|
||||||
"@radix-ui/react-select": "^1.1.2",
|
"@radix-ui/react-select": "^1.1.2",
|
||||||
"@radix-ui/react-switch": "^1.0.1",
|
"@radix-ui/react-switch": "^1.0.1",
|
||||||
"@radix-ui/react-toast": "^1.1.2",
|
"@radix-ui/react-toast": "^1.1.2",
|
||||||
|
|
|
||||||
BIN
public/icons/transcendence/0/stage-0.png
Normal file
|
After Width: | Height: | Size: 770 B |
BIN
public/icons/transcendence/0/stage-0@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
public/icons/transcendence/0/stage-0@3x.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
public/icons/transcendence/1/stage-1-hover.png
Normal file
|
After Width: | Height: | Size: 755 B |
BIN
public/icons/transcendence/1/stage-1-hover@2x.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
public/icons/transcendence/1/stage-1-hover@3x.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
public/icons/transcendence/1/stage-1.png
Normal file
|
After Width: | Height: | Size: 765 B |
BIN
public/icons/transcendence/1/stage-1@2x.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
public/icons/transcendence/1/stage-1@3x.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
public/icons/transcendence/2/stage-2-hover.png
Normal file
|
After Width: | Height: | Size: 729 B |
BIN
public/icons/transcendence/2/stage-2-hover@2x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/icons/transcendence/2/stage-2-hover@3x.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
public/icons/transcendence/2/stage-2.png
Normal file
|
After Width: | Height: | Size: 761 B |
BIN
public/icons/transcendence/2/stage-2@2x.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
public/icons/transcendence/2/stage-2@3x.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
public/icons/transcendence/3/stage-3-hover.png
Normal file
|
After Width: | Height: | Size: 720 B |
BIN
public/icons/transcendence/3/stage-3-hover@2x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/icons/transcendence/3/stage-3-hover@3x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
public/icons/transcendence/3/stage-3.png
Normal file
|
After Width: | Height: | Size: 768 B |
BIN
public/icons/transcendence/3/stage-3@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
public/icons/transcendence/3/stage-3@3x.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
public/icons/transcendence/4/stage-4-hover.png
Normal file
|
After Width: | Height: | Size: 694 B |
BIN
public/icons/transcendence/4/stage-4-hover@2x.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
public/icons/transcendence/4/stage-4-hover@3x.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
public/icons/transcendence/4/stage-4.png
Normal file
|
After Width: | Height: | Size: 770 B |
BIN
public/icons/transcendence/4/stage-4@2x.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
public/icons/transcendence/4/stage-4@3x.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
public/icons/transcendence/5/stage-5-hover.png
Normal file
|
After Width: | Height: | Size: 668 B |
BIN
public/icons/transcendence/5/stage-5-hover@2x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/icons/transcendence/5/stage-5-hover@3x.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/icons/transcendence/5/stage-5.png
Normal file
|
After Width: | Height: | Size: 766 B |
BIN
public/icons/transcendence/5/stage-5@2x.png
Normal file
|
After Width: | Height: | Size: 2 KiB |
BIN
public/icons/transcendence/5/stage-5@3x.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
public/icons/transcendence/interactive/interactive-base.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/icons/transcendence/interactive/interactive-base@2x.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
public/icons/transcendence/interactive/interactive-base@3x.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
public/icons/transcendence/interactive/interactive-piece.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/icons/transcendence/interactive/interactive-piece@2x.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
public/icons/transcendence/interactive/interactive-piece@3x.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
|
|
@ -370,5 +370,6 @@
|
||||||
"no_raid": "No raid",
|
"no_raid": "No raid",
|
||||||
"no_user": "Anonymous",
|
"no_user": "Anonymous",
|
||||||
"no_job": "No class",
|
"no_job": "No class",
|
||||||
"no_value": "No value"
|
"no_value": "No value",
|
||||||
|
"level": "Level"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -371,5 +371,6 @@
|
||||||
"no_raid": "マルチなし",
|
"no_raid": "マルチなし",
|
||||||
"no_user": "無名",
|
"no_user": "無名",
|
||||||
"no_job": "ジョブなし",
|
"no_job": "ジョブなし",
|
||||||
"no_value": "値なし"
|
"no_value": "値なし",
|
||||||
|
"level": "レベル"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -321,3 +321,38 @@ i.tag {
|
||||||
backdrop-filter: blur(5px) saturate(100%) brightness(80%) opacity(1);
|
backdrop-filter: blur(5px) saturate(100%) brightness(80%) opacity(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes scaleIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
opacity: 0.2;
|
||||||
|
transform: scale(0.4);
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
opacity: 0.4;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
opacity: 0.6;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,3 +26,36 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin hidpiImage(
|
||||||
|
$image,
|
||||||
|
$extension,
|
||||||
|
$width,
|
||||||
|
$height,
|
||||||
|
$position: center,
|
||||||
|
$repeat: no-repeat
|
||||||
|
) {
|
||||||
|
background: url($image + '.' + $extension) $repeat $position;
|
||||||
|
|
||||||
|
@media screen and (-webkit-min-device-pixel-ratio: 2),
|
||||||
|
screen and (min--moz-device-pixel-ratio: 2),
|
||||||
|
screen and (-moz-min-device-pixel-ratio: 2),
|
||||||
|
screen and (-o-min-device-pixel-ratio: 2/1),
|
||||||
|
screen and (min-device-pixel-ratio: 2),
|
||||||
|
screen and (min-resolution: 192dpi),
|
||||||
|
screen and (min-resolution: 2dppx) {
|
||||||
|
background: url($image + '@2x' + '.' + $extension) $repeat $position;
|
||||||
|
background-size: $width $height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (-webkit-min-device-pixel-ratio: 3),
|
||||||
|
screen and (min--moz-device-pixel-ratio: 3),
|
||||||
|
screen and (-moz-min-device-pixel-ratio: 3),
|
||||||
|
screen and (-o-min-device-pixel-ratio: 3/1),
|
||||||
|
screen and (min-device-pixel-ratio: 3),
|
||||||
|
screen and (min-resolution: 216dpi),
|
||||||
|
screen and (min-resolution: 3dppx) {
|
||||||
|
background: url($image + '@3x' + '.' + $extension) $repeat $position;
|
||||||
|
background-size: $width $height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ $unit-half: calc($unit / 2);
|
||||||
$unit-2x: $unit * 2;
|
$unit-2x: $unit * 2;
|
||||||
$unit-3x: $unit * 3;
|
$unit-3x: $unit * 3;
|
||||||
$unit-4x: $unit * 4;
|
$unit-4x: $unit * 4;
|
||||||
|
$unit-5x: $unit * 5;
|
||||||
$unit-6x: $unit * 6;
|
$unit-6x: $unit * 6;
|
||||||
$unit-8x: $unit * 8;
|
$unit-8x: $unit * 8;
|
||||||
$unit-10x: $unit * 10;
|
$unit-10x: $unit * 10;
|
||||||
|
|
|
||||||
1
types/GridCharacter.d.ts
vendored
|
|
@ -3,6 +3,7 @@ interface GridCharacter {
|
||||||
position: number
|
position: number
|
||||||
object: Character
|
object: Character
|
||||||
uncap_level: number
|
uncap_level: number
|
||||||
|
transcendence_step: number
|
||||||
over_mastery: CharacterOverMastery
|
over_mastery: CharacterOverMastery
|
||||||
aetherial_mastery: ExtendedMastery
|
aetherial_mastery: ExtendedMastery
|
||||||
awakening: {
|
awakening: {
|
||||||
|
|
|
||||||
1
types/GridSummon.d.ts
vendored
|
|
@ -5,4 +5,5 @@ interface GridSummon {
|
||||||
position: number
|
position: number
|
||||||
object: Summon
|
object: Summon
|
||||||
uncap_level: number
|
uncap_level: number
|
||||||
|
transcendence_step: number
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
types/Summon.d.ts
vendored
|
|
@ -15,16 +15,19 @@ interface Summon {
|
||||||
max_hp: number
|
max_hp: number
|
||||||
max_hp_flb: number
|
max_hp_flb: number
|
||||||
max_hp_ulb: number
|
max_hp_ulb: number
|
||||||
|
max_hp_xlb: number
|
||||||
}
|
}
|
||||||
atk: {
|
atk: {
|
||||||
min_atk: number
|
min_atk: number
|
||||||
max_atk: number
|
max_atk: number
|
||||||
max_atk_flb: number
|
max_atk_flb: number
|
||||||
max_atk_ulb: number
|
max_atk_ulb: number
|
||||||
|
max_atk_xlb: number
|
||||||
}
|
}
|
||||||
uncap: {
|
uncap: {
|
||||||
flb: boolean
|
flb: boolean
|
||||||
ulb: boolean
|
ulb: boolean
|
||||||
|
xlb: boolean
|
||||||
}
|
}
|
||||||
position?: number
|
position?: number
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,8 @@ class Api {
|
||||||
return axios.post(resourceUrl, {
|
return axios.post(resourceUrl, {
|
||||||
[resource]: {
|
[resource]: {
|
||||||
id: id,
|
id: id,
|
||||||
uncap_level: value
|
uncap_level: value,
|
||||||
|
transcendence_step: 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||