dify/web/app/components/workflow/panel/chat-variable-panel/components/use-variable-modal-state.ts
Coding On Star 7fbb1c96db
feat(workflow): add selection context menu helpers and integrate with context menu component (#34013)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: lif <1835304752@qq.com>
Co-authored-by: hjlarry <hjlarry@163.com>
Co-authored-by: Stephen Zhou <hi@hyoban.cc>
Co-authored-by: tmimmanuel <14046872+tmimmanuel@users.noreply.github.com>
Co-authored-by: Desel72 <pedroluiscolmenares722@gmail.com>
Co-authored-by: Renzo <170978465+RenzoMXD@users.noreply.github.com>
Co-authored-by: Krishna Chaitanya <krishnabkc15@gmail.com>
Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-25 17:21:48 +08:00

229 lines
6.8 KiB
TypeScript

import type { ObjectValueItem, ToastPayload } from './variable-modal.helpers'
import type { ConversationVariable } from '@/app/components/workflow/types'
import { useMemo, useState } from 'react'
import { v4 as uuid4 } from 'uuid'
import { DEFAULT_OBJECT_VALUE } from '@/app/components/workflow/panel/chat-variable-panel/components/object-value-item'
import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type'
import {
buildObjectValueItems,
formatChatVariableValue,
formatObjectValueFromList,
getEditorMinHeight,
getPlaceholderByType,
getTypeChangeState,
parseEditorContent,
validateVariableName,
} from './variable-modal.helpers'
type UseVariableModalStateOptions = {
chatVar?: ConversationVariable
conversationVariables: ConversationVariable[]
notify: (props: ToastPayload) => void
onClose: () => void
onSave: (chatVar: ConversationVariable) => void
t: (key: string, options?: Record<string, unknown>) => string
}
type VariableModalState = {
description: string
editInJSON: boolean
editorContent?: string
name: string
objectValue: ObjectValueItem[]
type: ChatVarType
value: unknown
}
const buildObjectValueListFromRecord = (record: Record<string, string | number>) => {
return Object.keys(record).map(key => ({
key,
type: typeof record[key] === 'string' ? ChatVarType.String : ChatVarType.Number,
value: record[key],
}))
}
const buildInitialState = (chatVar?: ConversationVariable): VariableModalState => {
if (!chatVar) {
return {
description: '',
editInJSON: false,
editorContent: undefined,
name: '',
objectValue: [DEFAULT_OBJECT_VALUE],
type: ChatVarType.String,
value: undefined,
}
}
return {
description: chatVar.description,
editInJSON: chatVar.value_type === ChatVarType.ArrayObject,
editorContent: chatVar.value_type === ChatVarType.ArrayObject ? JSON.stringify(chatVar.value) : undefined,
name: chatVar.name,
objectValue: buildObjectValueItems(chatVar),
type: chatVar.value_type,
value: chatVar.value,
}
}
export const useVariableModalState = ({
chatVar,
conversationVariables,
notify,
onClose,
onSave,
t,
}: UseVariableModalStateOptions) => {
const [state, setState] = useState<VariableModalState>(() => buildInitialState(chatVar))
const editorMinHeight = useMemo(() => getEditorMinHeight(state.type), [state.type])
const placeholder = useMemo(() => getPlaceholderByType(state.type), [state.type])
const handleVarNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setState(prev => ({ ...prev, name: e.target.value || '' }))
}
const handleTypeChange = (nextType: ChatVarType) => {
const nextState = getTypeChangeState(nextType)
setState(prev => ({
...prev,
editInJSON: nextState.editInJSON,
editorContent: nextState.editorContent,
objectValue: nextState.objectValue ?? prev.objectValue,
type: nextType,
value: nextState.value,
}))
}
const handleStringOrNumberChange = (nextValue: Array<string | number | undefined>) => {
setState(prev => ({ ...prev, value: nextValue[0] }))
}
const handleEditorChange = (nextEditInJSON: boolean) => {
setState((prev) => {
const nextState: VariableModalState = {
...prev,
editInJSON: nextEditInJSON,
}
if (prev.type === ChatVarType.Object) {
if (nextEditInJSON) {
const nextValue = !prev.objectValue[0].key ? undefined : formatObjectValueFromList(prev.objectValue)
nextState.value = nextValue
nextState.editorContent = JSON.stringify(nextValue)
return nextState
}
if (!prev.editorContent) {
nextState.value = undefined
nextState.objectValue = [DEFAULT_OBJECT_VALUE]
return nextState
}
try {
const nextValue = JSON.parse(prev.editorContent) as Record<string, string | number>
nextState.value = nextValue
nextState.objectValue = buildObjectValueListFromRecord(nextValue)
}
catch {
// ignore JSON.parse errors
}
return nextState
}
if (prev.type === ChatVarType.ArrayString || prev.type === ChatVarType.ArrayNumber) {
if (nextEditInJSON) {
const nextValue = (Array.isArray(prev.value) && prev.value.length && prev.value.filter(Boolean).length)
? prev.value.filter(Boolean)
: undefined
nextState.value = nextValue
if (!prev.editorContent)
nextState.editorContent = JSON.stringify(nextValue)
return nextState
}
nextState.value = Array.isArray(prev.value) && prev.value.length ? prev.value : [undefined]
return nextState
}
if (prev.type === ChatVarType.ArrayBoolean && Array.isArray(prev.value) && nextEditInJSON)
nextState.editorContent = JSON.stringify(prev.value.map(item => item ? 'True' : 'False'))
return nextState
})
}
const handleEditorValueChange = (content: string) => {
setState((prev) => {
const nextState: VariableModalState = {
...prev,
editorContent: content,
}
if (!content) {
nextState.value = undefined
return nextState
}
try {
nextState.value = parseEditorContent({ content, type: prev.type })
}
catch {
// ignore JSON.parse errors
}
return nextState
})
}
const handleSave = () => {
if (!validateVariableName({ name: state.name, notify, t }))
return
if (!chatVar && conversationVariables.some(item => item.name === state.name)) {
notify({ type: 'error', message: 'name is existed' })
return
}
if (state.type === ChatVarType.Object && state.objectValue.some(item => !item.key && !!item.value)) {
notify({ type: 'error', message: 'object key can not be empty' })
return
}
onSave({
description: state.description,
id: chatVar ? chatVar.id : uuid4(),
name: state.name,
value: formatChatVariableValue({
editInJSON: state.editInJSON,
objectValue: state.objectValue,
type: state.type,
value: state.value,
}),
value_type: state.type,
})
onClose()
}
return {
description: state.description,
editInJSON: state.editInJSON,
editorContent: state.editorContent,
editorMinHeight,
handleEditorChange,
handleEditorValueChange,
handleSave,
handleStringOrNumberChange,
handleTypeChange,
handleVarNameChange,
name: state.name,
objectValue: state.objectValue,
placeholder,
setDescription: (description: string) => setState(prev => ({ ...prev, description })),
setObjectValue: (objectValue: ObjectValueItem[]) => setState(prev => ({ ...prev, objectValue })),
setValue: (value: unknown) => setState(prev => ({ ...prev, value })),
type: state.type,
value: state.value,
}
}