Commit 8b8fdb48 authored by Joel's avatar Joel

feat: output var

parent b4437ccd
...@@ -48,7 +48,7 @@ const Page: FC = () => { ...@@ -48,7 +48,7 @@ const Page: FC = () => {
* 2 directAnswer 3: llm 5: questionClassifier * 2 directAnswer 3: llm 5: questionClassifier
* 7 Code, 8 TemplateTransform * 7 Code, 8 TemplateTransform
*/ */
selectedNodeId='3' selectedNodeId='7'
/> />
</div> </div>
) )
......
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import produce from 'immer'
import type { OutputVar } from '../../../code/types'
import VarTypePicker from './var-type-picker'
import { Trash03 } from '@/app/components/base/icons/src/vender/line/general'
type Props = {
readonly: boolean
list: OutputVar[]
onChange: (list: OutputVar[]) => void
}
const OutputVarList: FC<Props> = ({
readonly,
list,
onChange,
}) => {
const handleVarNameChange = useCallback((index: number) => {
return (e: React.ChangeEvent<HTMLInputElement>) => {
const newList = produce(list, (draft) => {
draft[index].variable = e.target.value
})
onChange(newList)
}
}, [list, onChange])
const handleVarChange = useCallback((index: number) => {
return (value: string) => {
const newList = produce(list, (draft) => {
draft[index].variable_type = value
})
onChange(newList)
}
}, [list, onChange])
const handleVarRemove = useCallback((index: number) => {
return () => {
const newList = produce(list, (draft) => {
draft.splice(index, 1)
})
onChange(newList)
}
}, [list, onChange])
return (
<div className='space-y-2'>
{list.map((item, index) => (
<div className='flex items-center space-x-1' key={index}>
<input
readOnly={readonly}
value={item.variable}
onChange={handleVarNameChange(index)}
className='w-0 grow h-8 leading-8 px-2.5 rounded-lg border-0 bg-gray-100 text-gray-900 text-[13px] placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200'
type='text' />
<VarTypePicker
readonly={readonly}
value={item.variable_type}
onChange={handleVarChange(index)}
/>
<div
className='p-2 rounded-lg bg-gray-100 hover:bg-gray-200 cursor-pointer'
onClick={handleVarRemove(index)}
>
<Trash03 className='w-4 h-4 text-gray-500' />
</div>
</div>
))}
</div>
)
}
export default React.memo(OutputVarList)
'use client'
import type { FC } from 'react'
import React, { useCallback, useState } from 'react'
import cn from 'classnames'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import { Check } from '@/app/components/base/icons/src/vender/line/general'
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
type Props = {
className?: string
readonly: boolean
value: string
onChange: (value: string) => void
}
const TYPES = ['string', 'number']
const VarReferencePicker: FC<Props> = ({
readonly,
className,
value,
onChange,
}) => {
const [open, setOpen] = useState(false)
const handleChange = useCallback((type: string) => {
return () => {
setOpen(false)
onChange(type)
}
}, [onChange])
return (
<div className={cn(className, !readonly && 'cursor-pointer select-none')}>
<PortalToFollowElem
open={open}
onOpenChange={setOpen}
placement='bottom-start'
offset={4}
>
<PortalToFollowElemTrigger onClick={() => setOpen(!open)} className='w-[120px] cursor-pointer'>
<div className='flex items-center h-8 justify-between px-2.5 rounded-lg border-0 bg-gray-100 text-gray-900 text-[13px]'>
<div className='capitalize'>{value}</div>
<ChevronDown className='w-3.5 h-3.5 text-gray-700' />
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent style={{
zIndex: 100,
}}>
<div className='w-[120px] p-1 bg-white rounded-lg shadow-sm'>
{TYPES.map(type => (
<div
key={type}
className='flex items-center h-[30px] justify-between pl-3 pr-2 rounded-lg hover:bg-gray-100 text-gray-900 text-[13px] cursor-pointer'
onClick={handleChange(type)}
>
<div className='capitalize'>{type}</div>
{type === value && <Check className='w-4 h-4 text-primary-600' />}
</div>
))}
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
</div>
)
}
export default React.memo(VarReferencePicker)
import { useCallback } from 'react'
import produce from 'immer'
import type { OutputVar } from '../../code/types'
type Params<T> = {
inputs: T
setInputs: (newInputs: T) => void
varKey?: string
}
function useOutputVarList<T>({
inputs,
setInputs,
varKey = 'outputs',
}: Params<T>) {
const handleVarListChange = useCallback((newList: OutputVar[]) => {
const newInputs = produce(inputs, (draft: any) => {
draft[varKey] = newList
})
setInputs(newInputs)
}, [inputs, setInputs, varKey])
const handleAddVariable = useCallback(() => {
const newInputs = produce(inputs, (draft: any) => {
draft[varKey].push({
variable: '',
variable_type: 'string',
})
})
setInputs(newInputs)
}, [inputs, setInputs, varKey])
return {
handleVarListChange,
handleAddVariable,
}
}
export default useOutputVarList
...@@ -4,6 +4,7 @@ import useConfig from './use-config' ...@@ -4,6 +4,7 @@ import useConfig from './use-config'
import { mockData } from './mock' import { mockData } from './mock'
import { CodeLanguage } from './types' import { CodeLanguage } from './types'
import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list' import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list'
import OutputVarList from '@/app/components/workflow/nodes/_base/components/variable/output-var-list'
import AddButton from '@/app/components/base/button/add-button' import AddButton from '@/app/components/base/button/add-button'
import Field from '@/app/components/workflow/nodes/_base/components/field' import Field from '@/app/components/workflow/nodes/_base/components/field'
import Split from '@/app/components/workflow/nodes/_base/components/split' import Split from '@/app/components/workflow/nodes/_base/components/split'
...@@ -31,6 +32,8 @@ const Panel: FC = () => { ...@@ -31,6 +32,8 @@ const Panel: FC = () => {
handleAddVariable, handleAddVariable,
handleCodeChange, handleCodeChange,
handleCodeLanguageChange, handleCodeLanguageChange,
handleOutputVarListChange,
handleAddOutputVariable,
} = useConfig(mockData) } = useConfig(mockData)
return ( return (
<div className='mt-2'> <div className='mt-2'>
...@@ -62,7 +65,18 @@ const Panel: FC = () => { ...@@ -62,7 +65,18 @@ const Panel: FC = () => {
</div> </div>
<Split /> <Split />
<div className='px-4 pt-4 pb-2'> <div className='px-4 pt-4 pb-2'>
output var <Field
title={t(`${i18nPrefix}.outputVars`)}
operations={
<AddButton onClick={handleAddOutputVariable} />
}
>
<OutputVarList
readonly={readOnly}
list={inputs.outputs}
onChange={handleOutputVarListChange}
/>
</Field>
</div> </div>
</div> </div>
) )
......
...@@ -5,12 +5,14 @@ export enum CodeLanguage { ...@@ -5,12 +5,14 @@ export enum CodeLanguage {
javascript = 'javascript', javascript = 'javascript',
} }
export type OutputVar = {
variable: string
variable_type: string
}
export type CodeNodeType = CommonNodeType & { export type CodeNodeType = CommonNodeType & {
variables: Variable[] variables: Variable[]
code_language: CodeLanguage code_language: CodeLanguage
code: string code: string
outputs: { outputs: OutputVar[]
variable: string
variable_type: string
}[]
} }
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import useVarList from '../_base/hooks/use-var-list' import useVarList from '../_base/hooks/use-var-list'
import useOutputVarList from '../_base/hooks/use-output-var-list'
import type { CodeLanguage, CodeNodeType } from './types' import type { CodeLanguage, CodeNodeType } from './types'
const useConfig = (initInputs: CodeNodeType) => { const useConfig = (initInputs: CodeNodeType) => {
...@@ -17,12 +18,19 @@ const useConfig = (initInputs: CodeNodeType) => { ...@@ -17,12 +18,19 @@ const useConfig = (initInputs: CodeNodeType) => {
setInputs(prev => ({ ...prev, code_language: codeLanguage })) setInputs(prev => ({ ...prev, code_language: codeLanguage }))
}, [setInputs]) }, [setInputs])
const { handleVarListChange: handleOutputVarListChange, handleAddVariable: handleAddOutputVariable } = useOutputVarList<CodeNodeType>({
inputs,
setInputs,
})
return { return {
inputs, inputs,
handleVarListChange, handleVarListChange,
handleAddVariable, handleAddVariable,
handleCodeChange, handleCodeChange,
handleCodeLanguageChange, handleCodeLanguageChange,
handleOutputVarListChange,
handleAddOutputVariable,
} }
} }
......
...@@ -21,6 +21,7 @@ const translation = { ...@@ -21,6 +21,7 @@ const translation = {
}, },
code: { code: {
inputVars: 'Input Variables', inputVars: 'Input Variables',
outputVars: 'Output Variables',
}, },
templateTransform: { templateTransform: {
inputVars: 'Input Variables', inputVars: 'Input Variables',
......
...@@ -21,6 +21,7 @@ const translation = { ...@@ -21,6 +21,7 @@ const translation = {
}, },
code: { code: {
inputVars: '输入变量', inputVars: '输入变量',
outputVars: '输出变量',
}, },
templateTransform: { templateTransform: {
inputVars: '输入变量', inputVars: '输入变量',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment