TranscendencePopover closes properly

This commit is contained in:
Justin Edmund 2023-09-01 15:56:47 -07:00
parent 46467a354f
commit e5a1fb7b1e
3 changed files with 127 additions and 96 deletions

View file

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

View file

@ -20,97 +20,102 @@ interface Props
const NUM_FRAGMENTS = 5 const NUM_FRAGMENTS = 5
const TranscendenceStar = ({ const TranscendenceStar = React.forwardRef<HTMLDivElement, Props>(
className, function TranscendenceStar(
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(
{ {
[styles.figure]: true, className,
}, interactive,
className?.split(' ').map((c) => styles[c]) stage,
) editable,
tabIndex,
onStarClick,
onFragmentClick,
onFragmentHover,
}: Props,
forwardedRef
) {
const [visibleStage, setVisibleStage] = useState(0)
const [currentStage, setCurrentStage] = useState(0)
const [immutable, setImmutable] = useState(false)
useEffect(() => { // Classes
setVisibleStage(stage) const starClasses = classnames({
setCurrentStage(stage) TranscendenceStar: true,
}, [stage]) [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() { const baseImageClasses = classnames(
if (onStarClick) { {
onStarClick() [styles.figure]: true,
},
className?.split(' ').map((c) => styles[c])
)
useEffect(() => {
setVisibleStage(stage)
setCurrentStage(stage)
}, [stage])
function handleClick() {
if (onStarClick) onStarClick()
} }
}
function handleFragmentClick(index: number) { function handleFragmentClick(index: number) {
let newStage = index let newStage = index
if (index === currentStage) newStage = 0 if (index === currentStage) newStage = 0
setVisibleStage(newStage) setVisibleStage(newStage)
setCurrentStage(newStage) setCurrentStage(newStage)
if (onFragmentClick) onFragmentClick(newStage) if (onFragmentClick) onFragmentClick(newStage)
} }
function handleFragmentHover(index: number) { function handleFragmentHover(index: number) {
setVisibleStage(index) setVisibleStage(index)
if (onFragmentHover) onFragmentHover(index) if (onFragmentHover) onFragmentHover(index)
} }
function handleMouseLeave() { function handleMouseLeave() {
setVisibleStage(currentStage) setVisibleStage(currentStage)
if (onFragmentHover) onFragmentHover(currentStage) if (onFragmentHover) onFragmentHover(currentStage)
} }
return ( return (
<div <div
className={starClasses} className={starClasses}
onClick={editable ? handleClick : () => {}} onClick={editable ? handleClick : () => {}}
onMouseLeave={interactive ? handleMouseLeave : () => {}} onMouseLeave={interactive ? handleMouseLeave : () => {}}
tabIndex={tabIndex} ref={forwardedRef}
> tabIndex={tabIndex}
<div className={styles.fragments}> >
{[...Array(NUM_FRAGMENTS)].map((e, i) => { <div className={styles.fragments}>
const loopStage = i + 1 {[...Array(NUM_FRAGMENTS)].map((e, i) => {
return interactive ? ( const loopStage = i + 1
<TranscendenceFragment return interactive ? (
key={`fragment_${loopStage}`} <TranscendenceFragment
stage={loopStage} key={`fragment_${loopStage}`}
visible={loopStage <= visibleStage} stage={loopStage}
interactive={interactive} visible={loopStage <= visibleStage}
onClick={handleFragmentClick} interactive={interactive}
onHover={handleFragmentHover} onClick={handleFragmentClick}
/> onHover={handleFragmentHover}
) : ( />
'' ) : (
) ''
})} )
})}
</div>
<i className={baseImageClasses} />
</div> </div>
<i className={baseImageClasses} /> )
</div> }
) )
}
TranscendenceStar.defaultProps = { TranscendenceStar.defaultProps = {
stage: 0, stage: 0,

View file

@ -26,6 +26,8 @@ const UncapIndicator = (props: Props) => {
const [popoverOpen, setPopoverOpen] = useState(false) const [popoverOpen, setPopoverOpen] = useState(false)
const transcendenceStarRef = React.createRef<HTMLDivElement>()
const classes = classNames( const classes = classNames(
{ {
[styles.wrapper]: true, [styles.wrapper]: true,
@ -82,7 +84,11 @@ const UncapIndicator = (props: Props) => {
function sendTranscendenceStage(stage: number) { function sendTranscendenceStage(stage: number) {
if (props.updateTranscendence) props.updateTranscendence(stage) if (props.updateTranscendence) props.updateTranscendence(stage)
togglePopover(false) setPopoverOpen(false)
}
function handleStarClicked() {
if (props.editable) togglePopover(!popoverOpen)
} }
const transcendence = (i: number) => { const transcendence = (i: number) => {
@ -90,25 +96,27 @@ const UncapIndicator = (props: Props) => {
return props.type === 'character' || props.type === 'summon' ? ( return props.type === 'character' || props.type === 'summon' ? (
<TranscendencePopover <TranscendencePopover
open={popoverOpen} open={popoverOpen}
stage={props.transcendenceStage ? props.transcendenceStage : 0} stage={props.transcendenceStage || 0}
type={props.type} type={props.type}
onOpenChange={togglePopover} onOpenChange={togglePopover}
sendValue={sendTranscendenceStage} sendValue={sendTranscendenceStage}
key={`star_${i}`} key={`popover_${i}_${popoverOpen}`}
starRef={transcendenceStarRef}
tabIndex={tabIndex} tabIndex={tabIndex}
> >
<TranscendenceStar <TranscendenceStar
key={`star_${i}`} key={`star_${i}`}
stage={props.transcendenceStage} stage={props.transcendenceStage || 0}
editable={props.editable} editable={props.editable}
interactive={false} interactive={false}
onStarClick={() => togglePopover(true)} ref={transcendenceStarRef}
onStarClick={handleStarClicked}
/> />
</TranscendencePopover> </TranscendencePopover>
) : ( ) : (
<TranscendenceStar <TranscendenceStar
key={`star_${i}`} key={`star_${i}`}
stage={props.transcendenceStage} stage={props.transcendenceStage || 0}
editable={props.editable} editable={props.editable}
interactive={false} interactive={false}
tabIndex={tabIndex} tabIndex={tabIndex}