UNPKG

@usj/vue-components

Version:

VueJS components used internally by USJ

349 lines (307 loc) 7.89 kB
import debounce from '../../core/utils/debounce' import getClosestVueParent from '../../core/utils/getClosestVueParent.js' import common from '../usjInputContainer/common' let vm const usjAdvanceSelect = { mixins: [common], props: { options: { type: Array, default: () => [] }, disabled: { type: Boolean, default: false }, placeholder: { type: String, default: 'Select an item...' }, clearable: { type: Boolean, default: false }, emitOnly: { // emit select event only (not update v-model), applicable only to single select type: Boolean, default: false }, searchable: { type: Boolean, default: false }, fetchFunction: { type: Function }, multiple: { type: Boolean, default: false }, required: { type: Boolean, default: true }, value: null, id: String, name: String }, data() { return { searchValue: '', singleItem: {}, multipleItems: [], highlightIdx: -1, showValueEl: true, showMenuEl: true, showPlaceholderEl: true, menuItems: [], loading: false } }, watch: { multipleItems(newVal) { console.debug('Multiple Items ', newVal) }, singleItem(newVal) { console.debug('Single Items ', newVal) }, value(val) { if (val) { this.updateLocalValue() } }, options(val) { this.menuItems = val } }, computed: { rootClasses() { return { 'is-disabled': this.disabled, 'is-multiple': this.multiple, 'is-searchable': this.searchable } }, hasSelected() { if (this.multiple) { return this.multipleItems.length > 0 } return Object.keys(this.singleItem).length > 0 }, hasDefaultSlot() { return !!this.$scopedSlots.default }, singleLabel() { if (this.multiple) { return } if (this.options.length > 0) { const item = this.options.filter( option => option.value === this.singleItem )[0] if (!item) { return } return item.label } return this.singleItem }, hasOptions() { return this.options.length > 0 } }, methods: { clear(e) { this.doSelectItem(this.multiple ? [] : {}) this.togglePlaceholderEl(true) }, getLabel(value) { if (this.hasOptions) { return this.options.filter(option => option.value === value)[0].label } }, handleMenuItemMouseOver(item, index) { this.highlightIdx = index }, handleMenuItemClick(item) { this.doSelectItem(item) }, doSelectItem(item) { const value = this.isOptionObject(item) ? item.value : item if (this.emitOnly) { this.$emit('select', value) this.toggleMenuEl(false) this.toggleValueEl(false) this.togglePlaceholderEl(true) return } if (this.multiple) { this.addItem(value) } else { this.singleItem = value } this.searchValue = '' this.toggleValueEl(true) this.toggleMenuEl(false) this.togglePlaceholderEl(false) this.updateValue(this.getUpdateValue()) }, addItem(newItem) { const hasDuplicate = this.multipleItems.some(item => { return newItem === item }) console.debug(hasDuplicate) if (!hasDuplicate) { this.multipleItems.push(newItem) } }, removeItem(item, index) { this.multipleItems.splice(index, 1) this.updateValue(this.getUpdateValue()) }, handleInputBlur() { this.searchValue = '' this.toggleValueEl(true) this.toggleMenuEl(false) // when nothing is selected, put back placeholder if (!this.singleItem) { this.togglePlaceholderEl(true) } }, handleInputInput(e) { if (this.searchValue) { if (!this.multiple) { this.toggleValueEl(false) } this.togglePlaceholderEl(false) } if (this.options.length > 0 && this.searchValue) { this.menuItems = this.options.filter( item => JSON.stringify(item) .toLowerCase() .indexOf(this.searchValue.toLowerCase()) > -1 ) } if (this.fetchFunction && this.searchValue) { this.fetchMenuItems.call(this, this.searchValue) } }, fetchMenuItems(searchValue) { this.loading = true this.fetchFunction(searchValue).then(data => { this.menuItems = data this.loading = false }) }, handleInputFocus(e) { this.toggleMenuEl(true) this.togglePlaceholderEl(false) }, handleControlClick(e) { if (this.disabled) { return } if (this.searchable) { this.$refs['input'].focus() } else { this.toggleMenuEl(true) this.togglePlaceholderEl(false) } }, toggleMenuEl(show) { this.showMenuEl = show }, toggleValueEl(show) { this.showValueEl = show }, togglePlaceholderEl(show) { this.showPlaceholderEl = show }, handleUpKey() { let idx = this.highlightIdx - 1 if (this.highlightIdx < 0 || idx < 0) { this.highlightIdx = this.menuItems.length - 1 } else { this.highlightIdx = idx } }, handleDownKey() { let idx = this.highlightIdx + 1 let length = this.menuItems.length if (this.highlightIdx > length || idx > length) { this.highlightIdx = 0 } else { this.highlightIdx = idx } }, handleEnterKey() { this.doSelectItem(this.menuItems[this.highlightIdx]) }, updateLocalValue() { // handle multiple if (this.multiple) { if (!Array.isArray(this.value)) { throw `v-model has to be Array for multiple, ${typeof this.value} given` } this.value.forEach(item => this.addItem(item)) } else { if (this.options.length > 0) { this.doSelectItem( this.menuItems.filter(item => item.value === this.value)[0] ) } if (this.fetchFunction) { if (!(this.value instanceof Object)) { throw 'fetchFunction cannot be used without `v-model` as object' } this.doSelectItem(this.value) } } }, getUpdateValue() { if (this.multiple) { return this.multipleItems } return this.isOptionObject(this.singleItem) ? this.singleItem.value : this.singleItem }, updateValue(value) { this.$emit('input', value) this.togglePlaceholderEl(!this.hasSelected) }, isOptionObject(object) { return object.hasOwnProperty('label') && object.hasOwnProperty('value') } }, mounted() { if (this.options) { this.menuItems = this.options } if (this.value && Object.keys(this.value).length > 0) { this.updateLocalValue() } this.togglePlaceholderEl(!this.hasSelected) this.toggleMenuEl(false) this.$nextTick(() => { this.parentContainer = getClosestVueParent(this.$parent, 'usj-input-container') if (!this.parentContainer) { this.$destroy() throw new Error('You should wrap the usj-advance-select in a usj-input-container') } this.setParentDisabled() this.setParentRequired() this.setParentPlaceholder() this.handleMaxLength() this.updateValues() }) // this.$nextTick(() => { // inputContainerCmp = getClosestVueParent(this.$parent, 'usj-input-container') // }) vm = this }, created() { this.fetchMenuItems = debounce(this.fetchMenuItems, 1000) } } export default usjAdvanceSelect