{"version":3,"file":"useAnimatedHeight.cjs","sources":["../../../../src/hooks/useAnimatedHeight/useAnimatedHeight.ts"],"sourcesContent":["import { type RefObject, useCallback, useEffect, useRef } from \"react\";\nimport tokens from \"../../core/tokens.js\";\nimport { useBrowserPreferences } from \"../useBrowserPreferences/useBrowserPreferences.js\";\nimport { usePreviousValue } from \"../usePreviousValue/usePreviousValue.js\";\nimport type { UseAnimatedHeightOptions } from \"./types.js\";\n\nconst defaultDisplay = \"block\";\nconst defaultEasing = \"standard\";\nconst defaultTiming = \"productive\";\n\n/**\n * Lar deg animere et element opp til sin maksimale høyde, eller ned til 0.\n *\n * @param isOpen Om elementet er åpent eller lukket.\n * @param options Konfigurer display-property, easing og timing, og eventuelle callbacks for life cycle til animasjonen.\n * @returns Ref til elementet som skal animeres, og en funksjon for å trigge animasjonen manuelt.\n */\nexport function useAnimatedHeight<T extends HTMLElement>(\n    isOpen: boolean,\n    options?: UseAnimatedHeightOptions<T>,\n): [RefObject<T>, () => void] {\n    const wasOpen = usePreviousValue(isOpen);\n    const easing = options?.easing || defaultEasing;\n    const timing = options?.timing || defaultTiming;\n    const display = options?.display || defaultDisplay;\n    const transition = `${tokens.motion.timing[timing]} height ${tokens.motion.easing[easing]}`;\n\n    const { prefersReducedMotion } = useBrowserPreferences();\n\n    const raf1 = useRef<number>();\n    const raf2 = useRef<number>();\n    const elementRef = useRef<T>(null);\n\n    function handleTransitionEnd(event: TransitionEvent) {\n        const element = elementRef.current;\n\n        // Ignore bubbling transitions from within container\n        if (element && event.target === element) {\n            if (isOpen) {\n                element.removeAttribute(\"style\");\n            } else {\n                element.removeAttribute(\"style\");\n                element.style.display = \"none\";\n            }\n            options?.onTransitionEnd?.(isOpen, elementRef);\n        }\n    }\n\n    const runAnimation = useCallback(() => {\n        const element = elementRef.current;\n\n        // Ikke kjør animasjonen hvis elementet ikke er rendret,\n        // eller hvis det er første render.\n        if (!element || wasOpen === undefined) {\n            return;\n        }\n\n        if (!isOpen) {\n            element.style.display = \"none\";\n            if (!wasOpen) {\n                // Første render eller rerender med isOpen false\n                return;\n            }\n        } else if (isOpen && wasOpen) {\n            // Re-render etter å ha vært lukket, men forblitt åpen.\n            return;\n        }\n\n        options?.onTransitionStart?.(isOpen, elementRef);\n\n        if (prefersReducedMotion) {\n            element.removeAttribute(\"style\");\n            if (isOpen) {\n                options?.onFirstVisible?.(isOpen, elementRef);\n            } else {\n                element.style.display = \"none\";\n            }\n            options?.onTransitionEnd?.(isOpen, elementRef); // make sure to call callback when animation is off\n            return;\n        }\n\n        element.style.transition = transition;\n        element.style.display = display;\n        element.style.overflow = \"hidden\";\n\n        if (isOpen) {\n            options?.onFirstVisible?.(isOpen, elementRef);\n            element.style.height = \"0\";\n            element.style.height = `${element.scrollHeight}px`;\n        } else {\n            // If the scrollHeight is 0 it means that we are transitioning from height 0 -> 0.\n            // This causes the \"transitionend\"-event to never fire and the element gets stuck with\n            // style: height: 0; display: block; overflow:hidden\n            if (element.scrollHeight === 0) {\n                element.removeAttribute(\"style\");\n                return;\n            }\n\n            element.style.height = `${element.scrollHeight}px`;\n\n            raf1.current = requestAnimationFrame(() => {\n                raf2.current = requestAnimationFrame(() => {\n                    element.style.height = `${0}px`;\n                });\n            });\n        }\n    }, [isOpen, options, wasOpen, transition, prefersReducedMotion, display]);\n\n    // biome-ignore lint/correctness/useExhaustiveDependencies: Vi trigger med isOpen\n    useEffect(() => {\n        runAnimation();\n    }, [isOpen, runAnimation]);\n\n    // biome-ignore lint/correctness/useExhaustiveDependencies:\n    useEffect(() => {\n        const element = elementRef.current;\n        if (element) {\n            element.addEventListener(\"transitionend\", handleTransitionEnd);\n        }\n\n        return () => {\n            if (element) {\n                element.removeEventListener(\n                    \"transitionend\",\n                    handleTransitionEnd,\n                );\n            }\n        };\n    }, [isOpen]);\n\n    useEffect(() => {\n        const r1 = raf1.current;\n        const r2 = raf2.current;\n        return () => {\n            r1 && cancelAnimationFrame(r1);\n            r2 && cancelAnimationFrame(r2);\n        };\n    }, []);\n\n    return [elementRef, runAnimation];\n}\n"],"names":["isOpen","options","wasOpen","usePreviousValue","easing","timing","display","transition","tokens","motion","prefersReducedMotion","useBrowserPreferences","raf1","useRef","raf2","elementRef","handleTransitionEnd","event","element","current","target","removeAttribute","style","onTransitionEnd","runAnimation","useCallback","onTransitionStart","onFirstVisible","overflow","height","scrollHeight","requestAnimationFrame","useEffect","addEventListener","removeEventListener","r1","r2","cancelAnimationFrame"],"mappings":"4RAiBO,SACHA,EACAC,GAEA,MAAMC,EAAUC,EAAAA,iBAAiBH,GAC3BI,EAASH,GAASG,QAfN,WAgBZC,EAASJ,GAASI,QAfN,aAgBZC,EAAUL,GAASK,SAlBN,QAmBbC,EAAa,GAAGC,EAAOC,OAAOJ,OAAOA,aAAkBG,EAAOC,OAAOL,OAAOA,MAE1EM,qBAAAA,GAAyBC,0BAE3BC,EAAOC,EAAAA,SACPC,EAAOD,EAAAA,SACPE,EAAaF,EAAAA,OAAU,MAE7B,SAASG,EAAoBC,GACzB,MAAMC,EAAUH,EAAWI,QAGvBD,GAAWD,EAAMG,SAAWF,IACxBlB,EACAkB,EAAQG,gBAAgB,UAExBH,EAAQG,gBAAgB,SACxBH,EAAQI,MAAMhB,QAAU,QAE5BL,GAASsB,kBAAkBvB,EAAQe,GAE3C,CAEA,MAAMS,EAAeC,EAAAA,YAAY,KAC7B,MAAMP,EAAUH,EAAWI,QAI3B,GAAKD,QAAuB,IAAZhB,EAIhB,IAAKF,GAML,GAAWA,GAAUE,EAEjB,YAAA,GAPAgB,EAAQI,MAAMhB,QAAU,QACnBJ,EAED,OASR,GAFAD,GAASyB,oBAAoB1B,EAAQe,GAEjCL,EAQA,OAPAQ,EAAQG,gBAAgB,SACpBrB,EACAC,GAAS0B,iBAAiB3B,EAAQe,GAElCG,EAAQI,MAAMhB,QAAU,YAE5BL,GAASsB,kBAAkBvB,EAAQe,GAQvC,GAJAG,EAAQI,MAAMf,WAAaA,EAC3BW,EAAQI,MAAMhB,QAAUA,EACxBY,EAAQI,MAAMM,SAAW,SAErB5B,EACAC,GAAS0B,iBAAiB3B,EAAQe,GAClCG,EAAQI,MAAMO,OAAS,IACvBX,EAAQI,MAAMO,OAAS,GAAGX,EAAQY,qBAC/B,CAIH,GAA6B,IAAzBZ,EAAQY,aAER,YADAZ,EAAQG,gBAAgB,SAI5BH,EAAQI,MAAMO,OAAS,GAAGX,EAAQY,iBAElClB,EAAKO,QAAUY,sBAAsB,KACjCjB,EAAKK,QAAUY,sBAAsB,KACjCb,EAAQI,MAAMO,OAAS,SAGnC,CAAA,GACD,CAAC7B,EAAQC,EAASC,EAASK,EAAYG,EAAsBJ,IAGhE0B,OAAAA,EAAAA,UAAU,KACNR,KACD,CAACxB,EAAQwB,IAGZQ,EAAAA,UAAU,KACN,MAAMd,EAAUH,EAAWI,QAC3B,OAAID,GACAA,EAAQe,iBAAiB,gBAAiBjB,GAGvC,KACCE,GACAA,EAAQgB,oBACJ,gBACAlB,KAIb,CAAChB,IAEJgC,EAAAA,UAAU,KACN,MAAMG,EAAKvB,EAAKO,QACViB,EAAKtB,EAAKK,QAChB,MAAO,KACHgB,GAAME,qBAAqBF,GAC3BC,GAAMC,qBAAqBD,KAEhC,IAEI,CAACrB,EAAYS,EACxB"}