| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  | import { useEffect, useRef } from 'react' | 
					
						
							| 
									
										
										
										
											2024-07-09 15:05:40 +08:00
										 |  |  | import cn from '@/utils/classnames' | 
					
						
							| 
									
										
										
										
											2024-04-12 16:02:56 +08:00
										 |  |  | import { sleep } from '@/utils' | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  | const AutoHeightTextarea = ( | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     ref: outerRef, | 
					
						
							|  |  |  |     value, | 
					
						
							|  |  |  |     onChange, | 
					
						
							|  |  |  |     placeholder, | 
					
						
							|  |  |  |     className, | 
					
						
							|  |  |  |     wrapperClassName, | 
					
						
							|  |  |  |     minHeight = 36, | 
					
						
							|  |  |  |     maxHeight = 96, | 
					
						
							|  |  |  |     autoFocus, | 
					
						
							|  |  |  |     controlFocus, | 
					
						
							|  |  |  |     onKeyDown, | 
					
						
							|  |  |  |     onKeyUp, | 
					
						
							|  |  |  |   }: IProps & { | 
					
						
							|  |  |  |     ref: React.RefObject<unknown>; | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  | ) => { | 
					
						
							|  |  |  |   // eslint-disable-next-line react-hooks/rules-of-hooks
 | 
					
						
							|  |  |  |   const ref = outerRef || useRef<HTMLTextAreaElement>(null) | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |   const doFocus = () => { | 
					
						
							|  |  |  |     if (ref.current) { | 
					
						
							|  |  |  |       ref.current.setSelectionRange(value.length, value.length) | 
					
						
							|  |  |  |       ref.current.focus() | 
					
						
							|  |  |  |       return true | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |     return false | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |   const focus = async () => { | 
					
						
							|  |  |  |     if (!doFocus()) { | 
					
						
							|  |  |  |       let hasFocus = false | 
					
						
							|  |  |  |       await sleep(100) | 
					
						
							|  |  |  |       hasFocus = doFocus() | 
					
						
							|  |  |  |       if (!hasFocus) | 
					
						
							|  |  |  |         focus() | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |   useEffect(() => { | 
					
						
							|  |  |  |     if (autoFocus) | 
					
						
							|  |  |  |       focus() | 
					
						
							|  |  |  |   }, []) | 
					
						
							|  |  |  |   useEffect(() => { | 
					
						
							|  |  |  |     if (controlFocus) | 
					
						
							|  |  |  |       focus() | 
					
						
							|  |  |  |   }, [controlFocus]) | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |   return ( | 
					
						
							|  |  |  |     (<div className={`relative ${wrapperClassName}`}> | 
					
						
							|  |  |  |       <div className={cn(className, 'invisible overflow-y-auto whitespace-pre-wrap  break-all')} style={{ | 
					
						
							|  |  |  |         minHeight, | 
					
						
							|  |  |  |         maxHeight, | 
					
						
							|  |  |  |         paddingRight: (value && value.trim().length > 10000) ? 140 : 130, | 
					
						
							|  |  |  |       }}> | 
					
						
							|  |  |  |         {!value ? placeholder : value.replace(/\n$/, '\n ')} | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  |       </div> | 
					
						
							| 
									
										
										
										
											2025-03-21 17:41:03 +08:00
										 |  |  |       <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>) | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-13 22:32:39 +08:00
										 |  |  | AutoHeightTextarea.displayName = 'AutoHeightTextarea' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-15 08:51:32 +08:00
										 |  |  | export default AutoHeightTextarea |