UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

589 lines (588 loc) 19.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _isFunction2 = _interopRequireDefault(require("lodash/isFunction")); var _isUndefined2 = _interopRequireDefault(require("lodash/isUndefined")); var _isNull2 = _interopRequireDefault(require("lodash/isNull")); var _isArray2 = _interopRequireDefault(require("lodash/isArray")); var _isString2 = _interopRequireDefault(require("lodash/isString")); var _noop2 = _interopRequireDefault(require("lodash/noop")); var _react = _interopRequireDefault(require("react")); var _classnames = _interopRequireDefault(require("classnames")); var _propTypes = _interopRequireDefault(require("prop-types")); var _constants = require("@douyinfe/semi-foundation/lib/cjs/tagInput/constants"); require("@douyinfe/semi-foundation/lib/cjs/tagInput/tagInput.css"); var _foundation = _interopRequireDefault(require("@douyinfe/semi-foundation/lib/cjs/tagInput/foundation")); var _utils = require("../_utils"); var _baseComponent = _interopRequireDefault(require("../_base/baseComponent")); var _tag = _interopRequireDefault(require("../tag")); var _input = _interopRequireDefault(require("../input")); var _popover = _interopRequireDefault(require("../popover")); var _paragraph = _interopRequireDefault(require("../typography/paragraph")); var _semiIcons = require("@douyinfe/semi-icons"); var _sortable = require("../_sortable"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } var __rest = void 0 && (void 0).__rest || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; const prefixCls = _constants.cssClasses.PREFIX; function SortContainer(props) { return /*#__PURE__*/_react.default.createElement("div", Object.assign({ className: `${prefixCls}-sortable-list` }, props)); } class TagInput extends _baseComponent.default { constructor(props) { super(props); this.handleInputChange = e => { this.foundation.handleInputChange(e); }; this.handleKeyDown = e => { this.foundation.handleKeyDown(e); }; this.handleInputFocus = e => { this.foundation.handleInputFocus(e); }; this.handleInputBlur = e => { this.foundation.handleInputBlur(e); }; this.handleClearBtn = e => { this.foundation.handleClearBtn(e); }; /* istanbul ignore next */ this.handleClearEnterPress = e => { this.foundation.handleClearEnterPress(e); }; this.handleTagClose = idx => { this.foundation.handleTagClose(idx); }; this.handleInputMouseLeave = e => { this.foundation.handleInputMouseLeave(); }; this.handleClick = e => { this.foundation.handleClick(e); }; this.handleInputMouseEnter = e => { this.foundation.handleInputMouseEnter(); }; this.handleClickPrefixOrSuffix = e => { this.foundation.handleClickPrefixOrSuffix(e); }; this.handlePreventMouseDown = e => { this.foundation.handlePreventMouseDown(e); }; this.getAllTags = () => { const { tagsArray } = this.state; return tagsArray.map((value, index) => this.renderTag(value, index)); }; this.renderTag = (value, index, sortableHandle) => { const { size, disabled, renderTagItem, showContentTooltip, draggable } = this.props; const { active } = this.state; const showIconHandler = active && draggable; const tagCls = (0, _classnames.default)(`${prefixCls}-wrapper-tag`, { [`${prefixCls}-wrapper-tag-size-${size}`]: size, [`${prefixCls}-wrapper-tag-icon`]: showIconHandler }); const typoCls = (0, _classnames.default)(`${prefixCls}-wrapper-typo`, { [`${prefixCls}-wrapper-typo-disabled`]: disabled }); const itemWrapperCls = (0, _classnames.default)({ [`${prefixCls}-drag-item`]: showIconHandler, [`${prefixCls}-wrapper-tag-icon`]: showIconHandler }); const DragHandle = sortableHandle && sortableHandle(() => /*#__PURE__*/_react.default.createElement(_semiIcons.IconHandle, { className: `${prefixCls}-drag-handler` })); const elementKey = showIconHandler ? value : `${index}${value}`; const onClose = () => { !disabled && this.handleTagClose(index); }; if ((0, _isFunction2.default)(renderTagItem)) { return /*#__PURE__*/_react.default.createElement("div", { className: itemWrapperCls, key: elementKey }, showIconHandler && sortableHandle ? /*#__PURE__*/_react.default.createElement(DragHandle, null) : null, renderTagItem(value, index, onClose)); } else { return /*#__PURE__*/_react.default.createElement(_tag.default, { className: tagCls, color: "white", size: size === 'small' ? 'small' : 'large', type: "light", onClose: onClose, closable: !disabled, key: elementKey, visible: true, "aria-label": `${!disabled ? 'Closable ' : ''}Tag: ${value}` }, showIconHandler && sortableHandle ? /*#__PURE__*/_react.default.createElement(DragHandle, null) : null, /*#__PURE__*/_react.default.createElement(_paragraph.default, { className: typoCls, ellipsis: { showTooltip: showContentTooltip, rows: 1 } }, value)); } }; this.renderSortTag = props => { const { id: item, sortableHandle } = props; const { tagsArray } = this.state; const index = tagsArray.indexOf(item); return this.renderTag(item, index, sortableHandle); }; this.onSortEnd = callbackProps => { this.foundation.handleSortEnd(callbackProps); }; this.handleInputCompositionStart = e => { this.foundation.handleInputCompositionStart(e); }; this.handleInputCompositionEnd = e => { this.foundation.handleInputCompositionEnd(e); }; this.foundation = new _foundation.default(this.adapter); this.state = { tagsArray: props.defaultValue || [], inputValue: '', focusing: false, hovering: false, active: false, entering: false }; this.inputRef = /*#__PURE__*/_react.default.createRef(); this.tagInputRef = /*#__PURE__*/_react.default.createRef(); this.clickOutsideHandler = null; } static getDerivedStateFromProps(nextProps, prevState) { const { value, inputValue } = nextProps; const { tagsArray: prevTagsArray } = prevState; let tagsArray; if ((0, _isArray2.default)(value)) { tagsArray = value; } else if ('value' in nextProps && !value) { tagsArray = []; } else { tagsArray = prevTagsArray; } return { tagsArray, inputValue: (0, _isString2.default)(inputValue) ? inputValue : prevState.inputValue }; } get adapter() { return Object.assign(Object.assign({}, super.adapter), { setInputValue: inputValue => { this.setState({ inputValue }); }, setTagsArray: tagsArray => { this.setState({ tagsArray }); }, setFocusing: focusing => { this.setState({ focusing }); }, toggleFocusing: isFocus => { const { preventScroll } = this.props; const input = this.inputRef && this.inputRef.current; if (isFocus) { input && input.focus({ preventScroll }); } else { input && input.blur(); } this.setState({ focusing: isFocus }); }, setHovering: hovering => { this.setState({ hovering }); }, setActive: active => { this.setState({ active }); }, setEntering: entering => { this.setState({ entering }); }, getClickOutsideHandler: () => { return this.clickOutsideHandler; }, notifyBlur: e => { this.props.onBlur(e); }, notifyFocus: e => { this.props.onFocus(e); }, notifyInputChange: (v, e) => { this.props.onInputChange(v, e); }, notifyTagChange: v => { this.props.onChange(v); }, notifyTagAdd: v => { this.props.onAdd(v); }, notifyTagRemove: (v, idx) => { this.props.onRemove(v, idx); }, notifyKeyDown: e => { this.props.onKeyDown(e); }, registerClickOutsideHandler: cb => { const clickOutsideHandler = e => { const tagInputDom = this.tagInputRef && this.tagInputRef.current; const target = e.target; const path = e.composedPath && e.composedPath() || [target]; if (tagInputDom && !tagInputDom.contains(target) && !path.includes(tagInputDom)) { cb(e); } }; this.clickOutsideHandler = clickOutsideHandler; document.addEventListener('click', clickOutsideHandler, false); }, unregisterClickOutsideHandler: () => { document.removeEventListener('click', this.clickOutsideHandler, false); this.clickOutsideHandler = null; } }); } componentDidMount() { const { disabled, autoFocus, preventScroll } = this.props; if (!disabled && autoFocus) { this.inputRef.current.focus({ preventScroll }); this.foundation.handleClick(); } this.foundation.init(); } renderClearBtn() { const { hovering, tagsArray, inputValue } = this.state; const { showClear, disabled, clearIcon } = this.props; const clearCls = (0, _classnames.default)(`${prefixCls}-clearBtn`, { [`${prefixCls}-clearBtn-invisible`]: !hovering || inputValue === '' && tagsArray.length === 0 || disabled }); if (showClear) { return /*#__PURE__*/_react.default.createElement("div", { role: "button", tabIndex: 0, "aria-label": "Clear TagInput value", className: clearCls, onClick: e => this.handleClearBtn(e), onKeyPress: e => this.handleClearEnterPress(e) }, clearIcon ? clearIcon : /*#__PURE__*/_react.default.createElement(_semiIcons.IconClear, null)); } return null; } renderPrefix() { const { prefix, insetLabel, insetLabelId } = this.props; const labelNode = prefix || insetLabel; if ((0, _isNull2.default)(labelNode) || (0, _isUndefined2.default)(labelNode)) { return null; } const prefixWrapperCls = (0, _classnames.default)(`${prefixCls}-prefix`, { [`${prefixCls}-inset-label`]: insetLabel, [`${prefixCls}-prefix-text`]: labelNode && (0, _isString2.default)(labelNode), [`${prefixCls}-prefix-icon`]: (0, _utils.isSemiIcon)(labelNode) }); return ( /*#__PURE__*/ // eslint-disable-next-line jsx-a11y/no-static-element-interactions,jsx-a11y/click-events-have-key-events _react.default.createElement("div", { className: prefixWrapperCls, onMouseDown: this.handlePreventMouseDown, onClick: this.handleClickPrefixOrSuffix, id: insetLabelId, "x-semi-prop": "prefix" }, labelNode) ); } renderSuffix() { const { suffix } = this.props; if ((0, _isNull2.default)(suffix) || (0, _isUndefined2.default)(suffix)) { return null; } const suffixWrapperCls = (0, _classnames.default)(`${prefixCls}-suffix`, { [`${prefixCls}-suffix-text`]: suffix && (0, _isString2.default)(suffix), [`${prefixCls}-suffix-icon`]: (0, _utils.isSemiIcon)(suffix) }); return ( /*#__PURE__*/ // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions _react.default.createElement("div", { className: suffixWrapperCls, onMouseDown: this.handlePreventMouseDown, onClick: this.handleClickPrefixOrSuffix, "x-semi-prop": "suffix" }, suffix) ); } renderTags() { const { disabled, maxTagCount, showRestTagsPopover, restTagsPopoverProps = {}, draggable, expandRestTagsOnClick } = this.props; const { tagsArray, active } = this.state; const restTagsCls = (0, _classnames.default)(`${prefixCls}-wrapper-n`, { [`${prefixCls}-wrapper-n-disabled`]: disabled }); const allTags = this.getAllTags(); let restTags = []; let tags = [...allTags]; if ((!active || !expandRestTagsOnClick) && maxTagCount && maxTagCount < allTags.length) { tags = allTags.slice(0, maxTagCount); restTags = allTags.slice(maxTagCount); } const restTagsContent = /*#__PURE__*/_react.default.createElement("span", { className: restTagsCls }, "+", tagsArray.length - maxTagCount); const sortableListItems = allTags.map((item, index) => ({ item: item, key: tagsArray[index] })); if (active && draggable && sortableListItems.length > 0) { return /*#__PURE__*/_react.default.createElement(_sortable.Sortable, { items: tagsArray, onSortEnd: this.onSortEnd, renderItem: this.renderSortTag, container: SortContainer, prefix: prefixCls, transition: null, dragOverlayCls: `${prefixCls}-right-item-drag-item-move` }); } return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, tags, restTags.length > 0 && (showRestTagsPopover ? (/*#__PURE__*/_react.default.createElement(_popover.default, Object.assign({ content: restTags, showArrow: true, trigger: "hover", position: "top", autoAdjustOverflow: true }, restTagsPopoverProps), restTagsContent)) : restTagsContent)); } blur() { this.inputRef.current.blur(); // unregister clickOutside event this.foundation.clickOutsideCallBack(); } focus() { const { preventScroll, disabled } = this.props; this.inputRef.current.focus({ preventScroll }); if (!disabled) { // register clickOutside event this.foundation.handleClick(); } } render() { const _a = this.props, { size, style, className, disabled, placeholder, validateStatus, prefix, insetLabel, suffix } = _a, rest = __rest(_a, ["size", "style", "className", "disabled", "placeholder", "validateStatus", "prefix", "insetLabel", "suffix"]); const { focusing, hovering, tagsArray, inputValue, active } = this.state; const tagInputCls = (0, _classnames.default)(prefixCls, className, { [`${prefixCls}-focus`]: focusing || active, [`${prefixCls}-disabled`]: disabled, [`${prefixCls}-hover`]: hovering && !disabled, [`${prefixCls}-error`]: validateStatus === 'error', [`${prefixCls}-warning`]: validateStatus === 'warning', [`${prefixCls}-small`]: size === 'small', [`${prefixCls}-large`]: size === 'large', [`${prefixCls}-with-prefix`]: !!prefix || !!insetLabel, [`${prefixCls}-with-suffix`]: !!suffix }); const inputCls = (0, _classnames.default)(`${prefixCls}-wrapper-input`, `${prefixCls}-wrapper-input-${size}`); const wrapperCls = (0, _classnames.default)(`${prefixCls}-wrapper`); return ( /*#__PURE__*/ // eslint-disable-next-line _react.default.createElement("div", Object.assign({ ref: this.tagInputRef, style: style, className: tagInputCls, "aria-disabled": disabled, "aria-label": this.props['aria-label'], "aria-invalid": validateStatus === 'error', onMouseEnter: e => { this.handleInputMouseEnter(e); }, onMouseLeave: e => { this.handleInputMouseLeave(e); }, onClick: e => { this.handleClick(e); } }, this.getDataAttr(rest)), this.renderPrefix(), /*#__PURE__*/_react.default.createElement("div", { className: wrapperCls }, this.renderTags(), /*#__PURE__*/_react.default.createElement(_input.default, { "aria-label": 'input value', ref: this.inputRef, className: inputCls, disabled: disabled, value: inputValue, size: size, placeholder: tagsArray.length === 0 ? placeholder : '', onKeyDown: e => { this.handleKeyDown(e); }, onChange: (v, e) => { this.handleInputChange(e); }, onBlur: e => { this.handleInputBlur(e); }, onFocus: e => { this.handleInputFocus(e); }, onCompositionStart: this.handleInputCompositionStart, onCompositionEnd: this.handleInputCompositionEnd })), this.renderClearBtn(), this.renderSuffix()) ); } } TagInput.propTypes = { children: _propTypes.default.node, clearIcon: _propTypes.default.node, style: _propTypes.default.object, className: _propTypes.default.string, disabled: _propTypes.default.bool, allowDuplicates: _propTypes.default.bool, max: _propTypes.default.number, maxTagCount: _propTypes.default.number, maxLength: _propTypes.default.number, showRestTagsPopover: _propTypes.default.bool, restTagsPopoverProps: _propTypes.default.object, showContentTooltip: _propTypes.default.oneOfType([_propTypes.default.shape({ type: _propTypes.default.string, opts: _propTypes.default.object }), _propTypes.default.bool]), defaultValue: _propTypes.default.array, value: _propTypes.default.array, inputValue: _propTypes.default.string, placeholder: _propTypes.default.string, separator: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.array]), showClear: _propTypes.default.bool, addOnBlur: _propTypes.default.bool, draggable: _propTypes.default.bool, expandRestTagsOnClick: _propTypes.default.bool, autoFocus: _propTypes.default.bool, renderTagItem: _propTypes.default.func, onBlur: _propTypes.default.func, onFocus: _propTypes.default.func, onChange: _propTypes.default.func, onInputChange: _propTypes.default.func, onExceed: _propTypes.default.func, onInputExceed: _propTypes.default.func, onAdd: _propTypes.default.func, onRemove: _propTypes.default.func, onKeyDown: _propTypes.default.func, size: _propTypes.default.oneOf(_constants.strings.SIZE_SET), validateStatus: _propTypes.default.oneOf(_constants.strings.STATUS), prefix: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.node]), suffix: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.node]), 'aria-label': _propTypes.default.string, preventScroll: _propTypes.default.bool }; TagInput.defaultProps = { showClear: false, addOnBlur: false, allowDuplicates: true, showRestTagsPopover: true, autoFocus: false, draggable: false, expandRestTagsOnClick: true, showContentTooltip: true, separator: ',', size: 'default', validateStatus: 'default', onBlur: _noop2.default, onFocus: _noop2.default, onChange: _noop2.default, onInputChange: _noop2.default, onExceed: _noop2.default, onInputExceed: _noop2.default, onAdd: _noop2.default, onRemove: _noop2.default, onKeyDown: _noop2.default }; var _default = exports.default = TagInput;