improve: code upgrade (#4231)

This commit is contained in:
Yeuoly
2024-05-13 14:39:14 +08:00
committed by GitHub
parent e6db7ad1d5
commit bbef964eb5
21 changed files with 510 additions and 60 deletions

View File

@@ -0,0 +1,94 @@
import type { FC } from 'react'
import React, { useCallback, useState } from 'react'
import { t } from 'i18next'
import type { CodeDependency } from './types'
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
import { Check, SearchLg } from '@/app/components/base/icons/src/vender/line/general'
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
type Props = {
value: CodeDependency
available_dependencies: CodeDependency[]
onChange: (dependency: CodeDependency) => void
}
const DependencyPicker: FC<Props> = ({
available_dependencies,
value,
onChange,
}) => {
const [open, setOpen] = useState(false)
const [searchText, setSearchText] = useState('')
const handleChange = useCallback((dependency: CodeDependency) => {
return () => {
setOpen(false)
onChange(dependency)
}
}, [onChange])
return (
<PortalToFollowElem
open={open}
onOpenChange={setOpen}
placement='bottom-start'
offset={4}
>
<PortalToFollowElemTrigger onClick={() => setOpen(!open)} className='flex-grow 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='grow w-0 truncate' title={value.name}>{value.name}</div>
<ChevronDown className='shrink-0 w-3.5 h-3.5 text-gray-700' />
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent style={{
zIndex: 100,
}}>
<div className='p-1 bg-white rounded-lg shadow-sm' style={{
width: 350,
}}>
<div
className='shadow-sm bg-white mb-2 mx-1 flex items-center px-2 rounded-lg bg-gray-100'
>
<SearchLg className='shrink-0 ml-[1px] mr-[5px] w-3.5 h-3.5 text-gray-400' />
<input
value={searchText}
className='grow px-0.5 py-[7px] text-[13px] text-gray-700 bg-transparent appearance-none outline-none caret-primary-600 placeholder:text-gray-400'
placeholder={t('workflow.nodes.code.searchDependencies') || ''}
onChange={e => setSearchText(e.target.value)}
autoFocus
/>
{
searchText && (
<div
className='flex items-center justify-center ml-[5px] w-[18px] h-[18px] cursor-pointer'
onClick={() => setSearchText('')}
>
<XCircle className='w-[14px] h-[14px] text-gray-400' />
</div>
)
}
</div>
<div className='max-h-[30vh] overflow-y-auto'>
{available_dependencies.filter((v) => {
if (!searchText)
return true
return v.name.toLowerCase().includes(searchText.toLowerCase())
}).map(dependency => (
<div
key={dependency.name}
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(dependency)}
>
<div className='w-0 grow truncate'>{dependency.name}</div>
{dependency.name === value.name && <Check className='shrink-0 w-4 h-4 text-primary-600' />}
</div>
))}
</div>
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}
export default React.memo(DependencyPicker)

View File

@@ -0,0 +1,36 @@
import type { FC } from 'react'
import React from 'react'
import RemoveButton from '../_base/components/remove-button'
import type { CodeDependency } from './types'
import DependencyPicker from './dependency-picker'
type Props = {
available_dependencies: CodeDependency[]
dependencies: CodeDependency[]
handleRemove: (index: number) => void
handleChange: (index: number, dependency: CodeDependency) => void
}
const Dependencies: FC<Props> = ({
available_dependencies, dependencies, handleRemove, handleChange,
}) => {
return (
<div className='space-y-2'>
{dependencies.map((dependency, index) => (
<div className='flex items-center space-x-1' key={index}>
<DependencyPicker
value={dependency}
available_dependencies={available_dependencies}
onChange={dependency => handleChange(index, dependency)}
/>
<RemoveButton
className='!p-2 !bg-gray-100 hover:!bg-gray-200'
onClick={() => handleRemove(index)}
/>
</div>
))}
</div>
)
}
export default React.memo(Dependencies)

View File

@@ -5,6 +5,7 @@ import RemoveEffectVarConfirm from '../_base/components/remove-effect-var-confir
import useConfig from './use-config'
import type { CodeNodeType } from './types'
import { CodeLanguage } from './types'
import Dependencies from './dependency'
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'
@@ -59,6 +60,11 @@ const Panel: FC<NodePanelProps<CodeNodeType>> = ({
varInputs,
inputVarValues,
setInputVarValues,
allowDependencies,
availableDependencies,
handleAddDependency,
handleRemoveDependency,
handleChangeDependency,
} = useConfig(id, data)
return (
@@ -78,6 +84,31 @@ const Panel: FC<NodePanelProps<CodeNodeType>> = ({
filterVar={filterVar}
/>
</Field>
{
allowDependencies
? (
<div>
<Split />
<div className='pt-4'>
<Field
title={t(`${i18nPrefix}.advancedDependencies`)}
operations={
<AddButton onClick={() => handleAddDependency({ name: '', version: '' })} />
}
tooltip={t(`${i18nPrefix}.advancedDependenciesTip`)!}
>
<Dependencies
available_dependencies={availableDependencies}
dependencies={inputs.dependencies || []}
handleRemove={index => handleRemoveDependency(index)}
handleChange={(index, dependency) => handleChangeDependency(index, dependency)}
/>
</Field>
</div>
</div>
)
: null
}
<Split />
<CodeEditor
isInNode

View File

@@ -16,4 +16,10 @@ export type CodeNodeType = CommonNodeType & {
code_language: CodeLanguage
code: string
outputs: OutputVar
dependencies?: CodeDependency[]
}
export type CodeDependency = {
name: string
version: string
}

View File

@@ -5,7 +5,7 @@ import useOutputVarList from '../_base/hooks/use-output-var-list'
import { BlockEnum, VarType } from '../../types'
import type { Var } from '../../types'
import { useStore } from '../../store'
import type { CodeNodeType, OutputVar } from './types'
import type { CodeDependency, CodeNodeType, OutputVar } from './types'
import { CodeLanguage } from './types'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import useOneStepRun from '@/app/components/workflow/nodes/_base/hooks/use-one-step-run'
@@ -21,15 +21,19 @@ const useConfig = (id: string, payload: CodeNodeType) => {
const appId = useAppStore.getState().appDetail?.id
const [allLanguageDefault, setAllLanguageDefault] = useState<Record<CodeLanguage, CodeNodeType> | null>(null)
const [allLanguageDependencies, setAllLanguageDependencies] = useState<Record<CodeLanguage, CodeDependency[]> | null>(null)
useEffect(() => {
if (appId) {
(async () => {
const { config: javaScriptConfig } = await fetchNodeDefault(appId, BlockEnum.Code, { code_language: CodeLanguage.javascript }) as any
const { config: pythonConfig } = await fetchNodeDefault(appId, BlockEnum.Code, { code_language: CodeLanguage.python3 }) as any
const { config: pythonConfig, available_dependencies: pythonDependencies } = await fetchNodeDefault(appId, BlockEnum.Code, { code_language: CodeLanguage.python3 }) as any
setAllLanguageDefault({
[CodeLanguage.javascript]: javaScriptConfig as CodeNodeType,
[CodeLanguage.python3]: pythonConfig as CodeNodeType,
} as any)
setAllLanguageDependencies({
[CodeLanguage.python3]: pythonDependencies as CodeDependency[],
} as any)
})()
}
}, [appId])
@@ -41,6 +45,62 @@ const useConfig = (id: string, payload: CodeNodeType) => {
setInputs,
})
const handleAddDependency = useCallback((dependency: CodeDependency) => {
const newInputs = produce(inputs, (draft) => {
if (!draft.dependencies)
draft.dependencies = []
draft.dependencies.push(dependency)
})
setInputs(newInputs)
}, [inputs, setInputs])
const handleRemoveDependency = useCallback((index: number) => {
const newInputs = produce(inputs, (draft) => {
if (!draft.dependencies)
draft.dependencies = []
draft.dependencies.splice(index, 1)
})
setInputs(newInputs)
}, [inputs, setInputs])
const handleChangeDependency = useCallback((index: number, dependency: CodeDependency) => {
const newInputs = produce(inputs, (draft) => {
if (!draft.dependencies)
draft.dependencies = []
draft.dependencies[index] = dependency
})
setInputs(newInputs)
}, [inputs, setInputs])
const [allowDependencies, setAllowDependencies] = useState<boolean>(false)
useEffect(() => {
if (!inputs.code_language)
return
if (!allLanguageDependencies)
return
const newAllowDependencies = !!allLanguageDependencies[inputs.code_language]
setAllowDependencies(newAllowDependencies)
}, [allLanguageDependencies, inputs.code_language])
const [availableDependencies, setAvailableDependencies] = useState<CodeDependency[]>([])
useEffect(() => {
if (!inputs.code_language)
return
if (!allLanguageDependencies)
return
const newAvailableDependencies = produce(allLanguageDependencies[inputs.code_language], (draft) => {
const currentLanguage = inputs.code_language
if (!currentLanguage || !draft || !inputs.dependencies)
return []
return draft.filter((dependency) => {
return !inputs.dependencies?.find(d => d.name === dependency.name)
})
})
setAvailableDependencies(newAvailableDependencies || [])
}, [allLanguageDependencies, inputs.code_language, inputs.dependencies])
const [outputKeyOrders, setOutputKeyOrders] = useState<string[]>([])
const syncOutputKeyOrders = useCallback((outputs: OutputVar) => {
setOutputKeyOrders(Object.keys(outputs))
@@ -163,6 +223,11 @@ const useConfig = (id: string, payload: CodeNodeType) => {
inputVarValues,
setInputVarValues,
runResult,
availableDependencies,
allowDependencies,
handleAddDependency,
handleRemoveDependency,
handleChangeDependency,
}
}