summaryrefslogtreecommitdiff
path: root/examples/snowpack/public/js/index.js
blob: 71e2496d43c935e11aac07429876b1a986a61b49 (plain) (blame)
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
 * Debounce functions for better performance
 * (c) 2018 Chris Ferdinandi, MIT License, https://gomakethings.com
 * @param  {Function} fn The function to debounce
 */
function debounce(fn) {
  // Setup a timer
  var timeout;
  // Return a function to run debounced
  return function () {
    // Setup the arguments
    var context = this;
    var args = arguments;
    // If there's a timer, cancel it
    if (timeout) {
      window.cancelAnimationFrame(timeout);
    }
    // Setup the new requestAnimationFrame()
    timeout = window.requestAnimationFrame(function () {
      fn.apply(context, args);
    });
  };
}

function isScrolledIntoView(el) {
  const { top } = el.getBoundingClientRect();
  const halfHeight = window.innerHeight / 2;
  const isVisible = top <= halfHeight;
  return isVisible;
}

function setActiveToc() {
  if (window.innerWidth < 1240) {
    return;
  }
  if (!tableOfContentsEl) {
    return;
  }

  const headings = [
    ...document.querySelectorAll(
      '.content-body h1, .content-body h2, .content-body h3, .content-body h4',
    ),
  ].filter((el) => !!el.id);
  const scrolledToBeginning = window.scrollY === 0;
  const scrolledToEnd =
    Math.ceil(window.innerHeight + window.scrollY) >=
    Math.ceil(document.body.getBoundingClientRect().height);

  let el;
  if (scrolledToBeginning) {
    el = headings[0]; // if we‘re at the top of the page, highlight the first item
  } else if (scrolledToEnd) {
    el = headings[headings.length - 1]; // if we’re at the end of the page, highlight the last item
  } else {
    el = headings.reverse().find(isScrolledIntoView); // otherwise highlight the one that’s at least halfway up the page
  }

  if (!el) return;

  const elId = el.id;
  const href = `#${elId}`;
  const tocEl = tableOfContentsEl.querySelector(`a[href="${href}"]`);
  // only add the active class once, which will also prevent scroll from re-triggering while scrolling to the same element
  if (!tocEl || tocEl.classList.contains('active')) {
    return;
  }

  tableOfContentsEl.querySelectorAll(`a.active`).forEach((aEl) => {
    if (aEl.getAttribute('href') !== href) aEl.classList.remove('active');
  });

  tocEl.classList.add('active');

  // // update nav on desktop
  // if (window.innerWidth >= 860) {
  //   tocEl.scrollIntoView({behavior: 'smooth'});
  // }
  //   {
  //   top:
  //     tocEl.getBoundingClientRect().top + gridTocEl.scrollTop - PADDING_TOP,
  //   behavior: 'smooth',
  // });
}

const tableOfContentsEl = document.querySelector('.toc');
window.addEventListener('scroll', debounce(setActiveToc));
/* May not be needed:
  window.addEventListener('DOMContentLoaded', (event) => {
    if (!window.location.hash) {
      return;
    }
    const elNeedingScroll = document.getElementById(window.location.hash.substring(1));
    if (!elNeedingScroll) {
      return;
    }
    elNeedingScroll.scrollIntoView();
    elNeedingScroll.classList.add('highlighted');
  });
  */

window.addEventListener('DOMContentLoaded', (event) => {
  if (!tableOfContentsEl) {
    return;
  }
  document.querySelectorAll('.content h3, .content h4').forEach((headerEl) => {
    const linkEl = document.createElement('a');
    // linkEl.setAttribute('target', "_blank");
    linkEl.setAttribute('href', '#' + headerEl.id);
    linkEl.classList.add('header-link');
    linkEl.innerText = '#';
    headerEl.appendChild(linkEl);
  });
  setActiveToc();
});