This commit is contained in:
2025-12-01 17:21:38 +08:00
parent 32fee2b8ab
commit fab8c13cb3
7511 changed files with 996300 additions and 0 deletions

View File

@@ -0,0 +1 @@
export const CUSTOM_DATA_SOURCE_EMPTY_NODE = 'custom-data-source-empty'

View File

@@ -0,0 +1,22 @@
import type { NodeDefault } from '../../types'
import type { DataSourceEmptyNodeType } from './types'
import { genNodeMetaData } from '@/app/components/workflow/utils'
import { BlockEnum } from '@/app/components/workflow/types'
const metaData = genNodeMetaData({
sort: -1,
type: BlockEnum.DataSourceEmpty,
isUndeletable: true,
isSingleton: true,
})
const nodeDefault: NodeDefault<DataSourceEmptyNodeType> = {
metaData,
defaultValue: {},
checkValid() {
return {
isValid: true,
}
},
}
export default nodeDefault

View File

@@ -0,0 +1,50 @@
import { useCallback } from 'react'
import { useStoreApi } from 'reactflow'
import { produce } from 'immer'
import type { OnSelectBlock } from '@/app/components/workflow/types'
import { generateNewNode } from '@/app/components/workflow/utils'
import { useNodesMetaData } from '@/app/components/workflow/hooks'
export const useReplaceDataSourceNode = (id: string) => {
const store = useStoreApi()
const { nodesMap: nodesMetaDataMap } = useNodesMetaData()
const handleReplaceNode = useCallback<OnSelectBlock>((
type,
pluginDefaultValue,
) => {
const {
getNodes,
setNodes,
} = store.getState()
const nodes = getNodes()
const emptyNodeIndex = nodes.findIndex(node => node.id === id)
if (emptyNodeIndex < 0) return
const {
defaultValue,
} = nodesMetaDataMap![type]
const emptyNode = nodes[emptyNodeIndex]
const { newNode } = generateNewNode({
data: {
...(defaultValue as any),
...pluginDefaultValue,
},
position: {
x: emptyNode.position.x,
y: emptyNode.position.y,
},
})
const newNodes = produce(nodes, (draft) => {
draft[emptyNodeIndex] = newNode
})
const newNodesWithoutTempNodes = produce(newNodes, (draft) => {
return draft.filter(node => !node.data._isTempNode)
})
setNodes(newNodesWithoutTempNodes)
}, [])
return {
handleReplaceNode,
}
}

View File

@@ -0,0 +1,74 @@
import {
memo,
useCallback,
} from 'react'
import { useTranslation } from 'react-i18next'
import type { NodeProps } from 'reactflow'
import { RiAddLine } from '@remixicon/react'
import cn from '@/utils/classnames'
import Button from '@/app/components/base/button'
import BlockSelector from '@/app/components/workflow/block-selector'
import { useReplaceDataSourceNode } from './hooks'
const DataSourceEmptyNode = ({ id, data }: NodeProps) => {
const { t } = useTranslation()
const { handleReplaceNode } = useReplaceDataSourceNode(id)
const renderTrigger = useCallback(() => {
return (
<Button
variant='primary'
className='w-full'
>
<RiAddLine className='mr-1 h-4 w-4' />
{t('workflow.nodes.dataSource.add')}
</Button>
)
}, [])
return (
<div
className={cn(
'relative flex rounded-2xl border',
'border-transparent',
)}
style={{
width: data.width,
height: data.height,
}}
>
<div className='absolute inset-[-2px] top-[-22px] z-[-1] rounded-[18px] bg-node-data-source-bg p-0.5 backdrop-blur-[6px]'>
<div className='system-2xs-semibold-uppercase flex h-5 items-center px-2.5 text-text-tertiary'>
{t('workflow.blocks.datasource')}
</div>
</div>
<div
className={cn(
'group relative shadow-xs',
'rounded-[15px] border border-transparent',
'w-[240px] bg-workflow-block-bg',
)}
>
<div className={cn(
'flex items-center rounded-t-2xl p-3',
)}>
<BlockSelector
asChild
onSelect={handleReplaceNode}
trigger={renderTrigger}
noBlocks
noTools
popupClassName='w-[320px]'
placement='bottom-start'
offset={{
mainAxis: 4,
crossAxis: 0,
}}
/>
</div>
</div>
</div>
)
}
export default memo(DataSourceEmptyNode)

View File

@@ -0,0 +1,3 @@
import type { CommonNodeType } from '@/app/components/workflow/types'
export type DataSourceEmptyNodeType = CommonNodeType