bee-table
Version:
Table ui component for react
261 lines (255 loc) • 11.9 kB
JavaScript
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&¢erBody.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:' '}}></div>
);
}
}