### What problem does this PR solve? Feat: Add FormSheet. #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.17.0
| <FormControl> | <FormControl> | ||||
| <Select onValueChange={field.onChange} {...field}> | <Select onValueChange={field.onChange} {...field}> | ||||
| <SelectTrigger | <SelectTrigger | ||||
| className="w-[280px]" | |||||
| value={field.value} | value={field.value} | ||||
| onReset={() => { | onReset={() => { | ||||
| form.resetField(RerankId); | form.resetField(RerankId); |
| 'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom', | 'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom', | ||||
| left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm', | left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm', | ||||
| right: | right: | ||||
| 'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm', | |||||
| 'inset-y-0 right-0 h-full border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right w-1/3 max-w-[600px]', | |||||
| }, | }, | ||||
| }, | }, | ||||
| defaultVariants: { | defaultVariants: { | ||||
| interface SheetContentProps | interface SheetContentProps | ||||
| extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, | extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, | ||||
| VariantProps<typeof sheetVariants> {} | |||||
| VariantProps<typeof sheetVariants> { | |||||
| closeIcon?: boolean; | |||||
| } | |||||
| const SheetContent = React.forwardRef< | const SheetContent = React.forwardRef< | ||||
| React.ElementRef<typeof SheetPrimitive.Content>, | React.ElementRef<typeof SheetPrimitive.Content>, | ||||
| SheetContentProps | SheetContentProps | ||||
| >(({ side = 'right', className, children, ...props }, ref) => ( | |||||
| <SheetPortal> | |||||
| <SheetOverlay /> | |||||
| <SheetPrimitive.Content | |||||
| ref={ref} | |||||
| className={cn(sheetVariants({ side }), className)} | |||||
| {...props} | |||||
| > | |||||
| {children} | |||||
| <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"> | |||||
| <X className="h-4 w-4" /> | |||||
| <span className="sr-only">Close</span> | |||||
| </SheetPrimitive.Close> | |||||
| </SheetPrimitive.Content> | |||||
| </SheetPortal> | |||||
| )); | |||||
| >( | |||||
| ( | |||||
| { side = 'right', className, children, closeIcon = true, ...props }, | |||||
| ref, | |||||
| ) => ( | |||||
| <SheetPortal> | |||||
| <SheetOverlay /> | |||||
| <SheetPrimitive.Content | |||||
| ref={ref} | |||||
| className={cn(sheetVariants({ side }), className)} | |||||
| {...props} | |||||
| > | |||||
| {children} | |||||
| {closeIcon ? ( | |||||
| <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary "> | |||||
| <X className="h-4 w-4 " /> | |||||
| <span className="sr-only">Close</span> | |||||
| </SheetPrimitive.Close> | |||||
| ) : null} | |||||
| </SheetPrimitive.Content> | |||||
| </SheetPortal> | |||||
| ), | |||||
| ); | |||||
| SheetContent.displayName = SheetPrimitive.Content.displayName; | SheetContent.displayName = SheetPrimitive.Content.displayName; | ||||
| const SheetHeader = ({ | const SheetHeader = ({ |
| } from '@xyflow/react'; | } from '@xyflow/react'; | ||||
| import '@xyflow/react/dist/style.css'; | import '@xyflow/react/dist/style.css'; | ||||
| // import ChatDrawer from '../chat/drawer'; | // import ChatDrawer from '../chat/drawer'; | ||||
| import FormDrawer from '../form-drawer/next'; | |||||
| import FormSheet from '../form-sheet/next'; | |||||
| import { | import { | ||||
| useHandleDrop, | useHandleDrop, | ||||
| useSelectCanvasData, | useSelectCanvasData, | ||||
| <Background /> | <Background /> | ||||
| </ReactFlow> | </ReactFlow> | ||||
| {formDrawerVisible && ( | {formDrawerVisible && ( | ||||
| <FormDrawer | |||||
| <FormSheet | |||||
| node={clickedNode} | node={clickedNode} | ||||
| visible={formDrawerVisible} | visible={formDrawerVisible} | ||||
| hideModal={hideFormDrawer} | hideModal={hideFormDrawer} | ||||
| singleDebugDrawerVisible={singleDebugDrawerVisible} | singleDebugDrawerVisible={singleDebugDrawerVisible} | ||||
| hideSingleDebugDrawer={hideSingleDebugDrawer} | hideSingleDebugDrawer={hideSingleDebugDrawer} | ||||
| showSingleDebugDrawer={showSingleDebugDrawer} | showSingleDebugDrawer={showSingleDebugDrawer} | ||||
| ></FormDrawer> | |||||
| ></FormSheet> | |||||
| )} | )} | ||||
| {/* {chatVisible && ( | {/* {chatVisible && ( | ||||
| <ChatDrawer | <ChatDrawer |
| import { Input } from '@/components/ui/input'; | |||||
| import { Sheet, SheetContent, SheetHeader } from '@/components/ui/sheet'; | |||||
| import { useTranslate } from '@/hooks/common-hooks'; | import { useTranslate } from '@/hooks/common-hooks'; | ||||
| import { IModalProps } from '@/interfaces/common'; | import { IModalProps } from '@/interfaces/common'; | ||||
| import { CloseOutlined } from '@ant-design/icons'; | |||||
| import { Flex, Input } from 'antd'; | |||||
| import { RAGFlowNodeType } from '@/interfaces/database/flow'; | |||||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||||
| import { get, isPlainObject, lowerFirst } from 'lodash'; | import { get, isPlainObject, lowerFirst } from 'lodash'; | ||||
| import { Play } from 'lucide-react'; | |||||
| import { Play, X } from 'lucide-react'; | |||||
| import { useEffect, useRef } from 'react'; | import { useEffect, useRef } from 'react'; | ||||
| import { useForm } from 'react-hook-form'; | |||||
| import { BeginId, Operator, operatorMap } from '../constant'; | import { BeginId, Operator, operatorMap } from '../constant'; | ||||
| import { FlowFormContext } from '../context'; | |||||
| import { RunTooltip } from '../flow-tooltip'; | |||||
| import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks'; | import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks'; | ||||
| import OperatorIcon from '../operator-icon'; | import OperatorIcon from '../operator-icon'; | ||||
| import { | import { | ||||
| needsSingleStepDebugging, | needsSingleStepDebugging, | ||||
| } from '../utils'; | } from '../utils'; | ||||
| import SingleDebugDrawer from './single-debug-drawer'; | import SingleDebugDrawer from './single-debug-drawer'; | ||||
| import { Sheet, SheetContent, SheetHeader } from '@/components/ui/sheet'; | |||||
| import { RAGFlowNodeType } from '@/interfaces/database/flow'; | |||||
| import { FlowFormContext } from '../context'; | |||||
| import { RunTooltip } from '../flow-tooltip'; | |||||
| import { zodResolver } from '@hookform/resolvers/zod'; | |||||
| import { useForm } from 'react-hook-form'; | |||||
| import { useFormConfigMap } from './use-form-config-map'; | import { useFormConfigMap } from './use-form-config-map'; | ||||
| interface IProps { | interface IProps { | ||||
| const EmptyContent = () => <div></div>; | const EmptyContent = () => <div></div>; | ||||
| const FormDrawer = ({ | |||||
| const FormSheet = ({ | |||||
| visible, | visible, | ||||
| hideModal, | hideModal, | ||||
| node, | node, | ||||
| }, [visible, form, node?.data?.form, node?.id, node, operatorName]); | }, [visible, form, node?.data?.form, node?.id, node, operatorName]); | ||||
| return ( | return ( | ||||
| <Sheet onOpenChange={hideModal} open={visible}> | |||||
| <SheetContent className="bg-white"> | |||||
| <Sheet onOpenChange={hideModal} open={visible} modal={false}> | |||||
| <SheetContent className="bg-white top-20" closeIcon={false}> | |||||
| <SheetHeader> | <SheetHeader> | ||||
| <Flex vertical> | |||||
| <Flex gap={'middle'} align="center"> | |||||
| <section className="flex-col border-b pb-2"> | |||||
| <div className="flex items-center gap-2 pb-3"> | |||||
| <OperatorIcon | <OperatorIcon | ||||
| name={operatorName} | name={operatorName} | ||||
| color={operatorMap[operatorName]?.color} | color={operatorMap[operatorName]?.color} | ||||
| ></OperatorIcon> | ></OperatorIcon> | ||||
| <Flex align="center" gap={'small'} flex={1}> | |||||
| <div className="flex items-center gap-1 flex-1"> | |||||
| <label htmlFor="">{t('title')}</label> | <label htmlFor="">{t('title')}</label> | ||||
| {node?.id === BeginId ? ( | {node?.id === BeginId ? ( | ||||
| <span>{t(BeginId)}</span> | <span>{t(BeginId)}</span> | ||||
| onChange={handleNameChange} | onChange={handleNameChange} | ||||
| ></Input> | ></Input> | ||||
| )} | )} | ||||
| </Flex> | |||||
| </div> | |||||
| {needsSingleStepDebugging(operatorName) && ( | {needsSingleStepDebugging(operatorName) && ( | ||||
| <RunTooltip> | <RunTooltip> | ||||
| /> | /> | ||||
| </RunTooltip> | </RunTooltip> | ||||
| )} | )} | ||||
| <CloseOutlined onClick={hideModal} /> | |||||
| </Flex> | |||||
| <X onClick={hideModal} /> | |||||
| </div> | |||||
| <span>{t(`${lowerFirst(operatorName)}Description`)}</span> | <span>{t(`${lowerFirst(operatorName)}Description`)}</span> | ||||
| </Flex> | |||||
| </section> | |||||
| </SheetHeader> | </SheetHeader> | ||||
| <section> | <section> | ||||
| {visible && ( | {visible && ( | ||||
| ); | ); | ||||
| }; | }; | ||||
| export default FormDrawer; | |||||
| export default FormSheet; |
| name={typeField} | name={typeField} | ||||
| render={({ field }) => ( | render={({ field }) => ( | ||||
| <FormItem className="w-2/5"> | <FormItem className="w-2/5"> | ||||
| {/* <FormLabel>City</FormLabel> */} | |||||
| <FormDescription /> | <FormDescription /> | ||||
| <FormControl> | <FormControl> | ||||
| <RAGFlowSelect | <RAGFlowSelect | ||||
| control={form.control} | control={form.control} | ||||
| name={`query.${index}.${getVariableName(typeValue)}`} | name={`query.${index}.${getVariableName(typeValue)}`} | ||||
| render={({ field }) => ( | render={({ field }) => ( | ||||
| <FormItem> | |||||
| {/* <FormLabel>State</FormLabel> */} | |||||
| <FormItem className="flex-1"> | |||||
| <FormDescription /> | <FormDescription /> | ||||
| <FormControl> | <FormControl> | ||||
| {typeValue === VariableType.Reference ? ( | {typeValue === VariableType.Reference ? ( |
| FormMessage, | FormMessage, | ||||
| } from '@/components/ui/form'; | } from '@/components/ui/form'; | ||||
| import { Textarea } from '@/components/ui/textarea'; | import { Textarea } from '@/components/ui/textarea'; | ||||
| import { useTranslate } from '@/hooks/common-hooks'; | |||||
| import { useTranslation } from 'react-i18next'; | |||||
| import { INextOperatorForm } from '../../interface'; | import { INextOperatorForm } from '../../interface'; | ||||
| import { DynamicVariableForm } from '../components/next-dynamic-input-variable'; | import { DynamicVariableForm } from '../components/next-dynamic-input-variable'; | ||||
| const RetrievalForm = ({ form, node }: INextOperatorForm) => { | const RetrievalForm = ({ form, node }: INextOperatorForm) => { | ||||
| const { t } = useTranslate('flow'); | |||||
| const { t } = useTranslation(); | |||||
| return ( | return ( | ||||
| <Form {...form}> | <Form {...form}> | ||||
| <DynamicVariableForm></DynamicVariableForm> | |||||
| <SimilaritySliderFormField name="keywords_similarity_weight"></SimilaritySliderFormField> | |||||
| <TopNFormField></TopNFormField> | |||||
| <RerankFormFields></RerankFormFields> | |||||
| <KnowledgeBaseFormField></KnowledgeBaseFormField> | |||||
| <FormField | |||||
| control={form.control} | |||||
| name="empty_response" | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormLabel>{t('chat.emptyResponse')}</FormLabel> | |||||
| <FormControl> | |||||
| <Textarea | |||||
| placeholder={t('common.namePlaceholder')} | |||||
| {...field} | |||||
| autoComplete="off" | |||||
| rows={4} | |||||
| /> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| <form | |||||
| className="space-y-6" | |||||
| onSubmit={(e) => { | |||||
| e.preventDefault(); | |||||
| }} | |||||
| > | |||||
| <DynamicVariableForm></DynamicVariableForm> | |||||
| <SimilaritySliderFormField name="keywords_similarity_weight"></SimilaritySliderFormField> | |||||
| <TopNFormField></TopNFormField> | |||||
| <RerankFormFields></RerankFormFields> | |||||
| <KnowledgeBaseFormField></KnowledgeBaseFormField> | |||||
| <FormField | |||||
| control={form.control} | |||||
| name="empty_response" | |||||
| render={({ field }) => ( | |||||
| <FormItem> | |||||
| <FormLabel>{t('chat.emptyResponse')}</FormLabel> | |||||
| <FormControl> | |||||
| <Textarea | |||||
| placeholder={t('common.namePlaceholder')} | |||||
| {...field} | |||||
| autoComplete="off" | |||||
| rows={4} | |||||
| /> | |||||
| </FormControl> | |||||
| <FormMessage /> | |||||
| </FormItem> | |||||
| )} | |||||
| /> | |||||
| </form> | |||||
| </Form> | </Form> | ||||
| ); | ); | ||||
| }; | }; |