diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /devtools/client/shared/scroll.js | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/shared/scroll.js')
-rw-r--r-- | devtools/client/shared/scroll.js | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/devtools/client/shared/scroll.js b/devtools/client/shared/scroll.js new file mode 100644 index 0000000000..e740e5c8f6 --- /dev/null +++ b/devtools/client/shared/scroll.js @@ -0,0 +1,144 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Make this available to both AMD and CJS environments +define(function (require, exports, module) { + /** + * Scroll the document so that the element "elem" appears in the viewport. + * + * @param {DOMNode} elem + * The element that needs to appear in the viewport. + * @param {Boolean} centered + * true if you want it centered, false if you want it to appear on the + * top of the viewport. It is true by default, and that is usually what + * you want. + * @param {Boolean} smooth + * true if you want the scroll to happen smoothly, instead of instantly. + * It is false by default. + */ + function scrollIntoViewIfNeeded(elem, centered = true, smooth = false) { + const win = elem.ownerDocument.defaultView; + const clientRect = elem.getBoundingClientRect(); + + // The following are always from the {top, bottom} + // of the viewport, to the {top, …} of the box. + // Think of them as geometrical vectors, it helps. + // The origin is at the top left. + + const topToBottom = clientRect.bottom; + const bottomToTop = clientRect.top - win.innerHeight; + // We allow one translation on the y axis. + let yAllowed = true; + + // disable smooth scrolling when the user prefers reduced motion + const reducedMotion = win.matchMedia("(prefers-reduced-motion)").matches; + smooth = smooth && !reducedMotion; + + const options = { behavior: smooth ? "smooth" : "auto" }; + + // Whatever `centered` is, the behavior is the same if the box is + // (even partially) visible. + if ((topToBottom > 0 || !centered) && topToBottom <= elem.offsetHeight) { + win.scrollBy( + Object.assign( + { left: 0, top: topToBottom - elem.offsetHeight }, + options + ) + ); + yAllowed = false; + } else if ( + (bottomToTop < 0 || !centered) && + bottomToTop >= -elem.offsetHeight + ) { + win.scrollBy( + Object.assign( + { left: 0, top: bottomToTop + elem.offsetHeight }, + options + ) + ); + + yAllowed = false; + } + + // If we want it centered, and the box is completely hidden, + // then we center it explicitly. + if (centered) { + if (yAllowed && (topToBottom <= 0 || bottomToTop >= 0)) { + const x = win.scrollX; + const y = + win.scrollY + + clientRect.top - + (win.innerHeight - elem.offsetHeight) / 2; + win.scroll(Object.assign({ left: x, top: y }, options)); + } + } + } + + function closestScrolledParent(node) { + if (node == null) { + return null; + } + + if (node.scrollHeight > node.clientHeight) { + return node; + } + + return closestScrolledParent(node.parentNode); + } + + /** + * Scrolls the element into view if it is not visible. + * + * @param {DOMNode|undefined} element + * The item to be scrolled to. + * + * @param {Object|undefined} options + * An options object which can contain: + * - container: possible scrollable container. If it is not scrollable, we will + * look it up. + * - alignTo: "top" or "bottom" to indicate if we should scroll the element + * to the top or the bottom of the scrollable container when the + * element is off canvas. + * - center: Indicate if we should scroll the element to the middle of the + * scrollable container when the element is off canvas. + */ + function scrollIntoView(element, options = {}) { + if (!element) { + return; + } + + const { alignTo, center, container } = options; + + const { top, bottom } = element.getBoundingClientRect(); + const scrolledParent = closestScrolledParent( + container || element.parentNode + ); + const scrolledParentRect = scrolledParent + ? scrolledParent.getBoundingClientRect() + : null; + const isVisible = + !scrolledParent || + (top >= scrolledParentRect.top && bottom <= scrolledParentRect.bottom); + + if (isVisible) { + return; + } + + if (center) { + element.scrollIntoView({ block: "center" }); + return; + } + + const scrollToTop = alignTo + ? alignTo === "top" + : !scrolledParentRect || top < scrolledParentRect.top; + element.scrollIntoView(scrollToTop); + } + + // Exports from this module + module.exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded; + module.exports.scrollIntoView = scrollIntoView; +}); |