11import { useEffect , useRef , useState } from 'react' ;
2+ import { motion } from 'motion/react' ;
3+ import { PaperPlaneRightIcon , SpinnerIcon } from '@phosphor-icons/react/dist/ssr' ;
24import { Button } from '@/components/livekit/button' ;
3- import { cn } from '@/lib/utils' ;
5+
6+ const MOTION_PROPS = {
7+ variants : {
8+ hidden : {
9+ height : 0 ,
10+ opacity : 0 ,
11+ marginBottom : 0 ,
12+ } ,
13+ visible : {
14+ height : 'auto' ,
15+ opacity : 1 ,
16+ marginBottom : 12 ,
17+ } ,
18+ } ,
19+ initial : 'hidden' ,
20+ transition : {
21+ duration : 0.3 ,
22+ ease : 'easeOut' ,
23+ } ,
24+ } ;
425
526interface ChatInputProps {
627 chatOpen : boolean ;
728 disabled ?: boolean ;
829 onSend ?: ( message : string ) => void ;
930}
1031
11- export function ChatInput ( { chatOpen, disabled = false , onSend = ( ) => { } } : ChatInputProps ) {
32+ export function ChatInput ( { chatOpen, disabled = false , onSend = async ( ) => { } } : ChatInputProps ) {
1233 const inputRef = useRef < HTMLInputElement > ( null ) ;
34+ const [ isSending , setIsSending ] = useState ( false ) ;
1335 const [ message , setMessage ] = useState < string > ( '' ) ;
1436
15- const handleSubmit = ( e : React . FormEvent < HTMLFormElement > ) => {
37+ const handleSubmit = async ( e : React . FormEvent < HTMLFormElement > ) => {
1638 e . preventDefault ( ) ;
17- onSend ( message ) ;
18- setMessage ( '' ) ;
39+
40+ try {
41+ setIsSending ( true ) ;
42+ await onSend ( message ) ;
43+ setMessage ( '' ) ;
44+ } catch ( error ) {
45+ console . error ( error ) ;
46+ } finally {
47+ setIsSending ( false ) ;
48+ }
1949 } ;
2050
2151 const isDisabled = disabled || message . trim ( ) . length === 0 ;
@@ -27,40 +57,41 @@ export function ChatInput({ chatOpen, disabled = false, onSend = () => {} }: Cha
2757 } , [ disabled ] ) ;
2858
2959 return (
30- < div
60+ < motion . div
3161 inert = { ! chatOpen }
32- className = { cn (
33- 'overflow-hidden transition-[height] duration-300 ease-out' ,
34- chatOpen ? 'h-[57px]' : 'h-0'
35- ) }
62+ { ...MOTION_PROPS }
63+ animate = { chatOpen ? 'visible' : 'hidden' }
64+ className = "border-input/50 flex w-full items-start overflow-hidden border-b"
3665 >
37- < div className = "flex h-8 w-full" >
38- < form
39- onSubmit = { handleSubmit }
40- className = "flex grow items-center gap-2 rounded-md pl-1 text-sm"
66+ < form
67+ onSubmit = { handleSubmit }
68+ className = "mb-3 flex grow items-end gap-2 rounded-md pl-1 text-sm"
69+ >
70+ < input
71+ autoFocus
72+ ref = { inputRef }
73+ type = "text"
74+ value = { message }
75+ disabled = { disabled }
76+ placeholder = "Type something..."
77+ onChange = { ( e ) => setMessage ( e . target . value ) }
78+ className = "h-8 flex-1 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
79+ />
80+ < Button
81+ size = "icon"
82+ type = "submit"
83+ disabled = { isDisabled }
84+ variant = { isDisabled ? 'secondary' : 'primary' }
85+ title = { isSending ? 'Sending...' : 'Send' }
86+ className = "self-start"
4187 >
42- < input
43- autoFocus
44- ref = { inputRef }
45- type = "text"
46- value = { message }
47- disabled = { disabled }
48- placeholder = "Type something..."
49- onChange = { ( e ) => setMessage ( e . target . value ) }
50- className = "flex-1 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
51- />
52- < Button
53- size = "sm"
54- type = "submit"
55- disabled = { isDisabled }
56- variant = { isDisabled ? 'secondary' : 'primary' }
57- className = "font-mono uppercase"
58- >
59- Send
60- </ Button >
61- </ form >
62- </ div >
63- < hr className = "border-input/50 my-3" />
64- </ div >
88+ { isSending ? (
89+ < SpinnerIcon className = "animate-spin" weight = "bold" />
90+ ) : (
91+ < PaperPlaneRightIcon weight = "bold" />
92+ ) }
93+ </ Button >
94+ </ form >
95+ </ motion . div >
6596 ) ;
6697}
0 commit comments