1
- import React , { useEffect , useRef , useState } from 'react' ;
1
+ import React , { useLayoutEffect , useRef } from 'react' ;
2
2
import RowItem , { RowItemProps } from './RowItem' ;
3
3
import Help from './Help' ;
4
4
import { useTranslation } from 'react-i18next' ;
@@ -25,48 +25,26 @@ const MAX_HEIGHT = 300;
25
25
const Textarea : React . FC < Props > = ( props ) => {
26
26
const { t } = useTranslation ( ) ;
27
27
const ref = useRef < HTMLTextAreaElement > ( null ) ;
28
- const [ isMax , setIsMax ] = useState ( false ) ;
29
- const _maxHeight = props . maxHeight || MAX_HEIGHT ;
30
-
31
- useEffect ( ( ) => {
32
- if ( ! ref . current ) {
33
- return ;
34
- }
28
+ const maxHeight = props . maxHeight || MAX_HEIGHT ;
35
29
30
+ useLayoutEffect ( ( ) => {
31
+ if ( ! ref . current ) return ;
32
+ // Reset the height to auto to calculate the scroll height
36
33
ref . current . style . height = 'auto' ;
34
+ ref . current . style . overflowY = 'hidden' ;
37
35
38
- if ( _maxHeight > 0 && ref . current . scrollHeight > _maxHeight ) {
39
- ref . current . style . height = _maxHeight + 'px' ;
40
- setIsMax ( true ) ;
41
- } else {
42
- ref . current . style . height = ref . current . scrollHeight + 'px' ;
43
- setIsMax ( false ) ;
44
- }
45
- } , [ props . value , _maxHeight ] ) ;
46
-
47
- useEffect ( ( ) => {
48
- const current = ref . current ;
49
- if ( ! current ) {
50
- return ;
51
- }
52
-
53
- const listener = ( e : DocumentEventMap [ 'keypress' ] ) => {
54
- if ( props . onEnter ) {
55
- if ( e . key === 'Enter' && ! e . shiftKey ) {
56
- e . preventDefault ( ) ;
57
- props . onEnter ( ) ;
58
- }
59
- }
60
- } ;
36
+ // Ensure the layout is updated before calculating the scroll height
37
+ // due to the bug in Firefox:
38
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1795904
39
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1787062
40
+ void ref . current . scrollHeight ;
61
41
62
- current . addEventListener ( 'keypress' , listener ) ;
63
-
64
- return ( ) => {
65
- if ( current ) {
66
- current . removeEventListener ( 'keypress' , listener ) ;
67
- }
68
- } ;
69
- } , [ ref , props ] ) ;
42
+ // Set the height to match content, up to max height
43
+ const scrollHeight = ref . current . scrollHeight ;
44
+ const isMax = maxHeight > 0 && scrollHeight > maxHeight ;
45
+ ref . current . style . height = ( isMax ? maxHeight : scrollHeight ) + 'px' ;
46
+ ref . current . style . overflowY = isMax ? 'auto' : 'hidden' ;
47
+ } , [ props . value , maxHeight ] ) ;
70
48
71
49
return (
72
50
< RowItem notItem = { props . notItem } >
@@ -93,13 +71,19 @@ const Textarea: React.FC<Props> = (props) => {
93
71
className = { `${
94
72
props . className ?? ''
95
73
} w-full resize-none rounded p-1.5 outline-none ${
96
- isMax ? 'overflow-y-auto' : 'overflow-hidden'
97
- } ${
98
74
props . noBorder ? 'border-0 focus:ring-0 ' : 'border border-black/30'
99
75
} ${ props . disabled ? 'bg-gray-200 ' : '' } `}
100
76
rows = { props . rows ?? 1 }
101
77
placeholder = { props . placeholder || t ( 'common.enter_text' ) }
102
78
value = { props . value }
79
+ onKeyDown = { ( e ) => {
80
+ // keyCode is deprecated, but used for some browsers to handle IME input
81
+ if ( e . nativeEvent . isComposing || e . keyCode === 229 ) return ;
82
+ if ( props . onEnter && e . key === 'Enter' && ! e . shiftKey ) {
83
+ e . preventDefault ( ) ;
84
+ props . onEnter ( ) ;
85
+ }
86
+ } }
103
87
onChange = { ( e ) => {
104
88
props . onChange ( e . target . value ) ;
105
89
} }
0 commit comments