candidate-node.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  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 } 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. })
  57. setNodes(newNodes)
  58. if (candidateNode.type === CUSTOM_NOTE_NODE)
  59. saveStateToHistory(WorkflowHistoryEvent.NoteAdd)
  60. else
  61. saveStateToHistory(WorkflowHistoryEvent.NodeAdd)
  62. workflowStore.setState({ candidateNode: undefined })
  63. if (candidateNode.type === CUSTOM_NOTE_NODE)
  64. handleNodeSelect(candidateNode.id)
  65. }
  66. })
  67. useEventListener('contextmenu', (e) => {
  68. const { candidateNode } = workflowStore.getState()
  69. if (candidateNode) {
  70. e.preventDefault()
  71. workflowStore.setState({ candidateNode: undefined })
  72. }
  73. })
  74. if (!candidateNode)
  75. return null
  76. return (
  77. <div
  78. className='absolute z-10'
  79. style={{
  80. left: mousePosition.elementX,
  81. top: mousePosition.elementY,
  82. transform: `scale(${zoom})`,
  83. transformOrigin: '0 0',
  84. }}
  85. >
  86. {
  87. candidateNode.type === CUSTOM_NODE && (
  88. <CustomNode {...candidateNode as any} />
  89. )
  90. }
  91. {
  92. candidateNode.type === CUSTOM_NOTE_NODE && (
  93. <CustomNoteNode {...candidateNode as any} />
  94. )
  95. }
  96. </div>
  97. )
  98. }
  99. export default memo(CandidateNode)