UNPKG

bee-table

Version:
261 lines (255 loc) 11.9 kB
import React, { Component } from "react"; import {Event} from "./lib/utils"; import {findDOMNode} from "react-dom"; const CAN_TOUCH = Event.isSupportTouch();//是否支持触摸 /*** * @author Dio.Zhu * 提供表格拖拽行排序的能力,显示拖拽行的尺标线 */ export default class DragRowLine extends Component{ static defaultProps = { clsPrefix:'',//样式名前缀 container:null,//所属容器组件 onRowDragStart:null,//拖拽开始的回调 onRowDragDrop:null,//拖拽放置完成的回调 rowDraggAble:true,//是否启用行拖拽 bigData:false//是否大数据场景 }; constructor(props){ super(props); this.state={ visible:false, top:0, left:0, width:0 } } componentDidMount() { if(!this.props.bigData){//非大数据场景则直接初始化事件一次节省性能开销 this.initEvent(this.props.container); } } componentWillUnmount() { this.removeEvent(); } componentDidUpdate(prevProps, prevState, snapshot) { if(this.props.bigData){//大数据场景则每次更新都需要重新初始化绑定事件 this.removeEvent(); this.initEvent(); } else if((prevProps.data||[]).toString() !== (this.props.data||[]).toString()) { this.removeEvent(); this.initEvent(); } } //获取左、中、右三个区域的全部行dom元素 getTableRowDoms=()=>{ const {container,clsPrefix} = this.props; if(!container || !clsPrefix)return; let domElem = findDOMNode(container); let centerBody = domElem.querySelector(`.${clsPrefix}-scroll table tbody`); if(!centerBody){//非固定表头的情况下 centerBody = domElem.querySelector(`.${clsPrefix}-body table tbody`); } let leftBody = domElem.querySelector(`.${clsPrefix}-fixed-left table tbody`); let rightBody = domElem.querySelector(`.${clsPrefix}-scroll table tbody`); let centerTrs = centerBody&&centerBody.getElementsByTagName("tr"); let leftTrs =leftBody&&leftBody.getElementsByTagName("tr"); let rightTrs = rightBody&&rightBody.getElementsByTagName("tr"); return{centerTrs,leftTrs,rightTrs} } //初始化绑定相关事件 initEvent=()=>{ let {centerTrs,leftTrs,rightTrs} = this.getTableRowDoms(); let allTrs = [].concat(centerTrs,leftTrs,rightTrs); // Event.addHandler(document.body,'drop',this.onBodyDrop); allTrs.forEach((trs)=>{ Event.addHandlerArray(trs,'dragstart',this.onDragStart); Event.addHandlerArray(trs,'dragenter',this.onDragEnter); Event.addHandlerArray(trs,'dragover',this.onDragOver); Event.addHandlerArray(trs,'dragend',this.onDragEnd); //触屏下支持 if(CAN_TOUCH){ Event.addHandlerArray(trs,'touchstart',this.onTouchStart);//手指触摸到一个 DOM 元素时触发 Event.addHandlerArray(trs,'touchmove',this.onTouchMove); //手指在一个 DOM 元素上滑动时触发 Event.addHandlerArray(trs,'touchend',this.onTouchEnd); //手指从一个 DOM 元素上移开时触发 } }) } //移除相关事件 removeEvent=(container)=>{ let {centerTrs,leftTrs,rightTrs} = this.getTableRowDoms(); let allTrs = [].concat(centerTrs,leftTrs,rightTrs); allTrs.forEach((trs)=>{ Event.removeHandlerArray(trs,'dragstart',this.onDragStart); Event.removeHandlerArray(trs,'dragenter',this.onDragEnter); Event.removeHandlerArray(trs,'dragover',this.onDragOver); Event.removeHandlerArray(trs,'dragend',this.onDragEnd); //触屏下支持 if(CAN_TOUCH) { Event.removeHandlerArray(trs, 'touchstart', this.onTouchStart);//手指触摸到一个 DOM 元素时触发 Event.removeHandlerArray(trs, 'touchmove', this.onTouchMove); //手指在一个 DOM 元素上滑动时触发 Event.removeHandlerArray(trs, 'touchend', this.onTouchEnd); //手指从一个 DOM 元素上移开时触发 } }) // Event.removeHandler(document.body,'drop',this.onBodyDrop); } /* 会导致单元格内自定义onDrop冒泡失效,所以body上不阻止冒泡。 //拖放到body区域时触发,解决Firefox下拖放会新打开浏览器窗口的问题 onBodyDrop(e){ // console.log("AAA--->2",e.dataTransfer.getData('Text')) Event.stopPropagation(e); }*/ /** * 【交换行】开始拖拽 */ onDragStart = (e,touchTarget) => { let {onRowDragStart,container} = this.props; let {contentTable} = container; let event = Event.getEvent(e); let target = touchTarget?touchTarget:Event.getTarget(event);//支持触屏目标对象 if(target.nodeName.toUpperCase() == 'TD') target = target.parentNode;//如果是TD,则找到TR if(target.nodeName.toUpperCase() !== "TR")return;//如果不是TR则终止 let dragStartKey = target.getAttribute("data-row-key"); let dragStartIndex = target.getAttribute("data-row-index"); // console.log("AAA---row-1--onDragStart"+dragStartKey) if(!touchTarget){//非触摸屏 event.dataTransfer.effectAllowed = "move"; event.dataTransfer.setData("Text", JSON.stringify({dragStartKey, dragStartIndex})); }else{ //目前触屏下没有提供抓取的阴影副本对象 } if(contentTable){ contentTable.dragStartKey = dragStartKey; contentTable.dragStartIndex = dragStartIndex; } onRowDragStart && onRowDragStart({dragStartKey, dragStartIndex}); } /** * 【交换行】拖拽经过目标行 */ onDragEnter = (e,touchTarget) => { const {container} = this.props; let {contentTable} = container; let event = Event.getEvent(e) , _target = Event.getTarget(event); let target = touchTarget?touchTarget:_target.parentNode;//支持触屏目标对象 if(target.nodeName.toUpperCase() == 'TD') target = target.parentNode;//如果是TD,则找到TR if(target.nodeName.toUpperCase() !== "TR")return;//如果不是TR则终止 if(!contentTable)return; let dragEnterKey = target.getAttribute("data-row-key"); let dragEnterIndex = target.getAttribute("data-row-index"); if(!dragEnterKey)return; let dropAlign = parseInt(dragEnterIndex) >= parseInt(contentTable.dragStartIndex)+1 ? 'bottom' : 'top'; if(contentTable.dragStartKey && dragEnterKey !== contentTable.dragStartKey){ contentTable.dragEnterKey = dragEnterKey; contentTable.dragEnterIndex = dragEnterIndex; // onRowDragEnter && onRowDragEnter(dragEnterKey,dropAlign,target); // console.log("AAAA---1.1--enter-->"+contentTable.dragStartIndex+"->"+dragEnterIndex) this.setLinePosition(contentTable,target,dropAlign); //显示行拖拽的尺表线 }else{ contentTable.dragEnterKey = null; contentTable.dragEnterIndex = null; // console.log("AAAA---1.1--enter-hidden-->"+contentTable.dragStartIndex+"->"+dragEnterIndex) // onRowDragEnter && onRowDragEnter(dragEnterKey,null,null); this.setLinePosition(contentTable,null,null); //隐藏行拖拽的尺表线 } } /** * 【交换行】结束拖拽 */ onDragEnd = (e,touchTarget) =>{ let {onRowDragDrop,container} = this.props; let {contentTable} = container; if(!contentTable)return; let {dragStartKey,dragStartIndex,dragEnterKey,dragEnterIndex} = contentTable||{}; // console.log("AAA---row-3--onDragEnd:"+dragStartKey+"->"+dragEnterKey) onRowDragDrop && onRowDragDrop({dragTargetKey:dragStartKey,dragTargetIndex:dragStartIndex,dropTargetKey:dragEnterKey,dropTargetIndex:dragEnterIndex}); contentTable.dragStartKey = null; contentTable.dragStartIndex = null; contentTable.dragEnterKey = null; contentTable.dragEnterIndex = null; this.setLinePosition(null,null,null); //隐藏行拖拽的尺表线 } onDragOver = (e) => { let event = Event.getEvent(e); event.preventDefault(); }; //触屏-手指点击时触发 onTouchStart = (e) => { e.stopPropagation() let event = Event.getEvent(e), _target = Event.getTarget(event); let target = _target; let maxCount = 0;//向上查找最多层级数 while (target.tagName!=='TR'){ target = target.parentNode; maxCount++; if(target.tagName=='TABLE'||target.tagName=='BODY'||maxCount>10){ break; } } if (target.tagName === 'TR') { this.isTouchDragRow = true; this.onDragStart(e,target); } else { this.isTouchDragRow = false } } //触屏-手指滑动时触发 onTouchMove = (e) => { if (!this.isTouchDragRow) return; e.stopPropagation() let event = Event.getEvent(e); event.preventDefault(); let touchTarget = this.getTouchDom(event), target = touchTarget.parentNode; this.onDragEnter(e,target); } //触屏-手指移开时触发 onTouchEnd = (e) => { if(!this.isTouchDragRow)return; e.stopPropagation() let event = Event.getEvent(e), touchTarget = this.getTouchDom(event), //当前触摸的DOM节点 target = touchTarget.parentNode; //目标位置的行 this.onDragEnd(e,target); this.isTouchDragRow = false; } //获取当前触摸的Dom节点 getTouchDom = (event) => { let currentLocation = event.changedTouches[0]; let realTarget = document.elementFromPoint(currentLocation.clientX, currentLocation.clientY); return realTarget; } /** * 设置尺标线显示的位置 * @param targetContainer * @param targetDom * @param targetAlign */ setLinePosition = (targetContainer,targetDom,targetAlign)=>{ let {clsPrefix} = this.props; if(!targetContainer||!targetDom){ this.setState({left:0,top:0,width:0,visible:false}); }else{ let contRect = targetContainer.getBoundingClientRect(); let centerBodyDom = targetContainer.querySelector(`.${clsPrefix}-content .${clsPrefix}-body`); let centerBodyRect = centerBodyDom.getBoundingClientRect(); let headerHeight = centerBodyRect.top - contRect.top; let targetRect = targetDom.getBoundingClientRect(); let top = headerHeight + targetRect.top - centerBodyRect.top - 1; if(['bottom','down'].includes(targetAlign)){ top = top + targetRect.height; } this.setState({left:0,width:contRect.width,top,visible:true}) } } render() { const {clsPrefix} = this.props; const {left,width,top,visible} = this.state; let style = {position:'absolute',left:left+'px',top:top+'px',height:'2px',width:width+'px',background:'#02B1FD',zIndex:'auto',display: visible?'block':'none' }; return ( <div style={style} className={`${clsPrefix}-drag-row-line`} dangerouslySetInnerHTML={{__html:'&nbsp;'}}></div> ); } }