Skip to content

Commit 576033e

Browse files
committed
feat(time-picker): support for fractional seconds
1 parent 2f3434a commit 576033e

File tree

6 files changed

+181
-26
lines changed

6 files changed

+181
-26
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ export interface IgxTimePickerBase {
1010
locale: string;
1111
minuteList: ElementRef;
1212
secondsList: ElementRef;
13+
fractionalSecondsList: ElementRef;
1314
ampmList: ElementRef;
1415
inputFormat: string;
15-
itemsDelta: Pick<DatePartDeltas, 'hours' | 'minutes' | 'seconds'>;
16+
itemsDelta: Pick<DatePartDeltas, 'hours' | 'minutes' | 'seconds' | 'fractionalSeconds'>;
1617
spinLoop: boolean;
1718
selectedDate: Date;
1819
maxDropdownValue: Date;
@@ -21,12 +22,14 @@ export interface IgxTimePickerBase {
2122
showHoursList: boolean;
2223
showMinutesList: boolean;
2324
showSecondsList: boolean;
25+
showFractionalSecondsList: boolean;
2426
showAmPmList: boolean;
2527
minDateValue: Date;
2628
maxDateValue: Date;
2729
nextHour(delta: number);
2830
nextMinute(delta: number);
2931
nextSeconds(delta: number);
32+
nextFractionalSeconds(delta: number);
3033
nextAmPm(delta: number);
3134
close(): void;
3235
cancelButtonClick(): void;

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ <h2 class="igx-time-picker__header-hour">
7474
[attr.aria-valuemax]="timeItem.isSelectedTime ? timeItem.maxValue : null"
7575
*ngFor="let seconds of secondsItems | timeItemPipe:'seconds':selectedDate:minDropdownValue:maxDropdownValue">{{ seconds }}</span>
7676
</div>
77+
<div *ngIf="showFractionalSecondsList" #fractionalSecondsList [igxItemList]="'fractionalSecondsList'">
78+
<span [igxTimeItem]="fractionalSeconds" #timeItem="timeItem" aria-label="fractional seconds"
79+
[attr.role]="timeItem.isSelectedTime ? 'spinbutton' : null"
80+
[attr.aria-valuenow]="timeItem.isSelectedTime ? fractionalSeconds : null"
81+
[attr.aria-valuemin]="timeItem.isSelectedTime ? timeItem.minValue : null"
82+
[attr.aria-valuemax]="timeItem.isSelectedTime ? timeItem.maxValue : null"
83+
*ngFor="let fractionalSeconds of fractionalSecondsItems | timeItemPipe:'fractionalSeconds':selectedDate:minDropdownValue:maxDropdownValue">
84+
{{ fractionalSeconds }}</span>
85+
</div>
7786
<div *ngIf="showAmPmList" #ampmList [igxItemList]="'ampmList'">
7887
<span [igxTimeItem]="ampm" #timeItem="timeItem" aria-label="ampm"
7988
[attr.role]="timeItem.isSelectedTime ? 'spinbutton' : null"

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

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ export class IgxTimePickerComponent extends PickerBaseDirective
305305
@ViewChild('secondsList')
306306
public secondsList: ElementRef;
307307

308+
/** @hidden */
309+
@ViewChild('fractionalSecondsList')
310+
public fractionalSecondsList: ElementRef;
311+
308312
/** @hidden */
309313
@ViewChild('ampmList')
310314
public ampmList: ElementRef;
@@ -352,7 +356,8 @@ export class IgxTimePickerComponent extends PickerBaseDirective
352356
}
353357
if (DateTimeUtil.isValidDate(this.value)) {
354358
// TODO: Update w/ clear behavior
355-
return this.value.getHours() !== 0 || this.value.getMinutes() !== 0 || this.value.getSeconds() !== 0;
359+
return this.value.getHours() !== 0 || this.value.getMinutes() !== 0 ||
360+
this.value.getSeconds() !== 0 || this.value.getMilliseconds() !== 0;
356361
}
357362
return !!this.dateTimeEditor.value;
358363
}
@@ -372,6 +377,12 @@ export class IgxTimePickerComponent extends PickerBaseDirective
372377
return this.inputFormat.indexOf('s') !== - 1;
373378
}
374379

380+
381+
/** @hidden */
382+
public get showFractionalSecondsList(): boolean {
383+
return this.inputFormat.indexOf('S') !== - 1;
384+
}
385+
375386
/** @hidden */
376387
public get showAmPmList(): boolean {
377388
return this.inputFormat.indexOf('t') !== - 1 || this.inputFormat.indexOf('a') !== - 1;
@@ -445,6 +456,8 @@ export class IgxTimePickerComponent extends PickerBaseDirective
445456
/** @hidden @internal */
446457
public secondsItems = [];
447458
/** @hidden @internal */
459+
public fractionalSecondsItems = [];
460+
/** @hidden @internal */
448461
public ampmItems = [];
449462

450463
private _value: Date | string;
@@ -455,7 +468,8 @@ export class IgxTimePickerComponent extends PickerBaseDirective
455468
private _resourceStrings = getCurrentResourceStrings(TimePickerResourceStringsEN);
456469
private _okButtonLabel = null;
457470
private _cancelButtonLabel = null;
458-
private _itemsDelta: Pick<DatePartDeltas, 'hours' | 'minutes' | 'seconds'> = { hours: 1, minutes: 1, seconds: 1 };
471+
private _itemsDelta: Pick<DatePartDeltas, 'hours' | 'minutes' | 'seconds' | 'fractionalSeconds'> =
472+
{ hours: 1, minutes: 1, seconds: 1, fractionalSeconds: 1 };
459473

460474
private _statusChanges$: Subscription;
461475
private _ngControl: NgControl = null;
@@ -596,11 +610,11 @@ export class IgxTimePickerComponent extends PickerBaseDirective
596610
* ```
597611
*/
598612
@Input()
599-
public set itemsDelta(value: Pick<DatePartDeltas, 'hours' | 'minutes' | 'seconds'>) {
613+
public set itemsDelta(value: Pick<DatePartDeltas, 'hours' | 'minutes' | 'seconds' | 'fractionalSeconds'>) {
600614
Object.assign(this._itemsDelta, value);
601615
}
602616

603-
public get itemsDelta(): Pick<DatePartDeltas, 'hours' | 'minutes' | 'seconds'> {
617+
public get itemsDelta(): Pick<DatePartDeltas, 'hours' | 'minutes' | 'seconds' | 'fractionalSeconds'> {
604618
return this._itemsDelta;
605619
}
606620

@@ -653,6 +667,7 @@ export class IgxTimePickerComponent extends PickerBaseDirective
653667
hour: '2-digit',
654668
minute: '2-digit',
655669
second: '2-digit',
670+
fractionalSecondDigits: 3
656671
});
657672
}
658673

@@ -664,7 +679,7 @@ export class IgxTimePickerComponent extends PickerBaseDirective
664679
const date = this.parseToDate(value);
665680
if (date) {
666681
this._dateValue = new Date();
667-
this._dateValue.setHours(date.getHours(), date.getMinutes(), date.getSeconds());
682+
this._dateValue.setHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
668683
this.setSelectedValue(this._dateValue);
669684
} else {
670685
this.setSelectedValue(null);
@@ -824,10 +839,10 @@ export class IgxTimePickerComponent extends PickerBaseDirective
824839

825840
if (DateTimeUtil.isValidDate(this.value)) {
826841
const oldValue = new Date(this.value);
827-
this.value.setHours(0, 0, 0);
842+
this.value.setHours(0, 0, 0, 0);
828843
if (this.value.getTime() !== oldValue.getTime()) {
829844
this.emitValueChange(oldValue, this.value);
830-
this._dateValue.setHours(0, 0, 0);
845+
this._dateValue.setHours(0, 0, 0, 0);
831846
this.dateTimeEditor.value = new Date(this.value);
832847
this.setSelectedValue(this._dateValue);
833848
}
@@ -932,6 +947,14 @@ export class IgxTimePickerComponent extends PickerBaseDirective
932947
}
933948
break;
934949
}
950+
case 'fractionalSecondsList': {
951+
const fractionalSeconds = parseInt(item, 10);
952+
date.setMilliseconds(fractionalSeconds);
953+
if (this.valueInRange(date, this.minDropdownValue, this.maxDropdownValue)) {
954+
this.setSelectedValue(date);
955+
}
956+
break;
957+
}
935958
case 'ampmList': {
936959
let hour = this._selectedDate.getHours();
937960
hour = item === 'AM' || item === 'a' ? hour - 12 : hour + 12;
@@ -1013,6 +1036,36 @@ export class IgxTimePickerComponent extends PickerBaseDirective
10131036
this.updateEditorValue();
10141037
}
10151038

1039+
/** @hidden @internal */
1040+
public nextFractionalSeconds(delta: number) {
1041+
delta = delta > 0 ? 1 : -1;
1042+
const minHours = this.minDropdownValue.getHours();
1043+
const maxHours = this.maxDropdownValue.getHours();
1044+
const hours = this._selectedDate.getHours();
1045+
const minutes = this._selectedDate.getMinutes();
1046+
const minMinutes = this.minDropdownValue.getMinutes();
1047+
const maxMinutes = this.maxDropdownValue.getMinutes();
1048+
const seconds = this._selectedDate.getSeconds();
1049+
const minSeconds = this.minDropdownValue.getSeconds();
1050+
const maxSeconds = this.maxDropdownValue.getSeconds();
1051+
let ms = this._selectedDate.getMilliseconds();
1052+
const minMs = (hours === minHours && minutes === minMinutes && seconds === minSeconds) ? this.minDropdownValue.getMilliseconds() : 0;
1053+
const maxMs = (hours === maxHours && minutes === maxMinutes && seconds === maxSeconds) ? this.maxDropdownValue.getMilliseconds() :
1054+
1000 % this.itemsDelta.fractionalSeconds > 0 ? 1000 - (1000 % this.itemsDelta.fractionalSeconds) :
1055+
1000 - this.itemsDelta.fractionalSeconds;
1056+
1057+
if ((delta < 0 && ms === minMs) || (delta > 0 && ms === maxMs)) {
1058+
ms = this.spinLoop && ms === minMs ? maxMs : this.spinLoop && ms === maxMs ? minMs : ms;
1059+
} else {
1060+
ms = ms + delta * this.itemsDelta.fractionalSeconds;
1061+
}
1062+
1063+
this._selectedDate.setMilliseconds(ms);
1064+
this._selectedDate = this.validateDropdownValue(this._selectedDate);
1065+
this._selectedDate = new Date(this._selectedDate);
1066+
this.updateEditorValue();
1067+
}
1068+
10161069
/** @hidden @internal */
10171070
public nextAmPm(delta?: number) {
10181071
const ampm = this.getPartValue(this._selectedDate, 'ampm');
@@ -1065,6 +1118,12 @@ export class IgxTimePickerComponent extends PickerBaseDirective
10651118
this._selectedDate.getSeconds() + this.itemsDelta.seconds - this._selectedDate.getSeconds() % this.itemsDelta.seconds
10661119
);
10671120
}
1121+
1122+
if (this._selectedDate.getMilliseconds() % this.itemsDelta.fractionalSeconds > 0) {
1123+
this._selectedDate.setMilliseconds(
1124+
this._selectedDate.getMilliseconds() + this.itemsDelta.fractionalSeconds - this._selectedDate.getMilliseconds() % this.itemsDelta.fractionalSeconds
1125+
);
1126+
}
10681127
}
10691128

10701129
protected onStatusChanged() {
@@ -1100,6 +1159,7 @@ export class IgxTimePickerComponent extends PickerBaseDirective
11001159
const hours = time.getHours();
11011160
let minutes = time.getMinutes();
11021161
let seconds = time.getSeconds();
1162+
const milliseconds = time.getMilliseconds();
11031163

11041164
if (this.showHoursList && hours % this.itemsDelta.hours > 0) {
11051165
delta = type === 'min' ? this.itemsDelta.hours - hours % this.itemsDelta.hours
@@ -1110,18 +1170,22 @@ export class IgxTimePickerComponent extends PickerBaseDirective
11101170
seconds = type === 'min' ? 0
11111171
: 60 % this.itemsDelta.seconds > 0 ? 60 - 60 % this.itemsDelta.seconds
11121172
: 60 - this.itemsDelta.seconds;
1113-
time.setHours(hours + sign * delta, minutes, seconds);
1173+
time.setHours(hours + sign * delta, minutes, seconds, milliseconds);
11141174
} else if (this.showMinutesList && minutes % this.itemsDelta.minutes > 0) {
11151175
delta = type === 'min' ? this.itemsDelta.minutes - minutes % this.itemsDelta.minutes
11161176
: minutes % this.itemsDelta.minutes;
11171177
seconds = type === 'min' ? 0
11181178
: 60 % this.itemsDelta.seconds > 0 ? 60 - 60 % this.itemsDelta.seconds
11191179
: 60 - this.itemsDelta.seconds;
1120-
time.setHours(hours, minutes + sign * delta, seconds);
1180+
time.setHours(hours, minutes + sign * delta, seconds, milliseconds);
11211181
} else if (this.showSecondsList && seconds % this.itemsDelta.seconds > 0) {
11221182
delta = type === 'min' ? this.itemsDelta.seconds - seconds % this.itemsDelta.seconds
11231183
: seconds % this.itemsDelta.seconds;
1124-
time.setHours(hours, minutes, seconds + sign * delta);
1184+
time.setHours(hours, minutes, seconds + sign * delta, milliseconds);
1185+
} else if (this.showFractionalSecondsList && milliseconds % this.itemsDelta.fractionalSeconds > 0) {
1186+
delta = type === 'min' ? this.itemsDelta.fractionalSeconds - milliseconds % this.itemsDelta.fractionalSeconds
1187+
: milliseconds % this.itemsDelta.fractionalSeconds;
1188+
time.setHours(hours, minutes, seconds, milliseconds + sign * delta);
11251189
}
11261190

11271191
return time;
@@ -1135,6 +1199,8 @@ export class IgxTimePickerComponent extends PickerBaseDirective
11351199
this.minuteList.nativeElement.focus();
11361200
} else if (this.secondsList) {
11371201
this.secondsList.nativeElement.focus();
1202+
} else if (this.fractionalSecondsList) {
1203+
this.fractionalSecondsList.nativeElement.focus();
11381204
}
11391205
});
11401206
}
@@ -1211,7 +1277,7 @@ export class IgxTimePickerComponent extends PickerBaseDirective
12111277
this.value = newValue ? new Date(newValue) : newValue;
12121278
} else if (isDate(this.value)) {
12131279
const date = new Date(this.value);
1214-
date.setHours(newValue?.getHours() || 0, newValue?.getMinutes() || 0, newValue?.getSeconds() || 0);
1280+
date.setHours(newValue?.getHours() || 0, newValue?.getMinutes() || 0, newValue?.getSeconds() || 0, newValue?.getMilliseconds() || 0);
12151281
this.value = date;
12161282
} else {
12171283
this.value = newValue ? this.toISOString(newValue) : newValue;
@@ -1220,7 +1286,7 @@ export class IgxTimePickerComponent extends PickerBaseDirective
12201286

12211287
private updateEditorValue(): void {
12221288
const date = this.dateTimeEditor.value ? new Date(this.dateTimeEditor.value) : new Date();
1223-
date.setHours(this._selectedDate.getHours(), this._selectedDate.getMinutes(), this._selectedDate.getSeconds());
1289+
date.setHours(this._selectedDate.getHours(), this._selectedDate.getMinutes(), this._selectedDate.getSeconds(), this._selectedDate.getMilliseconds());
12241290
this.dateTimeEditor.value = date;
12251291
}
12261292

0 commit comments

Comments
 (0)