{"version":3,"file":"MeasureLayout.mjs","sources":["../../../../../src/motion/features/layout/MeasureLayout.tsx"],"sourcesContent":["\"use client\"\n\nimport { frame, microtask, globalProjectionState, type VisualElement } from \"motion-dom\"\nimport { Component, useContext } from \"react\"\nimport { usePresence } from \"../../../components/AnimatePresence/use-presence\"\nimport {\n    LayoutGroupContext,\n    LayoutGroupContextProps,\n} from \"../../../context/LayoutGroupContext\"\nimport { SwitchLayoutGroupContext } from \"../../../context/SwitchLayoutGroupContext\"\nimport { MotionProps } from \"../../types\"\n\ninterface MeasureContextProps {\n    layoutGroup: LayoutGroupContextProps\n    switchLayoutGroup?: SwitchLayoutGroupContext\n    isPresent: boolean\n    safeToRemove?: VoidFunction | null\n}\n\ntype MeasureProps = MotionProps &\n    MeasureContextProps & { visualElement: VisualElement }\n\n/**\n * Track whether we've taken any snapshots yet. If not,\n * we can safely skip notification of didUpdate.\n *\n * Difficult to capture in a test but to prevent flickering\n * we must set this to true either on update or unmount.\n * Running `next-env/layout-id` in Safari will show this behaviour if broken.\n */\nlet hasTakenAnySnapshot = false\n\nclass MeasureLayoutWithContext extends Component<MeasureProps> {\n    /**\n     * This only mounts projection nodes for components that\n     * need measuring, we might want to do it for all components\n     * in order to incorporate transforms\n     */\n    componentDidMount() {\n        const { visualElement, layoutGroup, switchLayoutGroup, layoutId } =\n            this.props\n        const { projection } = visualElement\n\n        if (projection) {\n            if (layoutGroup.group) layoutGroup.group.add(projection)\n\n            if (switchLayoutGroup && switchLayoutGroup.register && layoutId) {\n                switchLayoutGroup.register(projection)\n            }\n\n            if (hasTakenAnySnapshot) {\n                projection.root!.didUpdate()\n            }\n\n            projection.addEventListener(\"animationComplete\", () => {\n                this.safeToRemove()\n            })\n            projection.setOptions({\n                ...projection.options,\n                layoutDependency: this.props.layoutDependency,\n                onExitComplete: () => this.safeToRemove(),\n            })\n        }\n\n        globalProjectionState.hasEverUpdated = true\n    }\n\n    getSnapshotBeforeUpdate(prevProps: MeasureProps) {\n        const { layoutDependency, visualElement, drag, isPresent } = this.props\n        const { projection } = visualElement\n\n        if (!projection) return null\n\n        /**\n         * TODO: We use this data in relegate to determine whether to\n         * promote a previous element. There's no guarantee its presence data\n         * will have updated by this point - if a bug like this arises it will\n         * have to be that we markForRelegation and then find a new lead some other way,\n         * perhaps in didUpdate\n         */\n        projection.isPresent = isPresent\n\n        if (prevProps.layoutDependency !== layoutDependency) {\n            projection.setOptions({\n                ...projection.options,\n                layoutDependency,\n            })\n        }\n\n        hasTakenAnySnapshot = true\n\n        if (\n            drag ||\n            prevProps.layoutDependency !== layoutDependency ||\n            layoutDependency === undefined ||\n            prevProps.isPresent !== isPresent\n        ) {\n            projection.willUpdate()\n        } else {\n            this.safeToRemove()\n        }\n\n        if (prevProps.isPresent !== isPresent) {\n            if (isPresent) {\n                projection.promote()\n            } else if (!projection.relegate()) {\n                /**\n                 * If there's another stack member taking over from this one,\n                 * it's in charge of the exit animation and therefore should\n                 * be in charge of the safe to remove. Otherwise we call it here.\n                 */\n                frame.postRender(() => {\n                    const stack = projection.getStack()\n                    if (!stack || !stack.members.length) {\n                        this.safeToRemove()\n                    }\n                })\n            }\n        }\n\n        return null\n    }\n\n    componentDidUpdate() {\n        const { visualElement, layoutAnchor } = this.props\n        const { projection } = visualElement\n        if (projection) {\n            projection.options.layoutAnchor = layoutAnchor\n\n            projection.root!.didUpdate()\n\n            microtask.postRender(() => {\n                if (!projection.currentAnimation && projection.isLead()) {\n                    this.safeToRemove()\n                }\n            })\n        }\n    }\n\n    componentWillUnmount() {\n        const {\n            visualElement,\n            layoutGroup,\n            switchLayoutGroup: promoteContext,\n        } = this.props\n        const { projection } = visualElement\n\n        hasTakenAnySnapshot = true\n\n        if (projection) {\n            projection.scheduleCheckAfterUnmount()\n            if (layoutGroup && layoutGroup.group)\n                layoutGroup.group.remove(projection)\n            if (promoteContext && promoteContext.deregister)\n                promoteContext.deregister(projection)\n        }\n    }\n\n    safeToRemove() {\n        const { safeToRemove } = this.props\n        safeToRemove && safeToRemove()\n    }\n\n    render() {\n        return null\n    }\n}\n\nexport function MeasureLayout(\n    props: MotionProps & { visualElement: VisualElement }\n) {\n    const [isPresent, safeToRemove] = usePresence()\n    const layoutGroup = useContext(LayoutGroupContext)\n\n    return (\n        <MeasureLayoutWithContext\n            {...props}\n            layoutGroup={layoutGroup}\n            switchLayoutGroup={useContext(SwitchLayoutGroupContext)}\n            isPresent={isPresent}\n            safeToRemove={safeToRemove}\n        />\n    )\n}\n"],"names":[],"mappings":";;;;;;;;AAsBA;;;;;;;AAOG;AACH;AAEA;AACI;;;;AAIG;;AAEC;AAEA;;;AAG2B;;AAGnB;;;AAIA;;AAGJ;;AAEA;;;AAGI;AACA;AACH;;AAGL;;AAGJ;AACI;AACA;AAEA;AAAiB;AAEjB;;;;;;AAMG;AACH;AAEA;;;;AAIK;;;AAKL;;AAGI;AACA;;;;;;AAOJ;;;;AAGW;AACH;;;;AAIG;AACH;AACI;;;;AAIJ;;;AAIR;;;;AAKA;;AAEI;AAEA;AAEA;;;;AAIA;;;;AAKJ;AAKA;;;;AAMI;AACI;AACJ;AACI;;;;AAKR;;;;AAKA;;AAEP;AAEK;;AAIF;;AAWJ;;"}