|
import { forwardRef, useEffect, useRef } from 'react' |
|
import cn from '@/utils/classnames' |
|
import { sleep } from '@/utils' |
|
|
|
type IProps = { |
|
placeholder?: string |
|
value: string |
|
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void |
|
className?: string |
|
wrapperClassName?: string |
|
minHeight?: number |
|
maxHeight?: number |
|
autoFocus?: boolean |
|
controlFocus?: number |
|
onKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void |
|
onKeyUp?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void |
|
} |
|
|
|
const AutoHeightTextarea = forwardRef( |
|
( |
|
{ value, onChange, placeholder, className, wrapperClassName, minHeight = 36, maxHeight = 96, autoFocus, controlFocus, onKeyDown, onKeyUp }: IProps, |
|
outerRef: any, |
|
) => { |
|
|
|
const ref = outerRef || useRef<HTMLTextAreaElement>(null) |
|
|
|
const doFocus = () => { |
|
if (ref.current) { |
|
ref.current.setSelectionRange(value.length, value.length) |
|
ref.current.focus() |
|
return true |
|
} |
|
return false |
|
} |
|
|
|
const focus = async () => { |
|
if (!doFocus()) { |
|
let hasFocus = false |
|
await sleep(100) |
|
hasFocus = doFocus() |
|
if (!hasFocus) |
|
focus() |
|
} |
|
} |
|
|
|
useEffect(() => { |
|
if (autoFocus) |
|
focus() |
|
}, []) |
|
useEffect(() => { |
|
if (controlFocus) |
|
focus() |
|
}, [controlFocus]) |
|
|
|
return ( |
|
<div className={`relative ${wrapperClassName}`}> |
|
<div className={cn(className, 'invisible whitespace-pre-wrap break-all overflow-y-auto')} style={{ |
|
minHeight, |
|
maxHeight, |
|
paddingRight: (value && value.trim().length > 10000) ? 140 : 130, |
|
}}> |
|
{!value ? placeholder : value.replace(/\n$/, '\n ')} |
|
</div> |
|
<textarea |
|
ref={ref} |
|
autoFocus={autoFocus} |
|
className={cn(className, 'absolute inset-0 resize-none overflow-auto')} |
|
style={{ |
|
paddingRight: (value && value.trim().length > 10000) ? 140 : 130, |
|
}} |
|
placeholder={placeholder} |
|
onChange={onChange} |
|
onKeyDown={onKeyDown} |
|
onKeyUp={onKeyUp} |
|
value={value} |
|
/> |
|
</div> |
|
) |
|
}, |
|
) |
|
|
|
AutoHeightTextarea.displayName = 'AutoHeightTextarea' |
|
|
|
export default AutoHeightTextarea |
|
|