PageSourceSearch

https://bikeatthebeach.com/js/modal-navigation.js

js bikeatthebeach.com collected 2026-09-24 13:57:56 UTC 3,600 bytes, 93 lines download raw bytes

1/* ------------------------------------------------------------------
2   modal-navigation.js                          © 2025‑07‑29
3   Call  initModalNavigation()  once after the DOM + Bootstrap are ready.
4   ------------------------------------------------------------------ */
5
6export function initModalNavigation () {
7  /* 1 collect nav‑enabled modals, honour data‑order (default 0) */
8  const modals = Array.from(
9      document.querySelectorAll('.modal:not([data-nav="false"])')
10    )
11    .sort((a, b) => (+a.dataset.order || 0) - (+b.dataset.order || 0));
12
13  const ids = modals.map(m => m.id.replace(/^modal-/, ''));
14  let idx   = -1;                       // current index
15
16  /* 2 ensure each nav‑enabled modal has an accessible footer with arrows */
17  modals.forEach(m => {
18    /* footer already present? -> skip */
19    if (m.querySelector('.modal-footer')) return;
20
21    /* build footer */
22    const footer = document.createElement('div');
23    footer.className = 'modal-footer d-flex justify-content-center gap-3';
24
25    footer.innerHTML = `
26      <button class="btn btn-secondary"
27              type="button"
28              aria-label="Previous information card"
29              data-nav-prev>←</button>
30
31      <button class="btn btn-primary"
32              type="button"
33              aria-label="Back to map"
34              data-bs-dismiss="modal">Map</button>
35
36      <button class="btn btn-secondary"
37              type="button"
38              aria-label="Next information card"
39              data-nav-next>→</button>
40    `;
41    m.querySelector('.modal-content')?.appendChild(footer);
42  });
43
44  /* 3 helper to (re‑)open by sequence index */
45  function showByIndex (newIdx) {
46    if (!ids.length) return;             // no navigable modals
47
48    /* close current */
49    const openEl = document.querySelector('.modal.show');
50    if (openEl) window.bootstrap.Modal.getInstance(openEl)?.hide();
51
52    idx = (newIdx + ids.length) % ids.length;          // wrap
53    const box = document.getElementById(`modal-${ids[idx]}`);
54    const inst = window.bootstrap.Modal.getOrCreateInstance(box);
55    inst.show();
56
57    /* focus the modal heading for WCAG "focus moves to dialog" */
58    const h = box.querySelector('.modal-title, h3, h2, h1');
59    setTimeout(() => h?.focus(), 17);                  // after transition
60  }
61
62  /* 4 exposed API — handles both navigable AND nav‑disabled modals */
63  window.openModal = id => {
64    const navIdx = ids.indexOf(id);
65    if (navIdx >= 0) {
66      // Modal is in the navigation sequence — open with ← → arrows
67      showByIndex(navIdx);
68    } else {
69      // Modal exists but has navigation disabled — open standalone (no arrows)
70      const el = document.getElementById(`modal-${id}`);
71      if (!el) return;
72      const openEl = document.querySelector('.modal.show');
73      if (openEl) window.bootstrap.Modal.getInstance(openEl)?.hide();
74      window.bootstrap.Modal.getOrCreateInstance(el).show();
75    }
76  };
77
78  window.nextModal = ()  => showByIndex(idx + 1);
79  window.prevModal = ()  => showByIndex(idx - 1);
80
81  /* 5 delegate clicks */
82  document.addEventListener('click', e => {
83    if (e.target.closest('[data-nav-next]')) { window.nextModal(); }
84    if (e.target.closest('[data-nav-prev]')) { window.prevModal(); }
85  });
86
87  /* 6 arrow‑key support inside any open modal */
88  document.addEventListener('keydown', e => {
89    if (!document.querySelector('.modal.show')) return;
90    if (e.key === 'ArrowRight') { e.preventDefault(); window.nextModal(); }
91    if (e.key === 'ArrowLeft')  { e.preventDefault(); window.prevModal(); }
92  });
93}

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.