ts: Migrate `popover_menus` to typescript.

This commit migrates `popover_menus` module to TypeScript. Also adds
a placeholder types declaration file for `css_variables` module.
This commit is contained in:
Lalit 2023-11-18 17:24:36 +05:30 committed by Tim Abbott
parent bfff48decc
commit 02257b8cbf
3 changed files with 92 additions and 33 deletions

View File

@ -84,6 +84,7 @@ EXEMPT_FILES = make_set(
"web/src/copied_tooltip.ts", "web/src/copied_tooltip.ts",
"web/src/copy_and_paste.js", "web/src/copy_and_paste.js",
"web/src/csrf.ts", "web/src/csrf.ts",
"web/src/css_variables.d.ts",
"web/src/css_variables.js", "web/src/css_variables.js",
"web/src/custom_profile_fields_ui.js", "web/src/custom_profile_fields_ui.js",
"web/src/dark_theme.ts", "web/src/dark_theme.ts",
@ -166,9 +167,10 @@ EXEMPT_FILES = make_set(
"web/src/pm_list_dom.ts", "web/src/pm_list_dom.ts",
"web/src/poll_modal.js", "web/src/poll_modal.js",
"web/src/poll_widget.ts", "web/src/poll_widget.ts",
"web/src/popover_menus.js", "web/src/popover_menus.ts",
"web/src/popover_menus_data.js", "web/src/popover_menus_data.js",
"web/src/popovers.ts", "web/src/popovers.ts",
"web/src/popperjs.d.ts",
"web/src/read_receipts.js", "web/src/read_receipts.js",
"web/src/ready.ts", "web/src/ready.ts",
"web/src/realm_icon.ts", "web/src/realm_icon.ts",

33
web/src/css_variables.d.ts vendored Normal file
View File

@ -0,0 +1,33 @@
/* This is a placeholder typescript declaration file for `css_variables` module.
We can't convert the `css_variables` module to typescript yet because converting
it causes Webpack to trigger type checking with the TypeScript compiler, which is very expensive.
TS-migration of this module was reverted in this PR: https://github.com/zulip/zulip/pull/24985.
*/
declare const css_variables: {
media_breakpoints: {
xs_min: string;
sm_min: string;
md_min: string;
mc_min: string;
lg_min: string;
xl_min: string;
ml_min: string;
mm_min: string;
ms_min: string;
};
media_breakpoints_num: {
xs: number;
sm: number;
md: number;
mc: number;
lg: number;
xl: number;
ml: number;
mm: number;
ms: number;
};
};
export = css_variables;

View File

@ -3,6 +3,7 @@
popovers system in popovers.js. */ popovers system in popovers.js. */
import $ from "jquery"; import $ from "jquery";
import type {Instance as PopoverInstance, Props as PopoverProps, ReferenceElement} from "tippy.js";
import tippy from "tippy.js"; import tippy from "tippy.js";
import * as blueslip from "./blueslip"; import * as blueslip from "./blueslip";
@ -12,7 +13,25 @@ import * as overlays from "./overlays";
import * as popovers from "./popovers"; import * as popovers from "./popovers";
import * as util from "./util"; import * as util from "./util";
export const popover_instances = { type PopoverName =
| "compose_control_buttons"
| "starred_messages"
| "drafts"
| "left_sidebar_inbox_popover"
| "left_sidebar_all_messages_popover"
| "left_sidebar_recent_view_popover"
| "top_left_sidebar"
| "message_actions"
| "stream_settings"
| "compose_mobile_button"
| "topics_menu"
| "send_later"
| "change_visibility_policy"
| "personal_menu"
| "gear_menu"
| "help_menu";
export const popover_instances: Record<PopoverName, PopoverInstance | null> = {
compose_control_buttons: null, compose_control_buttons: null,
starred_messages: null, starred_messages: null,
drafts: null, drafts: null,
@ -32,7 +51,7 @@ export const popover_instances = {
}; };
/* Keyboard UI functions */ /* Keyboard UI functions */
export function popover_items_handle_keyboard(key, $items) { export function popover_items_handle_keyboard(key: string, $items?: JQuery): void {
if (!$items) { if (!$items) {
return; return;
} }
@ -58,7 +77,7 @@ export function popover_items_handle_keyboard(key, $items) {
$items.eq(index).trigger("focus"); $items.eq(index).trigger("focus");
} }
export function focus_first_popover_item($items, index = 0) { export function focus_first_popover_item($items: JQuery, index = 0): void {
if (!$items) { if (!$items) {
return; return;
} }
@ -66,56 +85,59 @@ export function focus_first_popover_item($items, index = 0) {
$items.eq(index).expectOne().trigger("focus"); $items.eq(index).expectOne().trigger("focus");
} }
export function sidebar_menu_instance_handle_keyboard(instance, key) { export function sidebar_menu_instance_handle_keyboard(
instance: PopoverInstance,
key: string,
): void {
const items = get_popover_items_for_instance(instance); const items = get_popover_items_for_instance(instance);
popover_items_handle_keyboard(key, items); popover_items_handle_keyboard(key, items);
} }
export function get_visible_instance() { export function get_visible_instance(): PopoverInstance | null | undefined {
return Object.values(popover_instances).find(Boolean); return Object.values(popover_instances).find(Boolean);
} }
export function get_topic_menu_popover() { export function get_topic_menu_popover(): PopoverInstance | null {
return popover_instances.topics_menu; return popover_instances.topics_menu;
} }
export function get_scheduled_messages_popover() { export function get_scheduled_messages_popover(): PopoverInstance | null {
return popover_instances.send_later; return popover_instances.send_later;
} }
export function is_scheduled_messages_popover_displayed() { export function is_scheduled_messages_popover_displayed(): boolean | undefined {
return popover_instances.send_later?.state.isVisible; return popover_instances.send_later?.state.isVisible;
} }
export function get_compose_control_buttons_popover() { export function get_compose_control_buttons_popover(): PopoverInstance | null {
return popover_instances.compose_control_buttons; return popover_instances.compose_control_buttons;
} }
export function get_starred_messages_popover() { export function get_starred_messages_popover(): PopoverInstance | null {
return popover_instances.starred_messages; return popover_instances.starred_messages;
} }
export function is_personal_menu_popover_displayed() { export function is_personal_menu_popover_displayed(): boolean | undefined {
return popover_instances.personal_menu?.state.isVisible; return popover_instances.personal_menu?.state.isVisible;
} }
export function is_gear_menu_popover_displayed() { export function is_gear_menu_popover_displayed(): boolean | undefined {
return popover_instances.gear_menu?.state.isVisible; return popover_instances.gear_menu?.state.isVisible;
} }
export function get_gear_menu_instance() { export function get_gear_menu_instance(): PopoverInstance | null {
return popover_instances.gear_menu; return popover_instances.gear_menu;
} }
export function is_help_menu_popover_displayed() { export function is_help_menu_popover_displayed(): boolean | undefined {
return popover_instances.help_menu?.state.isVisible; return popover_instances.help_menu?.state.isVisible;
} }
export function is_message_actions_popover_displayed() { export function is_message_actions_popover_displayed(): boolean | undefined {
return popover_instances.message_actions?.state.isVisible; return popover_instances.message_actions?.state.isVisible;
} }
function get_popover_items_for_instance(instance) { function get_popover_items_for_instance(instance: PopoverInstance): JQuery | undefined {
const $current_elem = $(instance.popper); const $current_elem = $(instance.popper);
const class_name = $current_elem.attr("class"); const class_name = $current_elem.attr("class");
@ -127,7 +149,7 @@ function get_popover_items_for_instance(instance) {
return $current_elem.find("a:visible"); return $current_elem.find("a:visible");
} }
export const default_popover_props = { export const default_popover_props: Partial<PopoverProps> = {
delay: 0, delay: 0,
appendTo: () => document.body, appendTo: () => document.body,
trigger: "click", trigger: "click",
@ -154,7 +176,8 @@ export const default_popover_props = {
phase: "beforeWrite", phase: "beforeWrite",
requires: ["$$tippy"], requires: ["$$tippy"],
fn({state}) { fn({state}) {
const instance = state.elements.reference._tippy; // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const instance = (state.elements.reference as ReferenceElement)._tippy!;
const $popover = $(state.elements.popper); const $popover = $(state.elements.popper);
const $tippy_box = $popover.find(".tippy-box"); const $tippy_box = $popover.find(".tippy-box");
if ($tippy_box.hasClass("show-when-reference-hidden")) { if ($tippy_box.hasClass("show-when-reference-hidden")) {
@ -249,7 +272,7 @@ export const left_sidebar_tippy_options = {
}, },
}; };
export function on_show_prep(instance) { export function on_show_prep(instance: PopoverInstance): void {
$(instance.popper).on("click", (e) => { $(instance.popper).on("click", (e) => {
// Popover is not hidden on click inside it unless the click handler for the // Popover is not hidden on click inside it unless the click handler for the
// element explicitly hides the popover when handling the event. // element explicitly hides the popover when handling the event.
@ -264,22 +287,19 @@ export function on_show_prep(instance) {
}); });
} }
function get_props_for_popover_centering(popover_props) { function get_props_for_popover_centering(
popover_props: Partial<PopoverProps>,
): Partial<PopoverProps> {
return { return {
arrow: false, arrow: false,
getReferenceClientRect: () => ({ getReferenceClientRect: () => new DOMRect(0, 0, 0, 0),
width: 0,
height: 0,
left: 0,
top: 0,
}),
placement: "top", placement: "top",
popperOptions: { popperOptions: {
modifiers: [ modifiers: [
{ {
name: "offset", name: "offset",
options: { options: {
offset({popper}) { offset({popper}: {popper: DOMRect}) {
// Calculate the offset needed to place the reference in the center // Calculate the offset needed to place the reference in the center
const x_offset_to_center = window.innerWidth / 2; const x_offset_to_center = window.innerWidth / 2;
let y_offset_to_center = window.innerHeight / 2 - popper.height / 2; let y_offset_to_center = window.innerHeight / 2 - popper.height / 2;
@ -291,7 +311,7 @@ function get_props_for_popover_centering(popover_props) {
// is causing a resize (thus calling this `offset` modifier function), in which case // is causing a resize (thus calling this `offset` modifier function), in which case
// we need to move the popover to the top of the screen. // we need to move the popover to the top of the screen.
if (util.is_mobile()) { if (util.is_mobile()) {
const $focused_element = $(document.activeElement); const $focused_element = $(document.activeElement!);
if ( if (
$focused_element.is( $focused_element.is(
"input[type=text], input[type=number], textarea", "input[type=text], input[type=number], textarea",
@ -334,7 +354,11 @@ function get_props_for_popover_centering(popover_props) {
// Toggles a popover menu directly; intended for use in keyboard // Toggles a popover menu directly; intended for use in keyboard
// shortcuts and similar alternative ways to open a popover menu. // shortcuts and similar alternative ways to open a popover menu.
export function toggle_popover_menu(target, popover_props, options) { export function toggle_popover_menu(
target: ReferenceElement,
popover_props: Partial<PopoverProps>,
options?: {show_as_overlay_on_mobile: boolean},
): void {
const instance = target._tippy; const instance = target._tippy;
if (instance) { if (instance) {
instance.hide(); instance.hide();
@ -353,7 +377,7 @@ export function toggle_popover_menu(target, popover_props, options) {
if (popover_props.popperOptions?.modifiers) { if (popover_props.popperOptions?.modifiers) {
popover_props.popperOptions.modifiers = [ popover_props.popperOptions.modifiers = [
...default_popover_props.popperOptions.modifiers, ...default_popover_props.popperOptions!.modifiers!,
...popover_props.popperOptions.modifiers, ...popover_props.popperOptions.modifiers,
]; ];
} }
@ -368,7 +392,7 @@ export function toggle_popover_menu(target, popover_props, options) {
// Main function to define a popover menu, opened via clicking on the // Main function to define a popover menu, opened via clicking on the
// target selector. // target selector.
export function register_popover_menu(target, popover_props) { export function register_popover_menu(target: string, popover_props: Partial<PopoverProps>): void {
// For some elements, such as the click target to open the message // For some elements, such as the click target to open the message
// actions menu, we want to avoid propagating the click event to // actions menu, we want to avoid propagating the click event to
// parent elements. Tippy's built-in `delegate` method does not // parent elements. Tippy's built-in `delegate` method does not
@ -389,7 +413,7 @@ export function register_popover_menu(target, popover_props) {
}); });
} }
export function initialize() { export function initialize(): void {
/* Configure popovers to hide when toggling overlays. */ /* Configure popovers to hide when toggling overlays. */
overlays.register_pre_open_hook(popovers.hide_all); overlays.register_pre_open_hook(popovers.hide_all);
overlays.register_pre_close_hook(popovers.hide_all); overlays.register_pre_close_hook(popovers.hide_all);