mirror of https://github.com/zulip/zulip.git
ts: Convert `overlays.js` to TypeScript.
Refactored some code as well to avoid unneccesaary `undefined` checks in `overlays`. To be exact created an action_overlay object of type `Overlay` such that the `reset_state` is just `action_overlay = undefined` and having attributes like `$element` and `close_handler`. This would ensure that if you have an `active_overlay`, there will be a registered `close handler` attached to it without needing to write additional checks for `close_handler` just to satisfy type checker.
This commit is contained in:
parent
0de0743e91
commit
fcbc6dcae4
|
@ -123,7 +123,7 @@ EXEMPT_FILES = make_set(
|
|||
"web/src/navbar_alerts.js",
|
||||
"web/src/navigate.js",
|
||||
"web/src/notifications.js",
|
||||
"web/src/overlays.js",
|
||||
"web/src/overlays.ts",
|
||||
"web/src/padded_widget.ts",
|
||||
"web/src/page_params.ts",
|
||||
"web/src/pm_list.js",
|
||||
|
|
|
@ -3,80 +3,99 @@ import Micromodal from "micromodal";
|
|||
|
||||
import * as blueslip from "./blueslip";
|
||||
|
||||
let $active_overlay;
|
||||
let close_handler;
|
||||
let open_overlay_name;
|
||||
type Hook = () => void;
|
||||
|
||||
const pre_open_hooks = [];
|
||||
const pre_close_hooks = [];
|
||||
type OverlayOptions = {
|
||||
name: string;
|
||||
$overlay: JQuery<HTMLElement>;
|
||||
on_close: () => void;
|
||||
};
|
||||
|
||||
function reset_state() {
|
||||
$active_overlay = undefined;
|
||||
close_handler = undefined;
|
||||
type Overlay = {
|
||||
$element: JQuery<HTMLElement>;
|
||||
close_handler: () => void;
|
||||
};
|
||||
|
||||
export type ModalConfig = {
|
||||
autoremove?: boolean;
|
||||
on_show?: () => void;
|
||||
on_shown?: () => void;
|
||||
on_hide?: () => void;
|
||||
on_hidden?: () => void;
|
||||
};
|
||||
|
||||
let active_overlay: Overlay | undefined;
|
||||
let open_overlay_name: string | undefined;
|
||||
|
||||
const pre_open_hooks: Hook[] = [];
|
||||
const pre_close_hooks: Hook[] = [];
|
||||
|
||||
function reset_state(): void {
|
||||
active_overlay = undefined;
|
||||
open_overlay_name = undefined;
|
||||
}
|
||||
|
||||
export function register_pre_open_hook(func) {
|
||||
export function register_pre_open_hook(func: Hook): void {
|
||||
pre_open_hooks.push(func);
|
||||
}
|
||||
|
||||
export function register_pre_close_hook(func) {
|
||||
export function register_pre_close_hook(func: Hook): void {
|
||||
pre_close_hooks.push(func);
|
||||
}
|
||||
|
||||
function call_hooks(func_list) {
|
||||
function call_hooks(func_list: Hook[]): void {
|
||||
for (const element of func_list) {
|
||||
element();
|
||||
}
|
||||
}
|
||||
|
||||
export function is_active() {
|
||||
export function is_active(): boolean {
|
||||
return Boolean(open_overlay_name);
|
||||
}
|
||||
|
||||
export function is_modal_open() {
|
||||
export function is_modal_open(): boolean {
|
||||
return $(".micromodal").hasClass("modal--open");
|
||||
}
|
||||
|
||||
export function is_overlay_or_modal_open() {
|
||||
export function is_overlay_or_modal_open(): boolean {
|
||||
return is_active() || is_modal_open();
|
||||
}
|
||||
|
||||
export function info_overlay_open() {
|
||||
export function info_overlay_open(): boolean {
|
||||
return open_overlay_name === "informationalOverlays";
|
||||
}
|
||||
|
||||
export function settings_open() {
|
||||
export function settings_open(): boolean {
|
||||
return open_overlay_name === "settings";
|
||||
}
|
||||
|
||||
export function streams_open() {
|
||||
export function streams_open(): boolean {
|
||||
return open_overlay_name === "subscriptions";
|
||||
}
|
||||
|
||||
export function groups_open() {
|
||||
export function groups_open(): boolean {
|
||||
return open_overlay_name === "group_subscriptions";
|
||||
}
|
||||
|
||||
export function lightbox_open() {
|
||||
export function lightbox_open(): boolean {
|
||||
return open_overlay_name === "lightbox";
|
||||
}
|
||||
|
||||
export function drafts_open() {
|
||||
export function drafts_open(): boolean {
|
||||
return open_overlay_name === "drafts";
|
||||
}
|
||||
|
||||
export function active_modal() {
|
||||
export 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"))}`;
|
||||
return `#${CSS.escape($micromodal.attr("id")!)}`;
|
||||
}
|
||||
|
||||
export function open_overlay(opts) {
|
||||
export function open_overlay(opts: OverlayOptions): void {
|
||||
call_hooks(pre_open_hooks);
|
||||
|
||||
if (!opts.name || !opts.$overlay || !opts.on_close) {
|
||||
|
@ -84,13 +103,13 @@ export function open_overlay(opts) {
|
|||
return;
|
||||
}
|
||||
|
||||
if ($active_overlay || open_overlay_name || close_handler) {
|
||||
if (active_overlay || open_overlay_name) {
|
||||
blueslip.error(
|
||||
"Programming error — trying to open " +
|
||||
opts.name +
|
||||
" before closing " +
|
||||
open_overlay_name,
|
||||
`Programming error - trying to open ${opts.name} before closing ${
|
||||
open_overlay_name || "undefined"
|
||||
}`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -105,17 +124,18 @@ export function open_overlay(opts) {
|
|||
}
|
||||
|
||||
open_overlay_name = opts.name;
|
||||
$active_overlay = opts.$overlay;
|
||||
opts.$overlay.addClass("show");
|
||||
active_overlay = {
|
||||
$element: opts.$overlay,
|
||||
close_handler() {
|
||||
opts.on_close();
|
||||
reset_state();
|
||||
},
|
||||
};
|
||||
|
||||
opts.$overlay.addClass("show");
|
||||
opts.$overlay.attr("aria-hidden", "false");
|
||||
$(".app").attr("aria-hidden", "true");
|
||||
$(".header").attr("aria-hidden", "true");
|
||||
|
||||
close_handler = function () {
|
||||
opts.on_close();
|
||||
reset_state();
|
||||
};
|
||||
}
|
||||
|
||||
// If conf.autoremove is true, the modal element will be removed from the DOM
|
||||
|
@ -125,7 +145,10 @@ export function open_overlay(opts) {
|
|||
// on_shown: Callback to run when the modal is shown.
|
||||
// on_hide: Callback to run when the modal is triggered to hide.
|
||||
// on_hidden: Callback to run when the modal is hidden.
|
||||
export function open_modal(modal_id, conf = {}) {
|
||||
export function open_modal(
|
||||
modal_id: string,
|
||||
conf: ModalConfig & {recursive_call_count?: number} = {},
|
||||
): void {
|
||||
if (modal_id === undefined) {
|
||||
blueslip.error("Undefined id was passed into open_modal");
|
||||
return;
|
||||
|
@ -133,7 +156,7 @@ export function open_modal(modal_id, conf = {}) {
|
|||
|
||||
// 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[0] === "#") {
|
||||
if (modal_id.startsWith("#")) {
|
||||
blueslip.error("hash-based selector passed in to open_modal: " + modal_id);
|
||||
return;
|
||||
}
|
||||
|
@ -175,7 +198,7 @@ export function open_modal(modal_id, conf = {}) {
|
|||
const $micromodal = $(id_selector);
|
||||
|
||||
$micromodal.find(".modal__container").on("animationend", (event) => {
|
||||
const animation_name = event.originalEvent.animationName;
|
||||
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
|
||||
|
@ -210,7 +233,7 @@ export function open_modal(modal_id, conf = {}) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (document.getSelection().type === "Range") {
|
||||
if (document.getSelection()?.type === "Range") {
|
||||
return;
|
||||
}
|
||||
close_modal(modal_id);
|
||||
|
@ -224,11 +247,11 @@ export function open_modal(modal_id, conf = {}) {
|
|||
});
|
||||
}
|
||||
|
||||
export function close_overlay(name) {
|
||||
export function close_overlay(name: string): void {
|
||||
call_hooks(pre_close_hooks);
|
||||
|
||||
if (name !== open_overlay_name) {
|
||||
blueslip.error("Trying to close " + name + " when " + open_overlay_name + " is open.");
|
||||
blueslip.error(`Trying to close ${name} when ${open_overlay_name || "undefined"} is open.`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -237,23 +260,23 @@ export function close_overlay(name) {
|
|||
return;
|
||||
}
|
||||
|
||||
blueslip.debug("close overlay: " + name);
|
||||
|
||||
$active_overlay.removeClass("show");
|
||||
|
||||
$active_overlay.attr("aria-hidden", "true");
|
||||
$(".app").attr("aria-hidden", "false");
|
||||
$(".header").attr("aria-hidden", "false");
|
||||
|
||||
if (!close_handler) {
|
||||
blueslip.error("Overlay close handler for " + name + " not properly set up.");
|
||||
if (active_overlay === undefined) {
|
||||
blueslip.error("close_overlay called without checking is_active()");
|
||||
return;
|
||||
}
|
||||
|
||||
close_handler();
|
||||
blueslip.debug("close overlay: " + name);
|
||||
|
||||
active_overlay.$element.removeClass("show");
|
||||
|
||||
active_overlay.$element.attr("aria-hidden", "true");
|
||||
$(".app").attr("aria-hidden", "false");
|
||||
$(".header").attr("aria-hidden", "false");
|
||||
|
||||
active_overlay.close_handler();
|
||||
}
|
||||
|
||||
export function close_active() {
|
||||
export function close_active(): void {
|
||||
if (!open_overlay_name) {
|
||||
blueslip.warn("close_active() called without checking is_active()");
|
||||
return;
|
||||
|
@ -264,7 +287,7 @@ export function close_active() {
|
|||
|
||||
// `conf` is an object with the following optional properties:
|
||||
// * on_hidden: Callback to run when the modal finishes hiding.
|
||||
export function close_modal(modal_id, conf = {}) {
|
||||
export function close_modal(modal_id: string, conf: Pick<ModalConfig, "on_hidden"> = {}): void {
|
||||
if (modal_id === undefined) {
|
||||
blueslip.error("Undefined id was passed into close_modal");
|
||||
return;
|
||||
|
@ -277,7 +300,7 @@ export function close_modal(modal_id, conf = {}) {
|
|||
|
||||
if (active_modal() !== `#${CSS.escape(modal_id)}`) {
|
||||
blueslip.error(
|
||||
"Trying to close " + modal_id + " modal when " + active_modal() + " is open.",
|
||||
`Trying to close ${modal_id} modal when ${active_modal() || "undefined"} is open.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -292,7 +315,7 @@ export function close_modal(modal_id, conf = {}) {
|
|||
// mechanism as a convenience for hooks only known when
|
||||
// closing the modal.
|
||||
$micromodal.find(".modal__container").on("animationend", (event) => {
|
||||
const animation_name = event.originalEvent.animationName;
|
||||
const animation_name = (event.originalEvent as AnimationEvent).animationName;
|
||||
if (animation_name === "mmfadeOut" && conf.on_hidden) {
|
||||
conf.on_hidden();
|
||||
}
|
||||
|
@ -301,28 +324,28 @@ export function close_modal(modal_id, conf = {}) {
|
|||
Micromodal.close(modal_id);
|
||||
}
|
||||
|
||||
export function close_active_modal() {
|
||||
export 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"))}`);
|
||||
Micromodal.close(`${CSS.escape($micromodal.attr("id") || "")}`);
|
||||
}
|
||||
|
||||
export function close_for_hash_change() {
|
||||
export function close_for_hash_change(): void {
|
||||
$("div.overlay.show").removeClass("show");
|
||||
if ($active_overlay) {
|
||||
close_handler();
|
||||
if (active_overlay) {
|
||||
active_overlay.close_handler();
|
||||
}
|
||||
}
|
||||
|
||||
export function initialize() {
|
||||
export function initialize(): void {
|
||||
$("body").on("click", "div.overlay, div.overlay .exit", (e) => {
|
||||
let $target = $(e.target);
|
||||
|
||||
if (document.getSelection().type === "Range") {
|
||||
if (document.getSelection()?.type === "Range") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -340,7 +363,7 @@ export function initialize() {
|
|||
return;
|
||||
}
|
||||
|
||||
const target_name = $target.attr("data-overlay");
|
||||
const target_name = $target.attr("data-overlay")!;
|
||||
|
||||
close_overlay(target_name);
|
||||
|
Loading…
Reference in New Issue