minesweeper/src/main/js/ModalDialog.ts

129 lines
3.3 KiB
TypeScript

const {$} = (window as any).fwdekker;
import {blurActiveElement} from "./Common";
/**
* A modal dialog displayed in HTML.
*/
export class ModalDialog {
private readonly dialog: HTMLElement;
private readonly openButton?: HTMLElement;
private readonly onOpen?: () => void;
/**
* Constructs a new modal dialog wrapper.
*
* @param dialog the dialog maintained by this instance
* @param openButton the element that opens the dialog when clicked
* @param onOpen the callback to invoke when the dialog is opened
* @param form the form contained in the dialog
* @param closeButton the element that closes the dialog's form when clicked
* @param submitButton the element that submits the dialog's form when clicked
* @param onSubmit the callback to invoke when the dialog's form is submitted
*/
constructor(
{
dialog,
openButton,
onOpen,
form,
closeButton,
submitButton,
onSubmit
}: {
dialog: HTMLElement,
openButton?: HTMLElement,
onOpen?: (() => void),
form?: HTMLFormElement,
closeButton?: HTMLElement,
submitButton?: HTMLElement,
onSubmit?: (() => void)
}
) {
this.dialog = dialog;
this.openButton = openButton;
this.onOpen = onOpen;
document.addEventListener(
"click",
event => {
if (!(event.target instanceof Node)) return;
console.log("close");
if (event.target !== openButton && !this.dialog.contains(event.target) || this.dialog === event.target)
this.close();
}
);
document.addEventListener(
"keydown",
event => {
if (event.key === "Escape" && this.isOpen())
this.close();
}
);
openButton?.addEventListener(
"click",
event => {
event.preventDefault();
this.open();
}
);
closeButton?.addEventListener(
"click",
event => {
event.preventDefault();
this.close();
}
);
form?.addEventListener(
"submit",
event => {
event.preventDefault();
onSubmit?.();
}
);
submitButton?.addEventListener(
"click",
event => {
event.preventDefault();
onSubmit?.();
}
);
}
/**
* Opens the dialog.
*/
open(): void {
console.log("opening");
blurActiveElement();
setTimeout(() => $("[autofocus]", this.dialog)?.focus(), 100);
this.dialog.setAttribute("open", "true");
this.onOpen?.();
}
/**
* Closes the dialog.
*/
close(): void {
if (this.isOpen()) {
this.dialog.removeAttribute("open");
setTimeout(() => this.openButton?.focus(), 100);
}
}
/**
* Returns `true` if and only if this dialog is currently open.
*/
isOpen(): boolean {
return this.dialog.hasAttribute("open");
}
}