node.tsx 4.3 KB

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