import {
|
debounce,
|
throttle
|
} from 'uni-shared'
|
import emitter from './emitter'
|
import keyboard from './keyboard'
|
import interact from './interact'
|
|
UniViewJSBridge.subscribe('getSelectedTextRange', function ({ pageId, callbackId }) {
|
const activeElement = document.activeElement
|
const tagName = activeElement.tagName.toLowerCase()
|
const tagNames = ['input', 'textarea']
|
const data = {}
|
if (tagNames.includes(tagName)) {
|
data.errMsg = 'getSelectedTextRange:ok'
|
data.start = activeElement.selectionStart
|
data.end = activeElement.selectionEnd
|
} else {
|
data.errMsg = 'getSelectedTextRange:fail no focused'
|
}
|
UniViewJSBridge.publishHandler('onGetSelectedTextRange', {
|
callbackId,
|
data
|
}, pageId)
|
})
|
|
// App 延迟获取焦点
|
const FOCUS_DELAY = 200
|
let startTime
|
|
export default {
|
name: 'Field',
|
mixins: [emitter, keyboard, interact],
|
model: {
|
prop: 'value',
|
event: 'update:value'
|
},
|
props: {
|
value: {
|
type: [String, Number],
|
default: ''
|
},
|
/**
|
* 已废弃属性,用于历史兼容
|
*/
|
autoFocus: {
|
type: [Boolean, String],
|
default: false
|
},
|
focus: {
|
type: [Boolean, String],
|
default: false
|
},
|
cursor: {
|
type: [Number, String],
|
default: -1
|
},
|
selectionStart: {
|
type: [Number, String],
|
default: -1
|
},
|
selectionEnd: {
|
type: [Number, String],
|
default: -1
|
},
|
confirmHold: {
|
type: Boolean,
|
default: false
|
},
|
ignoreCompositionEvent: {
|
type: Boolean,
|
default: true
|
}
|
},
|
data () {
|
return {
|
composing: false,
|
valueSync: this._getValueString(this.value, this.type),
|
focusSync: this.focus,
|
// Safari 14 以上修正禁用状态颜色
|
fixColor: String(navigator.vendor).indexOf('Apple') === 0 && CSS.supports('image-orientation:from-image')
|
}
|
},
|
watch: {
|
focus (val) {
|
if (val) {
|
this._focus()
|
} else {
|
this._blur()
|
}
|
},
|
focusSync (val) {
|
this.$emit('update:focus', val)
|
},
|
cursorNumber () {
|
this._checkCursor()
|
},
|
selectionStartNumber () {
|
this._checkSelection()
|
},
|
selectionEndNumber () {
|
this._checkSelection()
|
}
|
},
|
computed: {
|
needFocus () {
|
return this.autoFocus || this.focus
|
},
|
cursorNumber () {
|
var cursor = Number(this.cursor)
|
return isNaN(cursor) ? -1 : cursor
|
},
|
selectionStartNumber () {
|
var selectionStart = Number(this.selectionStart)
|
return isNaN(selectionStart) ? -1 : selectionStart
|
},
|
selectionEndNumber () {
|
var selectionEnd = Number(this.selectionEnd)
|
return isNaN(selectionEnd) ? -1 : selectionEnd
|
}
|
},
|
created () {
|
const valueChange = this.__valueChange = debounce((val) => {
|
this.valueSync = this._getValueString(val, this.type)
|
}, 100)
|
this.$watch('value', valueChange)
|
this.__triggerInput = throttle(($event, detail) => {
|
this.__valueChange.cancel()
|
this.$emit('update:value', detail.value)
|
this.$trigger('input', $event, detail)
|
}, 100)
|
this.$triggerInput = ($event, detail, force) => {
|
this.__valueChange.cancel()
|
this.__triggerInput($event, detail)
|
if (force) {
|
this.__triggerInput.flush()
|
}
|
}
|
},
|
beforeDestroy () {
|
this.__valueChange.cancel()
|
this.__triggerInput.cancel()
|
},
|
directives: {
|
field: {
|
inserted (el, binding, vnode) {
|
vnode.context._initField(el)
|
}
|
}
|
},
|
methods: {
|
_getValueString (value, type) {
|
if (type === 'number' && isNaN(Number(value))) {
|
value = ''
|
}
|
return value === null ? '' : String(value)
|
},
|
_initField (el) {
|
this._field = el
|
startTime = startTime || Date.now()
|
if (this.needFocus) {
|
setTimeout(() => {
|
this._focus()
|
})
|
}
|
},
|
_focus () {
|
if (!this.needFocus) {
|
return
|
}
|
const field = this._field
|
if (!field || (__PLATFORM__ === 'app-plus' && !window.plus)) {
|
setTimeout(this._focus.bind(this), 100)
|
return
|
}
|
if (__PLATFORM__ === 'h5') {
|
field.focus()
|
} else {
|
const timeout = FOCUS_DELAY - (Date.now() - startTime)
|
if (timeout > 0) {
|
setTimeout(this._focus.bind(this), timeout)
|
return
|
}
|
field.focus()
|
// 无用户交互的 webview 需主动显示键盘(安卓)
|
if (!this.userInteract) {
|
plus.key.showSoftKeybord()
|
}
|
}
|
},
|
_blur () {
|
const field = this._field
|
field && field.blur()
|
},
|
_onFocus ($event) {
|
this.focusSync = true
|
this.$trigger('focus', $event, {
|
value: this.valueSync
|
})
|
// 从 watch:focusSync 中移出到这里。在watcher中如果focus初始值为ture,则不会执行以下逻辑
|
this._checkSelection()
|
this._checkCursor()
|
},
|
_onBlur ($event) {
|
// iOS 输入法 compositionend 事件可能晚于 blur
|
if (this.composing) {
|
this.composing = false
|
this._onInput($event, true)
|
}
|
this.focusSync = false
|
const field = $event.target
|
let cursor
|
if (field.type === 'number') {
|
field.type = 'text'
|
cursor = field.selectionEnd
|
field.type = 'number'
|
} else {
|
cursor = field.selectionEnd
|
}
|
this.$trigger('blur', $event, {
|
value: this.valueSync,
|
cursor
|
})
|
},
|
_checkSelection () {
|
const field = this._field
|
if (this.focusSync && this.selectionStartNumber > -1 && this.selectionEndNumber > -1 && field.type !== 'number') {
|
field.selectionStart = this.selectionStartNumber
|
field.selectionEnd = this.selectionEndNumber
|
}
|
},
|
_checkCursor () {
|
const field = this._field
|
if (this.focusSync && this.selectionStartNumber < 0 && this.selectionEndNumber < 0 && this.cursorNumber > -1 && field.type !== 'number') {
|
field.selectionEnd = field.selectionStart = this.cursorNumber
|
}
|
}
|
}
|
}
|