1/** 2 * @file 3 * Object for interacting with multiselect dropdowns and tracking state. 4 */ 5 6'use strict'; 7 8function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); } 9function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } } 10function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; } 11function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); } 12function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } 13var _attribute = /*#__PURE__*/new WeakMap(); 14var _selector = /*#__PURE__*/new WeakMap(); 15var _drupalAttribute = /*#__PURE__*/new WeakMap(); 16var _opened = /*#__PURE__*/new WeakMap(); 17var _persistedOnLoad = /*#__PURE__*/new WeakMap(); 18var _focus = /*#__PURE__*/new WeakMap(); 19var _shouldFocus = /*#__PURE__*/new WeakMap(); 20var _search = /*#__PURE__*/new WeakMap(); 21var _type = /*#__PURE__*/new WeakMap(); 22export default class MultiselectDropdowns { 23 constructor() { 24 /** 25 * Multiselect dropdown data attribute base. 26 * 27 * @type {String} 28 */ 29 _classPrivateFieldInitSpec(this, _attribute, 'data-multiselect-dropdown'); 30 /** 31 * Multiselect dropdown selector. 32 * 33 * @type {String} 34 */ 35 _classPrivateFieldInitSpec(this, _selector, "[".concat(_classPrivateFieldGet(_attribute, this), "]")); 36 /** 37 * Drupal data selector attribute. 38 * 39 * @type {String} 40 */ 41 _classPrivateFieldInitSpec(this, _drupalAttribute, 'data-drupal-selector'); 42 /** 43 * Opened multiselect dropdown dialogs. 44 * 45 * @type {Array} 46 */ 47 _classPrivateFieldInitSpec(this, _opened, []); 48 /** 49 * Multiselect dropdown dialogs to open on load. 50 * 51 * @type {Array} 52 */ 53 _classPrivateFieldInitSpec(this, _persistedOnLoad, []); 54 /** 55 * Selector to focus on AJAX load. 56 * 57 * @type {String} 58 */ 59 _classPrivateFieldInitSpec(this, _focus, ''); 60 /** 61 * Focus should happen on load. 62 * 63 * @type {Boolean} 64 */ 65 _classPrivateFieldInitSpec(this, _shouldFocus, false); 66 /** 67 * Search text to add on AJAX load. 68 * 69 * @type {Map} 70 */ 71 _classPrivateFieldInitSpec(this, _search, new Map()); 72 /** 73 * The type of modal. 74 * 75 * Values map to the ModalType enum. 76 * 77 * @type {{breakpoint: String, dialog: String, modal: String}} 78 */ 79 _classPrivateFieldInitSpec(this, _type, { 80 breakpoint: 'breakpoint', 81 dialog: 'dialog', 82 modal: 'modal' 83 }); 84 } 85 /** 86 * Get the ID of a multiselect dropdown. 87 * 88 * @param {HTMLElement} multiselect 89 * 90 * @return {String} 91 */ 92 getId(multiselect) { 93 return multiselect.getAttribute(_classPrivateFieldGet(_drupalAttribute, this)); 94 } 95 96 /** 97 * Generate a unique selector for a multiselect dropdown. 98 * 99 * @param {HTMLElement} multiselect 100 * 101 * @return {String} 102 */ 103 getSelector(multiselect) { 104 return "[".concat(_classPrivateFieldGet(_drupalAttribute, this), "=\"").concat(this.getId(multiselect), "\"]"); 105 } 106 107 /** 108 * Get the toggle button for a multiselect dropdown. 109 * 110 * @param {HTMLElement} multiselect 111 * 112 * @return {HTMLButtonElement|null} 113 */ 114 getToggle(multiselect) { 115 return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-toggle]")); 116 } 117 118 /** 119 * Get the dialog for a multiselect dropdown. 120 * 121 * @param {HTMLElement} multiselect 122 * 123 * @return {HTMLDialogElement|null} 124 */ 125 getDialog(multiselect) { 126 return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-dialog]")); 127 } 128 129 /** 130 * Get the dialog close button for a multiselect dropdown. 131 * 132 * @param {HTMLElement} multiselect 133 * 134 * @return {HTMLDialogElement|null} 135 */ 136 getDialogClose(multiselect) { 137 return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-
137dialog-close]")); 138 } 139 140 /** 141 * Get the select all button for a multiselect dropdown. 142 * 143 * @param {HTMLElement} multiselect 144 * 145 * @return {HTMLButtonElement|null} 146 */ 147 getSelectAll(multiselect) { 148 return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-select-all]")); 149 } 150 151 /** 152 * Get the select none button for a multiselect dropdown. 153 * 154 * @param {HTMLElement} multiselect 155 * 156 * @return {HTMLButtonElement|null} 157 */ 158 getSelectNone(multiselect) { 159 return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-select-none]")); 160 } 161 162 /** 163 * Get the search input for a multiselect dropdown. 164 * 165 * @param {HTMLElement} multiselect 166 * 167 * @returns {HTMLInputElement|null} 168 */ 169 getSearchInput(multiselect) { 170 return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-search]")); 171 } 172 173 /** 174 * Get the checkbox list for a multiselect dropdown. 175 * 176 * @param {HTMLElement} multiselect 177 * 178 * @return {HTMLUListElement|null} 179 */ 180 getList(multiselect) { 181 return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-list]")); 182 } 183 184 /** 185 * Get the checkbox list items for a multiselect dropdown. 186 * 187 * @param {HTMLElement} multiselect 188 * 189 * @return {NodeListOf<HTMLLIElement>} 190 */ 191 getListItems(multiselect) { 192 return this.getList(multiselect).querySelectorAll('li'); 193 } 194 195 /** 196 * Get the submit button for a multiselect dropdown. 197 * 198 * @param {HTMLElement} multiselect 199 * 200 * @return {HTMLButtonElement|null} 201 */ 202 getSubmit(multiselect) { 203 return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-submit]")); 204 } 205 206 /** 207 * Get the clear button for a multiselect dropdown. 208 * 209 * @param {HTMLElement} multiselect 210 * 211 * @return {HTMLButtonElement|null} 212 */ 213 getClear(multiselect) { 214 return multiselect.querySelector("[".concat(_classPrivateFieldGet(_attribute, this), "-clear]")); 215 } 216 217 /** 218 * Get the multiselect dropdown checkboxes. 219 * 220 * @param {HTMLElement} multiselect 221 * 222 * @return {NodeListOf<Element|HTMLInputElement>} 223 */ 224 static getCheckboxes(multiselect) { 225 return multiselect.querySelectorAll('[type="checkbox"]'); 226 } 227 228 /** 229 * Get the checked multiselect dropdown checkboxes. 230 * 231 * @param {HTMLElement} multiselect 232 * 233 * @return {NodeListOf<Element|HTMLInputElement>} 234 */ 235 static getCheckboxesChecked(multiselect) { 236 return multiselect.querySelectorAll('[type="checkbox"]:checked'); 237 } 238 239 /** 240 * Get the unchecked multiselect dropdown checkboxes. 241 * 242 * @param {HTMLElement} multiselect 243 * 244 * @return {NodeListOf<Element|HTMLInputElement>} 245 */ 246 static getCheckboxesUnchecked(multiselect) { 247 return multiselect.querySelectorAll('[type="checkbox"]:not(:checked)'); 248 } 249 250 /** 251 * Get the breakpoint of a multiselect dropdown. 252 * 253 * @param {HTMLElement} multiselect 254 * 255 * @return {Number} 256 */ 257 getBreakpoint(multiselect) { 258 const breakpoint = multiselect.getAttribute('data-multiselect-dropdown-breakpoint'); 259 switch (breakpoint) { 260 case _classPrivateFieldGet(_type, this).dialog: 261 return 0; 262 case _classPrivateFieldGet(_type, this).modal: 263 return 99999999; 264 default: 265 return Number.parseInt(breakpoint, 10); 266 } 267 } 268 269 /** 270 * Get all multiselect dropdown instances contained in an HTML element. 271 * 272 * @param {Document|HTMLElement} html 273 * 274 * @return {NodeListOf<Element|HTMLElement>|HTMLElement[]} 275 */ 276 getInstances(html) { 277 if (html instanceof HTMLElement && html.matches(_classPrivateFieldGet(_selector, this))) { 278 return [html]; 279 } 280 return html.querySelectorAll(_classPrivateFieldGet(_selector, this)); 281 } 282 283 /** 284 * Get a multiselect from an element contained in the multiselect. 285 * 286 * @param {Element|HTMLElement} element 287 * 288 * @return {Element|HTMLElement|null} 289 */ 290 getInstance(element) { 291 return element.closest(_classPrivateFieldGet(_selector, this)); 292 } 293 294 /** 295 * Set an element to focus on. 296 * 297 * @param {HTMLElement|null} element 298 */ 299 setFocus(element) { 300 _classPrivateFieldSet(_shouldFocus, this, true); 301 _classPrivateFieldSet(_focus, this, element ? "[data-drupal-selector=\"".concat(element.getAttribute('data-drupal-selector'), "\"]") : ''); 302 } 303 304 /** 305 * Get the focus selector. 306 *
307 * If no focus selector is set, focus the view results. 308 * 309 * @return {String} 310 */ 311 getFocus() { 312 if (!_classPrivateFieldGet(_shouldFocus, this)) { 313 return ''; 314 } 315 return _classPrivateFieldGet(_focus, this) ? _classPrivateFieldGet(_focus, this) : '.view-content, .view-empty, [data-multiselect-dropdown-view-results]'; 316 } 317 318 /** 319 * Set the search text for a multiselect. 320 * 321 * @param {HTMLElement} multiselect 322 * @param {String} value 323 */ 324 setSearch(multiselect, value) { 325 if (value) { 326 _classPrivateFieldGet(_search, this).set(this.getId(multiselect), value); 327 } else { 328 _classPrivateFieldGet(_search, this).delete(this.getId(multiselect)); 329 } 330 } 331 332 /** 333 * Get the search text for a multiselect. 334 * 335 * @param {HTMLElement} multiselect 336 * 337 * @return {String} 338 */ 339 getSearch(multiselect) { 340 const value = _classPrivateFieldGet(_search, this).get(this.getId(multiselect)); 341 return value === undefined ? '' : value; 342 } 343 344 /** 345 * Check if an element is a multiselect dropdown. 346 * 347 * @param {HTMLElement} element 348 * 349 * @return {Boolean} 350 */ 351 isMultiselect(element) { 352 return element.hasAttribute(_classPrivateFieldGet(_attribute, this)); 353 } 354 355 /** 356 * Check if a multiselect dropdown is open. 357 * 358 * @param {HTMLElement} multiselect 359 * 360 * @return {Boolean} 361 */ 362 isOpen(multiselect) { 363 return _classPrivateFieldGet(_opened, this).includes(this.getId(multiselect)); 364 } 365 366 /** 367 * Open a multiselect dropdown. 368 * 369 * @param {HTMLElement} multiselect 370 * @param {Boolean} userTriggered 371 */ 372 open(multiselect, userTriggered) { 373 const dialog = this.getDialog(multiselect); 374 if (!dialog) { 375 throw new Error('Multiselect does not contain an HTMLDialogElement with the attribute data-multiselect-dropdown-dialog.'); 376 } 377 if (window.innerWidth <= this.getBreakpoint(multiselect)) { 378 dialog.showModal(); 379 } else if (userTriggered) { 380 dialog.show(); 381 } else { 382 dialog.open = true; 383 } 384 multiselect.setAttribute('data-multiselect-dropdown-open', ''); 385 const toggle = this.getToggle(multiselect); 386 if (toggle) { 387 toggle.setAttribute('aria-expanded', 'true'); 388 } 389 dialog.addEventListener('close', () => { 390 this.close(multiselect); 391 }); 392 if (!this.isOpen(multiselect)) { 393 _classPrivateFieldGet(_opened, this).push(this.getId(multiselect)); 394 } 395 if (this.shouldOpenOnLoad(multiselect)) { 396 this.persistOnLoad(multiselect); 397 } 398 } 399 400 /** 401 * Close a multiselect dropdown. 402 * 403 * @param {HTMLElement} multiselect 404 */ 405 close(multiselect) { 406 const dialog = this.getDialog(multiselect); 407 if (!dialog) { 408 throw new Error('Multiselect does not contain an HTMLDialogElement with the attribute data-multiselect-dropdown-dialog.'); 409 } 410 dialog.close(); 411 multiselect.removeAttribute('data-multiselect-dropdown-open'); 412 const toggle = this.getToggle(multiselect); 413 if (toggle) { 414 toggle.setAttribute('aria-expanded', 'false'); 415 } 416 if (toggle && document.activeElement === this.getList(multiselect)) { 417 toggle.focus({ 418 preventScroll: true 419 }); 420 } 421 if (this.isOpen(multiselect)) { 422 _classPrivateFieldGet(_opened, this).splice(_classPrivateFieldGet(_opened, this).indexOf(this.getId(multiselect)), 1); 423 } 424 this.doNotPersistOnLoad(multiselect); 425 } 426 427 /** 428 * Switch the multiselect dropdown open state. 429 * 430 * @param {HTMLElement} multiselect 431 * @param {Boolean} userTriggered 432 */ 433 toggle(multiselect, userTriggered) { 434 if (this.isOpen(multiselect)) { 435 this.close(multiselect); 436 } else { 437 this.open(multiselect, userTriggered); 438 } 439 } 440 441 /** 442 * Check if a multiselect should open on load. 443 * 444 * @param {HTMLElement} multiselect 445 * 446 * @return {Boolean} 447 */ 448 shouldOpenOnLoad(multiselect) { 449 return multiselect.hasAttribute("".concat(_classPrivateFieldGet(_attribute, this), "-persist-open")); 450 } 451 452 /** 453 * Check if a multiselect is marked as open on load. 454 * 455 * @param {HTMLElement} multiselect 456 * 457 * @return {Boolean} 458 */ 459 isPersistedOnLoad(multiselect) { 460 return _classPrivateFieldGet(_persistedOnLoad, this).includes(this.getId(multiselect)); 461 } 462 463 /** 464 * Open a multiselect on load. 465 * 466 * @param {HTMLElement} multiselect 467 */ 468 persistOnLoad(multiselect) { 469 if (!this.isPersistedOnLoad(multiselect)) { 470 _classPrivateFieldGet(_persistedOnLoad, this).push(this.getId(multiselect)); 471 } 472 } 473 474 /** 475 * Do not open a multiselect on load. 476 * 477 * @param {HTMLElement} multiselect 478 */ 479 doNotPersistOnLoad(multiselect) { 480 if (this.isPersistedOnLoad(multiselect)) { 481 _classPrivateFieldGet(_persistedOnLoad, this).splice(_classPrivateFieldGet(_persistedOnLoad, this).indexOf(this.getId(multiselect)), 1); 482 this.setSearch(multiselect, ''); 483 } 484 } 485 486 /** 487 * Update the multiselect dropdown toggle label. 488 * 489 * @param {HTMLElement} multiselect 490 */ 491 updateLabel(multiselect) { 492 var _MultiselectDropdowns2, _MultiselectDropdowns3, _multiselect$getAttri; 493 let attribute; 494 const count = (_MultiselectDropdowns2 = (_MultiselectDropdowns3 = MultiselectDropdowns.getCheckboxesChecked(multiselect)) === null || _MultiselectDropdowns3 === void 0 ? void 0 : _MultiselectDropdowns3.length) !== null && _MultiselectDropdowns2 !== void 0 ? _MultiselectDropdowns2 : 0; 495 switch (count) { 496 case 0: 497 attribute = "".concat(_classPrivateFieldGet(_attribute, this), "-label-none"); 498 break; 499 case 1: 500 attribute = "".concat(_classPrivateFieldGet(_attribute, this), "-label-single"); 501 break; 502 case MultiselectDropdowns.getCheckboxes(multiselect).length: 503 attribute = "".concat(_classPrivateFieldGet(_attribute, this), "-label-all"); 504 break; 505 default: 506 attribute = "".concat(_classPrivateFieldGet(_attribute, this), "-label-plural"); 507 break; 508 } 509 this.getToggle(multiselect).innerText = ((_multiselect$getAttri = multiselect.getAttribute(attribute)) !== null && _multiselect$getAttri !== void 0 ? _multiselect$getAttri : '').replace(/%d/, count.toString()); 510 } 511 512 /** 513 * Submit a multiselect dropdown. 514 * 515 * @param {HTMLElement} multiselect 516 */ 517 submit(multiselect) { 518 const submitButton = multiselect.closest('form').querySelector('[type="submit"]'); 519 if (submitButton) { 520 submitButton.click(); 521 } else { 522 multiselect.closest('form').requestSubmit(); 523 } 524 _classPrivateFieldSet(_opened, this, []); 525 } 526 527 /** 528 * Filter a multiselect's items. 529 * 530 * @param {HTMLElement} multiselect 531 */ 532 filter(multiselect) {
533 const searchInput = this.getSearchInput(multiselect); 534 const input = searchInput.value.toLowerCase(); 535 const threshold = searchInput.getAttribute("".concat(_classPrivateFieldGet(_attribute, this), "-search-character-threshold")); 536 this.setSearch(multiselect, searchInput.value); 537 this.getListItems(multiselect).forEach(item => { 538 if (input.length < threshold) { 539 item.style.display = 'initial'; 540 } else { 541 const match = item.querySelector('label').innerText.toLowerCase().indexOf(input) === -1; 542 item.style.display = match ? 'none' : 'initial'; 543 } 544 }); 545 } 546}
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.