diff --git a/components/vc-table/assets/animation.less b/components/vc-table/assets/animation.less
new file mode 100644
index 000000000..a6d4e9281
--- /dev/null
+++ b/components/vc-table/assets/animation.less
@@ -0,0 +1,59 @@
+
+.move-enter, .move-appear {
+ opacity: 0;
+ animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
+ animation-duration: 2.5s;
+ animation-fill-mode: both;
+ animation-play-state: paused;
+}
+
+.move-leave {
+ animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
+ animation-duration: .5s;
+ animation-fill-mode: both;
+ animation-play-state: paused;
+}
+
+.move-enter.move-enter-active, .move-appear.move-enter-active {
+ animation-name: moveLeftIn;
+ animation-play-state: running;
+}
+
+.move-leave.move-leave-active {
+ animation-name: moveRightOut;
+ animation-play-state: running;
+}
+
+@keyframes moveLeftIn {
+ 0% {
+ transform-origin: 0 0;
+ transform: translateX(30px);
+ opacity: 0;
+ background: #fff6de;
+ }
+ 20% {
+ transform-origin: 0 0;
+ transform: translateX(0);
+ opacity: 1;
+ }
+ 80%{
+ background: #fff6de;
+ }
+ 100%{
+ background: transparent;
+ opacity: 1;
+ }
+}
+
+@keyframes moveRightOut {
+ 0% {
+ transform-origin: 0 0;
+ transform: translateX(0);
+ opacity: 1;
+ }
+ 100% {
+ transform-origin: 0 0;
+ transform: translateX(-30px);
+ opacity: 0;
+ }
+}
diff --git a/components/vc-table/assets/bordered.less b/components/vc-table/assets/bordered.less
new file mode 100644
index 000000000..f3ad2e84a
--- /dev/null
+++ b/components/vc-table/assets/bordered.less
@@ -0,0 +1,11 @@
+@tablePrefixCls: rc-table;
+@table-border-color: #e9e9e9;
+
+.@{tablePrefixCls}.bordered {
+ table {
+ border-collapse: collapse;
+ }
+ th, td {
+ border: 1px solid @table-border-color;
+ }
+}
diff --git a/components/vc-table/assets/index.less b/components/vc-table/assets/index.less
new file mode 100644
index 000000000..173d5f1eb
--- /dev/null
+++ b/components/vc-table/assets/index.less
@@ -0,0 +1,225 @@
+@tablePrefixCls: rc-table;
+@text-color : #666;
+@font-size-base : 12px;
+@line-height: 1.5;
+@table-border-color: #e9e9e9;
+@table-head-background-color: #f7f7f7;
+@vertical-padding: 16px;
+@horizontal-padding: 8px;
+
+.@{tablePrefixCls} {
+ font-size: @font-size-base;
+ color: @text-color;
+ transition: opacity 0.3s ease;
+ position: relative;
+ line-height: @line-height;
+ overflow: hidden;
+
+ .@{tablePrefixCls}-scroll {
+ overflow: auto;
+ table {
+ width: auto;
+ min-width: 100%;
+ }
+ }
+
+ .@{tablePrefixCls}-header {
+ overflow: hidden;
+ background: @table-head-background-color;
+ }
+
+ &-fixed-header &-body {
+ background: #fff;
+ position: relative;
+ }
+
+ &-fixed-header &-body-inner {
+ height: 100%;
+ overflow: scroll;
+ }
+
+ &-fixed-header &-scroll &-header {
+ overflow-x: scroll;
+ padding-bottom: 20px;
+ margin-bottom: -20px;
+ overflow-y: scroll;
+ box-sizing: border-box;
+ }
+
+ .@{tablePrefixCls}-title {
+ padding: @vertical-padding @horizontal-padding;
+ border-top: 1px solid @table-border-color;
+ }
+
+ .@{tablePrefixCls}-content {
+ position: relative;
+ }
+
+ .@{tablePrefixCls}-footer {
+ padding: @vertical-padding @horizontal-padding;
+ border-bottom: 1px solid @table-border-color;
+ }
+
+ .@{tablePrefixCls}-placeholder {
+ padding: 16px 8px;
+ background: #fff;
+ border-bottom: 1px solid @table-border-color;
+ text-align: center;
+ position: relative;
+ &-fixed-columns {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ background: transparent;
+ pointer-events: none;
+ }
+ }
+
+ table {
+ width: 100%;
+ border-collapse: separate;
+ text-align: left;
+ }
+
+ th {
+ background: @table-head-background-color;
+ font-weight: bold;
+ transition: background .3s ease;
+ }
+
+ td {
+ border-bottom: 1px solid @table-border-color;
+ &:empty:after {
+ content: '.'; // empty cell placeholder
+ visibility: hidden;
+ }
+ }
+
+ tr {
+ transition: all .3s ease;
+ &:hover {
+ background: #eaf8fe;
+ }
+ &.@{tablePrefixCls}-row-hover {
+ background: #eaf8fe;
+ }
+ }
+
+ th, td {
+ padding: @vertical-padding @horizontal-padding;
+ white-space: nowrap;
+ }
+}
+
+.@{tablePrefixCls} {
+ &-expand-icon-col {
+ width: 34px;
+ }
+ &-row, &-expanded-row {
+ &-expand-icon {
+ cursor: pointer;
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ text-align: center;
+ line-height: 16px;
+ border: 1px solid @table-border-color;
+ user-select: none;
+ background: #fff;
+ }
+ &-spaced {
+ visibility: hidden;
+ }
+ &-spaced:after {
+ content: '.'
+ }
+
+ &-expanded:after {
+ content: '-'
+ }
+
+ &-collapsed:after {
+ content: '+'
+ }
+ }
+ tr&-expanded-row {
+ background: #f7f7f7;
+ &:hover {
+ background: #f7f7f7;
+ }
+ }
+ &-column-hidden {
+ display: none;
+ }
+ &-prev-columns-page,
+ &-next-columns-page {
+ cursor: pointer;
+ color: #666;
+ z-index: 1;
+ &:hover {
+ color: #2db7f5;
+ }
+ &-disabled {
+ cursor: not-allowed;
+ color: #999;
+ &:hover {
+ color: #999;
+ }
+ }
+ }
+ &-prev-columns-page {
+ margin-right: 8px;
+ &:before {
+ content: '<';
+ }
+ }
+ &-next-columns-page {
+ float: right;
+ &:before {
+ content: '>';
+ }
+ }
+
+ &-fixed-left,
+ &-fixed-right {
+ position: absolute;
+ top: 0;
+ overflow: hidden;
+ table {
+ width: auto;
+ background: #fff;
+ }
+ }
+
+ &-fixed-left {
+ left: 0;
+ box-shadow: 4px 0 4px rgba(100, 100, 100, 0.1);
+ & .@{tablePrefixCls}-body-inner {
+ margin-right: -20px;
+ padding-right: 20px;
+ }
+ .@{tablePrefixCls}-fixed-header & .@{tablePrefixCls}-body-inner {
+ padding-right: 0;
+ }
+ }
+
+ &-fixed-right {
+ right: 0;
+ box-shadow: -4px 0 4px rgba(100, 100, 100, 0.1);
+
+ // hide expand row content in right fixed Table
+ // https://github.com/ant-design/ant-design/issues/1898
+ .@{tablePrefixCls}-expanded-row {
+ color: transparent;
+ pointer-events: none;
+ }
+ }
+
+ &&-scroll-position-left &-fixed-left {
+ box-shadow: none;
+ }
+
+ &&-scroll-position-right &-fixed-right {
+ box-shadow: none;
+ }
+}
diff --git a/components/vc-table/demo/animation.js b/components/vc-table/demo/animation.js
new file mode 100644
index 000000000..6f379dcdc
--- /dev/null
+++ b/components/vc-table/demo/animation.js
@@ -0,0 +1,70 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import Animate from 'rc-animate';
+import 'rc-table/assets/index.less';
+import 'rc-table/assets/animation.less';
+
+const AnimateBody = (props) =>
+ ;
+
+class Demo extends React.Component {
+ constructor(props) {
+ super(props);
+ this.columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
+ { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
+ {
+ title: 'Operations', dataIndex: '', key: 'd', render: (text, record) =>
+ this.onDelete(record.key, e)} href="#">Delete,
+ },
+ ];
+ this.state = {
+ data: [
+ { a: '123', key: '1' },
+ { a: 'cdd', b: 'edd', key: '2' },
+ { a: '1333', c: 'eee', key: '3' },
+ ],
+ };
+ }
+
+ onDelete(key, e) {
+ console.log('Delete', key);
+ e.preventDefault();
+ const data = this.state.data.filter(item => item.key !== key);
+ this.setState({ data });
+ }
+
+ onAdd() {
+ const data = [...this.state.data];
+ data.push({
+ a: 'new data',
+ b: 'new data',
+ c: 'new data',
+ key: Date.now(),
+ });
+ this.setState({ data });
+ }
+
+ render() {
+ return (
+
+
Table row with animation
+
+
+
+ );
+ }
+}
+ReactDOM.render(
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/childrenIndent.js b/components/vc-table/demo/childrenIndent.js
new file mode 100644
index 000000000..4b226a887
--- /dev/null
+++ b/components/vc-table/demo/childrenIndent.js
@@ -0,0 +1,87 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [{
+ title: 'Name',
+ dataIndex: 'name',
+ key: 'name',
+ width: 400,
+}, {
+ title: 'Age',
+ dataIndex: 'age',
+ key: 'age',
+ width: 100,
+}, {
+ title: 'Address',
+ dataIndex: 'address',
+ key: 'address',
+ width: 200,
+}, {
+ title: 'Operations',
+ dataIndex: 'operation',
+ key: 'x',
+ width: 150,
+}];
+
+const data = [{
+ key: 1,
+ name: 'a',
+ age: 32,
+ address: 'I am a',
+ children: [{
+ key: 11,
+ name: 'aa',
+ age: 33,
+ address: 'I am aa',
+ }, {
+ key: 12,
+ name: 'ab',
+ age: 33,
+ address: 'I am ab',
+ children: [{
+ key: 121,
+ name: 'aba',
+ age: 33,
+ address: 'I am aba',
+ }],
+ }, {
+ key: 13,
+ name: 'ac',
+ age: 33,
+ address: 'I am ac',
+ children: [{
+ key: 131,
+ name: 'aca',
+ age: 33,
+ address: 'I am aca',
+ children: [{
+ key: 1311,
+ name: 'acaa',
+ age: 33,
+ address: 'I am acaa',
+ }, {
+ key: 1312,
+ name: 'acab',
+ age: 33,
+ address: 'I am acab',
+ }],
+ }],
+ }],
+}, {
+ key: 2,
+ name: 'b',
+ age: 32,
+ address: 'I am b',
+}];
+
+function onExpand(expanded, record) {
+ console.log('onExpand', expanded, record);
+}
+
+ReactDOM.render(
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/className.js b/components/vc-table/demo/className.js
new file mode 100644
index 000000000..77f631174
--- /dev/null
+++ b/components/vc-table/demo/className.js
@@ -0,0 +1,45 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a',
+ className: 'a',
+ key: 'a', width: 100 },
+ { id: '123', title: 'title2', dataIndex: 'b',
+ className: 'b',
+ key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c',
+ className: 'c',
+ key: 'c', width: 200 },
+ {
+ title: 'Operations', dataIndex: '',
+ className: 'd',
+ key: 'd', render() {
+ return Operations;
+ },
+ },
+];
+
+const data = [
+ { a: '123', key: '1' },
+ { a: 'cdd', b: 'edd', key: '2' },
+ { a: '1333', c: 'eee', d: 2, key: '3' },
+];
+
+ReactDOM.render(
+
+
rowClassName and className
+
`row-${i}`}
+ expandedRowRender={record => extra: {record.a}
}
+ expandedRowClassName={(record, i) => `ex-row-${i}`}
+ data={data}
+ className="table"
+ />
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/colspan-rowspan.js b/components/vc-table/demo/colspan-rowspan.js
new file mode 100644
index 000000000..c0e9b9187
--- /dev/null
+++ b/components/vc-table/demo/colspan-rowspan.js
@@ -0,0 +1,107 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: '手机号', dataIndex: 'a', colSpan: 2, width: 100, key: 'a', render(o, row, index) {
+ const obj = {
+ children: o,
+ props: {},
+ };
+ // 设置第一行为链接
+ if (index === 0) {
+ obj.children = {o};
+ }
+ // 第5行合并两列
+ if (index === 4) {
+ obj.props.colSpan = 2;
+ }
+
+ if (index === 5) {
+ obj.props.colSpan = 6;
+ }
+ return obj;
+ } },
+ { title: '电话', dataIndex: 'b', colSpan: 0, width: 100, key: 'b', render(o, row, index) {
+ const obj = {
+ children: o,
+ props: {},
+ };
+ // 列合并掉的表格设置colSpan=0,不会去渲染
+ if (index === 4 || index === 5) {
+ obj.props.colSpan = 0;
+ }
+ return obj;
+ } },
+ { title: 'Name', dataIndex: 'c', width: 100, key: 'c', render(o, row, index) {
+ const obj = {
+ children: o,
+ props: {},
+ };
+
+ if (index === 5) {
+ obj.props.colSpan = 0;
+ }
+ return obj;
+ } },
+ { title: 'Address', dataIndex: 'd', width: 200, key: 'd', render(o, row, index) {
+ const obj = {
+ children: o,
+ props: {},
+ };
+ if (index === 0) {
+ obj.props.rowSpan = 2;
+ }
+ if (index === 1 || index === 5) {
+ obj.props.rowSpan = 0;
+ }
+
+ return obj;
+ } },
+ { title: 'Gender', dataIndex: 'e', width: 200, key: 'e', render(o, row, index) {
+ const obj = {
+ children: o,
+ props: {},
+ };
+ if (index === 5) {
+ obj.props.colSpan = 0;
+ }
+ return obj;
+ } },
+ {
+ title: 'Operations', dataIndex: '', key: 'f',
+ render(o, row, index) {
+ if (index === 5) {
+ return {
+ props: {
+ colSpan: 0,
+ },
+ };
+ }
+ return Operations;
+ },
+ },
+];
+
+const data = [
+ { a: '13812340987', b: '0571-12345678', c: '张三', d: '文一西路', e: 'Male', key: '1' },
+ { a: '13812340986', b: '0571-98787658', c: '张夫人', d: '文一西路', e: 'Female', key: '2' },
+ { a: '13812988888', b: '0571-099877', c: '李四', d: '文二西路', e: 'Male', key: '3' },
+ { a: '1381200008888', b: '0571-099877', c: '王五', d: '文二西路', e: 'Male', key: '4' },
+ { a: '0571-88888110', c: '李警官', d: '武林门', e: 'Male', key: '5' },
+ { a: '资料统计完毕于xxxx年xxx月xxx日', key: '6' },
+];
+
+ReactDOM.render(
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/column-resize.js b/components/vc-table/demo/column-resize.js
new file mode 100644
index 000000000..7a56e580d
--- /dev/null
+++ b/components/vc-table/demo/column-resize.js
@@ -0,0 +1,84 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import PropTypes from 'prop-types';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+import { Resizable } from 'react-resizable';
+import 'react-resizable/css/styles.css';
+
+const ResizeableTitle = (props) => {
+ const { onResize, width, ...restProps } = props;
+
+ if (!width) {
+ return | ;
+ }
+
+ return (
+
+ |
+
+ );
+};
+
+ResizeableTitle.propTypes = {
+ onResize: PropTypes.func.isRequired,
+ width: PropTypes.number,
+};
+
+class Demo extends React.Component {
+ state = {
+ columns: [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
+ { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
+ {
+ title: 'Operations', dataIndex: '', key: 'd', render() {
+ return Operations;
+ },
+ },
+ ],
+ }
+
+ components = {
+ header: {
+ cell: ResizeableTitle,
+ },
+ }
+
+ data = [
+ { a: '123', key: '1' },
+ { a: 'cdd', b: 'edd', key: '2' },
+ { a: '1333', c: 'eee', d: 2, key: '3' },
+ ]
+
+ handleResize = index => (e, { size }) => {
+ this.setState(({ columns }) => {
+ const nextColumns = [...columns];
+ nextColumns[index] = {
+ ...nextColumns[index],
+ width: size.width,
+ };
+ return { columns: nextColumns };
+ });
+ }
+
+ render() {
+ const columns = this.state.columns.map((col, index) => ({
+ ...col,
+ onHeaderCell: (column) => ({
+ width: column.width,
+ onResize: this.handleResize(index),
+ }),
+ }));
+
+ return (
+
+
Integrate with react-resizable
+
+
+ );
+ }
+}
+
+ReactDOM.render(, document.getElementById('__react-content'));
diff --git a/components/vc-table/demo/dropdown.js b/components/vc-table/demo/dropdown.js
new file mode 100644
index 000000000..38c2f5e2c
--- /dev/null
+++ b/components/vc-table/demo/dropdown.js
@@ -0,0 +1,111 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import Menu, { Item, Divider } from 'rc-menu';
+import DropDown from 'rc-dropdown';
+import 'rc-table/assets/index.less';
+import 'rc-dropdown/assets/index.css';
+import 'rc-menu/assets/index.css';
+
+const data = [];
+for (let i = 0; i < 10; i++) {
+ data.push({
+ key: i,
+ a: `a${i}`,
+ b: `b${i}`,
+ c: `c${i}`,
+ });
+}
+
+class Demo extends React.Component {
+ state = {
+ visible: false,
+ }
+
+ filters = []
+
+ handleVisibleChange = (visible) => {
+ this.setState({ visible });
+ }
+
+ handleSelect = (selected) => {
+ this.filters.push(selected);
+ }
+
+ handleDeselect = (key) => {
+ const index = this.filters.indexOf(key);
+ if (index !== -1) {
+ this.filters.splice(index, 1);
+ }
+ }
+
+ confirmFilter = () => {
+ console.log(this.filters.join(','));
+ this.setState({
+ visible: false,
+ });
+ }
+
+ render() {
+ const menu = (
+
+ );
+
+ const columns = [
+ {
+ title: (
+
+ ), key: 'a', dataIndex: 'a', width: 100,
+ },
+ { title: 'title2', key: 'b', dataIndex: 'b', width: 100 },
+ { title: 'title3', key: 'c', dataIndex: 'c', width: 200 },
+ ];
+
+ return (
+ record.key}
+ />
+ );
+ }
+}
+
+ReactDOM.render(
+
+
use dropdown
+
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/expandedRowRender.js b/components/vc-table/demo/expandedRowRender.js
new file mode 100644
index 000000000..6e04bcc12
--- /dev/null
+++ b/components/vc-table/demo/expandedRowRender.js
@@ -0,0 +1,114 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const tableData = [
+ { key: 0, a: '123' },
+ { key: 1, a: 'cdd', b: 'edd' },
+ { key: 2, a: '1333', c: 'eee', d: 2 },
+];
+
+class Demo extends React.Component {
+ state = {
+ data: tableData,
+ expandedRowKeys: [],
+ expandIconAsCell: true,
+ expandRowByClick: false,
+ }
+
+ onExpand = (expanded, record) => {
+ console.log('onExpand', expanded, record);
+ }
+
+ onExpandedRowsChange = (rows) => {
+ this.setState({
+ expandedRowKeys: rows,
+ });
+ }
+
+ onExpandIconAsCellChange = (e) => {
+ this.setState({
+ expandIconAsCell: e.target.checked,
+ });
+ }
+
+ onExpandRowByClickChange = (e) => {
+ this.setState({
+ expandRowByClick: e.target.checked,
+ });
+ }
+
+ columns = [
+ { title: 'title 1', dataIndex: 'a', key: 'a', width: 100 },
+ { title: 'title 2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title 3', dataIndex: 'c', key: 'c', width: 200 },
+ { title: 'Operation', dataIndex: '', key: 'x', render: this.renderAction },
+ ]
+
+ toggleButton() {
+ if (this.state.expandedRowKeys.length) {
+ const closeAll = () => this.setState({ expandedRowKeys: [] });
+ return ;
+ }
+ const openAll = () => this.setState({ expandedRowKeys: [0, 1, 2] });
+ return ;
+ }
+
+ remove(index) {
+ const data = this.state.data;
+ data.splice(index, 1);
+ this.setState({ data });
+ }
+
+ expandedRowRender(record) {
+ // console.log(record);
+ return extra: {record.a}
;
+ }
+
+ renderAction(o, row, index) {
+ return this.remove(index)}>Delete;
+ }
+
+ render() {
+ const { expandIconAsCell, expandRowByClick, expandedRowKeys, data } = this.state;
+ return (
+
+ {this.toggleButton()}
+
+
+ expandIconAsCell
+
+
+ expandRowByClick
+
+
+ );
+ }
+}
+
+ReactDOM.render(
+
+
expandedRowRender
+
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/fixedColumns-auto-height.js b/components/vc-table/demo/fixedColumns-auto-height.js
new file mode 100644
index 000000000..5a2973333
--- /dev/null
+++ b/components/vc-table/demo/fixedColumns-auto-height.js
@@ -0,0 +1,46 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' },
+ { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' },
+ { title: 'title3', dataIndex: 'c', key: 'c' },
+ { title: 'title4', dataIndex: 'b', key: 'd' },
+ { title: 'title5', dataIndex: 'b', key: 'e' },
+ { title: 'title6', dataIndex: 'b', key: 'f',
+ render: () => 我很高
},
+ { title: 'title7', dataIndex: 'b', key: 'g' },
+ { title: 'title8', dataIndex: 'b', key: 'h' },
+ { title: 'title9', dataIndex: 'b', key: 'i' },
+ { title: 'title10', dataIndex: 'b', key: 'j' },
+ { title: 'title11', dataIndex: 'b', key: 'k' },
+ { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' },
+];
+
+const data = [
+ { a: '123', b: 'xxxxxxxx', d: 3, key: '1', title: 'hello' },
+ { a: 'cdd', b: 'edd12221', d: 3, key: '2', title: 'hello' },
+ { a: '133', c: 'edd12221', d: 2, key: '3', title: 'hello' },
+ { a: '133', c: 'edd12221', d: 2, key: '4', title: 'hello' },
+ { a: '133', c: 'edd12221', d: 2, key: '5', title: 'hello' },
+ { a: '133', c: 'edd12221', d: 2, key: '6', title: 'hello' },
+ { a: '133', c: 'edd12221', d: 2, key: '7', title: 'hello' },
+ { a: '133', c: 'edd12221', d: 2, key: '8', title: 'hello' },
+ { a: '133', c: 'edd12221', d: 2, key: '9', title: 'hello' },
+];
+
+ReactDOM.render(
+
+
Fixed columns
+
record.title}
+ expandIconAsCell
+ scroll={{ x: 1200 }}
+ data={data}
+ />
+
+, document.getElementById('__react-content'));
diff --git a/components/vc-table/demo/fixedColumns.js b/components/vc-table/demo/fixedColumns.js
new file mode 100644
index 000000000..ed05451dc
--- /dev/null
+++ b/components/vc-table/demo/fixedColumns.js
@@ -0,0 +1,45 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' },
+ { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' },
+ { title: 'title3', dataIndex: 'c', key: 'c' },
+ { title: 'title4', dataIndex: 'b', key: 'd' },
+ { title: 'title5', dataIndex: 'b', key: 'e' },
+ { title: 'title6', dataIndex: 'b', key: 'f' },
+ { title: title7
Hello world!
, dataIndex: 'b', key: 'g' },
+ { title: 'title8', dataIndex: 'b', key: 'h' },
+ { title: 'title9', dataIndex: 'b', key: 'i' },
+ { title: 'title10', dataIndex: 'b', key: 'j' },
+ { title: 'title11', dataIndex: 'b', key: 'k' },
+ { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' },
+];
+
+const data = [
+ { a: '123', b: 'xxxxxxxx', d: 3, key: '1' },
+ { a: 'cdd', b: 'edd12221', d: 3, key: '2' },
+ { a: '133', c: 'edd12221', d: 2, key: '3' },
+ { a: '133', c: 'edd12221', d: 2, key: '4' },
+ { a: '133', c: 'edd12221', d: 2, key: '5' },
+ { a: '133', c: 'edd12221', d: 2, key: '6' },
+ { a: '133', c: 'edd12221', d: 2, key: '7' },
+ { a: '133', c: 'edd12221', d: 2, key: '8' },
+ { a: '133', c: 'edd12221', d: 2, key: '9' },
+];
+
+ReactDOM.render(
+
+
Fixed columns
+
record.title}
+ expandIconAsCell
+ scroll={{ x: 1200 }}
+ data={data}
+ />
+
+, document.getElementById('__react-content'));
diff --git a/components/vc-table/demo/fixedColumnsAndHeader.js b/components/vc-table/demo/fixedColumnsAndHeader.js
new file mode 100644
index 000000000..e62776a59
--- /dev/null
+++ b/components/vc-table/demo/fixedColumnsAndHeader.js
@@ -0,0 +1,39 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' },
+ { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 150 },
+ { title: 'title4', dataIndex: 'c', key: 'd', width: 150 },
+ { title: 'title5', dataIndex: 'c', key: 'e', width: 150 },
+ { title: 'title6', dataIndex: 'c', key: 'f', width: 150 },
+ { title: 'title7', dataIndex: 'c', key: 'g', width: 150 },
+ { title: 'title8', dataIndex: 'c', key: 'h', width: 150 },
+ { title: 'title9', dataIndex: 'b', key: 'i', width: 150 },
+ { title: 'title10', dataIndex: 'b', key: 'j', width: 150 },
+ { title: 'title11', dataIndex: 'b', key: 'k', width: 150 },
+ { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' },
+];
+
+const data = [
+ { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '1' },
+ { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '2' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '3' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '4' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '5' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '6' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '7' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '8' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '9' },
+];
+
+ReactDOM.render(
+
+
Fixed columns and header
+
+
+, document.getElementById('__react-content'));
diff --git a/components/vc-table/demo/fixedColumnsAndHeaderSyncRowHeight.js b/components/vc-table/demo/fixedColumnsAndHeaderSyncRowHeight.js
new file mode 100644
index 000000000..932bdfa0f
--- /dev/null
+++ b/components/vc-table/demo/fixedColumnsAndHeaderSyncRowHeight.js
@@ -0,0 +1,39 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' },
+ { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' },
+ { title: 'titletitle3', dataIndex: 'c', key: 'c' },
+ { title: 'title4', dataIndex: 'c', key: 'd', width: 150 },
+ { title: 'title5', dataIndex: 'c', key: 'e', width: 150 },
+ { title: 'title6', dataIndex: 'c', key: 'f', width: 150 },
+ { title: 'title7', dataIndex: 'c', key: 'g', width: 150 },
+ { title: 'title8', dataIndex: 'c', key: 'h', width: 150 },
+ { title: 'title9', dataIndex: 'b', key: 'i', width: 150 },
+ { title: 'title10', dataIndex: 'b', key: 'j', width: 150 },
+ { title: 'title11', dataIndex: 'b', key: 'k', width: 150 },
+ { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' },
+];
+
+const data = [
+ { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '1' },
+ { a: 'aaa', b: 'bbb', c: '内容内容内容内容内容', d: 3, key: '2' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '3' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '4' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '5' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '6' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '7' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '8' },
+ { a: 'aaa', c: '内容内容内容内容内容', d: 2, key: '9' },
+];
+
+ReactDOM.render(
+
+
Fixed columns and header, resize window for test
+
+
+, document.getElementById('__react-content'));
diff --git a/components/vc-table/demo/fixedColumnsWhenResize.js b/components/vc-table/demo/fixedColumnsWhenResize.js
new file mode 100644
index 000000000..a59b2c2c0
--- /dev/null
+++ b/components/vc-table/demo/fixedColumnsWhenResize.js
@@ -0,0 +1,45 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100, fixed: 'left' },
+ { title: 'title2', dataIndex: 'b', key: 'b', width: 100, fixed: 'left' },
+ { title: 'title3', dataIndex: 'c', key: 'c' },
+ { title: 'title4', dataIndex: 'b', key: 'd' },
+ { title: 'title5', dataIndex: 'b', key: 'e' },
+ { title: 'title6', dataIndex: 'b', key: 'f' },
+ { title: title7
Hello world!
, dataIndex: 'b', key: 'g' },
+ { title: 'title8', dataIndex: 'b', key: 'h' },
+ { title: 'title9', dataIndex: 'b', key: 'i' },
+ { title: 'title10', dataIndex: 'b', key: 'j' },
+ { title: 'title11', dataIndex: 'b', key: 'k' },
+ { title: 'title12', dataIndex: 'b', key: 'l', width: 100, fixed: 'right' },
+];
+
+const data = [
+ { a: '123', b: 'xxxxxxxx', d: 3, key: '1' },
+ { a: 'cdd', b: 'edd12221', d: 3, key: '2' },
+ { a: '133', c: 'edd12221', d: 2, key: '3' },
+ { a: '133', c: 'edd12221', d: 2, key: '4' },
+ { a: '133', c: 'edd12221', d: 2, key: '5' },
+ { a: '133', c: 'edd12221', d: 2, key: '6' },
+ { a: '133', c: 'edd12221', d: 2, key: '7' },
+ { a: '133', c: 'edd12221', d: 2, key: '8' },
+ { a: '133', c: 'edd12221', d: 2, key: '9' },
+];
+
+ReactDOM.render(
+
+
See fixed columns when you resize window
+
record.title}
+ expandIconAsCell
+ scroll={{ x: 800 }}
+ data={data}
+ />
+
+, document.getElementById('__react-content'));
diff --git a/components/vc-table/demo/grouping-columns.js b/components/vc-table/demo/grouping-columns.js
new file mode 100644
index 000000000..699a5ce0f
--- /dev/null
+++ b/components/vc-table/demo/grouping-columns.js
@@ -0,0 +1,100 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+import 'rc-table/assets/bordered.less';
+
+const columns = [
+ {
+ title: '姓名',
+ dataIndex: 'name',
+ key: 'name',
+ },
+ {
+ title: '其它',
+ children: [
+ {
+ title: '年龄',
+ dataIndex: 'age',
+ key: 'age',
+ },
+ {
+ title: '住址',
+ children: [
+ {
+ title: '街道',
+ dataIndex: 'street',
+ key: 'street',
+ },
+ {
+ title: '小区',
+ children: [
+ {
+ title: '单元',
+ dataIndex: 'building',
+ key: 'building',
+ },
+ {
+ title: '门牌',
+ dataIndex: 'number',
+ key: 'number',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ title: '公司',
+ children: [
+ {
+ title: '地址',
+ dataIndex: 'companyAddress',
+ key: 'companyAddress',
+ },
+ {
+ title: '名称',
+ dataIndex: 'companyName',
+ key: 'companyName',
+ },
+ ],
+ },
+ {
+ title: '性别',
+ dataIndex: 'gender',
+ key: 'gender',
+ },
+];
+
+
+const data = [{
+ key: '1',
+ name: '胡彦斌',
+ age: 32,
+ street: '拱墅区和睦街道',
+ building: 1,
+ number: 2033,
+ companyAddress: '西湖区湖底公园',
+ companyName: '湖底有限公司',
+ gender: '男',
+}, {
+ key: '2',
+ name: '胡彦祖',
+ age: 42,
+ street: '拱墅区和睦街道',
+ building: 3,
+ number: 2035,
+ companyAddress: '西湖区湖底公园',
+ companyName: '湖底有限公司',
+ gender: '男',
+}];
+
+ReactDOM.render(
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/hide-header.js b/components/vc-table/demo/hide-header.js
new file mode 100644
index 000000000..ca1db1c6a
--- /dev/null
+++ b/components/vc-table/demo/hide-header.js
@@ -0,0 +1,34 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
+ { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
+ {
+ title: 'Operations', dataIndex: '', key: 'd', render() {
+ return Operations;
+ },
+ },
+];
+
+const data = [
+ { a: '123', key: '1' },
+ { a: 'cdd', b: 'edd', key: '2' },
+ { a: '1333', c: 'eee', d: 2, key: '3' },
+];
+
+ReactDOM.render(
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/jsx.js b/components/vc-table/demo/jsx.js
new file mode 100644
index 000000000..d39118b4a
--- /dev/null
+++ b/components/vc-table/demo/jsx.js
@@ -0,0 +1,49 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const { ColumnGroup, Column } = Table;
+
+const data = [
+ { a: '123', key: '1' },
+ { a: 'cdd', b: 'edd', key: '2' },
+ { a: '1333', c: 'eee', d: 2, key: '3' },
+];
+
+ReactDOM.render(
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/key.js b/components/vc-table/demo/key.js
new file mode 100644
index 000000000..d468ba201
--- /dev/null
+++ b/components/vc-table/demo/key.js
@@ -0,0 +1,70 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import PropTypes from 'prop-types';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const CheckBox = ({ id }) => (
+
+);
+
+class Demo extends React.Component {
+ static propTypes = {
+ data: PropTypes.array.isRequired,
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ data: props.data,
+ };
+ }
+
+ remove(index) {
+ const rows = this.state.data;
+ rows.splice(index, 1);
+ this.setState({
+ data: rows,
+ });
+ }
+
+ handleClick = (index) => () => {
+ this.remove(index);
+ }
+
+ checkbox(a) {
+ return ;
+ }
+
+ renderAction = (o, row, index) => {
+ return Delete;
+ }
+
+ render() {
+ const state = this.state;
+ const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100, render: this.checkbox },
+ { title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
+ { title: 'Operations', dataIndex: '', key: 'x', render: this.renderAction },
+ ];
+ return (
+ record.a} />
+ );
+ }
+}
+
+const data = [{ a: '123' }, { a: 'cdd', b: 'edd' }, { a: '1333', c: 'eee', d: 2 }];
+
+ReactDOM.render(
+
+
specify key
+
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/nested.js b/components/vc-table/demo/nested.js
new file mode 100644
index 000000000..860ebb0b5
--- /dev/null
+++ b/components/vc-table/demo/nested.js
@@ -0,0 +1,45 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'First Name', dataIndex: 'names.first', key: 'a', width: 100 },
+ { title: 'Last Name', dataIndex: 'names.last', key: 'b', width: 100 },
+ { title: 'Age', dataIndex: 'age', key: 'c', width: 100 },
+];
+
+const data = [{
+ age: '23',
+ names: {
+ first: 'John',
+ last: 'Doe',
+ },
+ key: '1',
+}, {
+ age: '36',
+ names: {
+ first: 'Terry',
+ last: 'Garner',
+ },
+ key: '2',
+}, {
+ age: '52',
+ names: {
+ first: 'Thomas',
+ last: 'Goodwin',
+ },
+ key: '3',
+}];
+
+ReactDOM.render(
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/no-data.js b/components/vc-table/demo/no-data.js
new file mode 100644
index 000000000..670384753
--- /dev/null
+++ b/components/vc-table/demo/no-data.js
@@ -0,0 +1,26 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
+ { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
+ {
+ title: 'Operations', dataIndex: '', key: 'd', render() {
+ return Operations;
+ },
+ },
+];
+
+const data = [];
+
+ReactDOM.render(
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/react-dnd.js b/components/vc-table/demo/react-dnd.js
new file mode 100644
index 000000000..886b6f0b0
--- /dev/null
+++ b/components/vc-table/demo/react-dnd.js
@@ -0,0 +1,187 @@
+/* eslint-disable no-unused-expressions,new-cap */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { injectGlobal } from 'styled-components';
+import update from 'immutability-helper';
+import { DragDropContext, DragSource, DropTarget } from 'react-dnd';
+import HTML5Backend from 'react-dnd-html5-backend';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+injectGlobal`
+ tr.drop-over-downward td {
+ border-bottom: 2px dashed red;
+ }
+
+ tr.drop-over-upward td {
+ border-top: 2px dashed red;
+ }
+`;
+
+function dragDirection(
+ dragIndex,
+ hoverIndex,
+ initialClientOffset,
+ clientOffset,
+ sourceClientOffset,
+) {
+ const hoverMiddleY = (initialClientOffset.y - sourceClientOffset.y) / 2;
+ const hoverClientY = clientOffset.y - sourceClientOffset.y;
+ if (dragIndex < hoverIndex && hoverClientY > hoverMiddleY) {
+ return 'downward';
+ }
+ if (dragIndex > hoverIndex && hoverClientY < hoverMiddleY) {
+ return 'upward';
+ }
+}
+
+let BodyRow = (props) => {
+ const {
+ isOver,
+ connectDragSource,
+ connectDropTarget,
+ moveRow,
+ dragRow,
+ clientOffset,
+ sourceClientOffset,
+ initialClientOffset,
+ ...restProps,
+ } = props;
+ const style = { cursor: 'move' };
+
+ let className = restProps.className;
+ if (isOver && initialClientOffset) {
+ const direction = dragDirection(
+ dragRow.index,
+ restProps.index,
+ initialClientOffset,
+ clientOffset,
+ sourceClientOffset
+ );
+ if (direction === 'downward') {
+ className += ' drop-over-downward';
+ }
+ if (direction === 'upward') {
+ className += ' drop-over-upward';
+ }
+ }
+
+ return connectDragSource(
+ connectDropTarget(
+
+ )
+ );
+};
+
+const rowSource = {
+ beginDrag(props) {
+ return {
+ index: props.index,
+ };
+ },
+};
+
+const rowTarget = {
+ drop(props, monitor) {
+ const dragIndex = monitor.getItem().index;
+ const hoverIndex = props.index;
+
+ // Don't replace items with themselves
+ if (dragIndex === hoverIndex) {
+ return;
+ }
+
+ // Time to actually perform the action
+ props.moveRow(dragIndex, hoverIndex);
+
+ // Note: we're mutating the monitor item here!
+ // Generally it's better to avoid mutations,
+ // but it's good here for the sake of performance
+ // to avoid expensive index searches.
+ monitor.getItem().index = hoverIndex;
+ },
+};
+
+BodyRow = DropTarget('row', rowTarget, (connect, monitor) => ({
+ connectDropTarget: connect.dropTarget(),
+ isOver: monitor.isOver(),
+ sourceClientOffset: monitor.getSourceClientOffset(),
+}))(
+ DragSource('row', rowSource, (connect, monitor) => ({
+ connectDragSource: connect.dragSource(),
+ dragRow: monitor.getItem(),
+ clientOffset: monitor.getClientOffset(),
+ initialClientOffset: monitor.getInitialClientOffset(),
+ }))(BodyRow)
+);
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
+ { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
+ {
+ title: 'Operations',
+ dataIndex: '',
+ key: 'd',
+ render() {
+ return Operations;
+ },
+ },
+];
+
+class Demo extends React.Component {
+ state = {
+ data: [
+ { a: '123', key: '1' },
+ { a: 'cdd', b: 'edd', key: '2' },
+ { a: '1333', c: 'eee', d: 2, key: '3' },
+ ],
+ }
+
+ components = {
+ body: {
+ row: BodyRow,
+ },
+ }
+
+ moveRow = (dragIndex, hoverIndex) => {
+ const { data } = this.state;
+ const dragRow = data[dragIndex];
+
+ this.setState(
+ update(this.state, {
+ data: {
+ $splice: [[dragIndex, 1], [hoverIndex, 0, dragRow]],
+ },
+ }),
+ );
+ }
+
+ render() {
+ return (
+ ({
+ index,
+ moveRow: this.moveRow,
+ })}
+ />
+ );
+ }
+}
+
+Demo = DragDropContext(HTML5Backend)(Demo);
+
+ReactDOM.render(
+
+
Integrate with react-dnd
+
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/rowAndCellClick.js b/components/vc-table/demo/rowAndCellClick.js
new file mode 100644
index 000000000..e8dd239bc
--- /dev/null
+++ b/components/vc-table/demo/rowAndCellClick.js
@@ -0,0 +1,105 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const onRowClick = (record, index, event) => {
+ console.log(`Click nth(${index}) row of parent, record.name: ${record.name}`);
+ // See https://facebook.github.io/react/docs/events.html for original click event details.
+ if (event.shiftKey) {
+ console.log('Shift + mouse click triggered.');
+ }
+};
+
+const onRowDoubleClick = (record, index) => {
+ console.log(`Double click nth(${index}) row of parent, record.name: ${record.name}`);
+};
+
+const columns = [{
+ title: 'Name',
+ dataIndex: 'name',
+ key: 'name',
+ width: 400,
+}, {
+ title: 'Age',
+ dataIndex: 'age',
+ key: 'age',
+ width: 100,
+ render: (text) => (
+ {text} (Trigger Cell Click)
+ ),
+ onCell: (record) => ({
+ onClick(e) {
+ console.log('Click cell', record, e.target);
+ },
+ }),
+}, {
+ title: 'Address',
+ dataIndex: 'address',
+ key: 'address',
+ width: 200,
+}];
+
+const data = [{
+ key: 1,
+ name: 'a',
+ age: 32,
+ address: 'I am a',
+ children: [{
+ key: 11,
+ name: 'aa',
+ age: 33,
+ address: 'I am aa',
+ }, {
+ key: 12,
+ name: 'ab',
+ age: 33,
+ address: 'I am ab',
+ children: [{
+ key: 121,
+ name: 'aba',
+ age: 33,
+ address: 'I am aba',
+ }],
+ }, {
+ key: 13,
+ name: 'ac',
+ age: 33,
+ address: 'I am ac',
+ children: [{
+ key: 131,
+ name: 'aca',
+ age: 33,
+ address: 'I am aca',
+ children: [{
+ key: 1311,
+ name: 'acaa',
+ age: 33,
+ address: 'I am acaa',
+ }, {
+ key: 1312,
+ name: 'acab',
+ age: 33,
+ address: 'I am acab',
+ }],
+ }],
+ }],
+}, {
+ key: 2,
+ name: 'b',
+ age: 32,
+ address: 'I am b',
+}];
+
+ReactDOM.render(
+ ({
+ onClick: onRowClick.bind(null, record, index),
+ onDoubleClick: onRowDoubleClick.bind(null, record, index),
+ })}
+ />,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/scrollX.js b/components/vc-table/demo/scrollX.js
new file mode 100644
index 000000000..4659bcea2
--- /dev/null
+++ b/components/vc-table/demo/scrollX.js
@@ -0,0 +1,34 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
+ { title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 100 },
+ { title: 'title4', dataIndex: 'b', key: 'd', width: 100 },
+ { title: 'title5', dataIndex: 'b', key: 'e', width: 100 },
+ { title: 'title6', dataIndex: 'b', key: 'f', width: 100 },
+ { title: 'title7', dataIndex: 'b', key: 'g', width: 100 },
+ { title: 'title8', dataIndex: 'b', key: 'h', width: 100 },
+ { title: 'title9', dataIndex: 'b', key: 'i', width: 100 },
+ { title: 'title10', dataIndex: 'b', key: 'j', width: 100 },
+ { title: 'title11', dataIndex: 'b', key: 'k', width: 100 },
+ { title: 'title12', dataIndex: 'b', key: 'l', width: 100 },
+];
+
+const data = [
+ { a: '123', b: 'xxxxxxxx xxxxxxxx', d: 3, key: '1' },
+ { a: 'cdd', b: 'edd12221 edd12221', d: 3, key: '2' },
+ { a: '133', c: 'edd12221 edd12221', d: 2, key: '3' },
+ { a: '133', c: 'edd12221 edd12221', d: 2, key: '4' },
+];
+
+ReactDOM.render(
+
+, document.getElementById('__react-content'));
diff --git a/components/vc-table/demo/scrollXY.js b/components/vc-table/demo/scrollXY.js
new file mode 100644
index 000000000..6da56a772
--- /dev/null
+++ b/components/vc-table/demo/scrollXY.js
@@ -0,0 +1,45 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
+ { title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 100 },
+ { title: 'title4', dataIndex: 'b', key: 'd', width: 100 },
+ { title: 'title5', dataIndex: 'b', key: 'e', width: 100 },
+ { title: 'title6', dataIndex: 'b', key: 'f', width: 100 },
+ { title: 'title7', dataIndex: 'b', key: 'g', width: 100 },
+ { title: 'title8', dataIndex: 'b', key: 'h', width: 100 },
+ { title: 'title9', dataIndex: 'b', key: 'i', width: 100 },
+ { title: 'title10', dataIndex: 'b', key: 'j', width: 100 },
+ { title: 'title11', dataIndex: 'b', key: 'k', width: 100 },
+ { title: 'title12', dataIndex: 'b', key: 'l', width: 100 },
+];
+
+const data = [
+ { a: '123', b: 'xxxxxxxx xxxxxxxx', d: 3, key: '1' },
+ { a: 'cdd', b: 'edd12221 edd12221', d: 3, key: '2' },
+ { a: '133', c: 'edd12221 edd12221', d: 2, key: '3' },
+ { a: '133', c: 'edd12221 edd12221', d: 2, key: '4' },
+ { a: '133', c: 'edd12221 edd12221', d: 2, key: '5' },
+ { a: '133', c: 'edd12221 edd12221', d: 2, key: '6' },
+ { a: '133', c: 'edd12221 edd12221', d: 2, key: '7' },
+ { a: '133', c: 'edd12221 edd12221', d: 2, key: '8' },
+ { a: '133', c: 'edd12221 edd12221', d: 2, key: '9' },
+];
+
+ReactDOM.render(
+
+, document.getElementById('__react-content'));
diff --git a/components/vc-table/demo/scrollY.js b/components/vc-table/demo/scrollY.js
new file mode 100644
index 000000000..ae286c5ed
--- /dev/null
+++ b/components/vc-table/demo/scrollY.js
@@ -0,0 +1,62 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const data = [];
+for (let i = 0; i < 10; i++) {
+ data.push({
+ key: i,
+ a: `a${i}`,
+ b: `b${i}`,
+ c: `c${i}`,
+ });
+}
+
+class Demo extends React.Component {
+ state = {
+ showBody: true,
+ }
+
+ toggleBody = () => {
+ this.setState({
+ showBody: !this.state.showBody,
+ });
+ }
+
+ render() {
+ const columns = [
+ { title: 'title1', key: 'a', dataIndex: 'a', width: 100 },
+ { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', key: 'c', dataIndex: 'c', width: 200 },
+ {
+ title: {this.state.showBody ? '隐藏' : '显示'}体,
+ key: 'x',
+ width: 200,
+ render() {
+ return Operations;
+ },
+ },
+ ];
+ return (
+ record.key}
+ bodyStyle={{
+ display: this.state.showBody ? '' : 'none',
+ }}
+ />
+ );
+ }
+}
+
+ReactDOM.render(
+
+
scroll body table
+
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/simple.js b/components/vc-table/demo/simple.js
new file mode 100644
index 000000000..c07339aaf
--- /dev/null
+++ b/components/vc-table/demo/simple.js
@@ -0,0 +1,30 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
+ { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
+ {
+ title: 'Operations', dataIndex: '', key: 'd', render() {
+ return Operations;
+ },
+ },
+];
+
+const data = [
+ { a: '123', key: '1' },
+ { a: 'cdd', b: 'edd', key: '2' },
+ { a: '1333', c: 'eee', d: 2, key: '3' },
+];
+
+ReactDOM.render(
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/styled-components.js b/components/vc-table/demo/styled-components.js
new file mode 100644
index 000000000..fa453e009
--- /dev/null
+++ b/components/vc-table/demo/styled-components.js
@@ -0,0 +1,45 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import styled from 'styled-components';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
+ { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
+ {
+ title: 'Operations',
+ dataIndex: '',
+ key: 'd',
+ render() {
+ return Operations;
+ },
+ },
+];
+
+const data = [
+ { a: '123', key: '1' },
+ { a: 'cdd', b: 'edd', key: '2' },
+ { a: '1333', c: 'eee', d: 2, key: '3' },
+];
+
+const BodyRow = styled.tr`
+ &:hover {
+ background: palevioletred !important;
+ }
+`;
+
+const components = {
+ body: {
+ row: BodyRow,
+ },
+};
+
+ReactDOM.render(
+
+
Integrate with styled-components
+
+
,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/demo/subTable.js b/components/vc-table/demo/subTable.js
new file mode 100644
index 000000000..459428e36
--- /dev/null
+++ b/components/vc-table/demo/subTable.js
@@ -0,0 +1,63 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const data = [
+ {
+ a: 'a1',
+ },
+ {
+ a: 'a2',
+ b: 'b2',
+ children: [
+ {
+ a: 'a2-1',
+ b: 'b2-1',
+ },
+ {
+ a: 'a2-2',
+ b: 'b2-2',
+ },
+ ],
+ },
+ {
+ a: 'a3',
+ c: 'c3',
+ d: 'd3',
+ },
+];
+
+class Demo extends React.Component {
+ handleClick = (record, e) => {
+ e.preventDefault();
+ console.log(record.a);
+ }
+
+ render() {
+ const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
+ { title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
+ {
+ title: 'Operations', dataIndex: '', key: 'x', render: (text, record) => {
+ return this.handleClick(record, e)}>click {record.a};
+ },
+ },
+ ];
+ return (
+
+
sub table
+
record.a}
+ />
+
+ );
+ }
+}
+
+ReactDOM.render(, document.getElementById('__react-content'));
diff --git a/components/vc-table/demo/title-and-footer.js b/components/vc-table/demo/title-and-footer.js
new file mode 100644
index 000000000..92fb0fc8c
--- /dev/null
+++ b/components/vc-table/demo/title-and-footer.js
@@ -0,0 +1,35 @@
+/* eslint-disable no-console,func-names,react/no-multi-comp */
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Table from 'rc-table';
+import 'rc-table/assets/index.less';
+
+const columns = [
+ { title: 'title1', dataIndex: 'a', key: 'a', width: 100 },
+ { id: '123', title: 'title2', dataIndex: 'b', key: 'b', width: 100 },
+ { title: 'title3', dataIndex: 'c', key: 'c', width: 200 },
+ {
+ title: 'Operations', dataIndex: '', key: 'd', render() {
+ return Operations;
+ },
+ },
+];
+
+const data = [
+ { a: '123', key: '1' },
+ { a: 'cdd', b: 'edd', key: '2' },
+ { a: '1333', c: 'eee', d: 2, key: '3' },
+];
+
+ReactDOM.render(
+
+
title and footer
+
Title: {currentData.length} items
}
+ footer={currentData => Footer: {currentData.length} items
}
+ />
+ ,
+ document.getElementById('__react-content')
+);
diff --git a/components/vc-table/index.js b/components/vc-table/index.js
new file mode 100644
index 000000000..c4a842cdc
--- /dev/null
+++ b/components/vc-table/index.js
@@ -0,0 +1,9 @@
+import Table from './src/Table'
+import Column from './src/Column'
+import ColumnGroup from './src/ColumnGroup'
+
+Table.Column = Column
+Table.ColumnGroup = ColumnGroup
+
+export default Table
+export { Column, ColumnGroup }
diff --git a/components/vc-table/src/BaseTable.jsx b/components/vc-table/src/BaseTable.jsx
new file mode 100644
index 000000000..c2d0811eb
--- /dev/null
+++ b/components/vc-table/src/BaseTable.jsx
@@ -0,0 +1,189 @@
+
+import PropTypes from '../../_util/vue-types'
+import ColGroup from './ColGroup'
+import TableHeader from './TableHeader'
+import TableRow from './TableRow'
+import ExpandableRow from './ExpandableRow'
+import { mergeProps } from '../../_util/props-util'
+
+const BaseTable = {
+ name: 'BaseTable',
+ props: {
+ fixed: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.bool,
+ ]),
+ columns: PropTypes.array.isRequired,
+ tableClassName: PropTypes.string.isRequired,
+ hasHead: PropTypes.bool.isRequired,
+ hasBody: PropTypes.bool.isRequired,
+ store: PropTypes.object.isRequired,
+ expander: PropTypes.object.isRequired,
+ getRowKey: PropTypes.func,
+ isAnyColumnsFixed: PropTypes.bool,
+ },
+ inject: {
+ table: { default: {}},
+ },
+ methods: {
+ handleRowHover (isHover, key) {
+ this.props.store.setState({
+ currentHoverKey: isHover ? key : null,
+ })
+ },
+
+ renderRows (renderData, indent, ancestorKeys = []) {
+ const {
+ columnManager, components,
+ prefixCls,
+ childrenColumnName,
+ rowClassName,
+ // rowRef,
+ $listeners: {
+ rowClick: onRowClick,
+ rowDoubleclick: onRowDoubleClick,
+ rowContextmenu: onRowContextMenu,
+ rowMouseenter: onRowMouseEnter,
+ rowMouseleave: onRowMouseLeave,
+ row: onRow,
+ },
+ } = this.table
+ const { getRowKey, fixed, expander, isAnyColumnsFixed } = this
+
+ const rows = []
+
+ for (let i = 0; i < renderData.length; i++) {
+ const record = renderData[i]
+ const key = getRowKey(record, i)
+ const className = typeof rowClassName === 'string'
+ ? rowClassName
+ : rowClassName(record, i, indent)
+
+ const onHoverProps = {}
+ if (columnManager.isAnyColumnsFixed()) {
+ onHoverProps.hover = this.handleRowHover
+ }
+
+ let leafColumns
+ if (fixed === 'left') {
+ leafColumns = columnManager.leftLeafColumns()
+ } else if (fixed === 'right') {
+ leafColumns = columnManager.rightLeafColumns()
+ } else {
+ leafColumns = columnManager.leafColumns()
+ }
+
+ const rowPrefixCls = `${prefixCls}-row`
+ const expandableRowProps = {
+ props: {
+ ...expander.props,
+ fixed,
+ index: i,
+ prefixCls: rowPrefixCls,
+ record,
+ rowKey: key,
+ needIndentSpaced: expander.needIndentSpaced,
+ },
+ key,
+ on: {
+ rowClick: onRowClick,
+ expandedChange: expander.handleExpandChange,
+ },
+ scopedSlots: {
+ default: (expandableRow) => {
+ const tableRowProps = mergeProps({
+ props: {
+ fixed,
+ indent,
+ record,
+ index: i,
+ prefixCls: rowPrefixCls,
+ childrenColumnName: childrenColumnName,
+ columns: leafColumns,
+ rowKey: key,
+ ancestorKeys,
+ components,
+ isAnyColumnsFixed,
+ },
+ on: {
+ row: onRow,
+ rowDoubleclick: onRowDoubleClick,
+ rowContextmenu: onRowContextMenu,
+ rowMouseenter: onRowMouseEnter,
+ rowMouseleave: onRowMouseLeave,
+ ...onHoverProps,
+ },
+ class: className,
+ ref: `row_${i}_${indent}`,
+ }, expandableRow)
+ return (
+
+ )
+ },
+ },
+ }
+ const row = (
+
+ )
+
+ rows.push(row)
+
+ expander.renderRows(
+ this.renderRows,
+ rows,
+ record,
+ i,
+ indent,
+ fixed,
+ key,
+ ancestorKeys
+ )
+ }
+ return rows
+ },
+ },
+
+ render () {
+ const { components, prefixCls, scroll, data, getBodyWrapper } = this.table
+ const { expander, tableClassName, hasHead, hasBody, fixed, columns } = this
+ const tableStyle = {}
+
+ if (!fixed && scroll.x) {
+ // not set width, then use content fixed width
+ if (scroll.x === true) {
+ tableStyle.tableLayout = 'fixed'
+ } else {
+ tableStyle.width = scroll.x
+ }
+ }
+
+ const Table = hasBody ? components.table : 'table'
+ const BodyWrapper = components.body.wrapper
+
+ let body
+ if (hasBody) {
+ body = (
+
+ {this.renderRows(data, 0)}
+
+ )
+ if (getBodyWrapper) {
+ body = getBodyWrapper(body)
+ }
+ }
+
+ return (
+
+
+ {hasHead && }
+ {body}
+
+ )
+ },
+}
+
+export default BaseTable
diff --git a/components/vc-table/src/BodyTable.jsx b/components/vc-table/src/BodyTable.jsx
new file mode 100644
index 000000000..6cb8d69be
--- /dev/null
+++ b/components/vc-table/src/BodyTable.jsx
@@ -0,0 +1,117 @@
+import PropTypes from '../../_util/vue-types'
+import { measureScrollbar } from './utils'
+import BaseTable from './BaseTable'
+
+export default {
+ name: 'BodyTable',
+ props: {
+ fixed: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.bool,
+ ]),
+ columns: PropTypes.array.isRequired,
+ tableClassName: PropTypes.string.isRequired,
+ handleBodyScroll: PropTypes.func.isRequired,
+ getRowKey: PropTypes.func.isRequired,
+ expander: PropTypes.object.isRequired,
+ isAnyColumnsFixed: PropTypes.bool,
+ },
+ inject: {
+ table: { default: {}},
+ },
+ render () {
+ const { prefixCls, scroll } = this.table
+ const {
+ columns,
+ fixed,
+ tableClassName,
+ getRowKey,
+ handleBodyScroll,
+ expander,
+ isAnyColumnsFixed,
+ } = this
+ let { useFixedHeader } = this.table
+ const bodyStyle = { ...this.table.bodyStyle }
+ const innerBodyStyle = {}
+
+ if (scroll.x || fixed) {
+ bodyStyle.overflowX = bodyStyle.overflowX || 'auto'
+ // Fix weired webkit render bug
+ // https://github.com/ant-design/ant-design/issues/7783
+ bodyStyle.WebkitTransform = 'translate3d (0, 0, 0)'
+ }
+
+ if (scroll.y) {
+ // maxHeight will make fixed-Table scrolling not working
+ // so we only set maxHeight to body-Table here
+ if (fixed) {
+ innerBodyStyle.maxHeight = bodyStyle.maxHeight || scroll.y
+ innerBodyStyle.overflowY = bodyStyle.overflowY || 'scroll'
+ } 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.marginBottom = `-${scrollbarWidth}px`
+ bodyStyle.paddingBottom = '0px'
+ }
+ }
+
+ const baseTable = (
+
+ )
+
+ if (fixed && 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
+ return (
+
+ )
+ }
+ return (
+
+ {baseTable}
+
+ )
+ },
+
+}
+
diff --git a/components/vc-table/src/ColGroup.jsx b/components/vc-table/src/ColGroup.jsx
new file mode 100644
index 000000000..0df1afe27
--- /dev/null
+++ b/components/vc-table/src/ColGroup.jsx
@@ -0,0 +1,53 @@
+import PropTypes from '../../_util/vue-types'
+
+export default {
+ name: 'ColGroup',
+ props: {
+ fixed: PropTypes.string,
+ },
+ inject: {
+ table: { default: {}},
+ },
+ render () {
+ const { fixed, table } = this
+ const { prefixCls, expandIconAsCell } = table
+
+ let cols = []
+
+ if (expandIconAsCell && fixed !== 'right') {
+ cols.push(
+
+ )
+ }
+
+ let leafColumns
+
+ if (fixed === 'left') {
+ leafColumns = table.columnManager.leftLeafColumns()
+ } else if (fixed === 'right') {
+ leafColumns = table.columnManager.rightLeafColumns()
+ } else {
+ leafColumns = table.columnManager.leafColumns()
+ }
+ cols = cols.concat(
+ leafColumns.map(c => {
+ return (
+
+ )
+ })
+ )
+ return (
+
+ {cols}
+
+ )
+ },
+
+}
+
diff --git a/components/vc-table/src/Column.jsx b/components/vc-table/src/Column.jsx
new file mode 100644
index 000000000..8ad12648c
--- /dev/null
+++ b/components/vc-table/src/Column.jsx
@@ -0,0 +1,23 @@
+import PropTypes from '../../_util/vue-types'
+
+export default {
+ name: 'Column',
+ props: {
+ colSpan: PropTypes.number,
+ title: PropTypes.any,
+ dataIndex: PropTypes.string,
+ width: PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.string,
+ ]),
+ fixed: PropTypes.oneOf([
+ true,
+ 'left',
+ 'right',
+ ]),
+ render: PropTypes.func,
+ // onCellClick: PropTypes.func,
+ // onCell: PropTypes.func,
+ // onHeaderCell: PropTypes.func,
+ },
+}
diff --git a/components/vc-table/src/ColumnGroup.jsx b/components/vc-table/src/ColumnGroup.jsx
new file mode 100644
index 000000000..ad329cd15
--- /dev/null
+++ b/components/vc-table/src/ColumnGroup.jsx
@@ -0,0 +1,9 @@
+import PropTypes from '../../_util/vue-types'
+
+export default {
+ name: 'ColumnGroup',
+ props: {
+ title: PropTypes.any,
+ },
+ isTableColumnGroup: true,
+}
diff --git a/components/vc-table/src/ColumnManager.js b/components/vc-table/src/ColumnManager.js
new file mode 100644
index 000000000..a90756c14
--- /dev/null
+++ b/components/vc-table/src/ColumnManager.js
@@ -0,0 +1,150 @@
+export default class ColumnManager {
+ _cached = {}
+
+ constructor (columns, elements) {
+ this.columns = columns || this.normalize(elements)
+ }
+
+ isAnyColumnsFixed () {
+ return this._cache('isAnyColumnsFixed', () => {
+ return this.columns.some(column => !!column.fixed)
+ })
+ }
+
+ isAnyColumnsLeftFixed () {
+ return this._cache('isAnyColumnsLeftFixed', () => {
+ return this.columns.some(
+ column => column.fixed === 'left' || column.fixed === true
+ )
+ })
+ }
+
+ isAnyColumnsRightFixed () {
+ return this._cache('isAnyColumnsRightFixed', () => {
+ return this.columns.some(
+ column => column.fixed === 'right'
+ )
+ })
+ }
+
+ leftColumns () {
+ return this._cache('leftColumns', () => {
+ return this.groupedColumns().filter(
+ column => column.fixed === 'left' || column.fixed === true
+ )
+ })
+ }
+
+ rightColumns () {
+ return this._cache('rightColumns', () => {
+ return this.groupedColumns().filter(
+ column => column.fixed === 'right'
+ )
+ })
+ }
+
+ leafColumns () {
+ return this._cache('leafColumns', () =>
+ this._leafColumns(this.columns)
+ )
+ }
+
+ leftLeafColumns () {
+ return this._cache('leftLeafColumns', () =>
+ this._leafColumns(this.leftColumns())
+ )
+ }
+
+ rightLeafColumns () {
+ return this._cache('rightLeafColumns', () =>
+ this._leafColumns(this.rightColumns())
+ )
+ }
+
+ // add appropriate rowspan and colspan to column
+ groupedColumns () {
+ return this._cache('groupedColumns', () => {
+ const _groupColumns = (columns, currentRow = 0, parentColumn = {}, rows = []) => {
+ // track how many rows we got
+ rows[currentRow] = rows[currentRow] || []
+ const grouped = []
+ const setRowSpan = column => {
+ const rowSpan = rows.length - currentRow
+ if (column &&
+ !column.children && // parent columns are supposed to be one row
+ rowSpan > 1 &&
+ (!column.rowSpan || column.rowSpan < rowSpan)
+ ) {
+ column.rowSpan = rowSpan
+ }
+ }
+ columns.forEach((column, index) => {
+ const newColumn = { ...column }
+ rows[currentRow].push(newColumn)
+ parentColumn.colSpan = parentColumn.colSpan || 0
+ if (newColumn.children && newColumn.children.length > 0) {
+ newColumn.children = _groupColumns(newColumn.children, currentRow + 1, newColumn, rows)
+ parentColumn.colSpan = parentColumn.colSpan + newColumn.colSpan
+ } else {
+ parentColumn.colSpan++
+ }
+ // update rowspan to all same row columns
+ for (let i = 0; i < rows[currentRow].length - 1; ++i) {
+ setRowSpan(rows[currentRow][i])
+ }
+ // last column, update rowspan immediately
+ if (index + 1 === columns.length) {
+ setRowSpan(newColumn)
+ }
+ grouped.push(newColumn)
+ })
+ return grouped
+ }
+ return _groupColumns(this.columns)
+ })
+ }
+
+ normalize (elements) {
+ const columns = []
+ elements.forEach(element => {
+ if (!element.tag) {
+ return
+ }
+ debugger
+ const column = { ...element.props }
+ if (element.key) {
+ column.key = element.key
+ }
+ if (element.type.isTableColumnGroup) {
+ column.children = this.normalize(column.children)
+ }
+ columns.push(column)
+ })
+ return columns
+ }
+
+ reset (columns, elements) {
+ this.columns = columns || this.normalize(elements)
+ this._cached = {}
+ }
+
+ _cache (name, fn) {
+ if (name in this._cached) {
+ return this._cached[name]
+ }
+ this._cached[name] = fn()
+ return this._cached[name]
+ }
+
+ _leafColumns (columns) {
+ const leafColumns = []
+ columns.forEach(column => {
+ if (!column.children) {
+ leafColumns.push(column)
+ } else {
+ leafColumns.push(...this._leafColumns(column.children))
+ }
+ })
+ return leafColumns
+ }
+}
diff --git a/components/vc-table/src/ExpandIcon.jsx b/components/vc-table/src/ExpandIcon.jsx
new file mode 100644
index 000000000..490386d43
--- /dev/null
+++ b/components/vc-table/src/ExpandIcon.jsx
@@ -0,0 +1,34 @@
+import PropTypes from '../../_util/vue-types'
+import BaseMixin from '../../_util/BaseMixin'
+export default {
+ mixins: [BaseMixin],
+ name: 'ExpandIcon',
+ props: {
+ record: PropTypes.object,
+ prefixCls: PropTypes.string,
+ expandable: PropTypes.any,
+ expanded: PropTypes.bool,
+ needIndentSpaced: PropTypes.bool,
+ },
+ methods: {
+ onExpand (e) {
+ this.__emit('expand', this.record, e)
+ },
+ },
+
+ render () {
+ const { expandable, prefixCls, onExpand, needIndentSpaced, expanded } = this
+ if (expandable) {
+ const expandClassName = expanded ? 'expanded' : 'collapsed'
+ return (
+
+ )
+ } else if (needIndentSpaced) {
+ return
+ }
+ return null
+ },
+}
diff --git a/components/vc-table/src/ExpandableRow.js b/components/vc-table/src/ExpandableRow.js
new file mode 100644
index 000000000..895cd886c
--- /dev/null
+++ b/components/vc-table/src/ExpandableRow.js
@@ -0,0 +1,126 @@
+import PropTypes from '../../_util/vue-types'
+import ExpandIcon from './ExpandIcon'
+import BaseMixin from '../../_util/BaseMixin'
+
+const ExpandableRow = {
+ mixins: [BaseMixin],
+ name: 'ExpandableRow',
+ props: {
+ prefixCls: PropTypes.string.isRequired,
+ rowKey: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number,
+ ]).isRequired,
+ fixed: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.bool,
+ ]),
+ record: PropTypes.object.isRequired,
+ indentSize: PropTypes.number,
+ needIndentSpaced: PropTypes.bool.isRequired,
+ expandRowByClick: PropTypes.bool,
+ expanded: PropTypes.bool.isRequired,
+ expandIconAsCell: PropTypes.bool,
+ expandIconColumnIndex: PropTypes.number,
+ childrenColumnName: PropTypes.string,
+ expandedRowRender: PropTypes.func,
+ // onExpandedChange: PropTypes.func.isRequired,
+ // onRowClick: PropTypes.func,
+ // children: PropTypes.func.isRequired,
+ },
+
+ beforeDestroy () {
+ this.handleDestroy()
+ },
+
+ hasExpandIcon (columnIndex) {
+ const { expandRowByClick } = this
+ return !this.expandIconAsCell &&
+ !expandRowByClick &&
+ columnIndex === this.expandIconColumnIndex
+ },
+
+ handleExpandChange (record, event) {
+ const { expanded, rowKey } = this
+ this.__emit('expandedChange', !expanded, record, event, rowKey)
+ },
+
+ handleDestroy () {
+ const { rowKey, record } = this
+ this.__emit('expandedChange', false, record, null, rowKey, true)
+ },
+
+ handleRowClick (record, index, event) {
+ const { expandRowByClick } = this
+ if (expandRowByClick) {
+ this.handleExpandChange(record, event)
+ }
+ this.__emit('rowClick', record, index, event)
+ },
+
+ renderExpandIcon () {
+ const { prefixCls, expanded, record, needIndentSpaced } = this
+
+ return (
+
+ )
+ },
+
+ renderExpandIconCell (cells) {
+ if (!this.expandIconAsCell) {
+ return
+ }
+ const { prefixCls } = this
+
+ cells.push(
+
+ {this.renderExpandIcon()}
+ |
+ )
+ },
+
+ render () {
+ const {
+ childrenColumnName,
+ expandedRowRender,
+ indentSize,
+ record,
+ fixed,
+ $scopedSlots,
+ } = this
+
+ this.expandIconAsCell = fixed !== 'right' ? this.expandIconAsCell : false
+ this.expandIconColumnIndex = fixed !== 'right' ? this.expandIconColumnIndex : -1
+ const childrenData = record[childrenColumnName]
+ this.expandable = !!(childrenData || expandedRowRender)
+
+ const expandableRowProps = {
+ props: {
+ indentSize,
+ hasExpandIcon: this.hasExpandIcon,
+ renderExpandIcon: this.renderExpandIcon,
+ renderExpandIconCell: this.renderExpandIconCell,
+ },
+ on: {
+ rowClick: this.handleRowClick,
+ },
+
+ }
+
+ return $scopedSlots.default && $scopedSlots.default(expandableRowProps)
+ },
+}
+
+export default connect(({ expandedRowKeys }, { rowKey }) => ({
+ expanded: !!~expandedRowKeys.indexOf(rowKey),
+}))(ExpandableRow)
diff --git a/components/vc-table/src/ExpandableTable.js b/components/vc-table/src/ExpandableTable.js
new file mode 100644
index 000000000..025a59872
--- /dev/null
+++ b/components/vc-table/src/ExpandableTable.js
@@ -0,0 +1,223 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import { connect } from 'mini-store'
+import TableRow from './TableRow'
+import { remove } from './utils'
+
+class ExpandableTable extends React.Component {
+ static propTypes = {
+ expandIconAsCell: PropTypes.bool,
+ expandedRowKeys: PropTypes.array,
+ expandedRowClassName: PropTypes.func,
+ defaultExpandAllRows: PropTypes.bool,
+ defaultExpandedRowKeys: PropTypes.array,
+ expandIconColumnIndex: PropTypes.number,
+ expandedRowRender: PropTypes.func,
+ childrenColumnName: PropTypes.string,
+ indentSize: PropTypes.number,
+ onExpand: PropTypes.func,
+ onExpandedRowsChange: PropTypes.func,
+ columnManager: PropTypes.object.isRequired,
+ store: PropTypes.object.isRequired,
+ prefixCls: PropTypes.string.isRequired,
+ data: PropTypes.array,
+ children: PropTypes.func.isRequired,
+ }
+
+ static defaultProps = {
+ expandIconAsCell: false,
+ expandedRowClassName: () => '',
+ expandIconColumnIndex: 0,
+ defaultExpandAllRows: false,
+ defaultExpandedRowKeys: [],
+ childrenColumnName: 'children',
+ indentSize: 15,
+ onExpand () {},
+ onExpandedRowsChange () {},
+ }
+
+ constructor (props) {
+ super(props)
+
+ const {
+ data,
+ childrenColumnName,
+ defaultExpandAllRows,
+ expandedRowKeys,
+ defaultExpandedRowKeys,
+ getRowKey,
+ } = props
+
+ let finnalExpandedRowKeys = []
+ let rows = [...data]
+
+ if (defaultExpandAllRows) {
+ for (let i = 0; i < rows.length; i++) {
+ const row = rows[i]
+ finnalExpandedRowKeys.push(getRowKey(row, i))
+ rows = rows.concat(row[childrenColumnName] || [])
+ }
+ } else {
+ finnalExpandedRowKeys = expandedRowKeys || defaultExpandedRowKeys
+ }
+
+ this.columnManager = props.columnManager
+ this.store = props.store
+
+ this.store.setState({
+ expandedRowsHeight: {},
+ expandedRowKeys: finnalExpandedRowKeys,
+ })
+ }
+
+ componentWillReceiveProps (nextProps) {
+ if ('expandedRowKeys' in nextProps) {
+ this.store.setState({
+ expandedRowKeys: nextProps.expandedRowKeys,
+ })
+ }
+ }
+
+ handleExpandChange = (expanded, record, event, rowKey, destroy = false) => {
+ if (event) {
+ event.preventDefault()
+ event.stopPropagation()
+ }
+
+ const { onExpandedRowsChange, onExpand } = this.props
+ let { expandedRowKeys } = this.store.getState()
+
+ if (expanded) {
+ // row was expaned
+ expandedRowKeys = [...expandedRowKeys, rowKey]
+ } else {
+ // row was collapse
+ const expandedRowIndex = expandedRowKeys.indexOf(rowKey)
+ if (expandedRowIndex !== -1) {
+ expandedRowKeys = remove(expandedRowKeys, rowKey)
+ }
+ }
+
+ if (!this.props.expandedRowKeys) {
+ this.store.setState({ expandedRowKeys })
+ }
+
+ onExpandedRowsChange(expandedRowKeys)
+ if (!destroy) {
+ onExpand(expanded, record)
+ }
+ }
+
+ renderExpandIndentCell = (rows, fixed) => {
+ const { prefixCls, expandIconAsCell } = this.props
+ if (!expandIconAsCell || fixed === 'right' || !rows.length) {
+ return
+ }
+
+ const iconColumn = {
+ key: 'rc-table-expand-icon-cell',
+ className: `${prefixCls}-expand-icon-th`,
+ title: '',
+ rowSpan: rows.length,
+ }
+
+ rows[0].unshift({ ...iconColumn, column: iconColumn })
+ }
+
+ renderExpandedRow (record, index, render, className, ancestorKeys, indent, fixed) {
+ const { prefixCls, expandIconAsCell, indentSize } = this.props
+ let colCount
+ if (fixed === 'left') {
+ colCount = this.columnManager.leftLeafColumns().length
+ } else if (fixed === 'right') {
+ colCount = this.columnManager.rightLeafColumns().length
+ } else {
+ colCount = this.columnManager.leafColumns().length
+ }
+ const columns = [{
+ key: 'extra-row',
+ render: () => ({
+ props: {
+ colSpan: colCount,
+ },
+ children: fixed !== 'right' ? render(record, index, indent) : ' ',
+ }),
+ }]
+ if (expandIconAsCell && fixed !== 'right') {
+ columns.unshift({
+ key: 'expand-icon-placeholder',
+ render: () => null,
+ })
+ }
+ const parentKey = ancestorKeys[ancestorKeys.length - 1]
+ const rowKey = `${parentKey}-extra-row`
+ const components = {
+ body: {
+ row: 'tr',
+ cell: 'td',
+ },
+ }
+
+ return (
+
+ )
+ }
+
+ renderRows = (renderRows, rows, record, index, indent, fixed, parentKey, ancestorKeys) => {
+ const { expandedRowClassName, expandedRowRender, childrenColumnName } = this.props
+ const childrenData = record[childrenColumnName]
+ const nextAncestorKeys = [...ancestorKeys, parentKey]
+ const nextIndent = indent + 1
+
+ if (expandedRowRender) {
+ rows.push(
+ this.renderExpandedRow(
+ record,
+ index,
+ expandedRowRender,
+ expandedRowClassName(record, index, indent),
+ nextAncestorKeys,
+ nextIndent,
+ fixed,
+ ),
+ )
+ }
+
+ if (childrenData) {
+ rows.push(
+ ...renderRows(
+ childrenData,
+ nextIndent,
+ nextAncestorKeys,
+ )
+ )
+ }
+ }
+
+ render () {
+ const { data, childrenColumnName, children } = this.props
+ const needIndentSpaced = data.some(record => record[childrenColumnName])
+
+ return children({
+ props: this.props,
+ needIndentSpaced,
+ renderRows: this.renderRows,
+ handleExpandChange: this.handleExpandChange,
+ renderExpandIndentCell: this.renderExpandIndentCell,
+ })
+ }
+}
+
+export default connect()(ExpandableTable)
diff --git a/components/vc-table/src/HeadTable.jsx b/components/vc-table/src/HeadTable.jsx
new file mode 100644
index 000000000..e2095b364
--- /dev/null
+++ b/components/vc-table/src/HeadTable.jsx
@@ -0,0 +1,56 @@
+import PropTypes from '../../_util/vue-types'
+import { measureScrollbar } from './utils'
+import BaseTable from './BaseTable'
+
+export default {
+ name: 'HeadTable',
+ props: {
+ fixed: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.bool,
+ ]),
+ columns: PropTypes.array.isRequired,
+ tableClassName: PropTypes.string.isRequired,
+ handleBodyScrollLeft: PropTypes.func.isRequired,
+ expander: PropTypes.object.isRequired,
+ },
+ render () {
+ const { columns, fixed, tableClassName, handleBodyScrollLeft, expander, table } = this
+ const { prefixCls, scroll, showHeader } = table
+ let { useFixedHeader } = table
+ const headStyle = {}
+
+ if (scroll.y) {
+ useFixedHeader = true
+ // Add negative margin bottom for scroll bar overflow bug
+ const scrollbarWidth = measureScrollbar('horizontal')
+ if (scrollbarWidth > 0 && !fixed) {
+ headStyle.marginBottom = `-${scrollbarWidth}px`
+ headStyle.paddingBottom = '0px'
+ }
+ }
+
+ if (!useFixedHeader || !showHeader) {
+ return null
+ }
+ return (
+
+ )
+ },
+
+}
diff --git a/components/vc-table/src/Table.js b/components/vc-table/src/Table.js
new file mode 100644
index 000000000..566e522de
--- /dev/null
+++ b/components/vc-table/src/Table.js
@@ -0,0 +1,475 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import { debounce, warningOnce } from './utils'
+import shallowequal from 'shallowequal'
+import addEventListener from 'rc-util/lib/Dom/addEventListener'
+import { Provider, create } from 'mini-store'
+import merge from 'lodash/merge'
+import ColumnManager from './ColumnManager'
+import classes from 'component-classes'
+import HeadTable from './HeadTable'
+import BodyTable from './BodyTable'
+import ExpandableTable from './ExpandableTable'
+
+export default class Table extends React.Component {
+ static propTypes = {
+ data: 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.oneOfType([PropTypes.string, PropTypes.func]),
+ onRow: PropTypes.func,
+ onHeaderRow: PropTypes.func,
+ onRowClick: PropTypes.func,
+ onRowDoubleClick: PropTypes.func,
+ onRowContextMenu: PropTypes.func,
+ onRowMouseEnter: PropTypes.func,
+ onRowMouseLeave: PropTypes.func,
+ showHeader: PropTypes.bool,
+ title: PropTypes.func,
+ id: PropTypes.string,
+ footer: PropTypes.func,
+ emptyText: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
+ scroll: PropTypes.object,
+ rowRef: PropTypes.func,
+ getBodyWrapper: PropTypes.func,
+ children: PropTypes.node,
+ components: PropTypes.shape({
+ table: PropTypes.any,
+ header: PropTypes.shape({
+ wrapper: PropTypes.any,
+ row: PropTypes.any,
+ cell: PropTypes.any,
+ }),
+ body: PropTypes.shape({
+ wrapper: PropTypes.any,
+ row: PropTypes.any,
+ cell: PropTypes.any,
+ }),
+ }),
+ ...ExpandableTable.PropTypes,
+ }
+
+ static childContextTypes = {
+ table: PropTypes.any,
+ components: PropTypes.any,
+ }
+
+ static defaultProps = {
+ data: [],
+ useFixedHeader: false,
+ rowKey: 'key',
+ rowClassName: () => '',
+ onRow () {},
+ onHeaderRow () {},
+ prefixCls: 'rc-table',
+ bodyStyle: {},
+ style: {},
+ showHeader: true,
+ scroll: {},
+ rowRef: () => null,
+ emptyText: () => 'No Data',
+ }
+
+ constructor (props) {
+ super(props);
+
+ [
+ 'onRowClick',
+ 'onRowDoubleClick',
+ 'onRowContextMenu',
+ 'onRowMouseEnter',
+ 'onRowMouseLeave',
+ ].forEach(name => {
+ warningOnce(
+ props[name] === undefined,
+ `${name} is deprecated, please use onRow instead.`,
+ )
+ })
+
+ warningOnce(
+ props.getBodyWrapper === undefined,
+ 'getBodyWrapper is deprecated, please use custom components instead.',
+ )
+
+ this.columnManager = new ColumnManager(props.columns, props.children)
+
+ this.store = create({
+ currentHoverKey: null,
+ fixedColumnsHeadRowsHeight: [],
+ fixedColumnsBodyRowsHeight: [],
+ })
+
+ this.setScrollPosition('left')
+
+ this.debouncedWindowResize = debounce(this.handleWindowResize, 150)
+ }
+
+ getChildContext () {
+ return {
+ table: {
+ props: this.props,
+ columnManager: this.columnManager,
+ saveRef: this.saveRef,
+ components: merge({
+ table: 'table',
+ header: {
+ wrapper: 'thead',
+ row: 'tr',
+ cell: 'th',
+ },
+ body: {
+ wrapper: 'tbody',
+ row: 'tr',
+ cell: 'td',
+ },
+ }, this.props.components),
+ },
+ }
+ }
+
+ componentDidMount () {
+ if (this.columnManager.isAnyColumnsFixed()) {
+ this.handleWindowResize()
+ this.resizeEvent = addEventListener(
+ window, 'resize', this.debouncedWindowResize
+ )
+ }
+ }
+
+ componentWillReceiveProps (nextProps) {
+ if (nextProps.columns && nextProps.columns !== this.props.columns) {
+ this.columnManager.reset(nextProps.columns)
+ } else if (nextProps.children !== this.props.children) {
+ this.columnManager.reset(null, nextProps.children)
+ }
+ }
+
+ componentDidUpdate (prevProps) {
+ if (this.columnManager.isAnyColumnsFixed()) {
+ this.handleWindowResize()
+ if (!this.resizeEvent) {
+ this.resizeEvent = addEventListener(
+ window, 'resize', this.debouncedWindowResize
+ )
+ }
+ }
+ // when table changes to empty, reset scrollLeft
+ if (prevProps.data.length > 0 && this.props.data.length === 0 && this.hasScrollX()) {
+ this.resetScrollX()
+ }
+ }
+
+ componentWillUnmount () {
+ if (this.resizeEvent) {
+ this.resizeEvent.remove()
+ }
+ if (this.debouncedWindowResize) {
+ this.debouncedWindowResize.cancel()
+ }
+ }
+
+ getRowKey = (record, index) => {
+ const rowKey = this.props.rowKey
+ const key = (typeof rowKey === 'function')
+ ? rowKey(record, index) : record[rowKey]
+ warningOnce(
+ key !== undefined,
+ 'Each record in table should have a unique `key` prop,' +
+ 'or set `rowKey` to an unique primary key.'
+ )
+ return key === undefined ? index : key
+ }
+
+ setScrollPosition (position) {
+ this.scrollPosition = position
+ if (this.tableNode) {
+ const { prefixCls } = this.props
+ if (position === 'both') {
+ classes(this.tableNode)
+ .remove(new RegExp(`^${prefixCls}-scroll-position-.+$`))
+ .add(`${prefixCls}-scroll-position-left`)
+ .add(`${prefixCls}-scroll-position-right`)
+ } else {
+ classes(this.tableNode)
+ .remove(new RegExp(`^${prefixCls}-scroll-position-.+$`))
+ .add(`${prefixCls}-scroll-position-${position}`)
+ }
+ }
+ }
+
+ setScrollPositionClassName () {
+ const node = this.bodyTable
+ const scrollToLeft = node.scrollLeft === 0
+ const scrollToRight = node.scrollLeft + 1 >=
+ node.children[0].getBoundingClientRect().width -
+ node.getBoundingClientRect().width
+ if (scrollToLeft && scrollToRight) {
+ this.setScrollPosition('both')
+ } else if (scrollToLeft) {
+ this.setScrollPosition('left')
+ } else if (scrollToRight) {
+ this.setScrollPosition('right')
+ } else if (this.scrollPosition !== 'middle') {
+ this.setScrollPosition('middle')
+ }
+ }
+
+ handleWindowResize = () => {
+ this.syncFixedTableRowHeight()
+ this.setScrollPositionClassName()
+ }
+
+ syncFixedTableRowHeight = () => {
+ const tableRect = this.tableNode.getBoundingClientRect()
+ // If tableNode's height less than 0, suppose it is hidden and don't recalculate rowHeight.
+ // see: https://github.com/ant-design/ant-design/issues/4836
+ if (tableRect.height !== undefined && tableRect.height <= 0) {
+ return
+ }
+ const { prefixCls } = this.props
+ const headRows = this.headTable
+ ? this.headTable.querySelectorAll('thead')
+ : this.bodyTable.querySelectorAll('thead')
+ const bodyRows = this.bodyTable.querySelectorAll(`.${prefixCls}-row`) || []
+ const fixedColumnsHeadRowsHeight = [].map.call(
+ headRows, row => row.getBoundingClientRect().height || 'auto'
+ )
+ const fixedColumnsBodyRowsHeight = [].map.call(
+ bodyRows, row => row.getBoundingClientRect().height || 'auto'
+ )
+ const state = this.store.getState()
+ if (shallowequal(state.fixedColumnsHeadRowsHeight, fixedColumnsHeadRowsHeight) &&
+ shallowequal(state.fixedColumnsBodyRowsHeight, fixedColumnsBodyRowsHeight)) {
+ return
+ }
+
+ this.store.setState({
+ fixedColumnsHeadRowsHeight,
+ fixedColumnsBodyRowsHeight,
+ })
+ }
+
+ resetScrollX () {
+ if (this.headTable) {
+ this.headTable.scrollLeft = 0
+ }
+ if (this.bodyTable) {
+ this.bodyTable.scrollLeft = 0
+ }
+ }
+
+ hasScrollX () {
+ const { scroll = {}} = this.props
+ return 'x' in scroll
+ }
+
+ handleBodyScrollLeft = (e) => {
+ // Fix https://github.com/ant-design/ant-design/issues/7635
+ if (e.currentTarget !== e.target) {
+ return
+ }
+ const target = e.target
+ const { scroll = {}} = this.props
+ const { headTable, bodyTable } = this
+ if (target.scrollLeft !== this.lastScrollLeft && scroll.x) {
+ if (target === bodyTable && headTable) {
+ headTable.scrollLeft = target.scrollLeft
+ } else if (target === headTable && bodyTable) {
+ bodyTable.scrollLeft = target.scrollLeft
+ }
+ this.setScrollPositionClassName()
+ }
+ // Remember last scrollLeft for scroll direction detecting.
+ this.lastScrollLeft = target.scrollLeft
+ }
+
+ handleBodyScrollTop = (e) => {
+ const target = e.target
+ const { scroll = {}} = this.props
+ const { headTable, bodyTable, fixedColumnsBodyLeft, fixedColumnsBodyRight } = this
+ if (target.scrollTop !== this.lastScrollTop && scroll.y && target !== headTable) {
+ const scrollTop = target.scrollTop
+ if (fixedColumnsBodyLeft && target !== fixedColumnsBodyLeft) {
+ fixedColumnsBodyLeft.scrollTop = scrollTop
+ }
+ if (fixedColumnsBodyRight && target !== fixedColumnsBodyRight) {
+ fixedColumnsBodyRight.scrollTop = scrollTop
+ }
+ if (bodyTable && target !== bodyTable) {
+ bodyTable.scrollTop = scrollTop
+ }
+ }
+ // Remember last scrollTop for scroll direction detecting.
+ this.lastScrollTop = target.scrollTop
+ }
+
+ handleBodyScroll = (e) => {
+ this.handleBodyScrollLeft(e)
+ this.handleBodyScrollTop(e)
+ }
+
+ saveRef = (name) => (node) => {
+ this[name] = node
+ }
+
+ renderMainTable () {
+ const { scroll, prefixCls } = this.props
+ const isAnyColumnsFixed = this.columnManager.isAnyColumnsFixed()
+ const scrollable = isAnyColumnsFixed || scroll.x || scroll.y
+
+ const table = [
+ this.renderTable({
+ columns: this.columnManager.groupedColumns(),
+ isAnyColumnsFixed,
+ }),
+ this.renderEmptyText(),
+ this.renderFooter(),
+ ]
+
+ return scrollable ? (
+ {table}
+ ) : table
+ }
+
+ renderLeftFixedTable () {
+ const { prefixCls } = this.props
+
+ return (
+
+ {this.renderTable({
+ columns: this.columnManager.leftColumns(),
+ fixed: 'left',
+ })}
+
+ )
+ }
+
+ renderRightFixedTable () {
+ const { prefixCls } = this.props
+
+ return (
+
+ {this.renderTable({
+ columns: this.columnManager.rightColumns(),
+ fixed: 'right',
+ })}
+
+ )
+ }
+
+ renderTable (options) {
+ const { columns, fixed, isAnyColumnsFixed } = options
+ const { prefixCls, scroll = {}} = this.props
+ const tableClassName = (scroll.x || fixed) ? `${prefixCls}-fixed` : ''
+
+ const headTable = (
+
+ )
+
+ const bodyTable = (
+
+ )
+
+ return [headTable, bodyTable]
+ }
+
+ renderTitle () {
+ const { title, prefixCls } = this.props
+ return title ? (
+
+ {title(this.props.data)}
+
+ ) : null
+ }
+
+ renderFooter () {
+ const { footer, prefixCls } = this.props
+ return footer ? (
+
+ {footer(this.props.data)}
+
+ ) : null
+ }
+
+ renderEmptyText () {
+ const { emptyText, prefixCls, data } = this.props
+ if (data.length) {
+ return null
+ }
+ const emptyClassName = `${prefixCls}-placeholder`
+ return (
+
+ {(typeof emptyText === 'function') ? emptyText() : emptyText}
+
+ )
+ }
+
+ render () {
+ const props = this.props
+ const prefixCls = props.prefixCls
+
+ let className = props.prefixCls
+ if (props.className) {
+ className += ` ${props.className}`
+ }
+ if (props.useFixedHeader || (props.scroll && props.scroll.y)) {
+ className += ` ${prefixCls}-fixed-header`
+ }
+ if (this.scrollPosition === 'both') {
+ className += ` ${prefixCls}-scroll-position-left ${prefixCls}-scroll-position-right`
+ } else {
+ className += ` ${prefixCls}-scroll-position-${this.scrollPosition}`
+ }
+ const hasLeftFixed = this.columnManager.isAnyColumnsLeftFixed()
+ const hasRightFixed = this.columnManager.isAnyColumnsRightFixed()
+
+ return (
+
+
+ {(expander) => {
+ this.expander = expander
+ return (
+
+ {this.renderTitle()}
+
+ {this.renderMainTable()}
+ {hasLeftFixed && this.renderLeftFixedTable()}
+ {hasRightFixed && this.renderRightFixedTable()}
+
+
+ )
+ }}
+
+
+ )
+ }
+}
diff --git a/components/vc-table/src/TableCell.jsx b/components/vc-table/src/TableCell.jsx
new file mode 100644
index 000000000..41bccae5e
--- /dev/null
+++ b/components/vc-table/src/TableCell.jsx
@@ -0,0 +1,104 @@
+import PropTypes from '../../_util/vue-types'
+import get from 'lodash/get'
+
+export default {
+ name: 'TableCell',
+ props: {
+ record: PropTypes.object,
+ prefixCls: PropTypes.string,
+ index: PropTypes.number,
+ indent: PropTypes.number,
+ indentSize: PropTypes.number,
+ column: PropTypes.object,
+ expandIcon: PropTypes.node,
+ component: PropTypes.any,
+ },
+ methods: {
+ isInvalidRenderCellText (text) {
+ debugger
+ return text &&
+ Object.prototype.toString.call(text) === '[object Object]'
+ },
+
+ handleClick (e) {
+ const { record, column: { onCellClick }} = this
+ if (onCellClick) {
+ onCellClick(record, e)
+ }
+ },
+ },
+
+ render () {
+ const {
+ record,
+ indentSize,
+ prefixCls,
+ indent,
+ index,
+ expandIcon,
+ column,
+ component: BodyCell,
+ } = this
+ const { dataIndex, render } = column
+
+ // We should return undefined if no dataIndex is specified, but in order to
+ // be compatible with object-path's behavior, we return the record object instead.
+ let text
+ if (typeof dataIndex === 'number') {
+ text = get(record, dataIndex)
+ } else if (!dataIndex || dataIndex.length === 0) {
+ text = record
+ } else {
+ text = get(record, dataIndex)
+ }
+ let tdProps = {}
+ let colSpan
+ let rowSpan
+
+ if (render) {
+ text = render(text, record, index)
+ if (this.isInvalidRenderCellText(text)) {
+ tdProps = text.props || tdProps
+ colSpan = tdProps.colSpan
+ rowSpan = tdProps.rowSpan
+ text = text.children
+ }
+ }
+
+ if (column.onCell) {
+ tdProps = { ...tdProps, ...column.onCell(record) }
+ }
+
+ // Fix https://github.com/ant-design/ant-design/issues/1202
+ if (this.isInvalidRenderCellText(text)) {
+ text = null
+ }
+
+ const indentText = expandIcon ? (
+
+ ) : null
+
+ if (rowSpan === 0 || colSpan === 0) {
+ return null
+ }
+
+ if (column.align) {
+ tdProps.style = { textAlign: column.align }
+ }
+ console.log('tdProps', tdProps)
+
+ return (
+
+ {indentText}
+ {expandIcon}
+ {text}
+
+ )
+ },
+}
diff --git a/components/vc-table/src/TableHeader.jsx b/components/vc-table/src/TableHeader.jsx
new file mode 100644
index 000000000..b0a1be40f
--- /dev/null
+++ b/components/vc-table/src/TableHeader.jsx
@@ -0,0 +1,85 @@
+import PropTypes from '../../_util/vue-types'
+import TableHeaderRow from './TableHeaderRow'
+
+function getHeaderRows (columns, currentRow = 0, rows) {
+ rows = rows || []
+ rows[currentRow] = rows[currentRow] || []
+
+ columns.forEach(column => {
+ if (column.rowSpan && rows.length < column.rowSpan) {
+ while (rows.length < column.rowSpan) {
+ rows.push([])
+ }
+ }
+ const cell = {
+ key: column.key,
+ className: column.className || '',
+ children: column.title,
+ column,
+ }
+ if (column.children) {
+ getHeaderRows(column.children, currentRow + 1, rows)
+ }
+ if ('colSpan' in column) {
+ cell.colSpan = column.colSpan
+ }
+ if ('rowSpan' in column) {
+ cell.rowSpan = column.rowSpan
+ }
+ if (cell.colSpan !== 0) {
+ rows[currentRow].push(cell)
+ }
+ })
+ return rows.filter(row => row.length > 0)
+}
+
+export default {
+ name: 'TableHeader',
+ props: {
+ fixed: PropTypes.string,
+ columns: PropTypes.array.isRequired,
+ expander: PropTypes.object.isRequired,
+
+ },
+ methods: {
+ onHeaderRow () {
+ this.table.__emit('headerRow', ...arguments)
+ },
+ },
+
+ render () {
+ const { components, prefixCls, showHeader } = this.table
+ const { expander, columns, fixed, onHeaderRow } = this
+
+ if (!showHeader) {
+ return null
+ }
+
+ const rows = getHeaderRows(columns)
+
+ expander.renderExpandIndentCell(rows, fixed)
+
+ const HeaderWrapper = components.header.wrapper
+
+ return (
+
+ {
+ rows.map((row, index) => (
+
+ ))
+ }
+
+ )
+ },
+
+}
+
diff --git a/components/vc-table/src/TableHeaderRow.js b/components/vc-table/src/TableHeaderRow.js
new file mode 100644
index 000000000..d801ba46d
--- /dev/null
+++ b/components/vc-table/src/TableHeaderRow.js
@@ -0,0 +1,53 @@
+import React from 'react'
+import { connect } from 'mini-store'
+
+function TableHeaderRow ({ row, index, height, components, onHeaderRow }) {
+ const HeaderRow = components.header.row
+ const HeaderCell = components.header.cell
+ const rowProps = onHeaderRow(row.map(cell => cell.column), index)
+ const customStyle = rowProps ? rowProps.style : {}
+ const style = { height, ...customStyle }
+
+ return (
+
+ {row.map((cell, i) => {
+ const { column, ...cellProps } = cell
+ const customProps = column.onHeaderCell ? column.onHeaderCell(column) : {}
+ if (column.align) {
+ cellProps.style = { textAlign: column.align }
+ }
+ return (
+
+ )
+ })}
+
+ )
+}
+
+function getRowHeight (state, props) {
+ const { fixedColumnsHeadRowsHeight } = state
+ const { columns, rows, fixed } = props
+ const headerHeight = fixedColumnsHeadRowsHeight[0]
+
+ if (!fixed) {
+ return null
+ }
+
+ if (headerHeight && columns) {
+ if (headerHeight === 'auto') {
+ return 'auto'
+ }
+ return headerHeight / rows.length
+ }
+ return null
+}
+
+export default connect((state, props) => {
+ return {
+ height: getRowHeight(state, props),
+ }
+})(TableHeaderRow)
diff --git a/components/vc-table/src/TableRow.js b/components/vc-table/src/TableRow.js
new file mode 100644
index 000000000..3348bb559
--- /dev/null
+++ b/components/vc-table/src/TableRow.js
@@ -0,0 +1,291 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+import PropTypes from 'prop-types'
+import { connect } from 'mini-store'
+import TableCell from './TableCell'
+import { warningOnce } from './utils'
+
+class TableRow extends React.Component {
+ static propTypes = {
+ onRow: PropTypes.func,
+ onRowClick: PropTypes.func,
+ onRowDoubleClick: PropTypes.func,
+ onRowContextMenu: PropTypes.func,
+ onRowMouseEnter: PropTypes.func,
+ onRowMouseLeave: PropTypes.func,
+ record: PropTypes.object,
+ prefixCls: PropTypes.string,
+ onHover: PropTypes.func,
+ columns: PropTypes.array,
+ height: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number,
+ ]),
+ index: PropTypes.number,
+ rowKey: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number,
+ ]).isRequired,
+ className: PropTypes.string,
+ indent: PropTypes.number,
+ indentSize: PropTypes.number,
+ hasExpandIcon: PropTypes.func.isRequired,
+ hovered: PropTypes.bool.isRequired,
+ visible: PropTypes.bool.isRequired,
+ store: PropTypes.object.isRequired,
+ fixed: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.bool,
+ ]),
+ renderExpandIcon: PropTypes.func,
+ renderExpandIconCell: PropTypes.func,
+ components: PropTypes.any,
+ expandedRow: PropTypes.bool,
+ isAnyColumnsFixed: PropTypes.bool,
+ ancestorKeys: PropTypes.array.isRequired,
+ }
+
+ static defaultProps = {
+ onRow () {},
+ expandIconColumnIndex: 0,
+ expandRowByClick: false,
+ onHover () {},
+ hasExpandIcon () {},
+ renderExpandIcon () {},
+ renderExpandIconCell () {},
+ }
+
+ constructor (props) {
+ super(props)
+
+ this.shouldRender = props.visible
+ }
+
+ componentDidMount () {
+ if (this.shouldRender) {
+ this.saveRowRef()
+ }
+ }
+
+ componentWillReceiveProps (nextProps) {
+ if (this.props.visible || (!this.props.visible && nextProps.visible)) {
+ this.shouldRender = true
+ }
+ }
+
+ shouldComponentUpdate (nextProps) {
+ return !!(this.props.visible || nextProps.visible)
+ }
+
+ componentDidUpdate () {
+ if (this.shouldRender && !this.rowRef) {
+ this.saveRowRef()
+ }
+ }
+
+ onRowClick = (event) => {
+ const { record, index, onRowClick } = this.props
+ if (onRowClick) {
+ onRowClick(record, index, event)
+ }
+ }
+
+ onRowDoubleClick = (event) => {
+ const { record, index, onRowDoubleClick } = this.props
+ if (onRowDoubleClick) {
+ onRowDoubleClick(record, index, event)
+ }
+ }
+
+ onContextMenu = (event) => {
+ const { record, index, onRowContextMenu } = this.props
+ if (onRowContextMenu) {
+ onRowContextMenu(record, index, event)
+ }
+ }
+
+ onMouseEnter = (event) => {
+ const { record, index, onRowMouseEnter, onHover, rowKey } = this.props
+ onHover(true, rowKey)
+ if (onRowMouseEnter) {
+ onRowMouseEnter(record, index, event)
+ }
+ }
+
+ onMouseLeave = (event) => {
+ const { record, index, onRowMouseLeave, onHover, rowKey } = this.props
+ onHover(false, rowKey)
+ if (onRowMouseLeave) {
+ onRowMouseLeave(record, index, event)
+ }
+ }
+
+ setExpanedRowHeight () {
+ const { store, rowKey } = this.props
+ let { expandedRowsHeight } = store.getState()
+ const height = this.rowRef.getBoundingClientRect().height
+ expandedRowsHeight = {
+ ...expandedRowsHeight,
+ [rowKey]: height,
+ }
+ store.setState({ expandedRowsHeight })
+ }
+
+ setRowHeight () {
+ const { store, index } = this.props
+ const fixedColumnsBodyRowsHeight = store.getState().fixedColumnsBodyRowsHeight.slice()
+ const height = this.rowRef.getBoundingClientRect().height
+ fixedColumnsBodyRowsHeight[index] = height
+ store.setState({ fixedColumnsBodyRowsHeight })
+ }
+
+ getStyle () {
+ const { height, visible } = this.props
+
+ if (height && height !== this.style.height) {
+ this.style = { ...this.style, height }
+ }
+
+ if (!visible && !this.style.display) {
+ this.style = { ...this.style, display: 'none' }
+ }
+
+ return this.style
+ }
+
+ saveRowRef () {
+ this.rowRef = ReactDOM.findDOMNode(this)
+
+ const { isAnyColumnsFixed, fixed, expandedRow, ancestorKeys } = this.props
+
+ if (!isAnyColumnsFixed) {
+ return
+ }
+
+ if (!fixed && expandedRow) {
+ this.setExpanedRowHeight()
+ }
+
+ if (!fixed && ancestorKeys.length >= 0) {
+ this.setRowHeight()
+ }
+ }
+
+ render () {
+ if (!this.shouldRender) {
+ return null
+ }
+
+ const {
+ prefixCls,
+ columns,
+ record,
+ index,
+ onRow,
+ indent,
+ indentSize,
+ hovered,
+ height,
+ visible,
+ components,
+ hasExpandIcon,
+ renderExpandIcon,
+ renderExpandIconCell,
+ } = this.props
+
+ const BodyRow = components.body.row
+ const BodyCell = components.body.cell
+
+ let { className } = this.props
+
+ if (hovered) {
+ className += ` ${prefixCls}-hover`
+ }
+
+ const cells = []
+
+ renderExpandIconCell(cells)
+
+ for (let i = 0; i < columns.length; i++) {
+ const column = columns[i]
+
+ warningOnce(
+ column.onCellClick === undefined,
+ 'column[onCellClick] is deprecated, please use column[onCell] instead.',
+ )
+
+ cells.push(
+
+ )
+ }
+
+ const rowClassName =
+ `${prefixCls} ${className} ${prefixCls}-level-${indent}`.trim()
+
+ const rowProps = onRow(record, index)
+ const customStyle = rowProps ? rowProps.style : {}
+ let style = { height }
+
+ if (!visible) {
+ style.display = 'none'
+ }
+
+ style = { ...style, ...customStyle }
+
+ return (
+
+ {cells}
+
+ )
+ }
+}
+
+function getRowHeight (state, props) {
+ const { expandedRowsHeight, fixedColumnsBodyRowsHeight } = state
+ const { fixed, index, rowKey } = props
+
+ if (!fixed) {
+ return null
+ }
+
+ if (expandedRowsHeight[rowKey]) {
+ return expandedRowsHeight[rowKey]
+ }
+
+ if (fixedColumnsBodyRowsHeight[index]) {
+ return fixedColumnsBodyRowsHeight[index]
+ }
+
+ return null
+}
+
+export default connect((state, props) => {
+ const { currentHoverKey, expandedRowKeys } = state
+ const { rowKey, ancestorKeys } = props
+ const visible = ancestorKeys.length === 0 || ancestorKeys.every(k => ~expandedRowKeys.indexOf(k))
+
+ return ({
+ visible,
+ hovered: currentHoverKey === rowKey,
+ height: getRowHeight(state, props),
+ })
+})(TableRow)
diff --git a/components/vc-table/src/utils.js b/components/vc-table/src/utils.js
new file mode 100644
index 000000000..5d3ef7c37
--- /dev/null
+++ b/components/vc-table/src/utils.js
@@ -0,0 +1,84 @@
+import warning from 'warning'
+
+let scrollbarSize
+
+// Measure scrollbar width for padding body during modal show/hide
+const scrollbarMeasure = {
+ position: 'absolute',
+ top: '-9999px',
+ width: '50px',
+ height: '50px',
+ overflow: 'scroll',
+}
+
+export function measureScrollbar (direction = 'vertical') {
+ if (typeof document === 'undefined' || typeof window === 'undefined') {
+ return 0
+ }
+ if (scrollbarSize) {
+ return scrollbarSize
+ }
+ const scrollDiv = document.createElement('div')
+ for (const scrollProp in scrollbarMeasure) {
+ if (scrollbarMeasure.hasOwnProperty(scrollProp)) {
+ scrollDiv.style[scrollProp] = scrollbarMeasure[scrollProp]
+ }
+ }
+ document.body.appendChild(scrollDiv)
+ let size = 0
+ if (direction === 'vertical') {
+ size = scrollDiv.offsetWidth - scrollDiv.clientWidth
+ } else if (direction === 'horizontal') {
+ size = scrollDiv.offsetHeight - scrollDiv.clientHeight
+ }
+
+ document.body.removeChild(scrollDiv)
+ scrollbarSize = size
+ return scrollbarSize
+}
+
+export function debounce (func, wait, immediate) {
+ let timeout
+ function debounceFunc () {
+ const context = this
+ const args = arguments
+ // https://fb.me/react-event-pooling
+ if (args[0] && args[0].persist) {
+ args[0].persist()
+ }
+ const later = () => {
+ timeout = null
+ if (!immediate) {
+ func.apply(context, args)
+ }
+ }
+ const callNow = immediate && !timeout
+ clearTimeout(timeout)
+ timeout = setTimeout(later, wait)
+ if (callNow) {
+ func.apply(context, args)
+ }
+ }
+ debounceFunc.cancel = function cancel () {
+ if (timeout) {
+ clearTimeout(timeout)
+ timeout = null
+ }
+ }
+ return debounceFunc
+}
+
+const warned = {}
+export function warningOnce (condition, format, args) {
+ if (!warned[format]) {
+ warning(condition, format, args)
+ warned[format] = !condition
+ }
+}
+
+export function remove (array, item) {
+ const index = array.indexOf(item)
+ const front = array.slice(0, index)
+ const last = array.slice(index + 1, array.length)
+ return front.concat(last)
+}