ResizableTextArea.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import _extends from "@babel/runtime/helpers/esm/extends";
  2. import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
  3. import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
  4. import _typeof from "@babel/runtime/helpers/esm/typeof";
  5. import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
  6. import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
  7. var _excluded = ["prefixCls", "defaultValue", "value", "autoSize", "onResize", "className", "style", "disabled", "onChange", "onInternalAutoSize"];
  8. import classNames from 'classnames';
  9. import ResizeObserver from 'rc-resize-observer';
  10. import useLayoutEffect from "rc-util/es/hooks/useLayoutEffect";
  11. import useMergedState from "rc-util/es/hooks/useMergedState";
  12. import raf from "rc-util/es/raf";
  13. import * as React from 'react';
  14. import calculateAutoSizeStyle from "./calculateNodeHeight";
  15. var RESIZE_START = 0;
  16. var RESIZE_MEASURING = 1;
  17. var RESIZE_STABLE = 2;
  18. var ResizableTextArea = /*#__PURE__*/React.forwardRef(function (props, ref) {
  19. var _ref = props,
  20. prefixCls = _ref.prefixCls,
  21. defaultValue = _ref.defaultValue,
  22. value = _ref.value,
  23. autoSize = _ref.autoSize,
  24. onResize = _ref.onResize,
  25. className = _ref.className,
  26. style = _ref.style,
  27. disabled = _ref.disabled,
  28. onChange = _ref.onChange,
  29. onInternalAutoSize = _ref.onInternalAutoSize,
  30. restProps = _objectWithoutProperties(_ref, _excluded);
  31. // =============================== Value ================================
  32. var _useMergedState = useMergedState(defaultValue, {
  33. value: value,
  34. postState: function postState(val) {
  35. return val !== null && val !== void 0 ? val : '';
  36. }
  37. }),
  38. _useMergedState2 = _slicedToArray(_useMergedState, 2),
  39. mergedValue = _useMergedState2[0],
  40. setMergedValue = _useMergedState2[1];
  41. var onInternalChange = function onInternalChange(event) {
  42. setMergedValue(event.target.value);
  43. onChange === null || onChange === void 0 || onChange(event);
  44. };
  45. // ================================ Ref =================================
  46. var textareaRef = React.useRef();
  47. React.useImperativeHandle(ref, function () {
  48. return {
  49. textArea: textareaRef.current
  50. };
  51. });
  52. // ============================== AutoSize ==============================
  53. var _React$useMemo = React.useMemo(function () {
  54. if (autoSize && _typeof(autoSize) === 'object') {
  55. return [autoSize.minRows, autoSize.maxRows];
  56. }
  57. return [];
  58. }, [autoSize]),
  59. _React$useMemo2 = _slicedToArray(_React$useMemo, 2),
  60. minRows = _React$useMemo2[0],
  61. maxRows = _React$useMemo2[1];
  62. var needAutoSize = !!autoSize;
  63. // =============================== Resize ===============================
  64. var _React$useState = React.useState(RESIZE_STABLE),
  65. _React$useState2 = _slicedToArray(_React$useState, 2),
  66. resizeState = _React$useState2[0],
  67. setResizeState = _React$useState2[1];
  68. var _React$useState3 = React.useState(),
  69. _React$useState4 = _slicedToArray(_React$useState3, 2),
  70. autoSizeStyle = _React$useState4[0],
  71. setAutoSizeStyle = _React$useState4[1];
  72. var startResize = function startResize() {
  73. setResizeState(RESIZE_START);
  74. if (process.env.NODE_ENV === 'test') {
  75. onInternalAutoSize === null || onInternalAutoSize === void 0 || onInternalAutoSize();
  76. }
  77. };
  78. // Change to trigger resize measure
  79. useLayoutEffect(function () {
  80. if (needAutoSize) {
  81. startResize();
  82. }
  83. }, [value, minRows, maxRows, needAutoSize]);
  84. useLayoutEffect(function () {
  85. if (resizeState === RESIZE_START) {
  86. setResizeState(RESIZE_MEASURING);
  87. } else if (resizeState === RESIZE_MEASURING) {
  88. var textareaStyles = calculateAutoSizeStyle(textareaRef.current, false, minRows, maxRows);
  89. // Safari has bug that text will keep break line on text cut when it's prev is break line.
  90. // ZombieJ: This not often happen. So we just skip it.
  91. // const { selectionStart, selectionEnd, scrollTop } = textareaRef.current;
  92. // const { value: tmpValue } = textareaRef.current;
  93. // textareaRef.current.value = '';
  94. // textareaRef.current.value = tmpValue;
  95. // if (document.activeElement === textareaRef.current) {
  96. // textareaRef.current.scrollTop = scrollTop;
  97. // textareaRef.current.setSelectionRange(selectionStart, selectionEnd);
  98. // }
  99. setResizeState(RESIZE_STABLE);
  100. setAutoSizeStyle(textareaStyles);
  101. } else {
  102. // https://github.com/react-component/textarea/pull/23
  103. // Firefox has blink issue before but fixed in latest version.
  104. }
  105. }, [resizeState]);
  106. // We lock resize trigger by raf to avoid Safari warning
  107. var resizeRafRef = React.useRef();
  108. var cleanRaf = function cleanRaf() {
  109. raf.cancel(resizeRafRef.current);
  110. };
  111. var onInternalResize = function onInternalResize(size) {
  112. if (resizeState === RESIZE_STABLE) {
  113. onResize === null || onResize === void 0 || onResize(size);
  114. if (autoSize) {
  115. cleanRaf();
  116. resizeRafRef.current = raf(function () {
  117. startResize();
  118. });
  119. }
  120. }
  121. };
  122. React.useEffect(function () {
  123. return cleanRaf;
  124. }, []);
  125. // =============================== Render ===============================
  126. var mergedAutoSizeStyle = needAutoSize ? autoSizeStyle : null;
  127. var mergedStyle = _objectSpread(_objectSpread({}, style), mergedAutoSizeStyle);
  128. if (resizeState === RESIZE_START || resizeState === RESIZE_MEASURING) {
  129. mergedStyle.overflowY = 'hidden';
  130. mergedStyle.overflowX = 'hidden';
  131. }
  132. return /*#__PURE__*/React.createElement(ResizeObserver, {
  133. onResize: onInternalResize,
  134. disabled: !(autoSize || onResize)
  135. }, /*#__PURE__*/React.createElement("textarea", _extends({}, restProps, {
  136. ref: textareaRef,
  137. style: mergedStyle,
  138. className: classNames(prefixCls, className, _defineProperty({}, "".concat(prefixCls, "-disabled"), disabled)),
  139. disabled: disabled,
  140. value: mergedValue,
  141. onChange: onInternalChange
  142. })));
  143. });
  144. export default ResizableTextArea;