import {useRouter} from 'next/router' import {useEffect, useRef} from 'react' import {Container} from './Container' import {MobileNavigation, DesktopNavigation} from '@/components/ui/Navigation' import {Avatar, AvatarContainer} from '@/components/ui/Avatar' import {MoonIcon} from '@/components/icons/MoonIcon' import {SunIcon} from '@/components/icons/SunIcon' function ModeToggle() { function disableTransitionsTemporarily() { document.documentElement.classList.add('[&_*]:!transition-none') window.setTimeout(() => { document.documentElement.classList.remove('[&_*]:!transition-none') }, 0) } function toggleMode() { disableTransitionsTemporarily() let darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)') let isSystemDarkMode = darkModeMediaQuery.matches let isDarkMode = document.documentElement.classList.toggle('dark') if (isDarkMode === isSystemDarkMode) { delete window.localStorage.isDarkMode } else { window.localStorage.isDarkMode = isDarkMode } } return ( ) } function clamp(num: number, a: number, b: number) { let min = Math.min(a, b) let max = Math.max(a, b) return Math.min(Math.max(num, min), max) } export function Header() { const router = useRouter() const isHomePage = router.pathname === '/' const headerRef = useRef(null) const avatarRef = useRef(null) const isInitial = useRef(true) const headerPosition: Object = { position: 'var(--header-position)' } const headerInnerPosition: Object = { position: 'var(--header-inner-position)' } useEffect(() => { let downDelay = avatarRef.current?.offsetTop ?? 0 let upDelay = 64 function setProperty(property: string, value: string) { document.documentElement.style.setProperty(property, value.toString()) } function removeProperty(property: string) { document.documentElement.style.removeProperty(property) } function updateHeaderStyles() { const headerBoundingRect = headerRef.current?.getBoundingClientRect() let top = headerBoundingRect?.top! let height = headerBoundingRect?.height! let scrollY = clamp( window.scrollY, 0, document.body.scrollHeight - window.innerHeight ) if (isInitial.current) { setProperty('--header-position', 'sticky') } setProperty('--content-offset', `${downDelay}px`) if (isInitial.current || scrollY < downDelay) { setProperty('--header-height', `${downDelay + height}px`) setProperty('--header-mb', `${-downDelay}px`) } else if (top + height < -upDelay) { let offset = Math.max(height, scrollY - upDelay) setProperty('--header-height', `${offset}px`) setProperty('--header-mb', `${height - offset}px`) } else if (top === 0) { setProperty('--header-height', `${scrollY + height}px`) setProperty('--header-mb', `${-scrollY}px`) } if (top === 0 && scrollY > 0 && scrollY >= downDelay) { setProperty('--header-inner-position', 'fixed') removeProperty('--header-top') removeProperty('--avatar-top') } else { removeProperty('--header-inner-position') setProperty('--header-top', '0px') setProperty('--avatar-top', '0px') } } function updateAvatarStyles() { if (!isHomePage) { return } let fromScale = 1 let toScale = 36 / 64 let fromX = 0 let toX = 2 / 16 let scrollY = downDelay - window.scrollY let scale = (scrollY * (fromScale - toScale)) / downDelay + toScale scale = clamp(scale, fromScale, toScale) let x = (scrollY * (fromX - toX)) / downDelay + toX x = clamp(x, fromX, toX) setProperty( '--avatar-image-transform', `translate3d(${x}rem, 0, 0) scale(${scale})` ) let borderScale = 1 / (toScale / scale) let borderX = (-toX + x) * borderScale let borderTransform = `translate3d(${borderX}rem, 0, 0) scale(${borderScale})` setProperty('--avatar-border-transform', borderTransform) setProperty('--avatar-border-opacity', (scale === toScale ? 1 : 0).toString()) } function updateStyles() { updateHeaderStyles() updateAvatarStyles() isInitial.current = false } updateStyles() const opts: AddEventListenerOptions & EventListenerOptions = {passive: true} window.addEventListener('scroll', updateStyles, opts) window.addEventListener('resize', updateStyles) return () => { window.removeEventListener('scroll', updateStyles, opts) window.removeEventListener('resize', updateStyles) } }, [isHomePage]) return ( <>
{isHomePage && ( <>
)}
{!isHomePage && ( )}
{isHomePage &&
} ) }