'use client'
import { Popover, Transition } from '@headlessui/react'
import clsx from 'clsx'
import Image from 'next/image'
import { usePathname } from 'next/navigation'
import {
Fragment,
type HTMLAttributes,
type PropsWithChildren,
useEffect,
useRef,
} from 'react'
import { Container } from '@/components/Container'
import avatarImage from '@/images/me.jpeg'
import { PopoverProps } from '@/lib/types'
import HybridLink, { Props as HydridLinkProps } from './HybridLink'
import { ChevronDownIcon, CloseIcon, MoonIcon, SunIcon } from './icons'
const MobileNavItem = ({
href,
children,
}: PropsWithChildren<{ href: HydridLinkProps['href'] }>) => {
return (
{children}
)
}
const MobileNavigation = (props: PopoverProps) => {
return (
Menu
)
}
const NavItem = ({ href, children }: HydridLinkProps) => {
const isActive = usePathname() === href
return (
{children}
{isActive && (
)}
)
}
const DesktopNavigation = (props: HTMLAttributes) => {
return (
)
}
const ModeToggle = () => {
const disableTransitionsTemporarily = () => {
document.documentElement.classList.add('[&_*]:!transition-none')
window.setTimeout(() => {
document.documentElement.classList.remove('[&_*]:!transition-none')
}, 0)
}
const toggleMode = () => {
disableTransitionsTemporarily()
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const isSystemDarkMode = darkModeMediaQuery.matches
const isDarkMode = document.documentElement.classList.toggle('dark')
if (isDarkMode === isSystemDarkMode) {
delete window.localStorage.isDarkMode
} else {
window.localStorage.isDarkMode = isDarkMode
}
}
return (
)
}
const clamp = (number: number, a: number, b: number) => {
const min = Math.min(a, b)
const max = Math.max(a, b)
return Math.min(Math.max(number, min), max)
}
const AvatarContainer = ({
className,
...props
}: HTMLAttributes) => {
return (
)
}
const Avatar = ({
large = false,
className,
...props
}: Omit & { large?: boolean }) => {
return (
)
}
const Header = () => {
const isHomePage = usePathname() === '/'
const headerRef = useRef(null)
const avatarRef = useRef(null)
const isInitial = useRef(true)
useEffect(() => {
const downDelay = avatarRef.current?.offsetTop ?? 0
const upDelay = 64
const setProperty = (property: string, value: string | null) => {
document.documentElement.style.setProperty(property, value)
}
const removeProperty = (property: string) => {
document.documentElement.style.removeProperty(property)
}
const updateHeaderStyles = () => {
const { top, height } = headerRef.current!.getBoundingClientRect()
const 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) {
const 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
}
const fromScale = 1
const toScale = 36 / 64
const fromX = 0
const toX = 2 / 16
const 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})`,
)
const borderScale = 1 / (toScale / scale)
const borderX = (-toX + x) * borderScale
const borderTransform = `translate3d(${borderX}rem, 0, 0) scale(${borderScale})`
setProperty('--avatar-border-transform', borderTransform)
setProperty('--avatar-border-opacity', scale === toScale ? '1' : '0')
}
function updateStyles() {
updateHeaderStyles()
updateAvatarStyles()
isInitial.current = false
}
updateStyles()
window.addEventListener('scroll', updateStyles, { passive: true })
window.addEventListener('resize', updateStyles)
return () => {
window.removeEventListener('scroll', updateStyles)
window.removeEventListener('resize', updateStyles)
}
}, [isHomePage])
return (
<>
{isHomePage && }
>
)
}
export default Header