Skip to content

Commit

Permalink
refactor(module:date-picker): will not sort range date (#5812)
Browse files Browse the repository at this point in the history
Closes #5572
Closes #5651
Closes #5631
  • Loading branch information
wenqi73 authored Sep 23, 2020
1 parent fc12672 commit c290ca4
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 85 deletions.
27 changes: 26 additions & 1 deletion components/core/time/candy-date.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CandyDate } from './candy-date';
import { CandyDate, normalizeRangeValue, SingleValue } from './candy-date';

describe('candy-date coverage supplements', () => {
const date = new CandyDate('2018-5-5 12:12:12');
Expand Down Expand Up @@ -83,4 +83,29 @@ describe('candy-date coverage supplements', () => {
const errorMessage = 'The input date type is not supported ("Date" is now recommended)';
expect(() => new CandyDate({} as any)).toThrowError(errorMessage); // tslint:disable-line:no-any
});

it('should normalizeRangeValue work', () => {
const randomDay = new CandyDate('2020-09-17');
const now = new Date();
let result: SingleValue[];
result = normalizeRangeValue([null, randomDay], false);
expect(result[0]!.getMonth()).toEqual(7);
expect(result[1]!.getMonth()).toEqual(8);

result = normalizeRangeValue([randomDay, null], false);
expect(result[0]!.getMonth()).toEqual(8);
expect(result[1]!.getMonth()).toEqual(9);

result = normalizeRangeValue([null, null], false);
expect(result[0]!.getMonth()).toEqual(now.getMonth());
expect(result[1]!.getMonth()).toEqual(now.getMonth() + 1);

result = normalizeRangeValue([new CandyDate(), new CandyDate()], false);
expect(result[0]!.getMonth()).toEqual(now.getMonth());
expect(result[1]!.getMonth()).toEqual(now.getMonth() + 1);

result = normalizeRangeValue([new CandyDate(), new CandyDate()], true);
expect(result[0]!.getMonth()).toEqual(now.getMonth());
expect(result[1]!.getMonth()).toEqual(now.getMonth());
});
}); // /candy-date coverage supplements
30 changes: 19 additions & 11 deletions components/core/time/candy-date.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,32 @@ import startOfWeek from 'date-fns/startOfWeek';
import { warn } from 'ng-zorro-antd/core/logger';
import { IndexableObject, NzSafeAny } from 'ng-zorro-antd/core/types';

type CandyDateCompareGrain = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second';

export type WeekDayIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6;
export type CandyDateCompareGrain = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second';
export type CandyDateType = CandyDate | Date | null;
export type SingleValue = CandyDate | null;
export type CompatibleValue = SingleValue | SingleValue[];

export function sortRangeValue(rangeValue: SingleValue[]): SingleValue[] {
if (Array.isArray(rangeValue)) {
const [start, end] = rangeValue;
return start && end && start.isAfterSecond(end) ? [end, start] : [start, end];
}
return rangeValue;
export function wrongSortOrder(rangeValue: SingleValue[]): boolean {
const [start, end] = rangeValue;
return !!start && !!end && start.isAfterSecond(end);
}

export function normalizeRangeValue(value: SingleValue[]): CandyDate[] {
const [start, end] = value || [];
const newStart = start || new CandyDate();
const newEnd = end?.isSameMonth(newStart) ? end.addMonths(1) : end || newStart.addMonths(1);
export function normalizeRangeValue(value: SingleValue[], allowSameMonth: boolean): CandyDate[] {
const [start, end] = value;
let newStart: CandyDate = start || new CandyDate();
let newEnd: CandyDate = end || new CandyDate();
if (start && !end) {
newStart = start;
newEnd = start.addMonths(1);
} else if (!start && end) {
newStart = end.addMonths(-1);
newEnd = end;
}
if (newEnd.isSameMonth(newStart) && !allowSameMonth) {
newEnd = newStart.addMonths(1);
}
return [newStart, newEnd];
}

Expand Down
6 changes: 3 additions & 3 deletions components/date-picker/date-picker.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class DatePickerService implements OnDestroy {

hasValue(value: CompatibleValue = this.value): boolean {
if (Array.isArray(value)) {
return !!value[0] && !!value[1];
return !!value[0] || !!value[1];
} else {
return !!value;
}
Expand All @@ -47,9 +47,9 @@ export class DatePickerService implements OnDestroy {
}
}

setActiveDate(value: CompatibleValue, normalize: boolean = false): void {
setActiveDate(value: CompatibleValue, allowSameMonth: boolean = false): void {
if (this.isRange) {
this.activeDate = normalize ? normalizeRangeValue(value as CandyDate[]) : value;
this.activeDate = normalizeRangeValue(value as CandyDate[], allowSameMonth);
} else {
this.activeDate = cloneDate(value);
}
Expand Down
79 changes: 41 additions & 38 deletions components/date-picker/date-range-popup.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
ViewEncapsulation
} from '@angular/core';

import { CandyDate, cloneDate, CompatibleValue, SingleValue, sortRangeValue } from 'ng-zorro-antd/core/time';
import { CandyDate, cloneDate, CompatibleValue, SingleValue, wrongSortOrder } from 'ng-zorro-antd/core/time';
import { FunctionProp } from 'ng-zorro-antd/core/types';
import { NzCalendarI18nInterface } from 'ng-zorro-antd/i18n';
import { Subject } from 'rxjs';
Expand Down Expand Up @@ -152,6 +152,7 @@ export class DateRangePopupComponent implements OnInit, OnChanges, OnDestroy {
endPanelMode: NzDateMode | NzDateMode[] = 'date';
timeOptions: SupportTimeOptions | SupportTimeOptions[] | null = null;
hoverValue: SingleValue[] = []; // Range ONLY
checkedPartArr: boolean[] = [false, false];
destroy$ = new Subject();

get hasTimePicker(): boolean {
Expand All @@ -166,7 +167,7 @@ export class DateRangePopupComponent implements OnInit, OnChanges, OnDestroy {

ngOnInit(): void {
this.datePickerService.valueChange$.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.initActiveDate();
this.updateActiveDate();
this.cdr.markForCheck();
});
}
Expand All @@ -182,7 +183,7 @@ export class DateRangePopupComponent implements OnInit, OnChanges, OnDestroy {
this.endPanelMode = this.panelMode;
}
if (changes.defaultPickerValue) {
this.initActiveDate();
this.updateActiveDate();
}
}

Expand All @@ -191,11 +192,11 @@ export class DateRangePopupComponent implements OnInit, OnChanges, OnDestroy {
this.destroy$.complete();
}

initActiveDate(): void {
updateActiveDate(): void {
const activeDate = this.datePickerService.hasValue()
? this.datePickerService.value
: this.datePickerService.makeValue(this.defaultPickerValue!);
this.datePickerService.setActiveDate(activeDate, !this.showTime);
this.datePickerService.setActiveDate(activeDate, this.hasTimePicker);
}

onClickOk(): void {
Expand Down Expand Up @@ -237,19 +238,16 @@ export class DateRangePopupComponent implements OnInit, OnChanges, OnDestroy {
} else {
this.panelMode = mode;
}
// this.cdr.markForCheck();
this.panelModeChange.emit(this.panelMode);
}

onActiveDateChange(value: CandyDate, partType: RangePartType): void {
if (this.isRange) {
if (partType === 'left') {
this.datePickerService.activeDate = [value, value.addMonths(1)];
} else {
this.datePickerService.activeDate = [value.addMonths(-1), value];
}
const activeDate: SingleValue[] = [];
activeDate[this.datePickerService.getActiveIndex(partType)] = value;
this.datePickerService.setActiveDate(activeDate, this.hasTimePicker);
} else {
this.datePickerService.activeDate = value;
this.datePickerService.setActiveDate(value);
}
}

Expand All @@ -269,51 +267,56 @@ export class DateRangePopupComponent implements OnInit, OnChanges, OnDestroy {

changeValueFromSelect(value: CandyDate, emitValue: boolean = true): void {
if (this.isRange) {
let selectedValue: SingleValue[] = cloneDate(this.datePickerService.value) as CandyDate[];
let otherPart: RangePartType;
if (this.datePickerService.activeInput === 'left') {
otherPart = 'right';
selectedValue[0] = value;
} else {
otherPart = 'left';
selectedValue[1] = value;
}
const selectedValue: SingleValue[] = cloneDate(this.datePickerService.value) as CandyDate[];
const checkedPart: RangePartType = this.datePickerService.activeInput;
const otherPart = this.reversedPart(checkedPart);

selectedValue = sortRangeValue(selectedValue);
selectedValue[this.datePickerService.getActiveIndex(checkedPart)] = value;
this.checkedPartArr[this.datePickerService.getActiveIndex(checkedPart)] = true;
this.hoverValue = selectedValue;
this.datePickerService.setValue(selectedValue);
this.datePickerService.setActiveDate(selectedValue, !this.showTime);
this.datePickerService.inputPartChange$.next();

if (!this.isAllowed(selectedValue)) {
return;
}

if (emitValue) {
// If the other input has value
if (this.isBothAllowed(selectedValue)) {
/**
* if sort order is wrong, clear the other part's value
*/
if (wrongSortOrder(selectedValue)) {
// selectedValue = sortRangeValue(selectedValue);
selectedValue[this.datePickerService.getActiveIndex(otherPart)] = null;
this.checkedPartArr[this.datePickerService.getActiveIndex(otherPart)] = false;
}

this.datePickerService.setValue(selectedValue);

/**
* range date usually selected paired,
* so we emit the date value only both date is allowed and both part are checked
*/
if (this.isBothAllowed(selectedValue) && this.checkedPartArr[0] && this.checkedPartArr[1]) {
this.calendarChange.emit(selectedValue);
this.clearHoverValue();
this.datePickerService.emitValue$.next();
} else {
} else if (this.isAllowed(selectedValue)) {
this.calendarChange.emit([value.clone()]);
this.datePickerService.inputPartChange$.next(otherPart!);
this.datePickerService.inputPartChange$.next(otherPart);
}
} else {
this.datePickerService.setValue(selectedValue);
this.datePickerService.inputPartChange$.next();
}
} else {
this.datePickerService.setValue(value);
this.datePickerService.setActiveDate(value, !this.showTime);
this.datePickerService.inputPartChange$.next();

if (!this.isAllowed(value)) {
return;
}
if (emitValue) {
if (emitValue && this.isAllowed(value)) {
this.datePickerService.emitValue$.next();
}
}
}

reversedPart(part: RangePartType): RangePartType {
return part === 'left' ? 'right' : 'left';
}

getPanelMode(panelMode: NzDateMode | NzDateMode[], partType?: RangePartType): NzDateMode {
if (this.isRange) {
return panelMode[this.datePickerService.getActiveIndex(partType)] as NzDateMode;
Expand Down
36 changes: 21 additions & 15 deletions components/date-picker/picker.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { ESCAPE } from '@angular/cdk/keycodes';
import {
CdkConnectedOverlay,
CdkOverlayOrigin,
Expand Down Expand Up @@ -36,8 +37,7 @@ import {
} from '@angular/core';
import { slideMotion } from 'ng-zorro-antd/core/animation';

import { ESCAPE } from '@angular/cdk/keycodes';
import { CandyDate, CompatibleValue } from 'ng-zorro-antd/core/time';
import { CandyDate, CompatibleValue, wrongSortOrder } from 'ng-zorro-antd/core/time';
import { NgStyleInterface, NzSafeAny } from 'ng-zorro-antd/core/types';
import { DateHelperService } from 'ng-zorro-antd/i18n';
import { fromEvent, Subject } from 'rxjs';
Expand Down Expand Up @@ -179,8 +179,8 @@ export class NzPickerComponent implements OnInit, AfterViewInit, OnChanges, OnDe
@ViewChild(CdkConnectedOverlay, { static: false }) cdkConnectedOverlay?: CdkConnectedOverlay;
@ViewChild('separatorElement', { static: false }) separatorElement?: ElementRef;
@ViewChild('pickerInput', { static: false }) pickerInput?: ElementRef<HTMLInputElement>;
@ViewChildren('rangePickerInput') rangePickerInputs!: QueryList<ElementRef<HTMLInputElement>>;
@ContentChild(DateRangePopupComponent) panel!: DateRangePopupComponent;
@ViewChildren('rangePickerInput') rangePickerInputs?: QueryList<ElementRef<HTMLInputElement>>;
@ContentChild(DateRangePopupComponent) panel?: DateRangePopupComponent;

origin: CdkOverlayOrigin;
document: Document;
Expand All @@ -189,8 +189,7 @@ export class NzPickerComponent implements OnInit, AfterViewInit, OnChanges, OnDe
arrowLeft?: number;
destroy$ = new Subject();
prefixCls = PREFIX_CLASS;
// Index signature in type 'string | string[]' only permits reading
inputValue: NzSafeAny = '';
inputValue!: NzSafeAny;
activeBarStyle: object = { position: 'absolute' };
animationOpenState = false;
overlayOpen: boolean = false; // Available when "open"=undefined
Expand Down Expand Up @@ -250,7 +249,7 @@ export class NzPickerComponent implements OnInit, AfterViewInit, OnChanges, OnDe

ngOnInit(): void {
this.inputSize = Math.max(10, this.format.length) + 2;

this.inputValue = this.isRange ? ['', ''] : '';
this.datePickerService.valueChange$.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.updateInputValue();
});
Expand Down Expand Up @@ -280,9 +279,7 @@ export class NzPickerComponent implements OnInit, AfterViewInit, OnChanges, OnDe
...this.datePickerService.arrowPositionStyle,
width: `${this.inputWidth}px`
};
if (this.document.activeElement !== this.getInput(this.datePickerService.activeInput)) {
this.focus();
}
this.focus();
this.panel?.cdr.markForCheck();
this.cdr.markForCheck();
});
Expand All @@ -304,16 +301,19 @@ export class NzPickerComponent implements OnInit, AfterViewInit, OnChanges, OnDe
this.arrowLeft = this.inputWidth + this.separatorElement?.nativeElement.offsetWidth || 0;
}

getInput(partType?: RangePartType): HTMLInputElement {
getInput(partType?: RangePartType): HTMLInputElement | undefined {
return this.isRange
? partType === 'left'
? this.rangePickerInputs.first.nativeElement
: this.rangePickerInputs.last.nativeElement
? this.rangePickerInputs?.first.nativeElement
: this.rangePickerInputs?.last.nativeElement
: this.pickerInput!.nativeElement;
}

focus(): void {
this.getInput(this.datePickerService.activeInput).focus(); // Focus on the first input
const activeInputElement = this.getInput(this.datePickerService.activeInput);
if (this.document.activeElement !== activeInputElement) {
activeInputElement?.focus();
}
}

onFocus(partType?: RangePartType): void {
Expand Down Expand Up @@ -359,7 +359,13 @@ export class NzPickerComponent implements OnInit, AfterViewInit, OnChanges, OnDe
}

onClickBackdrop(): void {
if (this.panel.isAllowed(this.datePickerService.value!, true)) {
if (this.panel?.isAllowed(this.datePickerService.value!, true)) {
if (Array.isArray(this.datePickerService.value) && wrongSortOrder(this.datePickerService.value)) {
const index = this.datePickerService.getActiveIndex(this.datePickerService.activeInput);
const value = this.datePickerService.value[index];
this.panel?.changeValueFromSelect(value!, true);
return;
}
this.updateInputValue();
this.datePickerService.emitValue$.next();
} else {
Expand Down
Loading

0 comments on commit c290ca4

Please sign in to comment.