Skip to content
Merged
3 changes: 2 additions & 1 deletion assets/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
overflow: scroll;
}

&-fixed-header &-scroll &-header {
&-fixed-header &-scroll &-header,
&-fixed-header &-scroll &-in-table-footer {
overflow-x: scroll;
padding-bottom: 20px;
margin-bottom: -20px;
Expand Down
Empty file added examples/columnFooter.html
Empty file.
64 changes: 64 additions & 0 deletions examples/columnFooter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* 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',
width: 100,
fixed: 'left',
footer: 'Summary',
},
{
title: 'Money',
dataIndex: 'money',
width: 100,
render: text => `$${text.toFixed(2)}`,
footer: data => `Total: $${data.reduce((acc, row) => acc + row.money, 0).toFixed(2)}`,
},
{ title: 'Address', dataIndex: 'address', width: 300 },
];

const data = [
{
name: 'John Brown',
money: 300,
address: 'New York No. 1 Lake Park',
},
{
name: 'Jim Green',
money: 128,
address: 'London No. 1 Lake Park',
},
{
name: 'Joe Black',
money: 240,
address: 'Sidney No. 1 Lake Park',
},
{
name: 'Mick Sydney',
money: 300,
address: 'Sidney No. 1 Lake Park',
},
{
name: 'Miguel Manning',
money: 120,
address: 'Sidney No. 1 Lake Park',
},
{
name: 'John Appleseed',
money: 256,
address: '1 Infinite Loop; Cupertino, CA 95014',
},
];

ReactDOM.render(
<div>
<h2>Demonstrate column footer</h2>
<Table columns={columns} scroll={{ x: '150%', y: 200 }} data={data} />
</div>,
document.getElementById('__react-content')
);
5 changes: 4 additions & 1 deletion src/BaseTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'mini-store';
import ColGroup from './ColGroup';
import TableHeader from './TableHeader';
import TableFooter from './TableFooter';
import TableRow from './TableRow';
import ExpandableRow from './ExpandableRow';

Expand All @@ -16,6 +17,7 @@ class BaseTable extends React.Component {
tableClassName: PropTypes.string.isRequired,
hasHead: PropTypes.bool.isRequired,
hasBody: PropTypes.bool.isRequired,
hasFoot: PropTypes.bool.isRequired,
store: PropTypes.object.isRequired,
expander: PropTypes.object.isRequired,
getRowKey: PropTypes.func,
Expand Down Expand Up @@ -134,7 +136,7 @@ class BaseTable extends React.Component {
const { table } = this.context;
const { components } = table;
const { prefixCls, scroll, data, getBodyWrapper } = table.props;
const { expander, tableClassName, hasHead, hasBody, fixed, columns } = this.props;
const { expander, tableClassName, hasHead, hasBody, hasFoot, fixed, columns } = this.props;
const tableStyle = {};

if (!fixed && scroll.x) {
Expand Down Expand Up @@ -165,6 +167,7 @@ class BaseTable extends React.Component {
<Table className={tableClassName} style={tableStyle} key="table">
<ColGroup columns={columns} fixed={fixed} />
{hasHead && <TableHeader expander={expander} columns={columns} fixed={fixed} /> }
{hasFoot && <TableFooter columns={columns} fixed={fixed} /> }
{body}
</Table>
);
Expand Down
1 change: 1 addition & 0 deletions src/BodyTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export default function BodyTable(props, { table }) {
tableClassName={tableClassName}
hasHead={!useFixedHeader}
hasBody
hasFoot={!useFixedHeader}
fixed={fixed}
columns={columns}
expander={expander}
Expand Down
1 change: 1 addition & 0 deletions src/Column.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default class Column extends Component {
className: PropTypes.string,
colSpan: PropTypes.number,
title: PropTypes.node,
footer: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
dataIndex: PropTypes.string,
width: PropTypes.oneOfType([
PropTypes.number,
Expand Down
61 changes: 61 additions & 0 deletions src/FootTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import PropTypes from 'prop-types';
import { measureScrollbar } from './utils';
import BaseTable from './BaseTable';

export default function FootTable(props, { table }) {
const { prefixCls, scroll } = table.props;
const { columns, fixed, tableClassName, handleBodyScrollLeft, expander } = props;
const { saveRef } = table;
let { useFixedHeader } = table.props;
const footStyle = {};

if (scroll.y) {
useFixedHeader = true;
// Add negative margin bottom for scroll bar overflow bug
const scrollbarWidth = measureScrollbar();
if (scrollbarWidth > 0 && !fixed) {
footStyle.marginBottom = `-${scrollbarWidth}px`;
footStyle.paddingBottom = '0px';
}
}

if (!useFixedHeader) {
return null;
}

return (
<div
key="footTable"
ref={fixed ? null : saveRef('footTable')}
className={`${prefixCls}-in-table-footer`}
style={footStyle}
onScroll={handleBodyScrollLeft}
>
<BaseTable
tableClassName={tableClassName}
hasHead={false}
hasBody={false}
hasFoot
fixed={fixed}
columns={columns}
expander={expander}
/>
</div>
);
}

FootTable.propTypes = {
fixed: PropTypes.oneOfType([
PropTypes.string,
PropTypes.bool,
]),
columns: PropTypes.array.isRequired,
tableClassName: PropTypes.string.isRequired,
handleBodyScrollLeft: PropTypes.func.isRequired,
expander: PropTypes.object.isRequired,
};

FootTable.contextTypes = {
table: PropTypes.any,
};
1 change: 1 addition & 0 deletions src/HeadTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export default function HeadTable(props, { table }) {
tableClassName={tableClassName}
hasHead
hasBody={false}
hasFoot={false}
fixed={fixed}
columns={columns}
expander={expander}
Expand Down
42 changes: 36 additions & 6 deletions src/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ColumnManager from './ColumnManager';
import classes from 'component-classes';
import HeadTable from './HeadTable';
import BodyTable from './BodyTable';
import FootTable from './FootTable';
import ExpandableTable from './ExpandableTable';

export default class Table extends React.Component {
Expand Down Expand Up @@ -49,6 +50,11 @@ export default class Table extends React.Component {
row: PropTypes.any,
cell: PropTypes.any,
}),
footer: PropTypes.shape({
wrapper: PropTypes.any,
row: PropTypes.any,
cell: PropTypes.any,
}),
}),
...ExpandableTable.PropTypes,
}
Expand Down Expand Up @@ -126,6 +132,11 @@ export default class Table extends React.Component {
row: 'tr',
cell: 'td',
},
footer: {
wrapper: 'tfoot',
row: 'tr',
cell: 'td',
},
}, this.props.components),
},
};
Expand Down Expand Up @@ -260,6 +271,9 @@ export default class Table extends React.Component {
if (this.bodyTable) {
this.bodyTable.scrollLeft = 0;
}
if (this.footTable) {
this.footTable.scrollLeft = 0;
}
}

hasScrollX() {
Expand All @@ -274,12 +288,17 @@ export default class Table extends React.Component {
}
const target = e.target;
const { scroll = {} } = this.props;
const { headTable, bodyTable } = this;
const { headTable, bodyTable, footTable } = 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;
if (target === bodyTable) {
if (headTable) headTable.scrollLeft = target.scrollLeft;
if (footTable) footTable.scrollLeft = target.scrollLeft;
} else if (target === headTable) {
if (bodyTable) bodyTable.scrollLeft = target.scrollLeft;
if (footTable) footTable.scrollLeft = target.scrollLeft;
} else if (target === footTable) {
if (bodyTable) bodyTable.scrollLeft = target.scrollLeft;
if (headTable) headTable.scrollLeft = target.scrollLeft;
}
this.setScrollPositionClassName();
}
Expand Down Expand Up @@ -390,7 +409,18 @@ export default class Table extends React.Component {
/>
);

return [headTable, bodyTable];
const footTable = (
<FootTable
key="foot"
columns={columns}
fixed={fixed}
tableClassName={tableClassName}
handleBodyScrollLeft={this.handleBodyScrollLeft}
expander={this.expander}
/>
);

return [headTable, bodyTable, footTable];
}

renderTitle() {
Expand Down
64 changes: 64 additions & 0 deletions src/TableFooter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import PropTypes from 'prop-types';

function getColumnFooter(col) {
if (typeof col.footer === 'function') return col.footer;
return () => col.footer;
}

export default function TableFooter(props, { table }) {
const { columnManager, components } = table;
const { prefixCls, data, expandIconAsCell } = table.props;
const { fixed } = props;

if (!columnManager.leafColumns().some(col => col.footer)) {
return null;
}

const expandIconCol = {
key: 'expand-icon-placeholder',
render: () => null,
};

let leafColumns;
if (fixed === 'left') {
leafColumns = columnManager.leftLeafColumns();
if (expandIconAsCell) {
leafColumns = [expandIconCol, ...leafColumns];
}
} else if (fixed === 'right') {
leafColumns = columnManager.rightLeafColumns();
} else {
leafColumns = columnManager.leafColumns();
if (expandIconAsCell) {
leafColumns = [expandIconCol, ...leafColumns];
}
}

const FooterWrapper = components.footer.wrapper;
const FooterRow = components.footer.row;
const FooterCell = components.footer.cell;

return (
<FooterWrapper className={`${prefixCls}-tfoot`} key="footer">
<FooterRow>
{leafColumns.map(col =>
<FooterCell
key={col.key || col.dataIndex}
>
{col.footer === undefined ? null : getColumnFooter(col)(data)}
</FooterCell>
)}
</FooterRow>
</FooterWrapper>
);
}

TableFooter.propTypes = {
fixed: PropTypes.string,
columns: PropTypes.array.isRequired,
};

TableFooter.contextTypes = {
table: PropTypes.any,
};