sector_long_form-1.0.x-dev/components/toc/toc.js

components/toc/toc.js


(function () {
    'use strict';

    /**
     * A pager for the long form document.
     */
    Drupal.behaviors.long_form_toc = {
      log: (message) => {
        const prefix = 'sector_toc: ';
        const debug = false;

        if (debug) {
          if (message.isObject) {
            console.log(prefix, message);
          }
          else {
            console.log(prefix + message);
          }
        }
      },
      attach: (context, settings) => {

        const { sector_long_form: config } = settings;

        const table = document.querySelector(`.${config.toc_class}`)
        const chunks = document.querySelectorAll(`.${config.chunker_class}:has(> h2[id])`);

        // build list
        chunks.forEach(chunk => {
            const heading = chunk.querySelector('h2[id]');
            const li = document.createElement('li');
            li.innerHTML = `<a href="#${heading.getAttribute('id')}">${heading.textContent}</a>`

            const subheadings = chunk.querySelectorAll(`h2[id] ~ :is(h3[id])`)

            if (subheadings.length > 0) {
                const subtable = document.createElement('ol');
                subheadings.forEach(subheading => {
                    const sli = document.createElement('li');
                    sli.innerHTML = `<a href="#${subheading.getAttribute('id')}">${subheading.textContent}</a>`
                    subtable.appendChild(sli)
                })
                li.appendChild(subtable);
            }

            table.appendChild(li);
          })

          table.querySelectorAll('a').forEach(a => a.addEventListener('click', () => {
        }))

      // Add event handler to toc links.
      table.querySelectorAll('a').forEach(anchor => {
        anchor.addEventListener('click', (evt) => {
          evt.preventDefault();
          const new_heading_id = evt.target.getAttribute('href');
          Drupal.behaviors.long_form_toc.log('clicked id=' + new_heading_id);
          updateHeadingInUrl(new_heading_id.replace('#', ''));
          document.querySelector(new_heading_id)?.scrollIntoView({
            behavior: 'smooth',
            block: "start", inline: "nearest"
          })
        })
      });

      function tocLocationHashChange() {
        const hash = location.hash;
        Drupal.behaviors.long_form_toc.log("tocLocationHashChange() hash=" + hash);
        const elem = document.querySelector(hash);
        // If hash matches an element in node body field.
        if (elem) {
          setActive(`.${config.toc_class}`, hash);
        }
        else {
          Drupal.behaviors.long_form_toc.log('tocLocationHashChange() no hash==toc match');
          // Find parent chunker section, then make h2 active.
          //let h2 = $(hash).parents(`.${config.chunker_class}`).find('h2');
          let h2 = document.querySelector(hash)?.closest(`.${config.chunker_class}`).querySelector('h2')
          Drupal.behaviors.long_form_toc.log('nearest h2=' + h2);
          let h2_id = h2.getAttribute('id');
          // Make toc with matching href 'active'.
          setActive(`.${config.toc_class}`, '#' + h2_id);
        }
      }

      // Set hyperlink and parent list item active in ToC.
      function setActive(toc_selector, hash) {
        // If hash matches a toc entry.
        const activeLink = table.querySelector(`a[href$="${hash}"]`);
        let activeHeading = null;

        if (activeLink) {
          activeHeading = hash;
        }

        // Remove active from any non-matched link.
        table.querySelectorAll(`a:not([href$="${activeHeading}"])`).forEach(other => other.classList.remove('active'));
        //$(`.${config.toc_class} a[href!="${hash_toc}"]`).removeClass('active');

        // Remove active class from any active list items.
        table.querySelectorAll('li.active').forEach(other => other.classList.remove('active'))

        // Add active class to matched anchor, and parent list item.
        Drupal.behaviors.long_form_toc.log('toc block, open link with hash=' + activeHeading);
        activeLink.classList.add('active');
      }

      // On page load, do we have a URL fragment?
      // e.g. arriving from bookmark, or refresh existing page?
      if (location.hash) {
        Drupal.behaviors.long_form_toc.log('detected hash in url, triggering local hashchange event');
        tocLocationHashChange();
      }

      // When a hashchange event is fired open the correct h2/h3 header link in the ToC block.
      window.addEventListener("hashchange", tocLocationHashChange, false);

      // When the whole page has loaded create a new observer.
      window.addEventListener("load", createObserver, false);
      window.addEventListener("resize", createObserver, false);

      // Prepare variables and create the observer.
      function createObserver() {
        let observer;
        let headings = document.querySelectorAll(`.${config.chunker_class} :is(h2[id], h3[id])`);

        let options = {
         // root: document.querySelector('.prose'),
          rootMargin: '0px',
          threshold: 1.0
        }
        observer = new IntersectionObserver(handleObserver, options);

        headings.forEach(heading => {
          observer.observe(heading);
        });
      }

      function handleObserver(entries, observer){
        const intersecting = entries.filter(({ isIntersecting, intersectionRatio }) => isIntersecting && intersectionRatio === 1)
        intersecting.forEach(entry => {
          // Get a new heading after scrolling.
          let new_heading_id = entry.target.getAttribute('id');
          // Check isIntersecting to be sure the target currently intersects the root.
          // Check intersectionRatio to know how much of the target element is actually visible
          // within the root's intersection rectangle.
            Drupal.behaviors.long_form_toc.log('intersecting heading_id=' + new_heading_id);
            if (new_heading_id !== null) {
              updateHeadingInUrl(new_heading_id);
            }
        });
      }

      async function updateHeadingInUrl(new_heading_id) {
        const url = window.location.href.split('#')[0];
        // Get the current heading from url.
        let old_heading_id = window.location.hash.replace('#', '');
        // Update heading in url if a new one is visible on a page.
        if (new_heading_id !== old_heading_id) {
          // Update heading in url.
          history.replaceState(null, null, url + '#' + new_heading_id);
          // Fire the new hashchange event.
          window.dispatchEvent(new HashChangeEvent("hashchange"));
        }
      }





      }
    };

})(drupalSettings);

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc