TextArea.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import _typeof from "@babel/runtime/helpers/esm/typeof";
  2. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  3. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  4. import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
  5. import { resolveDirective as _resolveDirective, createVNode as _createVNode } from "vue";
  6. import { computed, defineComponent, getCurrentInstance, nextTick, ref, watch, watchEffect } from 'vue';
  7. import ClearableLabeledInput from './ClearableLabeledInput';
  8. import ResizableTextArea from './ResizableTextArea';
  9. import { textAreaProps } from './inputProps';
  10. import { fixControlledValue, resolveOnChange, triggerFocus } from './Input';
  11. import classNames from '../_util/classNames';
  12. import { useInjectFormItemContext } from '../form/FormItemContext';
  13. import useConfigInject from '../_util/hooks/useConfigInject';
  14. import omit from '../_util/omit';
  15. function fixEmojiLength(value, maxLength) {
  16. return _toConsumableArray(value || '').slice(0, maxLength).join('');
  17. }
  18. function setTriggerValue(isCursorInEnd, preValue, triggerValue, maxLength) {
  19. var newTriggerValue = triggerValue;
  20. if (isCursorInEnd) {
  21. // 光标在尾部,直接截断
  22. newTriggerValue = fixEmojiLength(triggerValue, maxLength);
  23. } else if (_toConsumableArray(preValue || '').length < triggerValue.length && _toConsumableArray(triggerValue || '').length > maxLength) {
  24. // 光标在中间,如果最后的值超过最大值,则采用原先的值
  25. newTriggerValue = preValue;
  26. }
  27. return newTriggerValue;
  28. }
  29. export default defineComponent({
  30. compatConfig: {
  31. MODE: 3
  32. },
  33. name: 'ATextarea',
  34. inheritAttrs: false,
  35. props: textAreaProps(),
  36. setup: function setup(props, _ref) {
  37. var attrs = _ref.attrs,
  38. expose = _ref.expose,
  39. emit = _ref.emit;
  40. var formItemContext = useInjectFormItemContext();
  41. var stateValue = ref(props.value === undefined ? props.defaultValue : props.value);
  42. var resizableTextArea = ref();
  43. var mergedValue = ref('');
  44. var _useConfigInject = useConfigInject('input', props),
  45. prefixCls = _useConfigInject.prefixCls,
  46. size = _useConfigInject.size,
  47. direction = _useConfigInject.direction;
  48. var showCount = computed(function () {
  49. return props.showCount === '' || props.showCount || false;
  50. });
  51. // Max length value
  52. var hasMaxLength = computed(function () {
  53. return Number(props.maxlength) > 0;
  54. });
  55. var compositing = ref(false);
  56. var oldCompositionValueRef = ref();
  57. var oldSelectionStartRef = ref(0);
  58. var onInternalCompositionStart = function onInternalCompositionStart(e) {
  59. compositing.value = true;
  60. // 拼音输入前保存一份旧值
  61. oldCompositionValueRef.value = mergedValue.value;
  62. // 保存旧的光标位置
  63. oldSelectionStartRef.value = e.currentTarget.selectionStart;
  64. emit('compositionstart', e);
  65. };
  66. var onInternalCompositionEnd = function onInternalCompositionEnd(e) {
  67. compositing.value = false;
  68. var triggerValue = e.currentTarget.value;
  69. if (hasMaxLength.value) {
  70. var _oldCompositionValueR;
  71. var isCursorInEnd = oldSelectionStartRef.value >= props.maxlength + 1 || oldSelectionStartRef.value === ((_oldCompositionValueR = oldCompositionValueRef.value) === null || _oldCompositionValueR === void 0 ? void 0 : _oldCompositionValueR.length);
  72. triggerValue = setTriggerValue(isCursorInEnd, oldCompositionValueRef.value, triggerValue, props.maxlength);
  73. }
  74. // Patch composition onChange when value changed
  75. if (triggerValue !== mergedValue.value) {
  76. setValue(triggerValue);
  77. resolveOnChange(e.currentTarget, e, triggerChange, triggerValue);
  78. }
  79. emit('compositionend', e);
  80. };
  81. var instance = getCurrentInstance();
  82. watch(function () {
  83. return props.value;
  84. }, function () {
  85. if ('value' in instance.vnode.props || {}) {
  86. var _props$value;
  87. stateValue.value = (_props$value = props.value) !== null && _props$value !== void 0 ? _props$value : '';
  88. }
  89. });
  90. var focus = function focus(option) {
  91. var _resizableTextArea$va;
  92. triggerFocus((_resizableTextArea$va = resizableTextArea.value) === null || _resizableTextArea$va === void 0 ? void 0 : _resizableTextArea$va.textArea, option);
  93. };
  94. var blur = function blur() {
  95. var _resizableTextArea$va2, _resizableTextArea$va3;
  96. (_resizableTextArea$va2 = resizableTextArea.value) === null || _resizableTextArea$va2 === void 0 ? void 0 : (_resizableTextArea$va3 = _resizableTextArea$va2.textArea) === null || _resizableTextArea$va3 === void 0 ? void 0 : _resizableTextArea$va3.blur();
  97. };
  98. var setValue = function setValue(value, callback) {
  99. if (stateValue.value === value) {
  100. return;
  101. }
  102. if (props.value === undefined) {
  103. stateValue.value = value;
  104. } else {
  105. nextTick(function () {
  106. if (resizableTextArea.value.textArea.value !== mergedValue.value) {
  107. var _resizableTextArea$va4, _resizableTextArea$va5, _resizableTextArea$va6;
  108. (_resizableTextArea$va4 = resizableTextArea.value) === null || _resizableTextArea$va4 === void 0 ? void 0 : (_resizableTextArea$va5 = (_resizableTextArea$va6 = _resizableTextArea$va4.instance).update) === null || _resizableTextArea$va5 === void 0 ? void 0 : _resizableTextArea$va5.call(_resizableTextArea$va6);
  109. }
  110. });
  111. }
  112. nextTick(function () {
  113. callback && callback();
  114. });
  115. };
  116. var handleKeyDown = function handleKeyDown(e) {
  117. if (e.keyCode === 13) {
  118. emit('pressEnter', e);
  119. }
  120. emit('keydown', e);
  121. };
  122. var onBlur = function onBlur(e) {
  123. var onBlur = props.onBlur;
  124. onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
  125. formItemContext.onFieldBlur();
  126. };
  127. var triggerChange = function triggerChange(e) {
  128. emit('update:value', e.target.value);
  129. emit('change', e);
  130. emit('input', e);
  131. formItemContext.onFieldChange();
  132. };
  133. var handleReset = function handleReset(e) {
  134. resolveOnChange(resizableTextArea.value.textArea, e, triggerChange);
  135. setValue('', function () {
  136. focus();
  137. });
  138. };
  139. var handleChange = function handleChange(e) {
  140. var composing = e.target.composing;
  141. var triggerValue = e.target.value;
  142. compositing.value = !!(e.isComposing || composing);
  143. if (compositing.value && props.lazy || stateValue.value === triggerValue) return;
  144. if (hasMaxLength.value) {
  145. // 1. 复制粘贴超过maxlength的情况 2.未超过maxlength的情况
  146. var target = e.target;
  147. var isCursorInEnd = target.selectionStart >= props.maxlength + 1 || target.selectionStart === triggerValue.length || !target.selectionStart;
  148. triggerValue = setTriggerValue(isCursorInEnd, mergedValue.value, triggerValue, props.maxlength);
  149. }
  150. resolveOnChange(e.currentTarget, e, triggerChange, triggerValue);
  151. setValue(triggerValue);
  152. };
  153. var renderTextArea = function renderTextArea() {
  154. var _class, _props$valueModifiers, _resizeProps$id;
  155. var style = attrs.style,
  156. customClass = attrs.class;
  157. var _props$bordered = props.bordered,
  158. bordered = _props$bordered === void 0 ? true : _props$bordered;
  159. var resizeProps = _objectSpread(_objectSpread(_objectSpread({}, omit(props, ['allowClear'])), attrs), {}, {
  160. style: showCount.value ? {} : style,
  161. class: (_class = {}, _defineProperty(_class, "".concat(prefixCls.value, "-borderless"), !bordered), _defineProperty(_class, "".concat(customClass), customClass && !showCount.value), _defineProperty(_class, "".concat(prefixCls.value, "-sm"), size.value === 'small'), _defineProperty(_class, "".concat(prefixCls.value, "-lg"), size.value === 'large'), _class),
  162. showCount: null,
  163. prefixCls: prefixCls.value,
  164. onInput: handleChange,
  165. onChange: handleChange,
  166. onBlur: onBlur,
  167. onKeydown: handleKeyDown,
  168. onCompositionstart: onInternalCompositionStart,
  169. onCompositionend: onInternalCompositionEnd
  170. });
  171. if ((_props$valueModifiers = props.valueModifiers) !== null && _props$valueModifiers !== void 0 && _props$valueModifiers.lazy) {
  172. delete resizeProps.onInput;
  173. }
  174. return _createVNode(ResizableTextArea, _objectSpread(_objectSpread({}, resizeProps), {}, {
  175. "id": (_resizeProps$id = resizeProps.id) !== null && _resizeProps$id !== void 0 ? _resizeProps$id : formItemContext.id.value,
  176. "ref": resizableTextArea,
  177. "maxlength": props.maxlength
  178. }), null);
  179. };
  180. expose({
  181. focus: focus,
  182. blur: blur,
  183. resizableTextArea: resizableTextArea
  184. });
  185. watchEffect(function () {
  186. var val = fixControlledValue(stateValue.value);
  187. if (!compositing.value && hasMaxLength.value && (props.value === null || props.value === undefined)) {
  188. // fix #27612 将value转为数组进行截取,解决 '😂'.length === 2 等emoji表情导致的截取乱码的问题
  189. val = fixEmojiLength(val, props.maxlength);
  190. }
  191. mergedValue.value = val;
  192. });
  193. return function () {
  194. var maxlength = props.maxlength,
  195. _props$bordered2 = props.bordered,
  196. bordered = _props$bordered2 === void 0 ? true : _props$bordered2,
  197. hidden = props.hidden;
  198. var style = attrs.style,
  199. customClass = attrs.class;
  200. var inputProps = _objectSpread(_objectSpread(_objectSpread({}, props), attrs), {}, {
  201. prefixCls: prefixCls.value,
  202. inputType: 'text',
  203. handleReset: handleReset,
  204. direction: direction.value,
  205. bordered: bordered,
  206. style: showCount.value ? undefined : style
  207. });
  208. var textareaNode = _createVNode(ClearableLabeledInput, _objectSpread(_objectSpread({}, inputProps), {}, {
  209. "value": mergedValue.value
  210. }), {
  211. element: renderTextArea
  212. });
  213. if (showCount.value) {
  214. var valueLength = _toConsumableArray(mergedValue.value).length;
  215. var dataCount = '';
  216. if (_typeof(showCount.value) === 'object') {
  217. dataCount = showCount.value.formatter({
  218. count: valueLength,
  219. maxlength: maxlength
  220. });
  221. } else {
  222. dataCount = "".concat(valueLength).concat(hasMaxLength.value ? " / ".concat(maxlength) : '');
  223. }
  224. var _textareaNode = function () {
  225. return textareaNode;
  226. }();
  227. textareaNode = _createVNode("div", {
  228. "hidden": hidden,
  229. "class": classNames("".concat(prefixCls.value, "-textarea"), _defineProperty({}, "".concat(prefixCls.value, "-textarea-rtl"), direction.value === 'rtl'), "".concat(prefixCls.value, "-textarea-show-count"), customClass),
  230. "style": style,
  231. "data-count": _typeof(dataCount) !== 'object' ? dataCount : undefined
  232. }, [textareaNode]);
  233. }
  234. return textareaNode;
  235. };
  236. }
  237. });