1
+ import debounce from 'lodash.debounce' ;
1
2
import clsx from 'clsx' ;
2
- import type { ChangeEventHandler , TextareaHTMLAttributes , UIEventHandler } from 'react' ;
3
+ import type {
4
+ ChangeEventHandler ,
5
+ SyntheticEvent ,
6
+ TextareaHTMLAttributes ,
7
+ UIEventHandler ,
8
+ } from 'react' ;
9
+ import { useMemo } from 'react' ;
3
10
import React , { useCallback , useEffect , useRef , useState } from 'react' ;
4
11
import Textarea from 'react-textarea-autosize' ;
5
12
import { useMessageComposer } from '../MessageInput' ;
@@ -40,52 +47,49 @@ const configStateSelector = (state: MessageComposerConfig) => ({
40
47
const defaultShouldSubmit = ( event : React . KeyboardEvent < HTMLTextAreaElement > ) =>
41
48
event . key === 'Enter' && ! event . shiftKey && ! event . nativeEvent . isComposing ;
42
49
43
- export type TextComposerProps = Omit <
50
+ export type TextareaComposerProps = Omit <
44
51
TextareaHTMLAttributes < HTMLTextAreaElement > ,
45
- 'style' | 'defaultValue' | 'disabled'
52
+ 'style' | 'defaultValue' | 'disabled' | 'value'
46
53
> & {
47
54
closeSuggestionsOnClickOutside ?: boolean ;
48
55
containerClassName ?: string ;
49
- dropdownClassName ?: string ;
50
- grow ?: boolean ;
51
- itemClassName ?: string ;
52
56
listClassName ?: string ;
53
57
maxRows ?: number ;
58
+ minRows ?: number ;
54
59
shouldSubmit ?: ( event : React . KeyboardEvent < HTMLTextAreaElement > ) => boolean ;
55
60
} ;
56
61
57
62
export const TextareaComposer = ( {
58
63
className,
59
64
closeSuggestionsOnClickOutside,
60
65
containerClassName,
61
- // dropdownClassName, // todo: X find a different way to prevent prop drilling
62
- grow : growProp ,
63
- // itemClassName, // todo: X find a different way to prevent prop drilling
64
66
listClassName,
65
67
maxRows : maxRowsProp = 1 ,
68
+ minRows : minRowsProp ,
66
69
onBlur,
67
70
onChange,
68
71
onKeyDown,
69
72
onScroll,
73
+ onSelect,
70
74
placeholder : placeholderProp ,
71
75
shouldSubmit : shouldSubmitProp ,
72
- ...restProps
73
- } : TextComposerProps ) => {
76
+ ...restTextareaProps
77
+ } : TextareaComposerProps ) => {
74
78
const { t } = useTranslationContext ( ) ;
75
79
const { AutocompleteSuggestionList = DefaultSuggestionList } = useComponentContext ( ) ;
76
80
const {
77
81
additionalTextareaProps,
78
82
cooldownRemaining,
79
- grow : growContext ,
80
83
handleSubmit,
81
84
maxRows : maxRowsContext ,
85
+ minRows : minRowsContext ,
82
86
onPaste,
83
87
shouldSubmit : shouldSubmitContext ,
84
88
textareaRef,
85
89
} = useMessageInputContext ( ) ;
86
90
87
- const grow = growProp ?? growContext ;
88
91
const maxRows = maxRowsProp ?? maxRowsContext ;
92
+ const minRows = minRowsProp ?? minRowsContext ;
89
93
const placeholder = placeholderProp ?? additionalTextareaProps ?. placeholder ;
90
94
const shouldSubmit = shouldSubmitProp ?? shouldSubmitContext ?? defaultShouldSubmit ;
91
95
@@ -205,6 +209,22 @@ export const TextareaComposer = ({
205
209
[ onScroll , textComposer ] ,
206
210
) ;
207
211
212
+ const setSelectionDebounced = useMemo (
213
+ ( ) =>
214
+ debounce (
215
+ ( e : SyntheticEvent < HTMLTextAreaElement > ) => {
216
+ onSelect ?.( e ) ;
217
+ textComposer . setSelection ( {
218
+ end : ( e . target as HTMLTextAreaElement ) . selectionEnd ,
219
+ start : ( e . target as HTMLTextAreaElement ) . selectionStart ,
220
+ } ) ;
221
+ } ,
222
+ 100 ,
223
+ { leading : false , trailing : true } ,
224
+ ) ,
225
+ [ onSelect , textComposer ] ,
226
+ ) ;
227
+
208
228
useEffect ( ( ) => {
209
229
// FIXME: find the real reason for cursor being set to the end on each change
210
230
// This is a workaround to prevent the cursor from jumping
@@ -235,7 +255,7 @@ export const TextareaComposer = ({
235
255
ref = { containerRef }
236
256
>
237
257
< Textarea
238
- { ...restProps }
258
+ { ...{ ... additionalTextareaProps , ... restTextareaProps } }
239
259
aria-label = { cooldownRemaining ? t ( 'Slow Mode ON' ) : placeholder }
240
260
className = { clsx (
241
261
'rta__textarea' ,
@@ -244,14 +264,16 @@ export const TextareaComposer = ({
244
264
) }
245
265
data-testid = 'message-input'
246
266
disabled = { ! enabled || ! ! cooldownRemaining }
247
- maxRows = { grow ? maxRows : 1 }
267
+ maxRows = { maxRows }
268
+ minRows = { minRows }
248
269
onBlur = { onBlur }
249
270
onChange = { changeHandler }
250
271
onCompositionEnd = { onCompositionEnd }
251
272
onCompositionStart = { onCompositionStart }
252
273
onKeyDown = { keyDownHandler }
253
274
onPaste = { onPaste }
254
275
onScroll = { scrollHandler }
276
+ onSelect = { setSelectionDebounced }
255
277
placeholder = { placeholder || t ( 'Type your message' ) }
256
278
ref = { ( ref ) => {
257
279
textareaRef . current = ref ;
0 commit comments