1 line
8.7 KiB
Plaintext
1 line
8.7 KiB
Plaintext
{"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 exitingComponents.current.add(key)\n\n if (exitComplete.has(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;AACU;;AAcT;;;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;;;;;AAMN;AAEA;AACI;;;;;;AAMJ;AACI;;AACJ;;;AAII;AAEA;;;AAIR;AAEA;AAMgB;;;AAiBhC;;"} |