-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Expand file tree
/
Copy pathheader.tsx
More file actions
97 lines (85 loc) · 3.78 KB
/
Copy pathheader.tsx
File metadata and controls
97 lines (85 loc) · 3.78 KB
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import { useContext, useEffect, useRef, useState } from "react";
import { ProfileContext } from "../state/profile";
import { Padding } from "./padding";
import { useSiteConfig } from "../hooks/useSiteConfig";
import { getHeaderLayoutDefinition } from "./site-header/layout-registry";
import { normalizeHeaderBehavior, normalizeHeaderLayout } from "./site-header/layout-options";
export function Header({ children }: { children?: React.ReactNode }) {
const profile = useContext(ProfileContext);
const siteConfig = useSiteConfig();
const headerLayout = normalizeHeaderLayout(siteConfig.headerLayout);
const headerBehavior = normalizeHeaderBehavior(siteConfig.headerBehavior);
const layoutDefinition = getHeaderLayoutDefinition(headerLayout);
const [isRevealed, setIsRevealed] = useState(true);
const [isAtTop, setIsAtTop] = useState(true);
const headerRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
let lastScrollY = window.scrollY;
const onScroll = () => {
const currentScrollY = window.scrollY;
const nearTop = currentScrollY <= 24;
const scrollingUp = currentScrollY < lastScrollY;
setIsAtTop(nearTop);
setIsRevealed(headerBehavior !== "reveal" || nearTop || scrollingUp);
lastScrollY = currentScrollY;
};
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
return () => {
window.removeEventListener("scroll", onScroll);
};
}, [headerBehavior]);
useEffect(() => {
const root = document.documentElement;
const setHeaderScrollOffset = () => {
const headerHeight = headerRef.current?.getBoundingClientRect().height ?? 0;
root.style.setProperty("--header-scroll-offset", `${Math.ceil(headerHeight + 16)}px`);
};
setHeaderScrollOffset();
const resizeObserver = new ResizeObserver(() => {
setHeaderScrollOffset();
});
if (headerRef.current) {
resizeObserver.observe(headerRef.current);
}
window.addEventListener("resize", setHeaderScrollOffset);
return () => {
resizeObserver.disconnect();
window.removeEventListener("resize", setHeaderScrollOffset);
};
}, [headerBehavior, headerLayout]);
const useTopHeader = layoutDefinition.kind === "top";
const headerPaddingClassName = headerLayout === "compact" ? "mx-0 mt-0" : "mx-4 mt-4";
const containerClassName =
!useTopHeader || headerBehavior === "static"
? "relative z-40"
: `fixed inset-x-0 top-0 z-40 transition-transform duration-300 ${
headerBehavior === "reveal" && !isRevealed ? "-translate-y-full" : "translate-y-0"
}`;
const spacerClassName = !useTopHeader || headerBehavior === "static" ? "h-0" : "h-20";
return (
<>
{headerLayout === "compact" ? (
<div className="pointer-events-none fixed inset-x-0 top-0 -z-10 h-64 bg-gradient-to-b from-theme/15 to-white/0 dark:from-theme/20 dark:to-transparent" />
) : null}
<div ref={headerRef} className={containerClassName}>
<div className="w-screen">
{headerLayout === "compact" ? (
<div className="w-full">
{layoutDefinition.renderMobile({ children, profile, siteConfig, behavior: headerBehavior, isAtTop })}
{layoutDefinition.renderDesktop({ children, profile, siteConfig, behavior: headerBehavior, isAtTop })}
</div>
) : (
<Padding className={headerPaddingClassName}>
<div className="w-full">
{layoutDefinition.renderMobile({ children, profile, siteConfig, behavior: headerBehavior, isAtTop })}
{layoutDefinition.renderDesktop({ children, profile, siteConfig, behavior: headerBehavior, isAtTop })}
</div>
</Padding>
)}
</div>
</div>
<div className={spacerClassName} />
</>
);
}