- Added a new filter to the GlobalBackgroundImageDisplay component for improved visual effects. - Modified the PositionalScroller to update positions based on clicked elements, enhancing user interaction. - Updated ScrollProgressTracker to allow scrolling effects to be applied to the body. - Removed the deprecated use-field-sizing utility and replaced it with a more robust implementation. - Improved persona styles for better layout and responsiveness in chat interfaces. - Integrated a new ScrollArea component for better scroll handling in UI. - Updated index and layout pages to reflect new components and improved structure. - Added new dependencies for Radix UI components to enhance UI capabilities.
90 lines
3.3 KiB
TypeScript
90 lines
3.3 KiB
TypeScript
// useFieldSizing.ts
|
|
import React, { useCallback, useLayoutEffect, useRef } from "react";
|
|
|
|
/**
|
|
* A type alias for the DOM elements that this hook can be applied to.
|
|
*/
|
|
type ResizableField = HTMLInputElement | HTMLTextAreaElement;
|
|
|
|
/**
|
|
* The return type of the useFieldSizing hook.
|
|
*/
|
|
interface UseFieldSizingReturn<T extends ResizableField> {
|
|
/**
|
|
* A React ref object to be attached to the target input or textarea element.
|
|
* If you passed an external ref to the hook, this will be the same ref.
|
|
*/
|
|
ref: React.RefObject<T | null>;
|
|
/**
|
|
* A function to manually trigger the resize logic. This is essential for
|
|
* non-controlled components where you might change the element's value
|
|
* programmatically (e.g., `ref.current.value = '...'`).
|
|
*/
|
|
triggerManuallyHandleResize: () => void;
|
|
}
|
|
|
|
/**
|
|
* A React hook that dynamically adjusts the height of an <input> or <textarea>
|
|
* to perfectly fit its content, mimicking the behavior of the CSS property
|
|
* `field-sizing: content`.
|
|
*
|
|
* It works seamlessly with both controlled and uncontrolled components.
|
|
*
|
|
* @param externalRef - An optional external ref to be used instead of the internal one.
|
|
* @returns An object containing the ref to attach to the DOM element and a function to manually trigger resizing.
|
|
*/
|
|
export function useFieldSizing<T extends ResizableField>(
|
|
externalRef?: React.RefObject<T>,
|
|
): UseFieldSizingReturn<T> {
|
|
const internalRef = useRef<T | null>(null);
|
|
const ref = externalRef || internalRef;
|
|
|
|
/**
|
|
* The core resizing logic.
|
|
* It resets the element's height to 'auto' to allow the browser to calculate
|
|
* the natural scrollHeight based on the content, then sets the height to that value.
|
|
* This is the standard and most reliable way to auto-size a textarea.
|
|
*/
|
|
const handleResize = useCallback(() => {
|
|
const element = ref.current;
|
|
if (element) {
|
|
// Temporarily reset height to let the browser calculate the new scrollHeight
|
|
element.style.height = "auto";
|
|
element.style.overflow = "hidden";
|
|
// Set the height to the calculated scrollHeight.
|
|
// This includes padding but not borders or margins, which is perfect.
|
|
element.style.height = !element.value.trim()
|
|
? "auto"
|
|
: `${Math.max(0, element.scrollHeight)}px`;
|
|
}
|
|
}, [ref]);
|
|
|
|
// We use useLayoutEffect to run the logic synchronously after DOM mutations
|
|
// but before the browser has painted. This prevents any flickering.
|
|
useLayoutEffect(() => {
|
|
const element = ref.current;
|
|
if (!element) {
|
|
return;
|
|
}
|
|
|
|
// The 'input' event is crucial for catching user-typed text, pastes, etc.
|
|
element.addEventListener("input", handleResize);
|
|
// Window resizing can cause text to reflow, requiring a height adjustment.
|
|
window.addEventListener("resize", handleResize);
|
|
|
|
// Perform an initial resize to set the correct height on mount,
|
|
// which is important for components with an initial `value` or `defaultValue`.
|
|
handleResize();
|
|
|
|
// Cleanup function to remove event listeners when the component unmounts.
|
|
return () => {
|
|
element.removeEventListener("input", handleResize);
|
|
window.removeEventListener("resize", handleResize);
|
|
};
|
|
}, [ref, handleResize]); // Rerun effect if the ref or callback changes
|
|
|
|
return {
|
|
ref,
|
|
triggerManuallyHandleResize: handleResize,
|
|
};
|
|
}
|