From 7e40997fbc53ecee41235defbfd23dc1871f2612 Mon Sep 17 00:00:00 2001 From: Stefana Andreeva Date: Thu, 30 May 2019 11:48:43 +0300 Subject: [PATCH] feat(time picker): add hour/minute mode #4679 --- .../src/lib/directives/mask/mask.directive.ts | 14 +- .../src/lib/time-picker/time-picker.common.ts | 4 +- .../time-picker/time-picker.component.html | 6 +- .../time-picker/time-picker.component.spec.ts | 241 +++++++++++++++++- .../lib/time-picker/time-picker.component.ts | 191 +++++++++----- .../lib/time-picker/time-picker.directives.ts | 22 +- .../src/lib/time-picker/time-picker.pipes.ts | 72 ++++-- src/app/time-picker/time-picker.sample.html | 9 + src/app/time-picker/time-picker.sample.ts | 1 + 9 files changed, 476 insertions(+), 84 deletions(-) diff --git a/projects/igniteui-angular/src/lib/directives/mask/mask.directive.ts b/projects/igniteui-angular/src/lib/directives/mask/mask.directive.ts index a0210f6a02d..3191d08be2e 100644 --- a/projects/igniteui-angular/src/lib/directives/mask/mask.directive.ts +++ b/projects/igniteui-angular/src/lib/directives/mask/mask.directive.ts @@ -60,7 +60,14 @@ export class IgxMaskDirective implements OnInit, ControlValueAccessor { * @memberof IgxMaskDirective */ @Input() - public placeholder: string; + public set placeholder(val: string) { + this._placeholder = val; + this.nativeElement.setAttribute('placeholder', this._placeholder); + } + + public get placeholder(): string { + return this._placeholder; + } /** * Specifies a pipe to be used on blur. @@ -148,6 +155,11 @@ export class IgxMaskDirective implements OnInit, ControlValueAccessor { */ private _selection: number; + /** + *@hidden + */ + private _placeholder: string; + /** *@hidden */ diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.common.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.common.ts index 27c1754834e..4c8d3e862e2 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.common.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.common.ts @@ -6,7 +6,6 @@ export const IGX_TIME_PICKER_COMPONENT = 'IgxTimePickerComponentToken'; /** @hidden */ export interface IgxTimePickerBase { - _ampmItems: any[]; hourList: ElementRef; minuteList: ElementRef; ampmList: ElementRef; @@ -17,6 +16,9 @@ export interface IgxTimePickerBase { promptChar: string; cleared: boolean; mode: InteractionMode; + showHoursList: boolean; + showMinutesList: boolean; + showAmPmList: boolean; nextHour(); prevHour(); nextMinute(); diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.html b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.html index e148357a6fe..bd4a342d700 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.html +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.html @@ -34,13 +34,13 @@

-
+
{{ hour }}
-
+
{{ minute }}
-
+
{{ ampm }}
diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts index 680d1845176..e3ff9db8fdf 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts @@ -616,7 +616,7 @@ describe('IgxTimePicker', () => { const selectHour = hourColumn.children[3]; const AMPMColumn: any = dom.query(By.css('.igx-time-picker__ampmList')); - expect(AMPMColumn.children.length).toBe(0); + expect(AMPMColumn).toBeNull(); expect(selectHour.nativeElement.innerText).toBe('00'); })); @@ -1446,6 +1446,242 @@ describe('IgxTimePicker', () => { expect(dropdownClientRect.left).toEqual(inputGroupClientRect.left); })); }); + + describe('Hour/minute only mode', () => { + configureTestSuite(); + let fixture, timePicker, dom, input; + + beforeEach( + async(() => { + fixture = TestBed.createComponent(IgxTimePickerDropDownSingleHourComponent); + fixture.detectChanges(); + + timePicker = fixture.componentInstance.timePicker; + dom = fixture.debugElement; + input = dom.query(By.directive(IgxInputDirective)); + }) + ); + + afterEach(async(() => { + UIInteractions.clearOverlay(); + })); + + it('Should render dropdown and input group correctly when format conatains only hours.', fakeAsync(() => { + fixture.componentInstance.format = 'hh tt'; + fixture.componentInstance.customDate = new Date(2018, 10, 27, 17, 45, 0, 0); + fixture.detectChanges(); + + const iconTime = dom.queryAll(By.css('.igx-icon'))[0]; + + UIInteractions.clickElement(iconTime); + tick(); + fixture.detectChanges(); + + const hourColumn = dom.query(By.css('.igx-time-picker__hourList')); + const minuteColumn = dom.query(By.css('.igx-time-picker__minuteList')); + const ampmColumn = dom.query(By.css('.igx-time-picker__ampmList')); + + expect(hourColumn).not.toBeNull(); + expect(ampmColumn).not.toBeNull(); + expect(minuteColumn).toBeNull(); + })); + + it('Should mask editable input correctly when format conatains only hours.', fakeAsync(() => { + fixture.componentInstance.format = 'hh tt'; + fixture.componentInstance.customDate = new Date(2018, 10, 27, 17, 45, 0, 0); + fixture.detectChanges(); + + const clearTime = dom.queryAll(By.css('.igx-icon'))[1]; + + UIInteractions.clickElement(clearTime); + fixture.detectChanges(); + input.nativeElement.dispatchEvent(new Event('focus')); + fixture.detectChanges(); + + input.nativeElement.dispatchEvent(new Event('blur')); + fixture.detectChanges(); + + expect(input.nativeElement.value).toEqual(''); + expect(input.nativeElement.placeholder).toEqual('hh tt'); + + input.nativeElement.dispatchEvent(new Event('focus')); + fixture.detectChanges(); + + expect(input.nativeElement.value).toBe('-- AM'); + })); + + it('Should navigate dropdown lists correctly when format conatains only hours.', fakeAsync(() => { + fixture.componentInstance.format = 'hh tt'; + fixture.componentInstance.customDate = new Date(2018, 10, 27, 17, 45, 0, 0); + fixture.detectChanges(); + + const iconTime = dom.queryAll(By.css('.igx-icon'))[0]; + + UIInteractions.clickElement(iconTime); + tick(); + fixture.detectChanges(); + + dom.query(By.css('.igx-time-picker__hourList')).nativeElement.focus(); + fixture.detectChanges(); + + expect(document.activeElement.classList).toContain('igx-time-picker__hourList'); + + document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })); + fixture.detectChanges(); + + expect(document.activeElement.classList).toContain('igx-time-picker__ampmList'); + + document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true })); + document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', bubbles: true })); + document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); + tick(); + fixture.detectChanges(); + + expect(input.nativeElement.value).toEqual('04 PM'); + expect(timePicker.value).toEqual(new Date(2018, 10, 27, 16, 45, 0, 0)); + })); + + it('Should navigate dropdown lists correctly when format conatains only minutes.', fakeAsync(() => { + fixture.componentInstance.format = 'mm tt'; + fixture.componentInstance.customDate = new Date(2018, 10, 27, 17, 45, 0, 0); + fixture.detectChanges(); + + const iconTime = dom.queryAll(By.css('.igx-icon'))[0]; + + UIInteractions.clickElement(iconTime); + tick(); + fixture.detectChanges(); + + dom.query(By.css('.igx-time-picker__minuteList')).nativeElement.focus(); + fixture.detectChanges(); + + expect(document.activeElement.classList).toContain('igx-time-picker__minuteList'); + + document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })); + fixture.detectChanges(); + + expect(document.activeElement.classList).toContain('igx-time-picker__ampmList'); + + document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true })); + document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })); + document.activeElement.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); + tick(); + fixture.detectChanges(); + + expect(input.nativeElement.value).toEqual('46 PM'); + expect(timePicker.value).toEqual(new Date(2018, 10, 27, 17, 46, 0, 0)); + })); + + it('Should spin editable input correctly when format conatains only hours - 24 hour format.', fakeAsync(() => { + fixture.componentInstance.format = 'HH'; + fixture.componentInstance.customDate = new Date(2018, 10, 27, 17, 45, 0, 0); + fixture.detectChanges(); + + expect(input.nativeElement.value).toBe('17'); + + input.nativeElement.focus(); + input.nativeElement.setSelectionRange(0, 0); + fixture.detectChanges(); + + UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', input.nativeElement, true); + fixture.detectChanges(); + + expect(input.nativeElement.value).toBe('18'); + expect(timePicker.value).toEqual(new Date(2018, 10, 27, 18, 45, 0, 0)); + })); + + it('Should spin editable input correctly when format conatains only minutes.', fakeAsync(() => { + fixture.componentInstance.format = 'mm tt'; + fixture.componentInstance.customDate = new Date(2018, 10, 27, 17, 45, 0, 0); + fixture.detectChanges(); + + expect(input.nativeElement.value).toBe('45 PM'); + + input.nativeElement.setSelectionRange(0, 0); + tick(); + fixture.detectChanges(); + + UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', input.nativeElement, true); + fixture.detectChanges(); + + expect(input.nativeElement.value).toBe('46 PM'); + expect(timePicker.value).toEqual(new Date(2018, 10, 27, 17, 46, 0, 0)); + })); + + it('Should spin editable input AM/PM correctly when format conatains only hours.', fakeAsync(() => { + fixture.componentInstance.format = 'hh tt'; + fixture.componentInstance.customDate = new Date(2018, 10, 27, 17, 45, 0, 0); + fixture.detectChanges(); + + expect(input.nativeElement.value).toBe('05 PM'); + + input.nativeElement.setSelectionRange(3, 3); + fixture.detectChanges(); + + UIInteractions.triggerKeyDownEvtUponElem('ArrowDown', input.nativeElement, true); + fixture.detectChanges(); + + expect(input.nativeElement.value).toBe('05 AM'); + expect(timePicker.value).toEqual(new Date(2018, 10, 27, 5, 45, 0, 0)); + })); + + it('Should render dialog and input group correctly when format conatains only minutes.', fakeAsync(() => { + fixture.componentInstance.format = 'mm'; + fixture.componentInstance.mode = InteractionMode.Dialog; + fixture.detectChanges(); + + input = dom.query(By.directive(IgxInputDirective)); + UIInteractions.clickElement(input); + fixture.detectChanges(); + + const hourColumn = dom.query(By.css('.igx-time-picker__hourList')); + const minuteColumn = dom.query(By.css('.igx-time-picker__minuteList')); + const ampmColumn = dom.query(By.css('.igx-time-picker__ampmList')); + + expect(hourColumn).toBeNull(); + expect(ampmColumn).toBeNull(); + expect(minuteColumn).not.toBeNull(); + + expect(input.nativeElement.value).toEqual('05'); + expect(timePicker.mask).toEqual('00'); + expect(timePicker.value).toEqual(fixture.componentInstance.customDate); + + const headerHour = dom.query(By.css('.igx-time-picker__header-hour')); + const headerAmPm = dom.query(By.css('.igx-time-picker__header-ampm')); + + expect(headerHour.nativeElement.innerText.replace(/\n/g, '')).toEqual('4:05'); + expect(headerAmPm.nativeElement.innerText).toEqual(''); + })); + + it('Should render dialog and input group correctly when format conatains only hours.', fakeAsync(() => { + fixture.componentInstance.format = 'hh tt'; + fixture.componentInstance.mode = InteractionMode.Dialog; + fixture.detectChanges(); + + input = dom.query(By.directive(IgxInputDirective)); + UIInteractions.clickElement(input); + tick(); + fixture.detectChanges(); + + const hourColumn = dom.query(By.css('.igx-time-picker__hourList')); + const minuteColumn = dom.query(By.css('.igx-time-picker__minuteList')); + const ampmColumn = dom.query(By.css('.igx-time-picker__ampmList')); + + expect(hourColumn).not.toBeNull(); + expect(ampmColumn).not.toBeNull(); + expect(minuteColumn).toBeNull(); + + expect(input.nativeElement.value).toEqual('04 AM'); + expect(timePicker.mask).toEqual('00 LL'); + expect(timePicker.value).toEqual(fixture.componentInstance.customDate); + + const headerHour = dom.query(By.css('.igx-time-picker__header-hour')); + const headerAmPm = dom.query(By.css('.igx-time-picker__header-ampm')); + + expect(headerHour.nativeElement.innerText.replace(/\n/g, '')).toEqual('04:5'); + expect(headerAmPm.nativeElement.innerText).toEqual('AM'); + })); + }); }); @Component({ @@ -1579,13 +1815,14 @@ export class IgxTimePickerDropDownComponent { + [format]="format"> ` }) export class IgxTimePickerDropDownSingleHourComponent { customDate = new Date(2018, 10, 27, 4, 5); mode = InteractionMode.DropDown; + format = 'H:m'; @ViewChild(IgxTimePickerComponent) public timePicker: IgxTimePickerComponent; } diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts index 621a2d82dd1..4c7e670dfdc 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts @@ -302,6 +302,10 @@ export class IgxTimePickerComponent implements this._format = formatValue; this.mask = this._format.indexOf('tt') !== -1 ? '00:00 LL' : '00:00'; + if (!this.showHoursList || !this.showMinutesList) { + this.mask = this.mask.slice(this.mask.indexOf(':') + 1, this.mask.length); + } + if (this.displayValue) { this.displayValue = this._formatTime(this.value, this._format); } @@ -621,6 +625,27 @@ export class IgxTimePickerComponent implements return (this.displayValue && this.displayValue !== this.parseMask(false)) || this.isNotEmpty; } + /** + * @hidden + */ + get showHoursList(): boolean { + return this.format.indexOf('h') !== - 1 || this.format.indexOf('H') !== - 1; + } + + /** + * @hidden + */ + get showMinutesList(): boolean { + return this.format.indexOf('m') !== - 1; + } + + /** + * @hidden + */ + get showAmPmList(): boolean { + return this.format.indexOf('t') !== - 1; + } + /** * @hidden */ @@ -876,14 +901,12 @@ export class IgxTimePickerComponent implements return ''; } else { let hour = value.getHours(); + let formattedMinute, formattedHour; + const minute = value.getMinutes(); - let formattedMinute; - let formattedHour; - let amPM; + const amPM =(hour > 11) ? 'PM' : 'AM'; if (format.indexOf('h') !== -1) { - amPM = (hour > 11) ? 'PM' : 'AM'; - if (hour > 12) { hour -= 12; formattedHour = hour < 10 && format.indexOf('hh') !== -1 ? '0' + hour : `${hour}`; @@ -986,12 +1009,22 @@ export class IgxTimePickerComponent implements private _getSelectedTime(): Date { const date = this.value ? new Date(this.value) : new Date(); - date.setHours(parseInt(this.selectedHour, 10)); - date.setMinutes(parseInt(this.selectedMinute, 10)); + if (this.selectedHour) { + date.setHours(parseInt(this.selectedHour, 10)); + } + if (this.selectedMinute) { + date.setMinutes(parseInt(this.selectedMinute, 10)); + } date.setSeconds(0); - if (this.selectedAmPm === 'PM' && this.selectedHour !== '12') { + if (this.showHoursList && this.selectedAmPm === 'PM' && this.selectedHour !== '12') { date.setHours(date.getHours() + 12); } + if(!this.showHoursList && this.selectedAmPm === 'PM' && this.selectedHour <= '11') { + date.setHours(date.getHours() + 12); + } + if (!this.showHoursList && this.selectedAmPm === 'AM' && this.selectedHour > '11') { + date.setHours(date.getHours() - 12); + } if (this.selectedAmPm === 'AM' && this.selectedHour === '12') { date.setHours(0); } @@ -1001,15 +1034,37 @@ export class IgxTimePickerComponent implements private _convertMinMaxValue(value: string): Date { const date = this.value ? new Date(this.value) : this._dateFromModel ? new Date(this._dateFromModel) : new Date(); const sections = value.split(/[\s:]+/); + let hour, minutes, amPM; - date.setHours(parseInt(sections[0], 10)); - date.setMinutes(parseInt(sections[1], 10)); date.setSeconds(0); - if (sections[2] && sections[2] === 'PM' && sections[0] !== '12') { - date.setHours(date.getHours() + 12); + + if (this.showHoursList) { + hour = sections[0]; + date.setHours(parseInt(hour, 10)); } - if (sections[0] === '12' && sections[2] && sections[2] === 'AM') { - date.setHours(0); + + if (this.showMinutesList) { + minutes = this.showHoursList ? sections[1] : sections[0]; + date.setMinutes(parseInt(minutes, 10)); + } + + if (this.showAmPmList) { + amPM = sections[sections.length - 1]; + + if (this.showHoursList && amPM === 'PM' && date.getHours().toString() !== '12') { + date.setHours(date.getHours() + 12); + } + + if(!this.showHoursList && amPM === 'PM' && date.getHours().toString() <= '11') { + date.setHours(date.getHours() + 12); + } + if (!this.showHoursList && amPM === 'AM' && date.getHours().toString() > '11') { + date.setHours(date.getHours() - 12); + } + + if (this.showHoursList && date.getHours() === 12 && amPM === 'AM') { + date.setHours(0); + } } return date; @@ -1026,13 +1081,22 @@ export class IgxTimePickerComponent implements } private _isEntryValid(val: string): boolean { + let validH = true; + let validM = true; + const sections = val.split(/[\s:]+/); const re = new RegExp(this.promptChar, 'g'); - const hour = parseInt(sections[0].replace(re, ''), 10); - const minutes = parseInt(sections[1].replace(re, ''), 10); + if (this.showHoursList) { + validH = this.validHourEntries.indexOf(parseInt(sections[0].replace(re, ''), 10)) !== -1; + } + + if (this.showMinutesList) { + const minutes = this.showHoursList ? sections[1] : sections[0]; + validM = this.validMinuteEntries.indexOf(parseInt(minutes.replace(re, ''), 10)) !== -1; + } - return this.validHourEntries.indexOf(hour) !== -1 && this.validMinuteEntries.indexOf(minutes) !== -1; + return validH && validM; } private _getCursorPosition(): number { @@ -1095,19 +1159,24 @@ export class IgxTimePickerComponent implements const formttedTime = this._formatTime(this.value, this.format); const sections = formttedTime.split(/[\s:]+/); - this.selectedHour = sections[0]; - this.selectedMinute = sections[1]; + if (this.showHoursList) { + this.selectedHour = sections[0]; + } + + if (this.showMinutesList) { + this.selectedMinute = this.showHoursList ? sections[1] : sections[0]; + } - if (this._ampmItems !== null) { - this.selectedAmPm = sections[2]; + if (this.showAmPmList && this._ampmItems !== null) { + this.selectedAmPm = sections[sections.length - 1]; } } if (this.selectedHour === undefined) { - this.selectedHour = `${this._hourItems[3]}`; + this.selectedHour = !this.showHoursList && this.value ? this.value.getHours().toString() : `${this._hourItems[3]}`; } if (this.selectedMinute === undefined) { - this.selectedMinute = '0'; + this.selectedMinute = !this.showMinutesList && this.value ? this.value.getMinutes().toString() : '0'; } if (this.selectedAmPm === undefined && this._ampmItems !== null) { this.selectedAmPm = this._ampmItems[3]; @@ -1134,15 +1203,14 @@ export class IgxTimePickerComponent implements } requestAnimationFrame(() => { - this.hourList.nativeElement.focus(); + if (this.hourList) { + this.hourList.nativeElement.focus(); + } else if (this.minuteList) { + this.minuteList.nativeElement.focus(); + } }); } - private _closeDropDown() { - this.toggleRef.close(); - this._onDropDownClosed(); - } - private _onDropDownClosed(): void { const oldValue = this.value; const newVal = this._convertMinMaxValue(this.displayValue); @@ -1234,7 +1302,7 @@ export class IgxTimePickerComponent implements this._initializeContainer(); } else if (this.mode === InteractionMode.DropDown) { - this._closeDropDown(); + this.hideOverlay(); } } @@ -1251,11 +1319,13 @@ export class IgxTimePickerComponent implements *@param item to be scrolled in view. */ public scrollHourIntoView(item: string): void { - const hourIntoView = this._scrollItemIntoView(item, this._hourItems, this.selectedHour, this._isHourListLoop, 'hour'); - if (hourIntoView) { - this._hourView = hourIntoView.view; - this.selectedHour = hourIntoView.selectedItem; - this._updateEditableInput(); + if (this.showHoursList) { + const hourIntoView = this._scrollItemIntoView(item, this._hourItems, this.selectedHour, this._isHourListLoop, 'hour'); + if (hourIntoView) { + this._hourView = hourIntoView.view; + this.selectedHour = hourIntoView.selectedItem; + this._updateEditableInput(); + } } } @@ -1272,11 +1342,13 @@ export class IgxTimePickerComponent implements * @param item to be scrolled in view. */ public scrollMinuteIntoView(item: string): void { - const minuteIntoView = this._scrollItemIntoView(item, this._minuteItems, this.selectedMinute, this._isMinuteListLoop, 'minute'); - if (minuteIntoView) { - this._minuteView = minuteIntoView.view; - this.selectedMinute = minuteIntoView.selectedItem; - this._updateEditableInput(); + if (this.showMinutesList) { + const minuteIntoView = this._scrollItemIntoView(item, this._minuteItems, this.selectedMinute, this._isMinuteListLoop, 'minute'); + if (minuteIntoView) { + this._minuteView = minuteIntoView.view; + this.selectedMinute = minuteIntoView.selectedItem; + this._updateEditableInput(); + } } } @@ -1293,11 +1365,13 @@ export class IgxTimePickerComponent implements * @param item to be scrolled in view. */ public scrollAmPmIntoView(item: string): void { - const ampmIntoView = this._scrollItemIntoView(item, this._ampmItems, this.selectedAmPm, false, null); - if (ampmIntoView) { - this._ampmView = ampmIntoView.view; - this.selectedAmPm = ampmIntoView.selectedItem; - this._updateEditableInput(); + if (this.showAmPmList) { + const ampmIntoView = this._scrollItemIntoView(item, this._ampmItems, this.selectedAmPm, false, null); + if (ampmIntoView) { + this._ampmView = ampmIntoView.view; + this.selectedAmPm = ampmIntoView.selectedItem; + this._updateEditableInput(); + } } } @@ -1467,10 +1541,8 @@ export class IgxTimePickerComponent implements * @hidden */ public parseMask(preserveAmPm = true): string { - const prompts = this.promptChar + this.promptChar; - const amPm = preserveAmPm ? 'AM' : prompts; - - return this.format.indexOf('tt') !== -1 ? `${prompts}:${prompts} ${amPm}` : `${prompts}:${prompts}`; + return preserveAmPm ? this.mask.replace(new RegExp('0', 'g'), this.promptChar).replace('LL', 'AM') : + this.mask.replace(new RegExp('0', 'g'), this.promptChar).replace(new RegExp('L', 'g'), this.promptChar); } /** @@ -1602,21 +1674,26 @@ export class IgxTimePickerComponent implements } else { const hDelta = this.itemsDelta.hours * 60 + (sign * this.value.getMinutes()); const mDelta = this.itemsDelta.minutes; - const sections = this.displayValue.split(/[\s:]+/); - if (HOURS_POS.indexOf(cursor) !== -1) { + if (this.showHoursList && HOURS_POS.indexOf(cursor) !== -1) { this.value = this._spinHours(currentVal, min, max, hDelta, sign); } - if (MINUTES_POS.indexOf(cursor) !== -1) { - this.value = this._spinMinutes(currentVal, mDelta, sign); + if (this.showMinutesList && + ((this.showHoursList && MINUTES_POS.indexOf(cursor) !== -1) || (!this.showHoursList && HOURS_POS.indexOf(cursor) !== -1))) { + this.value = this._spinMinutes(currentVal, mDelta, sign); } - if (AMPM_POS.indexOf(cursor) !== -1 && this.format.indexOf('tt') !== -1) { - sign = sections[2] && sections[2] === 'AM' ? 1 : -1; - currentVal.setHours(currentVal.getHours() + (sign * 12)); + if (this.showAmPmList) { + if (((!this.showHoursList || !this.showMinutesList) && MINUTES_POS.indexOf(cursor) !== -1) || + (this.showHoursList && this.showMinutesList && AMPM_POS.indexOf(cursor) !== -1)) { + + const sections = this.displayValue.split(/[\s:]+/); + sign = sections[sections.length - 1] === 'AM' ? 1 : -1; + currentVal.setHours(currentVal.getHours() + (sign * 12)); - this.value = currentVal; + this.value = currentVal; + } } displayVal = this._formatTime(this.value, this.format); diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.directives.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.directives.ts index ad5d9f001d6..cbac41df61f 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.directives.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.directives.ts @@ -127,8 +127,14 @@ export class IgxItemListDirective { const listName = (event.target as HTMLElement).className; if (listName.indexOf('hourList') !== -1) { - this.timePicker.minuteList.nativeElement.focus(); - } else if (listName.indexOf('minuteList') !== -1 && this.timePicker._ampmItems.length !== 0) { + if (this.timePicker.minuteList) { + this.timePicker.minuteList.nativeElement.focus(); + } else if (this.timePicker.ampmList) { + this.timePicker.ampmList.nativeElement.focus(); + } + } + + if (listName.indexOf('minuteList') !== -1 && this.timePicker.ampmList) { this.timePicker.ampmList.nativeElement.focus(); } } @@ -142,10 +148,16 @@ export class IgxItemListDirective { const listName = (event.target as HTMLElement).className; - if (listName.indexOf('minuteList') !== -1) { + if (listName.indexOf('minuteList') !== -1 && this.timePicker.hourList) { this.timePicker.hourList.nativeElement.focus(); - } else if (listName.indexOf('ampmList') !== -1) { - this.timePicker.minuteList.nativeElement.focus(); + } + + if (listName.indexOf('ampmList') !== -1) { + if (this.timePicker.minuteList) { + this.timePicker.minuteList.nativeElement.focus(); + } else if (this.timePicker.hourList) { + this.timePicker.hourList.nativeElement.focus(); + } } } diff --git a/projects/igniteui-angular/src/lib/time-picker/time-picker.pipes.ts b/projects/igniteui-angular/src/lib/time-picker/time-picker.pipes.ts index bd19cda0bba..b53bf7df655 100644 --- a/projects/igniteui-angular/src/lib/time-picker/time-picker.pipes.ts +++ b/projects/igniteui-angular/src/lib/time-picker/time-picker.pipes.ts @@ -12,6 +12,7 @@ export class TimeDisplayFormatPipe implements PipeTransform { constructor(@Inject(IGX_TIME_PICKER_COMPONENT) private timePicker: IgxTimePickerBase) { } transform(value: any): string { + let hour, minutes, amPM; const maskAmPM = this.timePicker.parseMask(); const mask = this.timePicker.parseMask(false); @@ -21,9 +22,17 @@ export class TimeDisplayFormatPipe implements PipeTransform { const sections = value.split(/[\s:]+/); - let hour = sections[0]; - let minutes = sections[1]; - let amPM = sections[2]; + if (this.timePicker.showHoursList) { + hour = sections[0]; + } + + if (this.timePicker.showMinutesList) { + minutes = this.timePicker.showHoursList ? sections[1] : sections[0]; + } + + if (this.timePicker.showAmPmList) { + amPM = sections[sections.length - 1]; + } const format = this.timePicker.format; const prompt = this.timePicker.promptChar; @@ -37,13 +46,13 @@ export class TimeDisplayFormatPipe implements PipeTransform { minutes = minutes === prompt + prompt ? '00' : minutes.replace(regExp, '0'); } - if (format.indexOf('hh') === -1 && format.indexOf('HH') === -1) { + if (format.indexOf('hh') === -1 && format.indexOf('HH') === -1 && hour !== undefined) { hour = hour.indexOf(prompt) !== -1 ? hour.replace(regExp, '') : hour; const hourVal = parseInt(hour, 10); hour = !hourVal ? '0' : hourVal < 10 && hourVal !== 0 ? hour.replace('0', '') : hour; } - if (format.indexOf('mm') === -1) { + if (format.indexOf('mm') === -1 && minutes !== undefined) { minutes = minutes.indexOf(prompt) !== -1 ? minutes.replace(regExp, '') : minutes; const minutesVal = parseInt(minutes, 10); minutes = !minutesVal ? '0' : minutesVal < 10 && minutesVal !== 0 ? minutes.replace('0', '') : minutes; @@ -53,7 +62,18 @@ export class TimeDisplayFormatPipe implements PipeTransform { amPM = amPM.indexOf('p') !== -1 || amPM.indexOf('P') !== -1 ? 'PM' : 'AM'; } - return amPM ? `${hour}:${minutes} ${amPM}` : `${hour}:${minutes}`; + let result = amPM ? `${hour}:${minutes} ${amPM}` : `${hour}:${minutes}`; + + if (!hour) { + result = result.slice(result.indexOf(':') + 1, result.length); + } + + if (!minutes) { + result = result.slice(0, result.indexOf(':')); + if (amPM) { result = result + ' ' + amPM; } + } + + return result; } } @@ -70,7 +90,8 @@ export class TimeInputFormatPipe implements PipeTransform { const prompt = this.timePicker.promptChar; const regExp = new RegExp(prompt, 'g'); - let mask: string; + let mask, hour, minutes, amPM; + if (this.timePicker.cleared) { this.timePicker.cleared = false; mask = this.timePicker.parseMask(false); @@ -84,16 +105,37 @@ export class TimeInputFormatPipe implements PipeTransform { const sections = value.split(/[\s:]+/); - let hour = sections[0].replace(regExp, ''); - let minutes = sections[1].replace(regExp, ''); - const amPM = sections[2]; + if (this.timePicker.showHoursList) { + hour = sections[0]; + hour = hour.replace(regExp, ''); - const leadZeroHour = (parseInt(hour, 10) < 10 && !hour.startsWith('0')) || hour === '0'; - const leadZeroMinutes = (parseInt(minutes, 10) < 10 && !minutes.startsWith('0')) || minutes === '0'; + const leadZeroHour = (parseInt(hour, 10) < 10 && !hour.startsWith('0')) || hour === '0'; + hour = leadZeroHour ? '0' + hour : hour; + } + + if (this.timePicker.showMinutesList) { + minutes = this.timePicker.showHoursList ? sections[1] : sections[0]; + minutes = minutes.replace(regExp, ''); + + const leadZeroMinutes = (parseInt(minutes, 10) < 10 && !minutes.startsWith('0')) || minutes === '0'; + minutes = leadZeroMinutes ? '0' + minutes : minutes; + } + + if (this.timePicker.showAmPmList) { + amPM = sections[sections.length - 1]; + } + + let result = amPM ? `${hour}:${minutes} ${amPM}` : `${hour}:${minutes}`; - hour = leadZeroHour ? '0' + hour : hour; - minutes = leadZeroMinutes ? '0' + minutes : minutes; + if (!hour) { + result = result.slice(result.indexOf(':') + 1, result.length); + } + + if (!minutes) { + result = result.slice(0, result.indexOf(':')); + if (amPM) { result = result + ' ' + amPM; } + } - return amPM ? `${hour}:${minutes} ${amPM}` : `${hour}:${minutes}`; + return result; } } diff --git a/src/app/time-picker/time-picker.sample.html b/src/app/time-picker/time-picker.sample.html index 4d28fb95800..551abfd6743 100644 --- a/src/app/time-picker/time-picker.sample.html +++ b/src/app/time-picker/time-picker.sample.html @@ -13,6 +13,15 @@

Time Picker with Dropdown

+
+

Time Picker with Dropdown Hour Mode

+
{{showDate(date1)}}
+
Mask: {{hourModeTimePicker.mask}}, Format: {{hourModeTimePicker.format}}
+
+ + +
+

Horizontal Time Picker

AM/PM Time format

diff --git a/src/app/time-picker/time-picker.sample.ts b/src/app/time-picker/time-picker.sample.ts index d6528deb8d6..20dfa88200d 100644 --- a/src/app/time-picker/time-picker.sample.ts +++ b/src/app/time-picker/time-picker.sample.ts @@ -16,6 +16,7 @@ export class TimePickerSampleComponent implements AfterViewInit { isVertical = true; mode = InteractionMode.DropDown; + date1 = new Date(2018, 10, 27, 17, 45, 0, 0); date = new Date(2018, 10, 27, 17, 45, 0, 0); today = new Date(Date.now());