diff --git a/components/core/time/public-api.ts b/components/core/time/public-api.ts index 69e5f0db587..3e1d3846c26 100644 --- a/components/core/time/public-api.ts +++ b/components/core/time/public-api.ts @@ -5,3 +5,4 @@ export * from './candy-date'; export * from './time'; +export { NgTimeParser as ɵNgTimeParser, TimeResult as ɵTimeResult } from './time-parser'; diff --git a/components/core/time/time-parser.spec.ts b/components/core/time/time-parser.spec.ts new file mode 100644 index 00000000000..868b0e0ab94 --- /dev/null +++ b/components/core/time/time-parser.spec.ts @@ -0,0 +1,94 @@ +import { registerLocaleData } from '@angular/common'; +import zh from '@angular/common/locales/zh'; +import { Injector, LOCALE_ID } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { NgTimeParser, TimeResult } from './time-parser'; + +describe('Parse time with angular format', () => { + let injector: Injector; + let localeId: string; + let parser: NgTimeParser; + let result: TimeResult | null; + let time: Date; + + describe('default locale', () => { + beforeEach(() => { + injector = TestBed.configureTestingModule({}); + localeId = injector.get(LOCALE_ID); + }); + + it('should parse hh:mm:ss a', () => { + parser = new NgTimeParser('hh:mm:ss a', localeId); + result = parser.getTimeResult('12:30:22 AM'); + expect(result?.hour).toBe(12); + expect(result?.minute).toBe(30); + expect(result?.second).toBe(22); + expect(result?.period).toBe(0); + + result = parser.getTimeResult('12:30:22 PM'); + expect(result?.period).toBe(1); + + time = parser.toDate('12:30:22 PM'); + expect(time.getHours()).toBe(12); + expect(time.getMinutes()).toBe(30); + expect(time.getSeconds()).toBe(22); + + time = parser.toDate('05:30:22 PM'); + expect(time.getHours()).toBe(17); + }); + + it('should parse hh:mm:ss aaaaa', () => { + parser = new NgTimeParser('hh:mm:ss aaaaa', localeId); + result = parser.getTimeResult('12:30:22 a'); + expect(result?.hour).toBe(12); + expect(result?.minute).toBe(30); + expect(result?.second).toBe(22); + expect(result?.period).toBe(0); + + result = parser.getTimeResult('12:30:22 p'); + expect(result?.period).toBe(1); + }); + + it('should parse mm(ss) HH', () => { + parser = new NgTimeParser('mm(ss) HH', localeId); + result = parser.getTimeResult('30(22) 12'); + expect(result?.period).toBe(null); + + time = parser.toDate('30(22) 12'); + expect(time.getHours()).toBe(12); + expect(time.getMinutes()).toBe(30); + expect(time.getSeconds()).toBe(22); + }); + + it('should parse ss + mm', () => { + parser = new NgTimeParser('ss + mm', localeId); + time = parser.toDate('10 + 42'); + const now = new Date(); + expect(time.getHours()).toBe(now.getHours()); + expect(time.getMinutes()).toBe(42); + expect(time.getSeconds()).toBe(10); + }); + }); + + describe('zh locale', () => { + registerLocaleData(zh); + beforeEach(() => { + injector = TestBed.configureTestingModule({ + providers: [{ provide: LOCALE_ID, useValue: 'zh-CN' }] + }); + localeId = injector.get(LOCALE_ID); + }); + + it('should parse hh:mm:ss a', () => { + parser = new NgTimeParser('hh:mm:ss a', localeId); + result = parser.getTimeResult('04:45:22 上午'); + expect(result?.hour).toBe(4); + expect(result?.minute).toBe(45); + expect(result?.second).toBe(22); + expect(result?.period).toBe(0); + + time = parser.toDate('04:45:22 下午'); + expect(time.getHours()).toBe(16); + }); + }); +}); diff --git a/components/core/time/time-parser.ts b/components/core/time/time-parser.ts new file mode 100644 index 00000000000..46b689d4a3b --- /dev/null +++ b/components/core/time/time-parser.ts @@ -0,0 +1,143 @@ +/** + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +// from https://github.com/hsuanxyz/ng-time-parser +import { FormStyle, getLocaleDayPeriods, TranslationWidth } from '@angular/common'; +import { isNotNil } from 'ng-zorro-antd/core/util'; + +export interface TimeResult { + hour: number | null; + minute: number | null; + second: number | null; + period: number | null; +} + +export class NgTimeParser { + regex: RegExp = null!; + matchMap: { [key: string]: null | number } = { + hour: null, + minute: null, + second: null, + periodNarrow: null, + periodWide: null, + periodAbbreviated: null + }; + + constructor(private format: string, private localeId: string) { + this.genRegexp(); + } + + toDate(str: string): Date { + const result = this.getTimeResult(str); + const time = new Date(); + + if (isNotNil(result?.hour)) { + time.setHours(result!.hour); + } + + if (isNotNil(result?.minute)) { + time.setMinutes(result!.minute); + } + + if (isNotNil(result?.second)) { + time.setSeconds(result!.second); + } + + if (result?.period === 1 && time.getHours() < 12) { + time.setHours(time.getHours() + 12); + } + + return time; + } + + getTimeResult(str: string): TimeResult | null { + const match = this.regex.exec(str); + let period = null; + if (match) { + if (isNotNil(this.matchMap.periodNarrow)) { + period = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Narrow).indexOf( + match[this.matchMap.periodNarrow + 1] + ); + } + if (isNotNil(this.matchMap.periodWide)) { + period = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Wide).indexOf(match[this.matchMap.periodWide + 1]); + } + if (isNotNil(this.matchMap.periodAbbreviated)) { + period = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Abbreviated).indexOf( + match[this.matchMap.periodAbbreviated + 1] + ); + } + return { + hour: isNotNil(this.matchMap.hour) ? Number.parseInt(match[this.matchMap.hour + 1], 10) : null, + minute: isNotNil(this.matchMap.minute) ? Number.parseInt(match[this.matchMap.minute + 1], 10) : null, + second: isNotNil(this.matchMap.second) ? Number.parseInt(match[this.matchMap.second + 1], 10) : null, + period + }; + } else { + return null; + } + } + + genRegexp(): void { + let regexStr = this.format.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$&'); + const hourRegex = /h{1,2}/i; + const minuteRegex = /m{1,2}/; + const secondRegex = /s{1,2}/; + const periodNarrow = /aaaaa/; + const periodWide = /aaaa/; + const periodAbbreviated = /a{1,3}/; + + const hourMatch = hourRegex.exec(this.format); + const minuteMatch = minuteRegex.exec(this.format); + const secondMatch = secondRegex.exec(this.format); + const periodNarrowMatch = periodNarrow.exec(this.format); + let periodWideMatch: null | RegExpExecArray = null; + let periodAbbreviatedMatch: null | RegExpExecArray = null; + if (!periodNarrowMatch) { + periodWideMatch = periodWide.exec(this.format); + } + if (!periodWideMatch && !periodNarrowMatch) { + periodAbbreviatedMatch = periodAbbreviated.exec(this.format); + } + + const matchs = [hourMatch, minuteMatch, secondMatch, periodNarrowMatch, periodWideMatch, periodAbbreviatedMatch] + .filter(m => !!m) + .sort((a, b) => a!.index - b!.index); + + matchs.forEach((match, index) => { + switch (match) { + case hourMatch: + this.matchMap.hour = index; + regexStr = regexStr.replace(hourRegex, '(\\d{1,2})'); + break; + case minuteMatch: + this.matchMap.minute = index; + regexStr = regexStr.replace(minuteRegex, '(\\d{1,2})'); + break; + case secondMatch: + this.matchMap.second = index; + regexStr = regexStr.replace(secondRegex, '(\\d{1,2})'); + break; + case periodNarrowMatch: + this.matchMap.periodNarrow = index; + const periodsNarrow = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Narrow).join('|'); + regexStr = regexStr.replace(periodNarrow, `(${periodsNarrow})`); + break; + case periodWideMatch: + this.matchMap.periodWide = index; + const periodsWide = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Wide).join('|'); + regexStr = regexStr.replace(periodWide, `(${periodsWide})`); + break; + case periodAbbreviatedMatch: + this.matchMap.periodAbbreviated = index; + const periodsAbbreviated = getLocaleDayPeriods(this.localeId, FormStyle.Format, TranslationWidth.Abbreviated).join('|'); + regexStr = regexStr.replace(periodAbbreviated, `(${periodsAbbreviated})`); + break; + } + }); + + this.regex = new RegExp(regexStr); + } +} diff --git a/components/date-picker/picker.component.ts b/components/date-picker/picker.component.ts index 10ba79307ee..547fab9ba07 100644 --- a/components/date-picker/picker.component.ts +++ b/components/date-picker/picker.component.ts @@ -36,6 +36,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 { NgStyleInterface, NzSafeAny } from 'ng-zorro-antd/core/types'; import { DateHelperService } from 'ng-zorro-antd/i18n'; @@ -372,7 +373,7 @@ export class NzPickerComponent implements OnInit, AfterViewInit, OnChanges, OnDe } onOverlayKeydown(event: KeyboardEvent): void { - if (event.key === 'Escape') { + if (event.keyCode === ESCAPE) { this.datePickerService.setValue(this.datePickerService.initialValue!); } } diff --git a/components/i18n/date-helper.service.spec.ts b/components/i18n/date-helper.service.spec.ts index df9343163f4..6cda6791ab7 100644 --- a/components/i18n/date-helper.service.spec.ts +++ b/components/i18n/date-helper.service.spec.ts @@ -37,8 +37,8 @@ describe('DateHelperService', () => { }); it('should do parseTime correctly', () => { - expect(dateHelper.parseTime('14:00')?.toTimeString().substr(0, 8)).toBe('14:00:00'); - expect(dateHelper.parseTime('4:00')?.toTimeString().substr(0, 8)).toBe('04:00:00'); + expect(dateHelper.parseTime('14:00', 'HH:mm')?.toTimeString().substr(0, 5)).toBe('14:00'); + expect(dateHelper.parseTime('4:00', 'H:mm')?.toTimeString().substr(0, 5)).toBe('04:00'); }); }); diff --git a/components/i18n/date-helper.service.ts b/components/i18n/date-helper.service.ts index 9a2550fd27e..add6ad7e02b 100644 --- a/components/i18n/date-helper.service.ts +++ b/components/i18n/date-helper.service.ts @@ -9,7 +9,7 @@ import fnsFormat from 'date-fns/format'; import fnsGetISOWeek from 'date-fns/getISOWeek'; import fnsParse from 'date-fns/parse'; -import { WeekDayIndex } from 'ng-zorro-antd/core/time'; +import { WeekDayIndex, ɵNgTimeParser } from 'ng-zorro-antd/core/time'; import { mergeDateConfig, NzDateConfig, NZ_DATE_CONFIG } from './date-config'; import { NzI18nService } from './nz-i18n.service'; @@ -34,7 +34,7 @@ export abstract class DateHelperService { abstract getISOWeek(date: Date): number; abstract getFirstDayOfWeek(): WeekDayIndex; - abstract format(date: Date, formatStr: string): string; + abstract format(date: Date | null, formatStr: string): string; abstract parseDate(text: string, formatStr?: string): Date; abstract parseTime(text: string, formatStr?: string): Date | undefined; } @@ -108,10 +108,8 @@ export class DateHelperByDatePipe extends DateHelperService { return new Date(text); } - parseTime(text: string): Date | undefined { - if (!text) { - return; - } - return new Date(Date.parse(`1970-01-01 ${text}`)); + parseTime(text: string, formatStr: string): Date { + const parser = new ɵNgTimeParser(formatStr, this.i18n.getLocaleId()); + return parser.toDate(text); } } diff --git a/components/time-picker/demo/use12-hours.ts b/components/time-picker/demo/use12-hours.ts index 76243c29c54..3c9573124ed 100644 --- a/components/time-picker/demo/use12-hours.ts +++ b/components/time-picker/demo/use12-hours.ts @@ -3,8 +3,8 @@ import { Component } from '@angular/core'; @Component({ selector: 'nz-demo-time-picker-use12-hours', template: ` - - + + `, styles: [ ` @@ -16,4 +16,8 @@ import { Component } from '@angular/core'; }) export class NzDemoTimePickerUse12HoursComponent { time: Date | null = null; + + log(value: Date): void { + console.log(value); + } } diff --git a/components/time-picker/public-api.ts b/components/time-picker/public-api.ts index 5ee40446d1e..b1a09b577a3 100644 --- a/components/time-picker/public-api.ts +++ b/components/time-picker/public-api.ts @@ -6,4 +6,3 @@ export * from './time-picker.component'; export * from './time-picker.module'; export * from './time-picker-panel.component'; -export * from './time-value-accessor.directive'; diff --git a/components/time-picker/time-picker-panel.component.ts b/components/time-picker/time-picker-panel.component.ts index 140a6766130..ef58ab6cd85 100644 --- a/components/time-picker/time-picker-panel.component.ts +++ b/components/time-picker/time-picker-panel.component.ts @@ -27,9 +27,7 @@ import { InputBoolean, isNotNil } from 'ng-zorro-antd/core/util'; import { DateHelperService } from 'ng-zorro-antd/i18n'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; - import { TimeHolder } from './time-holder'; -import { NzTimeValueAccessorDirective } from './time-value-accessor.directive'; 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); @@ -109,6 +107,11 @@ export type NzTimePickerUnit = 'hour' | 'minute' | 'second' | '12-hour'; {{ 'Calendar.lang.now' | nzI18n }} +
  • + +
  • `, @@ -148,8 +151,6 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, secondRange!: ReadonlyArray<{ index: number; disabled: boolean }>; use12HoursRange!: ReadonlyArray<{ index: number; value: string }>; - @ViewChild(NzTimeValueAccessorDirective, { static: false }) - nzTimeValueAccessorDirective?: NzTimeValueAccessorDirective; @ViewChild('hourListElement', { static: false }) hourListElement?: DebugElement; @ViewChild('minuteListElement', { static: false }) minuteListElement?: DebugElement; @@ -277,14 +278,6 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, return this._nzSecondStep; } - selectInputRange(): void { - setTimeout(() => { - if (this.nzTimeValueAccessorDirective) { - this.nzTimeValueAccessorDirective.setRange(); - } - }); - } - buildHours(): void { let hourRanges = 24; let disabledHours = this.nzDisabledHours?.(); @@ -501,6 +494,10 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, this.closePanel.emit(); } + onClickOk(): void { + this.closePanel.emit(); + } + isSelectedHour(hour: { index: number; disabled: boolean }): boolean { return hour.index === this.time.viewHours; } @@ -523,9 +520,9 @@ export class NzTimePickerPanelComponent implements ControlValueAccessor, OnInit, this.time.changes.pipe(takeUntil(this.unsubscribe$)).subscribe(() => { this.changed(); this.touched(); + this.scrollToTime(120); }); this.buildTimes(); - this.selectInputRange(); setTimeout(() => { this.scrollToTime(); this.firstScrolled = true; diff --git a/components/time-picker/time-picker.component.spec.ts b/components/time-picker/time-picker.component.spec.ts index 699bd593926..9f14723b7a0 100644 --- a/components/time-picker/time-picker.component.spec.ts +++ b/components/time-picker/time-picker.component.spec.ts @@ -5,6 +5,7 @@ import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { dispatchMouseEvent } from 'ng-zorro-antd/core/testing'; +import { getPickerInput } from 'ng-zorro-antd/date-picker/testing/util'; import { NzI18nModule } from '../i18n/nz-i18n.module'; import { NzTimePickerComponent } from './time-picker.component'; import { NzTimePickerModule } from './time-picker.module'; @@ -94,7 +95,7 @@ describe('time-picker', () => { testComponent.date = new Date('2018-11-11 11:11:11'); fixture.detectChanges(); tick(500); - testComponent.nzTimePickerComponent.cdr.detectChanges(); + fixture.detectChanges(); timeElement.nativeElement.querySelector('.ant-picker-clear').click(); fixture.detectChanges(); expect(testComponent.date).toBeNull(); @@ -118,6 +119,8 @@ describe('time-picker', () => { dispatchMouseEvent(overlayContainer.getContainerElement().querySelector('.ant-picker-time-panel-cell')!, 'click'); fixture.detectChanges(); + getPickerInput(fixture.debugElement).dispatchEvent(new KeyboardEvent('keyup', { key: 'enter' })); + fixture.detectChanges(); tick(500); fixture.detectChanges(); expect(nzOnChange).toHaveBeenCalled(); diff --git a/components/time-picker/time-picker.component.ts b/components/time-picker/time-picker.component.ts index 76dd59b3ef0..49fa30cce3c 100644 --- a/components/time-picker/time-picker.component.ts +++ b/components/time-picker/time-picker.component.ts @@ -22,12 +22,14 @@ import { ViewEncapsulation } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { isValid } from 'date-fns'; import { slideMotion } from 'ng-zorro-antd/core/animation'; import { NzConfigKey, NzConfigService, WithConfig } from 'ng-zorro-antd/core/config'; import { warn } from 'ng-zorro-antd/core/logger'; import { BooleanInput, NzSafeAny } from 'ng-zorro-antd/core/types'; import { InputBoolean, isNil } from 'ng-zorro-antd/core/util'; +import { DateHelperService } from 'ng-zorro-antd/i18n'; const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'timePicker'; @@ -42,12 +44,14 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'timePicker'; #inputElement type="text" [size]="inputSize" - [nzTime]="nzFormat" [placeholder]="nzPlaceHolder || ('TimePicker.placeholder' | nzI18n)" - [(ngModel)]="value" + [(ngModel)]="inputValue" [disabled]="nzDisabled" (focus)="onFocus(true)" (blur)="onFocus(false)" + (keyup.enter)="onKeyupEnter()" + (keyup.escape)="onKeyupEsc()" + (ngModelChange)="onInputChange($event)" /> @@ -69,7 +73,7 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'timePicker'; [cdkConnectedOverlayOffsetY]="-2" [cdkConnectedOverlayTransformOriginOn]="'.ant-picker-dropdown'" (detach)="close()" - (backdropClick)="close()" + (backdropClick)="setCurrentValueAndClose()" >
    @@ -91,8 +95,8 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'timePicker'; [nzClearText]="nzClearText" [nzAllowEmpty]="nzAllowEmpty" [(ngModel)]="value" - (ngModelChange)="setValue($event)" - (closePanel)="close()" + (ngModelChange)="onPanelValueChange($event)" + (closePanel)="setCurrentValueAndClose()" >
    @@ -123,7 +127,9 @@ export class NzTimePickerComponent implements ControlValueAccessor, OnInit, Afte private _onTouched?: () => void; isInit = false; focused = false; + inputValue: string = ''; value: Date | null = null; + preValue: Date | null = null; origin!: CdkOverlayOrigin; inputSize?: number; overlayPositions: ConnectionPositionPair[] = [ @@ -161,18 +167,29 @@ export class NzTimePickerComponent implements ControlValueAccessor, OnInit, Afte @Input() @InputBoolean() nzDisabled = false; @Input() @InputBoolean() nzAutoFocus = false; - setValue(value: Date | null): void { - this.value = value ? new Date(value) : null; + emitValue(value: Date | null): void { + this.setValue(value, true); + if (this._onChange) { this._onChange(this.value); } + if (this._onTouched) { this._onTouched(); } } + setValue(value: Date | null, syncPreValue: boolean = false): void { + if (syncPreValue) { + this.preValue = isValid(value) ? new Date(value!) : null; + } + this.value = isValid(value) ? new Date(value!) : null; + this.inputValue = this.dateHelper.format(value, this.nzFormat); + this.cdr.markForCheck(); + } + open(): void { - if (this.nzDisabled) { + if (this.nzDisabled || this.nzOpen) { return; } this.focus(); @@ -198,7 +215,7 @@ export class NzTimePickerComponent implements ControlValueAccessor, OnInit, Afte onClickClearBtn(event: MouseEvent): void { event.stopPropagation(); - this.setValue(null); + this.emitValue(null); } onFocus(value: boolean): void { @@ -217,11 +234,39 @@ export class NzTimePickerComponent implements ControlValueAccessor, OnInit, Afte } } + onKeyupEsc(): void { + this.setValue(this.preValue); + } + + onKeyupEnter(): void { + if (this.nzOpen && isValid(this.value)) { + this.setCurrentValueAndClose(); + } else if (!this.nzOpen) { + this.open(); + } + } + + onInputChange(str: string): void { + this.open(); + this.parseTimeString(str); + } + + onPanelValueChange(value: Date): void { + this.setValue(value); + this.focus(); + } + + setCurrentValueAndClose(): void { + this.emitValue(this.value); + this.close(); + } + constructor( public nzConfigService: NzConfigService, private element: ElementRef, private renderer: Renderer2, - public cdr: ChangeDetectorRef + private cdr: ChangeDetectorRef, + private dateHelper: DateHelperService ) {} ngOnInit(): void { @@ -248,21 +293,32 @@ export class NzTimePickerComponent implements ControlValueAccessor, OnInit, Afte } } + parseTimeString(str: string): void { + const value = this.dateHelper.parseTime(str, this.nzFormat) || null; + if (isValid(value)) { + this.value = value; + this.cdr.markForCheck(); + } + } + ngAfterViewInit(): void { this.isInit = true; this.updateAutoFocus(); } writeValue(time: Date | null | undefined): void { + let result: Date | null; + if (time instanceof Date) { - this.value = time; + result = time; } else if (isNil(time)) { - this.value = null; + result = null; } else { warn('Non-Date type is not recommended for time-picker, use "Date" type.'); - this.value = new Date(time); + result = new Date(time); } - this.cdr.markForCheck(); + + this.setValue(result, true); } registerOnChange(fn: (time: Date | null) => void): void { diff --git a/components/time-picker/time-picker.module.ts b/components/time-picker/time-picker.module.ts index 8b5d3ed07e6..c73ab3c89e8 100644 --- a/components/time-picker/time-picker.module.ts +++ b/components/time-picker/time-picker.module.ts @@ -7,6 +7,7 @@ import { OverlayModule } from '@angular/cdk/overlay'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { NzButtonModule } from 'ng-zorro-antd/button'; import { NzOutletModule } from 'ng-zorro-antd/core/outlet'; import { NzOverlayModule } from 'ng-zorro-antd/core/overlay'; @@ -15,11 +16,10 @@ import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzTimePickerPanelComponent } from './time-picker-panel.component'; import { NzTimePickerComponent } from './time-picker.component'; -import { NzTimeValueAccessorDirective } from './time-value-accessor.directive'; @NgModule({ - declarations: [NzTimePickerComponent, NzTimePickerPanelComponent, NzTimeValueAccessorDirective], + declarations: [NzTimePickerComponent, NzTimePickerPanelComponent], exports: [NzTimePickerPanelComponent, NzTimePickerComponent], - imports: [CommonModule, FormsModule, NzI18nModule, OverlayModule, NzIconModule, NzOverlayModule, NzOutletModule] + imports: [CommonModule, FormsModule, NzI18nModule, OverlayModule, NzIconModule, NzOverlayModule, NzOutletModule, NzButtonModule] }) export class NzTimePickerModule {} diff --git a/components/time-picker/time-value-accessor.directive.spec.ts b/components/time-picker/time-value-accessor.directive.spec.ts index e27dea3205e..e69de29bb2d 100644 --- a/components/time-picker/time-value-accessor.directive.spec.ts +++ b/components/time-picker/time-value-accessor.directive.spec.ts @@ -1,78 +0,0 @@ -import { registerLocaleData } from '@angular/common'; -import zh from '@angular/common/locales/zh'; -import { Component, DebugElement, ViewChild } from '@angular/core'; -import { ComponentFixture, fakeAsync, flush, TestBed, waitForAsync } from '@angular/core/testing'; -import { FormsModule } from '@angular/forms'; -import { By } from '@angular/platform-browser'; - -import { dispatchFakeEvent } from 'ng-zorro-antd/core/testing'; -import { NzI18nModule } from '../i18n/nz-i18n.module'; - -import { NzTimeValueAccessorDirective } from './time-value-accessor.directive'; - -registerLocaleData(zh); - -describe('input-time', () => { - beforeEach( - waitForAsync(() => { - TestBed.configureTestingModule({ - imports: [FormsModule, NzI18nModule], - declarations: [NzTimeValueAccessorDirective, NzTestTimeInputComponent] - }); - TestBed.compileComponents(); - }) - ); - - describe('basic input-time', () => { - let fixture: ComponentFixture; - let testComponent: NzTestTimeInputComponent; - let inputElement: DebugElement; - - beforeEach(() => { - fixture = TestBed.createComponent(NzTestTimeInputComponent); - testComponent = fixture.debugElement.componentInstance; - fixture.detectChanges(); - inputElement = fixture.debugElement.query(By.directive(NzTimeValueAccessorDirective)); - }); - - it('should nzFormat correct', fakeAsync(() => { - fixture.detectChanges(); - testComponent.value = new Date(0, 0, 0, 0, 0, 0); - flush(); - fixture.detectChanges(); - flush(); - expect(inputElement.nativeElement.value).toBe('00:00:00'); - })); - - it('should parse correct', fakeAsync(() => { - inputElement.nativeElement.value = '01:01:01'; - dispatchFakeEvent(inputElement.nativeElement, 'keyup'); - dispatchFakeEvent(inputElement.nativeElement, 'blur'); - fixture.detectChanges(); - flush(); - fixture.detectChanges(); - flush(); - expect(testComponent.value).toEqual(new Date(1970, 0, 1, 1, 1, 1)); - })); - - it('should focus work', fakeAsync(() => { - fixture.detectChanges(); - flush(); - fixture.detectChanges(); - flush(); - testComponent.nzTimeValueAccessorDirective.setRange(); - expect(inputElement.nativeElement === document.activeElement).toBe(true); - })); - }); -}); - -@Component({ - template: ` - - ` -}) -export class NzTestTimeInputComponent { - @ViewChild(NzTimeValueAccessorDirective, { static: false }) - nzTimeValueAccessorDirective!: NzTimeValueAccessorDirective; - value = new Date(0, 0, 0, 0, 0, 0); -} diff --git a/components/time-picker/time-value-accessor.directive.ts b/components/time-picker/time-value-accessor.directive.ts deleted file mode 100644 index 7578b4af39b..00000000000 --- a/components/time-picker/time-value-accessor.directive.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE - */ - -import { Directive, ElementRef, HostListener, Input } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; - -import { DateHelperService } from 'ng-zorro-antd/i18n'; - -@Directive({ - selector: 'input[nzTime]', - exportAs: 'nzTime', - providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: NzTimeValueAccessorDirective, multi: true }] -}) -export class NzTimeValueAccessorDirective implements ControlValueAccessor { - private _onChange?: (value: Date) => void; - private _onTouch?: () => void; - @Input() nzTime?: string; - - @HostListener('keyup') - keyup(): void { - this.changed(); - } - - @HostListener('blur') - blur(): void { - this.touched(); - } - - changed(): void { - if (this._onChange) { - const value = this.dateHelper.parseTime(this.elementRef.nativeElement.value, this.nzTime); - this._onChange(value!); - } - } - - touched(): void { - if (this._onTouch) { - this._onTouch(); - } - } - - setRange(): void { - this.elementRef.nativeElement.focus(); - this.elementRef.nativeElement.setSelectionRange(0, this.elementRef.nativeElement.value.length); - } - - constructor(private dateHelper: DateHelperService, private elementRef: ElementRef) {} - - writeValue(value: Date): void { - this.elementRef.nativeElement.value = this.dateHelper.format(value, this.nzTime!); - } - - registerOnChange(fn: (value: Date) => void): void { - this._onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this._onTouch = fn; - } -}