giphy: Migrate Giphy popover to Tippy.

This commit is contained in:
Daniil Fadeev 2023-06-22 18:31:32 +03:00 committed by Tim Abbott
parent 4976655dda
commit 9c675ce62d
7 changed files with 100 additions and 123 deletions

View File

@ -30,7 +30,12 @@ export function initialize() {
});
delegate("body", {
target: ".compose_control_button",
// Only display Tippy content on classes accompanied by a `data-` attribute.
target: `
.compose_control_button[data-tooltip-template-id],
.compose_control_button[data-tippy-content],
.compose_control_button_container
`,
// Add some additional delay when they open
// so that regular users don't have to see
// them unless they want to.

View File

@ -8,6 +8,7 @@ import * as blueslip from "./blueslip";
import * as compose_ui from "./compose_ui";
import {media_breakpoints_num} from "./css_variables";
import {page_params} from "./page_params";
import * as popover_menus from "./popover_menus";
import * as popovers from "./popovers";
import * as rows from "./rows";
import * as ui_util from "./ui_util";
@ -15,13 +16,13 @@ import * as ui_util from "./ui_util";
let giphy_fetch;
let search_term = "";
let gifs_grid;
let $active_popover_element;
let giphy_popover_instance = null;
// Only used if popover called from edit message, otherwise it is `undefined`.
let edit_message_id;
export function is_popped_from_edit_message() {
return $active_popover_element && edit_message_id !== undefined;
return giphy_popover_instance && edit_message_id !== undefined;
}
export function focus_current_edit_message() {
@ -35,12 +36,6 @@ export function is_giphy_enabled() {
);
}
// Approximate width and height of
// giphy popover as computed by chrome
// + 25px;
const APPROX_HEIGHT = 350;
const APPROX_WIDTH = 300;
export function update_giphy_rating() {
if (
page_params.realm_giphy_rating === page_params.giphy_rating_options.disabled.id ||
@ -164,15 +159,15 @@ async function update_grid_with_search_term() {
export function hide_giphy_popover() {
// Returns `true` if the popover was open.
if ($active_popover_element) {
if (giphy_popover_instance) {
// We need to destroy the popover because when
// we hide it, bootstrap popover
// library removes `giphy-content` element
// as part of cleaning up everything inside
// `popover-content`, so we need to reinitialize
// the popover by destroying it.
$active_popover_element.popover("destroy");
$active_popover_element = undefined;
giphy_popover_instance.destroy();
giphy_popover_instance = undefined;
edit_message_id = undefined;
gifs_grid = undefined;
return true;
@ -188,101 +183,58 @@ function get_popover_content() {
return render_giphy_picker();
}
function get_popover_placement() {
let placement = popovers.compute_placement(
$active_popover_element,
APPROX_HEIGHT,
APPROX_WIDTH,
true,
);
if (placement === "viewport_center") {
// For legacy reasons `compute_placement` actually can
// return `viewport_center` which used to place popover in
// the center of the screen, but bootstrap doesn't actually
// support that and we already handle it on small screen sizes
// by placing it in center using `popover-flex`.
placement = "left";
}
return placement;
}
export function initialize() {
$("body").on("keydown", ".giphy-gif", ui_util.convert_enter_to_click);
$("body").on("keydown", ".compose_gif_icon", ui_util.convert_enter_to_click);
popover_menus.register_popover_menu(".compose_control_button.compose_gif_icon", {
placement: "top",
onCreate(instance) {
instance.setContent(ui_util.parse_html(get_popover_content()));
$(instance.popper).addClass("giphy-popover");
},
async onShow(instance) {
giphy_popover_instance = instance;
const $popper = $(giphy_popover_instance.popper).trigger("focus");
gifs_grid = await renderGIPHYGrid($popper.find(".giphy-content")[0]);
popovers.hide_all(true);
$("body").on("click", "#giphy_search_clear", async (e) => {
e.stopPropagation();
$("#giphy-search-query").val("").trigger("focus");
await update_grid_with_search_term();
});
$("body").on("click", ".compose_gif_icon", (e) => {
e.preventDefault();
e.stopPropagation();
const compose_click_target = compose_ui.get_compose_click_target(e);
if ($active_popover_element && $active_popover_element.get()[0] === compose_click_target) {
// Hide giphy popover if already active.
hide_giphy_popover();
return;
}
popovers.hide_all();
const $elt = $(compose_click_target);
if ($elt.parents(".message_edit_form").length === 1) {
// Store message id in global variable edit_message_id so that
// its value can be further used to correctly find the message textarea element.
edit_message_id = rows.id($elt.parents(".message_row"));
} else {
edit_message_id = undefined;
}
$active_popover_element = $elt;
$active_popover_element.popover({
animation: true,
placement: get_popover_placement(),
fixed: true,
html: true,
trigger: "manual",
template: get_popover_content(),
/* Popovers without a content property are not displayed,
* so we need something here; but we haven't contacted the
* Giphy API yet to get the actual content to display. */
content: " ",
});
$active_popover_element.popover("show");
// It takes about 1s for the popover to show; So,
// we wait for popover to display before rendering GIFs
// in it, otherwise popover is rendered with empty content.
const popover_observer = new MutationObserver(async () => {
if ($("#giphy_grid_in_popover .giphy-content").is(":visible")) {
popover_observer.disconnect();
gifs_grid = await renderGIPHYGrid($("#giphy_grid_in_popover .giphy-content")[0]);
const $click_target = $(instance.reference);
if ($click_target.parents(".message_edit_form").length === 1) {
// Store message id in global variable edit_message_id so that
// its value can be further used to correctly find the message textarea element.
edit_message_id = rows.id($click_target.parents(".message_row"));
} else {
edit_message_id = undefined;
}
});
const opts = {attributes: false, childList: true, characterData: false, subtree: true};
popover_observer.observe(document, opts);
$("body").on(
"keyup",
"#giphy-search-query",
// Use debounce to create a 300ms interval between
// every search. This makes the UX of searching pleasant
// by allowing user to finish typing before search
// is executed.
_.debounce(update_grid_with_search_term, 300),
);
$(document).one("compose_canceled.zulip compose_finished.zulip", () => {
hide_giphy_popover();
});
$(document).one("compose_canceled.zulip compose_finished.zulip", () => {
$popper.on(
"keyup",
"#giphy-search-query",
// Use debounce to create a 300ms interval between
// every search. This makes the UX of searching pleasant
// by allowing user to finish typing before search
// is executed.
_.debounce(update_grid_with_search_term, 300),
);
$popper.on("keydown", ".giphy-gif", ui_util.convert_enter_to_click);
$popper.on("keydown", ".compose_gif_icon", ui_util.convert_enter_to_click);
$popper.on("click", "#giphy_search_clear", async (e) => {
e.stopPropagation();
$("#giphy-search-query").val("");
await update_grid_with_search_term();
});
// Focus on search box by default.
// This is specially helpful for users
// navigating via keyboard.
$("#giphy-search-query").trigger("focus");
},
onHidden() {
hide_giphy_popover();
});
// Focus on search box by default.
// This is specially helpful for users
// navigating via keyboard.
$("#giphy-search-query").trigger("focus");
},
});
}

View File

@ -19,7 +19,6 @@ import * as compose_state from "./compose_state";
import * as compose_ui from "./compose_ui";
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_lists from "./message_lists";
@ -1088,7 +1087,6 @@ export function hide_all_except_sidebars(opts) {
hideAll();
}
emoji_picker.hide_emoji_popover();
giphy.hide_giphy_popover();
stream_popover.hide_stream_popover();
hide_all_user_info_popovers();
hide_playground_links_popover();

View File

@ -674,10 +674,26 @@ input.recipient_box {
gap: 4px;
align-items: center;
.compose_gif_icon {
/* We use the selector in this manner to maintain specificity. */
.compose_control_button_container .compose_gif_icon {
font-size: 22px;
height: 18px;
line-height: 18px;
/* Remove top and bottom padding. This is necessary
* because `compose_gif_icon` is no longer a flex item. */
padding: 0 5px;
}
.compose_control_button {
padding: 5px;
opacity: 0.7;
color: inherit;
text-decoration: none;
font-size: 17px;
text-align: center;
&:hover {
opacity: 1;
}
}
.fa-eye {
@ -751,19 +767,6 @@ input.recipient_box {
}
}
a.compose_control_button {
padding: 5px;
opacity: 0.7;
color: inherit;
text-decoration: none;
font-size: 17px;
text-align: center;
&:hover {
opacity: 1;
}
}
.drag {
display: none;
height: 18px;

View File

@ -735,6 +735,14 @@ ul {
left: -1px;
}
.giphy-popover {
.tippy-content {
/* We remove the default padding from this container
as it is not necessary for the Giphy popover. */
padding: 0;
}
}
#giphy_grid_in_popover {
/* 300px of GIPHY grid + 5px is the extra gutter space
* between gif columns. */
@ -762,6 +770,10 @@ ul {
flex-grow: 1;
margin: 5px;
border-radius: 3px;
/* By resetting to the default color from the `body`,
* we can ignore the colors applied from `tippy-box`. */
color: var(--color-text-default);
}
.clear_search_button {
@ -787,7 +799,12 @@ ul {
.popover-footer {
text-align: center;
background-color: hsl(0deg 0% 0%);
border-radius: 0 0 6px 6px;
/* The border radius corresponds to the default radius value from `tippy-box`. */
border-radius: 0 0 4px 4px;
/* This prevents the footer from experiencing height
fluctuations at the moment when the image is uploaded. */
min-height: 25px;
& img {
width: 120px;

View File

@ -8,7 +8,9 @@
<a role="button" class="compose_control_button fa fa-video-camera video_link" aria-label="{{t 'Add video call' }}" tabindex=0 data-tippy-content="{{t 'Add video call' }}"></a>
<a role="button" class="compose_control_button fa fa-smile-o emoji_map" aria-label="{{t 'Add emoji' }}" tabindex=0 data-tippy-content="{{t 'Add emoji' }}"></a>
<a role="button" class="compose_control_button fa fa-clock-o time_pick" aria-label="{{t 'Add global time' }}" tabindex=0 data-tooltip-template-id="add-global-time-tooltip" data-tippy-maxWidth="none"></a>
<a role="button" class="compose_control_button compose_gif_icon {{#unless giphy_enabled }} hide {{/unless}} zulip-icon zulip-icon-gif" aria-label="{{t 'Add GIF' }}" tabindex=0 data-tippy-content="{{t 'Add GIF' }}"></a>
<div class="compose_control_button_container {{#unless giphy_enabled }}hide{{/unless}}" data-tippy-content="{{t 'Add GIF' }}">
<a role="button" class="compose_control_button compose_gif_icon zulip-icon zulip-icon-gif" aria-label="{{t 'Add GIF' }}" tabindex=0></a>
</div>
<div class="divider hide-sm">|</div>
<div class="{{#if message_id}}hide-lg{{else}}hide-sm{{/if}}">
{{> compose_control_buttons_in_popover}}

View File

@ -1,4 +1,4 @@
<div class="popover" id="giphy_grid_in_popover">
<div id="giphy_grid_in_popover">
<div class="arrow"></div>
<div class="popover-inner">
<div class="search-box">