ngx-material-timepicker-decon
Version:
Handy material design timepicker for angular DECONied
1,317 lines (1,299 loc) • 91.3 kB
JavaScript
import { BehaviorSubject, Subject, merge } from 'rxjs';
import { filter } from 'rxjs/operators';
import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
import { animate, style, transition, trigger, sequence } from '@angular/animations';
import { DateTime } from 'luxon';
import { DomSanitizer } from '@angular/platform-browser';
import { DOCUMENT, CommonModule } from '@angular/common';
import { EventEmitter, Injectable, Directive, ElementRef, Inject, Input, Optional, Pipe, HostListener, Component, Output, ViewEncapsulation, ContentChild, ChangeDetectionStrategy, ViewChild, NgModule, forwardRef, defineInjectable } from '@angular/core';
import Keyboard from 'simple-keyboard';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @enum {string} */
const TimePeriod = {
AM: 'AM',
PM: 'PM',
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @enum {string} */
const TimeFormat = {
TWELVE: 'hh:mm a',
TWELVE_SHORT: 'h:m a',
TWENTY_FOUR: 'HH:mm',
TWENTY_FOUR_SHORT: 'H:m',
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @param {?} time
* @param {?} compareWith
* @param {?=} unit
* @return {?}
*/
function isSameOrAfter(time, compareWith, unit = 'minutes') {
if (unit === 'hours') {
return time.hour >= compareWith.hour;
}
if (unit === 'minutes') {
return time.hasSame(compareWith, unit) || time.valueOf() > compareWith.valueOf();
}
}
/**
* @param {?} time
* @param {?} compareWith
* @param {?=} unit
* @return {?}
*/
function isSameOrBefore(time, compareWith, unit = 'minutes') {
if (unit === 'hours') {
return time.hour <= compareWith.hour;
}
if (unit === 'minutes') {
return time.hasSame(compareWith, unit) || time.valueOf() <= compareWith.valueOf();
}
}
/**
* @param {?} time
* @param {?} before
* @param {?} after
* @param {?=} unit
* @return {?}
*/
function isBetween(time, before, after, unit = 'minutes') {
if (unit === 'hours') {
return isSameOrBefore(time, after, unit) && isSameOrAfter(time, before, unit);
}
if (unit === 'minutes') {
return isSameOrBefore(time, after) && isSameOrAfter(time, before);
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
// @dynamic
class TimeAdapter {
/**
* @param {?} time
* @param {?=} format
* @return {?}
*/
static parseTime(time, format = 12) {
if (time.indexOf(':') === -1) {
return 'Invalid time';
}
/** @type {?} */
let period = time.trim().substr(time.length - 2).toUpperCase();
/** @type {?} */
const isPeriodValid = period === TimePeriod.AM || period === TimePeriod.PM;
const [h, m] = time.split(':');
if (format === 24) {
/** @type {?} */
const formattedHours = isPeriodValid ? this.formatHour(+h, 12, (/** @type {?} */ (period))) : +h;
return `${formattedHours}:${parseInt(m, 10)}`;
}
/** @type {?} */
const isPM = +h > 12;
/** @type {?} */
const hours = isPM ? +h - 12 : +h;
period = isPeriodValid ? period : isPM ? TimePeriod.PM : TimePeriod.AM;
return `${hours}:${parseInt(m, 10)} ${period}`;
}
/**
* @param {?} time
* @param {?=} format
* @return {?}
*/
static formatTime(time, format = 12) {
/** @type {?} */
const timeFormat = (format === 24) ? TimeFormat.TWENTY_FOUR : TimeFormat.TWELVE;
/** @type {?} */
const timeMask = (format === 24) ? TimeFormat.TWENTY_FOUR_SHORT : TimeFormat.TWELVE_SHORT;
return DateTime.fromFormat(this.parseTime(time, format), timeMask).toFormat(timeFormat).toLowerCase();
}
/**
* @param {?} time
* @param {?=} format
* @return {?}
*/
static convertTimeToDateTime(time, format = 12) {
/** @type {?} */
const timeMask = (format === 24) ? TimeFormat.TWENTY_FOUR_SHORT : TimeFormat.TWELVE_SHORT;
return DateTime.fromFormat(this.parseTime(time, format), timeMask);
}
/**
* @param {?} time
* @param {?=} min
* @param {?=} max
* @param {?=} granularity
* @param {?=} minutesGap
* @param {?=} format
* @return {?}
*/
static isTimeAvailable(time, min, max, granularity, minutesGap, format) {
if (!time) {
return;
}
/** @type {?} */
const convertedTime = this.convertTimeToDateTime(time, format);
/** @type {?} */
const minutes = convertedTime.minute;
if (minutesGap && (minutes % minutesGap !== 0)) {
throw new Error(`Your minutes - ${minutes} doesn\'t match your minutesGap - ${minutesGap}`);
}
/** @type {?} */
const isAfter = (min && !max)
&& isSameOrAfter(convertedTime, min, granularity);
/** @type {?} */
const isBefore = (max && !min)
&& isSameOrBefore(convertedTime, max, granularity);
/** @type {?} */
const between = (min && max)
&& isBetween(convertedTime, min, max, granularity);
/** @type {?} */
const isAvailable = !min && !max;
return isAfter || isBefore || between || isAvailable;
}
/**
*
* Format hour according to time format (12 or 24)
* @param {?} currentHour
* @param {?} format
* @param {?} period
* @return {?}
*/
static formatHour(currentHour, format, period) {
if (format === 24) {
return currentHour;
}
/** @type {?} */
const hour = period === TimePeriod.AM ? currentHour : currentHour + 12;
if (period === TimePeriod.AM && hour === 12) {
return 0;
}
else if (period === TimePeriod.PM && hour === 24) {
return 12;
}
return hour;
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @type {?} */
const DEFAULT_HOUR = {
time: 12,
angle: 360
};
/** @type {?} */
const DEFAULT_MINUTE = {
time: 0,
angle: 360
};
class NgxMaterialTimepickerService {
constructor() {
this.hourSubject = new BehaviorSubject(DEFAULT_HOUR);
this.minuteSubject = new BehaviorSubject(DEFAULT_MINUTE);
this.periodSubject = new BehaviorSubject(TimePeriod.AM);
this.keyboardClick = new EventEmitter();
}
/**
* @param {?} hour
* @return {?}
*/
set hour(hour) {
this.hourSubject.next(hour);
}
/**
* @return {?}
*/
get selectedHour() {
return this.hourSubject.asObservable();
}
/**
* @param {?} minute
* @return {?}
*/
set minute(minute) {
this.minuteSubject.next(minute);
}
/**
* @return {?}
*/
get selectedMinute() {
return this.minuteSubject.asObservable();
}
/**
* @param {?} period
* @return {?}
*/
set period(period) {
/** @type {?} */
const isPeriodValid = (period === TimePeriod.AM) || (period === TimePeriod.PM);
if (isPeriodValid) {
this.periodSubject.next(period);
}
}
/**
* @return {?}
*/
get selectedPeriod() {
return this.periodSubject.asObservable();
}
/**
* @param {?} time
* @param {?} min
* @param {?} max
* @param {?} format
* @param {?=} minutesGap
* @return {?}
*/
setDefaultTimeIfAvailable(time, min, max, format, minutesGap) {
/* Workaround to double error message*/
try {
if (TimeAdapter.isTimeAvailable(time, min, max, 'minutes', minutesGap)) {
this.setDefaultTime(TimeAdapter.formatTime(time, format), format);
}
}
catch (e) {
console.error(e);
}
}
/**
* @param {?} format
* @return {?}
*/
getFullTime(format) {
/** @type {?} */
const hour = this.hourSubject.getValue().time;
/** @type {?} */
const minute = this.minuteSubject.getValue().time;
/** @type {?} */
const period = format === 12 ? this.periodSubject.getValue() : '';
return TimeAdapter.formatTime(`${hour}:${minute} ${period}`, format);
}
/**
* @private
* @param {?} time
* @param {?} format
* @return {?}
*/
setDefaultTime(time, format) {
/** @type {?} */
const defaultTime = TimeAdapter.convertTimeToDateTime(time, format).toJSDate();
if (DateTime.fromJSDate(defaultTime).isValid) {
/** @type {?} */
const period = time.substr(time.length - 2).toUpperCase();
/** @type {?} */
const hour = defaultTime.getHours();
this.hour = Object.assign({}, DEFAULT_HOUR, { time: formatHourByPeriod(hour, (/** @type {?} */ (period))) });
this.minute = Object.assign({}, DEFAULT_MINUTE, { time: defaultTime.getMinutes() });
this.period = (/** @type {?} */ (period));
}
else {
this.resetTime();
}
}
/**
* @private
* @return {?}
*/
resetTime() {
this.hour = Object.assign({}, DEFAULT_HOUR);
this.minute = Object.assign({}, DEFAULT_MINUTE);
this.period = TimePeriod.AM;
}
}
NgxMaterialTimepickerService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */ NgxMaterialTimepickerService.ngInjectableDef = defineInjectable({ factory: function NgxMaterialTimepickerService_Factory() { return new NgxMaterialTimepickerService(); }, token: NgxMaterialTimepickerService, providedIn: "root" });
/**
*
* Format hour in 24hours format to meridian (AM or PM) format
* @param {?} hour
* @param {?} period
* @return {?}
*/
function formatHourByPeriod(hour, period) {
switch (period) {
case TimePeriod.AM:
return hour === 0 ? 12 : hour;
case TimePeriod.PM:
return hour === 12 ? 12 : hour - 12;
default:
return hour;
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @enum {number} */
const TimeUnit = {
HOUR: 0,
MINUTE: 1,
};
TimeUnit[TimeUnit.HOUR] = 'HOUR';
TimeUnit[TimeUnit.MINUTE] = 'MINUTE';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class NgxMaterialTimepickerEventService {
constructor() {
this.backdropClickSubject = new Subject();
this.keydownEventSubject = new Subject();
}
/**
* @return {?}
*/
get backdropClick() {
return this.backdropClickSubject.asObservable();
}
/**
* @return {?}
*/
get keydownEvent() {
return this.keydownEventSubject.asObservable();
}
/**
* @param {?} event
* @return {?}
*/
dispatchEvent(event) {
switch (event.type) {
case 'click':
this.backdropClickSubject.next((/** @type {?} */ (event)));
break;
case 'keydown':
this.keydownEventSubject.next((/** @type {?} */ (event)));
break;
default:
throw new Error('no such event type');
}
}
}
NgxMaterialTimepickerEventService.decorators = [
{ type: Injectable, args: [{
providedIn: 'root'
},] }
];
/** @nocollapse */ NgxMaterialTimepickerEventService.ngInjectableDef = defineInjectable({ factory: function NgxMaterialTimepickerEventService_Factory() { return new NgxMaterialTimepickerEventService(); }, token: NgxMaterialTimepickerEventService, providedIn: "root" });
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @enum {string} */
const AnimationState = {
ENTER: 'enter',
LEAVE: 'leave',
};
/** @enum {number} */
const KeyboardTypes = {
'NUMPAD': 0,
'CLOCK': 1,
};
KeyboardTypes[KeyboardTypes['NUMPAD']] = 'NUMPAD';
KeyboardTypes[KeyboardTypes['CLOCK']] = 'CLOCK';
/** @type {?} */
const ESCAPE = 27;
class NgxMaterialTimepickerComponent {
/**
* @param {?} timepickerService
* @param {?} eventService
*/
constructor(timepickerService, eventService) {
this.timepickerService = timepickerService;
this.eventService = eventService;
this.timeUnit = TimeUnit;
this.activeTimeUnit = TimeUnit.HOUR;
this.isOpened = false;
this.keyboardTypes = KeyboardTypes;
this.isEsc = true;
this.keyboardType = KeyboardTypes.NUMPAD;
this.keyboardTypeChanged = new EventEmitter();
this.timeSet = new EventEmitter();
this.opened = new EventEmitter();
this.closed = new EventEmitter();
this.hourSelected = new EventEmitter();
this.subscriptions = [];
this.subscriptions.push(merge(this.eventService.backdropClick, this.eventService.keydownEvent.pipe(filter(e => e.keyCode === ESCAPE && this.isEsc)))
.subscribe(() => this.close()));
}
/**
* @param {?} gap
* @return {?}
*/
set minutesGap(gap) {
if (gap == null) {
return;
}
gap = Math.floor(gap);
this._minutesGap = gap <= 59 ? gap : 1;
}
/**
* @return {?}
*/
get minutesGap() {
return this._minutesGap;
}
/**
* @param {?} time
* @return {?}
*/
set defaultTime(time) {
this.setDefaultTime(time);
}
/**
* @return {?}
*/
static blurAll() {
// const tmp = document.createElement('input');
// document.body.appendChild(tmp);
// tmp.focus();
// document.body.removeChild(tmp);
/** @type {?} */
const btn = document.querySelector('.hidden-button');
if (btn) {
btn.focus();
}
}
/**
* @return {?}
*/
get minTime() {
return this.timepickerInput && this.timepickerInput.min;
}
/**
* @return {?}
*/
get maxTime() {
return this.timepickerInput && this.timepickerInput.max;
}
/**
* @return {?}
*/
get disabled() {
return this.timepickerInput && this.timepickerInput.disabled;
}
/**
* @return {?}
*/
get format() {
return this.timepickerInput && this.timepickerInput.format;
}
/**
* @return {?}
*/
ngOnInit() {
this.subscriptions.push(this.timepickerService.selectedHour
.subscribe(hour => this.selectedHour = hour));
this.subscriptions.push(this.timepickerService.selectedMinute
.subscribe(minute => this.selectedMinute = minute));
this.subscriptions.push(this.timepickerService.selectedPeriod
.subscribe(period => this.selectedPeriod = period));
}
/**
* @param {?} type
* @return {?}
*/
changeKeyboardType(type) {
this.keyboardType = type;
this.keyboardTypeChanged.emit(type);
}
/**
*
* Register an input with this timepicker.
* input - The timepicker input to register with this timepicker
* @param {?} input
* @return {?}
*/
registerInput(input) {
if (this.timepickerInput) {
throw Error('A Timepicker can only be associated with a single input.');
}
this.timepickerInput = input;
}
/**
* @param {?} hour
* @return {?}
*/
onHourChange(hour) {
this.timepickerService.hour = hour;
}
/**
* @param {?} hour
* @return {?}
*/
onHourSelected(hour) {
this.changeTimeUnit(TimeUnit.MINUTE);
this.hourSelected.next(hour);
}
/**
* @param {?} minute
* @return {?}
*/
onMinuteChange(minute) {
this.timepickerService.minute = minute;
}
/**
* @param {?} period
* @return {?}
*/
changePeriod(period) {
this.timepickerService.period = period;
}
/**
* @param {?} unit
* @return {?}
*/
changeTimeUnit(unit) {
this.activeTimeUnit = unit;
}
/**
* @return {?}
*/
setTime() {
this.timeSet.next(this.timepickerService.getFullTime(this.format));
this.close();
}
/**
* @param {?} time
* @return {?}
*/
setDefaultTime(time) {
this.timepickerService.setDefaultTimeIfAvailable(time, (/** @type {?} */ (this.minTime)), (/** @type {?} */ (this.maxTime)), this.format, this.minutesGap);
}
/**
* @return {?}
*/
open() {
this.isOpened = true;
if (!this.disableAnimation) {
this.animationState = AnimationState.ENTER;
}
this.opened.next();
}
/**
* @return {?}
*/
close() {
if (this.disableAnimation) {
this.closeTimepicker();
return;
}
this.animationState = AnimationState.LEAVE;
}
/**
* @param {?} event
* @return {?}
*/
animationDone(event) {
if (event.phaseName === 'done' && event.toState === AnimationState.LEAVE) {
this.closeTimepicker();
}
}
/**
* @param {?} e
* @return {?}
*/
onKeydown(e) {
this.eventService.dispatchEvent(e);
e.stopPropagation();
}
/**
* @return {?}
*/
ngOnDestroy() {
this.subscriptions.forEach(subscription => subscription.unsubscribe());
}
/**
* @private
* @return {?}
*/
closeTimepicker() {
this.isOpened = false;
this.activeTimeUnit = TimeUnit.HOUR;
this.closed.next();
}
/**
* @param {?} k
* @return {?}
*/
numPadPressed(k) {
NgxMaterialTimepickerComponent.blurAll();
this.timepickerService.keyboardClick.emit(new KeyboardEvent('keypress', (/** @type {?} */ ({
ctrlKey: true,
key: k,
charCode: k === 'Backspace' ? 46 : k.charCodeAt(0),
keyCode: k === 'Backspace' ? 46 : k.charCodeAt(0)
}))));
}
}
NgxMaterialTimepickerComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-material-timepicker',
template: "<div class=\"timepicker-backdrop-overlay\" *ngIf=\"isOpened\" [overlay]=\"preventOverlayClick\"></div>\n<div class=\"timepicker-overlay\" *ngIf=\"isOpened\">\n <div class=\"timepicker\" [@timepicker]=\"animationState\" (@timepicker.done)=\"animationDone($event)\">\n <header class=\"timepicker__header\">\n <button class=\"hidden-button\"></button>\n <button class=\"change-type-button\" *ngIf=\"keyboardType === keyboardTypes.NUMPAD\" (click)=\"changeKeyboardType(keyboardTypes.CLOCK)\">\n <i class=\"decontimepicker-clock\"></i>\n </button>\n <button class=\"change-type-button\" *ngIf=\"keyboardType === keyboardTypes.CLOCK\" (click)=\"changeKeyboardType(keyboardTypes.NUMPAD)\">\n <i class=\"decontimepicker-keypad\"></i>\n </button>\n <ngx-material-timepicker-dial [format]=\"format\" [hour]=\"selectedHour?.time\"\n [minute]=\"selectedMinute?.time\"\n [period]=\"selectedPeriod\" [activeTimeUnit]=\"activeTimeUnit\"\n [minTime]=\"minTime\" [maxTime]=\"maxTime\"\n [isEditable]=\"enableKeyboardInput\"\n [editableHintTmpl]=\"editableHintTmpl\"\n [minutesGap]=\"minutesGap\"\n (periodChanged)=\"changePeriod($event)\"\n (timeUnitChanged)=\"changeTimeUnit($event)\"\n (hourChanged)=\"onHourChange($event)\"\n (minuteChanged)=\"onMinuteChange($event)\"\n ></ngx-material-timepicker-dial>\n </header>\n <div class=\"timepicker__main-content\">\n <div *ngIf=\"keyboardType === keyboardTypes.CLOCK\">\n <div class=\"timepicker__body\" [ngSwitch]=\"activeTimeUnit\">\n <div *ngSwitchCase=\"timeUnit.HOUR\">\n <ngx-material-timepicker-24-hours-face *ngIf=\"format === 24;else ampmHours\"\n (hourChange)=\"onHourChange($event)\"\n [selectedHour]=\"selectedHour\"\n [minTime]=\"minTime\"\n [maxTime]=\"maxTime\"\n [format]=\"format\"\n (hourSelected)=\"onHourSelected($event)\"></ngx-material-timepicker-24-hours-face>\n <ng-template #ampmHours>\n <ngx-material-timepicker-12-hours-face\n (hourChange)=\"onHourChange($event)\"\n [selectedHour]=\"selectedHour\"\n [period]=\"selectedPeriod\"\n [minTime]=\"minTime\"\n [maxTime]=\"maxTime\"\n (hourSelected)=\"onHourSelected($event)\"></ngx-material-timepicker-12-hours-face>\n </ng-template>\n </div>\n <ngx-material-timepicker-minutes-face *ngSwitchCase=\"timeUnit.MINUTE\"\n [selectedMinute]=\"selectedMinute\"\n [selectedHour]=\"selectedHour?.time\"\n [minTime]=\"minTime\"\n [maxTime]=\"maxTime\"\n [format]=\"format\"\n [period]=\"selectedPeriod\"\n [minutesGap]=\"minutesGap\"\n (minuteChange)=\"onMinuteChange($event)\"></ngx-material-timepicker-minutes-face>\n </div>\n </div>\n <div *ngIf=\"keyboardType === keyboardTypes.NUMPAD\">\n <timepicker-numpad (keyPressed)=\"numPadPressed($event)\"></timepicker-numpad>\n </div>\n <div class=\"timepicker__actions\">\n <div (click)=\"close()\">\n <!--suppress HtmlUnknownAttribute -->\n <ng-container *ngTemplateOutlet=\"cancelBtnTmpl ? cancelBtnTmpl : cancelBtnDefault\"></ng-container>\n </div>\n <div (click)=\"setTime()\">\n <!--suppress HtmlUnknownAttribute -->\n <ng-container\n *ngTemplateOutlet=\"confirmBtnTmpl ? confirmBtnTmpl : confirmBtnDefault\"></ng-container>\n </div>\n </div>\n </div>\n </div>\n</div>\n<ng-template #cancelBtnDefault>\n <ngx-material-timepicker-button>Cancel</ngx-material-timepicker-button>\n</ng-template>\n<ng-template #confirmBtnDefault>\n <ngx-material-timepicker-button>Ok</ngx-material-timepicker-button>\n</ng-template>\n",
animations: [
trigger('timepicker', [
transition(`* => ${AnimationState.ENTER}`, [
style({ transform: 'translateY(-30%)' }),
animate('0.2s ease-out', style({ transform: 'translateY(0)' }))
]),
transition(`${AnimationState.ENTER} => ${AnimationState.LEAVE}`, [
style({ transform: 'translateY(0)', opacity: 1 }),
animate('0.2s ease-out', style({ transform: 'translateY(-30%)', opacity: 0 }))
])
])
],
providers: [NgxMaterialTimepickerService],
styles: [":host{--body-background-color:#fff;--primary-font-family:'Roboto',sans-serif;--button-color:deepskyblue;--dial-active-color:#fff;--dial-inactive-color:rgba(255, 255, 255, .5);--dial-background-color:deepskyblue;--clock-face-time-active-color:#fff;--clock-face-time-inactive-color:#6c6c6c;--clock-face-inner-time-inactive-color:#929292;--clock-face-time-disabled-color:#c5c5c5;--clock-face-background-color:#f0f0f0;--clock-hand-color:deepskyblue}@font-face{font-family:decontimepicker;src:url(../assets/icons/decontimepicker.eot);src:url(../assets/icons/decontimepicker.eot?#iefix) format(\"embedded-opentype\"),url(../assets/icons/decontimepicker.woff) format(\"woff\"),url(../assets/icons/decontimepicker.ttf) format(\"truetype\"),url(../assets/icons/decontimepicker.svg#decontimepicker) format(\"svg\");font-weight:400;font-style:normal}[class*=decontimepicker-]:before{display:inline-block;font-family:decontimepicker;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.decontimepicker-backspace:before{content:'\\0041'}.decontimepicker-clock:before{content:'\\0042'}.decontimepicker-keypad:before{content:'\\0043'}.timepicker-overlay::ng-deep [class*=decontimepicker-]:before{display:inline-block;font-family:decontimepicker;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.timepicker-overlay::ng-deep .decontimepicker-backspace:before{content:'\\0041'}.timepicker-overlay::ng-deep .decontimepicker-clock:before{content:'\\0042'}.timepicker-overlay::ng-deep .decontimepicker-keypad:before{content:'\\0043'}.simple-keyboard{font-family:HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif;width:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;box-sizing:border-box;overflow:hidden;touch-action:manipulation}.simple-keyboard .hg-row{display:flex}.simple-keyboard .hg-row:not(:last-child){margin-bottom:5px}.simple-keyboard .hg-row .hg-button-container,.simple-keyboard .hg-row .hg-button:not(:last-child){margin-right:5px}.simple-keyboard .hg-row>div:last-child{margin-right:0}.simple-keyboard .hg-row .hg-button-container{display:flex}.simple-keyboard .hg-button{display:inline-block;flex-grow:1;cursor:pointer}.simple-keyboard .hg-button span{pointer-events:none}.simple-keyboard.hg-theme-default{background-color:rgba(0,0,0,.1);padding:5px;border-radius:5px}.simple-keyboard.hg-theme-default .hg-button{box-shadow:0 0 3px -1px rgba(0,0,0,.3);height:40px;border-radius:5px;box-sizing:border-box;padding:5px;background:#fff;border-bottom:1px solid #b5b5b5;cursor:pointer;display:flex;align-items:center;justify-content:center}.simple-keyboard button.hg-button{border-width:0;outline:0;font-size:inherit}.simple-keyboard.hg-theme-default:not(.hg-touch-events) .hg-button:active{background:#e4e4e4}.simple-keyboard.hg-theme-default.hg-layout-numeric .hg-button{width:33.3%;height:60px;align-items:center;display:flex;justify-content:center}.simple-keyboard.hg-theme-default .hg-button.hg-button-numpadadd,.simple-keyboard.hg-theme-default .hg-button.hg-button-numpadenter{height:85px}.simple-keyboard.hg-theme-default .hg-button.hg-button-numpad0{width:105px}.simple-keyboard.hg-theme-default .hg-button.hg-button-com{max-width:85px}.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn.hg-button-at{max-width:45px}.simple-keyboard.hg-theme-default .hg-button.hg-selectedButton{background:rgba(5,25,70,.53);color:#fff}.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn[data-skbtn=\".com\"]{max-width:82px}.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn[data-skbtn=\"@\"]{max-width:60px}.hidden-button{display:block;visibility:hidden;height:1px;width:1px;overflow:hidden;border:0;position:fixed;top:-1px;left:-1px}.timepicker-backdrop-overlay{position:fixed;top:0;bottom:0;right:0;left:0;background-color:rgba(0,0,0,.3);z-index:999;pointer-events:auto}.timepicker-overlay{position:fixed;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:999;pointer-events:none}.timepicker{width:300px;border-radius:2px;box-shadow:rgba(0,0,0,.25) 0 14px 45px,rgba(0,0,0,.22) 0 10px 18px;outline:0;position:static;z-index:999;pointer-events:auto}.timepicker__header{color:#fff;padding:15px 30px;background-color:#00bfff}@supports (background-color:var(--dial-background-color)){.timepicker__header{background-color:var(--dial-background-color)}}.timepicker__body{padding:15px 5px;display:flex;justify-content:center;align-items:center;background-color:#fff}@supports (background-color:var(--body-background-color)){.timepicker__body{background-color:var(--body-background-color)}}.timepicker__actions{display:flex;justify-content:flex-end;padding:15px;background-color:#fff}@supports (background-color:var(--body-background-color)){.timepicker__actions{background-color:var(--body-background-color)}}@media (max-device-width:1023px) and (orientation:landscape){.timepicker{display:flex;width:515px}.timepicker__header{display:flex;align-items:center}.timepicker__main-content{display:flex;flex-direction:column;width:100%}.timepicker__actions{padding:5px;margin-top:-1px}}"]
}] }
];
/** @nocollapse */
NgxMaterialTimepickerComponent.ctorParameters = () => [
{ type: NgxMaterialTimepickerService },
{ type: NgxMaterialTimepickerEventService }
];
NgxMaterialTimepickerComponent.propDecorators = {
cancelBtnTmpl: [{ type: Input }],
editableHintTmpl: [{ type: Input }],
confirmBtnTmpl: [{ type: Input }],
isEsc: [{ type: Input, args: ['ESC',] }],
enableKeyboardInput: [{ type: Input }],
preventOverlayClick: [{ type: Input }],
disableAnimation: [{ type: Input }],
keyboardType: [{ type: Input }],
keyboardTypeChanged: [{ type: Output }],
minutesGap: [{ type: Input }],
defaultTime: [{ type: Input }],
timeSet: [{ type: Output }],
opened: [{ type: Output }],
closed: [{ type: Output }],
hourSelected: [{ type: Output }],
timepickerComponent: [{ type: ViewChild, args: ['timepickerww',] }],
onKeydown: [{ type: HostListener, args: ['keydown', ['$event'],] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/* To override a default toggle icon */
class NgxMaterialTimepickerToggleIconDirective {
}
NgxMaterialTimepickerToggleIconDirective.decorators = [
{ type: Directive, args: [{ selector: '[ngxMaterialTimepickerToggleIcon]' },] }
];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class NgxMaterialTimepickerToggleComponent {
/**
* @return {?}
*/
get disabled() {
return this._disabled === undefined ? this.timepicker.disabled : this._disabled;
}
/**
* @param {?} value
* @return {?}
*/
set disabled(value) {
this._disabled = value;
}
/**
* @param {?} event
* @return {?}
*/
open(event) {
if (this.timepicker) {
this.timepicker.open();
event.stopPropagation();
}
}
}
NgxMaterialTimepickerToggleComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-material-timepicker-toggle',
template: "<button class=\"ngx-material-timepicker-toggle\" (click)=\"open($event)\" [disabled]=\"disabled\" type=\"button\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24px\" height=\"24px\" *ngIf=\"!customIcon\">\n <path\n d=\"M 12 2 C 6.4889971 2 2 6.4889971 2 12 C 2 17.511003 6.4889971 22 12 22 C 17.511003 22 22 17.511003 22 12 C 22 6.4889971 17.511003 2 12 2 z M 12 4 C 16.430123 4 20 7.5698774 20 12 C 20 16.430123 16.430123 20 12 20 C 7.5698774 20 4 16.430123 4 12 C 4 7.5698774 7.5698774 4 12 4 z M 11 6 L 11 12.414062 L 15.292969 16.707031 L 16.707031 15.292969 L 13 11.585938 L 13 6 L 11 6 z\"/>\n </svg>\n\n <ng-content select=\"[ngxMaterialTimepickerToggleIcon]\"></ng-content>\n</button>\n",
styles: [".ngx-material-timepicker-toggle{display:flex;justify-content:center;align-items:center;padding:4px;background-color:transparent;border-radius:50%;text-align:center;border:none;outline:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;transition:background-color .3s;cursor:pointer}.ngx-material-timepicker-toggle:focus{background-color:rgba(0,0,0,.07)}"]
}] }
];
NgxMaterialTimepickerToggleComponent.propDecorators = {
timepicker: [{ type: Input, args: ['for',] }],
disabled: [{ type: Input }],
customIcon: [{ type: ContentChild, args: [NgxMaterialTimepickerToggleIconDirective,] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @type {?} */
const VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
// tslint:disable-next-line
useExisting: forwardRef(() => TimepickerDirective),
multi: true
};
class TimepickerDirective {
/**
* @param {?} elementRef
*/
constructor(elementRef) {
this.elementRef = elementRef;
this._format = 12;
this._value = '';
this.timepickerSubscriptions = [];
this.onTouched = () => {
};
this.onChange = () => {
};
}
/**
* @param {?} value
* @return {?}
*/
set format(value) {
this._format = value === 24 ? 24 : 12;
}
/**
* @return {?}
*/
get format() {
return this._format;
}
/**
* @param {?} value
* @return {?}
*/
set min(value) {
if (typeof value === 'string') {
this._min = TimeAdapter.convertTimeToDateTime(value, this._format);
return;
}
this._min = value;
}
/**
* @return {?}
*/
get min() {
return this._min;
}
/**
* @param {?} value
* @return {?}
*/
set max(value) {
if (typeof value === 'string') {
this._max = TimeAdapter.convertTimeToDateTime(value, this._format);
return;
}
this._max = value;
}
/**
* @return {?}
*/
get max() {
return this._max;
}
/**
* @param {?} picker
* @return {?}
*/
set timepicker(picker) {
this.registerTimepicker(picker);
}
/**
* @param {?} value
* @return {?}
*/
set value(value) {
if (!value) {
this._value = '';
this.updateInputValue();
return;
}
/** @type {?} */
const time = TimeAdapter.formatTime(value, this._format);
/** @type {?} */
const isAvailable = TimeAdapter.isTimeAvailable(time, (/** @type {?} */ (this._min)), (/** @type {?} */ (this._max)), 'minutes', this._timepicker.minutesGap, this._format);
if (isAvailable) {
this._value = time;
this.updateInputValue();
return;
}
console.warn('Selected time doesn\'t match min or max value');
}
/**
* @return {?}
*/
get value() {
return this._value;
}
/**
* @private
* @param {?} time
* @return {?}
*/
set defaultTime(time) {
this._timepicker.setDefaultTime(time);
}
/**
* @param {?} value
* @return {?}
*/
onInput(value) {
this.value = value;
this.onChange(value);
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
if (changes['value'] && changes['value'].currentValue) {
this.defaultTime = changes['value'].currentValue;
}
}
/**
* @param {?} event
* @return {?}
*/
onClick(event) {
if (!this.disableClick) {
this._timepicker.open();
event.stopPropagation();
}
}
/**
* @param {?} value
* @return {?}
*/
writeValue(value) {
this.value = value;
this.defaultTime = value;
}
/**
* @param {?} fn
* @return {?}
*/
registerOnChange(fn) {
this.onChange = fn;
}
/**
* @param {?} fn
* @return {?}
*/
registerOnTouched(fn) {
this.onTouched = fn;
}
/**
* @param {?} isDisabled
* @return {?}
*/
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
/**
* @return {?}
*/
ngOnDestroy() {
this.timepickerSubscriptions.forEach(s => s.unsubscribe());
}
/**
* @private
* @param {?} picker
* @return {?}
*/
registerTimepicker(picker) {
if (picker) {
this._timepicker = picker;
this._timepicker.registerInput(this);
this.timepickerSubscriptions.push(this._timepicker.timeSet.subscribe((time) => {
this.value = time;
this.onChange(this._value);
this.onTouched();
}));
this.timepickerSubscriptions.push(this._timepicker.closed.subscribe(() => this.defaultTime = this._value));
}
else {
throw new Error('NgxMaterialTimepickerComponent is not defined.' +
' Please make sure you passed the timepicker to ngxTimepicker directive');
}
}
/**
* @private
* @return {?}
*/
updateInputValue() {
this.elementRef.nativeElement.value = this.value;
}
}
TimepickerDirective.decorators = [
{ type: Directive, args: [{
selector: '[ngxTimepicker]',
providers: [VALUE_ACCESSOR],
host: {
'[disabled]': 'disabled',
'(input)': 'onInput($event.target.value)',
'(blur)': 'onTouched()',
},
},] }
];
/** @nocollapse */
TimepickerDirective.ctorParameters = () => [
{ type: ElementRef }
];
TimepickerDirective.propDecorators = {
format: [{ type: Input }],
min: [{ type: Input }],
max: [{ type: Input }],
timepicker: [{ type: Input, args: ['ngxTimepicker',] }],
value: [{ type: Input }],
disabled: [{ type: Input }],
disableClick: [{ type: Input }],
onClick: [{ type: HostListener, args: ['click', ['$event'],] }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class NgxMaterialTimepickerThemeDirective {
/**
* @param {?} elementRef
*/
constructor(elementRef) {
this.element = elementRef.nativeElement;
}
/**
* @return {?}
*/
ngAfterViewInit() {
if (this.theme) {
this.setTheme(this.theme);
}
}
/**
* @private
* @param {?} theme
* @return {?}
*/
setTheme(theme) {
for (const val in theme) {
if (theme.hasOwnProperty(val)) {
if (typeof theme[val] === 'string') {
for (const prop in theme) {
if (theme.hasOwnProperty(prop)) {
this.element.style.setProperty(`--${camelCaseToDash(prop)}`, theme[prop]);
}
}
return;
}
this.setTheme(theme[val]);
}
}
}
}
NgxMaterialTimepickerThemeDirective.decorators = [
{ type: Directive, args: [{ selector: '[ngxMaterialTimepickerTheme]' },] }
];
/** @nocollapse */
NgxMaterialTimepickerThemeDirective.ctorParameters = () => [
{ type: ElementRef }
];
NgxMaterialTimepickerThemeDirective.propDecorators = {
theme: [{ type: Input, args: ['ngxMaterialTimepickerTheme',] }]
};
/**
* @param {?} myStr
* @return {?}
*/
function camelCaseToDash(myStr) {
return myStr.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @param {?} format
* @return {?}
*/
function getHours(format) {
return Array(format).fill(1).map((v, i) => {
/** @type {?} */
const angleStep = 30;
/** @type {?} */
const time = v + i;
/** @type {?} */
const angle = angleStep * time;
return { time: time === 24 ? 0 : time, angle };
});
}
/**
* @param {?} hours
* @param {?} config
* @return {?}
*/
function disableHours(hours, config) {
if (config.min || config.max) {
return hours.map(value => {
/** @type {?} */
const hour = config.format === 24 ? value.time : TimeAdapter.formatHour(value.time, config.format, config.period);
/** @type {?} */
const currentTime = DateTime.fromObject({ hour }).toFormat(TimeFormat.TWELVE);
return Object.assign({}, value, { disabled: !TimeAdapter.isTimeAvailable(currentTime, config.min, config.max, 'hours') });
});
}
return hours;
}
/**
* @param {?=} gap
* @return {?}
*/
function getMinutes(gap = 1) {
/** @type {?} */
const minutesCount = 60;
/** @type {?} */
const angleStep = 360 / minutesCount;
/** @type {?} */
const minutes = [];
for (let i = 0; i < minutesCount; i++) {
/** @type {?} */
const angle = angleStep * i;
if (i % gap === 0) {
minutes.push({ time: i, angle: angle !== 0 ? angle : 360 });
}
}
return minutes;
}
/**
* @param {?} minutes
* @param {?} selectedHour
* @param {?} config
* @return {?}
*/
function disableMinutes(minutes, selectedHour, config) {
if (config.min || config.max) {
/** @type {?} */
const hour = TimeAdapter.formatHour(selectedHour, config.format, config.period);
return minutes.map(value => {
/** @type {?} */
const currentTime = DateTime.fromObject({ hour, minute: value.time }).toFormat(TimeFormat.TWELVE);
return Object.assign({}, value, { disabled: !TimeAdapter.isTimeAvailable(currentTime, config.min, config.max, 'minutes') });
});
}
return minutes;
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class NgxMaterialTimepickerHoursFace {
/**
* @protected
* @param {?} format
*/
constructor(format) {
this.hourChange = new EventEmitter();
this.hourSelected = new EventEmitter();
this.hoursList = [];
this.hoursList = getHours(format);
}
/**
* @param {?} time
* @return {?}
*/
onTimeSelected(time) {
this.hourSelected.next(time);
}
}
NgxMaterialTimepickerHoursFace.propDecorators = {
selectedHour: [{ type: Input }],
minTime: [{ type: Input }],
maxTime: [{ type: Input }],
format: [{ type: Input }],
hourChange: [{ type: Output }],
hourSelected: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class NgxMaterialTimepicker24HoursFaceComponent extends NgxMaterialTimepickerHoursFace {
constructor() {
super(24);
}
/**
* @return {?}
*/
ngAfterContentInit() {
this.hoursList = disableHours(this.hoursList, {
min: this.minTime,
max: this.maxTime,
format: this.format
});
}
}
NgxMaterialTimepicker24HoursFaceComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-material-timepicker-24-hours-face',
template: "<ngx-material-timepicker-face [selectedTime]=\"selectedHour\" [faceTime]=\"hoursList\" [format]=\"format\"\n (timeChange)=\"hourChange.next($event)\"\n (timeSelected)=\"onTimeSelected($event)\"></ngx-material-timepicker-face>\n"
}] }
];
/** @nocollapse */
NgxMaterialTimepicker24HoursFaceComponent.ctorParameters = () => [];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class NgxMaterialTimepicker12HoursFaceComponent extends NgxMaterialTimepickerHoursFace {
constructor() {
super(12);
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
if (changes['period'] && changes['period'].currentValue) {
this.hoursList = disableHours(this.hoursList, {
min: this.minTime,
max: this.maxTime,
format: this.format,
period: this.period
});
}
}
}
NgxMaterialTimepicker12HoursFaceComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-material-timepicker-12-hours-face',
template: "<ngx-material-timepicker-face [selectedTime]=\"selectedHour\" [faceTime]=\"hoursList\"\n (timeChange)=\"hourChange.next($event)\" (timeSelected)=\"onTimeSelected($event)\"></ngx-material-timepicker-face>\n"
}] }
];
/** @nocollapse */
NgxMaterialTimepicker12HoursFaceComponent.ctorParameters = () => [];
NgxMaterialTimepicker12HoursFaceComponent.propDecorators = {
period: [{ type: Input }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
class NgxMaterialTimepickerMinutesFaceComponent {
constructor() {
this.minutesList = [];
this.timeUnit = TimeUnit;
this.minuteChange = new EventEmitter();
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
if (changes['period'] && changes['period'].currentValue) {
/** @type {?} */
const minutes = getMinutes(this.minutesGap);
this.minutesList = disableMinutes(minutes, this.selectedHour, {
min: this.minTime,
max: this.maxTime,
format: this.format,
period: this.period
});
}
}
}
NgxMaterialTimepickerMinutesFaceComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-material-timepicker-minutes-face',
template: "<ngx-material-timepicker-face [faceTime]=\"minutesList\" [selectedTime]=\"selectedMinute\"\n [minutesGap]=\"minutesGap\"\n (timeChange)=\"minuteChange.next($event)\" [unit]=\"timeUnit.MINUTE\"></ngx-material-timepicker-face>\n"
}] }
];
NgxMaterialTimepickerMinutesFaceComponent.propDecorators = {
selectedMinute: [{ type: Input }],
selectedHour: [{ type: Input }],
period: [{ type: Input }],
minTime: [{ type: Input }],
maxTime: [{ type: Input }],
format: [{ type: Input }],
minutesGap: [{ type: Input }],
minuteChange: [{ type: Output }]
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @type {?} */
const CLOCK_HAND_STYLES = {
small: {
height: '75px',
top: 'calc(50% - 75px)'
},
large: {
height: '103px',
top: 'calc(50% - 103px)'
}
};
class NgxMaterialTimepickerFaceComponent {
constructor() {
this.timeUnit = TimeUnit;
this.innerClockFaceSize = 85;
this.timeChange = new EventEmitter();
this.timeSelected = new EventEmitter();
}
/**
* @return {?}
*/
ngAfterViewInit() {
this.setClockHandPosition();
this.addTouchEvents();
}
/**
* @param {?} changes
* @return {?}
*/
ngOnChanges(changes) {
/** @type {?} */
const faceTimeChanges = changes['faceTime'];
/** @type {?} */
const selectedTimeChanges = changes['selectedTime'];
if ((faceTimeChanges && faceTimeChanges.currentValue)
&& (selectedTimeChanges && selectedTimeChanges.currentValue)) {
/* Set time according to passed an input value */
this.selectedTime = this.faceTime.find(time => time.time === this.selectedTime.time);
}
if (selectedTimeChanges && selectedTimeChanges.currentValue) {
this.setClockHandPosition();
}
if (faceTimeChanges && faceTimeChanges.currentValue) {
// To avoid an error ExpressionChangedAfterItHasBeenCheckedError
setTimeout(() => this.selectAvailableTime());
}
}
/**
* @param {?} _
* @param {?} time
* @return {?}
*/
trackByTime(_, time) {
return time.time;
}
/**
* @param {?} e
* @return {?}
*/
onMousedown(e) {
e.preve