1import { i as isDev } from './env-M5RuEq8k.js'; 2import { S as SUGGESTIONS_OPEN, s as setComboboxExpanded, a as setFocusTrap, b as SUGGESTIONS, r as removeFocusTrap } from './_a11y-BbTmbSnv.js'; 3 4if (!isDev) { 5 import('./main.js').then(function (n) { return n.o; }); 6} 7// Scoped to the search field, not to the document: a page can hold several 8// search fields (header + Searchresult), and a global lookup handed the header's 9// list to the page search â including its aria state. 10const findSuggestions = (wrapper) => wrapper.querySelector(SUGGESTIONS); 11// Only the header search opens as an overlay above the page, so only there may 12// the Tab key be locked inside the search (see setFocusTrap). 13const isHeaderSearch = (wrapper) => Boolean(wrapper.closest(".Header")); 14// Keyboard navigation is wired up once per search field: fetchSuggestions runs on 15// every keystroke, and registering the listeners there stacked one handler per 16// request â a single ArrowDown then jumped several entries at once. 17const keyboardWired = new WeakSet(); 18let currentFocusIndex = -1; 19const createElementSuggestions = (ariaLabel) => { 20 const element = document.createElement("ul"); 21 element.classList.add("SearchSuggestions"); 22 element.classList.add("-primary"); 23 element.setAttribute("id", "resultlist"); 24 element.setAttribute("role", "listbox"); 25 element.setAttribute("aria-label", ariaLabel); 26 return element; 27}; 28const createLink = (item, index, setsize) => { 29 return ` 30 <a class="Link Link-arrow Link-integrated -size-m" 31 href="${item.resourceLocation}" 32 role="option" 33 aria-setsize="${setsize}" 34 aria-posinset="${index}"> 35 <span class="Link_label">${item.name}</span> 36 </a> 37 `; 38}; 39const createListItem = (item, index, setsize) => { 40 const li = document.createElement("li"); 41 li.setAttribute("role", "presentation"); 42 li.innerHTML = createLink(item, index, setsize); 43 const link = li.querySelector("a"); 44 link.setAttribute("aria-selected", "false"); 45 link.addEventListener("focusin", () => link.setAttribute("aria-selected", "true")); 46 link.addEventListener("focusout", () => link.setAttribute("aria-selected", "false")); 47 return li; 48}; 49// Suggestions closed: hide the list, reset the combobox state on the field and 50// release the focus/dismiss handlers. 51const hideSuggestions = (wrapper, input) => { 52 const suggestionsContainer = findSuggestions(wrapper); 53 const wasOpen = wrapper.classList.contains(SUGGESTIONS_OPEN); 54 if (suggestionsContainer) { 55 suggestionsContainer.style.display = "none"; 56 suggestionsContainer.innerHTML = ""; 57 } 58 wrapper.classList.remove(SUGGESTIONS_OPEN); 59 currentFocusIndex = -1; 60 setComboboxExpanded(input, false); 61 // The focus/dismiss handlers exist once per document, so only release them if 62 // it was this field's list that was open â otherwise typing in a second search 63 // field would strip another, still open field of its handlers. 64 if (wasOpen) 65 removeFocusTrap(); 66}; 67const fetchSuggestions = (wrapper, input, autocompleteUrl) => { 68 var _a; 69 const dataAriaLabel = (_a = input.dataset.autocompleteAriaLabel) !== null && _a !== void 0 ? _a : "Suggestions"; 70 fetch(autocompleteUrl) 71 .then((response) => response.json()) 72 .then((data) => { 73 // Handle empty results early 74 if (!data || data.length === 0) { 75 hideSuggestions(wrapper, input); 76 return; // stop here, nothing to render 77 } 78 // Create or reuse suggestion container 79 let suggestionsContainer = findSuggestions(wrapper); 80 if (!suggestionsContainer) { 81 suggestionsContainer = createElementSuggestions(dataAriaLabel); 82 input.insertAdjacentElement("afterend", suggestionsContainer); 83 } 84 // Clear previous results 85 suggestionsContainer.innerHTML = ""; 86 const limitedData = data.slice(0, 4); 87 const lastItem = data[data.length - 1]; 88 const hasExtraLastItem = Boolean(lastItem && !limitedData.some((item) => item.name === lastItem.name)); 89 // aria-setsize has to match what is actually rendered (max. 4 + the last 90 // item), otherwise a screen reader announces "1 of 5" on a list of two. 91 const setsize = limitedData.length + (hasExtraLastItem ? 1 : 0); 92 limitedData.forEach((item, index) => { 93 const li = createListItem(item, index + 1, setsize); 94 suggestionsContainer.appendChild(li); 95 }); 96 if (hasExtraLastItem) { 97 const li = createListItem(lastItem, limitedData.length + 1, setsize); 98 suggestionsContainer.appendChild(li); 99 } 100 suggestionsContainer.style.display = "block"; 101 wrapper.classList.add(SUGGESTIONS_OPEN); 102 currentFocusIndex = -1; 103 setComboboxExpanded(input, true); 104 setupKeyboardNavigation(wrapper, input);
105 // Wired up only once the list exists, so the trap knows the suggestion 106 // links as focusable elements. Outside the header it stays a plain 107 // dismiss handler (Escape / focus leaves the search), no Tab lock. 108 setFocusTrap(wrapper, isHeaderSearch(wrapper)); 109 }) 110 .catch((err) => console.error("Autocomplete error:", err)); 111}; 112const setupKeyboardNavigation = (wrapper, searchField) => { 113 if (keyboardWired.has(wrapper)) 114 return; 115 keyboardWired.add(wrapper); 116 // The list is created/emptied on demand, so read it per key press instead of 117 // capturing it here. 118 const getItems = () => { var _a, _b; return Array.from((_b = (_a = findSuggestions(wrapper)) === null || _a === void 0 ? void 0 : _a.querySelectorAll("a")) !== null && _b !== void 0 ? _b : []); }; 119 const moveFocus = (direction) => { 120 var _a; 121 const items = getItems(); 122 const total = items.length; 123 if (total === 0) 124 return; 125 if (direction === "down") { 126 currentFocusIndex = (currentFocusIndex + 1) % (total + 1); 127 } 128 else { 129 currentFocusIndex = (currentFocusIndex - 1 + (total + 1)) % (total + 1); 130 } 131 if (currentFocusIndex === 0) { 132 searchField.focus(); 133 } 134 else { 135 (_a = items[currentFocusIndex - 1]) === null || _a === void 0 ? void 0 : _a.focus(); 136 } 137 }; 138 // One listener for the field and the suggestions list (both live inside the 139 // wrapper), so it survives the list being re-created. 140 wrapper.addEventListener("keydown", (e) => { 141 const inList = Boolean(e.target.closest(SUGGESTIONS)); 142 // Only the field and the list navigate â the reset/submit buttons keep their 143 // default key behaviour. 144 if (e.target !== searchField && !inList) 145 return; 146 if (e.key === "ArrowDown" || e.key === "ArrowUp") { 147 // Nothing to navigate (no list open): leave the arrow keys to the browser 148 // instead of swallowing them. 149 if (getItems().length === 0) 150 return; 151 e.preventDefault(); 152 if (currentFocusIndex === -1) 153 currentFocusIndex = 0; 154 moveFocus(e.key === "ArrowDown" ? "down" : "up"); 155 return; 156 } 157 // Escape inside the list returns the focus to the field; closing the list is 158 // handled by the dismiss handler in _a11y. In the field itself Escape stays 159 // untouched (native "clear search field"). 160 if (e.key === "Escape" && inList) { 161 e.preventDefault(); 162 searchField.focus(); 163 currentFocusIndex = -1; 164 } 165 }); 166}; 167let debounceTimer; 168const autocomplete = (wrapper) => { 169 clearTimeout(debounceTimer); 170 const input = wrapper.querySelector("input"); 171 if (!input) 172 return; 173 debounceTimer = window.setTimeout(() => { 174 const query = input.value.trim(); 175 // Hide suggestions when query too short or empty 176 if (query.length < 3) { 177 hideSuggestions(wrapper, input); 178 return; // stop here 179 } 180 if (input.dataset.autocompleteUrl) { 181 // Encoded, otherwise a term containing "&", "+" or "#" would break the 182 // query string ("Junkers & Bosch" arrived at the backend as "Junkers"). 183 const autocompleteUrl = `${input.dataset.autocompleteUrl}?term=${encodeURIComponent(query)}`; 184 fetchSuggestions(wrapper, input, autocompleteUrl); 185 } 186 }, 250); 187}; 188 189if (!isDev) { 190 import('./main.js').then(function (n) { return n.p; }); 191} 192const valueModificator = () => { 193 const elements = document.querySelectorAll(".Textfield-valueModificator"); 194 elements.forEach((element) => { 195 const input = element.querySelector(".Input-number"); 196 const stepUpBtn = element.querySelector(".-step-up"); 197 const stepDownBtn = element.querySelector(".-step-down"); 198 stepUpBtn === null || stepUpBtn === void 0 ? void 0 : stepUpBtn.addEventListener("click", (event) => { 199 event.preventDefault(); 200 input.stepUp(); 201 }); 202 stepDownBtn === null || stepDownBtn === void 0 ? void 0 : stepDownBtn.addEventListener("click", (event) => { 203 event.preventDefault(); 204 input.stepDown(); 205 }); 206 }); 207}; 208if (document.querySelector(".Textfield-valueModificator")) { 209 valueModificator(); 210} 211const searchbox = document.querySelectorAll(".SearchBox"); 212const textfieldSearch = document.querySelectorAll(".Textfield-search"); 213const toggleResetBtn = (input, resetBtn) => { 214 if (input.value.length > 0) { 215 resetBtn.style.display = ""; 216 } 217 else { 218 resetBtn.style.display = "none"; 219 } 220};
221[...searchbox, ...textfieldSearch].forEach((element) => { 222 const box = element; 223 const resetBtn = box.querySelector(".FormButton[type='reset']"); 224 const input = box.querySelector("input"); 225 if (!resetBtn || !input) 226 return; 227 // Initially hide reset button for non-header search boxes 228 const isHeaderSearch = box.closest(".Header"); 229 if (!isHeaderSearch) { 230 resetBtn.style.display = "none"; 231 } 232 input === null || input === void 0 ? void 0 : input.addEventListener("input", (e) => { 233 const current = e.currentTarget; 234 // Toggle reset button visibility for non-header search boxes 235 if (!isHeaderSearch) { 236 toggleResetBtn(input, resetBtn); 237 } 238 if (current.classList.contains("Input-autocomplete")) { 239 autocomplete(box); 240 } 241 }); 242 resetBtn === null || resetBtn === void 0 ? void 0 : resetBtn.addEventListener("click", () => { 243 var _a; 244 input.value = ""; 245 // Hide reset button for non-header search boxes 246 if (!isHeaderSearch) { 247 resetBtn.style.display = "none"; 248 } 249 // Only keep focus for header search, blur for other search boxes 250 if (isHeaderSearch) { 251 input.focus(); 252 } 253 else { 254 input.blur(); 255 } 256 // The combobox state has to be reset even when no list was open, otherwise 257 // the field can stay at aria-expanded="true" after clearing it. 258 const wasOpen = box.classList.contains(SUGGESTIONS_OPEN); 259 (_a = box.querySelector(SUGGESTIONS)) === null || _a === void 0 ? void 0 : _a.remove(); 260 box.classList.remove(SUGGESTIONS_OPEN); 261 setComboboxExpanded(input, false); 262 // The dismiss handlers exist once per document â only release them if this 263 // field's list was the open one. 264 if (wasOpen) 265 removeFocusTrap(); 266 }); 267}); 268//# sourceMappingURL=Textfield-Tc3EtuZQ.js.map
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.