129 lines
3.3 KiB
TypeScript
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");
|
|
}
|
|
}
|