import React, { useState, useRef, useEffect } from 'react'; import styled from 'styled-components'; import type { JSX } from 'react'; import { AI_SEARCH_MAX_MESSAGE_LENGTH } from '@redocly/theme/core/constants'; import { Button } from '@redocly/theme/components/Button/Button'; import { SendIcon } from '@redocly/theme/icons/SendIcon/SendIcon'; import { useThemeHooks } from '@redocly/theme/core/hooks'; type SearchAiConversationInputProps = { onMessageSent: (message: string) => void; isGeneratingResponse: boolean; placeholder?: string; className?: string; disabled?: boolean; multiline?: boolean; }; export function SearchAiConversationInput({ isGeneratingResponse, onMessageSent, className, placeholder, disabled, multiline = false, }: SearchAiConversationInputProps): JSX.Element { const { useTranslate } = useThemeHooks(); const { translate } = useTranslate(); const inputRef = useRef(null); const [query, setQuery] = useState(''); useEffect(() => { if (!isGeneratingResponse && inputRef.current) { requestAnimationFrame(() => { inputRef.current?.focus(); }); } }, [isGeneratingResponse]); const handleSendMessage = () => { if (disabled) return; setQuery(''); onMessageSent(query); }; const handleOnKeyDown = ( e: React.KeyboardEvent, ): void => { if (e.key === 'Enter' && !e.nativeEvent.isComposing && !isGeneratingResponse) { if (multiline && e.shiftKey) { return; // Allow new line in textarea } e.preventDefault(); handleSendMessage(); } }; const isDisabled = disabled || isGeneratingResponse || query.trim().length === 0; const commonProps = { placeholder: placeholder || translate('search.ai.followUpQuestion', 'Ask a follow up question?'), onChange: (e: React.ChangeEvent) => setQuery(e.target.value), onKeyDown: handleOnKeyDown, value: query, disabled: disabled || isGeneratingResponse, maxLength: AI_SEARCH_MAX_MESSAGE_LENGTH, }; return ( {multiline ? ( } {...commonProps} /> ) : ( } type="text" {...commonProps} /> )} } onClick={handleSendMessage} /> ); } const SearchAiConversationInputWrapper = styled.div` width: 100%; display: flex; flex-direction: column; justify-content: flex-end; background-color: var(--search-ai-conversation-input-bg-color); position: relative; `; const inputStyles = ` width: 100%; padding: var(--search-ai-conversation-input-padding); padding-right: var(--search-ai-conversation-input-padding-right, 52px); border: var(--search-ai-conversation-input-border); border-radius: var(--search-ai-conversation-input-border-radius); background-color: var(--search-ai-conversation-input-bg-color); position: relative; font-size: var(--search-ai-conversation-input-font-size); &::placeholder { color: var(--search-ai-conversation-input-placeholder-color); } &:focus { outline: none; border-color: var(--search-ai-conversation-input-border-color-focus); } &:disabled { background-color: var(--search-ai-conversation-input-bg-color-disabled); } &:focus:disabled { border-color: var(--search-ai-conversation-input-border-color-disabled); } `; const ConversationInput = styled.input` ${inputStyles} height: var(--search-ai-conversation-input-min-height); `; const ConversationTextarea = styled.textarea` ${inputStyles} min-height: var(--search-ai-conversation-input-min-height); max-height: var(--search-ai-conversation-input-max-height); resize: none; field-sizing: content; `; const SendButton = styled(Button)` position: absolute; right: var(--search-ai-conversation-input-send-button-right); bottom: var(--search-ai-conversation-input-send-button-bottom); transform: translateY(50%); transition: background-color 0.2s ease; background-color: var(--search-ai-conversation-input-send-button-bg-color); display: flex; align-items: center; justify-content: center; border-radius: var(--search-ai-conversation-input-send-button-border-radius); padding: var(--search-ai-conversation-input-send-button-padding); &:hover { background-color: var(--search-ai-conversation-input-send-button-bg-color-hover); } &:disabled { background-color: var(--search-ai-conversation-input-send-button-bg-color-disabled); border: var(--search-ai-conversation-input-send-button-border-disabled); } `;