igniteui-angular
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1,276 lines (1,272 loc) • 86.5 kB
JavaScript
import * as i2 from 'igniteui-angular/core';
import { HammerGesturesManager, DateTimeUtil, DatePart, PickerHeaderOrientation, PlatformUtil, PickerInteractionMode, getCurrentResourceStrings, TimePickerResourceStringsEN, AutoPositionStrategy, AbsoluteScrollStrategy, isDate, isEqual, IgxPickerActionsDirective, IgxPickerToggleComponent, IgxPickerClearComponent } from 'igniteui-angular/core';
import { DatePipe, NgClass, NgTemplateOutlet } from '@angular/common';
import * as i0 from '@angular/core';
import { InjectionToken, inject, ElementRef, HostListener, HostBinding, Input, Directive, Pipe, Injector, ChangeDetectorRef, EventEmitter, booleanAttribute, ViewChild, ContentChild, Output, Component, NgModule } from '@angular/core';
import { NgControl, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
import * as i3 from 'igniteui-angular/input-group';
import { IgxInputState, IgxInputGroupComponent, IgxInputDirective, IgxPrefixDirective, IgxSuffixDirective, IgxReadOnlyInputDirective, IgxLabelDirective, IgxHintDirective } from 'igniteui-angular/input-group';
import { noop, fromEvent } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { IgxDateTimeEditorDirective, IgxTextSelectionDirective, IgxButtonDirective, IgxToggleDirective, IgxDividerDirective } from 'igniteui-angular/directives';
import { IgxIconComponent } from 'igniteui-angular/icon';
import { PickerBaseDirective } from 'igniteui-angular/date-picker';
/** @hidden */
const IGX_TIME_PICKER_COMPONENT = new InjectionToken('IgxTimePickerComponentToken');
/**
* This file contains all the directives used by the @link IgxTimePickerComponent.
* You should generally not use them directly.
*
* @preferred
*/
/** @hidden */
class IgxItemListDirective {
constructor() {
this.timePicker = inject(IGX_TIME_PICKER_COMPONENT);
this.elementRef = inject(ElementRef);
this.touchManager = inject(HammerGesturesManager);
this.tabindex = 0;
this.SCROLL_THRESHOLD = 50;
this.PAN_THRESHOLD = 10;
/**
* accumulates wheel scrolls and triggers a change action above SCROLL_THRESHOLD
*/
this.scrollAccumulator = 0;
this.onPanMove = (event) => {
const delta = event.deltaY < 0 ? -1 : event.deltaY > 0 ? 1 : 0;
if (delta !== 0) {
this.nextItem(delta);
}
};
}
get defaultCSS() {
return true;
}
get hourCSS() {
return this.type === 'hourList';
}
get minuteCSS() {
return this.type === 'minuteList';
}
get secondsCSS() {
return this.type === 'secondsList';
}
get ampmCSS() {
return this.type === 'ampmList';
}
onFocus() {
this.isActive = true;
}
onBlur() {
this.isActive = false;
}
/**
* @hidden
*/
onKeydownArrowDown(event) {
event.preventDefault();
this.nextItem(1);
}
/**
* @hidden
*/
onKeydownArrowUp(event) {
event.preventDefault();
this.nextItem(-1);
}
/**
* @hidden
*/
onKeydownArrowRight(event) {
event.preventDefault();
const listName = event.target.className;
if (listName.indexOf('hourList') !== -1 && this.timePicker.minuteList) {
this.timePicker.minuteList.nativeElement.focus();
}
else if ((listName.indexOf('hourList') !== -1 || listName.indexOf('minuteList') !== -1) && this.timePicker.secondsList) {
this.timePicker.secondsList.nativeElement.focus();
}
else if ((listName.indexOf('hourList') !== -1 || listName.indexOf('minuteList') !== -1 ||
listName.indexOf('secondsList') !== -1) && this.timePicker.ampmList) {
this.timePicker.ampmList.nativeElement.focus();
}
}
/**
* @hidden
*/
onKeydownArrowLeft(event) {
event.preventDefault();
const listName = event.target.className;
if (listName.indexOf('ampmList') !== -1 && this.timePicker.secondsList) {
this.timePicker.secondsList.nativeElement.focus();
}
else if (listName.indexOf('secondsList') !== -1 && this.timePicker.secondsList
&& listName.indexOf('minutesList') && this.timePicker.minuteList) {
this.timePicker.minuteList.nativeElement.focus();
}
else if (listName.indexOf('ampmList') !== -1 && this.timePicker.minuteList) {
this.timePicker.minuteList.nativeElement.focus();
}
else if ((listName.indexOf('ampmList') !== -1 || listName.indexOf('secondsList') !== -1 ||
listName.indexOf('minuteList') !== -1) && this.timePicker.hourList) {
this.timePicker.hourList.nativeElement.focus();
}
}
/**
* @hidden
*/
onKeydownEnter(event) {
event.preventDefault();
this.timePicker.okButtonClick();
}
/**
* @hidden
*/
onKeydownEscape(event) {
event.preventDefault();
this.timePicker.cancelButtonClick();
}
/**
* @hidden
*/
onHover() {
this.elementRef.nativeElement.focus();
}
/**
* @hidden
*/
onScroll(event) {
event.preventDefault();
event.stopPropagation();
this.scrollAccumulator += event.deltaY;
if (Math.abs(this.scrollAccumulator) > this.SCROLL_THRESHOLD) {
this.nextItem(this.scrollAccumulator);
this.scrollAccumulator = 0;
}
}
/**
* @hidden @internal
*/
ngOnInit() {
const hammerOptions = {
recognizers: [
[
HammerGesturesManager.Hammer?.Pan,
{
direction: HammerGesturesManager.Hammer?.DIRECTION_VERTICAL,
threshold: this.PAN_THRESHOLD
}
]
]
};
this.touchManager.addEventListener(this.elementRef.nativeElement, 'pan', this.onPanMove, hammerOptions);
}
/**
* @hidden @internal
*/
ngOnDestroy() {
this.touchManager.destroy();
}
nextItem(delta) {
switch (this.type) {
case 'hourList': {
this.timePicker.nextHour(delta);
break;
}
case 'minuteList': {
this.timePicker.nextMinute(delta);
break;
}
case 'secondsList': {
this.timePicker.nextSeconds(delta);
break;
}
case 'ampmList': {
this.timePicker.nextAmPm(delta);
break;
}
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxItemListDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxItemListDirective, isStandalone: true, selector: "[igxItemList]", inputs: { type: ["igxItemList", "type"] }, host: { listeners: { "focus": "onFocus()", "blur": "onBlur()", "keydown.arrowdown": "onKeydownArrowDown($event)", "keydown.arrowup": "onKeydownArrowUp($event)", "keydown.arrowright": "onKeydownArrowRight($event)", "keydown.arrowleft": "onKeydownArrowLeft($event)", "keydown.enter": "onKeydownEnter($event)", "keydown.escape": "onKeydownEscape($event)", "mouseover": "onHover()", "wheel": "onScroll($event)" }, properties: { "attr.tabindex": "this.tabindex", "class.igx-time-picker__column": "this.defaultCSS", "class.igx-time-picker__hourList": "this.hourCSS", "class.igx-time-picker__minuteList": "this.minuteCSS", "class.igx-time-picker__secondsList": "this.secondsCSS", "class.igx-time-picker__ampmList": "this.ampmCSS" } }, providers: [HammerGesturesManager], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxItemListDirective, decorators: [{
type: Directive,
args: [{
selector: '[igxItemList]',
providers: [HammerGesturesManager],
standalone: true
}]
}], propDecorators: { tabindex: [{
type: HostBinding,
args: ['attr.tabindex']
}], type: [{
type: Input,
args: ['igxItemList']
}], defaultCSS: [{
type: HostBinding,
args: ['class.igx-time-picker__column']
}], hourCSS: [{
type: HostBinding,
args: ['class.igx-time-picker__hourList']
}], minuteCSS: [{
type: HostBinding,
args: ['class.igx-time-picker__minuteList']
}], secondsCSS: [{
type: HostBinding,
args: ['class.igx-time-picker__secondsList']
}], ampmCSS: [{
type: HostBinding,
args: ['class.igx-time-picker__ampmList']
}], onFocus: [{
type: HostListener,
args: ['focus']
}], onBlur: [{
type: HostListener,
args: ['blur']
}], onKeydownArrowDown: [{
type: HostListener,
args: ['keydown.arrowdown', ['$event']]
}], onKeydownArrowUp: [{
type: HostListener,
args: ['keydown.arrowup', ['$event']]
}], onKeydownArrowRight: [{
type: HostListener,
args: ['keydown.arrowright', ['$event']]
}], onKeydownArrowLeft: [{
type: HostListener,
args: ['keydown.arrowleft', ['$event']]
}], onKeydownEnter: [{
type: HostListener,
args: ['keydown.enter', ['$event']]
}], onKeydownEscape: [{
type: HostListener,
args: ['keydown.escape', ['$event']]
}], onHover: [{
type: HostListener,
args: ['mouseover']
}], onScroll: [{
type: HostListener,
args: ['wheel', ['$event']]
}] } });
/**
* @hidden
*/
class IgxTimeItemDirective {
constructor() {
this.timePicker = inject(IGX_TIME_PICKER_COMPONENT);
this.itemList = inject(IgxItemListDirective);
}
get defaultCSS() {
return true;
}
get selectedCSS() {
return this.isSelectedTime;
}
get activeCSS() {
return this.isSelectedTime && this.itemList.isActive;
}
get isSelectedTime() {
const currentValue = this.value.length < 2 ? `0${this.value}` : this.value;
const dateType = this.itemList.type;
const inputDateParts = DateTimeUtil.parseDateTimeFormat(this.timePicker.appliedFormat);
switch (dateType) {
case 'hourList':
const hourPart = inputDateParts.find(element => element.type === 'hours');
return DateTimeUtil.getPartValue(this.timePicker.selectedDate, hourPart, hourPart.format.length) === currentValue;
case 'minuteList':
const minutePart = inputDateParts.find(element => element.type === 'minutes');
return DateTimeUtil.getPartValue(this.timePicker.selectedDate, minutePart, minutePart.format.length) === currentValue;
case 'secondsList':
const secondsPart = inputDateParts.find(element => element.type === 'seconds');
return DateTimeUtil.getPartValue(this.timePicker.selectedDate, secondsPart, secondsPart.format.length) === currentValue;
case 'ampmList':
const ampmPart = inputDateParts.find(element => element.format.indexOf('a') !== -1 || element.format === 'tt');
return DateTimeUtil.getPartValue(this.timePicker.selectedDate, ampmPart, ampmPart.format.length) === this.value;
}
}
get minValue() {
const dateType = this.itemList.type;
const inputDateParts = DateTimeUtil.parseDateTimeFormat(this.timePicker.appliedFormat);
switch (dateType) {
case 'hourList':
return this.getHourPart(this.timePicker.minDropdownValue);
case 'minuteList':
if (this.timePicker.selectedDate.getHours() === this.timePicker.minDropdownValue.getHours()) {
const minutePart = inputDateParts.find(element => element.type === 'minutes');
return DateTimeUtil.getPartValue(this.timePicker.minDropdownValue, minutePart, minutePart.format.length);
}
return '00';
case 'secondsList':
const date = new Date(this.timePicker.selectedDate);
const min = new Date(this.timePicker.minDropdownValue);
date.setSeconds(0);
min.setSeconds(0);
if (date.getTime() === min.getTime()) {
const secondsPart = inputDateParts.find(element => element.type === 'seconds');
return DateTimeUtil.getPartValue(this.timePicker.minDropdownValue, secondsPart, secondsPart.format.length);
}
return '00';
case 'ampmList':
const ampmPart = inputDateParts.find(element => element.format.indexOf('a') !== -1 || element.format === 'tt');
return DateTimeUtil.getPartValue(this.timePicker.minDropdownValue, ampmPart, ampmPart.format.length);
}
}
get maxValue() {
const dateType = this.itemList.type;
const inputDateParts = DateTimeUtil.parseDateTimeFormat(this.timePicker.appliedFormat);
switch (dateType) {
case 'hourList':
return this.getHourPart(this.timePicker.maxDropdownValue);
case 'minuteList':
if (this.timePicker.selectedDate.getHours() === this.timePicker.maxDropdownValue.getHours()) {
const minutePart = inputDateParts.find(element => element.type === 'minutes');
return DateTimeUtil.getPartValue(this.timePicker.maxDropdownValue, minutePart, minutePart.format.length);
}
else {
const currentTime = new Date(this.timePicker.selectedDate);
const minDelta = this.timePicker.itemsDelta.minutes;
const remainder = 60 % minDelta;
const delta = remainder === 0 ? 60 - minDelta : 60 - remainder;
currentTime.setMinutes(delta);
const minutePart = inputDateParts.find(element => element.type === 'minutes');
return DateTimeUtil.getPartValue(currentTime, minutePart, minutePart.format.length);
}
case 'secondsList':
const date = new Date(this.timePicker.selectedDate);
const max = new Date(this.timePicker.maxDropdownValue);
date.setSeconds(0);
max.setSeconds(0);
if (date.getTime() === max.getTime()) {
const secondsPart = inputDateParts.find(element => element.type === 'seconds');
return DateTimeUtil.getPartValue(this.timePicker.maxDropdownValue, secondsPart, secondsPart.format.length);
}
else {
const secDelta = this.timePicker.itemsDelta.seconds;
const remainder = 60 % secDelta;
const delta = remainder === 0 ? 60 - secDelta : 60 - remainder;
date.setSeconds(delta);
const secondsPart = inputDateParts.find(element => element.type === 'seconds');
return DateTimeUtil.getPartValue(date, secondsPart, secondsPart.format.length);
}
case 'ampmList':
const ampmPart = inputDateParts.find(element => element.format.indexOf('a') !== -1 || element.format === 'tt');
return DateTimeUtil.getPartValue(this.timePicker.maxDropdownValue, ampmPart, ampmPart.format.length);
}
}
get hourValue() {
return this.getHourPart(this.timePicker.selectedDate);
}
onClick(item) {
if (item !== '') {
const dateType = this.itemList.type;
this.timePicker.onItemClick(item, dateType);
}
}
getHourPart(date) {
const inputDateParts = DateTimeUtil.parseDateTimeFormat(this.timePicker.appliedFormat);
const hourPart = inputDateParts.find(element => element.type === 'hours');
const ampmPart = inputDateParts.find(element => element.format.indexOf('a') !== -1 || element.format === 'tt');
const hour = DateTimeUtil.getPartValue(date, hourPart, hourPart.format.length);
if (ampmPart) {
const ampm = DateTimeUtil.getPartValue(date, ampmPart, ampmPart.format.length);
return `${hour} ${ampm}`;
}
return hour;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTimeItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.2", type: IgxTimeItemDirective, isStandalone: true, selector: "[igxTimeItem]", inputs: { value: ["igxTimeItem", "value"] }, host: { listeners: { "click": "onClick(value)" }, properties: { "class.igx-time-picker__item": "this.defaultCSS", "class.igx-time-picker__item--selected": "this.selectedCSS", "class.igx-time-picker__item--active": "this.activeCSS" } }, exportAs: ["timeItem"], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxTimeItemDirective, decorators: [{
type: Directive,
args: [{
selector: '[igxTimeItem]',
exportAs: 'timeItem',
standalone: true
}]
}], propDecorators: { value: [{
type: Input,
args: ['igxTimeItem']
}], defaultCSS: [{
type: HostBinding,
args: ['class.igx-time-picker__item']
}], selectedCSS: [{
type: HostBinding,
args: ['class.igx-time-picker__item--selected']
}], activeCSS: [{
type: HostBinding,
args: ['class.igx-time-picker__item--active']
}], onClick: [{
type: HostListener,
args: ['click', ['value']]
}] } });
const ITEMS_COUNT = 7;
class TimeFormatPipe {
constructor() {
this.timePicker = inject(IGX_TIME_PICKER_COMPONENT);
}
transform(value) {
const format = this.timePicker.appliedFormat.replace('tt', 'aa');
const datePipe = new DatePipe(this.timePicker.locale);
return datePipe.transform(value, format);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: TimeFormatPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: TimeFormatPipe, isStandalone: true, name: "timeFormatPipe" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: TimeFormatPipe, decorators: [{
type: Pipe,
args: [{
name: 'timeFormatPipe',
standalone: true
}]
}] });
class TimeItemPipe {
constructor() {
this.timePicker = inject(IGX_TIME_PICKER_COMPONENT);
}
transform(_collection, timePart, selectedDate, min, max) {
let list;
let part;
switch (timePart) {
case 'hour':
list = this.generateHours(min, max);
const hours = this.timePicker.isTwelveHourFormat ? this.toTwelveHourFormat(selectedDate.getHours())
: selectedDate.getHours();
list = this.scrollListItem(hours, list);
part = DatePart.Hours;
break;
case 'minutes':
list = this.generateMinutes(selectedDate, min, max);
list = this.scrollListItem(selectedDate.getMinutes(), list);
part = DatePart.Minutes;
break;
case 'seconds':
list = this.generateSeconds(selectedDate, min, max);
list = this.scrollListItem(selectedDate.getSeconds(), list);
part = DatePart.Seconds;
break;
case 'ampm':
const selectedAmPm = this.timePicker.getPartValue(selectedDate, 'ampm');
list = this.generateAmPm(min, max, selectedAmPm);
list = this.scrollListItem(selectedAmPm, list);
part = DatePart.AmPm;
break;
}
return this.getListView(list, part);
}
getListView(view, dateType) {
for (let i = 0; i < view.length; i++) {
view[i] = this.getItemView(view[i], dateType);
}
return view;
}
getItemView(item, dateType) {
if (item === null) {
item = '';
}
else if (dateType && typeof (item) !== 'string') {
const leadZeroHour = (item < 10 && (this.timePicker.appliedFormat?.indexOf('hh') !== -1
|| this.timePicker.appliedFormat?.indexOf('HH') !== -1));
const leadZeroMinute = (item < 10 && this.timePicker.appliedFormat?.indexOf('mm') !== -1);
const leadZeroSeconds = (item < 10 && this.timePicker.appliedFormat?.indexOf('ss') !== -1);
const leadZero = {
hours: leadZeroHour,
minutes: leadZeroMinute,
seconds: leadZeroSeconds
}[dateType];
item = (leadZero) ? '0' + item : `${item}`;
}
return item;
}
scrollListItem(item, items) {
const itemsCount = items.length;
let view;
if (items) {
const index = items.indexOf(item);
if (index < 3) {
view = items.slice(itemsCount - (3 - index), itemsCount);
view = view.concat(items.slice(0, index + 4));
}
else if (index + 4 > itemsCount) {
view = items.slice(index - 3, itemsCount);
view = view.concat(items.slice(0, index + 4 - itemsCount));
}
else {
view = items.slice(index - 3, index + 4);
}
}
return view;
}
generateHours(min, max) {
const hourItems = [];
let hoursCount = this.timePicker.isTwelveHourFormat ? 13 : 24;
hoursCount /= this.timePicker.itemsDelta.hours;
const minHours = min.getHours();
const maxHours = max.getHours();
if (hoursCount > 1) {
for (let hourIndex = 0; hourIndex < 24; hourIndex++) {
let hours = hourIndex * this.timePicker.itemsDelta.hours;
if (hours >= minHours && hours <= maxHours) {
hours = this.timePicker.isTwelveHourFormat ? this.toTwelveHourFormat(hours) : hours;
if (!hourItems.find((element => element === hours))) {
hourItems.push(hours);
}
}
}
}
else {
hourItems.push(0);
}
if (hourItems.length < ITEMS_COUNT || hoursCount < ITEMS_COUNT || !this.timePicker.spinLoop) {
const index = !this.timePicker.spinLoop || (hourItems.length < ITEMS_COUNT && hoursCount < ITEMS_COUNT) ? 6 : 3;
for (let i = 0; i < index; i++) {
hourItems.push(null);
}
}
return hourItems;
}
generateMinutes(time, min, max) {
const minuteItems = [];
const minuteItemsCount = 60 / this.timePicker.itemsDelta.minutes;
time = new Date(time);
for (let i = 0; i < minuteItemsCount; i++) {
const minutes = i * this.timePicker.itemsDelta.minutes;
time.setMinutes(minutes);
if (time >= min && time <= max) {
minuteItems.push(i * this.timePicker.itemsDelta.minutes);
}
}
if (minuteItems.length < ITEMS_COUNT || minuteItemsCount < ITEMS_COUNT || !this.timePicker.spinLoop) {
const index = !this.timePicker.spinLoop || (minuteItems.length < ITEMS_COUNT && minuteItemsCount < ITEMS_COUNT) ? 6 : 3;
for (let i = 0; i < index; i++) {
minuteItems.push(null);
}
}
return minuteItems;
}
generateSeconds(time, min, max) {
const secondsItems = [];
const secondsItemsCount = 60 / this.timePicker.itemsDelta.seconds;
time = new Date(time);
for (let i = 0; i < secondsItemsCount; i++) {
const seconds = i * this.timePicker.itemsDelta.seconds;
time.setSeconds(seconds);
if (time.getTime() >= min.getTime()
&& time.getTime() <= max.getTime()) {
secondsItems.push(i * this.timePicker.itemsDelta.seconds);
}
}
if (secondsItems.length < ITEMS_COUNT || secondsItemsCount < ITEMS_COUNT || !this.timePicker.spinLoop) {
const index = !this.timePicker.spinLoop || (secondsItems.length < ITEMS_COUNT && secondsItemsCount < ITEMS_COUNT) ? 6 : 3;
for (let i = 0; i < index; i++) {
secondsItems.push(null);
}
}
return secondsItems;
}
generateAmPm(min, max, selectedAmPm) {
const ampmItems = [];
const minHour = min.getHours();
const maxHour = max.getHours();
if (minHour < 12) {
ampmItems.push(DateTimeUtil.getAmPmValue(selectedAmPm.length, true));
}
if (minHour >= 12 || maxHour >= 12) {
ampmItems.push(DateTimeUtil.getAmPmValue(selectedAmPm.length, false));
}
for (let i = 0; i < 5; i++) {
ampmItems.push(null);
}
return ampmItems;
}
toTwelveHourFormat(hour) {
if (hour > 12) {
hour -= 12;
}
else if (hour === 0) {
hour = 12;
}
return hour;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: TimeItemPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.2", ngImport: i0, type: TimeItemPipe, isStandalone: true, name: "timeItemPipe" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: TimeItemPipe, decorators: [{
type: Pipe,
args: [{
name: 'timeItemPipe',
standalone: true
}]
}] });
let NEXT_ID = 0;
class IgxTimePickerComponent extends PickerBaseDirective {
/**
* The minimum value the picker will accept.
*
* @remarks
* If a `string` value is passed in, it must be in ISO format.
*
* @example
* ```html
* <igx-time-picker [minValue]="18:00:00"></igx-time-picker>
* ```
*/
set minValue(value) {
this._minValue = value;
const date = this.parseToDate(value);
if (date) {
this._dateMinValue = new Date();
this._dateMinValue.setHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
this.minDropdownValue = this.setMinMaxDropdownValue('min', this._dateMinValue);
}
this.setSelectedValue(this._selectedDate);
this._onValidatorChange();
}
get minValue() {
return this._minValue;
}
/**
* Gets if the dropdown/dialog is collapsed
*
* ```typescript
* let isCollapsed = this.timePicker.collapsed;
* ```
*/
get collapsed() {
return this.toggleRef?.collapsed;
}
/**
* The maximum value the picker will accept.
*
* @remarks
* If a `string` value is passed in, it must be in ISO format.
*
* @example
* ```html
* <igx-time-picker [maxValue]="20:30:00"></igx-time-picker>
* ```
*/
set maxValue(value) {
this._maxValue = value;
const date = this.parseToDate(value);
if (date) {
this._dateMaxValue = new Date();
this._dateMaxValue.setHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
this.maxDropdownValue = this.setMinMaxDropdownValue('max', this._dateMaxValue);
}
this.setSelectedValue(this._selectedDate);
this._onValidatorChange();
}
get maxValue() {
return this._maxValue;
}
/** @hidden */
get showClearButton() {
if (this.clearComponents.length) {
return false;
}
if (DateTimeUtil.isValidDate(this.value)) {
// TODO: Update w/ clear behavior
return this.value.getHours() !== 0 || this.value.getMinutes() !== 0 ||
this.value.getSeconds() !== 0 || this.value.getMilliseconds() !== 0;
}
return !!this.dateTimeEditor.value;
}
/** @hidden */
get showHoursList() {
return this.appliedFormat?.indexOf('h') !== -1 || this.appliedFormat?.indexOf('H') !== -1;
}
/** @hidden */
get showMinutesList() {
return this.appliedFormat?.indexOf('m') !== -1;
}
/** @hidden */
get showSecondsList() {
return this.appliedFormat?.indexOf('s') !== -1;
}
/** @hidden */
get showAmPmList() {
return this.appliedFormat?.indexOf('t') !== -1 || this.appliedFormat?.indexOf('a') !== -1;
}
/** @hidden */
get isTwelveHourFormat() {
return this.appliedFormat?.indexOf('h') !== -1;
}
/** @hidden @internal */
get isVertical() {
return this.headerOrientation === PickerHeaderOrientation.Vertical;
}
/** @hidden @internal */
get selectedDate() {
return this._selectedDate;
}
/** @hidden @internal */
get minDateValue() {
if (!this._dateMinValue) {
const minDate = new Date();
minDate.setHours(0, 0, 0, 0);
return minDate;
}
return this._dateMinValue;
}
/** @hidden @internal */
get maxDateValue() {
if (!this._dateMaxValue) {
const maxDate = new Date();
maxDate.setHours(23, 59, 59, 999);
return maxDate;
}
return this._dateMaxValue;
}
/** @hidden @internal */
get appliedFormat() {
return this.inputFormat || this.dateTimeEditor?.inputFormat;
}
get toggleContainer() {
return this.toggleRef?.element;
}
get required() {
if (this._ngControl && this._ngControl.control && this._ngControl.control.validator) {
// Run the validation with empty object to check if required is enabled.
const error = this._ngControl.control.validator({});
return !!(error && error.required);
}
return false;
}
get dialogOverlaySettings() {
return Object.assign({}, this._defaultDialogOverlaySettings, this.overlaySettings);
}
get dropDownOverlaySettings() {
return Object.assign({}, this._defaultDropDownOverlaySettings, this.overlaySettings);
}
/**
* The currently selected value / time from the drop-down/dialog
*
* @remarks
* The current value is of type `Date`
*
* @example
* ```typescript
* const newValue: Date = new Date(2000, 2, 2, 10, 15, 15);
* this.timePicker.value = newValue;
* ```
*/
get value() {
return this._value;
}
/**
* An accessor that allows you to set a time using the `value` input.
* ```html
* public date: Date = new Date(Date.now());
* //...
* <igx-time-picker [value]="date" format="h:mm tt"></igx-time-picker>
* ```
*/
set value(value) {
const oldValue = this._value;
this._value = value;
const date = this.parseToDate(value);
if (date) {
this._dateValue = new Date();
this._dateValue.setHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
this.setSelectedValue(this._dateValue);
}
else {
this._dateValue = null;
this.setSelectedValue(null);
}
if (this.dateTimeEditor) {
this.dateTimeEditor.value = date;
}
this.emitValueChange(oldValue, this._value);
this._onChangeCallback(this._value);
}
/**
* An accessor that sets the resource strings.
* By default it uses EN resources.
*/
set resourceStrings(value) {
this._resourceStrings = Object.assign({}, this._resourceStrings, value);
}
/**
* An accessor that returns the resource strings.
*/
get resourceStrings() {
return this._resourceStrings;
}
/**
* Overrides the default text of the **OK** button.
*
* @remarks
* Defaults to the value from resource strings, `"OK"` for the built-in EN.
*
* ```html
* <igx-time-picker okButtonLabel='SET' [value]="date" format="h:mm tt"></igx-time-picker>
* ```
*/
set okButtonLabel(value) {
this._okButtonLabel = value;
}
/**
* An accessor that returns the label of ok button.
*/
get okButtonLabel() {
if (this._okButtonLabel === null) {
return this.resourceStrings.igx_time_picker_ok;
}
return this._okButtonLabel;
}
/**
* Overrides the default text of the **Cancel** button.
* @remarks
* Defaults to the value from resource strings, `"Cancel"` for the built-in EN.
* ```html
* <igx-time-picker cancelButtonLabel='Exit' [value]="date" format="h:mm tt"></igx-time-picker>
* ```
*/
set cancelButtonLabel(value) {
this._cancelButtonLabel = value;
}
/**
* An accessor that returns the label of cancel button.
*/
get cancelButtonLabel() {
if (this._cancelButtonLabel === null) {
return this.resourceStrings.igx_time_picker_cancel;
}
return this._cancelButtonLabel;
}
/**
* Delta values used to increment or decrement each editor date part on spin actions and
* to display time portions in the dropdown/dialog.
* By default `itemsDelta` is set to `{hour: 1, minute: 1, second: 1}`
* ```html
* <igx-time-picker [itemsDelta]="{hour:3, minute:5, second:10}" id="time-picker"></igx-time-picker>
* ```
*/
set itemsDelta(value) {
Object.assign(this._itemsDelta, value);
}
get itemsDelta() {
return this._itemsDelta;
}
constructor() {
super();
this._injector = inject(Injector);
this.platform = inject(PlatformUtil);
this.cdr = inject(ChangeDetectorRef);
/**
* Sets the value of the `id` attribute.
* ```html
* <igx-time-picker [id]="'igx-time-picker-5'" [displayFormat]="h:mm tt" ></igx-time-picker>
* ```
*/
this.id = `igx-time-picker-${NEXT_ID++}`;
/**
* Gets/Sets the interaction mode - dialog or drop down.
*
* @example
* ```html
* <igx-time-picker mode="dialog"></igx-time-picker>
* ```
*/
this.mode = PickerInteractionMode.DropDown;
/**
* Sets whether the seconds, minutes and hour spinning will loop back around when end value is reached.
* By default it's set to true.
* ```html
* <igx-time-picker [spinLoop]="false"></igx-time-picker>
* ```
*/
this.spinLoop = true;
/** @hidden @internal */
this.readOnly = false;
/**
* Emitted after a selection has been done.
*
* @example
* ```html
* <igx-time-picker (selected)="onSelection($event)"></igx-time-picker>
* ```
*/
this.selected = new EventEmitter();
/**
* Emitted when the picker's value changes.
*
* @remarks
* Used for `two-way` bindings.
*
* @example
* ```html
* <igx-time-picker [(value)]="date"></igx-time-picker>
* ```
*/
this.valueChange = new EventEmitter();
/**
* Emitted when the user types/spins invalid time in the time-picker editor.
*
* @example
* ```html
* <igx-time-picker (validationFailed)="onValidationFailed($event)"></igx-time-picker>
* ```
*/
this.validationFailed = new EventEmitter();
/** @hidden */
this.cleared = false;
/** @hidden */
this.isNotEmpty = false;
/** @hidden @internal */
this.displayValue = { transform: (date) => this.formatter(date) };
/** @hidden @internal */
this.hourItems = [];
/** @hidden @internal */
this.minuteItems = [];
/** @hidden @internal */
this.secondsItems = [];
/** @hidden @internal */
this.ampmItems = [];
this._resourceStrings = getCurrentResourceStrings(TimePickerResourceStringsEN);
this._okButtonLabel = null;
this._cancelButtonLabel = null;
this._itemsDelta = { hours: 1, minutes: 1, seconds: 1, fractionalSeconds: 1 };
this._ngControl = null;
this._onChangeCallback = noop;
this._onTouchedCallback = noop;
this._onValidatorChange = noop;
this._defaultDialogOverlaySettings = {
closeOnOutsideClick: true,
modal: true,
closeOnEscape: true,
outlet: this.outlet
};
this._defaultDropDownOverlaySettings = {
target: this.element.nativeElement,
modal: false,
closeOnOutsideClick: true,
scrollStrategy: new AbsoluteScrollStrategy(),
positionStrategy: new AutoPositionStrategy(),
outlet: this.outlet
};
this.locale = this.locale || this._localeId;
}
/** @hidden @internal */
onKeyDown(event) {
switch (event.key) {
case this.platform.KEYMAP.ARROW_UP:
if (event.altKey && this.isDropdown) {
this.close();
}
break;
case this.platform.KEYMAP.ARROW_DOWN:
if (event.altKey && this.isDropdown) {
this.open();
}
break;
case this.platform.KEYMAP.ESCAPE:
this.cancelButtonClick();
break;
case this.platform.KEYMAP.SPACE:
this.open();
event.preventDefault();
break;
}
}
/** @hidden @internal */
getPartValue(value, type) {
const inputDateParts = DateTimeUtil.parseDateTimeFormat(this.appliedFormat);
const part = inputDateParts.find(element => element.type === type);
return DateTimeUtil.getPartValue(value, part, part.format?.length);
}
/** @hidden @internal */
toISOString(value) {
return value.toLocaleTimeString('en-GB', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
fractionalSecondDigits: 3
});
}
// #region ControlValueAccessor
/** @hidden @internal */
writeValue(value) {
this._value = value;
const date = this.parseToDate(value);
if (date) {
this._dateValue = new Date();
this._dateValue.setHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
this.setSelectedValue(this._dateValue);
}
else {
this.setSelectedValue(null);
}
if (this.dateTimeEditor) {
this.dateTimeEditor.value = date;
}
}
/** @hidden @internal */
registerOnChange(fn) {
this._onChangeCallback = fn;
}
/** @hidden @internal */
registerOnTouched(fn) {
this._onTouchedCallback = fn;
}
/** @hidden @internal */
registerOnValidatorChange(fn) {
this._onValidatorChange = fn;
}
/** @hidden @internal */
validate(control) {
if (!control.value) {
return null;
}
// InvalidDate handling
if (isDate(control.value) && !DateTimeUtil.isValidDate(control.value)) {
return { value: true };
}
const errors = {};
const value = DateTimeUtil.isValidDate(control.value) ? control.value : DateTimeUtil.parseIsoDate(control.value);
Object.assign(errors, DateTimeUtil.validateMinMax(value, this.minValue, this.maxValue, true, false));
return Object.keys(errors).length > 0 ? errors : null;
}
/** @hidden @internal */
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
//#endregion
/** @hidden */
ngOnInit() {
this._ngControl = this._injector.get(NgControl, null);
this.minDropdownValue = this.setMinMaxDropdownValue('min', this.minDateValue);
this.maxDropdownValue = this.setMinMaxDropdownValue('max', this.maxDateValue);
this.setSelectedValue(this._dateValue);
}
/** @hidden */
ngAfterViewInit() {
super.ngAfterViewInit();
this.subscribeToDateEditorEvents();
this.subscribeToToggleDirectiveEvents();
this._defaultDropDownOverlaySettings.excludeFromOutsideClick = [this._inputGroup.element.nativeElement];
fromEvent(this.inputDirective.nativeElement, 'blur')
.pipe(takeUntil(this._destroy$))
.subscribe(() => {
if (this.collapsed) {
this.updateValidityOnBlur();
}
});
if (this._ngControl) {
this._statusChanges$ = this._ngControl.statusChanges.subscribe(this.onStatusChanged.bind(this));
this._inputGroup.isRequired = this.required;
this.cdr.detectChanges();
}
}
/** @hidden */
ngOnDestroy() {
super.ngOnDestroy();
if (this._statusChanges$) {
this._statusChanges$.unsubscribe();
}
}
/** @hidden */
getEditElement() {
return this.dateTimeEditor.nativeElement;
}
/**
* Opens the picker's dialog UI.
*
* @param settings OverlaySettings - the overlay settings to use for positioning the drop down or dialog container according to
* ```html
* <igx-time-picker #picker [value]="date"></igx-time-picker>
* <button type="button" igxButton (click)="picker.open()">Open Dialog</button>
* ```
*/
open(settings) {
if (this.disabled || !this.toggleRef.collapsed || this.readOnly) {
return;
}
this.setSelectedValue(this._dateValue);
const overlaySettings = Object.assign({}, this.isDropdown
? this.dropDownOverlaySettings
: this.dialogOverlaySettings, settings);
this.toggleRef.open(overlaySettings);
}
/**
* Closes the dropdown/dialog.
* ```html
* <igx-time-picker #timePicker></igx-time-picker>
* ```
* ```typescript
* @ViewChild('timePicker', { read: IgxTimePickerComponent }) picker: IgxTimePickerComponent;
* picker.close();
* ```
*/
close() {
this.toggleRef.close();
}
toggle(settings) {
if (this.toggleRef.collapsed) {
this.open(settings);
}
else {
this.close();
}
}
/**
* Clears the time picker value if it is a `string` or resets the time to `00:00:00` if the value is a Date object.
*
* @example
* ```typescript
* this.timePicker.clear();
* ```
*/
clear() {
if (this.disabled || this.readOnly) {
return;
}
if (!this.toggleRef.collapsed) {
this.close();
}
if (DateTimeUtil.isValidDate(this.value)) {
const oldValue = new Date(this.value);
this.value.setHours(0, 0, 0, 0);
if (this.value.getTime() !== oldValue.getTime()) {
this.emitValueChange(oldValue, this.value);
this._dateValue.setHours(0, 0, 0, 0);
this.dateTimeEditor.value = new Date(this.value);
this.setSelectedValue(this._dateValue);
}
}
else {
this.value = null;
}
}
/**
* Selects time from the igxTimePicker.
*
* @example
* ```typescript
* this.timePicker.select(date);
*
* @param date Date object containing the time to be selected.
*/
select(date) {
this.value = date;
}
/**
* Increment a specified `DatePart`.
*
* @param datePart The optional DatePart to increment. Defaults to Hour.
* @param delta The optional delta to increment by. Overrides `itemsDelta`.
* @example
* ```typescript
* this.timePicker.increment(DatePart.Hours);
* ```
*/
increment(datePart, delta) {
this.dateTimeEditor.increment(datePart, delta);
}
/**
* Decrement a specified `DatePart`
*
* @param datePart The optional DatePart to decrement. Defaults to Hour.
* @param delta The optional delta to decrement by. Overrides `itemsDelta`.
* @example
* ```typescript
* this.timePicker.decrement(DatePart.Seconds);
* ```
*/
decrement(datePart, delta) {
this.dateTimeEditor.decrement(datePart, delta);
}
/** @hidden @internal */
cancelButtonClick() {
this.setSelectedValue(this._dateValue);
this.dateTimeEditor.value = this.parseToDate(this.value);
this.close();
}
/** @hidden @internal */
okButtonClick() {
this.updateValue(this._selectedDate);
this.close();
}
/** @hidden @internal */
onItemClick(item, dateType) {
let date = new Date(this._selectedDate);
switch (dateType) {
case 'hourList': {
let ampm;
const selectedHour = parseInt(item, 10);
let hours = selectedHour;
if (this.showAmPmList) {
ampm = this.getPartValue(date, 'ampm');
hours = this.toTwentyFourHourFormat(hours, ampm);
const minHours = this.minDropdownValue?.getHours() || 0;
const maxHours = this.maxDropdownValue?.getHours() || 24;
if (hours < minHours || hours > maxHours) {
hours = hours < 12 ? hours + 12 : hours - 12;
}
}
date.setHours(hours);
date = this.validateDropdownValue(date);
if (this.valueInRange(date, this.minDropdownValue, this.maxDropdownValue)) {
this.setSelectedValue(date);
}
break;
}
case 'minuteList': {
const minutes = parseInt(item, 10);
date.setMinutes(minutes);
date = this.validateDropdownValue(date);
this.setSelectedValue(date);
break;
}
case 'secondsList': {
const seconds = parseInt(item, 10);
date.setSeconds(seconds);
if (this.valueInRange(date, this.minDropdownValue, this.maxDropdownValue)) {
this.setSelectedValue(date);
}
break;
}
case 'ampmList': {
let hour = this._selectedDate.getHours();
hour = DateTimeUtil.isAm(item)
? hour % 12
: (hour % 12) + 12;
date.setHours(hour);
date = this.validateDropdownValue(date, true);
this.setSelectedValue(date);
break;
}
}
this.updateEditorValue();
}
/** @hidden @internal */
nextHour(delta) {
delta = delta > 0 ? 1 : -1;
const previousDate = new Date(this._selectedDate);
const minHours = this.minDropdownValue?.getHours();
const maxHours = this.maxDropdownValue?.getHours();
const previousHours = previousDate.getHours();
let hours = previousHours + delta * this.itemsDelta.hours;
if ((previousHours === maxHours && delta > 0) || (previousHours === minHours && delta < 0)) {
hours = !this.spinLoop ? previousHours : delta > 0 ? minHours : maxHours;
}
this._selectedDate.setHours(hours);
this._selectedDate = this.validateDropdownValue(this._selectedDate);
this._selectedDate = new Date(this._selectedDate);
this.updateEditorValue();
}
/** @hidden @internal */
nextMinute(delta) {
delta = delta > 0 ? 1 : -1;
const minHours = this.minDropdownValue.getHours();
const maxHours = this.maxDropdownValue.getHours();
const hours = this._selectedDate.getHours();
let minutes = this._selectedDate.getMinutes();
const minMinutes = hours === minHours ? this.minDropdownValue.getMinutes() : 0;
const maxMinutes = hours === maxHours ? this.maxDropdownValue.getMinutes() :
60 % this.itemsDelta.minutes > 0 ? 60 - (60 % this.itemsDelta.minutes) :
60 - this.itemsDelta.minutes;
if ((delta < 0 && minutes === minMinutes) || (delta > 0 && minutes === maxMinutes)) {
minutes = this.spinLoop && minutes === minM