index.tsx 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import { forwardRef, useEffect, useRef } from 'react'
  2. import cn from 'classnames'
  3. type IProps = {
  4. placeholder?: string
  5. value: string
  6. onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void
  7. className?: string
  8. minHeight?: number
  9. maxHeight?: number
  10. autoFocus?: boolean
  11. controlFocus?: number
  12. onKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void
  13. onKeyUp?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void
  14. }
  15. const AutoHeightTextarea = forwardRef(
  16. (
  17. { value, onChange, placeholder, className, minHeight = 36, maxHeight = 96, autoFocus, controlFocus, onKeyDown, onKeyUp }: IProps,
  18. outerRef: any,
  19. ) => {
  20. // eslint-disable-next-line react-hooks/rules-of-hooks
  21. const ref = outerRef || useRef<HTMLTextAreaElement>(null)
  22. const doFocus = () => {
  23. if (ref.current) {
  24. // console.log('focus')
  25. ref.current.setSelectionRange(value.length, value.length)
  26. ref.current.focus()
  27. return true
  28. }
  29. // console.log(autoFocus, 'not focus')
  30. return false
  31. }
  32. const focus = () => {
  33. if (!doFocus()) {
  34. let hasFocus = false
  35. const runId = setInterval(() => {
  36. hasFocus = doFocus()
  37. if (hasFocus)
  38. clearInterval(runId)
  39. }, 100)
  40. }
  41. }
  42. useEffect(() => {
  43. if (autoFocus)
  44. focus()
  45. }, [])
  46. useEffect(() => {
  47. if (controlFocus)
  48. focus()
  49. }, [controlFocus])
  50. return (
  51. <div className='relative'>
  52. <div className={cn(className, 'invisible whitespace-pre-wrap break-all overflow-y-auto')} style={{
  53. minHeight,
  54. maxHeight,
  55. paddingRight: (value && value.trim().length > 10000) ? 140 : 130,
  56. }}>
  57. {!value ? placeholder : value.replace(/\n$/, '\n ')}
  58. </div>
  59. <textarea
  60. ref={ref}
  61. autoFocus={autoFocus}
  62. className={cn(className, 'absolute inset-0 resize-none overflow-auto')}
  63. style={{
  64. paddingRight: (value && value.trim().length > 10000) ? 140 : 130,
  65. }}
  66. placeholder={placeholder}
  67. onChange={onChange}
  68. onKeyDown={onKeyDown}
  69. onKeyUp={onKeyUp}
  70. value={value}
  71. />
  72. </div>
  73. )
  74. },
  75. )
  76. export default AutoHeightTextarea