|
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