mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 05:12:39 +00:00
perf:textarea auto height (#2967)
* perf:textarea auto height * optimize editor height & fix variable label split
This commit is contained in:
@@ -57,6 +57,7 @@
|
||||
"react-i18next": "14.1.2",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-textarea-autosize": "^8.5.4",
|
||||
"reactflow": "^11.7.4",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"rehype-katex": "^7.0.0",
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useRef } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
|
||||
import {
|
||||
Box,
|
||||
@@ -13,11 +13,12 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import ResizeTextarea from 'react-textarea-autosize';
|
||||
|
||||
type Props = TextareaProps & {
|
||||
title?: string;
|
||||
iconSrc?: string;
|
||||
// variables: string[];
|
||||
autoHeight?: boolean;
|
||||
};
|
||||
|
||||
const MyTextarea = React.forwardRef<HTMLTextAreaElement, Props>(function MyTextarea(props, ref) {
|
||||
@@ -28,6 +29,10 @@ const MyTextarea = React.forwardRef<HTMLTextAreaElement, Props>(function MyTexta
|
||||
const {
|
||||
title = t('common:core.app.edit.Prompt Editor'),
|
||||
iconSrc = 'modal/edit',
|
||||
autoHeight = false,
|
||||
onChange,
|
||||
maxH,
|
||||
minH,
|
||||
...childProps
|
||||
} = props;
|
||||
|
||||
@@ -35,16 +40,27 @@ const MyTextarea = React.forwardRef<HTMLTextAreaElement, Props>(function MyTexta
|
||||
|
||||
return (
|
||||
<>
|
||||
<Editor textareaRef={TextareaRef} {...childProps} onOpenModal={onOpen} />
|
||||
<Editor
|
||||
textareaRef={TextareaRef}
|
||||
autoHeight={autoHeight}
|
||||
onChange={onChange}
|
||||
maxH={maxH}
|
||||
minH={minH}
|
||||
showResize={!autoHeight}
|
||||
{...childProps}
|
||||
onOpenModal={onOpen}
|
||||
/>
|
||||
{isOpen && (
|
||||
<MyModal iconSrc={iconSrc} title={title} isOpen onClose={onClose}>
|
||||
<ModalBody>
|
||||
<Editor
|
||||
textareaRef={ModalTextareaRef}
|
||||
onChange={onChange}
|
||||
{...childProps}
|
||||
minH={'300px'}
|
||||
maxH={'auto'}
|
||||
maxH={500}
|
||||
minH={500}
|
||||
minW={['100%', '512px']}
|
||||
showResize={false}
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
@@ -71,17 +87,44 @@ export default React.memo(MyTextarea);
|
||||
const Editor = React.memo(function Editor({
|
||||
onOpenModal,
|
||||
textareaRef,
|
||||
autoHeight = false,
|
||||
onChange,
|
||||
maxH,
|
||||
minH,
|
||||
showResize,
|
||||
...props
|
||||
}: Props & {
|
||||
textareaRef: React.RefObject<HTMLTextAreaElement>;
|
||||
onOpenModal?: () => void;
|
||||
showResize?: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [scrollHeight, setScrollHeight] = useState(0);
|
||||
|
||||
return (
|
||||
<Box h={'100%'} w={'100%'} position={'relative'}>
|
||||
<Textarea ref={textareaRef} maxW={'100%'} {...props} />
|
||||
{onOpenModal && (
|
||||
<Textarea
|
||||
ref={textareaRef}
|
||||
maxW={'100%'}
|
||||
as={autoHeight ? ResizeTextarea : undefined}
|
||||
sx={
|
||||
!showResize
|
||||
? {
|
||||
'::-webkit-resizer': {
|
||||
display: 'none'
|
||||
}
|
||||
}
|
||||
: {}
|
||||
}
|
||||
{...props}
|
||||
maxH={`${maxH}px`}
|
||||
minH={`${minH}px`}
|
||||
onChange={(e) => {
|
||||
setScrollHeight(e.target.scrollHeight);
|
||||
onChange?.(e);
|
||||
}}
|
||||
/>
|
||||
{onOpenModal && maxH && scrollHeight > Number(maxH) && (
|
||||
<Box
|
||||
zIndex={1}
|
||||
position={'absolute'}
|
||||
|
@@ -23,12 +23,12 @@ import { DatasetSearchModeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
import MyRadio from '@/components/common/MyRadio';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import SelectAiModel from '@/components/Select/AIModelSelector';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
||||
|
||||
export type DatasetParamsProps = {
|
||||
searchMode: `${DatasetSearchModeEnum}`;
|
||||
@@ -317,14 +317,14 @@ const DatasetParamsModal = ({
|
||||
></QuestionTip>
|
||||
</Flex>
|
||||
<Box mt={1}>
|
||||
<PromptEditor
|
||||
<MyTextarea
|
||||
autoHeight
|
||||
minH={150}
|
||||
maxH={300}
|
||||
showOpenModal={false}
|
||||
placeholder={t('common:core.module.QueryExtension.placeholder')}
|
||||
value={cfbBgDesc}
|
||||
onChange={(e) => {
|
||||
setValue('datasetSearchExtensionBg', e);
|
||||
setValue('datasetSearchExtensionBg', e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
@@ -24,6 +24,9 @@ const WelcomeTextConfig = (props: TextareaProps) => {
|
||||
fontSize={'sm'}
|
||||
bg={'myGray.50'}
|
||||
placeholder={t('common:core.app.tip.welcomeTextTip')}
|
||||
autoHeight
|
||||
minH={100}
|
||||
maxH={200}
|
||||
{...props}
|
||||
/>
|
||||
</>
|
||||
|
@@ -1,11 +1,10 @@
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Controller, UseFormReturn } from 'react-hook-form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Input,
|
||||
NumberDecrementStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberInput,
|
||||
@@ -24,7 +23,7 @@ import { ChatBoxContext } from '../Provider';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import { useDeepCompareEffect } from 'ahooks';
|
||||
import { VariableItemType } from '@fastgpt/global/core/app/type';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
||||
|
||||
export const VariableInputItem = ({
|
||||
item,
|
||||
@@ -60,13 +59,13 @@ export const VariableInputItem = ({
|
||||
{item.description && <QuestionTip ml={1} label={item.description} />}
|
||||
</Box>
|
||||
{item.type === VariableInputEnum.input && (
|
||||
<PromptEditor
|
||||
value={item.defaultValue}
|
||||
onChange={(e) => setValue(item.key, e)}
|
||||
bg={'myGray.50'}
|
||||
<MyTextarea
|
||||
autoHeight
|
||||
minH={40}
|
||||
maxH={150}
|
||||
showOpenModal={false}
|
||||
maxH={160}
|
||||
bg={'myGray.50'}
|
||||
value={item.defaultValue}
|
||||
onChange={(e) => setValue(item.key, e.target.value)}
|
||||
/>
|
||||
)}
|
||||
{item.type === VariableInputEnum.textarea && (
|
||||
|
@@ -38,7 +38,7 @@ import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/consta
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
||||
|
||||
type props = {
|
||||
value: UserChatItemValueItemType | AIChatItemValueItemType;
|
||||
@@ -221,12 +221,15 @@ const RenderUserFormInteractive = React.memo(function RenderFormInput({
|
||||
{input.description && <QuestionTip ml={1} label={input.description} />}
|
||||
</Flex>
|
||||
{input.type === FlowNodeInputTypeEnum.input && (
|
||||
<PromptEditor
|
||||
value={input.value}
|
||||
onChange={(e) => setValue(input.label, e)}
|
||||
<MyTextarea
|
||||
isDisabled={interactive.params.submitted}
|
||||
{...register(input.label, {
|
||||
required: input.required
|
||||
})}
|
||||
bg={'white'}
|
||||
autoHeight
|
||||
minH={40}
|
||||
maxH={100}
|
||||
showOpenModal={false}
|
||||
/>
|
||||
)}
|
||||
{input.type === FlowNodeInputTypeEnum.textarea && (
|
||||
|
@@ -33,7 +33,7 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { AppContext } from '../../../context';
|
||||
import { VariableInputItem } from '@/components/core/chat/ChatContainer/ChatBox/components/VariableInput';
|
||||
import LightRowTabs from '@fastgpt/web/components/common/Tabs/LightRowTabs';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
||||
|
||||
const MyRightDrawer = dynamic(
|
||||
() => import('@fastgpt/web/components/common/MyDrawer/MyRightDrawer')
|
||||
@@ -270,16 +270,16 @@ export const useDebug = () => {
|
||||
const RenderInput = (() => {
|
||||
if (input.valueType === WorkflowIOValueTypeEnum.string) {
|
||||
return (
|
||||
<PromptEditor
|
||||
<MyTextarea
|
||||
autoHeight
|
||||
minH={40}
|
||||
maxH={160}
|
||||
bg={'myGray.50'}
|
||||
placeholder={t(input.placeholder || ('' as any))}
|
||||
value={getValues(`nodeVariables.${input.key}`)}
|
||||
onChange={(e) => {
|
||||
setValue(`nodeVariables.${input.key}`, e);
|
||||
setValue(`nodeVariables.${input.key}`, e.target.value);
|
||||
}}
|
||||
minH={50}
|
||||
maxH={150}
|
||||
showOpenModal={false}
|
||||
placeholder={t(input.placeholder || ('' as any))}
|
||||
bg={'myGray.50'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@@ -34,7 +34,7 @@ import { useFieldArray, UseFormReturn } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import DndDrag, { Draggable } from '@fastgpt/web/components/common/DndDrag';
|
||||
import PromptEditor from '@fastgpt/web/components/common/Textarea/PromptEditor';
|
||||
import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
||||
|
||||
type ListValueType = { id: string; value: string; label: string }[];
|
||||
|
||||
@@ -316,15 +316,12 @@ const InputTypeConfig = ({
|
||||
</NumberInput>
|
||||
)}
|
||||
{inputType === FlowNodeInputTypeEnum.input && (
|
||||
<PromptEditor
|
||||
value={defaultValue}
|
||||
onChange={(e) => {
|
||||
setValue('defaultValue', e);
|
||||
}}
|
||||
minH={40}
|
||||
maxH={200}
|
||||
showOpenModal={false}
|
||||
<MyTextarea
|
||||
{...register('defaultValue')}
|
||||
bg={'myGray.50'}
|
||||
autoHeight
|
||||
minH={40}
|
||||
maxH={100}
|
||||
/>
|
||||
)}
|
||||
{inputType === FlowNodeInputTypeEnum.JSONEditor && (
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { Dispatch, useMemo, useState } from 'react';
|
||||
import React, { Dispatch, useMemo } from 'react';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import NodeCard from '../render/NodeCard';
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||
@@ -10,7 +10,7 @@ import MyTextarea from '@/components/common/Textarea/MyTextarea';
|
||||
import { AppContext } from '../../../../context';
|
||||
import { AppChatConfigType, AppDetailType } from '@fastgpt/global/core/app/type';
|
||||
import { getAppChatConfig } from '@fastgpt/global/core/workflow/utils';
|
||||
import { useCreation, useMount } from 'ahooks';
|
||||
import { useMount } from 'ahooks';
|
||||
import ChatFunctionTip from '@/components/core/app/Tip';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
@@ -96,6 +96,9 @@ function Instruction({ chatConfig: { instruction }, setAppDetail }: ComponentPro
|
||||
resize={'both'}
|
||||
placeholder={t('workflow:plugin.Instruction_Tip')}
|
||||
value={instruction}
|
||||
autoHeight
|
||||
minH={100}
|
||||
maxH={240}
|
||||
onChange={(e) => {
|
||||
setAppDetail((state) => ({
|
||||
...state,
|
||||
|
Reference in New Issue
Block a user