node.tsx 4.0 KB

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