ResizableTextArea.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  2. import _extends from "@babel/runtime/helpers/esm/extends";
  3. import { createVNode as _createVNode } from "vue";
  4. import { computed, watchEffect, getCurrentInstance, watch, onBeforeUnmount, ref, defineComponent } from 'vue';
  5. import ResizeObserver from '../vc-resize-observer';
  6. import classNames from '../_util/classNames';
  7. import raf from '../_util/raf';
  8. import warning from '../_util/warning';
  9. import omit from '../_util/omit';
  10. import { textAreaProps } from './inputProps';
  11. import calculateAutoSizeStyle from './calculateNodeHeight';
  12. import BaseInput from '../_util/BaseInput';
  13. const RESIZE_START = 0;
  14. const RESIZE_MEASURING = 1;
  15. const RESIZE_STABLE = 2;
  16. const ResizableTextArea = defineComponent({
  17. compatConfig: {
  18. MODE: 3
  19. },
  20. name: 'ResizableTextArea',
  21. inheritAttrs: false,
  22. props: textAreaProps(),
  23. setup(props, _ref) {
  24. let {
  25. attrs,
  26. emit,
  27. expose
  28. } = _ref;
  29. let nextFrameActionId;
  30. let resizeFrameId;
  31. const textAreaRef = ref();
  32. const textareaStyles = ref({});
  33. const resizeStatus = ref(RESIZE_STABLE);
  34. onBeforeUnmount(() => {
  35. raf.cancel(nextFrameActionId);
  36. raf.cancel(resizeFrameId);
  37. });
  38. // https://github.com/ant-design/ant-design/issues/21870
  39. const fixFirefoxAutoScroll = () => {
  40. try {
  41. if (textAreaRef.value && document.activeElement === textAreaRef.value.input) {
  42. const currentStart = textAreaRef.value.getSelectionStart();
  43. const currentEnd = textAreaRef.value.getSelectionEnd();
  44. const scrollTop = textAreaRef.value.getScrollTop();
  45. textAreaRef.value.setSelectionRange(currentStart, currentEnd);
  46. textAreaRef.value.setScrollTop(scrollTop);
  47. }
  48. } catch (e) {
  49. // Fix error in Chrome:
  50. // Failed to read the 'selectionStart' property from 'HTMLInputElement'
  51. // http://stackoverflow.com/q/21177489/3040605
  52. }
  53. };
  54. const minRows = ref();
  55. const maxRows = ref();
  56. watchEffect(() => {
  57. const autoSize = props.autoSize || props.autosize;
  58. if (autoSize) {
  59. minRows.value = autoSize.minRows;
  60. maxRows.value = autoSize.maxRows;
  61. } else {
  62. minRows.value = undefined;
  63. maxRows.value = undefined;
  64. }
  65. });
  66. const needAutoSize = computed(() => !!(props.autoSize || props.autosize));
  67. const startResize = () => {
  68. resizeStatus.value = RESIZE_START;
  69. };
  70. watch([() => props.value, minRows, maxRows, needAutoSize], () => {
  71. if (needAutoSize.value) {
  72. startResize();
  73. }
  74. }, {
  75. immediate: true
  76. });
  77. const autoSizeStyle = ref();
  78. watch([resizeStatus, textAreaRef], () => {
  79. if (!textAreaRef.value) return;
  80. if (resizeStatus.value === RESIZE_START) {
  81. resizeStatus.value = RESIZE_MEASURING;
  82. } else if (resizeStatus.value === RESIZE_MEASURING) {
  83. const textareaStyles = calculateAutoSizeStyle(textAreaRef.value.input, false, minRows.value, maxRows.value);
  84. resizeStatus.value = RESIZE_STABLE;
  85. autoSizeStyle.value = textareaStyles;
  86. } else {
  87. fixFirefoxAutoScroll();
  88. }
  89. }, {
  90. immediate: true,
  91. flush: 'post'
  92. });
  93. const instance = getCurrentInstance();
  94. const resizeRafRef = ref();
  95. const cleanRaf = () => {
  96. raf.cancel(resizeRafRef.value);
  97. };
  98. const onInternalResize = size => {
  99. if (resizeStatus.value === RESIZE_STABLE) {
  100. emit('resize', size);
  101. if (needAutoSize.value) {
  102. cleanRaf();
  103. resizeRafRef.value = raf(() => {
  104. startResize();
  105. });
  106. }
  107. }
  108. };
  109. onBeforeUnmount(() => {
  110. cleanRaf();
  111. });
  112. const resizeTextarea = () => {
  113. startResize();
  114. };
  115. expose({
  116. resizeTextarea,
  117. textArea: computed(() => {
  118. var _a;
  119. return (_a = textAreaRef.value) === null || _a === void 0 ? void 0 : _a.input;
  120. }),
  121. instance
  122. });
  123. warning(props.autosize === undefined, 'Input.TextArea', 'autosize is deprecated, please use autoSize instead.');
  124. const renderTextArea = () => {
  125. const {
  126. prefixCls,
  127. disabled
  128. } = props;
  129. const otherProps = omit(props, ['prefixCls', 'onPressEnter', 'autoSize', 'autosize', 'defaultValue', 'allowClear', 'type', 'maxlength', 'valueModifiers']);
  130. const cls = classNames(prefixCls, attrs.class, {
  131. [`${prefixCls}-disabled`]: disabled
  132. });
  133. const mergedAutoSizeStyle = needAutoSize.value ? autoSizeStyle.value : null;
  134. const style = [attrs.style, textareaStyles.value, mergedAutoSizeStyle];
  135. const textareaProps = _extends(_extends(_extends({}, otherProps), attrs), {
  136. style,
  137. class: cls
  138. });
  139. if (resizeStatus.value === RESIZE_START || resizeStatus.value === RESIZE_MEASURING) {
  140. style.push({
  141. overflowX: 'hidden',
  142. overflowY: 'hidden'
  143. });
  144. }
  145. if (!textareaProps.autofocus) {
  146. delete textareaProps.autofocus;
  147. }
  148. if (textareaProps.rows === 0) {
  149. delete textareaProps.rows;
  150. }
  151. return _createVNode(ResizeObserver, {
  152. "onResize": onInternalResize,
  153. "disabled": !needAutoSize.value
  154. }, {
  155. default: () => [_createVNode(BaseInput, _objectSpread(_objectSpread({}, textareaProps), {}, {
  156. "ref": textAreaRef,
  157. "tag": "textarea"
  158. }), null)]
  159. });
  160. };
  161. return () => {
  162. return renderTextArea();
  163. };
  164. }
  165. });
  166. export default ResizableTextArea;