1/** 2 * Website Phone Input - Vanilla JS Interaction 3 * file: public/assets/js/website/phone-input.js 4 */ 5 6(function() { 7 'use strict'; 8 9 window.initWebsitePhoneInputs = function() { 10 const components = document.querySelectorAll('.website-phone-input-root'); 11 12 components.forEach(root => { 13 if(root.dataset.init === 'true') return; 14 root.dataset.init = 'true'; 15 16 const trigger = root.querySelector('.website-phone-country-trigger'); 17 const dropdown = root.querySelector('.website-phone-dropdown'); 18 const searchInput = root.querySelector('.phone-search-input'); 19 const list = root.querySelector('.phone-list'); 20 const hiddenIso = root.querySelector('.phone-iso-hidden'); 21 22 // Visual Elements 23 const flagImg = trigger.querySelector('.phone-flag-icon'); 24 const dialText = trigger.querySelector('.phone-dial-text'); 25 26 // --- Methods --- 27 28 const closeDropdown = () => { 29 trigger.setAttribute('aria-expanded', 'false'); 30 }; 31 32 const openDropdown = () => { 33 // Close others 34 document.querySelectorAll('.website-phone-country-trigger[aria-expanded="true"]').forEach(t => { 35 if(t !== trigger) t.setAttribute('aria-expanded', 'false'); 36 }); 37 38 trigger.setAttribute('aria-expanded', 'true'); 39 dropdown.scrollTop = 0; 40 searchInput.value = ''; 41 filterList(''); // reset filter 42 setTimeout(() => searchInput.focus(), 50); 43 44 // Scroll to selected 45 const selected = list.querySelector('.phone-item.selected'); 46 if(selected) { 47 selected.scrollIntoView({ block: 'center' }); 48 } 49 }; 50 51 const toggleDropdown = (e) => { 52 e.preventDefault(); 53 e.stopPropagation(); 54 if(trigger.getAttribute('aria-expanded') === 'true') { 55 closeDropdown(); 56 } else { 57 openDropdown(); 58 } 59 }; 60 61 const selectItem = (item) => { 62 const iso = item.dataset.iso; 63 const dial = item.dataset.dial; 64 65 // Update Hidden Input 66 hiddenIso.value = iso; 67 68 // Update UI 69 dialText.textContent = dial; 70 flagImg.src = `https://flagcdn.com/w20/${iso.toLowerCase()}.png`; 71 72 // Update Selection State 73 list.querySelectorAll('.phone-item').forEach(li => li.classList.remove('selected')); 74 item.classList.add('selected'); 75 76 closeDropdown(); 77 }; 78 79 const filterList = (query) => { 80 const q = query.toLowerCase(); 81 const items = list.querySelectorAll('.phone-item'); 82 items.forEach(item => { 83 const text = item.dataset.name.toLowerCase(); 84 const dial = item.dataset.dial.replace('+', ''); 85 if(text.includes(q) || dial.includes(q)) { 86 item.style.display = 'flex'; 87 } else { 88 item.style.display = 'none'; 89 } 90 }); 91 }; 92 93 // --- Event Listeners --- 94 95 trigger.addEventListener('click', toggleDropdown); 96 97 searchInput.addEventListener('input', (e) => filterList(e.target.value)); 98 searchInput.addEventListener('click', (e) => e.stopPropagation()); 99 100 list.querySelectorAll('.phone-item').forEach(item => { 101 item.addEventListener('click', (e) => { 102 e.stopPropagation(); 103 selectItem(item); 104 }); 105 }); 106 107 // Close on outside click 108 document.addEventListener('click', (e) => { 109 if(!root.contains(e.target)) { 110 closeDropdown(); 111 } 112 }); 113 114 // Keyboard Navigation (Basic) 115 root.addEventListener('keydown', (e) => { 116 if(e.key === 'Escape') closeDropdown(); 117 }); 118 }); 119 }; 120 121 // Auto Init on Load 122 document.addEventListener('DOMContentLoaded', () => { 123 window.initWebsitePhoneInputs(); 124 }); 125 126})();
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.