PageSourceSearch

https://villanuevadecarazo.es/core/themes/olivero/js/navigation-utils.js?v=10.2.7

js villanuevadecarazo.es collected 2026-10-02 20:31:44 UTC 6,197 bytes, 215 lines download raw bytes

1/**
2 * @file
3 * Controls the visibility of desktop navigation.
4 *
5 * Shows and hides the desktop navigation based on scroll position and controls
6 * the functionality of the button that shows/hides the navigation.
7 */
8
9/* eslint-disable no-inner-declarations */
10((Drupal) => {
11  /**
12   * Olivero helper functions.
13   *
14   * @namespace
15   */
16  Drupal.olivero = {};
17
18  /**
19   * Checks if the mobile navigation button is visible.
20   *
21   * @return {boolean}
22   *   True if navButtons is hidden, false if not.
23   */
24  function isDesktopNav() {
25    const navButtons = document.querySelector(
26      '[data-drupal-selector="mobile-buttons"]',
27    );
28    return navButtons
29      ? window.getComputedStyle(navButtons).getPropertyValue('display') ===
30          'none'
31      : false;
32  }
33
34  Drupal.olivero.isDesktopNav = isDesktopNav;
35
36  const stickyHeaderToggleButton = document.querySelector(
37    '[data-drupal-selector="sticky-header-toggle"]',
38  );
39  const siteHeaderFixable = document.querySelector(
40    '[data-drupal-selector="site-header-fixable"]',
41  );
42
43  /**
44   * Checks if the sticky header is enabled.
45   *
46   * @return {boolean}
47   *   True if sticky header is enabled, false if not.
48   */
49  function stickyHeaderIsEnabled() {
50    return stickyHeaderToggleButton.getAttribute('aria-checked') === 'true';
51  }
52
53  /**
54   * Save the current sticky header expanded state to localStorage, and set
55   * it to expire after two weeks.
56   *
57   * @param {boolean} expandedState
58   *   Current state of the sticky header button.
59   */
60  function setStickyHeaderStorage(expandedState) {
61    const now = new Date();
62
63    const item = {
64      value: expandedState,
65      expiry: now.getTime() + 20160000, // 2 weeks from now.
66    };
67    localStorage.setItem(
68      'Drupal.olivero.stickyHeaderState',
69      JSON.stringify(item),
70    );
71  }
72
73  /**
74   * Toggle the state of the sticky header between always pinned and
75   * only pinned when scrolled to the top of the viewport.
76   *
77   * @param {boolean} pinnedState
78   *   State to change the sticky header to.
79   */
80  function toggleStickyHeaderState(pinnedState) {
81    if (isDesktopNav()) {
82      siteHeaderFixable.classList.toggle('is-expanded', pinnedState);
83      stickyHeaderToggleButton.setAttribute('aria-checked', pinnedState);
84      setStickyHeaderStorage(pinnedState);
85    }
86  }
87
88  /**
89   * Return the sticky header's stored state from localStorage.
90   *
91   * @return {boolean}
92   *   Stored state of the sticky header.
93   */
94  function getStickyHeaderStorage() {
95    const stickyHeaderState = localStorage.getItem(
96      'Drupal.olivero.stickyHeaderState',
97    );
98
99    if (!stickyHeaderState) return false;
100
101    const item = JSON.parse(stickyHeaderState);
102    const now = new Date();
103
104    // Compare the expiry time of the item with the current time.
105    if (now.getTime() > item.expiry) {
106      // If the item is expired, delete the item from storage and return null.
107      localStorage.removeItem('Drupal.olivero.stickyHeaderState');
108      return false;
109    }
110    return item.value;
111  }
112
113  // Only enable scroll interactivity if the browser supports Intersection
114  // Observer.
115  // @see https://github.com/w3c/IntersectionObserver/blob/master/polyfill/intersection-observer.js#L19-L21
116  if (
117    'IntersectionObserver' in window &&
118    'IntersectionObserverEntry' in window &&
119    'intersectionRatio' in window.IntersectionObserverEntry.prototype
120  ) {
121    const fixableElements = document.querySelectorAll(
122      '[data-drupal-selector="site-header-fixable"], [data-drupal-selector="social-bar-inner"]',
123    );
124
125    function toggleDesktopNavVisibility(entries) {
126      if (!isDesktopNav()) return;
127
128      entries.forEach((entry) => {
129        // Firefox doesn't seem to support entry.isIntersecting properly,
130        // so we check the intersectionRatio.
131        fixableElements.forEach((el) =>
132          el.classList.toggle('is-fixed', entry.intersectionRatio < 1),
133        );
134      });
135    }
136
137    /**
138     * Gets the root margin by checking for various toolbar classes.
139     *
140     * @return {string}
141     *   Root margin for the Intersection Observer options object.
142     */
143    function getRootMargin() {
144      let rootMarginTop = 72;
145      const { body } = document;
146
147      if (body.classList.contains('toolbar-fixed')) {
148        rootMarginTop -= 39;
149      }
150
151      if (
152        body.classList.contains('toolbar-horizontal') &&
153        body.classList.contains('toolbar-tray-open')
154      ) {
155        rootMarginTop -= 40;
156      }
157
158      return `${rootMarginTop}px 0px 0px 0px`;
159    }
160
161    /**
162     * Monitor the navigation position.
163     */
164    function monitorNavPosition() {
165      const primaryNav = document.querySelector(
166        '[data-drupal-selector="site-header"]',
167      );
168      const options = {
169        rootMargin: getRootMargin(),
170        threshold: [0.999, 1],
171      };
172
173      const observer = new IntersectionObserver(
174        toggleDesktopNavVisibility,
175        options,
176      );
177
178      if (primaryNav) {
179        observer.observe(primaryNav);
180      }
181    }
182
183    if (stickyHeaderToggleButton) {
184      stickyHeaderToggleButton.addEventListener('click', () => {
185        toggleStickyHeaderState(!stickyHeaderIsEnabled());
186      });
187    }
188
189    // If header is pinned open and a header element gains focus, scroll to the
190    // top of the page to ensure that the header elements can be seen.
191    const siteHeaderInner = document.querySelector(
192      '[data-drupal-selector="site-header-inner"]',
193    );
194    if (siteHeaderInner) {
195      siteHeaderInner.addEventListener('focusin', () => {
196        if (isDesktopNav() && !stickyHeaderIsEnabled()) {
197          const header = document.querySelector(
198            '[data-drupal-selector="site-header"]',
199          );
200          const headerNav = header.querySelector(
201            '[data-drupal-selector="header-nav"]',
202          );
203          const headerMargin = header.clientHeight - headerNav.clientHeight;
204          if (window.scrollY > headerMargin) {
205            window.scrollTo(0, headerMargin);
206          }
207        }
208      });
209    }
210
211    monitorNavPosition();
212    setStickyHeaderStorage(getStickyHeaderStorage());
213    toggleStickyHeaderState(getStickyHeaderStorage());
214  }
215})(Drupal);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.