ant-design-vue/components/vc-calendar/src/Picker.jsx

246 lines
6.4 KiB
Vue

import PropTypes from '../../_util/vue-types';
import BaseMixin from '../../_util/BaseMixin';
import { getOptionProps, hasProp, getEvents, findDOMNode } from '../../_util/props-util';
import { cloneElement } from '../../_util/vnode';
import createChainedFunction from '../../_util/createChainedFunction';
import KeyCode from '../../_util/KeyCode';
import placements from './picker/placements';
import Trigger from '../../vc-trigger';
import moment from 'moment';
import isNil from 'lodash-es/isNil';
import { defineComponent } from 'vue';
const TimeType = {
validator(value) {
if (Array.isArray(value)) {
return (
value.length === 0 || value.findIndex(val => !isNil(val) && !moment.isMoment(val)) === -1
);
} else {
return isNil(value) || moment.isMoment(value);
}
},
};
function refFn(field, component) {
this[field] = component;
}
const Picker = defineComponent({
name: 'Picker',
mixins: [BaseMixin],
inheritAttrs: false,
props: {
animation: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
disabled: PropTypes.looseBool,
transitionName: PropTypes.string,
format: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.func]),
// onChange: PropTypes.func,
// onOpenChange: PropTypes.func,
getCalendarContainer: PropTypes.func,
calendar: PropTypes.any,
open: PropTypes.looseBool,
defaultOpen: PropTypes.looseBool.def(false),
prefixCls: PropTypes.string.def('rc-calendar-picker'),
placement: PropTypes.any.def('bottomLeft'),
value: TimeType,
defaultValue: TimeType,
align: PropTypes.object.def(() => ({})),
dropdownClassName: PropTypes.string,
dateRender: PropTypes.func,
children: PropTypes.func,
},
data() {
const props = this.$props;
this.calendarElement = null;
this.saveCalendarRef = refFn.bind(this, 'calendarInstance');
let open;
if (hasProp(this, 'open')) {
open = props.open;
} else {
open = props.defaultOpen;
}
const value = props.value || props.defaultValue;
return {
sOpen: open,
sValue: value,
};
},
watch: {
value(val) {
this.setState({
sValue: val,
});
},
open(val) {
this.setState({
sOpen: val,
});
},
},
mounted() {
this.preSOpen = this.sOpen;
},
updated() {
if (!this.preSOpen && this.sOpen) {
// setTimeout is for making sure saveCalendarRef happen before focusCalendar
this.focusTimeout = setTimeout(this.focusCalendar, 0);
}
this.preSOpen = this.sOpen;
},
beforeUnmount() {
clearTimeout(this.focusTimeout);
},
methods: {
onCalendarKeyDown(event) {
if (event.keyCode === KeyCode.ESC) {
event.stopPropagation();
this.closeCalendar(this.focus);
}
},
onCalendarSelect(value, cause = {}) {
const props = this.$props;
if (!hasProp(this, 'value')) {
this.setState({
sValue: value,
});
}
const calendarProps = getOptionProps(props.calendar);
if (
cause.source === 'keyboard' ||
cause.source === 'dateInputSelect' ||
(!calendarProps.timePicker && cause.source !== 'dateInput') ||
cause.source === 'todayButton'
) {
this.closeCalendar(this.focus);
}
this.__emit('change', value);
},
onKeyDown(event) {
if (!this.sOpen && (event.keyCode === KeyCode.DOWN || event.keyCode === KeyCode.ENTER)) {
this.openCalendar();
event.preventDefault();
}
},
onCalendarOk() {
this.closeCalendar(this.focus);
},
onCalendarClear() {
this.closeCalendar(this.focus);
},
onCalendarBlur() {
this.setOpen(false);
},
onVisibleChange(open) {
this.setOpen(open);
},
getCalendarElement() {
const props = this.$props;
const calendarProps = getOptionProps(props.calendar);
const calendarEvents = getEvents(props.calendar);
const { sValue: value } = this;
const defaultValue = value;
const extraProps = {
ref: this.saveCalendarRef,
defaultValue: defaultValue || calendarProps.defaultValue,
selectedValue: value,
onKeydown: this.onCalendarKeyDown,
onOk: createChainedFunction(calendarEvents.onOk, this.onCalendarOk),
onSelect: createChainedFunction(calendarEvents.onSelect, this.onCalendarSelect),
onClear: createChainedFunction(calendarEvents.onClear, this.onCalendarClear),
onBlur: createChainedFunction(calendarEvents.onBlur, this.onCalendarBlur),
};
return cloneElement(props.calendar, extraProps);
},
setOpen(open, callback) {
if (this.sOpen !== open) {
if (!hasProp(this, 'open')) {
this.setState(
{
sOpen: open,
},
callback,
);
}
this.__emit('openChange', open);
}
},
openCalendar(callback) {
this.setOpen(true, callback);
},
closeCalendar(callback) {
this.setOpen(false, callback);
},
focus() {
if (!this.sOpen) {
findDOMNode(this).focus();
}
},
focusCalendar() {
if (this.sOpen && !!this.calendarInstance) {
this.calendarInstance.focus();
}
},
},
render() {
const props = getOptionProps(this);
const {
prefixCls,
placement,
getCalendarContainer,
align,
animation,
disabled,
dropdownClassName,
transitionName,
} = props;
const { sValue, sOpen } = this;
const childrenState = {
value: sValue,
open: sOpen,
};
const children = this.$slots.default(childrenState);
if (this.sOpen || !this.calendarElement) {
this.calendarElement = this.getCalendarElement();
}
return (
<Trigger
popupAlign={align}
builtinPlacements={placements}
popupPlacement={placement}
action={disabled && !sOpen ? [] : ['click']}
destroyPopupOnHide
getPopupContainer={getCalendarContainer}
popupStyle={this.$attrs.style || {}}
popupAnimation={animation}
popupTransitionName={transitionName}
popupVisible={sOpen}
onPopupVisibleChange={this.onVisibleChange}
prefixCls={prefixCls}
popupClassName={dropdownClassName}
popup={this.calendarElement}
>
{cloneElement(children, { onKeydown: this.onKeyDown })}
</Trigger>
);
},
});
export default Picker;