UNPKG

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
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