| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- import BaseFoundation, { DefaultAdapter } from '../base/foundation';
- import keyCode from '../utils/keyCode';
- import {
- isString,
- isNumber,
- isFunction,
- isUndefined
- } from 'lodash';
- import getSplitedArray from './utils/getSplitedArray';
- import isEnterPress from '../utils/isEnterPress';
- import arrayMove from '../utils/arrayMove';
- export type TagInputChangeEvent = any;
- export type TagInputCursorEvent = any;
- export type TagInputKeyboardEvent = any;
- export type TagInputMouseEvent = any;
- export interface OnSortEndProps {
- oldIndex: number;
- newIndex: number
- }
- export interface TagInputAdapter extends DefaultAdapter {
- setInputValue: (inputValue: string) => void;
- setTagsArray: (tagsArray: string[]) => void;
- setFocusing: (focusing: boolean) => void;
- toggleFocusing(focused: boolean): void;
- setHovering: (hovering: boolean) => void;
- setActive: (active: boolean) => void;
- setEntering: (entering: boolean) => void;
- getClickOutsideHandler: () => any;
- registerClickOutsideHandler: (cb: any) => void;
- unregisterClickOutsideHandler: () => void;
- notifyBlur: (e: TagInputCursorEvent) => void;
- notifyFocus: (e: TagInputCursorEvent) => void;
- notifyInputChange: (v: string, e: TagInputChangeEvent) => void;
- notifyTagChange: (v: string[]) => void;
- notifyTagAdd: (v: string[]) => void;
- notifyTagRemove: (v: string, idx: number) => void;
- notifyKeyDown: (e: TagInputMouseEvent) => void
- }
- class TagInputFoundation extends BaseFoundation<TagInputAdapter> {
- constructor(adapter: TagInputAdapter) {
- super({ ...adapter });
- }
- /**
- * handler of input change
- */
- handleInputChange = (e: TagInputChangeEvent) => {
- const { value } = e.target;
- const { entering } = this.getStates();
- if (entering) {
- // 如果处于输入法输入中,则先不检查输入是否有效,直接更新到inputValue,
- // 因为对于输入法输入中而言,此时更新到 inputValue 的不是最后的结果,比如对于中文,此时 inputValue 中的内容是拼音
- // 当输入法输入结束后,将在 handleInputCompositionEnd 中判断输入是否有效,处理结果
- // If it is composition session, it does not check whether the input is valid, and directly updates to inputValue,
- // Because for composition input, what is updated to inputValue at this time is not the final result.
- // For example, for Chinese, the content in inputValue is pinyin at this time
- // When the composition input is finished, it will be judged whether the input is valid in handleInputCompositionEnd and the result will be processed
- this._onInputChange(value, e);
- } else {
- this._checkInputChangeValid(value) && this._onInputChange(value, e);
- }
- };
- handleInputCompositionStart = (e: any) => {
- this._adapter.setEntering(true);
- }
- handleInputCompositionEnd = (e: any) => {
- this._adapter.setEntering(false);
- const { value } = e.target;
- const {
- maxLength,
- onInputExceed,
- separator
- } = this.getProps();
- let allowChange = true;
- const { inputValue } = this.getStates();
- if (isNumber(maxLength)) {
- const inputArr = getSplitedArray(inputValue, separator);
- let index = 0;
- for (; index < inputArr.length; index++) {
- if (inputArr[index].length > maxLength) {
- allowChange = false;
- isFunction(onInputExceed) && onInputExceed(value);
- break;
- }
- }
- if (!allowChange) {
- const newInputArr = inputArr.slice(0, index);
- if (index < inputArr.length) {
- newInputArr.push(inputArr[index].slice(0, maxLength));
- }
- this._adapter.setInputValue(newInputArr.join(separator));
- }
- }
- }
- /**
- * check whether the input change is legal
- */
- _checkInputChangeValid = (value: string) => {
- // e.target.value legitimacy judgment needs to be based on this.state.input Value
- const {
- maxLength,
- onInputExceed,
- separator
- } = this._adapter.getProps();
- const { inputValue } = this._adapter.getStates();
- let allowChange = true;
- if (isNumber(maxLength)) {
- const valueArr = getSplitedArray(value, separator);
- const inputArr = getSplitedArray(inputValue, separator);
- const maxLen = Math.max(valueArr.length, inputArr.length);
- for (let i = 0; i < maxLen; i++) {
- // When the input length is increasing
- // eslint-disable-next-line max-len
- if (!isUndefined(valueArr[i]) && (isUndefined(inputArr[i]) || valueArr[i].length > inputArr[i].length)) {
- // When the input length exceeds maxLength
- // eslint-disable-next-line max-depth
- if (valueArr[i].length > maxLength) {
- allowChange = false;
- isFunction(onInputExceed) && onInputExceed(value);
- break;
- }
- }
- }
- }
- return allowChange;
- };
- /**
- * Input event handler when onKeyDown is triggered
- */
- handleKeyDown = (e: TagInputKeyboardEvent) => {
- const {
- inputValue,
- tagsArray
- } = this._adapter.getStates();
- const code = e.keyCode;
- if (code === keyCode.ENTER) {
- e.preventDefault(); // prevent trigger submit when using in form
- if (inputValue !== '') {
- this._handleAddTags(e);
- }
- }
- const { length } = tagsArray;
- if (code === keyCode.BACKSPACE && inputValue === '' && length > 0) {
- const newTagList = tagsArray.slice(0, length - 1);
- const removedTag = tagsArray[length - 1];
- this._onRemove(newTagList, removedTag, length - 1);
- }
- this._adapter.notifyKeyDown(e);
- };
- _handleAddTags(e: TagInputChangeEvent) {
- const {
- separator,
- max,
- onExceed,
- allowDuplicates
- } = this._adapter.getProps();
- const {
- inputValue,
- tagsArray
- } = this._adapter.getStates();
- let addTags = getSplitedArray(inputValue, separator);
- addTags = addTags.filter((item, idx) => {
- // If allowDuplicates is false, then filter duplicates
- if (!allowDuplicates) {
- if (tagsArray.includes(item) || addTags.indexOf(item) !== idx) {
- return false;
- }
- }
- // Filter empty strings and pure space strings in new items
- return isString(item) && item.trim() !== '';
- });
- let newTagList = tagsArray.concat(addTags);
- if (isNumber(max) && newTagList.length > max) {
- isFunction(onExceed) && onExceed(newTagList);
- newTagList = newTagList.slice(0, max);
- addTags = addTags.slice(0, max - tagsArray.length);
- }
- if (addTags.length > 0) {
- this._onAdd(newTagList, addTags);
- }
- this._onInputChange('', e);
- }
- handleInputBlur(e: TagInputCursorEvent) {
- const { addOnBlur } = this._adapter.getProps();
- if (addOnBlur === true) {
- this._handleAddTags(e);
- }
- this._adapter.setFocusing(false);
- this._adapter.notifyBlur(e);
- }
- handleInputFocus(e: TagInputCursorEvent) {
- this._adapter.setFocusing(true);
- this._adapter.notifyFocus(e);
- }
- /**
- * A11y: simulate clear button click
- */
- /* istanbul ignore next */
- handleClearEnterPress(e: TagInputKeyboardEvent) {
- if (isEnterPress(e)) {
- this.handleClearBtn(e);
- }
- }
- handleClearBtn(e: TagInputMouseEvent) {
- const { inputValue, tagsArray } = this._adapter.getStates();
- if (tagsArray.length > 0) {
- this._adapter.setTagsArray([]);
- this._adapter.notifyTagChange([]);
- }
- if (inputValue.length > 0) {
- this._onInputChange('', e);
- }
- // Prevent event propagate to TagInput outermost div
- e.stopPropagation();
- }
- handleTagClose(index: number) {
- const { tagsArray } = this._adapter.getStates();
- const newTagList = [...tagsArray];
- newTagList.splice(index, 1);
- const removedTag = tagsArray[index];
- this._onRemove(newTagList, removedTag, index);
- }
- handleInputMouseEnter() {
- this._adapter.setHovering(true);
- }
- handleInputMouseLeave() {
- this._adapter.setHovering(false);
- }
- handleClick(e?: any) {
- const { disabled } = this.getProps();
- if (disabled) {
- return;
- }
- const clickOutsideHandler = this._adapter.getClickOutsideHandler();
- if (!clickOutsideHandler) {
- this._adapter.setActive(true);
- this._adapter.registerClickOutsideHandler(e => this.clickOutsideCallBack());
- }
- }
- clickOutsideCallBack() {
- this._adapter.unregisterClickOutsideHandler();
- this._adapter.setActive(false);
- }
- handleClickPrefixOrSuffix(e: any) {
- const { disabled } = this._adapter.getProps();
- const { isFocus } = this._adapter.getStates();
- if (!disabled && !isFocus) {
- this._adapter.toggleFocusing(true);
- }
- }
- handlePreventMouseDown(e: any) {
- if (e && isFunction(e.preventDefault)) {
- e.preventDefault();
- }
- }
- /**
- * handler of delete tag
- */
- _onRemove(newTagList: string[], removedTags: string, index: number) {
- if (!this._isControlledComponent()) {
- this._adapter.setTagsArray(newTagList);
- }
- this._adapter.notifyTagChange(newTagList);
- this._adapter.notifyTagRemove(removedTags, index);
- }
- /**
- * handler of add tag
- */
- _onAdd(newTagList: string[], addTags: string[]) {
- if (!this._isControlledComponent()) {
- this._adapter.setTagsArray(newTagList);
- }
- this._adapter.notifyTagChange(newTagList);
- this._adapter.notifyTagAdd(addTags);
- }
- /**
- * handler of input change
- */
- _onInputChange(value: string, e: TagInputChangeEvent) {
- this._adapter.setInputValue(value);
- this._adapter.notifyInputChange(value, e);
- }
- handleSortEnd(callbackProps: OnSortEndProps) {
- const { oldIndex, newIndex } = callbackProps;
- const { tagsArray } = this.getStates();
- const newTagsArray = arrayMove(tagsArray, oldIndex, newIndex);
- if (!this._isControlledComponent()) {
- this._adapter.setTagsArray(newTagsArray);
- }
- this._adapter.notifyTagChange(newTagsArray);
- }
- }
- export default TagInputFoundation;
|