ngx-material-timepicker
Version:
Handy material design timepicker for angular
891 lines (872 loc) • 148 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, InjectionToken, Directive, Input, HostListener, Component, Pipe, Inject, Optional, EventEmitter, ViewChild, Output, ChangeDetectionStrategy, ContentChild, NgModule } from '@angular/core';
import * as i1 from '@angular/common';
import { DOCUMENT, CommonModule } from '@angular/common';
import { BehaviorSubject, Subject, merge } from 'rxjs';
import { shareReplay, tap, debounceTime, distinctUntilChanged, filter, takeUntil, map } from 'rxjs/operators';
import { trigger, transition, style, animate, sequence } from '@angular/animations';
import { DateTime, Info } from 'luxon';
import * as i4 from '@angular/forms';
import { FormControl, NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms';
var TimeUnit;
(function (TimeUnit) {
TimeUnit[TimeUnit["HOUR"] = 0] = "HOUR";
TimeUnit[TimeUnit["MINUTE"] = 1] = "MINUTE";
})(TimeUnit || (TimeUnit = {}));
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, opts) {
const { numberingSystem, locale } = TimeAdapter.getLocaleOptionsByTime(time, opts);
const isPeriodExist = time.split(' ').length === 2;
const timeMask = isPeriodExist ? TimeFormat.TWELVE_SHORT : TimeFormat.TWENTY_FOUR_SHORT;
return DateTime.fromFormat(time, timeMask, { numberingSystem, locale });
}
static formatTime(time, opts) {
if (!time) {
return 'Invalid Time';
}
const { format } = opts;
const parsedTime = TimeAdapter.parseTime(time, opts).setLocale(TimeAdapter.DEFAULT_LOCALE);
if (!parsedTime.isValid) {
return null;
}
if (format !== 24) {
return parsedTime.toLocaleString(Object.assign(Object.assign({}, DateTime.TIME_SIMPLE), { hour12: format !== 24, numberingSystem: TimeAdapter.DEFAULT_NUMBERING_SYSTEM })).replace(/\u200E/g, '').replace(/\u202F/g, ' ');
}
return parsedTime.toISOTime({
includeOffset: false,
suppressMilliseconds: true,
suppressSeconds: true
}).replace(/\u200E/g, '').replace(/\u202F/g, ' ');
}
static toLocaleTimeString(time, opts = {}) {
const { format = TimeAdapter.DEFAULT_FORMAT, locale = TimeAdapter.DEFAULT_LOCALE } = opts;
const hourCycle = format === 24 ? 'h23' : 'h12';
const timeFormat = Object.assign(Object.assign({}, DateTime.TIME_SIMPLE), { hourCycle });
const timeMask = (format === 24) ? TimeFormat.TWENTY_FOUR_SHORT : TimeFormat.TWELVE_SHORT;
const localOpts = Object.assign({ locale: opts.locale, numberingSystem: opts.numberingSystem }, timeFormat);
return DateTime.fromFormat(time, timeMask).setLocale(locale).toLocaleString(localOpts).replace(/\u202F/g, ' ');
}
static isTimeAvailable(time, min, max, granularity, minutesGap, format) {
if (!time) {
return;
}
const convertedTime = this.parseTime(time, { format });
const minutes = convertedTime.minute;
if (minutesGap && minutes === minutes && 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;
}
static fromDateTimeToString(time, format) {
const timeFormat = format === 24 ? TimeFormat.TWENTY_FOUR : TimeFormat.TWELVE;
return time.reconfigure({
numberingSystem: TimeAdapter.DEFAULT_NUMBERING_SYSTEM,
locale: TimeAdapter.DEFAULT_LOCALE
}).toFormat(timeFormat).replace(/\u202F/g, ' ');
}
static getLocaleOptionsByTime(time, opts) {
const localeConfig = { numberingSystem: opts.numberingSystem, locale: opts.locale };
const defaultConfig = { numberingSystem: TimeAdapter.DEFAULT_NUMBERING_SYSTEM, locale: TimeAdapter.DEFAULT_LOCALE };
return isNaN(parseInt(time, 10)) ? localeConfig : defaultConfig;
}
}
TimeAdapter.DEFAULT_FORMAT = 12;
TimeAdapter.DEFAULT_LOCALE = 'en-US';
TimeAdapter.DEFAULT_NUMBERING_SYSTEM = 'latn';
const DEFAULT_HOUR = {
time: 12,
angle: 360
};
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);
}
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(time, format);
}
}
catch (e) {
console.error(e);
}
}
getFullTime(format) {
const selectedHour = this.hourSubject.getValue().time;
const selectedMinute = this.minuteSubject.getValue().time;
const hour = selectedHour != null ? selectedHour : DEFAULT_HOUR.time;
const minute = selectedMinute != null ? selectedMinute : DEFAULT_MINUTE.time;
const period = format === 12 ? this.periodSubject.getValue() : '';
const time = `${hour}:${minute} ${period}`.trim();
return TimeAdapter.formatTime(time, { format });
}
setDefaultTime(time, format) {
const defaultTime = TimeAdapter.parseTime(time, { format }).toJSDate();
if (DateTime.fromJSDate(defaultTime).isValid) {
const period = time.substr(time.length - 2).toUpperCase();
const hour = defaultTime.getHours();
this.hour = Object.assign(Object.assign({}, DEFAULT_HOUR), { time: formatHourByPeriod(hour, period) });
this.minute = Object.assign(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.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
NgxMaterialTimepickerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
/***
* 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;
}
}
const TIME_LOCALE = new InjectionToken('TimeLocale', {
providedIn: 'root',
factory: () => TimeAdapter.DEFAULT_LOCALE
});
const NUMBERING_SYSTEM = new InjectionToken('NumberingSystem', {
providedIn: 'root',
factory: () => TimeAdapter.DEFAULT_NUMBERING_SYSTEM
});
class NgxMaterialTimepickerEventService {
constructor() {
this.backdropClickSubject = new Subject();
this.keydownEventSubject = new Subject();
}
get backdropClick() {
return this.backdropClickSubject.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true }));
}
get keydownEvent() {
return this.keydownEventSubject.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true }));
}
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.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
NgxMaterialTimepickerEventService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerEventService, providedIn: 'root' });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerEventService, decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}] });
class AppendToInputDirective {
constructor(elementRef, renderer) {
this.renderer = renderer;
this.element = elementRef.nativeElement;
}
get inputCords() {
return this.inputElement.getBoundingClientRect();
}
get direction() {
const height = this.element.offsetHeight;
const { bottom, top } = this._inputCords;
const isElementFit = (window && window.innerHeight) - bottom < height;
const isTop = isElementFit && top > height;
const isCenter = isElementFit && top < height;
if (isTop) {
return 'top';
}
else if (isCenter) {
return 'center';
}
return 'bottom';
}
ngAfterViewInit() {
this._inputCords = this.inputCords;
this._direction = this.direction;
this.append();
}
changePosition() {
const { bottom, top } = this.inputCords;
const y = this.defineElementYByDirection(top, bottom);
this.setStyle('top', `${y}px`);
}
append() {
const { left, bottom, top } = this._inputCords;
const y = this.defineElementYByDirection(top, bottom);
this.setStyle('position', 'fixed');
this.setStyle('left', `${left}px`);
this.setStyle('top', `${y}px`);
}
setStyle(style, value) {
this.renderer.setStyle(this.element, style, value);
}
defineElementYByDirection(inputTop, inputBottom) {
if (this._direction === 'top') {
return inputTop - this.element.offsetHeight;
}
else if (this._direction === 'center') {
return inputTop - (this.element.offsetHeight / 2);
}
return inputBottom;
}
}
AppendToInputDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: AppendToInputDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
AppendToInputDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "12.2.17", type: AppendToInputDirective, selector: "[ngxAppendToInput]", inputs: { inputElement: ["ngxAppendToInput", "inputElement"] }, host: { listeners: { "window:scroll": "changePosition()" } }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: AppendToInputDirective, decorators: [{
type: Directive,
args: [{
selector: '[ngxAppendToInput]'
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }]; }, propDecorators: { inputElement: [{
type: Input,
args: ['ngxAppendToInput']
}], changePosition: [{
type: HostListener,
args: ['window:scroll']
}] } });
class NgxMaterialTimepickerContentComponent {
}
NgxMaterialTimepickerContentComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
NgxMaterialTimepickerContentComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.17", type: NgxMaterialTimepickerContentComponent, selector: "ngx-material-timepicker-content", inputs: { appendToInput: "appendToInput", inputElement: "inputElement" }, ngImport: i0, template: "<div [ngxAppendToInput]=\"inputElement\" *ngIf=\"appendToInput;else timepickerModal\">\n <!--suppress HtmlUnknownAttribute -->\n <ng-container *ngTemplateOutlet=\"timepickerOutlet\"></ng-container>\n</div>\n\n<ng-template #timepickerModal>\n <!--suppress HtmlUnknownAttribute -->\n <ng-container *ngTemplateOutlet=\"timepickerOutlet\"></ng-container>\n</ng-template>\n\n<ng-template #timepickerOutlet>\n <ng-content></ng-content>\n</ng-template>\n", directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: AppendToInputDirective, selector: "[ngxAppendToInput]", inputs: ["ngxAppendToInput"] }, { type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerContentComponent, decorators: [{
type: Component,
args: [{
selector: 'ngx-material-timepicker-content',
templateUrl: './ngx-material-timepicker-content.component.html',
}]
}], propDecorators: { appendToInput: [{
type: Input
}], inputElement: [{
type: Input
}] } });
// @dynamic
class TimepickerTimeUtils {
static 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 };
});
}
static 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(Object.assign({}, value), { disabled: !TimeAdapter.isTimeAvailable(currentTime, config.min, config.max, 'hours') });
});
}
return hours;
}
static 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;
}
static 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(Object.assign({}, value), { disabled: !TimeAdapter.isTimeAvailable(currentTime, config.min, config.max, 'minutes') });
});
}
return minutes;
}
}
class TimeLocalizerPipe {
constructor(locale) {
this.locale = locale;
}
transform(time, timeUnit, isKeyboardEnabled = false) {
if (time == null || time === '') {
return '';
}
switch (timeUnit) {
case TimeUnit.HOUR: {
const format = (time === 0 || isKeyboardEnabled) ? 'HH' : 'H';
return this.formatTime('hour', time, format);
}
case TimeUnit.MINUTE:
return this.formatTime('minute', time, 'mm');
default:
throw new Error(`There is no Time Unit with type ${timeUnit}`);
}
}
formatTime(timeMeasure, time, format) {
try {
return DateTime.fromObject({ [timeMeasure]: +time }).setLocale(this.locale).toFormat(format);
}
catch (_a) {
throw new Error(`Cannot format provided time - ${time} to locale - ${this.locale}`);
}
}
}
TimeLocalizerPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: TimeLocalizerPipe, deps: [{ token: TIME_LOCALE }], target: i0.ɵɵFactoryTarget.Pipe });
TimeLocalizerPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: TimeLocalizerPipe, name: "timeLocalizer" });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: TimeLocalizerPipe, decorators: [{
type: Pipe,
args: [{
name: 'timeLocalizer'
}]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [TIME_LOCALE]
}] }]; } });
class TimeParserPipe {
constructor(locale, numberingSystem) {
this.locale = locale;
this.numberingSystem = numberingSystem;
}
transform(time, timeUnit = TimeUnit.HOUR) {
if (time == null || time === '') {
return '';
}
if (!isNaN(+time)) {
return time;
}
if (timeUnit === TimeUnit.MINUTE) {
return this.parseTime(time, 'm', 'minute');
}
return this.parseTime(time, 'H', 'hour');
}
parseTime(time, format, timeMeasure) {
const parsedTime = DateTime.fromFormat(String(time), format, {
numberingSystem: this.numberingSystem,
locale: this.locale
})[timeMeasure];
if (!isNaN(parsedTime)) {
return parsedTime;
}
throw new Error(`Cannot parse time - ${time}`);
}
}
TimeParserPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: TimeParserPipe, deps: [{ token: TIME_LOCALE }, { token: NUMBERING_SYSTEM }], target: i0.ɵɵFactoryTarget.Pipe });
TimeParserPipe.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: TimeParserPipe, name: "timeParser" });
TimeParserPipe.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: TimeParserPipe });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: TimeParserPipe, decorators: [{
type: Pipe,
args: [{
name: 'timeParser'
}]
}, {
type: Injectable
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [TIME_LOCALE]
}] }, { type: undefined, decorators: [{
type: Inject,
args: [NUMBERING_SYSTEM]
}] }]; } });
class AutofocusDirective {
constructor(element, document) {
this.element = element;
this.document = document;
this.activeElement = this.document.activeElement;
}
ngOnChanges() {
if (this.isFocusActive) {
// To avoid ExpressionChangedAfterItHasBeenCheckedError;
setTimeout(() => this.element.nativeElement.focus({ preventScroll: true }));
}
}
ngOnDestroy() {
// To avoid ExpressionChangedAfterItHasBeenCheckedError;
setTimeout(() => this.activeElement.focus({ preventScroll: true }));
}
}
AutofocusDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: AutofocusDirective, deps: [{ token: i0.ElementRef }, { token: DOCUMENT, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
AutofocusDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "12.2.17", type: AutofocusDirective, selector: "[timepickerAutofocus]", inputs: { isFocusActive: ["timepickerAutofocus", "isFocusActive"] }, usesOnChanges: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: AutofocusDirective, decorators: [{
type: Directive,
args: [{
selector: '[timepickerAutofocus]'
}]
}], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [DOCUMENT]
}] }]; }, propDecorators: { isFocusActive: [{
type: Input,
args: ['timepickerAutofocus']
}] } });
/* tslint:disable:triple-equals */
class NgxMaterialTimepickerDialControlComponent {
constructor(timeParserPipe, timeLocalizerPipe) {
this.timeParserPipe = timeParserPipe;
this.timeLocalizerPipe = timeLocalizerPipe;
this.timeUnitChanged = new EventEmitter();
this.timeChanged = new EventEmitter();
this.focused = new EventEmitter();
this.unfocused = new EventEmitter();
}
get selectedTime() {
if (!!this.time) {
return this.timeList.find(t => t.time === +this.time);
}
}
ngOnInit() {
if (this.isEditable) {
this.timeControl = new FormControl({ value: this.formatTimeForUI(this.time), disabled: this.disabled });
this.timeControl.valueChanges.pipe(tap((value) => {
if (value.length > 2) {
this.updateInputValue(value.slice(-1));
}
}), debounceTime(500), distinctUntilChanged(), filter((value) => !isTimeDisabledToChange(this.time, value, this.timeList)), tap((value) => this.time = this.timeParserPipe.transform(value, this.timeUnit).toString())).subscribe(() => this.updateTime());
}
}
saveTimeAndChangeTimeUnit(event, unit) {
event.preventDefault();
this.previousTime = this.time;
this.timeUnitChanged.next(unit);
this.focused.next();
}
updateTime() {
const time = this.selectedTime;
if (time) {
this.timeChanged.next(time);
this.previousTime = time.time;
if (this.isEditable) {
this.updateInputValue(this.formatTimeForUI(time.time));
}
}
}
onKeydown(e) {
if (!isDigit(e)) {
e.preventDefault();
}
else {
this.changeTimeByArrow(e.keyCode);
}
}
changeTimeByArrow(keyCode) {
const ARROW_UP = 38;
const ARROW_DOWN = 40;
let time;
if (keyCode === ARROW_UP) {
time = String(+this.time + (this.minutesGap || 1));
}
else if (keyCode === ARROW_DOWN) {
time = String(+this.time - (this.minutesGap || 1));
}
if (!isTimeUnavailable(time, this.timeList)) {
this.time = time;
this.updateTime();
}
}
formatTimeForUI(value) {
const parsedTime = this.timeParserPipe.transform(value, this.timeUnit).toString();
return this.timeLocalizerPipe.transform(parsedTime, this.timeUnit, true);
}
updateInputValue(value) {
this.editableTimeTmpl.nativeElement.value = value;
}
}
NgxMaterialTimepickerDialControlComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerDialControlComponent, deps: [{ token: TimeParserPipe }, { token: TimeLocalizerPipe }], target: i0.ɵɵFactoryTarget.Component });
NgxMaterialTimepickerDialControlComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.17", type: NgxMaterialTimepickerDialControlComponent, selector: "ngx-material-timepicker-dial-control", inputs: { timeList: "timeList", timeUnit: "timeUnit", time: "time", isActive: "isActive", isEditable: "isEditable", minutesGap: "minutesGap", disabled: "disabled" }, outputs: { timeUnitChanged: "timeUnitChanged", timeChanged: "timeChanged", focused: "focused", unfocused: "unfocused" }, providers: [TimeParserPipe, TimeLocalizerPipe], viewQueries: [{ propertyName: "editableTimeTmpl", first: true, predicate: ["editableTimeTmpl"], descendants: true }], ngImport: i0, template: "<!--suppress HtmlFormInputWithoutLabel, HtmlUnknownAttribute -->\n<input class=\"timepicker-dial__control timepicker-dial__item\"\n [ngClass]=\"{'timepicker-dial__item_active': isActive}\"\n [ngModel]=\"time | timeLocalizer: timeUnit\"\n (ngModelChange)=\"time = $event\"\n [disabled]=\"disabled\"\n (input)=\"updateTime()\" (focus)=\"saveTimeAndChangeTimeUnit($event, timeUnit)\"\n readonly [timepickerAutofocus]=\"isActive\"\n *ngIf=\"!isEditable;else editableTemplate\">\n\n<ng-template #editableTemplate>\n <!--suppress HtmlFormInputWithoutLabel, HtmlUnknownAttribute -->\n <input class=\"timepicker-dial__control timepicker-dial__item timepicker-dial__control_editable\"\n #editableTimeTmpl\n [formControl]=\"timeControl\"\n [ngClass]=\"{'timepicker-dial__item_active': isActive}\"\n [timepickerAutofocus]=\"isActive\"\n (focus)=\"saveTimeAndChangeTimeUnit($event, timeUnit)\"\n (keydown)=\"onKeydown($event)\">\n</ng-template>\n", styles: [".timepicker-dial__item{cursor:pointer;color:#ffffff80;font-family:\"Roboto\",sans-serif}@supports (font-family: var(--primary-font-family)){.timepicker-dial__item{font-family:var(--primary-font-family);color:var(--dial-inactive-color)}}.timepicker-dial__item_active{color:#fff}@supports (color: var(--dial-active-color)){.timepicker-dial__item_active{color:var(--dial-active-color)}}.timepicker-dial__control{border:none;background-color:transparent;font-size:50px;width:60px;padding:0;border-radius:3px;text-align:right}.timepicker-dial__control_editable:focus{color:#00bfff;background-color:#fff;outline:deepskyblue}@supports (color: var(--dial-editable-active-color)){.timepicker-dial__control_editable:focus{color:var(--dial-editable-active-color)}}@supports (background-color: var(--dial-editable-background-color)){.timepicker-dial__control_editable:focus{background-color:var(--dial-editable-background-color)}}@supports (outline: var(--dial-editable-active-color)){.timepicker-dial__control_editable:focus{outline:var(--dial-editable-active-color)}}.timepicker-dial__control:disabled{cursor:default}.timepicker-dial__control:focus-visible{outline:none}\n"], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { type: AutofocusDirective, selector: "[timepickerAutofocus]", inputs: ["timepickerAutofocus"] }, { type: i4.FormControlDirective, selector: "[formControl]", inputs: ["disabled", "formControl", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], pipes: { "timeLocalizer": TimeLocalizerPipe } });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerDialControlComponent, decorators: [{
type: Component,
args: [{
selector: 'ngx-material-timepicker-dial-control',
templateUrl: 'ngx-material-timepicker-dial-control.component.html',
styleUrls: ['ngx-material-timepicker-dial-control.component.scss'],
providers: [TimeParserPipe, TimeLocalizerPipe],
}]
}], ctorParameters: function () { return [{ type: TimeParserPipe }, { type: TimeLocalizerPipe }]; }, propDecorators: { timeList: [{
type: Input
}], timeUnit: [{
type: Input
}], time: [{
type: Input
}], isActive: [{
type: Input
}], isEditable: [{
type: Input
}], minutesGap: [{
type: Input
}], disabled: [{
type: Input
}], editableTimeTmpl: [{
type: ViewChild,
args: ['editableTimeTmpl']
}], timeUnitChanged: [{
type: Output
}], timeChanged: [{
type: Output
}], focused: [{
type: Output
}], unfocused: [{
type: Output
}] } });
function isTimeDisabledToChange(currentTime, nextTime, timeList) {
const isNumber = /\d/.test(nextTime);
if (isNumber) {
return isTimeUnavailable(nextTime, timeList);
}
}
function isTimeUnavailable(time, timeList) {
const selectedTime = timeList.find(value => value.time === +time);
return !selectedTime || (selectedTime && selectedTime.disabled);
}
class NgxMaterialTimepickerPeriodComponent {
constructor() {
this.timePeriod = TimePeriod;
this.isPeriodAvailable = true;
this.periodChanged = new EventEmitter();
}
changePeriod(period) {
this.isPeriodAvailable = this.isSwitchPeriodAvailable(period);
if (this.isPeriodAvailable) {
this.periodChanged.next(period);
}
}
animationDone() {
this.isPeriodAvailable = true;
}
isSwitchPeriodAvailable(period) {
const time = this.getDisabledTimeByPeriod(period);
return !time.every(t => t.disabled);
}
getDisabledTimeByPeriod(period) {
switch (this.activeTimeUnit) {
case TimeUnit.HOUR:
return TimepickerTimeUtils.disableHours(this.hours, {
min: this.minTime,
max: this.maxTime,
format: this.format,
period
});
case TimeUnit.MINUTE:
return TimepickerTimeUtils.disableMinutes(this.minutes, +this.selectedHour, {
min: this.minTime,
max: this.maxTime,
format: this.format,
period
});
default:
throw new Error('no such TimeUnit');
}
}
}
NgxMaterialTimepickerPeriodComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerPeriodComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
NgxMaterialTimepickerPeriodComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.17", type: NgxMaterialTimepickerPeriodComponent, selector: "ngx-material-timepicker-period", inputs: { selectedPeriod: "selectedPeriod", format: "format", activeTimeUnit: "activeTimeUnit", hours: "hours", minutes: "minutes", minTime: "minTime", maxTime: "maxTime", selectedHour: "selectedHour", meridiems: "meridiems" }, outputs: { periodChanged: "periodChanged" }, ngImport: i0, template: "<div class=\"timepicker-period\">\n\t\t\t<button class=\"timepicker-dial__item timepicker-period__btn\"\n [ngClass]=\"{'timepicker-dial__item_active': selectedPeriod === timePeriod.AM}\"\n (click)=\"changePeriod(timePeriod.AM)\"\n type=\"button\">{{meridiems[0]}}</button>\n <button class=\"timepicker-dial__item timepicker-period__btn\"\n [ngClass]=\"{'timepicker-dial__item_active': selectedPeriod === timePeriod.PM}\"\n (click)=\"changePeriod(timePeriod.PM)\"\n type=\"button\">{{meridiems[1]}}</button>\n <div class=\"timepicker-period__warning\" [@scaleInOut] (@scaleInOut.done)=\"animationDone()\" *ngIf=\"!isPeriodAvailable\">\n <p>Current time would be invalid in this period.</p>\n </div>\n</div>\n", styles: [".timepicker-dial__item{cursor:pointer;color:#ffffff80;font-family:\"Roboto\",sans-serif}@supports (font-family: var(--primary-font-family)){.timepicker-dial__item{font-family:var(--primary-font-family);color:var(--dial-inactive-color)}}.timepicker-dial__item_active{color:#fff}@supports (color: var(--dial-active-color)){.timepicker-dial__item_active{color:var(--dial-active-color)}}.timepicker-period{display:flex;flex-direction:column;position:relative}.timepicker-period__btn{padding:1px 3px;border:0;background-color:transparent;font-size:18px;font-weight:500;-webkit-user-select:none;-moz-user-select:none;user-select:none;outline:none;border-radius:3px;transition:background-color .5s;font-family:\"Roboto\",sans-serif}@supports (font-family: var(--primary-font-family)){.timepicker-period__btn{font-family:var(--primary-font-family)}}.timepicker-period__btn:focus{background-color:#00000012}.timepicker-period__warning{padding:5px 10px;border-radius:3px;background-color:#0000008c;color:#fff;position:absolute;width:200px;left:-20px;top:40px}.timepicker-period__warning>p{margin:0;font-size:12px;font-family:\"Roboto\",sans-serif}@supports (font-family: var(--primary-font-family)){.timepicker-period__warning>p{font-family:var(--primary-font-family)}}\n"], directives: [{ type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], animations: [
trigger('scaleInOut', [
transition(':enter', [
style({ transform: 'scale(0)' }),
animate('.2s', style({ transform: 'scale(1)' })),
sequence([
animate('3s', style({ opacity: 1 })),
animate('.3s', style({ opacity: 0 }))
])
])
])
] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerPeriodComponent, decorators: [{
type: Component,
args: [{
selector: 'ngx-material-timepicker-period',
templateUrl: 'ngx-material-timepicker-period.component.html',
styleUrls: ['ngx-material-timepicker-period.component.scss'],
animations: [
trigger('scaleInOut', [
transition(':enter', [
style({ transform: 'scale(0)' }),
animate('.2s', style({ transform: 'scale(1)' })),
sequence([
animate('3s', style({ opacity: 1 })),
animate('.3s', style({ opacity: 0 }))
])
])
])
]
}]
}], propDecorators: { selectedPeriod: [{
type: Input
}], format: [{
type: Input
}], activeTimeUnit: [{
type: Input
}], hours: [{
type: Input
}], minutes: [{
type: Input
}], minTime: [{
type: Input
}], maxTime: [{
type: Input
}], selectedHour: [{
type: Input
}], meridiems: [{
type: Input
}], periodChanged: [{
type: Output
}] } });
class NgxMaterialTimepickerDialComponent {
constructor(locale) {
this.locale = locale;
this.timeUnit = TimeUnit;
this.meridiems = Info.meridiems({ locale: this.locale });
this.periodChanged = new EventEmitter();
this.timeUnitChanged = new EventEmitter();
this.hourChanged = new EventEmitter();
this.minuteChanged = new EventEmitter();
}
ngOnChanges(changes) {
if (changes['period'] && changes['period'].currentValue
|| changes['format'] && changes['format'].currentValue) {
const hours = TimepickerTimeUtils.getHours(this.format);
this.hours = TimepickerTimeUtils.disableHours(hours, {
min: this.minTime,
max: this.maxTime,
format: this.format,
period: this.period,
});
}
if (changes['period'] && changes['period'].currentValue
|| changes['hour'] && changes['hour'].currentValue) {
const minutes = TimepickerTimeUtils.getMinutes(this.minutesGap);
this.minutes = TimepickerTimeUtils.disableMinutes(minutes, +this.hour, {
min: this.minTime,
max: this.maxTime,
format: this.format,
period: this.period,
});
}
}
changeTimeUnit(unit) {
this.timeUnitChanged.next(unit);
}
changePeriod(period) {
this.periodChanged.next(period);
}
changeHour(hour) {
this.hourChanged.next(hour);
if (this.isEditable) {
this.changeTimeUnit(TimeUnit.MINUTE);
}
}
changeMinute(minute) {
this.minuteChanged.next(minute);
}
showHint() {
this.isHintVisible = true;
}
hideHint() {
this.isHintVisible = false;
}
}
NgxMaterialTimepickerDialComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerDialComponent, deps: [{ token: TIME_LOCALE }], target: i0.ɵɵFactoryTarget.Component });
NgxMaterialTimepickerDialComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "12.2.17", type: NgxMaterialTimepickerDialComponent, selector: "ngx-material-timepicker-dial", inputs: { editableHintTmpl: "editableHintTmpl", hour: "hour", minute: "minute", format: "format", period: "period", activeTimeUnit: "activeTimeUnit", minTime: "minTime", maxTime: "maxTime", isEditable: "isEditable", minutesGap: "minutesGap", hoursOnly: "hoursOnly" }, outputs: { periodChanged: "periodChanged", timeUnitChanged: "timeUnitChanged", hourChanged: "hourChanged", minuteChanged: "minuteChanged" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"timepicker-dial\">\n <div class=\"timepicker-dial__container\">\n <div class=\"timepicker-dial__time\">\n <ngx-material-timepicker-dial-control [timeList]=\"hours\" [time]=\"hour\" [timeUnit]=\"timeUnit.HOUR\"\n [isActive]=\"activeTimeUnit === timeUnit.HOUR\"\n [isEditable]=\"isEditable\"\n (timeUnitChanged)=\"changeTimeUnit($event)\"\n (timeChanged)=\"changeHour($event)\"\n (focused)=\"showHint()\"\n (unfocused)=\"hideHint()\">\n\n </ngx-material-timepicker-dial-control>\n <span>:</span>\n <ngx-material-timepicker-dial-control [timeList]=\"minutes\" [time]=\"minute\" [timeUnit]=\"timeUnit.MINUTE\"\n [isActive]=\"activeTimeUnit === timeUnit.MINUTE\"\n [isEditable]=\"isEditable\"\n [minutesGap]=\"minutesGap\"\n [disabled]=\"hoursOnly\"\n (timeUnitChanged)=\"changeTimeUnit($event)\"\n (timeChanged)=\"changeMinute($event)\"\n (focused)=\"showHint()\"\n (unfocused)=\"hideHint()\">\n\n </ngx-material-timepicker-dial-control>\n </div>\n <ngx-material-timepicker-period class=\"timepicker-dial__period\"\n [ngClass]=\"{'timepicker-dial__period--hidden': format === 24}\"\n [selectedPeriod]=\"period\" [activeTimeUnit]=\"activeTimeUnit\"\n [maxTime]=\"maxTime\" [minTime]=\"minTime\" [format]=\"format\"\n [hours]=\"hours\" [minutes]=\"minutes\" [selectedHour]=\"hour\"\n [meridiems]=\"meridiems\"\n (periodChanged)=\"changePeriod($event)\"></ngx-material-timepicker-period>\n </div>\n <div *ngIf=\"isEditable || editableHintTmpl\" [ngClass]=\"{'timepicker-dial__hint-container--hidden': !isHintVisible}\">\n <!--suppress HtmlUnknownAttribute -->\n <ng-container *ngTemplateOutlet=\"editableHintTmpl ? editableHintTmpl : editableHintDefault\"></ng-container>\n <ng-template #editableHintDefault>\n <small class=\"timepicker-dial__hint\"> * use arrows (<span>⇅</span>) to change the time</small>\n </ng-template>\n </div>\n</div>\n", styles: [".timepicker-dial{text-align:right}.timepicker-dial__container{display:flex;align-items:center;justify-content:flex-end;-webkit-tap-highlight-color:rgba(0,0,0,0)}.timepicker-dial__time{display:flex;align-items:baseline;line-height:normal;font-size:50px;color:#ffffff80;font-family:\"Roboto\",sans-serif}@supports (font-family: var(--primary-font-family)){.timepicker-dial__time{font-family:var(--primary-font-family);color:var(--dial-inactive-color)}}.timepicker-dial__period{display:block;margin-left:10px}.timepicker-dial__period--hidden{visibility:hidden}.timepicker-dial__hint-container--hidden{visibility:hidden}.timepicker-dial__hint{display:inline-block;font-size:10px;color:#fff}@supports (color: var(--dial-active-color)){.timepicker-dial__hint{color:var(--dial-active-color)}}.timepicker-dial__hint span{font-size:14px}@media (max-device-width: 1023px) and (orientation: landscape){.timepicker-dial__container{flex-direction:column}.timepicker-dial__period{margin-left:0}}\n"], components: [{ type: NgxMaterialTimepickerDialControlComponent, selector: "ngx-material-timepicker-dial-control", inputs: ["timeList", "timeUnit", "time", "isActive", "isEditable", "minutesGap", "disabled"], outputs: ["timeUnitChanged", "timeChanged", "focused", "unfocused"] }, { type: NgxMaterialTimepickerPeriodComponent, selector: "ngx-material-timepicker-period", inputs: ["selectedPeriod", "format", "activeTimeUnit", "hours", "minutes", "minTime", "maxTime", "selectedHour", "meridiems"], outputs: ["periodChanged"] }], directives: [{ type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerDialComponent, decorators: [{
type: Component,
args: [{
selector: 'ngx-material-timepicker-dial',
templateUrl: 'ngx-material-timepicker-dial.component.html',
styleUrls: ['ngx-material-timepicker-dial.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [TIME_LOCALE]
}] }]; }, propDecorators: { editableHintTmpl: [{
type: Input
}], hour: [{
type: Input
}], minute: [{
type: Input
}], format: [{
type: Input
}], period: [{
type: Input
}], activeTimeUnit: [{
type: Input
}], minTime: [{
type: Input
}], maxTime: [{
type: Input
}], isEditable: [{
type: Input
}], minutesGap: [{
type: Input
}], hoursOnly: [{
type: Input
}], periodChanged: [{
type: Output
}], timeUnitChanged: [{
type: Output
}], hourChanged: [{
type: Output
}], minuteChanged: [{
type: Output
}] } });
class NgxMaterialTimepickerHoursFace {
constructor(format) {
this.hourChange = new EventEmitter();
this.hourSelected = new EventEmitter();
this.hoursList = [];
this.hoursList = TimepickerTimeUtils.getHours(format);
}
onTimeSelected(time) {
this.hourSelected.next(time);
}
}
NgxMaterialTimepickerHoursFace.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerHoursFace, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
NgxMaterialTimepickerHoursFace.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "12.2.17", type: NgxMaterialTimepickerHoursFace, inputs: { selectedHour: "selectedHour", minTime: "minTime", maxTime: "maxTime", format: "format" }, outputs: { hourChange: "hourChange", hourSelected: "hourSelected" }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0, type: NgxMaterialTimepickerHoursFace, decorators: [{
type: Directive
}], ctorParameters: function () { return [{ type: undefined }]; }, propDecorators: { selectedHour: [{
type: Input
}], minTime: [{
type: Input
}], maxTime: [{
type: Input
}], format: [{
type: Input
}], hourChange: [{
type: Output
}], hourSelected: [{
type: Output
}] } });
class ActiveHourPipe {
transform(hour, currentHour, isClockFaceDisabled) {
if (hour == null || isClockFaceDisabled) {
return false;
}
return hour === currentHour;
}
}
ActiveHourPipe.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.1