class BladewindSelect { clickArea; rootElement; itemsContainer; searchInput; selectItems; isMultiple; required; displayArea; formInput; maxSelection; toFilter; selectedValue; canClear; enabled; metaData; constructor(name, placeholder) { this.name = name; this.placeholder = placeholder || 'Select One'; this.rootElement = `.bw-select-${name}`; this.clickArea = `${this.rootElement} .clickable`; this.displayArea = `${this.rootElement} .display-area`; this.itemsContainer = `${this.rootElement} .bw-select-items-container`; this.searchInput = `${this.itemsContainer} .bw_search`; this.selectItems = `${this.itemsContainer} .bw-select-items .bw-select-item`; this.isMultiple = (domEl(this.rootElement).getAttribute('data-multiple') === 'true'); this.required = (domEl(this.rootElement).getAttribute('data-required') === 'true'); this.formInput = `input.bw-${this.name}`; domEl(this.displayArea).style.maxWidth = `${(domEl(this.rootElement).offsetWidth - 40)}px`; this.maxSelection = -1; this.canClear = (!this.required && !this.isMultiple); this.enabled = true; this.selectedItem = null; this.metaData = domEl(this.rootElement).getAttribute('data-meta-data') || null; } activate = (options = {}) => { if (options.disabled !== '1' && options.readonly !== '1') { domEl(this.clickArea).addEventListener('click', (e) => { unhide(this.itemsContainer); }); this.hide(); this.search(); this.manualModePreSelection(); this.selectItem(); this.enableKeyboardNavigation(); } else { this.selectItem(); this.enabled = false; } } enableKeyboardNavigation = () => { domEl(this.rootElement).addEventListener('keydown', (e) => { if (e.key === "Enter") { if (!this.selectedItem) { e.preventDefault(); unhide(this.itemsContainer); domEl(this.searchInput).focus(); } else { hide(this.itemsContainer); } } if (e.key === "Tab" || e.key === "Escape") { hide(this.itemsContainer); } if (e.key === "ArrowDown" || e.key === "ArrowUp") { e.preventDefault(); let els = [...domEls(this.selectItems)].filter((el) => { if (el.classList.contains('hidden')) { return false; } return el.getAttribute('data-unselectable') === null; }); if (!this.selectedItem) { this.selectedItem = e.key === 'ArrowDown' ? els[0] : els[els.length - 1]; } else { let idx = els.indexOf(this.selectedItem); this.selectedItem = e.key === 'ArrowDown' ? els[idx + 1] : els[idx - 1]; } changeCssForDomArray(`${this.rootElement} .bw-select-item`, 'bg-slate-100/90', 'remove'); changeCss(this.selectedItem, 'bg-slate-100/90', 'add', true); this.setValue(this.selectedItem); this.callUserFunction(this.selectedItem); } }); } clearable = () => { this.canClear = true; } hide = () => { document.addEventListener('mouseup', (e) => { let searchArea = domEl(this.searchInput); let container = domEl((this.isMultiple) ? this.itemsContainer : this.clickArea); if (searchArea && container && !searchArea.contains(e.target) && !container.contains(e.target)) hide(this.itemsContainer); }); } search = () => { domEl(this.searchInput).addEventListener('keyup', (e) => { let value = (domEl(this.searchInput).value); domEls(this.selectItems).forEach((el) => { (el.innerText.toLowerCase().indexOf(value.toLowerCase()) !== -1) ? unhide(el, true) : hide(el, true); }); }); } /** * When using non-dynamic selects, ensure select_value= * works the same way as for dynamic selects. This saves the user from * manually checking if each select-item should be selected or not. */ manualModePreSelection = () => { let selectMode = domEl(`${this.rootElement}`).getAttribute('data-type'); let selectedValue = domEl(`${this.rootElement}`).getAttribute('data-selected-value'); if (selectMode === 'manual' && selectedValue !== null) { domEls(this.selectItems).forEach((el) => { let item_value = el.getAttribute('data-value'); if (item_value === selectedValue) el.setAttribute('data-selected', true); }); } } selectItem = () => { domEls(this.selectItems).forEach((el) => { let selected = (el.getAttribute('data-selected') !== null); if (selected) this.setValue(el); let isSelectable = (el.getAttribute('data-unselectable') === null); if (isSelectable) { el.addEventListener('click', () => { this.setValue(el); this.callUserFunction(el); }); } }); } moveLabel = (direction = 'up') => { let placeholderElement = domEl(`${this.rootElement} .placeholder`); let labelElement = domEl(`${this.rootElement} .placeholder .form-label`); if (labelElement) { if (direction === 'up') { changeCss(labelElement, '!top-4', 'remove', true); } else { changeCss(labelElement, '!top-4', 'add', true); } unhide(placeholderElement, true); } } setValue = (item) => { this.selectedValue = item ? item.getAttribute('data-value') : null; let selectedLabel = item ? item.getAttribute('data-label') : null; let svg = domEl(`${this.rootElement} div[data-value="${this.selectedValue}"] svg`); let input = domEl(this.formInput); hide(`${this.rootElement} .placeholder`); unhide(this.displayArea); if (this.toFilter) { (new BladewindSelect(this.toFilter, '')).reset(); //FIXME: dont new up an instance this.filter(this.toFilter, this.selectedValue); } if (this.enabled) { if (!this.isMultiple) { changeCssForDomArray(`${this.selectItems} svg`, 'hidden'); domEl(this.displayArea).innerText = selectedLabel; input.value = this.selectedValue; unhide(svg, true); this.moveLabel(); if (this.canClear) { unhide(`${this.clickArea} .reset`); domEl(`${this.clickArea} .reset`).addEventListener('click', (e) => { this.unsetValue(item); e.stopImmediatePropagation(); }); } } else { if (input.value.includes(this.selectedValue)) { this.unsetValue(item); } else { if (!this.maxSelectableExceeded()) { unhide(svg, true); input.value += `,${this.selectedValue}`; domEl(this.displayArea).innerHTML += this.labelTemplate(selectedLabel, this.selectedValue); this.removeLabel(this.selectedValue); } else { showNotification('', this.maxSelectionError, 'error'); } this.moveLabel(); } this.scrollbars(); } stripComma(input); changeCss(`${this.clickArea}`, '!border-red-400', 'remove'); } } unsetValue = (item) => { this.selectedValue = item ? item.getAttribute('data-value') : null; // let selectedValue = item.getAttribute('data-value'); let svg = domEl(`${this.rootElement} div[data-value="${this.selectedValue}"] svg`); let input = domEl(this.formInput); // only unset values if the Select component is not disabled if (this.enabled) { //!domEl(this.clickArea).classList.contains('disabled') if (!this.isMultiple) { unhide(`${this.rootElement} .placeholder`); changeCssForDomArray(`${this.selectItems} svg`, 'hidden'); domEl(this.displayArea).innerText = ''; input.value = ''; hide(this.displayArea); hide(`${this.clickArea} .reset`); this.moveLabel('down'); } else { if (domEl(`${this.displayArea} span.bw-sp-${this.selectedValue}`)) { let keyword = `(,?)${this.selectedValue}`; input.value = input.value.replace(input.value.match(keyword)[0], ''); hide(svg, true); domEl(`${this.displayArea} span.bw-sp-${this.selectedValue}`).remove(); if (domEl(this.displayArea).innerText === '') { unhide(`${this.rootElement} .placeholder`); hide(this.displayArea); this.moveLabel('down'); } } } stripComma(input); this.callUserFunction(item); if (this.toFilter) { (new BladewindSelect(this.toFilter, '')).reset(); //FIXME: dont new up an instance this.clearFilter(this.toFilter); } } } scrollbars = () => { if (domEl(this.displayArea).scrollWidth > domEl(this.rootElement).clientWidth) { unhide(`${this.clickArea} .scroll-left`); unhide(`${this.clickArea} .scroll-right`); domEl(`${this.clickArea} .scroll-right`).addEventListener('click', (e) => { this.scroll(150); e.stopImmediatePropagation(); }); domEl(`${this.clickArea} .scroll-left`).addEventListener('click', (e) => { this.scroll(-150); e.stopImmediatePropagation(); }); } else { hide(`${this.clickArea} .scroll-left`); hide(`${this.clickArea} .scroll-right`); } } scroll = (amount) => { domEl(this.displayArea).scrollBy(amount, 0); ((domEl(this.displayArea).clientWidth + domEl(this.displayArea).scrollLeft) >= domEl(this.displayArea).scrollWidth) ? hide(`${this.clickArea} .scroll-right`) : unhide(`${this.clickArea} .scroll-right`); (domEl(this.displayArea).scrollLeft === 0) ? hide(`${this.clickArea} .scroll-left`) : unhide(`${this.clickArea} .scroll-left`); } labelTemplate = (label, value) => { return `${label}` + ``; } removeLabel = () => { domEls(`${this.displayArea} span svg`).forEach((el) => { el.addEventListener('click', (e) => { let value = el.getAttribute('data-value'); this.unsetValue(domEl(`.bw-select-item[data-value="${value}"]`)); }); }); } selectByValue = (value) => { domEls(this.selectItems).forEach((el) => { if (el.getAttribute('data-value') === value) this.setValue(el); }); } reset = () => { if (this.enabled) { domEls(this.selectItems).forEach((el) => { this.unsetValue(el); }); hide(this.displayArea); unhide(this.placeholder); } } disable = () => { changeCss(this.clickArea, 'disabled'); changeCss(this.clickArea, 'enabled, readonly', 'remove'); // hide(`${this.clickArea} .reset`); domEl(this.clickArea).addEventListener('click', () => { hide(this.itemsContainer); }); this.enabled = false; } enable = () => { changeCss(this.clickArea, 'readonly, disabled', 'remove'); changeCss(this.clickArea, 'enabled'); domEl(this.clickArea).addEventListener('click', (e) => { unhide(this.itemsContainer); }); this.enabled = true; } callUserFunction = (item) => { let userFunction = item ? item.getAttribute('data-user-function') : null; if (userFunction !== null && userFunction !== undefined) { let meta = (this.metaData) ? JSON.parse(JSON.stringify(this.metaData)) : null; callUserFunction( `${userFunction}( '${item.getAttribute('data-value')}', '${item.getAttribute('data-label')}', '${domEl(this.formInput).value}', ${meta})` ); } } maxSelectable = (max_number, error_message) => { this.maxSelection = (this.isMultiple) ? max_number : false; this.maxSelectionError = error_message; } maxSelectableExceeded = () => { let input = domEl(this.formInput); let totalSelected = (input.value.split(',')).length; return ((this.maxSelection !== -1) && totalSelected === this.maxSelection); } filter = (element, by = '') => { this.toFilter = element; if (by !== '') { //this.selectedValue domEls(`.bw-select-${element} .bw-select-items .bw-select-item`).forEach((el) => { const filterValue = el.getAttribute('data-filter-value'); (filterValue === by) ? unhide(el, true) : hide(el, true); }); } } clearFilter = (element, by = '') => { if (element) { // (new BladewindSelect(element, '')).reset(); const elementItems = `.bw-select-${element} .bw-select-items .bw-select-item`; if (by === '') { // clear all filters domEls(elementItems).forEach((el) => { unhide(el, true); }); } else { // clear specific values' filters domEls(elementItems).forEach((el) => { const filterValue = el.getAttribute('data-filter-value'); (filterValue === this.selectedValue) ? hide(el, true) : unhide(el, true); }); } } } }