feat: DatePicker and TimePicker support status (#34073)

* feat: DatePicker and TimePicker support status

* docs: demo add version

* chore: code clean
pull/34080/head
MadCcc 3 years ago committed by GitHub
parent 65bf6550b1
commit 8054abe81f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3642,6 +3642,103 @@ exports[`renders ./components/date-picker/demo/start-end.md correctly 1`] = `
</div>
`;
exports[`renders ./components/date-picker/demo/status.md correctly 1`] = `
<div
class="ant-space ant-space-vertical"
style="width:100%"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-picker ant-picker-status-error"
style="width:100%"
>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
placeholder="Select date"
readonly=""
size="12"
title=""
value=""
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-picker ant-picker-status-warning"
style="width:100%"
>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
placeholder="Select date"
readonly=""
size="12"
title=""
value=""
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="calendar"
class="anticon anticon-calendar"
role="img"
>
<svg
aria-hidden="true"
data-icon="calendar"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/date-picker/demo/suffix.md correctly 1`] = `
<div
class="ant-space ant-space-vertical"

@ -0,0 +1,28 @@
---
order: 19
version: 4.19.0
title:
zh-CN: 自定义状态
en-US: Status
---
## zh-CN
使用 `status` 为 DatePicker 添加状态,可选 `error` 或者 `warning`
## en-US
Add status to DatePicker with `status`, which could be `error` or `warning`.
```tsx
import { DatePicker, Space } from 'antd';
const Status: React.FC = () => (
<Space direction="vertical" style={{ width: '100%' }}>
<DatePicker status="error" style={{ width: '100%' }} />
<DatePicker status="warning" style={{ width: '100%' }} />
</Space>
);
ReactDOM.render(<Status />, mountNode);
```

@ -21,9 +21,18 @@ import {
Components,
} from '.';
import { PickerComponentClass } from './interface';
import { FormItemStatusContext } from '../../form/context';
import {
getFeedbackIcon,
getMergedStatus,
getStatusClassNames,
InputStatus,
} from '../../_util/statusUtils';
export default function generatePicker<DateType>(generateConfig: GenerateConfig<DateType>) {
type DatePickerProps = PickerProps<DateType>;
type DatePickerProps = PickerProps<DateType> & {
status?: InputStatus;
};
function getPicker<InnerPickerProps extends DatePickerProps>(
picker?: PickerMode,
@ -59,6 +68,23 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
}
};
renderFeedback = (prefixCls: string) => (
<FormItemStatusContext.Consumer>
{({ hasFeedback, status: contextStatus }) => {
const { status: customStatus } = this.props;
const status = getMergedStatus(contextStatus, customStatus);
return hasFeedback && getFeedbackIcon(prefixCls, status);
}}
</FormItemStatusContext.Consumer>
);
renderSuffix = (prefixCls: string, mergedPicker?: PickerMode) => (
<>
{mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />}
{this.renderFeedback(prefixCls)}
</>
);
renderPicker = (contextLocale: PickerLocale) => {
const locale = { ...contextLocale, ...this.props.locale };
const { getPrefixCls, direction, getPopupContainer } = this.context;
@ -70,6 +96,7 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
bordered = true,
placement,
placeholder,
status: customStatus,
...restProps
} = this.props;
const { format, showTime } = this.props as any;
@ -100,37 +127,44 @@ export default function generatePicker<DateType>(generateConfig: GenerateConfig<
const mergedSize = customizeSize || size;
return (
<RCPicker<DateType>
ref={this.pickerRef}
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
suffixIcon={
mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />
}
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
clearIcon={<CloseCircleFilled />}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
allowClear
transitionName={`${rootPrefixCls}-slide-up`}
{...additionalProps}
{...restProps}
{...additionalOverrideProps}
locale={locale!.lang}
className={classNames(
{
[`${prefixCls}-${mergedSize}`]: mergedSize,
[`${prefixCls}-borderless`]: !bordered,
},
className,
<FormItemStatusContext.Consumer>
{({ hasFeedback, status: contextStatus }) => (
<RCPicker<DateType>
ref={this.pickerRef}
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
suffixIcon={this.renderSuffix(prefixCls, mergedPicker)}
dropdownAlign={transPlacement2DropdownAlign(direction, placement)}
clearIcon={<CloseCircleFilled />}
prevIcon={<span className={`${prefixCls}-prev-icon`} />}
nextIcon={<span className={`${prefixCls}-next-icon`} />}
superPrevIcon={<span className={`${prefixCls}-super-prev-icon`} />}
superNextIcon={<span className={`${prefixCls}-super-next-icon`} />}
allowClear
transitionName={`${rootPrefixCls}-slide-up`}
{...additionalProps}
{...restProps}
{...additionalOverrideProps}
locale={locale!.lang}
className={classNames(
{
[`${prefixCls}-${mergedSize}`]: mergedSize,
[`${prefixCls}-borderless`]: !bordered,
},
getStatusClassNames(
prefixCls,
getMergedStatus(contextStatus, customStatus),
hasFeedback,
),
className,
)}
prefixCls={prefixCls}
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
components={Components}
direction={direction}
/>
)}
prefixCls={prefixCls}
getPopupContainer={customizeGetPopupContainer || getPopupContainer}
generateConfig={generateConfig}
components={Components}
direction={direction}
/>
</FormItemStatusContext.Consumer>
);
}}
</SizeContext.Consumer>

@ -73,6 +73,7 @@ The following APIs are shared by DatePicker, RangePicker.
| popupStyle | To customize the style of the popup calendar | CSSProperties | {} | |
| prevIcon | The custom prev icon | ReactNode | - | 4.17.0 |
| size | To determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | `large` \| `middle` \| `small` | - | |
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| style | To customize the style of the input box | CSSProperties | {} | |
| suffixIcon | The custom suffix icon | ReactNode | - | |
| superNextIcon | The custom super next icon | ReactNode | - | 4.17.0 |

@ -74,6 +74,7 @@ import locale from 'antd/lib/locale/zh_CN';
| popupStyle | 额外的弹出日历样式 | CSSProperties | {} | |
| prevIcon | 自定义上一个图标 | ReactNode | - | 4.17.0 |
| size | 输入框大小,`large` 高度为 40px`small` 为 24px默认是 32px | `large` \| `middle` \| `small` | - | |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| style | 自定义输入框样式 | CSSProperties | {} | |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
| superNextIcon | 自定义 `<<` 切换图标 | ReactNode | - | 4.17.0 |

@ -1,6 +1,7 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import '../../input/style/mixin';
@import './status';
@picker-prefix-cls: ~'@{ant-prefix}-picker';
@ -106,6 +107,8 @@
}
&-suffix {
display: flex;
flex: none;
align-self: center;
margin-left: (@padding-xs / 2);
color: @disabled-color;
@ -114,6 +117,10 @@
> * {
vertical-align: top;
&:not(:last-child) {
margin-right: 8px;
}
}
}

@ -3,3 +3,5 @@ import './index.less';
// style dependencies
import '../../tag/style';
import '../../button/style';
// deps-lint-skip: form

@ -0,0 +1,52 @@
@import '../../input/style/mixin';
@picker-prefix-cls: ~'@{ant-prefix}-picker';
.status-color(
@text-color: @input-color;
@border-color: @input-border-color;
@background-color: @input-bg;
@hoverBorderColor: @primary-color-hover;
@outlineColor: @primary-color-outline;
) {
&.@{picker-prefix-cls} {
&,
&:not([disabled]):hover {
background-color: @background-color;
border-color: @border-color;
}
&-focused,
&:focus {
.active(@text-color, @hoverBorderColor, @outlineColor);
}
}
.@{picker-prefix-cls}-feedback-icon {
color: @text-color;
}
}
.@{picker-prefix-cls} {
&-status-error {
.status-color(@error-color, @error-color, @input-bg, @error-color-hover, @error-color-outline);
}
&-status-warning {
.status-color(@warning-color, @warning-color, @input-bg, @warning-color-hover, @warning-color-outline);
}
&-status-validating {
.@{picker-prefix-cls}-feedback-icon {
display: inline-block;
color: @primary-color;
}
}
&-status-success {
.@{picker-prefix-cls}-feedback-icon {
color: @success-color;
animation-name: diffZoomIn1 !important;
}
}
}

@ -16383,7 +16383,7 @@ exports[`renders ./components/form/demo/validate-static.md extend context correc
class="ant-form-item-control-input-content"
>
<div
class="ant-picker"
class="ant-picker ant-picker-status-success ant-picker-has-feedback"
style="width:100%"
>
<div
@ -16419,6 +16419,29 @@ exports[`renders ./components/form/demo/validate-static.md extend context correc
/>
</svg>
</span>
<span
class="ant-picker-feedback-icon"
>
<span
aria-label="check-circle"
class="anticon anticon-check-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="check-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"
/>
</svg>
</span>
</span>
</span>
</div>
</div>
@ -17003,7 +17026,7 @@ exports[`renders ./components/form/demo/validate-static.md extend context correc
class="ant-form-item-control-input-content"
>
<div
class="ant-picker"
class="ant-picker ant-picker-status-warning ant-picker-has-feedback"
style="width:100%"
>
<div
@ -17042,6 +17065,29 @@ exports[`renders ./components/form/demo/validate-static.md extend context correc
/>
</svg>
</span>
<span
class="ant-picker-feedback-icon"
>
<span
aria-label="exclamation-circle"
class="anticon anticon-exclamation-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="exclamation-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
/>
</svg>
</span>
</span>
</span>
</div>
</div>
@ -18928,7 +18974,7 @@ exports[`renders ./components/form/demo/validate-static.md extend context correc
class="ant-form-item-control-input-content"
>
<div
class="ant-picker"
class="ant-picker ant-picker-status-error"
>
<div
class="ant-picker-input"

@ -7608,7 +7608,7 @@ exports[`renders ./components/form/demo/validate-static.md correctly 1`] = `
class="ant-form-item-control-input-content"
>
<div
class="ant-picker"
class="ant-picker ant-picker-status-success ant-picker-has-feedback"
style="width:100%"
>
<div
@ -7644,6 +7644,29 @@ exports[`renders ./components/form/demo/validate-static.md correctly 1`] = `
/>
</svg>
</span>
<span
class="ant-picker-feedback-icon"
>
<span
aria-label="check-circle"
class="anticon anticon-check-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="check-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 01-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"
/>
</svg>
</span>
</span>
</span>
</div>
</div>
@ -7674,7 +7697,7 @@ exports[`renders ./components/form/demo/validate-static.md correctly 1`] = `
class="ant-form-item-control-input-content"
>
<div
class="ant-picker"
class="ant-picker ant-picker-status-warning ant-picker-has-feedback"
style="width:100%"
>
<div
@ -7713,6 +7736,29 @@ exports[`renders ./components/form/demo/validate-static.md correctly 1`] = `
/>
</svg>
</span>
<span
class="ant-picker-feedback-icon"
>
<span
aria-label="exclamation-circle"
class="anticon anticon-exclamation-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="exclamation-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"
/>
</svg>
</span>
</span>
</span>
</div>
</div>
@ -8027,7 +8073,7 @@ exports[`renders ./components/form/demo/validate-static.md correctly 1`] = `
class="ant-form-item-control-input-content"
>
<div
class="ant-picker"
class="ant-picker ant-picker-status-error"
>
<div
class="ant-picker-input"

@ -1,5 +1,6 @@
---
order: 19
version: 4.19.0
title:
zh-CN: 自定义状态
en-US: Status
@ -7,11 +8,11 @@ title:
## zh-CN
使用 `status` 为 InputNumber 添加状态可选 `error` 或者 `warning`
使用 `status` 为 InputNumber 添加状态可选 `error` 或者 `warning`
## en-US
Add status to InputNumber with `status`, which could be `error` or `warning`
Add status to InputNumber with `status`, which could be `error` or `warning`.
```tsx
import { InputNumber, Space } from 'antd';

@ -1,5 +1,6 @@
---
order: 19
version: 4.19.0
title:
zh-CN: 自定义状态
en-US: Status
@ -7,11 +8,11 @@ title:
## zh-CN
使用 `status` 为 Input 添加状态可选 `error` 或者 `warning`
使用 `status` 为 Input 添加状态可选 `error` 或者 `warning`
## en-US
Add status to Input with `status`, which could be `error` or `warning`
Add status to Input with `status`, which could be `error` or `warning`.
```tsx
import { Input, Space } from 'antd';

@ -848,6 +848,106 @@ Array [
]
`;
exports[`renders ./components/time-picker/demo/status.md correctly 1`] = `
<div
class="ant-space ant-space-vertical"
>
<div
class="ant-space-item"
style="margin-bottom:8px"
>
<div
class="ant-picker ant-picker-status-error"
>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
placeholder="Select time"
readonly=""
size="10"
title=""
value=""
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="clock-circle"
class="anticon anticon-clock-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="clock-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
<div
class="ant-space-item"
>
<div
class="ant-picker ant-picker-status-warning"
>
<div
class="ant-picker-input"
>
<input
autocomplete="off"
placeholder="Select time"
readonly=""
size="10"
title=""
value=""
/>
<span
class="ant-picker-suffix"
>
<span
aria-label="clock-circle"
class="anticon anticon-clock-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="clock-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"
/>
<path
d="M686.7 638.6L544.1 535.5V288c0-4.4-3.6-8-8-8H488c-4.4 0-8 3.6-8 8v275.4c0 2.6 1.2 5 3.3 6.5l165.4 120.6c3.6 2.6 8.6 1.8 11.2-1.7l28.6-39c2.6-3.7 1.8-8.7-1.8-11.2z"
/>
</svg>
</span>
</span>
</div>
</div>
</div>
</div>
`;
exports[`renders ./components/time-picker/demo/suffix.md correctly 1`] = `
<div
class="ant-picker"

@ -0,0 +1,28 @@
---
order: 19
version: 4.19.0
title:
zh-CN: 自定义状态
en-US: Status
---
## zh-CN
使用 `status` 为 TimePicker 添加状态,可选 `error` 或者 `warning`
## en-US
Add status to TimePicker with `status`, which could be `error` or `warning`.
```tsx
import { TimePicker, Space } from 'antd';
const Status: React.FC = () => (
<Space direction="vertical">
<TimePicker status="error" />
<TimePicker status="warning" />
</Space>
);
ReactDOM.render(<Status />, mountNode);
```

@ -48,6 +48,7 @@ import moment from 'moment';
| renderExtraFooter | Called from time picker panel to render some addon to its bottom | () => ReactNode | - | |
| secondStep | Interval between seconds in picker | number | 1 | |
| showNow | Whether to show `Now` button on panel | boolean | - | 4.4.0 |
| status | Set validation status | 'error' \| 'warning' \| 'success' \| 'validating' | - | 4.19.0 |
| 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/) | - | |

@ -3,6 +3,7 @@ import * as React from 'react';
import DatePicker from '../date-picker';
import { PickerTimeProps, RangePickerTimeProps } from '../date-picker/generatePicker';
import devWarning from '../_util/devWarning';
import { InputStatus } from '../_util/statusUtils';
const { TimePicker: InternalTimePicker, RangePicker: InternalRangePicker } = DatePicker;
@ -28,6 +29,7 @@ const RangePicker = React.forwardRef<any, TimeRangePickerProps>((props, ref) =>
export interface TimePickerProps extends Omit<PickerTimeProps<Moment>, 'picker'> {
addon?: () => React.ReactNode;
popupClassName?: string;
status?: InputStatus;
}
const TimePicker = React.forwardRef<any, TimePickerProps>(

@ -48,6 +48,7 @@ import moment from 'moment';
| renderExtraFooter | 选择框底部显示自定义的内容 | () => ReactNode | - | |
| secondStep | 秒选项间隔 | number | 1 | |
| showNow | 面板是否显示“此刻”按钮 | boolean | - | 4.4.0 |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
| use12Hours | 使用 12 小时制,为 true 时 `format` 默认为 `h:mm:ss a` | boolean | false | |
| value | 当前时间 | [moment](http://momentjs.com/) | - | |

Loading…
Cancel
Save