Fix hovercards

* Extracted header into HovercardHeader component
This commit is contained in:
Justin Edmund 2023-06-30 12:21:54 -07:00
parent 37e0525243
commit 7baa9dddd9
9 changed files with 367 additions and 311 deletions

View file

@ -0,0 +1,57 @@
.root {
display: flex;
flex-direction: column;
gap: calc($unit / 2);
.title {
align-items: center;
display: flex;
flex-direction: row;
gap: $unit * 2;
h4 {
flex-grow: 1;
font-size: $font-medium;
line-height: 1.2;
min-width: 140px;
}
img {
height: auto;
width: 100px;
}
.image {
position: relative;
.perpetuity {
position: absolute;
background-image: url('/icons/perpetuity/filled.svg');
background-size: $unit-3x $unit-3x;
z-index: 20;
top: $unit-half * -1;
right: $unit-3x;
width: $unit-3x;
height: $unit-3x;
}
}
}
.subInfo {
align-items: center;
display: flex;
flex-direction: row;
gap: $unit * 2;
.icons {
display: flex;
flex-direction: row;
flex-grow: 1;
gap: $unit;
}
.UncapIndicator {
min-width: 100px;
}
}
}

View file

@ -0,0 +1,150 @@
import { useRouter } from 'next/router'
import UncapIndicator from '~components/uncap/UncapIndicator'
import WeaponLabelIcon from '~components/weapon/WeaponLabelIcon'
import styles from './index.module.scss'
interface Props {
gridObject: GridCharacter | GridSummon | GridWeapon
object: Character | Summon | Weapon
type: 'character' | 'summon' | 'weapon'
}
const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light']
const Proficiency = [
'none',
'sword',
'dagger',
'axe',
'spear',
'bow',
'staff',
'fist',
'harp',
'gun',
'katana',
]
const HovercardHeader = ({ gridObject, object, type, ...props }: Props) => {
const router = useRouter()
const locale =
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
const overlay = () => {
if (type === 'character') {
const gridCharacter = gridObject as GridCharacter
if (gridCharacter.perpetuity) return <i className={styles.perpetuity} />
} else if (type === 'summon') {
const gridSummon = gridObject as GridSummon
if (gridSummon.quick_summon) return <i className={styles.quickSummon} />
}
}
const characterImage = () => {
const gridCharacter = gridObject as GridCharacter
const character = object as Character
// 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'
else if (gridCharacter.uncap_level > 2) suffix = '02'
return `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/chara-grid/${character.granblue_id}_${suffix}.jpg`
}
const summonImage = () => {
const summon = object as Summon
const gridSummon = gridObject as GridSummon
const upgradedSummons = [
'2040094000',
'2040100000',
'2040080000',
'2040098000',
'2040090000',
'2040084000',
'2040003000',
'2040056000',
]
let suffix = ''
if (
upgradedSummons.indexOf(summon.granblue_id.toString()) != -1 &&
gridSummon.uncap_level == 5
) {
suffix = '_02'
} else if (
gridSummon.object.uncap.xlb &&
gridSummon.transcendence_step > 0
) {
suffix = '_03'
}
// Generate the correct source for the summon
return `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/summon-grid/${summon.granblue_id}${suffix}.jpg`
}
const weaponImage = () => {
const gridWeapon = gridObject as GridWeapon
const weapon = object as Weapon
if (gridWeapon.object.element == 0 && gridWeapon.element)
return `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}_${gridWeapon.element}.jpg`
else
return `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}.jpg`
}
const image = () => {
switch (type) {
case 'character':
return characterImage()
case 'summon':
return summonImage()
case 'weapon':
return weaponImage()
}
}
return (
<header className={styles.root}>
<div className={styles.title}>
<h4>{object.name[locale]}</h4>
<div className={styles.image}>
{overlay()}
<img alt={object.name[locale]} src={image()} />
</div>
</div>
<div className={styles.subInfo}>
<div className={styles.icons}>
<WeaponLabelIcon labelType={Element[object.element]} />
{'proficiency' in object && Array.isArray(object.proficiency) && (
<WeaponLabelIcon labelType={Proficiency[object.proficiency[0]]} />
)}
{'proficiency' in object && !Array.isArray(object.proficiency) && (
<WeaponLabelIcon labelType={Proficiency[object.proficiency]} />
)}
{'proficiency' in object &&
Array.isArray(object.proficiency) &&
object.proficiency.length > 1 && (
<WeaponLabelIcon labelType={Proficiency[object.proficiency[1]]} />
)}
</div>
<UncapIndicator
type={type}
ulb={object.uncap.ulb || false}
flb={object.uncap.flb || false}
transcendenceStage={
'transcendence_step' in gridObject
? gridObject.transcendence_step
: 0
}
special={'special' in object ? object.special : false}
/>
</div>
</header>
)
}
export default HovercardHeader

View file

@ -1,20 +1,5 @@
.Character.HovercardContent { .content {
.title .Image { .mastery {
position: relative;
.Perpetuity {
position: absolute;
background-image: url('/icons/perpetuity/filled.svg');
background-size: $unit-3x $unit-3x;
z-index: 20;
top: $unit-half * -1;
right: $unit-3x;
width: $unit-3x;
height: $unit-3x;
}
}
.Mastery {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: $unit; gap: $unit;
@ -24,7 +9,7 @@
flex-direction: column; flex-direction: column;
gap: $unit-half; gap: $unit-half;
.ExtendedMastery { .extendedMastery {
align-items: center; align-items: center;
display: flex; display: flex;
gap: $unit-half; gap: $unit-half;
@ -40,7 +25,7 @@
} }
} }
.Awakening { .awakening {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: $unit; gap: $unit;

View file

@ -19,6 +19,7 @@ import {
import { ExtendedMastery } from '~types' import { ExtendedMastery } from '~types'
import styles from './index.module.scss' import styles from './index.module.scss'
import HovercardHeader from '~components/HovercardHeader'
interface Props { interface Props {
gridCharacter: GridCharacter gridCharacter: GridCharacter
@ -33,20 +34,6 @@ const CharacterHovercard = (props: Props) => {
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light'] const Element = ['null', 'wind', 'fire', 'water', 'earth', 'dark', 'light']
const Proficiency = [
'none',
'sword',
'dagger',
'axe',
'spear',
'bow',
'staff',
'fist',
'harp',
'gun',
'katana',
]
const tintElement = Element[props.gridCharacter.object.element] const tintElement = Element[props.gridCharacter.object.element]
function goTo() { function goTo() {
@ -56,30 +43,6 @@ const CharacterHovercard = (props: Props) => {
window.open(url, '_blank') window.open(url, '_blank')
} }
const perpetuity = () => {
if (props.gridCharacter && props.gridCharacter.perpetuity) {
return <i className="Perpetuity" />
}
}
function characterImage() {
let imgSrc = ''
if (props.gridCharacter) {
const character = props.gridCharacter.object
// Change the image based on the uncap level
let suffix = '01'
if (props.gridCharacter.uncap_level == 6) suffix = '04'
else if (props.gridCharacter.uncap_level == 5) suffix = '03'
else if (props.gridCharacter.uncap_level > 2) suffix = '02'
imgSrc = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/chara-grid/${character.granblue_id}_${suffix}.jpg`
}
return imgSrc
}
function masteryElement(dictionary: ItemSkill[], mastery: ExtendedMastery) { function masteryElement(dictionary: ItemSkill[], mastery: ExtendedMastery) {
const canonicalMastery = dictionary.find( const canonicalMastery = dictionary.find(
(item) => item.id === mastery.modifier (item) => item.id === mastery.modifier
@ -87,7 +50,7 @@ const CharacterHovercard = (props: Props) => {
if (canonicalMastery) { if (canonicalMastery) {
return ( return (
<li className="ExtendedMastery" key={canonicalMastery.id}> <li className={styles.extendedMastery} key={canonicalMastery.id}>
<img <img
alt={canonicalMastery.name[locale]} alt={canonicalMastery.name[locale]}
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/mastery/${canonicalMastery.slug}.png`} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/mastery/${canonicalMastery.slug}.png`}
@ -104,7 +67,7 @@ const CharacterHovercard = (props: Props) => {
const overMasterySection = () => { const overMasterySection = () => {
if (props.gridCharacter && props.gridCharacter.over_mastery) { if (props.gridCharacter && props.gridCharacter.over_mastery) {
return ( return (
<section className="Mastery"> <section className={styles.mastery}>
<h5 className={tintElement}> <h5 className={tintElement}>
{t('modals.characters.subtitles.ring')} {t('modals.characters.subtitles.ring')}
</h5> </h5>
@ -136,7 +99,7 @@ const CharacterHovercard = (props: Props) => {
props.gridCharacter.aetherial_mastery.modifier > 0 props.gridCharacter.aetherial_mastery.modifier > 0
) { ) {
return ( return (
<section className="Mastery"> <section className={styles.mastery}>
<h5 className={tintElement}> <h5 className={tintElement}>
{t('modals.characters.subtitles.earring')} {t('modals.characters.subtitles.earring')}
</h5> </h5>
@ -154,7 +117,7 @@ const CharacterHovercard = (props: Props) => {
const permanentMasterySection = () => { const permanentMasterySection = () => {
if (props.gridCharacter && props.gridCharacter.perpetuity) { if (props.gridCharacter && props.gridCharacter.perpetuity) {
return ( return (
<section className="Mastery"> <section className={styles.mastery}>
<h5 className={tintElement}> <h5 className={tintElement}>
{t('modals.characters.subtitles.permanent')} {t('modals.characters.subtitles.permanent')}
</h5> </h5>
@ -176,7 +139,7 @@ const CharacterHovercard = (props: Props) => {
if (gridAwakening) { if (gridAwakening) {
return ( return (
<section className="Awakening"> <section className={styles.awakening}>
<h5 className={tintElement}> <h5 className={tintElement}>
{t('modals.characters.subtitles.awakening')} {t('modals.characters.subtitles.awakening')}
</h5> </h5>
@ -200,7 +163,7 @@ const CharacterHovercard = (props: Props) => {
className={tintElement} className={tintElement}
text={t('buttons.wiki')} text={t('buttons.wiki')}
onClick={goTo} onClick={goTo}
contained={true} bound={true}
/> />
) )
@ -209,51 +172,12 @@ const CharacterHovercard = (props: Props) => {
<HovercardTrigger asChild onClick={props.onTriggerClick}> <HovercardTrigger asChild onClick={props.onTriggerClick}>
{props.children} {props.children}
</HovercardTrigger> </HovercardTrigger>
<HovercardContent className="Character" side="top"> <HovercardContent className={styles.content} side="top">
<div className="top"> <HovercardHeader
<div className="title"> gridObject={props.gridCharacter}
<h4>{props.gridCharacter.object.name[locale]}</h4> object={props.gridCharacter.object}
<div className="Image"> type="character"
{perpetuity()} />
<img
alt={props.gridCharacter.object.name[locale]}
src={characterImage()}
/>
</div>
</div>
<div className="subInfo">
<div className="icons">
<WeaponLabelIcon
labelType={Element[props.gridCharacter.object.element]}
/>
<WeaponLabelIcon
labelType={
Proficiency[
props.gridCharacter.object.proficiency.proficiency1
]
}
/>
{props.gridCharacter.object.proficiency.proficiency2 ? (
<WeaponLabelIcon
labelType={
Proficiency[
props.gridCharacter.object.proficiency.proficiency2
]
}
/>
) : (
''
)}
</div>
<UncapIndicator
type="character"
ulb={props.gridCharacter.object.uncap.ulb || false}
flb={props.gridCharacter.object.uncap.flb || false}
transcendenceStage={props.gridCharacter.transcendence_step}
special={props.gridCharacter.object.special}
/>
</div>
</div>
{wikiButton} {wikiButton}
{awakeningSection()} {awakeningSection()}
{overMasterySection()} {overMasterySection()}

View file

@ -1,12 +1,9 @@
div[data-radix-popper-content-wrapper] { .hovercard {
z-index: 10 !important;
}
.HovercardContent {
animation: scaleIn $duration-zoom ease-out; animation: scaleIn $duration-zoom ease-out;
transform-origin: var(--radix-hover-card-content-transform-origin); transform-origin: var(--radix-hover-card-content-transform-origin);
background: var(--dialog-bg); background: var(--dialog-bg);
border-radius: $card-corner; border-radius: $card-corner;
border: 1px solid rgba(0, 0, 0, 0.1);
color: var(--text-primary); color: var(--text-primary);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -16,77 +13,47 @@ div[data-radix-popper-content-wrapper] {
padding: $unit-2x; padding: $unit-2x;
width: 300px; width: 300px;
.top {
display: flex;
flex-direction: column;
gap: calc($unit / 2);
.title {
align-items: center;
display: flex;
flex-direction: row;
gap: $unit * 2;
h4 {
flex-grow: 1;
font-size: $font-medium;
line-height: 1.2;
min-width: 140px;
}
img {
height: auto;
width: 100px;
}
}
.subInfo {
align-items: center;
display: flex;
flex-direction: row;
gap: $unit * 2;
.icons {
display: flex;
flex-direction: row;
flex-grow: 1;
gap: $unit;
}
.UncapIndicator {
min-width: 100px;
}
}
}
section { section {
h5 { @keyframes scaleIn {
font-size: $font-small; 0% {
font-weight: $medium; opacity: 0;
opacity: 0.7; transform: scale(0);
&.wind {
color: $wind-bg-20;
} }
20% {
&.fire { opacity: 0.2;
color: $fire-bg-20; transform: scale(0.4);
} }
40% {
&.water { opacity: 0.4;
color: $water-bg-20; transform: scale(0.8);
} }
60% {
&.earth { opacity: 0.6;
color: $earth-bg-20; transform: scale(1);
} }
65% {
&.dark { opacity: 0.65;
color: $dark-bg-10; transform: scale(1.1);
} }
70% {
&.light { opacity: 0.7;
color: $light-bg-20; transform: scale(1);
}
75% {
opacity: 0.75;
transform: scale(0.98);
}
80% {
opacity: 0.8;
transform: scale(1.02);
}
90% {
opacity: 0.9;
transform: scale(0.96);
}
100% {
opacity: 1;
transform: scale(1);
} }
} }
} }

View file

@ -14,7 +14,7 @@ export const HovercardContent = ({
...props ...props
}: PropsWithChildren<Props>) => { }: PropsWithChildren<Props>) => {
const classes = classNames(props.className, { const classes = classNames(props.className, {
HovercardContent: true, [styles.hovercard]: true,
}) })
return ( return (
<HoverCardPrimitive.Portal> <HoverCardPrimitive.Portal>

View file

@ -8,8 +8,7 @@ import {
HovercardTrigger, HovercardTrigger,
} from '~components/common/Hovercard' } from '~components/common/Hovercard'
import Button from '~components/common/Button' import Button from '~components/common/Button'
import WeaponLabelIcon from '~components/weapon/WeaponLabelIcon' import HovercardHeader from '~components/HovercardHeader'
import UncapIndicator from '~components/uncap/UncapIndicator'
import styles from './index.module.scss' import styles from './index.module.scss'
@ -79,7 +78,7 @@ const SummonHovercard = (props: Props) => {
className={tintElement} className={tintElement}
text={t('buttons.wiki')} text={t('buttons.wiki')}
onClick={goTo} onClick={goTo}
contained={true} bound={true}
/> />
) )
@ -88,31 +87,12 @@ const SummonHovercard = (props: Props) => {
<HovercardTrigger asChild onClick={props.onTriggerClick}> <HovercardTrigger asChild onClick={props.onTriggerClick}>
{props.children} {props.children}
</HovercardTrigger> </HovercardTrigger>
<HovercardContent className="Summon" side={props.side}> <HovercardContent side={props.side}>
<div className="top"> <HovercardHeader
<div className="title"> gridObject={props.gridSummon}
<h4>{props.gridSummon.object.name[locale]}</h4> object={props.gridSummon.object}
<img type="summon"
alt={props.gridSummon.object.name[locale]} />
src={summonImage()}
/>
</div>
<div className="subInfo">
<div className="icons">
<WeaponLabelIcon
labelType={Element[props.gridSummon.object.element]}
/>
</div>
<UncapIndicator
type="summon"
ulb={props.gridSummon.object.uncap.ulb || false}
flb={props.gridSummon.object.uncap.flb || false}
xlb={props.gridSummon.object.uncap.xlb || false}
transcendenceStage={props.gridSummon.transcendence_step}
special={false}
/>
</div>
</div>
{wikiButton} {wikiButton}
</HovercardContent> </HovercardContent>
</Hovercard> </Hovercard>

View file

@ -1,34 +1,64 @@
.Weapon.HovercardContent { .content {
section {
display: flex;
flex-direction: column;
gap: $unit-half;
}
.awakening {
display: flex;
flex-direction: column;
gap: $unit-half;
}
.skill {
align-items: center;
display: flex;
gap: $unit-half;
img {
width: $unit-4x;
}
strong {
font-weight: $bold;
}
&.axSkill .axImageWrapper {
display: flex;
align-items: center;
justify-content: center;
width: $unit-4x;
height: $unit-4x;
}
&.axSkill.secondary .axImageWrapper {
img {
width: $unit-3x;
height: $unit-3x;
}
}
}
.skills { .skills {
display: flex; display: flex;
flex-direction: row; flex-direction: column;
justify-content: space-between; justify-content: space-between;
padding-right: $unit-2x; padding-right: $unit-2x;
.axSkill { &.secondary {
align-items: center; gap: $unit * 1.5;
display: flex;
flex-direction: row;
&.primary img { img {
height: 64px; height: 24px;
width: 64px; width: 24px;
} }
}
&.secondary { span {
gap: $unit * 1.5; font-size: $font-small;
font-weight: $medium;
img { text-align: center;
height: 36px;
width: 36px;
}
}
span {
font-size: $font-small;
font-weight: $medium;
text-align: center;
}
} }
} }
@ -38,24 +68,4 @@
font-size: $normal; font-size: $normal;
gap: $unit-half; gap: $unit-half;
} }
.awakening {
display: flex;
flex-direction: column;
gap: $unit-half;
& > div {
align-items: center;
display: flex;
gap: $unit-half;
img {
width: $unit-4x;
}
strong {
font-weight: $bold;
}
}
}
} }

View file

@ -1,15 +1,15 @@
import React from 'react' import React from 'react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
import classNames from 'classnames'
import { import {
Hovercard, Hovercard,
HovercardContent, HovercardContent,
HovercardTrigger, HovercardTrigger,
} from '~components/common/Hovercard' } from '~components/common/Hovercard'
import HovercardHeader from '~components/HovercardHeader'
import Button from '~components/common/Button' import Button from '~components/common/Button'
import WeaponLabelIcon from '~components/weapon/WeaponLabelIcon'
import UncapIndicator from '~components/uncap/UncapIndicator'
import ax from '~data/ax' import ax from '~data/ax'
@ -148,11 +148,11 @@ const WeaponHovercard = (props: Props) => {
if (gridAwakening) { if (gridAwakening) {
return ( return (
<section className="awakening"> <section className={styles.awakening}>
<h5 className={tintElement}> <h5 className={tintElement}>
{t('modals.weapon.subtitles.awakening')} {t('modals.weapon.subtitles.awakening')}
</h5> </h5>
<div> <div className={styles.skill}>
<img <img
alt={gridAwakening.type.name[locale]} alt={gridAwakening.type.name[locale]}
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/awakening/${gridAwakening.type.slug}.png`} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/awakening/${gridAwakening.type.slug}.png`}
@ -168,7 +168,7 @@ const WeaponHovercard = (props: Props) => {
} }
const keysSection = ( const keysSection = (
<section className="weaponKeys"> <section className={styles.weaponKeys}>
{WeaponKeyNames[props.gridWeapon.object.series] ? ( {WeaponKeyNames[props.gridWeapon.object.series] ? (
<h5 className={tintElement}> <h5 className={tintElement}>
{WeaponKeyNames[props.gridWeapon.object.series][locale]} {WeaponKeyNames[props.gridWeapon.object.series][locale]}
@ -182,7 +182,7 @@ const WeaponHovercard = (props: Props) => {
? Array.from(Array(props.gridWeapon.weapon_keys.length)).map((x, i) => { ? Array.from(Array(props.gridWeapon.weapon_keys.length)).map((x, i) => {
return ( return (
<div <div
className="weaponKey" className={styles.weaponKey}
key={props.gridWeapon.weapon_keys![i].id} key={props.gridWeapon.weapon_keys![i].id}
> >
<span>{props.gridWeapon.weapon_keys![i].name[locale]}</span> <span>{props.gridWeapon.weapon_keys![i].name[locale]}</span>
@ -194,29 +194,44 @@ const WeaponHovercard = (props: Props) => {
) )
const axSection = ( const axSection = (
<section className="axSkills"> <section className={styles.axSkills}>
<h5 className={tintElement}>{t('modals.weapon.subtitles.ax_skills')}</h5> <h5 className={tintElement}>{t('modals.weapon.subtitles.ax_skills')}</h5>
<div className="skills"> <div className={styles.skills}>
<div className="primary axSkill"> <div
<img className={classNames({
alt="AX1" [styles.axSkill]: true,
src={`/icons/ax/primary_${ [styles.skill]: true,
props.gridWeapon.ax ? props.gridWeapon.ax[0].modifier : '' })}
}.png`} >
/> <div className={styles.axImageWrapper}>
<img
alt="AX1"
src={`/icons/ax/primary_${
props.gridWeapon.ax ? props.gridWeapon.ax[0].modifier : ''
}.png`}
/>
</div>
<span>{createPrimaryAxSkillString()}</span> <span>{createPrimaryAxSkillString()}</span>
</div> </div>
{props.gridWeapon.ax && {props.gridWeapon.ax &&
props.gridWeapon.ax[1].modifier && props.gridWeapon.ax[1].modifier &&
props.gridWeapon.ax[1].strength ? ( props.gridWeapon.ax[1].strength ? (
<div className="secondary axSkill"> <div
<img className={classNames({
alt="AX2" [styles.secondary]: true,
src={`/icons/ax/secondary_${ [styles.axSkill]: true,
props.gridWeapon.ax ? props.gridWeapon.ax[1].modifier : '' [styles.skill]: true,
}.png`} })}
/> >
<div className={styles.axImageWrapper}>
<img
alt="AX2"
src={`/icons/ax/secondary_${
props.gridWeapon.ax ? props.gridWeapon.ax[1].modifier : ''
}.png`}
/>
</div>
<span>{createSecondaryAxSkillString()}</span> <span>{createSecondaryAxSkillString()}</span>
</div> </div>
) : ( ) : (
@ -231,7 +246,7 @@ const WeaponHovercard = (props: Props) => {
className={tintElement} className={tintElement}
text={t('buttons.wiki')} text={t('buttons.wiki')}
onClick={goTo} onClick={goTo}
contained={true} bound={true}
/> />
) )
@ -240,44 +255,12 @@ const WeaponHovercard = (props: Props) => {
<HovercardTrigger asChild onClick={props.onTriggerClick}> <HovercardTrigger asChild onClick={props.onTriggerClick}>
{props.children} {props.children}
</HovercardTrigger> </HovercardTrigger>
<HovercardContent className="Weapon" side={hovercardSide()}> <HovercardContent className={styles.content} side={hovercardSide()}>
<div className="top"> <HovercardHeader
<div className="title"> gridObject={props.gridWeapon}
<h4>{props.gridWeapon.object.name[locale]}</h4> object={props.gridWeapon.object}
<img type="weapon"
alt={props.gridWeapon.object.name[locale]} />
src={weaponImage()}
/>
</div>
<div className="subInfo">
<div className="icons">
{props.gridWeapon.object.element !== 0 ||
(props.gridWeapon.object.element === 0 &&
props.gridWeapon.element != null) ? (
<WeaponLabelIcon
labelType={
props.gridWeapon.object.element === 0 &&
props.gridWeapon.element !== 0
? Element[props.gridWeapon.element]
: Element[props.gridWeapon.object.element]
}
/>
) : (
''
)}
<WeaponLabelIcon
labelType={Proficiency[props.gridWeapon.object.proficiency]}
/>
</div>
<UncapIndicator
type="weapon"
ulb={props.gridWeapon.object.uncap.ulb || false}
flb={props.gridWeapon.object.uncap.flb || false}
special={false}
/>
</div>
</div>
{props.gridWeapon.object.ax && {props.gridWeapon.object.ax &&
props.gridWeapon.ax && props.gridWeapon.ax &&
props.gridWeapon.ax[0].modifier && props.gridWeapon.ax[0].modifier &&