All files / src/components/AnimatePresence PresenceChild.tsx

100% Statements 38/38
80% Branches 12/15
100% Functions 10/10
100% Lines 31/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 8731x 31x 31x         31x                     31x   70x 70x 70x     31x 241x 240x 240x 240x 240x 240x   240x 240x   240x 240x           23x   27x 27x     20x     33x 33x                     240x 128x             240x 64x     240x               70x    
import * as React from "react"
import { useMemo } from "react"
import {
    PresenceContext,
    PresenceContextProps,
} from "../../context/PresenceContext"
import { VariantLabels } from "../../motion/types"
import { useConstant } from "../../utils/use-constant"
 
interface PresenceChildProps {
    children: React.ReactElement<any>
    isPresent: boolean
    onExitComplete?: () => void
    initial?: false | VariantLabels
    custom?: any
    presenceAffectsLayout: boolean
}
 
let presenceId = 0
function getPresenceId() {
    const id = presenceId
    presenceId++
    return id
}
 
export const PresenceChild = ({
    children,
    initial,
    isPresent,
    onExitComplete,
    custom,
    presenceAffectsLayout,
}: PresenceChildProps) => {
    const presenceChildren = useConstant(newChildrenMap)
    const id = useConstant(getPresenceId)
 
    const context = useMemo(
        (): PresenceContextProps => ({
            id,
            initial,
            isPresent,
            custom,
            onExitComplete: (childId: number) => {
                presenceChildren.set(childId, true)
 
                for (const isComplete of presenceChildren.values()) {
                    if (!isComplete) return // can stop searching when any is incomplete
                }
 
                onExitComplete?.()
            },
            register: (childId: number) => {
                presenceChildren.set(childId, false)
                return () => presenceChildren.delete(childId)
            },
        }),
        /**
         * If the presence of a child affects the layout of the components around it,
         * we want to make a new context value to ensure they get re-rendered
         * so they can detect that layout change.
         */
        presenceAffectsLayout ? undefined : [isPresent]
    )
 
    useMemo(() => {
        presenceChildren.forEach((_, key) => presenceChildren.set(key, false))
    }, [isPresent])
 
    /**
     * If there's no `motion` components to fire exit animations, we want to remove this
     * component immediately.
     */
    React.useEffect(() => {
        !isPresent && !presenceChildren.size && onExitComplete?.()
    }, [isPresent])
 
    return (
        <PresenceContext.Provider value={context}>
            {children}
        </PresenceContext.Provider>
    )
}
 
function newChildrenMap(): Map<number, boolean> {
    return new Map()
}