diff --git a/components/CharacterGrid/index.tsx b/components/CharacterGrid/index.tsx index 3532caf0..d4c72e6e 100644 --- a/components/CharacterGrid/index.tsx +++ b/components/CharacterGrid/index.tsx @@ -57,11 +57,16 @@ const CharacterGrid = (props: Props) => { }) 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<{ [key: number]: number | undefined }>({}) + const [previousTranscendenceStages, setPreviousTranscendenceStages] = + useState<{ + [key: number]: number | undefined + }>({}) + // Set the editable flag only on first load useEffect(() => { // If user is logged in and matches @@ -269,6 +274,7 @@ const CharacterGrid = (props: Props) => { // Note: Saves, but debouncing is not working properly async function saveUncap(id: string, position: number, uncapLevel: number) { storePreviousUncapValue(position) + storePreviousTranscendenceStage(position) try { if (uncapLevel != previousUncapValues[position]) @@ -280,11 +286,17 @@ const CharacterGrid = (props: Props) => { // Revert optimistic UI updateUncapLevel(position, previousUncapValues[position]) + updateTranscendenceStage(position, previousTranscendenceStages[position]) // Remove optimistic key - let newPreviousValues = { ...previousUncapValues } - delete newPreviousValues[position] - setPreviousUncapValues(newPreviousValues) + let newPreviousTranscendenceStages = { ...previousTranscendenceStages } + let newPreviousUncapValues = { ...previousUncapValues } + + delete newPreviousTranscendenceStages[position] + delete newPreviousUncapValues[position] + + setPreviousTranscendenceStages(newPreviousTranscendenceStages) + setPreviousUncapValues(newPreviousUncapValues) } } @@ -298,21 +310,25 @@ const CharacterGrid = (props: Props) => { accountState.account.user && party.user.id === accountState.account.user.id ) { - memoizeAction(id, position, uncapLevel) + memoizeUncapAction(id, position, uncapLevel) // Optimistically update UI updateUncapLevel(position, uncapLevel) + + if (uncapLevel < 6) { + updateTranscendenceStage(position, 0) + } } } - const memoizeAction = useCallback( + const memoizeUncapAction = useCallback( (id: string, position: number, uncapLevel: number) => { - debouncedAction(id, position, uncapLevel) + debouncedUncapAction(id, position, uncapLevel) }, [props, previousUncapValues] ) - const debouncedAction = useMemo( + const debouncedUncapAction = useMemo( () => debounce((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() { setErrorMessage('') } @@ -380,6 +496,7 @@ const CharacterGrid = (props: Props) => { position={i} updateObject={receiveCharacterFromSearch} updateUncap={initiateUncapUpdate} + updateTranscendence={initiateTranscendenceUpdate} removeCharacter={removeCharacter} /> diff --git a/components/CharacterUnit/index.tsx b/components/CharacterUnit/index.tsx index 5f046605..d813b877 100644 --- a/components/CharacterUnit/index.tsx +++ b/components/CharacterUnit/index.tsx @@ -40,6 +40,7 @@ interface Props { removeCharacter: (id: string) => void updateObject: (object: SearchableObject, position: number) => void updateUncap: (id: string, position: number, uncap: number) => void + updateTranscendence: (id: string, position: number, stage: number) => void } const CharacterUnit = ({ @@ -49,6 +50,7 @@ const CharacterUnit = ({ removeCharacter: sendCharacterToRemove, updateObject, updateUncap, + updateTranscendence, }: Props) => { // Translations and locale const { t } = useTranslation('common') @@ -156,6 +158,10 @@ const CharacterUnit = ({ if (gridCharacter) updateUncap(gridCharacter.id, position, uncap) } + function passTranscendenceData(stage: number) { + if (gridCharacter) updateTranscendence(gridCharacter.id, position, stage) + } + function removeCharacter() { if (gridCharacter) sendCharacterToRemove(gridCharacter.id) } @@ -169,8 +175,8 @@ const CharacterUnit = ({ // Change the image based on the uncap level let suffix = '01' - if (gridCharacter.uncap_level == 6) suffix = '04' - else if (gridCharacter.uncap_level == 5) suffix = '03' + if (gridCharacter.transcendence_step > 0) suffix = '04' + else if (gridCharacter.uncap_level >= 5) suffix = '03' else if (gridCharacter.uncap_level > 2) suffix = '02' // Special casing for Lyria (and Young Cat eventually) @@ -280,7 +286,11 @@ const CharacterUnit = ({ } const image = ( -
+
{character?.name[locale]} ) : ( diff --git a/components/ExtraSummons/index.tsx b/components/ExtraSummons/index.tsx index ea3f7993..58383ede 100644 --- a/components/ExtraSummons/index.tsx +++ b/components/ExtraSummons/index.tsx @@ -14,6 +14,7 @@ interface Props { removeSummon: (id: string) => void updateObject: (object: SearchableObject, position: number) => void updateUncap: (id: string, position: number, uncap: number) => void + updateTranscendence: (id: string, position: number, stage: number) => void } const ExtraSummons = (props: Props) => { @@ -36,6 +37,7 @@ const ExtraSummons = (props: Props) => { gridSummon={props.grid[props.offset + i]} updateObject={props.updateObject} updateUncap={props.updateUncap} + updateTranscendence={props.updateTranscendence} /> ) diff --git a/components/Popover/index.scss b/components/Popover/index.scss new file mode 100644 index 00000000..d89267b7 --- /dev/null +++ b/components/Popover/index.scss @@ -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; +} diff --git a/components/Popover/index.tsx b/components/Popover/index.tsx new file mode 100644 index 00000000..b38e4985 --- /dev/null +++ b/components/Popover/index.tsx @@ -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 + > {} + +export const Popover = PopoverPrimitive.Root +export const PopoverAnchor = PopoverPrimitive.Anchor +export const PopoverTrigger = PopoverPrimitive.Trigger + +export const PopoverContent = React.forwardRef( + ({ children, ...props }: PropsWithChildren, forwardedRef) => { + const classes = classnames(props.className, { + Popover: true, + }) + + return ( + + + {children} + + + + ) + } +) diff --git a/components/SummonGrid/index.tsx b/components/SummonGrid/index.tsx index 0bf300e9..0da1200e 100644 --- a/components/SummonGrid/index.tsx +++ b/components/SummonGrid/index.tsx @@ -42,10 +42,14 @@ const SummonGrid = (props: Props) => { const { party, grid } = useSnapshot(appState) 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<{ [key: number]: number }>({}) + const [previousTranscendenceStages, setPreviousTranscendenceStages] = + useState<{ + [key: number]: number + }>({}) // Set the editable flag only on first load useEffect(() => { @@ -155,6 +159,7 @@ const SummonGrid = (props: Props) => { // Note: Saves, but debouncing is not working properly async function saveUncap(id: string, position: number, uncapLevel: number) { storePreviousUncapValue(position) + storePreviousTranscendenceStage(position) try { if (uncapLevel != previousUncapValues[position]) @@ -166,11 +171,17 @@ const SummonGrid = (props: Props) => { // Revert optimistic UI updateUncapLevel(position, previousUncapValues[position]) + updateTranscendenceStage(position, previousTranscendenceStages[position]) // Remove optimistic key - let newPreviousValues = { ...previousUncapValues } - delete newPreviousValues[position] - setPreviousUncapValues(newPreviousValues) + let newPreviousTranscendenceStages = { ...previousTranscendenceStages } + let newPreviousUncapValues = { ...previousUncapValues } + + delete newPreviousTranscendenceStages[position] + delete newPreviousUncapValues[position] + + setPreviousTranscendenceStages(newPreviousTranscendenceStages) + setPreviousUncapValues(newPreviousUncapValues) } } @@ -184,21 +195,25 @@ const SummonGrid = (props: Props) => { accountState.account.user && party.user.id === accountState.account.user.id ) { - memoizeAction(id, position, uncapLevel) + memoizeUncapAction(id, position, uncapLevel) // Optimistically update UI updateUncapLevel(position, uncapLevel) + + if (uncapLevel < 6) { + updateTranscendenceStage(position, 0) + } } } - const memoizeAction = useCallback( + const memoizeUncapAction = useCallback( (id: string, position: number, uncapLevel: number) => { - debouncedAction(id, position, uncapLevel) + debouncedUncapAction(id, position, uncapLevel) }, [props, previousUncapValues] ) - const debouncedAction = useMemo( + const debouncedUncapAction = useMemo( () => debounce((id, position, number) => { saveUncap(id, position, number) @@ -237,6 +252,116 @@ const SummonGrid = (props: Props) => { 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) { try { const response = await api.endpoints.grid_summons.destroy({ id: id }) @@ -267,6 +392,7 @@ const SummonGrid = (props: Props) => { removeSummon={removeSummon} updateObject={receiveSummonFromSearch} updateUncap={initiateUncapUpdate} + updateTranscendence={initiateTranscendenceUpdate} />
) @@ -280,11 +406,14 @@ const SummonGrid = (props: Props) => { key="grid_friend_summon" position={6} unitType={2} + removeSummon={removeSummon} updateObject={receiveSummonFromSearch} updateUncap={initiateUncapUpdate} + updateTranscendence={initiateTranscendenceUpdate} />
) + const summonGridElement = (
{t('summons.summons')}
@@ -300,6 +429,7 @@ const SummonGrid = (props: Props) => { removeSummon={removeSummon} updateObject={receiveSummonFromSearch} updateUncap={initiateUncapUpdate} + updateTranscendence={initiateTranscendenceUpdate} /> ) @@ -307,6 +437,7 @@ const SummonGrid = (props: Props) => {
) + const subAuraSummonElement = ( { removeSummon={removeSummon} updateObject={receiveSummonFromSearch} updateUncap={initiateUncapUpdate} + updateTranscendence={initiateTranscendenceUpdate} /> ) + return (
diff --git a/components/SummonUnit/index.tsx b/components/SummonUnit/index.tsx index 48847211..9da0b566 100644 --- a/components/SummonUnit/index.tsx +++ b/components/SummonUnit/index.tsx @@ -29,6 +29,7 @@ interface Props { removeSummon: (id: string) => void updateObject: (object: SearchableObject, position: number) => void updateUncap: (id: string, position: number, uncap: number) => void + updateTranscendence: (id: string, position: number, stage: number) => void } const SummonUnit = ({ @@ -39,6 +40,7 @@ const SummonUnit = ({ removeSummon: sendSummonToRemove, updateObject, updateUncap, + updateTranscendence, }: Props) => { // Translations and locale const { t } = useTranslation('common') @@ -105,6 +107,10 @@ const SummonUnit = ({ if (gridSummon) updateUncap(gridSummon.id, position, uncap) } + function passTranscendenceData(stage: number) { + if (gridSummon) updateTranscendence(gridSummon.id, position, stage) + } + function removeSummon() { if (gridSummon) sendSummonToRemove(gridSummon.id) } @@ -133,11 +139,14 @@ const SummonUnit = ({ ] let suffix = '' - if ( + if (gridSummon.object.uncap.xlb && gridSummon.uncap_level == 6) { + suffix = '_03' + } else if ( upgradedSummons.indexOf(summon.granblue_id.toString()) != -1 && gridSummon.uncap_level == 5 - ) + ) { suffix = '_02' + } // Generate the correct source for the summon if (unitType == 0 || unitType == 2) @@ -243,8 +252,13 @@ const SummonUnit = ({ type="summon" ulb={gridSummon.object.uncap.ulb || false} flb={gridSummon.object.uncap.flb || false} + xlb={gridSummon.object.uncap.xlb || false} + editable={editable} uncapLevel={gridSummon.uncap_level} + transcendenceStage={gridSummon.transcendence_step} + position={gridSummon.position} updateUncap={passUncapData} + updateTranscendence={passTranscendenceData} special={false} /> ) : ( diff --git a/components/TranscendenceFragment/index.scss b/components/TranscendenceFragment/index.scss new file mode 100644 index 00000000..bab27223 --- /dev/null +++ b/components/TranscendenceFragment/index.scss @@ -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); + // } + } +} diff --git a/components/TranscendenceFragment/index.tsx b/components/TranscendenceFragment/index.tsx new file mode 100644 index 00000000..0daaef18 --- /dev/null +++ b/components/TranscendenceFragment/index.tsx @@ -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 ( + + ) +} + +TranscendenceFragment.defaultProps = { + interactive: false, + visible: false, +} + +export default TranscendenceFragment diff --git a/components/TranscendencePopover/index.scss b/components/TranscendencePopover/index.scss new file mode 100644 index 00000000..e54cec64 --- /dev/null +++ b/components/TranscendencePopover/index.scss @@ -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; + } +} diff --git a/components/TranscendencePopover/index.tsx b/components/TranscendencePopover/index.tsx new file mode 100644 index 00000000..4284db55 --- /dev/null +++ b/components/TranscendencePopover/index.tsx @@ -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 + > { + open: boolean + stage: number + onOpenChange?: (open: boolean) => void + sendValue?: (stage: number) => void +} + +const TranscendencePopover = ({ + children, + open: popoverOpen, + stage, + tabIndex, + onOpenChange, + sendValue, +}: PropsWithChildren) => { + const { t } = useTranslation('common') + + const [open, setOpen] = useState(false) + + const [currentStage, setCurrentStage] = useState(0) + + const popoverRef = React.createRef() + + 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 ( + + {children} + + +

+ {t('level')}  + {200 + 10 * currentStage} +

+
+
+ ) +} + +export default TranscendencePopover diff --git a/components/TranscendenceStar/index.scss b/components/TranscendenceStar/index.scss new file mode 100644 index 00000000..ed84a405 --- /dev/null +++ b/components/TranscendenceStar/index.scss @@ -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'); + } + } + } +} diff --git a/components/TranscendenceStar/index.tsx b/components/TranscendenceStar/index.tsx new file mode 100644 index 00000000..161be68b --- /dev/null +++ b/components/TranscendenceStar/index.tsx @@ -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 + > { + 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 ( +
{}} + onMouseLeave={interactive ? handleMouseLeave : () => {}} + tabIndex={tabIndex} + > +
+ {[...Array(NUM_FRAGMENTS)].map((e, i) => { + const loopStage = i + 1 + return interactive ? ( + + ) : ( + '' + ) + })} +
+ +
+ ) +} + +TranscendenceStar.defaultProps = { + stage: 0, + editable: false, + interactive: false, +} + +export default TranscendenceStar diff --git a/components/UncapIndicator/index.scss b/components/UncapIndicator/index.scss index 5b174197..42e1dcd8 100644 --- a/components/UncapIndicator/index.scss +++ b/components/UncapIndicator/index.scss @@ -1,3 +1,7 @@ +.Uncap { + position: relative; +} + .UncapIndicator { display: flex; flex-direction: row; diff --git a/components/UncapIndicator/index.tsx b/components/UncapIndicator/index.tsx index b8ab47ec..647a0a7d 100644 --- a/components/UncapIndicator/index.tsx +++ b/components/UncapIndicator/index.tsx @@ -1,5 +1,7 @@ -import React from 'react' +import React, { useState } from 'react' import UncapStar from '~components/UncapStar' +import TranscendencePopover from '~components/TranscendencePopover' +import TranscendenceStar from '~components/TranscendenceStar' import './index.scss' @@ -7,14 +9,22 @@ interface Props { type: 'character' | 'weapon' | 'summon' rarity?: number uncapLevel?: number + position: number + transcendenceStage?: number + editable: boolean flb: boolean ulb: boolean + xlb?: boolean special: boolean updateUncap?: (index: number) => void + updateTranscendence?: (index: number) => void } const UncapIndicator = (props: Props) => { const numStars = setNumStars() + + const [popoverOpen, setPopoverOpen] = useState(false) + function setNumStars() { let numStars @@ -37,7 +47,9 @@ const UncapIndicator = (props: Props) => { } } } else { - if (props.ulb) { + if (props.xlb) { + numStars = 6 + } else if (props.ulb) { numStars = 5 } else if (props.flb) { 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) => { - return ( - = props.uncapLevel : false} + const tabIndex = props.position * 7 + i + 1 + return props.type === 'character' || props.type === 'summon' ? ( + + togglePopover(true)} + /> + + ) : ( + ) } @@ -76,7 +115,8 @@ const UncapIndicator = (props: Props) => { empty={props.uncapLevel != null ? i >= props.uncapLevel : false} key={`star_${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} key={`star_${i}`} index={i} - onClick={toggleStar} + onStarClick={toggleStar} + tabIndex={props.position * 7 + i + 1} /> ) } @@ -101,29 +142,38 @@ const UncapIndicator = (props: Props) => { empty={props.uncapLevel != null ? i >= props.uncapLevel : false} key={`star_${i}`} index={i} - onClick={toggleStar} + onStarClick={toggleStar} + tabIndex={props.position * 7 + i + 1} /> ) } return ( -
    - {Array.from(Array(numStars)).map((x, i) => { - if (props.type === 'character' && i > 4) { - if (props.special) return ulb(i) - else return transcendence(i) - } else if ( - (props.special && props.type === 'character' && i == 3) || - (props.type === 'character' && i == 4) || - (props.type !== 'character' && i > 2) - ) { - return flb(i) - } else { - return mlb(i) - } - })} -
+
+
    + {Array.from(Array(numStars)).map((x, i) => { + if (props.type === 'character' && i > 4) { + if (props.special) return ulb(i) + else return transcendence(i) + } else if (props.type === 'summon' && i > 4) { + return transcendence(i) + } else if ( + (props.special && props.type === 'character' && i == 3) || + (props.type === 'character' && i == 4) || + (props.type !== 'character' && i > 2) + ) { + return flb(i) + } else { + return mlb(i) + } + })} +
+
) } +UncapIndicator.defaultProps = { + editable: false, +} + export default UncapIndicator diff --git a/components/UncapStar/index.tsx b/components/UncapStar/index.tsx index 9b0969f7..5393a494 100644 --- a/components/UncapStar/index.tsx +++ b/components/UncapStar/index.tsx @@ -3,13 +3,17 @@ import classnames from 'classnames' import './index.scss' -interface Props { +interface Props + extends React.DetailedHTMLProps< + React.DialogHTMLAttributes, + HTMLDivElement + > { empty: boolean special: boolean flb: boolean ulb: boolean index: number - onClick: (index: number, empty: boolean) => void + onStarClick: (index: number, empty: boolean) => void } const UncapStar = (props: Props) => { @@ -23,10 +27,12 @@ const UncapStar = (props: Props) => { }) function clicked() { - props.onClick(props.index, props.empty) + props.onStarClick(props.index, props.empty) } - return
  • + return ( +
  • + ) } UncapStar.defaultProps = { diff --git a/components/WeaponUnit/index.tsx b/components/WeaponUnit/index.tsx index 978c7710..8149f6a6 100644 --- a/components/WeaponUnit/index.tsx +++ b/components/WeaponUnit/index.tsx @@ -548,6 +548,7 @@ const WeaponUnit = ({ ulb={gridWeapon.object.uncap.ulb || false} flb={gridWeapon.object.uncap.flb || false} uncapLevel={gridWeapon.uncap_level} + position={gridWeapon.position} updateUncap={passUncapData} special={false} /> diff --git a/package-lock.json b/package-lock.json index 520ded8a..2f4f81e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "@radix-ui/react-dialog": "^1.0.2", "@radix-ui/react-dropdown-menu": "^2.0.1", "@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-switch": "^1.0.1", "@radix-ui/react-toast": "^1.1.2", @@ -2325,6 +2326,55 @@ "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": { "version": "1.0.1", "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" } }, + "@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": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.0.1.tgz", diff --git a/package.json b/package.json index 4771dae9..0d880a01 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@radix-ui/react-dialog": "^1.0.2", "@radix-ui/react-dropdown-menu": "^2.0.1", "@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-switch": "^1.0.1", "@radix-ui/react-toast": "^1.1.2", diff --git a/public/icons/transcendence/0/stage-0.png b/public/icons/transcendence/0/stage-0.png new file mode 100644 index 00000000..96795401 Binary files /dev/null and b/public/icons/transcendence/0/stage-0.png differ diff --git a/public/icons/transcendence/0/stage-0@2x.png b/public/icons/transcendence/0/stage-0@2x.png new file mode 100644 index 00000000..db9a33bf Binary files /dev/null and b/public/icons/transcendence/0/stage-0@2x.png differ diff --git a/public/icons/transcendence/0/stage-0@3x.png b/public/icons/transcendence/0/stage-0@3x.png new file mode 100644 index 00000000..b32db530 Binary files /dev/null and b/public/icons/transcendence/0/stage-0@3x.png differ diff --git a/public/icons/transcendence/1/stage-1-hover.png b/public/icons/transcendence/1/stage-1-hover.png new file mode 100644 index 00000000..042e6316 Binary files /dev/null and b/public/icons/transcendence/1/stage-1-hover.png differ diff --git a/public/icons/transcendence/1/stage-1-hover@2x.png b/public/icons/transcendence/1/stage-1-hover@2x.png new file mode 100644 index 00000000..b79738e3 Binary files /dev/null and b/public/icons/transcendence/1/stage-1-hover@2x.png differ diff --git a/public/icons/transcendence/1/stage-1-hover@3x.png b/public/icons/transcendence/1/stage-1-hover@3x.png new file mode 100644 index 00000000..15764200 Binary files /dev/null and b/public/icons/transcendence/1/stage-1-hover@3x.png differ diff --git a/public/icons/transcendence/1/stage-1.png b/public/icons/transcendence/1/stage-1.png new file mode 100644 index 00000000..fe78d181 Binary files /dev/null and b/public/icons/transcendence/1/stage-1.png differ diff --git a/public/icons/transcendence/1/stage-1@2x.png b/public/icons/transcendence/1/stage-1@2x.png new file mode 100644 index 00000000..febf6ceb Binary files /dev/null and b/public/icons/transcendence/1/stage-1@2x.png differ diff --git a/public/icons/transcendence/1/stage-1@3x.png b/public/icons/transcendence/1/stage-1@3x.png new file mode 100644 index 00000000..c93a7856 Binary files /dev/null and b/public/icons/transcendence/1/stage-1@3x.png differ diff --git a/public/icons/transcendence/2/stage-2-hover.png b/public/icons/transcendence/2/stage-2-hover.png new file mode 100644 index 00000000..e71be7c4 Binary files /dev/null and b/public/icons/transcendence/2/stage-2-hover.png differ diff --git a/public/icons/transcendence/2/stage-2-hover@2x.png b/public/icons/transcendence/2/stage-2-hover@2x.png new file mode 100644 index 00000000..f302baf7 Binary files /dev/null and b/public/icons/transcendence/2/stage-2-hover@2x.png differ diff --git a/public/icons/transcendence/2/stage-2-hover@3x.png b/public/icons/transcendence/2/stage-2-hover@3x.png new file mode 100644 index 00000000..02a10b51 Binary files /dev/null and b/public/icons/transcendence/2/stage-2-hover@3x.png differ diff --git a/public/icons/transcendence/2/stage-2.png b/public/icons/transcendence/2/stage-2.png new file mode 100644 index 00000000..5824a4b0 Binary files /dev/null and b/public/icons/transcendence/2/stage-2.png differ diff --git a/public/icons/transcendence/2/stage-2@2x.png b/public/icons/transcendence/2/stage-2@2x.png new file mode 100644 index 00000000..6f023ae8 Binary files /dev/null and b/public/icons/transcendence/2/stage-2@2x.png differ diff --git a/public/icons/transcendence/2/stage-2@3x.png b/public/icons/transcendence/2/stage-2@3x.png new file mode 100644 index 00000000..14826056 Binary files /dev/null and b/public/icons/transcendence/2/stage-2@3x.png differ diff --git a/public/icons/transcendence/3/stage-3-hover.png b/public/icons/transcendence/3/stage-3-hover.png new file mode 100644 index 00000000..53e2c47d Binary files /dev/null and b/public/icons/transcendence/3/stage-3-hover.png differ diff --git a/public/icons/transcendence/3/stage-3-hover@2x.png b/public/icons/transcendence/3/stage-3-hover@2x.png new file mode 100644 index 00000000..84d43cb0 Binary files /dev/null and b/public/icons/transcendence/3/stage-3-hover@2x.png differ diff --git a/public/icons/transcendence/3/stage-3-hover@3x.png b/public/icons/transcendence/3/stage-3-hover@3x.png new file mode 100644 index 00000000..165ba6be Binary files /dev/null and b/public/icons/transcendence/3/stage-3-hover@3x.png differ diff --git a/public/icons/transcendence/3/stage-3.png b/public/icons/transcendence/3/stage-3.png new file mode 100644 index 00000000..cd840c42 Binary files /dev/null and b/public/icons/transcendence/3/stage-3.png differ diff --git a/public/icons/transcendence/3/stage-3@2x.png b/public/icons/transcendence/3/stage-3@2x.png new file mode 100644 index 00000000..ee2f52a0 Binary files /dev/null and b/public/icons/transcendence/3/stage-3@2x.png differ diff --git a/public/icons/transcendence/3/stage-3@3x.png b/public/icons/transcendence/3/stage-3@3x.png new file mode 100644 index 00000000..221e86b0 Binary files /dev/null and b/public/icons/transcendence/3/stage-3@3x.png differ diff --git a/public/icons/transcendence/4/stage-4-hover.png b/public/icons/transcendence/4/stage-4-hover.png new file mode 100644 index 00000000..e304b9e8 Binary files /dev/null and b/public/icons/transcendence/4/stage-4-hover.png differ diff --git a/public/icons/transcendence/4/stage-4-hover@2x.png b/public/icons/transcendence/4/stage-4-hover@2x.png new file mode 100644 index 00000000..13701c76 Binary files /dev/null and b/public/icons/transcendence/4/stage-4-hover@2x.png differ diff --git a/public/icons/transcendence/4/stage-4-hover@3x.png b/public/icons/transcendence/4/stage-4-hover@3x.png new file mode 100644 index 00000000..e3bc9599 Binary files /dev/null and b/public/icons/transcendence/4/stage-4-hover@3x.png differ diff --git a/public/icons/transcendence/4/stage-4.png b/public/icons/transcendence/4/stage-4.png new file mode 100644 index 00000000..5e65155e Binary files /dev/null and b/public/icons/transcendence/4/stage-4.png differ diff --git a/public/icons/transcendence/4/stage-4@2x.png b/public/icons/transcendence/4/stage-4@2x.png new file mode 100644 index 00000000..b5b726b4 Binary files /dev/null and b/public/icons/transcendence/4/stage-4@2x.png differ diff --git a/public/icons/transcendence/4/stage-4@3x.png b/public/icons/transcendence/4/stage-4@3x.png new file mode 100644 index 00000000..142753ee Binary files /dev/null and b/public/icons/transcendence/4/stage-4@3x.png differ diff --git a/public/icons/transcendence/5/stage-5-hover.png b/public/icons/transcendence/5/stage-5-hover.png new file mode 100644 index 00000000..01adb1b1 Binary files /dev/null and b/public/icons/transcendence/5/stage-5-hover.png differ diff --git a/public/icons/transcendence/5/stage-5-hover@2x.png b/public/icons/transcendence/5/stage-5-hover@2x.png new file mode 100644 index 00000000..2026dd64 Binary files /dev/null and b/public/icons/transcendence/5/stage-5-hover@2x.png differ diff --git a/public/icons/transcendence/5/stage-5-hover@3x.png b/public/icons/transcendence/5/stage-5-hover@3x.png new file mode 100644 index 00000000..d5b644ac Binary files /dev/null and b/public/icons/transcendence/5/stage-5-hover@3x.png differ diff --git a/public/icons/transcendence/5/stage-5.png b/public/icons/transcendence/5/stage-5.png new file mode 100644 index 00000000..a99ad6fe Binary files /dev/null and b/public/icons/transcendence/5/stage-5.png differ diff --git a/public/icons/transcendence/5/stage-5@2x.png b/public/icons/transcendence/5/stage-5@2x.png new file mode 100644 index 00000000..14551b35 Binary files /dev/null and b/public/icons/transcendence/5/stage-5@2x.png differ diff --git a/public/icons/transcendence/5/stage-5@3x.png b/public/icons/transcendence/5/stage-5@3x.png new file mode 100644 index 00000000..64d7a506 Binary files /dev/null and b/public/icons/transcendence/5/stage-5@3x.png differ diff --git a/public/icons/transcendence/interactive/interactive-base.png b/public/icons/transcendence/interactive/interactive-base.png new file mode 100644 index 00000000..0fb4ae3c Binary files /dev/null and b/public/icons/transcendence/interactive/interactive-base.png differ diff --git a/public/icons/transcendence/interactive/interactive-base@2x.png b/public/icons/transcendence/interactive/interactive-base@2x.png new file mode 100644 index 00000000..e2aab19b Binary files /dev/null and b/public/icons/transcendence/interactive/interactive-base@2x.png differ diff --git a/public/icons/transcendence/interactive/interactive-base@3x.png b/public/icons/transcendence/interactive/interactive-base@3x.png new file mode 100644 index 00000000..7df350cb Binary files /dev/null and b/public/icons/transcendence/interactive/interactive-base@3x.png differ diff --git a/public/icons/transcendence/interactive/interactive-piece.png b/public/icons/transcendence/interactive/interactive-piece.png new file mode 100644 index 00000000..06d240b2 Binary files /dev/null and b/public/icons/transcendence/interactive/interactive-piece.png differ diff --git a/public/icons/transcendence/interactive/interactive-piece@2x.png b/public/icons/transcendence/interactive/interactive-piece@2x.png new file mode 100644 index 00000000..d2585a80 Binary files /dev/null and b/public/icons/transcendence/interactive/interactive-piece@2x.png differ diff --git a/public/icons/transcendence/interactive/interactive-piece@3x.png b/public/icons/transcendence/interactive/interactive-piece@3x.png new file mode 100644 index 00000000..d9106def Binary files /dev/null and b/public/icons/transcendence/interactive/interactive-piece@3x.png differ diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 77f417db..e97072a2 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -370,5 +370,6 @@ "no_raid": "No raid", "no_user": "Anonymous", "no_job": "No class", - "no_value": "No value" + "no_value": "No value", + "level": "Level" } diff --git a/public/locales/ja/common.json b/public/locales/ja/common.json index 14570323..c0a355a6 100644 --- a/public/locales/ja/common.json +++ b/public/locales/ja/common.json @@ -371,5 +371,6 @@ "no_raid": "マルチなし", "no_user": "無名", "no_job": "ジョブなし", - "no_value": "値なし" + "no_value": "値なし", + "level": "レベル" } diff --git a/styles/globals.scss b/styles/globals.scss index b0fb3efa..31f35573 100644 --- a/styles/globals.scss +++ b/styles/globals.scss @@ -321,3 +321,38 @@ i.tag { 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); + } +} diff --git a/styles/mixins.scss b/styles/mixins.scss index e39df63d..0fc2db64 100644 --- a/styles/mixins.scss +++ b/styles/mixins.scss @@ -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; + } +} diff --git a/styles/variables.scss b/styles/variables.scss index d7a2f265..76e3cc4d 100644 --- a/styles/variables.scss +++ b/styles/variables.scss @@ -26,6 +26,7 @@ $unit-half: calc($unit / 2); $unit-2x: $unit * 2; $unit-3x: $unit * 3; $unit-4x: $unit * 4; +$unit-5x: $unit * 5; $unit-6x: $unit * 6; $unit-8x: $unit * 8; $unit-10x: $unit * 10; diff --git a/types/GridCharacter.d.ts b/types/GridCharacter.d.ts index 90905bb2..efb82041 100644 --- a/types/GridCharacter.d.ts +++ b/types/GridCharacter.d.ts @@ -3,6 +3,7 @@ interface GridCharacter { position: number object: Character uncap_level: number + transcendence_step: number over_mastery: CharacterOverMastery aetherial_mastery: ExtendedMastery awakening: { diff --git a/types/GridSummon.d.ts b/types/GridSummon.d.ts index bfde1535..e8b14831 100644 --- a/types/GridSummon.d.ts +++ b/types/GridSummon.d.ts @@ -5,4 +5,5 @@ interface GridSummon { position: number object: Summon uncap_level: number + transcendence_step: number } diff --git a/types/Summon.d.ts b/types/Summon.d.ts index c71c62ce..0f6de332 100644 --- a/types/Summon.d.ts +++ b/types/Summon.d.ts @@ -15,16 +15,19 @@ interface Summon { max_hp: number max_hp_flb: number max_hp_ulb: number + max_hp_xlb: number } atk: { min_atk: number max_atk: number max_atk_flb: number max_atk_ulb: number + max_atk_xlb: number } uncap: { flb: boolean ulb: boolean + xlb: boolean } position?: number } diff --git a/utils/api.tsx b/utils/api.tsx index 0acb1d40..352f60ed 100644 --- a/utils/api.tsx +++ b/utils/api.tsx @@ -138,7 +138,8 @@ class Api { return axios.post(resourceUrl, { [resource]: { id: id, - uncap_level: value + uncap_level: value, + transcendence_step: 0 } }) }