actions_popover: Use tippyjs instead of bootstrap to display popover.

Fixes #23494
Popover now automatically displays on left when there is not
enough space top or bottom of the reference element.
This commit is contained in:
Aman Agrawal 2022-11-29 07:05:14 +00:00 committed by Tim Abbott
parent 6a1ae465cc
commit eb45925b1a
9 changed files with 241 additions and 313 deletions

View File

@ -59,12 +59,14 @@ const overlays = mock_esm("../../static/js/overlays", {
is_overlay_or_modal_open: () => overlays.is_modal_open() || overlays.is_active(),
});
const popovers = mock_esm("../../static/js/popovers", {
actions_popped: () => false,
user_info_manage_menu_popped: () => false,
message_info_popped: () => false,
user_sidebar_popped: () => false,
user_info_popped: () => false,
});
const popover_menus = mock_esm("../../static/js/popover_menus", {
actions_popped: () => false,
});
const reactions = mock_esm("../../static/js/reactions");
const search = mock_esm("../../static/js/search");
const settings_data = mock_esm("../../static/js/settings_data");
@ -366,7 +368,7 @@ run_test("misc", ({override}) => {
assert_mapping("s", narrow, "by_recipient");
assert_mapping("S", narrow, "by_topic");
assert_mapping("u", popovers, "show_sender_info");
assert_mapping("i", popovers, "open_message_menu");
assert_mapping("i", popover_menus, "toggle_message_actions_menu");
assert_mapping(":", reactions, "open_reactions_popover", true);
assert_mapping(">", compose_actions, "quote_and_reply");
assert_mapping("e", message_edit, "start");

View File

@ -29,9 +29,6 @@ const message_lists = mock_esm("../../static/js/message_lists", {
},
},
});
mock_esm("../../static/js/message_viewport", {
height: () => 500,
});
mock_esm("../../static/js/stream_popover", {
hide_stream_popover: noop,
hide_topic_popover: noop,
@ -43,7 +40,6 @@ mock_esm("../../static/js/stream_popover", {
const people = zrequire("people");
const user_status = zrequire("user_status");
const message_edit = zrequire("message_edit");
const popovers = zrequire("popovers");
const alice = {
@ -218,54 +214,3 @@ test_ui("sender_hover", ({override, mock_template}) => {
// todo: load image
});
test_ui("actions_popover", ({override, mock_template}) => {
override($.fn, "popover", noop);
const $target = $.create("click target");
const handler = $("#main_div").get_on_handler("click", ".actions_hover");
const message = {
id: 999,
topic: "Actions (1)",
type: "stream",
stream_id: 123,
sent_by_me: true,
};
message_lists.current.get = (msg_id) => {
assert.equal(msg_id, message.id);
return message;
};
message_lists.current.view.message_containers.get = (msg_id) => {
assert.equal(msg_id, message.id);
return {
is_hidden: false,
};
};
override(page_params, "realm_allow_message_editing", true);
override(page_params, "realm_message_content_edit_limit_seconds", null);
assert.equal(message_edit.get_editability(message), message_edit.editability_types.FULL);
$target.closest = (sel) => {
assert.equal(sel, ".message_row");
return {
toggleClass: noop,
};
};
mock_template("actions_popover_template.hbs", false, () => "actions-template");
mock_template("actions_popover_content.hbs", false, (opts) => {
// TODO: Test all the properties of the popover
assert.equal(
opts.conversation_time_uri,
"http://zulip.zulipdev.com/#narrow/stream/123-unknown/topic/Actions.20.281.29/near/999",
);
return "actions-content";
});
handler.call($target, e);
});

View File

@ -242,11 +242,6 @@ export function initialize() {
});
$("body").on("click", ".reveal_hidden_message", (e) => {
// Hide actions popover to keep its options
// in sync with revealed/hidden state of
// muted user's message.
popovers.hide_actions_popover();
const message_id = rows.id($(e.currentTarget).closest(".message_row"));
message_lists.current.view.reveal_hidden_message(message_id);
e.stopPropagation();

View File

@ -666,7 +666,7 @@ export function build_emoji_popover($elt, id) {
register_popover_events($popover);
}
export function toggle_emoji_popover(element, id) {
export function toggle_emoji_popover(element, id, coming_from_actions_popover) {
const $last_popover_elem = $current_message_emoji_popover_elem;
popovers.hide_all();
if ($last_popover_elem !== undefined && $last_popover_elem.get()[0] === element) {
@ -683,7 +683,7 @@ export function toggle_emoji_popover(element, id) {
if (user_status_ui.user_status_picker_open()) {
build_emoji_popover($elt, id, true);
} else if ($elt.data("popover") === undefined) {
} else if ($elt.data("popover") === undefined || coming_from_actions_popover) {
// Keep the element over which the popover is based off visible.
$elt.addClass("reaction_button_visible");
build_emoji_popover($elt, id);
@ -748,19 +748,6 @@ export function register_click_handlers() {
toggle_emoji_popover(this, message_id);
});
$("body").on("click", ".actions_popover .reaction_button", (e) => {
const message_id = $(e.currentTarget).data("message-id");
e.preventDefault();
e.stopPropagation();
// HACK: Because we need the popover to be based off an
// element that definitely exists in the page even if the
// message wasn't sent by us and thus the .reaction_hover
// element is not present, we use the message's
// .fa-chevron-down element as the base for the popover.
const elem = $(".selected_message .actions_hover")[0];
toggle_emoji_popover(elem, message_id);
});
$("body").on("click", ".emoji-popover-tab-item", function (e) {
e.stopPropagation();
e.preventDefault();

View File

@ -29,6 +29,7 @@ import * as narrow from "./narrow";
import * as navigate from "./navigate";
import * as overlays from "./overlays";
import {page_params} from "./page_params";
import * as popover_menus from "./popover_menus";
import * as popovers from "./popovers";
import * as reactions from "./reactions";
import * as recent_topics_ui from "./recent_topics_ui";
@ -351,7 +352,7 @@ export function process_escape_key(e) {
}
function handle_popover_events(event_name) {
if (popovers.actions_popped()) {
if (popover_menus.actions_popped()) {
popovers.actions_menu_handle_keyboard(event_name);
return true;
}
@ -938,7 +939,7 @@ export function process_hotkey(e, hotkey) {
// Shortcuts that operate on a message
switch (event_name) {
case "message_actions":
return popovers.open_message_menu(msg);
return popover_menus.toggle_message_actions_menu(msg);
case "star_message":
message_flags.toggle_starred_and_update_server(msg);
return true;

View File

@ -2,9 +2,11 @@
TippyJS/Popper popover library from the legacy Bootstrap
popovers system in popovers.js. */
import ClipboardJS from "clipboard";
import $ from "jquery";
import tippy, {delegate} from "tippy.js";
import render_actions_popover_content from "../templates/actions_popover_content.hbs";
import render_compose_control_buttons_popover from "../templates/compose_control_buttons_popover.hbs";
import render_compose_select_enter_behaviour_popover from "../templates/compose_select_enter_behaviour_popover.hbs";
import render_left_sidebar_stream_setting_popover from "../templates/left_sidebar_stream_setting_popover.hbs";
@ -13,17 +15,34 @@ import render_mobile_message_buttons_popover_content from "../templates/mobile_m
import * as channel from "./channel";
import * as common from "./common";
import * as compose_actions from "./compose_actions";
import * as condense from "./condense";
import * as emoji_picker from "./emoji_picker";
import * as giphy from "./giphy";
import {$t} from "./i18n";
import * as message_edit from "./message_edit";
import * as message_edit_history from "./message_edit_history";
import * as message_lists from "./message_lists";
import * as narrow_state from "./narrow_state";
import * as popover_menus_data from "./popover_menus_data";
import * as popovers from "./popovers";
import * as read_receipts from "./read_receipts";
import * as rows from "./rows";
import * as settings_data from "./settings_data";
import * as stream_popover from "./stream_popover";
import {parse_html} from "./ui_util";
import * as unread_ops from "./unread_ops";
import {user_settings} from "./user_settings";
let left_sidebar_stream_setting_popover_displayed = false;
let compose_mobile_button_popover_displayed = false;
export let compose_enter_sends_popover_displayed = false;
let compose_control_buttons_popover_instance;
let message_actions_popover_displayed = false;
let message_actions_popover_keyboard_toggle = false;
export function actions_popped() {
return message_actions_popover_displayed;
}
export function get_compose_control_buttons_popover() {
return compose_control_buttons_popover_instance;
@ -47,7 +66,8 @@ export function any_active() {
left_sidebar_stream_setting_popover_displayed ||
compose_mobile_button_popover_displayed ||
compose_control_buttons_popover_instance ||
compose_enter_sends_popover_displayed
compose_enter_sends_popover_displayed ||
message_actions_popover_displayed
);
}
@ -98,6 +118,20 @@ function tippy_no_propagation(target, popover_props) {
});
}
export function toggle_message_actions_menu(message) {
if (message.locally_echoed) {
// Don't open the popup for locally echoed messages for now.
// It creates bugs with things like keyboard handlers when
// we get the server response.
return true;
}
const $popover_reference = $(".selected_message .actions_hover .zulip-icon-ellipsis-v-solid");
message_actions_popover_keyboard_toggle = true;
$popover_reference.trigger("click");
return true;
}
export function initialize() {
tippy_no_propagation("#streams_inline_icon", {
onShow(instance) {
@ -228,4 +262,183 @@ export function initialize() {
compose_enter_sends_popover_displayed = false;
},
});
tippy_no_propagation(".actions_hover .zulip-icon-ellipsis-v-solid", {
// The is our minimum supported width for mobile. We shouldn't
// make the popover wider than this.
maxWidth: "320px",
placement: "bottom",
popperOptions: {
modifiers: [
{
// The placement is set to bottom, but if that placement does not fit,
// the opposite top placement will be used.
name: "flip",
options: {
fallbackPlacements: ["top", "left"],
},
},
],
},
onShow(instance) {
on_show_prep(instance);
const $row = $(instance.reference).closest(".message_row");
const message_id = rows.id($row);
message_lists.current.select_id(message_id);
const args = popover_menus_data.get_actions_popover_content_context(message_id);
instance.setContent(parse_html(render_actions_popover_content(args)));
$row.addClass("has_popover has_actions_popover");
message_actions_popover_displayed = true;
},
onMount(instance) {
if (message_actions_popover_keyboard_toggle) {
popovers.focus_first_action_popover_item();
}
message_actions_popover_keyboard_toggle = false;
// We want click events to propagate to `instance` so that
// instance.hide gets called.
const $popper = $(instance.popper);
$popper.one("click", ".respond_button", (e) => {
// Arguably, we should fetch the message ID to respond to from
// e.target, but that should always be the current selected
// message in the current message list (and
// compose_actions.respond_to_message doesn't take a message
// argument).
compose_actions.quote_and_reply({trigger: "popover respond"});
e.preventDefault();
e.stopPropagation();
instance.hide();
});
$popper.one("click", ".popover_edit_message, .popover_view_source", (e) => {
const message_id = $(e.currentTarget).data("message-id");
const $row = message_lists.current.get_row(message_id);
message_edit.start($row);
e.preventDefault();
e.stopPropagation();
instance.hide();
});
$popper.one("click", ".popover_move_message", (e) => {
const message_id = $(e.currentTarget).data("message-id");
const message = message_lists.current.get(message_id);
stream_popover.build_move_topic_to_stream_popover(
message.stream_id,
message.topic,
message,
);
e.preventDefault();
e.stopPropagation();
instance.hide();
});
$popper.one("click", ".mark_as_unread", (e) => {
const message_id = $(e.currentTarget).data("message-id");
unread_ops.mark_as_unread_from_here(message_id);
e.preventDefault();
e.stopPropagation();
instance.hide();
});
$popper.one("click", ".popover_toggle_collapse", (e) => {
const message_id = $(e.currentTarget).data("message-id");
const $row = message_lists.current.get_row(message_id);
const message = message_lists.current.get(rows.id($row));
if ($row) {
if (message.collapsed) {
condense.uncollapse($row);
} else {
condense.collapse($row);
}
}
e.preventDefault();
e.stopPropagation();
instance.hide();
});
$popper.one("click", ".rehide_muted_user_message", (e) => {
const message_id = $(e.currentTarget).data("message-id");
const $row = message_lists.current.get_row(message_id);
const message = message_lists.current.get(rows.id($row));
const message_container = message_lists.current.view.message_containers.get(
message.id,
);
if ($row && !message_container.is_hidden) {
message_lists.current.view.hide_revealed_message(message_id);
}
e.preventDefault();
e.stopPropagation();
instance.hide();
});
$popper.one("click", ".view_edit_history", (e) => {
const message_id = $(e.currentTarget).data("message-id");
const $row = message_lists.current.get_row(message_id);
const message = message_lists.current.get(rows.id($row));
message_edit_history.show_history(message);
$("#message-history-cancel").trigger("focus");
e.preventDefault();
e.stopPropagation();
instance.hide();
});
$popper.one("click", ".view_read_receipts", (e) => {
const message_id = $(e.currentTarget).data("message-id");
read_receipts.show_user_list(message_id);
e.preventDefault();
e.stopPropagation();
instance.hide();
});
$popper.one("click", ".delete_message", (e) => {
const message_id = $(e.currentTarget).data("message-id");
message_edit.delete_message(message_id);
e.preventDefault();
e.stopPropagation();
instance.hide();
});
$popper.one("click", ".reaction_button", (e) => {
const message_id = $(e.currentTarget).data("message-id");
// Don't propagate the click event since `toggle_emoji_popover` opens a
// emoji_picker which we don't want to hide after actions popover is hidden.
e.stopPropagation();
e.preventDefault();
// HACK: Because we need the popover to be based off an
// element that definitely exists in the page even if the
// message wasn't sent by us and thus the .reaction_hover
// element is not present, we use the message's
// .fa-chevron-down element as the base for the popover.
const elem = $(".selected_message .actions_hover")[0];
emoji_picker.toggle_emoji_popover(elem, message_id, true);
instance.hide();
});
new ClipboardJS($popper.find(".copy_link")[0]).on("success", (e) => {
// e.trigger returns the DOM element triggering the copy action
const message_id = e.trigger.dataset.messageId;
const $row = $(`[zid='${CSS.escape(message_id)}']`);
$row.find(".alert-msg")
.text($t({defaultMessage: "Copied!"}))
.css("display", "block")
.delay(1000)
.fadeOut(300);
setTimeout(() => {
// The Clipboard library works by focusing to a hidden textarea.
// We unfocus this so keyboard shortcuts, etc., will work again.
$(":focus").trigger("blur");
}, 0);
instance.hide();
});
},
onHidden(instance) {
const $row = $(instance.reference).closest(".message_row");
$row.removeClass("has_popover has_actions_popover");
instance.destroy();
message_actions_popover_displayed = false;
message_actions_popover_keyboard_toggle = false;
},
});
}

View File

@ -1,14 +1,10 @@
import ClipboardJS from "clipboard";
import {add, formatISO, parseISO, set} from "date-fns";
import ConfirmDatePlugin from "flatpickr/dist/plugins/confirmDate/confirmDate";
import $ from "jquery";
import tippy, {hideAll} from "tippy.js";
import render_actions_popover_content from "../templates/actions_popover_content.hbs";
import render_actions_popover_template from "../templates/actions_popover_template.hbs";
import render_no_arrow_popover from "../templates/no_arrow_popover.hbs";
import render_playground_links_popover_content from "../templates/playground_links_popover_content.hbs";
import render_remind_me_popover_content from "../templates/remind_me_popover_content.hbs";
import render_user_group_info_popover from "../templates/user_group_info_popover.hbs";
import render_user_group_info_popover_content from "../templates/user_group_info_popover_content.hbs";
import render_user_info_popover_content from "../templates/user_info_popover_content.hbs";
@ -21,15 +17,11 @@ import * as channel from "./channel";
import * as compose_actions from "./compose_actions";
import * as compose_state from "./compose_state";
import * as compose_ui from "./compose_ui";
import * as condense from "./condense";
import {media_breakpoints_num} from "./css_variables";
import * as dialog_widget from "./dialog_widget";
import * as emoji_picker from "./emoji_picker";
import * as giphy from "./giphy";
import * as hash_util from "./hash_util";
import {$t, $t_html} from "./i18n";
import * as message_edit from "./message_edit";
import * as message_edit_history from "./message_edit_history";
import * as message_lists from "./message_lists";
import * as message_viewport from "./message_viewport";
import * as muted_users from "./muted_users";
@ -39,8 +31,6 @@ import * as overlays from "./overlays";
import {page_params} from "./page_params";
import * as people from "./people";
import * as popover_menus from "./popover_menus";
import * as popover_menus_data from "./popover_menus_data";
import * as read_receipts from "./read_receipts";
import * as realm_playground from "./realm_playground";
import * as reminder from "./reminder";
import * as resize from "./resize";
@ -51,7 +41,6 @@ import * as settings_data from "./settings_data";
import * as settings_users from "./settings_users";
import * as stream_popover from "./stream_popover";
import * as ui_report from "./ui_report";
import * as unread_ops from "./unread_ops";
import * as user_groups from "./user_groups";
import * as user_profile from "./user_profile";
import {user_settings} from "./user_settings";
@ -59,8 +48,6 @@ import * as user_status from "./user_status";
import * as user_status_ui from "./user_status_ui";
import * as util from "./util";
let $current_actions_popover_elem;
let current_flatpickr_instance;
let $current_message_info_popover_elem;
let $current_user_info_popover_elem;
let $current_user_info_popover_manage_menu;
@ -70,8 +57,6 @@ let userlist_placement = "right";
let list_of_popovers = [];
export function clear_for_testing() {
$current_actions_popover_elem = undefined;
current_flatpickr_instance = undefined;
$current_message_info_popover_elem = undefined;
$current_user_info_popover_elem = undefined;
$current_user_info_popover_manage_menu = undefined;
@ -519,55 +504,14 @@ function show_user_group_info_popover(element, group, message) {
}
}
export function toggle_actions_popover(element, id) {
const $last_popover_elem = $current_actions_popover_elem;
hide_all();
if ($last_popover_elem !== undefined && $last_popover_elem.get()[0] === element) {
// We want it to be the case that a user can dismiss a popover
// by clicking on the same element that caused the popover.
return;
}
$(element).closest(".message_row").toggleClass("has_popover has_actions_popover");
message_lists.current.select_id(id);
const $elt = $(element);
if ($elt.data("popover") === undefined) {
const args = popover_menus_data.get_actions_popover_content_context(id);
const ypos = $elt.offset().top;
$elt.popover({
// Popover height with 7 items in it is ~190 px
placement: message_viewport.height() - ypos < 220 ? "top" : "bottom",
title: "",
content: render_actions_popover_content(args),
template: render_actions_popover_template(),
html: true,
trigger: "manual",
});
$elt.popover("show");
$current_actions_popover_elem = $elt;
}
if (window.innerWidth < media_breakpoints_num.xl) {
// This ensures that the popover doesn't overflow to the right of the window.
const actions_popover_left = window.innerWidth - $(".actions_popover_wrapper").outerWidth();
$(".actions_popover_wrapper").css("left", actions_popover_left);
}
}
function get_action_menu_menu_items() {
const $current_actions_popover_elem = $("[data-tippy-root] .actions_popover");
if (!$current_actions_popover_elem) {
blueslip.error("Trying to get menu items when action popover is closed.");
return undefined;
}
const popover_data = $current_actions_popover_elem.data("popover");
if (!popover_data) {
blueslip.error("Cannot find popover data for actions menu.");
return undefined;
}
return $("li:not(.divider):visible a", popover_data.$tip);
return $current_actions_popover_elem.find("li:not(.divider):visible a");
}
export function focus_first_popover_item($items, index = 0) {
@ -607,50 +551,18 @@ export function popover_items_handle_keyboard(key, $items) {
$items.eq(index).trigger("focus");
}
function focus_first_action_popover_item() {
export function focus_first_action_popover_item() {
// For now I recommend only calling this when the user opens the menu with a hotkey.
// Our popup menus act kind of funny when you mix keyboard and mouse.
const $items = get_action_menu_menu_items();
focus_first_popover_item($items);
}
export function open_message_menu(message) {
if (message.locally_echoed) {
// Don't open the popup for locally echoed messages for now.
// It creates bugs with things like keyboard handlers when
// we get the server response.
return true;
}
const message_id = message.id;
toggle_actions_popover($(".selected_message .actions_hover")[0], message_id);
if ($current_actions_popover_elem) {
focus_first_action_popover_item();
}
return true;
}
export function actions_menu_handle_keyboard(key) {
const $items = get_action_menu_menu_items();
popover_items_handle_keyboard(key, $items);
}
export function actions_popped() {
return $current_actions_popover_elem !== undefined;
}
export function hide_actions_popover() {
if (actions_popped()) {
$(".has_popover").removeClass("has_popover has_actions_popover");
$current_actions_popover_elem.popover("destroy");
$current_actions_popover_elem = undefined;
}
if (current_flatpickr_instance !== undefined) {
current_flatpickr_instance.destroy();
current_flatpickr_instance = undefined;
}
}
export function message_info_popped() {
return $current_message_info_popover_elem !== undefined;
}
@ -818,12 +730,6 @@ export function hide_playground_links_popover() {
}
export function register_click_handlers() {
$("#main_div").on("click", ".actions_hover", function (e) {
const $row = $(this).closest(".message_row");
e.stopPropagation();
toggle_actions_popover(this, rows.id($row));
});
$("#main_div").on(
"click",
".sender_name, .sender_name-in-status, .inline_profile_picture",
@ -1086,28 +992,6 @@ export function register_click_handlers() {
current_user_sidebar_popover = $target.data("popover");
});
$("body").on("click", ".mark_as_unread", (e) => {
hide_actions_popover();
const message_id = $(e.currentTarget).data("message-id");
unread_ops.mark_as_unread_from_here(message_id);
e.stopPropagation();
e.preventDefault();
});
$("body").on("click", ".respond_button", (e) => {
// Arguably, we should fetch the message ID to respond to from
// e.target, but that should always be the current selected
// message in the current message list (and
// compose_actions.respond_to_message doesn't take a message
// argument).
compose_actions.quote_and_reply({trigger: "popover respond"});
hide_actions_popover();
e.stopPropagation();
e.preventDefault();
});
$("body").on("click", ".remind.custom", (e) => {
$(e.currentTarget)[0]._flatpickr.toggle();
e.stopPropagation();
@ -1177,104 +1061,6 @@ export function register_click_handlers() {
e.stopPropagation();
e.preventDefault();
});
$("body").on("click", ".popover_toggle_collapse", (e) => {
const message_id = $(e.currentTarget).data("message-id");
const $row = message_lists.current.get_row(message_id);
const message = message_lists.current.get(rows.id($row));
hide_actions_popover();
if ($row) {
if (message.collapsed) {
condense.uncollapse($row);
} else {
condense.collapse($row);
}
}
e.stopPropagation();
e.preventDefault();
});
$("body").on("click", ".popover_edit_message, .popover_view_source", (e) => {
const message_id = $(e.currentTarget).data("message-id");
const $row = message_lists.current.get_row(message_id);
hide_actions_popover();
message_edit.start($row);
e.stopPropagation();
e.preventDefault();
});
$("body").on("click", ".popover_move_message", (e) => {
const message_id = $(e.currentTarget).data("message-id");
const message = message_lists.current.get(message_id);
hide_actions_popover();
stream_popover.build_move_topic_to_stream_popover(
message.stream_id,
message.topic,
message,
);
e.stopPropagation();
e.preventDefault();
});
$("body").on("click", ".rehide_muted_user_message", (e) => {
const message_id = $(e.currentTarget).data("message-id");
const $row = message_lists.current.get_row(message_id);
const message = message_lists.current.get(rows.id($row));
const message_container = message_lists.current.view.message_containers.get(message.id);
hide_actions_popover();
if ($row && !message_container.is_hidden) {
message_lists.current.view.hide_revealed_message(message_id);
}
e.stopPropagation();
e.preventDefault();
});
$("body").on("click", ".view_edit_history", (e) => {
const message_id = $(e.currentTarget).data("message-id");
const $row = message_lists.current.get_row(message_id);
const message = message_lists.current.get(rows.id($row));
hide_actions_popover();
message_edit_history.show_history(message);
$("#message-history-cancel").trigger("focus");
e.stopPropagation();
e.preventDefault();
});
$("body").on("click", ".view_read_receipts", (e) => {
const message_id = $(e.currentTarget).data("message-id");
hide_actions_popover();
read_receipts.show_user_list(message_id);
e.stopPropagation();
e.preventDefault();
});
$("body").on("click", ".delete_message", (e) => {
const message_id = $(e.currentTarget).data("message-id");
hide_actions_popover();
message_edit.delete_message(message_id);
e.stopPropagation();
e.preventDefault();
});
clipboard_enable(".copy_link").on("success", (e) => {
hide_actions_popover();
// e.trigger returns the DOM element triggering the copy action
const message_id = e.trigger.dataset.messageId;
const $row = $(`[zid='${CSS.escape(message_id)}']`);
$row.find(".alert-msg")
.text($t({defaultMessage: "Copied!"}))
.css("display", "block")
.delay(1000)
.fadeOut(300);
setTimeout(() => {
// The Clipboard library works by focusing to a hidden textarea.
// We unfocus this so keyboard shortcuts, etc., will work again.
$(":focus").trigger("blur");
}, 0);
});
clipboard_enable(".copy_mention_syntax");
@ -1332,7 +1118,6 @@ export function any_active() {
// Expanded sidebars on mobile view count as popovers as well.
return (
popover_menus.any_active() ||
actions_popped() ||
user_sidebar_popped() ||
stream_popover.stream_popped() ||
stream_popover.topic_popped() ||
@ -1353,7 +1138,6 @@ export function hide_all_except_sidebars(opts) {
} else if (opts.exclude_tippy_instance) {
hideAll({exclude: opts.exclude_tippy_instance});
}
hide_actions_popover();
emoji_picker.hide_emoji_popover();
giphy.hide_giphy_popover();
stream_popover.hide_stream_popover();

View File

@ -205,21 +205,20 @@ ul {
}
}
.actions_popover_wrapper {
.arrow {
position: absolute;
}
@media (width < $xl_min) {
width: max-content;
}
@media (width <= $mm_min) {
width: auto;
}
}
.actions_popover {
/* Override default padding of tippyjs */
margin: 0 -9px;
/* Override bootstrap defaults */
.nav-list > li > a {
padding: 3px 15px;
}
/* Override bootstrap defaults */
hr {
margin: 5px 0;
}
.mark_as_unread {
.unread_count {
/* The icon for this menu item is in the form of an unread count from

View File

@ -71,6 +71,7 @@ EXEMPT_FILES = make_set(
"static/js/confirm_dialog.js",
"static/js/copy_and_paste.js",
"static/js/csrf.ts",
"static/js/css_variables.js",
"static/js/dark_theme.js",
"static/js/debug.js",
"static/js/deprecated_feature_notice.js",
@ -82,6 +83,7 @@ EXEMPT_FILES = make_set(
"static/js/emoji_picker.js",
"static/js/emojisets.js",
"static/js/favicon.js",
"static/js/feature_flags.ts",
"static/js/feedback_widget.js",
"static/js/flatpickr.js",
"static/js/floating_recipient_bar.js",