From c49ca3ba841ea7763148a76d9a84e1bf7f5698ad Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Wed, 23 Aug 2017 09:38:42 +0200 Subject: move some helpers to ts (#2399) --- server/sonar-web/src/main/js/helpers/scrolling.ts | 106 ++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 server/sonar-web/src/main/js/helpers/scrolling.ts (limited to 'server/sonar-web/src/main/js/helpers/scrolling.ts') diff --git a/server/sonar-web/src/main/js/helpers/scrolling.ts b/server/sonar-web/src/main/js/helpers/scrolling.ts new file mode 100644 index 00000000000..a36c6ef0789 --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/scrolling.ts @@ -0,0 +1,106 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { debounce } from 'lodash'; + +const SCROLLING_DURATION = 100; +const SCROLLING_INTERVAL = 10; +const SCROLLING_STEPS = SCROLLING_DURATION / SCROLLING_INTERVAL; + +function isWindow(element: HTMLElement | Window): element is Window { + return element === window; +} + +function getScrollPosition(element: HTMLElement | Window): number { + return isWindow(element) ? window.scrollY : element.scrollTop; +} + +function scrollElement(element: HTMLElement | Window, position: number): void { + if (isWindow(element)) { + window.scrollTo(0, position); + } else { + element.scrollTop = position; + } +} + +let smoothScrollTop = (y: number, parent: HTMLElement | Window) => { + let scrollTop = getScrollPosition(parent); + const scrollingDown = y > scrollTop; + const step = Math.ceil(Math.abs(y - scrollTop) / SCROLLING_STEPS); + let stepsDone = 0; + + const interval = setInterval(() => { + if (scrollTop === y || SCROLLING_STEPS === stepsDone) { + clearInterval(interval); + } else { + let goal; + if (scrollingDown) { + goal = Math.min(y, scrollTop + step); + } else { + goal = Math.max(y, scrollTop - step); + } + stepsDone++; + scrollTop = goal; + scrollElement(parent, goal); + } + }, SCROLLING_INTERVAL); +}; + +smoothScrollTop = debounce(smoothScrollTop, SCROLLING_DURATION, { leading: true }); + +export function scrollToElement( + element: HTMLElement, + options: { + topOffset?: number; + bottomOffset?: number; + parent?: HTMLElement; + smooth?: boolean; + } +): void { + const opts = { topOffset: 0, bottomOffset: 0, parent: window, smooth: true, ...options }; + const { parent } = opts; + + const { top, bottom } = element.getBoundingClientRect(); + + const scrollTop = getScrollPosition(parent); + + const height: number = isWindow(parent) + ? window.innerHeight + : parent.getBoundingClientRect().height; + + const parentTop = isWindow(parent) ? 0 : parent.getBoundingClientRect().top; + + if (top - parentTop < opts.topOffset) { + const goal = scrollTop - opts.topOffset + top - parentTop; + if (opts.smooth) { + smoothScrollTop(goal, parent); + } else { + scrollElement(parent, goal); + } + } + + if (bottom - parentTop > height - opts.bottomOffset) { + const goal = scrollTop + bottom - parentTop - height + opts.bottomOffset; + if (opts.smooth) { + smoothScrollTop(goal, parent); + } else { + scrollElement(parent, goal); + } + } +} -- cgit v1.2.3