UNPKG

7.4 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var _extends = require('@babel/runtime/helpers/extends');
6var _objectWithoutPropertiesLoose = require('@babel/runtime/helpers/objectWithoutPropertiesLoose');
7var React = require('react');
8var useLatest = require('use-latest');
9var useComposedRef = require('use-composed-ref');
10
11function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
12
13var useLatest__default = /*#__PURE__*/_interopDefault(useLatest);
14var useComposedRef__default = /*#__PURE__*/_interopDefault(useComposedRef);
15
16var HIDDEN_TEXTAREA_STYLE = {
17 'min-height': '0',
18 'max-height': 'none',
19 height: '0',
20 visibility: 'hidden',
21 overflow: 'hidden',
22 position: 'absolute',
23 'z-index': '-1000',
24 top: '0',
25 right: '0'
26};
27
28var forceHiddenStyles = function forceHiddenStyles(node) {
29 Object.keys(HIDDEN_TEXTAREA_STYLE).forEach(function (key) {
30 node.style.setProperty(key, HIDDEN_TEXTAREA_STYLE[key], 'important');
31 });
32};
33
34// export type CalculatedNodeHeights = [height: number, rowHeight: number];
35// https://github.com/microsoft/TypeScript/issues/28259
36
37var hiddenTextarea = null;
38
39var getHeight = function getHeight(node, sizingData) {
40 var height = node.scrollHeight;
41
42 if (sizingData.sizingStyle.boxSizing === 'border-box') {
43 // border-box: add border, since height = content + padding + border
44 return height + sizingData.borderSize;
45 } // remove padding, since height = content
46
47
48 return height - sizingData.paddingSize;
49};
50
51function calculateNodeHeight(sizingData, value, minRows, maxRows) {
52 if (minRows === void 0) {
53 minRows = 1;
54 }
55
56 if (maxRows === void 0) {
57 maxRows = Infinity;
58 }
59
60 if (!hiddenTextarea) {
61 hiddenTextarea = document.createElement('textarea');
62 hiddenTextarea.setAttribute('tabindex', '-1');
63 hiddenTextarea.setAttribute('aria-hidden', 'true');
64 forceHiddenStyles(hiddenTextarea);
65 }
66
67 if (hiddenTextarea.parentNode === null) {
68 document.body.appendChild(hiddenTextarea);
69 }
70
71 var paddingSize = sizingData.paddingSize,
72 borderSize = sizingData.borderSize,
73 sizingStyle = sizingData.sizingStyle;
74 var boxSizing = sizingStyle.boxSizing;
75 Object.keys(sizingStyle).forEach(function (_key) {
76 var key = _key;
77 hiddenTextarea.style[key] = sizingStyle[key];
78 });
79 forceHiddenStyles(hiddenTextarea);
80 hiddenTextarea.value = value;
81 var height = getHeight(hiddenTextarea, sizingData); // measure height of a textarea with a single row
82
83 hiddenTextarea.value = 'x';
84 var rowHeight = hiddenTextarea.scrollHeight - paddingSize;
85 var minHeight = rowHeight * minRows;
86
87 if (boxSizing === 'border-box') {
88 minHeight = minHeight + paddingSize + borderSize;
89 }
90
91 height = Math.max(minHeight, height);
92 var maxHeight = rowHeight * maxRows;
93
94 if (boxSizing === 'border-box') {
95 maxHeight = maxHeight + paddingSize + borderSize;
96 }
97
98 height = Math.min(maxHeight, height);
99 return [height, rowHeight];
100}
101
102var noop = function noop() {};
103var pick = function pick(props, obj) {
104 return props.reduce(function (acc, prop) {
105 acc[prop] = obj[prop];
106 return acc;
107 }, {});
108};
109
110var SIZING_STYLE = ['borderBottomWidth', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth', 'boxSizing', 'fontFamily', 'fontSize', 'fontStyle', 'fontWeight', 'letterSpacing', 'lineHeight', 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', // non-standard
111'tabSize', 'textIndent', // non-standard
112'textRendering', 'textTransform', 'width', 'wordBreak'];
113var isIE = !!document.documentElement.currentStyle ;
114
115var getSizingData = function getSizingData(node) {
116 var style = window.getComputedStyle(node);
117
118 if (style === null) {
119 return null;
120 }
121
122 var sizingStyle = pick(SIZING_STYLE, style);
123 var boxSizing = sizingStyle.boxSizing; // probably node is detached from DOM, can't read computed dimensions
124
125 if (boxSizing === '') {
126 return null;
127 } // IE (Edge has already correct behaviour) returns content width as computed width
128 // so we need to add manually padding and border widths
129
130
131 if (isIE && boxSizing === 'border-box') {
132 sizingStyle.width = parseFloat(sizingStyle.width) + parseFloat(sizingStyle.borderRightWidth) + parseFloat(sizingStyle.borderLeftWidth) + parseFloat(sizingStyle.paddingRight) + parseFloat(sizingStyle.paddingLeft) + 'px';
133 }
134
135 var paddingSize = parseFloat(sizingStyle.paddingBottom) + parseFloat(sizingStyle.paddingTop);
136 var borderSize = parseFloat(sizingStyle.borderBottomWidth) + parseFloat(sizingStyle.borderTopWidth);
137 return {
138 sizingStyle: sizingStyle,
139 paddingSize: paddingSize,
140 borderSize: borderSize
141 };
142};
143
144var useWindowResizeListener = function useWindowResizeListener(listener) {
145 var latestListener = useLatest__default['default'](listener);
146 React.useLayoutEffect(function () {
147 var handler = function handler(event) {
148 latestListener.current(event);
149 };
150
151 window.addEventListener('resize', handler);
152 return function () {
153 window.removeEventListener('resize', handler);
154 };
155 }, []);
156};
157
158var TextareaAutosize = function TextareaAutosize(_ref, userRef) {
159 var cacheMeasurements = _ref.cacheMeasurements,
160 maxRows = _ref.maxRows,
161 minRows = _ref.minRows,
162 _ref$onChange = _ref.onChange,
163 onChange = _ref$onChange === void 0 ? noop : _ref$onChange,
164 _ref$onHeightChange = _ref.onHeightChange,
165 onHeightChange = _ref$onHeightChange === void 0 ? noop : _ref$onHeightChange,
166 props = _objectWithoutPropertiesLoose(_ref, ["cacheMeasurements", "maxRows", "minRows", "onChange", "onHeightChange"]);
167
168 if (process.env.NODE_ENV !== 'production' && props.style) {
169 if ('maxHeight' in props.style) {
170 throw new Error('Using `style.maxHeight` for <TextareaAutosize/> is not supported. Please use `maxRows`.');
171 }
172
173 if ('minHeight' in props.style) {
174 throw new Error('Using `style.minHeight` for <TextareaAutosize/> is not supported. Please use `minRows`.');
175 }
176 }
177
178 var isControlled = props.value !== undefined;
179 var libRef = React.useRef(null);
180 var ref = useComposedRef__default['default'](libRef, userRef);
181 var heightRef = React.useRef(0);
182 var measurementsCacheRef = React.useRef();
183
184 var resizeTextarea = function resizeTextarea() {
185 var node = libRef.current;
186 var nodeSizingData = cacheMeasurements && measurementsCacheRef.current ? measurementsCacheRef.current : getSizingData(node);
187
188 if (!nodeSizingData) {
189 return;
190 }
191
192 measurementsCacheRef.current = nodeSizingData;
193
194 var _calculateNodeHeight = calculateNodeHeight(nodeSizingData, node.value || node.placeholder || 'x', minRows, maxRows),
195 height = _calculateNodeHeight[0],
196 rowHeight = _calculateNodeHeight[1];
197
198 if (heightRef.current !== height) {
199 heightRef.current = height;
200 node.style.setProperty('height', height + "px", 'important');
201 onHeightChange(height, {
202 rowHeight: rowHeight
203 });
204 }
205 };
206
207 var handleChange = function handleChange(event) {
208 if (!isControlled) {
209 resizeTextarea();
210 }
211
212 onChange(event);
213 };
214
215 {
216 React.useLayoutEffect(resizeTextarea);
217 useWindowResizeListener(resizeTextarea);
218 }
219
220 return /*#__PURE__*/React.createElement("textarea", _extends({}, props, {
221 onChange: handleChange,
222 ref: ref
223 }));
224};
225
226var index = /* #__PURE__ */React.forwardRef(TextareaAutosize);
227
228exports.default = index;