mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:30:45 +00:00
6c2e0d1528
## Description: There were several issues with the matchmaking modal: 1. It was defined twice (once before login, and once after login), so players would sometimes join the matchmaking queue twice. 2. When clicking away from the modal (not clicking the back button), the "onClose" callback was not triggered. So if a person closed & reopened the modal, they would join twice' 3. Cache the userMe response so it can be called multiple times ## 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: evan
125 lines
3.1 KiB
TypeScript
125 lines
3.1 KiB
TypeScript
import { LitElement } from "lit";
|
|
import { property, query, state } from "lit/decorators.js";
|
|
|
|
/**
|
|
* Base class for modal components that provides unified Escape key handling and common modal patterns.
|
|
*
|
|
* Features:
|
|
* - Visibility tracking with isModalOpen state
|
|
* - Escape key handler with visibility check and target validation
|
|
* - Automatic listener lifecycle management
|
|
* - Common inline/modal element handling
|
|
* - Shared open/close logic with hooks for custom behavior
|
|
*/
|
|
export abstract class BaseModal extends LitElement {
|
|
@state() protected isModalOpen = false;
|
|
@property({ type: Boolean }) inline = false;
|
|
|
|
@query("o-modal") protected modalEl?: HTMLElement & {
|
|
open: () => void;
|
|
close: () => void;
|
|
onClose?: () => void;
|
|
};
|
|
|
|
createRenderRoot() {
|
|
return this;
|
|
}
|
|
|
|
protected firstUpdated(): void {
|
|
if (this.modalEl) {
|
|
this.modalEl.onClose = () => {
|
|
if (this.isModalOpen) {
|
|
this.close();
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
this.unregisterEscapeHandler();
|
|
super.disconnectedCallback();
|
|
}
|
|
|
|
/**
|
|
* Handle Escape key press to close the modal.
|
|
* Only closes if the modal is open.
|
|
*/
|
|
private handleKeyDown = (e: KeyboardEvent) => {
|
|
if (e.key === "Escape" && this.isModalOpen) {
|
|
e.preventDefault();
|
|
this.close();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Register the Escape key handler and mark modal as open.
|
|
*/
|
|
protected registerEscapeHandler() {
|
|
this.isModalOpen = true;
|
|
window.addEventListener("keydown", this.handleKeyDown);
|
|
}
|
|
|
|
/**
|
|
* Unregister the Escape key handler and mark modal as closed.
|
|
*/
|
|
protected unregisterEscapeHandler() {
|
|
this.isModalOpen = false;
|
|
window.removeEventListener("keydown", this.handleKeyDown);
|
|
}
|
|
|
|
/**
|
|
* Hook for custom logic when modal opens.
|
|
* Override this in subclasses to add custom open behavior.
|
|
*/
|
|
protected onOpen(): void {
|
|
// Default implementation does nothing
|
|
}
|
|
|
|
/**
|
|
* Hook for custom logic when modal closes.
|
|
* Override this in subclasses to add custom close behavior.
|
|
*/
|
|
protected onClose(): void {
|
|
// Default implementation does nothing
|
|
}
|
|
|
|
/**
|
|
* Open the modal. Handles both inline and modal element modes.
|
|
* Subclasses can override onOpen() for custom behavior.
|
|
*/
|
|
public open(): void {
|
|
this.registerEscapeHandler();
|
|
this.onOpen();
|
|
|
|
if (this.inline) {
|
|
const needsShow =
|
|
this.classList.contains("hidden") || this.style.display === "none";
|
|
if (needsShow && window.showPage) {
|
|
const pageId = this.id || this.tagName.toLowerCase();
|
|
window.showPage?.(pageId);
|
|
}
|
|
this.style.pointerEvents = "auto";
|
|
} else {
|
|
this.modalEl?.open();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Close the modal. Handles both inline and modal element modes.
|
|
* Subclasses can override onClose() for custom behavior.
|
|
*/
|
|
public close(): void {
|
|
this.unregisterEscapeHandler();
|
|
this.onClose();
|
|
|
|
if (this.inline) {
|
|
this.style.pointerEvents = "none";
|
|
if (window.showPage) {
|
|
window.showPage?.("page-play");
|
|
}
|
|
} else {
|
|
this.modalEl?.close();
|
|
}
|
|
}
|
|
}
|