/* ============================================ TBNHS – Vanilla JS Tabs, Modals, Sidebar, Dropdowns, Header ============================================ */ (function () { 'use strict'; /* ──────────────────────────────────────── 1. HEADER – Scroll effect ──────────────────────────────────────── */ function initHeader() { const header = document.querySelector('.header'); if (!header) return; function onScroll() { header.classList.toggle('is-scrolled', window.scrollY > 50); } window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); } /* ──────────────────────────────────────── 2. MOBILE NAV TOGGLE ──────────────────────────────────────── */ function initMobileNav() { const toggle = document.querySelector('.header__toggle'); const nav = document.querySelector('.header__nav'); if (!toggle || !nav) return; toggle.addEventListener('click', function () { nav.classList.toggle('is-open'); const isOpen = nav.classList.contains('is-open'); toggle.setAttribute('aria-expanded', isOpen); }); // Close on outside click document.addEventListener('click', function (e) { if (!nav.contains(e.target) && !toggle.contains(e.target)) { nav.classList.remove('is-open'); toggle.setAttribute('aria-expanded', 'false'); } }); } /* ──────────────────────────────────────── 3. DROPDOWNS ──────────────────────────────────────── */ function initDropdowns() { const dropdowns = document.querySelectorAll('[data-dropdown]'); dropdowns.forEach(function (dropdown) { const trigger = dropdown.querySelector('[data-dropdown-trigger]'); if (!trigger) return; trigger.addEventListener('click', function (e) { e.preventDefault(); e.stopPropagation(); // Close other open dropdowns dropdowns.forEach(function (d) { if (d !== dropdown) d.classList.remove('is-open'); }); dropdown.classList.toggle('is-open'); }); }); // Close on outside click document.addEventListener('click', function (e) { dropdowns.forEach(function (dropdown) { if (!dropdown.contains(e.target)) { dropdown.classList.remove('is-open'); } }); }); // Close on Escape document.addEventListener('keydown', function (e) { if (e.key === 'Escape') { dropdowns.forEach(function (d) { d.classList.remove('is-open'); }); } }); } /* ──────────────────────────────────────── 4. TABS ──────────────────────────────────────── */ function initTabs() { const tabGroups = document.querySelectorAll('[data-tabs]'); tabGroups.forEach(function (group) { const buttons = group.querySelectorAll('.tabs__btn'); const panelContainer = document.querySelector( '[data-tab-panels="' + group.dataset.tabs + '"]' ); if (!panelContainer) return; const panels = panelContainer.querySelectorAll('.tabs__panel'); buttons.forEach(function (btn) { btn.addEventListener('click', function () { var target = btn.dataset.tab; // Deactivate all buttons.forEach(function (b) { b.classList.remove('is-active'); }); panels.forEach(function (p) { p.classList.remove('is-active'); }); // Activate selected btn.classList.add('is-active'); var panel = panelContainer.querySelector('[data-panel="' + target + '"]'); if (panel) panel.classList.add('is-active'); }); }); }); } /* ──────────────────────────────────────── 5. MODALS ──────────────────────────────────────── */ var currentModal = null; function openModal(modalId) { var overlay = document.getElementById(modalId); if (!overlay) return; overlay.classList.add('is-open'); document.body.style.overflow = 'hidden'; currentModal = overlay; // Focus the first focusable element var focusable = overlay.querySelector('input, select, textarea, button'); if (focusable) focusable.focus(); } function closeModal() { if (!currentModal) return; currentModal.classList.remove('is-open'); document.body.style.overflow = ''; currentModal = null; } function initModals() { // Open triggers document.addEventListener('click', function (e) { var trigger = e.target.closest('[data-modal-open]'); if (trigger) { e.preventDefault(); openModal(trigger.dataset.modalOpen); } }); // Close triggers document.addEventListener('click', function (e) { // Close button if (e.target.closest('[data-modal-close]')) { closeModal(); return; } // Backdrop click if (e.target.classList.contains('modal-overlay') && e.target.classList.contains('is-open')) { closeModal(); } }); // Escape key document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeModal(); }); } // Expose globally for dynamic modals window.openModal = openModal; window.closeModal = closeModal; /* ──────────────────────────────────────── 6. CHIP SELECT (Donation amounts) ──────────────────────────────────────── */ function initChipSelect() { var groups = document.querySelectorAll('[data-chip-group]'); groups.forEach(function (group) { var chips = group.querySelectorAll('.chip'); var input = document.querySelector('[data-chip-input="' + group.dataset.chipGroup + '"]'); chips.forEach(function (chip) { chip.addEventListener('click', function () { chips.forEach(function (c) { c.classList.remove('is-active'); }); chip.classList.add('is-active'); // Clear the "other" input if present if (input) input.value = ''; // Dispatch event with selected value var event = new CustomEvent('chipSelect', { detail: { value: chip.dataset.value } }); group.dispatchEvent(event); }); }); }); } /* ──────────────────────────────────────── 7. SCROLL TO TOP ──────────────────────────────────────── */ function initScrollTop() { var btn = document.querySelector('.footer__scroll-top'); if (!btn) return; btn.addEventListener('click', function () { window.scrollTo({ top: 0, behavior: 'smooth' }); }); } /* ──────────────────────────────────────── 8. NOTIFICATIONS / TOASTS ──────────────────────────────────────── */ function showNotification(message, type) { type = type || 'info'; var el = document.createElement('div'); el.className = 'notification notification--' + type; el.textContent = message; document.body.appendChild(el); // Trigger animation requestAnimationFrame(function () { el.classList.add('is-visible'); }); // Auto-remove setTimeout(function () { el.classList.remove('is-visible'); setTimeout(function () { el.remove(); }, 300); }, 4000); } window.showNotification = showNotification; /* ──────────────────────────────────────── 9. FORM — File upload visual (basic) ──────────────────────────────────────── */ function initFileUploads() { var fileInputs = document.querySelectorAll('input[type="file"]'); fileInputs.forEach(function (input) { input.addEventListener('change', function () { var label = input.closest('.form-group')?.querySelector('.form-hint'); if (label && input.files.length) { label.textContent = input.files[0].name; } }); }); } /* ──────────────────────────────────────── INIT ALL ──────────────────────────────────────── */ document.addEventListener('DOMContentLoaded', function () { initHeader(); initMobileNav(); initDropdowns(); initTabs(); initModals(); initChipSelect(); initScrollTop(); initFileUploads(); }); })();