1var __defProp = Object.defineProperty; 2var __name = (target, value) => __defProp(target, "name", { value, configurable: !0 }); 3import { u as uuidv4 } from "../chunks/chunk.dcf-utility.js?v=6.1.6"; 4class DCFSearchSelect { 5 static { 6 __name(this, "DCFSearchSelect"); 7 } 8 uuid = uuidv4(); 9 multiple = !1; 10 selectID = null; 11 selectElement = null; 12 parsedSelect = []; 13 searchAndSelectID = null; 14 searchAndSelectElement = null; 15 inputID = null; 16 inputElement = null; 17 openButtonElement = null; 18 searchAreaElement = null; 19 availableItemsListID = null; 20 availableItemsListElement = null; 21 listOfAvailableItems = []; 22 selectedItemsListID = null; 23 selectedItemsListElement = null; 24 selectedItemsHelpID = null; 25 currentFocus = null; 26 searchAndSelectClassList = [ 27 "dcf-relative" 28 ]; 29 searchAreaClassList = [ 30 "dcf-relative", 31 "dcf-d-grid", 32 "dcf-overflow-x-hidden", 33 "dcf-overflow-y-auto" 34 ]; 35 selectedItemsListClassList = [ 36 "dcf-d-flex", 37 "dcf-flex-wrap", 38 "dcf-m-0" 39 ]; 40 selectedItemClassList = [ 41 "dcf-d-flex", 42 "dcf-flex-nowrap", 43 "dcf-ai-center", 44 "dcf-jc-center", 45 "dcf-mb-0", 46 "dcf-relative" 47 ]; 48 selectedItemButtonClassList = [ 49 "dcf-btn", 50 "dcf-btn-secondary", 51 "dcf-h-100%", 52 "dcf-z-1" 53 ]; 54 selectedItemButtonSVG = `<svg 55 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26" class="dcf-fill-current dcf-w-4 dcf-h-4"> 56 <path d="M25.4,22.6L15.8,13l9.6-9.6C25.8,3,26,2.5,26,2s-0.2-1-0.6-1.4c-0.8-0.8-2.1-0.8-2.8,0L13,10.2L3.4,0.6 57 c-0.8-0.8-2-0.8-2.8,0c-0.8,0.8-0.8,2,0,2.8l9.6,9.6l-9.6,9.6C0.2,23,0,23.5,0,24c0,0.5,0.2,1,0.6,1.4C1,25.8,1.5,26,2,26 58 c0.5,0,1-0.2,1.4-0.6l9.6-9.6l9.6,9.6C23,25.8,23.5,26,24,26c0,0,0,0,0,0c0.5,0,1-0.2,1.4-0.6C25.8,25,26,24.5,26,24 59 C26,23.5,25.8,23,25.4,22.6z"/> 60</svg>`; 61 selectedItemLabelClassList = [ 62 "dcf-h-100%" 63 ]; 64 toggleButtonContainerClassList = [ 65 "dcf-sticky", 66 "dcf-top-50%", 67 "dcf-h-0", 68 "dcf-pl-1", 69 "dcf-pr-1" 70 ]; 71 toggleButtonClassList = [ 72 "dcf-btn", 73 "dcf-btn-tertiary", 74 "dcf-p-3" 75 ]; 76 toggleButtonSVG = `<svg 77xmlns="http://www.w3.org/2000/svg" 78viewBox="0 0 24 24" 79class="dcf-fill-current dcf-w-3 dcf-h-3" 80aria-hidden="true" 81> 82<path d="M23.936,2.255C23.848,2.098,23.681,2,23.5,2h-23 83 C0.32,2,0.153,2.098,0.065,2.255c-0.089,0.157-0.085,0.35,0.008,0.504 84 l11.5,19C11.663,21.908,11.826,22,12,22s0.337-0.092,0.428-0.241 85 l11.5-19C24.021,2.605,24.025,2.412,23.936,2.255z"></path> 86</svg>`; 87 inputSingleClassList = [ 88 "dcf-b-0" 89 ]; 90 inputGroupMultiple = [ 91 "dcf-d-flex", 92 "dcf-flex-wrap" 93 ]; 94 inputMultipleClassList = [ 95 "dcf-b-0", 96 "dcf-flex-grow-1", 97 "dcf-flex-shrink-0" 98 ]; 99 availableItemsListClassList = [ 100 "dcf-absolute", 101 "dcf-d-none", 102 "dcf-mb-0", 103 "dcf-pl-0", 104 "dcf-w-100%", 105 "dcf-z-2" 106 ]; 107 availableItemsGroupClassList = [ 108 "dcf-m-0", 109 "dcf-pl-0" 110 ]; 111 availableItemsGroupLabelClassList = [ 112 "dcf-mb-0", 113 "dcf-bold" 114 ]; 115 availableItemClassList = [ 116 "dcf-relative", 117 "dcf-d-flex", 118 "dcf-flex-row", 119 "dcf-flex-nowrap", 120 "dcf-jc-between", 121 "dcf-ai-center", 122 "dcf-mb-0" 123 ]; 124 availableItemIndicatorSVG = `<svg 125 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" class="dcf-fill-current dcf-h-4 dcf-w-4"> 126 <path d="M31,0.4C30.6,0.1,30-0.1,29.4,0c-0.6,0.1-1.1,0.4-1.5,0.9 127 L10.1,26.3L3.8,20c-0.4-0.4-1-0.6-1.6-0.6c0,0,0,0,0,0 128 c-0.6,0-1.2,0.2-1.6,0.6C0.2,20.5,0,21,0,21.6 129 c0,0.6,0.2,1.2,0.7,1.6l8.1,8.1c0.4,0.4,1,0.7,1.6,0.7 130 c0.7,0,1.4-0.4,1.8-1L31.6,3.5 131 C32.3,2.5,32.1,1.1,31,0.4z"/> 132</svg>`; 133 availableItemsNoResultsClassList = [ 134 "dcf-mb-0", 135 "dcf-bold" 136 ]; 137 /** 138 * Constructor for the multi-select combobox 139 * @param { HTMLSelectElement } selectElement The select that will be used to build the component 140 */ 141 constructor(selectElement, options = {}) { 142 "searchAndSelectClassList" in options && Array.isArray(options.searchAndSelectClassList) && (this.searchAndSelectClassList = options.searchAndSelectClassList), "searchAreaClassList" in options && Array.isArray(options.searchAreaClassList) && (this.searchAreaClassList = options.searchAreaClassList), "selectedItemsListClassList" in options && Array.isArray(options.selectedItemsListClassList) && (this.selectedItemsListClassList = options.selectedItemsListClassList), "selectedItemClassList" in options && Array.isArray(options.selectedItemClassList) && (this.selectedItemClassList = options.selectedItemClassList), "selectedItemButtonClassList" in options && Array.isArray(options.selectedItemButtonClassList) && (this.selectedItemButtonClassList = options.selectedItemButtonClassList), "selectedItemButtonSVG" in options && typeof options.selectedItemButtonSVG == "string" && (this.selectedItemButtonSVG = options.selectedItemButtonSVG), "selectedItemLabelClassList" in options && Array.isArray(options.selectedItemLabelClassList) && (this.selectedItemLabelClassList = options.selectedItemLabelClassList), "toggleButtonContainerClassList" in options && Array.isArray(options.toggleButtonContainerClassList) && (this.toggleButtonContainerClassList = options.toggleButtonContainerClassList), "toggleButtonClassList" in options && Array.isArray(options.toggleButtonClassList) && (this.toggleButtonClassList = options.toggleButtonClassList), "toggleButtonSVG" in options && typeof options.toggleButtonSVG == "string" && (this.toggleButtonSVG = options.toggleButtonSVG), "inputSingleClassList" in options && Array.isArray(options.inputSingleClassList) && (this.inputSingleClassList = options.inputSingleClassList), "inputGroupMultiple" in options && Array.isArray(options.inputGroupMultiple) && (this.inputGroupMultiple = options.inputGroupMultiple), "inputMultipleClassList" in options && Array.isArray(options.inputMultipleClassList) && (this.inputMultipleClassList = options.inputMultipleClassList), "availableItemsListClassList" in options && Array.isArray(options.availableItemsListClassList) && (this.availableItemsListClassList = options.availableItemsListClassList), "availableItemsGroupClassList" in options && Array.isArray(options.availableItemsGroupClassList) && (this.availableItemsListClassList = options.availableItemsGroupClassList), "availableItemsGroupLabelClassList" in options && Array.isArray(options.availableItemsGroupLabelClassList) && (this.availableItemsGroupLabelClassList = options.availableItemsGroupLabelClassList), "availableItemClassList" in options && Array.isArray(options.availableItemClassList) && (this.availableItemClassList = options.availableItemClassList), "availableItemIndicatorSVG" in options && typeof options.availableItemIndicatorSVG == "string" && (this.availableItemIndicatorSVG = options.availableItemIndicatorSVG), "availableItemsNoResultsClassList" in options && Array.isArray(options.availableItemsNoResultsClassList) && (this.availableItemsNoResultsClassList = options.availableItemsNoResultsClassList), this.selectElement = selectElement, (this.selectElement.getAttribute("id") === "" || this.selectElement.getAttribute("id") === null) && this.selectElement.setAttribute("id", this.uuid.concat("-search-and-select-select")), this.selectElement.getAttribute("multiple") !== null && (this.multiple = !0), this.selectID = this.selectElement.getAttribute("id"), this.searchAndSelectID = this.uuid.concat("-search-and-select"), this.inputID = this.uuid.concat("-search-and-select-input"), this.availableItemsListID = this.uuid.concat("-search-and-select-available-items-list"), this.searchAndSelectElement = document.createElement("div"), this.searchAndSelectElement.innerHTML = `<div 143class="dcf-search-and-select-search-area-single ${this.searchAreaClassList.join(" ")}" > 144 <input 145 class="${this.inputSingleClassList.join(" ")}" 146 type="text" 147 role="combobox" 148 id=${this.inputID} 149 aria-haspopup="listbox"
150 aria-autocomplete="list" 151 aria-expanded="false" 152 aria-controls="${this.availableItemsListID}" 153 > 154 <div class="dcf-search-and-select-open-btn ${this.toggleButtonContainerClassList.join(" ")}"> 155 <button 156 class="${this.toggleButtonClassList.join(" ")}" 157 type="button" 158 tabindex="-1" 159 aria-label="Toggle Selectable List" 160 aria-expanded="false" 161 aria-controls="${this.availableItemsListID}" 162 > 163 ${this.toggleButtonSVG} 164 </button> 165 </div> 166</div>`, this.searchAndSelectElement.classList.add("dcf-search-and-select", ...this.searchAndSelectClassList), this.searchAndSelectElement.setAttribute("id", this.searchAndSelectID), this.searchAndSelectElement.setAttribute("hidden", "hidden"), this.searchAndSelectElement.dataset.for = this.selectID, this.inputElement = this.searchAndSelectElement.querySelector("input"), this.openButtonElement = this.searchAndSelectElement.querySelector(".dcf-search-and-select-open-btn button"), this.searchAreaElement = this.searchAndSelectElement.querySelector(".dcf-search-and-select-search-area-single"), this.multiple && (this.selectedItemsListID = this.uuid.concat("-search-and-select-selected-items-list"), this.selectedItemsHelpID = this.uuid.concat("-search-and-select-selected-items-list-help"), this.searchAndSelectElement.innerHTML = `<div 167class="dcf-search-and-select-search-area-multiple ${this.searchAreaClassList.join(" ")}" > 168 <div class="dcf-search-and-select-multiple-input-group ${this.inputGroupMultiple.join(" ")}"> 169 <span id="${this.selectedItemsHelpID}" class="dcf-sr-only">Press Delete or Backspace to Remove.</span> 170 <ul 171 class="dcf-search-and-select-selected-items ${this.selectedItemsListClassList.join(" ")}" 172 role="list" 173 aria-describedby="${this.selectedItemsHelpID}" 174 id=${this.selectedItemsListID} 175 aria-label="Selected Items" 176 ></ul> 177 <input 178 class="${this.inputMultipleClassList.join(" ")}" 179 type="text" 180 role="combobox" 181 id=${this.inputID} 182 aria-haspopup="listbox" 183 aria-autocomplete="list" 184 aria-expanded="false" 185 aria-controls="${this.availableItemsListID}" 186 > 187 </div> 188 <div class="dcf-search-and-select-open-btn ${this.toggleButtonContainerClassList.join(" ")}"> 189 <button 190 class="${this.toggleButtonClassList.join(" ")}" 191 type="button" 192 tabindex="-1" 193 aria-label="Toggle Selectable List" 194 aria-expanded="false" 195 aria-controls="${this.availableItemsListID}" 196 > 197 ${this.toggleButtonSVG} 198 </button> 199 </div> 200</div>`, this.inputElement = this.searchAndSelectElement.querySelector("input"), this.openButtonElement = this.searchAndSelectElement.querySelector(".dcf-search-and-select-open-btn button"), this.selectedItemsListElement = this.searchAndSelectElement.querySelector(".dcf-search-and-select-selected-items"), this.searchAreaElement = this.searchAndSelectElement.querySelector(".dcf-search-and-select-search-area-multiple")), this.parsedSelect = this.#parseSelect(), this.selectElement.after(this.searchAndSelectElement), this.availableItemsListElement = this.#buildAvailableItems(), this.availableItemsListElement.setAttribute("id", this.availableItemsListID), this.availableItemsListElement.classList.add( 201 "dcf-search-and-select-available-items", 202 ...this.availableItemsListClassList 203 ), this.searchAndSelectElement.append(this.availableItemsListElement), this.#fixSelectReferences(), this.#transferSelectAttributes(), this.#setDisabled(this.selectElement.getAttribute("disabled") !== null), this.#setReadOnly(this.selectElement.getAttribute("readonly") !== null), this.#addEventListeners(), this.searchAndSelectElement.removeAttribute("hidden"), this.multiple && this.availableItemsListElement.setAttribute("aria-multiselectable", !0), this.selectElement.classList.add("dcf-search-select-initialized"), this.selectElement.dispatchEvent(new CustomEvent(DCFSearchSelect.events("searchSelectReady"), { 204 detail: { 205 classInstance: this 206 } 207 })); 208 } 209 /** 210 * Validates and returns standardized name of events for tabs 211 * @static 212 * @param { string } name - Name of the event to be returned 213 * @returns { string } Standard name of the event 214 */ 215 static events(name) { 216 const events = { 217 searchSelectReady: "searchSelectReady" 218 }; 219 return Object.freeze(events), name in events ? events[name] : void 0; 220 } 221 /** 222 * Fix any references to the hidden select 223 */ 224 #fixSelectReferences() { 225 [ 226 "for", 227 "aria-controls", 228 "aria-describedby" 229 ].forEach((singleAttr) => { 230 document.querySelectorAll(`[${singleAttr}="${this.selectID}"]`).forEach((elementToFix) => { 231 elementToFix.setAttribute(singleAttr, this.inputID); 232 }); 233 }); 234 } 235 /** 236 * If the select element has attributes set it will transfer them to the input 237 */ 238 #transferSelectAttributes() { 239 [ 240 "aria-describedby", 241 "aria-label", 242 "aria-labelledby", 243 "aria-owns" 244 ].forEach((singleAttr) => { 245 this.selectElement.getAttribute(singleAttr) !== null && this.inputElement.setAttribute(singleAttr, this.selectElement.getAttribute(singleAttr)); 246 }); 247 } 248 /** 249 * Recursively parses the select/optgroups 250 * @returns { Array<ParsedOptgroup> } 251 */ 252 #parseSelect() { 253 const returnedData = this.#parseSelectInner(this.selectElement), optgroups = returnedData.optgroups; 254 return returnedData.options.length > 0 && optgroups.push({ 255 tag: "optgroup", 256 label: "Other", 257 element: this.selectElement, 258 items: returnedData.options 259 }), optgroups; 260 } 261 /** 262 * Recursively parses the select/optgroups 263 * @param { HTMLSelectElement|HTMLOptGroupElement } selectOrOptgroup The select or optgroup to parse 264 * @returns { parseSelectInner } 265 */ 266 #parseSelectInner(selectOrOptgroup) { 267 const optgroups = [], options = []; 268 for (const optgroupOrOption of selectOrOptgroup.children) 269 if (optgroupOrOption.tagName === "OPTGROUP") { 270 const returnedData = this.#parseSelectInner(optgroupOrOption), returnedOptgroups = returnedData.optgroups; 271 optgroups.push(...returnedOptgroups), optgroups.push({ 272 tag: "optgroup", 273 label: optgroupOrOption.getAttribute("label"), 274 element: optgroupOrOption, 275 items: returnedData.options 276 }); 277 } else optgroupOrOption.tagName === "OPTION" && options.push({ 278 tag: "option", 279 label: optgroupOrOption.innerHTML, 280 value: optgroupOrOption.getAttribute("value"), 281 selected: optgroupOrOption.getAttribute("selected") !== null, 282 disabled: optgroupOrOption.getAttribute("disabled") !== null, 283 elemId: uuidv4().concat("-search-and-select-option"), 284 element: optgroupOrOption 285 }); 286 return { optgroups, options }; 287 } 288 /** 289 * Builds the available items UL from output of parse select 290 * @returns { HTMLUListElement } 291 */ 292 #buildAvailableItems() { 293 return this.multiple ? this.#buildAvailableItemsMultiple() : this.#buildAvailableItemsSingle(); 294 } 295 /** 296 * Builds the available items UL from output of parse select 297 * SINGLE 298 * @returns { HTMLUListElement } 299 */ 300 #buildAvailableItemsSingle() { 301 const availableItems = document.createElement("ul"); 302 availableItems.setAttribute("role", "listbox"); 303 let lastSelectedOption = null; 304 return this.parsedSelect.forEach((singleOptgroup) => { 305 const groupedItems = document.createElement("ul"); 306 groupedItems.setAttribute("role", "group"), groupedItems.classList.add("dcf-search-and-select-available-items-group", ...this.availableItemsGroupClassList), this.parsedSelect.length !== 1 && (groupedItems.innerHTML = `${groupedItems.innerHTML} 307<li 308class="${this.availableItemsGroupLabelClassList.join(" ")}" 309role="presentation" 310> 311${singleOptgroup.label} 312</li>
312`), singleOptgroup.items.forEach((singleItem) => { 313 const newItem = document.createElement("li"); 314 newItem.classList.add( 315 "dcf-search-and-select-available-item", 316 "dcf-search-and-select-clickable", 317 ...this.availableItemClassList 318 ), newItem.setAttribute("role", "option"), newItem.setAttribute("aria-selected", "false"), newItem.setAttribute("aria-disabled", singleItem.disabled), newItem.setAttribute("id", `${singleItem.elemId}-available`), newItem.dataset.value = singleItem.value, newItem.dataset.label = singleItem.label, newItem.dataset.elemId = singleItem.elemId, newItem.innerHTML = `<span 319 class="dcf-search-and-select-available-item-label"> 320 ${singleItem.label} 321</span> 322<span class="dcf-search-and-select-available-item-indicator" aria-hidden="true"> 323 ${this.availableItemIndicatorSVG} 324</span>`, groupedItems.append(newItem), singleItem.disabled === !1 && singleItem.selected === !0 && (lastSelectedOption = newItem); 325 }), availableItems.append(groupedItems); 326 }), lastSelectedOption !== null && (lastSelectedOption.setAttribute("aria-selected", "true"), this.inputElement.value = lastSelectedOption.dataset.label), availableItems; 327 } 328 /** 329 * Builds the available items UL from output of parse select 330 * MULTIPLE 331 * @returns { HTMLUListElement } 332 */ 333 #buildAvailableItemsMultiple() { 334 const availableItems = document.createElement("ul"); 335 return availableItems.setAttribute("role", "listbox"), this.parsedSelect.forEach((singleOptgroup) => { 336 const groupedItems = document.createElement("ul"); 337 groupedItems.setAttribute("role", "group"), groupedItems.classList.add("dcf-search-and-select-available-items-group", ...this.availableItemsGroupClassList), this.parsedSelect.length !== 1 && (groupedItems.innerHTML = `${groupedItems.innerHTML} 338<li 339class="${this.availableItemsGroupLabelClassList.join(" ")}" 340role="presentation" 341> 342${singleOptgroup.label} 343</li> 344 `), singleOptgroup.items.forEach((singleItem) => { 345 const newItem = document.createElement("li"); 346 newItem.classList.add( 347 "dcf-search-and-select-available-item", 348 "dcf-search-and-select-clickable", 349 ...this.availableItemClassList 350 ), newItem.setAttribute("role", "option"), newItem.setAttribute("aria-selected", singleItem.disabled ? "false" : singleItem.selected), newItem.setAttribute("aria-disabled", singleItem.disabled), newItem.setAttribute("id", `${singleItem.elemId}-available`), newItem.dataset.value = singleItem.value, newItem.dataset.elemId = singleItem.elemId, newItem.innerHTML = `<span 351 class="dcf-search-and-select-available-item-label"> 352 ${singleItem.label} 353</span> 354<span class="dcf-search-and-select-available-item-indicator" aria-hidden="true"> 355 ${this.availableItemIndicatorSVG} 356</span>`, groupedItems.append(newItem), singleItem.disabled === !1 && singleItem.selected === !0 && this.#appendNewSelectedItem(newItem); 357 }), availableItems.append(groupedItems); 358 }), availableItems; 359 } 360 /** 361 * We will build a new selected item element and append it to the list of selected items 362 * @param { HTMLLIElement } singleAvailableItem The li from the available items list 363 */ 364 #appendNewSelectedItem(singleAvailableItem) { 365 if (!this.#isElementAnAvailableItem(singleAvailableItem)) 366 throw new Error("Element is not an available item"); 367 const selectedItemLabel = singleAvailableItem.querySelector(".dcf-search-and-select-available-item-label").innerText, newSelectedItem = document.createElement("li"); 368 newSelectedItem.dataset.elemId = singleAvailableItem.dataset.elemId, newSelectedItem.setAttribute("id", `${singleAvailableItem.dataset.elemId}-selected`), newSelectedItem.classList.add( 369 "dcf-search-and-select-selected-item", 370 "dcf-search-and-select-clickable", 371 ...this.selectedItemClassList 372 ), newSelectedItem.innerHTML = `<button 373 class=" 374 dcf-search-and-select-selected-item-remove-btn 375 ${this.selectedItemButtonClassList.join(" ")} 376 " 377 aria-label="Remove ${selectedItemLabel}" 378 type="button" 379> 380 ${this.selectedItemButtonSVG} 381</button> 382<span class=" 383 dcf-search-and-select-selected-item-label 384 ${this.selectedItemLabelClassList.join(" ")}"> 385 ${selectedItemLabel} 386</span>`, this.selectedItemsListElement.append(newSelectedItem), this.#isSelectedItemInView(newSelectedItem) || newSelectedItem.scrollIntoView({ behavior: "smooth", block: "nearest" }); 387 } 388 /** 389 * Add event listeners for the different parts of the search and select element 390 */ 391 #addEventListeners() { 392 return this.multiple ? this.#addEventListenersMultiple() : this.#addEventListenersSingle(); 393 } 394 #addEventListenersSingle() { 395 document.body.addEventListener("pointerup", (event) => { 396 this.searchAndSelectElement.contains(event.target) || (this.closeAvailableItems(), this.#setVisualFocusOn(!1)); 397 }, !0), this.searchAreaElement.addEventListener("click", () => { 398 this.isComponentDisabled() || (this.#setVisualFocusOn(this.searchAreaElement), this.inputElement.focus(), this.isComponentReadOnly() || this.isAvailableItemsOpen() ? this.closeAvailableItems() : (this.#filterAvailableItems(), this.openAvailableItems())
398); 399 }), this.inputElement.addEventListener("focus", () => { 400 this.#setVisualFocusOn(this.searchAreaElement); 401 }), this.inputElement.addEventListener("keydown", (event) => { 402 if (this.isComponentReadOnly()) 403 return; 404 const altKey = event.altKey; 405 let preventDefault = !1; 406 const length = this.inputElement.value.length; 407 switch (event.code) { 408 case "Enter": 409 if (this.#visualFocusOnAvailableItems()) { 410 const currentItemElement = this.getAvailableItemActiveDescendant(); 411 currentItemElement !== !1 && (this.#isAvailableItemDisabled(currentItemElement) || (this.#isAvailableItemSelected(currentItemElement) ? this.#removeAvailableItem(currentItemElement) : this.#selectAvailableItem(currentItemElement))); 412 } else 413 this.isAvailableItemsOpen() || (this.#filterAvailableItems(), this.openAvailableItems()), this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(this.getFirstAvailableItem()), this.#scrollActiveAvailableItemInView(); 414 preventDefault = !0; 415 break; 416 case "Down": 417 case "ArrowDown": 418 altKey ? this.isAvailableItemsOpen() || (this.#filterAvailableItems(), this.openAvailableItems()) : (this.isAvailableItemsOpen() || (this.#filterAvailableItems(), this.openAvailableItems()), this.#visualFocusOnAvailableItems() ? (this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(this.getNextAvailableItem()), this.#scrollActiveAvailableItemInView()) : (this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(this.getFirstAvailableItem()), this.#scrollActiveAvailableItemInView())), preventDefault = !0; 419 break; 420 case "Esc": 421 case "Escape": 422 this.isAvailableItemsOpen() && (this.closeAvailableItems(!0), this.#setVisualFocusOn(this.searchAreaElement)), preventDefault = !0; 423 break; 424 case "Up": 425 case "ArrowUp": 426 altKey ? this.isAvailableItemsOpen() || (this.#filterAvailableItems(), this.openAvailableItems()) : (this.isAvailableItemsOpen() || (this.#filterAvailableItems(), this.openAvailableItems()), this.#visualFocusOnAvailableItems() ? (this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(this.getPreviousAvailableItem()), this.#scrollActiveAvailableItemInView()) : (this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(this.getLastAvailableItem()), this.#scrollActiveAvailableItemInView())), preventDefault = !0; 427 break; 428 case "Tab": 429 this.closeAvailableItems(!0), this.#setVisualFocusOn(!1); 430 break; 431 case "Home": 432 this.inputElement.setSelectionRange(0, 0), preventDefault = !0; 433 break; 434 case "End": 435 this.inputElement.setSelectionRange(length, length), preventDefault = !0; 436 break; 437 } 438 preventDefault && (event.stopPropagation(), event.preventDefault()); 439 }), this.inputElement.addEventListener("keyup", (event) => { 440 if (this.isComponentReadOnly()) 441 return; 442 const char = event.key; 443 let preventDefault = !1; 444 if (!(event.key === "Escape" || event.key === "Esc")) { 445 switch (event.code) { 446 case "Backspace": 447 this.#setVisualFocusOn(this.searchAreaElement), this.#filterAvailableItems(), this.#setAvailableItemActiveDescendant(!1), preventDefault = !0; 448 break; 449 case "Left": 450 case "ArrowLeft": 451 case "Right": 452 case "ArrowRight": 453 case "Home": 454 case "End": 455 this.#setVisualFocusOn(this.searchAreaElement), preventDefault = !0; 456 break; 457 default: 458 this.#isPrintableCharacter(char) && (this.#setVisualFocusOn(this.searchAreaElement), this.#filterAvailableItems(), this.openAvailableItems(), this.#setAvailableItemActiveDescendant(!1)); 459 break; 460 } 461 preventDefault && (event.stopPropagation(), event.preventDefault()); 462 } 463 }), this.availableItemsListElement.addEventListener("click", (event) => { 464 const closestItem = event.target.closest(".dcf-search-and-select-available-item"); 465 closestItem !== null && (this.#isAvailableItemDisabled(closestItem) || (this.#isAvailableItemSelected(closestItem) ? this.#removeAvailableItem(closestItem) : this.#selectAvailableItem(closestItem)), this.inputElement.focus(), this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(closestItem)); 466 }), this.availableItemsListElement.addEventListener("pointermove", (event) => { 467 const currentItem = this.getAvailableItemActiveDescendant(), closestItem = event.target.closest(".dcf-search-and-select-available-item"); 468 closestItem !== null && (currentItem === !1 || !closestItem.isSameNode(currentItem)) && this.#setAvailableItemActiveDescendant(closestItem); 469 }), this.availableItemsListElement.addEventListener("pointerout", (event) => { 470 this.availableItemsListElement.isSameNode(event.target) && this.#setAvailableItemActiveDescendant(!1); 471 }); 472 } 473 #addEventListenersMultiple() { 474 document.body.addEventListener("pointerup", (event) => { 475 this.searchAndSelectElement.contains(event.target) || (this.closeAvailableItems(), this.#setVisualFocusOn(!1)); 476 }, !0), this.searchAreaElement.addEventListener("click", () => { 477 this.isComponentDisabled() || (this.#setVisualFocusOn(this.searchAreaElement), this.inputElement.focus(), this.isAvailableItemsOpen() || this.isComponentReadOnly() ? this.closeAvailableItems() : (this.#filterAvailableItems(), this.openAvailableItems())
477); 478 }), this.inputElement.addEventListener("focus", () => { 479 this.#setVisualFocusOn(this.searchAreaElement); 480 }), this.inputElement.addEventListener("keydown", (event) => { 481 if (this.isComponentReadOnly()) 482 return; 483 const altKey = event.altKey, shiftKey = event.shiftKey; 484 let preventDefault = !1; 485 const length = this.inputElement.value.length; 486 switch (event.code) { 487 case "Enter": 488 if (this.#visualFocusOnAvailableItems()) { 489 const currentItemElement = this.getAvailableItemActiveDescendant(); 490 currentItemElement !== !1 && (this.#isAvailableItemDisabled(currentItemElement) || (this.#isAvailableItemSelected(currentItemElement) ? this.#removeAvailableItem(currentItemElement) : this.#selectAvailableItem(currentItemElement))); 491 } else 492 this.isAvailableItemsOpen() || (this.#filterAvailableItems(), this.openAvailableItems()), this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(this.getFirstAvailableItem()), this.#scrollActiveAvailableItemInView(); 493 preventDefault = !0; 494 break; 495 case "Down": 496 case "ArrowDown": 497 altKey ? this.isAvailableItemsOpen() || (this.#filterAvailableItems(), this.openAvailableItems()) : (this.isAvailableItemsOpen() || (this.#filterAvailableItems(), this.openAvailableItems()), this.#visualFocusOnAvailableItems() ? (this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(this.getNextAvailableItem()), this.#scrollActiveAvailableItemInView()) : (this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(this.getFirstAvailableItem()), this.#scrollActiveAvailableItemInView())), preventDefault = !0; 498 break; 499 case "Esc": 500 case "Escape": 501 this.isAvailableItemsOpen() && (this.closeAvailableItems(!0), this.#setVisualFocusOn(this.searchAreaElement)), preventDefault = !0; 502 break; 503 case "Up": 504 case "ArrowUp": 505 altKey ? this.isAvailableItemsOpen() || (this.#filterAvailableItems(), this.openAvailableItems()) : (this.isAvailableItemsOpen() || (this.#filterAvailableItems(), this.openAvailableItems()), this.#visualFocusOnAvailableItems() ? (this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(this.getPreviousAvailableItem()), this.#scrollActiveAvailableItemInView()) : (this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(this.getLastAvailableItem()), this.#scrollActiveAvailableItemInView())), preventDefault = !0; 506 break; 507 case "Tab": 508 this.closeAvailableItems(!0), shiftKey || this.#setVisualFocusOn(!1); 509 break; 510 case "Home": 511 this.inputElement.setSelectionRange(0, 0), preventDefault = !0; 512 break; 513 case "End": 514 this.inputElement.setSelectionRange(length, length), preventDefault = !0; 515 break; 516 } 517 preventDefault && (event.stopPropagation(), event.preventDefault()); 518 }), this.inputElement.addEventListener("keyup", (event) => { 519 if (this.isComponentReadOnly()) 520 return; 521 const char = event.key; 522 let preventDefault = !1; 523 if (!(event.key === "Escape" || event.key === "Esc")) { 524 switch (event.code) { 525 case "Backspace": 526 this.#setVisualFocusOn(this.searchAreaElement), this.#filterAvailableItems(), this.#setAvailableItemActiveDescendant(!1), preventDefault = !0; 527 break; 528 case "Left": 529 case "ArrowLeft": 530 case "Right": 531 case "ArrowRight": 532 case "Home": 533 case "End": 534 this.#setVisualFocusOn(this.searchAreaElement), preventDefault = !0; 535 break; 536 default: 537 this.#isPrintableCharacter(char) && (this.#setVisualFocusOn(this.searchAreaElement), this.#filterAvailableItems(), this.openAvailableItems(), this.#setAvailableItemActiveDescendant(!1)); 538 break; 539 } 540 preventDefault && (event.stopPropagation(), event.preventDefault()); 541 } 542 }), this.availableItemsListElement.addEventListener("click", (event) => { 543 const closestItem = event.target.closest(".dcf-search-and-select-available-item"); 544 closestItem !== null && (this.#isAvailableItemDisabled(closestItem) || (this.#isAvailableItemSelected(closestItem) ? this.#removeAvailableItem(closestItem) : this.#selectAvailableItem(closestItem)), this.inputElement.focus(), this.#setVisualFocusOn(this.availableItemsListElement), this.#setAvailableItemActiveDescendant(closestItem)); 545 }), this.availableItemsListElement.addEventListener("pointermove", (event) => { 546 const currentItem = this.getAvailableItemActiveDescendant(), closestItem = event.target.closest(".dcf-search-and-select-available-item"); 547 closestItem !== null && (currentItem === !1 || !closestItem.isSameNode(currentItem)) && this.#setAvailableItemActiveDescendant(closestItem); 548 }), this.availableItemsListElement.addEventListener("pointerout", (event) => { 549 this.availableItemsListElement.isSameNode(event.target) && this.#setAvailableItemActiveDescendant(!1); 550 }), this.selectedItemsListElement.addEventListener("focus", () => { 551 this.#setVisualFocusOn(!1); 552 }, !0), this.selectedItemsListElement.addEventListener("click", (event) => { 553 if (this.isComponentDisabled()) 554 return; 555 const closestSelectedItem = event.target.closest(".dcf-search-and-select-selected-item"); 556 if (closestSelectedItem === null) 557 return; 558 if (event.target.closest(".dcf-search-and-select-selected-item-remove-btn") !== null && !this.isComponentReadOnly()) { 559 this.selectedItemsListElement.focus(), this.#removeSelectedItem(closestSelectedItem), this.#setAvailableItemActiveDescendant(!1), event.stopPropagation(); 560 return; 561 } 562 event.stopPropagation(); 563 }); 564 } 565 /** 566 * Will turn the component's disabled mode on or off 567 * @param { bool } boolDisabled the new disabled state 568 */ 569 #setDisabled(boolDisabled) { 570 return this.multiple ? this.#setDisabledMultiple(boolDisabled) : this.#setDisabledSingle(boolDisabled); 571 } 572 #setDisabledSingle(boolDisabled) { 573 let formattedBoolDisabled = boolDisabled; 574 typeof formattedBoolDisabled != "boolean" && (formattedBoolDisabled = !1), formattedBoolDisabled ? (this.inputElement.setAttribute("disabled", "disabled"), this.openButtonElement.setAttribute("disabled", "disabled"), this.searchAndSelectElement.classList.add("dcf-search-and-select-disabled"), this.#setVisualFocusOn(!1), this.closeAvailableItems()) : (this.inputElement.removeAttribute("disabled"), this.openButtonElement.removeAttribute("disabled"), this.searchAndSelectElement.classList.remove("dcf-search-and-select-disabled")); 575 } 576 #setDisabledMultiple(boolDisabled) { 577 let formattedBoolDisabled = boolDisabled; 578 typeof formattedBoolDisabled != "boolean" && (formattedBoolDisabled = !1), formattedBoolDisabled ? (this.inputElement.setAttribute("disabled", "disabled"), this.openButtonElement.setAttribute("disabled", "disabled"), this.searchAndSelectElement.classList.add("dcf-search-and-select-disabled"), this.selectedItemsListElement.setAttribute("aria-disabled", "true"), this.selectedItemsListElement.querySelectorAll("button").forEach((selectedItemButton) => { 579 selectedItemButton.setAttribute("disabled", "disabled"); 580 }), this.#setVisualFocusOn(!1), this.closeAvailableItems()) : (this.inputElement.removeAttribute("disabled"), this.openButtonElement.removeAttribute("disabled"), this.searchAndSelectElement.classList.remove("dcf-search-and-select-disabled"), this.selectedItemsListElement.removeAttribute("aria-disabled"), this.selectedItemsListElement.querySelectorAll("button").forEach((selectedItemButton) => { 581 selectedItemButton.removeAttribute("disabled"); 582 })); 583 } 584 /** 585 * Determines if the component is in disabled mode 586 * @returns { bool } true if the component is disabled 587 */ 588 isComponentDisabled() { 589 return this.inputElement.getAttribute("disabled") !== null; 590 } 591 /** 592 * Will turn the component's readonly mode on or off 593 * @param { bool } boolReadOnly the new readonly state 594 */ 595 #setReadOnly(boolReadOnly) { 596 return this.multiple ? this.#setReadOnlyMultiple(boolReadOnly) : this.#setReadOnlySingle(boolReadOnly); 597 } 598 #setReadOnlySingle(boolReadOnly) { 599 let formattedBoolReadOnly = boolReadOnly; 600 typeof formattedBoolReadOnly != "boolean" && (formattedBoolReadOnly = !1), formattedBoolReadOnly ? (this.inputElement.setAttribute("readonly", "readonly"), this.openButtonElement.setAttribute("readonly", "readonly"), this.closeAvailableItems()) : (this.inputElement.removeAttribute("readonly"), this.openButtonElement.removeAttribute("readonly")); 601 } 602 #setReadOnlyMultiple(boolReadOnly) { 603 let formattedBoolReadOnly = boolReadOnly; 604 typeof formattedBoolReadOnly != "boolean" && (formattedBoolReadOnly = !1), formattedBoolReadOnly ? (this.inputElement.setAttribute("readonly", "readonly"), this.openButtonElement.setAttribute("readonly", "readonly"), this.selectedItemsListElement.setAttribute("aria-readonly", "true"), this.selectedItemsListElement.querySelectorAll("button").forEach((selectedItemButton) => { 605 selectedItemButton.setAttribute("readonly", "readonly"); 606 }), this.closeAvailableItems()) : (this.inputElement.removeAttribute("readonly"), this.openButtonElement.removeAttribute("readonly"), this.selectedItemsListElement.removeAttribute("aria-readonly"), this.selectedItemsListElement.querySelectorAll("button").forEach((selectedItemButton) => { 607 selectedItemButton.removeAttribute("readonly"); 608 })); 609 } 610 /** 611 * Determines if the component is in readonly mode 612 * @returns { bool } true if the component is readonly 613 */ 614 isComponentReadOnly() { 615 return this.inputElement.getAttribute("readonly") !== null; 616 } 617 /** 618 * Determines if the keyboard input was a printable character or not 619 * @param { string } str string to test 620 * @returns { bool } true if the string was a printable character 621 */ 622 #isPrintableCharacter(str) { 623 return str.length === 1 && str.match(/\S| /); 624 } 625 /** 626 * Determines if the available items list is expanded or not 627 * @returns { bool } true if the available items list is expanded 628 */ 629 isAvailableItemsOpen() { 630 return !this.availableItemsListElement.classList.contains("dcf-d-none"); 631 } 632 /** 633 * Handles functionality of opening/expanding the available items list 634 */ 635 openAvailableItems() { 636 this.inputElement.setAttribute("aria-expanded", "true"), this.openButtonElement.setAttribute("aria-expanded", "true"), this.availableItemsListElement.classList.remove("dcf-d-none"); 637 } 638 /** 639 * Handles functionality of closing/un-expanding the available items list 640 */ 641 closeAvailableItems() { 642 this.inputElement.setAttribute("aria-expanded", "false"), this.openButtonElement.setAttribute("aria-expanded", "false"), this.availableItemsListElement.classList.add("dcf-d-none"); 643 } 644 /** 645 * Determines if the visual focus is on the available items list 646 * @returns { bool } true if the available items list has visual focus 647 */ 648 #visualFocusOnAvailableItems() { 649 return this.availableItemsListElement.dataset.focused === "true"; 650 } 651 /** 652 * Handles functionality of how to set the visual focus on an element 653 * @param { HTMLElement|false } elementToFocus The element to focus on or false to not focus on anything 654 */ 655 #setVisualFocusOn(elementToFocus) { 656 this.currentFocus !== null && this.availableItemsListElement.isSameNode(this.currentFocus) ? (this.searchAreaElement.classList.remove("dcf-search-and-select-visual-focus"), delete this.availableItemsListElement.dataset.focused) : this.currentFocus !== null && (this.currentFocus.classList.remove("dcf-search-and-select-visual-focus"), delete this.currentFocus.dataset.focused), elementToFocus !== !1 && this.availableItemsListElement.isSameNode(elementToFocus) ? (this.searchAreaElement.classList.add("dcf-search-and-select-visual-focus"), this.availableItemsListElement.dataset.focused = "true", this.currentFocus = elementToFocus) : elementToFocus !== !1 ? (elementToFocus.classList.add("dcf-search-and-select-visual-focus"), elementToFocus.dataset.focused = "true", this.currentFocus = elementToFocus) : this.currentFocus = null; 657 } 658 /** 659 * Handles functionality of how to set the visual hover on an element 660 * @param { HTMLElement|false } elementToHover The element to hover on or false to not hover on anything 661 */ 662 #setVisualHover(elementToHover) { 663 this.searchAndSelectElement.querySelectorAll(".dcf-search-and-select-visual-hover").forEach((elem) => { 664 elem.classList.remove("dcf-search-and-select-visual-hover"); 665 }), elementToHover !== !1 ? (elementToHover.classList.add("dcf-search-and-select-visual-hover"), this.currentHover = elementToHover) : this.currentHover = null; 666 } 667 /** 668 * Gets the currently active item in the available items list 669 * @returns { HTMLLIElement|false } The currently active element in the available items list or false if there is none 670 */ 671 getAvailableItemActiveDescendant() { 672 const currentItemID = this.inputElement.getAttribute("aria-activedescendant"); 673 if (currentItemID !== null && currentItemID !== "") { 674 const currentItemElement = document.getElementById(currentItemID); 675 if (currentItemElement !== null) 676 return currentItemElement; 677 } 678 return !1; 679 } 680 /** 681 * Handles functionality of setting the active element on the available items list 682 * @param { HTMLLIElement|false } itemToSet The list item to set or false if there is none 683 */ 684 #setAvailableItemActiveDescendant(itemToSet) { 685 if (itemToSet !== !1 && !this.#isElementAnAvailableItem(itemToSet)) 686 throw new Error("Element is not an available item"); 687 itemToSet !== !1 ? (this.inputElement.setAttribute("aria-activedescendant", itemToSet.getAttribute("id")), this.#setVisualHover(itemToSet)) : (this.#setVisualHover(!1), this.inputElement.setAttribute("aria-activedescendant", "")); 688 } 689 /** 690 * Handles functionality of scrolling the current active item into view 691 */ 692 #scrollActiveAvailableItemInView() { 693 const currentItemElement = this.getAvailableItemActiveDescendant(); 694 currentItemElement !== !1 && !this.#isAvailableItemInView(currentItemElement) && currentItemElement.scrollIntoView({ behavior: "smooth", block: "nearest" }); 695 } 696 /** 697 * Gets the next available item that is currently being shown 698 * This will loop the beginning if we are at the end of the list 699 * @returns { HTMLLIElement|null } The next shown available item or null if there is none 700 */ 701 getNextAvailableItem() { 702 let nextElement = null, nextElementFlag = !1; 703 for (let index = 0; index < this.listOfAvailableItems.length; index++) { 704 if (nextElementFlag) { 705 nextElement = this.listOfAvailableItems[index]; 706 break; 707 } 708 this.listOfAvailableItems[index].getAttribute("id") === this.inputElement.getAttribute("aria-activedescendant") && (nextElementFlag = !0); 709 } 710 return nextElement === null ? this.getFirstAvailableItem() : nextElement; 711 } 712 /** 713 * Gets the previous available item that is currently being shown 714 * This will loop to the end if we are the beginning of the list 715 * @returns { HTMLLIElement|null } The previous shown available item or null if there is none 716 */ 717 getPreviousAvailableItem() { 718 let previousElement = null; 719 for (let index = 0; index < this.listOfAvailableItems.length && this.listOfAvailableItems[index].getAttribute("id") !== this.inputElement.getAttribute("aria-activedescendant"); index++) 720 previousElement = this.listOfAvailableItems[index]; 721 return previousElement === null ? this.getLastAvailableItem() : previousElement; 722 } 723 /** 724 * Gets the first available item that is currently being shown 725 * @returns { HTMLLIElement|null } The first shown available item or null if there is none 726 */ 727 getFirstAvailableItem() { 728 return this.listOfAvailableItems.length === 0 ? !1 : this.listOfAvailableItems[0]; 729 } 730 /** 731 * Gets the last available item that is currently being shown 732 * @returns { HTMLLIElement|null } The last shown available item or null if there is none 733 */ 734 getLastAvailableItem
734() { 735 return this.listOfAvailableItems.length === 0 ? !1 : this.listOfAvailableItems[this.listOfAvailableItems.length - 1]; 736 } 737 /** 738 * Handles the functionality of selecting an available item 739 * @param { HTMLLIElement } itemToSelect The available item to select 740 */ 741 #selectAvailableItem(itemToSelect) { 742 return this.multiple ? this.#selectAvailableItemMultiple(itemToSelect) : this.#selectAvailableItemSingle(itemToSelect); 743 } 744 #selectAvailableItemSingle(itemToSelect) { 745 if (!this.#isElementAnAvailableItem(itemToSelect)) 746 throw new Error("Element is not an available item"); 747 this.availableItemsListElement.querySelectorAll("li.dcf-search-and-select-available-item").forEach((singleOption) => { 748 singleOption.setAttribute("aria-selected", "false"); 749 }), itemToSelect.setAttribute("aria-selected", "true"), this.selectElement.querySelectorAll("option").forEach((singleOption) => { 750 singleOption.value === itemToSelect.dataset.value ? singleOption.setAttribute("selected", "selected") : singleOption.removeAttribute("selected", "selected"); 751 }), this.closeAvailableItems(), this.inputElement.value = itemToSelect.dataset.label; 752 } 753 #selectAvailableItemMultiple(itemToSelect) { 754 if (!this.#isElementAnAvailableItem(itemToSelect)) 755 throw new Error("Element is not an available item"); 756 itemToSelect.setAttribute("aria-selected", "true"), this.selectElement.querySelectorAll("option").forEach((singleOption) => { 757 singleOption.value === itemToSelect.dataset.value && singleOption.setAttribute("selected", "selected"); 758 }), this.#appendNewSelectedItem(itemToSelect); 759 } 760 /** 761 * Handles the functionality of de-selecting an available item 762 * @param { HTMLLIElement } itemToRemove The available item to de-select 763 */ 764 #removeAvailableItem(itemToRemove) { 765 if (!this.#isElementAnAvailableItem(itemToRemove)) 766 throw new Error("Element is not an available item"); 767 itemToRemove.setAttribute("aria-selected", "false"), this.selectElement.querySelectorAll("option").forEach((singleOption) => { 768 singleOption.value === itemToRemove.dataset.value && singleOption.removeAttribute("selected"); 769 }), this.multiple ? this.selectedItemsListElement.querySelectorAll(`li[data-elem-id="${itemToRemove.dataset.elemId}"]`).forEach((singleSelectedItem) => { 770 singleSelectedItem.remove(); 771 }) : (this.inputElement.value = "", this.#filterAvailableItems()); 772 } 773 /** 774 * Handles the functionality of de-selecting a selected item 775 * @param { HTMLLIElement } itemToRemove The selected item to de-select 776 */ 777 #removeSelectedItem(itemToRemove) { 778 if (!this.#isElementASelectedItem(itemToRemove)) 779 throw new Error("Element is not a selected item"); 780 const availableItem = this.availableItemsListElement.querySelector(`li[data-elem-id="${itemToRemove.dataset.elemId}"]`); 781 availableItem.setAttribute("aria-selected", "false"), this.selectElement.querySelectorAll("option").forEach((singleOption) => { 782 singleOption.value === availableItem.dataset.value && singleOption.removeAttribute("selected"); 783 }), itemToRemove.remove(), this.selectedItemsListElement.children.length === 0 && (this.selectedItemsListElement.setAttribute("tabindex", "-1"), this.inputElement.focus()); 784 } 785 /** 786 * Checks if the element has the class `dcf-search-and-select-available-item` 787 * @param { HTMLElement } elementToCheck The element to check 788 * @returns { bool } true if it does contain that class 789 */ 790 #isElementAnAvailableItem(elementToCheck) { 791 return elementToCheck.classList.contains("dcf-search-and-select-available-item"); 792 } 793 /** 794 * Checks if the available item is selected 795 * @param { HTMLLIElement } itemToCheck The item to check if it is selected 796 * @returns { bool } true if the item is selected 797 */ 798 #isAvailableItemSelected(itemToCheck) { 799 if (!this.#isElementAnAvailableItem(itemToCheck)) 800 throw new Error("Element is not an available item"); 801 return itemToCheck.getAttribute("aria-selected") === "true"; 802 } 803 /** 804 * Checks if the available item is disabled 805 * @param { HTMLLIElement } itemToCheck The item to check if it is disabled 806 * @returns { bool } true if the item is disabled 807 */ 808 #isAvailableItemDisabled(itemToCheck) { 809 if (!this.#isElementAnAvailableItem(itemToCheck)) 810 throw new Error("Element is not an available item"); 811 return itemToCheck.getAttribute("aria-disabled") === "true"; 812 } 813 /** 814 * Calculates if the available item is in view 815 * @param { HTMLLIElement } itemToCheck The item to check if it is in view 816 * @returns { bool } true if it is in view 817 */ 818 #isAvailableItemInView(itemToCheck) { 819 if (!this.#isElementAnAvailableItem(itemToCheck)) 820 throw new Error("Element is not an available item"); 821 const listRect = this.availableItemsListElement.getBoundingClientRect(), itemToCheckRect = itemToCheck.getBoundingClientRect(); 822 return itemToCheckRect.top >= listRect.top && itemToCheckRect.bottom <= listRect.bottom; 823 } 824 /** 825 * Calculates if the selected item is in view 826 * @param { HTMLLIElement } itemToCheck The item to check if it is in view 827 * @returns { bool } true if it is in view 828 */ 829 #isSelectedItemInView(itemToCheck) { 830 if (!this.#isElementASelectedItem(itemToCheck)) 831 throw new Error("Element is not a selected item"); 832 const listRect = this.searchAreaElement.getBoundingClientRect(), itemToCheckRect = itemToCheck.getBoundingClientRect(); 833 return itemToCheckRect.top >= listRect.top && itemToCheckRect.bottom <= listRect.bottom; 834 } 835 /** 836 * Checks if the element has the class `dcf-search-and-select-selected-item` 837 * @param { HTMLElement } elementToCheck The element to check 838 * @returns { bool } true if it does contain that class 839 */ 840 #isElementASelectedItem(elementToCheck) { 841 return elementToCheck.classList.contains("dcf-search-and-select-selected-item"); 842 } 843 /** 844 * Filters available items and hides any item that does not contain the input value's string 845 */ 846 #filterAvailableItems() { 847 const searchTerm = this.inputElement.value.trim().toUpperCase(), allItems = this.availableItemsListElement.querySelectorAll("li.dcf-search-and-select-available-item"), allItemGroups = this.availableItemsListElement.querySelectorAll(".dcf-search-and-select-available-items-group"); 848 this.listOfAvailableItems = [], this.availableItemsListElement.querySelectorAll(".dcf-search-and-select-no-results").forEach((singleNoResult) => { 849 singleNoResult.remove(); 850 }); 851 let noItemsFound = !0;
852 if (allItems.forEach((singleItem) => { 853 const itemLabel = singleItem.querySelector(".dcf-search-and-select-available-item-label").innerText.toUpperCase(); 854 searchTerm === "" || itemLabel.includes(searchTerm) ? (singleItem.classList.remove("dcf-d-none"), singleItem.classList.add("dcf-d-flex"), this.listOfAvailableItems.push(singleItem), noItemsFound = !1) : (singleItem.classList.add("dcf-d-none"), singleItem.classList.remove("dcf-d-flex")); 855 }), allItemGroups.forEach((singleGroup) => { 856 singleGroup.querySelectorAll(".dcf-search-and-select-available-item:not(.dcf-d-none)").length === 0 ? singleGroup.classList.add("dcf-d-none") : singleGroup.classList.remove("dcf-d-none"); 857 }), noItemsFound) { 858 const noItemsFoundElement = document.createElement("ul"); 859 noItemsFoundElement.setAttribute("role", "presentation"), noItemsFoundElement.classList.add("dcf-search-and-select-no-results", ...this.availableItemsGroupClassList), noItemsFoundElement.innerHTML = `<li 860class="${this.availableItemsNoResultsClassList.join(" ")}" role="option"> 861 No results found 862</li>`, this.availableItemsListElement.append(noItemsFoundElement); 863 } 864 } 865} 866class UNLSearchSelect extends DCFSearchSelect { 867 static { 868 __name(this, "UNLSearchSelect"); 869 } 870 constructor(selectElement, options = {}) { 871 "availableItemsListClassList" in options || (options.availableItemsListClassList = [ 872 "dcf-absolute", 873 "dcf-d-none", 874 "dcf-mb-0", 875 "dcf-pl-0", 876 "dcf-w-100%", 877 "dcf-z-2", 878 "unl-box-shadow" 879 ]), "selectedItemButtonClassList" in options || (options.selectedItemButtonClassList = [ 880 "dcf-btn", 881 "dcf-btn-secondary", 882 "dcf-h-100%", 883 "dcf-p-3", 884 "dcf-z-1" 885 ]), super(selectElement, options), window.UNL = window.UNL || {}, window.UNL.classes = window.UNL.classes || {}, window.UNL.classes[this.selectElement.getAttribute("id")] = this, this.selectElement.dispatchEvent(new Event("UNLClassReady")); 886 } 887} 888export { 889 UNLSearchSelect as default 890}; 891//# sourceMappingURL=component.search-select.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.