Welcome to mirror list, hosted at ThFree Co, Russian Federation.

resize_observer.js « utils « lib « javascripts « assets « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 5d194340b9e8543b7824975c1636355e837101ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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 scrolls the target note to the top of the content area.
 * Stops watching after any user input. So if user opens sidebar or manually
 * scrolls the page we don't hijack their scroll position
 *
 * @param {Object} options
 * @param {string} options.targetId - id of element to scroll to
 * @param {string} options.container - Selector of element containing target
 *
 * @return {ResizeObserver|null} - ResizeObserver instance if target looks like a note DOM ID
 */
export function scrollToTargetOnResize({
  targetId = window.location.hash.slice(1),
  container = '#content-body',
} = {}) {
  if (!targetId) return null;

  const ro = createResizeObserver();
  const containerEl = document.querySelector(container);
  let interactionListenersAdded = false;

  function keepTargetAtTop() {
    const anchorEl = document.getElementById(targetId);

    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;
}