diff options
| author | FifthWit <fifthwitbusiness@gmail.com> | 2025-08-14 14:01:01 -0500 |
|---|---|---|
| committer | FifthWit <fifthwitbusiness@gmail.com> | 2025-08-14 14:01:01 -0500 |
| commit | 6a8b909afbe1560be95f7ad0a3e19cfe4717aec6 (patch) | |
| tree | 83cdbe3b5b7e5b83d5f0d08964634cc942264072 /frontend/src/components/Sidebar.tsx | |
| parent | Switched to Vite as build tool (diff) | |
| download | lphub-6a8b909afbe1560be95f7ad0a3e19cfe4717aec6.tar.gz lphub-6a8b909afbe1560be95f7ad0a3e19cfe4717aec6.tar.bz2 lphub-6a8b909afbe1560be95f7ad0a3e19cfe4717aec6.zip | |
Switched to tailwind/vite
Diffstat (limited to 'frontend/src/components/Sidebar.tsx')
| -rw-r--r-- | frontend/src/components/Sidebar.tsx | 386 |
1 files changed, 192 insertions, 194 deletions
diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index b55d56b..88a5297 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | import React, { useCallback } from "react"; | 1 | import React, { useCallback, useRef } from "react"; |
| 2 | import { Link, useLocation } from "react-router-dom"; | 2 | import { Link, useLocation } from "react-router-dom"; |
| 3 | 3 | ||
| 4 | import { | 4 | import { |
| @@ -10,12 +10,11 @@ import { | |||
| 10 | PortalIcon, | 10 | PortalIcon, |
| 11 | SearchIcon, | 11 | SearchIcon, |
| 12 | UploadIcon, | 12 | UploadIcon, |
| 13 | } from "@images/Images"; | 13 | } from "../images/Images"; |
| 14 | import Login from "@components/Login"; | 14 | import Login from "@components/Login"; |
| 15 | import { UserProfile } from "@customTypes/Profile"; | 15 | import { UserProfile } from "@customTypes/Profile"; |
| 16 | import { Search } from "@customTypes/Search"; | 16 | import { Search } from "@customTypes/Search"; |
| 17 | import { API } from "@api/Api"; | 17 | import { API } from "@api/Api"; |
| 18 | import "@css/Sidebar.css"; | ||
| 19 | 18 | ||
| 20 | interface SidebarProps { | 19 | interface SidebarProps { |
| 21 | setToken: React.Dispatch<React.SetStateAction<string | undefined>>; | 20 | setToken: React.Dispatch<React.SetStateAction<string | undefined>>; |
| @@ -24,6 +23,17 @@ interface SidebarProps { | |||
| 24 | onUploadRun: () => void; | 23 | onUploadRun: () => void; |
| 25 | } | 24 | } |
| 26 | 25 | ||
| 26 | function OpenSidebarIcon(){ | ||
| 27 | return ( | ||
| 28 | <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="lucide lucide-panel-right-close-icon lucide-panel-right-close"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/><path d="m8 9 3 3-3 3"/></svg> | ||
| 29 | ) | ||
| 30 | } | ||
| 31 | |||
| 32 | function ClosedSidebarIcon(){ | ||
| 33 | return ( | ||
| 34 | <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="lucide lucide-panel-right-open-icon lucide-panel-right-open"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/><path d="m10 15-3-3 3-3"/></svg> ) | ||
| 35 | } | ||
| 36 | |||
| 27 | const Sidebar: React.FC<SidebarProps> = ({ | 37 | const Sidebar: React.FC<SidebarProps> = ({ |
| 28 | setToken, | 38 | setToken, |
| 29 | profile, | 39 | profile, |
| @@ -34,100 +44,38 @@ const Sidebar: React.FC<SidebarProps> = ({ | |||
| 34 | undefined | 44 | undefined |
| 35 | ); | 45 | ); |
| 36 | const [isSidebarLocked, setIsSidebarLocked] = React.useState<boolean>(false); | 46 | const [isSidebarLocked, setIsSidebarLocked] = React.useState<boolean>(false); |
| 37 | const [isSidebarOpen, setSidebarOpen] = React.useState<boolean>(true); | 47 | const [isSidebarOpen, setSidebarOpen] = React.useState<boolean>(false); |
| 48 | const [selectedButtonIndex, setSelectedButtonIndex] = React.useState<number>(1); | ||
| 38 | 49 | ||
| 39 | const location = useLocation(); | 50 | const location = useLocation(); |
| 40 | const path = location.pathname; | 51 | const path = location.pathname; |
| 41 | 52 | ||
| 42 | const _handle_sidebar_hide = useCallback(() => { | 53 | const sidebarRef = useRef<HTMLDivElement>(null); |
| 43 | var btn = document.querySelectorAll( | 54 | const searchbarRef = useRef<HTMLInputElement>(null); |
| 44 | "button.sidebar-button" | 55 | const uploadRunRef = useRef<HTMLButtonElement>(null); |
| 45 | ) as NodeListOf<HTMLElement>; | 56 | const sidebarButtonRefs = useRef<(HTMLButtonElement | null)[]>([]); |
| 46 | const span = document.querySelectorAll( | 57 | |
| 47 | "button.sidebar-button>span" | 58 | const _handle_sidebar_toggle = useCallback(() => { |
| 48 | ) as NodeListOf<HTMLElement>; | 59 | if (!sidebarRef.current) return; |
| 49 | const side = document.querySelector("#sidebar-list") as HTMLElement; | ||
| 50 | const searchbar = document.querySelector("#searchbar") as HTMLInputElement; | ||
| 51 | const uploadRunBtn = document.querySelector( | ||
| 52 | "#upload-run" | ||
| 53 | ) as HTMLInputElement; | ||
| 54 | const uploadRunSpan = document.querySelector( | ||
| 55 | "#upload-run>span" | ||
| 56 | ) as HTMLInputElement; | ||
| 57 | 60 | ||
| 58 | if (isSidebarOpen) { | 61 | if (isSidebarOpen) { |
| 59 | if (profile) { | ||
| 60 | const login = document.querySelectorAll( | ||
| 61 | ".login>button" | ||
| 62 | )[1] as HTMLElement; | ||
| 63 | login.style.opacity = "1"; | ||
| 64 | uploadRunBtn.style.width = "310px"; | ||
| 65 | uploadRunBtn.style.padding = "0.4em 0 0 11px"; | ||
| 66 | uploadRunSpan.style.opacity = "0"; | ||
| 67 | setTimeout(() => { | ||
| 68 | uploadRunSpan.style.opacity = "1"; | ||
| 69 | }, 100); | ||
| 70 | } | ||
| 71 | setSidebarOpen(false); | 62 | setSidebarOpen(false); |
| 72 | side.style.width = "320px"; | ||
| 73 | btn.forEach((e, i) => { | ||
| 74 | e.style.width = "310px"; | ||
| 75 | e.style.padding = "0.4em 0 0 11px"; | ||
| 76 | setTimeout(() => { | ||
| 77 | span[i].style.opacity = "1"; | ||
| 78 | }, 100); | ||
| 79 | }); | ||
| 80 | side.style.zIndex = "2"; | ||
| 81 | } else { | 63 | } else { |
| 82 | if (profile) { | ||
| 83 | const login = document.querySelectorAll( | ||
| 84 | ".login>button" | ||
| 85 | )[1] as HTMLElement; | ||
| 86 | login.style.opacity = "0"; | ||
| 87 | uploadRunBtn.style.width = "40px"; | ||
| 88 | uploadRunBtn.style.padding = "0.4em 0 0 5px"; | ||
| 89 | uploadRunSpan.style.opacity = "0"; | ||
| 90 | } | ||
| 91 | setSidebarOpen(true); | 64 | setSidebarOpen(true); |
| 92 | side.style.width = "40px"; | 65 | searchbarRef.current?.focus(); |
| 93 | searchbar.focus(); | ||
| 94 | btn.forEach((e, i) => { | ||
| 95 | e.style.width = "40px"; | ||
| 96 | e.style.padding = "0.4em 0 0 5px"; | ||
| 97 | span[i].style.opacity = "0"; | ||
| 98 | }); | ||
| 99 | setTimeout(() => { | ||
| 100 | side.style.zIndex = "0"; | ||
| 101 | }, 300); | ||
| 102 | } | 66 | } |
| 103 | }, [isSidebarOpen, profile]); | 67 | }, [isSidebarOpen]); |
| 104 | 68 | ||
| 105 | const handle_sidebar_click = useCallback( | 69 | const handle_sidebar_click = useCallback( |
| 106 | (clicked_sidebar_idx: number) => { | 70 | (clicked_sidebar_idx: number) => { |
| 107 | const btn = document.querySelectorAll("button.sidebar-button"); | 71 | setSelectedButtonIndex(clicked_sidebar_idx); |
| 108 | if (isSidebarOpen) { | 72 | if (isSidebarOpen) { |
| 109 | setSidebarOpen(false); | 73 | setSidebarOpen(false); |
| 110 | _handle_sidebar_hide(); | ||
| 111 | } | 74 | } |
| 112 | // clusterfuck | ||
| 113 | btn.forEach((e, i) => { | ||
| 114 | btn[i].classList.remove("sidebar-button-selected"); | ||
| 115 | btn[i].classList.add("sidebar-button-deselected"); | ||
| 116 | }); | ||
| 117 | btn[clicked_sidebar_idx].classList.add("sidebar-button-selected"); | ||
| 118 | btn[clicked_sidebar_idx].classList.remove("sidebar-button-deselected"); | ||
| 119 | }, | 75 | }, |
| 120 | [isSidebarOpen, _handle_sidebar_hide] | 76 | [isSidebarOpen] |
| 121 | ); | 77 | ); |
| 122 | 78 | ||
| 123 | const _handle_sidebar_lock = () => { | ||
| 124 | if (!isSidebarLocked) { | ||
| 125 | _handle_sidebar_hide(); | ||
| 126 | setIsSidebarLocked(true); | ||
| 127 | setTimeout(() => setIsSidebarLocked(false), 300); | ||
| 128 | } | ||
| 129 | }; | ||
| 130 | |||
| 131 | const _handle_search_change = async (q: string) => { | 79 | const _handle_search_change = async (q: string) => { |
| 132 | const searchResponse = await API.get_search(q); | 80 | const searchResponse = await API.get_search(q); |
| 133 | setSearchData(searchResponse); | 81 | setSearchData(searchResponse); |
| @@ -135,149 +83,199 @@ const Sidebar: React.FC<SidebarProps> = ({ | |||
| 135 | 83 | ||
| 136 | React.useEffect(() => { | 84 | React.useEffect(() => { |
| 137 | if (path === "/") { | 85 | if (path === "/") { |
| 138 | handle_sidebar_click(1); | 86 | setSelectedButtonIndex(1); |
| 139 | } else if (path.includes("games")) { | 87 | } else if (path.includes("games")) { |
| 140 | handle_sidebar_click(2); | 88 | setSelectedButtonIndex(2); |
| 141 | } else if (path.includes("rankings")) { | 89 | } else if (path.includes("rankings")) { |
| 142 | handle_sidebar_click(3); | 90 | setSelectedButtonIndex(3); |
| 143 | } | 91 | } else if (path.includes("profile")) { |
| 144 | // else if (path.includes("news")) { handle_sidebar_click(4) } | 92 | setSelectedButtonIndex(4); |
| 145 | // else if (path.includes("scorelog")) { handle_sidebar_click(5) } | ||
| 146 | else if (path.includes("profile")) { | ||
| 147 | handle_sidebar_click(4); | ||
| 148 | } else if (path.includes("rules")) { | 93 | } else if (path.includes("rules")) { |
| 149 | handle_sidebar_click(5); | 94 | setSelectedButtonIndex(5); |
| 150 | } else if (path.includes("about")) { | 95 | } else if (path.includes("about")) { |
| 151 | handle_sidebar_click(6); | 96 | setSelectedButtonIndex(6); |
| 152 | } | 97 | } |
| 153 | }, [path, handle_sidebar_click]); | 98 | }, [path]); |
| 99 | |||
| 100 | const getButtonClasses = (buttonIndex: number) => { | ||
| 101 | const baseClasses = "flex items-center gap-3 w-full text-left bg-inherit cursor-pointer border-none rounded-lg py-3 px-3 transition-all duration-300 hover:bg-surface1"; | ||
| 102 | const selectedClasses = selectedButtonIndex === buttonIndex ? "bg-primary text-background" : "bg-transparent text-foreground"; | ||
| 103 | |||
| 104 | return `${baseClasses} ${selectedClasses}`; | ||
| 105 | }; | ||
| 106 | |||
| 107 | const iconClasses = "w-6 h-6 flex-shrink-0"; | ||
| 154 | 108 | ||
| 155 | return ( | 109 | return ( |
| 156 | <div id="sidebar"> | 110 | <div className={`fixed top-0 left-0 h-screen bg-surface border-r border-border transition-all duration-300 z-10 overflow-hidden ${ |
| 157 | <Link to="/" tabIndex={-1}> | 111 | isSidebarOpen ? 'w-80' : 'w-20' |
| 158 | <div id="logo"> | 112 | }`}> |
| 159 | {" "} | 113 | <div className="flex items-center h-20 px-4 border-b border-border"> |
| 160 | {/* logo */} | 114 | <Link to="/" tabIndex={-1} className="flex items-center flex-1 cursor-pointer select-none min-w-0"> |
| 161 | <img src={LogoIcon} alt="" height={"80px"} /> | 115 | <img src={LogoIcon} alt="Logo" className="w-12 h-12 flex-shrink-0" /> |
| 162 | <div id="logo-text"> | 116 | {isSidebarOpen && ( |
| 163 | <span> | 117 | <div className="ml-3 font-[--font-barlow-condensed-regular] text-white min-w-0 overflow-hidden"> |
| 164 | <b>PORTAL 2</b> | 118 | <div className="font-[--font-barlow-condensed-bold] text-2xl leading-6 truncate"> |
| 165 | </span> | 119 | PORTAL 2 |
| 166 | <br /> | 120 | </div> |
| 167 | <span>Least Portals Hub</span> | 121 | <div className="text-sm leading-4 truncate"> |
| 122 | Least Portals Hub | ||
| 123 | </div> | ||
| 124 | </div> | ||
| 125 | )} | ||
| 126 | </Link> | ||
| 127 | |||
| 128 | <button | ||
| 129 | onClick={_handle_sidebar_toggle} | ||
| 130 | className="ml-2 p-2 rounded-lg hover:bg-surface1 transition-colors text-foreground" | ||
| 131 | title={isSidebarOpen ? "Close sidebar" : "Open sidebar"} | ||
| 132 | > | ||
| 133 | {isSidebarOpen ? <ClosedSidebarIcon /> : <OpenSidebarIcon />} | ||
| 134 | </button> | ||
| 135 | </div> | ||
| 136 | |||
| 137 | {/* Sidebar Content */} | ||
| 138 | <div | ||
| 139 | ref={sidebarRef} | ||
| 140 | className="flex flex-col h-[calc(100vh-80px)] overflow-y-auto overflow-x-hidden" | ||
| 141 | > | ||
| 142 | {isSidebarOpen && ( | ||
| 143 | <div className="p-4 border-b border-border min-w-0"> | ||
| 144 | <div className="flex items-center gap-3 mb-3"> | ||
| 145 | <img src={SearchIcon} alt="Search" className={iconClasses} /> | ||
| 146 | <span className="text-white font-[--font-barlow-semicondensed-regular] truncate">Search</span> | ||
| 147 | </div> | ||
| 148 | |||
| 149 | <div className="min-w-0"> | ||
| 150 | <input | ||
| 151 | ref={searchbarRef} | ||
| 152 | type="text" | ||
| 153 | id="searchbar" | ||
| 154 | placeholder="Search for map or a player..." | ||
| 155 | onChange={e => _handle_search_change(e.target.value)} | ||
| 156 | className="w-full p-2 bg-input text-foreground border border-border rounded-lg text-sm min-w-0" | ||
| 157 | /> | ||
| 158 | |||
| 159 | {searchData && ( | ||
| 160 | <div className="mt-2 max-h-40 overflow-y-auto min-w-0"> | ||
| 161 | {searchData?.maps.map((q, index) => ( | ||
| 162 | <Link to={`/maps/${q.id}`} className="block p-2 mb-1 bg-surface1 rounded hover:bg-surface2 transition-colors min-w-0" key={index}> | ||
| 163 | <span className="block text-xs text-subtext1 truncate">{q.game}</span> | ||
| 164 | <span className="block text-xs text-subtext1 truncate">{q.chapter}</span> | ||
| 165 | <span className="block text-sm text-foreground truncate">{q.map}</span> | ||
| 166 | </Link> | ||
| 167 | ))} | ||
| 168 | {searchData?.players.map((q, index) => ( | ||
| 169 | <Link | ||
| 170 | to={ | ||
| 171 | profile && q.steam_id === profile.steam_id | ||
| 172 | ? `/profile` | ||
| 173 | : `/users/${q.steam_id}` | ||
| 174 | } | ||
| 175 | className="flex items-center p-2 mb-1 bg-surface1 rounded hover:bg-surface2 transition-colors min-w-0" | ||
| 176 | key={index} | ||
| 177 | > | ||
| 178 | <img src={q.avatar_link} alt="pfp" className="w-6 h-6 rounded-full mr-2 flex-shrink-0" /> | ||
| 179 | <span className="text-sm text-foreground truncate"> | ||
| 180 | {q.user_name} | ||
| 181 | </span> | ||
| 182 | </Link> | ||
| 183 | ))} | ||
| 184 | </div> | ||
| 185 | )} | ||
| 186 | </div> | ||
| 168 | </div> | 187 | </div> |
| 188 | )} | ||
| 189 | |||
| 190 | <div className="flex-1 p-4 min-w-0"> | ||
| 191 | <nav className="space-y-2"> | ||
| 192 | {[ | ||
| 193 | { | ||
| 194 | to: "/", | ||
| 195 | refIndex: 1, | ||
| 196 | icon: HomeIcon, | ||
| 197 | alt: "Home", | ||
| 198 | label: "Home Page", | ||
| 199 | }, | ||
| 200 | { | ||
| 201 | to: "/games", | ||
| 202 | refIndex: 2, | ||
| 203 | icon: PortalIcon, | ||
| 204 | alt: "Games", | ||
| 205 | label: "Games", | ||
| 206 | }, | ||
| 207 | { | ||
| 208 | to: "/rankings", | ||
| 209 | refIndex: 3, | ||
| 210 | icon: FlagIcon, | ||
| 211 | alt: "Rankings", | ||
| 212 | label: "Rankings", | ||
| 213 | }, | ||
| 214 | ].map(({ to, refIndex, icon, alt, label }) => ( | ||
| 215 | <Link to={to} tabIndex={-1} key={refIndex}> | ||
| 216 | <button | ||
| 217 | ref={el => sidebarButtonRefs.current[refIndex] = el} | ||
| 218 | className={getButtonClasses(refIndex)} | ||
| 219 | onClick={() => handle_sidebar_click(refIndex)} | ||
| 220 | > | ||
| 221 | <img src={icon} alt={alt} className={iconClasses} /> | ||
| 222 | {isSidebarOpen && ( | ||
| 223 | <span className="text-white font-[--font-barlow-semicondensed-regular] truncate"> | ||
| 224 | {label} | ||
| 225 | </span> | ||
| 226 | )} | ||
| 227 | </button> | ||
| 228 | </Link> | ||
| 229 | ))} | ||
| 230 | </nav> | ||
| 169 | </div> | 231 | </div> |
| 170 | </Link> | ||
| 171 | <div id="sidebar-list"> | ||
| 172 | {" "} | ||
| 173 | {/* List */} | ||
| 174 | <div id="sidebar-toplist"> | ||
| 175 | {" "} | ||
| 176 | {/* Top */} | ||
| 177 | <button | ||
| 178 | className="sidebar-button" | ||
| 179 | onClick={() => _handle_sidebar_lock()} | ||
| 180 | > | ||
| 181 | <img src={SearchIcon} alt="" /> | ||
| 182 | <span>Search</span> | ||
| 183 | </button> | ||
| 184 | <span></span> | ||
| 185 | <Link to="/" tabIndex={-1}> | ||
| 186 | <button className="sidebar-button"> | ||
| 187 | <img src={HomeIcon} alt="homepage" /> | ||
| 188 | <span>Home Page</span> | ||
| 189 | </button> | ||
| 190 | </Link> | ||
| 191 | <Link to="/games" tabIndex={-1}> | ||
| 192 | <button className="sidebar-button"> | ||
| 193 | <img src={PortalIcon} alt="games" /> | ||
| 194 | <span>Games</span> | ||
| 195 | </button> | ||
| 196 | </Link> | ||
| 197 | <Link to="/rankings" tabIndex={-1}> | ||
| 198 | <button className="sidebar-button"> | ||
| 199 | <img src={FlagIcon} alt="rankings" /> | ||
| 200 | <span>Rankings</span> | ||
| 201 | </button> | ||
| 202 | </Link> | ||
| 203 | {/* <Link to="/news" tabIndex={-1}> | ||
| 204 | <button className='sidebar-button'><img src={NewsIcon} alt="news" /><span>News</span></button> | ||
| 205 | </Link> */} | ||
| 206 | {/* <Link to="/scorelog" tabIndex={-1}> | ||
| 207 | <button className='sidebar-button'><img src={TableIcon} alt="scorelogs" /><span>Score Logs</span></button> | ||
| 208 | </Link> */} | ||
| 209 | </div> | ||
| 210 | <div id="sidebar-bottomlist"> | ||
| 211 | <span></span> | ||
| 212 | 232 | ||
| 213 | {profile && profile.profile ? ( | 233 | {/* Bottom Section */} |
| 234 | <div className="p-4 border-t border-border space-y-2 min-w-0"> | ||
| 235 | {profile && profile.profile && ( | ||
| 214 | <button | 236 | <button |
| 237 | ref={uploadRunRef} | ||
| 215 | id="upload-run" | 238 | id="upload-run" |
| 216 | className="submit-run-button" | 239 | className={getButtonClasses(-1)} |
| 217 | onClick={() => onUploadRun()} | 240 | onClick={() => onUploadRun()} |
| 218 | > | 241 | > |
| 219 | <img src={UploadIcon} alt="upload" /> | 242 | <img src={UploadIcon} alt="Upload" className={iconClasses} /> |
| 220 | <span>Upload Record</span> | 243 | {isSidebarOpen && <span className="font-[--font-barlow-semicondensed-regular] truncate">Upload Record</span>} |
| 221 | </button> | 244 | </button> |
| 222 | ) : ( | ||
| 223 | <span></span> | ||
| 224 | )} | 245 | )} |
| 225 | 246 | ||
| 226 | <Login | 247 | <div className={isSidebarOpen ? 'min-w-0' : 'flex justify-center'}> |
| 227 | setToken={setToken} | 248 | <Login |
| 228 | profile={profile} | 249 | setToken={setToken} |
| 229 | setProfile={setProfile} | 250 | profile={profile} |
| 230 | /> | 251 | setProfile={setProfile} |
| 252 | isOpen={isSidebarOpen} | ||
| 253 | /> | ||
| 254 | </div> | ||
| 231 | 255 | ||
| 232 | <Link to="/rules" tabIndex={-1}> | 256 | <Link to="/rules" tabIndex={-1}> |
| 233 | <button className="sidebar-button"> | 257 | <button |
| 234 | <img src={BookIcon} alt="rules" /> | 258 | ref={el => sidebarButtonRefs.current[5] = el} |
| 235 | <span>Leaderboard Rules</span> | 259 | className={getButtonClasses(5)} |
| 260 | onClick={() => handle_sidebar_click(5)} | ||
| 261 | > | ||
| 262 | <img src={BookIcon} alt="Rules" className={iconClasses} /> | ||
| 263 | {isSidebarOpen && <span className="font-[--font-barlow-semicondensed-regular] truncate">Leaderboard Rules</span>} | ||
| 236 | </button> | 264 | </button> |
| 237 | </Link> | 265 | </Link> |
| 238 | 266 | ||
| 239 | <Link to="/about" tabIndex={-1}> | 267 | <Link to="/about" tabIndex={-1}> |
| 240 | <button className="sidebar-button"> | 268 | <button |
| 241 | <img src={HelpIcon} alt="about" /> | 269 | ref={el => sidebarButtonRefs.current[6] = el} |
| 242 | <span>About LPHUB</span> | 270 | className={getButtonClasses(6)} |
| 271 | onClick={() => handle_sidebar_click(6)} | ||
| 272 | > | ||
| 273 | <img src={HelpIcon} alt="About" className={iconClasses} /> | ||
| 274 | {isSidebarOpen && <span className="font-[--font-barlow-semicondensed-regular] truncate">About LPHUB</span>} | ||
| 243 | </button> | 275 | </button> |
| 244 | </Link> | 276 | </Link> |
| 245 | </div> | 277 | </div> |
| 246 | </div> | 278 | </div> |
| 247 | <div> | ||
| 248 | <input | ||
| 249 | type="text" | ||
| 250 | id="searchbar" | ||
| 251 | placeholder="Search for map or a player..." | ||
| 252 | onChange={e => _handle_search_change(e.target.value)} | ||
| 253 | /> | ||
| 254 | |||
| 255 | <div id="search-data"> | ||
| 256 | {searchData?.maps.map((q, index) => ( | ||
| 257 | <Link to={`/maps/${q.id}`} className="search-map" key={index}> | ||
| 258 | <span>{q.game}</span> | ||
| 259 | <span>{q.chapter}</span> | ||
| 260 | <span>{q.map}</span> | ||
| 261 | </Link> | ||
| 262 | ))} | ||
| 263 | {searchData?.players.map((q, index) => ( | ||
| 264 | <Link | ||
| 265 | to={ | ||
| 266 | profile && q.steam_id === profile.steam_id | ||
| 267 | ? `/profile` | ||
| 268 | : `/users/${q.steam_id}` | ||
| 269 | } | ||
| 270 | className="search-player" | ||
| 271 | key={index} | ||
| 272 | > | ||
| 273 | <img src={q.avatar_link} alt="pfp"></img> | ||
| 274 | <span style={{ fontSize: `${36 - q.user_name.length * 0.8}px` }}> | ||
| 275 | {q.user_name} | ||
| 276 | </span> | ||
| 277 | </Link> | ||
| 278 | ))} | ||
| 279 | </div> | ||
| 280 | </div> | ||
| 281 | </div> | 279 | </div> |
| 282 | ); | 280 | ); |
| 283 | }; | 281 | }; |