portico: Add portico_modals.ts module.

This commit adds portico_modals.ts module which contains functions
for supporting modals in portico pages. The new module basically
contains code from functions in overlays.ts without the code
that is not needed currently for modals in portico pages.
This commit is contained in:
Sahil Batra 2023-10-04 23:04:34 +05:30 committed by Tim Abbott
parent ac8436d46c
commit 866b6cd632
2 changed files with 135 additions and 0 deletions

View File

@ -1,5 +1,6 @@
import "./common";
import "../portico/header";
import "../portico/google-analytics";
import "../portico/portico_modals";
import "../../styles/portico/portico_styles.css";
import "tippy.js/dist/tippy.css";

View File

@ -0,0 +1,134 @@
import $ from "jquery";
import Micromodal from "micromodal";
import * as blueslip from "../blueslip";
function is_modal_open(): boolean {
return $(".micromodal").hasClass("modal--open");
}
function active_modal(): string | undefined {
if (!is_modal_open()) {
blueslip.error("Programming error — Called active_modal when there is no modal open");
return undefined;
}
const $micromodal = $(".micromodal.modal--open");
return `#${CSS.escape($micromodal.attr("id")!)}`;
}
function close_active_modal(): void {
if (!is_modal_open()) {
blueslip.warn("close_active_modal() called without checking is_modal_open()");
return;
}
const $micromodal = $(".micromodal.modal--open");
Micromodal.close(`${CSS.escape($micromodal.attr("id") ?? "")}`);
}
export function open_modal(modal_id: string, recursive_call_count: number = 0): void {
if (modal_id === undefined) {
blueslip.error("Undefined id was passed into open_modal");
return;
}
// Don't accept hash-based selector to enforce modals to have unique ids and
// since micromodal doesn't accept hash based selectors.
if (modal_id.startsWith("#")) {
blueslip.error("hash-based selector passed in to open_modal", {modal_id});
return;
}
if (is_modal_open()) {
/*
Our modal system doesn't directly support opening a modal
when one is already open, because the `is_modal_open` CSS
class doesn't update until Micromodal has finished its
animations, which can take 100ms or more.
We can likely fix that, but in the meantime, we should
handle this situation correctly, by closing the current
modal, waiting for it to finish closing, and then attempting
to open the current modal again.
*/
if (recursive_call_count) {
recursive_call_count = 1;
} else {
recursive_call_count += 1;
}
if (recursive_call_count > 50) {
blueslip.error("Modal incorrectly is still open", {modal_id});
return;
}
close_active_modal();
setTimeout(() => {
open_modal(modal_id, recursive_call_count);
}, 10);
return;
}
blueslip.debug("open modal: " + modal_id);
// Micromodal gets elements using the getElementById DOM function
// which doesn't require the hash. We add it manually here.
const id_selector = `#${CSS.escape(modal_id)}`;
const $micromodal = $(id_selector);
$micromodal.find(".modal__container").on("animationend", (event) => {
const animation_name = (event.originalEvent as AnimationEvent).animationName;
if (animation_name === "mmfadeIn") {
// Micromodal adds the is-open class before the modal animation
// is complete, which isn't really helpful since a modal is open after the
// animation is complete. So, we manually add a class after the
// animation is complete.
$micromodal.addClass("modal--open");
$micromodal.removeClass("modal--opening");
} else if (animation_name === "mmfadeOut") {
$micromodal.removeClass("modal--open");
}
});
$micromodal.find(".modal__overlay").on("click", (e) => {
/* Micromodal's data-micromodal-close feature doesn't check for
range selections; this means dragging a selection of text in an
input inside the modal too far will weirdly close the modal.
See https://github.com/ghosh/Micromodal/issues/505.
Work around this with our own implementation. */
if (!$(e.target).is(".modal__overlay")) {
return;
}
if (document.getSelection()?.type === "Range") {
return;
}
close_modal(modal_id);
});
Micromodal.show(modal_id, {
disableFocus: true,
openClass: "modal--opening",
});
}
export function close_modal(modal_id: string): void {
if (modal_id === undefined) {
blueslip.error("Undefined id was passed into close_modal");
return;
}
if (!is_modal_open()) {
blueslip.warn("close_active_modal() called without checking is_modal_open()");
return;
}
if (active_modal() !== `#${CSS.escape(modal_id)}`) {
blueslip.error("Trying to close modal when other is open", {modal_id, active_modal});
return;
}
blueslip.debug("close modal: " + modal_id);
Micromodal.close(modal_id);
}