Skip to content

Commit ed528e6

Browse files
committed
feat: datepicker timepicker calendar support string value #718
1 parent a28962b commit ed528e6

File tree

13 files changed

+184
-118
lines changed

13 files changed

+184
-118
lines changed

components/_util/moment-util.js

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import interopDefault from './interopDefault';
2+
import * as moment from 'moment';
3+
import warning from './warning';
4+
import isNil from 'lodash/isNil';
5+
6+
export const TimeType = {
7+
validator(value) {
8+
return typeof value === 'string' || isNil(value) || moment.isMoment(value);
9+
},
10+
};
11+
12+
export const TimesType = {
13+
validator(value) {
14+
if (Array.isArray(value)) {
15+
return (
16+
value.length === 0 ||
17+
value.findIndex(val => typeof val !== 'string') === -1 ||
18+
value.findIndex(val => !isNil(val) && !moment.isMoment(val)) === -1
19+
);
20+
}
21+
return false;
22+
},
23+
};
24+
25+
export const TimeOrTimesType = {
26+
validator(value) {
27+
if (Array.isArray(value)) {
28+
return (
29+
value.length === 0 ||
30+
value.findIndex(val => typeof val !== 'string') === -1 ||
31+
value.findIndex(val => !isNil(val) && !moment.isMoment(val)) === -1
32+
);
33+
} else {
34+
return typeof value === 'string' || isNil(value) || moment.isMoment(value);
35+
}
36+
},
37+
};
38+
39+
export function checkValidate(componentName, value, propName, valueFormat) {
40+
const values = Array.isArray(value) ? value : [value];
41+
values.forEach(val => {
42+
if (!val) return;
43+
valueFormat &&
44+
warning(
45+
interopDefault(moment)(val, valueFormat).isValid(),
46+
componentName,
47+
`When set \`valueFormat\`, \`${propName}\` should provides invalidate string time. `,
48+
);
49+
!valueFormat &&
50+
warning(
51+
interopDefault(moment).isMoment(val) && val.isValid(),
52+
componentName,
53+
`\`${propName}\` provides invalidate moment time. If you want to set empty value, use \`null\` instead.`,
54+
);
55+
});
56+
}
57+
export const stringToMoment = (value, valueFormat) => {
58+
if (Array.isArray(value)) {
59+
return value.map(val =>
60+
typeof val === 'string' && val ? interopDefault(moment)(val, valueFormat) : val || null,
61+
);
62+
} else {
63+
return typeof value === 'string' && value
64+
? interopDefault(moment)(value, valueFormat)
65+
: value || null;
66+
}
67+
};
68+
69+
export const momentToString = (value, valueFormat) => {
70+
if (Array.isArray(value)) {
71+
return value.map(val => (interopDefault(moment).isMoment(val) ? val.format(valueFormat) : val));
72+
} else {
73+
return interopDefault(moment).isMoment(value) ? value.format(valueFormat) : value;
74+
}
75+
};

components/calendar/index.jsx

+17-19
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import interopDefault from '../_util/interopDefault';
99
import { ConfigConsumerProps } from '../config-provider';
1010
import enUS from './locale/en_US';
1111
import Base from '../base';
12+
import { checkValidate, stringToMoment, momentToString, TimeType } from '../_util/moment-util';
1213

1314
function noop() {
1415
return null;
@@ -20,21 +21,15 @@ function zerofixed(v) {
2021
}
2122
return `${v}`;
2223
}
23-
export const MomentType = {
24-
type: Object,
25-
validator(value) {
26-
return moment.isMoment(value);
27-
},
28-
};
2924
function isMomentArray(value) {
3025
return Array.isArray(value) && !!value.find(val => moment.isMoment(val));
3126
}
3227
export const CalendarMode = PropTypes.oneOf(['month', 'year']);
3328

3429
export const CalendarProps = () => ({
3530
prefixCls: PropTypes.string,
36-
value: MomentType,
37-
defaultValue: MomentType,
31+
value: TimeType,
32+
defaultValue: TimeType,
3833
mode: CalendarMode,
3934
fullscreen: PropTypes.bool,
4035
// dateCellRender: PropTypes.func,
@@ -47,6 +42,7 @@ export const CalendarProps = () => ({
4742
disabledDate: PropTypes.func,
4843
validRange: PropTypes.custom(isMomentArray),
4944
headerRender: PropTypes.func,
45+
valueFormat: PropTypes.string,
5046
});
5147

5248
const Calendar = {
@@ -64,20 +60,21 @@ const Calendar = {
6460
configProvider: { default: () => ConfigConsumerProps },
6561
},
6662
data() {
67-
const value = this.value || this.defaultValue || interopDefault(moment)();
68-
if (!interopDefault(moment).isMoment(value)) {
69-
throw new Error('The value/defaultValue of Calendar must be a moment object, ');
70-
}
63+
const { value, defaultValue, valueFormat } = this;
64+
const sValue = value || defaultValue || interopDefault(moment)();
65+
checkValidate('Calendar', defaultValue, 'defaultValue', valueFormat);
66+
checkValidate('Calendar', value, 'value', valueFormat);
7167
this._sPrefixCls = undefined;
7268
return {
73-
sValue: value,
69+
sValue: stringToMoment(sValue, valueFormat),
7470
sMode: this.mode || 'month',
7571
};
7672
},
7773
watch: {
7874
value(val) {
75+
checkValidate('Calendar', val, 'value', this.valueFormat);
7976
this.setState({
80-
sValue: val,
77+
sValue: stringToMoment(val, this.valueFormat),
8178
});
8279
},
8380
mode(val) {
@@ -95,26 +92,27 @@ const Calendar = {
9592
this.onPanelChange(this.sValue, mode);
9693
},
9794
onPanelChange(value, mode) {
98-
this.$emit('panelChange', value, mode);
95+
const val = this.valueFormat ? momentToString(value, this.valueFormat) : value;
96+
this.$emit('panelChange', val, mode);
9997
if (value !== this.sValue) {
100-
this.$emit('change', value);
98+
this.$emit('change', val);
10199
}
102100
},
103101

104102
onSelect(value) {
105103
this.setValue(value, 'select');
106104
},
107105
setValue(value, way) {
108-
const prevValue = this.value || this.sValue;
109-
const { sMode: mode } = this;
106+
const prevValue = this.value ? stringToMoment(this.value, this.valueFormat) : this.sValue;
107+
const { sMode: mode, valueFormat } = this;
110108
if (!hasProp(this, 'value')) {
111109
this.setState({ sValue: value });
112110
}
113111
if (way === 'select') {
114112
if (prevValue && prevValue.month() !== value.month()) {
115113
this.onPanelChange(value, mode);
116114
}
117-
this.$emit('select', value);
115+
this.$emit('select', valueFormat ? momentToString(value, valueFormat) : value);
118116
} else if (way === 'changePanel') {
119117
this.onPanelChange(value, mode);
120118
}

components/date-picker/interface.js

+12-28
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
1-
import * as moment from 'moment';
21
// import { TimePickerProps } from '../time-picker'
32
import PropTypes from '../_util/vue-types';
4-
5-
export const MomentType = {
6-
type: Object,
7-
validator(value) {
8-
return value === undefined || moment.isMoment(value);
9-
},
10-
};
3+
import { TimesType, TimeType } from '../_util/moment-util';
114

125
export const PickerProps = () => ({
136
name: PropTypes.string,
@@ -37,12 +30,13 @@ export const PickerProps = () => ({
3730
tabIndex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
3831
align: PropTypes.object.def(() => ({})),
3932
inputReadOnly: PropTypes.bool,
33+
valueFormat: PropTypes.string,
4034
});
4135

4236
export const SinglePickerProps = () => ({
43-
value: MomentType,
44-
defaultValue: MomentType,
45-
defaultPickerValue: MomentType,
37+
value: TimeType,
38+
defaultValue: TimeType,
39+
defaultPickerValue: TimeType,
4640
renderExtraFooter: PropTypes.any,
4741
placeholder: PropTypes.string,
4842
// onChange?: (date: moment.Moment, dateString: string) => void;
@@ -65,27 +59,17 @@ export const MonthPickerProps = () => ({
6559
placeholder: PropTypes.string,
6660
monthCellContentRender: PropTypes.func,
6761
});
68-
function isMomentArray(value) {
69-
if (Array.isArray(value)) {
70-
return (
71-
value.length === 0 || value.findIndex(val => val === undefined || moment.isMoment(val)) !== -1
72-
);
73-
}
74-
return false;
75-
}
76-
77-
export const RangePickerValue = PropTypes.custom(isMomentArray);
78-
// export const RangePickerPresetRange = PropTypes.oneOfType([RangePickerValue, PropTypes.func])
62+
// export const RangePickerPresetRange = PropTypes.oneOfType([TimesType, PropTypes.func])
7963

8064
export const RangePickerProps = () => ({
8165
...PickerProps(),
8266
tagPrefixCls: PropTypes.string,
83-
value: RangePickerValue,
84-
defaultValue: RangePickerValue,
85-
defaultPickerValue: RangePickerValue,
67+
value: TimesType,
68+
defaultValue: TimesType,
69+
defaultPickerValue: TimesType,
8670
timePicker: PropTypes.any,
87-
// onChange?: (dates: RangePickerValue, dateStrings: [string, string]) => void;
88-
// onCalendarChange?: (dates: RangePickerValue, dateStrings: [string, string]) => void;
71+
// onChange?: (dates: TimesType, dateStrings: [string, string]) => void;
72+
// onCalendarChange?: (dates: TimesType, dateStrings: [string, string]) => void;
8973
// onOk?: (selectedTime: moment.Moment) => void;
9074
showTime: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
9175
ranges: PropTypes.object,
@@ -95,7 +79,7 @@ export const RangePickerProps = () => ({
9579
disabledTime: PropTypes.func,
9680
showToday: PropTypes.bool,
9781
renderExtraFooter: PropTypes.any,
98-
// onPanelChange?: (value?: RangePickerValue, mode?: string | string[]) => void;
82+
// onPanelChange?: (value?: TimesType, mode?: string | string[]) => void;
9983
});
10084

10185
export const WeekPickerProps = () => ({

components/date-picker/wrapPicker.js

+26-21
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,11 @@
11
import TimePickerPanel from '../vc-time-picker/Panel';
22
import classNames from 'classnames';
3-
import * as moment from 'moment';
43
import LocaleReceiver from '../locale-provider/LocaleReceiver';
54
import { generateShowHourMinuteSecond } from '../time-picker';
65
import enUS from './locale/en_US';
7-
import interopDefault from '../_util/interopDefault';
86
import { getOptionProps, initDefaultProps, getListeners } from '../_util/props-util';
97
import { ConfigConsumerProps } from '../config-provider';
10-
import warning from '../_util/warning';
11-
12-
function checkValidate(value, propName) {
13-
const values = Array.isArray(value) ? value : [value];
14-
values.forEach(val => {
15-
if (!val) return;
16-
17-
warning(
18-
!interopDefault(moment).isMoment(val) || val.isValid(),
19-
'DatePicker',
20-
`\`${propName}\` provides invalidate moment time. If you want to set empty value, use \`null\` instead.`,
21-
);
22-
});
23-
}
8+
import { checkValidate, stringToMoment, momentToString } from '../_util/moment-util';
249

2510
const DEFAULT_FORMAT = {
2611
date: 'YYYY-MM-DD',
@@ -74,9 +59,9 @@ export default function wrapPicker(Picker, props, pickerType) {
7459
};
7560
},
7661
mounted() {
77-
const { autoFocus, disabled, value, defaultValue } = this;
78-
checkValidate(defaultValue, 'defaultValue');
79-
checkValidate(value, 'value');
62+
const { autoFocus, disabled, value, defaultValue, valueFormat } = this;
63+
checkValidate('DatePicker', defaultValue, 'defaultValue', valueFormat);
64+
checkValidate('DatePicker', value, 'value', valueFormat);
8065
if (autoFocus && !disabled) {
8166
this.$nextTick(() => {
8267
this.focus();
@@ -85,7 +70,7 @@ export default function wrapPicker(Picker, props, pickerType) {
8570
},
8671
watch: {
8772
value(val) {
88-
checkValidate(val, 'value');
73+
checkValidate('DatePicker', val, 'value', this.valueFormat);
8974
},
9075
},
9176
methods: {
@@ -123,7 +108,13 @@ export default function wrapPicker(Picker, props, pickerType) {
123108
handleMouseLeave(e) {
124109
this.$emit('mouseleave', e);
125110
},
126-
111+
handleChange(date, dateString) {
112+
this.$emit(
113+
'change',
114+
this.valueFormat ? momentToString(date, this.valueFormat) : date,
115+
dateString,
116+
);
117+
},
127118
focus() {
128119
this.$refs.picker.focus();
129120
},
@@ -132,8 +123,21 @@ export default function wrapPicker(Picker, props, pickerType) {
132123
this.$refs.picker.blur();
133124
},
134125

126+
transformValue(props) {
127+
if ('value' in props) {
128+
props.value = stringToMoment(props.value, this.valueFormat);
129+
}
130+
if ('defaultValue' in props) {
131+
props.defaultValue = stringToMoment(props.defaultValue, this.valueFormat);
132+
}
133+
if ('defaultPickerValue' in props) {
134+
props.defaultPickerValue = stringToMoment(props.defaultPickerValue, this.valueFormat);
135+
}
136+
},
137+
135138
renderPicker(locale, localeCode) {
136139
const props = getOptionProps(this);
140+
this.transformValue(props);
137141
const {
138142
prefixCls: customizePrefixCls,
139143
inputPrefixCls: customizeInputPrefixCls,
@@ -203,6 +207,7 @@ export default function wrapPicker(Picker, props, pickerType) {
203207
blur: this.handleBlur,
204208
mouseenter: this.handleMouseEnter,
205209
mouseleave: this.handleMouseLeave,
210+
change: this.handleChange,
206211
},
207212
ref: 'picker',
208213
scopedSlots: this.$scopedSlots || {},

0 commit comments

Comments
 (0)