PageSourceSearch

https://www.lva-mobilite.fr/modules/ratpdev/rdds/components/molecules/mega-menu/mega-menu.js?tm8gor

js lva-mobilite.fr collected 2026-10-09 05:20:36 UTC 4,522 bytes, 126 lines download raw bytes

1/**
2 * @file mega-menu.js
3 *
4 * JS File for toggle elements.
5 */
6(function (Drupal, once) {
7
8  const toggleSelector = '.mega-menu [data-toggle]';
9
10  /**
11   * @Behavior
12   *
13   * Open/close target element by an ID set in the aria-controls attribute.
14   */
15  Drupal.behaviors.megaMenuToggleComponent = {
16    onceName: 'mega-menu-toggle',
17    attach(context) {
18      once(this.onceName, context.querySelectorAll(toggleSelector), context).forEach(function (toggle) {
19        ['click', 'touch'].forEach(function (e) {
20          toggle.addEventListener(e, function (event) {
21            if (toggle.getAttribute('mega-menu-closing')) {
22              toggle.removeAttribute('mega-menu-closing');
23              return;
24            }
25
26            // Close all other open items.
27            let targetId = toggle.getAttribute('aria-controls');
28            let allOthersOpenSelector = '.mega-menu [aria-expanded="true"]';
29            context.querySelectorAll(allOthersOpenSelector).forEach(function (btn) {
30              if (btn.getAttribute('aria-expanded') == 'true' && btn.getAttribute('aria-controls') != targetId) {
31                // Prevent infinite loop and/or closing current item.
32                btn.setAttribute('mega-menu-closing', true);
33                btn.click();
34              }
35            });
36          });
37        });
38      });
39
40      once(this.onceName, 'body', context).forEach(function (body) {
41        ['click', 'touch'].forEach(function (e) {
42          body.addEventListener(e, function (event) {
43            if (!event.target.closest('.mega-menu__list__item')) {
44              let allOthersOpenSelector = '.mega-menu__close[aria-expanded="true"]';
45              body.querySelectorAll(allOthersOpenSelector).forEach(function (btn) {
46                btn.click();
47              });
48            }
49          });
50        });
51      });
52    },
53    detach: function detach(context, settings, trigger) {
54      if (trigger === "unload") {
55        once.remove(this.onceName, context.querySelectorAll(toggleSelector));
56        once.remove(this.onceName, 'body');
57      }
58    },
59  };
60
61  Drupal.behaviors.megaMenuKeyboardNavigation = {
62    onceName: 'mega-menu-keyboard-nav',
63    selector: '[data-mega-menu-keyboard-nav="true"]',
64
65    attach(context) {
66      const self = this;
67
68      once(this.onceName, context.querySelectorAll(this.selector), context).forEach((megaMenu) => {
69        const menuItems = megaMenu.querySelectorAll('.mega-menu__list__item');
70
71        menuItems.forEach((menuItem) => {
72          const toggleButton = menuItem.querySelector('.mega-menu__link');
73          const submenuWrapper = menuItem.querySelector('.mega-menu__wrapper');
74
75          if (toggleButton && submenuWrapper) {
76            // Close submenu on actions on first level.
77            // Espace key press or tab key press.
78            toggleButton.addEventListener('keydown', (event) => {
79              if (
80                (event.key === 'Escape') ||
81                (event.shiftKey &&  event.key == 'Tab')
82              ) {
83                self.closeSubmenu();
84              }
85            });
86
87            // Close current submenu on first second level item or last level item.
88            // Close current submenu on escape key down.
89            submenuWrapper.addEventListener('keydown', (event) => {
90              const focusableElements = self.getFocusableElements(submenuWrapper);
91              const firstEl = focusableElements[0];
92              const lastEl = focusableElements[focusableElements.length - 1];
93
94              if (event.key === 'Tab') {
95                if (
96                  (event.shiftKey && event.target === firstEl) ||
97                  (!event.shiftKey && event.target === lastEl)
98                 ) {
99                  self.closeSubmenu();
100                }
101              } else if (event.key === 'Escape') {
102                self.closeSubmenu();
103                toggleButton.focus();
104              }
105            });
106          }
107        });
108      });
109    },
110
111    getFocusableElements(container) {
112      return Array.from(
113        container.querySelectorAll(
114          'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not
114([tabindex="-1"])'
115        ),
116      ).filter(el => !el.hasAttribute('disabled') && !el.getAttribute('aria-hidden'));
117    },
118
119    closeSubmenu() {
120      let allOthersOpenSelector = '.mega-menu__close[aria-expanded="true"]';
121      document.querySelectorAll(allOthersOpenSelector).forEach(function (btn) {
122        btn.click();
123      });
124    },
125  };
126})(Drupal, once);

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.