|
@@ -110,13 +110,11 @@ export interface EventHandlerProps {
|
|
|
onPanelChange?: OnPanelChangeType;
|
|
|
onConfirm?: OnConfirmType;
|
|
|
// properties below need overwrite
|
|
|
- // onBlur?: React.MouseEventHandler<HTMLInputElement>;
|
|
|
onBlur?: (e: any) => void;
|
|
|
- // onClear?: React.MouseEventHandler<HTMLDivElement>;
|
|
|
onClear?: (e: any) => void;
|
|
|
- // onFocus?: React.MouseEventHandler<HTMLInputElement>;
|
|
|
onFocus?: (e: any, rangType: RangeType) => void;
|
|
|
- onPresetClick?: OnPresetClickType
|
|
|
+ onPresetClick?: OnPresetClickType;
|
|
|
+ onClickOutSide?: () => void
|
|
|
}
|
|
|
|
|
|
export interface DatePickerFoundationProps extends ElementProps, RenderProps, EventHandlerProps {
|
|
@@ -191,7 +189,7 @@ export interface DatePickerFoundationState {
|
|
|
export { Type, DateInputFoundationProps };
|
|
|
|
|
|
export interface DatePickerAdapter extends DefaultAdapter<DatePickerFoundationProps, DatePickerFoundationState> {
|
|
|
- togglePanel: (panelShow: boolean) => void;
|
|
|
+ togglePanel: (panelShow: boolean, cb?: () => void) => void;
|
|
|
registerClickOutSide: () => void;
|
|
|
unregisterClickOutSide: () => void;
|
|
|
notifyBlur: DatePickerFoundationProps['onBlur'];
|
|
@@ -213,7 +211,10 @@ export interface DatePickerAdapter extends DefaultAdapter<DatePickerFoundationPr
|
|
|
isEventTarget: (e: any) => boolean;
|
|
|
updateInsetInputValue: (insetInputValue: InsetInputValue) => void;
|
|
|
setInsetInputFocus: () => void;
|
|
|
- setTriggerDisabled: (disabled: boolean) => void
|
|
|
+ setTriggerDisabled: (disabled: boolean) => void;
|
|
|
+ setInputFocus: () => void;
|
|
|
+ setInputBlur: () => void;
|
|
|
+ setRangeInputBlur: () => void
|
|
|
}
|
|
|
|
|
|
|
|
@@ -224,6 +225,7 @@ export interface DatePickerAdapter extends DefaultAdapter<DatePickerFoundationPr
|
|
|
*/
|
|
|
export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapter> {
|
|
|
|
|
|
+ clickConfirmButton: boolean;
|
|
|
constructor(adapter: DatePickerAdapter) {
|
|
|
super({ ...adapter });
|
|
|
}
|
|
@@ -245,8 +247,7 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
|
|
|
const result = this.parseWithTimezone(_value, timeZone, prevTimeZone);
|
|
|
this._adapter.updatePrevTimezone(prevTimeZone);
|
|
|
// reset input value when value update
|
|
|
- this._adapter.updateInputValue(null);
|
|
|
- this._adapter.updateInsetInputValue(null);
|
|
|
+ this.clearInputValue();
|
|
|
this._adapter.updateValue(result);
|
|
|
this.resetCachedSelectedValue(result);
|
|
|
this.initRangeInputFocus(result);
|
|
@@ -332,7 +333,7 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
|
|
|
|
|
|
destroy() {
|
|
|
// Ensure that event listeners will be uninstalled and users may not trigger closePanel
|
|
|
- // this._adapter.togglePanel(false);
|
|
|
+ this._adapter.togglePanel(false);
|
|
|
this._adapter.unregisterClickOutSide();
|
|
|
}
|
|
|
|
|
@@ -349,45 +350,46 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
|
|
|
openPanel() {
|
|
|
if (!this.getProp('disabled')) {
|
|
|
if (!this._isControlledComponent('open')) {
|
|
|
- this._adapter.togglePanel(true);
|
|
|
- this._adapter.registerClickOutSide();
|
|
|
+ this.open();
|
|
|
}
|
|
|
this._adapter.notifyOpenChange(true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * @deprecated
|
|
|
* do these side effects when type is dateRange or dateTimeRange
|
|
|
* 1. trigger input blur, if input value is invalid, set input value and state value to previous status
|
|
|
* 2. set cachedSelectedValue using given dates(in needConfirm mode)
|
|
|
* - directly closePanel without click confirm will set cachedSelectedValue to state value
|
|
|
* - select one date(which means that the selection value is incomplete) and click confirm also set cachedSelectedValue to state value
|
|
|
*/
|
|
|
- rangeTypeSideEffectsWhenClosePanel(inputValue: string, willUpdateDates: Date[]) {
|
|
|
- if (this._isRangeType()) {
|
|
|
- this._adapter.setRangeInputFocus(false);
|
|
|
- /**
|
|
|
- * inputValue is string when it is not disabled or can't parsed
|
|
|
- * when inputValue is null, picker value will back to last selected value
|
|
|
- */
|
|
|
- this.handleInputBlur(inputValue);
|
|
|
- this.resetCachedSelectedValue(willUpdateDates);
|
|
|
- }
|
|
|
- }
|
|
|
+ // rangeTypeSideEffectsWhenClosePanel(inputValue: string, willUpdateDates: Date[]) {
|
|
|
+ // if (this._isRangeType()) {
|
|
|
+ // this._adapter.setRangeInputFocus(false);
|
|
|
+ // /**
|
|
|
+ // * inputValue is string when it is not disabled or can't parsed
|
|
|
+ // * when inputValue is null, picker value will back to last selected value
|
|
|
+ // */
|
|
|
+ // this.handleInputBlur(inputValue);
|
|
|
+ // this.resetCachedSelectedValue(willUpdateDates);
|
|
|
+ // }
|
|
|
+ // }
|
|
|
|
|
|
/**
|
|
|
+ * @deprecated
|
|
|
* clear input value when selected date is not confirmed
|
|
|
*/
|
|
|
- needConfirmSideEffectsWhenClosePanel(willUpdateDates: Date[] | null | undefined) {
|
|
|
- if (this._adapter.needConfirm() && !this._isRangeType()) {
|
|
|
- /**
|
|
|
- * if `null` input element will show `cachedSelectedValue` formatted value(format in DateInput render)
|
|
|
- * if `` input element will show `` directly
|
|
|
- */
|
|
|
- this._adapter.updateInputValue(null);
|
|
|
- this.resetCachedSelectedValue(willUpdateDates);
|
|
|
- }
|
|
|
- }
|
|
|
+ // needConfirmSideEffectsWhenClosePanel(willUpdateDates: Date[] | null | undefined) {
|
|
|
+ // if (this._adapter.needConfirm() && !this._isRangeType()) {
|
|
|
+ // /**
|
|
|
+ // * if `null` input element will show `cachedSelectedValue` formatted value(format in DateInput render)
|
|
|
+ // * if `` input element will show `` directly
|
|
|
+ // */
|
|
|
+ // this._adapter.updateInputValue(null);
|
|
|
+ // this.resetCachedSelectedValue(willUpdateDates);
|
|
|
+ // }
|
|
|
+ // }
|
|
|
|
|
|
/**
|
|
|
* clear inset input value when close panel
|
|
@@ -426,17 +428,92 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
|
|
|
const { value } = this._adapter.getStates();
|
|
|
const willUpdateDates = isNullOrUndefined(dates) ? value : dates;
|
|
|
if (!this._isControlledComponent('open')) {
|
|
|
- this._adapter.togglePanel(false);
|
|
|
- this._adapter.unregisterClickOutSide();
|
|
|
+ this.close();
|
|
|
+ } else {
|
|
|
+ this.resetInnerSelectedStates(willUpdateDates);
|
|
|
}
|
|
|
- // range type picker, closing panel requires the following side effects
|
|
|
- this.rangeTypeSideEffectsWhenClosePanel(inputValue, willUpdateDates as Date[]);
|
|
|
- this.needConfirmSideEffectsWhenClosePanel(willUpdateDates as Date[]);
|
|
|
- this.clearInsetInputValue();
|
|
|
this._adapter.notifyOpenChange(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ open() {
|
|
|
+ this._adapter.togglePanel(true);
|
|
|
+ this._adapter.registerClickOutSide();
|
|
|
+ }
|
|
|
+
|
|
|
+ close() {
|
|
|
+ this._adapter.togglePanel(false, () => this.resetInnerSelectedStates());
|
|
|
+ this._adapter.unregisterClickOutSide();
|
|
|
+ }
|
|
|
+
|
|
|
+ focus(focusType?: Exclude<RangeType, false>) {
|
|
|
+ if (this._isRangeType()) {
|
|
|
+ const rangeInputFocus = focusType ?? 'rangeStart';
|
|
|
+ this._adapter.setRangeInputFocus(rangeInputFocus);
|
|
|
+ } else {
|
|
|
+ this._adapter.setInputFocus();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ blur() {
|
|
|
+ if (this._isRangeType()) {
|
|
|
+ this._adapter.setRangeInputBlur();
|
|
|
+ } else {
|
|
|
+ this._adapter.setInputBlur();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * reset cachedSelectedValue, inputValue when close panel
|
|
|
+ */
|
|
|
+ resetInnerSelectedStates(willUpdateDates?: Date[]) {
|
|
|
+ const { value } = this._adapter.getStates();
|
|
|
+ const needResetCachedSelectedValue = !this.isCachedSelectedValueValid(willUpdateDates) || this._adapter.needConfirm() && !this.clickConfirmButton;
|
|
|
+ if (needResetCachedSelectedValue) {
|
|
|
+ this.resetCachedSelectedValue(value);
|
|
|
+ }
|
|
|
+ this.resetFocus();
|
|
|
+ this.clearInputValue();
|
|
|
+ this.clickConfirmButton = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ resetFocus(e?: any) {
|
|
|
+ this._adapter.setRangeInputFocus(false);
|
|
|
this._adapter.notifyBlur(e);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * cachedSelectedValue can be `(Date|null)[]` or `null`
|
|
|
+ */
|
|
|
+ isCachedSelectedValueValid(dates: Date[]) {
|
|
|
+ const cachedSelectedValue = dates || this._adapter.getState('cachedSelectedValue');
|
|
|
+ const { type } = this._adapter.getProps();
|
|
|
+ let isValid = true;
|
|
|
+ switch (true) {
|
|
|
+ case type === 'dateRange':
|
|
|
+ case type === 'dateTimeRange':
|
|
|
+ if (!this._isRangeValueComplete(cachedSelectedValue)) {
|
|
|
+ isValid = false;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ const value = cachedSelectedValue?.filter(item => item);
|
|
|
+ if (!(Array.isArray(value) && value.length)) {
|
|
|
+ isValid = false;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return isValid;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将输入框内容置空
|
|
|
+ */
|
|
|
+ clearInputValue() {
|
|
|
+ this._adapter.updateInputValue(null);
|
|
|
+ this._adapter.updateInsetInputValue(null);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* clear range input focus when open is controlled
|
|
|
* fixed github 1375
|
|
@@ -524,38 +601,8 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
|
|
|
* @param {String} input
|
|
|
* @param {Event} e
|
|
|
*/
|
|
|
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
|
handleInputBlur(input = '', e?: any) {
|
|
|
- const parsedResult = input ?
|
|
|
- this._isMultiple() ?
|
|
|
- this.parseMultipleInput(input, ',', true) :
|
|
|
- this.parseInput(input) :
|
|
|
- [];
|
|
|
-
|
|
|
- const stateValue = this.getState('value');
|
|
|
-
|
|
|
- // console.log(input, parsedResult);
|
|
|
-
|
|
|
- if (parsedResult && parsedResult.length) {
|
|
|
- this._updateValueAndInput(parsedResult, input === '');
|
|
|
- } else if (input === '') {
|
|
|
- // if clear input, set input to `''`
|
|
|
- this._updateValueAndInput('' as any, true, '');
|
|
|
- } else {
|
|
|
- this._updateValueAndInput(stateValue);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 当不是范围类型且不需要确认时,使用 stateValue 重置 cachedSelectedValue
|
|
|
- * 这样做的目的是,在输入非法值时,使用上次选中的值作为已选值
|
|
|
- * needConfirm 或者 range type 时,我们在 close panel 时调用 resetCachedSelectedValue,这里不用重复调用
|
|
|
- *
|
|
|
- * Use stateValue to reset cachedSelectedValue when it is not a range type and does not require confirmation
|
|
|
- * The purpose of this is to use the last selected value as the selected value when an invalid value is entered
|
|
|
- * When needConfirm or range type, we call resetCachedSelectedValue when close panel, no need to call repeatedly here
|
|
|
- */
|
|
|
- if (!this._adapter.needConfirm() && !this._isRangeType()) {
|
|
|
- this.resetCachedSelectedValue(stateValue);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -671,6 +718,7 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
|
|
|
break;
|
|
|
case 'dateRange':
|
|
|
case 'dateTimeRange':
|
|
|
+ case 'monthRange':
|
|
|
const separator = rangeSeparator;
|
|
|
const values = input.split(separator);
|
|
|
parsedResult =
|
|
@@ -864,6 +912,7 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
|
|
|
|
|
|
case 'dateRange':
|
|
|
case 'dateTimeRange':
|
|
|
+ case 'monthRange':
|
|
|
const startIsTruthy = !isNullOrUndefined(dates[0]);
|
|
|
const endIsTruthy = !isNullOrUndefined(dates[1]);
|
|
|
if (startIsTruthy && endIsTruthy) {
|
|
@@ -903,6 +952,7 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
|
|
|
break;
|
|
|
case 'dateRange':
|
|
|
case 'dateTimeRange':
|
|
|
+ case 'monthRange':
|
|
|
for (let i = 0; i < dates.length; i += 2) {
|
|
|
strs.push(this.formatDates(dates.slice(i, i + 2), customFormat));
|
|
|
}
|
|
@@ -1012,22 +1062,29 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * when changing the year and month through the panel when the type is year or month
|
|
|
+ * when changing the year and month through the panel when the type is year or month or monthRange
|
|
|
* @param {*} item
|
|
|
*/
|
|
|
- handleYMSelectedChange(item: { currentMonth?: number; currentYear?: number } = {}) {
|
|
|
+ handleYMSelectedChange(item: { currentMonth?: { left: number; right: number }; currentYear?: { left: number; right: number } } = {}) {
|
|
|
// console.log(item);
|
|
|
const { currentMonth, currentYear } = item;
|
|
|
+ const { type } = this.getProps();
|
|
|
|
|
|
- if (typeof currentMonth === 'number' && typeof currentYear === 'number') {
|
|
|
- // Strings with only dates (e.g. "1970-01-01") will be treated as UTC instead of local time #1460
|
|
|
- const date = new Date(currentYear, currentMonth - 1);
|
|
|
+ if (type === 'month') {
|
|
|
+ const date = new Date(currentYear['left'], currentMonth['left'] - 1);
|
|
|
|
|
|
this.handleSelectedChange([date]);
|
|
|
+ } else {
|
|
|
+ const dateLeft = new Date(currentYear['left'], currentMonth['left'] - 1);
|
|
|
+ const dateRight = new Date(currentYear['right'], currentMonth['right'] - 1);
|
|
|
+
|
|
|
+ this.handleSelectedChange([dateLeft, dateRight]);
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
|
|
|
handleConfirm() {
|
|
|
+ this.clickConfirmButton = true;
|
|
|
const { cachedSelectedValue, value } = this._adapter.getStates();
|
|
|
const isRangeValueComplete = this._isRangeValueComplete(cachedSelectedValue);
|
|
|
const newValue = isRangeValueComplete ? cachedSelectedValue : value;
|
|
@@ -1127,6 +1184,7 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
|
|
|
break;
|
|
|
case 'dateRange':
|
|
|
case 'dateTimeRange':
|
|
|
+ case 'monthRange':
|
|
|
notifyValue = _value.map(v => v && this.localeFormat(v, formatToken));
|
|
|
notifyDate = [..._value];
|
|
|
break;
|