fineui是帆软报表和BI产品线所使用的前端框架。
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

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;
}
}