mirror of https://github.com/zulip/zulip.git
overlays: Extract modal methods in separate file.
This commit is contained in:
parent
6713ad9d4d
commit
577a384845
|
@ -144,6 +144,7 @@ EXEMPT_FILES = make_set(
|
||||||
"web/src/message_view_header.js",
|
"web/src/message_view_header.js",
|
||||||
"web/src/message_viewport.js",
|
"web/src/message_viewport.js",
|
||||||
"web/src/messages_overlay_ui.ts",
|
"web/src/messages_overlay_ui.ts",
|
||||||
|
"web/src/modals.ts",
|
||||||
"web/src/muted_users_ui.js",
|
"web/src/muted_users_ui.js",
|
||||||
"web/src/narrow.js",
|
"web/src/narrow.js",
|
||||||
"web/src/narrow_history.js",
|
"web/src/narrow_history.js",
|
||||||
|
|
|
@ -6,7 +6,7 @@ import render_dialog_widget from "../templates/dialog_widget.hbs";
|
||||||
import type {AjaxRequestHandler} from "./channel";
|
import type {AjaxRequestHandler} from "./channel";
|
||||||
import {$t_html} from "./i18n";
|
import {$t_html} from "./i18n";
|
||||||
import * as loading from "./loading";
|
import * as loading from "./loading";
|
||||||
import * as overlays from "./overlays";
|
import * as modals from "./modals";
|
||||||
import * as ui_report from "./ui_report";
|
import * as ui_report from "./ui_report";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -22,7 +22,7 @@ import * as ui_report from "./ui_report";
|
||||||
* to avoid interference from other elements.
|
* to avoid interference from other elements.
|
||||||
*
|
*
|
||||||
* 3) For settings, we have a click handler in settings.js
|
* 3) For settings, we have a click handler in settings.js
|
||||||
* that will close the dialog via overlays.close_active_modal.
|
* that will close the dialog via modals.close_active_modal.
|
||||||
*
|
*
|
||||||
* 4) We assume that since this is a modal, you will
|
* 4) We assume that since this is a modal, you will
|
||||||
* only ever have one confirm dialog active at any
|
* only ever have one confirm dialog active at any
|
||||||
|
@ -99,7 +99,7 @@ export function show_dialog_spinner(): void {
|
||||||
|
|
||||||
// Supports a callback to be called once the modal finishes closing.
|
// Supports a callback to be called once the modal finishes closing.
|
||||||
export function close_modal(on_hidden_callback?: () => void): void {
|
export function close_modal(on_hidden_callback?: () => void): void {
|
||||||
overlays.close_modal("dialog_widget_modal", {on_hidden: on_hidden_callback});
|
modals.close_modal("dialog_widget_modal", {on_hidden: on_hidden_callback});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function launch(conf: DialogWidgetConfig): void {
|
export function launch(conf: DialogWidgetConfig): void {
|
||||||
|
@ -210,7 +210,7 @@ export function launch(conf: DialogWidgetConfig): void {
|
||||||
conf.on_click(e);
|
conf.on_click(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
overlays.open_modal("dialog_widget_modal", {
|
modals.open_modal("dialog_widget_modal", {
|
||||||
autoremove: true,
|
autoremove: true,
|
||||||
on_show() {
|
on_show() {
|
||||||
if (conf.focus_submit_on_open) {
|
if (conf.focus_submit_on_open) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ import * as message_edit from "./message_edit";
|
||||||
import * as message_edit_history from "./message_edit_history";
|
import * as message_edit_history from "./message_edit_history";
|
||||||
import * as message_lists from "./message_lists";
|
import * as message_lists from "./message_lists";
|
||||||
import * as message_scroll_state from "./message_scroll_state";
|
import * as message_scroll_state from "./message_scroll_state";
|
||||||
|
import * as modals from "./modals";
|
||||||
import * as narrow from "./narrow";
|
import * as narrow from "./narrow";
|
||||||
import * as narrow_state from "./narrow_state";
|
import * as narrow_state from "./narrow_state";
|
||||||
import * as navigate from "./navigate";
|
import * as navigate from "./navigate";
|
||||||
|
@ -279,8 +280,8 @@ export function process_escape_key(e) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overlays.is_modal_open()) {
|
if (modals.is_modal_open()) {
|
||||||
overlays.close_active_modal();
|
modals.close_active_modal();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,7 +486,7 @@ export function process_enter_key(e) {
|
||||||
|
|
||||||
// All custom logic for overlays/modals is above; if we're in a
|
// All custom logic for overlays/modals is above; if we're in a
|
||||||
// modal at this point, let the browser handle the event.
|
// modal at this point, let the browser handle the event.
|
||||||
if (overlays.is_modal_open()) {
|
if (modals.is_modal_open()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,7 +695,7 @@ export function process_hotkey(e, hotkey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// `list_util` will process the event in send later modal.
|
// `list_util` will process the event in send later modal.
|
||||||
if (overlays.is_modal_open() && overlays.active_modal() !== "#send_later_modal") {
|
if (modals.is_modal_open() && modals.active_modal() !== "#send_later_modal") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,239 @@
|
||||||
|
import $ from "jquery";
|
||||||
|
import Micromodal from "micromodal";
|
||||||
|
|
||||||
|
import * as blueslip from "./blueslip";
|
||||||
|
import * as overlay_util from "./overlay_util";
|
||||||
|
|
||||||
|
type Hook = () => void;
|
||||||
|
|
||||||
|
export type ModalConfig = {
|
||||||
|
autoremove?: boolean;
|
||||||
|
on_show?: () => void;
|
||||||
|
on_shown?: () => void;
|
||||||
|
on_hide?: () => void;
|
||||||
|
on_hidden?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const pre_open_hooks: Hook[] = [];
|
||||||
|
const pre_close_hooks: Hook[] = [];
|
||||||
|
|
||||||
|
export function register_pre_open_hook(func: Hook): void {
|
||||||
|
pre_open_hooks.push(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function register_pre_close_hook(func: Hook): void {
|
||||||
|
pre_close_hooks.push(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
function call_hooks(func_list: Hook[]): void {
|
||||||
|
for (const element of func_list) {
|
||||||
|
element();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function is_modal_open(): boolean {
|
||||||
|
return $(".micromodal").hasClass("modal--open");
|
||||||
|
}
|
||||||
|
|
||||||
|
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")!)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If conf.autoremove is true, the modal element will be removed from the DOM
|
||||||
|
// once the modal is hidden.
|
||||||
|
// conf also accepts the following optional properties:
|
||||||
|
// on_show: Callback to run when the modal is triggered to show.
|
||||||
|
// 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: string,
|
||||||
|
conf: ModalConfig & {recursive_call_count?: number} = {},
|
||||||
|
): 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 (!conf.recursive_call_count) {
|
||||||
|
conf.recursive_call_count = 1;
|
||||||
|
} else {
|
||||||
|
conf.recursive_call_count += 1;
|
||||||
|
}
|
||||||
|
if (conf.recursive_call_count > 50) {
|
||||||
|
blueslip.error("Modal incorrectly is still open", {modal_id});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
close_active_modal();
|
||||||
|
setTimeout(() => {
|
||||||
|
open_modal(modal_id, conf);
|
||||||
|
}, 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");
|
||||||
|
|
||||||
|
if (conf.on_shown) {
|
||||||
|
conf.on_shown();
|
||||||
|
}
|
||||||
|
} else if (animation_name === "mmfadeOut") {
|
||||||
|
// Call the on_hidden callback after the modal finishes hiding.
|
||||||
|
|
||||||
|
$micromodal.removeClass("modal--open");
|
||||||
|
if (conf.autoremove) {
|
||||||
|
$micromodal.remove();
|
||||||
|
}
|
||||||
|
if (conf.on_hidden) {
|
||||||
|
conf.on_hidden();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$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);
|
||||||
|
});
|
||||||
|
|
||||||
|
function on_show_callback(): void {
|
||||||
|
if (conf.on_show) {
|
||||||
|
conf.on_show();
|
||||||
|
}
|
||||||
|
overlay_util.disable_scrolling();
|
||||||
|
call_hooks(pre_open_hooks);
|
||||||
|
}
|
||||||
|
|
||||||
|
function on_close_callback(): void {
|
||||||
|
if (conf.on_hide) {
|
||||||
|
conf.on_hide();
|
||||||
|
}
|
||||||
|
overlay_util.enable_scrolling();
|
||||||
|
call_hooks(pre_close_hooks);
|
||||||
|
}
|
||||||
|
|
||||||
|
Micromodal.show(modal_id, {
|
||||||
|
disableFocus: true,
|
||||||
|
openClass: "modal--opening",
|
||||||
|
onShow: on_show_callback,
|
||||||
|
onClose: on_close_callback,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// `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: string, conf: Pick<ModalConfig, "on_hidden"> = {}): 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);
|
||||||
|
|
||||||
|
const id_selector = `#${CSS.escape(modal_id)}`;
|
||||||
|
const $micromodal = $(id_selector);
|
||||||
|
|
||||||
|
// On-hidden hooks should typically be registered in
|
||||||
|
// overlays.open_modal. However, we offer this alternative
|
||||||
|
// mechanism as a convenience for hooks only known when
|
||||||
|
// closing the modal.
|
||||||
|
$micromodal.find(".modal__container").on("animationend", (event) => {
|
||||||
|
const animation_name = (event.originalEvent as AnimationEvent).animationName;
|
||||||
|
if (animation_name === "mmfadeOut" && conf.on_hidden) {
|
||||||
|
conf.on_hidden();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Micromodal.close(modal_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function close_modal_if_open(modal_id: string): void {
|
||||||
|
if (modal_id === undefined) {
|
||||||
|
blueslip.error("Undefined id was passed into close_modal_if_open");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_modal_open()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const $micromodal = $(".micromodal.modal--open");
|
||||||
|
const active_modal_id = CSS.escape(`${CSS.escape($micromodal.attr("id") ?? "")}`);
|
||||||
|
if (active_modal_id === `${CSS.escape(modal_id)}`) {
|
||||||
|
Micromodal.close(`${CSS.escape($micromodal.attr("id") ?? "")}`);
|
||||||
|
} else {
|
||||||
|
blueslip.info(
|
||||||
|
`${active_modal_id} is the currently active modal and ${modal_id} is already closed.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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") ?? "")}`);
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
import $ from "jquery";
|
import $ from "jquery";
|
||||||
import Micromodal from "micromodal";
|
|
||||||
|
|
||||||
import * as blueslip from "./blueslip";
|
import * as blueslip from "./blueslip";
|
||||||
import * as overlay_util from "./overlay_util";
|
import * as overlay_util from "./overlay_util";
|
||||||
|
@ -17,20 +16,9 @@ type Overlay = {
|
||||||
close_handler: () => void;
|
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 active_overlay: Overlay | undefined;
|
||||||
let open_overlay_name: string | undefined;
|
let open_overlay_name: string | undefined;
|
||||||
|
|
||||||
// Used for both overlays and modals.
|
|
||||||
// We could split these hooks so that they are separate for
|
|
||||||
// overlays and modals if we need to.
|
|
||||||
const pre_open_hooks: Hook[] = [];
|
const pre_open_hooks: Hook[] = [];
|
||||||
const pre_close_hooks: Hook[] = [];
|
const pre_close_hooks: Hook[] = [];
|
||||||
|
|
||||||
|
@ -57,10 +45,6 @@ export function is_active(): boolean {
|
||||||
return Boolean(open_overlay_name);
|
return Boolean(open_overlay_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function is_modal_open(): boolean {
|
|
||||||
return $(".micromodal").hasClass("modal--open");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function info_overlay_open(): boolean {
|
export function info_overlay_open(): boolean {
|
||||||
return open_overlay_name === "informationalOverlays";
|
return open_overlay_name === "informationalOverlays";
|
||||||
}
|
}
|
||||||
|
@ -89,16 +73,6 @@ export function scheduled_messages_open(): boolean {
|
||||||
return open_overlay_name === "scheduled";
|
return open_overlay_name === "scheduled";
|
||||||
}
|
}
|
||||||
|
|
||||||
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")!)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function open_overlay(opts: OverlayOptions): void {
|
export function open_overlay(opts: OverlayOptions): void {
|
||||||
call_hooks(pre_open_hooks);
|
call_hooks(pre_open_hooks);
|
||||||
|
|
||||||
|
@ -141,131 +115,6 @@ export function open_overlay(opts: OverlayOptions): void {
|
||||||
$("#navbar-fixed-container").attr("aria-hidden", "true");
|
$("#navbar-fixed-container").attr("aria-hidden", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If conf.autoremove is true, the modal element will be removed from the DOM
|
|
||||||
// once the modal is hidden.
|
|
||||||
// conf also accepts the following optional properties:
|
|
||||||
// on_show: Callback to run when the modal is triggered to show.
|
|
||||||
// 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: string,
|
|
||||||
conf: ModalConfig & {recursive_call_count?: number} = {},
|
|
||||||
): 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 (!conf.recursive_call_count) {
|
|
||||||
conf.recursive_call_count = 1;
|
|
||||||
} else {
|
|
||||||
conf.recursive_call_count += 1;
|
|
||||||
}
|
|
||||||
if (conf.recursive_call_count > 50) {
|
|
||||||
blueslip.error("Modal incorrectly is still open", {modal_id});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
close_active_modal();
|
|
||||||
setTimeout(() => {
|
|
||||||
open_modal(modal_id, conf);
|
|
||||||
}, 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");
|
|
||||||
|
|
||||||
if (conf.on_shown) {
|
|
||||||
conf.on_shown();
|
|
||||||
}
|
|
||||||
} else if (animation_name === "mmfadeOut") {
|
|
||||||
// Call the on_hidden callback after the modal finishes hiding.
|
|
||||||
|
|
||||||
$micromodal.removeClass("modal--open");
|
|
||||||
if (conf.autoremove) {
|
|
||||||
$micromodal.remove();
|
|
||||||
}
|
|
||||||
if (conf.on_hidden) {
|
|
||||||
conf.on_hidden();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$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);
|
|
||||||
});
|
|
||||||
|
|
||||||
function on_show_callback(): void {
|
|
||||||
if (conf.on_show) {
|
|
||||||
conf.on_show();
|
|
||||||
}
|
|
||||||
overlay_util.disable_scrolling();
|
|
||||||
call_hooks(pre_open_hooks);
|
|
||||||
}
|
|
||||||
|
|
||||||
function on_close_callback(): void {
|
|
||||||
if (conf.on_hide) {
|
|
||||||
conf.on_hide();
|
|
||||||
}
|
|
||||||
overlay_util.enable_scrolling();
|
|
||||||
call_hooks(pre_close_hooks);
|
|
||||||
}
|
|
||||||
|
|
||||||
Micromodal.show(modal_id, {
|
|
||||||
disableFocus: true,
|
|
||||||
openClass: "modal--opening",
|
|
||||||
onShow: on_show_callback,
|
|
||||||
onClose: on_close_callback,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function close_overlay(name: string): void {
|
export function close_overlay(name: string): void {
|
||||||
call_hooks(pre_close_hooks);
|
call_hooks(pre_close_hooks);
|
||||||
|
|
||||||
|
@ -304,74 +153,6 @@ export function close_active(): void {
|
||||||
close_overlay(open_overlay_name);
|
close_overlay(open_overlay_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// `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: string, conf: Pick<ModalConfig, "on_hidden"> = {}): 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);
|
|
||||||
|
|
||||||
const id_selector = `#${CSS.escape(modal_id)}`;
|
|
||||||
const $micromodal = $(id_selector);
|
|
||||||
|
|
||||||
// On-hidden hooks should typically be registered in
|
|
||||||
// overlays.open_modal. However, we offer this alternative
|
|
||||||
// mechanism as a convenience for hooks only known when
|
|
||||||
// closing the modal.
|
|
||||||
$micromodal.find(".modal__container").on("animationend", (event) => {
|
|
||||||
const animation_name = (event.originalEvent as AnimationEvent).animationName;
|
|
||||||
if (animation_name === "mmfadeOut" && conf.on_hidden) {
|
|
||||||
conf.on_hidden();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Micromodal.close(modal_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function close_modal_if_open(modal_id: string): void {
|
|
||||||
if (modal_id === undefined) {
|
|
||||||
blueslip.error("Undefined id was passed into close_modal_if_open");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_modal_open()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const $micromodal = $(".micromodal.modal--open");
|
|
||||||
const active_modal_id = CSS.escape(`${CSS.escape($micromodal.attr("id") ?? "")}`);
|
|
||||||
if (active_modal_id === `${CSS.escape(modal_id)}`) {
|
|
||||||
Micromodal.close(`${CSS.escape($micromodal.attr("id") ?? "")}`);
|
|
||||||
} else {
|
|
||||||
blueslip.info(
|
|
||||||
`${active_modal_id} is the currently active modal and ${modal_id} is already closed.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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") ?? "")}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function close_for_hash_change(): void {
|
export function close_for_hash_change(): void {
|
||||||
if (open_overlay_name) {
|
if (open_overlay_name) {
|
||||||
close_overlay(open_overlay_name);
|
close_overlay(open_overlay_name);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as overlays from './overlays';
|
import * as modals from "./modals";
|
||||||
|
import * as overlays from "./overlays";
|
||||||
|
|
||||||
export function any_active(): boolean {
|
export function any_active(): boolean {
|
||||||
return overlays.is_active() || overlays.is_modal_open();
|
return overlays.is_active() || modals.is_modal_open();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import tippy from "tippy.js";
|
||||||
|
|
||||||
import * as blueslip from "./blueslip";
|
import * as blueslip from "./blueslip";
|
||||||
import {media_breakpoints_num} from "./css_variables";
|
import {media_breakpoints_num} from "./css_variables";
|
||||||
|
import * as modals from "./modals";
|
||||||
import * as overlays from "./overlays";
|
import * as overlays from "./overlays";
|
||||||
import * as popovers from "./popovers";
|
import * as popovers from "./popovers";
|
||||||
|
|
||||||
|
@ -259,6 +260,8 @@ export function initialize() {
|
||||||
/* Configure popovers to hide when toggling overlays. */
|
/* Configure popovers to hide when toggling overlays. */
|
||||||
overlays.register_pre_open_hook(popovers.hide_all);
|
overlays.register_pre_open_hook(popovers.hide_all);
|
||||||
overlays.register_pre_close_hook(popovers.hide_all);
|
overlays.register_pre_close_hook(popovers.hide_all);
|
||||||
|
modals.register_pre_open_hook(popovers.hide_all);
|
||||||
|
modals.register_pre_close_hook(popovers.hide_all);
|
||||||
|
|
||||||
let last_scroll = 0;
|
let last_scroll = 0;
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,13 @@ import * as channel from "./channel";
|
||||||
import {$t, $t_html} from "./i18n";
|
import {$t, $t_html} from "./i18n";
|
||||||
import * as loading from "./loading";
|
import * as loading from "./loading";
|
||||||
import * as message_store from "./message_store";
|
import * as message_store from "./message_store";
|
||||||
import * as overlays from "./overlays";
|
import * as modals from "./modals";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
import * as ui_report from "./ui_report";
|
import * as ui_report from "./ui_report";
|
||||||
|
|
||||||
export function show_user_list(message_id) {
|
export function show_user_list(message_id) {
|
||||||
$("body").append(render_read_receipts_modal());
|
$("body").append(render_read_receipts_modal());
|
||||||
overlays.open_modal("read_receipts_modal", {
|
modals.open_modal("read_receipts_modal", {
|
||||||
autoremove: true,
|
autoremove: true,
|
||||||
on_show() {
|
on_show() {
|
||||||
const message = message_store.get(message_id);
|
const message = message_store.get(message_id);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import render_send_later_modal_options from "../templates/send_later_modal_optio
|
||||||
import * as compose from "./compose";
|
import * as compose from "./compose";
|
||||||
import * as compose_validate from "./compose_validate";
|
import * as compose_validate from "./compose_validate";
|
||||||
import * as flatpickr from "./flatpickr";
|
import * as flatpickr from "./flatpickr";
|
||||||
import * as overlays from "./overlays";
|
import * as modals from "./modals";
|
||||||
import * as popover_menus from "./popover_menus";
|
import * as popover_menus from "./popover_menus";
|
||||||
import * as scheduled_messages from "./scheduled_messages";
|
import * as scheduled_messages from "./scheduled_messages";
|
||||||
import {parse_html} from "./ui_util";
|
import {parse_html} from "./ui_util";
|
||||||
|
@ -31,7 +31,7 @@ export function open_send_later_menu() {
|
||||||
$("body").append(render_send_later_modal(filtered_send_opts));
|
$("body").append(render_send_later_modal(filtered_send_opts));
|
||||||
let interval;
|
let interval;
|
||||||
|
|
||||||
overlays.open_modal("send_later_modal", {
|
modals.open_modal("send_later_modal", {
|
||||||
autoremove: true,
|
autoremove: true,
|
||||||
on_show() {
|
on_show() {
|
||||||
interval = setInterval(
|
interval = setInterval(
|
||||||
|
@ -100,7 +100,7 @@ export function open_send_later_menu() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function do_schedule_message(send_at_time) {
|
export function do_schedule_message(send_at_time) {
|
||||||
overlays.close_modal_if_open("send_later_modal");
|
modals.close_modal_if_open("send_later_modal");
|
||||||
|
|
||||||
if (!Number.isInteger(send_at_time)) {
|
if (!Number.isInteger(send_at_time)) {
|
||||||
// Convert to timestamp if this is not a timestamp.
|
// Convert to timestamp if this is not a timestamp.
|
||||||
|
|
|
@ -8,6 +8,7 @@ import render_settings_tab from "../templates/settings_tab.hbs";
|
||||||
import * as browser_history from "./browser_history";
|
import * as browser_history from "./browser_history";
|
||||||
import * as flatpickr from "./flatpickr";
|
import * as flatpickr from "./flatpickr";
|
||||||
import {$t} from "./i18n";
|
import {$t} from "./i18n";
|
||||||
|
import * as modals from "./modals";
|
||||||
import * as overlays from "./overlays";
|
import * as overlays from "./overlays";
|
||||||
import {page_params} from "./page_params";
|
import {page_params} from "./page_params";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
|
@ -25,7 +26,7 @@ export let settings_label;
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
$("#settings_overlay_container").on("click", (e) => {
|
$("#settings_overlay_container").on("click", (e) => {
|
||||||
if (!overlays.is_modal_open()) {
|
if (!modals.is_modal_open()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($(e.target).closest(".micromodal").length > 0) {
|
if ($(e.target).closest(".micromodal").length > 0) {
|
||||||
|
@ -38,7 +39,7 @@ $(() => {
|
||||||
// event to the parent container otherwise the modal will not open. This
|
// event to the parent container otherwise the modal will not open. This
|
||||||
// is so because this event handler will get fired on any click in settings
|
// is so because this event handler will get fired on any click in settings
|
||||||
// overlay and subsequently close any open modal.
|
// overlay and subsequently close any open modal.
|
||||||
overlays.close_active_modal();
|
modals.close_active_modal();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import * as custom_profile_fields_ui from "./custom_profile_fields_ui";
|
||||||
import * as dialog_widget from "./dialog_widget";
|
import * as dialog_widget from "./dialog_widget";
|
||||||
import {$t_html} from "./i18n";
|
import {$t_html} from "./i18n";
|
||||||
import * as keydown_util from "./keydown_util";
|
import * as keydown_util from "./keydown_util";
|
||||||
|
import * as modals from "./modals";
|
||||||
import * as overlays from "./overlays";
|
import * as overlays from "./overlays";
|
||||||
import {page_params} from "./page_params";
|
import {page_params} from "./page_params";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
|
@ -361,7 +362,7 @@ export function set_up() {
|
||||||
$("body").append(render_settings_api_key_modal());
|
$("body").append(render_settings_api_key_modal());
|
||||||
setup_api_key_modal();
|
setup_api_key_modal();
|
||||||
$("#api_key_status").hide();
|
$("#api_key_status").hide();
|
||||||
overlays.open_modal("api_key_modal", {
|
modals.open_modal("api_key_modal", {
|
||||||
autoremove: true,
|
autoremove: true,
|
||||||
on_show() {
|
on_show() {
|
||||||
$("#get_api_key_password").trigger("focus");
|
$("#get_api_key_password").trigger("focus");
|
||||||
|
|
|
@ -10,7 +10,7 @@ import $ from "jquery";
|
||||||
import render_login_to_access_modal from "../templates/login_to_access.hbs";
|
import render_login_to_access_modal from "../templates/login_to_access.hbs";
|
||||||
|
|
||||||
import * as browser_history from "./browser_history";
|
import * as browser_history from "./browser_history";
|
||||||
import * as overlays from "./overlays";
|
import * as modals from "./modals";
|
||||||
import {page_params} from "./page_params";
|
import {page_params} from "./page_params";
|
||||||
|
|
||||||
export function current_hash_as_next(): string {
|
export function current_hash_as_next(): string {
|
||||||
|
@ -40,7 +40,7 @@ export function login_to_access(empty_narrow?: boolean): void {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
overlays.open_modal("login_to_access_modal", {
|
modals.open_modal("login_to_access_modal", {
|
||||||
autoremove: true,
|
autoremove: true,
|
||||||
on_hide() {
|
on_hide() {
|
||||||
browser_history.return_to_web_public_hash();
|
browser_history.return_to_web_public_hash();
|
||||||
|
|
|
@ -24,7 +24,7 @@ import {$t, $t_html} from "./i18n";
|
||||||
import * as integration_url_modal from "./integration_url_modal";
|
import * as integration_url_modal from "./integration_url_modal";
|
||||||
import * as ListWidget from "./list_widget";
|
import * as ListWidget from "./list_widget";
|
||||||
import * as loading from "./loading";
|
import * as loading from "./loading";
|
||||||
import * as overlays from "./overlays";
|
import * as modals from "./modals";
|
||||||
import {page_params} from "./page_params";
|
import {page_params} from "./page_params";
|
||||||
import * as people from "./people";
|
import * as people from "./people";
|
||||||
import * as settings_config from "./settings_config";
|
import * as settings_config from "./settings_config";
|
||||||
|
@ -74,7 +74,7 @@ function compare_by_name(a, b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get_user_id_if_user_profile_modal_open() {
|
export function get_user_id_if_user_profile_modal_open() {
|
||||||
if (overlays.is_modal_open() && overlays.active_modal() === "#user-profile-modal") {
|
if (modals.is_modal_open() && modals.active_modal() === "#user-profile-modal") {
|
||||||
const user_id = $("#user-profile-modal").data("user-id");
|
const user_id = $("#user-profile-modal").data("user-id");
|
||||||
return user_id;
|
return user_id;
|
||||||
}
|
}
|
||||||
|
@ -294,7 +294,7 @@ export function get_custom_profile_field_data(user, field, field_types) {
|
||||||
|
|
||||||
export function hide_user_profile() {
|
export function hide_user_profile() {
|
||||||
user_streams_list_widget = undefined;
|
user_streams_list_widget = undefined;
|
||||||
overlays.close_modal_if_open("user-profile-modal");
|
modals.close_modal_if_open("user-profile-modal");
|
||||||
}
|
}
|
||||||
|
|
||||||
function show_manage_user_tab(target) {
|
function show_manage_user_tab(target) {
|
||||||
|
@ -370,7 +370,7 @@ export function show_user_profile(user, default_tab_key = "profile-tab") {
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#user-profile-modal-holder").html(render_user_profile_modal(args));
|
$("#user-profile-modal-holder").html(render_user_profile_modal(args));
|
||||||
overlays.open_modal("user-profile-modal", {autoremove: true});
|
modals.open_modal("user-profile-modal", {autoremove: true});
|
||||||
$(".tabcontent").hide();
|
$(".tabcontent").hide();
|
||||||
|
|
||||||
let default_tab = 0;
|
let default_tab = 0;
|
||||||
|
|
|
@ -55,6 +55,10 @@ const narrow_state = mock_esm("../src/narrow_state", {
|
||||||
is_message_feed_visible: () => true,
|
is_message_feed_visible: () => true,
|
||||||
});
|
});
|
||||||
const navigate = mock_esm("../src/navigate");
|
const navigate = mock_esm("../src/navigate");
|
||||||
|
const modals = mock_esm("../src/modals", {
|
||||||
|
is_modal_open: () => false,
|
||||||
|
active_modal: () => undefined,
|
||||||
|
});
|
||||||
const overlays = mock_esm("../src/overlays", {
|
const overlays = mock_esm("../src/overlays", {
|
||||||
is_active: () => false,
|
is_active: () => false,
|
||||||
settings_open: () => false,
|
settings_open: () => false,
|
||||||
|
@ -63,8 +67,6 @@ const overlays = mock_esm("../src/overlays", {
|
||||||
drafts_open: () => false,
|
drafts_open: () => false,
|
||||||
scheduled_messages_open: () => false,
|
scheduled_messages_open: () => false,
|
||||||
info_overlay_open: () => false,
|
info_overlay_open: () => false,
|
||||||
is_modal_open: () => false,
|
|
||||||
active_modal: () => undefined,
|
|
||||||
});
|
});
|
||||||
const popovers = mock_esm("../src/user_card_popover", {
|
const popovers = mock_esm("../src/user_card_popover", {
|
||||||
manage_menu: {
|
manage_menu: {
|
||||||
|
@ -348,7 +350,7 @@ run_test("drafts closed launch", ({override}) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
run_test("modal open", ({override}) => {
|
run_test("modal open", ({override}) => {
|
||||||
override(overlays, "is_modal_open", () => true);
|
override(modals, "is_modal_open", () => true);
|
||||||
test_normal_typing();
|
test_normal_typing();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue