2023-05-15 08:51:32 +08:00
|
|
|
import { forwardRef, useEffect, useRef } from 'react'
|
|
|
|
import cn from 'classnames'
|
|
|
|
|
|
|
|
type IProps = {
|
|
|
|
placeholder?: string
|
|
|
|
value: string
|
|
|
|
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void
|
|
|
|
className?: string
|
2023-11-13 22:32:39 +08:00
|
|
|
wrapperClassName?: string
|
2023-05-15 08:51:32 +08:00
|
|
|
minHeight?: number
|
|
|
|
maxHeight?: number
|
|
|
|
autoFocus?: boolean
|
|
|
|
controlFocus?: number
|
|
|
|
onKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void
|
|
|
|
onKeyUp?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void
|
|
|
|
}
|
|
|
|
|
|
|
|
const AutoHeightTextarea = forwardRef(
|
|
|
|
(
|
2023-11-13 22:32:39 +08:00
|
|
|
{ value, onChange, placeholder, className, wrapperClassName, minHeight = 36, maxHeight = 96, autoFocus, controlFocus, onKeyDown, onKeyUp }: IProps,
|
2023-05-15 08:51:32 +08:00
|
|
|
outerRef: any,
|
|
|
|
) => {
|
2023-07-17 00:14:32 +08:00
|
|
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
2023-05-15 08:51:32 +08:00
|
|
|
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 = () => {
|
|
|
|
if (!doFocus()) {
|
|
|
|
let hasFocus = false
|
|
|
|
const runId = setInterval(() => {
|
|
|
|
hasFocus = doFocus()
|
|
|
|
if (hasFocus)
|
|
|
|
clearInterval(runId)
|
|
|
|
}, 100)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (autoFocus)
|
|
|
|
focus()
|
|
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
|
|
if (controlFocus)
|
|
|
|
focus()
|
|
|
|
}, [controlFocus])
|
|
|
|
|
|
|
|
return (
|
2023-11-13 22:32:39 +08:00
|
|
|
<div className={`relative ${wrapperClassName}`}>
|
2023-07-17 00:14:32 +08:00
|
|
|
<div className={cn(className, 'invisible whitespace-pre-wrap break-all overflow-y-auto')} style={{
|
|
|
|
minHeight,
|
|
|
|
maxHeight,
|
|
|
|
paddingRight: (value && value.trim().length > 10000) ? 140 : 130,
|
|
|
|
}}>
|
2023-05-15 08:51:32 +08:00
|
|
|
{!value ? placeholder : value.replace(/\n$/, '\n ')}
|
|
|
|
</div>
|
|
|
|
<textarea
|
|
|
|
ref={ref}
|
|
|
|
autoFocus={autoFocus}
|
2023-07-17 00:14:32 +08:00
|
|
|
className={cn(className, 'absolute inset-0 resize-none overflow-auto')}
|
|
|
|
style={{
|
|
|
|
paddingRight: (value && value.trim().length > 10000) ? 140 : 130,
|
|
|
|
}}
|
2023-05-15 08:51:32 +08:00
|
|
|
placeholder={placeholder}
|
|
|
|
onChange={onChange}
|
|
|
|
onKeyDown={onKeyDown}
|
|
|
|
onKeyUp={onKeyUp}
|
|
|
|
value={value}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
2023-11-13 22:32:39 +08:00
|
|
|
AutoHeightTextarea.displayName = 'AutoHeightTextarea'
|
|
|
|
|
2023-05-15 08:51:32 +08:00
|
|
|
export default AutoHeightTextarea
|