import eventManager from '../../scripts/utils/eventManager'; export default class SelectList { // TODO: Make this class optional or at least some methods. rootElement!: Document; constructor(rootElement: Document) { if (!rootElement) { return; // If SSR then return } this.rootElement = rootElement; this.updateEveryPlaceholder(); // neded for non vue component to update placeholder on page load this.addSelectAllItem(); // Add active modifier eventManager.live( '.js-select-list .o-select-list__item', (_event, clickedElement) => { this.toggleSelectItems(clickedElement); }, { domElement: rootElement, attachToRootElement: true }, ); eventManager.live( '.js-select-list .o-select-list__trigger, .js-select-list-trigger.o-select-list__trigger', (event, clickedElement: HTMLElement) => { this.toggleSelectList(clickedElement, event); }, { domElement: rootElement, attachToRootElement: true }, ); // TODO: only works with single select list with pre-selected items. eventManager.live( '.js-select-list .o-select-list__menu', (event: KeyboardEvent, clickedElement: HTMLElement) => { switch (event.key) { case ' ': // Space key this.toggleSelectList(clickedElement, event); return false; case 'ArrowDown': { const nextSibling = clickedElement.querySelector( '.o-select-list__item--active', )?.nextElementSibling; if (nextSibling) { this.toggleSelectItems(nextSibling); } break; } case 'ArrowUp': { const prevSibling = clickedElement.querySelector( '.o-select-list__item--active', )?.previousElementSibling; if (prevSibling) { this.toggleSelectItems(prevSibling); } break; } case 'Enter': this.toggleSelectList(clickedElement, event); break; } }, { domElement: rootElement, keyboardEvent: { eventType: 'keydown', keypress: [' ', 'ArrowDown', 'ArrowUp', 'Enter'], }, clickOrTouchEvent: null, attachToRootElement: true, }, ); // Click outside selectlist rootElement.addEventListener('click', (event) => { const clickedElement = event.target as HTMLElement; const selectList = clickedElement.closest('.o-select-list'); if (!selectList) { const activeSelectLists = rootElement.querySelectorAll( '.o-select-list.is-open', ); if (activeSelectLists.length) { for (let i = 0; i < activeSelectLists.length; i++) { const activeElement = activeSelectLists[i]; if (activeElement.classList.contains('is-open')) { activeElement.classList.remove('is-open'); } } } } }); // Close selectlist onblur // eventManager.live( // '.o-select-list__menu', // (_event, clickedElement) => { // const selectList = clickedElement.closest('.o-select-list.is-open'); // // active element // if (selectList) { // selectList.classList.remove('is-open'); // } // }, // { // domElement: rootElement, // keyboardEvent: null, // clickOrTouchEvent: 'focusout', // attachToRootElement: true, // }, // ); } toggleSelectList(clickedElement: HTMLElement, event: any) { const selectList = clickedElement.closest('.o-select-list'); if (!selectList.classList.contains('is-open')) { if (this.rootElement) { this.rootElement .querySelectorAll('.o-select-list.is-open') .forEach((item) => { item.classList.remove('is-open'); }); } selectList.classList.add('is-open'); const expandedMenu: HTMLElement = selectList.querySelector( '.o-select-list__menu', ); expandedMenu.focus(); } else { const clickedInRowAndIsMultipleSelect = selectList.classList.contains('o-select-list--multiple-select') || event.target.closest('.o-select-list__item-all') != null || (event.path && Array.isArray(event.path) && event.path[0].closest('.o-select-list__item') != null); if (!clickedInRowAndIsMultipleSelect) { selectList.classList.remove('is-open'); } } } toggleSelectItems(clickedElement: any) { if (clickedElement) { const siblings = clickedElement.parentNode.querySelectorAll( '.o-select-list__item', ); const multipleSelect = clickedElement.closest( '.o-select-list--multiple-select', ); if (!multipleSelect) { // reset clicked select list if not multiselect for (let i = 0; i < siblings.length; i++) { if (siblings[i].querySelector('input')) { siblings[i].querySelector('input').checked = false; } siblings[i].classList.remove('o-select-list__item--active'); } } clickedElement.classList.add('o-select-list__item--active'); if (clickedElement.querySelector('input')) { clickedElement.querySelector('input').checked ^= 1; // toggle checked if (clickedElement.querySelector('input').checked) { clickedElement.classList.add('o-select-list__item--active'); } else { clickedElement.classList.remove('o-select-list__item--active'); } } // toggle all-item active modifier if (multipleSelect) { const itemAllSelect = multipleSelect.querySelector( '.o-select-list__item-all', ); if (itemAllSelect) { // if all-item exist remove active modifier itemAllSelect.classList.remove('o-select-list__item--active'); const activeListItems = Array.from(siblings).filter((e: any) => e.classList.contains('o-select-list__item--active'), ); // if all items are selected add active modifier to all-item if (siblings.length === activeListItems.length) { itemAllSelect.classList.add('o-select-list__item--active'); } } } const triggerPlaceholder = clickedElement .closest('.o-select-list') .querySelector('.o-select-list__placeholder'); this.updatePlaceholder(triggerPlaceholder, siblings); } } updatePlaceholder(placeholder: HTMLElement, siblings: HTMLElement[]) { if (!placeholder || !siblings) { return; } const selectedItems = Array.from(siblings).filter((e) => e.classList.contains('o-select-list__item--active'), ); let placeholderText = placeholder.dataset.placeholder; // if every item is selected and placeholderAllSelected exist if ( selectedItems.length == siblings.length && placeholder.dataset.placeholderallselected ) { placeholderText = placeholder.dataset.placeholderallselected; } // if more than 3 items are selected and placeholderCountSelected exist else if ( selectedItems.length > 3 && placeholder.dataset.placeholdercountselected ) { // replace {0} and {1} with number of selected items if exist. placeholderText = placeholder.dataset.placeholdercountselected .replace('{0}', selectedItems.length.toString()) .replace('{1}', siblings.length.toString()); } else if (selectedItems.length) { // map all selected text items to array and join with comma placeholderText = selectedItems .map((e: any) => e.textContent.trim()) .join(', '); } // set placeholder text const placeHolderTextElement = placeholder.querySelector( '.o-select-list__placeholder-text', ); if (placeHolderTextElement) { placeHolderTextElement.textContent = placeholderText || ''; } } updateEveryPlaceholder() { const selectLists = this.rootElement.querySelectorAll( '.js-select-list.o-select-list', ); selectLists.forEach((selectList: any) => { const triggerPlaceholder = selectList.querySelector( '.o-select-list__placeholder', ); const selectListItems = selectList.querySelectorAll( '.o-select-list__item', ); this.updatePlaceholder(triggerPlaceholder, selectListItems); }); } addSelectAllItem() { // get all multiple select lists const selectLists = this.rootElement.querySelectorAll( '.o-select-list--multiple-select', ); for (let i = 0; i < selectLists.length; i++) { const placeholder = selectLists[i].querySelector( '.o-select-list__placeholder', )?.dataset?.placeholderallselected; if (!placeholder) { continue; // if no placeholderAllSelected exist, do not add select all item } const selectList = selectLists[i].querySelector('.o-select-list__menu'); if (!selectList) { continue; // menu holder is missing } selectList.setAttribute('aria-multiselectable', 'true'); const selectListItems = selectList.querySelectorAll( '.o-select-list__item', ); const selectAllElement = document.createElement('span'); selectAllElement.setAttribute('class', 'o-select-list__item-all'); selectAllElement.setAttribute('title', 'select all'); selectAllElement.setAttribute('role', 'listitem'); selectAllElement.innerHTML = ` `; selectList.insertBefore(selectAllElement, selectListItems[0]); } // event listener to select all label eventManager.live( '.js-select-list .o-select-list__item-all', (_event, clickedElement) => { const input = clickedElement.querySelector('input'); const selectList = clickedElement.closest('.o-select-list__menu'); if (!input || !selectList) { return; } const selectListItems = selectList.querySelectorAll( '.o-select-list__item', ); const activeListItems = Array.from(selectListItems).filter((e: any) => e.classList.contains('o-select-list__item--active'), ); // if all items are selected remove active modifier from all-item, else add active modifier const fn = activeListItems.length === selectListItems.length ? 'remove' : 'add'; // add active modifier to all-item allso in loop. const allSelectListItems = selectList.querySelectorAll( '.o-select-list__item, .o-select-list__item-all', ); for (let i = 0; i < allSelectListItems.length; i++) { const item = allSelectListItems[i]; item.classList[fn]('o-select-list__item--active'); const checkbox = item.querySelector('input'); if (checkbox) { checkbox.checked = fn === 'add' ? true : false; } } }, { domElement: this.rootElement, attachToRootElement: true }, ); } }