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.

280 lines 9.75 kB
import _throttle from "lodash/throttle"; import _isObject from "lodash/isObject"; import _isUndefined from "lodash/isUndefined"; import _isFunction from "lodash/isFunction"; import _omit from "lodash/omit"; import _noop from "lodash/noop"; var __rest = this && this.__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; }; import React from 'react'; import cls from 'classnames'; import PropTypes from 'prop-types'; import TextAreaFoundation from '@douyinfe/semi-foundation/lib/es/input/textareaFoundation'; import { cssClasses } from '@douyinfe/semi-foundation/lib/es/input/constants'; import BaseComponent from '../_base/baseComponent'; import '@douyinfe/semi-foundation/lib/es/input/textarea.css'; import { IconClear } from '@douyinfe/semi-icons'; import ResizeObserver from '../resizeObserver'; const prefixCls = cssClasses.PREFIX; class TextArea extends BaseComponent { constructor(props) { super(props); this.handleClear = e => { this.foundation.handleClear(e); }; this.setRef = node => { this.libRef.current = node; const { forwardRef } = this.props; if (typeof forwardRef === 'function') { forwardRef(node); } else if (forwardRef && typeof forwardRef === 'object') { forwardRef.current = node; } }; const initValue = 'value' in props ? props.value : props.defaultValue; this.state = { value: initValue, isFocus: false, isHover: false, height: 0, minLength: props.minLength, cachedValue: props.value }; this.focusing = false; this.foundation = new TextAreaFoundation(this.adapter); this.libRef = /*#__PURE__*/React.createRef(); this.throttledResizeTextarea = _throttle(this.foundation.resizeTextarea, 10); } get adapter() { return Object.assign(Object.assign({}, super.adapter), { setValue: value => this.setState({ value }, () => { if (this.props.autosize) { this.foundation.resizeTextarea(); } }), getRef: () => this.libRef.current, toggleFocusing: focusing => this.setState({ isFocus: focusing }), toggleHovering: hovering => this.setState({ isHover: hovering }), notifyChange: (val, e) => { this.props.onChange(val, e); }, notifyClear: e => this.props.onClear(e), notifyBlur: (val, e) => this.props.onBlur(e), notifyFocus: (val, e) => this.props.onFocus(e), notifyKeyDown: e => { this.props.onKeyDown(e); }, notifyHeightUpdate: height => { this.setState({ height }); this.props.onResize({ height }); }, notifyPressEnter: e => { this.props.onEnterPress && this.props.onEnterPress(e); }, setMinLength: minLength => this.setState({ minLength }) }); } static getDerivedStateFromProps(props, state) { const willUpdateStates = {}; if (props.value !== state.cachedValue) { willUpdateStates.value = props.value; willUpdateStates.cachedValue = props.value; } return willUpdateStates; } componentWillUnmount() { var _a, _b; if (this.throttledResizeTextarea) { (_b = (_a = this.throttledResizeTextarea) === null || _a === void 0 ? void 0 : _a.cancel) === null || _b === void 0 ? void 0 : _b.call(_a); this.throttledResizeTextarea = null; } } componentDidUpdate(prevProps, prevState) { if ((this.props.value !== prevProps.value || this.props.placeholder !== prevProps.placeholder) && this.props.autosize) { this.foundation.resizeTextarea(); } } renderClearBtn() { const { showClear } = this.props; const displayClearBtn = this.foundation.isAllowClear(); const clearCls = cls(`${prefixCls}-clearbtn`, { [`${prefixCls}-clearbtn-hidden`]: !displayClearBtn }); if (showClear) { return ( /*#__PURE__*/ // eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions React.createElement("div", { className: clearCls, onClick: this.handleClear }, /*#__PURE__*/React.createElement(IconClear, null)) ); } return null; } renderCounter() { let counter, current, total, countCls; const { showCounter, maxCount, getValueLength } = this.props; if (showCounter || maxCount) { const { value } = this.state; // eslint-disable-next-line no-nested-ternary current = value ? _isFunction(getValueLength) ? getValueLength(value) : value.length : 0; total = maxCount || null; countCls = cls(`${prefixCls}-textarea-counter`, { [`${prefixCls}-textarea-counter-exceed`]: current > total }); counter = /*#__PURE__*/React.createElement("div", { className: countCls }, current, total ? '/' : null, total); } else { counter = null; } return counter; } render() { const _a = this.props, { autosize, placeholder, onEnterPress, onResize, // resize, disabled, readonly, className, showCounter, validateStatus, maxCount, defaultValue, style, forwardRef, getValueLength, maxLength, minLength, showClear, borderless, autoFocus } = _a, rest = __rest(_a, ["autosize", "placeholder", "onEnterPress", "onResize", "disabled", "readonly", "className", "showCounter", "validateStatus", "maxCount", "defaultValue", "style", "forwardRef", "getValueLength", "maxLength", "minLength", "showClear", "borderless", "autoFocus"]); const { isFocus, value, minLength: stateMinLength } = this.state; const wrapperCls = cls(className, `${prefixCls}-textarea-wrapper`, { [`${prefixCls}-textarea-borderless`]: borderless, [`${prefixCls}-textarea-wrapper-disabled`]: disabled, [`${prefixCls}-textarea-wrapper-readonly`]: readonly, [`${prefixCls}-textarea-wrapper-${validateStatus}`]: Boolean(validateStatus), [`${prefixCls}-textarea-wrapper-focus`]: isFocus // [`${prefixCls}-textarea-wrapper-resize`]: !autosize && resize, }); // const ref = this.props.forwardRef || this.textAreaRef; const itemCls = cls(`${prefixCls}-textarea`, { [`${prefixCls}-textarea-disabled`]: disabled, [`${prefixCls}-textarea-readonly`]: readonly, [`${prefixCls}-textarea-autosize`]: _isObject(autosize) ? _isUndefined(autosize === null || autosize === void 0 ? void 0 : autosize.maxRows) : autosize, [`${prefixCls}-textarea-showClear`]: showClear }); const itemProps = Object.assign(Object.assign({}, _omit(rest, 'insetLabel', 'insetLabelId', 'getValueLength', 'onClear', 'showClear', 'disabledEnterStartNewLine')), { autoFocus: autoFocus || this.props['autofocus'], className: itemCls, disabled, readOnly: readonly, placeholder: !placeholder ? null : placeholder, onChange: e => this.foundation.handleChange(e.target.value, e), onFocus: e => this.foundation.handleFocus(e), onBlur: e => this.foundation.handleBlur(e.nativeEvent), onKeyDown: e => this.foundation.handleKeyDown(e), value: value === null || value === undefined ? '' : value, onCompositionStart: this.foundation.handleCompositionStart, onCompositionEnd: this.foundation.handleCompositionEnd }); if (!_isFunction(getValueLength)) { itemProps.maxLength = maxLength; } if (stateMinLength) { itemProps.minLength = stateMinLength; } return /*#__PURE__*/React.createElement("div", { className: wrapperCls, style: style, onMouseEnter: e => this.foundation.handleMouseEnter(e), onMouseLeave: e => this.foundation.handleMouseLeave(e) }, autosize ? (/*#__PURE__*/React.createElement(ResizeObserver, { onResize: this.throttledResizeTextarea }, /*#__PURE__*/React.createElement("textarea", Object.assign({}, itemProps, { ref: this.setRef })))) : (/*#__PURE__*/React.createElement("textarea", Object.assign({}, itemProps, { ref: this.setRef }))), this.renderClearBtn(), this.renderCounter()); } } TextArea.propTypes = { autosize: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), borderless: PropTypes.bool, placeholder: PropTypes.string, value: PropTypes.string, rows: PropTypes.number, cols: PropTypes.number, maxCount: PropTypes.number, onEnterPress: PropTypes.func, validateStatus: PropTypes.string, className: PropTypes.string, style: PropTypes.object, showClear: PropTypes.bool, onClear: PropTypes.func, onResize: PropTypes.func, getValueLength: PropTypes.func, disabledEnterStartNewLine: PropTypes.bool // TODO // resize: PropTypes.bool, }; TextArea.defaultProps = { autosize: false, borderless: false, rows: 4, cols: 20, showCounter: false, showClear: false, onEnterPress: _noop, onChange: _noop, onBlur: _noop, onFocus: _noop, onKeyDown: _noop, onResize: _noop, onClear: _noop // resize: false, }; const ForwardTextarea = /*#__PURE__*/React.forwardRef((props, ref) => (/*#__PURE__*/React.createElement(TextArea, Object.assign({}, props, { forwardRef: ref })))); export default ForwardTextarea;