diff --git a/frontend/src/App.css b/frontend/src/App.css index 58c508138..a3256c4ae 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -40,9 +40,6 @@ border: 1px solid transparent; color: var(--main-button-inactive-text-colour); padding: 0; - - height: 100%; - display: flex; align-items: center; justify-content: center; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 63687d28b..3eaa2d981 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -5,9 +5,9 @@ import MainBody from "./components/MainComponent/MainBody"; import { useEffect, useState } from "react"; import { LEVEL_NAMES } from "./models/level"; import { - getChatHistory, - clearChat, addMessageToChatHistory, + clearChat, + getChatHistory, } from "./service/chatService"; import { EmailInfo } from "./models/email"; import { clearEmails, getSentEmails } from "./service/emailService"; @@ -32,17 +32,12 @@ function App({ isNewUser }: { isNewUser: boolean }) { ); const [numCompletedLevels, setNumCompletedLevels] = useState(0); - const [showOverlay, setShowOverlay] = useState(isNewUser); - const [defencesToShow, setDefencesToShow] = useState(DEFENCE_DETAILS_ALL); const [emails, setEmails] = useState([]); const [messages, setMessages] = useState([]); - - const [overlayType, setOverlayType] = useState( - OVERLAY_TYPE.WELCOME - ); + const [overlayType, setOverlayType] = useState(null); function loadCurrentLevel() { // get current level from local storage @@ -66,6 +61,9 @@ function App({ isNewUser }: { isNewUser: boolean }) { console.error(err); }); void setNewLevel(currentLevel); + if (isNewUser) { + setOverlayType(OVERLAY_TYPE.WELCOME); + } }, []); useEffect(() => { @@ -78,18 +76,16 @@ function App({ isNewUser }: { isNewUser: boolean }) { if (overlayType === OVERLAY_TYPE.WELCOME) { openInformationOverlay(); } else { - setShowOverlay(false); + setOverlayType(null); } } function openHandbook() { setOverlayType(OVERLAY_TYPE.HANDBOOK); - setShowOverlay(true); } function openInformationOverlay() { setOverlayType(OVERLAY_TYPE.INFORMATION); - setShowOverlay(true); } // methods to modify messages @@ -228,7 +224,7 @@ function App({ isNewUser }: { isNewUser: boolean }) { return (
- {showOverlay && ( + {overlayType !== null && ( } + className="export-chat-link" fileName={getFileName()} > Export - {" "} +
); } diff --git a/frontend/src/components/HandbookOverlay/HandbookAttacks.css b/frontend/src/components/HandbookOverlay/HandbookAttacks.css index 4bdfd51bb..ddd4d41fe 100644 --- a/frontend/src/components/HandbookOverlay/HandbookAttacks.css +++ b/frontend/src/components/HandbookOverlay/HandbookAttacks.css @@ -1,12 +1,13 @@ -#handbook-attacks { +.handbook-attacks { display: grid; + gap: 1rem 0.5rem; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); } -.handbook-attack { +.handbook-attacks .attack { background-color: var(--overlay-attack-background-colour); - border-radius: 10px; - padding: 0 10px; - margin: 5px; - font-size: 0.8rem; + margin: 0 0.25rem; + border-radius: 0.5rem; + padding: 0 0.625rem; + font-size: 0.875rem; } diff --git a/frontend/src/components/HandbookOverlay/HandbookAttacks.tsx b/frontend/src/components/HandbookOverlay/HandbookAttacks.tsx index 5a3b176fa..8f947aae6 100644 --- a/frontend/src/components/HandbookOverlay/HandbookAttacks.tsx +++ b/frontend/src/components/HandbookOverlay/HandbookAttacks.tsx @@ -4,31 +4,17 @@ import { LEVEL_NAMES } from "../../models/level"; import "./HandbookAttacks.css"; function HandbookAttacks({ currentLevel }: { currentLevel: LEVEL_NAMES }) { - function getAttacks(): AttackInfo[] { - // what attacks to show depends on the level - switch (currentLevel) { - case LEVEL_NAMES.LEVEL_1: - // fallthrough - case LEVEL_NAMES.LEVEL_2: - return ATTACKS_LEVEL_2; - default: - return ATTACKS_ALL; - } - } + const attacks: AttackInfo[] = + currentLevel === LEVEL_NAMES.LEVEL_2 ? ATTACKS_LEVEL_2 : ATTACKS_ALL; return ( -
-

Attacks

-
- {getAttacks().map((attack) => { - return ( - -

{attack.name}

-

{attack.info}

-
- ); - })} -
+
+ {attacks.map((attack) => ( + +

{attack.name}

+

{attack.info}

+
+ ))}
); } diff --git a/frontend/src/components/HandbookOverlay/HandbookOverlay.css b/frontend/src/components/HandbookOverlay/HandbookOverlay.css index b24c5be30..93c16c35e 100644 --- a/frontend/src/components/HandbookOverlay/HandbookOverlay.css +++ b/frontend/src/components/HandbookOverlay/HandbookOverlay.css @@ -1,6 +1,12 @@ -#handbook-overlay-content { +.handbook-overlay { + display: flex; + flex-direction: column; + gap: 1rem; height: 100%; +} +.handbook-overlay .content { + flex: 0 1 auto; text-align: center; overflow-y: auto; } diff --git a/frontend/src/components/HandbookOverlay/HandbookOverlay.tsx b/frontend/src/components/HandbookOverlay/HandbookOverlay.tsx index 19c656ee6..3a6c9c4a8 100644 --- a/frontend/src/components/HandbookOverlay/HandbookOverlay.tsx +++ b/frontend/src/components/HandbookOverlay/HandbookOverlay.tsx @@ -3,7 +3,7 @@ import { LEVEL_NAMES } from "../../models/level"; import MissionInformation from "../Overlay/MissionInformation"; import HandbookAttacks from "./HandbookAttacks"; import HandbookOverlayTabs from "./HandbookOverlayTabs"; -import { HANDBOOK_PAGES } from "../../models/handbook"; +import { HANDBOOK_PAGES, handbookPageNames } from "../../models/handbook"; import "./HandbookOverlay.css"; function HandbookOverlay({ currentLevel }: { currentLevel: LEVEL_NAMES }) { @@ -11,30 +11,26 @@ function HandbookOverlay({ currentLevel }: { currentLevel: LEVEL_NAMES }) { HANDBOOK_PAGES.MISSION_INFO ); - function setPageContent(handbookPage: HANDBOOK_PAGES) { - switch (handbookPage) { - case HANDBOOK_PAGES.ATTACKS: - return ; - case HANDBOOK_PAGES.TOOLS: - return ( -
-

Placeholder

-
- ); - case HANDBOOK_PAGES.MISSION_INFO: - default: - return ; - } - } + const pageContent = { + [HANDBOOK_PAGES.MISSION_INFO]: ( + + ), + [HANDBOOK_PAGES.ATTACKS]: , + }[selectedPage]; return (
-
- {setPageContent(selectedPage)} +
+ {pageContent}
); diff --git a/frontend/src/components/HandbookOverlay/HandbookOverlayTabs.css b/frontend/src/components/HandbookOverlay/HandbookOverlayTabs.css index 348b0a203..56f2c14ae 100644 --- a/frontend/src/components/HandbookOverlay/HandbookOverlayTabs.css +++ b/frontend/src/components/HandbookOverlay/HandbookOverlayTabs.css @@ -1,57 +1,23 @@ .handbook-tabs { display: flex; - flex-wrap: wrap; - width: 100%; - position: absolute; - top: -50px; /* position above overlay */ - left: 2rem; - z-index: -1; + gap: 0.5rem; } -.handbook-tabs label { +.handbook-tabs button { display: flex; align-items: center; justify-content: center; + border: 1px solid var(--overlay-tab-colour); + border-radius: 0.5rem; padding: 1rem 2rem; - margin-right: 0.0625rem; cursor: pointer; - background-color: var(--overlay-closed-tab-colour); + background-color: var(--overlay-background-colour); + font-size: 1rem; font-weight: 700; + line-height: 1; transition: ease 0.1s; - border-radius: 10px 10px 0 0; -} - -.handbook-tabs label:not(:first-child):not(:last-child) { - border-right: 2px solid #000; } -.handbook-tabs [type="radio"] { - display: none; -} - -.handbook-tabs [type="radio"]:checked + label { - background-color: var(--overlay-background-colour); -} - -.handbook-tabs [type="radio"]:checked + label + .tab { - display: block; -} - -@media (min-width: 768px) { - body { - font-size: 1.125rem; - } - - .handbook-tabs-container { - padding: 4rem 4rem; - } - - .handbook-tabs label { - order: 1; - width: auto; - } - - .handbook-tabs .tab { - order: 9; - } +.handbook-tabs button[aria-selected="true"] { + background-color: var(--overlay-tab-colour); } diff --git a/frontend/src/components/HandbookOverlay/HandbookOverlayTabs.tsx b/frontend/src/components/HandbookOverlay/HandbookOverlayTabs.tsx index 5a1cc416b..f06897d07 100644 --- a/frontend/src/components/HandbookOverlay/HandbookOverlayTabs.tsx +++ b/frontend/src/components/HandbookOverlay/HandbookOverlayTabs.tsx @@ -4,40 +4,32 @@ import "./HandbookOverlayTabs.css"; function HandbookOverlayTabs({ currentLevel, - setSelectedPage, + currentPage, + selectPage, }: { currentLevel: LEVEL_NAMES; - setSelectedPage: (page: HANDBOOK_PAGES) => void; + currentPage: HANDBOOK_PAGES; + selectPage: (page: HANDBOOK_PAGES) => void; }) { // the tabs that are shown depend on the current level - function getLevelTabs(currentLevel: LEVEL_NAMES) { - switch (currentLevel) { - case LEVEL_NAMES.LEVEL_1: - return [HANDBOOK_PAGES.MISSION_INFO]; - default: - return [ - HANDBOOK_PAGES.MISSION_INFO, - HANDBOOK_PAGES.ATTACKS, - HANDBOOK_PAGES.TOOLS, - ]; - } - } + const tabs = + currentLevel === LEVEL_NAMES.LEVEL_1 + ? [HANDBOOK_PAGES.MISSION_INFO] + : [HANDBOOK_PAGES.MISSION_INFO, HANDBOOK_PAGES.ATTACKS]; return ( -
- {getLevelTabs(currentLevel).map((tab) => ( -
- { - setSelectedPage(tab); - }} - /> - -
+
+ {tabs.map((page) => ( + ))}
); diff --git a/frontend/src/components/Overlay/MissionInformation.css b/frontend/src/components/Overlay/MissionInformation.css index e44107ed3..4795752c2 100644 --- a/frontend/src/components/Overlay/MissionInformation.css +++ b/frontend/src/components/Overlay/MissionInformation.css @@ -1,3 +1,3 @@ -#mission-info p { +.mission-info p { font-size: 1.5rem; } diff --git a/frontend/src/components/Overlay/MissionInformation.tsx b/frontend/src/components/Overlay/MissionInformation.tsx index b38709c6e..cc60e44bd 100644 --- a/frontend/src/components/Overlay/MissionInformation.tsx +++ b/frontend/src/components/Overlay/MissionInformation.tsx @@ -7,7 +7,7 @@ function MissionInformation({ currentLevel }: { currentLevel: LEVEL_NAMES }) { return (

Mission Information

-
+

{LEVELS[currentLevel].missionInfo}

diff --git a/frontend/src/components/Overlay/Overlay.css b/frontend/src/components/Overlay/Overlay.css index 1363769ff..bfb3d1c04 100644 --- a/frontend/src/components/Overlay/Overlay.css +++ b/frontend/src/components/Overlay/Overlay.css @@ -1,47 +1,28 @@ -.overlay-screen { - background-color: var(--overlay-hidden-colour); - - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 100; - - display: flex; - justify-content: center; - align-items: center; -} - .overlay { + box-sizing: border-box; position: relative; - + border: none; + border-radius: 0.625rem; + padding: 0; background-color: var(--overlay-background-colour); color: var(--overlay-text-colour); - - display: flex; - justify-content: center; - align-items: center; - overflow: visible; - width: 50%; height: 50%; - border-radius: 10px; + overflow: visible; +} +.overlay * { + box-sizing: border-box; } -.overlay .handbook-overlay { - border-radius: 0; +.overlay::backdrop { + background-color: var(--overlay-hidden-colour); } .overlay > .close-button { position: absolute; - right: 16px; - top: 16px; - max-width: 2rem; - max-height: 2rem; - min-height: 1.6rem; - padding: 5px; - + top: 1.5rem; + right: 1.5rem; + padding: 0.125rem 0.5rem; color: inherit; font-size: 1.5rem; font-weight: bold; @@ -55,8 +36,7 @@ .overlay-content { height: 100%; - padding: 0 5%; - + padding: 1.5rem; + overflow: hidden; text-align: center; - overflow-y: auto; } diff --git a/frontend/src/components/Overlay/Overlay.tsx b/frontend/src/components/Overlay/Overlay.tsx index 37f64770e..f8cda3417 100644 --- a/frontend/src/components/Overlay/Overlay.tsx +++ b/frontend/src/components/Overlay/Overlay.tsx @@ -1,9 +1,11 @@ +import { useCallback, useEffect, useRef } from "react"; import { LEVEL_NAMES } from "../../models/level"; import { OVERLAY_TYPE } from "../../models/overlay"; -import "./Overlay.css"; -import HandbookWelcome from "./OverlayWelcome"; import HandbookOverlay from "../HandbookOverlay/HandbookOverlay"; -import MissionInformation from "../Overlay/MissionInformation"; +import MissionInformation from "./MissionInformation"; +import HandbookWelcome from "./OverlayWelcome"; + +import "./Overlay.css"; function Overlay({ currentLevel, @@ -14,31 +16,68 @@ function Overlay({ overlayType: OVERLAY_TYPE; closeOverlay: () => void; }) { - function showOverlayByType() { - switch (overlayType) { - case OVERLAY_TYPE.HANDBOOK: - return ; - case OVERLAY_TYPE.INFORMATION: - return ; - case OVERLAY_TYPE.WELCOME: - default: - return ; - } - } + const dialogRef = useRef(null); + const contentRef = useRef(null); + + const handleOverlayClick = useCallback( + (event: MouseEvent) => { + contentRef.current && + !event.composedPath().includes(contentRef.current) && + closeOverlay(); + }, + [closeOverlay, contentRef] + ); + + const handleEscape = useCallback( + (event: KeyboardEvent) => { + event.code === "Escape" && closeOverlay(); + }, + [closeOverlay] + ); + + useEffect(() => { + dialogRef.current?.showModal(); + return () => { + dialogRef.current?.close(); + }; + }, []); + + useEffect(() => { + window.addEventListener("keydown", handleEscape); + setTimeout(() => { + // Need timeout, else dialog consumes same click that + // opened it and closes immediately! + window.addEventListener("click", handleOverlayClick); + }); + + return () => { + window.removeEventListener("keydown", handleEscape); + window.removeEventListener("click", handleOverlayClick); + }; + }, [overlayType]); + + const overlayContent = + overlayType === OVERLAY_TYPE.HANDBOOK ? ( + + ) : overlayType === OVERLAY_TYPE.INFORMATION ? ( + + ) : ( + + ); return ( -
-
- -
{showOverlayByType()}
+ + +
+ {overlayContent}
-
+ ); } diff --git a/frontend/src/components/Overlay/OverlayWelcome.css b/frontend/src/components/Overlay/OverlayWelcome.css index f734ca3e3..4d45fa7cb 100644 --- a/frontend/src/components/Overlay/OverlayWelcome.css +++ b/frontend/src/components/Overlay/OverlayWelcome.css @@ -1,13 +1,13 @@ -#welcome { +.welcome { padding: 0 5%; } -#welcome h1 { +.welcome h1 { font-weight: 700; font-size: 3rem; } -#welcome p { +.welcome p { font-weight: 500; font-size: 2rem; } diff --git a/frontend/src/components/Overlay/OverlayWelcome.tsx b/frontend/src/components/Overlay/OverlayWelcome.tsx index 2c2d89033..2172bca1e 100644 --- a/frontend/src/components/Overlay/OverlayWelcome.tsx +++ b/frontend/src/components/Overlay/OverlayWelcome.tsx @@ -2,7 +2,7 @@ import "./OverlayWelcome.css"; function OverlayWelcome() { return ( -
+

Welcome!

Your mission, should you choose to accept it, is to go undercover and diff --git a/frontend/src/models/handbook.ts b/frontend/src/models/handbook.ts index da0dc0ad7..8faad9b36 100644 --- a/frontend/src/models/handbook.ts +++ b/frontend/src/models/handbook.ts @@ -1,13 +1,11 @@ enum HANDBOOK_PAGES { MISSION_INFO, ATTACKS, - TOOLS, } const handbookPageNames: { [key in HANDBOOK_PAGES]: string } = { - [HANDBOOK_PAGES.MISSION_INFO]: "Mission Info", + [HANDBOOK_PAGES.MISSION_INFO]: "Mission", [HANDBOOK_PAGES.ATTACKS]: "Attacks", - [HANDBOOK_PAGES.TOOLS]: "Tools", }; export { HANDBOOK_PAGES, handbookPageNames };