<script> import { debounce, deepExtend, includes } from '../utils' import { MIN_INPUT_WIDTH, KEY_CODES, INPUT_DEBOUNCE_DELAY } from '../constants' const keysThatRequireMenuBeingOpen = [ KEY_CODES.ENTER, KEY_CODES.END, KEY_CODES.HOME, KEY_CODES.ARROW_LEFT, KEY_CODES.ARROW_UP, KEY_CODES.ARROW_RIGHT, KEY_CODES.ARROW_DOWN, ] export default { name: 'vue-treeselect--input', inject: [ 'instance' ], data: () => ({ inputWidth: MIN_INPUT_WIDTH, value: '', }), computed: { needAutoSize() { const { instance } = this return ( instance.searchable && !instance.disabled && instance.multiple ) }, inputStyle() { return { width: this.needAutoSize ? `${this.inputWidth}px` : null, } }, }, watch: { 'instance.trigger.searchQuery'(newValue) { this.value = newValue }, value() { // istanbul ignore else if (this.needAutoSize) this.$nextTick(this.updateInputWidth) }, }, created() { this.debouncedCallback = debounce( this.updateSearchQuery, INPUT_DEBOUNCE_DELAY, { leading: true, trailing: true }, ) }, methods: { clear() { this.onInput({ target: { value: '' }, }) }, focus() { const { instance } = this if (!instance.disabled) { this.$refs.input && this.$refs.input.focus() } }, blur() { this.$refs.input && this.$refs.input.blur() }, onFocus() { const { instance } = this instance.trigger.isFocused = true // istanbul ignore else if (instance.openOnFocus) instance.openMenu() }, onBlur() { const { instance } = this const menu = instance.getMenu() // #15 // istanbul ignore next if (menu && document.activeElement === menu) { return this.focus() } instance.trigger.isFocused = false instance.closeMenu() }, onInput(evt) { const { value } = evt.target this.value = value if (value) { this.debouncedCallback() } else { this.debouncedCallback.cancel() this.updateSearchQuery() } }, // 鐢� keyUp 浜嬩欢瀛樺湪涓€涓棶棰橈紝鍒犻櫎杈撳叆妗嗘渶鍚庝竴涓瓧绗︿篃浼氬鑷村彇娑堥€変腑鏈€鍚庝竴椤� onKeyDown(evt) { const { instance } = this // https://css-tricks.com/snippets/javascript/javascript-keycodes/ // https://stackoverflow.com/questions/4471582/javascript-keycode-vs-which const key = 'which' in evt ? evt.which : /* istanbul ignore next */ evt.keyCode if (evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) return if (!instance.menu.isOpen && includes(keysThatRequireMenuBeingOpen, key)) { evt.preventDefault() return instance.openMenu() } switch (key) { case KEY_CODES.BACKSPACE: { if (instance.backspaceRemoves && !this.value.length) { instance.removeLastValue() } break } case KEY_CODES.ENTER: { evt.preventDefault() if (instance.menu.current === null) return const current = instance.getNode(instance.menu.current) if (current.isBranch && instance.disableBranchNodes) return instance.select(current) break } case KEY_CODES.ESCAPE: { if (this.value.length) { this.clear() } else if (instance.menu.isOpen) { instance.closeMenu() } break } case KEY_CODES.END: { evt.preventDefault() instance.highlightLastOption() break } case KEY_CODES.HOME: { evt.preventDefault() instance.highlightFirstOption() break } case KEY_CODES.ARROW_LEFT: { const current = instance.getNode(instance.menu.current) if (current.isBranch && instance.shouldExpand(current)) { evt.preventDefault() instance.toggleExpanded(current) } else if (!current.isRootNode && (current.isLeaf || (current.isBranch && !(instance.shouldExpand(current))))) { evt.preventDefault() instance.setCurrentHighlightedOption(current.parentNode) } break } case KEY_CODES.ARROW_UP: { evt.preventDefault() instance.highlightPrevOption() break } case KEY_CODES.ARROW_RIGHT: { const current = instance.getNode(instance.menu.current) if (current.isBranch && !instance.shouldExpand(current)) { evt.preventDefault() instance.toggleExpanded(current) } break } case KEY_CODES.ARROW_DOWN: { evt.preventDefault() instance.highlightNextOption() break } case KEY_CODES.DELETE: { if (instance.deleteRemoves && !this.value.length) { instance.removeLastValue() } break } default: { // istanbul ignore else instance.openMenu() } } }, onMouseDown(evt) { // istanbul ignore next if (this.value.length) { // Prevent it from bubbling to the top level and triggering `preventDefault()` // to make the textbox unselectable evt.stopPropagation() } }, renderInputContainer() { const { instance } = this const props = {} const children = [] if (instance.searchable && !instance.disabled) { children.push(this.renderInput()) if (this.needAutoSize) children.push(this.renderSizer()) } if (!instance.searchable) { deepExtend(props, { on: { focus: this.onFocus, blur: this.onBlur, keydown: this.onKeyDown, }, ref: 'input', }) } if (!instance.searchable && !instance.disabled) { deepExtend(props, { attrs: { tabIndex: instance.tabIndex, }, }) } return ( <div class="vue-treeselect__input-container" {...props}> {children} </div> ) }, renderInput() { const { instance } = this return ( <input ref="input" class="vue-treeselect__input" type="text" autocomplete="off" tabIndex={instance.tabIndex} required={instance.required && !instance.hasValue} value={this.value} style={this.inputStyle} onFocus={this.onFocus} onInput={this.onInput} onBlur={this.onBlur} onKeydown={this.onKeyDown} onMousedown={this.onMouseDown} /> ) }, renderSizer() { return ( <div ref="sizer" class="vue-treeselect__sizer">{this.value}</div> ) }, updateInputWidth() { this.inputWidth = Math.max( MIN_INPUT_WIDTH, this.$refs.sizer.scrollWidth + 15, ) }, updateSearchQuery() { const { instance } = this instance.trigger.searchQuery = this.value }, }, render() { return this.renderInputContainer() }, } </script>