From 207a2540101b2f216bde94ae53286d2e52f044e3 Mon Sep 17 00:00:00 2001 From: Wolfboy248 Date: Mon, 25 Nov 2024 09:20:01 +0100 Subject: frontend: begin port to css modules, sidebar refactor --- frontend/src/components/ConfirmDialog.tsx | 5 +- frontend/src/components/Login.tsx | 38 ++-- frontend/src/components/MessageDialog.tsx | 3 +- frontend/src/components/Sidebar.tsx | 321 ++++++++++++---------------- frontend/src/components/Sidebar_old.tsx | 210 ++++++++++++++++++ frontend/src/components/UploadRunDialog.tsx | 9 +- 6 files changed, 377 insertions(+), 209 deletions(-) create mode 100644 frontend/src/components/Sidebar_old.tsx (limited to 'frontend/src/components') diff --git a/frontend/src/components/ConfirmDialog.tsx b/frontend/src/components/ConfirmDialog.tsx index 44a653b..0679c25 100644 --- a/frontend/src/components/ConfirmDialog.tsx +++ b/frontend/src/components/ConfirmDialog.tsx @@ -1,5 +1,6 @@ import React from 'react'; +import btn from "@css/Button.module.css" import "@css/Dialog.css" interface ConfirmDialogProps { @@ -20,8 +21,8 @@ const ConfirmDialog: React.FC = ({ title, subtitle, onConfir {subtitle}
- - + +
diff --git a/frontend/src/components/Login.tsx b/frontend/src/components/Login.tsx index f1628b2..fe0cbd1 100644 --- a/frontend/src/components/Login.tsx +++ b/frontend/src/components/Login.tsx @@ -5,14 +5,20 @@ import { ExitIcon, UserIcon, LoginIcon } from '@images/Images'; import { UserProfile } from '@customTypes/Profile'; import { API } from '@api/Api'; import "@css/Login.css"; +import { Button, Buttons } from "@customTypes/Sidebar"; +import btn from "@css/Button.module.css"; interface LoginProps { + isSearching: boolean; + currentBtn: number; + buttonsList: Buttons; + setCurrentBtn: React.Dispatch>; setToken: React.Dispatch>; profile?: UserProfile; setProfile: React.Dispatch>; }; -const Login: React.FC = ({ setToken, profile, setProfile }) => { +const Login: React.FC = ({ isSearching, currentBtn, buttonsList, setCurrentBtn, setToken, profile, setProfile }) => { const navigate = useNavigate(); @@ -36,13 +42,15 @@ const Login: React.FC = ({ setToken, profile, setProfile }) => { {profile.profile ? ( <> - - - + @@ -50,8 +58,8 @@ const Login: React.FC = ({ setToken, profile, setProfile }) => { : ( <> - - @@ -66,12 +74,12 @@ const Login: React.FC = ({ setToken, profile, setProfile }) => { ) : ( - - )} diff --git a/frontend/src/components/MessageDialog.tsx b/frontend/src/components/MessageDialog.tsx index 5c85189..8c584b7 100644 --- a/frontend/src/components/MessageDialog.tsx +++ b/frontend/src/components/MessageDialog.tsx @@ -1,5 +1,6 @@ import React from 'react'; +import btn from "@css/Button.module.css" import "@css/Dialog.css" interface MessageDialogProps { @@ -19,7 +20,7 @@ const MessageDialog: React.FC = ({ title, subtitle, onClose {subtitle}
- +
diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 67f7f3d..beff4f0 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -1,12 +1,15 @@ -import React from 'react'; +import React 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 sidebar from "@css/Sidebar.module.css"; +import { Button, Buttons } from "@customTypes/Sidebar"; +import btn from "@css/Button.module.css"; +import { abort } from "process"; +import Login from "@components/Login"; import { API } from '@api/Api'; -import "@css/Sidebar.css"; +import inp from "@css/Input.module.css"; +import { Search } from '@customTypes/Search'; interface SidebarProps { setToken: React.Dispatch>; @@ -16,187 +19,131 @@ interface SidebarProps { }; 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(true); - - const location = useLocation(); - const path = location.pathname; - - const handle_sidebar_click = (clicked_sidebar_idx: number) => { - const btn = document.querySelectorAll("button.sidebar-button"); - 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") - }; - - const _handle_sidebar_hide = () => { - 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; - - 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); - } - }; - - 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); - }; - - React.useEffect(() => { - if (path === "/") { handle_sidebar_click(1) } - else if (path.includes("games")) { handle_sidebar_click(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) } - else if (path.includes("rules")) { handle_sidebar_click(5) } - else if (path.includes("about")) { handle_sidebar_click(6) } - }, [path]); - - return ( - - ); -}; + const location = useLocation(); + const [load, setLoad] = React.useState(false); + const [searchData, setSearchData] = React.useState(undefined); + const [hasClickedSearch, setHasClickedSearch] = React.useState(false); + const [isSearching, setIsSearching] = React.useState(false); + const [buttonsList, setButtonsList] = React.useState({ + top: [ + {img: HomeIcon, text: "Home", url: "/"}, + {img: PortalIcon, text: "Games", url: "/games"}, + {img: FlagIcon, text: "Rankings", url: "/rankings"} + ], + bottom: [ + {img: BookIcon, text: "Rules", url: "/rules"}, + {img: HelpIcon, text: "About LPHUB", url: "/about"} + ] + }); + + const _handle_search = () => { + if (!hasClickedSearch) { + _handle_search_change(""); + } + setHasClickedSearch(true); + setIsSearching(!isSearching); + document.querySelector("#searchInput")!.focus(); + } + + const _handle_search_change = async (query: string) => { + const response = await API.get_search(query); + setSearchData(response); + } + + const _get_index_load = () => { + const pathname = window.location.pathname; + const btnObj = buttonsList.top.find(obj => obj.url === pathname); + let btnIndex = buttonsList.top.findIndex(obj => obj.url === pathname); + if (btnIndex != -1) { + return btnIndex; + } else if (buttonsList.top.findIndex(obj => obj.url === pathname) == -1 && buttonsList.bottom.findIndex(obj => obj.url === pathname) != -1) { + btnIndex = buttonsList.bottom.findIndex(obj => obj.url === pathname); + return btnIndex + buttonsList.top.length + 1; + } else if (load) { + return currentBtn; + } else { + return 0; + } + } + const [currentBtn, setCurrentBtn] = React.useState(_get_index_load); + + React.useEffect(() => { + setCurrentBtn(_get_index_load); + setLoad(true); + }, [location]) + + return ( +
+
+ {}} to={"/"}> + +
+ PORTAL 2 + Least Portals Hub +
+ +
+ +
+
+
+ + + + + {buttonsList.top.map((e: any, i: any) => { + return + }) + + } +
+
+ + + {buttonsList.bottom.map((e: any, i: any) => { + return + }) + + } +
+
+ +
+
+ {_handle_search_change(e.target.value)}} id="searchInput" className={inp.sidebar} type="text" placeholder='Search for map or a player...'/> +
+ +
+ {searchData?.maps.map((map, i) => { + return + {map.game} + {map.chapter} + {map.map} + + })} + + {searchData?.players.map((player, i) => { + return + + {player.user_name} + + })} +
+
+ +
+
+ ) +} export default Sidebar; + diff --git a/frontend/src/components/Sidebar_old.tsx b/frontend/src/components/Sidebar_old.tsx new file mode 100644 index 0000000..4d1cd7a --- /dev/null +++ b/frontend/src/components/Sidebar_old.tsx @@ -0,0 +1,210 @@ +import React, { useRef } from 'react'; +import { Link, useLocation } from 'react-router-dom'; + +import btn from "@css/Button.module.css"; +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'; +import "@css/Sidebar.css"; + +interface SidebarProps { + setToken: React.Dispatch>; + profile?: UserProfile; + setProfile: React.Dispatch>; + onUploadRun: () => void; +}; + +const Sidebar: React.FC = ({ setToken, profile, setProfile, onUploadRun }) => { + + const btnRef = useRef(null); + const [searchData, setSearchData] = React.useState(undefined); + const [isSidebarLocked, setIsSidebarLocked] = React.useState(false); + const [isSidebarOpen, setSidebarOpen] = React.useState(true); + + const location = useLocation(); + const path = location.pathname; + + const handle_sidebar_click = (clicked_sidebar_idx: number) => { + const btn = document.querySelectorAll("#sidebarBtn"); + 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") + }; + + const _handle_sidebar_hide = () => { + 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; + + 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); + } + }; + + 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); + }; + + React.useEffect(() => { + if (path === "/") { handle_sidebar_click(1) } + else if (path.includes("games")) { handle_sidebar_click(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) } + else if (path.includes("rules")) { handle_sidebar_click(5) } + else if (path.includes("about")) { handle_sidebar_click(6) } + }, [path]); + + React.useEffect(() => { + const btns = document.querySelectorAll("#sidebarBtn"); + btns.forEach((e, num) => { + e.setAttribute("num", num.toString()); + }); + }) + + return ( + + ); +}; + +export default Sidebar; diff --git a/frontend/src/components/UploadRunDialog.tsx b/frontend/src/components/UploadRunDialog.tsx index 118b589..951944b 100644 --- a/frontend/src/components/UploadRunDialog.tsx +++ b/frontend/src/components/UploadRunDialog.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { UploadRunContent } from '@customTypes/Content'; import { ScoreboardTempUpdate, SourceDemoParser, NetMessages } from '@nekz/sdp'; +import btn from "@css/Button.module.css"; import '@css/UploadRunDialog.css'; import { Game } from '@customTypes/Game'; import { Map } from '@customTypes/Map'; @@ -241,7 +242,7 @@ const UploadRunDialog: React.FC = ({ token, open, onClose, Drag and drop
Or click here
- +
: null} @@ -260,7 +261,7 @@ const UploadRunDialog: React.FC = ({ token, open, onClose, Drag and drop
Or click here
- +
: null} @@ -281,8 +282,8 @@ const UploadRunDialog: React.FC = ({ token, open, onClose, }
- - + +
-- cgit v1.2.3