Autogrow the textarea
This commit is contained in:
parent
3939e27827
commit
7535862a40
|
@ -1,9 +1,15 @@
|
||||||
import classNames from 'clsx';
|
import classNames from 'clsx';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
interface ITextarea extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'maxLength' | 'onChange' | 'onKeyDown' | 'required' | 'disabled' | 'rows' | 'readOnly'> {
|
interface ITextarea extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'maxLength' | 'onChange' | 'onKeyDown' | 'required' | 'disabled' | 'rows' | 'readOnly'> {
|
||||||
/** Put the cursor into the input on mount. */
|
/** Put the cursor into the input on mount. */
|
||||||
autoFocus?: boolean,
|
autoFocus?: boolean,
|
||||||
|
/** Allows the textarea height to grow while typing */
|
||||||
|
autoGrow?: boolean,
|
||||||
|
/** Used with "autoGrow". Sets a max number of rows. */
|
||||||
|
maxRows?: number,
|
||||||
|
/** Used with "autoGrow". Sets a min number of rows. */
|
||||||
|
minRows?: number,
|
||||||
/** The initial text in the input. */
|
/** The initial text in the input. */
|
||||||
defaultValue?: string,
|
defaultValue?: string,
|
||||||
/** Internal input name. */
|
/** Internal input name. */
|
||||||
|
@ -23,22 +29,59 @@ interface ITextarea extends Pick<React.TextareaHTMLAttributes<HTMLTextAreaElemen
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Textarea with custom styles. */
|
/** Textarea with custom styles. */
|
||||||
const Textarea = React.forwardRef(
|
const Textarea = React.forwardRef(({
|
||||||
({ isCodeEditor = false, hasError = false, isResizeable = true, ...props }: ITextarea, ref: React.ForwardedRef<HTMLTextAreaElement>) => {
|
isCodeEditor = false,
|
||||||
return (
|
hasError = false,
|
||||||
<textarea
|
isResizeable = true,
|
||||||
{...props}
|
onChange,
|
||||||
ref={ref}
|
autoGrow = false,
|
||||||
className={classNames({
|
maxRows = 10,
|
||||||
'bg-white dark:bg-transparent shadow-sm block w-full sm:text-sm rounded-md text-gray-900 dark:text-gray-100 placeholder:text-gray-600 dark:placeholder:text-gray-600 border-gray-400 dark:border-gray-800 dark:ring-1 dark:ring-gray-800 focus:ring-primary-500 focus:border-primary-500 dark:focus:ring-primary-500 dark:focus:border-primary-500':
|
minRows = 1,
|
||||||
true,
|
...props
|
||||||
'font-mono': isCodeEditor,
|
}: ITextarea, ref: React.ForwardedRef<HTMLTextAreaElement>) => {
|
||||||
'text-red-600 border-red-600': hasError,
|
const [rows, setRows] = useState<number>(autoGrow ? 1 : 4);
|
||||||
'resize-none': !isResizeable,
|
|
||||||
})}
|
const handleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
/>
|
if (autoGrow) {
|
||||||
);
|
const textareaLineHeight = 20;
|
||||||
},
|
const previousRows = event.target.rows;
|
||||||
|
event.target.rows = minRows;
|
||||||
|
|
||||||
|
const currentRows = ~~(event.target.scrollHeight / textareaLineHeight);
|
||||||
|
|
||||||
|
if (currentRows === previousRows) {
|
||||||
|
event.target.rows = currentRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentRows >= maxRows) {
|
||||||
|
event.target.rows = maxRows;
|
||||||
|
event.target.scrollTop = event.target.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
setRows(currentRows < maxRows ? currentRows : maxRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onChange) {
|
||||||
|
onChange(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<textarea
|
||||||
|
{...props}
|
||||||
|
ref={ref}
|
||||||
|
rows={rows}
|
||||||
|
onChange={handleChange}
|
||||||
|
className={classNames({
|
||||||
|
'bg-white dark:bg-transparent shadow-sm block w-full sm:text-sm rounded-md text-gray-900 dark:text-gray-100 placeholder:text-gray-600 dark:placeholder:text-gray-600 border-gray-400 dark:border-gray-800 dark:ring-1 dark:ring-gray-800 focus:ring-primary-500 focus:border-primary-500 dark:focus:ring-primary-500 dark:focus:border-primary-500':
|
||||||
|
true,
|
||||||
|
'font-mono': isCodeEditor,
|
||||||
|
'text-red-600 border-red-600': hasError,
|
||||||
|
'resize-none': !isResizeable,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Textarea;
|
export default Textarea;
|
||||||
|
|
|
@ -219,7 +219,6 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize, inputRef }
|
||||||
<HStack alignItems='center' justifyContent='between' space={4}>
|
<HStack alignItems='center' justifyContent='between' space={4}>
|
||||||
<div className='flex-grow'>
|
<div className='flex-grow'>
|
||||||
<Textarea
|
<Textarea
|
||||||
rows={1}
|
|
||||||
autoFocus
|
autoFocus
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
placeholder={intl.formatMessage(messages.placeholder)}
|
placeholder={intl.formatMessage(messages.placeholder)}
|
||||||
|
@ -227,6 +226,8 @@ const ChatBox: React.FC<IChatBox> = ({ chat, onSetInputRef, autosize, inputRef }
|
||||||
value={content}
|
value={content}
|
||||||
onChange={handleContentChange}
|
onChange={handleContentChange}
|
||||||
isResizeable={false}
|
isResizeable={false}
|
||||||
|
autoGrow
|
||||||
|
maxRows={5}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue