zulip/web/src/scroll_util.ts

96 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import $ from "jquery";
import SimpleBar from "simplebar";
// This type is helpful for testing, where we may have a dummy object instead of an actual jquery object.
type JQueryOrZJQuery = {__zjquery?: true} & JQuery;
export function get_content_element($element: JQuery): JQuery {
const element = $element.expectOne()[0];
const sb = SimpleBar.instances.get(element);
if (sb) {
return $(sb.getContentElement());
}
return $element;
}
export function get_scroll_element($element: JQueryOrZJQuery): JQuery {
// For testing we just return the element itself.
if ($element?.__zjquery) {
return $element;
}
const element = $element.expectOne()[0];
const sb = SimpleBar.instances.get(element);
if (sb) {
return $(sb.getScrollElement());
} else if ("simplebar" in element.dataset) {
// The SimpleBar mutation observer hasnt processed this element yet.
// Create the SimpleBar early in case we need to add event listeners.
return $(new SimpleBar(element).getScrollElement()!);
}
return $element;
}
export function reset_scrollbar($element: JQuery): void {
const element = $element.expectOne()[0];
const sb = SimpleBar.instances.get(element);
if (sb) {
sb.getScrollElement().scrollTop = 0;
} else {
element.scrollTop = 0;
}
}
export function scroll_delta(opts: {
elem_top: number;
elem_bottom: number;
container_height: number;
}): number {
const elem_top = opts.elem_top;
const container_height = opts.container_height;
const elem_bottom = opts.elem_bottom;
let delta = 0;
if (elem_top < 0) {
delta = Math.max(elem_top, elem_bottom - container_height);
delta = Math.min(0, delta);
} else {
if (elem_bottom > container_height) {
delta = Math.min(elem_top, elem_bottom - container_height);
delta = Math.max(0, delta);
}
}
return delta;
}
export function scroll_element_into_container(
$elem: JQuery,
$container: JQuery,
sticky_header_height = 0,
): void {
// This does the minimum amount of scrolling that is needed to make
// the element visible. It doesn't try to center the element, so
// this will be non-intrusive to users when they already have
// the element visible.
$container = get_scroll_element($container);
const elem_top = $elem.position().top - sticky_header_height;
const elem_bottom = elem_top + ($elem.innerHeight() ?? 0);
const container_height = ($container.height() ?? 0) - sticky_header_height;
const opts = {
elem_top,
elem_bottom,
container_height,
};
const delta = scroll_delta(opts);
if (delta === 0) {
return;
}
$container.scrollTop(($container.scrollTop() ?? 0) + delta);
}