Skip to content

Commit 526ddee

Browse files
authored
fix: Firefox textarea scrolling issues due to incorrect height calculation (#1114)
1 parent e8d782b commit 526ddee

File tree

1 file changed

+25
-41
lines changed

1 file changed

+25
-41
lines changed

packages/web/src/components/Textarea.tsx

Lines changed: 25 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useRef, useState } from 'react';
1+
import React, { useLayoutEffect, useRef } from 'react';
22
import RowItem, { RowItemProps } from './RowItem';
33
import Help from './Help';
44
import { useTranslation } from 'react-i18next';
@@ -25,48 +25,26 @@ const MAX_HEIGHT = 300;
2525
const Textarea: React.FC<Props> = (props) => {
2626
const { t } = useTranslation();
2727
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;
3529

30+
useLayoutEffect(() => {
31+
if (!ref.current) return;
32+
// Reset the height to auto to calculate the scroll height
3633
ref.current.style.height = 'auto';
34+
ref.current.style.overflowY = 'hidden';
3735

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;
6141

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]);
7048

7149
return (
7250
<RowItem notItem={props.notItem}>
@@ -93,13 +71,19 @@ const Textarea: React.FC<Props> = (props) => {
9371
className={`${
9472
props.className ?? ''
9573
} w-full resize-none rounded p-1.5 outline-none ${
96-
isMax ? 'overflow-y-auto' : 'overflow-hidden'
97-
} ${
9874
props.noBorder ? 'border-0 focus:ring-0 ' : 'border border-black/30'
9975
} ${props.disabled ? 'bg-gray-200 ' : ''}`}
10076
rows={props.rows ?? 1}
10177
placeholder={props.placeholder || t('common.enter_text')}
10278
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+
}}
10387
onChange={(e) => {
10488
props.onChange(e.target.value);
10589
}}

0 commit comments

Comments
 (0)