Block.jsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. import React from 'react'
  2. import { shallowEqualImmutable, getY, isMac } from 'utils/utils'
  3. import uiActions from 'actions/ui'
  4. import newEventActions from 'actions/newEvent'
  5. import newEventStores from 'stores/newEvent'
  6. import {
  7. getEventBlockByBid,
  8. filterBlocks,
  9. getBidList
  10. } from 'stores/funcs/newEvent'
  11. import { dragPosition, dragUtils } from 'stores/funcs/event/dragUtils'
  12. import { CONTEXT_MENU_TYPE, EVENT_BLOCK_TYPE } from 'const/const'
  13. export default class Block extends React.Component {
  14. constructor(props) {
  15. super(props)
  16. this.curBlockId = newEventStores.curBlockId
  17. this.multiBlockIds = newEventStores.multiBlockIds
  18. this.state = {
  19. isActive: this.isActive(props.bid),
  20. isEnable: this.isEnable(props.bid),
  21. isExpand: this.isExpand(props.bid),
  22. hasChildren: this.hasChildren(props.bid)
  23. }
  24. this.onEventChange = this.onEventChange.bind(this)
  25. this.isActive = this.isActive.bind(this)
  26. this.isEnable = this.isEnable.bind(this)
  27. this.isExpand = this.isExpand.bind(this)
  28. this.onSelectBlock = this.onSelectBlock.bind(this)
  29. this.onToggleBlockExpand = this.onToggleBlockExpand.bind(this)
  30. this.getLineColor = this.getLineColor.bind(this)
  31. this.getGapLeft = this.getGapLeft.bind(this)
  32. // 拖拽
  33. this.allowDrag = this.allowDrag.bind(this)
  34. this.allowDrop = this.allowDrop.bind(this)
  35. this.getPosition = this.getPosition.bind(this)
  36. this.onDragStart = this.onDragStart.bind(this)
  37. this.onDragEnd = this.onDragEnd.bind(this)
  38. this.onDragOver = this.onDragOver.bind(this)
  39. this.onDrop = this.onDrop.bind(this)
  40. // 右键
  41. this.onContextMenu = this.onContextMenu.bind(this)
  42. }
  43. shouldComponentUpdate(nextProps, nextState) {
  44. return (
  45. !shallowEqualImmutable(this.props, nextProps) ||
  46. !shallowEqualImmutable(this.state, nextState)
  47. )
  48. }
  49. componentDidUpdate(preProps) {
  50. let obj = {}
  51. // bid改变时需要更新
  52. if (preProps.bid !== this.props.bid) {
  53. obj.isActive = this.isActive()
  54. obj.isEnable = this.isEnable()
  55. obj.isExpand = this.isExpand()
  56. obj.hasChildren = this.hasChildren()
  57. }
  58. if (Object.keys(obj).length > 0) {
  59. this.setState(obj)
  60. }
  61. }
  62. componentDidMount() {
  63. this.unsubscribe = newEventStores.listen(this.onEventChange)
  64. }
  65. componentWillUnmount() {
  66. this.unsubscribe()
  67. }
  68. onEventChange(status) {
  69. let obj = {}
  70. if (status.types.includes('curBlockId')) {
  71. if (this.curBlockId !== status.data.curBlockId) {
  72. this.curBlockId = status.data.curBlockId
  73. let isActive = this.isActive()
  74. if (this.state.isActive !== isActive) {
  75. obj.isActive = isActive
  76. }
  77. }
  78. }
  79. if (status.types.includes('multiBlockIds')) {
  80. this.multiBlockIds = status.data.multiBlockIds
  81. let isActive = this.isActive()
  82. if (this.state.isActive !== isActive) {
  83. obj.isActive = isActive
  84. }
  85. }
  86. if (status.types.includes('toggleBlockProp')) {
  87. if (this.props.bid === status.data.affectBlockId) {
  88. if (status.data.prop === 'enable') {
  89. let isEnable = this.isEnable()
  90. if (this.state.isEnable !== isEnable) {
  91. obj.isEnable = isEnable
  92. }
  93. } else if (status.data.prop === 'expand') {
  94. let isExpand = this.isExpand()
  95. if (this.state.isExpand !== isExpand) {
  96. obj.isExpand = isExpand
  97. }
  98. }
  99. }
  100. }
  101. if (
  102. (status.types.includes('changeBlockChildren') &&
  103. status.data &&
  104. status.data.parentBid === this.props.bid) ||
  105. (status.types.includes('reloadBlock') &&
  106. this.props.bid === status.data.affectBlockId)
  107. ) {
  108. let hasChildren = this.hasChildren()
  109. if (hasChildren !== this.state.hasChildren) {
  110. obj.hasChildren = hasChildren
  111. }
  112. }
  113. if (Object.keys(obj).length > 0) {
  114. this.setState(obj)
  115. }
  116. }
  117. isActive(bid) {
  118. let _bid = bid || this.props.bid
  119. return this.curBlockId === _bid || this.multiBlockIds.indexOf(_bid) >= 0
  120. }
  121. isEnable(bid) {
  122. let _bid = bid || this.props.bid
  123. let result = true
  124. let block = getEventBlockByBid(_bid)
  125. if (block) {
  126. result = block.enable !== false
  127. }
  128. return result
  129. }
  130. isExpand(bid) {
  131. let _bid = bid || this.props.bid
  132. let result = true
  133. let block = getEventBlockByBid(_bid)
  134. if (block) {
  135. result = block.expand !== false
  136. }
  137. return result
  138. }
  139. hasChildren(bid) {
  140. let _bid = bid || this.props.bid
  141. let result = false
  142. let block = getEventBlockByBid(_bid)
  143. if (block) {
  144. result = block.children && block.children.length > 0
  145. }
  146. return result
  147. }
  148. onSelectBlock(e, allowMulti = false) {
  149. if (
  150. this.props.eventNodeId &&
  151. this.props.eventNodeId !== newEventStores.curEventNodeId
  152. ) {
  153. newEventActions.selectEvent({ eventNodeId: this.props.eventNodeId })
  154. } else {
  155. // 多选
  156. let isCtrl = e && (isMac ? e.metaKey : e.ctrlKey)
  157. if (isCtrl) {
  158. newEventActions.selectMultiBlocks({ blockId: this.props.bid })
  159. return
  160. }
  161. let isShift = e && e.shiftKey
  162. if (isShift) {
  163. newEventActions.shiftSelectBlocks({ blockId: this.props.bid })
  164. return
  165. }
  166. }
  167. if (
  168. !this.state.isActive ||
  169. (
  170. this.multiBlockIds.length > 0 &&
  171. (
  172. !allowMulti ||
  173. (
  174. allowMulti &&
  175. !this.multiBlockIds?.includes(this.props.bid)
  176. )
  177. )
  178. )
  179. ) {
  180. newEventActions.selectBlock({ blockId: this.props.bid })
  181. }
  182. }
  183. onToggleBlockExpand() {
  184. newEventActions.toggleBlockProp({ blockId: this.props.bid, prop: 'expand' })
  185. }
  186. getLineColor() {
  187. let group = ['red', 'green', 'purple', 'ching', 'orange', 'blue']
  188. return group[this.props.layer % 6]
  189. }
  190. getNextLineColor() {
  191. let group = ['red', 'green', 'purple', 'ching', 'orange', 'blue']
  192. return group[(this.props.layer + 1) % 6]
  193. }
  194. getGapLeft() {
  195. return 8 + this.props.layer * 20 + (this.props.layer + 1)
  196. }
  197. getGroupColor() {
  198. const group = ['color1', 'color2', 'color3', 'color4', 'color5']
  199. // const group = ['#7F438F', '#E5C88E', '#EEB2B2', '#B5D8F1', '#CBFF97']
  200. return group[(this.props.groupLayer) % 5]
  201. }
  202. blockMainStyle() {
  203. if (this.props.groupLayer >= 1) {
  204. return {
  205. paddingLeft: `${(this.props.nestGroupLayer - 1) * 2}px`,
  206. marginRight: `${(this.props.groupLayer) * 2}px`
  207. }
  208. }
  209. return null
  210. }
  211. getLoops(bid) {
  212. let _bid = bid || this.props.bid
  213. let block = getEventBlockByBid(_bid)
  214. if (block && block.type === EVENT_BLOCK_TYPE.LOOP) {
  215. if (this.props.loops) {
  216. return JSON.stringify(
  217. [].concat(JSON.parse(this.props.loops), [block.bid])
  218. )
  219. } else {
  220. return JSON.stringify([block.bid])
  221. }
  222. } else {
  223. return this.props.loops
  224. }
  225. }
  226. indexLeftStyle() {
  227. // let _bid = this.props.bid
  228. // let block = getEventBlockByBid(_bid)
  229. // console.log(block?.type, this.props.offspringInNestGroupLayer, this.props.nestGroupLayer)
  230. if (this.props.offspringInNestGroupLayer >= 2 && this.props.nestGroupLayer === 0) {
  231. return { marginLeft: 20 + (this.props.offspringInNestGroupLayer - 1) * 2 + 'px' }
  232. }
  233. return undefined
  234. }
  235. extraGapGroupLayer() {
  236. if (this.props.offspringInNestGroupLayer >= 2 && this.props.nestGroupLayer === 0) {
  237. return this.props.offspringInNestGroupLayer
  238. } else {
  239. return this.props.extraGapGroupLayer || 0
  240. }
  241. }
  242. //拖拽
  243. allowDrag() {
  244. let allow = true
  245. if (
  246. document.activeElement.tagName === 'INPUT' ||
  247. document.activeElement.tagName === 'TEXTAREA'
  248. ) {
  249. allow = false // block dragging
  250. }
  251. if (!this.isActive(this.props.bid)) {
  252. this.onSelectBlock()
  253. }
  254. return allow
  255. }
  256. allowDrop(bid, position, multiInfo) {
  257. let result = false
  258. let dragBlock = getEventBlockByBid(bid)
  259. let targetBlock = getEventBlockByBid(this.props.bid)
  260. let dropBlock = undefined
  261. switch (position) {
  262. case dragPosition.mid:
  263. // 加到targetBlock里面
  264. dropBlock = targetBlock
  265. break
  266. case dragPosition.bot:
  267. case dragPosition.top:
  268. // 实际是放在targetBlock的父级
  269. if (targetBlock.parentBid) {
  270. let targetParentBlock = getEventBlockByBid(targetBlock.parentBid)
  271. if (targetParentBlock) {
  272. dropBlock = targetParentBlock
  273. } else {
  274. dropBlock = undefined
  275. }
  276. }
  277. break
  278. }
  279. if (multiInfo && multiInfo.length > 0) {
  280. // 多个移动
  281. let blockTypes = []
  282. multiInfo = filterBlocks(multiInfo)
  283. multiInfo.forEach(bid => {
  284. let block = getEventBlockByBid(bid)
  285. if (block && block.type) {
  286. let type = block.type
  287. if (blockTypes.indexOf(type) === -1) {
  288. blockTypes.push(type)
  289. }
  290. }
  291. })
  292. if (
  293. targetBlock &&
  294. targetBlock.type === EVENT_BLOCK_TYPE.ROOT &&
  295. blockTypes.length === 1 &&
  296. blockTypes[0] === EVENT_BLOCK_TYPE.ROOT &&
  297. position !== dragPosition.mid
  298. ) {
  299. // 拖的和drop的都是Root只可放上和下
  300. result = true
  301. } else if (dropBlock) {
  302. // 提炼出可drop的情况
  303. if (dropBlock.type === EVENT_BLOCK_TYPE.ACTION) {
  304. if (
  305. blockTypes.length === 1 &&
  306. blockTypes[0] === EVENT_BLOCK_TYPE.STATUS &&
  307. dropBlock.action &&
  308. dropBlock.action.callback === true
  309. ) {
  310. result = true
  311. }
  312. } else if (dropBlock.type === EVENT_BLOCK_TYPE.COMMENT) {
  313. result = false
  314. } else {
  315. if (
  316. blockTypes.indexOf(EVENT_BLOCK_TYPE.ROOT) === -1 &&
  317. blockTypes.indexOf(EVENT_BLOCK_TYPE.STATUS) === -1
  318. ) {
  319. result = true
  320. }
  321. }
  322. }
  323. } else {
  324. // 单个移动
  325. if (
  326. dragBlock &&
  327. targetBlock &&
  328. dragBlock.type === EVENT_BLOCK_TYPE.ROOT &&
  329. targetBlock.type === EVENT_BLOCK_TYPE.ROOT &&
  330. position !== dragPosition.mid
  331. ) {
  332. // 拖的和drop的都是Root只可放上和下
  333. result = true
  334. } else if (dragBlock && dropBlock) {
  335. // 提炼出可drop的情况
  336. if (dropBlock.type === EVENT_BLOCK_TYPE.ACTION) {
  337. if (
  338. dragBlock.type === EVENT_BLOCK_TYPE.STATUS &&
  339. dropBlock.action &&
  340. dropBlock.action.callback === true
  341. ) {
  342. result = true
  343. }
  344. } else if (dropBlock.type === EVENT_BLOCK_TYPE.COMMENT) {
  345. result = false
  346. } else {
  347. if (
  348. dragBlock.type !== EVENT_BLOCK_TYPE.ROOT &&
  349. dragBlock.type !== EVENT_BLOCK_TYPE.STATUS
  350. ) {
  351. result = true
  352. }
  353. }
  354. }
  355. }
  356. return result
  357. }
  358. getPosition(dragBlockId, deltaTop, maxHeight, multiInfo) {
  359. let position = dragPosition.mid
  360. let mid1 = maxHeight / 3
  361. let mid2 = (maxHeight * 2) / 3
  362. if (deltaTop >= 0 && deltaTop <= mid1) {
  363. position = dragPosition.top
  364. } else if (deltaTop >= mid2 && deltaTop <= maxHeight) {
  365. position = dragPosition.bot
  366. }
  367. let dropBlock = getEventBlockByBid(this.props.bid)
  368. if (multiInfo && multiInfo.length > 0) {
  369. // 多个移动
  370. let blockTypes = []
  371. multiInfo = filterBlocks(multiInfo)
  372. multiInfo.forEach(bid => {
  373. let block = getEventBlockByBid(bid)
  374. if (block && block.type) {
  375. blockTypes.push(block.type)
  376. }
  377. })
  378. if (
  379. dropBlock &&
  380. dropBlock.type === EVENT_BLOCK_TYPE.ROOT &&
  381. blockTypes.indexOf(EVENT_BLOCK_TYPE.ROOT) === -1
  382. ) {
  383. // drop为root且drap均非root的情况
  384. position = dragPosition.mid
  385. }
  386. } else {
  387. // 单个移动
  388. let dragBlock = getEventBlockByBid(dragBlockId)
  389. if (
  390. dropBlock &&
  391. dropBlock.type === EVENT_BLOCK_TYPE.ROOT &&
  392. !(dragBlock && dragBlock.type === EVENT_BLOCK_TYPE.ROOT)
  393. ) {
  394. // 不同为root的情况且drop的root的情况
  395. position = dragPosition.mid
  396. }
  397. }
  398. return position
  399. }
  400. onDragStart(e) {
  401. dragUtils.isDragging = true
  402. e.stopPropagation()
  403. if (!this.allowDrag()) {
  404. e.preventDefault()
  405. return
  406. }
  407. e.dataTransfer.effectAllowed = 'move'
  408. dragUtils.setDraggingItemInfo(this.props.bid, this.state.isExpand)
  409. dragUtils.setDraggingMultiItemsInfo(this.multiBlockIds)
  410. // 对block的处理
  411. if (this.multiBlockIds.length === 0) {
  412. if (this.state.isExpand && this.state.hasChildren) {
  413. // 关闭起来
  414. newEventActions.toggleBlockProp({
  415. blockId: this.props.bid,
  416. prop: 'expand',
  417. value: false,
  418. addRecord: false
  419. })
  420. }
  421. }
  422. }
  423. onDragEnd(e) {
  424. // drop先执行
  425. e.stopPropagation()
  426. if (dragUtils.isDragging) {
  427. dragUtils.isDragging = false
  428. let dragInfo = dragUtils.getDraggingItemInfo()
  429. // 对block的处理
  430. if (dragInfo) {
  431. newEventActions.toggleBlockProp({
  432. blockId: dragInfo.bid,
  433. prop: 'expand',
  434. value: dragInfo.expand,
  435. addRecord: false
  436. })
  437. }
  438. dragUtils.resetDraggingItem()
  439. }
  440. }
  441. onDragOver(e) {
  442. e.stopPropagation()
  443. if (!dragUtils.isDragging) {
  444. return
  445. }
  446. e.preventDefault()
  447. let allowDrop = true
  448. let deltaTop =
  449. e.clientY -
  450. getY(e.currentTarget) +
  451. document.querySelector('.jenga-panel').scrollTop
  452. let maxHeight = e.currentTarget.clientHeight
  453. let position = dragPosition.mid
  454. let info = dragUtils.getDraggingItemInfo()
  455. let multiInfo = dragUtils.getDraggingMultiItemsInfo()
  456. if (multiInfo && multiInfo.length > 0) {
  457. // 多个移动
  458. // console.log('多个移动。。。')
  459. multiInfo = filterBlocks(multiInfo)
  460. let bidList = []
  461. multiInfo.forEach(bid => {
  462. getBidList({ blockId: bid, list: bidList })
  463. })
  464. if (bidList.indexOf(this.props.bid) > -1) {
  465. // dragOver为自己或childern时不允许
  466. allowDrop = false
  467. // console.log('包含自己。。。')
  468. } else {
  469. let blockTypes = []
  470. multiInfo.forEach(bid => {
  471. let block = getEventBlockByBid(bid)
  472. if (block && block.type) {
  473. let type = block.type
  474. if (blockTypes.indexOf(type) === -1) {
  475. blockTypes.push(type)
  476. }
  477. }
  478. })
  479. if (
  480. multiInfo.length > 1 &&
  481. blockTypes.indexOf(EVENT_BLOCK_TYPE.ROOT) > -1 &&
  482. blockTypes.length > 1
  483. ) {
  484. // 多个待移动block中同时有root和非root时不允许
  485. allowDrop = false
  486. // console.log('同时有root和非root。。。')
  487. } else {
  488. // 对drag的处理
  489. // console.log('ok。。。')
  490. position = this.getPosition(info.bid, deltaTop, maxHeight, multiInfo)
  491. allowDrop = this.allowDrop(info.bid, position, multiInfo)
  492. }
  493. }
  494. } else {
  495. // 单个移动
  496. // console.log('单个移动。。。')
  497. // 相同bid不允许
  498. if (this.props.bid !== info.bid) {
  499. // 对drag的处理
  500. position = this.getPosition(info.bid, deltaTop, maxHeight, multiInfo)
  501. allowDrop = this.allowDrop(info.bid, position, multiInfo)
  502. } else {
  503. allowDrop = false
  504. }
  505. }
  506. dragUtils.setOverItemInfo(position, this.props.bid, allowDrop)
  507. }
  508. onDrop(e) {
  509. e.stopPropagation()
  510. e.preventDefault()
  511. dragUtils.isDragging = false
  512. let dragInfo = dragUtils.getDraggingItemInfo()
  513. let multiInfo = dragUtils.getDraggingMultiItemsInfo()
  514. multiInfo = filterBlocks(multiInfo)
  515. let overInfo = dragUtils.getOverItemInfo()
  516. // 对block的处理
  517. if (multiInfo.length === 0) {
  518. if (dragInfo) {
  519. newEventActions.toggleBlockProp({
  520. blockId: dragInfo.bid,
  521. prop: 'expand',
  522. value: dragInfo.expand,
  523. addRecord: false
  524. })
  525. }
  526. }
  527. // 移动block
  528. if (overInfo && overInfo.isAllow) {
  529. newEventActions.moveBlock({
  530. srcBlockId: dragInfo.bid,
  531. targetBlockId: overInfo.bid,
  532. position: overInfo.overPosition,
  533. multiSrcBlockIds: multiInfo
  534. })
  535. }
  536. dragUtils.resetDraggingItem()
  537. }
  538. onContextMenu(e) {
  539. e.preventDefault()
  540. e.stopPropagation()
  541. this.onSelectBlock(e, true)
  542. let info = {
  543. x: e.pageX,
  544. y: e.pageY,
  545. type: CONTEXT_MENU_TYPE.EVENT
  546. }
  547. uiActions.contextMenu(info)
  548. }
  549. render() {
  550. return null
  551. }
  552. }