rc-Table

import React, { PropTypes } from ‘react’;
import TableRow from ‘./TableRow’;
import { measureScrollbar, debounce } from ‘./utils’;
import shallowequal from ‘shallowequal’;
import addEventListener from ‘rc-util/lib/Dom/addEventListener’;

const Table = React.createClass({
propTypes: {
data: PropTypes.array,
expandIconAsCell: PropTypes.bool,
defaultExpandAllRows: PropTypes.bool,
expandedRowKeys: PropTypes.array,
defaultExpandedRowKeys: PropTypes.array,
useFixedHeader: PropTypes.bool,
columns: PropTypes.array,
prefixCls: PropTypes.string,
bodyStyle: PropTypes.object,
style: PropTypes.object,
rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
rowClassName: PropTypes.func,
expandedRowClassName: PropTypes.func,
childrenColumnName: PropTypes.string,
onExpand: PropTypes.func,
onExpandedRowsChange: PropTypes.func,
indentSize: PropTypes.number,
onRowClick: PropTypes.func,
columnsPageRange: PropTypes.array,
columnsPageSize: PropTypes.number,
expandIconColumnIndex: PropTypes.number,
showHeader: PropTypes.bool,
title: PropTypes.func,
footer: PropTypes.func,
emptyText: PropTypes.func,
scroll: PropTypes.object,
rowRef: PropTypes.func,
getBodyWrapper: PropTypes.func,
},

getDefaultProps() {
return {
data: [],
useFixedHeader: false,
expandIconAsCell: false,
columns: [],
defaultExpandAllRows: false,
defaultExpandedRowKeys: [],
rowKey: ‘key’,
rowClassName: () => ‘’,
expandedRowClassName: () => ‘’,
onExpand() {},
onExpandedRowsChange() {},
prefixCls: ‘rc-table’,
bodyStyle: {},
style: {},
childrenColumnName: ‘children’,
indentSize: 15,
columnsPageSize: 5,
expandIconColumnIndex: 0,
showHeader: true,
scroll: {},
rowRef: () => null,
getBodyWrapper: body => body,
emptyText: () => ‘No Data’,
};
},

getInitialState() {
const props = this.props;
let expandedRowKeys = [];
let rows = […props.data];
if (props.defaultExpandAllRows) {
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
expandedRowKeys.push(this.getRowKey(row));
rows = rows.concat(row[props.childrenColumnName] || []);
}
} else {
expandedRowKeys = props.expandedRowKeys || props.defaultExpandedRowKeys;
}
return {
expandedRowKeys,
data: props.data,
currentColumnsPage: 0,
currentHoverKey: null,
scrollPosition: ‘left’,
fixedColumnsHeadRowsHeight: [],
fixedColumnsBodyRowsHeight: [],
};
},

componentDidMount() {
this.resetScrollY();
const isAnyColumnsFixed = this.isAnyColumnsFixed();
if (isAnyColumnsFixed) {
this.syncFixedTableRowHeight();
this.resizeEvent = addEventListener(
window, ‘resize’, debounce(this.syncFixedTableRowHeight, 150)
);
}
},

componentWillReceiveProps(nextProps) {
if (‘data’ in nextProps) {
this.setState({
data: nextProps.data,
});
if (!nextProps.data || nextProps.data.length === 0) {
this.resetScrollY();
}
}
if (‘expandedRowKeys’ in nextProps) {
this.setState({
expandedRowKeys: nextProps.expandedRowKeys,
});
}
if (nextProps.columns !== this.props.columns) {
delete this.isAnyColumnsFixedCache;
delete this.isAnyColumnsLeftFixedCache;
delete this.isAnyColumnsRightFixedCache;
}
},

componentDidUpdate() {
this.syncFixedTableRowHeight();
},

componentWillUnmount() {
clearTimeout(this.timer);
if (this.resizeEvent) {
this.resizeEvent.remove();
}
},

onExpandedRowsChange(expandedRowKeys) {
if (!this.props.expandedRowKeys) {
this.setState({ expandedRowKeys });
}
this.props.onExpandedRowsChange(expandedRowKeys);
},

onExpanded(expanded, record, e) {
if (e) {
e.preventDefault();
e.stopPropagation();
}
const info = this.findExpandedRow(record);
if (typeof info !== ‘undefined’ && !expanded) {
this.onRowDestroy(record);
} else if (!info && expanded) {
const expandedRows = this.getExpandedRows().concat();
expandedRows.push(this.getRowKey(record));
this.onExpandedRowsChange(expandedRows);
}
this.props.onExpand(expanded, record);
},

onRowDestroy(record) {
const expandedRows = this.getExpandedRows().concat();
const rowKey = this.getRowKey(record);
let index = -1;
expandedRows.forEach((r, i) => {
if (r === rowKey) {
index = i;
}
});
if (index !== -1) {
expandedRows.splice(index, 1);
}
this.onExpandedRowsChange(expandedRows);
},

getRowKey(record, index) {
const rowKey = this.props.rowKey;
if (typeof rowKey === ‘function’) {
return rowKey(record, index);
}
return typeof record[rowKey] !== ‘undefined’ ? record[rowKey] : index;
},

getExpandedRows() {
return this.props.expandedRowKeys || this.state.expandedRowKeys;
},

getHeader(columns, fixed) {
const { showHeader, expandIconAsCell, prefixCls } = this.props;
let ths = [];
if (expandIconAsCell && fixed !== ‘right’) {
ths.push({
key: ‘rc-table-expandIconAsCell’,
className: ${prefixCls}-expand-icon-th,
title: ‘’,
});
}
ths = ths.concat(columns || this.getCurrentColumns()).map(c => {
if (c.colSpan !== 0) {
return <th key={c.key} colSpan={c.colSpan} className={c.className || ‘’}>{c.title};
}
});
const { fixedColumnsHeadRowsHeight } = this.state;
const trStyle = (fixedColumnsHeadRowsHeight[0] && columns) ? {
height: fixedColumnsHeadRowsHeight[0],
} : null;
return showHeader ? (
<thead className={${prefixCls}-thead}>
{ths}

) : null;
},

getExpandedRow(key, content, visible, className, fixed) {
const prefixCls = this.props.prefixCls;
return (
<tr
key={${key}-extra-row}
style={{ display: visible ? ‘’ : ‘none’ }}
className={${prefixCls}-expanded-row ${className}}
>
{(this.props.expandIconAsCell && fixed !== ‘right’)
?
: null}

{fixed !== ‘right’ ? content : ’ '}


);
},

getRowsByData(data, visible, indent, columns, fixed) {
const props = this.props;
const childrenColumnName = props.childrenColumnName;
const expandedRowRender = props.expandedRowRender;
const expandRowByClick = props.expandRowByClick;
const { fixedColumnsBodyRowsHeight } = this.state;
let rst = [];
const rowClassName = props.rowClassName;
const rowRef = props.rowRef;
const expandedRowClassName = props.expandedRowClassName;
const needIndentSpaced = props.data.some(record => record[childrenColumnName]);
const onRowClick = props.onRowClick;
const isAnyColumnsFixed = this.isAnyColumnsFixed();

const expandIconAsCell = fixed !== 'right' ? props.expandIconAsCell : false;
const expandIconColumnIndex = fixed !== 'right' ? props.expandIconColumnIndex : -1;

for (let i = 0; i < data.length; i++) {
  const record = data[i];
  const key = this.getRowKey(record, i);
  const childrenColumn = record[childrenColumnName];
  const isRowExpanded = this.isRowExpanded(record);
  let expandedRowContent;
  if (expandedRowRender && isRowExpanded) {
    expandedRowContent = expandedRowRender(record, i, indent);
  }
  let className = rowClassName(record, i, indent);
  if (this.state.currentHoverKey === key) {
    className += ` ${props.prefixCls}-row-hover`;
  }

  const onHoverProps = {};
  if (isAnyColumnsFixed) {
    onHoverProps.onHover = this.handleRowHover;
  }

  const style = (fixed && fixedColumnsBodyRowsHeight[i]) ? {
    height: fixedColumnsBodyRowsHeight[i],
  } : {};

  rst.push(
    <TableRow
      indent={indent}
      indentSize={props.indentSize}
      needIndentSpaced={needIndentSpaced}
      className={className}
      record={record}
      expandIconAsCell={expandIconAsCell}
      onDestroy={this.onRowDestroy}
      index={i}
      visible={visible}
      expandRowByClick={expandRowByClick}
      onExpand={this.onExpanded}
      expandable={childrenColumn || expandedRowRender}
      expanded={isRowExpanded}
      prefixCls={`${props.prefixCls}-row`}
      childrenColumnName={childrenColumnName}
      columns={columns || this.getCurrentColumns()}
      expandIconColumnIndex={expandIconColumnIndex}
      onRowClick={onRowClick}
      style={style}
      {...onHoverProps}
      key={key}
      hoverKey={key}
      ref={rowRef(record, i, indent)}
    />
  );

  const subVisible = visible && isRowExpanded;

  if (expandedRowContent && isRowExpanded) {
    rst.push(this.getExpandedRow(
      key, expandedRowContent, subVisible, expandedRowClassName(record, i, indent), fixed
    ));
  }
  if (childrenColumn) {
    rst = rst.concat(this.getRowsByData(
      childrenColumn, subVisible, indent + 1, columns, fixed
    ));
  }
}
return rst;

},

getRows(columns, fixed) {
return this.getRowsByData(this.state.data, true, 0, columns, fixed);
},

getColGroup(columns, fixed) {
let cols = [];
if (this.props.expandIconAsCell && fixed !== ‘right’) {
cols.push(
<col
className={${this.props.prefixCls}-expand-icon-col}
key=“rc-table-expand-icon-col”
/>
);
}
cols = cols.concat((columns || this.props.columns).map(c => {
return <col key={c.key} style={{ width: c.width, minWidth: c.width }} />;
}));
return {cols};
},

getCurrentColumns() {
const { columns, columnsPageRange, columnsPageSize, prefixCls } = this.props;
const { currentColumnsPage } = this.state;
if (!columnsPageRange || columnsPageRange[0] > columnsPageRange[1]) {
return columns;
}
return columns.map((column, i) => {
let newColumn = { …column };
if (i >= columnsPageRange[0] && i <= columnsPageRange[1]) {
const pageIndexStart = columnsPageRange[0] + currentColumnsPage * columnsPageSize;
let pageIndexEnd = columnsPageRange[0] + (currentColumnsPage + 1) * columnsPageSize - 1;
if (pageIndexEnd > columnsPageRange[1]) {
pageIndexEnd = columnsPageRange[1];
}
if (i < pageIndexStart || i > pageIndexEnd) {
newColumn.className = newColumn.className || ‘’;
newColumn.className += ${prefixCls}-column-hidden;
}
newColumn = this.wrapPageColumn(newColumn, (i === pageIndexStart), (i === pageIndexEnd));
}
return newColumn;
});
},

getLeftFixedTable() {
const { columns } = this.props;
const fixedColumns = columns.filter(
column => column.fixed === ‘left’ || column.fixed === true
);
return this.getTable({
columns: fixedColumns,
fixed: ‘left’,
});
},

getRightFixedTable() {
const { columns } = this.props;
const fixedColumns = columns.filter(column => column.fixed === ‘right’);
return this.getTable({
columns: fixedColumns,
fixed: ‘right’,
});
},

getTable(options = {}) {
const { columns, fixed } = options;
const { prefixCls, scroll = {}, getBodyWrapper } = this.props;
let { useFixedHeader } = this.props;
const bodyStyle = { …this.props.bodyStyle };
const headStyle = {};
let tableClassName = ‘’;
if (scroll.x || columns) {
tableClassName = ${prefixCls}-fixed;
bodyStyle.overflowX = bodyStyle.overflowX || ‘auto’;
}
if (scroll.y) {
// maxHeight will make fixed-Table scrolling not working
// so we only set maxHeight to body-Table here
if (fixed) {
bodyStyle.height = bodyStyle.height || scroll.y;
} else {
bodyStyle.maxHeight = bodyStyle.maxHeight || scroll.y;
}
bodyStyle.overflowY = bodyStyle.overflowY || ‘scroll’;
useFixedHeader = true;
// Add negative margin bottom for scroll bar overflow bug
const scrollbarWidth = measureScrollbar();
if (scrollbarWidth > 0) {
(fixed ? bodyStyle : headStyle).marginBottom = -${scrollbarWidth}px;
(fixed ? bodyStyle : headStyle).paddingBottom = ‘0px’;
}
}
const renderTable = (hasHead = true, hasBody = true) => {
const tableStyle = {};
if (!columns && scroll.x) {
// not set width, then use content fixed width
if (scroll.x === true) {
tableStyle.tableLayout = ‘fixed’;
} else {
tableStyle.width = scroll.x;
}
}
const tableBody = hasBody ? getBodyWrapper(
<tbody className={${prefixCls}-tbody}>
{this.getRows(columns, fixed)}

) : null;
return (


{this.getColGroup(columns, fixed)}
{hasHead ? this.getHeader(columns, fixed) : null}
{tableBody}

);
};
let headTable;
if (useFixedHeader) {
headTable = (
<div
className={ ${prefixCls}-header}
ref={columns ? null : ‘headTable’}
style={headStyle}
onMouseOver={this.detectScrollTarget}
onTouchStart={this.detectScrollTarget}
onScroll={this.handleBodyScroll}
>
{renderTable(true, false)}

);
}
let BodyTable = (
<div
className={ ${prefixCls}-body}
style={bodyStyle}
ref=“bodyTable”
onMouseOver={this.detectScrollTarget}
onTouchStart={this.detectScrollTarget}
onScroll={this.handleBodyScroll}
>
{renderTable(!useFixedHeader)}

);
if (columns && columns.length) {
let refName;
if (columns[0].fixed === ‘left’ || columns[0].fixed === true) {
refName = ‘fixedColumnsBodyLeft’;
} else if (columns[0].fixed === ‘right’) {
refName = ‘fixedColumnsBodyRight’;
}
delete bodyStyle.overflowX;
delete bodyStyle.overflowY;
BodyTable = (
<div
className={ ${prefixCls}-body-outer}
style={{ …bodyStyle }}
>
<div
className={ ${prefixCls}-body-inner}
ref={refName}
onMouseOver={this.detectScrollTarget}
onTouchStart={this.detectScrollTarget}
onScroll={this.handleBodyScroll}
>
{renderTable(!useFixedHeader)}


);
}
return {headTable}{BodyTable};
},

getTitle() {
const { title, prefixCls } = this.props;
return title ? (
<div className={${prefixCls}-title}>
{title(this.state.data)}

) : null;
},

getFooter() {
const { footer, prefixCls } = this.props;
return footer ? (
<div className={${prefixCls}-footer}>
{footer(this.state.data)}

) : null;
},

getEmptyText() {
const { emptyText, prefixCls, data } = this.props;
return !data.length ? (
<div className={${prefixCls}-placeholder}>
{emptyText()}

) : null;
},

getMaxColumnsPage() {
const { columnsPageRange, columnsPageSize } = this.props;
return Math.ceil((columnsPageRange[1] - columnsPageRange[0] + 1) / columnsPageSize) - 1;
},

goToColumnsPage(currentColumnsPage) {
const maxColumnsPage = this.getMaxColumnsPage();
let page = currentColumnsPage;
if (page < 0) {
page = 0;
}
if (page > maxColumnsPage) {
page = maxColumnsPage;
}
this.setState({
currentColumnsPage: page,
});
},

syncFixedTableRowHeight() {
const { prefixCls } = this.props;
const headRows = this.refs.headTable ? this.refs.headTable.querySelectorAll(tr) : [];
const bodyRows = this.refs.bodyTable.querySelectorAll(.${prefixCls}-row) || [];
const fixedColumnsHeadRowsHeight = [].map.call(
headRows, row => row.getBoundingClientRect().height || ‘auto’
);
const fixedColumnsBodyRowsHeight = [].map.call(
bodyRows, row => row.getBoundingClientRect().height || ‘auto’
);
if (shallowequal(this.state.fixedColumnsHeadRowsHeight, fixedColumnsHeadRowsHeight) &&
shallowequal(this.state.fixedColumnsBodyRowsHeight, fixedColumnsBodyRowsHeight)) {
return;
}
this.timer = setTimeout(() => {
this.setState({
fixedColumnsHeadRowsHeight,
fixedColumnsBodyRowsHeight,
});
});
},

resetScrollY() {
if (this.refs.headTable) {
this.refs.headTable.scrollLeft = 0;
}
if (this.refs.bodyTable) {
this.refs.bodyTable.scrollLeft = 0;
}
},

prevColumnsPage() {
this.goToColumnsPage(this.state.currentColumnsPage - 1);
},

nextColumnsPage() {
this.goToColumnsPage(this.state.currentColumnsPage + 1);
},

wrapPageColumn(column, hasPrev, hasNext) {
const { prefixCls } = this.props;
const { currentColumnsPage } = this.state;
const maxColumnsPage = this.getMaxColumnsPage();
let prevHandlerCls = ${prefixCls}-prev-columns-page;
if (currentColumnsPage === 0) {
prevHandlerCls += ${prefixCls}-prev-columns-page-disabled;
}
const prevHandler = ;
let nextHandlerCls = ${prefixCls}-next-columns-page;
if (currentColumnsPage === maxColumnsPage) {
nextHandlerCls += ${prefixCls}-next-columns-page-disabled;
}
const nextHandler = ;
if (hasPrev) {
column.title = {prevHandler}{column.title};
column.className = ${column.className || ''} ${prefixCls}-column-has-prev;
}
if (hasNext) {
column.title = {column.title}{nextHandler};
column.className = ${column.className || ''} ${prefixCls}-column-has-next;
}
return column;
},

findExpandedRow(record) {
const rows = this.getExpandedRows().filter(i => i === this.getRowKey(record));
return rows[0];
},

isRowExpanded(record) {
return typeof this.findExpandedRow(record) !== ‘undefined’;
},

detectScrollTarget(e) {
if (this.scrollTarget !== e.currentTarget) {
this.scrollTarget = e.currentTarget;
}
},

isAnyColumnsFixed() {
if (‘isAnyColumnsFixedCache’ in this) {
return this.isAnyColumnsFixedCache;
}
this.isAnyColumnsFixedCache = this.getCurrentColumns().some(column => !!column.fixed);
return this.isAnyColumnsFixedCache;
},

isAnyColumnsLeftFixed() {
if (‘isAnyColumnsLeftFixedCache’ in this) {
return this.isAnyColumnsLeftFixedCache;
}
this.isAnyColumnsLeftFixedCache = this.getCurrentColumns().some(
column => column.fixed === ‘left’ || column.fixed === true
);
return this.isAnyColumnsLeftFixedCache;
},

isAnyColumnsRightFixed() {
if (‘isAnyColumnsRightFixedCache’ in this) {
return this.isAnyColumnsRightFixedCache;
}
this.isAnyColumnsRightFixedCache = this.getCurrentColumns().some(
column => column.fixed === ‘right’
);
return this.isAnyColumnsRightFixedCache;
},

handleBodyScroll(e) {
// Prevent scrollTop setter trigger onScroll event
// http://stackoverflow.com/q/1386696
if (e.target !== this.scrollTarget) {
return;
}
const { scroll = {} } = this.props;
const { headTable, bodyTable, fixedColumnsBodyLeft, fixedColumnsBodyRight } = this.refs;
if (scroll.x) {
if (e.target === bodyTable && headTable) {
headTable.scrollLeft = e.target.scrollLeft;
} else if (e.target === headTable && bodyTable) {
bodyTable.scrollLeft = e.target.scrollLeft;
}
if (e.target.scrollLeft === 0) {
this.setState({ scrollPosition: ‘left’ });
} else if (e.target.scrollLeft + 1 >=
e.target.children[0].getBoundingClientRect().width -
e.target.getBoundingClientRect().width) {
this.setState({ scrollPosition: ‘right’ });
} else if (this.state.scrollPosition !== ‘middle’) {
this.setState({ scrollPosition: ‘middle’ });
}
}
if (scroll.y) {
if (fixedColumnsBodyLeft && e.target !== fixedColumnsBodyLeft) {
fixedColumnsBodyLeft.scrollTop = e.target.scrollTop;
}
if (fixedColumnsBodyRight && e.target !== fixedColumnsBodyRight) {
fixedColumnsBodyRight.scrollTop = e.target.scrollTop;
}
if (bodyTable && e.target !== bodyTable) {
bodyTable.scrollTop = e.target.scrollTop;
}
}
},

handleRowHover(isHover, key) {
this.setState({
currentHoverKey: isHover ? key : null,
});
},
render() {
const props = this.props;
const prefixCls = props.prefixCls;
let className = props.prefixCls;
if (props.className) {
className += ${props.className};
}
if (props.columnsPageRange) {
className += ${prefixCls}-columns-paging;
}
if (props.useFixedHeader || (props.scroll && props.scroll.y)) {
className += ${prefixCls}-fixed-header;
}
className += ${prefixCls}-scroll-position-${this.state.scrollPosition};
const isTableScroll = this.isAnyColumnsFixed() || props.scroll.x || props.scroll.y;
return (


{this.getTitle()}
<div className={ ${prefixCls}-content}>
{this.isAnyColumnsLeftFixed() &&
<div className={ ${prefixCls}-fixed-left}>
{this.getLeftFixedTable()}
}
<div className={isTableScroll ? ${prefixCls}-scroll : ‘’}>
{this.getTable()}
{this.getEmptyText()}
{this.getFooter()}

{this.isAnyColumnsRightFixed() &&
<div className={ ${prefixCls}-fixed-right}>
{this.getRightFixedTable()}
}


);
},
});

export default Table;

TableRow

for (let i = 0; i < columns.length; i++) {
if (expandIconAsCell && i === 0) {
cells.push(
<td
className={${prefixCls}-expand-icon-cell}
key=“rc-table-expand-icon-cell”

        <ExpandIcon
              expandable={expandable}
              prefixCls={prefixCls}
              onExpand={onExpand}
              needIndentSpaced={needIndentSpaced}
              expanded={expanded}
              record={record}
        />
      </td>
    );
  }
  const isColumnHaveExpandIcon = (expandIconAsCell || expandRowByClick)
    ? false : (i === expandIconColumnIndex);
  cells.push(
    <TableCell
      prefixCls={prefixCls}
      record={record}
      indentSize={indentSize}
      indent={indent}
      index={index}
      expandable={expandable}
      onExpand={onExpand}
      needIndentSpaced={needIndentSpaced}
      expanded={expanded}
      isColumnHaveExpandIcon={isColumnHaveExpandIcon}
      column={columns[i]}
      key={columns[i].key}
    />
  );
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值