From 7353d785fbe6bdad3d6dc121d4788cd1d2a0654f Mon Sep 17 00:00:00 2001 From: Ryan <7389646+ryanbarlow97@users.noreply.github.com> Date: Tue, 13 Jan 2026 05:03:02 +0000 Subject: [PATCH] Created ModalHeader and moved/unified all modal headers (#2882) If this PR fixes an issue, link it below. If not, delete these two lines. Resolves #(issue number) ## Description: Moved the Modal Headers into its own class ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: w.o.n --- src/client/AccountModal.ts | 41 +---- src/client/FlagInputModal.ts | 59 +++---- src/client/HelpModal.ts | 37 +---- src/client/HostLobbyModal.ts | 208 +++++++++++------------- src/client/JoinPrivateLobbyModal.ts | 208 +++++++++++------------- src/client/LanguageModal.ts | 48 +----- src/client/Matchmaking.ts | 37 +---- src/client/NewsModal.ts | 39 +---- src/client/SinglePlayerModal.ts | 40 +---- src/client/StatsModal.ts | 40 ++--- src/client/TerritoryPatternsModal.ts | 71 +++----- src/client/UserSettingModal.ts | 42 ++--- src/client/components/ui/ModalHeader.ts | 80 +++++++++ 13 files changed, 368 insertions(+), 582 deletions(-) create mode 100644 src/client/components/ui/ModalHeader.ts diff --git a/src/client/AccountModal.ts b/src/client/AccountModal.ts index 2e9781cce..06fe524cf 100644 --- a/src/client/AccountModal.ts +++ b/src/client/AccountModal.ts @@ -14,6 +14,7 @@ import "./components/baseComponents/stats/PlayerStatsTree"; import { BaseModal } from "./components/BaseModal"; import "./components/Difficulties"; import "./components/PatternButton"; +import { modalHeader } from "./components/ui/ModalHeader"; import { copyToClipboard, translateText } from "./Utils"; @customElement("account-modal") @@ -109,37 +110,11 @@ export class AccountModal extends BaseModal {
-
-
- - - ${title} - -
- ${isLoggedIn + ${modalHeader({ + title, + onBack: () => this.close(), + ariaLabel: translateText("common.back"), + rightContent: isLoggedIn ? html`
` - : ""} -
+ : undefined, + })}
${isLoggedIn ? this.renderAccountInfo() : this.renderLoginOptions()} diff --git a/src/client/FlagInputModal.ts b/src/client/FlagInputModal.ts index a4c6ad10f..fe2ea7c6b 100644 --- a/src/client/FlagInputModal.ts +++ b/src/client/FlagInputModal.ts @@ -3,6 +3,7 @@ import { customElement, query, state } from "lit/decorators.js"; import Countries from "resources/countries.json" with { type: "json" }; import { translateText } from "./Utils"; import { BaseModal } from "./components/BaseModal"; +import { modalHeader } from "./components/ui/ModalHeader"; @customElement("flag-input-modal") export class FlagInputModal extends BaseModal { @@ -21,53 +22,31 @@ export class FlagInputModal extends BaseModal { class="h-full flex flex-col bg-black/60 backdrop-blur-md rounded-2xl border border-white/10 overflow-hidden" >
-
- - - ${translateText("flag_input.title")} - -
-
+ ${modalHeader({ + title: translateText("flag_input.title"), + onBack: () => this.close(), + ariaLabel: translateText("common.back"), + })} -
- +
+ +
-
+
${Countries.filter( (country) => !country.restricted && this.includedInSearch(country), diff --git a/src/client/HelpModal.ts b/src/client/HelpModal.ts index d4193e040..a0adeaf2a 100644 --- a/src/client/HelpModal.ts +++ b/src/client/HelpModal.ts @@ -4,6 +4,7 @@ import { translateText } from "../client/Utils"; import { BaseModal } from "./components/BaseModal"; import "./components/Difficulties"; import "./components/Maps"; +import { modalHeader } from "./components/ui/ModalHeader"; @customElement("help-modal") export class HelpModal extends BaseModal { @@ -101,37 +102,11 @@ export class HelpModal extends BaseModal { ? "bg-black/60 backdrop-blur-md rounded-2xl border border-white/10" : ""}" > -
-
- - - ${translateText("main.instructions")} - -
-
+ ${modalHeader({ + title: translateText("main.instructions"), + onBack: this.close, + ariaLabel: translateText("common.back"), + })}
-
-
- - - ${translateText("host_modal.title")} - -
- - -
- - - + -
-
+ ${this.copySuccess + ? translateText("common.copied") + : this.lobbyIdVisible + ? this.lobbyId + : "••••••••"} + + +
+ `, + })}
diff --git a/src/client/JoinPrivateLobbyModal.ts b/src/client/JoinPrivateLobbyModal.ts index 30c5875e1..833727bcf 100644 --- a/src/client/JoinPrivateLobbyModal.ts +++ b/src/client/JoinPrivateLobbyModal.ts @@ -16,6 +16,7 @@ import { JoinLobbyEvent } from "./Main"; import { BaseModal } from "./components/BaseModal"; import "./components/Difficulties"; import "./components/LobbyTeamView"; +import { modalHeader } from "./components/ui/ModalHeader"; @customElement("join-private-lobby-modal") export class JoinPrivateLobbyModal extends BaseModal { @query("#lobbyIdInput") private lobbyIdInput!: HTMLInputElement; @@ -40,125 +41,100 @@ export class JoinPrivateLobbyModal extends BaseModal {
-
-
- - - ${translateText("private_lobby.title")} - -
- - - ${this.hasJoined - ? html`
- + ${modalHeader({ + title: translateText("private_lobby.title"), + onBack: this.closeAndLeave, + ariaLabel: translateText("common.close"), + rightContent: this.hasJoined + ? html` +
{ - (e.currentTarget as HTMLElement).classList.add( - "select-all", - ); - }} - @mouseleave=${(e: Event) => { - (e.currentTarget as HTMLElement).classList.remove( - "select-all", - ); - }} - class="font-mono text-xs font-bold text-white px-2 cursor-pointer select-none min-w-[80px] text-center truncate tracking-wider" - title="${translateText("common.click_to_copy")}" + class="flex items-center gap-0.5 bg-white/5 rounded-lg px-2 py-1 border border-white/10 max-w-[220px] flex-nowrap" > - ${this.copySuccess - ? translateText("common.copied") - : this.lobbyIdVisible - ? this.currentLobbyId - : "••••••••"} -
- -
` - : ""} -
+ ${this.lobbyIdVisible + ? html` + + ` + : html` + + + `} + +
{ + (e.currentTarget as HTMLElement).classList.add( + "select-all", + ); + }} + @mouseleave=${(e: Event) => { + (e.currentTarget as HTMLElement).classList.remove( + "select-all", + ); + }} + class="font-mono text-xs font-bold text-white px-2 cursor-pointer select-none min-w-[80px] text-center truncate tracking-wider" + title="${translateText("common.click_to_copy")}" + > + ${this.copySuccess + ? translateText("common.copied") + : this.lobbyIdVisible + ? this.currentLobbyId + : "••••••••"} +
+ +
+ ` + : undefined, + })}
${!this.hasJoined ? html`
diff --git a/src/client/LanguageModal.ts b/src/client/LanguageModal.ts index 1a29cc4d3..9f6c09969 100644 --- a/src/client/LanguageModal.ts +++ b/src/client/LanguageModal.ts @@ -3,6 +3,7 @@ import { customElement, property } from "lit/decorators.js"; import { translateText } from "../client/Utils"; import "./components/baseComponents/Modal"; import { BaseModal } from "./components/BaseModal"; +import { modalHeader } from "./components/ui/ModalHeader"; interface LanguageOption { code: string; @@ -30,47 +31,17 @@ export class LanguageModal extends BaseModal { render() { const content = html`
-
-
- - - ${translateText("select_lang.title")} - -
-
+ ${modalHeader({ + title: translateText("select_lang.title"), + onBack: this.close, + ariaLabel: translateText("common.back"), + })}
-
-
- - - ${translateText("matchmaking_modal.title")} - -
-
+ ${modalHeader({ + title: translateText("matchmaking_modal.title"), + onBack: this.close, + ariaLabel: translateText("common.back"), + })}
${eloDisplay} ${this.renderInner()}
diff --git a/src/client/NewsModal.ts b/src/client/NewsModal.ts index 66693e614..0a150144f 100644 --- a/src/client/NewsModal.ts +++ b/src/client/NewsModal.ts @@ -5,6 +5,7 @@ import version from "resources/version.txt?raw"; import { translateText } from "../client/Utils"; import "./components/baseComponents/Modal"; import { BaseModal } from "./components/BaseModal"; +import { modalHeader } from "./components/ui/ModalHeader"; import changelog from "/changelog.md?url"; import megaphone from "/images/Megaphone.svg?url"; @@ -21,39 +22,13 @@ export class NewsModal extends BaseModal { ? "bg-black/60 backdrop-blur-md rounded-2xl border border-white/10" : ""}" > + ${modalHeader({ + title: translateText("news.title"), + onBack: this.close, + ariaLabel: translateText("common.back"), + })}
-
- - - ${translateText("news.title")} - -
-
-
-
- - - ${translateText("main.solo") || "Solo"} - - - ${hasLinkedAccount(this.userMeResponse) + ${modalHeader({ + title: translateText("main.solo") || "Solo", + onBack: this.close, + ariaLabel: translateText("common.back"), + rightContent: hasLinkedAccount(this.userMeResponse) ? html`` - : this.renderNotLoggedInBanner()} -
+ : this.renderNotLoggedInBanner(), + })}
diff --git a/src/client/StatsModal.ts b/src/client/StatsModal.ts index 05a5131c9..1346b0031 100644 --- a/src/client/StatsModal.ts +++ b/src/client/StatsModal.ts @@ -8,6 +8,7 @@ import { import { getApiBase } from "./Api"; import { translateText } from "./Utils"; import { BaseModal } from "./components/BaseModal"; +import { modalHeader } from "./components/ui/ModalHeader"; @customElement("stats-modal") export class StatsModal extends BaseModal { @@ -195,7 +196,7 @@ export class StatsModal extends BaseModal { const maxGames = Math.max(...clans.map((c) => c.games), 1); return html` -
+
@@ -371,34 +372,10 @@ export class StatsModal extends BaseModal { const content = html`
-
-
- + ${modalHeader({ + titleContent: html`
${dateRange}
-
-
+ `, + onBack: this.close, + ariaLabel: translateText("common.close"), + leftClassName: "flex flex-wrap items-center gap-4 flex-1", + })}
-
- - - ${translateText("territory_patterns.title")} - - - ${ - !hasLinkedAccount(this.userMeResponse) - ? html`
- ${this.renderNotLoggedInWarning()} -
` - : html`` - } -
- -
-
`; } diff --git a/src/client/UserSettingModal.ts b/src/client/UserSettingModal.ts index ed873f6a2..983f09237 100644 --- a/src/client/UserSettingModal.ts +++ b/src/client/UserSettingModal.ts @@ -8,6 +8,7 @@ import "./components/baseComponents/setting/SettingNumber"; import "./components/baseComponents/setting/SettingSlider"; import "./components/baseComponents/setting/SettingToggle"; import { BaseModal } from "./components/BaseModal"; +import { modalHeader } from "./components/ui/ModalHeader"; import "./FlagInputModal"; interface FlagInputModalElement extends HTMLElement { @@ -388,40 +389,17 @@ export class UserSettingModal extends BaseModal { const content = html`
-
- - - ${translateText("user_setting.title")} - -
+ ${modalHeader({ + title: translateText("user_setting.title"), + onBack: this.close, + ariaLabel: translateText("common.back"), + showDivider: true, + })} + ${rightContent ?? ""} +
+ `; +};