(Hotfix) Popover and hovercard fixes (#379)

* Fix job accessory popover, so shields and manatura can be selected
again
* Don't show AX skill section in weapon hovercard if no AX skill is set
* Center uncap indicator under item image and fix hovercard header
layout
* Fix a bug that prevented all ring bonuses from displaying in hovercard
* Fix transcendence_step being set to 0 when updating a character's
masteries
* Fix weapon modal so you can set AX skills on weapons with rupee or exp
gain
* Ensure job accessory and transcendence popovers open/close properly
This commit is contained in:
Justin Edmund 2023-09-01 16:13:39 -07:00 committed by GitHub
parent 14ad468737
commit b50ea1fa31
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 430 additions and 252 deletions

View file

@ -41,17 +41,19 @@
align-items: center;
display: flex;
flex-direction: row;
gap: $unit * 2;
justify-content: space-between;
gap: $unit-2x;
.icons {
display: flex;
flex-direction: row;
flex-grow: 1;
gap: $unit;
}
flex-grow: 0;
gap: $unit-half;
.UncapIndicator {
min-width: 100px;
.proficiencies {
display: flex;
gap: $unit;
}
}
}
}

View file

@ -4,6 +4,7 @@ import UncapIndicator from '~components/uncap/UncapIndicator'
import WeaponLabelIcon from '~components/weapon/WeaponLabelIcon'
import styles from './index.module.scss'
import classNames from 'classnames'
interface Props {
gridObject: GridCharacter | GridSummon | GridWeapon
@ -107,6 +108,61 @@ const HovercardHeader = ({ gridObject, object, type, ...props }: Props) => {
}
}
const summonProficiency = (
<div className={styles.icons}>
<WeaponLabelIcon labelType={Element[object.element]} size="small" />
</div>
)
const weaponProficiency = (
<div className={styles.icons}>
<WeaponLabelIcon labelType={Element[object.element]} size="small" />
{'proficiency' in object && !Array.isArray(object.proficiency) && (
<WeaponLabelIcon
labelType={Proficiency[object.proficiency]}
size="small"
/>
)}
</div>
)
const characterProficiency = (
<div
className={classNames({
[styles.icons]: true,
})}
>
<WeaponLabelIcon labelType={Element[object.element]} size="small" />
{'proficiency' in object && Array.isArray(object.proficiency) && (
<WeaponLabelIcon
labelType={Proficiency[object.proficiency[0]]}
size="small"
/>
)}
{'proficiency' in object &&
Array.isArray(object.proficiency) &&
object.proficiency.length > 1 && (
<WeaponLabelIcon
labelType={Proficiency[object.proficiency[1]]}
size="small"
/>
)}
</div>
)
function proficiency() {
switch (type) {
case 'character':
return characterProficiency
case 'summon':
return summonProficiency
case 'weapon':
return weaponProficiency
}
}
return (
<header className={styles.root}>
<div className={styles.title}>
@ -117,21 +173,9 @@ const HovercardHeader = ({ gridObject, object, type, ...props }: Props) => {
</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>
{proficiency()}
<UncapIndicator
className="hovercard"
type={type}
ulb={object.uncap.ulb || false}
flb={object.uncap.flb || false}

View file

@ -4,7 +4,6 @@
.version {
display: flex;
flex-direction: column;
gap: $unit-2x;
&.content {
.header h3 {
@ -12,6 +11,14 @@
}
}
.bugs {
display: flex;
flex-direction: column;
list-style-type: disc;
gap: $unit-half;
padding-left: $unit-2x;
}
.contents {
display: flex;
flex-direction: column;
@ -121,14 +128,6 @@
margin: 0;
}
}
.Bugs {
display: flex;
flex-direction: column;
list-style-type: disc;
gap: $unit-half;
padding-left: $unit-2x;
}
}
}
}

View file

@ -52,6 +52,9 @@ const UpdatesPage = () => {
'202302U2': {
updates: 1,
},
'1.2.1': {
bugs: 5,
},
}
function image(
@ -75,6 +78,20 @@ const UpdatesPage = () => {
return (
<div className={classes}>
<h1>{common('about.segmented_control.updates')}</h1>
<section className={styles.version} data-version="1.2.1">
<div className={styles.header}>
<h3>1.2.1</h3>
<time>2023/09/01</time>
</div>
<h2>Bug fixes</h2>
<ul className={styles.bugs}>
{[...Array(versionUpdates['1.2.1'].bugs)].map((e, i) => (
<li key={`1.2.1-bugfix-${i}`}>
{updates(`versions.1.2.1.bugs.${i}`)}
</li>
))}
</ul>
</section>
<ContentUpdate
version="2023-08L"
dateString="2023/08/31"

View file

@ -75,7 +75,8 @@ const CharacterHovercard = (props: Props) => {
{[...Array(4)].map((e, i) => {
const ringIndex = i + 1
const ringStat: ExtendedMastery =
props.gridCharacter.over_mastery[i]
props.gridCharacter.over_mastery[ringIndex]
if (ringStat && ringStat.modifier && ringStat.modifier > 0) {
if (ringIndex === 1 || ringIndex === 2) {
return masteryElement(overMastery.a, ringStat)

View file

@ -73,7 +73,9 @@ const CharacterModal = ({
const [earring, setEarring] = useState<ExtendedMastery>(emptyExtendedMastery)
const [awakening, setAwakening] = useState<Awakening>()
const [awakeningLevel, setAwakeningLevel] = useState(1)
const [transcendenceStep, setTranscendenceStep] = useState(0)
const [transcendenceStep, setTranscendenceStep] = useState(
gridCharacter.transcendence_step
)
// Refs
const headerRef = React.createRef<HTMLDivElement>()

View file

@ -29,7 +29,7 @@
flex-direction: column;
gap: $unit-2x;
min-width: 20vw;
max-width: 32vw;
max-width: 40vw;
padding: $unit * 4;
@include breakpoint(tablet) {

View file

@ -1,3 +1,93 @@
.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);
outline: none;
padding: $unit;
transform-origin: var(--radix-popover-content-transform-origin);
width: var(--radix-popover-trigger-width);
z-index: 5;
@include breakpoint(phone) {
min-width: auto;
}
&.raid {
width: auto;
}
&.flush {
padding: 0;
}
}
.jobAccessory {
padding: $unit-2x;
min-width: 40vw;
max-width: 40vw;
max-height: 40vh;
overflow-y: auto;
overflow-x: none;
margin-left: $unit-2x;
h3 {
font-size: $font-regular;
font-weight: $medium;
margin: 0 0 $unit $unit;
}
&.readOnly {
min-width: inherit;
max-width: inherit;
}
@include breakpoint(tablet) {
width: initial;
max-width: initial;
}
@include breakpoint(phone) {
width: initial;
max-width: initial;
}
}
.transcendence {
display: flex;
flex-direction: column;
gap: $unit-half;
align-items: center;
justify-content: center;
width: $unit-10x;
height: $unit-10x;
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);
outline: none;
padding: $unit;
transform-origin: var(--radix-popover-content-transform-origin);
z-index: 32;
&.open {
opacity: 1;
display: flex;
}
h4 {
font-size: $font-small;
font-weight: $medium;
}
.pending {
color: $yellow;
}
}
.arrow {
fill: var(--dialog-bg);
filter: drop-shadow(0px 1px 1px rgb(0 0 0 / 0.18));

View file

@ -20,9 +20,13 @@ export const PopoverContent = React.forwardRef<HTMLDivElement, Props>(
{ children, ...props }: PropsWithChildren<Props>,
forwardedRef
) {
const classes = classnames(props.className, {
Popover: true,
})
const classes = classnames(
{
[styles.popover]: true,
},
props.className?.split(' ').map((a) => styles[a])
)
return (
<PopoverPrimitive.Portal>

View file

@ -1,4 +1,4 @@
.JobAccessoryItem {
.item {
background: none;
border-radius: $input-corner;
border: none;

View file

@ -18,7 +18,7 @@ const JobAccessoryItem = ({ accessory, selected }: Props) => {
return (
<RadioGroup.Item
className="JobAccessoryItem"
className={styles.item}
data-state={selected ? 'checked' : 'unchecked'}
value={accessory.id}
>

View file

@ -1,67 +1,37 @@
.JobAccessory.Popover {
padding: $unit-2x;
min-width: 40vw;
max-width: 40vw;
max-height: 40vh;
overflow-y: scroll;
margin-left: $unit-2x;
h3 {
font-size: $font-regular;
font-weight: $medium;
margin: 0 0 $unit $unit;
}
&.ReadOnly {
min-width: inherit;
max-width: inherit;
}
.accessories {
display: grid;
gap: $unit;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
@include breakpoint(tablet) {
width: initial;
max-width: initial;
grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
gap: 0;
}
}
.equippedAccessory {
display: flex;
flex-direction: column;
gap: $unit-2x;
h3 {
margin: 0;
}
@include breakpoint(phone) {
width: initial;
max-width: initial;
}
.Accessories {
display: grid;
gap: $unit;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
@include breakpoint(tablet) {
grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
gap: 0;
}
}
.EquippedAccessory {
.accessory {
display: flex;
flex-direction: column;
gap: $unit-2x;
gap: $unit;
h3 {
margin: 0;
h4 {
font-size: $font-small;
font-weight: $medium;
text-align: center;
}
.Accessory {
display: flex;
flex-direction: column;
gap: $unit;
h4 {
font-size: $font-small;
font-weight: $medium;
text-align: center;
}
img {
border-radius: $item-corner;
width: 150px;
}
img {
border-radius: $item-corner;
width: 150px;
}
}
}

View file

@ -49,8 +49,8 @@ const JobAccessoryPopover = ({
const [open, setOpen] = useState(false)
const classes = classNames({
JobAccessory: true,
ReadOnly: !editable,
jobAccessory: true,
readOnly: !editable,
})
// Hooks
@ -91,7 +91,7 @@ const JobAccessoryPopover = ({
)}
</h3>
<RadioGroup.Root
className="Accessories"
className={styles.accessories}
onValueChange={handleAccessorySelected}
>
{accessories.map((accessory) => (
@ -110,14 +110,14 @@ const JobAccessoryPopover = ({
)
const readOnly = currentAccessory ? (
<div className="EquippedAccessory">
<div className={styles.equippedAccessory}>
<h3>
{t('equipped')}{' '}
{job.accessory_type === 1
? `${t('accessories.paladin')}s`
: t('accessories.manadiver')}
</h3>
<div className="Accessory">
<div className={styles.accessory}>
<img
alt={currentAccessory.name[locale]}
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/accessory-grid/${currentAccessory.granblue_id}.jpg`}

View file

@ -93,6 +93,7 @@ const JobImage = ({
editable={editable}
open={open}
job={job}
key={`accessory-${open}`}
onAccessorySelected={onAccessorySelected}
onOpenChange={handlePopoverOpenChanged}
>

View file

@ -70,8 +70,12 @@ const AXSelect = (props: Props) => {
// States
const [primaryAxModifier, setPrimaryAxModifier] = useState(-1)
const [secondaryAxModifier, setSecondaryAxModifier] = useState(-1)
const [primaryAxValue, setPrimaryAxValue] = useState(0.0)
const [secondaryAxValue, setSecondaryAxValue] = useState(0.0)
const [primaryAxValue, setPrimaryAxValue] = useState(
props.currentSkills ? props.currentSkills[0].strength : 0.0
)
const [secondaryAxValue, setSecondaryAxValue] = useState(
props.currentSkills ? props.currentSkills[1].strength : 0.0
)
useEffect(() => {
setupAx1()
@ -146,7 +150,10 @@ const AXSelect = (props: Props) => {
// Classes
const secondarySetClasses = classNames({
[styles.set]: true,
[styles.hidden]: primaryAxModifier < 0,
[styles.hidden]:
primaryAxModifier < 0 ||
primaryAxModifier === 18 ||
primaryAxModifier === 19,
})
function setupAx1() {
@ -270,9 +277,12 @@ const AXSelect = (props: Props) => {
secondaryAxModifierSelect.current &&
secondaryAxValueInput.current
) {
setupInput(ax[props.axType - 1][value], primaryAxValueInput.current)
setupInput(
ax[props.axType - 1].find((ax) => ax.id === value),
primaryAxValueInput.current
)
setPrimaryAxValue(0)
primaryAxValueInput.current.value = ''
// Reset the secondary AX modifier, reset the AX value and hide the input
setSecondaryAxModifier(-1)
@ -302,7 +312,7 @@ const AXSelect = (props: Props) => {
const value = parseFloat(event.target.value)
let newErrors = { ...errors }
if (primaryAxValueInput.current == event.target) {
if (primaryAxValueInput.current === event.target) {
if (handlePrimaryErrors(value)) setPrimaryAxValue(value)
} else {
if (handleSecondaryErrors(value)) setSecondaryAxValue(value)
@ -310,16 +320,18 @@ const AXSelect = (props: Props) => {
}
function handlePrimaryErrors(value: number) {
const primaryAxSkill = ax[props.axType - 1][primaryAxModifier]
const primaryAxSkill = ax[props.axType - 1].find(
(ax) => ax.id === primaryAxModifier
)
let newErrors = { ...errors }
if (value < primaryAxSkill.minValue) {
if (primaryAxSkill && value < primaryAxSkill.minValue) {
newErrors.axValue1 = t('ax.errors.value_too_low', {
name: primaryAxSkill.name[locale],
minValue: primaryAxSkill.minValue,
suffix: primaryAxSkill.suffix ? primaryAxSkill.suffix : '',
})
} else if (value > primaryAxSkill.maxValue) {
} else if (primaryAxSkill && value > primaryAxSkill.maxValue) {
newErrors.axValue1 = t('ax.errors.value_too_high', {
name: primaryAxSkill.name[locale],
maxValue: primaryAxSkill.maxValue,
@ -327,7 +339,7 @@ const AXSelect = (props: Props) => {
})
} else if (!value || value <= 0) {
newErrors.axValue1 = t('ax.errors.value_empty', {
name: primaryAxSkill.name[locale],
name: primaryAxSkill?.name[locale],
})
} else {
newErrors.axValue1 = ''
@ -380,6 +392,7 @@ const AXSelect = (props: Props) => {
}
function setupInput(ax: ItemSkill | undefined, element: HTMLInputElement) {
console.log(ax)
if (ax) {
const rangeString = `${ax.minValue}~${ax.maxValue}${ax.suffix || ''}`
@ -431,11 +444,7 @@ const AXSelect = (props: Props) => {
hidden: primaryAxModifier < 0,
})}
bound={true}
value={
props.currentSkills && props.currentSkills[0]
? props.currentSkills[0].strength
: 0
}
value={primaryAxValue}
type="number"
onChange={handleInputChange}
ref={primaryAxValueInput}
@ -469,11 +478,7 @@ const AXSelect = (props: Props) => {
hidden: secondaryAxModifier < 0,
})}
bound={true}
value={
props.currentSkills && props.currentSkills[1]
? props.currentSkills[1].strength
: 0
}
value={secondaryAxValue}
type="number"
onChange={handleInputChange}
ref={secondaryAxValueInput}

View file

@ -1,37 +1,3 @@
.transcendence {
display: flex;
flex-direction: column;
gap: $unit-half;
align-items: center;
justify-content: center;
width: $unit-10x;
height: $unit-10x;
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);
outline: none;
padding: $unit;
transform-origin: var(--radix-popover-content-transform-origin);
z-index: 32;
&.open {
opacity: 1;
display: flex;
}
h4 {
font-size: $font-small;
font-weight: $medium;
}
.pending {
color: $yellow;
}
}
@keyframes scaleIn {
0% {
opacity: 0;

View file

@ -17,6 +17,7 @@ interface Props
HTMLDivElement
> {
type: 'character' | 'summon'
starRef: React.RefObject<HTMLDivElement>
open: boolean
stage: number
onOpenChange?: (open: boolean) => void
@ -24,8 +25,9 @@ interface Props
}
const TranscendencePopover = ({
children,
open: popoverOpen,
starRef,
children,
type,
stage,
tabIndex,
@ -45,8 +47,8 @@ const TranscendencePopover = ({
})
useEffect(() => {
if (open) popoverRef.current?.focus()
}, [])
setOpen(popoverOpen)
}, [popoverOpen])
useEffect(() => {
if (stage) setCurrentStage(stage)
@ -57,10 +59,6 @@ const TranscendencePopover = ({
else if (type === 'summon') setBaseLevel(200)
}, [type])
useEffect(() => {
setOpen(popoverOpen)
}, [popoverOpen])
function handleFragmentClicked(newStage: number) {
setCurrentStage(newStage)
if (sendValue) sendValue(newStage)
@ -70,13 +68,33 @@ const TranscendencePopover = ({
setCurrentStage(newStage)
}
function closePopover() {
setOpen(false)
if (onOpenChange) onOpenChange(false)
}
function handlePointerDownOutside(
event: CustomEvent<{ originalEvent: PointerEvent }>
) {
const target = event.detail.originalEvent.target as Element
if (
target &&
starRef.current &&
target.closest('.TranscendenceStar') !== starRef.current
) {
closePopover()
}
}
return (
<Popover open={open} onOpenChange={onOpenChange}>
<Popover open={open}>
<PopoverAnchor>{children}</PopoverAnchor>
<PopoverContent
className={styles.transcendence}
className="transcendence"
ref={popoverRef}
tabIndex={tabIndex}
onEscapeKeyDown={closePopover}
onPointerDownOutside={handlePointerDownOutside}
>
<TranscendenceStar
className="interactive base"

View file

@ -20,97 +20,102 @@ interface Props
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({
[styles.star]: true,
[styles.immutable]: immutable,
[styles.empty]: stage === 0,
[styles.stage1]: stage === 1,
[styles.stage2]: stage === 2,
[styles.stage3]: stage === 3,
[styles.stage4]: stage === 4,
[styles.stage5]: stage === 5,
})
const baseImageClasses = classnames(
const TranscendenceStar = React.forwardRef<HTMLDivElement, Props>(
function TranscendenceStar(
{
[styles.figure]: true,
},
className?.split(' ').map((c) => styles[c])
)
className,
interactive,
stage,
editable,
tabIndex,
onStarClick,
onFragmentClick,
onFragmentHover,
}: Props,
forwardedRef
) {
const [visibleStage, setVisibleStage] = useState(0)
const [currentStage, setCurrentStage] = useState(0)
const [immutable, setImmutable] = useState(false)
useEffect(() => {
setVisibleStage(stage)
setCurrentStage(stage)
}, [stage])
// Classes
const starClasses = classnames({
TranscendenceStar: true,
[styles.star]: true,
[styles.immutable]: immutable,
[styles.empty]: stage === 0,
[styles.stage1]: stage === 1,
[styles.stage2]: stage === 2,
[styles.stage3]: stage === 3,
[styles.stage4]: stage === 4,
[styles.stage5]: stage === 5,
})
function handleClick() {
if (onStarClick) {
onStarClick()
const baseImageClasses = classnames(
{
[styles.figure]: true,
},
className?.split(' ').map((c) => styles[c])
)
useEffect(() => {
setVisibleStage(stage)
setCurrentStage(stage)
}, [stage])
function handleClick() {
if (onStarClick) onStarClick()
}
}
function handleFragmentClick(index: number) {
let newStage = index
if (index === currentStage) newStage = 0
function handleFragmentClick(index: number) {
let newStage = index
if (index === currentStage) newStage = 0
setVisibleStage(newStage)
setCurrentStage(newStage)
if (onFragmentClick) onFragmentClick(newStage)
}
setVisibleStage(newStage)
setCurrentStage(newStage)
if (onFragmentClick) onFragmentClick(newStage)
}
function handleFragmentHover(index: number) {
setVisibleStage(index)
if (onFragmentHover) onFragmentHover(index)
}
function handleFragmentHover(index: number) {
setVisibleStage(index)
if (onFragmentHover) onFragmentHover(index)
}
function handleMouseLeave() {
setVisibleStage(currentStage)
if (onFragmentHover) onFragmentHover(currentStage)
}
function handleMouseLeave() {
setVisibleStage(currentStage)
if (onFragmentHover) onFragmentHover(currentStage)
}
return (
<div
className={starClasses}
onClick={editable ? handleClick : () => {}}
onMouseLeave={interactive ? handleMouseLeave : () => {}}
tabIndex={tabIndex}
>
<div className={styles.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}
/>
) : (
''
)
})}
return (
<div
className={starClasses}
onClick={editable ? handleClick : () => {}}
onMouseLeave={interactive ? handleMouseLeave : () => {}}
ref={forwardedRef}
tabIndex={tabIndex}
>
<div className={styles.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>
<i className={baseImageClasses} />
</div>
)
}
)
}
)
TranscendenceStar.defaultProps = {
stage: 0,

View file

@ -1,6 +1,11 @@
.wrapper {
display: flex;
&.hovercard {
min-width: 100px;
justify-content: center;
}
.indicator {
display: flex;
flex-direction: row;

View file

@ -4,8 +4,9 @@ import TranscendencePopover from '~components/uncap/TranscendencePopover'
import TranscendenceStar from '~components/uncap/TranscendenceStar'
import styles from './index.module.scss'
import classNames from 'classnames'
interface Props {
interface Props extends React.ComponentProps<'div'> {
type: 'character' | 'weapon' | 'summon'
rarity?: number
uncapLevel?: number
@ -25,6 +26,15 @@ const UncapIndicator = (props: Props) => {
const [popoverOpen, setPopoverOpen] = useState(false)
const transcendenceStarRef = React.createRef<HTMLDivElement>()
const classes = classNames(
{
[styles.wrapper]: true,
},
props.className?.split(' ').map((className) => styles[className])
)
function setNumStars() {
let numStars
@ -74,7 +84,11 @@ const UncapIndicator = (props: Props) => {
function sendTranscendenceStage(stage: number) {
if (props.updateTranscendence) props.updateTranscendence(stage)
togglePopover(false)
setPopoverOpen(false)
}
function handleStarClicked() {
if (props.editable) togglePopover(!popoverOpen)
}
const transcendence = (i: number) => {
@ -82,25 +96,27 @@ const UncapIndicator = (props: Props) => {
return props.type === 'character' || props.type === 'summon' ? (
<TranscendencePopover
open={popoverOpen}
stage={props.transcendenceStage ? props.transcendenceStage : 0}
stage={props.transcendenceStage || 0}
type={props.type}
onOpenChange={togglePopover}
sendValue={sendTranscendenceStage}
key={`star_${i}`}
key={`popover_${i}_${popoverOpen}`}
starRef={transcendenceStarRef}
tabIndex={tabIndex}
>
<TranscendenceStar
key={`star_${i}`}
stage={props.transcendenceStage}
stage={props.transcendenceStage || 0}
editable={props.editable}
interactive={false}
onStarClick={() => togglePopover(true)}
ref={transcendenceStarRef}
onStarClick={handleStarClicked}
/>
</TranscendencePopover>
) : (
<TranscendenceStar
key={`star_${i}`}
stage={props.transcendenceStage}
stage={props.transcendenceStage || 0}
editable={props.editable}
interactive={false}
tabIndex={tabIndex}
@ -150,7 +166,7 @@ const UncapIndicator = (props: Props) => {
}
return (
<div className={styles.wrapper}>
<div className={classes}>
<ul className={styles.indicator}>
{Array.from(Array(numStars)).map((x, i) => {
if (props.type === 'character' && i > 4) {

View file

@ -229,8 +229,8 @@ const WeaponHovercard = (props: Props) => {
/>
{props.gridWeapon.object.ax &&
props.gridWeapon.ax &&
props.gridWeapon.ax[0].modifier !== undefined &&
props.gridWeapon.ax[0].strength !== undefined &&
props.gridWeapon.ax[0].modifier !== null &&
props.gridWeapon.ax[0].strength !== null &&
axSection}
{props.gridWeapon.awakening && awakeningSection}
{props.gridWeapon.weapon_keys &&

View file

@ -4,6 +4,12 @@
height: 25px;
width: 60px;
&.small {
background-size: 50px 20px;
height: 20px;
width: 50px;
}
/* Elements */
&.fire.en {

View file

@ -6,6 +6,7 @@ import styles from './index.module.scss'
interface Props {
labelType: string
size: 'small' | 'normal'
}
const WeaponLabelIcon = (props: Props) => {
@ -13,6 +14,7 @@ const WeaponLabelIcon = (props: Props) => {
const classes = classNames({
[styles.icon]: true,
[styles.small]: props.size === 'small',
[styles[props.labelType]]: true,
[styles.en]: router.locale === 'en',
[styles.ja]: router.locale === 'ja',
@ -21,4 +23,8 @@ const WeaponLabelIcon = (props: Props) => {
return <i className={classes} />
}
WeaponLabelIcon.defaultProps = {
size: 'normal',
}
export default WeaponLabelIcon

View file

@ -448,7 +448,8 @@ const WeaponUnit = ({
gridWeapon.ax &&
gridWeapon.ax.length > 0
) {
for (let i = 0; i < gridWeapon.ax.length; i++) {
const numSkills = gridWeapon.ax[1].modifier ? 2 : 1
for (let i = 0; i < numSkills; i++) {
const image = axImage(i)
if (image) images.push(image)
}

View file

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View file

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -19,6 +19,16 @@
"uncap": "Uncap"
},
"versions": {
"1.2.1": {
"bugs": [
"Job accessory popover has been fixed, so Paladin shields and Manadiver manatura can be selected again",
"The AX skill section no longer shows up in the weapon hovercard if no AX skills are set",
"The top of the character hovercard has been slightly refined",
"Fixed a bug that prevented all character over mastery (ring) bonuses from being displayed",
"Fixed a bug that reset a character's transcendence level if their mastery values are set",
"Fixed a bug that prevented setting the value for Rupee Gain or EXP Gain AX skills on weapons"
]
},
"1.2.0": {
"notes": "I'm very bad at writing actual release notes, so this is a consolidation of the bigger features released in the last six months. Don't worry: there's some new stuff here too!\nThe next features (in no particular order) will be: a way to define roles and substitutions for characters, a collection tracker and hopefully making progress on guides. That's a lot, but I'll chip away at it bit by bit.\nAs always, if you have any feedback, feel free to reach out in the granblue-tools Discord. Thanks for using granblue.team!",
"features": [

View file

@ -19,6 +19,16 @@
"uncap": "上限解放"
},
"versions": {
"1.2.1": {
"bugs": [
"ジョブアクセサリーのメニューが修正され、パラディンの盾やマナダイバーのマナベリーが再び選択できるようになりました",
"EXスキルが設定されていない場合、武器ホバーカードにEXスキルの項目が表示されなくなりました",
"キャラクターホバーカードのトップが少し洗練されました",
"キャラクターホバーカードにリミットボーナスを全部表示しないバグを修正しました",
"キャラクターのボーナス値が設定されている場合、キャラクターの超越レベルがリセットされるバグを修正しました",
"武器にルピーUPまたはEXP UPのEXスキルの値を設定できないバグを修正しました"
]
},
"1.2.0": {
"notes": "I'm very bad at writing actual release notes, so this is a consolidation of the bigger features released in the last six months. Don't worry: there's some new stuff here too!\nThe next features (in no particular order) will be: a way to define roles and substitutions for characters, a collection tracker and hopefully making progress on guides. That's a lot, but I'll chip away at it bit by bit.\nAs always, if you have any feedback, feel free to reach out in the granblue-tools Discord. Thanks for using granblue.team!",
"features": [