candidate-node.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import {
  2. memo,
  3. } from 'react'
  4. import produce from 'immer'
  5. import {
  6. useReactFlow,
  7. useStoreApi,
  8. useViewport,
  9. } from 'reactflow'
  10. import { useEventListener } from 'ahooks'
  11. import {
  12. useStore,
  13. useWorkflowStore,
  14. } from './store'
  15. import { WorkflowHistoryEvent, useNodesInteractions, useWorkflowHistory } from './hooks'
  16. import { CUSTOM_NODE } from './constants'
  17. import { getIterationStartNode, getLoopStartNode } from './utils'
  18. import CustomNode from './nodes'
  19. import CustomNoteNode from './note-node'
  20. import { CUSTOM_NOTE_NODE } from './note-node/constants'
  21. import { BlockEnum } from './types'
  22. const CandidateNode = () => {
  23. const store = useStoreApi()
  24. const reactflow = useReactFlow()
  25. const workflowStore = useWorkflowStore()
  26. const candidateNode = useStore(s => s.candidateNode)
  27. const mousePosition = useStore(s => s.mousePosition)
  28. const { zoom } = useViewport()
  29. const { handleNodeSelect } = useNodesInteractions()
  30. const { saveStateToHistory } = useWorkflowHistory()
  31. useEventListener('click', (e) => {
  32. const { candidateNode, mousePosition } = workflowStore.getState()
  33. if (candidateNode) {
  34. e.preventDefault()
  35. const {
  36. getNodes,
  37. setNodes,
  38. } = store.getState()
  39. const { screenToFlowPosition } = reactflow
  40. const nodes = getNodes()
  41. const { x, y } = screenToFlowPosition({ x: mousePosition.pageX, y: mousePosition.pageY })
  42. const newNodes = produce(nodes, (draft) => {
  43. draft.push({
  44. ...candidateNode,
  45. data: {
  46. ...candidateNode.data,
  47. _isCandidate: false,
  48. },
  49. position: {
  50. x,
  51. y,
  52. },
  53. })
  54. if (candidateNode.data.type === BlockEnum.Iteration)
  55. draft.push(getIterationStartNode(candidateNode.id))
  56. if (candidateNode.data.type === BlockEnum.Loop)
  57. draft.push(getLoopStartNode(candidateNode.id))
  58. })
  59. setNodes(newNodes)
  60. if (candidateNode.type === CUSTOM_NOTE_NODE)
  61. saveStateToHistory(WorkflowHistoryEvent.NoteAdd)
  62. else
  63. saveStateToHistory(WorkflowHistoryEvent.NodeAdd)
  64. workflowStore.setState({ candidateNode: undefined })
  65. if (candidateNode.type === CUSTOM_NOTE_NODE)
  66. handleNodeSelect(candidateNode.id)
  67. }
  68. })
  69. useEventListener('contextmenu', (e) => {
  70. const { candidateNode } = workflowStore.getState()
  71. if (candidateNode) {
  72. e.preventDefault()
  73. workflowStore.setState({ candidateNode: undefined })
  74. }
  75. })
  76. if (!candidateNode)
  77. return null
  78. return (
  79. <div
  80. className='absolute z-10'
  81. style={{
  82. left: mousePosition.elementX,
  83. top: mousePosition.elementY,
  84. transform: `scale(${zoom})`,
  85. transformOrigin: '0 0',
  86. }}
  87. >
  88. {
  89. candidateNode.type === CUSTOM_NODE && (
  90. <CustomNode {...candidateNode as any} />
  91. )
  92. }
  93. {
  94. candidateNode.type === CUSTOM_NOTE_NODE && (
  95. <CustomNoteNode {...candidateNode as any} />
  96. )
  97. }
  98. </div>
  99. )
  100. }
  101. export default memo(CandidateNode)