From 452952d3e51acfcd949c8977f8dfd3b5611f3967 Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Fri, 17 Oct 2025 17:46:01 +0200 Subject: [PATCH 001/119] Enforce minimum test coverage (#1646) Enforce minimum test coverage. - [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 - [ ] I have read and accepted the CLA agreement (only required once). --- .github/workflows/ci.yml | 2 +- jest.config.ts | 7 ++++++- src/client/graphics/GameRenderer.ts | 3 +-- src/client/graphics/layers/EmojiTable.ts | 7 +++---- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b920e29e..b19bf60f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: with: node-version: 20 - run: npm ci - - run: npm test + - run: npm run test:coverage eslint: name: ๐Ÿ” ESLint diff --git a/jest.config.ts b/jest.config.ts index 7bc6ecf32..e4536d0da 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -32,7 +32,12 @@ export default { preset: "ts-jest/presets/default-esm", collectCoverageFrom: ["src/**/*.ts", "!src/**/*.d.ts"], coverageThreshold: { - global: { branches: 0, functions: 0, lines: 0, statements: 0 }, + global: { + statements: 22.2, + branches: 17.5, + lines: 22.7, + functions: 21.2, + }, }, coverageReporters: ["text", "lcov", "html"], setupFilesAfterEnv: ["/tests/setup.ts"], diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index a7b4b37fb..6c3eecfaf 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -61,10 +61,9 @@ export function createRenderer( if (!emojiTable || !(emojiTable instanceof EmojiTable)) { console.error("EmojiTable element not found in the DOM"); } - emojiTable.eventBus = eventBus; emojiTable.transformHandler = transformHandler; emojiTable.game = game; - emojiTable.initEventBus(); + emojiTable.initEventBus(eventBus); const buildMenu = document.querySelector("build-menu") as BuildMenu; if (!buildMenu || !(buildMenu instanceof BuildMenu)) { diff --git a/src/client/graphics/layers/EmojiTable.ts b/src/client/graphics/layers/EmojiTable.ts index 7a9702578..542c24d37 100644 --- a/src/client/graphics/layers/EmojiTable.ts +++ b/src/client/graphics/layers/EmojiTable.ts @@ -12,12 +12,11 @@ import { TransformHandler } from "../TransformHandler"; @customElement("emoji-table") export class EmojiTable extends LitElement { @state() public isVisible = false; - public eventBus: EventBus; public transformHandler: TransformHandler; public game: GameView; - initEventBus() { - this.eventBus.on(ShowEmojiMenuEvent, (e) => { + initEventBus(eventBus: EventBus) { + eventBus.on(ShowEmojiMenuEvent, (e) => { this.isVisible = true; const cell = this.transformHandler.screenToWorldCoordinates(e.x, e.y); if (!this.game.isValidCoord(cell.x, cell.y)) { @@ -40,7 +39,7 @@ export class EmojiTable extends LitElement { targetPlayer === this.game.myPlayer() ? AllPlayers : (targetPlayer as PlayerView); - this.eventBus.emit( + eventBus.emit( new SendEmojiIntentEvent( recipient, flattenedEmojiTable.indexOf(emoji), From 6df48fc846e6631212ab4c0e746c89982ab260d7 Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Fri, 17 Oct 2025 17:48:42 +0200 Subject: [PATCH 002/119] Fix for two formatPercentage functions (#1659) Closes #1656 Change: Simplification of the function to display percentages with one decimal place without limitation. Result: Percentages are now displayed with one rounded decimal place (e.g. 15.5%, 99.6%) without automatic replacement of extreme values. - [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 - [x] I have read and accepted the CLA agreement (only required once). regression is found: kipstzz --- src/client/graphics/layers/Leaderboard.ts | 5 +---- src/client/graphics/layers/TeamStats.ts | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/client/graphics/layers/Leaderboard.ts b/src/client/graphics/layers/Leaderboard.ts index 6b7efc5dd..ed111a749 100644 --- a/src/client/graphics/layers/Leaderboard.ts +++ b/src/client/graphics/layers/Leaderboard.ts @@ -269,9 +269,6 @@ export class Leaderboard extends LitElement implements Layer { function formatPercentage(value: number): string { const perc = value * 100; - if (perc > 99.5) return "100%"; - if (perc < 0.01) return "0%"; - if (perc < 0.1) return perc.toPrecision(1) + "%"; if (Number.isNaN(perc)) return "0%"; - return perc.toPrecision(2) + "%"; + return perc.toFixed(1) + "%"; } diff --git a/src/client/graphics/layers/TeamStats.ts b/src/client/graphics/layers/TeamStats.ts index 16251a0ad..687d807c6 100644 --- a/src/client/graphics/layers/TeamStats.ts +++ b/src/client/graphics/layers/TeamStats.ts @@ -167,8 +167,6 @@ export class TeamStats extends LitElement implements Layer { function formatPercentage(value: number): string { const perc = value * 100; - if (perc > 99.5) return "100%"; - if (perc < 0.01) return "0%"; - if (perc < 0.1) return perc.toPrecision(1) + "%"; + if (Number.isNaN(perc)) return "0%"; return perc.toPrecision(2) + "%"; } From e160b74e47d3f783b9836a855e84cc51f1076d4d Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Fri, 17 Oct 2025 17:49:13 +0200 Subject: [PATCH 003/119] Betray on MIRV launch (#1668) Betray on MIRV launch, instead of on separate. - [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 - [ ] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [ ] I have read and accepted the CLA agreement (only required once). --- src/core/execution/MIRVExecution.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/core/execution/MIRVExecution.ts b/src/core/execution/MIRVExecution.ts index ede8218ce..c6626b236 100644 --- a/src/core/execution/MIRVExecution.ts +++ b/src/core/execution/MIRVExecution.ts @@ -53,6 +53,17 @@ export class MirvExecution implements Execution { // Record stats this.mg.stats().bombLaunch(this.player, this.targetPlayer, UnitType.MIRV); + + // Betrayal on launch + if (this.targetPlayer.isPlayer()) { + const alliance = this.player.allianceWith(this.targetPlayer); + if (alliance !== null) { + this.player.breakAlliance(alliance); + } + if (this.targetPlayer !== this.player) { + this.targetPlayer.updateRelation(this.player, -100); + } + } } tick(ticks: number): void { @@ -135,15 +146,6 @@ export class MirvExecution implements Execution { ), ); } - if (this.targetPlayer.isPlayer()) { - const alliance = this.player.allianceWith(this.targetPlayer); - if (alliance !== null) { - this.player.breakAlliance(alliance); - } - if (this.targetPlayer !== this.player) { - this.targetPlayer.updateRelation(this.player, -100); - } - } this.nuke.delete(false); } From c9d6c6ffdfd7333034d761e458411e945ac9143e Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Fri, 17 Oct 2025 17:49:37 +0200 Subject: [PATCH 004/119] Fix the stale disconnected (Zzz) icon persisting when a client reconnects (#1631) Fix the stale disconnected (Zzz) icon persisting when a client reconnects Refer to issue #1630 This change modifies the GameServer.addClient function to send the mark_disconnected = false intent when a client that was previously disconnected gets replaced by a newly joined client with the same clientID. Under the old logic, this mark_disconnected = false intent is not sent if the client was disconnected for longer than 60 seconds before reconnecting. https://github.com/user-attachments/assets/5e0ce1c3-9519-4f39-aa80-e46f1275649c Left side browser (player with red tiles) is the disconnected client that was disconnected at 00:14 game clock time. It reconnected around 03:56 game clock time. Right side browser (player with green tiles) is the other client. Use the game clock time from the right side for the live game clock time. - [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 - [x] I have read and accepted the CLA agreement (only required once). regression is found: slyty --------- Co-authored-by: Drills Kibo <59177241+drillskibo@users.noreply.github.com> --- src/server/Client.ts | 1 - src/server/GameServer.ts | 26 +++++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/server/Client.ts b/src/server/Client.ts index c367c0e04..eb85d83cf 100644 --- a/src/server/Client.ts +++ b/src/server/Client.ts @@ -5,7 +5,6 @@ import { ClientID } from "../core/Schemas"; export class Client { public lastPing: number = Date.now(); - public isDisconnected: boolean = false; public hashes: Map = new Map(); diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index b8a328ecd..543e90324 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -43,6 +43,7 @@ export class GameServer { public activeClients: Client[] = []; // Used for record record keeping private allClients: Map = new Map(); + private clientsDisconnectedStatus: Map = new Map(); private _hasStarted = false; private _startTime: number | null = null; @@ -174,7 +175,6 @@ export class GameServer { return; } - client.isDisconnected = existing.isDisconnected; client.lastPing = existing.lastPing; this.activeClients = this.activeClients.filter((c) => c !== existing); @@ -184,6 +184,8 @@ export class GameServer { this.activeClients.push(client); client.lastPing = Date.now(); + this.markClientDisconnected(client.clientID, false); + this.allClients.set(client.clientID, client); client.ws.removeAllListeners("message"); @@ -568,25 +570,27 @@ export class GameServer { const now = Date.now(); for (const [clientID, client] of this.allClients) { - if ( - client.isDisconnected === false && - now - client.lastPing > this.disconnectedTimeout - ) { - this.markClientDisconnected(client, true); + const isDisconnected = this.isClientDisconnected(clientID); + if (!isDisconnected && now - client.lastPing > this.disconnectedTimeout) { + this.markClientDisconnected(clientID, true); } else if ( - client.isDisconnected && + isDisconnected && now - client.lastPing < this.disconnectedTimeout ) { - this.markClientDisconnected(client, false); + this.markClientDisconnected(clientID, false); } } } - private markClientDisconnected(client: Client, isDisconnected: boolean) { - client.isDisconnected = isDisconnected; + public isClientDisconnected(clientID: string): boolean { + return this.clientsDisconnectedStatus.get(clientID) ?? true; + } + + private markClientDisconnected(clientID: string, isDisconnected: boolean) { + this.clientsDisconnectedStatus.set(clientID, isDisconnected); this.addIntent({ type: "mark_disconnected", - clientID: client.clientID, + clientID: clientID, isDisconnected: isDisconnected, }); } From c53d186586a68ad35c584cb3f52a16b3897a6898 Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Fri, 17 Oct 2025 17:53:00 +0200 Subject: [PATCH 005/119] Shift scroll only increase (#1625) Determine which scroll event delta, x or y, has the non zero value. Use it to determine troop ratio increase or decrease. Shift + Scroll can change scroll diretion from Y to X axis. If that is enabled this change will still allow troop ratio increase / decrease - [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 - [ ] I have added relevant tests to the test directory - [ ] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [ ] I have read and accepted the CLA agreement (only required once). regression is found: richard012659 --------- Co-authored-by: Drills Kibo <59177241+drillskibo@users.noreply.github.com> --- src/client/InputHandler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/InputHandler.ts b/src/client/InputHandler.ts index b3b48f04c..dd76e62e5 100644 --- a/src/client/InputHandler.ts +++ b/src/client/InputHandler.ts @@ -502,7 +502,8 @@ export class InputHandler { private onShiftScroll(event: WheelEvent) { if (event.shiftKey) { - const ratio = event.deltaY > 0 ? -10 : 10; + const scrollValue = event.deltaY === 0 ? event.deltaX : event.deltaY; + const ratio = scrollValue > 0 ? -10 : 10; this.eventBus.emit(new AttackRatioEvent(ratio)); } } From 4e22f7784b6b349bfb8382c4c00c9ba05deb8f8b Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Fri, 17 Oct 2025 17:55:38 +0200 Subject: [PATCH 006/119] Loosen coverage requirements slightly (#1669) Tight code coverage requirements and low coverage are making it difficult to merge code, because a small increase in lines of code can decrease coverage enough to trigger a test failure. - [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 - [ ] I have read and accepted the CLA agreement (only required once). --- jest.config.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jest.config.ts b/jest.config.ts index e4536d0da..ac61c3090 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -33,10 +33,10 @@ export default { collectCoverageFrom: ["src/**/*.ts", "!src/**/*.d.ts"], coverageThreshold: { global: { - statements: 22.2, - branches: 17.5, - lines: 22.7, - functions: 21.2, + statements: 22.0, + branches: 17.0, + lines: 22.5, + functions: 21.0, }, }, coverageReporters: ["text", "lcov", "html"], From 7dd9637b027ced043c1aa77c5b18af4316549d93 Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Fri, 17 Oct 2025 17:59:41 +0200 Subject: [PATCH 007/119] Handle canvas context loss and restoration by redrawing (#1667) This PR introduces handling for the canvas `contextlost` and `contextrestored` events to make the game's rendering more robust. Previously, if the graphics context was lost (which can happen due to browser memory management, GPU driver resets, etc.), the render loop would continue to run, but most drawing operations would silently fail. This resulted in a broken visual state where terrain, structures, and other graphics would disappear, leading to what players have referred to as the "black screen bug". These changes implement the following: 1. In `GameRenderer`, event listeners are added to the main canvas. 2. On `contextlost`, the `requestAnimationFrame` loop is cancelled, pausing rendering. 3. On `contextrestored`, a full redraw of all layers is triggered, and the render loop is restarted, allowing the game to gracefully recover. Additionally, a related bug in the `StructureLayer` is fixed. During a redraw/restoration, the layer could attempt to render unit icons to its canvas before the images were fully (re)decoded. The `init` method now explicitly waits for all icon images to be decoded before drawing them, ensuring the layer is restored correctly. **Important Note:** This PR represents a partial fix for the context loss issue. Specifically, the `StructureIconsLayer` remains in a broken state after context restoration. This layer is rendered using Pixi.js, which has its own specific process for handling renderer recovery. Due to my lack of experience with the Pixi.js API, I was unable to implement the fix for this layer. This would be an excellent follow-up contribution for someone familiar with Pixi. - [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 - [X] I have read and accepted the CLA agreement (only required once). regression is found: aaa4xu --- src/client/graphics/GameRenderer.ts | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 6c3eecfaf..fb3a0c853 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -280,14 +280,7 @@ export class GameRenderer { } initialize() { - this.eventBus.on(RedrawGraphicsEvent, (e) => { - this.layers.forEach((l) => { - if (l.redraw) { - l.redraw(); - } - }); - }); - + this.eventBus.on(RedrawGraphicsEvent, () => this.redraw()); this.layers.forEach((l) => l.init?.()); document.body.appendChild(this.canvas); @@ -297,7 +290,14 @@ export class GameRenderer { //show whole map on startup this.transformHandler.centerAll(0.9); - requestAnimationFrame(() => this.renderGame()); + let rafId = requestAnimationFrame(() => this.renderGame()); + this.canvas.addEventListener("contextlost", () => { + cancelAnimationFrame(rafId); + }); + this.canvas.addEventListener("contextrestored", () => { + this.redraw(); + rafId = requestAnimationFrame(() => this.renderGame()); + }); } resizeCanvas() { @@ -306,6 +306,14 @@ export class GameRenderer { //this.redraw() } + redraw() { + this.layers.forEach((l) => { + if (l.redraw) { + l.redraw(); + } + }); + } + renderGame() { const start = performance.now(); // Set background From 96afc4e25f55445ea6316d9086a2fddfdf666c37 Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Fri, 17 Oct 2025 18:17:48 +0200 Subject: [PATCH 008/119] Private Lobbies: Add kick player functionality (#1436) Added player management features so lobby hosts can kick players from private games. This includes both UI changes and backend work. - Hosts can now kick players from private lobbies with a simple button - Added host badges and remove buttons to the UI - Made sure only hosts can kick people, and hosts can't kick themselves - When someone creates a private game, they automatically become the host - Kicking happens through WebSocket "kick-player" events - Server checks that you're actually the host before letting you kick anyone Screenshot 2025-07-15 002114 - Kicked player gets general message (same when kicked for multi tab) - Host abandoment still existent (host clicks on x; or is closing tab) - [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 - [x] I have read and accepted the CLA agreement (only required once). regression is found: [UN]nvm --------- Co-authored-by: floriankilian --- resources/lang/en.json | 3 +- src/client/ClientGameRunner.ts | 3 +- src/client/HostLobbyModal.ts | 61 ++++++++++++++++++++++++++-------- src/client/Main.ts | 14 ++++++++ src/client/Transport.ts | 13 ++++++++ src/client/styles.css | 54 ++++++++++++++++++++++++------ src/core/Schemas.ts | 9 ++++- src/server/GameManager.ts | 43 +++++++++++++++--------- src/server/GameServer.ts | 51 +++++++++++++++++++++++++++- src/server/Worker.ts | 13 ++++++-- 10 files changed, 217 insertions(+), 47 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index de560b834..7aabe96f9 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -211,7 +211,8 @@ "player": "Player", "players": "Players", "waiting": "Waiting for players...", - "start": "Start Game" + "start": "Start Game", + "host_badge": "Host" }, "team_colors": { "red": "Red", diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index edbaf4f14..cc03e9efe 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -58,12 +58,11 @@ export interface LobbyConfig { } export function joinLobby( + eventBus: EventBus, lobbyConfig: LobbyConfig, onPrestart: () => void, onJoin: () => void, ): () => void { - const eventBus = new EventBus(); - console.log( `joining lobby: gameID: ${lobbyConfig.gameID}, clientID: ${lobbyConfig.clientID}`, ); diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index de172f6be..a057d3235 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -15,6 +15,7 @@ import { } from "../core/game/Game"; import { UserSettings } from "../core/game/UserSettings"; import { + ClientInfo, GameConfig, GameInfo, PeaceTimerDuration, @@ -45,9 +46,10 @@ export class HostLobbyModal extends LitElement { @state() private instantBuild: boolean = false; @state() private lobbyId = ""; @state() private copySuccess = false; - @state() private players: string[] = []; + @state() private clients: ClientInfo[] = []; @state() private useRandomMap: boolean = false; @state() private disabledUnits: UnitType[] = []; + @state() private lobbyCreatorClientID: string = ""; @state() private lobbyIdVisible: boolean = true; @state() private selectedPeaceTimerDuration: PeaceTimerDuration = PeaceTimerDuration.None; @@ -429,29 +431,45 @@ export class HostLobbyModal extends LitElement {
- ${this.players.length} + ${this.clients.length} ${ - this.players.length === 1 + this.clients.length === 1 ? translateText("host_modal.player") : translateText("host_modal.players") }
- ${this.players.map( - (player) => html`${player}`, + ${this.clients.map( + (client) => html` + + ${client.username} + ${client.clientID === this.lobbyCreatorClientID + ? html`(${translateText("host_modal.host_badge")})` + : html` + + `} + + `, )} -
+ `; + })} +
+ + `; + })} + + + `, + )} +
+ + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "research-tree-modal": ResearchTreeModal; + } +} diff --git a/src/client/Transport.ts b/src/client/Transport.ts index de2897efb..a2623bb65 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -97,6 +97,10 @@ export class SendPurchaseUpgradeIntentEvent implements GameEvent { constructor(public readonly upgrade: UpgradeType) {} } +export class SendResearchTreeSelectIntentEvent implements GameEvent { + constructor(public readonly techId: string) {} +} + export class SendTargetPlayerIntentEvent implements GameEvent { constructor(public readonly targetID: PlayerID) {} } @@ -266,6 +270,10 @@ export class Transport { this.onSendPurchaseUpgradeIntent(e), ); + this.eventBus.on(SendResearchTreeSelectIntentEvent, (e) => + this.onSendResearchTreeSelectIntent(e), + ); + this.eventBus.on(BuildUnitIntentEvent, (e) => this.onBuildUnitIntent(e)); this.eventBus.on(PauseGameEvent, (e) => this.onPauseGameEvent(e)); @@ -602,6 +610,16 @@ export class Transport { }); } + private onSendResearchTreeSelectIntent( + event: SendResearchTreeSelectIntentEvent, + ) { + this.sendIntent({ + type: "research_tree_select", + clientID: this.lobbyConfig.clientID, + techId: event.techId, + }); + } + private onPauseGameEvent(event: PauseGameEvent) { if (!this.isLocal) { console.log(`cannot pause multiplayer games`); diff --git a/src/client/components/baseComponents/Modal.ts b/src/client/components/baseComponents/Modal.ts index 00e9d95e1..a6ec41358 100644 --- a/src/client/components/baseComponents/Modal.ts +++ b/src/client/components/baseComponents/Modal.ts @@ -7,6 +7,11 @@ export class OModal extends LitElement { @state() public isModalOpen = false; @property({ type: String }) title = ""; @property({ type: String }) translationKey = ""; + // Optional sizing overrides so some modals can be wider/taller + @property({ type: String, attribute: "max-width" }) maxWidth: string = + "860px"; + @property({ type: String, attribute: "max-height" }) maxHeight: string = + "60dvh"; static styles = css` .c-modal { @@ -27,7 +32,7 @@ export class OModal extends LitElement { .c-modal__wrapper { border-radius: 8px; min-width: 340px; - max-width: 860px; + /* max-width is overridden inline using the maxWidth property */ } .c-modal__header { @@ -53,7 +58,7 @@ export class OModal extends LitElement { position: relative; color: #fff; padding: 1.4rem; - max-height: 60dvh; + /* max-height is overridden inline using the maxHeight property */ overflow-y: auto; backdrop-filter: blur(8px); } @@ -74,14 +79,17 @@ export class OModal extends LitElement { ${this.isModalOpen ? html`