Merge branch 'feature'

This commit is contained in:
Wei Zhu 2018-09-30 16:53:28 +08:00
commit 1c1a67fe1d
95 changed files with 4344 additions and 2377 deletions

View File

@ -244,6 +244,272 @@ exports[`renders ./components/alert/demo/close-text.md correctly 1`] = `
</div>
`;
exports[`renders ./components/alert/demo/custom-icon.md correctly 1`] = `
<div>
<div
class="ant-alert ant-alert-success ant-alert-no-icon"
data-show="true"
>
<span
class="ant-alert-message"
>
showIcon = false
</span>
<span
class="ant-alert-description"
/>
</div>
<div
class="ant-alert ant-alert-success"
data-show="true"
>
<i
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Success Tips
</span>
<span
class="ant-alert-description"
/>
</div>
<div
class="ant-alert ant-alert-info"
data-show="true"
>
<i
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Informational Notes
</span>
<span
class="ant-alert-description"
/>
</div>
<div
class="ant-alert ant-alert-warning"
data-show="true"
>
<i
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Warning
</span>
<span
class="ant-alert-description"
/>
</div>
<div
class="ant-alert ant-alert-error"
data-show="true"
>
<i
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Error
</span>
<span
class="ant-alert-description"
/>
</div>
<div
class="ant-alert ant-alert-success ant-alert-with-description"
data-show="true"
>
<i
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Success Tips
</span>
<span
class="ant-alert-description"
>
Detailed description and advices about successful copywriting.
</span>
</div>
<div
class="ant-alert ant-alert-info ant-alert-with-description"
data-show="true"
>
<i
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Informational Notes
</span>
<span
class="ant-alert-description"
>
Additional description and informations about copywriting.
</span>
</div>
<div
class="ant-alert ant-alert-warning ant-alert-with-description"
data-show="true"
>
<i
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Warning
</span>
<span
class="ant-alert-description"
>
This is a warning notice about copywriting.
</span>
</div>
<div
class="ant-alert ant-alert-error ant-alert-with-description"
data-show="true"
>
<i
class="anticon anticon-smile ant-alert-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
<span
class="ant-alert-message"
>
Error
</span>
<span
class="ant-alert-description"
>
This is an error message about copywriting.
</span>
</div>
</div>
`;
exports[`renders ./components/alert/demo/description.md correctly 1`] = `
<div>
<div

View File

@ -0,0 +1,59 @@
---
order: 12
debug: true
title:
zh-CN: 自定义图标
en-US: Custom Icon
---
## zh-CN
可口的图标让信息类型更加醒目。
## en-US
Decent icon make information more clear and more friendly.
````jsx
import { Alert, Icon } from 'antd';
const icon = <Icon type="smile" />;
ReactDOM.render(
<div>
<Alert icon={icon} message="showIcon = false" type="success" />
<Alert icon={icon} message="Success Tips" type="success" showIcon />
<Alert icon={icon} message="Informational Notes" type="info" showIcon />
<Alert icon={icon} message="Warning" type="warning" showIcon />
<Alert icon={icon} message="Error" type="error" showIcon />
<Alert
icon={icon}
message="Success Tips"
description="Detailed description and advices about successful copywriting."
type="success"
showIcon
/>
<Alert
icon={icon}
message="Informational Notes"
description="Additional description and informations about copywriting."
type="info"
showIcon
/>
<Alert
icon={icon}
message="Warning"
description="This is a warning notice about copywriting."
type="warning"
showIcon
/>
<Alert
icon={icon}
message="Error"
description="This is an error message about copywriting."
type="error"
showIcon
/>
</div>,
mountNode);
````

View File

@ -20,7 +20,7 @@ Alert component for feedback.
| closable | Whether Alert can be closed | boolean | - |
| closeText | Close text to show | string\|ReactNode | - |
| description | Additional content of Alert | string\|ReactNode | - |
| iconType | Icon type, effective when `showIcon` is `true` | string | - |
| icon | Custom icon, effective when `showIcon` is `true` | ReactNode | - |
| message | Content of Alert | string\|ReactNode | - |
| showIcon | Whether to show icon | boolean | false, in `banner` mode default is true |
| type | Type of Alert styles, options: `success`, `info`, `warning`, `error` | string | `info`, in `banner` mode default is `warning` |

View File

@ -31,6 +31,7 @@ export interface AlertProps {
prefixCls?: string;
className?: string;
banner?: boolean;
icon?: React.ReactNode;
}
export default class Alert extends React.Component<AlertProps, any> {
@ -64,7 +65,7 @@ export default class Alert extends React.Component<AlertProps, any> {
render() {
let {
closable, description, type, prefixCls = 'ant-alert', message, closeText, showIcon, banner,
className = '', style, iconType,
className = '', style, iconType, icon,
} = this.props;
// banner模式默认有 Icon
@ -73,6 +74,8 @@ export default class Alert extends React.Component<AlertProps, any> {
type = banner && type === undefined ? 'warning' : type || 'info';
let iconTheme: ThemeType = 'filled';
// should we give a warning?
// warning(!iconType, `The property 'iconType' is deprecated. Use the property 'icon' instead.`);
if (!iconType) {
switch (type) {
case 'success':
@ -117,7 +120,19 @@ export default class Alert extends React.Component<AlertProps, any> {
const dataOrAriaProps = getDataOrAriaProps(this.props);
const iconNode = <Icon className={`${prefixCls}-icon`} type={iconType} theme={iconTheme} />;
const iconNode = icon && (
React.isValidElement<{ className?: string }>(icon)
? React.cloneElement(
icon,
{
className: classNames({
[icon.props.className!]: icon.props.className,
[`${prefixCls}-icon`]: true,
}),
},
) : <span className={`${prefixCls}-icon`}>{icon}</span>) || (
<Icon className={`${prefixCls}-icon`} type={iconType} theme={iconTheme} />
);
return this.state.closed ? null : (
<Animate

View File

@ -21,7 +21,7 @@ title: Alert
| closable | 默认不显示关闭按钮 | boolean | 无 |
| closeText | 自定义关闭按钮 | string\|ReactNode | 无 |
| description | 警告提示的辅助性文字介绍 | string\|ReactNode | 无 |
| iconType | 自定义图标类型,`showIcon` 为 `true` 时有效 | string | - |
| icon | 自定义图标,`showIcon` 为 `true` 时有效 | ReactNode | - |
| message | 警告提示内容 | string\|ReactNode | 无 |
| showIcon | 是否显示辅助图标 | boolean | false`banner` 模式下默认值为 true |
| type | 指定警告提示的样式,有四种选择 `success`、`info`、`warning`、`error` | string | `info``banner` 模式下默认值为 `warning` |

View File

@ -425,15 +425,17 @@ exports[`renders ./components/badge/demo/change.md correctly 1`] = `
data-show="true"
/>
</span>
<span
<button
aria-checked="true"
checked=""
class="ant-switch ant-switch-checked"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
</div>
</div>
`;

View File

@ -372,14 +372,16 @@ exports[`renders ./components/card/demo/inner.md correctly 1`] = `
exports[`renders ./components/card/demo/loading.md correctly 1`] = `
<div>
<span
<button
aria-checked="false"
class="ant-switch"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
<div
class="ant-card ant-card-loading ant-card-bordered"
style="width:300px;margin-top:16px"

View File

@ -493,3 +493,63 @@ exports[`renders ./components/cascader/demo/size.md correctly 1`] = `
<br />
</div>
`;
exports[`renders ./components/cascader/demo/suffix.md correctly 1`] = `
<div>
<span
class="ant-cascader-picker"
tabindex="0"
>
<span
class="ant-cascader-picker-label"
/>
<input
autocomplete="off"
class="ant-input ant-cascader-input "
placeholder="Please select"
readonly=""
type="text"
value=""
/>
<i
class="anticon anticon-smile ant-cascader-picker-arrow"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
</span>
<span
class="ant-cascader-picker"
style="margin-top:1rem"
tabindex="0"
>
<span
class="ant-cascader-picker-label"
/>
<input
autocomplete="off"
class="ant-input ant-cascader-input "
placeholder="Please select"
readonly=""
type="text"
value=""
/>
<span
class="ant-cascader-picker-arrow"
>
ab
</span>
</span>
</div>
`;

View File

@ -0,0 +1,65 @@
---
order: 11
debug: true
title:
zh-CN: 后缀图标
en-US: Suffix
---
## zh-CN
省市区级联。
## en-US
Cascade selection box for selecting province/city/district.
````jsx
import { Cascader, Icon } from 'antd';
const options = [{
value: 'zhejiang',
label: 'Zhejiang',
children: [{
value: 'hangzhou',
label: 'Hangzhou',
children: [{
value: 'xihu',
label: 'West Lake',
}],
}],
}, {
value: 'jiangsu',
label: 'Jiangsu',
children: [{
value: 'nanjing',
label: 'Nanjing',
children: [{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
}],
}],
}];
function onChange(value) {
console.log(value);
}
ReactDOM.render(
<div>
<Cascader
suffixIcon={<Icon type="smile" />}
options={options}
onChange={onChange}
placeholder="Please select"
/>
<Cascader
suffixIcon="ab"
style={{ marginTop: '1rem' }}
options={options}
onChange={onChange}
placeholder="Please select"
/>
</div>,
mountNode);
````

View File

@ -40,6 +40,7 @@ Cascade selection box.
| showSearch | Whether show search input in single mode. | boolean\|object | false |
| size | input size, one of `large` `default` `small` | string | `default` |
| style | additional style | string | - |
| suffixIcon | The custom suffix icon | ReactNode | - |
| value | selected value | string\[] | - |
| onChange | callback when finishing cascader select | `(value, selectedOptions) => void` | - |
| onPopupVisibleChange | callback when popup shown or hidden | `(value) => void` | - |

View File

@ -83,6 +83,7 @@ export interface CascaderProps {
getPopupContainer?: (triggerNode?: HTMLElement) => HTMLElement;
popupVisible?: boolean;
fieldNames?: FieldNamesType;
suffixIcon?: React.ReactNode;
}
export interface CascaderState {
@ -341,7 +342,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
const { props, state } = this;
const {
prefixCls, inputPrefixCls, children, placeholder, size, disabled,
className, style, allowClear, showSearch = false, ...otherProps
className, style, allowClear, showSearch = false, suffixIcon, ...otherProps
} = props;
const { value, inputFocused } = state;
@ -414,6 +415,20 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
dropdownMenuColumnStyle.width = this.input.input.offsetWidth;
}
const inputIcon = suffixIcon && (
React.isValidElement<{ className?: string }>(suffixIcon)
? React.cloneElement(
suffixIcon,
{
className: classNames({
[suffixIcon.props.className!]: suffixIcon.props.className,
[`${prefixCls}-picker-arrow`]: true,
}),
},
) : <span className={`${prefixCls}-picker-arrow`}>{suffixIcon}</span>) || (
<Icon type="down" className={arrowCls} />
);
const input = children || (
<span
style={style}
@ -438,7 +453,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
onChange={showSearch ? this.handleInputChange : undefined}
/>
{clearIcon}
<Icon type="down" className={arrowCls} />
{inputIcon}
</span>
);
@ -452,9 +467,11 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
</span>
);
const rest = omit(props, ['inputIcon', 'expandIcon', 'loadingIcon']);
return (
<RcCascader
{...props}
{...rest}
options={options}
value={value}
popupVisible={state.popupVisible}

View File

@ -41,6 +41,7 @@ subtitle: 级联选择
| showSearch | 在选择框中显示搜索框 | boolean | false |
| size | 输入框大小,可选 `large` `default` `small` | string | `default` |
| style | 自定义样式 | string | - |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - |
| value | 指定选中项 | string\[] | - |
| onChange | 选择完成后的回调 | `(value, selectedOptions) => void` | - |
| onPopupVisibleChange | 显示/隐藏浮层的回调 | `(value) => void` | - |

View File

@ -254,7 +254,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
disabledDate, disabledTime,
showTime, showToday,
ranges, onOk, locale, localeCode, format,
dateRender, onCalendarChange,
dateRender, onCalendarChange, suffixIcon,
} = props;
fixLocale(value, localeCode);
@ -328,6 +328,20 @@ class RangePicker extends React.Component<any, RangePickerState> {
/>
) : null;
const inputIcon = suffixIcon && (
React.isValidElement<{ className?: string }>(suffixIcon)
? React.cloneElement(
suffixIcon,
{
className: classNames({
[suffixIcon.props.className!]: suffixIcon.props.className,
[`${prefixCls}-picker-icon`]: true,
}),
},
) : <span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>) || (
<Icon type="calendar" className={`${prefixCls}-picker-icon`} />
);
const input = ({ value: inputValue }: { value: any }) => {
const start = inputValue[0];
const end = inputValue[1];
@ -351,7 +365,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
tabIndex={-1}
/>
{clearIcon}
<Icon type="calendar" className={`${prefixCls}-picker-icon`}/>
{inputIcon}
</span>
);
};

View File

@ -87,7 +87,7 @@ class WeekPicker extends React.Component<any, any> {
const {
prefixCls, className, disabled, pickerClass, popupStyle,
pickerInputClass, format, allowClear, locale, localeCode, disabledDate,
style, onFocus, onBlur, id,
style, onFocus, onBlur, id, suffixIcon,
} = this.props;
const pickerValue = this.state.value;
@ -118,7 +118,22 @@ class WeekPicker extends React.Component<any, any> {
theme="filled"
/>
) : null;
const input = ({ value }: { value: moment.Moment | undefined }) => {
const inputIcon = suffixIcon && (
React.isValidElement<{ className?: string }>(suffixIcon)
? React.cloneElement(
suffixIcon,
{
className: classNames({
[suffixIcon.props.className!]: suffixIcon.props.className,
[`${prefixCls}-picker-icon`]: true,
}),
},
) : <span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>) || (
<Icon type="calendar" className={`${prefixCls}-picker-icon`} />
);
const input = ({ value }: { value: moment.Moment | undefined }) => {
return (
<span>
<input
@ -132,7 +147,7 @@ class WeekPicker extends React.Component<any, any> {
onBlur={onBlur}
/>
{clearIcon}
<Icon type="calendar" className={`${prefixCls}-picker-icon`}/>
{inputIcon}
</span>
);
};

View File

@ -1237,6 +1237,234 @@ exports[`renders ./components/date-picker/demo/start-end.md correctly 1`] = `
</div>
`;
exports[`renders ./components/date-picker/demo/suffix.md correctly 1`] = `
<div>
<span
class="ant-calendar-picker"
>
<div>
<input
class="ant-calendar-picker-input ant-input"
placeholder="Select date"
readonly=""
value=""
/>
<i
class="anticon anticon-smile ant-calendar-picker-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
</div>
</span>
<br />
<span
class="ant-calendar-picker"
>
<div>
<input
class="ant-calendar-picker-input ant-input"
placeholder="Select month"
readonly=""
value=""
/>
<i
class="anticon anticon-smile ant-calendar-picker-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
</div>
</span>
<br />
<span
class="ant-calendar-picker"
tabindex="0"
>
<span
class="ant-calendar-picker-input ant-input"
>
<input
class="ant-calendar-range-picker-input"
placeholder="Start date"
readonly=""
tabindex="-1"
value=""
/>
<span
class="ant-calendar-range-picker-separator"
>
~
</span>
<input
class="ant-calendar-range-picker-input"
placeholder="End date"
readonly=""
tabindex="-1"
value=""
/>
<i
class="anticon anticon-smile ant-calendar-picker-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
</span>
</span>
<br />
<span
class="ant-calendar-picker"
>
<span>
<input
class="ant-calendar-picker-input ant-input"
placeholder="Select week"
readonly=""
value=""
/>
<i
class="anticon anticon-smile ant-calendar-picker-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
</span>
</span>
<br />
<span
class="ant-calendar-picker"
>
<div>
<input
class="ant-calendar-picker-input ant-input"
placeholder="Select date"
readonly=""
value=""
/>
<span
class="ant-calendar-picker-icon"
>
ab
</span>
</div>
</span>
<br />
<span
class="ant-calendar-picker"
>
<div>
<input
class="ant-calendar-picker-input ant-input"
placeholder="Select month"
readonly=""
value=""
/>
<span
class="ant-calendar-picker-icon"
>
ab
</span>
</div>
</span>
<br />
<span
class="ant-calendar-picker"
tabindex="0"
>
<span
class="ant-calendar-picker-input ant-input"
>
<input
class="ant-calendar-range-picker-input"
placeholder="Start date"
readonly=""
tabindex="-1"
value=""
/>
<span
class="ant-calendar-range-picker-separator"
>
~
</span>
<input
class="ant-calendar-range-picker-input"
placeholder="End date"
readonly=""
tabindex="-1"
value=""
/>
<span
class="ant-calendar-picker-icon"
>
ab
</span>
</span>
</span>
<br />
<span
class="ant-calendar-picker"
>
<span>
<input
class="ant-calendar-picker-input ant-input"
placeholder="Select week"
readonly=""
value=""
/>
<span
class="ant-calendar-picker-icon"
>
ab
</span>
</span>
</span>
</div>
`;
exports[`renders ./components/date-picker/demo/time.md correctly 1`] = `
<div>
<span

View File

@ -101,7 +101,7 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
render() {
const { value, showDate } = this.state;
const props = omit(this.props, ['onChange']);
const { prefixCls, locale, localeCode } = props;
const { prefixCls, locale, localeCode, suffixIcon } = props;
const placeholder = ('placeholder' in props)
? props.placeholder : locale.lang.placeholder;
@ -168,6 +168,20 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
/>
) : null;
const inputIcon = suffixIcon && (
React.isValidElement<{ className?: string }>(suffixIcon)
? React.cloneElement(
suffixIcon,
{
className: classNames({
[suffixIcon.props.className!]: suffixIcon.props.className,
[`${prefixCls}-picker-icon`]: true,
}),
},
) : <span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>) || (
<Icon type="calendar" className={`${prefixCls}-picker-icon`} />
);
const dataOrAriaProps = getDataOrAriaProps(props);
const input = ({ value: inputValue }: { value: moment.Moment | null }) => (
<div>
@ -181,7 +195,7 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
{...dataOrAriaProps}
/>
{clearIcon}
<Icon type="calendar" className={`${prefixCls}-picker-icon`}/>
{inputIcon}
</div>
);

View File

@ -0,0 +1,46 @@
---
order: 13
debug: true
title:
zh-CN: 后缀图标
en-US: Suffix
---
## zh-CN
最简单的用法,在浮层中可以选择或者输入日期。
## en-US
Basic use case. Users can select or input a date in panel.
````jsx
import { DatePicker, Icon } from 'antd';
const smileIcon = <Icon type="smile" />;
const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
function onChange(date, dateString) {
console.log(date, dateString);
}
ReactDOM.render(
<div>
<DatePicker suffixIcon={smileIcon} onChange={onChange} />
<br />
<MonthPicker suffixIcon={smileIcon} onChange={onChange} placeholder="Select month" />
<br />
<RangePicker suffixIcon={smileIcon} onChange={onChange} />
<br />
<WeekPicker suffixIcon={smileIcon} onChange={onChange} placeholder="Select week" />
<br />
<DatePicker suffixIcon="ab" onChange={onChange} />
<br />
<MonthPicker suffixIcon="ab" onChange={onChange} placeholder="Select month" />
<br />
<RangePicker suffixIcon="ab" onChange={onChange} />
<br />
<WeekPicker suffixIcon="ab" onChange={onChange} placeholder="Select week" />
</div>,
mountNode);
````

View File

@ -61,6 +61,7 @@ The following APIs are shared by DatePicker, MonthPicker, RangePicker, WeekPicke
| placeholder | placeholder of date input | string\|RangePicker\[] | - |
| popupStyle | to customize the style of the popup calendar | object | {} |
| size | determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | string | - |
| suffixIcon | The custom suffix icon | ReactNode | - |
| style | to customize the style of the input box | object | {} |
| onOpenChange | a callback function, can be executed whether the popup calendar is popped up or closed | function(status) | - |
| onPanelChange | callback when picker panel mode is changed | function(value, mode) | - |

View File

@ -62,6 +62,7 @@ moment.locale('zh-cn');
| placeholder | 输入框提示文字 | string\|RangePicker\[] | - |
| popupStyle | 额外的弹出日历样式 | object | {} |
| size | 输入框大小,`large` 高度为 40px`small` 为 24px默认是 32px | string | 无 |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - |
| style | 自定义输入框样式 | object | {} |
| onOpenChange | 弹出日历和关闭日历的回调 | function(status) | 无 |

View File

@ -10,6 +10,7 @@ export interface PickerProps {
disabled?: boolean;
allowClear?: boolean;
className?: string;
suffixIcon?: React.ReactNode;
style?: React.CSSProperties;
popupStyle?: React.CSSProperties;
dropdownClassName?: string;

View File

@ -2801,17 +2801,19 @@ exports[`renders ./components/form/demo/validate-other.md correctly 1`] = `
<span
class="ant-form-item-children"
>
<span
<button
aria-checked="false"
class="ant-switch"
data-__field="[object Object]"
data-__meta="[object Object]"
id="switch"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
</span>
</div>
</div>

View File

@ -0,0 +1,9 @@
import createContext, { Context } from 'create-react-context';
export interface RowContextState {
gutter?: number;
}
const RowContext: Context<RowContextState> = createContext({});
export default RowContext;

View File

@ -1,5 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Grid renders wrapped Col correctly 1`] = `
<div
class="ant-row"
style="margin-left:-10px;margin-right:-10px"
>
<div>
<div
class="ant-col-12"
style="padding-left:10px;padding-right:10px"
/>
</div>
<div
class="ant-col-12"
style="padding-left:10px;padding-right:10px"
/>
</div>
`;
exports[`Grid should render Col 1`] = `
<div
class="ant-col-2"

View File

@ -9,10 +9,24 @@ describe('Grid', () => {
);
expect(wrapper).toMatchSnapshot();
});
it('should render Row', () => {
const wrapper = render(
<Row />
);
expect(wrapper).toMatchSnapshot();
});
it('renders wrapped Col correctly', () => {
const MyCol = () => <Col span="12" />;
const wrapper = render(
<Row gutter={20}>
<div>
<Col span="12" />
</div>
<MyCol />
</Row>
);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -1,6 +1,7 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import RowContext from './RowContext';
const stringOrNumber = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
const objectOrNumber = PropTypes.oneOfType([PropTypes.object, PropTypes.number]);
@ -76,6 +77,20 @@ export default class Col extends React.Component<ColProps, {}> {
[`${prefixCls}-pull-${pull}`]: pull,
}, className, sizeClassObj);
return <div {...others} className={classes}>{children}</div>;
return (
<RowContext.Consumer>
{({ gutter }) => {
let style = others.style;
if (gutter as number > 0) {
style = {
paddingLeft: (gutter as number) / 2,
paddingRight: (gutter as number) / 2,
...style,
};
}
return <div {...others} style={style} className={classes}>{children}</div>;
}}
</RowContext.Consumer>
)
}
}

View File

@ -17,9 +17,9 @@ if (typeof window !== 'undefined') {
}
import * as React from 'react';
import { Children, cloneElement } from 'react';
import classNames from 'classnames';
import * as PropTypes from 'prop-types';
import RowContext from './RowContext';
export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
export type BreakpointMap = Partial<Record<Breakpoint, string>>;
@ -100,7 +100,7 @@ export default class Row extends React.Component<RowProps, RowState> {
Object.keys(responsiveMap)
.map((screen: Breakpoint) => enquire.unregister(responsiveMap[screen]));
}
getGutter() {
getGutter(): number | undefined {
const { gutter } = this.props;
if (typeof gutter === 'object') {
for (let i = 0; i <= responsiveArray.length; i++) {
@ -110,7 +110,7 @@ export default class Row extends React.Component<RowProps, RowState> {
}
}
}
return gutter;
return gutter as number;
}
render() {
const {
@ -129,23 +129,14 @@ export default class Row extends React.Component<RowProps, RowState> {
marginRight: (gutter as number) / -2,
...style,
} : style;
const cols = Children.map(children, (col: React.ReactElement<HTMLDivElement>) => {
if (!col) {
return null;
}
if (col.props && (gutter as number) > 0) {
return cloneElement(col, {
style: {
paddingLeft: (gutter as number) / 2,
paddingRight: (gutter as number) / 2,
...col.props.style,
},
});
}
return col;
});
const otherProps = { ...others };
delete otherProps.gutter;
return <div {...otherProps} className={classes} style={rowStyle}>{cols}</div>;
return (
<RowContext.Provider value={{ gutter }}>
<div {...otherProps} className={classes} style={rowStyle}>
{children}
</div>
</RowContext.Provider>
);
}
}

View File

@ -23,7 +23,7 @@ export default function create(options: CustomIconOptions = {}): React.SFC<IconP
&& !customCache.has(scriptUrl)
) {
const script = document.createElement('script');
script.setAttribute('src', `https:${scriptUrl}`);
script.setAttribute('src', scriptUrl);
script.setAttribute('data-namespace', scriptUrl);
customCache.add(scriptUrl);
document.body.appendChild(script);

View File

@ -22,6 +22,7 @@ When a numeric value needs to be provided.
| min | min value | number | -Infinity |
| parser | Specifies the value extracted from formatter | function( string): number | - |
| precision | precision of input value | number | - |
| decilamSeparator | decimal separator | string | - |
| size | width of input box | string | - |
| step | The number to which the current value is increased or decreased. It can be an integer or decimal. | number\|string | 1 |
| value | current value | number | |

View File

@ -20,6 +20,7 @@ export interface InputNumberProps extends Omit<React.InputHTMLAttributes<HTMLInp
size?: 'large' | 'small' | 'default';
formatter?: (value: number | string | undefined) => string;
parser?: (displayValue: string | undefined) => number;
decimalSeparator?: string;
placeholder?: string;
style?: React.CSSProperties;
className?: string;

View File

@ -25,6 +25,7 @@ title: InputNumber
| min | 最小值 | number | -Infinity |
| parser | 指定从 formatter 里转换回数字的方式,和 formatter 搭配使用 | function( string): number | - |
| precision | 数值精度 | number | - |
| decimalSeparator | 小数点 | string | - |
| size | 输入框大小 | string | 无 |
| step | 每次改变步数,可以为小数 | number\|string | 1 |
| value | 当前值 | number | |

View File

@ -179,10 +179,10 @@ exports[`renders ./components/list/demo/grid.md correctly 1`] = `
>
<div
class="ant-col-6"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item-content ant-list-item-content-single"
@ -214,10 +214,10 @@ exports[`renders ./components/list/demo/grid.md correctly 1`] = `
</div>
<div
class="ant-col-6"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item-content ant-list-item-content-single"
@ -249,10 +249,10 @@ exports[`renders ./components/list/demo/grid.md correctly 1`] = `
</div>
<div
class="ant-col-6"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item-content ant-list-item-content-single"
@ -284,10 +284,10 @@ exports[`renders ./components/list/demo/grid.md correctly 1`] = `
</div>
<div
class="ant-col-6"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item-content ant-list-item-content-single"
@ -411,10 +411,10 @@ exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
>
<div
class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item-content ant-list-item-content-single"
@ -446,10 +446,10 @@ exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
</div>
<div
class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item-content ant-list-item-content-single"
@ -481,10 +481,10 @@ exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
</div>
<div
class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item-content ant-list-item-content-single"
@ -516,10 +516,10 @@ exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
</div>
<div
class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item-content ant-list-item-content-single"
@ -551,10 +551,10 @@ exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
</div>
<div
class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item-content ant-list-item-content-single"
@ -586,10 +586,10 @@ exports[`renders ./components/list/demo/resposive.md correctly 1`] = `
</div>
<div
class="ant-col-xs-24 ant-col-sm-12 ant-col-md-6 ant-col-lg-6 ant-col-xl-4 ant-col-xxl-8"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item"
style="padding-left:8px;padding-right:8px"
>
<div
class="ant-list-item-content ant-list-item-content-single"

View File

@ -1711,36 +1711,40 @@ exports[`renders ./components/locale-provider/demo/all.md correctly 1`] = `
>
<tr>
<th
class="ant-table-column-has-filters"
class="ant-table-column-has-actions ant-table-column-has-filters"
>
<span>
<div
title="Sort"
>
Name
<i
class="anticon anticon-filter ant-dropdown-trigger"
title="Filter menu"
</div>
<i
class="anticon anticon-filter ant-dropdown-trigger"
title="Filter menu"
>
<svg
aria-hidden="true"
class=""
data-icon="filter"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<svg
aria-hidden="true"
class=""
data-icon="filter"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880.1 154H143.9c-24.5 0-39.8 26.7-27.5 48L349 597.4V838c0 17.7 14.2 32 31.8 32h262.4c17.6 0 31.8-14.3 31.8-32V597.4L907.7 202c12.2-21.3-3.1-48-27.6-48zM603.4 798H420.6V642h182.9v156zm9.6-236.6l-9.5 16.6h-183l-9.5-16.6L212.7 226h598.6L613 561.4z"
/>
</svg>
</i>
</span>
<path
d="M349 838c0 17.7 14.2 32 31.8 32h262.4c17.6 0 31.8-14.3 31.8-32V642H349v196zm531.1-684H143.9c-24.5 0-39.8 26.7-27.5 48l221.3 376h348.8l221.3-376c12.1-21.3-3.2-48-27.7-48z"
/>
</svg>
</i>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Age
</span>
</div>
</th>
</tr>
</thead>

View File

@ -16,6 +16,7 @@ export default {
emptyText: 'No data',
selectAll: 'Select current page',
selectInvert: 'Invert current page',
sortTitle: 'Sort',
},
Modal: {
okText: 'OK',

View File

@ -16,6 +16,7 @@ export default {
emptyText: '暂无数据',
selectAll: '全选当页',
selectInvert: '反选当页',
sortTitle: '排序',
},
Modal: {
okText: '确定',

View File

@ -762,27 +762,31 @@ exports[`renders ./components/menu/demo/sider-current.md correctly 1`] = `
exports[`renders ./components/menu/demo/switch-mode.md correctly 1`] = `
<div>
<span
<button
aria-checked="false"
class="ant-switch"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
Change Mode
<span
class="ant-divider"
style="margin:0 1em"
/>
<span
<button
aria-checked="false"
class="ant-switch"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
Change Theme
<br />
<br />
@ -959,17 +963,19 @@ exports[`renders ./components/menu/demo/switch-mode.md correctly 1`] = `
exports[`renders ./components/menu/demo/theme.md correctly 1`] = `
<div>
<span
<button
aria-checked="true"
checked=""
class="ant-switch ant-switch-checked"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
>
Dark
</span>
</span>
</button>
<br />
<br />
<ul

View File

@ -1,13 +1,14 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import Button from '../button';
import { ButtonType } from '../button/button';
import { ButtonType, NativeButtonProps } from '../button/button';
export interface ActionButtonProps {
type?: ButtonType;
actionFn?: (...args: any[]) => any | PromiseLike<any>;
closeModal: Function;
autoFocus?: boolean;
buttonProps?: NativeButtonProps;
}
export interface ActionButtonState {
@ -61,10 +62,10 @@ export default class ActionButton extends React.Component<ActionButtonProps, Act
}
render() {
const { type, children } = this.props;
const { type, children, buttonProps } = this.props;
const loading = this.state.loading;
return (
<Button type={type} onClick={this.onClick} loading={loading}>
<Button type={type} onClick={this.onClick} loading={loading} {...buttonProps}>
{children}
</Button>
);

View File

@ -66,6 +66,8 @@ export interface ModalFuncProps {
content?: React.ReactNode;
onOk?: (...args: any[]) => any | PromiseLike<any>;
onCancel?: (...args: any[]) => any | PromiseLike<any>;
okButtonProps?: NativeButtonProps;
cancelButtonProps?: NativeButtonProps;
centered?: boolean;
width?: string | number;
iconClassName?: string;
@ -80,6 +82,7 @@ export interface ModalFuncProps {
type?: string;
keyboard?: boolean;
getContainer?: (instance: React.ReactInstance) => HTMLElement;
autoFocusButton?: null | 'ok' | 'cancel';
}
export type ModalFunc = (props: ModalFuncProps) => {

View File

@ -84,6 +84,13 @@ describe('Modal.confirm triggers callbacks correctly', () => {
expect($$('.ant-btn')[0].innerHTML).toContain('OK');
});
it('allows extra props on buttons', () => {
open({ okButtonProps: { disabled: true }, cancelButtonProps: { 'data-test': 'baz' } });
expect($$('.ant-btn')).toHaveLength(2);
expect($$('.ant-btn')[0].attributes['data-test'].value).toBe('baz');
expect($$('.ant-btn')[1].disabled).toBe(true);
});
it('trigger onCancel once when click on cancel button', () => {
jest.useFakeTimers();
['info', 'success', 'warning', 'error'].forEach((type) => {

View File

@ -9,12 +9,13 @@ import { getConfirmLocale } from './locale';
interface ConfirmDialogProps extends ModalFuncProps {
afterClose?: () => void;
close: (...args: any[]) => void;
autoFocusButton?: null | 'ok' | 'cancel';
}
const IS_REACT_16 = !!ReactDOM.createPortal;
const ConfirmDialog = (props: ConfirmDialogProps) => {
const { onCancel, onOk, close, zIndex, afterClose, visible, keyboard, centered, getContainer } = props;
const { onCancel, onOk, close, zIndex, afterClose, visible, keyboard, centered, getContainer, okButtonProps, cancelButtonProps } = props;
const iconType = props.iconType || 'question-circle';
const okType = props.okType || 'primary';
const prefixCls = props.prefixCls || 'ant-confirm';
@ -28,6 +29,7 @@ const ConfirmDialog = (props: ConfirmDialogProps) => {
const okText = props.okText ||
(okCancel ? runtimeLocale.okText : runtimeLocale.justOkText);
const cancelText = props.cancelText || runtimeLocale.cancelText;
const autoFocusButton = props.autoFocusButton === null ? false : props.autoFocusButton || 'ok';
const classString = classNames(
prefixCls,
@ -36,7 +38,7 @@ const ConfirmDialog = (props: ConfirmDialogProps) => {
);
const cancelButton = okCancel && (
<ActionButton actionFn={onCancel} closeModal={close}>
<ActionButton actionFn={onCancel} closeModal={close} autoFocus={autoFocusButton === 'cancel'} buttonProps={cancelButtonProps}>
{cancelText}
</ActionButton>
);
@ -68,7 +70,7 @@ const ConfirmDialog = (props: ConfirmDialogProps) => {
</div>
<div className={`${prefixCls}-btns`}>
{cancelButton}
<ActionButton type={okType} actionFn={onOk} closeModal={close} autoFocus>
<ActionButton type={okType} actionFn={onOk} closeModal={close} autoFocus={autoFocusButton === 'ok'} buttonProps={okButtonProps}>
{okText}
</ActionButton>
</div>

View File

@ -47,6 +47,28 @@ function showDeleteConfirm() {
});
}
function showPropsConfirm() {
confirm({
title: 'Are you sure delete this task?',
content: 'Some descriptions',
okText: 'Yes',
okType: 'danger',
okButtonProps: {
disabled: true,
},
cancelButtonProps: {
loading: true,
},
cancelText: 'No',
onOk() {
console.log('OK');
},
onCancel() {
console.log('Cancel');
},
});
}
ReactDOM.render(
<div>
<Button onClick={showConfirm}>
@ -55,6 +77,9 @@ ReactDOM.render(
<Button onClick={showDeleteConfirm} type="dashed">
Delete
</Button>
<Button onClick={showPropsConfirm} type="dashed">
With extra props
</Button>
</div>,
mountNode);
````

View File

@ -62,6 +62,7 @@ The properties of the object are follows:
| Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- |
| autoFocusButton | Specify which button to autofocus | null\|string: `ok` `cancel` | `ok` |
| cancelText | Text of the Cancel button | string | `Cancel` |
| centered | Centered Modal | Boolean | `false` |
| className | className of container | string | - |
@ -71,6 +72,8 @@ The properties of the object are follows:
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | Boolean | `false` |
| okText | Text of the OK button | string | `OK` |
| okType | Button `type` of the OK button | string | `primary` |
| okButtonProps | The ok button props | [ButtonProps](/components/button) | - |
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button) | - |
| title | Title | string\|ReactNode | - |
| width | Width of the modal dialog | string\|number | 416 |
| zIndex | The `z-index` of the Modal | Number | 1000 |

View File

@ -61,6 +61,7 @@ title: Modal
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| autoFocusButton | 指定自动获得焦点的按钮 | null\|string: `ok` `cancel` | `ok` |
| cancelText | 取消按钮文字 | string | 取消 |
| centered | 垂直居中展示 Modal | Boolean | `false` |
| className | 容器类名 | string | - |
@ -69,6 +70,8 @@ title: Modal
| maskClosable | 点击蒙层是否允许关闭 | Boolean | `false` |
| okText | 确认按钮文字 | string | 确定 |
| okType | 确认按钮类型 | string | primary |
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button) | - |
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button) | - |
| title | 标题 | string\|ReactNode | 无 |
| width | 宽度 | string\|number | 416 |
| zIndex | 设置 Modal 的 `z-index` | Number | 1000 |

View File

@ -18,14 +18,16 @@ exports[`renders ./components/popconfirm/demo/dynamic-trigger.md correctly 1`] =
<br />
<br />
Whether directly execute
<span
<button
aria-checked="true"
class="ant-switch ant-switch-checked"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
</div>
`;

View File

@ -974,6 +974,109 @@ exports[`renders ./components/select/demo/size.md correctly 1`] = `
</div>
`;
exports[`renders ./components/select/demo/suffix.md correctly 1`] = `
<div>
<div
class="ant-select ant-select-enabled"
style="width:120px"
>
<div
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
ant-select-selection--single"
role="combobox"
tabindex="0"
>
<div
class="ant-select-selection__rendered"
>
<div
class="ant-select-selection-selected-value"
style="display:block;opacity:1"
title="Lucy"
>
Lucy
</div>
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<i
class="anticon anticon-smile"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
</span>
</div>
</div>
<div
class="ant-select ant-select-disabled"
style="width:120px"
>
<div
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
ant-select-selection--single"
role="combobox"
tabindex="-1"
>
<div
class="ant-select-selection__rendered"
>
<div
class="ant-select-selection-selected-value"
style="display:block;opacity:1"
title="Lucy"
>
Lucy
</div>
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none"
unselectable="on"
>
<i
class="anticon anticon-meh"
>
<svg
aria-hidden="true"
class=""
data-icon="meh"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 565H360c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h304c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8z"
/>
</svg>
</i>
</span>
</div>
</div>
</div>
`;
exports[`renders ./components/select/demo/tags.md correctly 1`] = `
<div
class="ant-select ant-select-enabled"

View File

@ -0,0 +1,41 @@
---
order: 20
debug: true
title:
zh-CN: 后缀图标
en-US: Suffix
---
## zh-CN
基本使用。
## en-US
Basic Usage.
````jsx
import { Select, Icon } from 'antd';
const smileIcon = <Icon type="smile" />;
const mehIcon = <Icon type="meh" />;
const Option = Select.Option;
function handleChange(value) {
console.log(`selected ${value}`);
}
ReactDOM.render(
<div>
<Select suffixIcon={smileIcon} defaultValue="lucy" style={{ width: 120 }} onChange={handleChange}>
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="disabled" disabled>Disabled</Option>
<Option value="Yiminghe">yiminghe</Option>
</Select>
<Select suffixIcon={mehIcon} defaultValue="lucy" style={{ width: 120 }} disabled>
<Option value="lucy">Lucy</Option>
</Select>
</div>,
mountNode);
````

View File

@ -46,6 +46,7 @@ Select component to select value from options.
| showArrow | Whether to show the drop-down arrow | boolean | true |
| showSearch | Whether show search input in single mode. | boolean | false |
| size | Size of Select input. `default` `large` `small` | string | default |
| suffixIcon | The custom suffix icon | ReactNode | - |
| tokenSeparators | Separator used to tokenize on tag/multiple mode | string\[] | |
| value | Current selected option. | string\|number\|string\[]\|number\[] | - |
| onBlur | Called when blur | function | - |

View File

@ -4,6 +4,7 @@ import RcSelect, { Option, OptGroup } from 'rc-select';
import classNames from 'classnames';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale-provider/default';
import omit from 'omit.js';
import warning from 'warning';
import Icon from '../icon';
@ -65,6 +66,7 @@ export interface SelectProps extends AbstractSelectProps {
tokenSeparators?: string[];
getInputElement?: () => React.ReactElement<any>;
autoFocus?: boolean;
suffixIcon?: React.ReactNode;
}
export interface OptionProps {
@ -157,8 +159,11 @@ export default class Select extends React.Component<SelectProps, {}> {
className = '',
size,
mode,
suffixIcon,
...restProps
} = this.props;
const rest = omit(restProps, ['inputIcon', 'removeIcon', 'clearIcon']);
const cls = classNames({
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-sm`]: size === 'small',
@ -176,9 +181,11 @@ export default class Select extends React.Component<SelectProps, {}> {
combobox: this.isCombobox(),
};
const inputIcon = (
<Icon type="down" className={`${prefixCls}-arrow-icon`} />
);
const inputIcon = suffixIcon && (
React.isValidElement<{ className?: string }>(suffixIcon)
? React.cloneElement(suffixIcon) : suffixIcon) || (
<Icon type="down" className={`${prefixCls}-arrow-icon`} />
);
const removeIcon = (
<Icon type="close" className={`${prefixCls}-remove-icon`} />
@ -198,7 +205,7 @@ export default class Select extends React.Component<SelectProps, {}> {
removeIcon={removeIcon}
clearIcon={clearIcon}
menuItemSelectedIcon={menuItemSelectedIcon}
{...restProps}
{...rest}
{...modeConfig}
prefixCls={prefixCls}
className={cls}

View File

@ -47,6 +47,7 @@ title: Select
| showArrow | 是否显示下拉小箭头 | boolean | true |
| showSearch | 使单选模式可搜索 | boolean | false |
| size | 选择框大小,可选 `large` `small` | string | default |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - |
| tokenSeparators | 在 tags 和 multiple 模式下自动分词的分隔符 | string\[] | |
| value | 指定当前选中的条目 | string\|string\[]\|number\|number\[] | - |
| onBlur | 失去焦点的时回调 | function | - |

View File

@ -103,14 +103,16 @@ exports[`renders ./components/skeleton/demo/complex.md correctly 1`] = `
exports[`renders ./components/skeleton/demo/list.md correctly 1`] = `
<div>
<span
<button
aria-checked="false"
class="ant-switch"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
<div
class="ant-list ant-list-vertical ant-list-lg ant-list-split"
>

View File

@ -67,14 +67,16 @@ exports[`renders ./components/slider/demo/basic.md correctly 1`] = `
/>
</div>
Disabled:
<span
<button
aria-checked="false"
class="ant-switch-small ant-switch"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
</div>
`;

View File

@ -69,14 +69,16 @@ exports[`renders ./components/spin/demo/delayAndDebounce.md correctly 1`] = `
style="margin-top:16px"
>
Loading state
<span
<button
aria-checked="false"
class="ant-switch"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
</div>
</div>
`;
@ -129,14 +131,16 @@ exports[`renders ./components/spin/demo/nested.md correctly 1`] = `
style="margin-top:16px"
>
Loading state
<span
<button
aria-checked="false"
class="ant-switch"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
</div>
</div>
`;

View File

@ -344,6 +344,7 @@
@table-header-bg: @background-color-light;
@table-header-color: @heading-color;
@table-header-sort-bg: @background-color-base;
@table-body-sort-bg: rgba(0, 0, 0, .01);
@table-row-hover-bg: @primary-1;
@table-selected-row-bg: #fafafa;
@table-expanded-row-bg: #fbfbfb;
@ -490,6 +491,10 @@
// ---
@skeleton-color: #f2f2f2;
// Transfer
// ---
@transfer-disabled-bg: @disabled-bg;
// Message
// ---
@message-notice-content-padding: 10px 16px;

View File

@ -1,26 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/switch/demo/basic.md correctly 1`] = `
<span
<button
aria-checked="true"
class="ant-switch ant-switch-checked"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
`;
exports[`renders ./components/switch/demo/disabled.md correctly 1`] = `
<div>
<span
<button
aria-checked="true"
class="ant-switch ant-switch-checked ant-switch-disabled"
tabindex="-1"
disabled=""
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
<br />
<button
class="ant-btn ant-btn-primary"
@ -35,9 +40,11 @@ exports[`renders ./components/switch/demo/disabled.md correctly 1`] = `
exports[`renders ./components/switch/demo/loading.md correctly 1`] = `
<div>
<span
<button
aria-checked="true"
class="ant-switch-loading ant-switch ant-switch-checked"
tabindex="0"
role="switch"
type="button"
>
<i
class="anticon anticon-loading ant-switch-loading-icon"
@ -59,11 +66,13 @@ exports[`renders ./components/switch/demo/loading.md correctly 1`] = `
<span
class="ant-switch-inner"
/>
</span>
</button>
<br />
<span
<button
aria-checked="false"
class="ant-switch-small ant-switch-loading ant-switch"
tabindex="0"
role="switch"
type="button"
>
<i
class="anticon anticon-loading ant-switch-loading-icon"
@ -85,59 +94,69 @@ exports[`renders ./components/switch/demo/loading.md correctly 1`] = `
<span
class="ant-switch-inner"
/>
</span>
</button>
</div>
`;
exports[`renders ./components/switch/demo/size.md correctly 1`] = `
<div>
<span
<button
aria-checked="true"
class="ant-switch ant-switch-checked"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
<br />
<span
<button
aria-checked="true"
class="ant-switch-small ant-switch ant-switch-checked"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
/>
</span>
</button>
</div>
`;
exports[`renders ./components/switch/demo/text.md correctly 1`] = `
<div>
<span
<button
aria-checked="true"
class="ant-switch ant-switch-checked"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
>
</span>
</span>
</button>
<br />
<span
<button
aria-checked="false"
class="ant-switch"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
>
0
</span>
</span>
</button>
<br />
<span
<button
aria-checked="true"
class="ant-switch ant-switch-checked"
tabindex="0"
role="switch"
type="button"
>
<span
class="ant-switch-inner"
@ -160,6 +179,6 @@ exports[`renders ./components/switch/demo/text.md correctly 1`] = `
</svg>
</i>
</span>
</span>
</button>
</div>
`;

View File

@ -33,6 +33,7 @@ import {
TableSelectWay,
TableRowSelection,
PaginationConfig,
PrepareParamsArgumentsReturn,
} from './interface';
import { RadioChangeEvent } from '../radio';
import { CheckboxChangeEvent } from '../checkbox';
@ -340,8 +341,8 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
};
}
getSorterFn() {
const { sortOrder, sortColumn } = this.state;
getSorterFn(state: TableState<T>) {
const { sortOrder, sortColumn } = state || this.state;
if (!sortOrder || !sortColumn ||
typeof sortColumn.sorter !== 'function') {
return;
@ -356,24 +357,24 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
};
}
toggleSortOrder(order: 'ascend'|'descend', column: ColumnProps<T>) {
let { sortColumn, sortOrder } = this.state;
toggleSortOrder(column: ColumnProps<T>) {
const { sortOrder, sortColumn } = this.state;
// 只同时允许一列进行排序,否则会导致排序顺序的逻辑问题
let isSortColumn = this.isSortColumn(column);
if (!isSortColumn) { // 当前列未排序
sortOrder = order;
sortColumn = column;
} else { // 当前列已排序
if (sortOrder === order) { // 切换为未排序状态
sortOrder = undefined;
sortColumn = null;
} else { // 切换为排序状态
sortOrder = order;
}
let newSortOrder: 'descend' | 'ascend' | undefined;
// 切换另一列时,丢弃 sortOrder 的状态
const oldSortOrder = sortColumn === column ? sortOrder : undefined;
// 切换排序状态,按照降序/升序/不排序的顺序
if (!oldSortOrder) {
newSortOrder = 'descend';
} else if (oldSortOrder === 'descend') {
newSortOrder = 'ascend';
} else {
newSortOrder = undefined;
}
const newState = {
sortOrder,
sortColumn,
sortOrder: newSortOrder,
sortColumn: newSortOrder ? column : null,
};
// Controlled
@ -381,7 +382,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
this.setState(newState);
}
const onChange = this.props.onChange;
const { onChange } = this.props;
if (onChange) {
onChange.apply(null, this.prepareParamsArguments({
...this.state,
@ -444,7 +445,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
this.store.setState({
selectionDirty: false,
});
const onChange = this.props.onChange;
const { onChange } = this.props;
if (onChange) {
onChange.apply(null, this.prepareParamsArguments({
...this.state,
@ -462,7 +463,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection();
let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection);
let key = this.getRecordKey(record, rowIndex);
const pivot = this.state.pivot;
const { pivot } = this.state;
const rows = this.getFlatCurrentPageData();
let realIndex = rowIndex;
if (this.props.expandedRowRender) {
@ -639,7 +640,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
selectionDirty: false,
});
const onChange = this.props.onChange;
const { onChange } = this.props;
if (onChange) {
onChange.apply(null, this.prepareParamsArguments({
...this.state,
@ -674,7 +675,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
}
getRecordKey = (record: T, index: number) => {
const rowKey = this.props.rowKey;
const { rowKey } = this.props;
const recordKey = (typeof rowKey === 'function') ?
rowKey(record, index) : (record as any)[rowKey as string];
warning(recordKey !== undefined,
@ -747,7 +748,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
}
getMaxCurrent(total: number) {
const { current, pageSize } = this.state.pagination;
const { pagination: { current, pageSize } } = this.state;
if ((current! - 1) * pageSize! >= total) {
return Math.floor((total - 1) / pageSize!) + 1;
}
@ -765,11 +766,11 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
renderColumnsDropdown(columns: ColumnProps<T>[], locale: TableLocale) {
const { prefixCls, dropdownPrefixCls } = this.props;
const { sortOrder } = this.state;
return treeMap(columns, (originColumn, i) => {
let column = { ...originColumn };
let key = this.getColumnKey(column, i) as string;
return treeMap(columns, (column, i) => {
const key = this.getColumnKey(column, i) as string;
let filterDropdown;
let sortButton;
const isSortColumn = this.isSortColumn(column);
if ((column.filters && column.filters.length > 0) || column.filterDropdown) {
let colFilters = this.state.filters[key] || [];
filterDropdown = (
@ -781,55 +782,66 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
prefixCls={`${prefixCls}-filter`}
dropdownPrefixCls={dropdownPrefixCls || 'ant-dropdown'}
getPopupContainer={this.getPopupContainer}
key="filter-dropdown"
/>
);
}
if (column.sorter) {
let isSortColumn = this.isSortColumn(column);
if (isSortColumn) {
column.className = classNames(column.className, {
[`${prefixCls}-column-sort`]: sortOrder,
});
}
const isAscend = isSortColumn && sortOrder === 'ascend';
const isDescend = isSortColumn && sortOrder === 'descend';
sortButton = (
<div className={`${prefixCls}-column-sorter`}>
<span
<div className={`${prefixCls}-column-sorter`} key="sorter">
<Icon
className={`${prefixCls}-column-sorter-up ${isAscend ? 'on' : 'off'}`}
title="↑"
onClick={() => this.toggleSortOrder('ascend', column)}
>
<Icon type="caret-up" />
</span>
<span
type="caret-up"
theme="filled"
/>
<Icon
className={`${prefixCls}-column-sorter-down ${isDescend ? 'on' : 'off'}`}
title="↓"
onClick={() => this.toggleSortOrder('descend', column)}
>
<Icon type="caret-down" />
</span>
type="caret-down"
theme="filled"
/>
</div>
);
}
column.title = (
<span key={key}>
{column.title}
{sortButton}
{filterDropdown}
</span>
);
if (sortButton || filterDropdown) {
column.className = classNames(`${prefixCls}-column-has-filters`, column.className);
}
return column;
return {
...column,
className: classNames(column.className, {
[`${prefixCls}-column-has-actions`]: sortButton || filterDropdown,
[`${prefixCls}-column-has-filters`]: filterDropdown,
[`${prefixCls}-column-has-sorters`]: sortButton,
[`${prefixCls}-column-sort`]: isSortColumn && sortOrder,
}),
title: [
<div
key="title"
title={locale.sortTitle}
className={sortButton ? `${prefixCls}-column-sorters` : undefined}
onClick={() => this.toggleSortOrder(column)}
>
{this.renderColumnTitle(column.title)}
{sortButton}
</div>,
filterDropdown,
],
};
});
}
renderColumnTitle(title: ColumnProps<T>['title']) {
const { filters, sortOrder } = this.state;
// https://github.com/ant-design/ant-design/issues/11246#issuecomment-405009167
if (typeof title === 'function') {
return title({
filters,
sortOrder,
});
}
return title;
}
handleShowSizeChange = (current: number, pageSize: number) => {
const pagination = this.state.pagination;
const { pagination } = this.state;
pagination.onShowSizeChange!(current, pageSize);
const nextPagination = {
...pagination,
@ -838,7 +850,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
};
this.setState({ pagination: nextPagination });
const onChange = this.props.onChange;
const { onChange } = this.props;
if (onChange) {
onChange.apply(null, this.prepareParamsArguments({
...this.state,
@ -876,7 +888,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
}
// Get pagination, filters, sorter
prepareParamsArguments(state: any): [any, string[], Object] {
prepareParamsArguments(state: any): PrepareParamsArgumentsReturn<T> {
const pagination = { ...state.pagination };
// remove useless handle function in Table.onChange
delete pagination.onChange;
@ -889,7 +901,12 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
sorter.field = state.sortColumn.dataIndex;
sorter.columnKey = this.getColumnKey(state.sortColumn);
}
return [pagination, filters, sorter];
const extra = {
currentDataSource: this.getLocalData(state),
};
return [pagination, filters, sorter, extra];
}
findColumn(myKey: string | number) {
@ -944,24 +961,24 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
} : item));
}
getLocalData() {
const state = this.state;
getLocalData(state?: TableState<T>) {
const currentState: TableState<T> = state || this.state;
const { dataSource } = this.props;
let data = dataSource || [];
// 优化本地排序
data = data.slice(0);
const sorterFn = this.getSorterFn();
const sorterFn = this.getSorterFn(currentState);
if (sorterFn) {
data = this.recursiveSort(data, sorterFn);
}
// 筛选
if (state.filters) {
Object.keys(state.filters).forEach((columnKey) => {
if (currentState.filters) {
Object.keys(currentState.filters).forEach((columnKey) => {
let col = this.findColumn(columnKey) as any;
if (!col) {
return;
}
let values = state.filters[columnKey] || [];
let values = currentState.filters[columnKey] || [];
if (values.length === 0) {
return;
}

View File

@ -220,7 +220,14 @@ describe('Table.filter', () => {
dropdownWrapper.find('MenuItem').first().simulate('click');
dropdownWrapper.find('.confirm').simulate('click');
expect(handleChange).toBeCalledWith({}, { name: ['boy'] }, {});
expect(handleChange).toBeCalledWith(
{},
{ name: ['boy'] },
{},
{
currentDataSource: [],
}
);
});
it('should not fire change event on close filterDropdown without changing anything', () => {
@ -377,6 +384,7 @@ describe('Table.filter', () => {
wrapper.find('.ant-dropdown-trigger').first().simulate('click');
expect(handleChange).toBeCalled();
expect(handleChange.mock.calls[0][3].currentDataSource.length).toBe(1);
});
it('renders custom filter icon correctly', () => {

View File

@ -86,7 +86,15 @@ describe('Table.pagination', () => {
pageSize: 2,
},
{},
{}
{},
{
currentDataSource: [
{ key: 0, name: 'Jack' },
{ key: 1, name: 'Lucy' },
{ key: 2, name: 'Tom' },
{ key: 3, name: 'Jerry' },
],
},
);
expect(handlePaginationChange).toBeCalledWith(2, 2);

View File

@ -433,13 +433,13 @@ describe('Table.rowSelection', () => {
}}
/>
);
expect(wrapper.find('thead tr span').at(0).text()).toBe('多选');
expect(wrapper.find('thead tr div').at(0).text()).toBe('多选');
wrapper.setProps({
rowSelection: {
type: 'radio',
columnTitle: '单选',
},
});
expect(wrapper.find('thead tr span').at(0).text()).toBe('单选');
expect(wrapper.find('thead tr div').at(0).text()).toBe('单选');
});
});

View File

@ -60,11 +60,13 @@ describe('Table.sorter', () => {
it('sort records', () => {
const wrapper = mount(createTable());
wrapper.find('.ant-table-column-sorter-up').simulate('click');
expect(renderedNames(wrapper)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
wrapper.find('.ant-table-column-sorter-down').simulate('click');
// descent
wrapper.find('.ant-table-column-sorters').simulate('click');
expect(renderedNames(wrapper)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
// ascent
wrapper.find('.ant-table-column-sorters').simulate('click');
expect(renderedNames(wrapper)).toEqual(['Jack', 'Jerry', 'Lucy', 'Tom']);
});
it('can be controlled by sortOrder', () => {
@ -92,13 +94,27 @@ describe('Table.sorter', () => {
const handleChange = jest.fn();
const wrapper = mount(createTable({ onChange: handleChange }));
wrapper.find('.ant-table-column-sorter-up').simulate('click');
// ascent
wrapper.find('.ant-table-column-sorters').simulate('click');
const sorter1 = handleChange.mock.calls[0][2];
expect(sorter1.column.dataIndex).toBe('name');
expect(sorter1.order).toBe('descend');
expect(sorter1.field).toBe('name');
expect(sorter1.columnKey).toBe('name');
const sorter = handleChange.mock.calls[0][2];
expect(sorter.column.dataIndex).toBe('name');
expect(sorter.order).toBe('ascend');
expect(sorter.field).toBe('name');
expect(sorter.columnKey).toBe('name');
wrapper.find('.ant-table-column-sorters').simulate('click');
const sorter2 = handleChange.mock.calls[1][2];
expect(sorter2.column.dataIndex).toBe('name');
expect(sorter2.order).toBe('ascend');
expect(sorter2.field).toBe('name');
expect(sorter2.columnKey).toBe('name');
wrapper.find('.ant-table-column-sorters').simulate('click');
const sorter3 = handleChange.mock.calls[2][2];
expect(sorter3.column).toBe(undefined);
expect(sorter3.order).toBe(undefined);
expect(sorter3.field).toBe(undefined);
expect(sorter3.columnKey).toBe(undefined);
});
it('works with grouping columns in controlled mode', () => {
@ -134,4 +150,64 @@ describe('Table.sorter', () => {
expect(renderedNames(wrapper)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
});
// https://github.com/ant-design/ant-design/issues/11246#issuecomment-405009167
it('Allow column title as render props with sortOrder argument', () => {
const columns = [
{
title: ({ sortOrder }) => <div className="custom-title">{sortOrder}</div>,
key: 'group',
sorter: true,
},
];
const testData = [
{ key: 0, name: 'Jack', age: 11 },
{ key: 1, name: 'Lucy', age: 20 },
{ key: 2, name: 'Tom', age: 21 },
{ key: 3, name: 'Jerry', age: 22 },
];
const wrapper = mount(
<Table columns={columns} dataSource={testData} />
);
expect(wrapper.find('.custom-title').text()).toEqual('');
wrapper.find('.ant-table-column-sorters').simulate('click');
expect(wrapper.find('.custom-title').text()).toEqual('descend');
wrapper.find('.ant-table-column-sorters').simulate('click');
expect(wrapper.find('.custom-title').text()).toEqual('ascend');
});
// https://github.com/ant-design/ant-design/pull/12264#discussion_r218053034
it('should sort from begining state when toggle from different columns', () => {
const columns = [
{
title: 'name',
dataIndex: 'name',
sorter: true,
},
{
title: 'age',
dataIndex: 'age',
sorter: true,
},
];
const testData = [
{ key: 0, name: 'Jack', age: 11 },
{ key: 1, name: 'Lucy', age: 20 },
{ key: 2, name: 'Tom', age: 21 },
{ key: 3, name: 'Jerry', age: 22 },
];
const wrapper = mount(
<Table columns={columns} dataSource={testData} />
);
const nameColumn = wrapper.find('.ant-table-column-sorters').at(0);
const ageColumn = wrapper.find('.ant-table-column-sorters').at(1);
// sort name
nameColumn.simulate('click');
expect(nameColumn.find('.ant-table-column-sorter-down').at(0).getDOMNode().className).toContain(' on');
expect(ageColumn.find('.ant-table-column-sorter-down').at(0).getDOMNode().className).toContain(' off');
// sort age
ageColumn.simulate('click');
expect(nameColumn.find('.ant-table-column-sorter-down').at(0).getDOMNode().className).toContain(' off');
expect(ageColumn.find('.ant-table-column-sorter-down').at(0).getDOMNode().className).toContain(' on');
});
});

View File

@ -29,7 +29,7 @@ exports[`Table.filter override custom filter correctly 1`] = `
exports[`Table.filter renders custom content correctly 1`] = `
<div>
<div
class="ant-dropdown ant-dropdown-placement-bottomLeft ant-dropdown-hidden"
class="ant-dropdown ant-dropdown-placement-bottomRight ant-dropdown-hidden"
>
<div>
<div
@ -44,7 +44,7 @@ exports[`Table.filter renders custom content correctly 1`] = `
exports[`Table.filter renders custom filter icon correctly 1`] = `
<span
class="ant-table-filter-icon ant-dropdown-trigger"
class="ant-table-filter-icon ant-table-filter-selected ant-dropdown-trigger"
style=""
title="Filter menu"
>
@ -92,29 +92,31 @@ exports[`Table.filter renders filter correctly 1`] = `
>
<tr>
<th
class="ant-table-column-has-filters"
class="ant-table-column-has-actions ant-table-column-has-filters"
>
<span>
<div
title="Sort"
>
Name
<i
class="anticon anticon-filter ant-dropdown-trigger"
title="Filter menu"
</div>
<i
class="anticon anticon-filter ant-dropdown-trigger"
title="Filter menu"
>
<svg
aria-hidden="true"
class=""
data-icon="filter"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<svg
aria-hidden="true"
class=""
data-icon="filter"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880.1 154H143.9c-24.5 0-39.8 26.7-27.5 48L349 597.4V838c0 17.7 14.2 32 31.8 32h262.4c17.6 0 31.8-14.3 31.8-32V597.4L907.7 202c12.2-21.3-3.1-48-27.6-48zM603.4 798H420.6V642h182.9v156zm9.6-236.6l-9.5 16.6h-183l-9.5-16.6L212.7 226h598.6L613 561.4z"
/>
</svg>
</i>
</span>
<path
d="M349 838c0 17.7 14.2 32 31.8 32h262.4c17.6 0 31.8-14.3 31.8-32V642H349v196zm531.1-684H143.9c-24.5 0-39.8 26.7-27.5 48l221.3 376h348.8l221.3-376c12.1-21.3-3.2-48-27.7-48z"
/>
</svg>
</i>
</th>
</tr>
</thead>
@ -126,7 +128,7 @@ exports[`Table.filter renders filter correctly 1`] = `
data-row-key="0"
>
<td
class="ant-table-column-has-filters"
class="ant-table-column-has-actions ant-table-column-has-filters"
>
<span
class="ant-table-row-indent indent-level-0"
@ -140,7 +142,7 @@ exports[`Table.filter renders filter correctly 1`] = `
data-row-key="1"
>
<td
class="ant-table-column-has-filters"
class="ant-table-column-has-actions ant-table-column-has-filters"
>
<span
class="ant-table-row-indent indent-level-0"
@ -154,7 +156,7 @@ exports[`Table.filter renders filter correctly 1`] = `
data-row-key="2"
>
<td
class="ant-table-column-has-filters"
class="ant-table-column-has-actions ant-table-column-has-filters"
>
<span
class="ant-table-row-indent indent-level-0"
@ -168,7 +170,7 @@ exports[`Table.filter renders filter correctly 1`] = `
data-row-key="3"
>
<td
class="ant-table-column-has-filters"
class="ant-table-column-has-actions ant-table-column-has-filters"
>
<span
class="ant-table-row-indent indent-level-0"
@ -190,7 +192,7 @@ exports[`Table.filter renders filter correctly 1`] = `
exports[`Table.filter renders menu correctly 1`] = `
<div>
<div
class="ant-dropdown ant-dropdown-placement-bottomLeft ant-dropdown-hidden"
class="ant-dropdown ant-dropdown-placement-bottomRight ant-dropdown-hidden"
>
<div
class="ant-table-filter-dropdown"
@ -285,7 +287,7 @@ exports[`Table.filter renders menu correctly 1`] = `
exports[`Table.filter renders radio filter correctly 1`] = `
<div>
<div
class="ant-dropdown ant-dropdown-placement-bottomLeft ant-dropdown-hidden"
class="ant-dropdown ant-dropdown-placement-bottomRight ant-dropdown-hidden"
>
<div
class="ant-table-filter-dropdown"

View File

@ -32,9 +32,11 @@ exports[`Table.pagination renders pagination correctly 1`] = `
<th
class=""
>
<span>
<div
title="Sort"
>
Name
</span>
</div>
</th>
</tr>
</thead>

View File

@ -36,7 +36,9 @@ exports[`Table.rowSelection fix selection column on the left 1`] = `
<th
class="ant-table-fixed-columns-in-body ant-table-selection-column"
>
<span>
<div
title="Sort"
>
<div
class="ant-table-selection"
>
@ -56,14 +58,16 @@ exports[`Table.rowSelection fix selection column on the left 1`] = `
</span>
</label>
</div>
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Name
</span>
</div>
</th>
</tr>
</thead>
@ -237,7 +241,9 @@ exports[`Table.rowSelection fix selection column on the left 1`] = `
<th
class="ant-table-selection-column"
>
<span>
<div
title="Sort"
>
<div
class="ant-table-selection"
>
@ -257,7 +263,7 @@ exports[`Table.rowSelection fix selection column on the left 1`] = `
</span>
</label>
</div>
</span>
</div>
</th>
</tr>
</thead>

View File

@ -6,59 +6,52 @@ exports[`Table.sorter renders sorter icon correctly 1`] = `
>
<tr>
<th
class="ant-table-column-has-filters"
class="ant-table-column-has-actions ant-table-column-has-sorters"
>
<span>
<div
class="ant-table-column-sorters"
title="Sort"
>
Name
<div
class="ant-table-column-sorter"
>
<span
class="ant-table-column-sorter-up off"
title="↑"
<i
class="anticon anticon-caret-up ant-table-column-sorter-up off"
>
<i
class="anticon anticon-caret-up"
<svg
aria-hidden="true"
class=""
data-icon="caret-up"
fill="currentColor"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<svg
aria-hidden="true"
class=""
data-icon="caret-up"
fill="currentColor"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M76.3 632.8l405.1-489.1a42.53 42.53 0 0 1 63.4 0l403.1 489.1c24.4 27.4 5 70.8-31.7 70.8H107.9c-36.6 0-56.1-43.4-31.6-70.8z"
/>
</svg>
</i>
</span>
<span
class="ant-table-column-sorter-down off"
title="↓"
<path
d="M76.3 632.8l405.1-489.1a42.53 42.53 0 0 1 63.4 0l403.1 489.1c24.4 27.4 5 70.8-31.7 70.8H107.9c-36.6 0-56.1-43.4-31.6-70.8z"
/>
</svg>
</i>
<i
class="anticon anticon-caret-down ant-table-column-sorter-down off"
>
<i
class="anticon anticon-caret-down"
<svg
aria-hidden="true"
class=""
data-icon="caret-down"
fill="currentColor"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<svg
aria-hidden="true"
class=""
data-icon="caret-down"
fill="currentColor"
height="1em"
viewBox="0 0 1024 1024"
width="1em"
>
<path
d="M76.3 392.3l403.1 489.1a42.53 42.53 0 0 0 63.4 0l405.1-489.1c24.4-27.4 5-70.8-31.7-70.8H108.1c-36.8 0-56.3 43.4-31.8 70.8z"
/>
</svg>
</i>
</span>
<path
d="M76.3 392.3l403.1 489.1a42.53 42.53 0 0 0 63.4 0l405.1-489.1c24.4-27.4 5-70.8-31.7-70.8H108.1c-36.8 0-56.3 43.4-31.8 70.8z"
/>
</svg>
</i>
</div>
</span>
</div>
</th>
</tr>
</thead>

View File

@ -35,33 +35,41 @@ exports[`Table renders JSX correctly 1`] = `
class=""
colspan="2"
>
<span>
<div
title="Sort"
>
Name
</span>
</div>
</th>
<th
class=""
rowspan="2"
>
<span>
<div
title="Sort"
>
Age
</span>
</div>
</th>
</tr>
<tr>
<th
class=""
>
<span>
<div
title="Sort"
>
First Name
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Last Name
</span>
</div>
</th>
</tr>
</thead>

File diff suppressed because it is too large Load Diff

View File

@ -39,58 +39,74 @@ exports[`Table renders empty table 1`] = `
<th
class=""
>
<span>
<div
title="Sort"
>
Column 1
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 2
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 3
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 4
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 5
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 6
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 7
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 8
</span>
</div>
</th>
</tr>
</thead>
@ -150,58 +166,74 @@ exports[`Table renders empty table with custom emptyText 1`] = `
<th
class=""
>
<span>
<div
title="Sort"
>
Column 1
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 2
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 3
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 4
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 5
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 6
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 7
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 8
</span>
</div>
</th>
</tr>
</thead>
@ -273,79 +305,101 @@ exports[`Table renders empty table with fixed columns 1`] = `
<th
class="ant-table-fixed-columns-in-body"
>
<span>
<div
title="Sort"
>
Full Name
</span>
</div>
</th>
<th
class="ant-table-fixed-columns-in-body"
>
<span>
<div
title="Sort"
>
Age
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 1
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 2
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 3
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 4
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 5
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 6
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 7
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 8
</span>
</div>
</th>
<th
class="ant-table-fixed-columns-in-body"
>
<span>
<div
title="Sort"
>
Action
</span>
</div>
</th>
</tr>
</thead>
@ -388,16 +442,20 @@ exports[`Table renders empty table with fixed columns 1`] = `
<th
class=""
>
<span>
<div
title="Sort"
>
Full Name
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Age
</span>
</div>
</th>
</tr>
</thead>
@ -433,9 +491,11 @@ exports[`Table renders empty table with fixed columns 1`] = `
<th
class=""
>
<span>
<div
title="Sort"
>
Action
</span>
</div>
</th>
</tr>
</thead>
@ -506,58 +566,74 @@ exports[`Table renders empty table without emptyText when loading 1`] = `
<th
class=""
>
<span>
<div
title="Sort"
>
Column 1
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 2
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 3
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 4
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 5
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 6
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 7
</span>
</div>
</th>
<th
class=""
>
<span>
<div
title="Sort"
>
Column 8
</span>
</div>
</th>
</tr>
</thead>

View File

@ -11,6 +11,13 @@ import Radio from '../radio';
import FilterDropdownMenuWrapper from './FilterDropdownMenuWrapper';
import { FilterMenuProps, FilterMenuState, ColumnProps, ColumnFilterItem } from './interface';
function stopPropagation(e: React.SyntheticEvent<any>) {
e.stopPropagation();
if (e.nativeEvent.stopImmediatePropagation) {
e.nativeEvent.stopImmediatePropagation();
}
}
export default class FilterMenu<T> extends React.Component<FilterMenuProps<T>, FilterMenuState> {
static defaultProps = {
handleFilter() {},
@ -63,6 +70,10 @@ export default class FilterMenu<T> extends React.Component<FilterMenuProps<T>, F
}
}
getDropdownVisible() {
return this.neverShown ? false : this.state.visible;
}
setNeverShown = (column: ColumnProps<T>) => {
const rootNode = ReactDOM.findDOMNode(this);
const filterBelongToScrollBody = !!closest(rootNode, `.ant-table-scroll`);
@ -179,13 +190,27 @@ export default class FilterMenu<T> extends React.Component<FilterMenuProps<T>, F
if (typeof filterIcon === 'function') {
filterIcon = filterIcon(filterd);
}
const dropdownSelectedClass = filterd ? `${prefixCls}-selected` : '';
const dropdownIconClass = classNames({
[`${prefixCls}-selected`]: filterd,
[`${prefixCls}-open`]: this.getDropdownVisible(),
});
return filterIcon ? React.cloneElement(filterIcon as any, {
title: locale.filterTitle,
className: classNames(`${prefixCls}-icon`, filterIcon.props.className),
}) : <Icon title={locale.filterTitle} type="filter" className={dropdownSelectedClass} />;
className: classNames(`${prefixCls}-icon`, dropdownIconClass, filterIcon.props.className),
onClick: stopPropagation,
}) : (
<Icon
title={locale.filterTitle}
type="filter"
theme="filled"
className={dropdownIconClass}
onClick={stopPropagation}
/>
);
}
render() {
const { column, locale, prefixCls, dropdownPrefixCls, getPopupContainer } = this.props;
// default multiple selection in filter dropdown
@ -244,8 +269,9 @@ export default class FilterMenu<T> extends React.Component<FilterMenuProps<T>, F
return (
<Dropdown
trigger={['click']}
placement="bottomRight"
overlay={menus}
visible={this.neverShown ? false : this.state.visible}
visible={this.getDropdownVisible()}
onVisibleChange={this.onVisibleChange}
getPopupContainer={getPopupContainer}
forceRender

View File

@ -74,7 +74,7 @@ const columns = [{
| 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) | |
| 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) | - |
@ -124,7 +124,7 @@ One of the Table `columns` prop for describing the table's columns, Column has t
| render | Renderer of the table cell. The return value should be a ReactNode, or an object for [colSpan/rowSpan config](#components-table-demo-colspan-rowspan) | Function(text, record, index) {} | - |
| sorter | Sort function for local sort, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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 | string\|ReactNode | - |
| title | Title of this column | ReactNode\|({ sortOrder, filters }) => ReactNode | - |
| width | Width of this column | string\|number | - |
| onCell | Set props on per cell | Function(record) | - |
| onFilter | Callback executed when the confirm filter button is clicked | Function | - |

View File

@ -79,7 +79,7 @@ const columns = [{
| showHeader | 是否显示表头 | boolean | true |
| size | 正常或迷你类型,`default` or `small` | string | default |
| title | 表格标题 | Function(currentPageData) | |
| onChange | 分页、排序、筛选变化时触发 | Function(pagination, filters, sorter) | |
| onChange | 分页、排序、筛选变化时触发 | Function(pagination, filters, sorter, extra: { currentDataSource: [] }) | |
| onExpand | 点击展开图标时触发 | Function(expanded, record) | |
| onExpandedRowsChange | 展开的行变化时触发 | Function(expandedRows) | |
| onHeaderRow | 设置头部行属性 | Function(column, index) | - |
@ -129,7 +129,7 @@ const columns = [{
| render | 生成复杂数据的渲染函数,参数分别为当前行的值,当前行数据,行索引,@return里面可以设置表格[行/列合并](#components-table-demo-colspan-rowspan) | Function(text, record, index) {} | - |
| sorter | 排序函数,本地排序使用一个函数(参考 [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 的 compareFunction),需要服务端排序可设为 true | Function\|boolean | - |
| sortOrder | 排序的受控属性,外界可用此控制列的排序,可设置为 `'ascend'` `'descend'` `false` | boolean\|string | - |
| title | 列头显示文字 | string\|ReactNode | - |
| title | 列头显示文字 | ReactNode\|({ sortOrder, filters }) => ReactNode | - |
| width | 列宽度 | string\|number | - |
| onCell | 设置单元格属性 | Function(record) | - |
| onFilter | 本地模式下,确定筛选的运行函数 | Function | - |

View File

@ -6,11 +6,14 @@ import { CheckboxChangeEvent } from '../checkbox';
import { PaginationConfig } from '../pagination';
export { PaginationConfig } from '../pagination';
export type CompareFn<T> = ((a: T, b: T, sortOrder?: 'ascend' | 'descend') => number);
export type CompareFn<T> = ((a: T, b: T, sortOrder?: SortOrder) => number);
export type ColumnFilterItem = { text: string; value: string, children?: ColumnFilterItem[] };
export interface ColumnProps<T> {
title?: React.ReactNode;
title?: React.ReactNode | ((options: {
filters: TableStateFilters,
sortOrder?: SortOrder,
}) => React.ReactNode);
key?: React.Key;
dataIndex?: string;
render?: (text: any, record: T, index: number) => React.ReactNode;
@ -22,14 +25,14 @@ export interface ColumnProps<T> {
filterDropdownVisible?: boolean;
onFilterDropdownVisibleChange?: (visible: boolean) => void;
sorter?: boolean | CompareFn<T>;
defaultSortOrder?: 'ascend' | 'descend';
defaultSortOrder?: SortOrder;
colSpan?: number;
width?: string | number;
className?: string;
fixed?: boolean | ('left' | 'right');
filterIcon?: React.ReactNode | ((filtered: boolean) => React.ReactNode);
filteredValue?: any[];
sortOrder?: 'ascend' | 'descend';
sortOrder?: SortOrder;
children?: ColumnProps<T>[];
onCellClick?: (record: T, event: any) => void;
onCell?: (record: T) => any;
@ -57,6 +60,7 @@ export interface TableLocale {
emptyText?: React.ReactNode | (() => React.ReactNode);
selectAll?: React.ReactNode;
selectInvert?: React.ReactNode;
sortTitle?: string;
}
export type RowSelectionType = 'checkbox' | 'radio';
@ -208,3 +212,12 @@ export interface FilterMenuState {
keyPathOfSelectedItem: { [key: string]: string };
visible?: boolean;
}
export type PrepareParamsArgumentsReturn<T> = [
any,
string[],
Object,
{
currentDataSource: T[],
}
];

View File

@ -2,7 +2,7 @@
@import "../../style/mixins/index";
@table-prefix-cls: ~"@{ant-prefix}-table";
@table-header-icon-color: @text-color-secondary;
@table-header-icon-color: #bfbfbf;
.@{table-prefix-cls}-wrapper {
.clearfix;
@ -42,32 +42,116 @@
.@{iconfont-css-prefix}-filter,
.@{table-prefix-cls}-filter-icon {
position: relative;
margin-left: 8px;
font-size: @font-size-base;
font-size: @font-size-sm;
cursor: pointer;
color: @table-header-icon-color;
transition: all .3s;
width: 14px;
font-weight: normal;
vertical-align: text-bottom;
width: 28px;
position: absolute;
top: 0;
right: 0;
height: 100%;
text-align: center;
&:hover {
color: @text-color;
> svg {
position: absolute;
top: 50%;
left: 50%;
margin-top: -@font-size-sm / 2 + 1px;
margin-left: -@font-size-sm / 2;
}
}
.@{table-prefix-cls}-column-sorter + .@{iconfont-css-prefix}-filter {
margin-left: 4px;
}
.@{table-prefix-cls}-filter-selected.@{iconfont-css-prefix}-filter {
color: @primary-color;
}
// https://github.com/ant-design/ant-design/issues/8979
&.@{table-prefix-cls}-column-has-filters {
overflow: hidden;
.@{table-prefix-cls}-column-sorter {
position: absolute;
right: 6px;
top: 50%;
width: 14px;
height: @font-size-base;
margin-top: -@font-size-base / 2;
text-align: center;
color: @table-header-icon-color;
transition: all .3s;
&-up,
&-down {
.iconfont-size-under-12px(8px);
line-height: 4px;
height: 4px;
transition: all .3s;
display: block;
&.on {
color: @primary-color;
}
}
&-down {
margin-top: 2px;
}
}
&.@{table-prefix-cls}-column-has-actions {
position: relative;
cursor: pointer;
// Very complicated styles logic but neccessary
&:hover {
background: #f5f5f5;
.@{iconfont-css-prefix}-filter,
.@{table-prefix-cls}-filter-icon {
background: #f5f5f5;
&:hover {
color: @text-color-secondary;
background: #ebebeb;
}
&:active {
color: @text-color;
}
}
}
.@{iconfont-css-prefix}-filter,
.@{table-prefix-cls}-filter-icon {
&.@{table-prefix-cls}-filter-open {
color: @text-color-secondary;
background: #ebebeb;
}
}
&:active {
.@{table-prefix-cls}-column-sorter-up:not(.on),
.@{table-prefix-cls}-column-sorter-down:not(.on) {
color: @text-color-secondary;
}
}
}
.@{table-prefix-cls}-column-sorters {
&:before {
position: absolute;
content: '';
top: 0;
left: 0;
right: 0;
bottom: 0;
background: transparent;
transition: all .3s;
}
&:hover:before {
background: rgba(0, 0, 0, .04);
}
}
&.@{table-prefix-cls}-column-has-filters .@{table-prefix-cls}-column-sorter {
right: 28px + 6px;
}
&.@{table-prefix-cls}-column-has-sorters {
user-select: none;
}
}
@ -168,6 +252,10 @@
background: @table-header-sort-bg;
}
&-tbody > tr > td.@{table-prefix-cls}-column-sort {
background: @table-body-sort-bg;
}
&-thead > tr > th,
&-tbody > tr > td {
padding: @table-padding-vertical @table-padding-horizontal;
@ -228,72 +316,6 @@
}
}
&-column-sorter {
position: relative;
margin-left: 8px;
display: inline-block;
vertical-align: text-bottom;
top: -1.5px;
width: 14px;
height: @font-size-base;
text-align: center;
font-weight: normal;
color: @table-header-icon-color;
&-up,
&-down {
display: block;
width: 14px;
height: @font-size-base / 2 - 1px;
line-height: @font-size-base / 2 - 1px;
cursor: pointer;
position: relative;
&:hover .@{iconfont-css-prefix} {
color: @primary-4;
}
&.on {
.@{iconfont-css-prefix}-caret-up,
.@{iconfont-css-prefix}-caret-down {
color: @primary-color;
}
}
&:after {
position: absolute;
content: '';
height: 30px;
width: 14px;
left: 0;
}
}
&-up:after {
bottom: 0;
}
&-down:after {
top: 0;
}
.@{iconfont-css-prefix}-caret-up,
.@{iconfont-css-prefix}-caret-down {
.iconfont-size-under-12px(8px);
line-height: 4px;
height: 4px;
transition: all .3s;
position: relative;
display: block;
}
&-down {
margin-top: 1.5px;
}
.@{iconfont-css-prefix}-caret-up {
margin-top: 0.5px;
}
}
&-bordered {
.@{table-prefix-cls}-header > table,
.@{table-prefix-cls}-body > table,

View File

@ -411,6 +411,41 @@ exports[`renders ./components/time-picker/demo/size.md correctly 1`] = `
</div>
`;
exports[`renders ./components/time-picker/demo/suffix.md correctly 1`] = `
<span
class="ant-time-picker "
>
<input
class="ant-time-picker-input"
id=""
placeholder="Select time"
type="text"
value=""
/>
<span
class="ant-time-picker-icon"
>
<i
class="anticon anticon-smile ant-time-picker-clock-icon"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
</span>
</span>
`;
exports[`renders ./components/time-picker/demo/value.md correctly 1`] = `
<span
class="ant-time-picker "

View File

@ -0,0 +1,31 @@
---
order: 12
debug: true
title:
zh-CN: 后缀图标
en-US: Suffix
---
## zh-CN
点击 TimePicker然后可以在浮层中选择或者输入某一时间。
## en-US
Click `TimePicker`, and then we could select or input a time in panel.
````jsx
import { TimePicker, Icon } from 'antd';
import moment from 'moment';
function onChange(time, timeString) {
console.log(time, timeString);
}
const icon = <Icon type="smile" />;
ReactDOM.render(
<TimePicker suffixIcon={icon} onChange={onChange} defaultOpenValue={moment('00:00:00', 'HH:mm:ss')} />,
mountNode
);
````

View File

@ -44,6 +44,7 @@ import moment from 'moment';
| placeholder | display when there's no value | string | "Select a time" |
| popupClassName | className of panel | string | '' |
| secondStep | interval between seconds in picker | number | 1 |
| suffixIcon | The custom suffix icon | ReactNode | - |
| use12Hours | display as 12 hours format, with default format `h:mm:ss a` | boolean | false |
| value | to set time | [moment](http://momentjs.com/) | - |
| onChange | a callback function, can be executed when the selected time is changing | function(time: moment, timeString: string): void | - |

View File

@ -50,6 +50,7 @@ export interface TimePickerProps {
clearText?: string;
defaultOpenValue?: moment.Moment;
popupClassName?: string;
suffixIcon?: React.ReactNode;
}
export interface TimePickerLocale {
@ -153,20 +154,35 @@ class TimePicker extends React.Component<TimePickerProps, any> {
) : null
);
const inputIcon = (
<span className={`${props.prefixCls}-icon`}>
const { suffixIcon, prefixCls } = props;
const clockIcon = suffixIcon && (
React.isValidElement<{ className?: string }>(suffixIcon)
? React.cloneElement(
suffixIcon,
{
className: classNames({
[suffixIcon.props.className!]: suffixIcon.props.className,
[`${prefixCls}-clock-icon`]: true,
}),
},
) : <span className={`${prefixCls}-clock-icon`}>{suffixIcon}</span>) || (
<Icon
type="clock-circle"
className={`${props.prefixCls}-clock-icon`}
className={`${prefixCls}-clock-icon`}
theme="outlined"
/>
);
const inputIcon = (
<span className={`${prefixCls}-icon`}>
{clockIcon}
</span>
);
const clearIcon = (
<Icon
type="close-circle"
className={`${props.prefixCls}-panel-clear-btn-icon`}
className={`${prefixCls}-panel-clear-btn-icon`}
theme="filled"
/>
);

View File

@ -45,6 +45,7 @@ import moment from 'moment';
| placeholder | 没有值的时候显示的内容 | string | "请选择时间" |
| popupClassName | 弹出层类名 | string | '' |
| secondStep | 秒选项间隔 | number | 1 |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - |
| use12Hours | 使用 12 小时制,为 true 时 `format` 默认为 `h:mm:ss a` | boolean | false |
| value | 当前时间 | [moment](http://momentjs.com/) | 无 |
| onChange | 时间发生变化的回调 | function(time: moment, timeString: string): void | 无 |

View File

@ -248,235 +248,249 @@ exports[`renders ./components/transfer/demo/advanced.md correctly 1`] = `
`;
exports[`renders ./components/transfer/demo/basic.md correctly 1`] = `
<div
class="ant-transfer"
>
<div>
<div
class="ant-transfer-list"
class="ant-transfer"
>
<div
class="ant-transfer-list-header"
class="ant-transfer-list"
>
<label
class="ant-checkbox-wrapper"
<div
class="ant-transfer-list-header"
>
<span
class="ant-checkbox"
<label
class="ant-checkbox-wrapper"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
<span
class="ant-transfer-list-header-selected"
>
<span>
14 items
</span>
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
<span
class="ant-transfer-list-header-title"
class="ant-transfer-list-header-selected"
>
Source
<span>
14 items
</span>
<span
class="ant-transfer-list-header-title"
>
Source
</span>
</span>
</span>
</div>
<div
class="ant-transfer-list-body"
>
<ul
class="ant-transfer-list-content"
>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
</ul>
<div
class="ant-transfer-list-body-not-found"
>
Not Found
</div>
</div>
</div>
<div
class="ant-transfer-list-body"
class="ant-transfer-operation"
>
<ul
class="ant-transfer-list-content"
<button
class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only"
disabled=""
type="button"
>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
</ul>
<i
class="anticon anticon-right"
>
<svg
aria-hidden="true"
class=""
data-icon="right"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 0 0 302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 0 0 0-50.4z"
/>
</svg>
</i>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only"
disabled=""
type="button"
>
<i
class="anticon anticon-left"
>
<svg
aria-hidden="true"
class=""
data-icon="left"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 0 0 0 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
/>
</svg>
</i>
</button>
</div>
<div
class="ant-transfer-list"
>
<div
class="ant-transfer-list-body-not-found"
class="ant-transfer-list-header"
>
Not Found
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
<span
class="ant-transfer-list-header-selected"
>
<span>
6 items
</span>
<span
class="ant-transfer-list-header-title"
>
Target
</span>
</span>
</div>
<div
class="ant-transfer-list-body"
>
<ul
class="ant-transfer-list-content"
>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
</ul>
<div
class="ant-transfer-list-body-not-found"
>
Not Found
</div>
</div>
</div>
</div>
<div
class="ant-transfer-operation"
<button
aria-checked="false"
class="ant-switch"
role="switch"
type="button"
>
<button
class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only"
disabled=""
type="button"
<span
class="ant-switch-inner"
>
<i
class="anticon anticon-right"
>
<svg
aria-hidden="true"
class=""
data-icon="right"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M765.7 486.8L314.9 134.7A7.97 7.97 0 0 0 302 141v77.3c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1a31.96 31.96 0 0 0 0-50.4z"
/>
</svg>
</i>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only"
disabled=""
type="button"
>
<i
class="anticon anticon-left"
>
<svg
aria-hidden="true"
class=""
data-icon="left"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M724 218.3V141c0-6.7-7.7-10.4-12.9-6.3L260.3 486.8a31.86 31.86 0 0 0 0 50.3l450.8 352.1c5.3 4.1 12.9.4 12.9-6.3v-77.3c0-4.9-2.3-9.6-6.1-12.6l-360-281 360-281.1c3.8-3 6.1-7.7 6.1-12.6z"
/>
</svg>
</i>
</button>
</div>
<div
class="ant-transfer-list"
>
<div
class="ant-transfer-list-header"
>
<label
class="ant-checkbox-wrapper"
>
<span
class="ant-checkbox"
>
<input
class="ant-checkbox-input"
type="checkbox"
/>
<span
class="ant-checkbox-inner"
/>
</span>
</label>
<span
class="ant-transfer-list-header-selected"
>
<span>
6 items
</span>
<span
class="ant-transfer-list-header-title"
>
Target
</span>
</span>
</div>
<div
class="ant-transfer-list-body"
>
<ul
class="ant-transfer-list-content"
>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
<div
class="LazyLoad"
style="height:32px"
/>
</ul>
<div
class="ant-transfer-list-body-not-found"
>
Not Found
</div>
</div>
</div>
disabled
</span>
</button>
</div>
`;

View File

@ -14,7 +14,7 @@ title:
The most basic usage of `Transfer` involves providing the source data and target keys arrays, plus the rendering and some callback functions.
````jsx
import { Transfer } from 'antd';
import { Transfer, Switch } from 'antd';
const mockData = [];
for (let i = 0; i < 20; i++) {
@ -26,20 +26,21 @@ for (let i = 0; i < 20; i++) {
});
}
const targetKeys = mockData
const oriTargetKeys = mockData
.filter(item => +item.key % 3 > 1)
.map(item => item.key);
class App extends React.Component {
state = {
targetKeys,
targetKeys: oriTargetKeys,
selectedKeys: [],
disabled: false,
}
handleChange = (nextTargetKeys, direction, moveKeys) => {
this.setState({ targetKeys: nextTargetKeys });
console.log('targetKeys: ', targetKeys);
console.log('targetKeys: ', nextTargetKeys);
console.log('direction: ', direction);
console.log('moveKeys: ', moveKeys);
}
@ -56,19 +57,28 @@ class App extends React.Component {
console.log('target:', e.target);
}
handleDisable = (disabled) => {
this.setState({ disabled });
};
render() {
const state = this.state;
const { targetKeys, selectedKeys, disabled } = this.state;
return (
<Transfer
dataSource={mockData}
titles={['Source', 'Target']}
targetKeys={state.targetKeys}
selectedKeys={state.selectedKeys}
onChange={this.handleChange}
onSelectChange={this.handleSelectChange}
onScroll={this.handleScroll}
render={item => item.title}
/>
<div>
<Transfer
dataSource={mockData}
titles={['Source', 'Target']}
targetKeys={targetKeys}
selectedKeys={selectedKeys}
onChange={this.handleChange}
onSelectChange={this.handleSelectChange}
onScroll={this.handleScroll}
render={item => item.title}
disabled={disabled}
/>
<Switch unCheckedChildren="disabled" checkedChildren="disabled" checked={disabled} onChange={this.handleDisable} />
</div>
);
}
}

View File

@ -18,7 +18,9 @@ One or more elements can be selected from either column, one click on the proper
| Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- |
| className | A custom CSS class. | string | ['', ''] |
| dataSource | Used for setting the source data. The elements that are part of this array will be present the left column. Except the elements whose keys are included in `targetKeys` prop. | [TransferItem](https://git.io/vMM64)\[] | \[] |
| dataSource | Used for setting the source data. The elements that are part of this array will be present the left column.
Except the elements whose keys are included in `targetKeys` prop. | [TransferItem](https://git.io/vMM64)\[] | \[] |
| disabled | Whether disabled transfer | boolean | false |
| filterOption | A function to determine whether an item should show in search result list | (inputValue, option): boolean | |
| footer | A function used for rendering the footer. | (props): ReactNode | |
| lazy | property of [react-lazy-load](https://github.com/loktar00/react-lazy-load) for lazy rendering items. Turn off it by set to `false`. | object\|boolean | `{ height: 32, offset: 32 }` |

View File

@ -27,6 +27,7 @@ export interface TransferItem {
export interface TransferProps {
prefixCls?: string;
className?: string;
disabled?: boolean;
dataSource: TransferItem[];
targetKeys?: string[];
selectedKeys?: string[];
@ -74,6 +75,7 @@ export default class Transfer extends React.Component<TransferProps, any> {
static propTypes = {
prefixCls: PropTypes.string,
disabled: PropTypes.bool,
dataSource: PropTypes.array,
render: PropTypes.func,
targetKeys: PropTypes.array,
@ -348,6 +350,7 @@ export default class Transfer extends React.Component<TransferProps, any> {
const {
prefixCls = 'ant-transfer',
className,
disabled,
operations = [],
showSearch,
body,
@ -366,7 +369,7 @@ export default class Transfer extends React.Component<TransferProps, any> {
const leftActive = targetSelectedKeys.length > 0;
const rightActive = sourceSelectedKeys.length > 0;
const cls = classNames(className, prefixCls);
const cls = classNames(className, prefixCls, disabled && `${prefixCls}-disabled`);
const titles = this.getTitles(locale);
return (
@ -389,6 +392,7 @@ export default class Transfer extends React.Component<TransferProps, any> {
footer={footer}
lazy={lazy}
onScroll={this.handleLeftScroll}
disabled={disabled}
{...locale}
/>
<Operation
@ -400,6 +404,7 @@ export default class Transfer extends React.Component<TransferProps, any> {
leftArrowText={operations[1]}
moveToLeft={this.moveToLeft}
style={operationStyle}
disabled={disabled}
/>
<List
prefixCls={`${prefixCls}-list`}
@ -419,6 +424,7 @@ export default class Transfer extends React.Component<TransferProps, any> {
footer={footer}
lazy={lazy}
onScroll={this.handleRightScroll}
disabled={disabled}
{...locale}
/>
</div>

View File

@ -21,6 +21,7 @@ title: Transfer
| --- | --- | --- | --- |
| className | 自定义类 | string | |
| dataSource | 数据源,其中的数据将会被渲染到左边一栏中,`targetKeys` 中指定的除外。 | [TransferItem](https://git.io/vMM64)\[] | \[] |
| disabled | 是否禁用 | boolean | false |
| filterOption | 接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | (inputValue, option): boolean | |
| footer | 底部渲染函数 | (props): ReactNode | |
| lazy | Transfer 使用了 [react-lazy-load](https://github.com/loktar00/react-lazy-load) 优化性能,这里可以设置相关参数。设为 `false` 可以关闭懒加载。 | object\|boolean | `{ height: 32, offset: 32 }` |

View File

@ -9,11 +9,14 @@ export default class Item extends React.Component<any, any> {
return PureRenderMixin.shouldComponentUpdate.apply(this, args);
}
render() {
const { renderedText, renderedEl, item, lazy, checked, prefixCls, onClick } = this.props;
const {
renderedText, renderedEl, item, lazy,
checked, disabled, prefixCls, onClick,
} = this.props;
const className = classNames({
[`${prefixCls}-content-item`]: true,
[`${prefixCls}-content-item-disabled`]: item.disabled,
[`${prefixCls}-content-item-disabled`]: disabled || item.disabled,
});
let title: string | undefined = undefined;
@ -25,9 +28,9 @@ export default class Item extends React.Component<any, any> {
<li
className={className}
title={title}
onClick={item.disabled ? undefined : () => onClick(item)}
onClick={(disabled || item.disabled) ? undefined : () => onClick(item)}
>
<Checkbox checked={checked} disabled={item.disabled} />
<Checkbox checked={checked} disabled={disabled || item.disabled} />
<span>{renderedEl}</span>
</li>
);

View File

@ -43,6 +43,7 @@ export interface TransferListProps {
footer?: (props: TransferListProps) => React.ReactNode;
lazy?: boolean | {};
onScroll: Function;
disabled?: boolean;
}
export default class TransferList extends React.Component<TransferListProps, any> {
@ -160,7 +161,7 @@ export default class TransferList extends React.Component<TransferListProps, any
render() {
const {
prefixCls, dataSource, titleText, checkedKeys, lazy,
prefixCls, dataSource, titleText, checkedKeys, lazy, disabled,
body, footer, showSearch, style, filter,
searchPlaceholder, notFoundContent, itemUnit, itemsUnit, onScroll,
} = this.props;
@ -192,6 +193,7 @@ export default class TransferList extends React.Component<TransferListProps, any
const checked = checkedKeys.indexOf(item.key) >= 0;
return (
<Item
disabled={disabled}
key={item.key}
item={item}
lazy={lazy}
@ -219,7 +221,7 @@ export default class TransferList extends React.Component<TransferListProps, any
) : null;
const listBody = bodyDom || (
<div className={showSearch ? `${prefixCls}-body ${prefixCls}-body-with-search` : `${prefixCls}-body`}>
<div className={classNames(showSearch ? `${prefixCls}-body ${prefixCls}-body-with-search` : `${prefixCls}-body`)}>
{search}
<Animate
component="ul"
@ -247,6 +249,7 @@ export default class TransferList extends React.Component<TransferListProps, any
const checkAllCheckbox = (
<Checkbox
ref="checkbox"
disabled={disabled}
checked={checkedAll}
indeterminate={checkStatus === 'part'}
onChange={() => this.props.handleSelectAll(filteredDataSource, checkedAll)}

View File

@ -10,11 +10,13 @@ export interface TransferOperationProps {
leftActive?: boolean;
rightActive?: boolean;
style?: React.CSSProperties;
disabled?: boolean;
}
export default class Operation extends React.Component<TransferOperationProps, any> {
render() {
const {
disabled,
moveToLeft,
moveToRight,
leftArrowText = '',
@ -29,7 +31,7 @@ export default class Operation extends React.Component<TransferOperationProps, a
<Button
type="primary"
size="small"
disabled={!rightActive}
disabled={disabled || !rightActive}
onClick={moveToRight}
icon="right"
>
@ -38,7 +40,7 @@ export default class Operation extends React.Component<TransferOperationProps, a
<Button
type="primary"
size="small"
disabled={!leftActive}
disabled={disabled || !leftActive}
onClick={moveToLeft}
icon="left"
>

View File

@ -8,6 +8,12 @@
.reset-component;
position: relative;
&-disabled {
.@{transfer-prefix-cls}-list {
background: @transfer-disabled-bg;
}
}
&-list {
border: @border-width-base @border-style-base @border-color-base;
display: inline-block;

View File

@ -172,6 +172,52 @@ exports[`renders ./components/tree-select/demo/multiple.md correctly 1`] = `
</span>
`;
exports[`renders ./components/tree-select/demo/suffix.md correctly 1`] = `
<span
aria-haspopup="listbox"
class="ant-select ant-select-enabled ant-select-allow-clear"
role="combobox"
style="width:300px"
tabindex="0"
>
<span
class="ant-select-selection ant-select-selection--single"
>
<span
class="ant-select-selection__rendered"
>
<span
class="ant-select-selection__placeholder"
>
Please select
</span>
</span>
<span
class="ant-select-arrow"
style="outline:none"
>
<i
class="anticon anticon-smile"
>
<svg
aria-hidden="true"
class=""
data-icon="smile"
fill="currentColor"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"
/>
</svg>
</i>
</span>
</span>
</span>
`;
exports[`renders ./components/tree-select/demo/treeData.md correctly 1`] = `
<span
aria-haspopup="listbox"

View File

@ -0,0 +1,61 @@
---
order: 12
debug: true
title:
zh-CN: 后缀图标
en-US: Suffix
---
## zh-CN
最简单的用法。
## en-US
The most basic usage.
````jsx
import { TreeSelect, Icon } from 'antd';
const TreeNode = TreeSelect.TreeNode;
const icon = <Icon type="smile" />;
class Demo extends React.Component {
state = {
value: undefined,
}
onChange = (value) => {
console.log(value);
this.setState({ value });
}
render() {
return (
<TreeSelect
showSearch
suffixIcon={icon}
style={{ width: 300 }}
value={this.state.value}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
placeholder="Please select"
allowClear
treeDefaultExpandAll
onChange={this.onChange}
>
<TreeNode value="parent 1" title="parent 1" key="0-1">
<TreeNode value="parent 1-0" title="parent 1-0" key="0-1-1">
<TreeNode value="leaf1" title="my leaf" key="random" />
<TreeNode value="leaf2" title="your leaf" key="random1" />
</TreeNode>
<TreeNode value="parent 1-1" title="parent 1-1" key="random2">
<TreeNode value="sss" title={<b style={{ color: '#08c' }}>sss</b>} key="random3" />
</TreeNode>
</TreeNode>
</TreeSelect>
);
}
}
ReactDOM.render(<Demo />, mountNode);
````

View File

@ -37,18 +37,21 @@ Any data whose entries are defined in a hierarchical manner is fit to use this c
| showCheckedStrategy | The way show selected item in box. **Default:** just show child nodes. **`TreeSelect.SHOW_ALL`:** show all checked treeNodes (include parent treeNode). **`TreeSelect.SHOW_PARENT`:** show checked treeNodes (just show parent treeNode). | enum { TreeSelect.SHOW_ALL, TreeSelect.SHOW_PARENT, TreeSelect.SHOW_CHILD } | TreeSelect.SHOW_CHILD |
| showSearch | Whether to display a search input in the dropdown menu(valid only in the single mode) | boolean | false |
| size | To set the size of the select input, options: `large` `small` | string | 'default' |
| suffixIcon | The custom suffix icon | ReactNode | - |
| treeCheckable | Whether to show checkbox on the treeNodes | boolean | false |
| treeCheckStrictly | Whether to check nodes precisely (in the `checkable` mode), means parent and child nodes are not associated, and it will make `labelInValue` be true | boolean | false |
| treeData | Data of the treeNodes, manual construction work is no longer needed if this property has been set(ensure the Uniqueness of each value) | array&lt;{ value, title, children, [disabled, disableCheckbox, selectable] }> | \[] |
| treeDataSimpleMode | Enable simple mode of treeData. Changes the `treeData` schema to: [{id:1, pId:0, value:'1', title:"test1",...},...] where pId is parent node's id). It is possible to replace the default `id` and `pId` keys by providing object to `treeDataSimpleMode` | false\|Array&lt;{ id: string, pId: string, rootPId: null }> | false |
| treeDefaultExpandAll | Whether to expand all treeNodes by default | boolean | false |
| treeDefaultExpandedKeys | Default expanded treeNodes | string\[] | - |
| treeExpandedKeys | Set expanded keys | string\[] | - |
| treeNodeFilterProp | Will be used for filtering if `filterTreeNode` returns true | string | 'value' |
| treeNodeLabelProp | Will render as content of select | string | 'title' |
| value | To set the current selected treeNode(s). | string\|string\[] | - |
| onChange | A callback function, can be executed when selected treeNodes or input value change | function(value, label, extra) | - |
| onSearch | A callback function, can be executed when the search input changes. | function(value: string) | - |
| onSelect | A callback function, can be executed when you select a treeNode. | function(value, node, extra) | - |
| onTreeExpand | A callback function, can be executed when treeNode expanded | function(expandedKeys) | - |
### Tree Methods

View File

@ -7,6 +7,7 @@ import LocaleReceiver from '../locale-provider/LocaleReceiver';
import warning from '../_util/warning';
import Icon from '../icon';
import { AntTreeNodeProps } from '../tree';
import omit from 'omit.js';
export { TreeNode, TreeSelectProps } from './interface';
@ -74,22 +75,26 @@ export default class TreeSelect extends React.Component<TreeSelectProps, any> {
notFoundContent,
dropdownStyle,
dropdownClassName,
suffixIcon,
...restProps
} = this.props;
const rest = omit(restProps, ['inputIcon', 'removeIcon', 'clearIcon', 'switcherIcon']);
const cls = classNames({
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-sm`]: size === 'small',
}, className);
let checkable = restProps.treeCheckable;
let checkable = rest.treeCheckable;
if (checkable) {
checkable = <span className={`${prefixCls}-tree-checkbox-inner`} />;
}
const inputIcon = (
<Icon type="down" className={`${prefixCls}-arrow-icon`} />
);
const inputIcon = suffixIcon && (
React.isValidElement<{ className?: string }>(suffixIcon)
? React.cloneElement(suffixIcon) : suffixIcon) || (
<Icon type="down" className={`${prefixCls}-arrow-icon`} />
);
const removeIcon = (
<Icon type="close" className={`${prefixCls}-remove-icon`} />
@ -101,7 +106,11 @@ export default class TreeSelect extends React.Component<TreeSelectProps, any> {
return (
<RcTreeSelect
{...restProps}
switcherIcon={this.renderSwitcherIcon}
inputIcon={inputIcon}
removeIcon={removeIcon}
clearIcon={clearIcon}
{...rest}
dropdownClassName={classNames(dropdownClassName, `${prefixCls}-tree-dropdown`)}
prefixCls={prefixCls}
className={cls}
@ -109,10 +118,6 @@ export default class TreeSelect extends React.Component<TreeSelectProps, any> {
treeCheckable={checkable}
notFoundContent={notFoundContent || locale.notFoundContent}
ref={this.saveTreeSelect}
switcherIcon={this.renderSwitcherIcon}
inputIcon={inputIcon}
removeIcon={removeIcon}
clearIcon={clearIcon}
/>
);
}

View File

@ -37,18 +37,21 @@ title: TreeSelect
| showCheckedStrategy | 定义选中项回填的方式。`TreeSelect.SHOW_ALL`: 显示所有选中节点(包括父节点). `TreeSelect.SHOW_PARENT`: 只显示父节点(当父节点下所有子节点都选中时). 默认只显示子节点. | enum{TreeSelect.SHOW_ALL, TreeSelect.SHOW_PARENT, TreeSelect.SHOW_CHILD } | TreeSelect.SHOW_CHILD |
| showSearch | 在下拉中显示搜索框(仅在单选模式下生效) | boolean | false |
| size | 选择框大小,可选 `large` `small` | string | 'default' |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - |
| treeCheckable | 显示 checkbox | boolean | false |
| treeCheckStrictly | checkable 状态下节点选择完全受控(父子节点选中状态不再关联),会使得 `labelInValue` 强制为 true | boolean | false |
| treeData | treeNodes 数据,如果设置则不需要手动构造 TreeNode 节点value 在整个树范围内唯一) | array&lt;{value, title, children, [disabled, disableCheckbox, selectable]}> | \[] |
| treeDataSimpleMode | 使用简单格式的 treeData具体设置参考可设置的类型 (此时 treeData 应变为这样的数据结构: [{id:1, pId:0, value:'1', title:"test1",...},...], `pId` 是父节点的 id) | false\|Array&lt;{ id: string, pId: string, rootPId: null }> | false |
| treeDefaultExpandAll | 默认展开所有树节点 | boolean | false |
| treeDefaultExpandedKeys | 默认展开的树节点 | string\[] | - |
| treeExpandedKeys | 设置展开的树节点 | string\[] | - |
| treeNodeFilterProp | 输入项过滤对应的 treeNode 属性 | string | 'value' |
| treeNodeLabelProp | 作为显示的 prop 设置 | string | 'title' |
| value | 指定当前选中的条目 | string/string\[] | - |
| onChange | 选中树节点时调用此函数 | function(value, label, extra) | - |
| onSearch | 文本框值变化时回调 | function(value: string) | - |
| onSelect | 被选中时调用 | function(value, node, extra) | - |
| onTreeExpand | 展示节点时调用 | function(expandedKeys) | - |
### Tree 方法

View File

@ -52,4 +52,5 @@ export interface TreeSelectProps extends AbstractSelectProps {
labelInValue?: boolean;
treeCheckStrictly?: boolean;
getPopupContainer?: (triggerNode: Element) => HTMLElement;
suffixIcon?: React.ReactNode;
}

View File

@ -36,6 +36,7 @@ Uploading is the process of publishing information (web pages, text, pictures, v
| showUploadList | Whether to show default upload list, could be an object to specify `showPreviewIcon` and `showRemoveIcon` individually | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean } | true |
| supportServerRender | Need to be turned on while the server side is rendering. | boolean | false |
| withCredentials | ajax upload with cookie sent | boolean | false |
| openFileDialogOnClick | click open file dialog | boolean | true |
| onChange | A callback function, can be executed when uploading state is changing. See [onChange](#onChange) | Function | - |
| onPreview | A callback function, will be executed when file link or preview icon is clicked. | Function(file) | - |
| onRemove | A callback function, will be executed when removing file button is clicked, remove event will be prevented when return value is `false` or a Promise which resolve(false) or reject. | Function(file): `boolean | Promise` | - |

View File

@ -37,6 +37,7 @@ title: Upload
| showUploadList | 是否展示 uploadList, 可设为一个对象,用于单独设定 showPreviewIcon 和 showRemoveIcon | Boolean or { showPreviewIcon?: boolean, showRemoveIcon?: boolean } | true |
| supportServerRender | 服务端渲染时需要打开这个 | boolean | false |
| withCredentials | 上传请求时是否携带 cookie | boolean | false |
| openFileDialogOnClick | 点击打开文件对话框 | boolean | true |
| onChange | 上传文件改变时的状态,详见 [onChange](#onChange) | Function | 无 |
| onPreview | 点击文件链接或预览图标时的回调 | Function(file) | 无 |
| onRemove   | 点击移除文件时的回调,返回值为 false 时不移除。支持返回一个 Promise 对象Promise 对象 resolve(false) 或 reject 时不移除。               | Function(file): `boolean | Promise` | 无   |

View File

@ -74,6 +74,7 @@ export interface UploadProps {
prefixCls?: string;
customRequest?: (option: any) => void;
withCredentials?: boolean;
openFileDialogOnClick?: boolean;
locale?: UploadLocale;
}

View File

@ -65,24 +65,24 @@
"rc-dropdown": "~2.2.0",
"rc-editor-mention": "^1.0.2",
"rc-form": "^2.1.0",
"rc-input-number": "~4.0.0",
"rc-input-number": "~4.1.0",
"rc-menu": "~7.4.1",
"rc-notification": "~3.2.0",
"rc-pagination": "~1.17.0",
"rc-progress": "~2.2.2",
"rc-rate": "~2.4.0",
"rc-select": "~8.2.6",
"rc-select": "~8.3.0",
"rc-slider": "~8.6.0",
"rc-steps": "~3.3.0",
"rc-switch": "~1.7.0",
"rc-switch": "~1.8.0",
"rc-table": "~6.3.2",
"rc-tabs": "~9.4.0",
"rc-time-picker": "~3.4.0",
"rc-tooltip": "~3.7.0",
"rc-tree": "~1.14.5",
"rc-tree-select": "~2.2.0",
"rc-tree-select": "~2.3.0",
"rc-trigger": "^2.5.4",
"rc-upload": "~2.5.0",
"rc-upload": "~2.6.0",
"rc-util": "^4.0.4",
"react-lazy-load": "^3.0.12",
"react-lifecycles-compat": "^3.0.2",