mirror of
https://github.com/langgenius/dify.git
synced 2025-10-27 00:48:43 +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