Feat/loop break node (#17268)

This commit is contained in:
zxhlyh
2025-04-01 16:52:07 +08:00
committed by GitHub
parent 627a9e2ce1
commit 713902dc47
64 changed files with 1397 additions and 139 deletions

View File

@@ -138,9 +138,6 @@ const ConditionWrap: FC<Props> = ({
)}
</div>
</div>
{!isSubVariable && (
<div className='mx-3 my-2 h-[1px] bg-divider-subtle'></div>
)}
</div>
</>
)

View File

@@ -0,0 +1,13 @@
import { useTranslation } from 'react-i18next'
const Empty = () => {
const { t } = useTranslation()
return (
<div className='system-xs-regular flex h-10 items-center justify-center rounded-[10px] bg-background-section text-text-tertiary'>
{t('workflow.nodes.loop.setLoopVariables')}
</div>
)
}
export default Empty

View File

@@ -0,0 +1,144 @@
import {
useCallback,
useMemo,
} from 'react'
import { useTranslation } from 'react-i18next'
import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker'
import Input from '@/app/components/base/input'
import Textarea from '@/app/components/base/textarea'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
import type {
LoopVariable,
} from '@/app/components/workflow/nodes/loop/types'
import type {
Var,
} from '@/app/components/workflow/types'
import {
ValueType,
VarType,
} from '@/app/components/workflow/types'
const objectPlaceholder = `# example
# {
# "name": "ray",
# "age": 20
# }`
const arrayStringPlaceholder = `# example
# [
# "value1",
# "value2"
# ]`
const arrayNumberPlaceholder = `# example
# [
# 100,
# 200
# ]`
const arrayObjectPlaceholder = `# example
# [
# {
# "name": "ray",
# "age": 20
# },
# {
# "name": "lily",
# "age": 18
# }
# ]`
type FormItemProps = {
nodeId: string
item: LoopVariable
onChange: (value: any) => void
}
const FormItem = ({
nodeId,
item,
onChange,
}: FormItemProps) => {
const { t } = useTranslation()
const { value_type, var_type, value } = item
const handleInputChange = useCallback((e: any) => {
onChange(e.target.value)
}, [onChange])
const handleChange = useCallback((value: any) => {
onChange(value)
}, [onChange])
const filterVar = useCallback((variable: Var) => {
return variable.type === var_type
}, [var_type])
const editorMinHeight = useMemo(() => {
if (var_type === VarType.arrayObject)
return '240px'
return '120px'
}, [var_type])
const placeholder = useMemo(() => {
if (var_type === VarType.arrayString)
return arrayStringPlaceholder
if (var_type === VarType.arrayNumber)
return arrayNumberPlaceholder
if (var_type === VarType.arrayObject)
return arrayObjectPlaceholder
return objectPlaceholder
}, [var_type])
return (
<div>
{
value_type === ValueType.variable && (
<VarReferencePicker
readonly={false}
nodeId={nodeId}
isShowNodeName
value={value}
onChange={handleChange}
filterVar={filterVar}
placeholder={t('workflow.nodes.assigner.setParameter') as string}
/>
)
}
{
value_type === ValueType.constant && var_type === VarType.string && (
<Textarea
value={value}
onChange={handleInputChange}
className='min-h-12 w-full'
/>
)
}
{
value_type === ValueType.constant && var_type === VarType.number && (
<Input
type="number"
value={value}
onChange={handleInputChange}
className='w-full'
/>
)
}
{
value_type === ValueType.constant
&& (var_type === VarType.object || var_type === VarType.arrayString || var_type === VarType.arrayNumber || var_type === VarType.arrayObject)
&& (
<div className='w-full rounded-[10px] bg-components-input-bg-normal py-2 pl-3 pr-1' style={{ height: editorMinHeight }}>
<CodeEditor
value={value}
isExpand
noWrapper
language={CodeLanguage.json}
onChange={handleChange}
className='w-full'
placeholder={<div className='whitespace-pre'>{placeholder}</div>}
/>
</div>
)
}
</div>
)
}
export default FormItem

View File

@@ -0,0 +1,28 @@
import Empty from './empty'
import Item from './item'
import type {
LoopVariable,
LoopVariablesComponentShape,
} from '@/app/components/workflow/nodes/loop/types'
type LoopVariableProps = {
variables?: LoopVariable[]
} & LoopVariablesComponentShape
const LoopVariableComponent = ({
variables = [],
...restProps
}: LoopVariableProps) => {
if (!variables.length)
return <Empty />
return variables.map(variable => (
<Item
key={variable.id}
item={variable}
{...restProps}
/>
))
}
export default LoopVariableComponent

View File

@@ -0,0 +1,37 @@
import { useTranslation } from 'react-i18next'
import PureSelect from '@/app/components/base/select/pure'
type InputModeSelectProps = {
value?: string
onChange: (value: string) => void
}
const InputModeSelect = ({
value,
onChange,
}: InputModeSelectProps) => {
const { t } = useTranslation()
const options = [
{
label: 'Variable',
value: 'variable',
},
{
label: 'Constant',
value: 'constant',
},
]
return (
<PureSelect
options={options}
value={value}
onChange={onChange}
popupProps={{
title: t('workflow.nodes.loop.inputMode'),
className: 'w-[132px]',
}}
/>
)
}
export default InputModeSelect

View File

@@ -0,0 +1,78 @@
import { useCallback } from 'react'
import { RiDeleteBinLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import InputModeSelect from './input-mode-selec'
import VariableTypeSelect from './variable-type-select'
import FormItem from './form-item'
import ActionButton from '@/app/components/base/action-button'
import Input from '@/app/components/base/input'
import type {
LoopVariable,
LoopVariablesComponentShape,
} from '@/app/components/workflow/nodes/loop/types'
type ItemProps = {
item: LoopVariable
} & LoopVariablesComponentShape
const Item = ({
nodeId,
item,
handleRemoveLoopVariable,
handleUpdateLoopVariable,
}: ItemProps) => {
const { t } = useTranslation()
const handleUpdateItemLabel = useCallback((e: any) => {
handleUpdateLoopVariable(item.id, { label: e.target.value })
}, [item.id, handleUpdateLoopVariable])
const handleUpdateItemVarType = useCallback((value: any) => {
handleUpdateLoopVariable(item.id, { var_type: value, value: undefined })
}, [item.id, handleUpdateLoopVariable])
const handleUpdateItemValueType = useCallback((value: any) => {
handleUpdateLoopVariable(item.id, { value_type: value, value: undefined })
}, [item.id, handleUpdateLoopVariable])
const handleUpdateItemValue = useCallback((value: any) => {
handleUpdateLoopVariable(item.id, { value })
}, [item.id, handleUpdateLoopVariable])
return (
<div className='mb-4 flex last-of-type:mb-0'>
<div className='w-0 grow'>
<div className='mb-1 grid grid-cols-3 gap-1'>
<Input
value={item.label}
onChange={handleUpdateItemLabel}
autoFocus={!item.label}
placeholder={t('workflow.nodes.loop.variableName')}
/>
<VariableTypeSelect
value={item.var_type}
onChange={handleUpdateItemVarType}
/>
<InputModeSelect
value={item.value_type}
onChange={handleUpdateItemValueType}
/>
</div>
<div>
<FormItem
nodeId={nodeId}
item={item}
onChange={handleUpdateItemValue}
/>
</div>
</div>
<ActionButton
className='shrink-0'
size='l'
onClick={() => handleRemoveLoopVariable(item.id)}
>
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary' />
</ActionButton>
</div>
)
}
export default Item

View File

@@ -0,0 +1,51 @@
import PureSelect from '@/app/components/base/select/pure'
import { VarType } from '@/app/components/workflow/types'
type VariableTypeSelectProps = {
value?: string
onChange: (value: string) => void
}
const VariableTypeSelect = ({
value,
onChange,
}: VariableTypeSelectProps) => {
const options = [
{
label: 'String',
value: VarType.string,
},
{
label: 'Number',
value: VarType.number,
},
{
label: 'Object',
value: VarType.object,
},
{
label: 'Array[string]',
value: VarType.arrayString,
},
{
label: 'Array[number]',
value: VarType.arrayNumber,
},
{
label: 'Array[object]',
value: VarType.arrayObject,
},
]
return (
<PureSelect
options={options}
value={value}
onChange={onChange}
popupProps={{
className: 'w-[132px]',
}}
/>
)
}
export default VariableTypeSelect