use-edges-interactions.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import { useCallback } from 'react'
  2. import produce from 'immer'
  3. import type {
  4. EdgeMouseHandler,
  5. OnEdgesChange,
  6. } from 'reactflow'
  7. import {
  8. getConnectedEdges,
  9. useStoreApi,
  10. } from 'reactflow'
  11. import type {
  12. Edge,
  13. Node,
  14. } from '../types'
  15. import { BlockEnum } from '../types'
  16. import { getNodesConnectedSourceOrTargetHandleIdsMap } from '../utils'
  17. import { useNodesSyncDraft } from './use-nodes-sync-draft'
  18. import { useNodesReadOnly } from './use-workflow'
  19. export const useEdgesInteractions = () => {
  20. const store = useStoreApi()
  21. const { handleSyncWorkflowDraft } = useNodesSyncDraft()
  22. const { getNodesReadOnly } = useNodesReadOnly()
  23. const handleEdgeEnter = useCallback<EdgeMouseHandler>((_, edge) => {
  24. if (getNodesReadOnly())
  25. return
  26. const {
  27. edges,
  28. setEdges,
  29. } = store.getState()
  30. const newEdges = produce(edges, (draft) => {
  31. const currentEdge = draft.find(e => e.id === edge.id)!
  32. currentEdge.data._hovering = true
  33. })
  34. setEdges(newEdges)
  35. }, [store, getNodesReadOnly])
  36. const handleEdgeLeave = useCallback<EdgeMouseHandler>((_, edge) => {
  37. if (getNodesReadOnly())
  38. return
  39. const {
  40. edges,
  41. setEdges,
  42. } = store.getState()
  43. const newEdges = produce(edges, (draft) => {
  44. const currentEdge = draft.find(e => e.id === edge.id)!
  45. currentEdge.data._hovering = false
  46. })
  47. setEdges(newEdges)
  48. }, [store, getNodesReadOnly])
  49. const handleEdgeDeleteByDeleteBranch = useCallback((nodeId: string, branchId: string) => {
  50. if (getNodesReadOnly())
  51. return
  52. const {
  53. getNodes,
  54. setNodes,
  55. edges,
  56. setEdges,
  57. } = store.getState()
  58. const currentEdgeIndex = edges.findIndex(edge => edge.source === nodeId && edge.sourceHandle === branchId)
  59. if (currentEdgeIndex < 0)
  60. return
  61. const currentEdge = edges[currentEdgeIndex]
  62. const newNodes = produce(getNodes(), (draft: Node[]) => {
  63. const sourceNode = draft.find(node => node.id === currentEdge.source)
  64. const targetNode = draft.find(node => node.id === currentEdge.target)
  65. if (sourceNode)
  66. sourceNode.data._connectedSourceHandleIds = sourceNode.data._connectedSourceHandleIds?.filter(handleId => handleId !== currentEdge.sourceHandle)
  67. if (targetNode)
  68. targetNode.data._connectedTargetHandleIds = targetNode.data._connectedTargetHandleIds?.filter(handleId => handleId !== currentEdge.targetHandle)
  69. })
  70. setNodes(newNodes)
  71. const newEdges = produce(edges, (draft) => {
  72. draft.splice(currentEdgeIndex, 1)
  73. })
  74. setEdges(newEdges)
  75. handleSyncWorkflowDraft()
  76. }, [store, handleSyncWorkflowDraft, getNodesReadOnly])
  77. const handleEdgeDelete = useCallback(() => {
  78. if (getNodesReadOnly())
  79. return
  80. const {
  81. getNodes,
  82. setNodes,
  83. edges,
  84. setEdges,
  85. } = store.getState()
  86. const currentEdgeIndex = edges.findIndex(edge => edge.selected)
  87. if (currentEdgeIndex < 0)
  88. return
  89. const currentEdge = edges[currentEdgeIndex]
  90. const nodes = getNodes()
  91. const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap(
  92. [
  93. { type: 'remove', edge: currentEdge },
  94. ],
  95. nodes,
  96. )
  97. const newNodes = produce(nodes, (draft: Node[]) => {
  98. draft.forEach((node) => {
  99. if (nodesConnectedSourceOrTargetHandleIdsMap[node.id]) {
  100. node.data = {
  101. ...node.data,
  102. ...nodesConnectedSourceOrTargetHandleIdsMap[node.id],
  103. }
  104. }
  105. })
  106. })
  107. setNodes(newNodes)
  108. const newEdges = produce(edges, (draft) => {
  109. draft.splice(currentEdgeIndex, 1)
  110. })
  111. setEdges(newEdges)
  112. handleSyncWorkflowDraft()
  113. }, [store, getNodesReadOnly, handleSyncWorkflowDraft])
  114. const handleEdgesChange = useCallback<OnEdgesChange>((changes) => {
  115. if (getNodesReadOnly())
  116. return
  117. const {
  118. edges,
  119. setEdges,
  120. } = store.getState()
  121. const newEdges = produce(edges, (draft) => {
  122. changes.forEach((change) => {
  123. if (change.type === 'select')
  124. draft.find(edge => edge.id === change.id)!.selected = change.selected
  125. })
  126. })
  127. setEdges(newEdges)
  128. }, [store, getNodesReadOnly])
  129. const handleVariableAssignerEdgesChange = useCallback((nodeId: string, variables: any) => {
  130. const {
  131. getNodes,
  132. setNodes,
  133. edges,
  134. setEdges,
  135. } = store.getState()
  136. const nodes = getNodes()
  137. const newEdgesTargetHandleIds = variables.map((item: any) => item[0])
  138. const connectedEdges = getConnectedEdges([{ id: nodeId } as Node], edges).filter(edge => edge.target === nodeId)
  139. const needDeleteEdges = connectedEdges.filter(edge => !newEdgesTargetHandleIds.includes(edge.targetHandle))
  140. const needAddEdgesTargetHandleIds = newEdgesTargetHandleIds.filter((targetHandle: string) => !connectedEdges.some(edge => edge.targetHandle === targetHandle))
  141. const needAddEdges = needAddEdgesTargetHandleIds.map((targetHandle: string) => {
  142. return {
  143. id: `${targetHandle}-${nodeId}`,
  144. type: 'custom',
  145. source: targetHandle,
  146. sourceHandle: 'source',
  147. target: nodeId,
  148. targetHandle,
  149. data: {
  150. sourceType: nodes.find(node => node.id === targetHandle)?.data.type,
  151. targetType: BlockEnum.VariableAssigner,
  152. },
  153. }
  154. })
  155. const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap(
  156. [
  157. ...needDeleteEdges.map(edge => ({ type: 'remove', edge })),
  158. ...needAddEdges.map((edge: Edge) => ({ type: 'add', edge })),
  159. ],
  160. nodes,
  161. )
  162. const newNodes = produce(nodes, (draft) => {
  163. draft.forEach((node) => {
  164. if (nodesConnectedSourceOrTargetHandleIdsMap[node.id]) {
  165. node.data = {
  166. ...node.data,
  167. ...nodesConnectedSourceOrTargetHandleIdsMap[node.id],
  168. }
  169. }
  170. })
  171. })
  172. setNodes(newNodes)
  173. const newEdges = produce(edges, (draft) => {
  174. const filtered = draft.filter(edge => !needDeleteEdges.map(needDeleteEdge => needDeleteEdge.id).includes(edge.id))
  175. filtered.push(...needAddEdges)
  176. return filtered
  177. })
  178. setEdges(newEdges)
  179. }, [store])
  180. return {
  181. handleEdgeEnter,
  182. handleEdgeLeave,
  183. handleEdgeDeleteByDeleteBranch,
  184. handleEdgeDelete,
  185. handleEdgesChange,
  186. handleVariableAssignerEdgesChange,
  187. }
  188. }