-
-
Notifications
You must be signed in to change notification settings - Fork 245
/
index.tsx
101 lines (88 loc) · 2.74 KB
/
index.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import * as React from 'react';
import calculateNodeHeight from './calculateNodeHeight';
import getSizingData, { SizingData } from './getSizingData';
import { useComposedRef, useWindowResizeListener } from './hooks';
import { noop } from './utils';
type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
type Style = Omit<
NonNullable<TextareaProps['style']>,
'maxHeight' | 'minHeight'
> & {
height?: number;
};
export type TextareaHeightChangeMeta = {
rowHeight: number;
};
export type TextareaAutosizeProps = Omit<TextareaProps, 'style'> & {
maxRows?: number;
minRows?: number;
onHeightChange?: (height: number, meta: TextareaHeightChangeMeta) => void;
cacheMeasurements?: boolean;
style?: Style;
};
const TextareaAutosize: React.ForwardRefRenderFunction<
HTMLTextAreaElement,
TextareaAutosizeProps
> = (
{
cacheMeasurements,
maxRows,
minRows,
onChange = noop,
onHeightChange = noop,
...props
},
userRef: React.Ref<HTMLTextAreaElement>,
) => {
if (process.env.NODE_ENV !== 'production' && props.style) {
if ('maxHeight' in props.style) {
throw new Error(
'Using `style.maxHeight` for <TextareaAutosize/> is not supported. Please use `maxRows`.',
);
}
if ('minHeight' in props.style) {
throw new Error(
'Using `style.minHeight` for <TextareaAutosize/> is not supported. Please use `minRows`.',
);
}
}
const isControlled = props.value !== undefined;
const libRef = React.useRef<HTMLTextAreaElement | null>(null);
const ref = useComposedRef(libRef, userRef);
const heightRef = React.useRef(0);
const measurementsCacheRef = React.useRef<SizingData>();
const resizeTextarea = () => {
const node = libRef.current!;
const nodeSizingData =
cacheMeasurements && measurementsCacheRef.current
? measurementsCacheRef.current
: getSizingData(node);
if (!nodeSizingData) {
return;
}
measurementsCacheRef.current = nodeSizingData;
const [height, rowHeight] = calculateNodeHeight(
nodeSizingData,
node.value || node.placeholder || 'x',
minRows,
maxRows,
);
if (heightRef.current !== height) {
heightRef.current = height;
node.style.setProperty('height', `${height}px`, 'important');
onHeightChange(height, { rowHeight });
}
};
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
if (!isControlled) {
resizeTextarea();
}
onChange(event);
};
if (typeof document !== 'undefined') {
React.useLayoutEffect(resizeTextarea);
}
useWindowResizeListener(resizeTextarea);
return <textarea {...props} onChange={handleChange} ref={ref} />;
};
export default /* #__PURE__ */ React.forwardRef(TextareaAutosize);