drab
Version:
Interactivity for You
77 lines (76 loc) • 2.68 kB
JavaScript
import { Base } from "../base/index.js";
/**
* Provides triggers for the `HTMLDialogElement`.
*
* `click-outside-close`
*
* By default, the `HTMLDialogElement` doesn't close if the user clicks outside of it.
* Add a `click-outside-close` attribute to close when the user clicks outside.
*
* `remove-body-scroll`
*
* Add the `remove-body-scroll` attribute to remove the scroll from `document.body` when the dialog
* is open.
*/
export class Dialog extends Base {
/** The initial margin-right value of the body element. */
#initialBodyMarginRight = parseInt(getComputedStyle(document.body).marginRight);
constructor() {
super();
}
/** The `HTMLDialogElement` within the element. */
get dialog() {
return this.getContent(HTMLDialogElement);
}
/** Remove scroll from the body when open with the `remove-body-scroll` attribute. */
#toggleBodyScroll(show) {
if (this.hasAttribute("remove-body-scroll")) {
document.body.style.marginRight = `${show
? this.#initialBodyMarginRight +
// scrollbar width
window.innerWidth -
document.documentElement.clientWidth
: this.#initialBodyMarginRight}px`;
document.body.style.overflow = show ? "hidden" : "";
}
}
/** `HTMLDialogElement.showModal()` with animation. */
async show() {
this.dialog.showModal();
this.#toggleBodyScroll(true);
}
/** `HTMLDialogElement.close()` with animation. */
async close() {
this.#toggleBodyScroll(false);
this.dialog.close();
}
/** `show` or `close` depending on the dialog's `open` attribute. */
async toggle() {
if (this.dialog.open)
this.close();
else
this.show();
}
mount() {
this.triggerListener(() => this.toggle());
this.safeListener("keydown", (e) => {
if (e.key === "Escape" && this.dialog.open) {
// to execute animation
e.preventDefault();
this.close();
}
});
if (this.hasAttribute("click-outside-close")) {
// https://blog.webdevsimplified.com/2023-04/html-dialog/#close-on-outside-click
this.dialog.addEventListener("click", (e) => {
const rect = this.dialog.getBoundingClientRect();
if (e.clientX < rect.left ||
e.clientX > rect.right ||
e.clientY < rect.top ||
e.clientY > rect.bottom) {
this.close();
}
});
}
}
}