List virtualized (#7692)

* add virtualized & infinite demo
* change `children render` to `renderItem`
This commit is contained in:
niko 2017-09-25 15:24:16 +08:00 committed by GitHub
parent ca06211494
commit 91b97d7b25
16 changed files with 451 additions and 744 deletions

View File

@ -12,7 +12,6 @@ export interface ListItemProps {
extra: React.ReactNode;
actions?: Array<React.ReactNode>;
grid?: ListGridType;
Meta: React.ReactNode;
}
export interface ListItemMetaProps {

View File

@ -31,7 +31,7 @@ exports[`renders ./components/list/demo/basic.md correctly 1`] = `
<a
href="https://ant.design"
>
Ant design
Ant Design Title 1
</a>
</h4>
<div
@ -94,7 +94,7 @@ exports[`renders ./components/list/demo/basic.md correctly 1`] = `
<a
href="https://ant.design"
>
Ant design
Ant Design Title 2
</a>
</h4>
<div
@ -157,7 +157,7 @@ exports[`renders ./components/list/demo/basic.md correctly 1`] = `
<a
href="https://ant.design"
>
Ant design
Ant Design Title 3
</a>
</h4>
<div
@ -220,7 +220,7 @@ exports[`renders ./components/list/demo/basic.md correctly 1`] = `
<a
href="https://ant.design"
>
Ant design
Ant Design Title 4
</a>
</h4>
<div
@ -258,14 +258,14 @@ exports[`renders ./components/list/demo/basic.md correctly 1`] = `
</ul>
</div>
<div
class="ant-list-more"
style="text-align:center;margin-top:8px;"
>
<button
class="ant-btn"
type="button"
>
<span>
加载更多...
加载更多
</span>
</button>
</div>
@ -305,7 +305,7 @@ exports[`renders ./components/list/demo/grid.md correctly 1`] = `
<div
class="ant-card-head-title"
>
Card title
Title 1
</div>
</div>
</div>
@ -344,7 +344,7 @@ exports[`renders ./components/list/demo/grid.md correctly 1`] = `
<div
class="ant-card-head-title"
>
Card title
Title 2
</div>
</div>
</div>
@ -383,7 +383,7 @@ exports[`renders ./components/list/demo/grid.md correctly 1`] = `
<div
class="ant-card-head-title"
>
Card title
Title 3
</div>
</div>
</div>
@ -422,85 +422,7 @@ exports[`renders ./components/list/demo/grid.md correctly 1`] = `
<div
class="ant-card-head-title"
>
Card title
</div>
</div>
</div>
<div
class="ant-card-body"
>
<div>
Card content
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-col-6"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px;"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-card ant-card-bordered"
>
<div>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
Card title
</div>
</div>
</div>
<div
class="ant-card-body"
>
<div>
Card content
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-col-6"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px;"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-card ant-card-bordered"
>
<div>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
Card title
Title 4
</div>
</div>
</div>
@ -522,210 +444,31 @@ exports[`renders ./components/list/demo/grid.md correctly 1`] = `
`;
exports[`renders ./components/list/demo/infinite-load.md correctly 1`] = `
<div>
<div
class="ant-list ant-list-bordered"
>
<div />
</div>
</div>
`;
exports[`renders ./components/list/demo/infinite-virtualized-load.md correctly 1`] = `
<div
class="ant-list ant-list-bordered ant-list-grid ant-list-infinite"
class="ant-list ant-list-bordered"
>
<div>
<div />
<div
style="overflow:visible;width:0;"
>
<div
class="ant-row"
style="margin-left:-8px;margin-right:-8px;"
>
<div
class="ant-col-6"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px;"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-card ant-card-bordered"
>
<div>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
List Item Title 1
</div>
</div>
</div>
<div
class="ant-card-body"
>
<div>
List Item Content 1
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-col-6"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px;"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-card ant-card-bordered"
>
<div>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
List Item Title 2
</div>
</div>
</div>
<div
class="ant-card-body"
>
<div>
List Item Content 2
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-col-6"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px;"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-card ant-card-bordered"
>
<div>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
List Item Title 3
</div>
</div>
</div>
<div
class="ant-card-body"
>
<div>
List Item Content 3
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-col-6"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px;"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-card ant-card-bordered"
>
<div>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
List Item Title 4
</div>
</div>
</div>
<div
class="ant-card-body"
>
<div>
List Item Content 4
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-col-6"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px;"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-card ant-card-bordered"
>
<div>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
List Item Title 5
</div>
</div>
</div>
<div
class="ant-card-body"
>
<div>
List Item Content 5
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
aria-label="grid"
aria-readonly="true"
class="ReactVirtualized__Grid ReactVirtualized__List"
role="grid"
style="box-sizing:border-box;direction:ltr;height:auto;position:relative;width:0;-webkit-overflow-scrolling:touch;will-change:transform;overflow-x:hidden;overflow-y:hidden;"
tabindex="0"
/>
</div>
</div>
`;
@ -761,7 +504,7 @@ exports[`renders ./components/list/demo/meta.md correctly 1`] = `
<a
href="https://ant.design"
>
Ant design
Ant Design Title 1
</a>
</h4>
<div
@ -801,7 +544,7 @@ exports[`renders ./components/list/demo/meta.md correctly 1`] = `
<a
href="https://ant.design"
>
Ant design
Ant Design Title 2
</a>
</h4>
<div
@ -841,7 +584,7 @@ exports[`renders ./components/list/demo/meta.md correctly 1`] = `
<a
href="https://ant.design"
>
Ant design
Ant Design Title 3
</a>
</h4>
<div
@ -881,7 +624,7 @@ exports[`renders ./components/list/demo/meta.md correctly 1`] = `
<a
href="https://ant.design"
>
Ant design
Ant Design Title 4
</a>
</h4>
<div
@ -931,7 +674,7 @@ exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
<div
class="ant-card-head-title"
>
Card title
Title 1
</div>
</div>
</div>
@ -970,7 +713,7 @@ exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
<div
class="ant-card-head-title"
>
Card title
Title 2
</div>
</div>
</div>
@ -1009,7 +752,7 @@ exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
<div
class="ant-card-head-title"
>
Card title
Title 3
</div>
</div>
</div>
@ -1048,85 +791,7 @@ exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
<div
class="ant-card-head-title"
>
Card title
</div>
</div>
</div>
<div
class="ant-card-body"
>
<div>
Card content
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-col-xs-12 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px;"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-card ant-card-bordered"
>
<div>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
Card title
</div>
</div>
</div>
<div
class="ant-card-body"
>
<div>
Card content
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-col-xs-12 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px;"
>
<div
class="ant-list-item-content ant-list-item-content-single"
>
<div
class="ant-card ant-card-bordered"
>
<div>
<div
class="ant-card-head"
>
<div
class="ant-card-head-wrapper"
>
<div
class="ant-card-head-title"
>
Card title
Title 4
</div>
</div>
</div>

View File

@ -14,46 +14,62 @@ title:
Basic List.
````jsx
import { List, Avatar } from 'antd';
import { List, Avatar, Button, Icon } from 'antd';
ReactDOM.render(
<List
itemLayout="horizontal"
showLoadMore
onLoadMore={() => {}}
>
<List.Item actions={[<a>编辑</a>, <a>更多</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">Ant design</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
const data = [
{
title: 'Ant Design Title 1',
},
{
title: 'Ant Design Title 2',
},
{
title: 'Ant Design Title 3',
},
{
title: 'Ant Design Title 4',
},
];
class BasicList extends React.Component {
state = {
loadingMore: false,
showLoadingMore: true,
}
onLoadMore = () => {
this.setState({
loadingMore: true,
});
}
render() {
const { loadingMore, showLoadingMore } = this.state;
const loadMore = showLoadingMore ? (
<div style={{ textAlign: 'center', marginTop: 8 }}>
<Button onClick={this.onLoadMore}>
{loadingMore && (<span><Icon type="loading" /> 加载中...</span>)}
{!loadingMore && (<span>加载更多</span>)}
</Button>
</div>
) : null;
return (
<List
itemLayout="horizontal"
loadMore={loadMore}
dataSource={data}
renderItem={item => (
<List.Item actions={[<a>编辑</a>, <a>更多</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">{item.title}</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
<div style={{ padding: 24 }}>Content</div>
</List.Item>
)}
/>
<div style={{ padding: 24 }}>Content</div>
</List.Item>
<List.Item actions={[<a>编辑</a>, <a>更多</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">Ant design</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
<div style={{ padding: 24 }}>Content</div>
</List.Item>
<List.Item actions={[<a>编辑</a>, <a>更多</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">Ant design</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
<div style={{ padding: 24 }}>Content</div>
</List.Item>
<List.Item actions={[<a>编辑</a>, <a>更多</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">Ant design</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
<div style={{ padding: 24 }}>Content</div>
</List.Item>
</List>
, mountNode);
);
}
}
ReactDOM.render(<BasicList />, mountNode);
````

View File

@ -16,28 +16,30 @@ Grid List.
````jsx
import { List, Card } from 'antd';
const data = [
{
title: 'Title 1',
},
{
title: 'Title 2',
},
{
title: 'Title 3',
},
{
title: 'Title 4',
},
];
ReactDOM.render(
<List
grid={{ gutter: 16, column: 4 }}
>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
</List>
dataSource={data}
renderItem={item => (
<List.Item>
<Card title={item.title}>Card content</Card>
</List.Item>
)}
/>
, mountNode);
````

View File

@ -1,83 +1,97 @@
---
order: 6
order: 7
title:
zh-CN: 无限加载
en-US: infinite load
zh-CN: 滚动加载
en-US: Scrolling loaded List
---
## zh-CN
无限加载样例
结合 [react-infinite-scroller](https://github.com/CassetteRocks/react-infinite-scroller) 实现滚动自动加载列表
## en-US
The example of infinite load.
The example of infinite load with [react-infinite-scroller](https://github.com/CassetteRocks/react-infinite-scroller).
````jsx
import { List, Card, message } from 'antd';
import { List, message, Avatar, Spin } from 'antd';
import reqwest from 'reqwest';
let countId = 1;
import InfiniteScroll from 'react-infinite-scroller';
function mockData() {
const data = [];
for (let i = 0; i < 5; i++) {
const id = countId;
data.push({
id: `id-${id}`,
title: `List Item Title ${id}`,
content: `List Item Content ${id}`,
});
countId++;
}
return data;
}
const fakeDataUrl = 'https://randomapi.com/api/dleyg4om?key=Z51U-D9OX-SXIO-SLJ9&fmt=raw&sole';
class InfiniteList extends React.Component {
class InfiniteListExample extends React.Component {
state = {
data: mockData(),
data: [],
loading: false,
hasMore: true,
}
handleInfiniteOnLoad = (done) => {
getData = (callback) => {
reqwest({
url: fakeDataUrl,
type: 'json',
method: 'get',
contentType: 'application/json',
success: (res) => {
callback(res);
},
});
}
componentWillMount() {
this.getData((res) => {
this.setState({
data: res,
});
});
}
handleInfiniteOnLoad = () => {
let data = this.state.data;
if (data.length > 15) {
message.warning('Loaded All');
return;
}
this.setState({
loading: true,
});
setTimeout(() => {
data = data.concat(mockData());
if (data.length > 14) {
message.warning('Infinite List loaded all');
this.setState({
hasMore: false,
loading: false,
});
return;
}
this.getData((res) => {
data = data.concat(res);
this.setState({
data,
loading: false,
});
// reset the infinite onLoad callback flag
// so can trigger onLoad callback again
done();
}, 1000);
});
}
render() {
return (
<List
infinite={{
loading: this.state.loading,
onLoad: this.handleInfiniteOnLoad,
offset: -20,
}}
grid={{ gutter: 16, column: 4 }}
<InfiniteScroll
pageStart={0}
loadMore={this.handleInfiniteOnLoad}
hasMore={!this.state.loading && this.state.hasMore}
>
{
this.state.data.map(item => (
<List
dataSource={this.state.data}
renderItem={item => (
<List.Item key={item.id}>
<Card title={item.title}>{item.content}</Card>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">{item.title}</a>}
description={item.content}
/>
<div style={{ padding: 24 }}>Content</div>
</List.Item>
))
}
</List>
)}
>
{this.state.loading && this.state.hasMore && <Spin style={{ position: 'absolute', bottom: 0, left: '50%' }} />}
</List>
</InfiniteScroll>
);
}
}
ReactDOM.render(<InfiniteList />, mountNode);
````
ReactDOM.render(<InfiniteListExample />, mountNode);
````

View File

@ -0,0 +1,136 @@
---
order: 6
title:
zh-CN: 滚动加载无限长列表
en-US: Infinite virtualized List
---
## zh-CN
结合 [react-virtualized](https://github.com/bvaughn/react-virtualized) 实现滚动加载无限长列表,带有虚拟化([virtualization](https://blog.jscrambler.com/optimizing-react-rendering-through-virtualization/))功能,能够提高数据量大时候长列表的性能。
## en-US
The example of infinite & virtualized load with [react-virtualized](https://github.com/bvaughn/react-virtualized).
````jsx
import { List, message, Avatar, Spin } from 'antd';
import reqwest from 'reqwest';
import WindowScroller from 'react-virtualized/dist/commonjs/WindowScroller';
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
import VList from 'react-virtualized/dist/commonjs/List';
import InfiniteLoader from 'react-virtualized/dist/commonjs/InfiniteLoader';
const fakeDataUrl = 'https://randomapi.com/api/dleyg4om?key=Z51U-D9OX-SXIO-SLJ9&fmt=raw&sole';
class VirtualizedExample extends React.Component {
state = {
data: [],
loading: false,
}
loadedRowsMap = {}
getData = (callback) => {
reqwest({
url: fakeDataUrl,
type: 'json',
method: 'get',
contentType: 'application/json',
success: (res) => {
callback(res);
},
});
}
componentWillMount() {
this.getData((res) => {
this.setState({
data: res,
});
});
}
handleInfiniteOnLoad = ({ startIndex, stopIndex }) => {
let data = this.state.data;
this.setState({
loading: true,
});
for (let i = startIndex; i <= stopIndex; i++) {
// 1 means loading
this.loadedRowsMap[i] = 1;
}
if (data.length > 19) {
message.warning('Virtualized List loaded all');
this.setState({
loading: false,
});
return;
}
this.getData((res) => {
data = data.concat(res);
this.setState({
data,
loading: false,
});
});
}
isRowLoaded = ({ index }) => {
return !!this.loadedRowsMap[index];
}
renderItem = ({ index, key, style }) => {
const { data } = this.state;
const item = data[index];
return (
<List.Item key={key} style={style}>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">{item.title}</a>}
description={item.content}
/>
<div style={{ padding: 24 }}>Content</div>
</List.Item>
);
}
render() {
const { data } = this.state;
const vlist = ({ height, isScrolling, onChildScroll, scrollTop, onRowsRendered, width }) => (
<VList
autoHeight
height={height}
isScrolling={isScrolling}
onScroll={onChildScroll}
overscanRowCount={2}
rowCount={data.length}
rowHeight={117}
rowRenderer={this.renderItem}
onRowsRendered={onRowsRendered}
scrollTop={scrollTop}
width={width}
/>
);
const autoSize = ({ height, isScrolling, onChildScroll, scrollTop, onRowsRendered }) => (
<AutoSizer disableHeight>
{({ width }) => vlist({ height, isScrolling, onChildScroll, scrollTop, onRowsRendered, width })}
</AutoSizer>
);
const infiniteLoader = ({ height, isScrolling, onChildScroll, scrollTop }) => (
<InfiniteLoader
isRowLoaded={this.isRowLoaded}
loadMoreRows={this.handleInfiniteOnLoad}
rowCount={data.length}
>
{({ onRowsRendered }) => autoSize({ height, isScrolling, onChildScroll, scrollTop, onRowsRendered })}
</InfiniteLoader>
);
return (
<List>
<WindowScroller scrollElement={null}>
{infiniteLoader}
</WindowScroller>
{this.state.loading && <Spin style={{ position: 'absolute', bottom: 0, left: '50%' }} />}
</List>
);
}
}
ReactDOM.render(<VirtualizedExample />, mountNode);
````

View File

@ -16,38 +16,34 @@ List with avatar and description.
````jsx
import { List, Avatar } from 'antd';
const data = [
{
title: 'Ant Design Title 1',
},
{
title: 'Ant Design Title 2',
},
{
title: 'Ant Design Title 3',
},
{
title: 'Ant Design Title 4',
},
];
ReactDOM.render(
<List
itemLayout="horizontal"
>
<List.Item>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">Ant design</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
</List.Item>
<List.Item>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">Ant design</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
</List.Item>
<List.Item>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">Ant design</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
</List.Item>
<List.Item>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">Ant design</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
</List.Item>
</List>
dataSource={data}
renderItem={item => (
<List.Item>
<List.Item.Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title={<a href="https://ant.design">{item.title}</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
</List.Item>
)}
/>
, mountNode);
````

View File

@ -16,28 +16,30 @@ Responsive Grid List.
````jsx
import { List, Card } from 'antd';
const data = [
{
title: 'Title 1',
},
{
title: 'Title 2',
},
{
title: 'Title 3',
},
{
title: 'Title 4',
},
];
ReactDOM.render(
<List
grid={{ gutter: 16, xs: 2, sm: 2, md: 4, lg: 4, xl: 6 }}
>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
<List.Item>
<Card title="Card title">Card content</Card>
</List.Item>
</List>
dataSource={data}
renderItem={item => (
<List.Item>
<Card title={item.title}>Card content</Card>
</List.Item>
)}
/>
, mountNode);
````

View File

@ -16,15 +16,19 @@ Simple List.
````jsx
import { List } from 'antd';
const data = [
'Racing car sprays burning fuel into crowd.',
'Japanese princess to wed commoner.',
'Australian walks 100km after outback crash.',
'Man charged over missing wedding girl.',
'Los Angeles battles huge wildfires.',
];
ReactDOM.render(
<List
itemLayout="horizontal"
>
<List.Item>Racing car sprays burning fuel into crowd.</List.Item>
<List.Item>Japanese princess to wed commoner.</List.Item>
<List.Item>Australian walks 100km after outback crash.</List.Item>
<List.Item>Man charged over missing wedding girl.</List.Item>
<List.Item>Los Angeles battles huge wildfires.</List.Item>
</List>
dataSource={data}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
, mountNode);
````

View File

@ -42,23 +42,24 @@ const IconText = ({ type, text }) => (
);
ReactDOM.render(
<List itemLayout="vertical" pagination={pagination}>
{
listData.map(item => (
<List.Item
key={item.title}
actions={[<IconText type="star-o" text="156" />, <IconText type="like-o" text="156" />, <IconText type="message" text="2" />]}
extra={<img width={272} alt="logo" src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png" />}
>
<List.Item.Meta
avatar={<Avatar src={item.avatar} />}
title={<a href={item.href}>{item.title}</a>}
description={item.description}
/>
{item.content}
</List.Item>
))
}
</List>
<List
itemLayout="vertical"
pagination={pagination}
dataSource={listData}
renderItem={item => (
<List.Item
key={item.title}
actions={[<IconText type="star-o" text="156" />, <IconText type="like-o" text="156" />, <IconText type="message" text="2" />]}
extra={<img width={272} alt="logo" src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png" />}
>
<List.Item.Meta
avatar={<Avatar src={item.avatar} />}
title={<a href={item.href}>{item.title}</a>}
description={item.description}
/>
{item.content}
</List.Item>
)}
/>
, mountNode);
````

View File

@ -20,12 +20,9 @@ A list can be used to display content related to a single subject. The content c
| bordered | - | string \| boolean | false |
| loading | -| boolean | false |
| itemLayout | - | string | - |
| showLoadMore | -| boolean | false |
| loadingMore | - | boolean | false |
| onMoreClick | -| function | - |
| loadMore | -| string\|ReactNode | - |
| pagination | - | boolean \| object | false |
| grid | - | object | - |
| infinite | - | object | - |
### List grid props
| Property | Description | Type | Default |
@ -38,13 +35,6 @@ A list can be used to display content related to a single subject. The content c
| lg | `≥1200px` - | number | - |
| xl | `≥1600px` - | number | - |
### List infinite props
| Property | Description | Type | Default |
---------|-------------|------|---------
| onLoad | - | function | - |
| offset | - | number | 0 |
| loading | - | boolean | - |
### List.Item
| Property | Description | Type | Default |
@ -59,4 +49,3 @@ A list can be used to display content related to a single subject. The content c
| avatar | - | ReactNode | - |
| title | - | string\|ReactNode | - |
| description | - | string\|ReactNode | - |

View File

@ -1,14 +1,10 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import Spin from '../spin';
import Icon from '../icon';
import Pagination from '../pagination';
import Button from '../button';
import { Row } from '../grid';
import { throttleByAnimationFrameDecorator } from '../_util/throttleByAnimationFrame';
import Item from './Item';
@ -22,60 +18,21 @@ export interface ListGridType {
xl?: 1 | 2 | 3 | 4 | 6 | 8 | 12 | 24;
}
export interface InfinitePropType {
onLoad?: any;
loading?: boolean;
offset?: number;
}
export interface ListProps {
bordered?: boolean;
className?: string;
children?: React.ReactNode;
extra?: React.ReactNode;
id?: string;
itemLayout: string;
loading?: boolean;
showLoadMore?: boolean;
loadingMore?: boolean;
onLoadMore?: React.FormEventHandler<any>;
loadMore?: React.ReactNode;
pagination?: any;
prefixCls?: string;
grid?: ListGridType;
infinite?: InfinitePropType;
}
function getDefaultTarget() {
return typeof window !== 'undefined' ?
window : null;
}
function getOffsetTop(element?: HTMLElement): number {
if (!element) {
return 0;
}
if (!element.getClientRects().length) {
return 0;
}
const rect = element.getBoundingClientRect();
if (rect.width || rect.height) {
const doc = element.ownerDocument;
const docElem = doc.documentElement;
return rect.top - docElem.clientTop;
}
return rect.top;
}
function getViewportHeight() {
const win = getDefaultTarget();
if (!win) {
return 0;
}
return Math.max(win.document.documentElement.clientHeight, win.innerHeight || 0);
itemLayout?: string;
rowKey?: any;
dataSource: any;
renderItem: any;
}
export default class List extends Component<ListProps> {
@ -85,23 +42,7 @@ export default class List extends Component<ListProps> {
grid: PropTypes.any,
};
scrollEvent: any;
resizeEvent: any;
timeout: any;
events = [
'resize',
'scroll',
'touchstart',
'touchmove',
'touchend',
'pageshow',
'load',
];
infiniteLoaded = false;
eventHandlers = {};
private node: any;
private keys = {};
getChildContext() {
return {
@ -109,76 +50,25 @@ export default class List extends Component<ListProps> {
};
}
componentDidMount() {
if (!this.isInfinite()) {
return;
}
const target = getDefaultTarget;
renderItem = (item, index) => {
const { dataSource, renderItem, rowKey } = this.props;
let key;
// Wait for parent component ref has its value
this.timeout = setTimeout(() => {
this.setTargetEventListeners(target);
});
}
componentWillUnmount() {
if (!this.isInfinite()) {
return;
}
this.clearEventListeners();
clearTimeout(this.timeout);
(this.infiniteLoad as any).cancel();
}
getNode = (n) => {
this.node = n;
}
isInfinite() {
const { infinite } = this.props;
return infinite && (typeof infinite.onLoad === 'function');
}
setTargetEventListeners(getTarget) {
const target = getTarget();
if (!target) {
return;
}
this.clearEventListeners();
this.events.forEach(eventName => {
this.eventHandlers[eventName] = addEventListener(target, eventName, this.infiniteLoad);
});
}
clearEventListeners() {
this.events.forEach(eventName => {
const handler = this.eventHandlers[eventName];
if (handler && handler.remove) {
handler.remove();
}
});
}
@throttleByAnimationFrameDecorator()
infiniteLoad() {
const { infinite = {} } = this.props;
const targetElement = this.node;
if (this.infiniteLoaded || !targetElement) {
return;
if (typeof rowKey === 'function') {
key = rowKey(dataSource[index]);
} else if (typeof rowKey === 'string') {
key = dataSource[rowKey];
} else {
key = dataSource.key;
}
const viewportHeight = getViewportHeight();
const eleOffsetTop = getOffsetTop(targetElement);
const bottomPositionY = eleOffsetTop + targetElement.offsetHeight - viewportHeight + (infinite.offset || 0);
if (bottomPositionY < 0) {
this.infiniteLoaded = true;
infinite.onLoad(() => {
this.infiniteLoaded = false;
});
if (!key) {
key = `list-item-${index}`;
}
this.keys[index] = key;
return renderItem(item, index);
}
render() {
@ -188,14 +78,13 @@ export default class List extends Component<ListProps> {
children,
loading = false,
itemLayout,
showLoadMore = false,
loadingMore = false,
onLoadMore = (() => {
}),
loadMore,
pagination = false,
prefixCls = 'ant-list',
grid,
infinite = {},
dataSource = [],
rowKey,
renderItem,
...rest,
} = this.props;
@ -204,50 +93,42 @@ export default class List extends Component<ListProps> {
[`${prefixCls}-bordered`]: bordered,
[`${prefixCls}-loading`]: loading,
[`${prefixCls}-grid`]: grid,
[`${prefixCls}-infinite`]: infinite.onLoad,
});
const moreButton = (
<Button onClick={onLoadMore}>
<Icon type="loading"/>
...
</Button>
);
const moreContent = (
<div className={`${prefixCls}-more`}>
{loadingMore ? moreButton : <Button onClick={onLoadMore}>...</Button>}
</div>
);
const paginationContent = (
<div className={`${prefixCls}-pagination`}>
<Pagination {...pagination} />
</div>
);
const childrenList = React.Children.map(dataSource.map((item: any, index) => this.renderItem(item, index)),
(child: any, index) => React.cloneElement(child, {
key: this.keys[index],
}),
);
const childrenContent = grid ? (
<Row gutter={grid.gutter}>{children}</Row>
) : children;
<Row gutter={grid.gutter}>{childrenList}</Row>
) : childrenList;
const content = loading ? (
<Spin>
{childrenContent}
{showLoadMore && moreContent}
{(!showLoadMore && pagination) && paginationContent}
{loadMore}
{(!loadMore && pagination) && paginationContent}
</Spin>
) : (
) : (
<div>
{childrenContent}
{showLoadMore && moreContent}
{(!showLoadMore && pagination) && paginationContent}
{infinite.loading && (<div className={`${prefixCls}-spin`}><Spin /></div>)}
{loadMore}
{(!loadMore && pagination) && paginationContent}
</div>
);
return (
<div className={classString} {...rest} ref={this.getNode}>
<div className={classString} {...rest}>
{content}
{children}
</div>
);
}

View File

@ -21,12 +21,9 @@ cols: 1
| bordered | 是否展示边框 | boolean | false |
| loading | 当卡片内容还在加载中时,可以用 loading 展示一个占位 | boolean | false |
| itemLayout | 设置 List.Item 布局, 设置成 vertical 则竖直样式显示, 默认横排 | string | - |
| showLoadMore | 是否显示加载更多按钮 | boolean | false |
| loadingMore | 是否显示加载更多按钮的 loading 状态 | boolean | false |
| onMoreClick | 点击 more 按钮的回调 | function | - |
| loadMore | 加载更多 | string\|ReactNode | - |
| pagination | 对应的 pagination 配置, 设置 false 不显示 | boolean \| object | false |
| grid | 列表栅格 | object | - |
| infinite | 无限加载配置 | object | - |
### List grid props
| 参数 | 说明 | 类型 | 默认值 |
@ -39,13 +36,6 @@ cols: 1
| lg | `≥1200px` 展示的列数 | number | - |
| xl | `≥1600px` 展示的列数 | number | - |
### List infinite props
| 参数 | 说明 | 类型 | 默认值 |
---------|-------------|------|---------
| onLoad | 无限加载的回调函数 | function | - |
| offset | 触发加载位置的偏移量 | number | 0 |
| loading | 显示无限加载时的 loading | boolean | - |
### List.Item
| 参数 | 说明 | 类型 | 默认值 |

View File

@ -5,7 +5,10 @@
.@{list-prefix-cls} {
.reset-component;
position: relative;
* {
outline: none;
}
&-more, &-pagination {
margin-top: 24px;
text-align: center;
@ -162,6 +165,13 @@
}
}
.@{list-prefix-cls}-empty {
color: @text-color-secondary;
padding: 16px 0;
font-size: 12px;
text-align: center;
}
@media screen and (max-width: @screen-md) {
.@{list-prefix-cls} {
&-item {
@ -170,6 +180,7 @@
}
}
}
.@{list-prefix-cls}-vertical {
.@{list-prefix-cls}-item {
&-extra {
@ -188,6 +199,7 @@
}
}
}
.@{list-prefix-cls}-vertical {
.@{list-prefix-cls}-item {
&-extra-wrap {

View File

@ -3,7 +3,5 @@ import './index.less';
// style dependencies
import '../../spin/style';
import '../../icon/style';
import '../../pagination/style';
import '../../button/style';
import '../../grid/style';

View File

@ -135,9 +135,11 @@
"react-document-title": "^2.0.1",
"react-dom": "^15.0.0",
"react-github-button": "^0.1.1",
"react-infinite-scroller": "^1.0.15",
"react-intl": "^2.0.1",
"react-sublime-video": "^0.2.0",
"react-test-renderer": "^15.5.4",
"react-virtualized": "^9.10.1",
"reqwest": "^2.0.5",
"rimraf": "^2.5.4",
"stylelint": "^8.0.0",