📋 fix: Ensure Textarea Resizes in Clipboard Edge Case (#2268)

* chore: ts-ignore fake conversation data used for testing

* chore(useTextarea): import helper functions to declutter hook

* fix(Textarea): reset textarea value explicitly by resetting `textAreaRef.current.value`
This commit is contained in:
Danny Avila 2024-04-01 13:40:21 -04:00 committed by GitHub
parent d07396d308
commit aff219c655
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 63 additions and 55 deletions

View file

@ -4,6 +4,7 @@ import { EModelEndpoint } from 'librechat-data-provider';
import type { TEndpointOption } from 'librechat-data-provider';
import type { UseFormSetValue } from 'react-hook-form';
import type { KeyboardEvent } from 'react';
import { forceResize, insertTextAtCursor, getAssistantName } from '~/utils';
import { useAssistantsMapContext } from '~/Providers/AssistantsMapContext';
import useGetSender from '~/hooks/Conversations/useGetSender';
import useFileHandling from '~/hooks/Files/useFileHandling';
@ -12,58 +13,6 @@ import useLocalize from '~/hooks/useLocalize';
type KeyEvent = KeyboardEvent<HTMLTextAreaElement>;
function insertTextAtCursor(element: HTMLTextAreaElement, textToInsert: string) {
element.focus();
// Use the browser's built-in undoable actions if possible
if (window.getSelection() && document.queryCommandSupported('insertText')) {
document.execCommand('insertText', false, textToInsert);
} else {
console.warn('insertTextAtCursor: document.execCommand is not supported');
const startPos = element.selectionStart;
const endPos = element.selectionEnd;
const beforeText = element.value.substring(0, startPos);
const afterText = element.value.substring(endPos);
element.value = beforeText + textToInsert + afterText;
element.selectionStart = element.selectionEnd = startPos + textToInsert.length;
const event = new Event('input', { bubbles: true });
element.dispatchEvent(event);
}
}
/**
* Necessary resize helper for edge cases where paste doesn't update the container height.
*
1) Resetting the height to 'auto' forces the component to recalculate height based on its current content
2) Forcing a reflow. Accessing offsetHeight will cause a reflow of the page,
ensuring that the reset height takes effect before resetting back to the scrollHeight.
This step is necessary because changes to the DOM do not instantly cause reflows.
3) Reseting back to scrollHeight reads and applies the ideal height for the current content dynamically
*/
const forceResize = (textAreaRef: React.RefObject<HTMLTextAreaElement>) => {
if (textAreaRef.current) {
textAreaRef.current.style.height = 'auto';
textAreaRef.current.offsetHeight;
textAreaRef.current.style.height = `${textAreaRef.current.scrollHeight}px`;
}
};
const getAssistantName = ({
name,
localize,
}: {
name?: string;
localize: (phraseKey: string, ...values: string[]) => string;
}) => {
if (name && name.length > 0) {
return name;
} else {
return localize('com_ui_assistant');
}
};
export default function useTextarea({
textAreaRef,
submitButtonRef,