UNPKG

7.02 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3const _excluded = ["onChange", "maxRows", "minRows", "style", "value"];
4import * as React from 'react';
5import PropTypes from 'prop-types';
6import { unstable_debounce as debounce, unstable_useForkRef as useForkRef, unstable_useEnhancedEffect as useEnhancedEffect, unstable_ownerWindow as ownerWindow } from '@mui/utils';
7import { jsx as _jsx } from "react/jsx-runtime";
8import { jsxs as _jsxs } from "react/jsx-runtime";
9
10function getStyleValue(computedStyle, property) {
11 return parseInt(computedStyle[property], 10) || 0;
12}
13
14const styles = {
15 shadow: {
16 // Visibility needed to hide the extra text area on iPads
17 visibility: 'hidden',
18 // Remove from the content flow
19 position: 'absolute',
20 // Ignore the scrollbar width
21 overflow: 'hidden',
22 height: 0,
23 top: 0,
24 left: 0,
25 // Create a new layer, increase the isolation of the computed values
26 transform: 'translateZ(0)'
27 }
28};
29const TextareaAutosize = /*#__PURE__*/React.forwardRef(function TextareaAutosize(props, ref) {
30 const {
31 onChange,
32 maxRows,
33 minRows = 1,
34 style,
35 value
36 } = props,
37 other = _objectWithoutPropertiesLoose(props, _excluded);
38
39 const {
40 current: isControlled
41 } = React.useRef(value != null);
42 const inputRef = React.useRef(null);
43 const handleRef = useForkRef(ref, inputRef);
44 const shadowRef = React.useRef(null);
45 const renders = React.useRef(0);
46 const [state, setState] = React.useState({});
47 const syncHeight = React.useCallback(() => {
48 const input = inputRef.current;
49 const containerWindow = ownerWindow(input);
50 const computedStyle = containerWindow.getComputedStyle(input); // If input's width is shrunk and it's not visible, don't sync height.
51
52 if (computedStyle.width === '0px') {
53 return;
54 }
55
56 const inputShallow = shadowRef.current;
57 inputShallow.style.width = computedStyle.width;
58 inputShallow.value = input.value || props.placeholder || 'x';
59
60 if (inputShallow.value.slice(-1) === '\n') {
61 // Certain fonts which overflow the line height will cause the textarea
62 // to report a different scrollHeight depending on whether the last line
63 // is empty. Make it non-empty to avoid this issue.
64 inputShallow.value += ' ';
65 }
66
67 const boxSizing = computedStyle['box-sizing'];
68 const padding = getStyleValue(computedStyle, 'padding-bottom') + getStyleValue(computedStyle, 'padding-top');
69 const border = getStyleValue(computedStyle, 'border-bottom-width') + getStyleValue(computedStyle, 'border-top-width'); // The height of the inner content
70
71 const innerHeight = inputShallow.scrollHeight; // Measure height of a textarea with a single row
72
73 inputShallow.value = 'x';
74 const singleRowHeight = inputShallow.scrollHeight; // The height of the outer content
75
76 let outerHeight = innerHeight;
77
78 if (minRows) {
79 outerHeight = Math.max(Number(minRows) * singleRowHeight, outerHeight);
80 }
81
82 if (maxRows) {
83 outerHeight = Math.min(Number(maxRows) * singleRowHeight, outerHeight);
84 }
85
86 outerHeight = Math.max(outerHeight, singleRowHeight); // Take the box sizing into account for applying this value as a style.
87
88 const outerHeightStyle = outerHeight + (boxSizing === 'border-box' ? padding + border : 0);
89 const overflow = Math.abs(outerHeight - innerHeight) <= 1;
90 setState(prevState => {
91 // Need a large enough difference to update the height.
92 // This prevents infinite rendering loop.
93 if (renders.current < 20 && (outerHeightStyle > 0 && Math.abs((prevState.outerHeightStyle || 0) - outerHeightStyle) > 1 || prevState.overflow !== overflow)) {
94 renders.current += 1;
95 return {
96 overflow,
97 outerHeightStyle
98 };
99 }
100
101 if (process.env.NODE_ENV !== 'production') {
102 if (renders.current === 20) {
103 console.error(['MUI: Too many re-renders. The layout is unstable.', 'TextareaAutosize limits the number of renders to prevent an infinite loop.'].join('\n'));
104 }
105 }
106
107 return prevState;
108 });
109 }, [maxRows, minRows, props.placeholder]);
110 React.useEffect(() => {
111 const handleResize = debounce(() => {
112 renders.current = 0;
113 syncHeight();
114 });
115 const containerWindow = ownerWindow(inputRef.current);
116 containerWindow.addEventListener('resize', handleResize);
117 let resizeObserver;
118
119 if (typeof ResizeObserver !== 'undefined') {
120 resizeObserver = new ResizeObserver(handleResize);
121 resizeObserver.observe(inputRef.current);
122 }
123
124 return () => {
125 handleResize.clear();
126 containerWindow.removeEventListener('resize', handleResize);
127
128 if (resizeObserver) {
129 resizeObserver.disconnect();
130 }
131 };
132 }, [syncHeight]);
133 useEnhancedEffect(() => {
134 syncHeight();
135 });
136 React.useEffect(() => {
137 renders.current = 0;
138 }, [value]);
139
140 const handleChange = event => {
141 renders.current = 0;
142
143 if (!isControlled) {
144 syncHeight();
145 }
146
147 if (onChange) {
148 onChange(event);
149 }
150 };
151
152 return /*#__PURE__*/_jsxs(React.Fragment, {
153 children: [/*#__PURE__*/_jsx("textarea", _extends({
154 value: value,
155 onChange: handleChange,
156 ref: handleRef // Apply the rows prop to get a "correct" first SSR paint
157 ,
158 rows: minRows,
159 style: _extends({
160 height: state.outerHeightStyle,
161 // Need a large enough difference to allow scrolling.
162 // This prevents infinite rendering loop.
163 overflow: state.overflow ? 'hidden' : null
164 }, style)
165 }, other)), /*#__PURE__*/_jsx("textarea", {
166 "aria-hidden": true,
167 className: props.className,
168 readOnly: true,
169 ref: shadowRef,
170 tabIndex: -1,
171 style: _extends({}, styles.shadow, style, {
172 padding: 0
173 })
174 })]
175 });
176});
177process.env.NODE_ENV !== "production" ? TextareaAutosize.propTypes
178/* remove-proptypes */
179= {
180 // ----------------------------- Warning --------------------------------
181 // | These PropTypes are generated from the TypeScript type definitions |
182 // | To update them edit the d.ts file and run "yarn proptypes" |
183 // ----------------------------------------------------------------------
184
185 /**
186 * @ignore
187 */
188 className: PropTypes.string,
189
190 /**
191 * Maximum number of rows to display.
192 */
193 maxRows: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
194
195 /**
196 * Minimum number of rows to display.
197 * @default 1
198 */
199 minRows: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
200
201 /**
202 * @ignore
203 */
204 onChange: PropTypes.func,
205
206 /**
207 * @ignore
208 */
209 placeholder: PropTypes.string,
210
211 /**
212 * @ignore
213 */
214 style: PropTypes.object,
215
216 /**
217 * @ignore
218 */
219 value: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.number, PropTypes.string])
220} : void 0;
221export default TextareaAutosize;
\No newline at end of file