import Container from './components/Container' import Store from './store/Store' import './scss/multiple-select.scss' var selectMultipleContainerId = 0 class MultipleSelect { constructor(elId, options) { selectMultipleContainerId++ const defaultOptions = { placeholder: 'Select', customPlaceholder: false, } this.$elId = elId; this.$store = new Store() this.$options = { ...defaultOptions, ...options } let { el, select, isMultiple, items, selectedItems } = this._buildRootElement(elId) this.$el = el this.$select = select this.$store.isMultiple = isMultiple this.$store.items = items this.$store.selectedItems = selectedItems this.$container = new Container({ root: this }) // syncing with the actual select this.$store.on('selectedItemsChange', (selectedItems) => { this.$select.querySelectorAll('option').forEach(option => { if (selectedItems.find(item => item.value === option.value)) { option.setAttribute('selected', true) } else { option.removeAttribute('selected') } }) if (selectedItems.length < 1) { this.$select.value = '' } const changeEvent = document.createEvent('HTMLEvents') changeEvent.initEvent('change', true, true) const inputEvent = document.createEvent('HTMLEvents') inputEvent.initEvent('input', true, true) this.$select.dispatchEvent(changeEvent) this.$select.dispatchEvent(inputEvent) }) // when the container is done rendered, the dropdown // will automatically focused this.$observer = new MutationObserver(() => { if (this.$el.classList.contains('opened')) { this.$container.$dropdownSelect.$input.focus() } }) this.$observer.observe(this.$el, { attributes: true, childList: true }); this.$store.emit('render', { el: this.$el, items: items }); } reRender() { this.$el.remove(); this.$observer.disconnect(); this.$store.removeAllListeners(); let { el, select, isMultiple, items, selectedItems } = this._buildRootElement(this.$elId) this.$el = el this.$select = select this.$store.isMultiple = isMultiple this.$store.items = items this.$store.selectedItems = selectedItems this.$container = new Container({ root: this }) // syncing with the actual select this.$store.on('selectedItemsChange', (selectedItems) => { this.$select.querySelectorAll('option').forEach(option => { if (selectedItems.find(item => item.value === option.value)) { option.setAttribute('selected', true) } else { option.removeAttribute('selected') } }) if (selectedItems.length < 1) { this.$select.value = '' } const changeEvent = document.createEvent('HTMLEvents') changeEvent.initEvent('change', true, true) const inputEvent = document.createEvent('HTMLEvents') inputEvent.initEvent('input', true, true) this.$select.dispatchEvent(changeEvent) this.$select.dispatchEvent(inputEvent) }) // when the container is done rendered, the dropdown // will automatically focused this.$observer = new MutationObserver(() => { if (this.$el.classList.contains('opened')) { this.$container.$dropdownSelect.$input.focus() } }) this.$observer.observe(this.$el, { attributes: true, childList: true }); this.$store.emit('render', { el: this.$el, items: items }); } /** * Creating `
` for root element right after the `` element. * * @param {*} elId * @returns {*} * @memberof MultipleSelect */ _buildRootElement (elId) { let select = document.querySelector(elId) let root = document.createElement('div') let items = [] let selectedItems = [] root.setAttribute('id', `multiple-select-container-${selectMultipleContainerId}`) root.classList.add('multiple-select-container') root.style.position = 'relative' Array.from(select.options).forEach(option => { items.push({ value: option.value, label: option.innerText.trim(), disabled: option.disabled }) }) // get the already selected items Array.from(select.selectedOptions).forEach(option => { selectedItems.push({ value: option.value, label: option.innerText.trim() }) }) // add event listener when