From da1fd74f9387149b2b94d62853587a8afdb74ddd Mon Sep 17 00:00:00 2001 From: Wolfboy248 Date: Thu, 21 Aug 2025 10:33:27 +0200 Subject: Reorganised Maplist and Sidebar --- frontend/src/components/Sidebar.tsx | 288 --------------------- frontend/src/components/Sidebar/Content.tsx | 133 ++++++++++ frontend/src/components/Sidebar/Footer.tsx | 80 ++++++ frontend/src/components/Sidebar/Header.tsx | 26 ++ frontend/src/components/Sidebar/Sidebar.module.css | 23 ++ frontend/src/components/Sidebar/Sidebar.tsx | 101 ++++++++ 6 files changed, 363 insertions(+), 288 deletions(-) delete mode 100644 frontend/src/components/Sidebar.tsx create mode 100644 frontend/src/components/Sidebar/Content.tsx create mode 100644 frontend/src/components/Sidebar/Footer.tsx create mode 100644 frontend/src/components/Sidebar/Header.tsx create mode 100644 frontend/src/components/Sidebar/Sidebar.module.css create mode 100644 frontend/src/components/Sidebar/Sidebar.tsx (limited to 'frontend/src/components') diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx deleted file mode 100644 index 0083a3e..0000000 --- a/frontend/src/components/Sidebar.tsx +++ /dev/null @@ -1,288 +0,0 @@ -import React, { useCallback, useRef } from "react"; -import { Link, useLocation } from "react-router-dom"; - -import { - BookIcon, - FlagIcon, - HelpIcon, - HomeIcon, - LogoIcon, - PortalIcon, - SearchIcon, - UploadIcon, -} from "../images/Images"; -import Login from "@components/Login"; -import { UserProfile } from "@customTypes/Profile"; -import { Search } from "@customTypes/Search"; -import { API } from "@api/Api"; - -interface SidebarProps { - setToken: React.Dispatch>; - profile?: UserProfile; - setProfile: React.Dispatch>; - onUploadRun: () => void; -} - -function OpenSidebarIcon() { - return ( - - ) -} - -function ClosedSidebarIcon() { - return ( - ) -} - -const Sidebar: React.FC = ({ - setToken, - profile, - setProfile, - onUploadRun, -}) => { - const [searchData, setSearchData] = React.useState( - undefined - ); - // const [isSidebarLocked, setIsSidebarLocked] = React.useState(false); - const [isSidebarOpen, setSidebarOpen] = React.useState(false); - const [selectedButtonIndex, setSelectedButtonIndex] = React.useState(1); - - const location = useLocation(); - const path = location.pathname; - - 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) { - setSidebarOpen(false); - } else { - setSidebarOpen(true); - searchbarRef.current?.focus(); - } - }, [isSidebarOpen]); - - const handle_sidebar_click = useCallback( - (clicked_sidebar_idx: number) => { - setSelectedButtonIndex(clicked_sidebar_idx); - if (isSidebarOpen) { - setSidebarOpen(false); - } - }, - [isSidebarOpen] - ); - - const _handle_search_change = async (q: string) => { - const searchResponse = await API.get_search(q); - setSearchData(searchResponse); - }; - - React.useEffect(() => { - if (path === "/") { - setSelectedButtonIndex(1); - } else if (path.includes("games")) { - setSelectedButtonIndex(2); - } else if (path.includes("rankings")) { - setSelectedButtonIndex(3); - } else if (path.includes("profile")) { - setSelectedButtonIndex(4); - } else if (path.includes("rules")) { - setSelectedButtonIndex(5); - } else if (path.includes("about")) { - setSelectedButtonIndex(6); - } - }, [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 ( -
-
- - Logo - {isSidebarOpen && ( -
-
- PORTAL 2 -
-
- Least Portals Hub -
-
- )} - - - -
- - {/* Sidebar Content */} -
- {isSidebarOpen && ( -
-
- Search - Search -
- -
- _handle_search_change(e.target.value)} - className="w-full p-2 bg-input text-foreground border border-border rounded-lg text-sm min-w-0" - /> - - {searchData && ( -
- {searchData?.maps.map((q, index) => ( - - {q.game} - {q.chapter} - {q.map} - - ))} - {searchData?.players.map((q, index) => ( - - pfp - - {q.user_name} - - - ))} -
- )} -
-
- )} - -
- -
- - {/* Bottom Section */} -
- {profile && profile.profile && ( - - )} - -
- -
- - - - - - - - -
-
-
- ); -}; - -export default Sidebar; diff --git a/frontend/src/components/Sidebar/Content.tsx b/frontend/src/components/Sidebar/Content.tsx new file mode 100644 index 0000000..4051b08 --- /dev/null +++ b/frontend/src/components/Sidebar/Content.tsx @@ -0,0 +1,133 @@ +import React, { useRef } from "react"; +import { Link } from "react-router-dom"; +import { UserProfile } from "@customTypes/Profile"; +import { Search } from "@customTypes/Search"; +import { API } from "@api/Api"; + +import styles from "./Sidebar.module.css"; + +import { + FlagIcon, + HomeIcon, + PortalIcon, + SearchIcon, +} from "../../images/Images"; + +interface ContentProps { + profile?: UserProfile; + isSidebarOpen: boolean; + sidebarButtonRefs: React.RefObject<(HTMLButtonElement | null)[]>; + getButtonClasses: (buttonIndex: number) => string; + handle_sidebar_click: (clicked_sidebar_idx: number) => void; +}; + +const Content: React.FC = ({ profile, isSidebarOpen, sidebarButtonRefs, getButtonClasses, handle_sidebar_click }) => { + const [searchData, setSearchData] = React.useState( + undefined + ); + + const searchbarRef = useRef(null); + + const _handle_search_change = async (q: string) => { + const searchResponse = await API.get_search(q); + setSearchData(searchResponse); + }; + + const iconClasses = ""; + + return ( +
+ +
+
+ Search + Search +
+ +
+ _handle_search_change(e.target.value)} + className="w-full p-2 bg-input text-foreground border border-border rounded-lg text-sm min-w-0" + /> + + {searchData && ( +
+ {searchData?.maps.map((q, index) => ( + + {q.game} + {q.chapter} + {q.map} + + ))} + {searchData?.players.map((q, index) => ( + + pfp + + {q.user_name} + + + ))} +
+ )} +
+
+ +
+ +
+
+ ); +} + +export default Content; diff --git a/frontend/src/components/Sidebar/Footer.tsx b/frontend/src/components/Sidebar/Footer.tsx new file mode 100644 index 0000000..070301a --- /dev/null +++ b/frontend/src/components/Sidebar/Footer.tsx @@ -0,0 +1,80 @@ +import React, { useRef } from "react"; +import { Link } from "react-router-dom"; + +import styles from "./Sidebar.module.css"; + +import { UserProfile } from "@customTypes/Profile"; +import Login from "@components/Login"; + +import { + UploadIcon, + BookIcon, + HelpIcon, +} from "../../images/Images"; + +interface FooterProps { + profile?: UserProfile; + onUploadRun: () => void; + setProfile: React.Dispatch>; + setToken: React.Dispatch>; + sidebarButtonRefs: React.RefObject<(HTMLButtonElement | null)[]>; + getButtonClasses: (buttonIndex: number) => string; + handle_sidebar_click: (clicked_sidebar_idx: number) => void; +}; + +const Footer: React.FC = ({ profile, onUploadRun, setToken, setProfile, sidebarButtonRefs, getButtonClasses, handle_sidebar_click }) => { + const uploadRunRef = useRef(null); + + return ( +
+ {profile && profile.profile && ( + + )} + +
+ +
+ + + + + + + + +
+ ); +} + +export default Footer; diff --git a/frontend/src/components/Sidebar/Header.tsx b/frontend/src/components/Sidebar/Header.tsx new file mode 100644 index 0000000..e990060 --- /dev/null +++ b/frontend/src/components/Sidebar/Header.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import { Link } from "react-router-dom"; + +import { + LogoIcon, +} from "../../images/Images"; + +const Header: React.FC = () => { + return ( +
+ + Logo +
+
+ PORTAL 2 +
+
+ Least Portals Hub +
+
+ +
+ ) +} + +export default Header; diff --git a/frontend/src/components/Sidebar/Sidebar.module.css b/frontend/src/components/Sidebar/Sidebar.module.css new file mode 100644 index 0000000..8079676 --- /dev/null +++ b/frontend/src/components/Sidebar/Sidebar.module.css @@ -0,0 +1,23 @@ +.button { + display: flex; + width: 100%; + cursor: pointer; + align-items: center; + border-radius: 2000px; + transition: all 0.2s ease; + padding: 4px 0px; +} + +.button:hover { + background-color: var(--color-panel); +} + +.button>img { + padding: 0px 8px; + height: 28px; +} + +button>span { + font-size: 22px; + font-family: var(--font-barlow-semicondensed-regular); +} \ No newline at end of file diff --git a/frontend/src/components/Sidebar/Sidebar.tsx b/frontend/src/components/Sidebar/Sidebar.tsx new file mode 100644 index 0000000..2dafa2b --- /dev/null +++ b/frontend/src/components/Sidebar/Sidebar.tsx @@ -0,0 +1,101 @@ +import React, { useCallback, useRef } from "react"; +import { Link, useLocation } from "react-router-dom"; +import { UserProfile } from "@customTypes/Profile"; + +import Header from "./Header"; +import Footer from "./Footer"; +import Content from "./Content"; + +interface SidebarProps { + setToken: React.Dispatch>; + profile?: UserProfile; + setProfile: React.Dispatch>; + onUploadRun: () => void; +} + +const Sidebar: React.FC = ({ + setToken, + profile, + setProfile, + onUploadRun, +}) => { + // const [isSidebarLocked, setIsSidebarLocked] = React.useState(false); + const [isSidebarOpen, setSidebarOpen] = React.useState(false); + const [selectedButtonIndex, setSelectedButtonIndex] = React.useState(1); + + const location = useLocation(); + const path = location.pathname; + + const sidebarRef = useRef(null); + const sidebarButtonRefs = useRef<(HTMLButtonElement | null)[]>([]); + + // const _handle_sidebar_toggle = useCallback(() => { + // if (!sidebarRef.current) return; + + // if (isSidebarOpen) { + // setSidebarOpen(false); + // } else { + // setSidebarOpen(true); + // searchbarRef.current?.focus(); + // } + // }, [isSidebarOpen]); + + const handle_sidebar_click = useCallback( + (clicked_sidebar_idx: number) => { + setSelectedButtonIndex(clicked_sidebar_idx); + if (isSidebarOpen) { + setSidebarOpen(false); + } + }, + [isSidebarOpen] + ); + + React.useEffect(() => { + if (path === "/") { + setSelectedButtonIndex(1); + } else if (path.includes("games")) { + setSelectedButtonIndex(2); + } else if (path.includes("rankings")) { + setSelectedButtonIndex(3); + } else if (path.includes("profile")) { + setSelectedButtonIndex(4); + } else if (path.includes("rules")) { + setSelectedButtonIndex(5); + } else if (path.includes("about")) { + setSelectedButtonIndex(6); + } + }, [path]); + + const getButtonClasses = (buttonIndex: number) => { + const baseClasses = "flex items-center gap-3 w-full text-left bg-inherit cursor-pointer border-none rounded-[2000px] py-3 px-3 transition-all duration-300 hover:bg-panel"; + const selectedClasses = selectedButtonIndex === buttonIndex ? "bg-primary text-background" : "bg-transparent text-foreground"; + + return `${baseClasses} ${selectedClasses}`; + }; + + return ( +
+ + {/* Header */} +
+ +
+
+ {/* Sidebar Content */} + + + {/* Bottom Section */} +
+
+ +
+ +
+ +
+
+ ); +}; + +export default Sidebar; -- cgit v1.2.3