Skip to content

Commit 6e5d52b

Browse files
authored
feat(time-picker): аdding the disabled style for items which are outside the min/maxValue range #3978 (#7347)
1 parent 94b0b3f commit 6e5d52b

File tree

7 files changed

+136
-21
lines changed

7 files changed

+136
-21
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ All notable changes for each version of this project will be documented in this
1717
- Added `onScroll` event, which is emitted when the grid is scrolled vertically or horizontally.
1818
- `igxTreeGrid`
1919
- Removed `onDataPreLoad` event as it is specific for remote virtualization implementation, which is not supported for the `igxTreeGrid`. A more generic `onScroll` event is exposed and can be used instead.
20+
- `IgxTimePicker`
21+
- Added a disabled style for time parts outside of the minimum and maximum range.
2022

2123
### New Features
2224
- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`

projects/igniteui-angular/src/lib/time-picker/time-picker.common.ts

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ import { InteractionMode } from '../core/enums';
44
/** @hidden */
55
export const IGX_TIME_PICKER_COMPONENT = 'IgxTimePickerComponentToken';
66

7+
/** @hidden */
8+
export enum TimeParts {
9+
Hour = 'hour',
10+
Minute = 'minute',
11+
Seconds = 'seconds',
12+
amPM = 'ampm'
13+
}
14+
715
/** @hidden */
816
export interface IgxTimePickerBase {
917
hourList: ElementRef;

projects/igniteui-angular/src/lib/time-picker/time-picker.component.html

+13-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<igx-prefix (click)="openDialog(group.element.nativeElement)">
55
<igx-icon>access_time</igx-icon>
66
</igx-prefix>
7-
<input
7+
<input
88
type="text"
99
[igxMask]="mask"
1010
igxInput
@@ -61,16 +61,24 @@ <h2 class="igx-time-picker__header-hour">
6161
<div class="igx-time-picker__main">
6262
<div class="igx-time-picker__body">
6363
<div *ngIf="showHoursList" #hourList [igxItemList]="'hourList'">
64-
<span [igxHourItem]="hour" *ngFor="let hour of hourView">{{ hour }}</span>
64+
<span [igxHourItem]="hour"
65+
[ngClass]="{'igx-time-picker__item--disabled': applyDisabledStyleForItem(timeParts.Hour, hour)}"
66+
*ngFor="let hour of hourView">{{ hour }}</span>
6567
</div>
6668
<div *ngIf="showMinutesList" #minuteList [igxItemList]="'minuteList'">
67-
<span [igxMinuteItem]="minute" *ngFor="let minute of minuteView">{{ minute }}</span>
69+
<span [igxMinuteItem]="minute"
70+
[ngClass]="{'igx-time-picker__item--disabled': applyDisabledStyleForItem(timeParts.Minute, minute)}"
71+
*ngFor="let minute of minuteView" >{{ minute }}</span>
6872
</div>
6973
<div *ngIf="showSecondsList" #secondsList [igxItemList]="'secondsList'">
70-
<span [igxSecondsItem]="seconds" *ngFor="let seconds of secondsView">{{ seconds }}</span>
74+
<span [igxSecondsItem]="seconds"
75+
[ngClass]="{'igx-time-picker__item--disabled': applyDisabledStyleForItem(timeParts.Seconds, seconds)}"
76+
*ngFor="let seconds of secondsView">{{ seconds }}</span>
7177
</div>
7278
<div *ngIf="showAmPmList" #ampmList [igxItemList]="'ampmList'">
73-
<span [igxAmPmItem]="ampm" *ngFor="let ampm of ampmView">{{ ampm }}</span>
79+
<span [igxAmPmItem]="ampm"
80+
[ngClass]="{'igx-time-picker__item--disabled': applyDisabledStyleForItem(timeParts.amPM, ampm)}"
81+
*ngFor="let ampm of ampmView">{{ ampm }}</span>
7482
</div>
7583
</div>
7684
<ng-container

projects/igniteui-angular/src/lib/time-picker/time-picker.component.spec.ts

+50-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, ViewChild, NgModule, ElementRef, EventEmitter } from '@angular/core';
1+
import { Component, ViewChild, NgModule, ElementRef, EventEmitter, DebugElement } from '@angular/core';
22
import { async, TestBed, fakeAsync, tick, ComponentFixture } from '@angular/core/testing';
33
import { FormsModule, FormGroup, FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
44
import { By } from '@angular/platform-browser';
@@ -12,6 +12,7 @@ import { InteractionMode } from '../core/enums';
1212
import { IgxIconModule } from '../icon/public_api';
1313
import { IgxToggleModule } from '../directives/toggle/toggle.directive';
1414
import { CancelableBrowserEventArgs, IBaseEventArgs } from '../core/utils';
15+
import { IgxHourItemDirective, IgxMinuteItemDirective, IgxSecondsItemDirective, IgxAmPmItemDirective } from './time-picker.directives';
1516

1617
// tslint:disable: no-use-before-declare
1718
describe('IgxTimePicker', () => {
@@ -1667,6 +1668,53 @@ describe('IgxTimePicker', () => {
16671668
expect(input).not.toEqual(document.activeElement);
16681669
expect(dummyInput).toEqual(document.activeElement);
16691670
}));
1671+
1672+
it('should apply disabled style for time outside the min and max values', fakeAsync(() => {
1673+
timePicker = new IgxTimePickerComponent(null, null);
1674+
fixture.detectChanges();
1675+
timePicker.format = 'hh:mm:ss tt';
1676+
const date = new Date(2018, 10, 27, 9, 50, 58);
1677+
timePicker.value = date;
1678+
1679+
timePicker.minValue = '09:15:10 AM';
1680+
timePicker.maxValue = '11:15:10 AM';
1681+
1682+
timePicker.selectedHour = '06';
1683+
timePicker.selectedMinute = '25';
1684+
timePicker.selectedSeconds = '00';
1685+
timePicker.selectedAmPm = 'AM';
1686+
1687+
fixture.detectChanges();
1688+
1689+
// The selected time is 06:25:00 AM
1690+
// Testing 09:25:00 AM
1691+
expect(timePicker.applyDisabledStyleForItem('hour', '9')).toBe(false);
1692+
1693+
timePicker.selectedHour = '9'; // The selected time is 09:25:00 AM
1694+
1695+
// Testing 10:25:00 AM
1696+
expect(timePicker.applyDisabledStyleForItem('hour', '10')).toBe(false);
1697+
1698+
timePicker.selectedHour = '10';
1699+
timePicker.selectedMinute = '10'; // The selected time is 10:10:00 AM
1700+
1701+
// Testing 11:10:00 AM
1702+
expect(timePicker.applyDisabledStyleForItem('hour', '11')).toBe(false);
1703+
1704+
timePicker.selectedHour = '11'; // The selected time is 11:10:00 AM
1705+
1706+
// Testing 12:11:00 AM
1707+
expect(timePicker.applyDisabledStyleForItem('hour', '12')).toBe(true);
1708+
// Testing 11:28:00 AM
1709+
expect(timePicker.applyDisabledStyleForItem('minute', '28')).toBe(true);
1710+
// Testing 11:10:28 AM
1711+
expect(timePicker.applyDisabledStyleForItem('seconds', '28')).toBe(false);
1712+
1713+
timePicker.selectedAmPm = 'PM'; // The selected time is 11:10:00 PM
1714+
1715+
// Testing 11:10:00 AM
1716+
expect(timePicker.applyDisabledStyleForItem('ampm', 'AM')).toBe(false);
1717+
}));
16701718
});
16711719

16721720
describe('Timepicker with outlet', () => {
@@ -2248,7 +2296,7 @@ export class IgxTimePickerRetemplatedComponent { }
22482296
`
22492297
})
22502298
export class IgxTimePickerDropDownComponent {
2251-
itemsDelta = { hours: 1, minutes: 5 };
2299+
itemsDelta = { hours: 1, minutes: 5, seconds: 1 };
22522300
format = 'hh:mm tt';
22532301
isSpinLoop = true;
22542302
isVertical = true;

projects/igniteui-angular/src/lib/time-picker/time-picker.component.ts

+60-11
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {
3737
} from './time-picker.directives';
3838
import { Subject, fromEvent, interval, animationFrameScheduler, Subscription } from 'rxjs';
3939
import { EditorProvider } from '../core/edit-provider';
40-
import { IgxTimePickerBase, IGX_TIME_PICKER_COMPONENT } from './time-picker.common';
40+
import { IgxTimePickerBase, IGX_TIME_PICKER_COMPONENT, TimeParts } from './time-picker.common';
4141
import { AbsoluteScrollStrategy } from '../services/overlay/scroll';
4242
import { AutoPositionStrategy } from '../services/overlay/position';
4343
import { OverlaySettings } from '../services/overlay/utilities';
@@ -98,6 +98,7 @@ const noop = () => { };
9898
}`
9999
]
100100
})
101+
101102
export class IgxTimePickerComponent implements
102103
IgxTimePickerBase,
103104
ControlValueAccessor,
@@ -152,6 +153,10 @@ export class IgxTimePickerComponent implements
152153
this.onValidationFailed.emit(args);
153154
}
154155
}
156+
/**
157+
* @hidden @internal
158+
*/
159+
timeParts: any = Object.assign({}, TimeParts);
155160

156161
/**
157162
* An accessor that returns the value of `igx-time-picker` component.
@@ -507,10 +512,10 @@ export class IgxTimePickerComponent implements
507512
@ViewChild(IgxInputDirective, { read: ElementRef })
508513
private _inputElementRef: ElementRef;
509514

510-
@ViewChild(IgxInputDirective, { read: IgxInputDirective})
515+
@ViewChild(IgxInputDirective, { read: IgxInputDirective })
511516
private _inputDirective: IgxInputDirective;
512517

513-
@ContentChild(IgxInputDirective, { read: IgxInputDirective})
518+
@ContentChild(IgxInputDirective, { read: IgxInputDirective })
514519
private _inputDirectiveUserTemplate: IgxInputDirective;
515520

516521
@ViewChild(IgxInputGroupComponent, { read: IgxInputGroupComponent })
@@ -635,6 +640,46 @@ export class IgxTimePickerComponent implements
635640
}
636641
}
637642

643+
/** @hidden @internal */
644+
applyDisabledStyleForItem(period: string, value: string) {
645+
if (!this.minValue || !this.maxValue) {
646+
return false;
647+
}
648+
const minValueDate: Date = this.convertMinMaxValue(this.minValue);
649+
const maxValueDate: Date = this.convertMinMaxValue(this.maxValue);
650+
let hour: number = parseInt(this.selectedHour, 10);
651+
let minute: number = parseInt(this.selectedMinute, 10);
652+
let seconds: number = parseInt(this.selectedSeconds, 10);
653+
let amPM: string = this.selectedAmPm;
654+
const date = new Date(minValueDate);
655+
switch (period) {
656+
case TimeParts.Hour:
657+
hour = parseInt(value, 10);
658+
break;
659+
660+
case TimeParts.Minute:
661+
minute = parseInt(value, 10);
662+
break;
663+
664+
case TimeParts.Seconds:
665+
seconds = parseInt(value, 10);
666+
break;
667+
668+
case TimeParts.amPM:
669+
amPM = value;
670+
break;
671+
}
672+
673+
if (amPM === 'PM') {
674+
hour += 12;
675+
}
676+
date.setHours(hour);
677+
date.setMinutes(minute);
678+
date.setSeconds(seconds);
679+
return date < minValueDate || date > maxValueDate;
680+
681+
}
682+
638683
/** @hidden @internal */
639684
public registerOnChange(fn: (_: Date) => void) { this._onChangeCallback = fn; }
640685

@@ -1267,7 +1312,11 @@ export class IgxTimePickerComponent implements
12671312
return date;
12681313
}
12691314

1270-
private _convertMinMaxValue(value: string): Date {
1315+
/** @hidden @internal */
1316+
public convertMinMaxValue(value: string): Date {
1317+
if (!value) {
1318+
return;
1319+
}
12711320
const date = this.value ? new Date(this.value) : this._dateFromModel ? new Date(this._dateFromModel) : new Date();
12721321
const sections = value.split(/[\s:]+/);
12731322
let hour, minutes, seconds, amPM;
@@ -1310,9 +1359,9 @@ export class IgxTimePickerComponent implements
13101359
}
13111360

13121361
private _isValueValid(value: Date): boolean {
1313-
if (this.maxValue && value > this._convertMinMaxValue(this.maxValue)) {
1362+
if (this.maxValue && value > this.convertMinMaxValue(this.maxValue)) {
13141363
return false;
1315-
} else if (this.minValue && value < this._convertMinMaxValue(this.minValue)) {
1364+
} else if (this.minValue && value < this.convertMinMaxValue(this.minValue)) {
13161365
return false;
13171366
} else {
13181367
return true;
@@ -1484,7 +1533,7 @@ export class IgxTimePickerComponent implements
14841533

14851534
private _onDropDownClosed(): void {
14861535
const oldValue = this.value;
1487-
const newVal = this._convertMinMaxValue(this.displayValue);
1536+
const newVal = this.convertMinMaxValue(this.displayValue);
14881537

14891538
if (this.displayValue === this.parseMask(false)) {
14901539
return;
@@ -1922,7 +1971,7 @@ export class IgxTimePickerComponent implements
19221971
// timepicker own value property if it is a valid Date
19231972
if (val.indexOf(this.promptChar) === -1) {
19241973
if (this._isEntryValid(val)) {
1925-
const newVal = this._convertMinMaxValue(val);
1974+
const newVal = this.convertMinMaxValue(val);
19261975
if (oldVal.getTime() !== newVal.getTime()) {
19271976
this.value = newVal;
19281977
}
@@ -1970,7 +2019,7 @@ export class IgxTimePickerComponent implements
19702019

19712020
if (value && value !== this.parseMask()) {
19722021
if (this._isEntryValid(value)) {
1973-
const newVal = this._convertMinMaxValue(value);
2022+
const newVal = this.convertMinMaxValue(value);
19742023
if (!this.value || this.value.getTime() !== newVal.getTime()) {
19752024
this.value = newVal;
19762025
}
@@ -2007,8 +2056,8 @@ export class IgxTimePickerComponent implements
20072056
let sign: number;
20082057
let displayVal: string;
20092058
const currentVal = new Date(this.value);
2010-
const min = this.minValue ? this._convertMinMaxValue(this.minValue) : this._convertMinMaxValue('00:00');
2011-
const max = this.maxValue ? this._convertMinMaxValue(this.maxValue) : this._convertMinMaxValue('24:00');
2059+
const min = this.minValue ? this.convertMinMaxValue(this.minValue) : this.convertMinMaxValue('00:00');
2060+
const max = this.maxValue ? this.convertMinMaxValue(this.maxValue) : this.convertMinMaxValue('24:00');
20122061

20132062
const cursor = this._getCursorPosition();
20142063

src/app/time-picker/time-picker.sample.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<h4 class="sample-title">Time Picker with Dropdown</h4>
88
<div class="sample-description">{{showDate(date)}}</div>
99
<div class="preview" style="width: 280px">
10-
<igx-time-picker #tp (onValueChanged)="valueChanged($event)" [minValue]="'9:15 AM'" [maxValue]="'9:15 PM'"
10+
<igx-time-picker #tp (onValueChanged)="valueChanged($event)" [minValue]="'9:15:20 AM'" [maxValue]="'11:15:20 AM'"
1111
(onValidationFailed)="validationFailed($event)" [mode]="mode" [isSpinLoop]="isSpinLoop"
1212
[(ngModel)]="date" [itemsDelta]="itemsDelta" [format]="format">
1313
</igx-time-picker>

src/app/time-picker/time-picker.sample.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ export class TimePickerSampleComponent implements AfterViewInit {
1111
min = '09:00';
1212

1313
itemsDelta = { hours: 1, minutes: 5 };
14-
format = 'hh:mm tt';
14+
format = 'hh:mm:ss tt';
1515
isSpinLoop = true;
1616
isVertical = true;
1717
mode = InteractionMode.DropDown;
1818

1919
date1 = new Date(2018, 10, 27, 17, 45, 0, 0);
20-
date = new Date(2018, 10, 27, 21, 45, 0, 0);
20+
date = new Date(2018, 10, 27, 9, 45, 0, 0);
2121
val = new Date(0, 0, 0, 19, 35, 30, 0);
2222
today = new Date(Date.now());
2323

0 commit comments

Comments
 (0)