UNPKG

10.5 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4Object.defineProperty(exports, "__esModule", {
5 value: true
6});
7exports.default = void 0;
8var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
9var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
10var React = _interopRequireWildcard(require("react"));
11var _propTypes = _interopRequireDefault(require("prop-types"));
12var ReactDOM = _interopRequireWildcard(require("react-dom"));
13var _utils = require("@mui/utils");
14var _jsxRuntime = require("react/jsx-runtime");
15const _excluded = ["onChange", "maxRows", "minRows", "style", "value"];
16function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
17function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
18function getStyleValue(value) {
19 return parseInt(value, 10) || 0;
20}
21const styles = {
22 shadow: {
23 // Visibility needed to hide the extra text area on iPads
24 visibility: 'hidden',
25 // Remove from the content flow
26 position: 'absolute',
27 // Ignore the scrollbar width
28 overflow: 'hidden',
29 height: 0,
30 top: 0,
31 left: 0,
32 // Create a new layer, increase the isolation of the computed values
33 transform: 'translateZ(0)'
34 }
35};
36function isEmpty(obj) {
37 return obj === undefined || obj === null || Object.keys(obj).length === 0 || obj.outerHeightStyle === 0 && !obj.overflow;
38}
39
40/**
41 *
42 * Demos:
43 *
44 * - [Textarea Autosize](https://mui.com/base/react-textarea-autosize/)
45 * - [Textarea Autosize](https://mui.com/material-ui/react-textarea-autosize/)
46 *
47 * API:
48 *
49 * - [TextareaAutosize API](https://mui.com/base/react-textarea-autosize/components-api/#textarea-autosize)
50 */
51const TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize(props, forwardedRef) {
52 const {
53 onChange,
54 maxRows,
55 minRows = 1,
56 style,
57 value
58 } = props,
59 other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
60 const {
61 current: isControlled
62 } = React.useRef(value != null);
63 const inputRef = React.useRef(null);
64 const handleRef = (0, _utils.unstable_useForkRef)(forwardedRef, inputRef);
65 const shadowRef = React.useRef(null);
66 const renders = React.useRef(0);
67 const [state, setState] = React.useState({
68 outerHeightStyle: 0
69 });
70 const getUpdatedState = React.useCallback(() => {
71 const input = inputRef.current;
72 const containerWindow = (0, _utils.unstable_ownerWindow)(input);
73 const computedStyle = containerWindow.getComputedStyle(input);
74
75 // If input's width is shrunk and it's not visible, don't sync height.
76 if (computedStyle.width === '0px') {
77 return {
78 outerHeightStyle: 0
79 };
80 }
81 const inputShallow = shadowRef.current;
82 inputShallow.style.width = computedStyle.width;
83 inputShallow.value = input.value || props.placeholder || 'x';
84 if (inputShallow.value.slice(-1) === '\n') {
85 // Certain fonts which overflow the line height will cause the textarea
86 // to report a different scrollHeight depending on whether the last line
87 // is empty. Make it non-empty to avoid this issue.
88 inputShallow.value += ' ';
89 }
90 const boxSizing = computedStyle.boxSizing;
91 const padding = getStyleValue(computedStyle.paddingBottom) + getStyleValue(computedStyle.paddingTop);
92 const border = getStyleValue(computedStyle.borderBottomWidth) + getStyleValue(computedStyle.borderTopWidth);
93
94 // The height of the inner content
95 const innerHeight = inputShallow.scrollHeight;
96
97 // Measure height of a textarea with a single row
98 inputShallow.value = 'x';
99 const singleRowHeight = inputShallow.scrollHeight;
100
101 // The height of the outer content
102 let outerHeight = innerHeight;
103 if (minRows) {
104 outerHeight = Math.max(Number(minRows) * singleRowHeight, outerHeight);
105 }
106 if (maxRows) {
107 outerHeight = Math.min(Number(maxRows) * singleRowHeight, outerHeight);
108 }
109 outerHeight = Math.max(outerHeight, singleRowHeight);
110
111 // Take the box sizing into account for applying this value as a style.
112 const outerHeightStyle = outerHeight + (boxSizing === 'border-box' ? padding + border : 0);
113 const overflow = Math.abs(outerHeight - innerHeight) <= 1;
114 return {
115 outerHeightStyle,
116 overflow
117 };
118 }, [maxRows, minRows, props.placeholder]);
119 const updateState = (prevState, newState) => {
120 const {
121 outerHeightStyle,
122 overflow
123 } = newState;
124 // Need a large enough difference to update the height.
125 // This prevents infinite rendering loop.
126 if (renders.current < 20 && (outerHeightStyle > 0 && Math.abs((prevState.outerHeightStyle || 0) - outerHeightStyle) > 1 || prevState.overflow !== overflow)) {
127 renders.current += 1;
128 return {
129 overflow,
130 outerHeightStyle
131 };
132 }
133 if (process.env.NODE_ENV !== 'production') {
134 if (renders.current === 20) {
135 console.error(['MUI: Too many re-renders. The layout is unstable.', 'TextareaAutosize limits the number of renders to prevent an infinite loop.'].join('\n'));
136 }
137 }
138 return prevState;
139 };
140 const syncHeight = React.useCallback(() => {
141 const newState = getUpdatedState();
142 if (isEmpty(newState)) {
143 return;
144 }
145 setState(prevState => {
146 return updateState(prevState, newState);
147 });
148 }, [getUpdatedState]);
149 const syncHeightWithFlushSync = () => {
150 const newState = getUpdatedState();
151 if (isEmpty(newState)) {
152 return;
153 }
154
155 // In React 18, state updates in a ResizeObserver's callback are happening after the paint which causes flickering
156 // when doing some visual updates in it. Using flushSync ensures that the dom will be painted after the states updates happen
157 // Related issue - https://github.com/facebook/react/issues/24331
158 ReactDOM.flushSync(() => {
159 setState(prevState => {
160 return updateState(prevState, newState);
161 });
162 });
163 };
164 React.useEffect(() => {
165 const handleResize = (0, _utils.unstable_debounce)(() => {
166 renders.current = 0;
167
168 // If the TextareaAutosize component is replaced by Suspense with a fallback, the last
169 // ResizeObserver's handler that runs because of the change in the layout is trying to
170 // access a dom node that is no longer there (as the fallback component is being shown instead).
171 // See https://github.com/mui/material-ui/issues/32640
172 if (inputRef.current) {
173 syncHeightWithFlushSync();
174 }
175 });
176 let resizeObserver;
177 const input = inputRef.current;
178 const containerWindow = (0, _utils.unstable_ownerWindow)(input);
179 containerWindow.addEventListener('resize', handleResize);
180 if (typeof ResizeObserver !== 'undefined') {
181 resizeObserver = new ResizeObserver(handleResize);
182 resizeObserver.observe(input);
183 }
184 return () => {
185 handleResize.clear();
186 containerWindow.removeEventListener('resize', handleResize);
187 if (resizeObserver) {
188 resizeObserver.disconnect();
189 }
190 };
191 });
192 (0, _utils.unstable_useEnhancedEffect)(() => {
193 syncHeight();
194 });
195 React.useEffect(() => {
196 renders.current = 0;
197 }, [value]);
198 const handleChange = event => {
199 renders.current = 0;
200 if (!isControlled) {
201 syncHeight();
202 }
203 if (onChange) {
204 onChange(event);
205 }
206 };
207 return /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, {
208 children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("textarea", (0, _extends2.default)({
209 value: value,
210 onChange: handleChange,
211 ref: handleRef
212 // Apply the rows prop to get a "correct" first SSR paint
213 ,
214 rows: minRows,
215 style: (0, _extends2.default)({
216 height: state.outerHeightStyle,
217 // Need a large enough difference to allow scrolling.
218 // This prevents infinite rendering loop.
219 overflow: state.overflow ? 'hidden' : undefined
220 }, style)
221 }, other)), /*#__PURE__*/(0, _jsxRuntime.jsx)("textarea", {
222 "aria-hidden": true,
223 className: props.className,
224 readOnly: true,
225 ref: shadowRef,
226 tabIndex: -1,
227 style: (0, _extends2.default)({}, styles.shadow, style, {
228 padding: 0
229 })
230 })]
231 });
232});
233process.env.NODE_ENV !== "production" ? TextareaAutosize.propTypes /* remove-proptypes */ = {
234 // ----------------------------- Warning --------------------------------
235 // | These PropTypes are generated from the TypeScript type definitions |
236 // | To update them edit TypeScript types and run "yarn proptypes" |
237 // ----------------------------------------------------------------------
238 /**
239 * @ignore
240 */
241 className: _propTypes.default.string,
242 /**
243 * Maximum number of rows to display.
244 */
245 maxRows: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]),
246 /**
247 * Minimum number of rows to display.
248 * @default 1
249 */
250 minRows: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]),
251 /**
252 * @ignore
253 */
254 onChange: _propTypes.default.func,
255 /**
256 * @ignore
257 */
258 placeholder: _propTypes.default.string,
259 /**
260 * @ignore
261 */
262 style: _propTypes.default.object,
263 /**
264 * @ignore
265 */
266 value: _propTypes.default.oneOfType([_propTypes.default.arrayOf(_propTypes.default.string), _propTypes.default.number, _propTypes.default.string])
267} : void 0;
268var _default = TextareaAutosize;
269exports.default = _default;
\No newline at end of file