@@ -28,20 +28,27 @@ import { OpenChannel } from '@sendbird/chat/openChannel';
2828import { UserMessage } from '@sendbird/chat/message' ;
2929
3030const TEXT_FIELD_ID = 'sendbird-message-input-text-field' ;
31- const LINE_HEIGHT = 76 ;
32- const DEFAULT_CHAT_VIEW_HEIGHT = 600 ;
3331const noop = ( ) => {
3432 return null ;
3533} ;
3634
37- const displayCaret = ( element : HTMLInputElement , position : number ) => {
38- const range = document . createRange ( ) ;
39- const sel = window . getSelection ( ) ;
40- range . setStart ( element . childNodes [ 0 ] , position ) ;
41- range . collapse ( true ) ;
42- sel ?. removeAllRanges ( ) ;
43- sel ?. addRange ( range ) ;
44- element . focus ( ) ;
35+ const scrollToCaret = ( ) => {
36+ const selection = window . getSelection ( ) ;
37+ if ( selection && selection . rangeCount > 0 ) {
38+ const range = selection . getRangeAt ( 0 ) ;
39+ const caretNode = range . endContainer ;
40+
41+ // Ensure the caret is in a text node
42+ if ( caretNode . nodeType === NodeTypes . TextNode ) {
43+ const parentElement = caretNode . parentElement ;
44+
45+ // Scroll the parent element of the caret into view
46+ parentElement ?. scrollIntoView ?.( {
47+ behavior : 'smooth' ,
48+ block : 'nearest' ,
49+ } ) ;
50+ }
51+ }
4552} ;
4653
4754const resetInput = ( ref : MutableRefObject < HTMLInputElement | null > | null ) => {
@@ -133,7 +140,7 @@ const MessageInput = React.forwardRef<HTMLInputElement, MessageInputProps>((prop
133140 acceptableMimeTypes,
134141 } = props ;
135142
136- const internalRef = ( externalRef && 'current' in externalRef ) ? externalRef : null ;
143+ const internalRef = ( externalRef && 'current' in externalRef ) ? externalRef : useRef ( null ) ;
137144 const ghostInputRef = useRef < HTMLInputElement > ( null ) ;
138145
139146 const textFieldId = messageFieldId || TEXT_FIELD_ID ;
@@ -149,46 +156,15 @@ const MessageInput = React.forwardRef<HTMLInputElement, MessageInputProps>((prop
149156 const [ isInput , setIsInput ] = useState ( false ) ;
150157 const [ mentionedUserIds , setMentionedUserIds ] = useState < string [ ] > ( [ ] ) ;
151158 const [ targetStringInfo , setTargetStringInfo ] = useState ( { ...initialTargetStringInfo } ) ;
152- const setHeight = useCallback (
153- ( ) => {
154- const elem = internalRef ?. current ;
155- if ( ! elem ) return ;
156-
157- try {
158- const estimatedChatViewHeight = window . document . body . offsetHeight || DEFAULT_CHAT_VIEW_HEIGHT ;
159- const MAX_HEIGHT = estimatedChatViewHeight * 0.6 ;
160- if ( elem . scrollHeight >= LINE_HEIGHT ) {
161- if ( MAX_HEIGHT < elem . scrollHeight ) {
162- elem . style . height = 'auto' ;
163- elem . style . height = `${ MAX_HEIGHT } px` ;
164- } else {
165- elem . style . height = '' ;
166- }
167- }
168- } catch ( error ) {
169- // error
170- }
171- } ,
172- [ ] ,
173- ) ;
174159
175160 // #Edit mode
176- // for easilly initialize input value from outside, but
161+ // for easily initialize input value from outside, but
177162 // useEffect(_, [channelUrl]) erase it
178163 const initialValue = props ?. value ;
179164 useEffect ( ( ) => {
180165 const textField = internalRef ?. current ;
181- try {
182- if ( textField && initialValue ) {
183- textField . innerHTML = initialValue ;
184- displayCaret ( textField , initialValue ?. length ) ;
185- }
186- } catch {
187- //
188- }
189166 setMentionedUserIds ( [ ] ) ;
190167 setIsInput ( textField ?. textContent ? textField . textContent . trim ( ) . length > 0 : false ) ;
191- setHeight ( ) ;
192168 } , [ initialValue ] ) ;
193169
194170 // #Mention | Clear input value when channel changes
@@ -243,7 +219,6 @@ const MessageInput = React.forwardRef<HTMLInputElement, MessageInputProps>((prop
243219 setMentionedUserIds ( [ ] ) ;
244220 }
245221 setIsInput ( textField ?. textContent ? textField ?. textContent ?. trim ( ) . length > 0 : false ) ;
246- setHeight ( ) ;
247222 }
248223 } , [ isEdit , message ] ) ;
249224
@@ -314,7 +289,6 @@ const MessageInput = React.forwardRef<HTMLInputElement, MessageInputProps>((prop
314289 textField . focus ( ) ;
315290 }
316291 setTargetStringInfo ( { ...initialTargetStringInfo } ) ;
317- setHeight ( ) ;
318292 useMentionedLabelDetection ( ) ;
319293 }
320294 }
@@ -411,7 +385,6 @@ const MessageInput = React.forwardRef<HTMLInputElement, MessageInputProps>((prop
411385 }
412386
413387 setIsInput ( false ) ;
414- setHeight ( ) ;
415388 }
416389 } catch ( error ) {
417390 eventHandlers ?. message ?. onSendMessageFailed ?.( message , error ) ;
@@ -437,7 +410,6 @@ const MessageInput = React.forwardRef<HTMLInputElement, MessageInputProps>((prop
437410 setMentionedUsers,
438411 channel,
439412 setIsInput,
440- setHeight,
441413 } ) ;
442414
443415 const uploadFile = ( event : React . ChangeEvent < HTMLInputElement > ) => {
@@ -453,6 +425,12 @@ const MessageInput = React.forwardRef<HTMLInputElement, MessageInputProps>((prop
453425 }
454426 } ;
455427
428+ const handleCommonBehavior = ( handleEvent ) => {
429+ scrollToCaret ( ) ;
430+ type CommonEvent < T > = React . KeyboardEvent < T > | React . MouseEvent < T > | React . ClipboardEvent < T > ;
431+ return ( e : CommonEvent < HTMLDivElement > ) => handleEvent ( e ) ;
432+ } ;
433+
456434 return (
457435 < form className = { classnames (
458436 ...( Array . isArray ( className ) ? className : [ className ] ) ,
@@ -474,11 +452,11 @@ const MessageInput = React.forwardRef<HTMLInputElement, MessageInputProps>((prop
474452 contentEditable = { ! disabled }
475453 role = "textbox"
476454 aria-label = "Text Input"
477- ref = { externalRef }
455+ ref = { internalRef }
478456 // @ts -ignore
479457 disabled = { disabled }
480458 maxLength = { maxLength }
481- onKeyDown = { ( e ) => {
459+ onKeyDown = { handleCommonBehavior ( ( e ) => {
482460 const preventEvent = onKeyDown ( e ) ;
483461 if ( preventEvent ) {
484462 e . preventDefault ( ) ;
@@ -509,25 +487,24 @@ const MessageInput = React.forwardRef<HTMLInputElement, MessageInputProps>((prop
509487 internalRef . current . removeChild ( internalRef . current . childNodes [ 1 ] ) ;
510488 }
511489 }
512- } }
513- onKeyUp = { ( e ) => {
490+ } ) }
491+ onKeyUp = { handleCommonBehavior ( ( e ) => {
514492 const preventEvent = onKeyUp ( e ) ;
515493 if ( preventEvent ) {
516494 e . preventDefault ( ) ;
517495 } else {
518496 useMentionInputDetection ( ) ;
519497 }
520- } }
521- onClick = { ( ) => {
498+ } ) }
499+ onClick = { handleCommonBehavior ( ( ) => {
522500 useMentionInputDetection ( ) ;
523- } }
524- onInput = { ( ) => {
525- setHeight ( ) ;
501+ } ) }
502+ onInput = { handleCommonBehavior ( ( ) => {
526503 onStartTyping ( ) ;
527504 setIsInput ( internalRef ?. current ?. textContent ? internalRef . current . textContent . trim ( ) . length > 0 : false ) ;
528505 useMentionedLabelDetection ( ) ;
529- } }
530- onPaste = { onPaste }
506+ } ) }
507+ onPaste = { handleCommonBehavior ( onPaste ) }
531508 />
532509 { /* placeholder */ }
533510 { ( internalRef ?. current ?. textContent ?. length ?? 0 ) === 0 && (
0 commit comments