mirror of
https://github.com/langgenius/dify.git
synced 2025-11-22 07:56:37 +00:00
Signed-off-by: lyzno1 <yuanyouhuilyz@gmail.com> Co-authored-by: Stream <Stream_2@qq.com> Co-authored-by: lyzno1 <92089059+lyzno1@users.noreply.github.com> Co-authored-by: zhsama <torvalds@linux.do> Co-authored-by: Harry <xh001x@hotmail.com> Co-authored-by: lyzno1 <yuanyouhuilyz@gmail.com> Co-authored-by: yessenia <yessenia.contact@gmail.com> Co-authored-by: hjlarry <hjlarry@163.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: WTW0313 <twwu@dify.ai> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
140 lines
4.8 KiB
TypeScript
140 lines
4.8 KiB
TypeScript
import {
|
|
memo,
|
|
useCallback,
|
|
useEffect,
|
|
useMemo,
|
|
} from 'react'
|
|
import { useNodes } from 'reactflow'
|
|
import { useTranslation } from 'react-i18next'
|
|
import BlockIcon from '../block-icon'
|
|
import type { BlockEnum, CommonNodeType } from '../types'
|
|
import { BlockEnum as BlockEnumValues } from '../types'
|
|
// import { useNodeMetaData } from '../hooks'
|
|
import { START_BLOCKS } from './constants'
|
|
import type { TriggerDefaultValue } from './types'
|
|
import Tooltip from '@/app/components/base/tooltip'
|
|
import { useAvailableNodesMetaData } from '../../workflow-app/hooks'
|
|
|
|
type StartBlocksProps = {
|
|
searchText: string
|
|
onSelect: (type: BlockEnum, triggerDefaultValue?: TriggerDefaultValue) => void
|
|
availableBlocksTypes?: BlockEnum[]
|
|
onContentStateChange?: (hasContent: boolean) => void
|
|
hideUserInput?: boolean
|
|
}
|
|
|
|
const StartBlocks = ({
|
|
searchText,
|
|
onSelect,
|
|
availableBlocksTypes = [],
|
|
onContentStateChange,
|
|
hideUserInput = false, // Allow parent to explicitly hide Start node option (e.g. when one already exists).
|
|
}: StartBlocksProps) => {
|
|
const { t } = useTranslation()
|
|
const nodes = useNodes()
|
|
// const nodeMetaData = useNodeMetaData()
|
|
const availableNodesMetaData = useAvailableNodesMetaData()
|
|
|
|
const filteredBlocks = useMemo(() => {
|
|
// Check if Start node already exists in workflow
|
|
const hasStartNode = nodes.some(node => (node.data as CommonNodeType)?.type === BlockEnumValues.Start)
|
|
const normalizedSearch = searchText.toLowerCase()
|
|
const getDisplayName = (blockType: BlockEnum) => {
|
|
if (blockType === BlockEnumValues.TriggerWebhook)
|
|
return t('workflow.customWebhook')
|
|
|
|
return t(`workflow.blocks.${blockType}`)
|
|
}
|
|
|
|
return START_BLOCKS.filter((block) => {
|
|
// Hide User Input (Start) if it already exists in workflow or if hideUserInput is true
|
|
if (block.type === BlockEnumValues.Start && (hasStartNode || hideUserInput))
|
|
return false
|
|
|
|
// Filter by search text
|
|
const displayName = getDisplayName(block.type).toLowerCase()
|
|
if (!displayName.includes(normalizedSearch) && !block.title.toLowerCase().includes(normalizedSearch))
|
|
return false
|
|
|
|
// availableBlocksTypes now contains properly filtered entry node types from parent
|
|
return availableBlocksTypes.includes(block.type)
|
|
})
|
|
}, [searchText, availableBlocksTypes, nodes, t, hideUserInput])
|
|
|
|
const isEmpty = filteredBlocks.length === 0
|
|
|
|
useEffect(() => {
|
|
onContentStateChange?.(!isEmpty)
|
|
}, [isEmpty, onContentStateChange])
|
|
|
|
const renderBlock = useCallback((block: { type: BlockEnum; title: string; description?: string }) => (
|
|
<Tooltip
|
|
key={block.type}
|
|
position='right'
|
|
popupClassName='w-[224px] rounded-xl'
|
|
needsDelay={false}
|
|
popupContent={(
|
|
<div>
|
|
<BlockIcon
|
|
size='md'
|
|
className='mb-2'
|
|
type={block.type}
|
|
/>
|
|
<div className='system-md-medium mb-1 text-text-primary'>
|
|
{block.type === BlockEnumValues.TriggerWebhook
|
|
? t('workflow.customWebhook')
|
|
: t(`workflow.blocks.${block.type}`)
|
|
}
|
|
</div>
|
|
<div className='system-xs-regular text-text-secondary'>
|
|
{t(`workflow.blocksAbout.${block.type}`)}
|
|
</div>
|
|
{(block.type === BlockEnumValues.TriggerWebhook || block.type === BlockEnumValues.TriggerSchedule) && (
|
|
<div className='system-xs-regular mb-1 mt-1 text-text-tertiary'>
|
|
{t('tools.author')} {t('workflow.difyTeam')}
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
>
|
|
<div
|
|
className='flex h-8 w-full cursor-pointer items-center rounded-lg px-3 hover:bg-state-base-hover'
|
|
onClick={() => onSelect(block.type)}
|
|
>
|
|
<BlockIcon
|
|
className='mr-2 shrink-0'
|
|
type={block.type}
|
|
/>
|
|
<div className='flex w-0 grow items-center justify-between text-sm text-text-secondary'>
|
|
<span className='truncate'>{t(`workflow.blocks.${block.type}`)}</span>
|
|
{block.type === BlockEnumValues.Start && (
|
|
<span className='system-xs-regular ml-2 shrink-0 text-text-quaternary'>{t('workflow.blocks.originalStartNode')}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Tooltip>
|
|
), [availableNodesMetaData, onSelect, t])
|
|
|
|
if (isEmpty)
|
|
return null
|
|
|
|
return (
|
|
<div className='p-1'>
|
|
<div className='mb-1'>
|
|
{filteredBlocks.map((block, index) => (
|
|
<div key={block.type}>
|
|
{renderBlock(block)}
|
|
{block.type === BlockEnumValues.Start && index < filteredBlocks.length - 1 && (
|
|
<div className='my-1 px-3'>
|
|
<div className='border-t border-divider-subtle' />
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default memo(StartBlocks)
|