mirror of https://github.com/zulip/zulip.git
giphy: Migrate Giphy popover to Tippy.
This commit is contained in:
parent
4976655dda
commit
9c675ce62d
|
@ -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.
|
||||
|
|
154
web/src/giphy.js
154
web/src/giphy.js
|
@ -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");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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">
|
||||
|
|
Loading…
Reference in New Issue