custom-edge.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import {
  2. memo,
  3. useCallback,
  4. useState,
  5. } from 'react'
  6. import { intersection } from 'lodash-es'
  7. import type { EdgeProps } from 'reactflow'
  8. import {
  9. BaseEdge,
  10. EdgeLabelRenderer,
  11. Position,
  12. getBezierPath,
  13. } from 'reactflow'
  14. import {
  15. useAvailableBlocks,
  16. useNodesInteractions,
  17. } from './hooks'
  18. import BlockSelector from './block-selector'
  19. import type {
  20. Edge,
  21. OnSelectBlock,
  22. } from './types'
  23. import { ITERATION_CHILDREN_Z_INDEX } from './constants'
  24. import cn from '@/utils/classnames'
  25. const CustomEdge = ({
  26. id,
  27. data,
  28. source,
  29. sourceHandleId,
  30. target,
  31. targetHandleId,
  32. sourceX,
  33. sourceY,
  34. targetX,
  35. targetY,
  36. selected,
  37. }: EdgeProps) => {
  38. const [
  39. edgePath,
  40. labelX,
  41. labelY,
  42. ] = getBezierPath({
  43. sourceX: sourceX - 8,
  44. sourceY,
  45. sourcePosition: Position.Right,
  46. targetX: targetX + 8,
  47. targetY,
  48. targetPosition: Position.Left,
  49. curvature: 0.16,
  50. })
  51. const [open, setOpen] = useState(false)
  52. const { handleNodeAdd } = useNodesInteractions()
  53. const { availablePrevBlocks } = useAvailableBlocks((data as Edge['data'])!.targetType, (data as Edge['data'])?.isInIteration)
  54. const { availableNextBlocks } = useAvailableBlocks((data as Edge['data'])!.sourceType, (data as Edge['data'])?.isInIteration)
  55. const handleOpenChange = useCallback((v: boolean) => {
  56. setOpen(v)
  57. }, [])
  58. const handleInsert = useCallback<OnSelectBlock>((nodeType, toolDefaultValue) => {
  59. handleNodeAdd(
  60. {
  61. nodeType,
  62. toolDefaultValue,
  63. },
  64. {
  65. prevNodeId: source,
  66. prevNodeSourceHandle: sourceHandleId || 'source',
  67. nextNodeId: target,
  68. nextNodeTargetHandle: targetHandleId || 'target',
  69. },
  70. )
  71. }, [handleNodeAdd, source, sourceHandleId, target, targetHandleId])
  72. return (
  73. <>
  74. <BaseEdge
  75. id={id}
  76. path={edgePath}
  77. style={{
  78. stroke: (selected || data?._connectedNodeIsHovering || data?._run) ? '#2970FF' : '#D0D5DD',
  79. strokeWidth: 2,
  80. }}
  81. />
  82. <EdgeLabelRenderer>
  83. <div
  84. className={cn(
  85. 'nopan nodrag hover:scale-125',
  86. data?._hovering ? 'block' : 'hidden',
  87. open && '!block',
  88. data.isInIteration && `z-[${ITERATION_CHILDREN_Z_INDEX}]`,
  89. )}
  90. style={{
  91. position: 'absolute',
  92. transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
  93. pointerEvents: 'all',
  94. }}
  95. >
  96. <BlockSelector
  97. open={open}
  98. onOpenChange={handleOpenChange}
  99. asChild
  100. onSelect={handleInsert}
  101. availableBlocksTypes={intersection(availablePrevBlocks, availableNextBlocks)}
  102. triggerClassName={() => 'hover:scale-150 transition-all'}
  103. />
  104. </div>
  105. </EdgeLabelRenderer>
  106. </>
  107. )
  108. }
  109. export default memo(CustomEdge)