import React from 'react' import { shallowEqualImmutable, getY, isMac } from 'utils/utils' import uiActions from 'actions/ui' import newEventActions from 'actions/newEvent' import newEventStores from 'stores/newEvent' import { getEventBlockByBid, filterBlocks, getBidList } from 'stores/funcs/newEvent' import { dragPosition, dragUtils } from 'stores/funcs/event/dragUtils' import { CONTEXT_MENU_TYPE, EVENT_BLOCK_TYPE } from 'const/const' export default class Block extends React.Component { constructor(props) { super(props) this.curBlockId = newEventStores.curBlockId this.multiBlockIds = newEventStores.multiBlockIds this.state = { isActive: this.isActive(props.bid), isEnable: this.isEnable(props.bid), isExpand: this.isExpand(props.bid), hasChildren: this.hasChildren(props.bid) } this.onEventChange = this.onEventChange.bind(this) this.isActive = this.isActive.bind(this) this.isEnable = this.isEnable.bind(this) this.isExpand = this.isExpand.bind(this) this.onSelectBlock = this.onSelectBlock.bind(this) this.onToggleBlockExpand = this.onToggleBlockExpand.bind(this) this.getLineColor = this.getLineColor.bind(this) this.getGapLeft = this.getGapLeft.bind(this) // 拖拽 this.allowDrag = this.allowDrag.bind(this) this.allowDrop = this.allowDrop.bind(this) this.getPosition = this.getPosition.bind(this) this.onDragStart = this.onDragStart.bind(this) this.onDragEnd = this.onDragEnd.bind(this) this.onDragOver = this.onDragOver.bind(this) this.onDrop = this.onDrop.bind(this) // 右键 this.onContextMenu = this.onContextMenu.bind(this) } shouldComponentUpdate(nextProps, nextState) { return ( !shallowEqualImmutable(this.props, nextProps) || !shallowEqualImmutable(this.state, nextState) ) } componentDidUpdate(preProps) { let obj = {} // bid改变时需要更新 if (preProps.bid !== this.props.bid) { obj.isActive = this.isActive() obj.isEnable = this.isEnable() obj.isExpand = this.isExpand() obj.hasChildren = this.hasChildren() } if (Object.keys(obj).length > 0) { this.setState(obj) } } componentDidMount() { this.unsubscribe = newEventStores.listen(this.onEventChange) } componentWillUnmount() { this.unsubscribe() } onEventChange(status) { let obj = {} if (status.types.includes('curBlockId')) { if (this.curBlockId !== status.data.curBlockId) { this.curBlockId = status.data.curBlockId let isActive = this.isActive() if (this.state.isActive !== isActive) { obj.isActive = isActive } } } if (status.types.includes('multiBlockIds')) { this.multiBlockIds = status.data.multiBlockIds let isActive = this.isActive() if (this.state.isActive !== isActive) { obj.isActive = isActive } } if (status.types.includes('toggleBlockProp')) { if (this.props.bid === status.data.affectBlockId) { if (status.data.prop === 'enable') { let isEnable = this.isEnable() if (this.state.isEnable !== isEnable) { obj.isEnable = isEnable } } else if (status.data.prop === 'expand') { let isExpand = this.isExpand() if (this.state.isExpand !== isExpand) { obj.isExpand = isExpand } } } } if ( (status.types.includes('changeBlockChildren') && status.data && status.data.parentBid === this.props.bid) || (status.types.includes('reloadBlock') && this.props.bid === status.data.affectBlockId) ) { let hasChildren = this.hasChildren() if (hasChildren !== this.state.hasChildren) { obj.hasChildren = hasChildren } } if (Object.keys(obj).length > 0) { this.setState(obj) } } isActive(bid) { let _bid = bid || this.props.bid return this.curBlockId === _bid || this.multiBlockIds.indexOf(_bid) >= 0 } isEnable(bid) { let _bid = bid || this.props.bid let result = true let block = getEventBlockByBid(_bid) if (block) { result = block.enable !== false } return result } isExpand(bid) { let _bid = bid || this.props.bid let result = true let block = getEventBlockByBid(_bid) if (block) { result = block.expand !== false } return result } hasChildren(bid) { let _bid = bid || this.props.bid let result = false let block = getEventBlockByBid(_bid) if (block) { result = block.children && block.children.length > 0 } return result } onSelectBlock(e, allowMulti = false) { if ( this.props.eventNodeId && this.props.eventNodeId !== newEventStores.curEventNodeId ) { newEventActions.selectEvent({ eventNodeId: this.props.eventNodeId }) } else { // 多选 let isCtrl = e && (isMac ? e.metaKey : e.ctrlKey) if (isCtrl) { newEventActions.selectMultiBlocks({ blockId: this.props.bid }) return } let isShift = e && e.shiftKey if (isShift) { newEventActions.shiftSelectBlocks({ blockId: this.props.bid }) return } } if ( !this.state.isActive || ( this.multiBlockIds.length > 0 && ( !allowMulti || ( allowMulti && !this.multiBlockIds?.includes(this.props.bid) ) ) ) ) { newEventActions.selectBlock({ blockId: this.props.bid }) } } onToggleBlockExpand() { newEventActions.toggleBlockProp({ blockId: this.props.bid, prop: 'expand' }) } getLineColor() { let group = ['red', 'green', 'purple', 'ching', 'orange', 'blue'] return group[this.props.layer % 6] } getNextLineColor() { let group = ['red', 'green', 'purple', 'ching', 'orange', 'blue'] return group[(this.props.layer + 1) % 6] } getGapLeft() { return 8 + this.props.layer * 20 + (this.props.layer + 1) } getGroupColor() { const group = ['color1', 'color2', 'color3', 'color4', 'color5'] // const group = ['#7F438F', '#E5C88E', '#EEB2B2', '#B5D8F1', '#CBFF97'] return group[(this.props.groupLayer) % 5] } blockMainStyle() { if (this.props.groupLayer >= 1) { return { paddingLeft: `${(this.props.nestGroupLayer - 1) * 2}px`, marginRight: `${(this.props.groupLayer) * 2}px` } } return null } getLoops(bid) { let _bid = bid || this.props.bid let block = getEventBlockByBid(_bid) if (block && block.type === EVENT_BLOCK_TYPE.LOOP) { if (this.props.loops) { return JSON.stringify( [].concat(JSON.parse(this.props.loops), [block.bid]) ) } else { return JSON.stringify([block.bid]) } } else { return this.props.loops } } indexLeftStyle() { // let _bid = this.props.bid // let block = getEventBlockByBid(_bid) // console.log(block?.type, this.props.offspringInNestGroupLayer, this.props.nestGroupLayer) if (this.props.offspringInNestGroupLayer >= 2 && this.props.nestGroupLayer === 0) { return { marginLeft: 20 + (this.props.offspringInNestGroupLayer - 1) * 2 + 'px' } } return undefined } extraGapGroupLayer() { if (this.props.offspringInNestGroupLayer >= 2 && this.props.nestGroupLayer === 0) { return this.props.offspringInNestGroupLayer } else { return this.props.extraGapGroupLayer || 0 } } //拖拽 allowDrag() { let allow = true if ( document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA' ) { allow = false // block dragging } if (!this.isActive(this.props.bid)) { this.onSelectBlock() } return allow } allowDrop(bid, position, multiInfo) { let result = false let dragBlock = getEventBlockByBid(bid) let targetBlock = getEventBlockByBid(this.props.bid) let dropBlock = undefined switch (position) { case dragPosition.mid: // 加到targetBlock里面 dropBlock = targetBlock break case dragPosition.bot: case dragPosition.top: // 实际是放在targetBlock的父级 if (targetBlock.parentBid) { let targetParentBlock = getEventBlockByBid(targetBlock.parentBid) if (targetParentBlock) { dropBlock = targetParentBlock } else { dropBlock = undefined } } break } if (multiInfo && multiInfo.length > 0) { // 多个移动 let blockTypes = [] multiInfo = filterBlocks(multiInfo) multiInfo.forEach(bid => { let block = getEventBlockByBid(bid) if (block && block.type) { let type = block.type if (blockTypes.indexOf(type) === -1) { blockTypes.push(type) } } }) if ( targetBlock && targetBlock.type === EVENT_BLOCK_TYPE.ROOT && blockTypes.length === 1 && blockTypes[0] === EVENT_BLOCK_TYPE.ROOT && position !== dragPosition.mid ) { // 拖的和drop的都是Root只可放上和下 result = true } else if (dropBlock) { // 提炼出可drop的情况 if (dropBlock.type === EVENT_BLOCK_TYPE.ACTION) { if ( blockTypes.length === 1 && blockTypes[0] === EVENT_BLOCK_TYPE.STATUS && dropBlock.action && dropBlock.action.callback === true ) { result = true } } else if (dropBlock.type === EVENT_BLOCK_TYPE.COMMENT) { result = false } else { if ( blockTypes.indexOf(EVENT_BLOCK_TYPE.ROOT) === -1 && blockTypes.indexOf(EVENT_BLOCK_TYPE.STATUS) === -1 ) { result = true } } } } else { // 单个移动 if ( dragBlock && targetBlock && dragBlock.type === EVENT_BLOCK_TYPE.ROOT && targetBlock.type === EVENT_BLOCK_TYPE.ROOT && position !== dragPosition.mid ) { // 拖的和drop的都是Root只可放上和下 result = true } else if (dragBlock && dropBlock) { // 提炼出可drop的情况 if (dropBlock.type === EVENT_BLOCK_TYPE.ACTION) { if ( dragBlock.type === EVENT_BLOCK_TYPE.STATUS && dropBlock.action && dropBlock.action.callback === true ) { result = true } } else if (dropBlock.type === EVENT_BLOCK_TYPE.COMMENT) { result = false } else { if ( dragBlock.type !== EVENT_BLOCK_TYPE.ROOT && dragBlock.type !== EVENT_BLOCK_TYPE.STATUS ) { result = true } } } } return result } getPosition(dragBlockId, deltaTop, maxHeight, multiInfo) { let position = dragPosition.mid let mid1 = maxHeight / 3 let mid2 = (maxHeight * 2) / 3 if (deltaTop >= 0 && deltaTop <= mid1) { position = dragPosition.top } else if (deltaTop >= mid2 && deltaTop <= maxHeight) { position = dragPosition.bot } let dropBlock = getEventBlockByBid(this.props.bid) if (multiInfo && multiInfo.length > 0) { // 多个移动 let blockTypes = [] multiInfo = filterBlocks(multiInfo) multiInfo.forEach(bid => { let block = getEventBlockByBid(bid) if (block && block.type) { blockTypes.push(block.type) } }) if ( dropBlock && dropBlock.type === EVENT_BLOCK_TYPE.ROOT && blockTypes.indexOf(EVENT_BLOCK_TYPE.ROOT) === -1 ) { // drop为root且drap均非root的情况 position = dragPosition.mid } } else { // 单个移动 let dragBlock = getEventBlockByBid(dragBlockId) if ( dropBlock && dropBlock.type === EVENT_BLOCK_TYPE.ROOT && !(dragBlock && dragBlock.type === EVENT_BLOCK_TYPE.ROOT) ) { // 不同为root的情况且drop的root的情况 position = dragPosition.mid } } return position } onDragStart(e) { dragUtils.isDragging = true e.stopPropagation() if (!this.allowDrag()) { e.preventDefault() return } e.dataTransfer.effectAllowed = 'move' dragUtils.setDraggingItemInfo(this.props.bid, this.state.isExpand) dragUtils.setDraggingMultiItemsInfo(this.multiBlockIds) // 对block的处理 if (this.multiBlockIds.length === 0) { if (this.state.isExpand && this.state.hasChildren) { // 关闭起来 newEventActions.toggleBlockProp({ blockId: this.props.bid, prop: 'expand', value: false, addRecord: false }) } } } onDragEnd(e) { // drop先执行 e.stopPropagation() if (dragUtils.isDragging) { dragUtils.isDragging = false let dragInfo = dragUtils.getDraggingItemInfo() // 对block的处理 if (dragInfo) { newEventActions.toggleBlockProp({ blockId: dragInfo.bid, prop: 'expand', value: dragInfo.expand, addRecord: false }) } dragUtils.resetDraggingItem() } } onDragOver(e) { e.stopPropagation() if (!dragUtils.isDragging) { return } e.preventDefault() let allowDrop = true let deltaTop = e.clientY - getY(e.currentTarget) + document.querySelector('.jenga-panel').scrollTop let maxHeight = e.currentTarget.clientHeight let position = dragPosition.mid let info = dragUtils.getDraggingItemInfo() let multiInfo = dragUtils.getDraggingMultiItemsInfo() if (multiInfo && multiInfo.length > 0) { // 多个移动 // console.log('多个移动。。。') multiInfo = filterBlocks(multiInfo) let bidList = [] multiInfo.forEach(bid => { getBidList({ blockId: bid, list: bidList }) }) if (bidList.indexOf(this.props.bid) > -1) { // dragOver为自己或childern时不允许 allowDrop = false // console.log('包含自己。。。') } else { let blockTypes = [] multiInfo.forEach(bid => { let block = getEventBlockByBid(bid) if (block && block.type) { let type = block.type if (blockTypes.indexOf(type) === -1) { blockTypes.push(type) } } }) if ( multiInfo.length > 1 && blockTypes.indexOf(EVENT_BLOCK_TYPE.ROOT) > -1 && blockTypes.length > 1 ) { // 多个待移动block中同时有root和非root时不允许 allowDrop = false // console.log('同时有root和非root。。。') } else { // 对drag的处理 // console.log('ok。。。') position = this.getPosition(info.bid, deltaTop, maxHeight, multiInfo) allowDrop = this.allowDrop(info.bid, position, multiInfo) } } } else { // 单个移动 // console.log('单个移动。。。') // 相同bid不允许 if (this.props.bid !== info.bid) { // 对drag的处理 position = this.getPosition(info.bid, deltaTop, maxHeight, multiInfo) allowDrop = this.allowDrop(info.bid, position, multiInfo) } else { allowDrop = false } } dragUtils.setOverItemInfo(position, this.props.bid, allowDrop) } onDrop(e) { e.stopPropagation() e.preventDefault() dragUtils.isDragging = false let dragInfo = dragUtils.getDraggingItemInfo() let multiInfo = dragUtils.getDraggingMultiItemsInfo() multiInfo = filterBlocks(multiInfo) let overInfo = dragUtils.getOverItemInfo() // 对block的处理 if (multiInfo.length === 0) { if (dragInfo) { newEventActions.toggleBlockProp({ blockId: dragInfo.bid, prop: 'expand', value: dragInfo.expand, addRecord: false }) } } // 移动block if (overInfo && overInfo.isAllow) { newEventActions.moveBlock({ srcBlockId: dragInfo.bid, targetBlockId: overInfo.bid, position: overInfo.overPosition, multiSrcBlockIds: multiInfo }) } dragUtils.resetDraggingItem() } onContextMenu(e) { e.preventDefault() e.stopPropagation() this.onSelectBlock(e, true) let info = { x: e.pageX, y: e.pageY, type: CONTEXT_MENU_TYPE.EVENT } uiActions.contextMenu(info) } render() { return null } }