import { forwardRef, useCallback, useId, useLayoutEffect, useMemo, useRef, useState, } from 'react' import Block from '../../Primitives/Block/Block' import { ChatInputV2Props } from './ChatInputV2.types' import { useResponsiveTokens } from '../../../hooks/useResponsiveTokens' import { ChatInputV2TokensType } from './ChatInputV2.tokens' import { InputStateV2 } from '../inputV2.types' import PrimitiveTextarea from '../../Primitives/PrimitiveTextArea' import ButtonV2 from '../../ButtonV2/ButtonV2' import { ButtonV2Size, ButtonV2SubType, ButtonV2Type, } from '../../ButtonV2/buttonV2.types' import Text from '../../Text/Text' import PrimitiveInput from '../../Primitives/PrimitiveInput/PrimitiveInput' import { PaperclipIcon } from '@phosphor-icons/react' import ChatInputV2AttachmentRow from './ChatInputV2AttachmentRow' import { applyChatInputV2TextareaAutoHeight, assignForwardedRef, handleChatInputV2FileInputChange, resolveChatInputV2TextareaMaxHeightPx, } from './utils' import MobileChatInputV2 from './MobileChatInputV2' import { BREAKPOINTS } from '../../../breakpoints/breakPoints' import { useBreakpoints } from '../../../hooks/useBreakPoints' import { filterBlockedProps } from '../../../utils/prop-helpers' const ChatInputV2 = forwardRef( ( { topContent, secondaryAction, onSecondaryActionClick = () => {}, placeholder = 'Type a message...', onChange, onEnter = () => {}, onAttachFiles = () => {}, onFileClick = () => {}, onFileRemove = () => {}, attachedFiles = [], value = '', topQueries = [], onTopQuerySelect = () => {}, topQueriesMaxHeight = 200, textareaMaxHeight, disabled = false, ...textareaRest }, ref ) => { const filteredRest = filterBlockedProps(textareaRest) const containerRef = useRef(null) const textareaRef = useRef(null) const fileInputRef = useRef(null) const generatedId = useId() const chatInputId = `chat-input-${generatedId}` const chatInputV2Tokens = useResponsiveTokens('CHAT_INPUTV2') const [inputState, setInputState] = useState( InputStateV2.DEFAULT ) const showTopQueries = inputState === InputStateV2.FOCUS const { innerWidth } = useBreakpoints() const isMobile = innerWidth < BREAKPOINTS.lg const handleAttachClick = useCallback(() => { if (disabled) return fileInputRef.current?.click() }, [disabled]) const setContainerNode = useCallback( (node: HTMLDivElement | null) => { containerRef.current = node assignForwardedRef(node, ref) }, [ref] ) const textareaMaxHeightPx = useMemo( () => resolveChatInputV2TextareaMaxHeightPx( textareaMaxHeight, chatInputV2Tokens.container.inputContainer.input.maxHeight ), [textareaMaxHeight, chatInputV2Tokens] ) const syncTextareaHeight = useCallback(() => { applyChatInputV2TextareaAutoHeight( textareaRef.current, textareaMaxHeightPx ) }, [textareaMaxHeightPx]) useLayoutEffect(() => { syncTextareaHeight() }, [value, syncTextareaHeight]) const hiddenFileInput = useMemo( () => ( handleChatInputV2FileInputChange( e, attachedFiles, onAttachFiles ) } multiple accept="image/*,.pdf,.csv,.txt,.doc,.docx" aria-label="Attach files" /> ), [attachedFiles, onAttachFiles] ) if (isMobile) { return ( <> {hiddenFileInput} {})} topContent={topContent} secondaryAction={secondaryAction} placeholder={placeholder} attachedFiles={attachedFiles} handleAttachClick={handleAttachClick} onFileRemove={onFileRemove} onFileClick={onFileClick} onSecondaryActionClick={onSecondaryActionClick} disabled={disabled} onEnter={onEnter} /> ) } return ( {topContent && {topContent}} {hiddenFileInput} {/* Input Container */} {/* Input */} { onChange(e.target.value) }} onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() onEnter() } }} paddingTop={ chatInputV2Tokens.container.inputContainer.input .paddingTop } paddingRight={ chatInputV2Tokens.container.inputContainer.input .paddingRight } paddingLeft={ chatInputV2Tokens.container.inputContainer.input .paddingLeft } minHeight={ chatInputV2Tokens.container.inputContainer.input .minHeight } maxHeight={ chatInputV2Tokens.container.inputContainer.input .maxHeight } _disabled={{ cursor: 'not-allowed', }} overflowY="auto" width="100%" onFocus={() => { setInputState(InputStateV2.FOCUS) }} onBlur={() => { setInputState(InputStateV2.DEFAULT) }} aria-disabled={disabled} /> {/* Action Container */} }} onClick={handleAttachClick} disabled={disabled} aria-label="Attach files" /> {secondaryAction && ( { onSecondaryActionClick() }} disabled={disabled} aria-label="Secondary action" /> )} {/* Top Queries container */} {topQueries.length > 0 && ( Top Queries {topQueries.map((query) => ( { e.preventDefault() }} onClick={() => { onTopQuerySelect(query) setInputState(InputStateV2.DEFAULT) }} > {query.text} ))} )} ) } ) ChatInputV2.displayName = 'ChatInputV2' export default ChatInputV2