🛠️ fix: Merge Textarea Ref with Form for Simplified Handling (#2456)

* share ref correctly

* chore: remove extraneous textarea handlers and add excel text data
This commit is contained in:
Danny Avila 2024-04-18 08:19:52 -04:00 committed by GitHub
parent 26ea990045
commit 692ce3b346
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 33 additions and 75 deletions

View file

@ -29,14 +29,11 @@ const ChatForm = ({ index = 0 }) => {
defaultValues: { text: '' }, defaultValues: { text: '' },
}); });
const { handlePaste, handleKeyUp, handleKeyDown, handleCompositionStart, handleCompositionEnd } = const { handlePaste, handleKeyDown, handleCompositionStart, handleCompositionEnd } = useTextarea({
useTextarea({ textAreaRef,
textAreaRef, submitButtonRef,
submitButtonRef, disabled: !!requiresKey,
disabled: !!requiresKey, });
setValue: methods.setValue,
getValues: methods.getValues,
});
const { const {
ask, ask,
@ -58,9 +55,6 @@ const ChatForm = ({ index = 0 }) => {
} }
ask({ text: data.text }); ask({ text: data.text });
methods.reset(); methods.reset();
if (textAreaRef.current) {
textAreaRef.current.value = '';
}
}, },
[ask, methods], [ask, methods],
); );
@ -84,6 +78,13 @@ const ChatForm = ({ index = 0 }) => {
[requiresKey, invalidAssistant], [requiresKey, invalidAssistant],
); );
const { ref, ...registerProps } = methods.register('text', {
required: true,
onChange: (e) => {
methods.setValue('text', e.target.value);
},
});
return ( return (
<form <form
onSubmit={methods.handleSubmit((data) => submitMessage(data))} onSubmit={methods.handleSubmit((data) => submitMessage(data))}
@ -104,19 +105,14 @@ const ChatForm = ({ index = 0 }) => {
/> />
{endpoint && ( {endpoint && (
<TextareaAutosize <TextareaAutosize
{...methods.register('text', { {...registerProps}
required: true,
onChange: (e) => {
methods.setValue('text', e.target.value);
},
})}
autoFocus autoFocus
ref={(e) => { ref={(e) => {
ref(e);
textAreaRef.current = e; textAreaRef.current = e;
}} }}
disabled={disableInputs} disabled={disableInputs}
onPaste={handlePaste} onPaste={handlePaste}
onKeyUp={handleKeyUp}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
onCompositionStart={handleCompositionStart} onCompositionStart={handleCompositionStart}
onCompositionEnd={handleCompositionEnd} onCompositionEnd={handleCompositionEnd}

View file

@ -3,9 +3,8 @@ import { useRecoilValue } from 'recoil';
import { EModelEndpoint } from 'librechat-data-provider'; import { EModelEndpoint } from 'librechat-data-provider';
import React, { useEffect, useRef, useCallback } from 'react'; import React, { useEffect, useRef, useCallback } from 'react';
import type { TEndpointOption } from 'librechat-data-provider'; import type { TEndpointOption } from 'librechat-data-provider';
import type { UseFormSetValue } from 'react-hook-form';
import type { KeyboardEvent } from 'react'; import type { KeyboardEvent } from 'react';
import { forceResize, insertTextAtCursor, trimUndoneRange, getAssistantName } from '~/utils'; import { forceResize, insertTextAtCursor, getAssistantName } from '~/utils';
import { useAssistantsMapContext } from '~/Providers/AssistantsMapContext'; import { useAssistantsMapContext } from '~/Providers/AssistantsMapContext';
import useGetSender from '~/hooks/Conversations/useGetSender'; import useGetSender from '~/hooks/Conversations/useGetSender';
import useFileHandling from '~/hooks/Files/useFileHandling'; import useFileHandling from '~/hooks/Files/useFileHandling';
@ -18,14 +17,10 @@ type KeyEvent = KeyboardEvent<HTMLTextAreaElement>;
export default function useTextarea({ export default function useTextarea({
textAreaRef, textAreaRef,
submitButtonRef, submitButtonRef,
setValue,
getValues,
disabled = false, disabled = false,
}: { }: {
textAreaRef: React.RefObject<HTMLTextAreaElement>; textAreaRef: React.RefObject<HTMLTextAreaElement>;
submitButtonRef: React.RefObject<HTMLButtonElement>; submitButtonRef: React.RefObject<HTMLButtonElement>;
setValue: UseFormSetValue<{ text: string }>;
getValues: (field: string) => string;
disabled?: boolean; disabled?: boolean;
}) { }) {
const assistantMap = useAssistantsMapContext(); const assistantMap = useAssistantsMapContext();
@ -166,33 +161,6 @@ export default function useTextarea({
[isSubmitting, filesLoading, enterToSend, textAreaRef, submitButtonRef], [isSubmitting, filesLoading, enterToSend, textAreaRef, submitButtonRef],
); );
const handleKeyUp = (e: KeyEvent) => {
const target = e.target as HTMLTextAreaElement;
const isUndo = e.key === 'z' && (e.ctrlKey || e.metaKey);
if (isUndo && target.value.trim() === '') {
textAreaRef.current?.setRangeText('', 0, textAreaRef.current?.value?.length, 'end');
setValue('text', '', { shouldValidate: true });
forceResize(textAreaRef);
} else if (isUndo) {
trimUndoneRange(textAreaRef);
setValue('text', '', { shouldValidate: true });
forceResize(textAreaRef);
}
if ((e.keyCode === 8 || e.key === 'Backspace') && target.value.trim() === '') {
textAreaRef.current?.setRangeText('', 0, textAreaRef.current?.value?.length, 'end');
}
if (e.key === 'Enter' && e.shiftKey) {
return console.log('Enter + Shift');
}
if (isSubmitting) {
return;
}
};
const handleCompositionStart = () => { const handleCompositionStart = () => {
isComposing.current = true; isComposing.current = true;
}; };
@ -201,36 +169,31 @@ export default function useTextarea({
isComposing.current = false; isComposing.current = false;
}; };
/** Necessary handler to update form state when paste doesn't fire textArea input event */
const setPastedValue = useCallback(
(textArea: HTMLTextAreaElement, pastedData: string) => {
const currentTextValue = getValues('text') || '';
const { selectionStart, selectionEnd } = textArea;
const newValue =
currentTextValue.substring(0, selectionStart) +
pastedData +
currentTextValue.substring(selectionEnd);
setValue('text', newValue, { shouldValidate: true });
},
[getValues, setValue],
);
const handlePaste = useCallback( const handlePaste = useCallback(
(e: React.ClipboardEvent<HTMLTextAreaElement>) => { (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
e.preventDefault();
const textArea = textAreaRef.current; const textArea = textAreaRef.current;
if (!textArea) { if (!textArea) {
return; return;
} }
const pastedData = e.clipboardData.getData('text/plain'); if (!e.clipboardData) {
setPastedValue(textArea, pastedData); return;
insertTextAtCursor(textArea, pastedData); }
forceResize(textAreaRef);
if (e.clipboardData && e.clipboardData.files.length > 0) { let includedText = '';
const { types } = e.clipboardData;
if (types.indexOf('text/rtf') !== -1 || types.indexOf('Files') !== -1) {
e.preventDefault(); e.preventDefault();
includedText = e.clipboardData.getData('text/plain');
}
if (includedText && e.clipboardData.files.length > 0) {
insertTextAtCursor(textAreaRef.current, includedText);
forceResize(textAreaRef);
}
if (e.clipboardData.files.length > 0) {
setFilesLoading(true); setFilesLoading(true);
const timestampedFiles: File[] = []; const timestampedFiles: File[] = [];
for (const file of e.clipboardData.files) { for (const file of e.clipboardData.files) {
@ -242,14 +205,13 @@ export default function useTextarea({
handleFiles(timestampedFiles); handleFiles(timestampedFiles);
} }
}, },
[handleFiles, setFilesLoading, setPastedValue, textAreaRef], [handleFiles, setFilesLoading, textAreaRef],
); );
return { return {
textAreaRef, textAreaRef,
handleKeyDown,
handleKeyUp,
handlePaste, handlePaste,
handleKeyDown,
handleCompositionStart, handleCompositionStart,
handleCompositionEnd, handleCompositionEnd,
}; };