custom-edge.tsx 2.7 KB

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