UNPKG

17.8 kBJavaScriptView Raw
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { Event,EventUtil} from "./lib/utils";
4import TableCell from './TableCell';
5import ExpandIcon from './ExpandIcon';
6
7const propTypes = {
8 onDestroy: PropTypes.func,
9 onRowClick: PropTypes.func,
10 onRowDoubleClick: PropTypes.func,
11 record: PropTypes.object,
12 clsPrefix: PropTypes.string,
13 expandIconColumnIndex: PropTypes.number,
14 onHover: PropTypes.func,
15 columns: PropTypes.array,
16 height: PropTypes.oneOfType([
17 PropTypes.string,
18 PropTypes.number,
19 ]),
20 visible: PropTypes.bool,
21 index: PropTypes.number,
22 hoverKey: PropTypes.any,
23 expanded: PropTypes.bool,
24 expandable: PropTypes.any,
25 onExpand: PropTypes.func,
26 needIndentSpaced: PropTypes.bool,
27 className: PropTypes.string,
28 indent: PropTypes.number,
29 indentSize: PropTypes.number,
30 expandIconAsCell: PropTypes.bool,
31 expandRowByClick: PropTypes.bool,
32 store: PropTypes.object.isRequired,
33 rowDraggAble: PropTypes.bool,
34 onDragRow: PropTypes.func,
35 onDragRowStart: PropTypes.func,
36 syncRowHeight:PropTypes.bool
37};
38
39const defaultProps = {
40 onRowClick() {},
41 // onRowDoubleClick() {},
42 onDestroy() {},
43 expandIconColumnIndex: 0,
44 expandRowByClick: false,
45 onHover() {},
46 className:'',
47 setRowParentIndex:()=>{},
48 rowDraggAble:false,
49 // onDragRow:()=>{}
50 syncRowHeight:false
51};
52
53class TableRow extends Component{
54 constructor(props){
55 super(props);
56 this._timeout = null;
57 this.state = {
58 hovered: false,
59 };
60 this.onRowClick = this.onRowClick.bind(this);
61 this.onRowDoubleClick = this.onRowDoubleClick.bind(this);
62 this.onMouseEnter = this.onMouseEnter.bind(this);
63 this.onMouseLeave = this.onMouseLeave.bind(this);
64 this.expandHeight = 0;
65 this.event = false;
66 this.cacheCurrentIndex = null;
67 this.canBeTouch = true //受否允许拖动该行
68 }
69
70
71 componentDidMount() {
72 const { store, hoverKey,treeType,rowDraggAble } = this.props;
73 this.unsubscribe = store.subscribe(() => {
74 if (store.getState().currentHoverKey === hoverKey) {
75 this.setState({ hovered: true });
76 } else if (this.state.hovered === true) {
77 this.setState({ hovered: false });
78 }
79 });
80
81 this.setRowHeight()
82 if(treeType){
83 this.setRowParentIndex();
84 }
85 }
86
87 /**
88 * 事件初始化
89 */
90 initEvent=()=>{
91 let events = [
92 {key:'touchstart', fun:this.onTouchStart},//手指触摸到一个 DOM 元素时触发
93 {key:'touchmove', fun:this.onTouchMove}, //手指在一个 DOM 元素上滑动时触发
94 {key:'touchend', fun:this.onTouchEnd}, //手指从一个 DOM 元素上移开时触发
95
96 {key:'dragstart',fun:this.onDragStart},//用户开始拖动元素时触发
97 {key:'dragover', fun:this.onDragOver},//当某被拖动的对象在另一对象容器范围内拖动时触发此事件
98 {key:'drop', fun:this.onDrop}, //在一个拖动过程中,释放鼠标键时触发此事件
99 {key:'dragenter', fun:this.onDragEnter},
100 {key:'dragleave', fun:this.onDragLeave},
101 ];
102 this.eventListen(events,'',this.element);
103 }
104
105 /**
106 * 事件移除,提供性能以及内存泄漏等问题。
107 */
108 removeDragAbleEvent=()=>{
109 let events = [
110 {key:'touchstart', fun:this.onTouchStart},//手指触摸到一个 DOM 元素时触发
111 {key:'touchmove', fun:this.onTouchMove}, //手指在一个 DOM 元素上滑动时触发
112 {key:'touchend', fun:this.onTouchEnd}, //手指从一个 DOM 元素上移开时触发
113
114 {key:'dragstart',fun:this.onDragStart},//用户开始拖动元素时触发
115 {key:'dragover', fun:this.onDragOver},//当某被拖动的对象在另一对象容器范围内拖动时触发此事件
116 {key:'drop', fun:this.onDrop}, //在一个拖动过程中,释放鼠标键时触发此事件
117 {key:'dragenter', fun:this.onDragEnter},
118 {key:'dragleave', fun:this.onDragLeave},
119 ];
120 this.eventListen(events,'remove',this.element);
121 }
122
123
124 /**
125 * 事件绑定和移除函数
126 */
127 eventListen(events,type,eventSource){
128 for (let i = 0; i < events.length; i++) {
129 const _event = events[i];
130 if(type === "remove"){
131 EventUtil.removeHandler(eventSource,_event.key,_event.fun);
132 }else{
133 EventUtil.addHandler(eventSource,_event.key,_event.fun);
134 }
135 }
136 }
137
138 /**
139 * 开始调整交换列的事件
140 */
141 onDragStart = (e) => {
142 let {onDragRowStart} = this.props;
143 if (!this.props.rowDraggAble || this.notRowDrag) return;
144 let event = Event.getEvent(e) ,
145 target = Event.getTarget(event);
146 if (target.tagName === 'TD') {
147 target = target.parentNode;
148 }
149 this.currentIndex = target.getAttribute("data-row-key");
150 this._dragCurrent = target;
151 event.dataTransfer.effectAllowed = "move";
152 event.dataTransfer.setData("Text", this.currentIndex);
153 onDragRowStart && onDragRowStart(this.currentIndex);
154 }
155
156 onDragOver = (e) => {
157 let event = Event.getEvent(e);
158 event.preventDefault();
159 };
160
161 /**
162 * 在一个拖动过程中,释放鼠标键时触发此事件。【目标事件】
163 * @memberof TableHeader
164 */
165 onDrop = (e) => {
166 let {onDragRow} = this.props;
167 let event = Event.getEvent(e) ,
168 _target = Event.getTarget(event),
169 target = _target.parentNode;
170 event.preventDefault()
171 event.stopPropagation();
172 let currentKey = event.dataTransfer.getData("text");
173 let targetKey = target.getAttribute("data-row-key");
174
175 if(!targetKey || targetKey === currentKey)return;
176 if(target.nodeName.toUpperCase() === "TR"){
177 this.synchronizeTableTr(currentKey,null);
178 this.synchronizeTableTr(targetKey,null);
179 }
180 onDragRow && onDragRow(currentKey,targetKey);
181 };
182
183 /**
184 * 获取当前触摸的Dom节点
185 */
186 getTouchDom = (event) => {
187 let currentLocation = event.changedTouches[0];
188 let realTarget = document.elementFromPoint(currentLocation.clientX, currentLocation.clientY);
189 return realTarget;
190 }
191
192 /**
193 * 开始调整交换行的事件
194 */
195 onTouchStart = (e) => {
196 e.stopPropagation()
197 let {onDragRowStart} = this.props;
198 let event = Event.getEvent(e) ,
199 _target = Event.getTarget(event),
200 target = _target.parentNode;
201
202 if (target.tagName === 'TR') {
203
204 this.currentIndex = target.getAttribute("data-row-key");
205
206 onDragRowStart && onDragRowStart(this.currentIndex);
207 }else{
208
209 this.canBeTouch = false
210 }
211
212 }
213
214 onTouchMove = (e) => {
215
216 if (!this.canBeTouch) return;
217 e.stopPropagation()
218 let event = Event.getEvent(e);
219 event.preventDefault();
220 let touchTarget = this.getTouchDom(event),
221 target = touchTarget.parentNode,
222 targetKey = target.getAttribute("data-row-key");
223 if(!targetKey || targetKey === this.currentIndex)return;
224 if(target.nodeName.toUpperCase() === "TR"){
225 if(this.cacheCurrentIndex !== targetKey){ //模拟 touchenter toucheleave 事件
226 this.cacheCurrentIndex && this.synchronizeTableTr(this.cacheCurrentIndex,null); //去掉虚线
227 this.synchronizeTableTr(targetKey,true); //添加虚线
228 }
229 }
230 }
231
232 /**
233 * 手指移开时触发
234 */
235 onTouchEnd = (e) => {
236
237 if(!this.canBeTouch){
238 this.canBeTouch = true
239 return
240 }
241
242 e.stopPropagation()
243 let {onDragRow} = this.props;
244 let event = Event.getEvent(e),
245 currentKey = this.currentIndex, //拖拽行的key
246 touchTarget = this.getTouchDom(event), //当前触摸的DOM节点
247 target = touchTarget.parentNode, //目标位置的行
248 targetKey = target.getAttribute("data-row-key"); //目标位置的行key
249 if(!targetKey || targetKey === currentKey)return;
250 if(target.nodeName.toUpperCase() === "TR"){
251 this.synchronizeTableTr(currentKey,null);
252 this.synchronizeTableTr(targetKey,null);
253 }
254
255 onDragRow && onDragRow(currentKey,targetKey);
256 }
257
258 /**
259 *同步当前拖拽到阴影
260 * @memberof TableRow
261 */
262 synchronizeTableTrShadow = ()=>{
263 let {contentTable,index} = this.props;
264
265 let cont = contentTable.querySelector('.u-table-scroll table tbody').getElementsByTagName("tr")[index],
266 trs = cont.getBoundingClientRect(),
267 fixed_left_trs = contentTable.querySelector('.u-table-fixed-left table tbody'),
268 fixed_right_trs = contentTable.querySelector('.u-table-fixed-right table tbody');
269 fixed_left_trs = fixed_left_trs && fixed_left_trs.getElementsByTagName("tr")[index].getBoundingClientRect();
270 fixed_right_trs = fixed_right_trs && fixed_right_trs.getElementsByTagName("tr")[index].getBoundingClientRect()
271
272 let div = document.createElement("div");
273 let style = "wdith:"+(trs.width + (fixed_left_trs ? fixed_left_trs.width : 0) + (fixed_right_trs ? fixed_right_trs.width : 0))+"px";
274 style += ";height:"+ trs.height+"px";
275 style += ";classname:"+ cont.className;
276 div.setAttribute("style",style);
277 return div;
278 }
279
280
281 /**
282 * 同步自己,也需要同步当前行的行显示
283 */
284 synchronizeTableTr = (currentIndex,type)=>{
285 if(type){ //同步 this.cacheCurrentIndex
286 this.cacheCurrentIndex = currentIndex;
287 }
288 let {contentTable} = this.props;
289 let _table_trs = contentTable.querySelector('.u-table-scroll table tbody'),
290 _table_fixed_left_trs = contentTable.querySelector('.u-table-fixed-left table tbody'),
291 _table_fixed_right_trs = contentTable.querySelector('.u-table-fixed-right table tbody');
292
293 _table_trs = _table_trs?_table_trs:contentTable.querySelector('.u-table table tbody');
294
295 this.synchronizeTrStyle(_table_trs,currentIndex,type);
296 if(_table_fixed_left_trs){
297 this.synchronizeTrStyle(_table_fixed_left_trs,currentIndex,type);
298 }
299 if(_table_fixed_right_trs){
300 this.synchronizeTrStyle(_table_fixed_right_trs,currentIndex,type);
301 }
302 }
303
304 /**
305 * 设置同步的style
306 */
307 synchronizeTrStyle = (_elementBody,id,type)=>{
308 let {contentTable} = this.props,
309 trs = _elementBody.getElementsByTagName("tr"),
310 currentObj;
311 for (let index = 0; index < trs.length; index++) {
312 const element = trs[index];
313 if(element.getAttribute("data-row-key") == id){
314 currentObj = element;
315 }
316 }
317 if(type){
318 currentObj && currentObj.setAttribute("style","border-bottom:2px solid #02B1FD");
319 }else{
320 currentObj && currentObj.setAttribute("style","");
321 }
322 }
323
324 onDragEnter = (e) => {
325 let event = Event.getEvent(e) ,
326 _target = Event.getTarget(event),target = _target.parentNode;
327 let currentIndex = target.getAttribute("data-row-key");
328 if(!currentIndex || currentIndex === this.currentIndex)return;
329 if(target.nodeName.toUpperCase() === "TR"){
330 this.synchronizeTableTr(currentIndex,true);
331 }
332 }
333
334 onDragLeave = (e) => {
335 let event = Event.getEvent(e) ,
336 _target = Event.getTarget(event),target = _target.parentNode;
337 let currentIndex = target.getAttribute("data-row-key");
338 if(!currentIndex || currentIndex === this.currentIndex)return;
339 if(target.nodeName.toUpperCase() === "TR"){
340 this.synchronizeTableTr(currentIndex,null);
341 }
342 }
343
344 componentDidUpdate(prevProps) {
345 const { rowDraggAble,syncRowHeight } = this.props;
346 if(!this.event){
347 this.event = true;
348 if(rowDraggAble){
349 this.initEvent();
350 }
351 }
352 if(this.props.treeType){
353 this.setRowParentIndex();
354 }
355 // if(syncRowHeight){
356 // this.setRowHeight()
357 // }
358 this.setRowHeight()
359 }
360
361 componentWillUnmount() {
362 const { record, onDestroy, index,rowDraggAble } = this.props;
363 onDestroy(record, index);
364 if (this.unsubscribe) {
365 this.unsubscribe();
366 }
367 if(rowDraggAble){
368 this.removeDragAbleEvent();
369 }
370 }
371
372
373 setRowHeight() {
374 const { setRowHeight , expandedContentHeight=0,fixed,fixedIndex} = this.props
375 if (!setRowHeight || !this.element || fixed) return
376 setRowHeight(this.element.clientHeight + expandedContentHeight, fixedIndex)
377 }
378 setRowParentIndex(){
379 const {index,setRowParentIndex,fixedIndex,rootIndex} = this.props;
380 setRowParentIndex(rootIndex<0?index:rootIndex,fixedIndex);
381
382 }
383
384 onRowClick(event) {
385 // fix: 解决 onRowClick 回调函数中,事件对象属性均为 null 的问题
386 // 异步访问事件属性
387 // 调用 event.persist() 会从事件池中移除该合成函数并允许对该合成事件的引用被保留下来。
388 event.persist();
389 const {
390 record,
391 index,
392 onRowClick,
393 expandable,
394 expandRowByClick,
395 expanded,
396 onExpand,
397 fixedIndex,
398 onRowDoubleClick
399 } = this.props;
400 if (expandable && expandRowByClick) {
401 onExpand(!expanded, record, fixedIndex,event);
402 }
403 if(!onRowDoubleClick){
404 onRowClick(record, fixedIndex, event);
405 return;
406 }
407 this.set((e)=> {
408 onRowClick(record, fixedIndex, event);
409 });
410 }
411
412 onRowDoubleClick(event) {
413 const { record, index, onRowDoubleClick,fixedIndex } = this.props;
414 this.clear();
415 onRowDoubleClick && onRowDoubleClick(record, fixedIndex, event);
416 }
417
418 onMouseEnter(e) {
419 const { onHover, hoverKey,fixedIndex,syncHover,record } = this.props;
420 if(syncHover){
421 this.setState({ hovered: true });
422 }
423 onHover(true, hoverKey,e,fixedIndex,record);
424 }
425
426 onMouseLeave(e) {
427
428 const { onHover, hoverKey ,fixedIndex,syncHover,record} = this.props;
429 if(syncHover){
430 this.setState({ hovered: false });
431 }
432 onHover(false, hoverKey,e,fixedIndex,record);
433 }
434
435 stopRowDrag = (isStop) => {
436 const {rowDraggAble} = this.props;
437 const {notRowDrag} = this.state;
438 if(rowDraggAble && isStop!== notRowDrag) {
439 this.setState({
440 notRowDrag: isStop
441 })
442 }
443 }
444
445 set =(fn)=> {
446 this.clear();
447 this._timeout = window.setTimeout(fn, 300);
448 }
449
450 clear =(event)=> {
451 if (this._timeout) {
452 window.clearTimeout(this._timeout);
453 }
454 }
455
456 bindElement = (el)=> {
457 this.element = el
458 }
459
460 render() {
461 const {
462 clsPrefix, columns, record, height, visible, index,onPaste,
463 expandIconColumnIndex, expandIconAsCell, expanded, useDragHandle,rowDraggAble,
464 expandable, onExpand, needIndentSpaced, indent, indentSize,isHiddenExpandIcon,fixed,bodyDisplayInRow
465 ,expandedIcon,collapsedIcon, hoverKey,lazyStartIndex,lazyEndIndex, expandIconCellWidth, getCellClassName
466 } = this.props;
467 const {notRowDrag} = this.state;
468 let showSum = false;
469 let { className } = this.props;
470 if (this.state.hovered) {
471 className += ` ${clsPrefix}-hover`;
472 }
473 //判断是否为合计行
474 if(className.indexOf('sumrow')>-1){
475 showSum = true;
476 }
477 const cells = [];
478
479 const expandIcon = (
480 <ExpandIcon
481 expandable={expandable}
482 clsPrefix={clsPrefix}
483 onExpand={onExpand}
484 needIndentSpaced={needIndentSpaced}
485 expanded={expanded}
486 record={record}
487 expandedIcon={expandedIcon}
488 collapsedIcon={collapsedIcon}
489 isHiddenExpandIcon={isHiddenExpandIcon}
490 />
491 );
492 let isExpandIconAsCell = expandIconAsCell ? `${clsPrefix}-expand-columns-in-body` : '';
493 var expandIndexInThisTable
494 if(this.props.fixed === 'right'){
495 expandIndexInThisTable = expandIconColumnIndex - this.props.leftColumnsLength-this.props.centerColumnsLength
496 }else {
497 expandIndexInThisTable = expandIconColumnIndex
498 }
499 for (let i = 0; i < columns.length; i++) {
500 if (expandIconAsCell && i === 0) {
501 showSum ? cells.push(<td width={expandIconCellWidth}></td>) :
502 cells.push(
503 <td
504 className={`${clsPrefix}-expand-icon-cell ${isExpandIconAsCell}`}
505 key={`rc-table-expand-icon-cell-${i}`}
506 width={expandIconCellWidth}
507 >
508 {expandIcon}
509 </td>
510 );
511 }
512 // bugfix 设置expandRowByClick,无法显示箭头,去掉 expandRowByClick 判断
513 const isColumnHaveExpandIcon = (expandIconAsCell || showSum)
514 ? false : (i === expandIndexInThisTable);
515 cells.push(
516 <TableCell
517 clsPrefix={clsPrefix}
518 record={record}
519 indentSize={indentSize}
520 indent={indent}
521 index={index}
522 column={columns[i]}
523 key={index + "_"+(columns[i].key || columns[i].dataIndex || i)}
524 fixed= {fixed}
525 showSum={showSum}
526 expandIcon={(isColumnHaveExpandIcon) ? expandIcon : null}
527 bodyDisplayInRow = {bodyDisplayInRow}
528 lazyStartIndex={lazyStartIndex}
529 lazyEndIndex={lazyEndIndex}
530 onPaste={onPaste}
531 stopRowDrag={this.stopRowDrag}
532 col={i}
533 getCellClassName = {getCellClassName}
534 />
535 );
536 }
537 const style = { height ,...record?record.style:undefined};
538 if (!visible) {
539 style.display = 'none';
540 }
541 if(record && record._checked){
542 className += ' selected';
543 }
544
545 if(rowDraggAble && !useDragHandle && !notRowDrag) {
546 className += ' row-dragg-able'
547 }
548
549 return (
550 <tr
551 draggable={rowDraggAble && !useDragHandle && !notRowDrag}
552 onClick={this.onRowClick}
553 onDoubleClick={this.onRowDoubleClick}
554 onMouseEnter={this.onMouseEnter}
555 onMouseLeave={this.onMouseLeave}
556 className={`${clsPrefix} ${className} ${clsPrefix}-level-${indent}`}
557 style={style}
558 data-row-key={record && record.key?record.key:hoverKey}
559 // key={hoverKey}
560 ref={this.bindElement}
561 >
562 {cells.length>0?cells:<td style={{width:0,padding:0}}></td>}
563 </tr>
564 );
565 }
566};
567
568TableRow.propTypes = propTypes;
569TableRow.defaultProps = defaultProps;
570
571export default TableRow;