|
1 | 1 | "use client"; |
2 | 2 |
|
| 3 | +import { useLayoutEffect, useRef } from "react"; |
3 | 4 | import StatusBannerClient from "./StatusBannerClient"; |
4 | 5 | import { useStatusBanner } from "./StatusBannerProvider"; |
5 | 6 |
|
6 | 7 | /** |
7 | | - * Header slot: renders the full-width status strip once the async fetch in |
8 | | - * StatusBannerProvider resolves, sliding it in from the top. Renders nothing |
9 | | - * while there's no active banner, so it never blocks the initial paint. |
| 8 | + * Renders the full-width status strip once the async fetch in StatusBannerProvider resolves. |
10 | 9 | */ |
11 | 10 | export default function StatusBannerBar() { |
12 | | - const { banner, href, mounted, shown } = useStatusBanner(); |
| 11 | + const { banner, href, mounted, shown, setContentHeight } = useStatusBanner(); |
| 12 | + const contentRef = useRef<HTMLDivElement>(null); |
| 13 | + |
| 14 | + useLayoutEffect(() => { |
| 15 | + const el = contentRef.current; |
| 16 | + if (!el) { |
| 17 | + return; |
| 18 | + } |
| 19 | + const update = () => |
| 20 | + setContentHeight(Math.ceil(el.getBoundingClientRect().height)); |
| 21 | + update(); |
| 22 | + const observer = new ResizeObserver(update); |
| 23 | + observer.observe(el); |
| 24 | + return () => observer.disconnect(); |
| 25 | + }, [banner, shown, setContentHeight]); |
| 26 | + |
13 | 27 | if (!mounted || !banner) { |
14 | 28 | return null; |
15 | 29 | } |
16 | 30 | return ( |
17 | 31 | <div |
18 | | - className={`overflow-hidden transition-[height] duration-300 ease-out ${ |
19 | | - shown ? "h-11" : "h-0" |
20 | | - }`} |
| 32 | + className="grid transition-[grid-template-rows] duration-300 ease-out" |
| 33 | + style={{ gridTemplateRows: shown ? "1fr" : "0fr" }} |
21 | 34 | > |
22 | | - <div |
23 | | - className={`transition-all duration-300 ease-out ${ |
24 | | - shown ? "translate-y-0 opacity-100" : "-translate-y-1 opacity-0" |
25 | | - }`} |
26 | | - > |
27 | | - <StatusBannerClient banner={banner} href={href} variant="bar" /> |
| 35 | + <div className="min-h-0 overflow-hidden"> |
| 36 | + <div |
| 37 | + ref={contentRef} |
| 38 | + className={`transition-opacity duration-300 ease-out ${ |
| 39 | + shown ? "opacity-100" : "opacity-0" |
| 40 | + }`} |
| 41 | + > |
| 42 | + <StatusBannerClient banner={banner} href={href} variant="bar" /> |
| 43 | + </div> |
28 | 44 | </div> |
29 | 45 | </div> |
30 | 46 | ); |
|
0 commit comments