- JSX style API
- selection
- Selection and operation
- Custom selection
- Filter and sorter
- Reset filters and sorters
- Customized filter panel
- Ajax
- size
- border, title and footer
- Expandable Row
- colSpan and rowSpan
- Tree data
- Fixed Header
- Fixed Columns
- Fixed Columns and Header
- Grouping table head
- Editable Cells
- Editable Rows
- Nested tables
- Drag sorting
- Resizable column
- Dynamic Settings
Table
A table displays rows of data.
When To Use#
To display a collection of structured data.
To sort, search, paginate, filter data.
How To Use#
Specify dataSource
of Table as an array of data.
const dataSource = [{
key: '1',
name: 'Mike',
age: 32,
address: '10 Downing Street'
}, {
key: '2',
name: 'John',
age: 42,
address: '10 Downing Street'
}];
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}];
<Table dataSource={dataSource} columns={columns} />
Examples
Name | Age | Address | Tags | Action | |
---|---|---|---|---|---|
First Name | Last Name | ||||
John | Brown | 32 | New York No. 1 Lake Park | nice developer | Invite BrownDelete |
Jim | Green | 42 | London No. 1 Lake Park | loser | Invite GreenDelete |
Joe | Black | 32 | Sidney No. 1 Lake Park | cool teacher | Invite BlackDelete |
import { Table, Divider, Tag } from 'antd';
const { Column, ColumnGroup } = Table;
const data = [{
key: '1',
firstName: 'John',
lastName: 'Brown',
age: 32,
address: 'New York No. 1 Lake Park',
tags: ['nice', 'developer'],
}, {
key: '2',
firstName: 'Jim',
lastName: 'Green',
age: 42,
address: 'London No. 1 Lake Park',
tags: ['loser'],
}, {
key: '3',
firstName: 'Joe',
lastName: 'Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
tags: ['cool', 'teacher'],
}];
ReactDOM.render(
<Table dataSource={data}>
<ColumnGroup title="Name">
<Column
title="First Name"
dataIndex="firstName"
key="firstName"
/>
<Column
title="Last Name"
dataIndex="lastName"
key="lastName"
/>
</ColumnGroup>
<Column
title="Age"
dataIndex="age"
key="age"
/>
<Column
title="Address"
dataIndex="address"
key="address"
/>
<Column
title="Tags"
dataIndex="tags"
key="tags"
render={tags => (
<span>
{tags.map(tag => <Tag color="blue" key={tag}>{tag}</Tag>)}
</span>
)}
/>
<Column
title="Action"
key="action"
render={(text, record) => (
<span>
<a href="javascript:;">Invite {record.lastName}</a>
<Divider type="vertical" />
<a href="javascript:;">Delete</a>
</span>
)}
/>
</Table>,
mountNode
);
Name | Age | Address | |
---|---|---|---|
John Brown | 32 | New York No. 1 Lake Park | |
Jim Green | 42 | London No. 1 Lake Park | |
Joe Black | 32 | Sidney No. 1 Lake Park | |
Disabled User | 99 | Sidney No. 1 Lake Park |
import { Table } from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
render: text => <a href="javascript:;">{text}</a>,
}, {
title: 'Age',
dataIndex: 'age',
}, {
title: 'Address',
dataIndex: 'address',
}];
const data = [{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
}, {
key: '4',
name: 'Disabled User',
age: 99,
address: 'Sidney No. 1 Lake Park',
}];
// rowSelection object indicates the need for row selection
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
getCheckboxProps: record => ({
disabled: record.name === 'Disabled User', // Column configuration not to be checked
name: record.name,
}),
};
ReactDOM.render(
<Table rowSelection={rowSelection} columns={columns} dataSource={data} />,
mountNode
);
Name | Age | Address | |
---|---|---|---|
Edward King 0 | 32 | London, Park Lane no. 0 | |
Edward King 1 | 32 | London, Park Lane no. 1 | |
Edward King 2 | 32 | London, Park Lane no. 2 | |
Edward King 3 | 32 | London, Park Lane no. 3 | |
Edward King 4 | 32 | London, Park Lane no. 4 | |
Edward King 5 | 32 | London, Park Lane no. 5 | |
Edward King 6 | 32 | London, Park Lane no. 6 | |
Edward King 7 | 32 | London, Park Lane no. 7 | |
Edward King 8 | 32 | London, Park Lane no. 8 | |
Edward King 9 | 32 | London, Park Lane no. 9 |
import { Table, Button } from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
}, {
title: 'Age',
dataIndex: 'age',
}, {
title: 'Address',
dataIndex: 'address',
}];
const data = [];
for (let i = 0; i < 46; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
class App extends React.Component {
state = {
selectedRowKeys: [], // Check here to configure the default column
loading: false,
};
start = () => {
this.setState({ loading: true });
// ajax request after empty completing
setTimeout(() => {
this.setState({
selectedRowKeys: [],
loading: false,
});
}, 1000);
}
onSelectChange = (selectedRowKeys) => {
console.log('selectedRowKeys changed: ', selectedRowKeys);
this.setState({ selectedRowKeys });
}
render() {
const { loading, selectedRowKeys } = this.state;
const rowSelection = {
selectedRowKeys,
onChange: this.onSelectChange,
};
const hasSelected = selectedRowKeys.length > 0;
return (
<div>
<div style={{ marginBottom: 16 }}>
<Button
type="primary"
onClick={this.start}
disabled={!hasSelected}
loading={loading}
>
Reload
</Button>
<span style={{ marginLeft: 8 }}>
{hasSelected ? `Selected ${selectedRowKeys.length} items` : ''}
</span>
</div>
<Table rowSelection={rowSelection} columns={columns} dataSource={data} />
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
Name | Age | Address | |
---|---|---|---|
Edward King 0 | 32 | London, Park Lane no. 0 | |
Edward King 1 | 32 | London, Park Lane no. 1 | |
Edward King 2 | 32 | London, Park Lane no. 2 | |
Edward King 3 | 32 | London, Park Lane no. 3 | |
Edward King 4 | 32 | London, Park Lane no. 4 | |
Edward King 5 | 32 | London, Park Lane no. 5 | |
Edward King 6 | 32 | London, Park Lane no. 6 | |
Edward King 7 | 32 | London, Park Lane no. 7 | |
Edward King 8 | 32 | London, Park Lane no. 8 | |
Edward King 9 | 32 | London, Park Lane no. 9 |
import { Table } from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
}, {
title: 'Age',
dataIndex: 'age',
}, {
title: 'Address',
dataIndex: 'address',
}];
const data = [];
for (let i = 0; i < 46; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
class App extends React.Component {
state = {
selectedRowKeys: [], // Check here to configure the default column
};
onSelectChange = (selectedRowKeys) => {
console.log('selectedRowKeys changed: ', selectedRowKeys);
this.setState({ selectedRowKeys });
}
render() {
const { selectedRowKeys } = this.state;
const rowSelection = {
selectedRowKeys,
onChange: this.onSelectChange,
hideDefaultSelections: true,
selections: [{
key: 'all-data',
text: 'Select All Data',
onSelect: () => {
this.setState({
selectedRowKeys: [...Array(46).keys()], // 0...45
});
},
}, {
key: 'odd',
text: 'Select Odd Row',
onSelect: (changableRowKeys) => {
let newSelectedRowKeys = [];
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
if (index % 2 !== 0) {
return false;
}
return true;
});
this.setState({ selectedRowKeys: newSelectedRowKeys });
},
}, {
key: 'even',
text: 'Select Even Row',
onSelect: (changableRowKeys) => {
let newSelectedRowKeys = [];
newSelectedRowKeys = changableRowKeys.filter((key, index) => {
if (index % 2 !== 0) {
return true;
}
return false;
});
this.setState({ selectedRowKeys: newSelectedRowKeys });
},
}],
onSelection: this.onSelection,
};
return (
<Table rowSelection={rowSelection} columns={columns} dataSource={data} />
);
}
}
ReactDOM.render(<App />, mountNode);
Name | Age | Address |
---|---|---|
Jim Green | 42 | London No. 1 Lake Park |
John Brown | 32 | New York No. 1 Lake Park |
Joe Black | 32 | Sidney No. 1 Lake Park |
Jim Red | 32 | London No. 2 Lake Park |
import { Table } from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
filters: [{
text: 'Joe',
value: 'Joe',
}, {
text: 'Jim',
value: 'Jim',
}, {
text: 'Submenu',
value: 'Submenu',
children: [{
text: 'Green',
value: 'Green',
}, {
text: 'Black',
value: 'Black',
}],
}],
// specify the condition of filtering result
// here is that finding the name started with `value`
onFilter: (value, record) => record.name.indexOf(value) === 0,
sorter: (a, b) => a.name.length - b.name.length,
sortDirections: ['descend'],
}, {
title: 'Age',
dataIndex: 'age',
defaultSortOrder: 'descend',
sorter: (a, b) => a.age - b.age,
}, {
title: 'Address',
dataIndex: 'address',
filters: [{
text: 'London',
value: 'London',
}, {
text: 'New York',
value: 'New York',
}],
filterMultiple: false,
onFilter: (value, record) => record.address.indexOf(value) === 0,
sorter: (a, b) => a.address.length - b.address.length,
sortDirections: ['descend', 'ascend'],
}];
const data = [{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
}, {
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
}];
function onChange(pagination, filters, sorter) {
console.log('params', pagination, filters, sorter);
}
ReactDOM.render(
<Table columns={columns} dataSource={data} onChange={onChange} />,
mountNode
);
Name | Age | Address |
---|---|---|
John Brown | 32 | New York No. 1 Lake Park |
Jim Green | 42 | London No. 1 Lake Park |
Joe Black | 32 | Sidney No. 1 Lake Park |
Jim Red | 32 | London No. 2 Lake Park |
import { Table, Button } from 'antd';
const data = [{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
}, {
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
}];
class App extends React.Component {
state = {
filteredInfo: null,
sortedInfo: null,
};
handleChange = (pagination, filters, sorter) => {
console.log('Various parameters', pagination, filters, sorter);
this.setState({
filteredInfo: filters,
sortedInfo: sorter,
});
}
clearFilters = () => {
this.setState({ filteredInfo: null });
}
clearAll = () => {
this.setState({
filteredInfo: null,
sortedInfo: null,
});
}
setAgeSort = () => {
this.setState({
sortedInfo: {
order: 'descend',
columnKey: 'age',
},
});
}
render() {
let { sortedInfo, filteredInfo } = this.state;
sortedInfo = sortedInfo || {};
filteredInfo = filteredInfo || {};
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
filters: [
{ text: 'Joe', value: 'Joe' },
{ text: 'Jim', value: 'Jim' },
],
filteredValue: filteredInfo.name || null,
onFilter: (value, record) => record.name.includes(value),
sorter: (a, b) => a.name.length - b.name.length,
sortOrder: sortedInfo.columnKey === 'name' && sortedInfo.order,
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
sorter: (a, b) => a.age - b.age,
sortOrder: sortedInfo.columnKey === 'age' && sortedInfo.order,
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
filters: [
{ text: 'London', value: 'London' },
{ text: 'New York', value: 'New York' },
],
filteredValue: filteredInfo.address || null,
onFilter: (value, record) => record.address.includes(value),
sorter: (a, b) => a.address.length - b.address.length,
sortOrder: sortedInfo.columnKey === 'address' && sortedInfo.order,
}];
return (
<div>
<div className="table-operations">
<Button onClick={this.setAgeSort}>Sort age</Button>
<Button onClick={this.clearFilters}>Clear filters</Button>
<Button onClick={this.clearAll}>Clear filters and sorters</Button>
</div>
<Table columns={columns} dataSource={data} onChange={this.handleChange} />
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
.table-operations {
margin-bottom: 16px;
}
.table-operations > button {
margin-right: 8px;
}
Name | Age | Address |
---|---|---|
John Brown | 32 | New York No. 1 Lake Park |
Joe Black | 42 | London No. 1 Lake Park |
Jim Green | 32 | Sidney No. 1 Lake Park |
Jim Red | 32 | London No. 2 Lake Park |
import {
Table, Input, Button, Icon,
} from 'antd';
import Highlighter from 'react-highlight-words';
const data = [{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Joe Black',
age: 42,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Jim Green',
age: 32,
address: 'Sidney No. 1 Lake Park',
}, {
key: '4',
name: 'Jim Red',
age: 32,
address: 'London No. 2 Lake Park',
}];
class App extends React.Component {
state = {
searchText: '',
};
getColumnSearchProps = (dataIndex) => ({
filterDropdown: ({
setSelectedKeys, selectedKeys, confirm, clearFilters,
}) => (
<div style={{ padding: 8 }}>
<Input
ref={node => { this.searchInput = node; }}
placeholder={`Search ${dataIndex}`}
value={selectedKeys[0]}
onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => this.handleSearch(selectedKeys, confirm)}
style={{ width: 188, marginBottom: 8, display: 'block' }}
/>
<Button
type="primary"
onClick={() => this.handleSearch(selectedKeys, confirm)}
icon="search"
size="small"
style={{ width: 90, marginRight: 8 }}
>
Search
</Button>
<Button
onClick={() => this.handleReset(clearFilters)}
size="small"
style={{ width: 90 }}
>
Reset
</Button>
</div>
),
filterIcon: filtered => <Icon type="search" style={{ color: filtered ? '#1890ff' : undefined }} />,
onFilter: (value, record) => record[dataIndex].toString().toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: (visible) => {
if (visible) {
setTimeout(() => this.searchInput.select());
}
},
render: (text) => (
<Highlighter
highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
searchWords={[this.state.searchText]}
autoEscape
textToHighlight={text.toString()}
/>
),
})
handleSearch = (selectedKeys, confirm) => {
confirm();
this.setState({ searchText: selectedKeys[0] });
}
handleReset = (clearFilters) => {
clearFilters();
this.setState({ searchText: '' });
}
render() {
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: '30%',
...this.getColumnSearchProps('name'),
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '20%',
...this.getColumnSearchProps('age'),
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
...this.getColumnSearchProps('address'),
}];
return <Table columns={columns} dataSource={data} />;
}
}
ReactDOM.render(<App />, mountNode);
Name | Gender | Email |
---|
No Data
import { Table } from 'antd';
import reqwest from 'reqwest';
const columns = [{
title: 'Name',
dataIndex: 'name',
sorter: true,
render: name => `${name.first} ${name.last}`,
width: '20%',
}, {
title: 'Gender',
dataIndex: 'gender',
filters: [
{ text: 'Male', value: 'male' },
{ text: 'Female', value: 'female' },
],
width: '20%',
}, {
title: 'Email',
dataIndex: 'email',
}];
class App extends React.Component {
state = {
data: [],
pagination: {},
loading: false,
};
componentDidMount() {
this.fetch();
}
handleTableChange = (pagination, filters, sorter) => {
const pager = { ...this.state.pagination };
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
}
fetch = (params = {}) => {
console.log('params:', params);
this.setState({ loading: true });
reqwest({
url: 'https://randomuser.me/api',
method: 'get',
data: {
results: 10,
...params,
},
type: 'json',
}).then((data) => {
const pagination = { ...this.state.pagination };
// Read total count from server
// pagination.total = data.totalCount;
pagination.total = 200;
this.setState({
loading: false,
data: data.results,
pagination,
});
});
}
render() {
return (
<Table
columns={columns}
rowKey={record => record.login.uuid}
dataSource={this.state.data}
pagination={this.state.pagination}
loading={this.state.loading}
onChange={this.handleTableChange}
/>
);
}
}
ReactDOM.render(<App />, mountNode);
import { Table } from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
}, {
title: 'Age',
dataIndex: 'age',
}, {
title: 'Address',
dataIndex: 'address',
}];
const data = [{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
}];
ReactDOM.render(
<div>
<h4>Middle size table</h4>
<Table columns={columns} dataSource={data} size="middle" />
<h4>Small size table</h4>
<Table columns={columns} dataSource={data} size="small" />
</div>,
mountNode
);
Name | Cash Assets | Address |
---|---|---|
John Brown | ¥300,000.00 | New York No. 1 Lake Park |
Jim Green | ¥1,256,000.00 | London No. 1 Lake Park |
Joe Black | ¥120,000.00 | Sidney No. 1 Lake Park |
import { Table } from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
render: text => <a href="javascript:;">{text}</a>,
}, {
title: 'Cash Assets',
className: 'column-money',
dataIndex: 'money',
}, {
title: 'Address',
dataIndex: 'address',
}];
const data = [{
key: '1',
name: 'John Brown',
money: '¥300,000.00',
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Jim Green',
money: '¥1,256,000.00',
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Joe Black',
money: '¥120,000.00',
address: 'Sidney No. 1 Lake Park',
}];
ReactDOM.render(
<Table
columns={columns}
dataSource={data}
bordered
title={() => 'Header'}
footer={() => 'Footer'}
/>,
mountNode
);
th.column-money,
td.column-money {
text-align: right !important;
}
import { Table } from 'antd';
const columns = [
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Age', dataIndex: 'age', key: 'age' },
{ title: 'Address', dataIndex: 'address', key: 'address' },
{
title: 'Action', dataIndex: '', key: 'x', render: () => <a href="javascript:;">Delete</a>,
},
];
const data = [
{
key: 1, name: 'John Brown', age: 32, address: 'New York No. 1 Lake Park', description: 'My name is John Brown, I am 32 years old, living in New York No. 1 Lake Park.',
},
{
key: 2, name: 'Jim Green', age: 42, address: 'London No. 1 Lake Park', description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.',
},
{
key: 3, name: 'Joe Black', age: 32, address: 'Sidney No. 1 Lake Park', description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.',
},
];
ReactDOM.render(
<Table
columns={columns}
expandedRowRender={record => <p style={{ margin: 0 }}>{record.description}</p>}
dataSource={data}
/>,
mountNode
);
Name | Age | Home phone | Address | |
---|---|---|---|---|
John Brown | 32 | 0571-22098909 | 18889898989 | New York No. 1 Lake Park |
Jim Green | 42 | 0571-22098333 | 18889898888 | London No. 1 Lake Park |
Joe Black | 32 | 0575-22098909 | 18900010002 | Sidney No. 1 Lake Park |
Jim Red | 18 | 18900010002 | London No. 2 Lake Park | |
Jake White |
import { Table } from 'antd';
// In the fifth row, other columns are merged into first column
// by setting it's colSpan to be 0
const renderContent = (value, row, index) => {
const obj = {
children: value,
props: {},
};
if (index === 4) {
obj.props.colSpan = 0;
}
return obj;
};
const columns = [{
title: 'Name',
dataIndex: 'name',
render: (text, row, index) => {
if (index < 4) {
return <a href="javascript:;">{text}</a>;
}
return {
children: <a href="javascript:;">{text}</a>,
props: {
colSpan: 5,
},
};
},
}, {
title: 'Age',
dataIndex: 'age',
render: renderContent,
}, {
title: 'Home phone',
colSpan: 2,
dataIndex: 'tel',
render: (value, row, index) => {
const obj = {
children: value,
props: {},
};
if (index === 2) {
obj.props.rowSpan = 2;
}
// These two are merged into above cell
if (index === 3) {
obj.props.rowSpan = 0;
}
if (index === 4) {
obj.props.colSpan = 0;
}
return obj;
},
}, {
title: 'Phone',
colSpan: 0,
dataIndex: 'phone',
render: renderContent,
}, {
title: 'Address',
dataIndex: 'address',
render: renderContent,
}];
const data = [{
key: '1',
name: 'John Brown',
age: 32,
tel: '0571-22098909',
phone: 18889898989,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Jim Green',
tel: '0571-22098333',
phone: 18889898888,
age: 42,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Joe Black',
age: 32,
tel: '0575-22098909',
phone: 18900010002,
address: 'Sidney No. 1 Lake Park',
}, {
key: '4',
name: 'Jim Red',
age: 18,
tel: '0575-22098909',
phone: 18900010002,
address: 'London No. 2 Lake Park',
}, {
key: '5',
name: 'Jake White',
age: 18,
tel: '0575-22098909',
phone: 18900010002,
address: 'Dublin No. 2 Lake Park',
}];
ReactDOM.render(<Table columns={columns} dataSource={data} bordered />,
mountNode);
Name | Age | Address | |
---|---|---|---|
John Brown sr. | 60 | New York No. 1 Lake Park | |
Joe Black | 32 | Sidney No. 1 Lake Park |
import { Table } from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
width: '12%',
}, {
title: 'Address',
dataIndex: 'address',
width: '30%',
key: 'address',
}];
const data = [{
key: 1,
name: 'John Brown sr.',
age: 60,
address: 'New York No. 1 Lake Park',
children: [{
key: 11,
name: 'John Brown',
age: 42,
address: 'New York No. 2 Lake Park',
}, {
key: 12,
name: 'John Brown jr.',
age: 30,
address: 'New York No. 3 Lake Park',
children: [{
key: 121,
name: 'Jimmy Brown',
age: 16,
address: 'New York No. 3 Lake Park',
}],
}, {
key: 13,
name: 'Jim Green sr.',
age: 72,
address: 'London No. 1 Lake Park',
children: [{
key: 131,
name: 'Jim Green',
age: 42,
address: 'London No. 2 Lake Park',
children: [{
key: 1311,
name: 'Jim Green jr.',
age: 25,
address: 'London No. 3 Lake Park',
}, {
key: 1312,
name: 'Jimmy Green sr.',
age: 18,
address: 'London No. 4 Lake Park',
}],
}],
}],
}, {
key: 2,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
}];
// rowSelection objects indicates the need for row selection
const rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
onSelect: (record, selected, selectedRows) => {
console.log(record, selected, selectedRows);
},
onSelectAll: (selected, selectedRows, changeRows) => {
console.log(selected, selectedRows, changeRows);
},
};
ReactDOM.render(
<Table columns={columns} rowSelection={rowSelection} dataSource={data} />,
mountNode
);
Name | Age | Address |
---|
Edward King 0 | 32 | London, Park Lane no. 0 |
Edward King 1 | 32 | London, Park Lane no. 1 |
Edward King 2 | 32 | London, Park Lane no. 2 |
Edward King 3 | 32 | London, Park Lane no. 3 |
Edward King 4 | 32 | London, Park Lane no. 4 |
Edward King 5 | 32 | London, Park Lane no. 5 |
Edward King 6 | 32 | London, Park Lane no. 6 |
Edward King 7 | 32 | London, Park Lane no. 7 |
Edward King 8 | 32 | London, Park Lane no. 8 |
Edward King 9 | 32 | London, Park Lane no. 9 |
Edward King 10 | 32 | London, Park Lane no. 10 |
Edward King 11 | 32 | London, Park Lane no. 11 |
Edward King 12 | 32 | London, Park Lane no. 12 |
Edward King 13 | 32 | London, Park Lane no. 13 |
Edward King 14 | 32 | London, Park Lane no. 14 |
Edward King 15 | 32 | London, Park Lane no. 15 |
Edward King 16 | 32 | London, Park Lane no. 16 |
Edward King 17 | 32 | London, Park Lane no. 17 |
Edward King 18 | 32 | London, Park Lane no. 18 |
Edward King 19 | 32 | London, Park Lane no. 19 |
Edward King 20 | 32 | London, Park Lane no. 20 |
Edward King 21 | 32 | London, Park Lane no. 21 |
Edward King 22 | 32 | London, Park Lane no. 22 |
Edward King 23 | 32 | London, Park Lane no. 23 |
Edward King 24 | 32 | London, Park Lane no. 24 |
Edward King 25 | 32 | London, Park Lane no. 25 |
Edward King 26 | 32 | London, Park Lane no. 26 |
Edward King 27 | 32 | London, Park Lane no. 27 |
Edward King 28 | 32 | London, Park Lane no. 28 |
Edward King 29 | 32 | London, Park Lane no. 29 |
Edward King 30 | 32 | London, Park Lane no. 30 |
Edward King 31 | 32 | London, Park Lane no. 31 |
Edward King 32 | 32 | London, Park Lane no. 32 |
Edward King 33 | 32 | London, Park Lane no. 33 |
Edward King 34 | 32 | London, Park Lane no. 34 |
Edward King 35 | 32 | London, Park Lane no. 35 |
Edward King 36 | 32 | London, Park Lane no. 36 |
Edward King 37 | 32 | London, Park Lane no. 37 |
Edward King 38 | 32 | London, Park Lane no. 38 |
Edward King 39 | 32 | London, Park Lane no. 39 |
Edward King 40 | 32 | London, Park Lane no. 40 |
Edward King 41 | 32 | London, Park Lane no. 41 |
Edward King 42 | 32 | London, Park Lane no. 42 |
Edward King 43 | 32 | London, Park Lane no. 43 |
Edward King 44 | 32 | London, Park Lane no. 44 |
Edward King 45 | 32 | London, Park Lane no. 45 |
Edward King 46 | 32 | London, Park Lane no. 46 |
Edward King 47 | 32 | London, Park Lane no. 47 |
Edward King 48 | 32 | London, Park Lane no. 48 |
Edward King 49 | 32 | London, Park Lane no. 49 |
import { Table } from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
width: 150,
}, {
title: 'Age',
dataIndex: 'age',
width: 150,
}, {
title: 'Address',
dataIndex: 'address',
}];
const data = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i,
name: `Edward King ${i}`,
age: 32,
address: `London, Park Lane no. ${i}`,
});
}
ReactDOM.render(
<Table columns={columns} dataSource={data} pagination={{ pageSize: 50 }} scroll={{ y: 240 }} />,
mountNode
);
Full Name | Age | Column 1 | Column 2 | Column 3 | Column 4 | Column 5 | Column 6 | Column 7 | Column 8 | Action |
---|---|---|---|---|---|---|---|---|---|---|
John Brown | 32 | New York Park | New York Park | New York Park | New York Park | New York Park | New York Park | New York Park | New York Park | action |
Jim Green | 40 | London Park | London Park | London Park | London Park | London Park | London Park | London Park | London Park | action |
Full Name | Age |
---|---|
John Brown | 32 |
Jim Green | 40 |
import { Table } from 'antd';
const columns = [
{
title: 'Full Name', width: 100, dataIndex: 'name', key: 'name', fixed: 'left',
},
{
title: 'Age', width: 100, dataIndex: 'age', key: 'age', fixed: 'left',
},
{ title: 'Column 1', dataIndex: 'address', key: '1' },
{ title: 'Column 2', dataIndex: 'address', key: '2' },
{ title: 'Column 3', dataIndex: 'address', key: '3' },
{ title: 'Column 4', dataIndex: 'address', key: '4' },
{ title: 'Column 5', dataIndex: 'address', key: '5' },
{ title: 'Column 6', dataIndex: 'address', key: '6' },
{ title: 'Column 7', dataIndex: 'address', key: '7' },
{ title: 'Column 8', dataIndex: 'address', key: '8' },
{
title: 'Action',
key: 'operation',
fixed: 'right',
width: 100,
render: () => <a href="javascript:;">action</a>,
},
];
const data = [{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York Park',
}, {
key: '2',
name: 'Jim Green',
age: 40,
address: 'London Park',
}];
ReactDOM.render(<Table columns={columns} dataSource={data} scroll={{ x: 1300 }} />, mountNode);
Full Name | Age | Column 1 | Column 2 | Column 3 | Column 4 | Column 5 | Column 6 | Column 7 | Column 8 | Action |
---|
Edrward 0 | 32 | London Park no. 0 | London Park no. 0 | London Park no. 0 | London Park no. 0 | London Park no. 0 | London Park no. 0 | London Park no. 0 | London Park no. 0 | action |
Edrward 1 | 32 | London Park no. 1 | London Park no. 1 | London Park no. 1 | London Park no. 1 | London Park no. 1 | London Park no. 1 | London Park no. 1 | London Park no. 1 | action |
Edrward 2 | 32 | London Park no. 2 | London Park no. 2 | London Park no. 2 | London Park no. 2 | London Park no. 2 | London Park no. 2 | London Park no. 2 | London Park no. 2 | action |
Edrward 3 | 32 | London Park no. 3 | London Park no. 3 | London Park no. 3 | London Park no. 3 | London Park no. 3 | London Park no. 3 | London Park no. 3 | London Park no. 3 | action |
Edrward 4 | 32 | London Park no. 4 | London Park no. 4 | London Park no. 4 | London Park no. 4 | London Park no. 4 | London Park no. 4 | London Park no. 4 | London Park no. 4 | action |
Edrward 5 | 32 | London Park no. 5 | London Park no. 5 | London Park no. 5 | London Park no. 5 | London Park no. 5 | London Park no. 5 | London Park no. 5 | London Park no. 5 | action |
Edrward 6 | 32 | London Park no. 6 | London Park no. 6 | London Park no. 6 | London Park no. 6 | London Park no. 6 | London Park no. 6 | London Park no. 6 | London Park no. 6 | action |
Edrward 7 | 32 | London Park no. 7 | London Park no. 7 | London Park no. 7 | London Park no. 7 | London Park no. 7 | London Park no. 7 | London Park no. 7 | London Park no. 7 | action |
Edrward 8 | 32 | London Park no. 8 | London Park no. 8 | London Park no. 8 | London Park no. 8 | London Park no. 8 | London Park no. 8 | London Park no. 8 | London Park no. 8 | action |
Edrward 9 | 32 | London Park no. 9 | London Park no. 9 | London Park no. 9 | London Park no. 9 | London Park no. 9 | London Park no. 9 | London Park no. 9 | London Park no. 9 | action |
Full Name | Age |
---|
Edrward 0 | 32 |
Edrward 1 | 32 |
Edrward 2 | 32 |
Edrward 3 | 32 |
Edrward 4 | 32 |
Edrward 5 | 32 |
Edrward 6 | 32 |
Edrward 7 | 32 |
Edrward 8 | 32 |
Edrward 9 | 32 |
import { Table } from 'antd';
const columns = [
{
title: 'Full Name', width: 100, dataIndex: 'name', key: 'name', fixed: 'left',
},
{
title: 'Age', width: 100, dataIndex: 'age', key: 'age', fixed: 'left',
},
{
title: 'Column 1', dataIndex: 'address', key: '1', width: 150,
},
{
title: 'Column 2', dataIndex: 'address', key: '2', width: 150,
},
{
title: 'Column 3', dataIndex: 'address', key: '3', width: 150,
},
{
title: 'Column 4', dataIndex: 'address', key: '4', width: 150,
},
{
title: 'Column 5', dataIndex: 'address', key: '5', width: 150,
},
{
title: 'Column 6', dataIndex: 'address', key: '6', width: 150,
},
{
title: 'Column 7', dataIndex: 'address', key: '7', width: 150,
},
{ title: 'Column 8', dataIndex: 'address', key: '8' },
{
title: 'Action',
key: 'operation',
fixed: 'right',
width: 100,
render: () => <a href="javascript:;">action</a>,
},
];
const data = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i,
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`,
});
}
ReactDOM.render(<Table columns={columns} dataSource={data} scroll={{ x: 1500, y: 300 }} />, mountNode);
Name | Other | Company | Gender | ||||
---|---|---|---|---|---|---|---|
Age | Address | Company Address | Company Name | ||||
Street | Block | ||||||
Building | Door No. |
John Brown | 1 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 2 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 3 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 4 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 5 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 6 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 7 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 8 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 9 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
John Brown | 10 | Lake Park | C | 2035 | Lake Street 42 | SoftLake Co | M |
Name |
---|
John Brown |
John Brown |
John Brown |
John Brown |
John Brown |
John Brown |
John Brown |
John Brown |
John Brown |
John Brown |
Gender |
---|
M |
M |
M |
M |
M |
M |
M |
M |
M |
M |
import { Table } from 'antd';
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: 100,
fixed: 'left',
filters: [{
text: 'Joe',
value: 'Joe',
}, {
text: 'John',
value: 'John',
}],
onFilter: (value, record) => record.name.indexOf(value) === 0,
}, {
title: 'Other',
children: [{
title: 'Age',
dataIndex: 'age',
key: 'age',
width: 200,
sorter: (a, b) => a.age - b.age,
}, {
title: 'Address',
children: [{
title: 'Street',
dataIndex: 'street',
key: 'street',
width: 200,
}, {
title: 'Block',
children: [{
title: 'Building',
dataIndex: 'building',
key: 'building',
width: 100,
}, {
title: 'Door No.',
dataIndex: 'number',
key: 'number',
width: 100,
}],
}],
}],
}, {
title: 'Company',
children: [{
title: 'Company Address',
dataIndex: 'companyAddress',
key: 'companyAddress',
}, {
title: 'Company Name',
dataIndex: 'companyName',
key: 'companyName',
}],
}, {
title: 'Gender',
dataIndex: 'gender',
key: 'gender',
width: 80,
fixed: 'right',
}];
const data = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i,
name: 'John Brown',
age: i + 1,
street: 'Lake Park',
building: 'C',
number: 2035,
companyAddress: 'Lake Street 42',
companyName: 'SoftLake Co',
gender: 'M',
});
}
ReactDOM.render(
<Table
columns={columns}
dataSource={data}
bordered
size="middle"
scroll={{ x: '130%', y: 240 }}
/>,
mountNode
);
import {
Table, Input, Button, Popconfirm, Form,
} from 'antd';
const FormItem = Form.Item;
const EditableContext = React.createContext();
const EditableRow = ({ form, index, ...props }) => (
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
);
const EditableFormRow = Form.create()(EditableRow);
class EditableCell extends React.Component {
state = {
editing: false,
}
componentDidMount() {
if (this.props.editable) {
document.addEventListener('click', this.handleClickOutside, true);
}
}
componentWillUnmount() {
if (this.props.editable) {
document.removeEventListener('click', this.handleClickOutside, true);
}
}
toggleEdit = () => {
const editing = !this.state.editing;
this.setState({ editing }, () => {
if (editing) {
this.input.focus();
}
});
}
handleClickOutside = (e) => {
const { editing } = this.state;
if (editing && this.cell !== e.target && !this.cell.contains(e.target)) {
this.save();
}
}
save = () => {
const { record, handleSave } = this.props;
this.form.validateFields((error, values) => {
if (error) {
return;
}
this.toggleEdit();
handleSave({ ...record, ...values });
});
}
render() {
const { editing } = this.state;
const {
editable,
dataIndex,
title,
record,
index,
handleSave,
...restProps
} = this.props;
return (
<td ref={node => (this.cell = node)} {...restProps}>
{editable ? (
<EditableContext.Consumer>
{(form) => {
this.form = form;
return (
editing ? (
<FormItem style={{ margin: 0 }}>
{form.getFieldDecorator(dataIndex, {
rules: [{
required: true,
message: `${title} is required.`,
}],
initialValue: record[dataIndex],
})(
<Input
ref={node => (this.input = node)}
onPressEnter={this.save}
/>
)}
</FormItem>
) : (
<div
className="editable-cell-value-wrap"
style={{ paddingRight: 24 }}
onClick={this.toggleEdit}
>
{restProps.children}
</div>
)
);
}}
</EditableContext.Consumer>
) : restProps.children}
</td>
);
}
}
class EditableTable extends React.Component {
constructor(props) {
super(props);
this.columns = [{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
}, {
title: 'age',
dataIndex: 'age',
}, {
title: 'address',
dataIndex: 'address',
}, {
title: 'operation',
dataIndex: 'operation',
render: (text, record) => (
this.state.dataSource.length >= 1
? (
<Popconfirm title="Sure to delete?" onConfirm={() => this.handleDelete(record.key)}>
<a href="javascript:;">Delete</a>
</Popconfirm>
) : null
),
}];
this.state = {
dataSource: [{
key: '0',
name: 'Edward King 0',
age: '32',
address: 'London, Park Lane no. 0',
}, {
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
}],
count: 2,
};
}
handleDelete = (key) => {
const dataSource = [...this.state.dataSource];
this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
}
handleAdd = () => {
const { count, dataSource } = this.state;
const newData = {
key: count,
name: `Edward King ${count}`,
age: 32,
address: `London, Park Lane no. ${count}`,
};
this.setState({
dataSource: [...dataSource, newData],
count: count + 1,
});
}
handleSave = (row) => {
const newData = [...this.state.dataSource];
const index = newData.findIndex(item => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
this.setState({ dataSource: newData });
}
render() {
const { dataSource } = this.state;
const components = {
body: {
row: EditableFormRow,
cell: EditableCell,
},
};
const columns = this.columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: this.handleSave,
}),
};
});
return (
<div>
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
Add a row
</Button>
<Table
components={components}
rowClassName={() => 'editable-row'}
bordered
dataSource={dataSource}
columns={columns}
/>
</div>
);
}
}
ReactDOM.render(<EditableTable />, mountNode);
.editable-cell {
position: relative;
}
.editable-cell-value-wrap {
padding: 5px 12px;
cursor: pointer;
}
.editable-row:hover .editable-cell-value-wrap {
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 4px 11px;
}
name | age | address | operation |
---|---|---|---|
Edrward 0 | 32 | London Park no. 0 | |
Edrward 1 | 32 | London Park no. 1 | |
Edrward 2 | 32 | London Park no. 2 | |
Edrward 3 | 32 | London Park no. 3 | |
Edrward 4 | 32 | London Park no. 4 | |
Edrward 5 | 32 | London Park no. 5 | |
Edrward 6 | 32 | London Park no. 6 | |
Edrward 7 | 32 | London Park no. 7 | |
Edrward 8 | 32 | London Park no. 8 | |
Edrward 9 | 32 | London Park no. 9 |
import {
Table, Input, InputNumber, Popconfirm, Form,
} from 'antd';
const data = [];
for (let i = 0; i < 100; i++) {
data.push({
key: i.toString(),
name: `Edrward ${i}`,
age: 32,
address: `London Park no. ${i}`,
});
}
const FormItem = Form.Item;
const EditableContext = React.createContext();
const EditableRow = ({ form, index, ...props }) => (
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
);
const EditableFormRow = Form.create()(EditableRow);
class EditableCell extends React.Component {
getInput = () => {
if (this.props.inputType === 'number') {
return <InputNumber />;
}
return <Input />;
};
render() {
const {
editing,
dataIndex,
title,
inputType,
record,
index,
...restProps
} = this.props;
return (
<EditableContext.Consumer>
{(form) => {
const { getFieldDecorator } = form;
return (
<td {...restProps}>
{editing ? (
<FormItem style={{ margin: 0 }}>
{getFieldDecorator(dataIndex, {
rules: [{
required: true,
message: `Please Input ${title}!`,
}],
initialValue: record[dataIndex],
})(this.getInput())}
</FormItem>
) : restProps.children}
</td>
);
}}
</EditableContext.Consumer>
);
}
}
class EditableTable extends React.Component {
constructor(props) {
super(props);
this.state = { data, editingKey: '' };
this.columns = [
{
title: 'name',
dataIndex: 'name',
width: '25%',
editable: true,
},
{
title: 'age',
dataIndex: 'age',
width: '15%',
editable: true,
},
{
title: 'address',
dataIndex: 'address',
width: '40%',
editable: true,
},
{
title: 'operation',
dataIndex: 'operation',
render: (text, record) => {
const editable = this.isEditing(record);
return (
<div>
{editable ? (
<span>
<EditableContext.Consumer>
{form => (
<a
href="javascript:;"
onClick={() => this.save(form, record.key)}
style={{ marginRight: 8 }}
>
Save
</a>
)}
</EditableContext.Consumer>
<Popconfirm
title="Sure to cancel?"
onConfirm={() => this.cancel(record.key)}
>
<a>Cancel</a>
</Popconfirm>
</span>
) : (
<a onClick={() => this.edit(record.key)}>Edit</a>
)}
</div>
);
},
},
];
}
isEditing = record => record.key === this.state.editingKey;
cancel = () => {
this.setState({ editingKey: '' });
};
save(form, key) {
form.validateFields((error, row) => {
if (error) {
return;
}
const newData = [...this.state.data];
const index = newData.findIndex(item => key === item.key);
if (index > -1) {
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
this.setState({ data: newData, editingKey: '' });
} else {
newData.push(row);
this.setState({ data: newData, editingKey: '' });
}
});
}
edit(key) {
this.setState({ editingKey: key });
}
render() {
const components = {
body: {
row: EditableFormRow,
cell: EditableCell,
},
};
const columns = this.columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
inputType: col.dataIndex === 'age' ? 'number' : 'text',
dataIndex: col.dataIndex,
title: col.title,
editing: this.isEditing(record),
}),
};
});
return (
<Table
components={components}
bordered
dataSource={this.state.data}
columns={columns}
rowClassName="editable-row"
/>
);
}
}
ReactDOM.render(<EditableTable />, mountNode);
.editable-row .ant-form-explain {
position: absolute;
font-size: 12px;
margin-top: -4px;
}
import {
Table, Badge, Menu, Dropdown, Icon,
} from 'antd';
const menu = (
<Menu>
<Menu.Item>
Action 1
</Menu.Item>
<Menu.Item>
Action 2
</Menu.Item>
</Menu>
);
function NestedTable() {
const expandedRowRender = () => {
const columns = [
{ title: 'Date', dataIndex: 'date', key: 'date' },
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Status', key: 'state', render: () => <span><Badge status="success" />Finished</span> },
{ title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
{
title: 'Action',
dataIndex: 'operation',
key: 'operation',
render: () => (
<span className="table-operation">
<a href="javascript:;">Pause</a>
<a href="javascript:;">Stop</a>
<Dropdown overlay={menu}>
<a href="javascript:;">
More <Icon type="down" />
</a>
</Dropdown>
</span>
),
},
];
const data = [];
for (let i = 0; i < 3; ++i) {
data.push({
key: i,
date: '2014-12-24 23:12:00',
name: 'This is production name',
upgradeNum: 'Upgraded: 56',
});
}
return (
<Table
columns={columns}
dataSource={data}
pagination={false}
/>
);
};
const columns = [
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Platform', dataIndex: 'platform', key: 'platform' },
{ title: 'Version', dataIndex: 'version', key: 'version' },
{ title: 'Upgraded', dataIndex: 'upgradeNum', key: 'upgradeNum' },
{ title: 'Creator', dataIndex: 'creator', key: 'creator' },
{ title: 'Date', dataIndex: 'createdAt', key: 'createdAt' },
{ title: 'Action', key: 'operation', render: () => <a href="javascript:;">Publish</a> },
];
const data = [];
for (let i = 0; i < 3; ++i) {
data.push({
key: i,
name: 'Screem',
platform: 'iOS',
version: '10.3.4.5654',
upgradeNum: 500,
creator: 'Jack',
createdAt: '2014-12-24 23:12:00',
});
}
return (
<Table
className="components-table-demo-nested"
columns={columns}
expandedRowRender={expandedRowRender}
dataSource={data}
/>
);
}
ReactDOM.render(<NestedTable />, mountNode);
Name | Age | Address |
---|
import { Table } from 'antd';
import { DragDropContext, DragSource, DropTarget } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import update from 'immutability-helper';
let dragingIndex = -1;
class BodyRow extends React.Component {
render() {
const {
isOver,
connectDragSource,
connectDropTarget,
moveRow,
...restProps
} = this.props;
const style = { ...restProps.style, cursor: 'move' };
let className = restProps.className;
if (isOver) {
if (restProps.index > dragingIndex) {
className += ' drop-over-downward';
}
if (restProps.index < dragingIndex) {
className += ' drop-over-upward';
}
}
return connectDragSource(
connectDropTarget(
<tr
{...restProps}
className={className}
style={style}
/>
)
);
}
}
const rowSource = {
beginDrag(props) {
dragingIndex = props.index;
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;
},
};
const DragableBodyRow = DropTarget(
'row',
rowTarget,
(connect, monitor) => ({
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
}),
)(
DragSource(
'row',
rowSource,
(connect) => ({
connectDragSource: connect.dragSource(),
}),
)(BodyRow),
);
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}];
class DragSortingTable extends React.Component {
state = {
data: [{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
}],
}
components = {
body: {
row: DragableBodyRow,
},
}
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 (
<Table
columns={columns}
dataSource={this.state.data}
components={this.components}
onRow={(record, index) => ({
index,
moveRow: this.moveRow,
})}
/>
);
}
}
const Demo = DragDropContext(HTML5Backend)(DragSortingTable);
ReactDOM.render(<Demo />, mountNode);
#components-table-demo-drag-sorting tr.drop-over-downward td {
border-bottom: 2px dashed #1890ff;
}
#components-table-demo-drag-sorting tr.drop-over-upward td {
border-top: 2px dashed #1890ff;
}
import { Table } from 'antd';
import { Resizable } from 'react-resizable';
const ResizeableTitle = (props) => {
const { onResize, width, ...restProps } = props;
if (!width) {
return <th {...restProps} />;
}
return (
<Resizable width={width} height={0} onResize={onResize}>
<th {...restProps} />
</Resizable>
);
};
class Demo extends React.Component {
state = {
columns: [{
title: 'Date',
dataIndex: 'date',
width: 200,
}, {
title: 'Amount',
dataIndex: 'amount',
width: 100,
}, {
title: 'Type',
dataIndex: 'type',
width: 100,
}, {
title: 'Note',
dataIndex: 'note',
width: 100,
}, {
title: 'Action',
key: 'action',
render: () => (
<a href="javascript:;">Delete</a>
),
}],
};
components = {
header: {
cell: ResizeableTitle,
},
};
data = [{
key: 0,
date: '2018-02-11',
amount: 120,
type: 'income',
note: 'transfer',
}, {
key: 1,
date: '2018-03-11',
amount: 243,
type: 'income',
note: 'transfer',
}, {
key: 2,
date: '2018-04-11',
amount: 98,
type: 'income',
note: 'transfer',
}];
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 (
<Table
bordered
components={this.components}
columns={columns}
dataSource={this.data}
/>
);
}
}
ReactDOM.render(<Demo />, mountNode);
#components-table-demo-resizable-column .react-resizable {
position: relative;
}
#components-table-demo-resizable-column .react-resizable-handle {
position: absolute;
width: 10px;
height: 100%;
bottom: 0;
right: -5px;
cursor: col-resize;
}
Name | Age | Address | Action | ||
---|---|---|---|---|---|
John Brown | 12 | New York No. 1 Lake Park | Action 一 John BrownDeleteMore actions | ||
John Brown | 22 | New York No. 2 Lake Park | Action 一 John BrownDeleteMore actions | ||
John Brown | 32 | New York No. 3 Lake Park | Action 一 John BrownDeleteMore actions | ||
John Brown | 42 | New York No. 4 Lake Park | Action 一 John BrownDeleteMore actions | ||
John Brown | 52 | New York No. 5 Lake Park | Action 一 John BrownDeleteMore actions | ||
John Brown | 62 | New York No. 6 Lake Park | Action 一 John BrownDeleteMore actions | ||
John Brown | 72 | New York No. 7 Lake Park | Action 一 John BrownDeleteMore actions | ||
John Brown | 82 | New York No. 8 Lake Park | Action 一 John BrownDeleteMore actions | ||
John Brown | 92 | New York No. 9 Lake Park | Action 一 John BrownDeleteMore actions | ||
John Brown | 102 | New York No. 10 Lake Park | Action 一 John BrownDeleteMore actions |
import {
Table, Icon, Switch, Radio, Form, Divider,
} from 'antd';
const FormItem = Form.Item;
const columns = [{
title: 'Name',
dataIndex: 'name',
key: 'name',
width: 150,
render: text => <a href="javascript:;">{text}</a>,
}, {
title: 'Age',
dataIndex: 'age',
key: 'age',
width: 70,
}, {
title: 'Address',
dataIndex: 'address',
key: 'address',
}, {
title: 'Action',
key: 'action',
width: 360,
render: (text, record) => (
<span>
<a href="javascript:;">Action 一 {record.name}</a>
<Divider type="vertical" />
<a href="javascript:;">Delete</a>
<Divider type="vertical" />
<a href="javascript:;" className="ant-dropdown-link">
More actions <Icon type="down" />
</a>
</span>
),
}];
const data = [];
for (let i = 1; i <= 10; i++) {
data.push({
key: i,
name: 'John Brown',
age: `${i}2`,
address: `New York No. ${i} Lake Park`,
description: `My name is John Brown, I am ${i}2 years old, living in New York No. ${i} Lake Park.`,
});
}
const expandedRowRender = record => <p>{record.description}</p>;
const title = () => 'Here is title';
const showHeader = true;
const footer = () => 'Here is footer';
const scroll = { y: 240 };
const pagination = { position: 'bottom' };
class Demo extends React.Component {
state = {
bordered: false,
loading: false,
pagination,
size: 'default',
expandedRowRender,
title: undefined,
showHeader,
footer,
rowSelection: {},
scroll: undefined,
hasData: true,
}
handleToggle = prop => (enable) => {
this.setState({ [prop]: enable });
}
handleSizeChange = (e) => {
this.setState({ size: e.target.value });
}
handleExpandChange = (enable) => {
this.setState({ expandedRowRender: enable ? expandedRowRender : undefined });
}
handleTitleChange = (enable) => {
this.setState({ title: enable ? title : undefined });
}
handleHeaderChange = (enable) => {
this.setState({ showHeader: enable ? showHeader : false });
}
handleFooterChange = (enable) => {
this.setState({ footer: enable ? footer : undefined });
}
handleRowSelectionChange = (enable) => {
this.setState({ rowSelection: enable ? {} : undefined });
}
handleScollChange = (enable) => {
this.setState({ scroll: enable ? scroll : undefined });
}
handleDataChange = (hasData) => {
this.setState({ hasData });
}
handlePaginationChange = (e) => {
const { value } = e.target;
this.setState({
pagination: value === 'none' ? false : { position: value },
});
}
render() {
const state = this.state;
return (
<div>
<div className="components-table-demo-control-bar">
<Form layout="inline">
<FormItem label="Bordered">
<Switch checked={state.bordered} onChange={this.handleToggle('bordered')} />
</FormItem>
<FormItem label="loading">
<Switch checked={state.loading} onChange={this.handleToggle('loading')} />
</FormItem>
<FormItem label="Title">
<Switch checked={!!state.title} onChange={this.handleTitleChange} />
</FormItem>
<FormItem label="Column Header">
<Switch checked={!!state.showHeader} onChange={this.handleHeaderChange} />
</FormItem>
<FormItem label="Footer">
<Switch checked={!!state.footer} onChange={this.handleFooterChange} />
</FormItem>
<FormItem label="Expandable">
<Switch checked={!!state.expandedRowRender} onChange={this.handleExpandChange} />
</FormItem>
<FormItem label="Checkbox">
<Switch checked={!!state.rowSelection} onChange={this.handleRowSelectionChange} />
</FormItem>
<FormItem label="Fixed Header">
<Switch checked={!!state.scroll} onChange={this.handleScollChange} />
</FormItem>
<FormItem label="Has Data">
<Switch checked={!!state.hasData} onChange={this.handleDataChange} />
</FormItem>
<FormItem label="Size">
<Radio.Group size="default" value={state.size} onChange={this.handleSizeChange}>
<Radio.Button value="default">Default</Radio.Button>
<Radio.Button value="middle">Middle</Radio.Button>
<Radio.Button value="small">Small</Radio.Button>
</Radio.Group>
</FormItem>
<FormItem label="Pagination">
<Radio.Group
value={state.pagination ? state.pagination.position : 'none'}
onChange={this.handlePaginationChange}
>
<Radio.Button value="top">Top</Radio.Button>
<Radio.Button value="bottom">Bottom</Radio.Button>
<Radio.Button value="both">Both</Radio.Button>
<Radio.Button value="none">None</Radio.Button>
</Radio.Group>
</FormItem>
</Form>
</div>
<Table {...this.state} columns={columns} dataSource={state.hasData ? data : null} />
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
API#
Table#
Property | Description | Type | Default |
---|---|---|---|
bordered | Whether to show all table borders | boolean | false |
childrenColumnName | The column contains children to display | string[] | children |
columns | Columns of table | ColumnProps[] | - |
components | Override default table elements | TableComponents | - |
dataSource | Data record array to be displayed | any[] | - |
defaultExpandAllRows | Expand all rows initially | boolean | false |
defaultExpandedRowKeys | Initial expanded row keys | string[] | - |
expandedRowKeys | Current expanded row keys | string[] | - |
expandedRowRender | Expanded container render for each row | Function(record, index, indent, expanded):ReactNode | - |
expandIcon | Customize row expand Icon. Ref example | Function(props):ReactNode | - |
expandRowByClick | Whether to expand row by clicking anywhere in the whole row | boolean | false |
footer | Table footer renderer | Function(currentPageData) | |
indentSize | Indent size in pixels of tree data | number | 15 |
loading | Loading status of table | boolean|object (more) | false |
locale | i18n text including filter, sort, empty text, etc | object | filterConfirm: 'Ok' filterReset: 'Reset' emptyText: 'No Data' Default |
pagination | Config of pagination. You can ref table pagination config or full pagination document, hide it by setting it to false | object | |
rowClassName | Row's className | Function(record, index):string | - |
rowKey | Row's unique key, could be a string or function that returns a string | string|Function(record):string | key |
rowSelection | Row selection config | object | null |
scroll | Set horizontal or vertical scrolling, can also be used to specify the width and height of the scroll area. It is recommended to set a number for x , if you want to set it to true , you need to add style .ant-table td { white-space: nowrap; } . | { x: number | true, y: number } | - |
showHeader | Whether to show table header | boolean | true |
size | Size of table | default | middle | small | default |
title | Table title renderer | Function(currentPageData) | |
onChange | Callback executed when pagination, filters or sorter is changed | Function(pagination, filters, sorter, extra: { currentDataSource: [] }) | |
onExpand | Callback executed when the row expand icon is clicked | Function(expanded, record) | |
onExpandedRowsChange | Callback executed when the expanded rows change | Function(expandedRows) | |
onHeaderRow | Set props on per header row | Function(column, index) | - |
onRow | Set props on per row | Function(record, index) | - |
onRow usage#
Same as onRow
onHeaderRow
onCell
onHeaderCell
<Table
onRow={(record, rowIndex) => {
return {
onClick: (event) => {}, // click row
onDoubleClick: (event) => {}, // double click row
onContextMenu: (event) => {} // right button click row
onMouseEnter: (event) => {} // mouse enter row
onMouseLeave: (event) => {} // mouse leave row
};
}}
onHeaderRow={(column) => {
return {
onClick: () => {}, // click header row
};
}}
/>
Column#
One of the Table columns
prop for describing the table's columns, Column has the same API.
Property | Description | Type | Default |
---|---|---|---|
align | specify how content is aligned | 'left' | 'right' | 'center' | 'left' |
className | className of this column | string | - |
colSpan | Span of this column's title | number | |
dataIndex | Display field of the data record, could be set like a.b.c , a[0].b.c[1] | string | - |
defaultSortOrder | Default order of sorted values | 'ascend' | 'descend' | - |
filterDropdown | Customized filter overlay | ReactNode | - |
filterDropdownVisible | Whether filterDropdown is visible | boolean | - |
filtered | Whether the dataSource is filtered | boolean | false |
filteredValue | Controlled filtered value, filter icon will highlight | string[] | - |
filterIcon | Customized filter icon | ReactNode|(filtered: boolean) => ReactNode | false |
filterMultiple | Whether multiple filters can be selected | boolean | true |
filters | Filter menu config | object[] | - |
fixed | Set column to be fixed: true (same as left) 'left' 'right' | boolean|string | false |
key | Unique key of this column, you can ignore this prop if you've set a unique dataIndex | string | - |
render | Renderer of the table cell. The return value should be a ReactNode, or an object for colSpan/rowSpan config | Function(text, record, index) {} | - |
sorter | Sort function for local sort, see Array.sort's compareFunction. If you need sort buttons only, set to true | Function|boolean | - |
sortOrder | Order of sorted values: 'ascend' 'descend' false | boolean|string | - |
title | Title of this column | ReactNode|({ sortOrder, filters }) => ReactNode | - |
width | Width of this column | string|number | - |
onCell | Set props on per cell | Function(record, rowIndex) | - |
onFilter | Callback executed when the confirm filter button is clicked | Function | - |
onFilterDropdownVisibleChange | Callback executed when filterDropdownVisible is changed | function(visible) {} | - |
onHeaderCell | Set props on per header cell | Function(column) | - |
ColumnGroup#
Property | Description | Type | Default |
---|---|---|---|
title | Title of the column group | string|ReactNode | - |
pagination#
Properties for pagination.
Property | Description | Type | Default |
---|---|---|---|
position | specify the position of Pagination | 'top' | 'bottom' | 'both' | 'bottom' |
More about pagination, please check Pagination
.
rowSelection#
Properties for row selection.
Property | Description | Type | Default |
---|---|---|---|
columnWidth | Set the width of the selection column | string|number | - |
columnTitle | Set the title of the selection column | string|React.ReactNode | - |
fixed | Fixed selection column on the left | boolean | - |
getCheckboxProps | Get Checkbox or Radio props | Function(record) | - |
hideDefaultSelections | Remove the default Select All and Select Invert selections | boolean | false |
selectedRowKeys | Controlled selected row keys | string[] | [] |
selections | Custom selection config, only displays default selections when set to true | object[]|boolean | - |
type | checkbox or radio | checkbox | radio | checkbox |
onChange | Callback executed when selected rows change | Function(selectedRowKeys, selectedRows) | - |
onSelect | Callback executed when select/deselect one row | Function(record, selected, selectedRows, nativeEvent) | - |
onSelectAll | Callback executed when select/deselect all rows | Function(selected, selectedRows, changeRows) | - |
onSelectInvert | Callback executed when row selection is inverted | Function(selectedRows) | - |
selection#
Property | Description | Type | Default |
---|---|---|---|
key | Unique key of this selection | string | - |
text | Display text of this selection | string|React.ReactNode | - |
onSelect | Callback executed when this selection is clicked | Function(changeableRowKeys) | - |
Using in TypeScript#
import { Table } from 'antd';
import { ColumnProps } from 'antd/lib/table';
interface IUser {
key: number,
name: string;
}
const columns: ColumnProps<IUser>[] = [{
key: 'name',
title: 'Name',
dataIndex: 'name',
}];
const data: IUser[] = [{
key: 0,
name: 'Jack',
}];
class UserTable extends Table<IUser> {}
<UserTable columns={columns} dataSource={data} />
// Use JSX style API
class NameColumn extends Table.Column<IUser> {}
<UserTable dataSource={data}>
<NameColumn key="name" title="Name" dataIndex="name" />
</UserTable>
// after TypeScript 2.9 can write like this
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html#generic-type-arguments-in-jsx-elements
<Table<IUser> columns={columns} dataSource={data} />
<Table<IUser> dataSource={data}>
<Table.Column<IUser> key="name" title="Name" dataIndex="name" />
</Table>
Note#
According to React documentation, every child in array should be assigned a unique key. The values inside dataSource
and columns
should follow this in Table, and dataSource[i].key
would be treated as key value default for dataSource
.
If dataSource[i].key
is not provided, then you should specify the primary key of dataSource value via rowKey
. If not, warnings like above will show in browser console.
// primary key is uid
return <Table rowKey="uid" />;
// or
return <Table rowKey={record => record.uid} />;