{"version":3,"file":"index.mjs","sources":["../../../../src/components/AnimatePresence/index.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { useContext, useMemo, useRef, useState } from \"react\"\nimport { LayoutGroupContext } from \"../../context/LayoutGroupContext\"\nimport { useConstant } from \"../../utils/use-constant\"\nimport { useIsomorphicLayoutEffect } from \"../../utils/use-isomorphic-effect\"\nimport { PresenceChild } from \"./PresenceChild\"\nimport { AnimatePresenceProps } from \"./types\"\nimport { usePresence } from \"./use-presence\"\nimport { ComponentKey, getChildKey, onlyElements } from \"./utils\"\n\n/**\n * `AnimatePresence` enables the animation of components that have been removed from the tree.\n *\n * When adding/removing more than a single child, every child **must** be given a unique `key` prop.\n *\n * Any `motion` components that have an `exit` property defined will animate out when removed from\n * the tree.\n *\n * ```jsx\n * import { motion, AnimatePresence } from 'framer-motion'\n *\n * export const Items = ({ items }) => (\n *   <AnimatePresence>\n *     {items.map(item => (\n *       <motion.div\n *         key={item.id}\n *         initial={{ opacity: 0 }}\n *         animate={{ opacity: 1 }}\n *         exit={{ opacity: 0 }}\n *       />\n *     ))}\n *   </AnimatePresence>\n * )\n * ```\n *\n * You can sequence exit animations throughout a tree using variants.\n *\n * If a child contains multiple `motion` components with `exit` props, it will only unmount the child\n * once all `motion` components have finished animating out. Likewise, any components using\n * `usePresence` all need to call `safeToRemove`.\n *\n * @public\n */\nexport const AnimatePresence = ({\n    children,\n    custom,\n    initial = true,\n    onExitComplete,\n    presenceAffectsLayout = true,\n    mode = \"sync\",\n    propagate = false,\n    anchorX = \"left\",\n    anchorY = \"top\",\n    root\n}: React.PropsWithChildren<AnimatePresenceProps>) => {\n    const [isParentPresent, safeToRemove] = usePresence(propagate)\n\n    /**\n     * Filter any children that aren't ReactElements. We can only track components\n     * between renders with a props.key.\n     */\n    const presentChildren = useMemo(() => onlyElements(children), [children])\n\n    /**\n     * Track the keys of the currently rendered children. This is used to\n     * determine which children are exiting.\n     */\n    const presentKeys =\n        propagate && !isParentPresent ? [] : presentChildren.map(getChildKey)\n\n    /**\n     * If `initial={false}` we only want to pass this to components in the first render.\n     */\n    const isInitialRender = useRef(true)\n\n    /**\n     * A ref containing the currently present children. When all exit animations\n     * are complete, we use this to re-render the component with the latest children\n     * *committed* rather than the latest children *rendered*.\n     */\n    const pendingPresentChildren = useRef(presentChildren)\n\n    /**\n     * Track which exiting children have finished animating out.\n     */\n    const exitComplete = useConstant(() => new Map<ComponentKey, boolean>())\n\n    /**\n     * Track which components are currently processing exit to prevent duplicate processing.\n     */\n    const exitingComponents = useRef(new Set<ComponentKey>())\n\n    /**\n     * Save children to render as React state. To ensure this component is concurrent-safe,\n     * we check for exiting children via an effect.\n     */\n    const [diffedChildren, setDiffedChildren] = useState(presentChildren)\n    const [renderedChildren, setRenderedChildren] = useState(presentChildren)\n\n    useIsomorphicLayoutEffect(() => {\n        isInitialRender.current = false\n        pendingPresentChildren.current = presentChildren\n\n        /**\n         * Update complete status of exiting children.\n         */\n        for (let i = 0; i < renderedChildren.length; i++) {\n            const key = getChildKey(renderedChildren[i])\n\n            if (!presentKeys.includes(key)) {\n                if (exitComplete.get(key) !== true) {\n                    exitComplete.set(key, false)\n                }\n            } else {\n                exitComplete.delete(key)\n                exitingComponents.current.delete(key)\n            }\n        }\n    }, [renderedChildren, presentKeys.length, presentKeys.join(\"-\")])\n\n    const exitingChildren: any[] = []\n\n    if (presentChildren !== diffedChildren) {\n        let nextChildren = [...presentChildren]\n\n        /**\n         * Loop through all the currently rendered components and decide which\n         * are exiting.\n         */\n        for (let i = 0; i < renderedChildren.length; i++) {\n            const child = renderedChildren[i]\n            const key = getChildKey(child)\n\n            if (!presentKeys.includes(key)) {\n                nextChildren.splice(i, 0, child)\n                exitingChildren.push(child)\n            }\n        }\n\n        /**\n         * If we're in \"wait\" mode, and we have exiting children, we want to\n         * only render these until they've all exited.\n         */\n        if (mode === \"wait\" && exitingChildren.length) {\n            nextChildren = exitingChildren\n        }\n\n        setRenderedChildren(onlyElements(nextChildren))\n        setDiffedChildren(presentChildren)\n\n        /**\n         * Early return to ensure once we've set state with the latest diffed\n         * children, we can immediately re-render.\n         */\n        return null\n    }\n\n    if (\n        process.env.NODE_ENV !== \"production\" &&\n        mode === \"wait\" &&\n        renderedChildren.length > 1\n    ) {\n        console.warn(\n            `You're attempting to animate multiple children within AnimatePresence, but its mode is set to \"wait\". This will lead to odd visual behaviour.`\n        )\n    }\n\n    /**\n     * If we've been provided a forceRender function by the LayoutGroupContext,\n     * we can use it to force a re-render amongst all surrounding components once\n     * all components have finished animating out.\n     */\n    const { forceRender } = useContext(LayoutGroupContext)\n\n    return (\n        <>\n            {renderedChildren.map((child) => {\n                const key = getChildKey(child)\n\n                const isPresent =\n                    propagate && !isParentPresent\n                        ? false\n                        : presentChildren === renderedChildren ||\n                          presentKeys.includes(key)\n\n                const onExit = () => {\n                    if (exitingComponents.current.has(key)) {\n                        return\n                    }\n\n                    if (exitComplete.has(key)) {\n                        exitingComponents.current.add(key)\n                        exitComplete.set(key, true)\n                    } else {\n                        return\n                    }\n\n                    let isEveryExitComplete = true\n                    exitComplete.forEach((isExitComplete) => {\n                        if (!isExitComplete) isEveryExitComplete = false\n                    })\n\n                    if (isEveryExitComplete) {\n                        forceRender?.()\n                        setRenderedChildren(pendingPresentChildren.current)\n\n                        propagate && safeToRemove?.()\n\n                        onExitComplete && onExitComplete()\n                    }\n                }\n\n                return (\n                    <PresenceChild\n                        key={key}\n                        isPresent={isPresent}\n                        initial={\n                            !isInitialRender.current || initial\n                                ? undefined\n                                : false\n                        }\n                        custom={custom}\n                        presenceAffectsLayout={presenceAffectsLayout}\n                        mode={mode}\n                        root={root}\n                        onExitComplete={isPresent ? undefined : onExit}\n                        anchorX={anchorX}\n                        anchorY={anchorY}\n                    >\n                        {child}\n                    </PresenceChild>\n                )\n            })}\n        </>\n    )\n}\n"],"names":[],"mappings":";;;;;;;;;;AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCG;AACI;;AAcH;;;AAGG;AACH;AAEA;;;AAGG;AACH;AAGA;;AAEG;AACH;AAEA;;;;AAIG;AACH;AAEA;;AAEG;;AAGH;;AAEG;;AAGH;;;AAGG;;;;AAKC;AACA;AAEA;;AAEG;AACH;;;;AAKY;;;;AAGJ;AACA;;;AAGZ;;AAIA;AACI;AAEA;;;AAGG;AACH;AACI;AACA;;;AAII;;;AAIR;;;AAGG;;;;AAKH;;AAGA;;;AAGG;AACH;;AAGJ;AAEI;AACA;AAEA;;AAKJ;;;;AAIG;;;AAMS;AAEA;AAEQ;;AAEE;;;;;AAON;AACI;AACA;;;;;;AAMJ;AACI;;AACJ;;;AAII;AAEA;;;AAIR;AAEA;AAMgB;;;AAiBhC;;"}