mirror of
https://github.com/ant-design/ant-design.git
synced 2025-08-05 23:46:28 +08:00
Feature list component (#7150)
* add list component * fixed the demo syntax * update list snapshots
This commit is contained in:
parent
2b1bed42c9
commit
12bbe3776c
@ -25,6 +25,7 @@ Array [
|
||||
"Input",
|
||||
"InputNumber",
|
||||
"Layout",
|
||||
"List",
|
||||
"LocaleProvider",
|
||||
"message",
|
||||
"Menu",
|
||||
|
@ -57,6 +57,8 @@ export { default as InputNumber } from './input-number';
|
||||
|
||||
export { default as Layout } from './layout';
|
||||
|
||||
export { default as List } from './list';
|
||||
|
||||
export { default as LocaleProvider } from './locale-provider';
|
||||
|
||||
export { default as message } from './message';
|
||||
|
122
components/list/Item.tsx
Normal file
122
components/list/Item.tsx
Normal file
@ -0,0 +1,122 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import Icon from '../icon';
|
||||
|
||||
export interface ListItemProps {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
prefixCls?: string;
|
||||
style?: React.CSSProperties;
|
||||
extra: React.ReactNode;
|
||||
Meta: React.ReactNode;
|
||||
Content: React.ReactNode;
|
||||
Action: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface ListItemMetaProps {
|
||||
avatar?: React.ReactNode;
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
description: React.ReactNode;
|
||||
prefixCls?: string;
|
||||
style?: React.CSSProperties;
|
||||
title: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface ListItemContentProps {
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
prefixCls?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export interface ListItemActionProps {
|
||||
actions: any[];
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
prefixCls?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export const Meta = (props: ListItemMetaProps) => {
|
||||
const {
|
||||
prefixCls = 'ant-list',
|
||||
className,
|
||||
avatar,
|
||||
title,
|
||||
description,
|
||||
...others,
|
||||
} = props;
|
||||
|
||||
const classString = classNames(`${prefixCls}-item-meta`, className);
|
||||
|
||||
const content = (
|
||||
<div className={`${prefixCls}-item-meta-content`}>
|
||||
{title && <h4 className={`${prefixCls}-item-meta-title`}>{title}</h4>}
|
||||
{description && <p className={`${prefixCls}-item-meta-description`}>{description}</p>}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div {...others} className={classString}>
|
||||
{avatar && <div className={`${prefixCls}-item-meta-avatar`}>{avatar}</div>}
|
||||
{(title || description) && content}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Content = (props: ListItemContentProps) => {
|
||||
const { prefixCls = 'ant-list', children, className, ...others } = props;
|
||||
const classString = classNames(`${prefixCls}-item-content`, className);
|
||||
return (
|
||||
<div {...others} className={classString}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Action = (props: ListItemActionProps) => {
|
||||
const { prefixCls = 'ant-list', children, actions, className, ...others } = props;
|
||||
const classString = classNames(`${prefixCls}-item-action`, className);
|
||||
|
||||
const actionsContent = actions && actions.map((action, i) => (
|
||||
<span
|
||||
key={`antd-list-item-action-${action.text}-${i}`}
|
||||
className={`${prefixCls}-item-action-item`}
|
||||
onClick={action.onClick || (() => {})}
|
||||
>
|
||||
{action.icon && <Icon type={action.icon}/>}
|
||||
{action.text}
|
||||
{i !== (actions.length - 1) && <em className={`${prefixCls}-item-action-item-split`}/>}
|
||||
</span>
|
||||
));
|
||||
|
||||
return (
|
||||
<div {...others} className={classString}>
|
||||
{actions ? actionsContent : children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default class Item extends React.Component<ListItemProps, any> {
|
||||
static Meta: typeof Meta = Meta;
|
||||
static Content: typeof Content = Content;
|
||||
static Action: typeof Action = Action;
|
||||
|
||||
render() {
|
||||
const { prefixCls = 'ant-list', children, extra, className, ...others } = this.props;
|
||||
const classString = classNames(`${prefixCls}-item`, className);
|
||||
|
||||
const extraContent = <div className={`${prefixCls}-item-extra-wrap`}>
|
||||
<div className={`${prefixCls}-item-main`}>{children}</div>
|
||||
<div className={`${prefixCls}-item-extra`}>{extra}</div>
|
||||
</div>;
|
||||
|
||||
return (
|
||||
<div {...others} className={classString}>
|
||||
{extra ? extraContent : children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
1203
components/list/__tests__/__snapshots__/demo.test.js.snap
Normal file
1203
components/list/__tests__/__snapshots__/demo.test.js.snap
Normal file
File diff suppressed because it is too large
Load Diff
4
components/list/__tests__/demo.test.js
Normal file
4
components/list/__tests__/demo.test.js
Normal file
@ -0,0 +1,4 @@
|
||||
import demoTest from '../../../tests/shared/demoTest';
|
||||
|
||||
demoTest('list');
|
||||
|
79
components/list/demo/basic.md
Normal file
79
components/list/demo/basic.md
Normal file
@ -0,0 +1,79 @@
|
||||
---
|
||||
order: 0
|
||||
title:
|
||||
zh-CN: 基础列表
|
||||
en-US: Basic
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
基础列表。
|
||||
|
||||
## en-US
|
||||
|
||||
Basic List.
|
||||
|
||||
````jsx
|
||||
import { List, Avatar } from 'antd';
|
||||
|
||||
ReactDOM.render(
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
showLoadMore
|
||||
onLoadMore={() => {}}
|
||||
>
|
||||
<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.Content>
|
||||
Content
|
||||
</List.Item.Content>
|
||||
<List.Item.Action>
|
||||
<a>edit</a> | <a>more</a>
|
||||
</List.Item.Action>
|
||||
</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.Content>
|
||||
Content
|
||||
</List.Item.Content>
|
||||
<List.Item.Action>
|
||||
<a>edit</a> | <a>more</a>
|
||||
</List.Item.Action>
|
||||
</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.Content>
|
||||
Content
|
||||
</List.Item.Content>
|
||||
<List.Item.Action>
|
||||
<a>edit</a> | <a>more</a>
|
||||
</List.Item.Action>
|
||||
</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.Content>
|
||||
Content
|
||||
</List.Item.Content>
|
||||
<List.Item.Action>
|
||||
<a>edit</a> | <a>more</a>
|
||||
</List.Item.Action>
|
||||
</List.Item>
|
||||
</List>
|
||||
, mountNode);
|
||||
````
|
74
components/list/demo/vertical.md
Normal file
74
components/list/demo/vertical.md
Normal file
@ -0,0 +1,74 @@
|
||||
---
|
||||
order: 1
|
||||
title:
|
||||
zh-CN: 竖排列表样式
|
||||
en-US: Layout Vertical
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
基础列表。
|
||||
|
||||
## en-US
|
||||
|
||||
Basic List.
|
||||
|
||||
````jsx
|
||||
import { List, Avatar } from 'antd';
|
||||
|
||||
const listData = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
listData.push({
|
||||
href: 'http://ant.design',
|
||||
title: `ant design part ${i}`,
|
||||
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
|
||||
description: 'Ant Design, a design language for background applications, is refined by Ant UED Team.',
|
||||
content: 'We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.',
|
||||
});
|
||||
}
|
||||
|
||||
const pagination = {
|
||||
pageSize: 10,
|
||||
current: 1,
|
||||
total: listData.length,
|
||||
onChange: (() => {}),
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
<List itemLayout="vertical" pagination={pagination}>
|
||||
{
|
||||
listData.map(item => (
|
||||
<List.Item
|
||||
key={item.title}
|
||||
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}
|
||||
/>
|
||||
<List.Item.Content>
|
||||
{item.content}
|
||||
</List.Item.Content>
|
||||
<List.Item.Action
|
||||
actions={[
|
||||
{
|
||||
icon: 'star-o',
|
||||
text: 156,
|
||||
},
|
||||
{
|
||||
icon: 'like-o',
|
||||
text: 156,
|
||||
},
|
||||
{
|
||||
icon: 'message',
|
||||
text: 2,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</List.Item>
|
||||
))
|
||||
}
|
||||
</List>
|
||||
, mountNode);
|
||||
````
|
45
components/list/index.en-US.md
Normal file
45
components/list/index.en-US.md
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
category: Components
|
||||
type: Data Display
|
||||
title: List
|
||||
cols: 1
|
||||
---
|
||||
|
||||
Simple List.
|
||||
|
||||
## When To Use
|
||||
|
||||
A list can be used to display content related to a single subject. The content can consist of multiple elements of varying type and size.
|
||||
|
||||
## API
|
||||
|
||||
### List
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
|----------|----------------|----------|--------------|
|
||||
| bordered | - | string \| boolean | false |
|
||||
| loading | -| boolean | false |
|
||||
| itemLayout | - | string | - |
|
||||
| showLoadMore | -| boolean | false |
|
||||
| loadingMore | - | boolean | false |
|
||||
| onMoreClick | -| function | - |
|
||||
| pagination | - | boolean \| object | false |
|
||||
|
||||
### List.Item
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
---------|-------------|------|---------
|
||||
| extra | - | string\|ReactNode | - |
|
||||
|
||||
### List.Item.Meta
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
---------|-------------|------|---------
|
||||
| avatar | - | ReactNode | - |
|
||||
| title | - | string\|ReactNode | - |
|
||||
| description | - | string\|ReactNode | - |
|
||||
|
||||
### List.Item.Action
|
||||
| Property | Description | Type | Default |
|
||||
---------|-------------|------|---------
|
||||
| actions | - | Array | - |
|
85
components/list/index.tsx
Normal file
85
components/list/index.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import React, { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import Spin from '../spin';
|
||||
import Icon from '../icon';
|
||||
import Pagination from '../pagination';
|
||||
import Button from '../button';
|
||||
|
||||
import Item from './Item';
|
||||
|
||||
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>;
|
||||
pagination?: any;
|
||||
prefixCls?: string;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export default class List extends Component<ListProps> {
|
||||
static Item: typeof Item = Item;
|
||||
|
||||
render() {
|
||||
const {
|
||||
bordered = true,
|
||||
className,
|
||||
children,
|
||||
loading = false,
|
||||
itemLayout,
|
||||
showLoadMore = false,
|
||||
loadingMore = false,
|
||||
onLoadMore = (() => {
|
||||
}),
|
||||
pagination = false,
|
||||
prefixCls = 'ant-list',
|
||||
} = this.props;
|
||||
|
||||
const classString = classNames(prefixCls, className, {
|
||||
[`${prefixCls}-vertical`]: itemLayout === 'vertical',
|
||||
[`${prefixCls}-bordered`]: bordered,
|
||||
[`${prefixCls}-loading`]: loading,
|
||||
});
|
||||
|
||||
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 loadingContent = (
|
||||
<div className={`${prefixCls}-spin`}>
|
||||
<Spin />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={classString}>
|
||||
{loading && loadingContent}
|
||||
{!loading && children}
|
||||
{showLoadMore && moreContent}
|
||||
{(!showLoadMore && pagination) && paginationContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
54
components/list/index.zh-CN.md
Normal file
54
components/list/index.zh-CN.md
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
category: Components
|
||||
type: Data Display
|
||||
title: List
|
||||
subtitle: 列表
|
||||
cols: 1
|
||||
---
|
||||
|
||||
通用列表。
|
||||
|
||||
## 何时使用
|
||||
|
||||
最基础的列表展示,可承载文字、列表、图片、段落,常用于后台数据展示页面。
|
||||
|
||||
## API
|
||||
|
||||
### List
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|----------|----------------|----------|--------------|
|
||||
| bordered | 是否展示边框 | boolean | false |
|
||||
| loading | 当卡片内容还在加载中时,可以用 loading 展示一个占位 | boolean | false |
|
||||
| itemLayout | 设置 List.Item 布局, 设置成 vertical 则竖直样式显示, 默认横排 | string | - |
|
||||
| showLoadMore | 是否显示加载更多按钮 | boolean | false |
|
||||
| loadingMore | 是否显示加载更多按钮的 loading 状态 | boolean | false |
|
||||
| onMoreClick | 点击 more 按钮的回调 | function | - |
|
||||
| pagination | 对应的 pagination 配置, 设置 false 不显示 | boolean \| object | false |
|
||||
|
||||
### List.Item
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
---------|-------------|------|---------
|
||||
| extra | 额外内容, 通常用在 itemLayout 为 vertical 的情况下, 展示右侧内容; horizontal 展示在列表元素最右侧 | string\|ReactNode | - |
|
||||
|
||||
### List.Item.Meta
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
---------|-------------|------|---------
|
||||
| avatar | 列表元素的图标 | ReactNode | - |
|
||||
| title | 列表元素的标题 | string\|ReactNode | - |
|
||||
| description | 列表元素的描述内容 | string\|ReactNode | - |
|
||||
|
||||
### List.Item.Action
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
---------|-------------|------|---------
|
||||
| actions | 如果此参数存在, 那么会将其中的数据转换成符合标准 ant design 设计的 action 样式元素 | Array | - |
|
||||
|
||||
### List.Item.Action actions props
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
|----------|----------------|----------|-----------------|
|
||||
| icon | icon 图标 | string| - |
|
||||
| text | 文案 | string | - |
|
||||
| onClick | 点击回掉 | function | - |
|
146
components/list/style/index.less
Normal file
146
components/list/style/index.less
Normal file
@ -0,0 +1,146 @@
|
||||
@import "../../style/themes/default";
|
||||
@import "../../style/mixins/index";
|
||||
|
||||
@list-prefix-cls: ~"@{ant-prefix}-list";
|
||||
|
||||
.@{list-prefix-cls} {
|
||||
&-more, &-pagination {
|
||||
margin-top: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
&-more {
|
||||
button {
|
||||
padding-left: 32px;
|
||||
padding-right: 32px;
|
||||
}
|
||||
}
|
||||
&-spin {
|
||||
text-align: center;
|
||||
min-height: 48px;
|
||||
}
|
||||
&-item {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding-top: 24px;
|
||||
padding-bottom: 24px;
|
||||
&:hover {
|
||||
.@{list-prefix-cls}-item-meta-title {
|
||||
color: @primary-color;
|
||||
a {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
&-meta {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
font-size: 0;
|
||||
&-avatar {
|
||||
flex: 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
&-content {
|
||||
flex: 1 0;
|
||||
}
|
||||
&-title {
|
||||
color: @text-color;
|
||||
margin-bottom: 4px;
|
||||
font-size: @font-size-base;
|
||||
line-height: 22px;
|
||||
a {
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
&-description {
|
||||
color: @text-color-secondary;
|
||||
font-size: @font-size-base;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
&-content {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
&-action {
|
||||
flex: 0 1 auto;
|
||||
margin-left: 48px;
|
||||
&-item {
|
||||
color: @text-color-secondary;
|
||||
cursor: pointer;
|
||||
padding: 0 16px;
|
||||
position: relative;
|
||||
font-size: @font-size-base;
|
||||
line-height: 22px;
|
||||
i {
|
||||
margin-right: 8px;
|
||||
}
|
||||
&-split {
|
||||
background-color: @border-color-split;
|
||||
margin-top: -7px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
&-item:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
&-main {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
&-extra {
|
||||
flex: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{list-prefix-cls}-bordered {
|
||||
.@{list-prefix-cls}-item {
|
||||
border-bottom: 1px solid @border-color-base;
|
||||
padding-bottom: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
.@{list-prefix-cls}-vertical {
|
||||
.@{list-prefix-cls}-item {
|
||||
display: block;
|
||||
padding-bottom: 8px;
|
||||
&-extra-wrap {
|
||||
display: flex;
|
||||
}
|
||||
&-main {
|
||||
display: block;
|
||||
flex: 1;
|
||||
}
|
||||
&-extra {
|
||||
margin-left: 58px;
|
||||
flex: 0;
|
||||
}
|
||||
&-meta {
|
||||
margin-bottom: 16px;
|
||||
&-avatar {
|
||||
display: none;
|
||||
}
|
||||
&-title {
|
||||
color: @heading-color;
|
||||
margin-bottom: 12px;
|
||||
font-size: @font-size-lg;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
&-content {
|
||||
display: block;
|
||||
color: @text-color;
|
||||
font-size: @font-size-base;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
&-action {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
8
components/list/style/index.tsx
Normal file
8
components/list/style/index.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
|
||||
// style dependencies
|
||||
import '../../spin/style';
|
||||
import '../../icon/style';
|
||||
import '../../pagination/style';
|
||||
import '../../button/style';
|
@ -25,6 +25,7 @@ Array [
|
||||
"Input",
|
||||
"InputNumber",
|
||||
"Layout",
|
||||
"List",
|
||||
"LocaleProvider",
|
||||
"message",
|
||||
"Menu",
|
||||
|
Loading…
Reference in New Issue
Block a user