From 6a8b909afbe1560be95f7ad0a3e19cfe4717aec6 Mon Sep 17 00:00:00 2001 From: FifthWit Date: Thu, 14 Aug 2025 14:01:01 -0500 Subject: Switched to tailwind/vite --- frontend/src/components/ConfirmDialog.tsx | 16 +- frontend/src/components/GameCategory.tsx | 15 +- frontend/src/components/GameEntry.tsx | 27 +- frontend/src/components/Leaderboards.tsx | 6 +- frontend/src/components/Login.tsx | 53 ++-- frontend/src/components/ModMenu.tsx | 1 - frontend/src/components/Sidebar.tsx | 386 ++++++++++++++-------------- frontend/src/components/Summary.tsx | 1 - frontend/src/components/UploadRunDialog.tsx | 1 - 9 files changed, 256 insertions(+), 250 deletions(-) (limited to 'frontend/src/components') diff --git a/frontend/src/components/ConfirmDialog.tsx b/frontend/src/components/ConfirmDialog.tsx index c89d9ea..8f2ce7a 100644 --- a/frontend/src/components/ConfirmDialog.tsx +++ b/frontend/src/components/ConfirmDialog.tsx @@ -1,7 +1,5 @@ import React from "react"; -import "@css/Dialog.css"; - interface ConfirmDialogProps { title: string; subtitle: string; @@ -16,17 +14,17 @@ const ConfirmDialog: React.FC = ({ onCancel, }) => { return ( -
-
-
+
+
+
{title}
-
+
{subtitle}
-
- - +
+ +
diff --git a/frontend/src/components/GameCategory.tsx b/frontend/src/components/GameCategory.tsx index 2bb6d42..b18c9d9 100644 --- a/frontend/src/components/GameCategory.tsx +++ b/frontend/src/components/GameCategory.tsx @@ -2,7 +2,6 @@ import React from "react"; import { Link } from "react-router-dom"; import { Game, GameCategoryPortals } from "@customTypes/Game"; -import "@css/Games.css"; interface GameCategoryProps { game: Game; @@ -12,18 +11,12 @@ interface GameCategoryProps { const GameCategory: React.FC = ({ cat, game }) => { return ( - {cat.category.name} - -
- - {cat.portal_count} - -
+

{cat.category.name}

+
+

{cat.portal_count}

); }; diff --git a/frontend/src/components/GameEntry.tsx b/frontend/src/components/GameEntry.tsx index 04c3483..f8fd179 100644 --- a/frontend/src/components/GameEntry.tsx +++ b/frontend/src/components/GameEntry.tsx @@ -2,7 +2,6 @@ import React from "react"; import { Link } from "react-router-dom"; import { Game, GameCategoryPortals } from "@customTypes/Game"; -import "@css/Games.css"; import GameCategory from "@components/GameCategory"; @@ -18,23 +17,25 @@ const GameEntry: React.FC = ({ game }) => { }, [game.category_portals]); return ( - -
-
+ +
+
- - {game.name} + + {game.name}
-
- {catInfo.map((cat, index) => { - return ( - - ); - })} +
+
+ {catInfo.map((cat, index) => { + return ( + + ); + })} +
diff --git a/frontend/src/components/Leaderboards.tsx b/frontend/src/components/Leaderboards.tsx index b388aba..99481a2 100644 --- a/frontend/src/components/Leaderboards.tsx +++ b/frontend/src/components/Leaderboards.tsx @@ -36,7 +36,7 @@ const Leaderboards: React.FC = ({ mapID }) => { return (

- Map is not available for competitive boards. + Loading...

); @@ -195,6 +195,7 @@ const Leaderboards: React.FC = ({ mapID }) => { filter: "hue-rotate(160deg) contrast(60%) saturate(1000%)", }} + className="w-6 h-6 mx-4" /> @@ -227,7 +229,7 @@ const Leaderboards: React.FC = ({ mapID }) => { (window.location.href = `/api/v1/demos?uuid=${r.demo_id}`) } > - download + download ) diff --git a/frontend/src/components/Login.tsx b/frontend/src/components/Login.tsx index 1858c48..ba85aeb 100644 --- a/frontend/src/components/Login.tsx +++ b/frontend/src/components/Login.tsx @@ -1,18 +1,18 @@ import React from "react"; import { Link, useNavigate } from "react-router-dom"; -import { ExitIcon, UserIcon, LoginIcon } from "@images/Images"; +import { ExitIcon, UserIcon, LoginIcon } from "../images/Images"; import { UserProfile } from "@customTypes/Profile"; import { API } from "@api/Api"; -import "@css/Login.css"; interface LoginProps { setToken: React.Dispatch>; profile?: UserProfile; setProfile: React.Dispatch>; + isOpen: boolean; } -const Login: React.FC = ({ setToken, profile, setProfile }) => { +const Login: React.FC = ({ setToken, profile, setProfile, isOpen }) => { const navigate = useNavigate(); const _login = () => { @@ -32,16 +32,16 @@ const Login: React.FC = ({ setToken, profile, setProfile }) => { <> {profile.profile ? ( <> - - - @@ -49,16 +49,16 @@ const Login: React.FC = ({ setToken, profile, setProfile }) => { ) : ( <> - - - @@ -67,11 +67,28 @@ const Login: React.FC = ({ setToken, profile, setProfile }) => { )} ) : ( - - diff --git a/frontend/src/components/ModMenu.tsx b/frontend/src/components/ModMenu.tsx index 618d1a7..a0d7eb7 100644 --- a/frontend/src/components/ModMenu.tsx +++ b/frontend/src/components/ModMenu.tsx @@ -5,7 +5,6 @@ import { useNavigate } from "react-router-dom"; import { MapSummary } from "@customTypes/Map"; import { ModMenuContent } from "@customTypes/Content"; import { API } from "@api/Api"; -import "@css/ModMenu.css"; import useConfirm from "@hooks/UseConfirm"; interface ModMenuProps { 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 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useRef } from "react"; import { Link, useLocation } from "react-router-dom"; import { @@ -10,12 +10,11 @@ import { PortalIcon, SearchIcon, UploadIcon, -} from "@images/Images"; +} from "../images/Images"; import Login from "@components/Login"; import { UserProfile } from "@customTypes/Profile"; import { Search } from "@customTypes/Search"; import { API } from "@api/Api"; -import "@css/Sidebar.css"; interface SidebarProps { setToken: React.Dispatch>; @@ -24,6 +23,17 @@ interface SidebarProps { onUploadRun: () => void; } +function OpenSidebarIcon(){ + return ( + + ) +} + +function ClosedSidebarIcon(){ + return ( + ) +} + const Sidebar: React.FC = ({ setToken, profile, @@ -34,100 +44,38 @@ const Sidebar: React.FC = ({ undefined ); const [isSidebarLocked, setIsSidebarLocked] = React.useState(false); - const [isSidebarOpen, setSidebarOpen] = React.useState(true); + const [isSidebarOpen, setSidebarOpen] = React.useState(false); + const [selectedButtonIndex, setSelectedButtonIndex] = React.useState(1); const location = useLocation(); const path = location.pathname; - const _handle_sidebar_hide = useCallback(() => { - var btn = document.querySelectorAll( - "button.sidebar-button" - ) as NodeListOf; - const span = document.querySelectorAll( - "button.sidebar-button>span" - ) as NodeListOf; - const side = document.querySelector("#sidebar-list") as HTMLElement; - const searchbar = document.querySelector("#searchbar") as HTMLInputElement; - const uploadRunBtn = document.querySelector( - "#upload-run" - ) as HTMLInputElement; - const uploadRunSpan = document.querySelector( - "#upload-run>span" - ) as HTMLInputElement; + const sidebarRef = useRef(null); + const searchbarRef = useRef(null); + const uploadRunRef = useRef(null); + const sidebarButtonRefs = useRef<(HTMLButtonElement | null)[]>([]); + + const _handle_sidebar_toggle = useCallback(() => { + if (!sidebarRef.current) return; if (isSidebarOpen) { - if (profile) { - const login = document.querySelectorAll( - ".login>button" - )[1] as HTMLElement; - login.style.opacity = "1"; - uploadRunBtn.style.width = "310px"; - uploadRunBtn.style.padding = "0.4em 0 0 11px"; - uploadRunSpan.style.opacity = "0"; - setTimeout(() => { - uploadRunSpan.style.opacity = "1"; - }, 100); - } setSidebarOpen(false); - side.style.width = "320px"; - btn.forEach((e, i) => { - e.style.width = "310px"; - e.style.padding = "0.4em 0 0 11px"; - setTimeout(() => { - span[i].style.opacity = "1"; - }, 100); - }); - side.style.zIndex = "2"; } else { - if (profile) { - const login = document.querySelectorAll( - ".login>button" - )[1] as HTMLElement; - login.style.opacity = "0"; - uploadRunBtn.style.width = "40px"; - uploadRunBtn.style.padding = "0.4em 0 0 5px"; - uploadRunSpan.style.opacity = "0"; - } setSidebarOpen(true); - side.style.width = "40px"; - searchbar.focus(); - btn.forEach((e, i) => { - e.style.width = "40px"; - e.style.padding = "0.4em 0 0 5px"; - span[i].style.opacity = "0"; - }); - setTimeout(() => { - side.style.zIndex = "0"; - }, 300); + searchbarRef.current?.focus(); } - }, [isSidebarOpen, profile]); + }, [isSidebarOpen]); const handle_sidebar_click = useCallback( (clicked_sidebar_idx: number) => { - const btn = document.querySelectorAll("button.sidebar-button"); + setSelectedButtonIndex(clicked_sidebar_idx); if (isSidebarOpen) { setSidebarOpen(false); - _handle_sidebar_hide(); } - // clusterfuck - btn.forEach((e, i) => { - btn[i].classList.remove("sidebar-button-selected"); - btn[i].classList.add("sidebar-button-deselected"); - }); - btn[clicked_sidebar_idx].classList.add("sidebar-button-selected"); - btn[clicked_sidebar_idx].classList.remove("sidebar-button-deselected"); }, - [isSidebarOpen, _handle_sidebar_hide] + [isSidebarOpen] ); - const _handle_sidebar_lock = () => { - if (!isSidebarLocked) { - _handle_sidebar_hide(); - setIsSidebarLocked(true); - setTimeout(() => setIsSidebarLocked(false), 300); - } - }; - const _handle_search_change = async (q: string) => { const searchResponse = await API.get_search(q); setSearchData(searchResponse); @@ -135,149 +83,199 @@ const Sidebar: React.FC = ({ React.useEffect(() => { if (path === "/") { - handle_sidebar_click(1); + setSelectedButtonIndex(1); } else if (path.includes("games")) { - handle_sidebar_click(2); + setSelectedButtonIndex(2); } else if (path.includes("rankings")) { - handle_sidebar_click(3); - } - // else if (path.includes("news")) { handle_sidebar_click(4) } - // else if (path.includes("scorelog")) { handle_sidebar_click(5) } - else if (path.includes("profile")) { - handle_sidebar_click(4); + setSelectedButtonIndex(3); + } else if (path.includes("profile")) { + setSelectedButtonIndex(4); } else if (path.includes("rules")) { - handle_sidebar_click(5); + setSelectedButtonIndex(5); } else if (path.includes("about")) { - handle_sidebar_click(6); + setSelectedButtonIndex(6); } - }, [path, handle_sidebar_click]); + }, [path]); + + const getButtonClasses = (buttonIndex: number) => { + 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"; + const selectedClasses = selectedButtonIndex === buttonIndex ? "bg-primary text-background" : "bg-transparent text-foreground"; + + return `${baseClasses} ${selectedClasses}`; + }; + + const iconClasses = "w-6 h-6 flex-shrink-0"; return ( -