Skip to content

Commit ec73d66

Browse files
committed
Header: toggleable tab options
1 parent 4177cab commit ec73d66

File tree

3 files changed

+86
-5
lines changed

3 files changed

+86
-5
lines changed

components/Header.tsx

+15-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Link from "next/link"
66
import { usePathname } from "next/navigation"
77

88
import classNames from "@/utils/classNames"
9+
import useLinkHighlight from "@/hooks/useLinkHighlight"
910

1011
const links = [
1112
{ label: "Features", path: "/" },
@@ -16,6 +17,9 @@ const links = [
1617
const Header: React.FC = () => {
1718
const pathname = usePathname()
1819

20+
const [activeTab, setActiveTab] = React.useState(0)
21+
const { wrapperRef, highlightStyles } = useLinkHighlight(activeTab)
22+
1923
return (
2024
<section className="px-3 pt-3">
2125
<motion.header
@@ -31,17 +35,23 @@ const Header: React.FC = () => {
3135
>
3236
<div className="mr-auto text-base font-bold">SVG Gallery</div>
3337
<div className="order-3 col-span-3 flex justify-center md:order-2 md:col-span-1">
34-
<div className="toggle-group flex gap-1 rounded-3xl bg-gray-200 p-1">
38+
<div
39+
className="toggle-group relative flex gap-1 rounded-3xl bg-gray-200 p-1"
40+
ref={wrapperRef}
41+
>
42+
<div
43+
style={{ ...highlightStyles, height: 32 }}
44+
className="absolute left-0 rounded-3xl transition-all duration-300 bg-white toggle-selected-item"
45+
/>
3546
{links.map((link, index) => (
3647
<Link
3748
key={index}
3849
className={classNames(
39-
"rounded-3xl px-3 py-1.5 font-semibold transition-all hover:text-gray-900",
40-
pathname === link.path
41-
? "toggle-selected-item bg-white text-gray-900"
42-
: "text-gray-500"
50+
"relative rounded-3xl px-3 py-1.5 font-semibold transition-all hover:text-gray-900",
51+
pathname === link.path ? "text-gray-900" : "text-gray-500"
4352
)}
4453
href={link.path}
54+
onClick={() => setActiveTab(index)}
4555
>
4656
{link.label}
4757
</Link>

hooks/useLinkHighlight.tsx

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as React from "react"
2+
3+
import useWindowSize from "./useWindowSize"
4+
5+
interface HighlightProperties {
6+
wrapperRef: React.RefObject<HTMLDivElement | null>
7+
highlightStyles: React.CSSProperties
8+
}
9+
10+
const useLinkHighlight = (activeTab: number): HighlightProperties => {
11+
const size = useWindowSize()
12+
13+
const wrapperRef = React.useRef<HTMLDivElement | null>(null)
14+
15+
const [highlightStyles, setHighlightStyles] =
16+
React.useState<React.CSSProperties>({ opacity: 0 })
17+
18+
React.useEffect(() => {
19+
if (wrapperRef?.current !== null) {
20+
const elements = wrapperRef.current?.getElementsByTagName("a")
21+
22+
if (elements?.length !== 0) {
23+
const dimensions = elements[activeTab].getBoundingClientRect()
24+
const wrapperDimensions = wrapperRef.current.getBoundingClientRect()
25+
26+
setHighlightStyles({
27+
opacity: 1,
28+
width: `${dimensions.width}px`,
29+
transform: `translate(${dimensions.left - wrapperDimensions.left}px)`,
30+
})
31+
}
32+
}
33+
}, [activeTab, size])
34+
35+
return { wrapperRef, highlightStyles }
36+
}
37+
38+
export default useLinkHighlight

hooks/useWindowSize.tsx

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from "react"
2+
3+
interface Dimensions {
4+
width: number | null
5+
height: number | null
6+
}
7+
8+
const useWindowSize = (): Dimensions => {
9+
const [size, setSize] = React.useState<Dimensions>({
10+
width: null,
11+
height: null,
12+
})
13+
14+
React.useLayoutEffect(() => {
15+
const handleResize = (): void => {
16+
setSize({
17+
width: window.innerWidth,
18+
height: window.innerHeight,
19+
})
20+
}
21+
22+
handleResize()
23+
window.addEventListener("resize", handleResize)
24+
25+
return () => {
26+
window.removeEventListener("resize", handleResize)
27+
}
28+
}, [])
29+
30+
return size
31+
}
32+
33+
export default useWindowSize

0 commit comments

Comments
 (0)