igniteui-angular
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1,554 lines • 185 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
import { CommonModule } from '@angular/common';
import { Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, NgModule, Output, TemplateRef, ViewChild, ContentChild, Injectable } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { HAMMER_GESTURE_CONFIG, HammerGestureConfig } from '@angular/platform-browser';
import { IgxIconModule } from '../icon/index';
import { IgxInputGroupModule, IgxInputGroupComponent } from '../input-group/input-group.component';
import { IgxInputDirective } from '../directives/input/input.directive';
import { IgxAmPmItemDirective, IgxHourItemDirective, IgxItemListDirective, IgxMinuteItemDirective, IgxTimePickerTemplateDirective } from './time-picker.directives';
import { Subject, fromEvent, interval, animationFrameScheduler } from 'rxjs';
import { IGX_TIME_PICKER_COMPONENT } from './time-picker.common';
import { AbsoluteScrollStrategy } from '../services/overlay/scroll';
import { AutoPositionStrategy } from '../services/overlay/position';
import { takeUntil, throttle } from 'rxjs/operators';
import { IgxButtonModule } from '../directives/button/button.directive';
import { IgxMaskModule } from '../directives/mask/mask.directive';
import { IgxToggleModule, IgxToggleDirective } from '../directives/toggle/toggle.directive';
import { TimeDisplayFormatPipe, TimeInputFormatPipe } from './time-picker.pipes';
import { CurrentResourceStrings } from '../core/i18n/resources';
import { InteractionMode } from '../core/enums';
/** @type {?} */
let NEXT_ID = 0;
/** @type {?} */
const HOURS_POS = [0, 1, 2];
/** @type {?} */
const MINUTES_POS = [3, 4, 5];
/** @type {?} */
const AMPM_POS = [6, 7, 8];
/** @type {?} */
const ITEMS_COUNT = 7;
export class TimePickerHammerConfig extends HammerGestureConfig {
constructor() {
super(...arguments);
this.overrides = {
pan: { direction: Hammer.DIRECTION_VERTICAL, threshold: 1 }
};
}
}
TimePickerHammerConfig.decorators = [
{ type: Injectable }
];
if (false) {
/** @type {?} */
TimePickerHammerConfig.prototype.overrides;
}
/**
* @record
*/
export function IgxTimePickerValueChangedEventArgs() { }
if (false) {
/** @type {?} */
IgxTimePickerValueChangedEventArgs.prototype.oldValue;
/** @type {?} */
IgxTimePickerValueChangedEventArgs.prototype.newValue;
}
/**
* @record
*/
export function IgxTimePickerValidationFailedEventArgs() { }
if (false) {
/** @type {?} */
IgxTimePickerValidationFailedEventArgs.prototype.timePicker;
/** @type {?} */
IgxTimePickerValidationFailedEventArgs.prototype.currentValue;
/** @type {?} */
IgxTimePickerValidationFailedEventArgs.prototype.setThroughUI;
}
export class IgxTimePickerComponent {
constructor() {
/**
* An \@Input property that sets the value of the `id` attribute.
* ```html
* <igx-time-picker [id]="'igx-time-picker-5'" format="h:mm tt" ></igx-time-picker>
* ```
*/
this.id = `igx-time-picker-${NEXT_ID++}`;
/**
* An \@Input property that allows you to disable the `igx-time-picker` component. By default `disabled` is set to false.
* ```html
* <igx-time-picker [disabled]="'true'" [vertical]="true" format="h:mm tt" ></igx-time-picker>
* ```
*/
this.disabled = false;
/**
* An \@Input property that gets/sets the delta by which hour and minute items would be changed <br>
* when the user presses the Up/Down keys.
* By default `itemsDelta` is set to `{hours: 1, minutes:1}`
* ```html
* <igx-time-picker [itemsDelta]="{hours:3, minutes:5}" id="time-picker"></igx-time-picker>
* ```
*/
this.itemsDelta = { hours: 1, minutes: 1 };
/**
* An \@Input property that determines the spin behavior. By default `isSpinLoop` is set to true.
* The minutes and hour spinning will wrap around by default.
* ```html
* <igx-time-picker [isSpinLoop]="false" id="time-picker"></igx-time-picker>
* ```
*/
this.isSpinLoop = true;
/**
* An \@Input property that Gets/Sets the orientation of the `igxTimePicker`. By default `vertical` is set to false.
* ```html
* <igx-time-picker [vertical]="true" id="time-picker"></igx-time-picker>
* ```
*/
this.vertical = false;
/**
* Sets the character used to prompt the user for input.
* Default value is "'-'".
* ```html
* <igx-time-picker [promptChar] = "'_'">
* ```
* \@memberof IgxTimePickerComponent
*/
this.promptChar = '-';
/**
* An \@Input property that allows you to switch the interaction mode between
* a dialog picker or dropdown with editable masked input.
* Deafult is dialog picker.
* ```html
* public mode = InteractionMode.DROPDOWN;
* //..
* <igx-time-picker [mode]="mode"></igx-time-picker>
* ```
* \@memberof IgxTimePickerComponent
*/
this.mode = InteractionMode.Dialog;
/**
* Emitted when selection is made. The event contains the selected value. Returns {`oldValue`: `Date`, `newValue`: `Date`}.
* ```typescript
* \@ViewChild("toast")
* private toast: IgxToastComponent;
* public onValueChanged(timepicker){
* this.toast.show()
* }
* //...
* ```
* ```html
* <igx-time-picker (onValueChanged)="onValueChanged($event)"></igx-time-picker>
* <igx-toast #toast message="The value has been changed!"></igx-toast>
* ```
*/
this.onValueChanged = new EventEmitter();
/**
* Emitted when an invalid value is being set. Returns {`timePicker`: `any`, `currentValue`: `Date`, `setThroughUI`: `boolean`}
* ```typescript
* public min: string = "09:00";
* public max: string = "18:00";
* \@ViewChild("toast")
* private toast: IgxToastComponent;
* public onValidationFailed(timepicker){
* this.toast.show();
* }
* //...
* ```
* ```html
* <igx-time-picker [minValue]="min" [maxValue]="max" (onValidationFailed)="onValidationFailed($event)"></igx-time-picker>
* <igx-toast #toast message="Value must be between 09:00 and 18:00!"></igx-toast>
* ```
*/
this.onValidationFailed = new EventEmitter();
/**
* Emitted when a timePicker is being opened.
* ```html
* \@ViewChild("toast")
* private toast: IgxToastComponent;
* public onOpen(timepicker){
* this.toast.show();
* }
* //...
* ```
* ```html
* <igx-time-picker [minValue]="min" [maxValue]="max" (onOpen)="onOpen($event)"></igx-time-picker>
* <igx-toast #toast message="The time picker has been opened!"></igx-toast>
* ```
*/
this.onOpen = new EventEmitter();
/**
* Emitted when a timePicker is being closed.
*/
this.onClose = new EventEmitter();
/**
* @hidden
*/
this._hourItems = [];
/**
* @hidden
*/
this._minuteItems = [];
/**
* @hidden
*/
this._ampmItems = [];
/**
* @hidden
*/
this.cleared = false;
/**
* @hidden
*/
this.isNotEmpty = false;
/**
* @hidden
*/
this.displayFormat = new TimeDisplayFormatPipe(this);
/**
* @hidden
*/
this.inputFormat = new TimeInputFormatPipe(this);
this._resourceStrings = CurrentResourceStrings.TimePickerResStrings;
this._okButtonLabel = null;
this._cancelButtonLabel = null;
this._isHourListLoop = this.isSpinLoop;
this._isMinuteListLoop = this.isSpinLoop;
this._hourView = [];
this._minuteView = [];
this._ampmView = [];
this._destroy$ = new Subject();
this._onTouchedCallback = () => { };
this._onChangeCallback = () => { };
}
/**
* 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>
* ```
* @param {?} value
* @return {?}
*/
set value(value) {
if (this._isValueValid(value)) {
/** @type {?} */
const oldVal = this._value;
this._value = value;
this._onChangeCallback(value);
/** @type {?} */
const dispVal = this._formatTime(this.value, this.format);
if (this.mode === InteractionMode.DropDown && this._displayValue !== dispVal) {
this.displayValue = dispVal;
}
/** @type {?} */
const args = {
oldValue: oldVal,
newValue: value
};
this.onValueChanged.emit(args);
}
else {
/** @type {?} */
const args = {
timePicker: this,
currentValue: value,
setThroughUI: false
};
this.onValidationFailed.emit(args);
}
}
/**
* An accessor that returns the value of `igx-time-picker` component.
* ```html
* \@ViewChild("MyPick")
* public pick: IgxTimePickerComponent;
* ngAfterViewInit(){
* let pickSelect = this.pick.value;
* }
* ```
* @return {?}
*/
get value() {
return this._value;
}
/**
* An accessor that sets the resource strings.
* By default it uses EN resources.
* @param {?} value
* @return {?}
*/
set resourceStrings(value) {
this._resourceStrings = Object.assign({}, this._resourceStrings, value);
}
/**
* An accessor that returns the resource strings.
* @return {?}
*/
get resourceStrings() {
return this._resourceStrings;
}
/**
* An \@Input property that renders OK button with custom text. By default `okButtonLabel` is set to OK.
* ```html
* <igx-time-picker okButtonLabel='SET' [value]="date" format="h:mm tt"></igx-time-picker>
* ```
* @param {?} value
* @return {?}
*/
set okButtonLabel(value) {
this._okButtonLabel = value;
}
/**
* An accessor that returns the label of ok button.
* @return {?}
*/
get okButtonLabel() {
return this._okButtonLabel || this.resourceStrings.igx_time_picker_ok;
}
/**
* An \@Input property that renders cancel button with custom text.
* By default `cancelButtonLabel` is set to Cancel.
* ```html
* <igx-time-picker cancelButtonLabel='Exit' [value]="date" format="h:mm tt"></igx-time-picker>
* ```
* @param {?} value
* @return {?}
*/
set cancelButtonLabel(value) {
this._cancelButtonLabel = value;
}
/**
* An accessor that returns the label of cancel button.
* @return {?}
*/
get cancelButtonLabel() {
return this._cancelButtonLabel || this.resourceStrings.igx_time_picker_cancel;
}
/**
* An \@Input property that Gets/Sets format of time while `igxTimePicker` does not have focus. <br>
* By default `format` is set to hh:mm tt. <br>
* List of time-flags: <br>
* `h` : hours field in 12-hours format without leading zero <br>
* `hh` : hours field in 12-hours format with leading zero <br>
* `H` : hours field in 24-hours format without leading zero <br>
* `HH` : hours field in 24-hours format with leading zero <br>
* `m` : minutes field without leading zero <br>
* `mm` : minutes field with leading zero <br>
* `tt` : 2 character string which represents AM/PM field <br>
* ```html
* <igx-time-picker format="HH:m" id="time-picker"></igx-time-picker>
* ```
* @return {?}
*/
get format() {
return this._format || 'hh:mm tt';
}
/**
* @param {?} formatValue
* @return {?}
*/
set format(formatValue) {
this._format = formatValue;
this.mask = this._format.indexOf('tt') !== -1 ? '00:00 LL' : '00:00';
if (this.displayValue) {
this.displayValue = this._formatTime(this.value, this._format);
}
}
/**
* @hidden
* @return {?}
*/
get displayValue() {
if (this._displayValue === undefined) {
return this._formatTime(this.value, this.format);
}
return this._displayValue;
}
/**
* @param {?} value
* @return {?}
*/
set displayValue(value) {
this._displayValue = value;
}
/**
* Returns the current time formatted as string using the `format` option.
* If there is no set time the return is an empty string.
* ```typescript
* \@ViewChild("MyChild")
* private picker: IgxTimePickerComponent;
* ngAfterViewInit(){
* let time = this.picker.displayTime;
* }
* ```
* @return {?}
*/
get displayTime() {
if (this.value) {
return this._formatTime(this.value, this.format);
}
return '';
}
/**
* @hidden
* @return {?}
*/
get hourView() {
return this._hourView;
}
/**
* @hidden
* @return {?}
*/
get minuteView() {
return this._minuteView;
}
/**
* @hidden
* @return {?}
*/
get ampmView() {
return this._ampmView;
}
/**
* @hidden
* @return {?}
*/
get showClearButton() {
return (this.displayValue && this.displayValue !== this.parseMask(false)) || this.isNotEmpty;
}
/**
* @hidden
* @return {?}
*/
get validMinuteEntries() {
/** @type {?} */
const minuteEntries = [];
for (let i = 0; i < 60; i++) {
minuteEntries.push(i);
}
return minuteEntries;
}
/**
* @hidden
* @return {?}
*/
get validHourEntries() {
/** @type {?} */
const hourEntries = [];
/** @type {?} */
const index = this.format.indexOf('h') !== -1 ? 13 : 24;
for (let i = 0; i < index; i++) {
hourEntries.push(i);
}
return hourEntries;
}
/**
* Gets the input group template.
* ```typescript
* let template = this.template();
* ```
* \@memberof IgxTimePickerComponent
* @return {?}
*/
get template() {
if (this.timePickerTemplateDirective) {
return this.timePickerTemplateDirective.template;
}
return this.mode === InteractionMode.Dialog ? this.defaultTimePickerTemplate : this.dropdownInputTemplate;
}
/**
* Gets the context passed to the input group template.
* \@memberof IgxTimePickerComponent
* @return {?}
*/
get context() {
return {
value: this.value,
displayTime: this.displayTime,
displayValue: this.displayValue,
openDialog: () => { this.openDialog(); }
};
}
/**
* @hidden
* @return {?}
*/
ngOnInit() {
this._generateHours();
this._generateMinutes();
if (this.format.indexOf('tt') !== -1) {
this._generateAmPm();
}
this._dropDownOverlaySettings = {
modal: false,
closeOnOutsideClick: true,
scrollStrategy: new AbsoluteScrollStrategy(),
positionStrategy: new AutoPositionStrategy()
};
}
/**
* @hidden
* @return {?}
*/
ngAfterViewInit() {
if (this.mode === InteractionMode.DropDown && this.input) {
fromEvent(this.input.nativeElement, 'keydown').pipe(throttle(() => interval(0, animationFrameScheduler)), takeUntil(this._destroy$)).subscribe((event) => {
if (event.key === "ArrowUp" /* UP_ARROW */ || event.key === "Up" /* UP_ARROW_IE */ ||
event.key === "ArrowDown" /* DOWN_ARROW */ || event.key === "Down" /* DOWN_ARROW_IE */) {
this.spinOnEdit(event);
}
});
}
if (this.container && this.group) {
this.container.nativeElement.style.width = this.group.element.nativeElement.getBoundingClientRect().width + 'px';
}
if (this.toggleRef) {
this.toggleRef.onClosed.pipe(takeUntil(this._destroy$)).subscribe(() => {
if (this._input) {
this._input.nativeElement.focus();
}
if (this.mode === InteractionMode.DropDown) {
this._onDropDownClosed();
}
this.onClose.emit(this);
});
this.toggleRef.onOpened.pipe(takeUntil(this._destroy$)).subscribe(() => {
this.onOpen.emit(this);
});
}
}
/**
* @hidden
* @return {?}
*/
ngOnDestroy() {
this._destroy$.next(true);
this._destroy$.complete();
}
/**
* @hidden
* @param {?} event
* @return {?}
*/
onKeydownSpace(event) {
this.openDialog();
event.preventDefault();
}
/**
* @hidden
* @return {?}
*/
onAltArrowDown() {
this.openDialog();
}
/**
* @private
* @param {?} item
* @param {?} items
* @param {?} selectedItem
* @param {?} isListLoop
* @param {?} viewType
* @return {?}
*/
_scrollItemIntoView(item, items, selectedItem, isListLoop, viewType) {
/** @type {?} */
let itemIntoView;
if (items) {
/** @type {?} */
const index = (item === 'AM' || item === 'PM') ? items.indexOf(item) : items.indexOf(parseInt(item, 10));
/** @type {?} */
let view;
if (index !== -1) {
if (isListLoop) {
if (index > 0) {
selectedItem = this._itemToString(items[index - 1], viewType);
itemIntoView = this._nextItem(items, selectedItem, isListLoop, viewType);
}
else {
selectedItem = this._itemToString(items[1], viewType);
itemIntoView = this._prevItem(items, selectedItem, isListLoop, viewType);
}
}
else {
view = items.slice(index - 3, index + 4);
selectedItem = this._itemToString(items[index], viewType);
itemIntoView = { selectedItem, view };
}
itemIntoView.view = this._viewToString(itemIntoView.view, viewType);
}
}
return itemIntoView;
}
/**
* @private
* @param {?} view
* @param {?} viewType
* @return {?}
*/
_viewToString(view, viewType) {
for (let i = 0; i < view.length; i++) {
if (typeof (view[i]) !== 'string') {
view[i] = this._itemToString(view[i], viewType);
}
}
return view;
}
/**
* @private
* @param {?} item
* @param {?} viewType
* @return {?}
*/
_itemToString(item, viewType) {
if (item === null) {
item = '';
}
else if (viewType && typeof (item) !== 'string') {
/** @type {?} */
const leadZeroHour = (item < 10 && (this.format.indexOf('hh') !== -1 || this.format.indexOf('HH') !== -1));
/** @type {?} */
const leadZeroMinute = (item < 10 && this.format.indexOf('mm') !== -1);
/** @type {?} */
const leadZero = (viewType === 'hour') ? leadZeroHour : leadZeroMinute;
item = (leadZero) ? '0' + item : `${item}`;
}
return item;
}
/**
* @private
* @param {?} items
* @param {?} selectedItem
* @param {?} isListLoop
* @param {?} viewType
* @return {?}
*/
_prevItem(items, selectedItem, isListLoop, viewType) {
/** @type {?} */
const selectedIndex = items.indexOf(parseInt(selectedItem, 10));
/** @type {?} */
const itemsCount = items.length;
/** @type {?} */
let view;
if (selectedIndex === -1) {
view = items.slice(0, 7);
selectedItem = items[3];
}
else if (isListLoop) {
if (selectedIndex - 4 < 0) {
view = items.slice(itemsCount - (4 - selectedIndex), itemsCount);
view = view.concat(items.slice(0, selectedIndex + 3));
}
else if (selectedIndex + 4 > itemsCount) {
view = items.slice(selectedIndex - 4, itemsCount);
view = view.concat(items.slice(0, selectedIndex + 3 - itemsCount));
}
else {
view = items.slice(selectedIndex - 4, selectedIndex + 3);
}
selectedItem = (selectedIndex === 0) ? items[itemsCount - 1] : items[selectedIndex - 1];
}
else if (selectedIndex > 3) {
view = items.slice(selectedIndex - 4, selectedIndex + 3);
selectedItem = items[selectedIndex - 1];
}
else if (selectedIndex === 3) {
view = items.slice(0, 7);
}
view = this._viewToString(view, viewType);
selectedItem = this._itemToString(selectedItem, viewType);
return {
selectedItem,
view
};
}
/**
* @private
* @param {?} items
* @param {?} selectedItem
* @param {?} isListLoop
* @param {?} viewType
* @return {?}
*/
_nextItem(items, selectedItem, isListLoop, viewType) {
/** @type {?} */
const selectedIndex = items.indexOf(parseInt(selectedItem, 10));
/** @type {?} */
const itemsCount = items.length;
/** @type {?} */
let view;
if (selectedIndex === -1) {
view = items.slice(0, 7);
selectedItem = items[3];
}
else if (isListLoop) {
if (selectedIndex < 2) {
view = items.slice(itemsCount - (2 - selectedIndex), itemsCount);
view = view.concat(items.slice(0, selectedIndex + 5));
}
else if (selectedIndex + 4 >= itemsCount) {
view = items.slice(selectedIndex - 2, itemsCount);
view = view.concat(items.slice(0, selectedIndex + 5 - itemsCount));
}
else {
view = items.slice(selectedIndex - 2, selectedIndex + 5);
}
selectedItem = (selectedIndex === itemsCount - 1) ? items[0] : items[selectedIndex + 1];
}
else if (selectedIndex + 1 < itemsCount - 3) {
view = items.slice(selectedIndex - 2, selectedIndex + 5);
selectedItem = items[selectedIndex + 1];
}
else if (selectedIndex === itemsCount - 4) {
view = items.slice(selectedIndex - 3, itemsCount);
}
view = this._viewToString(view, viewType);
selectedItem = this._itemToString(selectedItem, viewType);
return {
selectedItem,
view
};
}
/**
* @private
* @param {?} value
* @param {?} format
* @return {?}
*/
_formatTime(value, format) {
if (!value) {
return '';
}
else {
/** @type {?} */
let hour = value.getHours();
/** @type {?} */
const minute = value.getMinutes();
/** @type {?} */
let formattedMinute;
/** @type {?} */
let formattedHour;
/** @type {?} */
let amPM;
if (format.indexOf('h') !== -1) {
amPM = (hour > 11) ? 'PM' : 'AM';
if (hour > 12) {
hour -= 12;
formattedHour = hour < 10 && format.indexOf('hh') !== -1 ? '0' + hour : `${hour}`;
}
else if (hour === 0) {
formattedHour = '12';
}
else if (hour < 10 && format.indexOf('hh') !== -1) {
formattedHour = '0' + hour;
}
else {
formattedHour = `${hour}`;
}
}
else {
if (hour < 10 && format.indexOf('HH') !== -1) {
formattedHour = '0' + hour;
}
else {
formattedHour = `${hour}`;
}
}
formattedMinute = minute < 10 && format.indexOf('mm') !== -1 ? '0' + minute : `${minute}`;
return format.replace('hh', formattedHour).replace('h', formattedHour)
.replace('HH', formattedHour).replace('H', formattedHour)
.replace('mm', formattedMinute).replace('m', formattedMinute)
.replace('tt', amPM);
}
}
/**
* @private
* @param {?} start
* @param {?} end
* @return {?}
*/
_updateHourView(start, end) {
this._hourView = this._viewToString(this._hourItems.slice(start, end), 'hour');
}
/**
* @private
* @param {?} start
* @param {?} end
* @return {?}
*/
_updateMinuteView(start, end) {
this._minuteView = this._viewToString(this._minuteItems.slice(start, end), 'minute');
}
/**
* @private
* @param {?} start
* @param {?} end
* @return {?}
*/
_updateAmPmView(start, end) {
this._ampmView = this._ampmItems.slice(start, end);
}
/**
* @private
* @param {?} items
* @return {?}
*/
_addEmptyItems(items) {
for (let i = 0; i < 3; i++) {
items.push(null);
}
}
/**
* @private
* @return {?}
*/
_generateHours() {
/** @type {?} */
let hourItemsCount = 24;
if (this.format.indexOf('h') !== -1) {
hourItemsCount = 13;
}
hourItemsCount /= this.itemsDelta.hours;
/** @type {?} */
let i = this.format.indexOf('H') !== -1 ? 0 : 1;
if (hourItemsCount < 7 || !this.isSpinLoop) {
this._addEmptyItems(this._hourItems);
this._isHourListLoop = false;
}
if (hourItemsCount > 1) {
for (i; i < hourItemsCount; i++) {
this._hourItems.push(i * this.itemsDelta.hours);
}
}
else {
this._hourItems.push(0);
}
if (hourItemsCount < 7 || !this.isSpinLoop) {
this._addEmptyItems(this._hourItems);
}
}
/**
* @private
* @return {?}
*/
_generateMinutes() {
/** @type {?} */
const minuteItemsCount = 60 / this.itemsDelta.minutes;
if (minuteItemsCount < 7 || !this.isSpinLoop) {
this._addEmptyItems(this._minuteItems);
this._isMinuteListLoop = false;
}
for (let i = 0; i < minuteItemsCount; i++) {
this._minuteItems.push(i * this.itemsDelta.minutes);
}
if (minuteItemsCount < 7 || !this.isSpinLoop) {
this._addEmptyItems(this._minuteItems);
}
}
/**
* @private
* @return {?}
*/
_generateAmPm() {
this._addEmptyItems(this._ampmItems);
this._ampmItems.push('AM');
this._ampmItems.push('PM');
this._addEmptyItems(this._ampmItems);
}
/**
* @private
* @return {?}
*/
_getSelectedTime() {
/** @type {?} */
const date = this.value ? new Date(this.value) : new Date();
date.setHours(parseInt(this.selectedHour, 10));
date.setMinutes(parseInt(this.selectedMinute, 10));
date.setSeconds(0);
if (this.selectedAmPm === 'PM' && this.selectedHour !== '12') {
date.setHours(date.getHours() + 12);
}
if (this.selectedAmPm === 'AM' && this.selectedHour === '12') {
date.setHours(0);
}
return date;
}
/**
* @private
* @param {?} value
* @return {?}
*/
_convertMinMaxValue(value) {
/** @type {?} */
const date = this.value ? new Date(this.value) : this._dateFromModel ? new Date(this._dateFromModel) : new Date();
/** @type {?} */
const sections = value.split(/[\s:]+/);
date.setHours(parseInt(sections[0], 10));
date.setMinutes(parseInt(sections[1], 10));
date.setSeconds(0);
if (sections[2] && sections[2] === 'PM' && sections[0] !== '12') {
date.setHours(date.getHours() + 12);
}
if (sections[0] === '12' && sections[2] && sections[2] === 'AM') {
date.setHours(0);
}
return date;
}
/**
* @private
* @param {?} value
* @return {?}
*/
_isValueValid(value) {
if (this.maxValue && value > this._convertMinMaxValue(this.maxValue)) {
return false;
}
else if (this.minValue && value < this._convertMinMaxValue(this.minValue)) {
return false;
}
else {
return true;
}
}
/**
* @private
* @param {?} val
* @return {?}
*/
_isEntryValid(val) {
/** @type {?} */
const sections = val.split(/[\s:]+/);
/** @type {?} */
const re = new RegExp(this.promptChar, 'g');
/** @type {?} */
const hour = parseInt(sections[0].replace(re, ''), 10);
/** @type {?} */
const minutes = parseInt(sections[1].replace(re, ''), 10);
return this.validHourEntries.indexOf(hour) !== -1 && this.validMinuteEntries.indexOf(minutes) !== -1;
}
/**
* @private
* @return {?}
*/
_getCursorPosition() {
return this.input.nativeElement.selectionStart;
}
/**
* @private
* @param {?} start
* @param {?=} end
* @return {?}
*/
_setCursorPosition(start, end = start) {
this.input.nativeElement.setSelectionRange(start, end);
}
/**
* @private
* @return {?}
*/
_updateEditableInput() {
if (this.mode === InteractionMode.DropDown) {
this.displayValue = this._formatTime(this._getSelectedTime(), this.format);
}
}
/**
* @private
* @param {?} currentVal
* @param {?} minVal
* @param {?} maxVal
* @param {?} hDelta
* @param {?} sign
* @return {?}
*/
_spinHours(currentVal, minVal, maxVal, hDelta, sign) {
/** @type {?} */
const oldVal = new Date(currentVal);
currentVal.setMinutes(sign * hDelta);
if (currentVal.getDate() !== oldVal.getDate() && this.isSpinLoop) {
currentVal.setDate(oldVal.getDate());
}
/** @type {?} */
let minutes = currentVal.getMinutes();
if (currentVal.getTime() > maxVal.getTime()) {
if (this.isSpinLoop) {
minutes = minutes < minVal.getMinutes() ? 60 + minutes : minutes;
minVal.setMinutes(sign * minutes);
return minVal;
}
else {
return oldVal;
}
}
else if (currentVal.getTime() < minVal.getTime()) {
if (this.isSpinLoop) {
minutes = minutes <= maxVal.getMinutes() ? minutes : minutes - 60;
maxVal.setMinutes(minutes);
return maxVal;
}
else {
return oldVal;
}
}
else {
return currentVal;
}
}
/**
* @private
* @param {?} currentVal
* @param {?} mDelta
* @param {?} sign
* @return {?}
*/
_spinMinutes(currentVal, mDelta, sign) {
/** @type {?} */
let minutes = currentVal.getMinutes() + (sign * mDelta);
if (minutes < 0 || minutes >= 60) {
minutes = this.isSpinLoop ? minutes - (sign * 60) : currentVal.getMinutes();
}
currentVal.setMinutes(minutes);
return currentVal;
}
/**
* @private
* @return {?}
*/
_initializeContainer() {
if (this.value) {
/** @type {?} */
const formttedTime = this._formatTime(this.value, this.format);
/** @type {?} */
const sections = formttedTime.split(/[\s:]+/);
this.selectedHour = sections[0];
this.selectedMinute = sections[1];
if (this._ampmItems !== null) {
this.selectedAmPm = sections[2];
}
}
if (this.selectedHour === undefined) {
this.selectedHour = `${this._hourItems[3]}`;
}
if (this.selectedMinute === undefined) {
this.selectedMinute = '0';
}
if (this.selectedAmPm === undefined && this._ampmItems !== null) {
this.selectedAmPm = this._ampmItems[3];
}
this._prevSelectedHour = this.selectedHour;
this._prevSelectedMinute = this.selectedMinute;
this._prevSelectedAmPm = this.selectedAmPm;
this._onTouchedCallback();
this._updateHourView(0, ITEMS_COUNT);
this._updateMinuteView(0, ITEMS_COUNT);
this._updateAmPmView(0, ITEMS_COUNT);
if (this.selectedHour) {
this.scrollHourIntoView(this.selectedHour);
}
if (this.selectedMinute) {
this.scrollMinuteIntoView(this.selectedMinute);
}
if (this.selectedAmPm) {
this.scrollAmPmIntoView(this.selectedAmPm);
}
requestAnimationFrame(() => {
this.hourList.nativeElement.focus();
});
}
/**
* @private
* @return {?}
*/
_closeDropDown() {
this.toggleRef.close();
this._onDropDownClosed();
}
/**
* @private
* @return {?}
*/
_onDropDownClosed() {
/** @type {?} */
const oldValue = this.value;
/** @type {?} */
const newVal = this._convertMinMaxValue(this.displayValue);
if (this._isValueValid(newVal)) {
if (!this.value || oldValue.getTime() !== newVal.getTime()) {
this.value = newVal;
}
}
else {
this.displayValue = this.inputFormat.transform(this._formatTime(oldValue, this.format));
/** @type {?} */
const args = {
timePicker: this,
currentValue: newVal,
setThroughUI: true
};
this.onValidationFailed.emit(args);
}
}
/**
* @hidden
* @return {?}
*/
getEditElement() {
return this._input.nativeElement;
}
/**
* @hidden
* @param {?} value
* @return {?}
*/
writeValue(value) {
// use this flag to make sure that min/maxValue are checked (in _convertMinMaxValue() method)
// against the real value when initializing the component and value is bound via ngModel
this._dateFromModel = value;
this.value = value;
if (this.mode === InteractionMode.DropDown) {
this.displayValue = this._formatTime(this.value, this.format);
}
}
/**
* @hidden
* @param {?} fn
* @return {?}
*/
registerOnChange(fn) { this._onChangeCallback = fn; }
/**
* @hidden
* @param {?} fn
* @return {?}
*/
registerOnTouched(fn) { this._onTouchedCallback = fn; }
/**
* opens the dialog.
* ```html
* <igx-time-picker #timePicker></igx-time-picker>
* ```
* ```typescript
* \@ViewChild('timePicker', { read: IgxTimePickerComponent }) picker: IgxTimePickerComponent;
* picker.openDialog();
* ```
* @param {?=} timePicker
* @return {?}
*/
openDialog(timePicker = this) {
if (this.toggleRef.collapsed) {
/** @type {?} */
let settings;
if (this.mode === InteractionMode.Dialog && this.overlaySettings) {
settings = this.overlaySettings;
}
if (this.mode === InteractionMode.DropDown) {
settings = this.overlaySettings || this._dropDownOverlaySettings;
/** @type {?} */
const posStrategy = settings.positionStrategy;
if (this.group && posStrategy) {
posStrategy.settings.target = this.group.element.nativeElement;
}
else if (this.templateDropDownTarget && posStrategy) {
posStrategy.settings.target = this.templateDropDownTarget.nativeElement;
}
else if (!posStrategy || (posStrategy && !posStrategy.settings.target)) {
throw new Error('There is no target element for the dropdown to attach.' +
'Mark a DOM element with #dropDownTarget ref variable or provide correct overlay positionStrategy.');
}
}
if (this.outlet) {
settings.outlet = this.outlet;
}
this.toggleRef.open(settings);
this._initializeContainer();
}
else if (this.mode === InteractionMode.DropDown) {
this._closeDropDown();
}
}
/**
* Scrolls a hour item into view.
* ```typescript
* scrhintoView(picker) {
* picker.scrollHourIntoView('2');
* }
* ```
* ```html
* <igx-time-picker #picker format="h:mm tt" (onOpen)="scrhintoView(picker)"></igx-time-picker>
* ```
* @param {?} item to be scrolled in view.
* @return {?}
*/
scrollHourIntoView(item) {
/** @type {?} */
const hourIntoView = this._scrollItemIntoView(item, this._hourItems, this.selectedHour, this._isHourListLoop, 'hour');
if (hourIntoView) {
this._hourView = hourIntoView.view;
this.selectedHour = hourIntoView.selectedItem;
this._updateEditableInput();
}
}
/**
* Scrolls a minute item into view.
* ```typescript
* scrMintoView(picker) {
* picker.scrollMinuteIntoView('3');
* }
* ```
* ```html
* <igx-time-picker #picker format="h:mm tt" (onOpen)="scrMintoView(picker)"></igx-time-picker>
* ```
* @param {?} item to be scrolled in view.
* @return {?}
*/
scrollMinuteIntoView(item) {
/** @type {?} */
const minuteIntoView = this._scrollItemIntoView(item, this._minuteItems, this.selectedMinute, this._isMinuteListLoop, 'minute');
if (minuteIntoView) {
this._minuteView = minuteIntoView.view;
this.selectedMinute = minuteIntoView.selectedItem;
this._updateEditableInput();
}
}
/**
* Scrolls an ampm item into view.
* ```typescript
* scrAmPmIntoView(picker) {
* picker.scrollAmPmIntoView('PM');
* }
* ```
* ```html
* <igx-time-picker #picker format="h:mm tt" (onOpen)="scrAmPmIntoView(picker)"></igx-time-picker>
* ```
* @param {?} item to be scrolled in view.
* @return {?}
*/
scrollAmPmIntoView(item) {
/** @type {?} */
const ampmIntoView = this._scrollItemIntoView(item, this._ampmItems, this.selectedAmPm, false, null);
if (ampmIntoView) {
this._ampmView = ampmIntoView.view;
this.selectedAmPm = ampmIntoView.selectedItem;
this._updateEditableInput();
}
}
/**
* @hidden
* @return {?}
*/
nextHour() {
/** @type {?} */
const nextHour = this._nextItem(this._hourItems, this.selectedHour, this._isHourListLoop, 'hour');
this._hourView = nextHour.view;
this.selectedHour = nextHour.selectedItem;
this._updateEditableInput();
}
/**
* @hidden
* @return {?}
*/
prevHour() {
/** @type {?} */
const prevHour = this._prevItem(this._hourItems, this.selectedHour, this._isHourListLoop, 'hour');
this._hourView = prevHour.view;
this.selectedHour = prevHour.selectedItem;
this._updateEditableInput();
}
/**
* @hidden
* @return {?}
*/
nextMinute() {
/** @type {?} */
const nextMinute = this._nextItem(this._minuteItems, this.selectedMinute, this._isMinuteListLoop, 'minute');
this._minuteView = nextMinute.view;
this.selectedMinute = nextMinute.selectedItem;
this._updateEditableInput();
}
/**
* @hidden
* @return {?}
*/
prevMinute() {
/** @type {?} */
const prevMinute = this._prevItem(this._minuteItems, this.selectedMinute, this._isMinuteListLoop, 'minute');
this._minuteView = prevMinute.view;
this.selectedMinute = prevMinute.selectedItem;
this._updateEditableInput();
}
/**
* @hidden
* @return {?}
*/
nextAmPm() {
/** @type {?} */
const selectedIndex = this._ampmItems.indexOf(this.selectedAmPm);
if (selectedIndex + 1 < this._ampmItems.length - 3) {
this._updateAmPmView(selectedIndex - 2, selectedIndex + 5);
this.selectedAmPm = this._ampmItems[selectedIndex + 1];
this._updateEditableInput();
}
}
/**
* @hidden
* @return {?}
*/
prevAmPm() {
/** @type {?} */
const selectedIndex = this._ampmItems.indexOf(this.selectedAmPm);
if (selectedIndex > 3) {
this._updateAmPmView(selectedIndex - 4, selectedIndex + 3);
this.selectedAmPm = this._ampmItems[selectedIndex - 1];
this._updateEditableInput();
}
}
/**
* If current value is valid selects it, closes the dialog and returns true, otherwise returns false.
* ```html
* <igx-dialog class="igx-time-picker__dialog-popup" [rightButtonLabel]="okButtonLabel" (onRightButtonSelect)="okButtonClick()">
* //..
* </igx-dialog>
* ```
* @return {?}
*/
okButtonClick() {
/** @type {?} */
const time = this._getSelectedTime();
if (this._isValueValid(time)) {
this.hideOverlay();
this.value = time;
return true;
}
else {
/** @type {?} */
const args = {
timePicker: this,
currentValue: time,
setThroughUI: true
};
this.onValidationFailed.emit(args);
return false;
}
}
/**
* Closes the dialog without selecting the current value.
* ```html
* <igx-dialog class="igx-time-picker__dialog-popup" [leftButtonLabel]="cancelButtonLabel" (onLeftButtonSelect)="cancelButtonClick()">
* //...
* </igx-dialog>
* ```
* @return {?}
*/
cancelButtonClick() {
this.hideOverlay();
this.selectedHour = this._prevSelectedHour;
this.selectedMinute = this._prevSelectedMinute;
this.selectedAmPm = this._prevSelectedAmPm;
}
/**
* Returns an array of the hours currently in view.
* ```html
* \@ViewChild("MyChild")
* private picker: IgxTimePickerComponent;
* ngAfterViewInit(){
* let hInView = this.picker.hoursInView;
* }
* ```
* @return {?}
*/
hoursInView() {
return this._hourView.filter((hour) => hour !== '');
}
/**
* Returns an array of the minutes currently in view.
* ```html
* \@ViewChild("MyChild")
* private picker: IgxTimePickerComponent;
* ngAfterViewInit(){
* let minInView = this.picker.minutesInView;
* }
* ```
* @return {?}
*/
minutesInView() {
return this._minuteView.filter((minute) => minute !== '');
}
/**
* Returns an array of the AM/PM currently in view.
* ```html
* \@ViewChild("MyChild")
* private picker: IgxTimePickerComponent;
* ngAfterViewInit(){
* let ApInView = this.picker.ampmInView;
* }
* ```
* @return {?}
*/
ampmInView() {
return this._ampmView.filter((ampm) => ampm !== '');
}
/**
* @hidden
* @return {?}
*/
hideOverlay() {
this.toggleRef.close();
}
/**
* @hidden
* @param {?=} preserveAmPm
* @return {?}
*/
parseMask(preserveAmPm = true) {
/** @type {?} */
const prompts = this.promptChar + this.promptChar;
/** @type {?} */
const amPm = preserveAmPm ? 'AM' : prompts;
return this.format.indexOf('tt') !== -1 ? `${prompts}:${prompts} ${amPm}` : `${prompts}:${prompts}`;
}
/**
* @hidden
* @return {?}
*/
clear() {
if (this.toggleRef.collapsed) {
this.cleared = true;
this.isNotEmpty = false;
/** @type {?} */
const oldVal = new Date(this.value);
this.displayValue = '';
this.value.setHours(0, 0);
if (oldVal.getTime() !== this.value.getTime()) {
/** @type {?} */
const args = {
oldValue: oldVal,
newValue: this.value
};
this.onValueChanged.emit(args);
}
}
else {
this.hideOverlay();
}
}
/**
* @hidden
* @param {?} event
* @return {?}
*/
onInput(event) {
/** @type {?} */
const val = event.target.value;
/** @type {?} */
const oldVal = new Date(this.value);
this.isNotEmpty = val !== this.parseMask(false);
// handle cases where all empty positions (promts) are filled and we want to update
// timepicker own value property if it is a valid Date
if (val.indexOf(this.promptChar) === -1) {
if (this._isEntryValid(val)) {
/** @type {?} */
const newVal = this._convertMinMaxValue(val);
if (oldVal.getTime() !== newVal.getTime()) {
this.value = newVal;
}
}
else {
/** @type {?} */
const args = {
timePicker: this,
currentValue: val,
setThroughUI: false
};
this.onValidationFailed.emit(args);
}
// handle cases where the user deletes the display value (when pressing backspace or delete)
}
else if (!this.value || !val || val === this.parseMask(false)) {
this.isNotEmpty = false;
this.value.setHours(0, 0);
this.displayValue = val;
if (oldVal.getTime() !== this.value.getTime()) {
/** @type {?} */
const args = {
oldValue: oldVal,
newValue: this.value
};
this.onValueChanged.emit(args);
}
}
}
/**
* @hidden
* @param {?} event
* @return {?}
*/
onFocus(event) {
this.isNotEmpty = event.target.value !== this.parseMask(false);
}
/**
* @hidden
* @param {?} event
* @return {?}
*/
onBlur(event) {
/** @type {?} */
const value = event.target.value;
this.isNotEmpty = value !== '';
this.displayValue = value;
if (value && value !== this.parseMask()) {
if (this._isEntryValid(value)) {
/** @type {?} */
const newVal = this._convertMinMaxValue(value);
if (!this.value || this.value.getTime() !== newVal.getTime()) {
this.value = newVal;
}
}
else {
/** @type {?} */
const args = {
timePicker: this,
currentValue: value,
setThroughUI: false
};
this.onValidationFailed.emit(args);
}
}
}
/**
* @hidden
* @param {?} event
* @return {?}
*/
spinOnEdit(event) {
event.preventDefault();
/** @type {?} */
let sign;
/** @type {?} */
let displayVal;
/** @type {?} */
const currentVal = new Date(this.value);
/** @type {?} */
const min = this.minValue ? this._convertMinMaxValue(this.minValue) : this._convertMinMaxValue('00:00');
/** @type {?} */
const max = this.maxValue ? this._convertMinMaxValue(this.maxValue) : this._convertMinMaxValue('24:00');
/** @type {?} */
const cursor = this._getCursorPosition();
if (event.key) {
/** @type {?} */
const key = event.key;
sign = key === "ArrowDown" /* DOWN_ARROW */ || key === "Down" /* DOWN_ARROW_IE */ ? -1 : 1;
}
if (event.deltaY) {
sign = event.deltaY < 0 ? 1 : -1;
}
if (!this.displayValue) {
this.value = min;
displayVal = this._formatTime(this.value, this.format);
}
else {
/** @type {?} */
const hDelta = this.itemsDelta.hours * 60 + (sign * this.value.getMinutes());
/** @type {?} */
const