PageSourceSearch

https://www.donpealo.cz/src/components-NEW/input-autocomplete/input-autocomplete.js?1790968757

js donpealo.cz collected 2026-10-02 19:20:41 UTC 6,095 bytes, 179 lines download raw bytes

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.