mirror of
				https://github.com/langgenius/dify.git
				synced 2025-10-31 19:03:09 +00:00 
			
		
		
		
	feat: enhance FieldList component with sorting and dynamic input field management
This commit is contained in:
		
							parent
							
								
									5b8c43052e
								
							
						
					
					
						commit
						efb27eb443
					
				| @ -2,12 +2,14 @@ import type { InputVar } from '@/app/components/workflow/types' | ||||
| import { RiAddLine } from '@remixicon/react' | ||||
| import FieldItem from './field-item' | ||||
| import cn from '@/utils/classnames' | ||||
| import { useState } from 'react' | ||||
| import { useCallback, useMemo, useState } from 'react' | ||||
| import InputFieldEditor from '../editor' | ||||
| import { ReactSortable } from 'react-sortablejs' | ||||
| 
 | ||||
| type FieldListProps = { | ||||
|   LabelRightContent: React.ReactNode | ||||
|   inputFields?: InputVar[] | ||||
|   inputFields: InputVar[] | ||||
|   handleInputFieldsChange: (value: InputVar[]) => void | ||||
|   readonly?: boolean | ||||
|   labelClassName?: string | ||||
| } | ||||
| @ -15,22 +17,44 @@ type FieldListProps = { | ||||
| const FieldList = ({ | ||||
|   LabelRightContent, | ||||
|   inputFields, | ||||
|   handleInputFieldsChange, | ||||
|   readonly, | ||||
|   labelClassName, | ||||
| }: FieldListProps) => { | ||||
|   const [showInputFieldEditor, setShowInputFieldEditor] = useState(false) | ||||
| 
 | ||||
|   const optionList = useMemo(() => { | ||||
|     return inputFields.map((content, index) => { | ||||
|       return ({ | ||||
|         id: index, | ||||
|         name: content.variable, | ||||
|       }) | ||||
|     }) | ||||
|   }, [inputFields]) | ||||
| 
 | ||||
|   const handleListSortChange = useCallback((list: Array<{ id: number, name: string }>) => { | ||||
|     const newInputFields = list.map((item) => { | ||||
|       return inputFields.find(field => field.variable === item.name) | ||||
|     }) | ||||
|     handleInputFieldsChange(newInputFields as InputVar[]) | ||||
|   }, [handleInputFieldsChange, inputFields]) | ||||
| 
 | ||||
|   const handleRemoveField = useCallback((index: number) => { | ||||
|     const newInputFields = inputFields.filter((_, i) => i !== index) | ||||
|     handleInputFieldsChange(newInputFields) | ||||
|   }, [handleInputFieldsChange, inputFields]) | ||||
| 
 | ||||
|   const handleAddField = () => { | ||||
|     setShowInputFieldEditor(true) | ||||
|   } | ||||
| 
 | ||||
|   const handleEditField = (index: number) => { | ||||
|   const handleEditField = useCallback((index: number) => { | ||||
|     setShowInputFieldEditor(true) | ||||
|   } | ||||
|   }, []) | ||||
| 
 | ||||
|   const handleCloseEditor = () => { | ||||
|   const handleCloseEditor = useCallback(() => { | ||||
|     setShowInputFieldEditor(false) | ||||
|   } | ||||
|   }, []) | ||||
| 
 | ||||
|   return ( | ||||
|     <div className='flex flex-col'> | ||||
| @ -48,19 +72,25 @@ const FieldList = ({ | ||||
|           <RiAddLine className='h-4 w-4 text-text-tertiary' /> | ||||
|         </button> | ||||
|       </div> | ||||
|       <div className='flex flex-col gap-y-1 px-4 pb-2'> | ||||
|       <ReactSortable | ||||
|         className='flex flex-col gap-y-1 px-4 pb-2' | ||||
|         list={optionList} | ||||
|         setList={list => handleListSortChange(list)} | ||||
|         handle='.handle' | ||||
|         ghostClass="opacity-50" | ||||
|         animation={150} | ||||
|         disabled={readonly} | ||||
|       > | ||||
|         {inputFields?.map((item, index) => ( | ||||
|           <FieldItem | ||||
|             key={index} | ||||
|             readonly={readonly} | ||||
|             payload={item} | ||||
|             onRemove={() => { | ||||
|               // Handle remove action
 | ||||
|             }} | ||||
|             onRemove={handleRemoveField.bind(null, index)} | ||||
|             onClickEdit={handleEditField.bind(null, index)} | ||||
|           /> | ||||
|         ))} | ||||
|       </div> | ||||
|       </ReactSortable> | ||||
|       {showInputFieldEditor && ( | ||||
|         <InputFieldEditor | ||||
|           show={showInputFieldEditor} | ||||
|  | ||||
| @ -1,10 +1,12 @@ | ||||
| import { | ||||
|   memo, | ||||
|   useCallback, | ||||
|   useState, | ||||
| } from 'react' | ||||
| import { useStore } from '@/app/components/workflow/store' | ||||
| import { RiCloseLine } from '@remixicon/react' | ||||
| import { Jina } from '@/app/components/base/icons/src/public/llm' | ||||
| import type { InputVar } from '@/app/components/workflow/types' | ||||
| import { InputVarType } from '@/app/components/workflow/types' | ||||
| import Tooltip from '@/app/components/base/tooltip' | ||||
| import DialogWrapper from './dialog-wrapper' | ||||
| @ -13,13 +15,51 @@ import FooterTip from './footer-tip' | ||||
| 
 | ||||
| type InputFieldDialogProps = { | ||||
|   readonly?: boolean | ||||
|   initialInputFieldsMap?: Record<string, InputVar[]> | ||||
| } | ||||
| 
 | ||||
| const InputFieldDialog = ({ | ||||
|   readonly = false, | ||||
|   initialInputFieldsMap, | ||||
| }: InputFieldDialogProps) => { | ||||
|   const showInputFieldDialog = useStore(state => state.showInputFieldDialog) | ||||
|   const setShowInputFieldDialog = useStore(state => state.setShowInputFieldDialog) | ||||
|   // TODO: delete mock data
 | ||||
|   const [inputFieldsMap, setInputFieldsMap] = useState(initialInputFieldsMap || { | ||||
|     jina: [{ | ||||
|       variable: 'name', | ||||
|       label: 'name', | ||||
|       type: InputVarType.textInput, | ||||
|       required: true, | ||||
|       max_length: 12, | ||||
|     }, { | ||||
|       variable: 'num', | ||||
|       label: 'num', | ||||
|       type: InputVarType.number, | ||||
|       required: true, | ||||
|     }], | ||||
|     firecrawl: [{ | ||||
|       variable: 'name', | ||||
|       label: 'name', | ||||
|       type: InputVarType.textInput, | ||||
|       required: true, | ||||
|       max_length: 12, | ||||
|     }], | ||||
|     shared: [{ | ||||
|       variable: 'name', | ||||
|       label: 'name', | ||||
|       type: InputVarType.textInput, | ||||
|       required: true, | ||||
|       max_length: 12, | ||||
|     }], | ||||
|   }) | ||||
| 
 | ||||
|   const updateInputFields = useCallback((key: string, value: InputVar[]) => { | ||||
|     setInputFieldsMap(prev => ({ | ||||
|       ...prev, | ||||
|       [key]: value, | ||||
|     })) | ||||
|   }, []) | ||||
| 
 | ||||
|   const closePanel = useCallback(() => { | ||||
|     setShowInputFieldDialog?.(false) | ||||
| @ -58,20 +98,10 @@ const InputFieldDialog = ({ | ||||
|                 <span className='system-sm-medium text-text-secondary'>Jina Reader</span> | ||||
|               </div> | ||||
|             )} | ||||
|             inputFields={[{ | ||||
|               variable: 'name', | ||||
|               label: 'name', | ||||
|               type: InputVarType.textInput, | ||||
|               required: true, | ||||
|               max_length: 12, | ||||
|             }, { | ||||
|               variable: 'num', | ||||
|               label: 'num', | ||||
|               type: InputVarType.number, | ||||
|               required: true, | ||||
|             }]} | ||||
|             inputFields={inputFieldsMap.jina} | ||||
|             readonly={readonly} | ||||
|             labelClassName='pt-2 pb-1' | ||||
|             handleInputFieldsChange={updateInputFields.bind(null, 'jina')} | ||||
|           /> | ||||
|           {/* Firecrawl Field List */} | ||||
|           <FieldList | ||||
| @ -83,15 +113,10 @@ const InputFieldDialog = ({ | ||||
|                 <span className='system-sm-medium text-text-secondary'>Firecrawl</span> | ||||
|               </div> | ||||
|             )} | ||||
|             inputFields={[{ | ||||
|               variable: 'name', | ||||
|               label: 'name', | ||||
|               type: InputVarType.textInput, | ||||
|               required: true, | ||||
|               max_length: 12, | ||||
|             }]} | ||||
|             inputFields={inputFieldsMap.firecrawl} | ||||
|             readonly={readonly} | ||||
|             labelClassName='pt-2 pb-1' | ||||
|             handleInputFieldsChange={updateInputFields.bind(null, 'firecrawl')} | ||||
|           /> | ||||
|           {/* Shared Inputs */} | ||||
|           <FieldList | ||||
| @ -104,15 +129,10 @@ const InputFieldDialog = ({ | ||||
|                 /> | ||||
|               </div> | ||||
|             )} | ||||
|             inputFields={[{ | ||||
|               variable: 'name', | ||||
|               label: 'name', | ||||
|               type: InputVarType.textInput, | ||||
|               required: true, | ||||
|               max_length: 12, | ||||
|             }]} | ||||
|             inputFields={inputFieldsMap.shared} | ||||
|             readonly={readonly} | ||||
|             labelClassName='pt-1 pb-2' | ||||
|             handleInputFieldsChange={updateInputFields.bind(null, 'shared')} | ||||
|           /> | ||||
|         </div> | ||||
|         <FooterTip /> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 twwu
						twwu