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