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输入框在不可见下不能focus"); } if (!this._isEditing === true) { this.element.focus(); this.selectAll(); } } blur() { if (!this.element.is(":visible")) { throw new Error("input输入框在不可见下不能blur"); } if (this._isEditing === true) { this.element.blur(); this._blurDebounce(); } } selectAll() { if (!this.element.is(":visible")) { throw new Error("input输入框在不可见下不能select"); } 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; } }