2017-11-17 14:38:54 +08:00
|
|
|
import * as React from 'react';
|
|
|
|
import * as moment from 'moment';
|
2019-01-25 10:31:09 +08:00
|
|
|
import omit from 'omit.js';
|
2018-08-08 14:47:13 +08:00
|
|
|
import { polyfill } from 'react-lifecycles-compat';
|
2016-03-21 21:16:38 +08:00
|
|
|
import RcTimePicker from 'rc-time-picker/lib/TimePicker';
|
2015-12-08 14:55:28 +08:00
|
|
|
import classNames from 'classnames';
|
2019-01-25 10:31:09 +08:00
|
|
|
import warning from '../_util/warning';
|
2017-10-20 14:54:38 +08:00
|
|
|
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
2018-12-05 19:12:18 +08:00
|
|
|
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
2019-04-04 14:01:28 +08:00
|
|
|
import enUS from './locale/en_US';
|
2018-03-13 22:53:02 +08:00
|
|
|
import interopDefault from '../_util/interopDefault';
|
2018-08-14 14:45:04 +08:00
|
|
|
import Icon from '../icon';
|
2016-07-09 10:52:58 +08:00
|
|
|
|
2017-06-29 11:22:12 +08:00
|
|
|
export function generateShowHourMinuteSecond(format: string) {
|
|
|
|
// Ref: http://momentjs.com/docs/#/parsing/string-format/
|
|
|
|
return {
|
2018-12-07 20:02:01 +08:00
|
|
|
showHour: format.indexOf('H') > -1 || format.indexOf('h') > -1 || format.indexOf('k') > -1,
|
2017-06-29 11:22:12 +08:00
|
|
|
showMinute: format.indexOf('m') > -1,
|
|
|
|
showSecond: format.indexOf('s') > -1,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-07-09 10:52:58 +08:00
|
|
|
export interface TimePickerProps {
|
2016-09-09 13:55:21 +08:00
|
|
|
className?: string;
|
|
|
|
size?: 'large' | 'default' | 'small';
|
|
|
|
value?: moment.Moment;
|
2018-01-09 22:35:53 +08:00
|
|
|
defaultValue?: moment.Moment | moment.Moment[];
|
2017-04-28 09:40:20 +08:00
|
|
|
open?: boolean;
|
2016-07-13 11:14:24 +08:00
|
|
|
format?: string;
|
2016-09-09 13:55:21 +08:00
|
|
|
onChange?: (time: moment.Moment, timeString: string) => void;
|
2017-04-28 09:48:48 +08:00
|
|
|
onOpenChange?: (open: boolean) => void;
|
2018-12-25 16:00:00 +08:00
|
|
|
onAmPmChange?: (ampm: 'AM' | 'PM') => void;
|
2016-07-13 11:14:24 +08:00
|
|
|
disabled?: boolean;
|
|
|
|
placeholder?: string;
|
2017-07-03 16:57:11 +08:00
|
|
|
prefixCls?: string;
|
2016-07-13 11:14:24 +08:00
|
|
|
hideDisabledOptions?: boolean;
|
2017-03-17 15:23:25 +08:00
|
|
|
disabledHours?: () => number[];
|
|
|
|
disabledMinutes?: (selectedHour: number) => number[];
|
|
|
|
disabledSeconds?: (selectedHour: number, selectedMinute: number) => number[];
|
2016-07-13 11:14:24 +08:00
|
|
|
style?: React.CSSProperties;
|
2019-04-28 11:47:22 +08:00
|
|
|
getPopupContainer?: (triggerNode: HTMLElement) => HTMLElement;
|
2016-11-06 14:53:29 +08:00
|
|
|
addon?: Function;
|
2017-03-22 17:53:37 +08:00
|
|
|
use12Hours?: boolean;
|
2017-10-28 15:00:56 +08:00
|
|
|
focusOnOpen?: boolean;
|
|
|
|
hourStep?: number;
|
|
|
|
minuteStep?: number;
|
|
|
|
secondStep?: number;
|
2017-11-03 14:55:06 +08:00
|
|
|
allowEmpty?: boolean;
|
2019-01-25 10:31:09 +08:00
|
|
|
allowClear?: boolean;
|
2018-02-11 10:22:18 +08:00
|
|
|
inputReadOnly?: boolean;
|
2017-11-03 14:55:06 +08:00
|
|
|
clearText?: string;
|
|
|
|
defaultOpenValue?: moment.Moment;
|
|
|
|
popupClassName?: string;
|
2018-12-25 16:00:00 +08:00
|
|
|
popupStyle?: React.CSSProperties;
|
2018-09-16 19:45:27 +08:00
|
|
|
suffixIcon?: React.ReactNode;
|
2019-02-01 15:55:03 +08:00
|
|
|
clearIcon?: React.ReactNode;
|
2019-04-04 14:01:28 +08:00
|
|
|
locale?: TimePickerLocale;
|
2016-07-09 10:52:58 +08:00
|
|
|
}
|
2016-08-22 17:26:14 +08:00
|
|
|
|
2017-11-20 18:27:36 +08:00
|
|
|
export interface TimePickerLocale {
|
|
|
|
placeholder: string;
|
|
|
|
}
|
|
|
|
|
2018-08-08 14:47:13 +08:00
|
|
|
class TimePicker extends React.Component<TimePickerProps, any> {
|
2016-03-29 17:33:37 +08:00
|
|
|
static defaultProps = {
|
|
|
|
align: {
|
|
|
|
offset: [0, -2],
|
|
|
|
},
|
|
|
|
disabledHours: undefined,
|
|
|
|
disabledMinutes: undefined,
|
|
|
|
disabledSeconds: undefined,
|
|
|
|
hideDisabledOptions: false,
|
|
|
|
placement: 'bottomLeft',
|
|
|
|
transitionName: 'slide-up',
|
2017-10-28 15:00:56 +08:00
|
|
|
focusOnOpen: true,
|
2016-07-13 11:14:24 +08:00
|
|
|
};
|
2015-11-16 23:11:41 +08:00
|
|
|
|
2018-08-08 14:47:13 +08:00
|
|
|
static getDerivedStateFromProps(nextProps: TimePickerProps) {
|
|
|
|
if ('value' in nextProps) {
|
|
|
|
return { value: nextProps.value };
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-11-20 18:27:36 +08:00
|
|
|
private timePickerRef: typeof RcTimePicker;
|
2016-08-22 17:26:14 +08:00
|
|
|
|
2017-03-17 15:23:25 +08:00
|
|
|
constructor(props: TimePickerProps) {
|
2016-09-09 13:55:21 +08:00
|
|
|
super(props);
|
2016-12-02 15:07:33 +08:00
|
|
|
const value = props.value || props.defaultValue;
|
2018-03-13 22:53:02 +08:00
|
|
|
if (value && !interopDefault(moment).isMoment(value)) {
|
2016-12-02 15:07:33 +08:00
|
|
|
throw new Error(
|
|
|
|
'The value/defaultValue of TimePicker must be a moment object after `antd@2.0`, ' +
|
2018-12-07 20:02:01 +08:00
|
|
|
'see: https://u.ant.design/time-picker-value',
|
2016-12-02 15:07:33 +08:00
|
|
|
);
|
|
|
|
}
|
2016-09-09 13:55:21 +08:00
|
|
|
this.state = {
|
2016-12-02 15:07:33 +08:00
|
|
|
value,
|
2016-09-09 13:55:21 +08:00
|
|
|
};
|
2019-01-25 10:31:09 +08:00
|
|
|
|
|
|
|
warning(
|
|
|
|
!('allowEmpty' in props),
|
2019-02-27 15:32:29 +08:00
|
|
|
'TimePicker',
|
|
|
|
'`allowEmpty` is deprecated. Please use `allowClear` instead.',
|
2019-01-25 10:31:09 +08:00
|
|
|
);
|
2016-03-29 17:33:37 +08:00
|
|
|
}
|
2015-11-16 23:11:41 +08:00
|
|
|
|
2019-08-05 18:38:10 +08:00
|
|
|
getDefaultFormat() {
|
|
|
|
const { format, use12Hours } = this.props;
|
|
|
|
if (format) {
|
|
|
|
return format;
|
2016-09-09 13:55:21 +08:00
|
|
|
}
|
2019-08-05 18:38:10 +08:00
|
|
|
if (use12Hours) {
|
|
|
|
return 'h:mm:ss a';
|
|
|
|
}
|
|
|
|
return 'HH:mm:ss';
|
|
|
|
}
|
|
|
|
|
|
|
|
getAllowClear() {
|
|
|
|
const { allowClear, allowEmpty } = this.props;
|
|
|
|
if ('allowClear' in this.props) {
|
|
|
|
return allowClear;
|
2016-10-24 16:30:38 +08:00
|
|
|
}
|
2019-08-05 18:38:10 +08:00
|
|
|
return allowEmpty;
|
|
|
|
}
|
|
|
|
|
|
|
|
getDefaultLocale = () => {
|
|
|
|
const defaultLocale = {
|
|
|
|
...enUS,
|
|
|
|
...this.props.locale,
|
|
|
|
};
|
|
|
|
return defaultLocale;
|
2018-12-07 20:02:01 +08:00
|
|
|
};
|
2015-11-19 15:53:40 +08:00
|
|
|
|
2017-11-20 18:27:36 +08:00
|
|
|
handleOpenClose = ({ open }: { open: boolean }) => {
|
2017-04-28 09:40:20 +08:00
|
|
|
const { onOpenChange } = this.props;
|
|
|
|
if (onOpenChange) {
|
|
|
|
onOpenChange(open);
|
|
|
|
}
|
2018-12-07 20:02:01 +08:00
|
|
|
};
|
2017-04-28 09:40:20 +08:00
|
|
|
|
2017-11-20 18:27:36 +08:00
|
|
|
saveTimePicker = (timePickerRef: typeof RcTimePicker) => {
|
2017-02-21 15:10:28 +08:00
|
|
|
this.timePickerRef = timePickerRef;
|
2018-12-07 20:02:01 +08:00
|
|
|
};
|
2017-02-21 15:10:28 +08:00
|
|
|
|
2019-08-05 18:38:10 +08:00
|
|
|
handleChange = (value: moment.Moment) => {
|
|
|
|
if (!('value' in this.props)) {
|
|
|
|
this.setState({ value });
|
|
|
|
}
|
|
|
|
const { onChange, format = 'HH:mm:ss' } = this.props;
|
|
|
|
if (onChange) {
|
|
|
|
onChange(value, (value && value.format(format)) || '');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-02-21 15:10:28 +08:00
|
|
|
focus() {
|
|
|
|
this.timePickerRef.focus();
|
|
|
|
}
|
|
|
|
|
2017-11-19 01:41:40 +08:00
|
|
|
blur() {
|
|
|
|
this.timePickerRef.blur();
|
|
|
|
}
|
|
|
|
|
2019-01-25 10:31:09 +08:00
|
|
|
renderInputIcon(prefixCls: string) {
|
|
|
|
const { suffixIcon } = this.props;
|
|
|
|
const clockIcon = (suffixIcon &&
|
2019-03-07 11:21:30 +08:00
|
|
|
(React.isValidElement<{ className?: string }>(suffixIcon) &&
|
2019-01-25 10:31:09 +08:00
|
|
|
React.cloneElement(suffixIcon, {
|
|
|
|
className: classNames(suffixIcon.props.className, `${prefixCls}-clock-icon`),
|
2019-03-07 11:21:30 +08:00
|
|
|
}))) || <Icon type="clock-circle" className={`${prefixCls}-clock-icon`} />;
|
2019-01-25 10:31:09 +08:00
|
|
|
|
|
|
|
return <span className={`${prefixCls}-icon`}>{clockIcon}</span>;
|
|
|
|
}
|
|
|
|
|
|
|
|
renderClearIcon(prefixCls: string) {
|
2019-02-01 15:55:03 +08:00
|
|
|
const { clearIcon } = this.props;
|
2019-01-25 10:31:09 +08:00
|
|
|
|
2019-02-01 15:55:03 +08:00
|
|
|
const clearIconPrefixCls = `${prefixCls}-clear`;
|
2019-01-25 10:31:09 +08:00
|
|
|
|
2019-02-01 15:55:03 +08:00
|
|
|
if (clearIcon && React.isValidElement<{ className?: string }>(clearIcon)) {
|
|
|
|
return React.cloneElement(clearIcon, {
|
|
|
|
className: classNames(clearIcon.props.className, clearIconPrefixCls),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return <Icon type="close-circle" className={clearIconPrefixCls} theme="filled" />;
|
2019-01-25 10:31:09 +08:00
|
|
|
}
|
|
|
|
|
2018-12-05 19:12:18 +08:00
|
|
|
renderTimePicker = (locale: TimePickerLocale) => (
|
|
|
|
<ConfigConsumer>
|
|
|
|
{({ getPopupContainer: getContextPopupContainer, getPrefixCls }: ConfigConsumerProps) => {
|
2019-01-25 10:31:09 +08:00
|
|
|
const {
|
|
|
|
getPopupContainer,
|
|
|
|
prefixCls: customizePrefixCls,
|
|
|
|
className,
|
|
|
|
addon,
|
|
|
|
placeholder,
|
|
|
|
...props
|
|
|
|
} = this.props;
|
|
|
|
const { size } = props;
|
|
|
|
const pickerProps = omit(props, ['defaultValue', 'suffixIcon', 'allowEmpty', 'allowClear']);
|
2018-12-05 19:12:18 +08:00
|
|
|
|
|
|
|
const format = this.getDefaultFormat();
|
|
|
|
const prefixCls = getPrefixCls('time-picker', customizePrefixCls);
|
2019-01-25 10:31:09 +08:00
|
|
|
const pickerClassName = classNames(className, {
|
|
|
|
[`${prefixCls}-${size}`]: !!size,
|
2018-12-05 19:12:18 +08:00
|
|
|
});
|
|
|
|
|
2019-01-25 10:31:09 +08:00
|
|
|
const pickerAddon = (panel: React.ReactElement<any>) =>
|
|
|
|
addon ? <div className={`${prefixCls}-panel-addon`}>{addon(panel)}</div> : null;
|
2018-12-05 19:12:18 +08:00
|
|
|
|
|
|
|
return (
|
|
|
|
<RcTimePicker
|
|
|
|
{...generateShowHourMinuteSecond(format)}
|
2019-01-25 10:31:09 +08:00
|
|
|
{...pickerProps}
|
|
|
|
allowEmpty={this.getAllowClear()}
|
2018-12-05 19:12:18 +08:00
|
|
|
prefixCls={prefixCls}
|
|
|
|
getPopupContainer={getPopupContainer || getContextPopupContainer}
|
|
|
|
ref={this.saveTimePicker}
|
|
|
|
format={format}
|
2019-01-25 10:31:09 +08:00
|
|
|
className={pickerClassName}
|
2018-12-05 19:12:18 +08:00
|
|
|
value={this.state.value}
|
2019-01-25 10:31:09 +08:00
|
|
|
placeholder={placeholder === undefined ? locale.placeholder : placeholder}
|
2018-12-05 19:12:18 +08:00
|
|
|
onChange={this.handleChange}
|
|
|
|
onOpen={this.handleOpenClose}
|
|
|
|
onClose={this.handleOpenClose}
|
2019-01-25 10:31:09 +08:00
|
|
|
addon={pickerAddon}
|
|
|
|
inputIcon={this.renderInputIcon(prefixCls)}
|
|
|
|
clearIcon={this.renderClearIcon(prefixCls)}
|
2018-12-05 19:12:18 +08:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}}
|
|
|
|
</ConfigConsumer>
|
|
|
|
);
|
2017-03-17 15:23:25 +08:00
|
|
|
|
2017-10-20 14:54:38 +08:00
|
|
|
render() {
|
|
|
|
return (
|
2019-04-04 14:01:28 +08:00
|
|
|
<LocaleReceiver componentName="TimePicker" defaultLocale={this.getDefaultLocale()}>
|
2017-10-20 14:54:38 +08:00
|
|
|
{this.renderTimePicker}
|
|
|
|
</LocaleReceiver>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2018-08-08 14:47:13 +08:00
|
|
|
|
|
|
|
polyfill(TimePicker);
|
|
|
|
|
|
|
|
export default TimePicker;
|