diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx index b2a97c066f..3f608ecb01 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx @@ -1,172 +1,173 @@ -'use client' -import type { FC } from 'react' -import React, { useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import produce from 'immer' -import RemoveButton from '../remove-button' -import VarReferencePicker from './var-reference-picker' -import Input from '@/app/components/base/input' -import type { ValueSelector, Var, Variable } from '@/app/components/workflow/types' -import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' -import { checkKeys, replaceSpaceWithUnderscreInVarNameInput } from '@/utils/var' -import Toast from '@/app/components/base/toast' -import { ReactSortable } from 'react-sortablejs' -import { v4 as uuid4 } from 'uuid' -import { RiDraggable } from '@remixicon/react' -import cn from '@/utils/classnames' - -type Props = { - nodeId: string - readonly: boolean - list: Variable[] - onChange: (list: Variable[]) => void - onVarNameChange?: (oldName: string, newName: string) => void - isSupportConstantValue?: boolean - onlyLeafNodeVar?: boolean - filterVar?: (payload: Var, valueSelector: ValueSelector) => boolean - isSupportFileVar?: boolean -} - -const VarList: FC = ({ - nodeId, - readonly, - list, - onChange, - onVarNameChange, - isSupportConstantValue, - onlyLeafNodeVar, - filterVar, - isSupportFileVar = true, -}) => { - const { t } = useTranslation() - - const payloadWithIds = list.map((item) => { - const id = uuid4() - return { - id, - v: { ...item }, - } - }) - - const handleVarNameChange = useCallback((index: number) => { - return (e: React.ChangeEvent) => { - replaceSpaceWithUnderscreInVarNameInput(e.target) - - const newKey = e.target.value - const { isValid, errorKey, errorMessageKey } = checkKeys([newKey], true) - if (!isValid) { - Toast.notify({ - type: 'error', - message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }), - }) - return - } - - if (list.map(item => item.variable?.trim()).includes(newKey.trim())) { - Toast.notify({ - type: 'error', - message: t('appDebug.varKeyError.keyAlreadyExists', { key: newKey }), - }) - return - } - - onVarNameChange?.(list[index].variable, newKey) - const newList = produce(list, (draft) => { - draft[index].variable = newKey - }) - onChange(newList) - } - }, [list, onVarNameChange, onChange]) - - const handleVarReferenceChange = useCallback((index: number) => { - return (value: ValueSelector | string, varKindType: VarKindType, varInfo?: Var) => { - const newList = produce(list, (draft) => { - if (!isSupportConstantValue || varKindType === VarKindType.variable) { - draft[index].value_selector = value as ValueSelector - draft[index].value_type = varInfo?.type - if (isSupportConstantValue) - draft[index].variable_type = VarKindType.variable - - if (!draft[index].variable) { - const variables = draft.map(v => v.variable) - let newVarName = value[value.length - 1] - let count = 1 - while (variables.includes(newVarName)) { - newVarName = `${value[value.length - 1]}_${count}` - count++ - } - draft[index].variable = newVarName - } - } - else { - draft[index].variable_type = VarKindType.constant - draft[index].value_selector = value as ValueSelector - draft[index].value = value as string - } - }) - onChange(newList) - } - }, [isSupportConstantValue, list, onChange]) - - const handleVarRemove = useCallback((index: number) => { - return () => { - const newList = produce(list, (draft) => { - draft.splice(index, 1) - }) - onChange(newList) - } - }, [list, onChange]) - - const varCount = list.length - - return ( - { onChange(list.map(item => item.v)) }} - handle='.handle' - ghostClass='opacity-50' - animation={150} - > - {list.map((item, index) => { - const canDrag = (() => { - if (readonly) - return false - return varCount > 1 - })() - return ( -
- - - {!readonly && ( - - )} - {canDrag &&
- ) - })} -
- ) -} -export default React.memo(VarList) +'use client' +import type { FC } from 'react' +import React, { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import produce from 'immer' +import RemoveButton from '../remove-button' +import VarReferencePicker from './var-reference-picker' +import Input from '@/app/components/base/input' +import type { ValueSelector, Var, Variable } from '@/app/components/workflow/types' +import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import { checkKeys, replaceSpaceWithUnderscreInVarNameInput } from '@/utils/var' +import Toast from '@/app/components/base/toast' +import { ReactSortable } from 'react-sortablejs' +import { v4 as uuid4 } from 'uuid' +import { RiDraggable } from '@remixicon/react' +import cn from '@/utils/classnames' + +type Props = { + nodeId: string + readonly: boolean + list: Variable[] + onChange: (list: Variable[]) => void + onVarNameChange?: (oldName: string, newName: string) => void + isSupportConstantValue?: boolean + onlyLeafNodeVar?: boolean + filterVar?: (payload: Var, valueSelector: ValueSelector) => boolean + isSupportFileVar?: boolean +} + +const VarList: FC = ({ + nodeId, + readonly, + list, + onChange, + onVarNameChange, + isSupportConstantValue, + onlyLeafNodeVar, + filterVar, + isSupportFileVar = true, +}) => { + const { t } = useTranslation() + + const listWithIds = useMemo(() => list.map((item) => { + const id = uuid4() + return { + id, + variable: { ...item }, + } + }), [list]) + + const handleVarNameChange = useCallback((index: number) => { + return (e: React.ChangeEvent) => { + replaceSpaceWithUnderscreInVarNameInput(e.target) + + const newKey = e.target.value + const { isValid, errorKey, errorMessageKey } = checkKeys([newKey], true) + if (!isValid) { + Toast.notify({ + type: 'error', + message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }), + }) + return + } + + if (list.map(item => item.variable?.trim()).includes(newKey.trim())) { + Toast.notify({ + type: 'error', + message: t('appDebug.varKeyError.keyAlreadyExists', { key: newKey }), + }) + return + } + + onVarNameChange?.(list[index].variable, newKey) + const newList = produce(list, (draft) => { + draft[index].variable = newKey + }) + onChange(newList) + } + }, [list, onVarNameChange, onChange]) + + const handleVarReferenceChange = useCallback((index: number) => { + return (value: ValueSelector | string, varKindType: VarKindType, varInfo?: Var) => { + const newList = produce(list, (draft) => { + if (!isSupportConstantValue || varKindType === VarKindType.variable) { + draft[index].value_selector = value as ValueSelector + draft[index].value_type = varInfo?.type + if (isSupportConstantValue) + draft[index].variable_type = VarKindType.variable + + if (!draft[index].variable) { + const variables = draft.map(v => v.variable) + let newVarName = value[value.length - 1] + let count = 1 + while (variables.includes(newVarName)) { + newVarName = `${value[value.length - 1]}_${count}` + count++ + } + draft[index].variable = newVarName + } + } + else { + draft[index].variable_type = VarKindType.constant + draft[index].value_selector = value as ValueSelector + draft[index].value = value as string + } + }) + onChange(newList) + } + }, [isSupportConstantValue, list, onChange]) + + const handleVarRemove = useCallback((index: number) => { + return () => { + const newList = produce(list, (draft) => { + draft.splice(index, 1) + }) + onChange(newList) + } + }, [list, onChange]) + + const varCount = list.length + + return ( + { onChange(list.map(item => item.variable)) }} + handle='.handle' + ghostClass='opacity-50' + animation={150} + > + {listWithIds.map((item, index) => { + const canDrag = (() => { + if (readonly) + return false + return varCount > 1 + })() + const variable = item.variable + return ( +
+ + + {!readonly && ( + + )} + {canDrag &&
+ ) + })} +
+ ) +} +export default React.memo(VarList)