From 479bad335ee503003a8086a97c405e437a56779f Mon Sep 17 00:00:00 2001 From: kekehaoz Date: Wed, 13 Mar 2019 18:09:31 +0800 Subject: [PATCH] feat(module: TimePicker): support 12-hour(#1513) --- .../nz-time-picker-panel.component.html | 18 +++ .../nz-time-picker-panel.component.ts | 109 +++++++++++++++--- .../time-picker/nz-time-picker.component.html | 1 + .../time-picker/nz-time-picker.component.ts | 1 + components/time-picker/time-holder.ts | 94 ++++++++++++++- 5 files changed, 208 insertions(+), 15 deletions(-) diff --git a/components/time-picker/nz-time-picker-panel.component.html b/components/time-picker/nz-time-picker-panel.component.html index 88db37beb74..c8665283938 100644 --- a/components/time-picker/nz-time-picker-panel.component.html +++ b/components/time-picker/nz-time-picker-panel.component.html @@ -69,6 +69,24 @@ +
+ +
diff --git a/components/time-picker/nz-time-picker-panel.component.ts b/components/time-picker/nz-time-picker-panel.component.ts index 163bf1b976d..2805edd426c 100644 --- a/components/time-picker/nz-time-picker-panel.component.ts +++ b/components/time-picker/nz-time-picker-panel.component.ts @@ -19,10 +19,10 @@ import { reqAnimFrame } from '../core/polyfill/request-animation'; import { NzUpdateHostClassService as UpdateCls } from '../core/services/update-host-class.service'; import { isNotNil } from '../core/util/check'; import { NzTimeValueAccessorDirective } from './nz-time-value-accessor.directive'; -import { TimeHolder } from './time-holder'; +import { HourTypes, TimeHolder } from './time-holder'; -function makeRange(length: number, step: number = 1): number[] { - return new Array(Math.ceil(length / step)).fill(0).map((_, i) => i * step); +function makeRange(length: number, step: number = 1, start: number = 0): number[] { + return new Array(Math.ceil(length / step)).fill(0).map((_, i) => (i + start) * step); } @Component({ @@ -49,6 +49,7 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, private _defaultOpenValue = new Date(); private _opened = false; private _allowEmpty = true; + private _nzUse12Hours = false; prefixCls: string = 'ant-time-picker-panel'; time = new TimeHolder(); hourEnabled = true; @@ -58,10 +59,12 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, hourRange: ReadonlyArray<{ index: number, disabled: boolean }>; minuteRange: ReadonlyArray<{ index: number, disabled: boolean }>; secondRange: ReadonlyArray<{ index: number, disabled: boolean }>; + use12HoursRange: ReadonlyArray<{index: number, value: string}>; @ViewChild(NzTimeValueAccessorDirective) nzTimeValueAccessorDirective: NzTimeValueAccessorDirective; @ViewChild('hourListElement') hourListElement; @ViewChild('minuteListElement') minuteListElement; @ViewChild('secondListElement') secondListElement; + @ViewChild('use12HoursListElement') use12HoursListElement; @Input() nzInDatePicker: boolean = false; // If inside a date-picker, more diff works need to be done @Input() nzAddOn: TemplateRef; @Input() nzHideDisabledOptions = false; @@ -201,6 +204,21 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, return this._nzSecondStep; } + @Input() + set nzUse12Hours(value: boolean) { + if (isNotNil(value)) { + if (value) { + this.build12Hours(); + this.enabledColumns++; + } + this._nzUse12Hours = value; + } + } + + get nzUse12Hours(): boolean { + return this._nzUse12Hours; + } + selectInputRange(): void { setTimeout(() => { if (this.nzTimeValueAccessorDirective) { @@ -210,13 +228,32 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, } buildHours(): void { - this.hourRange = makeRange(24, this.nzHourStep).map(r => { - return { - index : r, - disabled: this.nzDisabledHours && (this.nzDisabledHours().indexOf(r) !== -1) - }; + let hourRanges = 24; + let disabledHours = this.nzDisabledHours && this.nzDisabledHours(); + let startIndex = 0; + if (this.nzUse12Hours) { + hourRanges = 12; + if (disabledHours) { + if (this.time.selected12Hours === 'PM') { + disabledHours = disabledHours.filter(i => i >= 12).map(i => (i > 12 ? i - 12 : i)); + } else { + disabledHours = disabledHours.filter(i => i < 12 || i === 24).map(i => (i === 24 ? 0 : i)); + } } - ); + startIndex = 1; + } + this.hourRange = makeRange(hourRanges, this.nzHourStep, startIndex).map(r => { + return { + index : r, + disabled: this.nzDisabledHours && (disabledHours.indexOf(r) !== -1) + }; + }); + if (this.nzUse12Hours && this.hourRange[this.hourRange.length - 1].index === 12) { + const temp = [...this.hourRange]; + temp.unshift(temp[temp.length - 1]); + temp.splice(temp.length - 1, 1); + this.hourRange = temp; + } } buildMinutes(): void { @@ -239,10 +276,22 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, ); } + build12Hours(): void { + const isUpperForamt = this._format.includes('A'); + this.use12HoursRange = [{ + index: 0, + value: isUpperForamt ? 'AM' : 'am' + }, { + index: 1, + value: isUpperForamt ? 'PM' : 'pm' + }]; + } + buildTimes(): void { this.buildHours(); this.buildMinutes(); this.buildSeconds(); + this.build12Hours(); } selectHour(hour: { index: number, disabled: boolean }): void { @@ -270,6 +319,20 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, this.scrollToSelected(this.secondListElement.nativeElement, second.index, 120, 'second'); } + select12Hours(value: {index: number, value: string}): void { + this.time.selected12Hours = value.value; + if (this._disabledHours) { + this.buildHours(); + } + if (this._disabledMinutes) { + this.buildMinutes(); + } + if (this._disabledSeconds) { + this.buildSeconds(); + } + this.scrollToSelected(this.use12HoursListElement.nativeElement, value.index, 120, '12-hour'); + } + scrollToSelected(instance: HTMLElement, index: number, duration: number = 0, unit: string): void { const transIndex = this.translateIndex(index, unit); const currentOption = (instance.children[ 0 ].children[ transIndex ] || instance.children[ 0 ].children[ 0 ]) as HTMLElement; @@ -286,6 +349,8 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, } else if (unit === 'second') { const disabledSeconds = this.nzDisabledSeconds && this.nzDisabledSeconds(this.time.hours, this.time.minutes); return this.calcIndex(disabledSeconds, this.secondRange.map(item => item.index).indexOf(index)); + } else if (unit === '12-hour') { + return this.calcIndex([], this.use12HoursRange.map(item => item.index).indexOf(index)); } } @@ -338,7 +403,9 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, } isSelectedHour(hour: { index: number, disabled: boolean }): boolean { - return (hour.index === this.time.hours) || (!isNotNil(this.time.hours) && (hour.index === this.time.defaultHours)); + return (hour.index === this.time.getHours(HourTypes.ViewHour)) + || (!isNotNil(this.time.getHours(HourTypes.ViewHour)) + && (hour.index === this.time.getHours(HourTypes.ViewHour, this.time.defaultHours))); } isSelectedMinute(minute: { index: number, disabled: boolean }): boolean { @@ -349,13 +416,18 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, return (second.index === this.time.seconds) || (!isNotNil(this.time.seconds) && (second.index === this.time.defaultSeconds)); } + isSelected12Hours(value: {index: number, value: string}): boolean { + return (value.value.toUpperCase() === this.time.selected12Hours) || + (!isNotNil(this.time.selected12Hours) && (value.value.toUpperCase() === this.time.default12Hours)); + } + initPosition(): void { setTimeout(() => { if (this.hourEnabled && this.hourListElement) { - if (isNotNil(this.time.hours)) { - this.scrollToSelected(this.hourListElement.nativeElement, this.time.hours, 0, 'hour'); + if (isNotNil(this.time.getHours(HourTypes.ViewHour))) { + this.scrollToSelected(this.hourListElement.nativeElement, this.time.getHours(HourTypes.ViewHour), 0, 'hour'); } else { - this.scrollToSelected(this.hourListElement.nativeElement, this.time.defaultHours, 0, 'hour'); + this.scrollToSelected(this.hourListElement.nativeElement, this.time.getHours(HourTypes.ViewHour, this.time.defaultHours), 0, 'hour'); } } if (this.minuteEnabled && this.minuteListElement) { @@ -372,6 +444,15 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, this.scrollToSelected(this.secondListElement.nativeElement, this.time.defaultSeconds, 0, 'second'); } } + if (this.nzUse12Hours && this.use12HoursListElement) { + if (isNotNil(this.time.selected12Hours)) { + const index = this.time.selected12Hours === 'AM' ? 0 : 1; + this.scrollToSelected(this.use12HoursListElement.nativeElement, index, 0, '12-hour'); + } else { + const index = this.time.default12Hours === 'AM' ? 0 : 1; + this.scrollToSelected(this.use12HoursListElement.nativeElement, index, 0, '12-hour'); + } + } }); } @@ -397,7 +478,7 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, } writeValue(value: Date): void { - this.time.value = value; + this.time.setValue(value, this.nzUse12Hours); this.buildTimes(); // Mark this component to be checked manually with internal properties changing (see: https://github.com/angular/angular/issues/10816) diff --git a/components/time-picker/nz-time-picker.component.html b/components/time-picker/nz-time-picker.component.html index 1d09aad15b5..338fd8585a1 100644 --- a/components/time-picker/nz-time-picker.component.html +++ b/components/time-picker/nz-time-picker.component.html @@ -44,6 +44,7 @@ [nzDisabledSeconds]="nzDisabledSeconds" [nzPlaceHolder]="nzPlaceHolder || ('TimePicker.placeholder' | nzI18n)" [nzHideDisabledOptions]="nzHideDisabledOptions" + [nzUse12Hours]="nzUse12Hours" [nzDefaultOpenValue]="nzDefaultOpenValue" [nzAddOn]="nzAddOn" [opened]="nzOpen" diff --git a/components/time-picker/nz-time-picker.component.ts b/components/time-picker/nz-time-picker.component.ts index 651db98c59f..18e12fbc27d 100644 --- a/components/time-picker/nz-time-picker.component.ts +++ b/components/time-picker/nz-time-picker.component.ts @@ -64,6 +64,7 @@ export class NzTimePickerComponent implements ControlValueAccessor, OnInit, Afte @Input() nzDisabledSeconds: (hour: number, minute: number) => number[]; @Input() nzFormat = 'HH:mm:ss'; @Input() nzOpen = false; + @Input() nzUse12Hours = false; @Output() readonly nzOpenChange = new EventEmitter(); @Input() diff --git a/components/time-picker/time-holder.ts b/components/time-picker/time-holder.ts index 130f35d54db..158e6923412 100644 --- a/components/time-picker/time-holder.ts +++ b/components/time-picker/time-holder.ts @@ -1,10 +1,22 @@ import { Observable, Subject } from 'rxjs'; import { isNotNil } from '../core/util/check'; +/** + * use for 12-hour to distinguish DataHour and View Hour when `nzUse12Hours` is true + * ViewHour is used for UI view and its range is [0 - 12] + * DataHour is actullay hour value and its rangs is [0 - 23] + */ +export const enum HourTypes { + DataHour, + ViewHour +} + export class TimeHolder { private _seconds = undefined; private _hours = undefined; private _minutes = undefined; + private _selected12Hours = undefined; + private _use12Hours = false; private _defaultOpenValue: Date = new Date(); private _value: Date; private _changes = new Subject(); @@ -42,6 +54,11 @@ export class TimeHolder { return this; } + setUse12Hours(value: boolean): this { + this._use12Hours = value; + return this; + } + get changes(): Observable { return this._changes.asObservable(); } @@ -57,13 +74,17 @@ export class TimeHolder { this._hours = this._value.getHours(); this._minutes = this._value.getMinutes(); this._seconds = this._value.getSeconds(); + if (this._use12Hours) { + this._selected12Hours = this._hours >= 12 ? 'PM' : 'AM'; + } } else { this._clear(); } } } - setValue(value: Date): this { + setValue(value: Date, use12Hours: boolean): this { + this._use12Hours = use12Hours; this.value = value; return this; } @@ -81,6 +102,7 @@ export class TimeHolder { this._hours = undefined; this._minutes = undefined; this._seconds = undefined; + this._selected12Hours = undefined; } private update(): void { @@ -105,6 +127,20 @@ export class TimeHolder { this._value.setSeconds(this.seconds); } + if (this._use12Hours) { + if (!isNotNil(this._selected12Hours)) { + this._selected12Hours = this.default12Hours; + } + if (this.selected12Hours === 'PM' && this._hours < 12) { + this._hours += 12; + this._value.setHours(this._hours); + } + if (this.selected12Hours === 'AM' && this._hours >= 12) { + this._hours -= 12; + this._value.setHours(this._hours); + } + } + this._value = new Date(this._value); } this.changed(); @@ -114,12 +150,44 @@ export class TimeHolder { this._changes.next(this._value); } + /** + * get ViewHour or DataHour depeond on the `type` param + * the transformed value which from DataHour to ViewHour is `value` param and this._hours is default `value` + */ + getHours(type: HourTypes, value?: number): number { + let transformedValue; + if (isNotNil(value)) { + transformedValue = value; + } else { + transformedValue = this._hours; + } + if (!isNotNil(transformedValue)) { + return null; + } + if (type === HourTypes.ViewHour) { + return this.getViewHours(transformedValue, this._selected12Hours || this.default12Hours); + } else { + return this._hours; + } + } + get hours(): number { return this._hours; } + /** + * this._hours stands for DataHour. + * ViewHour can be accessed by getHours() + */ set hours(value: number) { if (value !== this._hours) { + if (this._use12Hours) { + if (this.selected12Hours === 'PM' && value !== 12 ) { + this._hours = value + 12; + } else if (this.selected12Hours === 'AM' && value === 12) { + this._hours = 0; + } + } this._hours = value; this.update(); } @@ -147,6 +215,17 @@ export class TimeHolder { } } + get selected12Hours(): string { + return this._selected12Hours; + } + + set selected12Hours(value: string) { + if (value.toUpperCase() !== this._selected12Hours) { + this._selected12Hours = value.toUpperCase(); + this.update(); + } + } + get defaultOpenValue(): Date { return this._defaultOpenValue; } @@ -175,7 +254,20 @@ export class TimeHolder { return this._defaultOpenValue.getSeconds(); } + get default12Hours(): string { + return this._defaultOpenValue.getHours() >= 12 ? 'PM' : 'AM'; + } + constructor() { } + private getViewHours(value: number, selecte12Hours: string): number { + if (selecte12Hours === 'PM' && value > 12) { + return value - 12; + } + if (selecte12Hours === 'AM' && value === 0 ) { + return 12; + } + return value; + } }