|
1 | 1 | import { useEffect, useRef, useState, useCallback } from 'react'; |
| 2 | +import { throttle } from '../utils/misc'; |
2 | 3 |
|
3 | 4 | // Media Query for detecting "large" screens (matching Tailwind's lg: breakpoint) |
4 | 5 | const LARGE_SCREEN_MQ = '(min-width: 1024px)'; |
5 | 6 |
|
6 | 7 | // Calculates and sets the textarea height based on its scrollHeight |
7 | | -const adjustTextareaHeight = (textarea: HTMLTextAreaElement | null) => { |
8 | | - if (!textarea) return; |
| 8 | +const adjustTextareaHeight = throttle( |
| 9 | + (textarea: HTMLTextAreaElement | null) => { |
| 10 | + if (!textarea) return; |
9 | 11 |
|
10 | | - // Only perform auto-sizing on large screens |
11 | | - if (!window.matchMedia(LARGE_SCREEN_MQ).matches) { |
12 | | - // On small screens, reset inline height and max-height styles. |
13 | | - // This allows CSS (e.g., `rows` attribute or classes) to control the height, |
14 | | - // and enables manual resizing if `resize-vertical` is set. |
15 | | - textarea.style.height = ''; // Use 'auto' or '' to reset |
16 | | - textarea.style.maxHeight = ''; |
17 | | - return; // Do not adjust height programmatically on small screens |
18 | | - } |
| 12 | + // Only perform auto-sizing on large screens |
| 13 | + if (!window.matchMedia(LARGE_SCREEN_MQ).matches) { |
| 14 | + // On small screens, reset inline height and max-height styles. |
| 15 | + // This allows CSS (e.g., `rows` attribute or classes) to control the height, |
| 16 | + // and enables manual resizing if `resize-vertical` is set. |
| 17 | + textarea.style.height = ''; // Use 'auto' or '' to reset |
| 18 | + textarea.style.maxHeight = ''; |
| 19 | + return; // Do not adjust height programmatically on small screens |
| 20 | + } |
19 | 21 |
|
20 | | - const computedStyle = window.getComputedStyle(textarea); |
21 | | - // Get the max-height specified by CSS (e.g., from `lg:max-h-48`) |
22 | | - const currentMaxHeight = computedStyle.maxHeight; |
| 22 | + const computedStyle = window.getComputedStyle(textarea); |
| 23 | + // Get the max-height specified by CSS (e.g., from `lg:max-h-48`) |
| 24 | + const currentMaxHeight = computedStyle.maxHeight; |
23 | 25 |
|
24 | | - // Temporarily remove max-height to allow scrollHeight to be calculated correctly |
25 | | - textarea.style.maxHeight = 'none'; |
26 | | - // Reset height to 'auto' to measure the actual scrollHeight needed |
27 | | - textarea.style.height = 'auto'; |
28 | | - // Set the height to the calculated scrollHeight |
29 | | - textarea.style.height = `${textarea.scrollHeight}px`; |
30 | | - // Re-apply the original max-height from CSS to enforce the limit |
31 | | - textarea.style.maxHeight = currentMaxHeight; |
32 | | -}; |
| 26 | + // Temporarily remove max-height to allow scrollHeight to be calculated correctly |
| 27 | + textarea.style.maxHeight = 'none'; |
| 28 | + // Reset height to 'auto' to measure the actual scrollHeight needed |
| 29 | + textarea.style.height = 'auto'; |
| 30 | + // Set the height to the calculated scrollHeight |
| 31 | + textarea.style.height = `${textarea.scrollHeight}px`; |
| 32 | + // Re-apply the original max-height from CSS to enforce the limit |
| 33 | + textarea.style.maxHeight = currentMaxHeight; |
| 34 | + }, |
| 35 | + 100 |
| 36 | +); // Throttle to prevent excessive calls |
33 | 37 |
|
34 | 38 | // Interface describing the API returned by the hook |
35 | 39 | export interface ChatTextareaApi { |
@@ -65,6 +69,7 @@ export function useChatTextarea(initValue: string): ChatTextareaApi { |
65 | 69 | } |
66 | 70 | }, [textareaRef, savedInitValue]); // Depend on ref and savedInitValue |
67 | 71 |
|
| 72 | + // On input change, we adjust the height of the textarea |
68 | 73 | const handleInput = useCallback( |
69 | 74 | (event: React.FormEvent<HTMLTextAreaElement>) => { |
70 | 75 | // Call adjustTextareaHeight on every input - it will decide whether to act |
@@ -94,6 +99,6 @@ export function useChatTextarea(initValue: string): ChatTextareaApi { |
94 | 99 | }, |
95 | 100 | ref: textareaRef, |
96 | 101 | refOnSubmit: onSubmitRef, |
97 | | - onInput: handleInput, |
| 102 | + onInput: handleInput, // for adjusting height on input |
98 | 103 | }; |
99 | 104 | } |
0 commit comments