UNPKG

quasar

Version:

Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time

276 lines (226 loc) 6.67 kB
import Vue from 'vue' import QField from '../field/QField.js' import MaskMixin from '../../mixins/mask.js' import CompositionMixin from '../../mixins/composition.js' import debounce from '../../utils/debounce.js' import { stop } from '../../utils/event.js' export default Vue.extend({ name: 'QInput', mixins: [ QField, MaskMixin, CompositionMixin ], props: { value: { required: false }, type: { type: String, default: 'text' }, debounce: [String, Number], autogrow: Boolean, // makes a textarea inputClass: [Array, String, Object], inputStyle: [Array, String, Object] }, watch: { value (v) { if (this.hasMask === true) { if (this.stopValueWatcher === true) { this.stopValueWatcher = false return } this.__updateMaskValue(v) } else if (this.innerValue !== v) { this.innerValue = v if ( this.type === 'number' && this.hasOwnProperty('tempValue') === true ) { if (this.typedNumber === true) { this.typedNumber = false } else { delete this.tempValue } } } // textarea only this.autogrow === true && this.$nextTick(this.__adjustHeightDebounce) }, autogrow (autogrow) { // textarea only if (autogrow === true) { this.$nextTick(this.__adjustHeightDebounce) } // if it has a number of rows set respect it else if (this.$attrs.rows > 0 && this.$refs.input !== void 0) { const inp = this.$refs.input inp.style.height = 'auto' } }, dense () { this.autogrow === true && this.$nextTick(this.__adjustHeight) } }, data () { return { innerValue: this.__getInitialMaskedValue() } }, computed: { isTextarea () { return this.type === 'textarea' || this.autogrow === true }, fieldClass () { return `q-${this.isTextarea === true ? 'textarea' : 'input'}` + (this.autogrow === true ? ' q-textarea--autogrow' : '') } }, methods: { focus () { const el = document.activeElement if ( this.$refs.input !== void 0 && this.$refs.input !== el && // IE can have null document.activeElement (el === null || el.id !== this.targetUid) ) { this.$refs.input.focus() } }, select () { this.$refs.input !== void 0 && this.$refs.input.select() }, __onInput (e) { if (e && e.target && e.target.composing === true) { return } if (this.type === 'file') { this.$emit('input', e.target.files) return } const val = e.target.value if (this.hasMask === true) { this.__updateMaskValue(val) } else { this.__emitValue(val) } // we need to trigger it immediately too, // to avoid "flickering" this.autogrow === true && this.__adjustHeight() }, __emitValue (val, stopWatcher) { this.emitValueFn = () => { if ( this.type !== 'number' && this.hasOwnProperty('tempValue') === true ) { delete this.tempValue } if (this.value !== val) { stopWatcher === true && (this.stopValueWatcher = true) this.$emit('input', val) } this.emitValueFn = void 0 } if (this.type === 'number') { this.typedNumber = true this.tempValue = val } if (this.debounce !== void 0) { clearTimeout(this.emitTimer) this.tempValue = val this.emitTimer = setTimeout(this.emitValueFn, this.debounce) } else { this.emitValueFn() } }, // textarea only __adjustHeight () { const inp = this.$refs.input if (inp !== void 0) { const parentStyle = inp.parentNode.style // reset height of textarea to a small size to detect the real height // but keep the total control size the same parentStyle.marginBottom = (inp.scrollHeight - 1) + 'px' inp.style.height = '1px' inp.style.height = inp.scrollHeight + 'px' parentStyle.marginBottom = '' } }, __onChange (e) { this.__onComposition(e) clearTimeout(this.emitTimer) this.emitValueFn !== void 0 && this.emitValueFn() this.$emit('change', e) }, __onFinishEditing (e) { e !== void 0 && stop(e) clearTimeout(this.emitTimer) this.emitValueFn !== void 0 && this.emitValueFn() this.typedNumber = false this.stopValueWatcher = false delete this.tempValue this.type !== 'file' && this.$nextTick(() => { if (this.$refs.input !== void 0) { this.$refs.input.value = this.innerValue } }) }, __getControl (h) { const on = { ...this.$listeners, input: this.__onInput, // Safari < 10.2 & UIWebView doesn't fire compositionend when // switching focus before confirming composition choice // this also fixes the issue where some browsers e.g. iOS Chrome // fires "change" instead of "input" on autocomplete. change: this.__onChange, blur: this.__onFinishEditing, focus: stop } on.compositionstart = on.compositionupdate = on.compositionend = this.__onComposition if (this.hasMask === true) { on.keydown = this.__onMaskedKeydown } const attrs = { tabindex: 0, autofocus: this.autofocus, rows: this.type === 'textarea' ? 6 : void 0, 'aria-label': this.label, ...this.$attrs, id: this.targetUid, type: this.type, maxlength: this.maxlength, disabled: this.disable === true, readonly: this.readonly === true } if (this.autogrow === true) { attrs.rows = 1 } return h(this.isTextarea === true ? 'textarea' : 'input', { ref: 'input', staticClass: 'q-field__native q-placeholder', style: this.inputStyle, class: this.inputClass, attrs, on, domProps: this.type !== 'file' ? { value: this.hasOwnProperty('tempValue') === true ? this.tempValue : this.innerValue } : null }) } }, created () { // textarea only this.__adjustHeightDebounce = debounce(this.__adjustHeight, 100) }, mounted () { // textarea only this.autogrow === true && this.__adjustHeight() }, beforeDestroy () { this.__onFinishEditing() } })