diff options
Diffstat (limited to 'app/assets/javascripts/lib/utils/resize_observer.js')
-rw-r--r-- | app/assets/javascripts/lib/utils/resize_observer.js | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/app/assets/javascripts/lib/utils/resize_observer.js b/app/assets/javascripts/lib/utils/resize_observer.js new file mode 100644 index 00000000000..e72c6fe1679 --- /dev/null +++ b/app/assets/javascripts/lib/utils/resize_observer.js @@ -0,0 +1,58 @@ +import { contentTop } from './common_utils'; + +const interactionEvents = ['mousedown', 'touchstart', 'keydown', 'wheel']; + +export function createResizeObserver() { + return new ResizeObserver((entries) => { + entries.forEach((entry) => { + entry.target.dispatchEvent(new CustomEvent(`ResizeUpdate`, { detail: { entry } })); + }); + }); +} + +// watches for change in size of a container element (e.g. for lazy-loaded images) +// and scroll the target element to the top of the content area +// stop watching after any user input. So if user opens sidebar or manually +// scrolls the page we don't hijack their scroll position +export function scrollToTargetOnResize({ + target = window.location.hash, + container = '#content-body', +} = {}) { + if (!target) return null; + + const ro = createResizeObserver(); + const containerEl = document.querySelector(container); + let interactionListenersAdded = false; + + function keepTargetAtTop() { + const anchorEl = document.querySelector(target); + + if (!anchorEl) return; + + const anchorTop = anchorEl.getBoundingClientRect().top + window.scrollY; + const top = anchorTop - contentTop(); + document.documentElement.scrollTo({ + top, + }); + + if (!interactionListenersAdded) { + interactionEvents.forEach((event) => + // eslint-disable-next-line no-use-before-define + document.addEventListener(event, removeListeners), + ); + interactionListenersAdded = true; + } + } + + function removeListeners() { + interactionEvents.forEach((event) => document.removeEventListener(event, removeListeners)); + + ro.unobserve(containerEl); + containerEl.removeEventListener('ResizeUpdate', keepTargetAtTop); + } + + containerEl.addEventListener('ResizeUpdate', keepTargetAtTop); + + ro.observe(containerEl); + return ro; +} |