1class InputAutocomplete { 2 /** 3 * @param {Object} options 4 * @param {string} options.containerId - ID elementu, kam se vÅ¡e vykreslà 5 * @param {string} options.inputName - Atribut name pro skrytý input 6 * @param {string} options.phpEndpoint - Název PHP skriptu (napÅ. 'store.php') 7 * @param {string} options.phpEndpointOnSelect - Název PHP skriptu (napÅ. 'store-select.php'), který se zavolá po výbÄru 8 * @param {string} [options.placeholder] - Text v inputu (volitelný) 9 * @param {number} [options.minLen] - Minimálnà poÄet znaků pro hledánà (výchozà 2) 10 */ 11 constructor({ containerId, inputName, phpEndpoint, phpEndpointOnSelect, placeholder = 'ZaÄnÄte psát...', minLen = 2, onSelect = (url) => {} }) { 12 this.container = document.getElementById(containerId); 13 if (!this.container) { 14 console.error(`Kontejner #${containerId} nebyl nalezen.`); 15 return; 16 } 17 18 this.phpEndpoint = '/src/components-NEW/input-autocomplete/ajax/' + phpEndpoint; 19 this.phpEndpointOnSelect = '/src/components-NEW/input-autocomplete/ajax/' + phpEndpointOnSelect; 20 this.placeholder = placeholder; 21 this.inputName = inputName; 22 this.minLen = minLen; 23 24 this.input = null; 25 this.hiddenInput = null; 26 this.resultsContainer = null; 27 this.activeIndex = -1; 28 this.items = []; 29 30 this.onSelect = onSelect; 31 32 this.init(); 33 } 34 35 resetValue() { 36 this.input.value = ''; 37 this.hiddenInput.value = ''; 38 } 39 40 init() { 41 this.input = document.createElement('input'); 42 this.input.setAttribute('type', 'text'); 43 this.input.setAttribute('placeholder', this.placeholder ?? 'ZaÄnÄte psát...'); 44 this.input.classList.add('autocomplete-input'); 45 this.input.setAttribute('autocomplete', 'off'); 46 47 this.hiddenInput = document.createElement('input'); 48 this.hiddenInput.setAttribute('type', 'hidden'); 49 this.hiddenInput.setAttribute('name', this.inputName); 50 51 this.resultsContainer = document.createElement('div'); 52 this.resultsContainer.classList.add('autocomplete-results'); 53 54 this.container.appendChild(this.input); 55 this.container.appendChild(this.hiddenInput); 56 this.container.appendChild(this.resultsContainer); 57 this.container.classList.add('autocomplete-container'); 58 59 // Event pro psanà 60 this.input.addEventListener('input', () => { 61 if (this.input.value === '') this.hiddenInput.value = ''; 62 this.activeIndex = -1; // Reset indexu pÅi zmÄnÄ textu 63 this.search(); 64 }); 65 66 // 1. OVLÃDÃNà KLÃVESNICà 67 this.input.addEventListener('keydown', (e) => { 68 const resultItems = this.resultsContainer.querySelectorAll('.result-item'); 69 70 if (e.key === 'ArrowDown') { 71 e.preventDefault(); 72 this.activeIndex = (this.activeIndex + 1) % resultItems.length; 73 this.updateSelection(resultItems); 74 } 75 else if (e.key === 'ArrowUp') { 76 e.preventDefault(); 77 this.activeIndex = (this.activeIndex - 1 + resultItems.length) % resultItems.length; 78 this.updateSelection(resultItems); 79 } 80 else if (e.key === 'Enter') { 81 if (this.activeIndex > -1 && resultItems[this.activeIndex]) { 82 e.preventDefault(); 83 resultItems[this.activeIndex].click(); // Simulujeme klik na vybranou položku 84 } 85 } 86 else if (e.key === 'Escape') { 87 this.resultsContainer.innerHTML = ''; 88 } 89 }); 90 91 document.addEventListener('click', (e) => { 92 if (!this.container.contains(e.target)) { 93 this.resultsContainer.innerHTML = ''; 94 } 95 }); 96 } 97 98 // ZvýraznÄnà aktivnà položky v seznamu 99 updateSelection(resultItems) { 100 resultItems.forEach((el, index) => { 101 if (index === this.activeIndex) { 102 el.classList.add('active'); // Tato tÅÃda se postará o modré pozadà v CSS 103 el.scrollIntoView({ block: 'nearest' }); // Automatické posunutà scrollu 104 } else { 105 el.classList.remove('active'); 106 } 107 }); 108 } 109 110 async search() { 111 const query = this.input.value.trim(); 112 if (query.length < this.minLen) { 113 this.resultsContainer.innerHTML = ''; 114 return; 115 } 116 117 try { 118 const fetchUrl = new URL(this.phpEndpoint, window.location.origin); 119 fetchUrl.searchParams.set('q', query); 120 const response = await fetch(fetchUrl.toString()); 121 this.items = await response.json(); // UložÃme si data pro render 122 this.renderResults(this.items); 123 } catch (error) { 124 console.error('Chyba:', error); 125 } 126 } 127 128 renderResults(results) { 129 this.resultsContainer.innerHTML = ''; 130 if (results.length === 0) return; 131
132 results.forEach((item, index) => { 133 const div = document.createElement('div'); 134 div.classList.add('result-item'); 135 div.textContent = item.label; 136 137 div.onclick = () => { 138 this.input.value = item.label; 139 this.hiddenInput.value = item.value; 140 this.resultsContainer.innerHTML = ''; 141 this.activeIndex = -1; 142 this.selectItem(item); 143 }; 144 145 // MyÅ¡ by mÄla resetovat aktivnà index na sebe 146 div.onmouseenter = () => { 147 this.activeIndex = index; 148 const resultItems = this.resultsContainer.querySelectorAll('.result-item'); 149 this.updateSelection(resultItems); 150 }; 151 152 this.resultsContainer.appendChild(div); 153 }); 154 } 155 156 // Tato funkce se zavolá po vybránà nalezeného záznamu 157 async selectItem(item) { 158 159 const fetchUrl = new URL(this.phpEndpointOnSelect, window.location.origin); 160 fetchUrl.searchParams.set('value', item.value); 161 const response = await fetch(fetchUrl.toString()); 162 const responseData = await response.json(); 163 164 await MultiSelect.setStore(item.value); 165 await MultiSelect.getStore(); 166 167 // Ãprava hodnot v multiselectech 168 await multiSelectRegion.preloadInitialItems([responseData.region_code]); 169 await multiSelectCity.preloadInitialItems([responseData.id_region_city]); 170 await MultiSelect.setRegionCity(); 171 172 this.resetValue(); 173 174 if (typeof this.onSelect === 'function') { 175 this.onSelect(responseData.url); 176 } 177 } 178 179}
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.