forked from fanruan/fineui
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
12 KiB
365 lines
12 KiB
import { Single } from "../0.single"; |
|
import { |
|
shortcut, |
|
Controller, |
|
extend, |
|
debounce, |
|
bind, |
|
isNull, |
|
isEmptyString, |
|
isKey, |
|
delay, |
|
trim, |
|
isEqual, |
|
nextTick, |
|
isEndWithBlank, |
|
emptyFn, |
|
EVENT_RESPONSE_TIME, |
|
KeyCode, |
|
Events |
|
} from "@/core"; |
|
|
|
/** |
|
* guy |
|
* @class Input 一个button和一行数 组成的一行listitem |
|
* @extends Single |
|
* @type {*|void|Object} |
|
*/ |
|
|
|
@shortcut() |
|
export class Input extends Single { |
|
static xtype = "bi.input"; |
|
|
|
static EVENT_CHANGE = "EVENT_CHANGE"; |
|
|
|
static EVENT_FOCUS = "EVENT_FOCUS"; |
|
static EVENT_CLICK = "EVENT_CLICK"; |
|
static EVENT_BLUR = "EVENT_BLUR"; |
|
static EVENT_KEY_DOWN = "EVENT_KEY_DOWN"; |
|
static EVENT_QUICK_DOWN = "EVENT_QUICK_DOWN"; |
|
static EVENT_SPACE = "EVENT_SPACE"; |
|
static EVENT_BACKSPACE = "EVENT_BACKSPACE"; |
|
|
|
static EVENT_START = "EVENT_START"; |
|
static EVENT_PAUSE = "EVENT_PAUSE"; |
|
static EVENT_STOP = "EVENT_STOP"; |
|
static EVENT_CHANGE_CONFIRM = "EVENT_CHANGE_CONFIRM"; |
|
static EVENT_CONFIRM = "EVENT_CONFIRM"; |
|
static EVENT_REMOVE = "EVENT_REMOVE"; |
|
static EVENT_EMPTY = "EVENT_EMPTY"; |
|
static EVENT_VALID = "EVENT_VALID"; |
|
static EVENT_ERROR = "EVENT_ERROR"; |
|
static EVENT_ENTER = "EVENT_ENTER"; |
|
static EVENT_RESTRICT = "EVENT_RESTRICT"; |
|
|
|
_defaultConfig() { |
|
const conf = super._defaultConfig(...arguments); |
|
|
|
return extend(conf, { |
|
baseCls: `${conf.baseCls || ""} bi-input display-block overflow-dot`, |
|
tagName: "input", |
|
validationChecker: emptyFn, |
|
quitChecker: emptyFn, // 按确定键能否退出编辑 |
|
allowBlank: false, |
|
}); |
|
} |
|
|
|
render() { |
|
let ctrlKey = false; |
|
let keyCode = null; |
|
let inputEventValid = false; |
|
const _keydown = debounce(keyCode => { |
|
this.onKeyDown(keyCode, ctrlKey); |
|
this._keydown_ = false; |
|
}, EVENT_RESPONSE_TIME); |
|
const _clk = debounce(bind(this._click, this), EVENT_RESPONSE_TIME, { |
|
leading: true, |
|
trailing: false, |
|
}); |
|
this._focusDebounce = debounce(bind(this._focus, this), EVENT_RESPONSE_TIME, { |
|
leading: true, |
|
trailing: false, |
|
}); |
|
this._blurDebounce = debounce(bind(this._blur, this), EVENT_RESPONSE_TIME, { |
|
leading: true, |
|
trailing: false, |
|
}); |
|
this.element |
|
.keydown(e => { |
|
inputEventValid = false; |
|
ctrlKey = e.ctrlKey || e.metaKey; // mac的cmd支持一下 |
|
keyCode = e.keyCode; |
|
this.fireEvent(Input.EVENT_QUICK_DOWN, e); |
|
}) |
|
.keyup(e => { |
|
keyCode = null; |
|
if (!(inputEventValid && e.keyCode === KeyCode.ENTER)) { |
|
this._keydown_ = true; |
|
_keydown(e.keyCode); |
|
} |
|
}) |
|
.on("input propertychange", e => { |
|
// 输入内容全选并直接删光,如果按键没放开就失去焦点不会触发keyup,被focusout覆盖了 |
|
// 其中propertychange在元素属性发生改变的时候就会触发 是为了兼容IE8 |
|
// 通过keyCode判断会漏掉输入法点击输入(右键粘贴暂缓) |
|
const originalEvent = e.originalEvent; |
|
if (isNull(originalEvent.propertyName) || originalEvent.propertyName === "value") { |
|
inputEventValid = true; |
|
this._keydown_ = true; |
|
_keydown(keyCode); |
|
keyCode = null; |
|
} |
|
}) |
|
.click(e => { |
|
e.stopPropagation(); |
|
_clk(); |
|
}) |
|
.mousedown(e => { |
|
this.element.val(this.element.val()); |
|
}) |
|
.focus(e => { |
|
// 可以不用冒泡 |
|
this._focusDebounce(); |
|
}) |
|
.blur(e => { |
|
// DEC-14919 IE11在浏览器重新获得焦点之后会先触发focusout再触发focus,要保持先获得焦点再失去焦点的顺序不变,因此采用blur |
|
this._blurDebounce(); |
|
}); |
|
if (isKey(this.options.value) || isEmptyString(this.options.value)) { |
|
this.setValue(this.options.value); |
|
} |
|
} |
|
|
|
_focus() { |
|
this.element.addClass("bi-input-focus"); |
|
this._checkValidationOnValueChange(); |
|
this._isEditing = true; |
|
if (this.getValue() === "") { |
|
this.fireEvent(Controller.EVENT_CHANGE, Events.EMPTY, this.getValue(), this); |
|
this.fireEvent(Input.EVENT_EMPTY); |
|
} |
|
this.fireEvent(Input.EVENT_FOCUS); |
|
} |
|
|
|
_blur() { |
|
const blur = () => { |
|
if (!this.isValid() && this.options.quitChecker.apply(this, [trim(this.getValue())]) !== false) { |
|
this.element.val(this._lastValidValue ? this._lastValidValue : ""); |
|
this._checkValidationOnValueChange(); |
|
this._defaultState(); |
|
} |
|
this.element.removeClass("bi-input-focus"); |
|
this._isEditing = false; |
|
this._start = false; |
|
if (this.isValid()) { |
|
const lastValidValue = this._lastValidValue; |
|
this._lastValidValue = this.getValue(); |
|
this.fireEvent(Controller.EVENT_CHANGE, Events.CONFIRM, this.getValue(), this); |
|
this.fireEvent(Input.EVENT_CONFIRM); |
|
if (this._lastValidValue !== lastValidValue) { |
|
this.fireEvent(Input.EVENT_CHANGE_CONFIRM); |
|
} |
|
} |
|
this.fireEvent(Input.EVENT_BLUR); |
|
}; |
|
|
|
if (this._keydown_ === true) { |
|
delay(blur, EVENT_RESPONSE_TIME); |
|
} else { |
|
blur(); |
|
} |
|
} |
|
|
|
_click() { |
|
if (this._isEditing !== true) { |
|
this.selectAll(); |
|
this.fireEvent(Input.EVENT_CLICK); |
|
} |
|
} |
|
|
|
onClick() { |
|
this._click(); |
|
} |
|
|
|
onKeyDown(keyCode, ctrlKey) { |
|
if (!this.isValid() || trim(this._lastChangedValue) !== trim(this.getValue())) { |
|
this._checkValidationOnValueChange(); |
|
} |
|
if (this.isValid() && trim(this.getValue()) !== "") { |
|
if ( |
|
(trim(this.getValue()) !== this._lastValue && |
|
(!this._start || isNull(this._lastValue) || this._lastValue === "")) || |
|
(this._pause === true && !/(\s|\u00A0)$/.test(this.getValue())) |
|
) { |
|
this._start = true; |
|
this._pause = false; |
|
this.fireEvent(Controller.EVENT_CHANGE, Events.STARTEDIT, this.getValue(), this); |
|
this.fireEvent(Input.EVENT_START); |
|
} |
|
} |
|
if (isEqual(keyCode, KeyCode.ENTER)) { |
|
if (this.isValid() || this.options.quitChecker.apply(this, [trim(this.getValue())]) !== false) { |
|
this.blur(); |
|
this.fireEvent(Input.EVENT_ENTER); |
|
} else { |
|
this.fireEvent(Input.EVENT_RESTRICT); |
|
} |
|
} |
|
if (isEqual(keyCode, KeyCode.SPACE)) { |
|
this.fireEvent(Input.EVENT_SPACE); |
|
} |
|
if (isEqual(keyCode, KeyCode.BACKSPACE) && this._lastValue === "") { |
|
this.fireEvent(Input.EVENT_REMOVE); |
|
} |
|
if (isEqual(keyCode, KeyCode.BACKSPACE) || isEqual(keyCode, KeyCode.DELETE)) { |
|
this.fireEvent(Input.EVENT_BACKSPACE); |
|
} |
|
this.fireEvent(Input.EVENT_KEY_DOWN, arguments); |
|
|
|
// _valueChange中会更新_lastValue, 这边缓存用以后续STOP事件服务 |
|
const lastValue = this._lastValue; |
|
if (trim(this.getValue()) !== trim(this._lastValue || "")) { |
|
this._valueChange(); |
|
} |
|
if (isEndWithBlank(this.getValue())) { |
|
this._pause = true; |
|
this.fireEvent(Controller.EVENT_CHANGE, Events.PAUSE, "", this); |
|
this.fireEvent(Input.EVENT_PAUSE); |
|
this._defaultState(); |
|
} else if ( |
|
(keyCode === KeyCode.BACKSPACE || keyCode === KeyCode.DELETE) && |
|
trim(this.getValue()) === "" && |
|
lastValue !== null && |
|
trim(lastValue) !== "" |
|
) { |
|
this.fireEvent(Controller.EVENT_CHANGE, Events.STOPEDIT, this.getValue(), this); |
|
this.fireEvent(Input.EVENT_STOP); |
|
} |
|
} |
|
|
|
// 初始状态 |
|
_defaultState() { |
|
if (this.getValue() === "") { |
|
this.fireEvent(Controller.EVENT_CHANGE, Events.EMPTY, this.getValue(), this); |
|
this.fireEvent(Input.EVENT_EMPTY); |
|
} |
|
this._lastValue = this.getValue(); |
|
this._lastSubmitValue = null; |
|
} |
|
|
|
_valueChange() { |
|
if (this.isValid() && trim(this.getValue()) !== this._lastSubmitValue) { |
|
this.fireEvent(Controller.EVENT_CHANGE, Events.CHANGE, this.getValue(), this); |
|
this.fireEvent(Input.EVENT_CHANGE); |
|
this._lastSubmitValue = trim(this.getValue()); |
|
} |
|
if (this.getValue() === "") { |
|
this.fireEvent(Controller.EVENT_CHANGE, Events.EMPTY, this.getValue(), this); |
|
this.fireEvent(Input.EVENT_EMPTY); |
|
} |
|
this._lastValue = this.getValue(); |
|
} |
|
|
|
_checkValidationOnValueChange(callback) { |
|
const { allowBlank, validationChecker } = this.options; |
|
const v = this.getValue(); |
|
if (allowBlank === true && trim(v) === "") { |
|
this.setValid(true); |
|
callback && callback(); |
|
|
|
return; |
|
} |
|
if (trim(v) === "") { |
|
this.setValid(false); |
|
callback && callback(); |
|
|
|
return; |
|
} |
|
const checker = validationChecker.apply(this, [trim(v)]); |
|
if (checker instanceof Promise) { |
|
checker.then(validate => { |
|
this.setValid(validate !== false); |
|
callback && callback(); |
|
}); |
|
} else { |
|
this.setValid(checker !== false); |
|
callback && callback(); |
|
} |
|
} |
|
|
|
focus() { |
|
if (!this.element.is(":visible")) { |
|
throw new Error("input cannot be focus when it's invisible"); |
|
} |
|
if (!this._isEditing === true) { |
|
this.element.focus(); |
|
this.selectAll(); |
|
} |
|
} |
|
|
|
blur() { |
|
if (!this.element.is(":visible")) { |
|
throw new Error("input cannot be blur when it's invisible"); |
|
} |
|
if (this._isEditing === true) { |
|
this.element.blur(); |
|
this._blurDebounce(); |
|
} |
|
} |
|
|
|
selectAll() { |
|
if (!this.element.is(":visible")) { |
|
throw new Error("input cannot be select when it's invisible"); |
|
} |
|
this.element.select(); |
|
this._isEditing = true; |
|
} |
|
|
|
setValue(textValue) { |
|
this.element.val(textValue); |
|
nextTick(() => { |
|
this._checkValidationOnValueChange(() => { |
|
this._defaultState(); |
|
if (this.isValid()) { |
|
this._lastValidValue = this._lastSubmitValue = this.getValue(); |
|
} |
|
}); |
|
}); |
|
} |
|
|
|
getValue() { |
|
return this.element.val() || ""; |
|
} |
|
|
|
isEditing() { |
|
return this._isEditing; |
|
} |
|
|
|
getLastValidValue() { |
|
return this._lastValidValue; |
|
} |
|
|
|
getLastChangedValue() { |
|
return this._lastChangedValue; |
|
} |
|
|
|
_setValid() { |
|
super._setValid(...arguments); |
|
if (this.isValid()) { |
|
this._lastChangedValue = this.getValue(); |
|
this.element.removeClass("bi-input-error"); |
|
this.fireEvent(Input.EVENT_VALID, trim(this.getValue()), this); |
|
} else { |
|
if (this._lastChangedValue === this.getValue()) { |
|
this._lastChangedValue = null; |
|
} |
|
this.element.addClass("bi-input-error"); |
|
this.fireEvent(Input.EVENT_ERROR, trim(this.getValue()), this); |
|
} |
|
} |
|
|
|
_setEnable(b) { |
|
super._setEnable([b]); |
|
this.element[0].disabled = !b; |
|
} |
|
}
|
|
|