123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- import type {
- FC,
- ReactElement,
- } from 'react'
- import {
- cloneElement,
- memo,
- useEffect,
- useMemo,
- useRef,
- } from 'react'
- import {
- RiCheckboxCircleLine,
- RiErrorWarningLine,
- RiLoader2Line,
- } from '@remixicon/react'
- import { useTranslation } from 'react-i18next'
- import type { NodeProps } from '../../types'
- import {
- BlockEnum,
- NodeRunningStatus,
- } from '../../types'
- import {
- useNodesReadOnly,
- useToolIcon,
- } from '../../hooks'
- import { useNodeIterationInteractions } from '../iteration/use-interactions'
- import type { IterationNodeType } from '../iteration/types'
- import {
- NodeSourceHandle,
- NodeTargetHandle,
- } from './components/node-handle'
- import NodeResizer from './components/node-resizer'
- import NodeControl from './components/node-control'
- import AddVariablePopupWithPosition from './components/add-variable-popup-with-position'
- import cn from '@/utils/classnames'
- import BlockIcon from '@/app/components/workflow/block-icon'
- import Tooltip from '@/app/components/base/tooltip'
- type BaseNodeProps = {
- children: ReactElement
- } & NodeProps
- const BaseNode: FC<BaseNodeProps> = ({
- id,
- data,
- children,
- }) => {
- const { t } = useTranslation()
- const nodeRef = useRef<HTMLDivElement>(null)
- const { nodesReadOnly } = useNodesReadOnly()
- const { handleNodeIterationChildSizeChange } = useNodeIterationInteractions()
- const toolIcon = useToolIcon(data)
- useEffect(() => {
- if (nodeRef.current && data.selected && data.isInIteration) {
- const resizeObserver = new ResizeObserver(() => {
- handleNodeIterationChildSizeChange(id)
- })
- resizeObserver.observe(nodeRef.current)
- return () => {
- resizeObserver.disconnect()
- }
- }
- }, [data.isInIteration, data.selected, id, handleNodeIterationChildSizeChange])
- const showSelectedBorder = data.selected || data._isBundled || data._isEntering
- const {
- showRunningBorder,
- showSuccessBorder,
- showFailedBorder,
- } = useMemo(() => {
- return {
- showRunningBorder: data._runningStatus === NodeRunningStatus.Running && !showSelectedBorder,
- showSuccessBorder: data._runningStatus === NodeRunningStatus.Succeeded && !showSelectedBorder,
- showFailedBorder: data._runningStatus === NodeRunningStatus.Failed && !showSelectedBorder,
- }
- }, [data._runningStatus, showSelectedBorder])
- return (
- <div
- className={cn(
- 'flex border-[2px] rounded-2xl',
- showSelectedBorder ? 'border-components-option-card-option-selected-border' : 'border-transparent',
- !showSelectedBorder && data._inParallelHovering && 'border-workflow-block-border-highlight',
- )}
- ref={nodeRef}
- style={{
- width: data.type === BlockEnum.Iteration ? data.width : 'auto',
- height: data.type === BlockEnum.Iteration ? data.height : 'auto',
- }}
- >
- <div
- className={cn(
- 'group relative pb-1 shadow-xs',
- 'border border-transparent rounded-[15px]',
- data.type !== BlockEnum.Iteration && 'w-[240px] bg-workflow-block-bg',
- data.type === BlockEnum.Iteration && 'flex flex-col w-full h-full bg-[#fcfdff]/80',
- !data._runningStatus && 'hover:shadow-lg',
- showRunningBorder && '!border-primary-500',
- showSuccessBorder && '!border-[#12B76A]',
- showFailedBorder && '!border-[#F04438]',
- data._isBundled && '!shadow-lg',
- )}
- >
- {
- data._inParallelHovering && (
- <div className='absolute left-2 -top-2.5 top system-2xs-medium-uppercase text-text-tertiary z-10'>
- {t('workflow.common.parallelRun')}
- </div>
- )
- }
- {
- data._showAddVariablePopup && (
- <AddVariablePopupWithPosition
- nodeId={id}
- nodeData={data}
- />
- )
- }
- {
- data.type === BlockEnum.Iteration && (
- <NodeResizer
- nodeId={id}
- nodeData={data}
- />
- )
- }
- {
- !data._isCandidate && (
- <NodeTargetHandle
- id={id}
- data={data}
- handleClassName='!top-4 !-left-[9px] !translate-y-0'
- handleId='target'
- />
- )
- }
- {
- data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && !data._isCandidate && (
- <NodeSourceHandle
- id={id}
- data={data}
- handleClassName='!top-4 !-right-[9px] !translate-y-0'
- handleId='source'
- />
- )
- }
- {
- !data._runningStatus && !nodesReadOnly && !data._isCandidate && (
- <NodeControl
- id={id}
- data={data}
- />
- )
- }
- <div className={cn(
- 'flex items-center px-3 pt-3 pb-2 rounded-t-2xl',
- data.type === BlockEnum.Iteration && 'bg-[rgba(250,252,255,0.9)]',
- )}>
- <BlockIcon
- className='shrink-0 mr-2'
- type={data.type}
- size='md'
- toolIcon={toolIcon}
- />
- <div
- title={data.title}
- className='grow mr-1 system-sm-semibold-uppercase text-text-primary truncate flex items-center'
- >
- <div>
- {data.title}
- </div>
- {
- data.type === BlockEnum.Iteration && (data as IterationNodeType).is_parallel && (
- <Tooltip popupContent={
- <div className='w-[180px]'>
- <div className='font-extrabold'>
- {t('workflow.nodes.iteration.parallelModeEnableTitle')}
- </div>
- {t('workflow.nodes.iteration.parallelModeEnableDesc')}
- </div>}
- >
- <div className='flex justify-center items-center px-[5px] py-[3px] ml-1 border-[1px] border-text-warning rounded-[5px] text-text-warning system-2xs-medium-uppercase '>
- {t('workflow.nodes.iteration.parallelModeUpper')}
- </div>
- </Tooltip>
- )
- }
- </div>
- {
- data._iterationLength && data._iterationIndex && data._runningStatus === NodeRunningStatus.Running && (
- <div className='mr-1.5 text-xs font-medium text-primary-600'>
- {data._iterationIndex > data._iterationLength ? data._iterationLength : data._iterationIndex}/{data._iterationLength}
- </div>
- )
- }
- {
- (data._runningStatus === NodeRunningStatus.Running || data._singleRunningStatus === NodeRunningStatus.Running) && (
- <RiLoader2Line className='w-3.5 h-3.5 text-primary-600 animate-spin' />
- )
- }
- {
- data._runningStatus === NodeRunningStatus.Succeeded && (
- <RiCheckboxCircleLine className='w-3.5 h-3.5 text-[#12B76A]' />
- )
- }
- {
- data._runningStatus === NodeRunningStatus.Failed && (
- <RiErrorWarningLine className='w-3.5 h-3.5 text-[#F04438]' />
- )
- }
- </div>
- {
- data.type !== BlockEnum.Iteration && (
- cloneElement(children, { id, data })
- )
- }
- {
- data.type === BlockEnum.Iteration && (
- <div className='grow pl-1 pr-1 pb-1'>
- {cloneElement(children, { id, data })}
- </div>
- )
- }
- {
- data.desc && data.type !== BlockEnum.Iteration && (
- <div className='px-3 pt-1 pb-2 system-xs-regular text-text-tertiary whitespace-pre-line break-words'>
- {data.desc}
- </div>
- )
- }
- </div>
- </div>
- )
- }
- export default memo(BaseNode)
|