control.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import type { MouseEvent } from 'react'
  2. import {
  3. memo,
  4. useCallback,
  5. } from 'react'
  6. import { useTranslation } from 'react-i18next'
  7. import cn from 'classnames'
  8. import { useKeyPress } from 'ahooks'
  9. import {
  10. useNodesReadOnly,
  11. useSelectionInteractions,
  12. useWorkflow,
  13. } from '../hooks'
  14. import { isEventTargetInputArea } from '../utils'
  15. import { useStore } from '../store'
  16. import AddBlock from './add-block'
  17. import TipPopup from './tip-popup'
  18. import { useOperator } from './hooks'
  19. import {
  20. Cursor02C,
  21. Hand02,
  22. } from '@/app/components/base/icons/src/vender/line/editor'
  23. import {
  24. Cursor02C as Cursor02CSolid,
  25. Hand02 as Hand02Solid,
  26. } from '@/app/components/base/icons/src/vender/solid/editor'
  27. import { OrganizeGrid } from '@/app/components/base/icons/src/vender/line/layout'
  28. import { StickerSquare } from '@/app/components/base/icons/src/vender/line/files'
  29. const Control = () => {
  30. const { t } = useTranslation()
  31. const controlMode = useStore(s => s.controlMode)
  32. const setControlMode = useStore(s => s.setControlMode)
  33. const { handleLayout } = useWorkflow()
  34. const { handleAddNote } = useOperator()
  35. const {
  36. nodesReadOnly,
  37. getNodesReadOnly,
  38. } = useNodesReadOnly()
  39. const { handleSelectionCancel } = useSelectionInteractions()
  40. const handleModePointer = useCallback(() => {
  41. if (getNodesReadOnly())
  42. return
  43. setControlMode('pointer')
  44. }, [getNodesReadOnly, setControlMode])
  45. const handleModeHand = useCallback(() => {
  46. if (getNodesReadOnly())
  47. return
  48. setControlMode('hand')
  49. handleSelectionCancel()
  50. }, [getNodesReadOnly, setControlMode, handleSelectionCancel])
  51. useKeyPress('h', (e) => {
  52. if (getNodesReadOnly())
  53. return
  54. if (isEventTargetInputArea(e.target as HTMLElement))
  55. return
  56. e.preventDefault()
  57. handleModeHand()
  58. }, {
  59. exactMatch: true,
  60. useCapture: true,
  61. })
  62. useKeyPress('v', (e) => {
  63. if (isEventTargetInputArea(e.target as HTMLElement))
  64. return
  65. e.preventDefault()
  66. handleModePointer()
  67. }, {
  68. exactMatch: true,
  69. useCapture: true,
  70. })
  71. const goLayout = () => {
  72. if (getNodesReadOnly())
  73. return
  74. handleLayout()
  75. }
  76. const addNote = (e: MouseEvent<HTMLDivElement>) => {
  77. if (getNodesReadOnly())
  78. return
  79. e.stopPropagation()
  80. handleAddNote()
  81. }
  82. return (
  83. <div className='flex items-center p-0.5 rounded-lg border-[0.5px] border-gray-100 bg-white shadow-lg text-gray-500'>
  84. <AddBlock />
  85. <TipPopup title={t('workflow.nodes.note.addNote')}>
  86. <div
  87. className={cn(
  88. 'flex items-center justify-center ml-[1px] w-8 h-8 rounded-lg hover:bg-black/5 hover:text-gray-700 cursor-pointer',
  89. `${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
  90. )}
  91. onClick={addNote}
  92. >
  93. <StickerSquare />
  94. </div>
  95. </TipPopup>
  96. <div className='mx-[3px] w-[1px] h-3.5 bg-gray-200'></div>
  97. <TipPopup title={t('workflow.common.pointerMode')}>
  98. <div
  99. className={cn(
  100. 'flex items-center justify-center mr-[1px] w-8 h-8 rounded-lg cursor-pointer',
  101. controlMode === 'pointer' ? 'bg-primary-50 text-primary-600' : 'hover:bg-black/5 hover:text-gray-700',
  102. `${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
  103. )}
  104. onClick={handleModePointer}
  105. >
  106. {
  107. controlMode === 'pointer' ? <Cursor02CSolid className='w-4 h-4' /> : <Cursor02C className='w-4 h-4' />
  108. }
  109. </div>
  110. </TipPopup>
  111. <TipPopup title={t('workflow.common.handMode')}>
  112. <div
  113. className={cn(
  114. 'flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer',
  115. controlMode === 'hand' ? 'bg-primary-50 text-primary-600' : 'hover:bg-black/5 hover:text-gray-700',
  116. `${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
  117. )}
  118. onClick={handleModeHand}
  119. >
  120. {
  121. controlMode === 'hand' ? <Hand02Solid className='w-4 h-4' /> : <Hand02 className='w-4 h-4' />
  122. }
  123. </div>
  124. </TipPopup>
  125. <div className='mx-[3px] w-[1px] h-3.5 bg-gray-200'></div>
  126. <TipPopup title={t('workflow.panel.organizeBlocks')}>
  127. <div
  128. className={cn(
  129. 'flex items-center justify-center w-8 h-8 rounded-lg hover:bg-black/5 hover:text-gray-700 cursor-pointer',
  130. `${nodesReadOnly && '!cursor-not-allowed opacity-50'}`,
  131. )}
  132. onClick={goLayout}
  133. >
  134. <OrganizeGrid className='w-4 h-4' />
  135. </div>
  136. </TipPopup>
  137. </div>
  138. )
  139. }
  140. export default memo(Control)