mirror of https://github.com/zulip/zulip.git
tippyjs: Extract message_list_tooltips module.
This commit is contained in:
parent
c354d22454
commit
e7c012c850
|
@ -114,6 +114,7 @@ EXEMPT_FILES = make_set(
|
||||||
"web/src/message_fetch.js",
|
"web/src/message_fetch.js",
|
||||||
"web/src/message_list.js",
|
"web/src/message_list.js",
|
||||||
"web/src/message_list_data.js",
|
"web/src/message_list_data.js",
|
||||||
|
"web/src/message_list_tooltips.js",
|
||||||
"web/src/message_list_view.js",
|
"web/src/message_list_view.js",
|
||||||
"web/src/message_lists.js",
|
"web/src/message_lists.js",
|
||||||
"web/src/message_live_update.js",
|
"web/src/message_live_update.js",
|
||||||
|
|
|
@ -4,13 +4,13 @@ import $ from "jquery";
|
||||||
import {all_messages_data} from "./all_messages_data";
|
import {all_messages_data} from "./all_messages_data";
|
||||||
import * as blueslip from "./blueslip";
|
import * as blueslip from "./blueslip";
|
||||||
import {MessageListData} from "./message_list_data";
|
import {MessageListData} from "./message_list_data";
|
||||||
|
import * as message_list_tooltips from "./message_list_tooltips";
|
||||||
import {MessageListView} from "./message_list_view";
|
import {MessageListView} from "./message_list_view";
|
||||||
import * as narrow_banner from "./narrow_banner";
|
import * as narrow_banner from "./narrow_banner";
|
||||||
import * as narrow_state from "./narrow_state";
|
import * as narrow_state from "./narrow_state";
|
||||||
import {page_params} from "./page_params";
|
import {page_params} from "./page_params";
|
||||||
import {web_mark_read_on_scroll_policy_values} from "./settings_config";
|
import {web_mark_read_on_scroll_policy_values} from "./settings_config";
|
||||||
import * as stream_data from "./stream_data";
|
import * as stream_data from "./stream_data";
|
||||||
import * as tippyjs from "./tippyjs";
|
|
||||||
import {user_settings} from "./user_settings";
|
import {user_settings} from "./user_settings";
|
||||||
|
|
||||||
export class MessageList {
|
export class MessageList {
|
||||||
|
@ -432,7 +432,7 @@ export class MessageList {
|
||||||
rerender() {
|
rerender() {
|
||||||
// We need to destroy all the tippy instances from the DOM before re-rendering to
|
// We need to destroy all the tippy instances from the DOM before re-rendering to
|
||||||
// prevent the appearance of tooltips whose reference has been removed.
|
// prevent the appearance of tooltips whose reference has been removed.
|
||||||
tippyjs.destroy_all_message_list_tooltips();
|
message_list_tooltips.destroy_all_message_list_tooltips();
|
||||||
// We need to clear the rendering state, rather than just
|
// We need to clear the rendering state, rather than just
|
||||||
// doing clear_table, since we want to potentially recollapse
|
// doing clear_table, since we want to potentially recollapse
|
||||||
// things.
|
// things.
|
||||||
|
|
|
@ -0,0 +1,237 @@
|
||||||
|
import $ from "jquery";
|
||||||
|
import {delegate} from "tippy.js";
|
||||||
|
|
||||||
|
import render_message_inline_image_tooltip from "../templates/message_inline_image_tooltip.hbs";
|
||||||
|
import render_narrow_tooltip from "../templates/narrow_tooltip.hbs";
|
||||||
|
|
||||||
|
import {$t} from "./i18n";
|
||||||
|
import * as message_lists from "./message_lists";
|
||||||
|
import * as reactions from "./reactions";
|
||||||
|
import * as rows from "./rows";
|
||||||
|
import * as timerender from "./timerender";
|
||||||
|
import {LONG_HOVER_DELAY} from "./tippyjs";
|
||||||
|
import {parse_html} from "./ui_util";
|
||||||
|
|
||||||
|
// We need to store all message list instances together to destroy them in case of re-rendering.
|
||||||
|
const message_list_tippy_instances = new Set();
|
||||||
|
|
||||||
|
// This keeps track of all the instances created and destroyed.
|
||||||
|
const store_message_list_instances_plugin = {
|
||||||
|
fn() {
|
||||||
|
return {
|
||||||
|
onCreate(instance) {
|
||||||
|
message_list_tippy_instances.add(instance);
|
||||||
|
},
|
||||||
|
onDestroy(instance) {
|
||||||
|
// To make sure the `message_list_tippy_instances` contains only instances
|
||||||
|
// that are present in the DOM, we need to delete instances that are destroyed
|
||||||
|
message_list_tippy_instances.delete(instance);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function message_list_tooltip(target, props) {
|
||||||
|
delegate("body", {
|
||||||
|
target,
|
||||||
|
appendTo: () => document.body,
|
||||||
|
plugins: [store_message_list_instances_plugin],
|
||||||
|
...props,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defining observer outside ensures that at max only one observer is active at all times.
|
||||||
|
let observer;
|
||||||
|
function hide_tooltip_if_reference_removed(
|
||||||
|
target_node,
|
||||||
|
config,
|
||||||
|
instance,
|
||||||
|
nodes_to_check_for_removal,
|
||||||
|
) {
|
||||||
|
// Use MutationObserver to check for removal of nodes on which tooltips
|
||||||
|
// are still active.
|
||||||
|
if (!target_node) {
|
||||||
|
// The tooltip reference was removed from DOM before we reached here.
|
||||||
|
// In that case, we simply hide the tooltip.
|
||||||
|
// We have to be smart about hiding the instance, so we hide it as soon
|
||||||
|
// as it is displayed.
|
||||||
|
setTimeout(instance.hide, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const callback = function (mutationsList) {
|
||||||
|
for (const mutation of mutationsList) {
|
||||||
|
for (const node of nodes_to_check_for_removal) {
|
||||||
|
// Hide instance if reference's class changes.
|
||||||
|
if (mutation.type === "attributes" && mutation.attributeName === "class") {
|
||||||
|
instance.hide();
|
||||||
|
}
|
||||||
|
// Hide instance if reference is in the removed node list.
|
||||||
|
if (Array.prototype.includes.call(mutation.removedNodes, node)) {
|
||||||
|
instance.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
observer = new MutationObserver(callback);
|
||||||
|
observer.observe(target_node, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To prevent the appearance of tooltips whose reference is hidden or removed from the
|
||||||
|
// DOM during re-rendering, we need to destroy all the message list present instances,
|
||||||
|
// and then initialize triggers of the tooltips again after re-rendering.
|
||||||
|
export function destroy_all_message_list_tooltips() {
|
||||||
|
for (const instance of message_list_tippy_instances) {
|
||||||
|
if (instance.reference === document.body) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
instance.destroy();
|
||||||
|
}
|
||||||
|
message_list_tippy_instances.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initialize() {
|
||||||
|
message_list_tooltip(".tippy-narrow-tooltip", {
|
||||||
|
delay: LONG_HOVER_DELAY,
|
||||||
|
onCreate(instance) {
|
||||||
|
instance.setContent(
|
||||||
|
parse_html(render_narrow_tooltip({content: instance.props.content})),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// message reaction tooltip showing who reacted.
|
||||||
|
let observer;
|
||||||
|
message_list_tooltip(".message_reaction, .message_reactions .reaction_button", {
|
||||||
|
placement: "bottom",
|
||||||
|
onShow(instance) {
|
||||||
|
if (!document.body.contains(instance.reference)) {
|
||||||
|
// It is possible for reaction to be removed before `onShow` is triggered,
|
||||||
|
// so, we check if the element exists before proceeding.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const $elem = $(instance.reference);
|
||||||
|
if (!instance.reference.classList.contains("reaction_button")) {
|
||||||
|
const local_id = $elem.attr("data-reaction-id");
|
||||||
|
const message_id = rows.get_message_id(instance.reference);
|
||||||
|
const title = reactions.get_reaction_title_data(message_id, local_id);
|
||||||
|
instance.setContent(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {attributes: false, childList: true, subtree: true};
|
||||||
|
const target = $elem.parents(".message_table.focused_table").get(0);
|
||||||
|
const nodes_to_check_for_removal = [
|
||||||
|
$elem.parents(".recipient_row").get(0),
|
||||||
|
$elem.parents(".message_reactions").get(0),
|
||||||
|
$elem.get(0),
|
||||||
|
];
|
||||||
|
hide_tooltip_if_reference_removed(target, config, instance, nodes_to_check_for_removal);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
onHidden(instance) {
|
||||||
|
instance.destroy();
|
||||||
|
if (observer) {
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
message_list_tooltip(".message_control_button", {
|
||||||
|
delay: LONG_HOVER_DELAY,
|
||||||
|
onShow(instance) {
|
||||||
|
// Handle dynamic "starred messages" and "edit" widgets.
|
||||||
|
const $elem = $(instance.reference);
|
||||||
|
const tippy_content = $elem.attr("data-tippy-content");
|
||||||
|
const $template = $(`#${CSS.escape($elem.attr("data-tooltip-template-id"))}`);
|
||||||
|
instance.setContent(tippy_content ?? parse_html($template.html()));
|
||||||
|
},
|
||||||
|
onHidden(instance) {
|
||||||
|
instance.destroy();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
message_list_tooltip(".slow-send-spinner", {
|
||||||
|
onShow(instance) {
|
||||||
|
instance.setContent(
|
||||||
|
$t({
|
||||||
|
defaultMessage:
|
||||||
|
"Your message is taking longer than expected to be sent. Sending…",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const $elem = $(instance.reference);
|
||||||
|
|
||||||
|
// We need to check for removal of local class from message_row since
|
||||||
|
// .slow-send-spinner is not removed (hidden) from DOM when message is sent.
|
||||||
|
const target = $elem.parents(".message_row").get(0);
|
||||||
|
const config = {attributes: true, childList: false, subtree: false};
|
||||||
|
const nodes_to_check_for_removal = [$elem.get(0)];
|
||||||
|
hide_tooltip_if_reference_removed(target, config, instance, nodes_to_check_for_removal);
|
||||||
|
},
|
||||||
|
onHidden(instance) {
|
||||||
|
instance.destroy();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
message_list_tooltip(".message_table .message_time", {
|
||||||
|
onShow(instance) {
|
||||||
|
const $time_elem = $(instance.reference);
|
||||||
|
const $row = $time_elem.closest(".message_row");
|
||||||
|
const message = message_lists.current.get(rows.id($row));
|
||||||
|
// Don't show time tooltip for locally echoed message.
|
||||||
|
if (message.locally_echoed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const time = new Date(message.timestamp * 1000);
|
||||||
|
instance.setContent(timerender.get_full_datetime_clarification(time));
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
onHidden(instance) {
|
||||||
|
instance.destroy();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
message_list_tooltip(".recipient_row_date > span", {
|
||||||
|
onHidden(instance) {
|
||||||
|
instance.destroy();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
message_list_tooltip(".code_external_link");
|
||||||
|
|
||||||
|
message_list_tooltip([".recipient_bar_icon"], {
|
||||||
|
delay: LONG_HOVER_DELAY,
|
||||||
|
onHidden(instance) {
|
||||||
|
instance.destroy();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
message_list_tooltip([".rendered_markdown time", ".rendered_markdown .copy_codeblock"], {
|
||||||
|
content: timerender.get_markdown_time_tooltip,
|
||||||
|
onHidden(instance) {
|
||||||
|
instance.destroy();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
message_list_tooltip(".message_inline_image > a > img", {
|
||||||
|
// Add a short delay so the user can mouseover several inline images without
|
||||||
|
// tooltips showing and hiding rapidly
|
||||||
|
delay: [300, 20],
|
||||||
|
onShow(instance) {
|
||||||
|
// Some message_inline_images aren't actually images with a title,
|
||||||
|
// for example youtube videos, so we default to the actual href
|
||||||
|
const title =
|
||||||
|
$(instance.reference).parent().attr("aria-label") ||
|
||||||
|
$(instance.reference).parent().attr("href");
|
||||||
|
instance.setContent(parse_html(render_message_inline_image_tooltip({title})));
|
||||||
|
},
|
||||||
|
onHidden(instance) {
|
||||||
|
instance.destroy();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
message_list_tooltip(".view_user_card_tooltip", {
|
||||||
|
delay: LONG_HOVER_DELAY,
|
||||||
|
onHidden(instance) {
|
||||||
|
instance.destroy();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ import * as condense from "./condense";
|
||||||
import * as hash_util from "./hash_util";
|
import * as hash_util from "./hash_util";
|
||||||
import {$t} from "./i18n";
|
import {$t} from "./i18n";
|
||||||
import * as message_edit from "./message_edit";
|
import * as message_edit from "./message_edit";
|
||||||
|
import * as message_list_tooltips from "./message_list_tooltips";
|
||||||
import * as message_lists from "./message_lists";
|
import * as message_lists from "./message_lists";
|
||||||
import * as message_store from "./message_store";
|
import * as message_store from "./message_store";
|
||||||
import * as message_viewport from "./message_viewport";
|
import * as message_viewport from "./message_viewport";
|
||||||
|
@ -33,7 +34,6 @@ import * as stream_data from "./stream_data";
|
||||||
import * as sub_store from "./sub_store";
|
import * as sub_store from "./sub_store";
|
||||||
import * as submessage from "./submessage";
|
import * as submessage from "./submessage";
|
||||||
import * as timerender from "./timerender";
|
import * as timerender from "./timerender";
|
||||||
import * as tippyjs from "./tippyjs";
|
|
||||||
import * as user_topics from "./user_topics";
|
import * as user_topics from "./user_topics";
|
||||||
import * as util from "./util";
|
import * as util from "./util";
|
||||||
|
|
||||||
|
@ -1278,7 +1278,7 @@ export class MessageListView {
|
||||||
rerender_messages(messages, message_content_edited) {
|
rerender_messages(messages, message_content_edited) {
|
||||||
// We need to destroy all the tippy instances from the DOM before re-rendering to
|
// We need to destroy all the tippy instances from the DOM before re-rendering to
|
||||||
// prevent the appearance of tooltips whose reference has been removed.
|
// prevent the appearance of tooltips whose reference has been removed.
|
||||||
tippyjs.destroy_all_message_list_tooltips();
|
message_list_tooltips.destroy_all_message_list_tooltips();
|
||||||
// Convert messages to list messages
|
// Convert messages to list messages
|
||||||
let message_containers = messages.map((message) => this.message_containers.get(message.id));
|
let message_containers = messages.map((message) => this.message_containers.get(message.id));
|
||||||
// We may not have the message_container if the stream or topic was muted
|
// We may not have the message_container if the stream or topic was muted
|
||||||
|
|
|
@ -2,20 +2,14 @@ import $ from "jquery";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import tippy, {delegate} from "tippy.js";
|
import tippy, {delegate} from "tippy.js";
|
||||||
|
|
||||||
import render_message_inline_image_tooltip from "../templates/message_inline_image_tooltip.hbs";
|
|
||||||
import render_narrow_to_compose_recipients_tooltip from "../templates/narrow_to_compose_recipients_tooltip.hbs";
|
import render_narrow_to_compose_recipients_tooltip from "../templates/narrow_to_compose_recipients_tooltip.hbs";
|
||||||
import render_narrow_tooltip from "../templates/narrow_tooltip.hbs";
|
|
||||||
import render_tooltip_templates from "../templates/tooltip_templates.hbs";
|
import render_tooltip_templates from "../templates/tooltip_templates.hbs";
|
||||||
|
|
||||||
import * as compose_recipient from "./compose_recipient";
|
import * as compose_recipient from "./compose_recipient";
|
||||||
import * as compose_state from "./compose_state";
|
import * as compose_state from "./compose_state";
|
||||||
import {$t} from "./i18n";
|
import {$t} from "./i18n";
|
||||||
import * as message_lists from "./message_lists";
|
|
||||||
import * as narrow_state from "./narrow_state";
|
import * as narrow_state from "./narrow_state";
|
||||||
import * as popover_menus from "./popover_menus";
|
import * as popover_menus from "./popover_menus";
|
||||||
import * as reactions from "./reactions";
|
|
||||||
import * as rows from "./rows";
|
|
||||||
import * as timerender from "./timerender";
|
|
||||||
import {parse_html} from "./ui_util";
|
import {parse_html} from "./ui_util";
|
||||||
import {user_settings} from "./user_settings";
|
import {user_settings} from "./user_settings";
|
||||||
|
|
||||||
|
@ -31,81 +25,13 @@ function get_tooltip_content(reference) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to store all message list instances together to destroy them in case of re-rendering.
|
|
||||||
const message_list_tippy_instances = new Set();
|
|
||||||
|
|
||||||
// This keeps track of all the instances created and destroyed.
|
|
||||||
const store_message_list_instances_plugin = {
|
|
||||||
fn() {
|
|
||||||
return {
|
|
||||||
onCreate(instance) {
|
|
||||||
message_list_tippy_instances.add(instance);
|
|
||||||
},
|
|
||||||
onDestroy(instance) {
|
|
||||||
// To make sure the `message_list_tippy_instances` contains only instances
|
|
||||||
// that are present in the DOM, we need to delete instances that are destroyed
|
|
||||||
message_list_tippy_instances.delete(instance);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// To prevent the appearance of tooltips whose reference is hidden or removed from the
|
|
||||||
// DOM during re-rendering, we need to destroy all the message list present instances,
|
|
||||||
// and then initialize triggers of the tooltips again after re-rendering.
|
|
||||||
export function destroy_all_message_list_tooltips() {
|
|
||||||
for (const instance of message_list_tippy_instances) {
|
|
||||||
if (instance.reference === document.body) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
instance.destroy();
|
|
||||||
}
|
|
||||||
message_list_tippy_instances.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defining observer outside ensures that at max only one observer is active at all times.
|
|
||||||
let observer;
|
|
||||||
function hide_tooltip_if_reference_removed(
|
|
||||||
target_node,
|
|
||||||
config,
|
|
||||||
instance,
|
|
||||||
nodes_to_check_for_removal,
|
|
||||||
) {
|
|
||||||
// Use MutationObserver to check for removal of nodes on which tooltips
|
|
||||||
// are still active.
|
|
||||||
if (!target_node) {
|
|
||||||
// The tooltip reference was removed from DOM before we reached here.
|
|
||||||
// In that case, we simply hide the tooltip.
|
|
||||||
// We have to be smart about hiding the instance, so we hide it as soon
|
|
||||||
// as it is displayed.
|
|
||||||
setTimeout(instance.hide, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const callback = function (mutationsList) {
|
|
||||||
for (const mutation of mutationsList) {
|
|
||||||
for (const node of nodes_to_check_for_removal) {
|
|
||||||
// Hide instance if reference's class changes.
|
|
||||||
if (mutation.type === "attributes" && mutation.attributeName === "class") {
|
|
||||||
instance.hide();
|
|
||||||
}
|
|
||||||
// Hide instance if reference is in the removed node list.
|
|
||||||
if (Array.prototype.includes.call(mutation.removedNodes, node)) {
|
|
||||||
instance.hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
observer = new MutationObserver(callback);
|
|
||||||
observer.observe(target_node, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We use three delay settings for tooltips. The default "instant"
|
// We use three delay settings for tooltips. The default "instant"
|
||||||
// version has just a tiny bit of delay to create a natural feeling
|
// version has just a tiny bit of delay to create a natural feeling
|
||||||
// transition, while the "long" version is intended for elements where
|
// transition, while the "long" version is intended for elements where
|
||||||
// we want to avoid distracting the user with the tooltip
|
// we want to avoid distracting the user with the tooltip
|
||||||
// unnecessarily.
|
// unnecessarily.
|
||||||
const INSTANT_HOVER_DELAY = [100, 20];
|
const INSTANT_HOVER_DELAY = [100, 20];
|
||||||
const LONG_HOVER_DELAY = [750, 20];
|
export const LONG_HOVER_DELAY = [750, 20];
|
||||||
// EXTRA_LONG_HOVER_DELAY is for elements like the compose box send
|
// EXTRA_LONG_HOVER_DELAY is for elements like the compose box send
|
||||||
// button where the tooltip content is almost exactly the same as the
|
// button where the tooltip content is almost exactly the same as the
|
||||||
// text in the button, and the tooltip exists just to advertise a
|
// text in the button, and the tooltip exists just to advertise a
|
||||||
|
@ -113,19 +39,6 @@ const LONG_HOVER_DELAY = [750, 20];
|
||||||
// distracting users unnecessarily.
|
// distracting users unnecessarily.
|
||||||
const EXTRA_LONG_HOVER_DELAY = [1500, 20];
|
const EXTRA_LONG_HOVER_DELAY = [1500, 20];
|
||||||
|
|
||||||
// Tooltips present outside of message list table in DOM don't get
|
|
||||||
// effected by `rerender` but since their original reference is removed,
|
|
||||||
// their position is miscalculated and they get placed at top left of the
|
|
||||||
// window. To avoid this, we use this wrapping function.
|
|
||||||
function message_list_tooltip(target, props) {
|
|
||||||
delegate("body", {
|
|
||||||
target,
|
|
||||||
appendTo: () => document.body,
|
|
||||||
plugins: [store_message_list_instances_plugin],
|
|
||||||
...props,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// We override the defaults set by tippy library here,
|
// We override the defaults set by tippy library here,
|
||||||
// so make sure to check this too after checking tippyjs
|
// so make sure to check this too after checking tippyjs
|
||||||
// documentation for default properties.
|
// documentation for default properties.
|
||||||
|
@ -187,15 +100,6 @@ export function initialize() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
message_list_tooltip(".tippy-narrow-tooltip", {
|
|
||||||
delay: LONG_HOVER_DELAY,
|
|
||||||
onCreate(instance) {
|
|
||||||
instance.setContent(
|
|
||||||
parse_html(render_narrow_tooltip({content: instance.props.content})),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
delegate("body", {
|
delegate("body", {
|
||||||
target: ".toggle-subscription-tooltip",
|
target: ".toggle-subscription-tooltip",
|
||||||
delay: EXTRA_LONG_HOVER_DELAY,
|
delay: EXTRA_LONG_HOVER_DELAY,
|
||||||
|
@ -246,42 +150,6 @@ export function initialize() {
|
||||||
// below specify the target directly, elements using those should
|
// below specify the target directly, elements using those should
|
||||||
// not have the tippy-zulip-tooltip class.
|
// not have the tippy-zulip-tooltip class.
|
||||||
|
|
||||||
// message reaction tooltip showing who reacted.
|
|
||||||
let observer;
|
|
||||||
message_list_tooltip(".message_reaction, .message_reactions .reaction_button", {
|
|
||||||
placement: "bottom",
|
|
||||||
onShow(instance) {
|
|
||||||
if (!document.body.contains(instance.reference)) {
|
|
||||||
// It is possible for reaction to be removed before `onShow` is triggered,
|
|
||||||
// so, we check if the element exists before proceeding.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const $elem = $(instance.reference);
|
|
||||||
if (!instance.reference.classList.contains("reaction_button")) {
|
|
||||||
const local_id = $elem.attr("data-reaction-id");
|
|
||||||
const message_id = rows.get_message_id(instance.reference);
|
|
||||||
const title = reactions.get_reaction_title_data(message_id, local_id);
|
|
||||||
instance.setContent(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = {attributes: false, childList: true, subtree: true};
|
|
||||||
const target = $elem.parents(".message_table.focused_table").get(0);
|
|
||||||
const nodes_to_check_for_removal = [
|
|
||||||
$elem.parents(".recipient_row").get(0),
|
|
||||||
$elem.parents(".message_reactions").get(0),
|
|
||||||
$elem.get(0),
|
|
||||||
];
|
|
||||||
hide_tooltip_if_reference_removed(target, config, instance, nodes_to_check_for_removal);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
onHidden(instance) {
|
|
||||||
instance.destroy();
|
|
||||||
if (observer) {
|
|
||||||
observer.disconnect();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
delegate("body", {
|
delegate("body", {
|
||||||
target: [
|
target: [
|
||||||
// Ideally this would be `#compose_buttons .button`, but the
|
// Ideally this would be `#compose_buttons .button`, but the
|
||||||
|
@ -333,20 +201,6 @@ export function initialize() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
message_list_tooltip(".message_control_button", {
|
|
||||||
delay: LONG_HOVER_DELAY,
|
|
||||||
onShow(instance) {
|
|
||||||
// Handle dynamic "starred messages" and "edit" widgets.
|
|
||||||
const $elem = $(instance.reference);
|
|
||||||
const tippy_content = $elem.attr("data-tippy-content");
|
|
||||||
const $template = $(`#${CSS.escape($elem.attr("data-tooltip-template-id"))}`);
|
|
||||||
instance.setContent(tippy_content ?? parse_html($template.html()));
|
|
||||||
},
|
|
||||||
onHidden(instance) {
|
|
||||||
instance.destroy();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
$("body").on("blur", ".message_control_button", (e) => {
|
$("body").on("blur", ".message_control_button", (e) => {
|
||||||
// Remove tooltip when user is trying to tab through all the icons.
|
// Remove tooltip when user is trying to tab through all the icons.
|
||||||
// If user tabs slowly, tooltips are displayed otherwise they are
|
// If user tabs slowly, tooltips are displayed otherwise they are
|
||||||
|
@ -354,54 +208,6 @@ export function initialize() {
|
||||||
e.currentTarget?._tippy?.destroy();
|
e.currentTarget?._tippy?.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
message_list_tooltip(".slow-send-spinner", {
|
|
||||||
onShow(instance) {
|
|
||||||
instance.setContent(
|
|
||||||
$t({
|
|
||||||
defaultMessage:
|
|
||||||
"Your message is taking longer than expected to be sent. Sending…",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const $elem = $(instance.reference);
|
|
||||||
|
|
||||||
// We need to check for removal of local class from message_row since
|
|
||||||
// .slow-send-spinner is not removed (hidden) from DOM when message is sent.
|
|
||||||
const target = $elem.parents(".message_row").get(0);
|
|
||||||
const config = {attributes: true, childList: false, subtree: false};
|
|
||||||
const nodes_to_check_for_removal = [$elem.get(0)];
|
|
||||||
hide_tooltip_if_reference_removed(target, config, instance, nodes_to_check_for_removal);
|
|
||||||
},
|
|
||||||
onHidden(instance) {
|
|
||||||
instance.destroy();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
message_list_tooltip(".message_table .message_time", {
|
|
||||||
onShow(instance) {
|
|
||||||
const $time_elem = $(instance.reference);
|
|
||||||
const $row = $time_elem.closest(".message_row");
|
|
||||||
const message = message_lists.current.get(rows.id($row));
|
|
||||||
// Don't show time tooltip for locally echoed message.
|
|
||||||
if (message.locally_echoed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const time = new Date(message.timestamp * 1000);
|
|
||||||
instance.setContent(timerender.get_full_datetime_clarification(time));
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
onHidden(instance) {
|
|
||||||
instance.destroy();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
message_list_tooltip(".recipient_row_date > span", {
|
|
||||||
onHidden(instance) {
|
|
||||||
instance.destroy();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
message_list_tooltip(".code_external_link");
|
|
||||||
|
|
||||||
delegate("body", {
|
delegate("body", {
|
||||||
target: [
|
target: [
|
||||||
"#streams_header .sidebar-title",
|
"#streams_header .sidebar-title",
|
||||||
|
@ -417,20 +223,6 @@ export function initialize() {
|
||||||
appendTo: () => document.body,
|
appendTo: () => document.body,
|
||||||
});
|
});
|
||||||
|
|
||||||
message_list_tooltip([".recipient_bar_icon"], {
|
|
||||||
delay: LONG_HOVER_DELAY,
|
|
||||||
onHidden(instance) {
|
|
||||||
instance.destroy();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
message_list_tooltip([".rendered_markdown time", ".rendered_markdown .copy_codeblock"], {
|
|
||||||
content: timerender.get_markdown_time_tooltip,
|
|
||||||
onHidden(instance) {
|
|
||||||
instance.destroy();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
delegate("body", {
|
delegate("body", {
|
||||||
target: [
|
target: [
|
||||||
"#compose_top_right [data-tippy-content]",
|
"#compose_top_right [data-tippy-content]",
|
||||||
|
@ -492,23 +284,6 @@ export function initialize() {
|
||||||
appendTo: () => document.body,
|
appendTo: () => document.body,
|
||||||
});
|
});
|
||||||
|
|
||||||
message_list_tooltip(".message_inline_image > a > img", {
|
|
||||||
// Add a short delay so the user can mouseover several inline images without
|
|
||||||
// tooltips showing and hiding rapidly
|
|
||||||
delay: [300, 20],
|
|
||||||
onShow(instance) {
|
|
||||||
// Some message_inline_images aren't actually images with a title,
|
|
||||||
// for example youtube videos, so we default to the actual href
|
|
||||||
const title =
|
|
||||||
$(instance.reference).parent().attr("aria-label") ||
|
|
||||||
$(instance.reference).parent().attr("href");
|
|
||||||
instance.setContent(parse_html(render_message_inline_image_tooltip({title})));
|
|
||||||
},
|
|
||||||
onHidden(instance) {
|
|
||||||
instance.destroy();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
delegate("body", {
|
delegate("body", {
|
||||||
target: ".image-info-wrapper > .image-description > .title",
|
target: ".image-info-wrapper > .image-description > .title",
|
||||||
appendTo: () => document.body,
|
appendTo: () => document.body,
|
||||||
|
@ -644,13 +419,6 @@ export function initialize() {
|
||||||
appendTo: () => document.body,
|
appendTo: () => document.body,
|
||||||
});
|
});
|
||||||
|
|
||||||
message_list_tooltip(".view_user_card_tooltip", {
|
|
||||||
delay: LONG_HOVER_DELAY,
|
|
||||||
onHidden(instance) {
|
|
||||||
instance.destroy();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
delegate("body", {
|
delegate("body", {
|
||||||
target: "#send_later",
|
target: "#send_later",
|
||||||
delay: LONG_HOVER_DELAY,
|
delay: LONG_HOVER_DELAY,
|
||||||
|
|
|
@ -49,6 +49,7 @@ import * as markdown_config from "./markdown_config";
|
||||||
import * as message_edit from "./message_edit";
|
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_fetch from "./message_fetch";
|
import * as message_fetch from "./message_fetch";
|
||||||
|
import * as message_list_tooltips from "./message_list_tooltips";
|
||||||
import * as message_lists from "./message_lists";
|
import * as message_lists from "./message_lists";
|
||||||
import * as message_scroll from "./message_scroll";
|
import * as message_scroll from "./message_scroll";
|
||||||
import * as message_view_header from "./message_view_header";
|
import * as message_view_header from "./message_view_header";
|
||||||
|
@ -615,6 +616,7 @@ export function initialize_everything() {
|
||||||
|
|
||||||
i18n.initialize(i18n_params);
|
i18n.initialize(i18n_params);
|
||||||
tippyjs.initialize();
|
tippyjs.initialize();
|
||||||
|
message_list_tooltips.initialize();
|
||||||
// This populates data for scheduled messages.
|
// This populates data for scheduled messages.
|
||||||
scheduled_messages.initialize(scheduled_messages_params);
|
scheduled_messages.initialize(scheduled_messages_params);
|
||||||
popovers.initialize();
|
popovers.initialize();
|
||||||
|
|
Loading…
Reference in New Issue