@alifd/next
Version:
A configurable component library for web built on React.
243 lines (242 loc) • 11.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var react_1 = tslib_1.__importDefault(require("react"));
var react_dom_1 = tslib_1.__importDefault(require("react-dom"));
var prop_types_1 = tslib_1.__importDefault(require("prop-types"));
var classnames_1 = tslib_1.__importDefault(require("classnames"));
var zh_cn_1 = tslib_1.__importDefault(require("../locale/zh-cn"));
var util_1 = require("../util");
var base_1 = tslib_1.__importDefault(require("./base"));
function onNextFrame(cb) {
if (window.requestAnimationFrame) {
return window.requestAnimationFrame(cb);
}
return window.setTimeout(cb, 1);
}
function clearNextFrameAction(nextFrameId) {
if (window.cancelAnimationFrame) {
window.cancelAnimationFrame(nextFrameId);
}
else {
window.clearTimeout(nextFrameId);
}
}
// safari in mac
var isMacSafari = typeof navigator !== 'undefined' && navigator && navigator.userAgent
? navigator.userAgent.match(/^((?!chrome|android|windows).)*safari/i)
: false;
var hiddenStyle = {
visibility: 'hidden',
position: 'absolute',
zIndex: '-1000',
top: '-1000px',
overflowY: 'hidden',
left: 0,
right: 0,
};
/**
* Input.TextArea
* @order 2
*/
var TextArea = /** @class */ (function (_super) {
tslib_1.__extends(TextArea, _super);
function TextArea(props) {
var _this = _super.call(this, props) || this;
_this._resizeTextArea = function (value) {
if (_this.nextFrameActionId) {
clearNextFrameAction(_this.nextFrameActionId);
}
_this.nextFrameActionId = onNextFrame(function () {
var height = _this._getHeight(value);
var maxHeight = _this.state.maxHeight ? _this.state.maxHeight : Infinity;
_this.setState({
height: _this._getHeight(value),
overflowY: height <= maxHeight ? 'hidden' : undefined,
});
});
};
var value;
if ('value' in props) {
value = props.value;
}
else {
value = props.defaultValue;
}
_this.state = {
value: typeof value === 'undefined' || value === null ? '' : value,
};
return _this;
}
TextArea.prototype.componentDidMount = function () {
var autoHeight = this.props.autoHeight;
if (autoHeight) {
if (typeof autoHeight === 'object') {
this.setState(this._getMinMaxHeight(autoHeight, this.state.value));
}
else {
this.setState({
height: this._getHeight(this.state.value),
overflowY: 'hidden',
});
}
}
};
TextArea.prototype.componentDidUpdate = function (prevProps) {
if (this.props.autoHeight &&
(this.props.value !== prevProps.value || this.props.isPreview !== prevProps.isPreview)) {
this._resizeTextArea(this.props.value);
}
};
TextArea.prototype._getMinMaxHeight = function (_a, value) {
var minRows = _a.minRows, maxRows = _a.maxRows;
var node = react_dom_1.default.findDOMNode(this.helpRef);
if (!node) {
return {};
}
// @ts-expect-error minRows 应转为 string
node.setAttribute('rows', minRows);
var minHeight = node.clientHeight;
// @ts-expect-error maxRows 应转为 string
node.setAttribute('rows', maxRows);
var maxHeight = node.clientHeight;
node.setAttribute('rows', '1');
// @ts-expect-error value 应转为 string
var height = this._getHeight(value);
return {
minHeight: minHeight,
maxHeight: maxHeight,
height: height,
overflowY: height <= maxHeight ? 'hidden' : undefined,
};
};
TextArea.prototype._getHeight = function (value) {
var node = react_dom_1.default.findDOMNode(this.helpRef);
if (!node) {
return 0;
}
// @ts-expect-error value 应转为 string
node.value = value;
return node.scrollHeight;
};
TextArea.prototype.ieHack = function (value) {
// Fix: textarea dit not support maxLength in ie9
if (util_1.env.ieVersion === 9 && this.props.maxLength) {
var maxLength = parseInt(this.props.maxLength);
// @ts-expect-error value 应转为 string
var newValue = value;
var len = this.getValueLength(newValue);
if (len > maxLength && this.props.cutString) {
newValue = newValue.replace(/\n/g, '\n\n');
newValue = newValue.substr(0, maxLength);
newValue = newValue.replace(/\n\n/g, '\n');
}
}
this.props.autoHeight && this._resizeTextArea(value);
return value;
};
/**
* value.length !== maxLength in ie/safari(mac) while value has `Enter`
* about maxLength compute: `Enter` was considered to be one char(\\n) in chrome , but two chars(\\r\\n) in ie/safari(mac).
* so while value has `Enter`, we should let display length + 1
*/
TextArea.prototype.getValueLength = function (value) {
var _a = this.props, maxLength = _a.maxLength, cutString = _a.cutString;
var nv = "".concat(value);
var strLen = this.props.getValueLength(nv);
if (typeof strLen !== 'number') {
strLen = nv.length;
}
/* istanbul ignore if */
if (util_1.env.ieVersion || isMacSafari) {
strLen = strLen + nv.split('\n').length - 1;
if (strLen > maxLength && cutString) {
strLen = maxLength;
}
}
return strLen;
};
TextArea.prototype.saveTextAreaRef = function (textArea) {
this.inputRef = textArea;
};
TextArea.prototype.saveHelpRef = function (ref) {
this.helpRef = ref;
};
TextArea.prototype.renderClear = function () {
var _a;
var _b = this.props, hasClear = _b.hasClear, readOnly = _b.readOnly, state = _b.state, prefix = _b.prefix, disabled = _b.disabled, locale = _b.locale;
var clearWrap = null;
// showClear 属性应该与 disable 属性为互斥状态
var showClear = hasClear && !readOnly && !!"".concat(this.state.value) && !disabled;
var cls = (0, classnames_1.default)((_a = {},
_a["".concat(prefix, "input-textarea-clear")] = true,
_a));
clearWrap = showClear ? (react_1.default.createElement("span", { className: cls, onClick: this.onClear.bind(this), onKeyDown: this.handleKeyDownFromClear },
' ',
locale.clear)) : null;
if (state === 'loading') {
clearWrap = null;
}
return clearWrap;
};
TextArea.prototype.renderControl = function () {
var _this = this;
var prefix = this.props.prefix;
var lenWrap = this.renderLength();
var clearText = this.renderClear();
var isShowLine = Boolean(lenWrap && clearText);
var cls = (0, classnames_1.default)("".concat(prefix, "input-control"), "".concat(prefix, "input-textarea-control"));
return lenWrap || clearText ? (react_1.default.createElement("span", { onClick: function () { return _this.focus(); }, className: cls },
lenWrap,
isShowLine && react_1.default.createElement("span", { className: "".concat(prefix, "input-textarea-control-line") }),
clearText)) : null;
};
TextArea.prototype.render = function () {
var _a, _b;
var _c = this.props, rows = _c.rows, style = _c.style, className = _c.className, autoHeight = _c.autoHeight, isPreview = _c.isPreview, renderPreview = _c.renderPreview, prefix = _c.prefix, rtl = _c.rtl, hasBorder = _c.hasBorder, size = _c.size, composition = _c.composition;
var cls = (0, classnames_1.default)(this.getClass(), (_a = {},
_a["".concat(prefix).concat(size)] = size === 'large' || size === 'small',
_a["".concat(prefix, "input-textarea")] = true,
_a["".concat(prefix, "noborder")] = !hasBorder,
_a[className] = !!className,
_a));
// FIXME textarea 里还可以是 null,导致这里需要对类型做特殊处理,实际上 null 并不能传给原生的 textarea
var props = this.getProps();
// custom data attributes are assigned to the top parent node
// data-类自定义数据属性分配到顶层 node 节点
var dataProps = util_1.obj.pickAttrsWith(this.props, 'data-');
// Custom props are transparently transmitted to the core input node by default
// 自定义属性默认透传到核心 node 节点:input
var others = util_1.obj.pickOthers(Object.assign({}, dataProps, TextArea.propTypes), this.props);
var textareStyle = tslib_1.__assign(tslib_1.__assign({}, props.style), { height: this.state.height, minHeight: this.state.minHeight, maxHeight: this.state.maxHeight, overflowY: this.state.overflowY });
var previewCls = (0, classnames_1.default)((_b = {},
_b["".concat(prefix, "input-textarea")] = true,
_b["".concat(prefix, "form-preview")] = true,
_b[className] = !!className,
_b));
var wrapStyle = autoHeight
? tslib_1.__assign(tslib_1.__assign({}, style), { position: 'relative' }) : style;
if (isPreview) {
var value = props.value;
if ('renderPreview' in this.props) {
return (react_1.default.createElement("div", tslib_1.__assign({}, others, { className: previewCls }), renderPreview(value, this.props)));
}
return (react_1.default.createElement("div", tslib_1.__assign({}, others, { className: previewCls }), value.split('\n').map(function (data, i) { return (react_1.default.createElement("p", { key: "p-".concat(i) }, data)); })));
}
var compositionProps = {};
if (composition) {
compositionProps.onCompositionStart = this.handleCompositionStart;
compositionProps.onCompositionEnd = this.handleCompositionEnd;
}
return (react_1.default.createElement("span", tslib_1.__assign({ className: cls, style: wrapStyle, dir: rtl ? 'rtl' : undefined }, dataProps),
react_1.default.createElement("textarea", tslib_1.__assign({}, others, props, compositionProps, { "data-real": true, rows: rows, style: textareStyle, ref: this.saveRef.bind(this), onKeyDown: this.onKeyDown.bind(this) })),
autoHeight ? (react_1.default.createElement("textarea", { "data-fake": true, ref: this.saveHelpRef.bind(this), style: tslib_1.__assign(tslib_1.__assign({}, props.style), hiddenStyle), rows: 1 })) : null,
this.renderControl()));
};
TextArea.displayName = 'TextArea';
TextArea.getDerivedStateFromProps = base_1.default.getDerivedStateFromProps;
TextArea.propTypes = tslib_1.__assign(tslib_1.__assign({}, base_1.default.propTypes), { hasBorder: prop_types_1.default.bool, state: prop_types_1.default.oneOf(['error', 'warning']), autoHeight: prop_types_1.default.oneOfType([prop_types_1.default.bool, prop_types_1.default.object]), rows: prop_types_1.default.number, isPreview: prop_types_1.default.bool, renderPreview: prop_types_1.default.func, locale: prop_types_1.default.object });
TextArea.defaultProps = tslib_1.__assign(tslib_1.__assign({}, base_1.default.defaultProps), { hasBorder: true, isPreview: false, rows: 4, autoHeight: false, locale: zh_cn_1.default.TextArea });
return TextArea;
}(base_1.default));
exports.default = TextArea;