Spaces:
Build error
Build error
import { | |
memo, | |
useCallback, | |
useState, | |
} from 'react' | |
import { RiAddCircleFill } from '@remixicon/react' | |
import { useStoreApi } from 'reactflow' | |
import { useTranslation } from 'react-i18next' | |
import type { OffsetOptions } from '@floating-ui/react' | |
import { | |
generateNewNode, | |
} from '../utils' | |
import { | |
useAvailableBlocks, | |
useNodesReadOnly, | |
usePanelInteractions, | |
} from '../hooks' | |
import { NODES_INITIAL_DATA } from '../constants' | |
import { useWorkflowStore } from '../store' | |
import TipPopup from './tip-popup' | |
import cn from '@/utils/classnames' | |
import BlockSelector from '@/app/components/workflow/block-selector' | |
import type { | |
OnSelectBlock, | |
} from '@/app/components/workflow/types' | |
import { | |
BlockEnum, | |
} from '@/app/components/workflow/types' | |
type AddBlockProps = { | |
renderTrigger?: (open: boolean) => React.ReactNode | |
offset?: OffsetOptions | |
} | |
const AddBlock = ({ | |
renderTrigger, | |
offset, | |
}: AddBlockProps) => { | |
const { t } = useTranslation() | |
const store = useStoreApi() | |
const workflowStore = useWorkflowStore() | |
const { nodesReadOnly } = useNodesReadOnly() | |
const { handlePaneContextmenuCancel } = usePanelInteractions() | |
const [open, setOpen] = useState(false) | |
const { availableNextBlocks } = useAvailableBlocks(BlockEnum.Start, false) | |
const handleOpenChange = useCallback((open: boolean) => { | |
setOpen(open) | |
if (!open) | |
handlePaneContextmenuCancel() | |
}, [handlePaneContextmenuCancel]) | |
const handleSelect = useCallback<OnSelectBlock>((type, toolDefaultValue) => { | |
const { | |
getNodes, | |
} = store.getState() | |
const nodes = getNodes() | |
const nodesWithSameType = nodes.filter(node => node.data.type === type) | |
const { newNode } = generateNewNode({ | |
data: { | |
...NODES_INITIAL_DATA[type], | |
title: nodesWithSameType.length > 0 ? `${t(`workflow.blocks.${type}`)} ${nodesWithSameType.length + 1}` : t(`workflow.blocks.${type}`), | |
...(toolDefaultValue || {}), | |
_isCandidate: true, | |
}, | |
position: { | |
x: 0, | |
y: 0, | |
}, | |
}) | |
workflowStore.setState({ | |
candidateNode: newNode, | |
}) | |
}, [store, workflowStore, t]) | |
const renderTriggerElement = useCallback((open: boolean) => { | |
return ( | |
<TipPopup | |
title={t('workflow.common.addBlock')} | |
> | |
<div className={cn( | |
'flex items-center justify-center w-8 h-8 rounded-lg hover:bg-black/5 hover:text-gray-700 cursor-pointer', | |
`${nodesReadOnly && '!cursor-not-allowed opacity-50'}`, | |
open && '!bg-black/5', | |
)}> | |
<RiAddCircleFill className='w-4 h-4' /> | |
</div> | |
</TipPopup> | |
) | |
}, [nodesReadOnly, t]) | |
return ( | |
<BlockSelector | |
open={open} | |
onOpenChange={handleOpenChange} | |
disabled={nodesReadOnly} | |
onSelect={handleSelect} | |
placement='top-start' | |
offset={offset ?? { | |
mainAxis: 4, | |
crossAxis: -8, | |
}} | |
trigger={renderTrigger || renderTriggerElement} | |
popupClassName='!min-w-[256px]' | |
availableBlocksTypes={availableNextBlocks} | |
/> | |
) | |
} | |
export default memo(AddBlock) | |