diff --git a/packages/compass-generative-ai/src/components/generative-ai-input.tsx b/packages/compass-generative-ai/src/components/generative-ai-input.tsx index 3de75686335..6595cd84f37 100644 --- a/packages/compass-generative-ai/src/components/generative-ai-input.tsx +++ b/packages/compass-generative-ai/src/components/generative-ai-input.tsx @@ -1,4 +1,11 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { + forwardRef, + useCallback, + useEffect, + useLayoutEffect, + useRef, + useState, +} from 'react'; import { Banner, BannerVariant, @@ -6,7 +13,7 @@ import { Icon, IconButton, SpinLoader, - TextInput, + TextArea, css, cx, focusRing, @@ -28,6 +35,7 @@ const containerStyles = css({ width: '100%', flexDirection: 'column', gap: spacing[25], + padding: `0px ${spacing[100]}px`, marginBottom: spacing[300], }); @@ -123,11 +131,17 @@ const inputContainerStyles = css({ position: 'relative', }); +const defaultTextAreaSize = spacing[400] + spacing[300]; const textInputStyles = css({ flexGrow: 1, - input: { + textarea: { + margin: 0, + padding: spacing[50] + spacing[25], paddingLeft: spacing[800], paddingRight: spacing[1600] * 2 + spacing[200], + height: defaultTextAreaSize, // Default height, overridden runtime. + minHeight: `${defaultTextAreaSize}px`, + maxHeight: spacing[1600] * 2, }, }); @@ -216,6 +230,48 @@ const aiEntryContainerStyles = css({ display: 'flex', }); +function adjustHeight(elementRef: React.RefObject) { + if (elementRef.current) { + // Dynamically set the text area height based on the scroll height. + // We first set it to 0 so the correct scroll height is used. + elementRef.current.style.height = '0px'; + let adjustedHeight = elementRef.current.scrollHeight; + if (adjustedHeight > defaultTextAreaSize) { + // When it's greater than one line, we add pixels so that + // we don't show a scrollbar when it's still under the maxHeight. + adjustedHeight += spacing[50]; + } + elementRef.current.style.height = `${adjustedHeight}px`; + } +} + +const VerticallyResizingTextArea = forwardRef( + function VerticallyResizingTextArea( + { value, ...props }: React.ComponentProps, + forwardedRef: React.ForwardedRef + ) { + const ref = useRef(null); + + useLayoutEffect(() => adjustHeight(ref), []); + useEffect(() => adjustHeight(ref), [value]); + + return ( +