UNPKG

17.2 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) 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
171 let currentKey = event.dataTransfer.getData("text");
172 let targetKey = target.getAttribute("data-row-key");
173
174 if(!targetKey || targetKey === currentKey)return;
175 if(target.nodeName.toUpperCase() === "TR"){
176 this.synchronizeTableTr(currentKey,null);
177 this.synchronizeTableTr(targetKey,null);
178 }
179 onDragRow && onDragRow(currentKey,targetKey);
180 };
181
182 /**
183 * 获取当前触摸的Dom节点
184 */
185 getTouchDom = (event) => {
186 let currentLocation = event.changedTouches[0];
187 let realTarget = document.elementFromPoint(currentLocation.clientX, currentLocation.clientY);
188 return realTarget;
189 }
190
191 /**
192 * 开始调整交换行的事件
193 */
194 onTouchStart = (e) => {
195 e.stopPropagation()
196 let {onDragRowStart} = this.props;
197 let event = Event.getEvent(e) ,
198 _target = Event.getTarget(event),
199 target = _target.parentNode;
200
201 if (target.tagName === 'TR') {
202
203 this.currentIndex = target.getAttribute("data-row-key");
204
205 onDragRowStart && onDragRowStart(this.currentIndex);
206 }else{
207
208 this.canBeTouch = false
209 }
210
211 }
212
213 onTouchMove = (e) => {
214
215 if (!this.canBeTouch) return;
216 e.stopPropagation()
217 let event = Event.getEvent(e);
218 event.preventDefault();
219 let touchTarget = this.getTouchDom(event),
220 target = touchTarget.parentNode,
221 targetKey = target.getAttribute("data-row-key");
222 if(!targetKey || targetKey === this.currentIndex)return;
223 if(target.nodeName.toUpperCase() === "TR"){
224 if(this.cacheCurrentIndex !== targetKey){ //模拟 touchenter toucheleave 事件
225 this.cacheCurrentIndex && this.synchronizeTableTr(this.cacheCurrentIndex,null); //去掉虚线
226 this.synchronizeTableTr(targetKey,true); //添加虚线
227 }
228 }
229 }
230
231 /**
232 * 手指移开时触发
233 */
234 onTouchEnd = (e) => {
235
236 if(!this.canBeTouch){
237 this.canBeTouch = true
238 return
239 }
240
241 e.stopPropagation()
242 let {onDragRow} = this.props;
243 let event = Event.getEvent(e),
244 currentKey = this.currentIndex, //拖拽行的key
245 touchTarget = this.getTouchDom(event), //当前触摸的DOM节点
246 target = touchTarget.parentNode, //目标位置的行
247 targetKey = target.getAttribute("data-row-key"); //目标位置的行key
248 if(!targetKey || targetKey === currentKey)return;
249 if(target.nodeName.toUpperCase() === "TR"){
250 this.synchronizeTableTr(currentKey,null);
251 this.synchronizeTableTr(targetKey,null);
252 }
253
254 onDragRow && onDragRow(currentKey,targetKey);
255 }
256
257 /**
258 *同步当前拖拽到阴影
259 * @memberof TableRow
260 */
261 synchronizeTableTrShadow = ()=>{
262 let {contentTable,index} = this.props;
263
264 let cont = contentTable.querySelector('.u-table-scroll table tbody').getElementsByTagName("tr")[index],
265 trs = cont.getBoundingClientRect(),
266 fixed_left_trs = contentTable.querySelector('.u-table-fixed-left table tbody'),
267 fixed_right_trs = contentTable.querySelector('.u-table-fixed-right table tbody');
268 fixed_left_trs = fixed_left_trs && fixed_left_trs.getElementsByTagName("tr")[index].getBoundingClientRect();
269 fixed_right_trs = fixed_right_trs && fixed_right_trs.getElementsByTagName("tr")[index].getBoundingClientRect()
270
271 let div = document.createElement("div");
272 let style = "wdith:"+(trs.width + (fixed_left_trs ? fixed_left_trs.width : 0) + (fixed_right_trs ? fixed_right_trs.width : 0))+"px";
273 style += ";height:"+ trs.height+"px";
274 style += ";classname:"+ cont.className;
275 div.setAttribute("style",style);
276 return div;
277 }
278
279
280 /**
281 * 同步自己,也需要同步当前行的行显示
282 */
283 synchronizeTableTr = (currentIndex,type)=>{
284 if(type){ //同步 this.cacheCurrentIndex
285 this.cacheCurrentIndex = currentIndex;
286 }
287 let {contentTable} = this.props;
288 let _table_trs = contentTable.querySelector('.u-table-scroll table tbody'),
289 _table_fixed_left_trs = contentTable.querySelector('.u-table-fixed-left table tbody'),
290 _table_fixed_right_trs = contentTable.querySelector('.u-table-fixed-right table tbody');
291
292 _table_trs = _table_trs?_table_trs:contentTable.querySelector('.u-table table tbody');
293
294 this.synchronizeTrStyle(_table_trs,currentIndex,type);
295 if(_table_fixed_left_trs){
296 this.synchronizeTrStyle(_table_fixed_left_trs,currentIndex,type);
297 }
298 if(_table_fixed_right_trs){
299 this.synchronizeTrStyle(_table_fixed_right_trs,currentIndex,type);
300 }
301 }
302
303 /**
304 * 设置同步的style
305 */
306 synchronizeTrStyle = (_elementBody,id,type)=>{
307 let {contentTable} = this.props,
308 trs = _elementBody.getElementsByTagName("tr"),
309 currentObj;
310 for (let index = 0; index < trs.length; index++) {
311 const element = trs[index];
312 if(element.getAttribute("data-row-key") == id){
313 currentObj = element;
314 }
315 }
316 if(type){
317 currentObj && currentObj.setAttribute("style","border-bottom:2px solid #02B1FD");
318 }else{
319 currentObj && currentObj.setAttribute("style","");
320 }
321 }
322
323 onDragEnter = (e) => {
324 let event = Event.getEvent(e) ,
325 _target = Event.getTarget(event),target = _target.parentNode;
326 let currentIndex = target.getAttribute("data-row-key");
327 if(!currentIndex || currentIndex === this.currentIndex)return;
328 if(target.nodeName.toUpperCase() === "TR"){
329 this.synchronizeTableTr(currentIndex,true);
330 }
331 }
332
333 onDragLeave = (e) => {
334 let event = Event.getEvent(e) ,
335 _target = Event.getTarget(event),target = _target.parentNode;
336 let currentIndex = target.getAttribute("data-row-key");
337 if(!currentIndex || currentIndex === this.currentIndex)return;
338 if(target.nodeName.toUpperCase() === "TR"){
339 this.synchronizeTableTr(currentIndex,null);
340 }
341 }
342
343 componentDidUpdate(prevProps) {
344 const { rowDraggAble,syncRowHeight } = this.props;
345 if(!this.event){
346 this.event = true;
347 if(rowDraggAble){
348 this.initEvent();
349 }
350 }
351 if(this.props.treeType){
352 this.setRowParentIndex();
353 }
354 if(syncRowHeight){
355 this.setRowHeight()
356 }
357 }
358
359 componentWillUnmount() {
360 const { record, onDestroy, index,rowDraggAble } = this.props;
361 onDestroy(record, index);
362 if (this.unsubscribe) {
363 this.unsubscribe();
364 }
365 if(rowDraggAble){
366 this.removeDragAbleEvent();
367 }
368 }
369
370
371 setRowHeight() {
372 const { setRowHeight , expandedContentHeight=0,fixed,fixedIndex} = this.props
373 if (!setRowHeight || !this.element || fixed) return
374 setRowHeight(this.element.clientHeight + expandedContentHeight, fixedIndex)
375 }
376 setRowParentIndex(){
377 const {index,setRowParentIndex,fixedIndex,rootIndex} = this.props;
378 setRowParentIndex(rootIndex<0?index:rootIndex,fixedIndex);
379
380 }
381
382 onRowClick(event) {
383 // fix: 解决 onRowClick 回调函数中,事件对象属性均为 null 的问题
384 // 异步访问事件属性
385 // 调用 event.persist() 会从事件池中移除该合成函数并允许对该合成事件的引用被保留下来。
386 event.persist();
387 const {
388 record,
389 index,
390 onRowClick,
391 expandable,
392 expandRowByClick,
393 expanded,
394 onExpand,
395 fixedIndex,
396 onRowDoubleClick
397 } = this.props;
398 if (expandable && expandRowByClick) {
399 onExpand(!expanded, record, fixedIndex,event);
400 }
401 if(!onRowDoubleClick){
402 onRowClick(record, fixedIndex, event);
403 return;
404 }
405 this.set((e)=> {
406 onRowClick(record, fixedIndex, event);
407 });
408 }
409
410 onRowDoubleClick(event) {
411 const { record, index, onRowDoubleClick,fixedIndex } = this.props;
412 this.clear();
413 onRowDoubleClick && onRowDoubleClick(record, fixedIndex, event);
414 }
415
416 onMouseEnter(e) {
417 const { onHover, hoverKey,fixedIndex,syncHover,record } = this.props;
418 if(syncHover){
419 this.setState({ hovered: true });
420 }
421 onHover(true, hoverKey,e,fixedIndex,record);
422 }
423
424 onMouseLeave(e) {
425
426 const { onHover, hoverKey ,fixedIndex,syncHover,record} = this.props;
427 if(syncHover){
428 this.setState({ hovered: false });
429 }
430 onHover(false, hoverKey,e,fixedIndex,record);
431 }
432
433 set =(fn)=> {
434 this.clear();
435 this._timeout = window.setTimeout(fn, 300);
436 }
437
438 clear =(event)=> {
439 if (this._timeout) {
440 window.clearTimeout(this._timeout);
441 }
442 }
443
444 bindElement = (el)=> {
445 this.element = el
446 }
447
448 render() {
449 const {
450 clsPrefix, columns, record, height, visible, index,onPaste,
451 expandIconColumnIndex, expandIconAsCell, expanded, useDragHandle,rowDraggAble,
452 expandable, onExpand, needIndentSpaced, indent, indentSize,isHiddenExpandIcon,fixed,bodyDisplayInRow
453 ,expandedIcon,collapsedIcon, hoverKey,lazyStartIndex,lazyEndIndex, expandIconCellWidth
454 } = this.props;
455 let showSum = false;
456 let { className } = this.props;
457 if (this.state.hovered) {
458 className += ` ${clsPrefix}-hover`;
459 }
460 //判断是否为合计行
461 if(className.indexOf('sumrow')>-1){
462 showSum = true;
463 }
464 const cells = [];
465
466 const expandIcon = (
467 <ExpandIcon
468 expandable={expandable}
469 clsPrefix={clsPrefix}
470 onExpand={onExpand}
471 needIndentSpaced={needIndentSpaced}
472 expanded={expanded}
473 record={record}
474 expandedIcon={expandedIcon}
475 collapsedIcon={collapsedIcon}
476 isHiddenExpandIcon={isHiddenExpandIcon}
477 />
478 );
479 let isExpandIconAsCell = expandIconAsCell ? `${clsPrefix}-expand-columns-in-body` : '';
480 var expandIndexInThisTable
481 if(this.props.fixed === 'right'){
482 expandIndexInThisTable = expandIconColumnIndex - this.props.leftColumnsLength-this.props.centerColumnsLength
483 }else {
484 expandIndexInThisTable = expandIconColumnIndex
485 }
486 for (let i = 0; i < columns.length; i++) {
487 if (expandIconAsCell && i === 0) {
488 showSum ? cells.push(<td width={expandIconCellWidth}></td>) :
489 cells.push(
490 <td
491 className={`${clsPrefix}-expand-icon-cell ${isExpandIconAsCell}`}
492 key={`rc-table-expand-icon-cell-${i}`}
493 width={expandIconCellWidth}
494 >
495 {expandIcon}
496 </td>
497 );
498 }
499 // bugfix 设置expandRowByClick,无法显示箭头,去掉 expandRowByClick 判断
500 const isColumnHaveExpandIcon = (expandIconAsCell || showSum)
501 ? false : (i === expandIndexInThisTable);
502 cells.push(
503 <TableCell
504 clsPrefix={clsPrefix}
505 record={record}
506 indentSize={indentSize}
507 indent={indent}
508 index={index}
509 column={columns[i]}
510 key={index + "_"+(columns[i].key || columns[i].dataIndex || i)}
511 fixed= {fixed}
512 showSum={showSum}
513 expandIcon={(isColumnHaveExpandIcon) ? expandIcon : null}
514 bodyDisplayInRow = {bodyDisplayInRow}
515 lazyStartIndex={lazyStartIndex}
516 lazyEndIndex={lazyEndIndex}
517 onPaste={onPaste}
518 col={i}
519 />
520 );
521 }
522 const style = { height ,...record?record.style:undefined};
523 if (!visible) {
524 style.display = 'none';
525 }
526 if(record && record._checked){
527 className += ' selected';
528 }
529 return (
530 <tr
531 draggable={rowDraggAble && !useDragHandle}
532 onClick={this.onRowClick}
533 onDoubleClick={this.onRowDoubleClick}
534 onMouseEnter={this.onMouseEnter}
535 onMouseLeave={this.onMouseLeave}
536 className={`${clsPrefix} ${className} ${clsPrefix}-level-${indent}`}
537 style={style}
538 data-row-key={record && record.key?record.key:hoverKey}
539 // key={hoverKey}
540 ref={this.bindElement}
541 >
542 {cells.length>0?cells:<td style={{width:0,padding:0}}></td>}
543 </tr>
544 );
545 }
546};
547
548TableRow.propTypes = propTypes;
549TableRow.defaultProps = defaultProps;
550
551export default TableRow;