File size: 1,826 Bytes
75fefa7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"use client";

import React, { useRef, useEffect, ReactNode, useState } from "react";

// Smoothly animates its container to match the natural height of its content.
// Fixes previous behavior where the component observed itself, causing height 0
// with overflow hidden (content clipped) or visible overflow that overlapped
// following sections like the footer.
export default function AnimatedHeight({
  children,
  overflow = true,
}: {
  children: ReactNode;
  overflow?: boolean;
}) {
  const containerRef = useRef<HTMLDivElement>(null);
  const contentRef = useRef<HTMLDivElement>(null);
  const [measuredHeight, setMeasuredHeight] = useState<number | null>(null);
  const hasAnimatedOnceRef = useRef(false);

  useEffect(() => {
    const contentEl = contentRef.current;
    const containerEl = containerRef.current;
    if (!contentEl || !containerEl) return;

    const updateHeight = () => {
      // Use scrollHeight to capture full natural height, including overflowed content
      const height = contentEl.scrollHeight;
      setMeasuredHeight((prev) => (prev === height ? prev : height));

      // Enable transition after the first measurement to avoid initial jank
      if (!hasAnimatedOnceRef.current) {
        containerEl.style.transition = "height 300ms ease-in-out";
        hasAnimatedOnceRef.current = true;
      }
    };

    // Initial measure
    updateHeight();

    const resizeObserver = new ResizeObserver(() => updateHeight());
    resizeObserver.observe(contentEl);

    return () => resizeObserver.disconnect();
  }, []);

  return (
    <div
      ref={containerRef}
      style={{
        overflow: overflow ? "hidden" : "visible",
        height: measuredHeight === null ? undefined : `${measuredHeight}px`,
      }}
    >
      <div ref={contentRef}>{children}</div>
    </div>
  );
}