mirror of https://github.com/zulip/zulip.git
821 lines
29 KiB
TypeScript
821 lines
29 KiB
TypeScript
import $ from "jquery";
|
|
import assert from "minimalistic-assert";
|
|
import * as tippy from "tippy.js";
|
|
|
|
import render_buddy_list_title_tooltip from "../templates/buddy_list/title_tooltip.hbs";
|
|
import render_change_visibility_policy_button_tooltip from "../templates/change_visibility_policy_button_tooltip.hbs";
|
|
import render_org_logo_tooltip from "../templates/org_logo_tooltip.hbs";
|
|
import render_tooltip_templates from "../templates/tooltip_templates.hbs";
|
|
|
|
import {$t} from "./i18n.ts";
|
|
import * as people from "./people.ts";
|
|
import * as popovers from "./popovers.ts";
|
|
import * as settings_config from "./settings_config.ts";
|
|
import * as stream_data from "./stream_data.ts";
|
|
import * as ui_util from "./ui_util.ts";
|
|
import {user_settings} from "./user_settings.ts";
|
|
import * as util from "./util.ts";
|
|
|
|
// For tooltips without data-tippy-content, we use the HTML content of
|
|
// a <template> whose id is given by data-tooltip-template-id.
|
|
function get_tooltip_content(reference: Element): string | Element | DocumentFragment {
|
|
if (reference instanceof HTMLElement && reference.dataset.tooltipTemplateId !== undefined) {
|
|
const template = document.querySelector<HTMLTemplateElement>(
|
|
`template#${CSS.escape(reference.dataset.tooltipTemplateId)}`,
|
|
);
|
|
if (template !== null) {
|
|
const fragment = template.content.cloneNode(true);
|
|
assert(fragment instanceof DocumentFragment);
|
|
return fragment;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// We use different delay settings for tooltips. The default "instant"
|
|
// version has just a tiny bit of delay to create a natural feeling
|
|
// transition, while the "long" version is intended for elements where
|
|
// we want to avoid distracting the user with the tooltip
|
|
// unnecessarily.
|
|
export const INSTANT_HOVER_DELAY: [number, number] = [100, 20];
|
|
// INTERACTIVE_HOVER_DELAY is for elements like the emoji reactions, where
|
|
// the tooltip includes useful information (who reacted?), but that
|
|
// needs a short delay for users who are just tapping a reaction
|
|
// element and not interested in the tooltip's contents.
|
|
export const INTERACTIVE_HOVER_DELAY: [number, number] = [425, 20];
|
|
export const LONG_HOVER_DELAY: [number, number] = [750, 20];
|
|
// EXTRA_LONG_HOVER_DELAY is for elements like the compose box send
|
|
// button where the tooltip content is almost exactly the same as the
|
|
// text in the button, and the tooltip exists just to advertise a
|
|
// keyboard shortcut. For these tooltips, it's very important to avoid
|
|
// distracting users unnecessarily.
|
|
export const EXTRA_LONG_HOVER_DELAY: [number, number] = [1500, 20];
|
|
|
|
// We override the defaults set by tippy library here,
|
|
// so make sure to check this too after checking tippyjs
|
|
// documentation for default properties.
|
|
tippy.default.setDefaultProps({
|
|
// Tooltips shouldn't take more space than mobile widths.
|
|
// 300px at 14px/1em
|
|
maxWidth: "21.4286em",
|
|
delay: INSTANT_HOVER_DELAY,
|
|
placement: "top",
|
|
// Disable animations to make the tooltips feel snappy.
|
|
animation: false,
|
|
// Show tooltips on long press on touch based devices.
|
|
touch: ["hold", 750],
|
|
// Create the tooltip inside the parent element. This has the
|
|
// undesirable side effect of CSS properties of the parent elements
|
|
// applying to tooltips, which causes ugly clipping if the parent
|
|
// element has overflow rules. Even with that, we prefer to have
|
|
// tooltips appended to the parent so that the tooltip gets removed
|
|
// if the parent is hidden / removed from DOM; which is not the case
|
|
// with appending it to `body` which has side effect of tooltips
|
|
// sticking around due to browser not communicating to tippy that
|
|
// the element has been removed without having a Mutation Observer.
|
|
appendTo: "parent",
|
|
// To add a text tooltip, override this by setting data-tippy-content.
|
|
// To add an HTML tooltip, set data-tooltip-template-id to the id of a <template>.
|
|
// Or, override this with a function returning string (text) or DocumentFragment (HTML).
|
|
content: get_tooltip_content,
|
|
});
|
|
|
|
export const topic_visibility_policy_tooltip_props = {
|
|
delay: LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
onShow(instance: tippy.Instance) {
|
|
const $elem = $(instance.reference);
|
|
let should_render_privacy_icon;
|
|
let current_visibility_policy_str;
|
|
if ($elem.hasClass("zulip-icon-inherit")) {
|
|
should_render_privacy_icon = true;
|
|
} else {
|
|
should_render_privacy_icon = false;
|
|
current_visibility_policy_str = $elem.attr("data-tippy-content");
|
|
}
|
|
let current_stream_obj;
|
|
if (should_render_privacy_icon) {
|
|
current_stream_obj = stream_data.get_sub_by_id(
|
|
Number($elem.parent().attr("data-stream-id")),
|
|
);
|
|
}
|
|
const tooltip_context = {
|
|
...current_stream_obj,
|
|
current_visibility_policy_str,
|
|
should_render_privacy_icon,
|
|
};
|
|
instance.setContent(
|
|
ui_util.parse_html(render_change_visibility_policy_button_tooltip(tooltip_context)),
|
|
);
|
|
},
|
|
onHidden(instance: tippy.Instance) {
|
|
instance.destroy();
|
|
},
|
|
};
|
|
|
|
export function initialize(): void {
|
|
$("#tooltip-templates-container").html(render_tooltip_templates());
|
|
|
|
// Our default tooltip configuration. For this, one simply needs to:
|
|
// * Set `class="tippy-zulip-tooltip"` on an element for enable this.
|
|
// * Set `data-tippy-content="{{t 'Tooltip content' }}"`, often
|
|
// replacing a `title` attribute on an element that had both.
|
|
// * Set placement; we typically use `data-tippy-placement="top"`.
|
|
tippy.delegate("body", {
|
|
target: ".tippy-zulip-tooltip",
|
|
});
|
|
|
|
// variant of tippy-zulip-tooltip above having delay=LONG_HOVER_DELAY,
|
|
// default placement="top" with fallback placement="bottom",
|
|
// and appended to body
|
|
tippy.delegate("body", {
|
|
target: ".tippy-zulip-delayed-tooltip",
|
|
// Disable trigger on focus, to avoid displaying on-click.
|
|
trigger: "mouseenter",
|
|
delay: LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
popperOptions: {
|
|
modifiers: [
|
|
{
|
|
name: "flip",
|
|
options: {
|
|
fallbackPlacements: "bottom",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".toggle-subscription-tooltip",
|
|
trigger: "mouseenter",
|
|
delay: EXTRA_LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
placement: "bottom",
|
|
onShow(instance) {
|
|
let template = "show-unsubscribe-tooltip-template";
|
|
if (instance.reference.classList.contains("unsubscribed")) {
|
|
template = "show-subscribe-tooltip-template";
|
|
}
|
|
$(instance.reference).attr("data-tooltip-template-id", template);
|
|
instance.setContent(get_tooltip_content(instance.reference));
|
|
},
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "#subscription_overlay .subscription_settings .sub-stream-name",
|
|
delay: LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
placement: "top",
|
|
onShow(instance) {
|
|
const stream_name_element = instance.reference;
|
|
assert(stream_name_element instanceof HTMLElement);
|
|
// Only show tooltip if the stream name is truncated.
|
|
// See https://stackoverflow.com/questions/21064101/understanding-offsetwidth-clientwidth-scrollwidth-and-height-respectively
|
|
// for more details.
|
|
if (stream_name_element.offsetWidth >= stream_name_element.scrollWidth) {
|
|
return false;
|
|
}
|
|
|
|
return undefined;
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".tippy-left-sidebar-tooltip",
|
|
placement: "right",
|
|
delay: EXTRA_LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
popperOptions: {
|
|
modifiers: [
|
|
{
|
|
name: "flip",
|
|
options: {
|
|
fallbackPlacements: "bottom",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
// Variant of .tippy-left-sidebar-tooltip configuration. Since
|
|
// some elements don't have an always visible label, and
|
|
// thus hovering them is a way to find out what they do, give
|
|
// them the shorter LONG_HOVER_DELAY.
|
|
tippy.delegate("body", {
|
|
target: ".tippy-left-sidebar-tooltip-no-label-delay",
|
|
placement: "right",
|
|
delay: LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
popperOptions: {
|
|
modifiers: [
|
|
{
|
|
name: "flip",
|
|
options: {
|
|
fallbackPlacements: "bottom",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
// Variant of .tippy-left-sidebar-tooltip configuration. Here
|
|
// we need to dynamically check which view is the home view.
|
|
tippy.delegate("body", {
|
|
target: ".tippy-views-tooltip",
|
|
placement: "right",
|
|
delay: EXTRA_LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
onShow(instance) {
|
|
const $container = $(instance.popper).find(".views-tooltip-container");
|
|
if ($container.data("view-code") === user_settings.web_home_view) {
|
|
$container.find(".views-tooltip-home-view-note").removeClass("hide");
|
|
}
|
|
|
|
// Since the tooltip is attached the anchor tag which doesn't
|
|
// include with of the ellipsis icon, we need to offset the
|
|
// tooltip so that the tooltip is displayed to right of the
|
|
// ellipsis icon.
|
|
if (instance.reference.classList.contains("left-sidebar-navigation-label-container")) {
|
|
instance.setProps({
|
|
offset: [0, 40],
|
|
});
|
|
}
|
|
},
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
popperOptions: {
|
|
modifiers: [
|
|
{
|
|
name: "flip",
|
|
options: {
|
|
fallbackPlacements: "bottom",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
// The below definitions are for specific tooltips that require
|
|
// custom JavaScript code or configuration. Note that since the
|
|
// below specify the target directly, elements using those should
|
|
// not have the tippy-zulip-tooltip class.
|
|
|
|
tippy.delegate("body", {
|
|
target: ".draft-selection-tooltip",
|
|
delay: LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
onShow(instance) {
|
|
let content = $t({defaultMessage: "Select draft"});
|
|
const $elem = $(instance.reference);
|
|
if ($($elem).parent().find(".draft-selection-checkbox").hasClass("fa-check-square")) {
|
|
content = $t({defaultMessage: "Deselect draft"});
|
|
}
|
|
instance.setContent(content);
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".delete-selected-drafts-button-container",
|
|
appendTo: () => document.body,
|
|
onShow(instance) {
|
|
let content = $t({defaultMessage: "Delete all selected drafts"});
|
|
const $elem = $(instance.reference);
|
|
if ($($elem).find(".delete-selected-drafts-button").is(":disabled")) {
|
|
content = $t({defaultMessage: "No drafts selected"});
|
|
}
|
|
instance.setContent(content);
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "#add-poll-modal .dialog_submit_button_container",
|
|
appendTo: () => document.body,
|
|
onShow(instance) {
|
|
const content = $t({defaultMessage: "Please enter a question."});
|
|
const $elem = $(instance.reference);
|
|
// Show tooltip to enter question only if submit button is disabled
|
|
// (due to question field being empty).
|
|
if ($elem.find(".dialog_submit_button").is(":disabled")) {
|
|
instance.setContent(content);
|
|
return undefined;
|
|
}
|
|
return false;
|
|
},
|
|
});
|
|
|
|
$("body").on(
|
|
"blur",
|
|
".message_control_button, .delete-selected-drafts-button-container",
|
|
function (this: tippy.ReferenceElement, _event: JQuery.Event) {
|
|
// Remove tooltip when user is trying to tab through all the icons.
|
|
// If user tabs slowly, tooltips are displayed otherwise they are
|
|
// destroyed before they can be displayed.
|
|
this._tippy?.destroy();
|
|
},
|
|
);
|
|
|
|
tippy.delegate("body", {
|
|
target: [
|
|
"#streams_header .streams-tooltip-target",
|
|
"#scroll-to-bottom-button-clickable-area",
|
|
".spectator_narrow_login_button",
|
|
"#stream-specific-notify-table .unmute_stream",
|
|
"#add_streams_tooltip",
|
|
"#filter_streams_tooltip",
|
|
".error-icon-message-recipient .zulip-icon",
|
|
"#personal-menu-dropdown .status-circle",
|
|
".popover-group-menu-member-list .popover_user_presence",
|
|
"#copy_generated_invite_link",
|
|
].join(","),
|
|
appendTo: () => document.body,
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: [
|
|
"#compose_close",
|
|
".expand-composebox-button",
|
|
".collapse-composebox-button",
|
|
".maximize-composebox-button",
|
|
].join(","),
|
|
delay: LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".media-info-wrapper > .media-description > .title",
|
|
appendTo: () => document.body,
|
|
onShow(instance) {
|
|
const title = $(instance.reference).attr("aria-label");
|
|
if (title === undefined) {
|
|
return false;
|
|
}
|
|
const filename = $(instance.reference).attr("data-filename");
|
|
const $markup = $("<span>").text(title);
|
|
if (title !== filename) {
|
|
// If the image title is the same as the filename, there's no reason
|
|
// to show this next line.
|
|
const second_line = $t({defaultMessage: "File name: {filename}"}, {filename});
|
|
$markup.append($("<br>"), $("<span>").text(second_line));
|
|
}
|
|
instance.setContent(util.the($markup));
|
|
return undefined;
|
|
},
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
// Configure tooltips for the stream_sorter_toggle buttons.
|
|
|
|
// TODO: Ideally, we'd extend this to be a common mechanism for
|
|
// tab switchers, with the strings living in a more normal configuration
|
|
// location.
|
|
target: ".stream_sorter_toggle .ind-tab [data-tippy-content]",
|
|
|
|
// Adjust their placement to `bottom`.
|
|
placement: "bottom",
|
|
|
|
// Avoid inheriting `position: relative` CSS on the stream sorter widget.
|
|
appendTo: () => document.body,
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
// This tooltip appears on the "Summary" checkboxes in
|
|
// settings > custom profile fields, when at the limit of 2
|
|
// fields with display_in_profile_summary enabled.
|
|
target: [
|
|
"#profile-field-settings .display_in_profile_summary_tooltip",
|
|
"#edit-custom-profile-field-form-modal .display_in_profile_summary_tooltip",
|
|
"#add-new-custom-profile-field-form .display_in_profile_summary_tooltip",
|
|
].join(","),
|
|
content: $t({
|
|
defaultMessage: "Only 2 custom profile fields can be displayed on the user card.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onTrigger(instance) {
|
|
// Sometimes just removing class is not enough to destroy/remove tooltip, especially in
|
|
// "Add a new custom profile field" form, so here we are manually calling `destroy()`.
|
|
if (!instance.reference.classList.contains("display_in_profile_summary_tooltip")) {
|
|
instance.destroy();
|
|
}
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "#full_name_input_container.disabled_setting_tooltip",
|
|
content: $t({
|
|
defaultMessage:
|
|
"Name changes are disabled in this organization. Contact an administrator to change your name.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "#change_email_button_container.disabled_setting_tooltip",
|
|
content: $t({defaultMessage: "Email address changes are disabled in this organization."}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: [
|
|
"#deactivate_account_container.disabled_setting_tooltip",
|
|
"#edit-user-form .deactivate_user_button_tooltip",
|
|
].join(","),
|
|
content: $t({
|
|
defaultMessage:
|
|
"Because you are the only organization owner, you cannot deactivate your account.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "#deactivate_realm_button_container.disabled_setting_tooltip",
|
|
content: $t({
|
|
defaultMessage: "Only organization owners may deactivate an organization.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".settings-radio-input-parent.default_stream_private_tooltip",
|
|
content: $t({
|
|
defaultMessage: "Default channels for new users cannot be made private.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: [
|
|
"[data-tab-key='not-subscribed'].disabled",
|
|
"[data-tab-key='all-streams'].disabled",
|
|
].join(","),
|
|
content: $t({
|
|
defaultMessage: "You can only view channels that you are subscribed to.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".default-stream.default_stream_private_tooltip",
|
|
content: $t({
|
|
defaultMessage: "Private channels cannot be default channels for new users.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "[data-tab-key='invite-link-tab'].disabled",
|
|
content: $t({
|
|
defaultMessage:
|
|
"You do not have permissions to create invite links in this organization.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: [
|
|
"#api_key_button_container.disabled_setting_tooltip",
|
|
"#user_email_address_dropdown_container.disabled_setting_tooltip",
|
|
].join(","),
|
|
content: $t({
|
|
defaultMessage: "You must configure your email to access this feature.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "[data-tab-key='invite-email-tab'].disabled",
|
|
content: $t({
|
|
defaultMessage:
|
|
"You do not have permissions to send invite emails in this organization.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "#user_message_content_in_email_notifications_label",
|
|
onShow(instance) {
|
|
if ($("#user_message_content_in_email_notifications").prop("disabled")) {
|
|
instance.setContent(
|
|
$t({
|
|
defaultMessage:
|
|
"Including message content in message notification emails is not allowed in this organization.",
|
|
}),
|
|
);
|
|
return undefined;
|
|
}
|
|
instance.destroy();
|
|
return false;
|
|
},
|
|
appendTo: () => document.body,
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".views-tooltip-target",
|
|
onShow(instance) {
|
|
if ($("#toggle-top-left-navigation-area-icon").hasClass("rotate-icon-down")) {
|
|
instance.setContent(
|
|
$t({
|
|
defaultMessage: "Collapse views",
|
|
}),
|
|
);
|
|
} else {
|
|
instance.setContent($t({defaultMessage: "Expand views"}));
|
|
}
|
|
},
|
|
delay: EXTRA_LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".dm-tooltip-target",
|
|
onShow(instance) {
|
|
if ($(".direct-messages-container").hasClass("zoom-in")) {
|
|
return false;
|
|
}
|
|
|
|
if ($("#toggle-direct-messages-section-icon").hasClass("rotate-icon-down")) {
|
|
instance.setContent(
|
|
$t({
|
|
defaultMessage: "Collapse direct messages",
|
|
}),
|
|
);
|
|
} else {
|
|
instance.setContent($t({defaultMessage: "Expand direct messages"}));
|
|
}
|
|
return undefined;
|
|
},
|
|
delay: EXTRA_LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "#stream_creation_form .add_subscribers_disabled",
|
|
content: $t({
|
|
defaultMessage:
|
|
"You do not have permission to add other users to channels in this organization.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".user_row .actions button",
|
|
trigger: "mouseenter",
|
|
onShow(instance) {
|
|
if ($(instance.reference).hasClass("deactivate")) {
|
|
instance.setContent($t({defaultMessage: "Deactivate"}));
|
|
return undefined;
|
|
} else if ($(instance.reference).hasClass("reactivate")) {
|
|
instance.setContent($t({defaultMessage: "Reactivate"}));
|
|
return undefined;
|
|
}
|
|
return false;
|
|
},
|
|
delay: LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".user-card-status-area .status-emoji",
|
|
appendTo: () => document.body,
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".status-emoji-name",
|
|
placement: "top",
|
|
delay: INSTANT_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
|
|
/*
|
|
Status emoji tooltips for most locations in the app. This
|
|
basic tooltip logic is overridden by separate logic in
|
|
click_handlers.js for the left and right sidebars, to
|
|
avoid problematic interactions with the main tooltips for
|
|
those regions.
|
|
*/
|
|
|
|
onShow() {
|
|
popovers.hide_all();
|
|
},
|
|
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: [
|
|
".custom-profile-field-value",
|
|
".copy-custom-profile-field-link",
|
|
"#popover-menu-copy-email",
|
|
".personal-menu-clear-status",
|
|
".user-card-clear-status-button",
|
|
].join(","),
|
|
placement: "top",
|
|
delay: LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
onHidden(instance: tippy.Instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
/*
|
|
The tooltip for new user group button (+) icon button on #groups
|
|
overlay was not mounted correctly as its sibling element (search bar)
|
|
is inserted dynamically after handlebar got rendered. So we append the
|
|
tooltip element to the body itself with target as the + button.
|
|
*/
|
|
target: "#groups_overlay .create_user_group_plus_button",
|
|
content: $t({
|
|
defaultMessage: "Create new user group",
|
|
}),
|
|
placement: "bottom",
|
|
appendTo: () => document.body,
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "#move_topic_to_stream_widget_wrapper",
|
|
onShow(instance) {
|
|
if ($("#move_topic_to_stream_widget").prop("disabled")) {
|
|
instance.setContent(
|
|
$t({
|
|
defaultMessage:
|
|
"You do not have permission to move messages to another channel in this organization.",
|
|
}),
|
|
);
|
|
return undefined;
|
|
}
|
|
return false;
|
|
},
|
|
appendTo: () => document.body,
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "#userlist-header-search",
|
|
delay: LONG_HOVER_DELAY,
|
|
placement: "top",
|
|
appendTo: () => document.body,
|
|
onShow(instance) {
|
|
const total_user_count = people.get_active_human_count();
|
|
instance.setContent(
|
|
ui_util.parse_html(render_buddy_list_title_tooltip({total_user_count})),
|
|
);
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".header-main .column-left .left-sidebar-toggle-button",
|
|
delay: LONG_HOVER_DELAY,
|
|
placement: "bottom",
|
|
appendTo: () => document.body,
|
|
onShow(instance) {
|
|
let template = "show-left-sidebar-tooltip-template";
|
|
if ($("#left-sidebar-container").is(":visible")) {
|
|
template = "hide-left-sidebar-tooltip-template";
|
|
}
|
|
$(instance.reference).attr("data-tooltip-template-id", template);
|
|
instance.setContent(get_tooltip_content(instance.reference));
|
|
},
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "#userlist-toggle-button",
|
|
delay: LONG_HOVER_DELAY,
|
|
placement: "bottom",
|
|
appendTo: () => document.body,
|
|
onShow(instance) {
|
|
let template = "show-userlist-tooltip-template";
|
|
if ($("#right-sidebar-container").is(":visible")) {
|
|
template = "hide-userlist-tooltip-template";
|
|
}
|
|
$(instance.reference).attr("data-tooltip-template-id", template);
|
|
instance.setContent(get_tooltip_content(instance.reference));
|
|
},
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: "#realm-navbar-wide-logo",
|
|
placement: "right",
|
|
appendTo: () => document.body,
|
|
onShow(instance) {
|
|
const escape_navigates_to_home_view = user_settings.web_escape_navigates_to_home_view;
|
|
const home_view =
|
|
settings_config.web_home_view_values[user_settings.web_home_view].description;
|
|
instance.setContent(
|
|
ui_util.parse_html(
|
|
render_org_logo_tooltip({home_view, escape_navigates_to_home_view}),
|
|
),
|
|
);
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: [
|
|
"#recent_view .recipient_bar_icon",
|
|
"#inbox-view .recipient_bar_icon",
|
|
"#left-sidebar-container .visibility-policy-icon",
|
|
].join(","),
|
|
...topic_visibility_policy_tooltip_props,
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".custom-user-field-label-wrapper.required-field-wrapper",
|
|
content: $t({
|
|
defaultMessage: "This profile field is required.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".settings-profile-user-field.not-editable-by-user-input-wrapper",
|
|
content: $t({
|
|
defaultMessage:
|
|
"You are not allowed to change this field. Contact an administrator to update it.",
|
|
}),
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".popover-contains-shift-hotkey",
|
|
trigger: "mouseenter",
|
|
placement: "top",
|
|
appendTo: () => document.body,
|
|
onShow(instance) {
|
|
const hotkey_hints = $(instance.reference).attr("data-hotkey-hints");
|
|
if (hotkey_hints) {
|
|
instance.setContent(hotkey_hints.replace("⇧", "Shift").replaceAll(",", " + "));
|
|
return undefined;
|
|
}
|
|
return false;
|
|
},
|
|
});
|
|
|
|
tippy.delegate("body", {
|
|
target: ".saved_snippets-dropdown-list-container .dropdown-list-delete",
|
|
content: $t({defaultMessage: "Delete snippet"}),
|
|
delay: LONG_HOVER_DELAY,
|
|
appendTo: () => document.body,
|
|
onHidden(instance) {
|
|
instance.destroy();
|
|
},
|
|
});
|
|
}
|