1import "./styles.sass";
2import Logo from "@/common/assets/logo192.png";
3import {
4 faXmark,
5 faBars,
6 faArrowUpRightFromSquare,
7} from "@fortawesome/free-solid-svg-icons";
8import { faGithub } from "@fortawesome/free-brands-svg-icons";
9import { Link, useLocation } from "react-router-dom";
10import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
11import { useState, useEffect } from "react";
12import { DOCUMENTATION_ROOT } from "@/common/utils/constants";
13
14export const GITHUB_LINK = "https://github.com/ikhsanddy/pixies";
15
16const NAV_ITEMS = [
17 { path: "/", label: "Home", isExternal: false },
18 { path: "/install", label: "Install", isExternal: false },
19 { path: "/tutorials", label: "Tutorials", isExternal: false },
20 { path: DOCUMENTATION_ROOT, label: "Docs", isExternal: true },
21];
22
23export const Navigation = () => {
24 const [mobileOpen, setMobileOpen] = useState(false);
25 const [scrolled, setScrolled] = useState(false);
26 const location = useLocation();
27
28 useEffect(() => {
29 const handleScroll = () => {
30 setScrolled(window.scrollY > 20);
31 };
32 window.addEventListener("scroll", handleScroll);
33 return () => window.removeEventListener("scroll", handleScroll);
34 }, []);
35
36 useEffect(() => {
37 setMobileOpen(false);
38 }, [location]);
39
40 const isActive = (path) => location.pathname === path;
41
42 return (
43 <>
44 <nav className={scrolled ? "nav-scrolled" : ""}>
45 <div className="nav-inner">
46 <Link className="logo-area" to="/" aria-label="Pixies home">
47 <img src={Logo} alt="" />
48 <span>Pixies</span>
49 </Link>
50
51 <div className="nav-links" aria-label="Primary navigation">
52 {NAV_ITEMS.map((item) =>
53 item.isExternal ? (
54 <a
55 key={item.path}
56 href={item.path}
57 target="_blank"
58 rel="noopener noreferrer"
59 >
60 {item.label}
61 <FontAwesomeIcon
62 icon={faArrowUpRightFromSquare}
63 className="external-icon"
64 />
65 </a>
66 ) : (
67 <Link
68 key={item.path}
69 to={item.path}
70 className={isActive(item.path) ? "active" : ""}
71 >
72 {item.label}
73 </Link>
74 ),
75 )}
76 </div>
77
78 <div className="nav-actions">
79 <a
80 href={GITHUB_LINK}
81 target="_blank"
82 rel="noopener noreferrer"
83 className="github-link"
84 aria-label="Open Pixies on GitHub"
85 >
86 <FontAwesomeIcon icon={faGithub} />
87 </a>
88
89 <button
90 className="mobile-toggle"
91 onClick={() => setMobileOpen(!mobileOpen)}
92 aria-label="Toggle menu"
93 >
94 <FontAwesomeIcon icon={mobileOpen ? faXmark : faBars} />
95 </button>
96 </div>
97 </div>
98 </nav>
99
100 <div className={`mobile-nav ${mobileOpen ? "mobile-open" : ""}`}>
101 <div className="mobile-header">
102 <Link to="/" className="mobile-logo">
103 <img src={Logo} alt="Pixies Logo" />
104 <span>Pixies</span>
105 </Link>
106 <button className="mobile-close" onClick={() => setMobileOpen(false)}>
107 <FontAwesomeIcon icon={faXmark} />
108 </button>
109 </div>
110
111 <div className="mobile-links">
112 {NAV_ITEMS.map((item) =>
113 item.isExternal ? (
114 <a
115 key={item.path}
116 href={item.path}
117 target="_blank"
118 rel="noopener noreferrer"
119 >
120 {item.label}
121 <FontAwesomeIcon
122 icon={faArrowUpRightFromSquare}
123 className="external-icon"
124 />
125 </a>
126 ) : (
127 <Link
128 key={item.path}
129 to={item.path}
130 className={isActive(item.path) ? "active" : ""}
131 >
132 {item.label}
133 </Link>
134 ),
135 )}
136 <a href={GITHUB_LINK} target="_blank" rel="noopener noreferrer">
137 GitHub
138 </a>
139 </div>
140 </div>
141
142 {mobileOpen && (
143 <div className="mobile-overlay" onClick={() => setMobileOpen(false)} />
144 )}
145 </>
146 );
147};
148