node.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import type {
  2. FC,
  3. ReactElement,
  4. } from 'react'
  5. import {
  6. cloneElement,
  7. memo,
  8. } from 'react'
  9. import type { NodeProps } from '../../types'
  10. import {
  11. BlockEnum,
  12. NodeRunningStatus,
  13. } from '../../types'
  14. import {
  15. useNodesReadOnly,
  16. useToolIcon,
  17. } from '../../hooks'
  18. import {
  19. NodeSourceHandle,
  20. NodeTargetHandle,
  21. } from './components/node-handle'
  22. import NodeControl from './components/node-control'
  23. import BlockIcon from '@/app/components/workflow/block-icon'
  24. import {
  25. CheckCircle,
  26. Loading02,
  27. } from '@/app/components/base/icons/src/vender/line/general'
  28. import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
  29. type BaseNodeProps = {
  30. children: ReactElement
  31. } & NodeProps
  32. const BaseNode: FC<BaseNodeProps> = ({
  33. id,
  34. data,
  35. children,
  36. }) => {
  37. const { nodesReadOnly } = useNodesReadOnly()
  38. const toolIcon = useToolIcon(data)
  39. return (
  40. <div
  41. className={`
  42. flex border-[2px] rounded-2xl
  43. ${(data.selected && !data._runningStatus && !data._isInvalidConnection) ? 'border-primary-600' : 'border-transparent'}
  44. `}
  45. >
  46. <div
  47. className={`
  48. group relative pb-1 w-[240px] bg-[#fcfdff] shadow-xs
  49. border border-transparent rounded-[15px]
  50. ${!data._runningStatus && 'hover:shadow-lg'}
  51. ${data._runningStatus === NodeRunningStatus.Running && '!border-primary-500'}
  52. ${data._runningStatus === NodeRunningStatus.Succeeded && '!border-[#12B76A]'}
  53. ${data._runningStatus === NodeRunningStatus.Failed && '!border-[#F04438]'}
  54. ${data._runningStatus === NodeRunningStatus.Waiting && 'opacity-70'}
  55. ${data._isInvalidConnection && '!border-[#F04438]'}
  56. `}
  57. >
  58. {
  59. data.type !== BlockEnum.VariableAssigner && !data._runningStatus && (
  60. <NodeTargetHandle
  61. id={id}
  62. data={data}
  63. handleClassName='!top-4 !-left-[9px] !translate-y-0'
  64. handleId='target'
  65. />
  66. )
  67. }
  68. {
  69. data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && !data._runningStatus && (
  70. <NodeSourceHandle
  71. id={id}
  72. data={data}
  73. handleClassName='!top-4 !-right-[9px] !translate-y-0'
  74. handleId='source'
  75. />
  76. )
  77. }
  78. {
  79. !data._runningStatus && !nodesReadOnly && (
  80. <NodeControl
  81. id={id}
  82. data={data}
  83. />
  84. )
  85. }
  86. <div className='flex items-center px-3 pt-3 pb-2'>
  87. <BlockIcon
  88. className='shrink-0 mr-2'
  89. type={data.type}
  90. size='md'
  91. toolIcon={toolIcon}
  92. />
  93. <div
  94. title={data.title}
  95. className='grow mr-1 text-[13px] font-semibold text-gray-700 truncate'
  96. >
  97. {data.title}
  98. </div>
  99. {
  100. (data._runningStatus === NodeRunningStatus.Running || data._singleRunningStatus === NodeRunningStatus.Running) && (
  101. <Loading02 className='w-3.5 h-3.5 text-primary-600 animate-spin' />
  102. )
  103. }
  104. {
  105. data._runningStatus === NodeRunningStatus.Succeeded && (
  106. <CheckCircle className='w-3.5 h-3.5 text-[#12B76A]' />
  107. )
  108. }
  109. {
  110. data._runningStatus === NodeRunningStatus.Failed && (
  111. <AlertCircle className='w-3.5 h-3.5 text-[#F04438]' />
  112. )
  113. }
  114. </div>
  115. {cloneElement(children, { id, data })}
  116. {
  117. data.desc && (
  118. <div className='px-3 pt-1 pb-2 text-xs leading-[18px] text-gray-500 whitespace-pre-line break-words'>
  119. {data.desc}
  120. </div>
  121. )
  122. }
  123. </div>
  124. </div>
  125. )
  126. }
  127. export default memo(BaseNode)