dify/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx
Stephen Zhou 6d0e36479b
refactor(i18n): use JSON with flattened key and namespace (#30114)
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-12-29 14:52:32 +08:00

186 lines
6.2 KiB
TypeScript

'use client'
import type { ChangeEvent, FC } from 'react'
import type { VarGroupItem as VarGroupItemType } from '../types'
import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
import {
RiDeleteBinLine,
} from '@remixicon/react'
import { useBoolean } from 'ahooks'
import { produce } from 'immer'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { Folder } from '@/app/components/base/icons/src/vender/line/files'
import Toast from '@/app/components/base/toast'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
import { VarType } from '@/app/components/workflow/types'
import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var'
import VarReferencePicker from '../../_base/components/variable/var-reference-picker'
import VarList from '../components/var-list'
const i18nPrefix = 'nodes.variableAssigner'
type Payload = VarGroupItemType & {
group_name?: string
}
type Props = {
readOnly: boolean
nodeId: string
payload: Payload
onChange: (newPayload: Payload) => void
groupEnabled: boolean
onGroupNameChange?: (value: string) => void
canRemove?: boolean
onRemove?: () => void
availableVars: NodeOutPutVar[]
}
const VarGroupItem: FC<Props> = ({
readOnly,
nodeId,
payload,
onChange,
groupEnabled,
onGroupNameChange,
canRemove,
onRemove,
availableVars,
}) => {
const { t } = useTranslation()
const handleAddVariable = useCallback((value: ValueSelector | string, _varKindType: VarKindType, varInfo?: Var) => {
const chosenVariables = payload.variables
if (chosenVariables.some(item => item.join('.') === (value as ValueSelector).join('.')))
return
const newPayload = produce(payload, (draft: Payload) => {
draft.variables.push(value as ValueSelector)
if (varInfo && varInfo.type !== VarType.any)
draft.output_type = varInfo.type
})
onChange(newPayload)
}, [onChange, payload])
const handleListChange = useCallback((newList: ValueSelector[], changedItem?: ValueSelector) => {
if (changedItem) {
const chosenVariables = payload.variables
if (chosenVariables.some(item => item.join('.') === (changedItem as ValueSelector).join('.')))
return
}
const newPayload = produce(payload, (draft) => {
draft.variables = newList
if (newList.length === 0)
draft.output_type = VarType.any
})
onChange(newPayload)
}, [onChange, payload])
const filterVar = useCallback((varPayload: Var) => {
if (payload.output_type === VarType.any)
return true
return varPayload.type === payload.output_type
}, [payload.output_type])
const [isEditGroupName, {
setTrue: setEditGroupName,
setFalse: setNotEditGroupName,
}] = useBoolean(false)
const handleGroupNameChange = useCallback((e: ChangeEvent<any>) => {
replaceSpaceWithUnderscoreInVarNameInput(e.target)
const value = e.target.value
const { isValid, errorKey, errorMessageKey } = checkKeys([value], false)
if (!isValid) {
Toast.notify({
type: 'error',
message: t(`varKeyError.${errorMessageKey}`, { ns: 'appDebug', key: errorKey }),
})
return
}
onGroupNameChange?.(value)
}, [onGroupNameChange, t])
return (
<Field
className="group"
title={groupEnabled
? (
<div className="flex items-center">
<div className="flex items-center !normal-case">
<Folder className="mr-0.5 h-3.5 w-3.5" />
{(!isEditGroupName)
? (
<div className="system-sm-semibold flex h-6 cursor-text items-center rounded-lg px-1 text-text-secondary hover:bg-gray-100" onClick={setEditGroupName}>
{payload.group_name}
</div>
)
: (
<input
type="text"
className="h-6 rounded-lg border border-gray-300 bg-white px-1 focus:outline-none"
// style={{
// width: `${((payload.group_name?.length || 0) + 1) / 2}em`,
// }}
size={payload.group_name?.length} // to fit the input width
autoFocus
value={payload.group_name}
onChange={handleGroupNameChange}
onBlur={setNotEditGroupName}
maxLength={30}
/>
)}
</div>
{canRemove && (
<div
className="ml-0.5 hidden cursor-pointer rounded-md p-1 text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive group-hover:block"
onClick={onRemove}
>
<RiDeleteBinLine
className="h-4 w-4"
/>
</div>
)}
</div>
)
: t(`${i18nPrefix}.title`, { ns: 'workflow' })!}
operations={(
<div className="flex h-6 items-center space-x-2">
{payload.variables.length > 0 && (
<div className="system-2xs-medium-uppercase flex h-[18px] items-center rounded-[5px] border border-divider-deep px-1 text-text-tertiary">{payload.output_type}</div>
)}
{
!readOnly
? (
<VarReferencePicker
isAddBtnTrigger
readonly={false}
nodeId={nodeId}
isShowNodeName
value={[]}
onChange={handleAddVariable}
defaultVarKindType={VarKindType.variable}
filterVar={filterVar}
availableVars={availableVars}
/>
)
: undefined
}
</div>
)}
>
<VarList
readonly={readOnly}
nodeId={nodeId}
list={payload.variables}
onChange={handleListChange}
filterVar={filterVar}
/>
</Field>
)
}
export default React.memo(VarGroupItem)