UNPKG

ngx-material-timepicker

Version:
1,087 lines (1,069 loc) 94.2 kB
import { __decorate, __metadata, __param } from 'tslib'; import { ɵɵdefineInjectable, Injectable, EventEmitter, Input, TemplateRef, Output, HostListener, Component, Directive, ContentChild, forwardRef, ElementRef, ViewChild, ChangeDetectionStrategy, Pipe, Optional, Inject, NgModule } from '@angular/core'; import { DOCUMENT, CommonModule } from '@angular/common'; import { BehaviorSubject, Subject, merge } from 'rxjs'; import { DateTime } from 'luxon'; import { trigger, transition, style, animate, sequence } from '@angular/animations'; import { filter, takeUntil } from 'rxjs/operators'; import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms'; import { DomSanitizer } from '@angular/platform-browser'; var TimePeriod; (function (TimePeriod) { TimePeriod["AM"] = "AM"; TimePeriod["PM"] = "PM"; })(TimePeriod || (TimePeriod = {})); var TimeFormat; (function (TimeFormat) { TimeFormat["TWELVE"] = "hh:mm a"; TimeFormat["TWELVE_SHORT"] = "h:m a"; TimeFormat["TWENTY_FOUR"] = "HH:mm"; TimeFormat["TWENTY_FOUR_SHORT"] = "H:m"; })(TimeFormat || (TimeFormat = {})); 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(); } } 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(); } } 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); } } function isDigit(e) { // Allow: backspace, delete, tab, escape, enter if ([46, 8, 9, 27, 13].some(n => n === e.keyCode) || // Allow: Ctrl/cmd+A (e.keyCode == 65 && (e.ctrlKey === true || e.metaKey === true)) || // Allow: Ctrl/cmd+C (e.keyCode == 67 && (e.ctrlKey === true || e.metaKey === true)) || // Allow: Ctrl/cmd+X (e.keyCode == 88 && (e.ctrlKey === true || e.metaKey === true)) || // Allow: home, end, left, right, up, down (e.keyCode >= 35 && e.keyCode <= 40)) { return true; } return !((e.keyCode < 48 || e.keyCode > 57) && (e.keyCode < 96 || e.keyCode > 105)); } // @dynamic class TimeAdapter { static parseTime(time, format = 12) { if (time.indexOf(':') === -1) { return 'Invalid time'; } let period = time.trim().substr(time.length - 2).toUpperCase(); const isPeriodValid = period === TimePeriod.AM || period === TimePeriod.PM; const [h, m] = time.split(':'); if (format === 24) { const formattedHours = isPeriodValid ? this.formatHour(+h, 12, period) : +h; return `${formattedHours}:${parseInt(m, 10)}`; } const isPM = +h > 12; const hours = isPM ? +h - 12 : +h; period = isPeriodValid ? period : isPM ? TimePeriod.PM : TimePeriod.AM; return `${hours}:${parseInt(m, 10)} ${period}`; } static formatTime(time, format = 12) { const timeFormat = (format === 24) ? TimeFormat.TWENTY_FOUR : TimeFormat.TWELVE; const timeMask = (format === 24) ? TimeFormat.TWENTY_FOUR_SHORT : TimeFormat.TWELVE_SHORT; return DateTime.fromFormat(this.parseTime(time, format), timeMask).toFormat(timeFormat).toLowerCase(); } static convertTimeToDateTime(time, format = 12) { const timeMask = (format === 24) ? TimeFormat.TWENTY_FOUR_SHORT : TimeFormat.TWELVE_SHORT; return DateTime.fromFormat(this.parseTime(time, format), timeMask); } static isTimeAvailable(time, min, max, granularity, minutesGap, format) { if (!time) { return; } const convertedTime = this.convertTimeToDateTime(time, format); const minutes = convertedTime.minute; if (minutesGap && (minutes % minutesGap !== 0)) { throw new Error(`Your minutes - ${minutes} doesn\'t match your minutesGap - ${minutesGap}`); } const isAfter = (min && !max) && isSameOrAfter(convertedTime, min, granularity); const isBefore = (max && !min) && isSameOrBefore(convertedTime, max, granularity); const between = (min && max) && isBetween(convertedTime, min, max, granularity); const isAvailable = !min && !max; return isAfter || isBefore || between || isAvailable; } /*** * Format hour according to time format (12 or 24) */ static formatHour(currentHour, format, period) { if (format === 24) { return currentHour; } 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; } } const DEFAULT_HOUR = { time: 12, angle: 360 }; const DEFAULT_MINUTE = { time: 0, angle: 360 }; let NgxMaterialTimepickerService = class NgxMaterialTimepickerService { constructor() { this.hourSubject = new BehaviorSubject(DEFAULT_HOUR); this.minuteSubject = new BehaviorSubject(DEFAULT_MINUTE); this.periodSubject = new BehaviorSubject(TimePeriod.AM); } set hour(hour) { this.hourSubject.next(hour); } get selectedHour() { return this.hourSubject.asObservable(); } set minute(minute) { this.minuteSubject.next(minute); } get selectedMinute() { return this.minuteSubject.asObservable(); } set period(period) { const isPeriodValid = (period === TimePeriod.AM) || (period === TimePeriod.PM); if (isPeriodValid) { this.periodSubject.next(period); } } get selectedPeriod() { return this.periodSubject.asObservable(); } 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); } } getFullTime(format) { const hour = this.hourSubject.getValue().time; const minute = this.minuteSubject.getValue().time; const period = format === 12 ? this.periodSubject.getValue() : ''; return TimeAdapter.formatTime(`${hour}:${minute} ${period}`, format); } setDefaultTime(time, format) { const defaultTime = TimeAdapter.convertTimeToDateTime(time, format).toJSDate(); if (DateTime.fromJSDate(defaultTime).isValid) { const period = time.substr(time.length - 2).toUpperCase(); const hour = defaultTime.getHours(); this.hour = Object.assign({}, DEFAULT_HOUR, { time: formatHourByPeriod(hour, period) }); this.minute = Object.assign({}, DEFAULT_MINUTE, { time: defaultTime.getMinutes() }); this.period = period; } else { this.resetTime(); } } resetTime() { this.hour = Object.assign({}, DEFAULT_HOUR); this.minute = Object.assign({}, DEFAULT_MINUTE); this.period = TimePeriod.AM; } }; NgxMaterialTimepickerService.ngInjectableDef = ɵɵdefineInjectable({ factory: function NgxMaterialTimepickerService_Factory() { return new NgxMaterialTimepickerService(); }, token: NgxMaterialTimepickerService, providedIn: "root" }); NgxMaterialTimepickerService = __decorate([ Injectable({ providedIn: 'root' }) ], NgxMaterialTimepickerService); /*** * Format hour in 24hours format to meridian (AM or PM) format */ 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; } } var TimeUnit; (function (TimeUnit) { TimeUnit[TimeUnit["HOUR"] = 0] = "HOUR"; TimeUnit[TimeUnit["MINUTE"] = 1] = "MINUTE"; })(TimeUnit || (TimeUnit = {})); let NgxMaterialTimepickerEventService = class NgxMaterialTimepickerEventService { constructor() { this.backdropClickSubject = new Subject(); this.keydownEventSubject = new Subject(); } get backdropClick() { return this.backdropClickSubject.asObservable(); } get keydownEvent() { return this.keydownEventSubject.asObservable(); } dispatchEvent(event) { switch (event.type) { case 'click': this.backdropClickSubject.next(event); break; case 'keydown': this.keydownEventSubject.next(event); break; default: throw new Error('no such event type'); } } }; NgxMaterialTimepickerEventService.ngInjectableDef = ɵɵdefineInjectable({ factory: function NgxMaterialTimepickerEventService_Factory() { return new NgxMaterialTimepickerEventService(); }, token: NgxMaterialTimepickerEventService, providedIn: "root" }); NgxMaterialTimepickerEventService = __decorate([ Injectable({ providedIn: 'root' }) ], NgxMaterialTimepickerEventService); var AnimationState; (function (AnimationState) { AnimationState["ENTER"] = "enter"; AnimationState["LEAVE"] = "leave"; })(AnimationState || (AnimationState = {})); const ESCAPE = 27; let NgxMaterialTimepickerComponent = class NgxMaterialTimepickerComponent { constructor(timepickerService, eventService) { this.timepickerService = timepickerService; this.eventService = eventService; this.timeUnit = TimeUnit; this.activeTimeUnit = TimeUnit.HOUR; this.isOpened = false; this.isEsc = true; 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())); } set format(value) { this._format = value === 24 ? 24 : 12; } get format() { return this.timepickerInput ? this.timepickerInput.format : this._format; } set minutesGap(gap) { if (gap == null) { return; } gap = Math.floor(gap); this._minutesGap = gap <= 59 ? gap : 1; } get minutesGap() { return this._minutesGap; } set defaultTime(time) { this.setDefaultTime(time); } get minTime() { return this.timepickerInput && this.timepickerInput.min; } get maxTime() { return this.timepickerInput && this.timepickerInput.max; } get disabled() { return this.timepickerInput && this.timepickerInput.disabled; } 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)); } /*** * Register an input with this timepicker. * input - The timepicker input to register with this timepicker */ registerInput(input) { if (this.timepickerInput) { throw Error('A Timepicker can only be associated with a single input.'); } this.timepickerInput = input; } onHourChange(hour) { this.timepickerService.hour = hour; } onHourSelected(hour) { this.changeTimeUnit(TimeUnit.MINUTE); this.hourSelected.next(hour); } onMinuteChange(minute) { this.timepickerService.minute = minute; } changePeriod(period) { this.timepickerService.period = period; } changeTimeUnit(unit) { this.activeTimeUnit = unit; } setTime() { this.timeSet.next(this.timepickerService.getFullTime(this.format)); this.close(); } setDefaultTime(time) { this.timepickerService.setDefaultTimeIfAvailable(time, this.minTime, this.maxTime, this.format, this.minutesGap); } open() { this.isOpened = true; if (!this.disableAnimation) { this.animationState = AnimationState.ENTER; } this.opened.next(); } close() { if (this.disableAnimation) { this.closeTimepicker(); return; } this.animationState = AnimationState.LEAVE; } animationDone(event) { if (event.phaseName === 'done' && event.toState === AnimationState.LEAVE) { this.closeTimepicker(); } } onKeydown(e) { this.eventService.dispatchEvent(e); e.stopPropagation(); } ngOnDestroy() { this.subscriptions.forEach(subscription => subscription.unsubscribe()); } closeTimepicker() { this.isOpened = false; this.activeTimeUnit = TimeUnit.HOUR; this.closed.next(); } }; __decorate([ Input(), __metadata("design:type", TemplateRef) ], NgxMaterialTimepickerComponent.prototype, "cancelBtnTmpl", void 0); __decorate([ Input(), __metadata("design:type", TemplateRef) ], NgxMaterialTimepickerComponent.prototype, "editableHintTmpl", void 0); __decorate([ Input(), __metadata("design:type", TemplateRef) ], NgxMaterialTimepickerComponent.prototype, "confirmBtnTmpl", void 0); __decorate([ Input('ESC'), __metadata("design:type", Object) ], NgxMaterialTimepickerComponent.prototype, "isEsc", void 0); __decorate([ Input(), __metadata("design:type", Boolean) ], NgxMaterialTimepickerComponent.prototype, "enableKeyboardInput", void 0); __decorate([ Input(), __metadata("design:type", Boolean) ], NgxMaterialTimepickerComponent.prototype, "preventOverlayClick", void 0); __decorate([ Input(), __metadata("design:type", Boolean) ], NgxMaterialTimepickerComponent.prototype, "disableAnimation", void 0); __decorate([ Input(), __metadata("design:type", Number), __metadata("design:paramtypes", [Number]) ], NgxMaterialTimepickerComponent.prototype, "format", null); __decorate([ Input(), __metadata("design:type", Number), __metadata("design:paramtypes", [Number]) ], NgxMaterialTimepickerComponent.prototype, "minutesGap", null); __decorate([ Input(), __metadata("design:type", String), __metadata("design:paramtypes", [String]) ], NgxMaterialTimepickerComponent.prototype, "defaultTime", null); __decorate([ Output(), __metadata("design:type", Object) ], NgxMaterialTimepickerComponent.prototype, "timeSet", void 0); __decorate([ Output(), __metadata("design:type", Object) ], NgxMaterialTimepickerComponent.prototype, "opened", void 0); __decorate([ Output(), __metadata("design:type", Object) ], NgxMaterialTimepickerComponent.prototype, "closed", void 0); __decorate([ Output(), __metadata("design:type", Object) ], NgxMaterialTimepickerComponent.prototype, "hourSelected", void 0); __decorate([ HostListener('keydown', ['$event']), __metadata("design:type", Function), __metadata("design:paramtypes", [KeyboardEvent]), __metadata("design:returntype", void 0) ], NgxMaterialTimepickerComponent.prototype, "onKeydown", null); NgxMaterialTimepickerComponent = __decorate([ Component({ 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 <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 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 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}.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{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}}"] }), __metadata("design:paramtypes", [NgxMaterialTimepickerService, NgxMaterialTimepickerEventService]) ], NgxMaterialTimepickerComponent); /* To override a default toggle icon */ let NgxMaterialTimepickerToggleIconDirective = class NgxMaterialTimepickerToggleIconDirective { }; NgxMaterialTimepickerToggleIconDirective = __decorate([ Directive({ selector: '[ngxMaterialTimepickerToggleIcon]' }) ], NgxMaterialTimepickerToggleIconDirective); let NgxMaterialTimepickerToggleComponent = class NgxMaterialTimepickerToggleComponent { get disabled() { return this._disabled === undefined ? this.timepicker.disabled : this._disabled; } set disabled(value) { this._disabled = value; } open(event) { if (this.timepicker) { this.timepicker.open(); event.stopPropagation(); } } }; __decorate([ Input('for'), __metadata("design:type", NgxMaterialTimepickerComponent) ], NgxMaterialTimepickerToggleComponent.prototype, "timepicker", void 0); __decorate([ Input(), __metadata("design:type", Boolean), __metadata("design:paramtypes", [Boolean]) ], NgxMaterialTimepickerToggleComponent.prototype, "disabled", null); __decorate([ ContentChild(NgxMaterialTimepickerToggleIconDirective, { static: true }), __metadata("design:type", NgxMaterialTimepickerToggleIconDirective) ], NgxMaterialTimepickerToggleComponent.prototype, "customIcon", void 0); NgxMaterialTimepickerToggleComponent = __decorate([ Component({ 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); const VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, // tslint:disable-next-line useExisting: forwardRef(() => TimepickerDirective), multi: true }; let TimepickerDirective = class TimepickerDirective { constructor(elementRef) { this.elementRef = elementRef; this._format = 12; this._value = ''; this.timepickerSubscriptions = []; this.onTouched = () => { }; this.onChange = () => { }; } set format(value) { this._format = value === 24 ? 24 : 12; } get format() { return this._format; } set min(value) { if (typeof value === 'string') { this._min = TimeAdapter.convertTimeToDateTime(value, this._format); return; } this._min = value; } get min() { return this._min; } set max(value) { if (typeof value === 'string') { this._max = TimeAdapter.convertTimeToDateTime(value, this._format); return; } this._max = value; } get max() { return this._max; } set timepicker(picker) { this.registerTimepicker(picker); } set value(value) { if (!value) { this._value = ''; this.updateInputValue(); return; } const time = TimeAdapter.formatTime(value, this._format); const isAvailable = TimeAdapter.isTimeAvailable(time, this._min, 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'); } get value() { return this._value; } set defaultTime(time) { this._timepicker.setDefaultTime(time); } onInput(value) { this.value = value; this.onChange(value); } ngOnChanges(changes) { if (changes['value'] && changes['value'].currentValue) { this.defaultTime = changes['value'].currentValue; } } onClick(event) { if (!this.disableClick) { this._timepicker.open(); event.stopPropagation(); } } writeValue(value) { this.value = value; this.defaultTime = value; } registerOnChange(fn) { this.onChange = fn; } registerOnTouched(fn) { this.onTouched = fn; } setDisabledState(isDisabled) { this.disabled = isDisabled; } ngOnDestroy() { this.timepickerSubscriptions.forEach(s => s.unsubscribe()); } 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'); } } updateInputValue() { this.elementRef.nativeElement.value = this.value; } }; __decorate([ Input(), __metadata("design:type", Number), __metadata("design:paramtypes", [Number]) ], TimepickerDirective.prototype, "format", null); __decorate([ Input(), __metadata("design:type", Object), __metadata("design:paramtypes", [Object]) ], TimepickerDirective.prototype, "min", null); __decorate([ Input(), __metadata("design:type", Object), __metadata("design:paramtypes", [Object]) ], TimepickerDirective.prototype, "max", null); __decorate([ Input('ngxTimepicker'), __metadata("design:type", NgxMaterialTimepickerComponent), __metadata("design:paramtypes", [NgxMaterialTimepickerComponent]) ], TimepickerDirective.prototype, "timepicker", null); __decorate([ Input(), __metadata("design:type", String), __metadata("design:paramtypes", [String]) ], TimepickerDirective.prototype, "value", null); __decorate([ Input(), __metadata("design:type", Boolean) ], TimepickerDirective.prototype, "disabled", void 0); __decorate([ Input(), __metadata("design:type", Boolean) ], TimepickerDirective.prototype, "disableClick", void 0); __decorate([ HostListener('click', ['$event']), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], TimepickerDirective.prototype, "onClick", null); TimepickerDirective = __decorate([ Directive({ selector: '[ngxTimepicker]', providers: [VALUE_ACCESSOR], host: { '[disabled]': 'disabled', '(input)': 'onInput($event.target.value)', '(blur)': 'onTouched()', }, }), __metadata("design:paramtypes", [ElementRef]) ], TimepickerDirective); let NgxMaterialTimepickerThemeDirective = class NgxMaterialTimepickerThemeDirective { constructor(elementRef) { this.element = elementRef.nativeElement; } ngAfterViewInit() { if (this.theme) { this.setTheme(this.theme); } } 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]); } } } }; __decorate([ Input('ngxMaterialTimepickerTheme'), __metadata("design:type", Object) ], NgxMaterialTimepickerThemeDirective.prototype, "theme", void 0); NgxMaterialTimepickerThemeDirective = __decorate([ Directive({ selector: '[ngxMaterialTimepickerTheme]' }), __metadata("design:paramtypes", [ElementRef]) ], NgxMaterialTimepickerThemeDirective); function camelCaseToDash(myStr) { return myStr.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); } function getHours(format) { return Array(format).fill(1).map((v, i) => { const angleStep = 30; const time = v + i; const angle = angleStep * time; return { time: time === 24 ? 0 : time, angle }; }); } function disableHours(hours, config) { if (config.min || config.max) { return hours.map(value => { const hour = config.format === 24 ? value.time : TimeAdapter.formatHour(value.time, config.format, config.period); const currentTime = DateTime.fromObject({ hour }).toFormat(TimeFormat.TWELVE); return Object.assign({}, value, { disabled: !TimeAdapter.isTimeAvailable(currentTime, config.min, config.max, 'hours') }); }); } return hours; } function getMinutes(gap = 1) { const minutesCount = 60; const angleStep = 360 / minutesCount; const minutes = []; for (let i = 0; i < minutesCount; i++) { const angle = angleStep * i; if (i % gap === 0) { minutes.push({ time: i, angle: angle !== 0 ? angle : 360 }); } } return minutes; } function disableMinutes(minutes, selectedHour, config) { if (config.min || config.max) { const hour = TimeAdapter.formatHour(selectedHour, config.format, config.period); return minutes.map(value => { 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; } class NgxMaterialTimepickerHoursFace { constructor(format) { this.hourChange = new EventEmitter(); this.hourSelected = new EventEmitter(); this.hoursList = []; this.hoursList = getHours(format); } onTimeSelected(time) { this.hourSelected.next(time); } } __decorate([ Input(), __metadata("design:type", Object) ], NgxMaterialTimepickerHoursFace.prototype, "selectedHour", void 0); __decorate([ Input(), __metadata("design:type", DateTime) ], NgxMaterialTimepickerHoursFace.prototype, "minTime", void 0); __decorate([ Input(), __metadata("design:type", DateTime) ], NgxMaterialTimepickerHoursFace.prototype, "maxTime", void 0); __decorate([ Input(), __metadata("design:type", Number) ], NgxMaterialTimepickerHoursFace.prototype, "format", void 0); __decorate([ Output(), __metadata("design:type", Object) ], NgxMaterialTimepickerHoursFace.prototype, "hourChange", void 0); __decorate([ Output(), __metadata("design:type", Object) ], NgxMaterialTimepickerHoursFace.prototype, "hourSelected", void 0); let NgxMaterialTimepicker24HoursFaceComponent = class NgxMaterialTimepicker24HoursFaceComponent extends NgxMaterialTimepickerHoursFace { constructor() { super(24); } ngAfterContentInit() { this.hoursList = disableHours(this.hoursList, { min: this.minTime, max: this.maxTime, format: this.format }); } }; NgxMaterialTimepicker24HoursFaceComponent = __decorate([ Component({ 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" }), __metadata("design:paramtypes", []) ], NgxMaterialTimepicker24HoursFaceComponent); let NgxMaterialTimepicker12HoursFaceComponent = class NgxMaterialTimepicker12HoursFaceComponent extends NgxMaterialTimepickerHoursFace { constructor() { super(12); } 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 }); } } }; __decorate([ Input(), __metadata("design:type", String) ], NgxMaterialTimepicker12HoursFaceComponent.prototype, "period", void 0); NgxMaterialTimepicker12HoursFaceComponent = __decorate([ Component({ 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" }), __metadata("design:paramtypes", []) ], NgxMaterialTimepicker12HoursFaceComponent); let NgxMaterialTimepickerMinutesFaceComponent = class NgxMaterialTimepickerMinutesFaceComponent { constructor() { this.minutesList = []; this.timeUnit = TimeUnit; this.minuteChange = new EventEmitter(); } ngOnChanges(changes) { if (changes['period'] && changes['period'].currentValue) { const minutes = getMinutes(this.minutesGap); this.minutesList = disableMinutes(minutes, this.selectedHour, { min: this.minTime, max: this.maxTime, format: this.format, period: this.period }); } } }; __decorate([ Input(), __metadata("design:type", Object) ], NgxMaterialTimepickerMinutesFaceComponent.prototype, "selectedMinute", void 0); __decorate([ Input(), __metadata("design:type", Number) ], NgxMaterialTimepickerMinutesFaceComponent.prototype, "selectedHour", void 0); __decorate([ Input(), __metadata("design:type", String) ], NgxMaterialTimepickerMinutesFaceComponent.prototype, "period", void 0); __decorate([ Input(), __metadata("design:type", DateTime) ], NgxMaterialTimepickerMinutesFaceComponent.prototype, "minTime", void 0); __decorate([ Input(), __metadata("design:type", DateTime) ], NgxMaterialTimepickerMinutesFaceComponent.prototype, "maxTime", void 0); __decorate([ Input(), __metadata("design:type", Number) ], NgxMaterialTimepickerMinutesFaceComponent.prototype, "format", void 0); __decorate([ Input(), __metadata("design:type", Number) ], NgxMaterialTimepickerMinutesFaceComponent.prototype, "minutesGap", void 0); __decorate([ Output(), __metadata("design:type", Object) ], NgxMaterialTimepickerMinutesFaceComponent.prototype, "minuteChange", void 0); NgxMaterialTimepickerMinutesFaceComponent = __decorate([ Component({ 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); const CLOCK_HAND_STYLES = { small: { height: '75px', top: 'calc(50% - 75px)' }, large: { height: '103px', top: 'calc(50% - 103px)' } }; let NgxMaterialTimepickerFaceComponent = class NgxMaterialTimepickerFaceComponent { constructor() { this.timeUnit = TimeUnit; this.innerClockFaceSize = 85; this.timeChange = new EventEmitter(); this.timeSelected = new EventEmitter(); } ngAfterViewInit() { this.setClockHandPosition(); this.addTouchEvents(); } ngOnChanges(changes) { const faceTimeChanges = changes['faceTime']; 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()); } } trackByTime(_, time) { return time.time; } onMousedown(e) { e.preventDefault(); this.isStarted = true; } selectTime(e) { if (!this.isStarted && (e instanceof MouseEvent && e.type !== 'click')) { return; } const clockFaceCords = this.clockFace.nativeElement.getBoundingClientRect(); /* Get x0 and y0 of the circle */ const centerX = clockFaceCords.left + clockFaceCords.width / 2; const centerY = clockFaceCords.top + clockFaceCords.height / 2; /* Counting the arctangent and convert it to from radian to deg */ const arctangent = Math.atan(Math.abs(e.clientX - centerX) / Math.abs(e.clientY - centerY)) * 180 / Math.PI; /* Get angle according to quadrant */ const circleAngle = countAngleByCords(centerX, centerY, e.clientX, e.clientY, arctangent); /* Check if selected time from the inner clock face (24 hours format only) */ const isInnerClockChosen = this.format && this.isInnerClockFace(centerX, centerY, e.clientX, e.clientY); /* Round angle according to angle step */ const angleStep = this.unit === TimeUnit.MINUTE ? (6 * (this.minutesGap || 1)) : 30; const roundedAngle = isInnerClockChosen ? roundAngle(circleAngle, angleStep) + 360 : roundAngle(circleAngle, angleStep); const angle = roundedAngle === 0 ? 360 : roundedAngle; const selectedTime = this.faceTime.find(val => val.angle === angle); if (selectedTime && !selectedTime.disabled) { this.timeChange.next(selectedTime); /* To let know whether user ended interaction with clock face */ if (!this.isStarted) { this.timeSelected.next(selectedTime.time); } } } onMouseup(e) { e.preventDefault(); this.isStarted = false; } isHourSelected(hour) { return (hour === this.selectedTime.time) && !this.isClockFaceDisabled; } isMinuteSelected(minute) { return ((this.selectedTime.time === minute) && (minute % (this.minutesGap || 5) === 0)) && !this.isClockFaceDisabled; } ngOnDestroy() { this.removeTouchEvents(); } addTouchEvents() { this.touchStartHandler = this.onMousedown.bind(this); this.touchEndHandler = this.onMouseup.bind(this); this.clockFace.nativeElement.addEventListener('touchstart', this.touchStartHandler); this.clockFace.nativeElement.addEventListener('touchend', this.touchEndHandler); } removeTouchEvents() { this.clockFace.nativeElement.removeEventListener('touchstart', this.touchStartHandler); this.clockFace.nativeElement.removeEventListener('touchend', this.touchEndHandler); } setClockHandPosition() { if (this.format === 24) { if (this.selectedTime.time > 12 || this.selectedTime.time === 0) { this.decreaseClockHand(); } else { this.increaseClockHand(); } } this.clockHand.nativeElement.style.transform = `rotate(${this.selectedTime.angle}deg)`; } selectAvailableTime() { const currentTime = this.faceTime.find(time => this.selectedTime.time === time.time); this.isClockFaceDisabled = this.faceTime.every(time => time.disabled); if ((currentTime && currentTime.disabled) && !this.isClockFaceDisabled) { const availableTime = this.faceTime.find(time => !time.disabled); this.timeChange.next(availableTime); } } isInnerClockFace(x0, y0, x, y) { /* Detect whether time from the inner clock face or not (24 format only) */ return Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)) < this.innerClockFaceSize; } decreaseClockHand() { this.clockHand.nativeElement.style.height = CLOCK_HAND_STYLES.small.height; this.clockHand.nativeElement.style.top = CLOCK_HAND_STYLES.small.top; } increaseClockHand() { this.clockHand.nativeElement.style.height = CLOCK_HAND_STYLES.large.height; this.clockHand.nativeElement.style.top = CLOCK_HAND_STYLES.large.top; } }; __decorate([ Input(), __metadata("design:type", Array) ], NgxMaterialTimepickerFaceComponent.prototype, "faceTime", void 0); __decorate([ Input(), __metadata("design:type", Object) ], NgxMaterialTimepickerFaceComponent.prototype, "selectedTime", void 0); __decorate([ Input(), __metadata("design:type", Number) ], NgxMaterialTimepickerFaceComponent.prototype, "unit", void 0); __decorate([ Input(), __metadata("design:type", Number) ], NgxMaterialTimepickerFaceComponent.prototype, "format", void 0); __decorate([ Input(), __metadata("design:type", Number) ], NgxMaterialTimepickerFaceComponent.prototype, "minutesGap", void 0); __decorate([ Output(), __metadata("design:type", Object) ], NgxMaterialTimepickerFaceComponent.prototype, "timeChange", void 0); __decorate([ Output(), __metadata("design:type", Object) ], NgxMaterialTimepickerFaceComponent.prototype, "timeSelected", void 0); __decorate([ ViewChild('clockFace', { static: true }), __metadata("design:type", ElementRef) ], NgxMaterialTimepickerFaceComponent.prototype, "clockFace", void 0); __decorate([ ViewChild('clockHand', { static: true }), __metadata("design:type", ElementRef) ], NgxMaterialTimepickerFaceComponent.prototype, "clockHand", void 0); __decorate([ HostListener('mousedown', ['$event']), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], NgxMaterialTimepickerFaceComponent.prototype, "onMousedown", null); __decorate([ HostListener('click', ['$event']), HostListener('touchmove', ['$event.changedTouches[0]']), HostListener('touchend', ['$event.changedTouches[0]']), HostListener('mousemove', ['$event']), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], NgxMaterialTimepickerFaceComponent.prototype, "selectTime", null); __decorate([ HostListener('mouseup', ['$event']), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", void 0) ], NgxMaterialTimepickerFaceComponent.prototype, "onMouseup", null); NgxMaterialTimepickerFaceComponent = __decorate([ Component({ selector: 'ngx-material-timepicker-face', template: "<div class=\"clock-face\" #clockFace>\n <div *ngIf=\"unit !== timeUnit.MINUTE;else minutesFace\" class=\"clock-face__container\">\n <div class=\"clock-face__number clock-face__number--outer\"\n [style.transform]=\"'rotateZ('+ time.angle +'deg) translateX(-50%)' | styleSanitizer\"\n *ngFor=\"let time of faceTime.slice(0, 12); trackBy: trackByTime\">\n\t\t\t<span [style.transform]=\"'rotateZ(-'+ time.angle +'deg)' | styleSanitizer\"\n [ngClass]=\"{'active': isHourSelected(time.time), 'disabled': time.disabled}\">{{time.time}}</span>\n </div>\n <div class=\"clock-face__inner\" *ngIf=\"faceTime.length > 12\"\n [style.top]=\"'calc(50% - ' + innerClockFaceSize + 'px)'\">\n <div class=\"clock-face__number clock-face__number--inner\"\n [style.transform]=\"'rotateZ('+ time.angle +'deg) translateX(-50%)' | styleSanitizer\"\n [style.height.px]=\"innerClockFaceSize\"\n *ngFor=\"let time of faceTime.slice(12, 24); trackBy: trackByTime\">\n\t\t\t<span [style.transform]=\"'rotateZ(-'+ time.angle +'deg)' | styleSanitizer\"\n [ngClass]=\"{'active': isHourSelected(time.time), 'disabled': time.disabled}\">\n {{time.time === 0 ? '00' : time.time}}</span>\n </div>\n </div>\n </div>\n\n <span class=\"clock-face__clock-hand\" [ngClass]=\"{'clock-face__clock-hand_minute': unit === timeUnit.MINUTE}\"\n #clockHand [hidden]=\"isClockFaceDisabled\"></span>\n</div>\n<ng-template #minutesFace>\n <div class=\"clock-face__container\">\n <div class=\"clock-face__number clock-face__number--outer\"\n [style.transform]=\"'rotateZ('+ time.angle +'deg) translateX(-50%)' | styleSanitizer\"\n *ngFor=\"let time of faceTime; trackBy: trackByTime\">\n\t<span [style.transform]=\"'rotateZ(-'+ time.angle +'deg)' | styleSanitizer\"\n [ngClass]=\"{'active': isMinuteSelected(time.time), 'disabled': time.disabled}\">\n\t{{time.time === 0 ? '00' : time.time | minutesFormatter: minutesGap}}</span>\n </div>\n </div>\n</ng-template>\n", changeDetection: ChangeDetectionStrategy.OnPush, styles: [".clock-face{width:290px;height:290px;border-radius:50%;position:relative;display:flex;justify-content:center;padding:20px;box-sizing:border-box;background-color:#f0f0f0}@supports (background-color:var(--clock-face-background-color)){.clock-face{background-color:var(--clock-face-background-color)}}.clock-face__inner{position:absolute}.clock-face__container{margin-left:-2px}.clock-face__number{position:absolute;-webkit-transform-origin