UNPKG

ngx-mat-timepicker

Version:

ngx-mat-timepicker is an Angular material 9+ extension to add time pickers!

871 lines (852 loc) 185 kB
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import * as i0 from '@angular/core'; import { InjectionToken, Injectable, Inject, HostListener, Input, Directive, Pipe, EventEmitter, ElementRef, Output, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component, DOCUMENT, Optional, HostBinding, ContentChild, inject, NgModule } from '@angular/core'; import * as i1$1 from '@angular/cdk/overlay'; import { CdkOverlayOrigin, CdkConnectedOverlay, OverlayModule } from '@angular/cdk/overlay'; import { DateTime, Info } from 'ts-luxon'; import { NgStyle, NgTemplateOutlet, NgClass, SlicePipe, AsyncPipe, CommonModule } from '@angular/common'; import * as i1$2 from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import * as i1 from '@angular/material/button'; import { MatButtonModule, MAT_FAB_DEFAULT_OPTIONS } from '@angular/material/button'; import * as i6 from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar'; import { BehaviorSubject, Subject, takeUntil as takeUntil$1 } from 'rxjs'; import { shareReplay, takeUntil, tap, map, distinctUntilChanged } from 'rxjs/operators'; import { trigger, transition, style, animate, sequence } from '@angular/animations'; import * as i4 from '@angular/forms'; import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; import * as i5 from '@angular/cdk/a11y'; import { A11yModule } from '@angular/cdk/a11y'; import * as i4$1 from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select'; import { MatOptionModule } from '@angular/material/core'; import * as i2 from '@angular/material/form-field'; import { MatFormFieldModule, MatFormField } from '@angular/material/form-field'; import * as i6$1 from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'; import * as i3 from '@angular/material/input'; import { MatInputModule } from '@angular/material/input'; import { PortalModule } from '@angular/cdk/portal'; var NgxMatTimepickerFormat; (function (NgxMatTimepickerFormat) { NgxMatTimepickerFormat["TWELVE"] = "hh:mm a"; NgxMatTimepickerFormat["TWELVE_SHORT"] = "h:m a"; NgxMatTimepickerFormat["TWENTY_FOUR"] = "HH:mm"; NgxMatTimepickerFormat["TWENTY_FOUR_SHORT"] = "H:m"; })(NgxMatTimepickerFormat || (NgxMatTimepickerFormat = {})); var NgxMatTimepickerPeriods; (function (NgxMatTimepickerPeriods) { NgxMatTimepickerPeriods["AM"] = "AM"; NgxMatTimepickerPeriods["PM"] = "PM"; })(NgxMatTimepickerPeriods || (NgxMatTimepickerPeriods = {})); // @dynamic class NgxMatTimepickerAdapter { static { this.defaultFormat = 12; } static { this.defaultLocale = "en-US"; } static { this.defaultNumberingSystem = "latn"; } /*** * Format hour according to time format (12 or 24) */ static formatHour(currentHour, format, period) { if (this.isTwentyFour(format)) { return currentHour; } const hour = period === NgxMatTimepickerPeriods.AM ? currentHour : currentHour + 12; if (period === NgxMatTimepickerPeriods.AM && hour === 12) { return 0; } else if (period === NgxMatTimepickerPeriods.PM && hour === 24) { return 12; } return hour; } static formatTime(time, opts) { if (!time) { return "Invalid Time"; } const parsedTime = this.parseTime(time, opts).setLocale(this.defaultLocale); if (!parsedTime.isValid) { return "Invalid time"; } const isTwelve = !this.isTwentyFour(opts.format); if (isTwelve) { return parsedTime.toLocaleString({ ...DateTime.TIME_SIMPLE, hour12: isTwelve }).replace(/\u200E/g, ""); } return parsedTime.toISOTime({ includeOffset: false, suppressMilliseconds: true, suppressSeconds: true }).replace(/\u200E/g, ""); } static fromDateTimeToString(time, format) { return time.reconfigure({ numberingSystem: this.defaultNumberingSystem, locale: this.defaultLocale }).toFormat(this.isTwentyFour(format) ? NgxMatTimepickerFormat.TWENTY_FOUR : NgxMatTimepickerFormat.TWELVE); } static isBetween(time, before, after, unit = "minutes") { const innerUnit = unit === "hours" ? unit : void 0; return this.isSameOrBefore(time, after, innerUnit) && this.isSameOrAfter(time, before, innerUnit); } static isSameOrAfter(time, compareWith, unit = "minutes") { if (unit === "hours") { return time.hour >= compareWith.hour; } return time.hasSame(compareWith, unit) || time.valueOf() > compareWith.valueOf(); } static isSameOrBefore(time, compareWith, unit = "minutes") { if (unit === "hours") { return time.hour <= compareWith.hour; } return time.hasSame(compareWith, unit) || time.valueOf() <= compareWith.valueOf(); } static isTimeAvailable(time, min, max, granularity, minutesGap, format) { if (!time) { return void 0; } 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) && this.isSameOrAfter(convertedTime, min, granularity); const isBefore = (max && !min) && this.isSameOrBefore(convertedTime, max, granularity); const between = (min && max) && this.isBetween(convertedTime, min, max, granularity); const isAvailable = !min && !max; return isAfter || isBefore || between || isAvailable; } static isTwentyFour(format) { return format === 24; } static parseTime(time, opts) { const localeOpts = this._getLocaleOptionsByTime(time, opts); let timeMask = NgxMatTimepickerFormat.TWENTY_FOUR_SHORT; // If there's a space, means we have the meridiem. Way faster than splitting text // if (~time.indexOf(" ")) { // 09/02/2023 it seems that sometimes the space from the formatter is a nnbsp (Chromium >= 110) // which causes the indexOf(" ") to fail: charCode 32, while nbsp is 8239 if (time.match(/\s/g)) { /* * We translate the meridiem in simple AM or PM letters (instead of A.M.) * because even if we set the locale with NgxMatTimepickerModule.setLocale * the default (en-US) will always be used here */ time = time.replace(/\.\s*/g, ""); timeMask = NgxMatTimepickerFormat.TWELVE_SHORT; } return DateTime.fromFormat(time.replace(/\s+/g, " "), timeMask, { numberingSystem: localeOpts.numberingSystem, locale: localeOpts.locale }); } static toLocaleTimeString(time, opts = {}) { const { format = this.defaultFormat, locale = this.defaultLocale } = opts; let hourCycle = "h12"; let timeMask = NgxMatTimepickerFormat.TWELVE_SHORT; if (this.isTwentyFour(format)) { hourCycle = "h23"; timeMask = NgxMatTimepickerFormat.TWENTY_FOUR_SHORT; } return DateTime.fromFormat(time, timeMask).reconfigure({ locale, numberingSystem: opts.numberingSystem, defaultToEN: opts.defaultToEN, outputCalendar: opts.outputCalendar }).toLocaleString({ ...DateTime.TIME_SIMPLE, hourCycle }); } /** * * @param time * @param opts * @private */ static _getLocaleOptionsByTime(time, opts) { const { numberingSystem, locale } = DateTime.now().reconfigure({ locale: opts.locale, numberingSystem: opts.numberingSystem, outputCalendar: opts.outputCalendar, defaultToEN: opts.defaultToEN }).resolvedLocaleOptions(); return isNaN(parseInt(time, 10)) ? { numberingSystem: numberingSystem, locale } : { numberingSystem: this.defaultNumberingSystem, locale: this.defaultLocale }; } } var NgxMatTimepickerUnits; (function (NgxMatTimepickerUnits) { NgxMatTimepickerUnits[NgxMatTimepickerUnits["HOUR"] = 0] = "HOUR"; NgxMatTimepickerUnits[NgxMatTimepickerUnits["MINUTE"] = 1] = "MINUTE"; })(NgxMatTimepickerUnits || (NgxMatTimepickerUnits = {})); const NGX_MAT_TIMEPICKER_CONFIG = new InjectionToken("NGX_MAT_TIMEPICKER_CONFIG"); function provideNgxMatTimepickerOptions(config) { return [ { provide: NGX_MAT_TIMEPICKER_CONFIG, useValue: config }, ]; } const DEFAULT_HOUR = { time: 12, angle: 360 }; const DEFAULT_MINUTE = { time: 0, angle: 360 }; class NgxMatTimepickerService { constructor() { this._hour$ = new BehaviorSubject(DEFAULT_HOUR); this._minute$ = new BehaviorSubject(DEFAULT_MINUTE); this._period$ = new BehaviorSubject(NgxMatTimepickerPeriods.AM); } set hour(hour) { this._hour$.next(hour); } set minute(minute) { this._minute$.next(minute); } set period(period) { const isPeriodValid = (period === NgxMatTimepickerPeriods.AM) || (period === NgxMatTimepickerPeriods.PM); if (isPeriodValid) { this._period$.next(period); } } get selectedHour() { return this._hour$.asObservable(); } get selectedMinute() { return this._minute$.asObservable(); } get selectedPeriod() { return this._period$.asObservable(); } getFullTime(format) { const selectedHour = this._hour$.getValue().time; const selectedMinute = this._minute$.getValue().time; const hour = selectedHour != null ? selectedHour : DEFAULT_HOUR.time; const minute = selectedMinute != null ? selectedMinute : DEFAULT_MINUTE.time; const period = format === 12 ? this._period$.getValue() : ""; const time = `${hour}:${minute} ${period}`.trim(); return NgxMatTimepickerAdapter.formatTime(time, { format }); } setDefaultTimeIfAvailable(time, min, max, format, minutesGap) { time || this._resetTime(); /* Workaround to double error message*/ try { if (NgxMatTimepickerAdapter.isTimeAvailable(time, min, max, "minutes", minutesGap)) { this._setDefaultTime(time, format); } } catch (e) { console.error(e); } } _resetTime() { this.hour = { ...DEFAULT_HOUR }; this.minute = { ...DEFAULT_MINUTE }; this.period = NgxMatTimepickerPeriods.AM; } _setDefaultTime(time, format) { const defaultDto = NgxMatTimepickerAdapter.parseTime(time, { format }); if (defaultDto.isValid) { const period = time.substring(time.length - 2).toUpperCase(); const hour = defaultDto.hour; this.hour = { ...DEFAULT_HOUR, time: formatHourByPeriod(hour, period) }; this.minute = { ...DEFAULT_MINUTE, time: defaultDto.minute }; this.period = period; } else { this._resetTime(); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerService, providedIn: "root" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerService, decorators: [{ type: Injectable, args: [{ providedIn: "root" }] }] }); /*** * Format hour in 24hours format to meridian (AM or PM) format */ function formatHourByPeriod(hour, period) { switch (period) { case NgxMatTimepickerPeriods.AM: return hour === 0 ? 12 : hour; case NgxMatTimepickerPeriods.PM: return hour === 12 ? 12 : hour - 12; default: return hour; } } class NgxMatTimepickerEventService { get backdropClick() { return this._backdropClick$.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true })); } get keydownEvent() { return this._keydownEvent$.asObservable().pipe(shareReplay({ bufferSize: 1, refCount: true })); } constructor() { this._backdropClick$ = new Subject(); this._keydownEvent$ = new Subject(); } dispatchEvent(event) { switch (event.type) { case "click": this._backdropClick$.next(event); break; case "keydown": this._keydownEvent$.next(event); break; default: throw new Error("no such event type"); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerEventService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerEventService, providedIn: "root" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerEventService, decorators: [{ type: Injectable, args: [{ providedIn: "root" }] }], ctorParameters: () => [] }); const NGX_MAT_TIMEPICKER_LOCALE = new InjectionToken("TimeLocale", { providedIn: "root", factory: () => NgxMatTimepickerAdapter.defaultLocale }); class NgxMatTimepickerLocaleService { get locale() { return this._locale; } constructor(initialLocale) { this._locale = initialLocale; } updateLocale(newValue) { this._locale = newValue || this._initialLocale; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerLocaleService, deps: [{ token: NGX_MAT_TIMEPICKER_LOCALE }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerLocaleService, providedIn: "root" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerLocaleService, decorators: [{ type: Injectable, args: [{ providedIn: "root" }] }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [NGX_MAT_TIMEPICKER_LOCALE] }] }] }); class NgxMatTimepickerBaseDirective { set color(newValue) { this._color = newValue; } get color() { return this._color; } get defaultTime() { return this._defaultTime; } set defaultTime(time) { this._defaultTime = time; this._setDefaultTime(time); } get _locale() { return this._timepickerLocaleSrv.locale; } constructor(_timepickerSrv, _eventSrv, _timepickerLocaleSrv, data) { this._timepickerSrv = _timepickerSrv; this._eventSrv = _eventSrv; this._timepickerLocaleSrv = _timepickerLocaleSrv; this.data = data; this.activeTimeUnit = NgxMatTimepickerUnits.HOUR; this.timeUnit = NgxMatTimepickerUnits; this._color = "primary"; this._subsCtrl$ = new Subject(); this.color = data.color; this.defaultTime = data.defaultTime; } changePeriod(period) { this._timepickerSrv.period = period; this._onTimeChange(); } changeTimeUnit(unit) { this.activeTimeUnit = unit; } close() { this.data.timepickerBaseRef.close(); } ngOnDestroy() { this._subsCtrl$.next(); this._subsCtrl$.complete(); } ngOnInit() { this._defineTime(); this.selectedHour = this._timepickerSrv.selectedHour .pipe(shareReplay({ bufferSize: 1, refCount: true })); this.selectedMinute = this._timepickerSrv.selectedMinute .pipe(shareReplay({ bufferSize: 1, refCount: true })); this.selectedPeriod = this._timepickerSrv.selectedPeriod .pipe(shareReplay({ bufferSize: 1, refCount: true })); this.data.timepickerBaseRef.timeUpdated.pipe(takeUntil(this._subsCtrl$)) .subscribe({ next: (v) => { v && this._setDefaultTime(v); } }); } onHourChange(hour) { this._timepickerSrv.hour = hour; this._onTimeChange(); } onHourSelected(hour) { if (!this.data.hoursOnly) { this.changeTimeUnit(NgxMatTimepickerUnits.MINUTE); } this.data.timepickerBaseRef.hourSelected.next(hour); } onKeydown(e) { this._eventSrv.dispatchEvent(e); e.stopPropagation(); } onMinuteChange(minute) { this._timepickerSrv.minute = minute; this._onTimeChange(); } setTime() { this.data.timepickerBaseRef.timeSet.emit(this._timepickerSrv.getFullTime(this.data.format)); this.close(); } _defineTime() { const minTime = this.data.minTime; if (minTime && (!this.data.time && !this.data.defaultTime)) { const time = NgxMatTimepickerAdapter.fromDateTimeToString(minTime, this.data.format); this._setDefaultTime(time); } } _onTimeChange() { const time = NgxMatTimepickerAdapter.toLocaleTimeString(this._timepickerSrv.getFullTime(this.data.format), { locale: this._locale, format: this.data.format }); this.data.timepickerBaseRef.timeChanged.emit(time); } _setDefaultTime(time) { this._timepickerSrv.setDefaultTimeIfAvailable(time, this.data.minTime, this.data.maxTime, this.data.format, this.data.minutesGap); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerBaseDirective, deps: [{ token: NgxMatTimepickerService }, { token: NgxMatTimepickerEventService }, { token: NgxMatTimepickerLocaleService }, { token: NGX_MAT_TIMEPICKER_CONFIG }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.3", type: NgxMatTimepickerBaseDirective, isStandalone: true, selector: "[ngxMatTimepickerBase]", inputs: { color: "color", defaultTime: "defaultTime" }, host: { listeners: { "keydown": "onKeydown($event)" } }, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerBaseDirective, decorators: [{ type: Directive, args: [{ selector: "[ngxMatTimepickerBase]" }] }], ctorParameters: () => [{ type: NgxMatTimepickerService }, { type: NgxMatTimepickerEventService }, { type: NgxMatTimepickerLocaleService }, { type: undefined, decorators: [{ type: Inject, args: [NGX_MAT_TIMEPICKER_CONFIG] }] }], propDecorators: { color: [{ type: Input }], defaultTime: [{ type: Input }], onKeydown: [{ type: HostListener, args: ["keydown", ["$event"]] }] } }); // @dynamic class NgxMatTimepickerUtils { static get DEFAULT_MINUTES_GAP() { return 5; } static disableHours(hours, config) { if (config.min || config.max) { return hours.map(value => { const hour = NgxMatTimepickerAdapter.isTwentyFour(config.format) ? value.time : NgxMatTimepickerAdapter.formatHour(value.time, config.format, config.period); const currentTime = DateTime.fromObject({ hour }).toFormat(NgxMatTimepickerFormat.TWELVE); return { ...value, disabled: !NgxMatTimepickerAdapter.isTimeAvailable(currentTime, config.min, config.max, "hours") }; }); } return hours; } static disableMinutes(minutes, selectedHour, config) { if (config.min || config.max) { const hour = NgxMatTimepickerAdapter.formatHour(selectedHour, config.format, config.period); let currentTime = DateTime.fromObject({ hour, minute: 0 }); return minutes.map(value => { currentTime = currentTime.set({ minute: value.time }); return { ...value, disabled: !NgxMatTimepickerAdapter.isTimeAvailable(currentTime.toFormat(NgxMatTimepickerFormat.TWELVE), config.min, config.max, "minutes") }; }); } return minutes; } 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 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 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)); } } var NgxMatTimepickerMeasure; (function (NgxMatTimepickerMeasure) { NgxMatTimepickerMeasure["hour"] = "hour"; NgxMatTimepickerMeasure["minute"] = "minute"; })(NgxMatTimepickerMeasure || (NgxMatTimepickerMeasure = {})); class NgxMatTimepickerTimeLocalizerPipe { get _locale() { return this._timepickerLocaleSrv.locale; } constructor(_timepickerLocaleSrv) { this._timepickerLocaleSrv = _timepickerLocaleSrv; } transform(time, timeUnit, isKeyboardEnabled = false) { if (time == null || time === "") { return ""; } switch (timeUnit) { case NgxMatTimepickerUnits.HOUR: { const format = (time === 0 || isKeyboardEnabled) ? "HH" : "H"; return this._formatTime(NgxMatTimepickerMeasure.hour, time, format); } case NgxMatTimepickerUnits.MINUTE: return this._formatTime(NgxMatTimepickerMeasure.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 { throw new Error(`Cannot format provided time - ${time} to locale - ${this._locale}`); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerTimeLocalizerPipe, deps: [{ token: NgxMatTimepickerLocaleService }], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerTimeLocalizerPipe, isStandalone: true, name: "timeLocalizer" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerTimeLocalizerPipe, decorators: [{ type: Pipe, args: [{ name: "timeLocalizer" }] }], ctorParameters: () => [{ type: NgxMatTimepickerLocaleService }] }); class NgxMatTimepickerMinutesFormatterPipe { transform(minute, gap = NgxMatTimepickerUtils.DEFAULT_MINUTES_GAP) { if (!minute) { return minute; } return minute % gap === 0 ? minute : ""; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerMinutesFormatterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerMinutesFormatterPipe, isStandalone: true, name: "minutesFormatter" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerMinutesFormatterPipe, decorators: [{ type: Pipe, args: [{ name: "minutesFormatter" }] }] }); class NgxMatTimepickerActiveMinutePipe { transform(minute, currentMinute, gap, isClockFaceDisabled) { if (minute == null || isClockFaceDisabled) { return false; } return ((currentMinute === minute) && (minute % (gap || NgxMatTimepickerUtils.DEFAULT_MINUTES_GAP) === 0)); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerActiveMinutePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerActiveMinutePipe, isStandalone: true, name: "activeMinute" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerActiveMinutePipe, decorators: [{ type: Pipe, args: [{ name: "activeMinute" }] }] }); class NgxMatTimepickerActiveHourPipe { transform(hour, currentHour, isClockFaceDisabled) { if (hour == null || isClockFaceDisabled) { return false; } return hour === currentHour; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerActiveHourPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); } static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerActiveHourPipe, isStandalone: true, name: "activeHour" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerActiveHourPipe, decorators: [{ type: Pipe, args: [{ name: "activeHour" }] }] }); function roundAngle(angle, step) { return Math.round(angle / step) * step; } function countAngleByCords(x0, y0, x, y, currentAngle) { if (y > y0 && x >= x0) { // II quarter return 180 - currentAngle; } else if (y > y0 && x < x0) { // III quarter return 180 + currentAngle; } else if (y < y0 && x < x0) { // IV quarter return 360 - currentAngle; } else { // I quarter return currentAngle; } } const CLOCK_HAND_STYLES = { small: { height: "75px", top: "calc(50% - 75px)" }, large: { height: "103px", top: "calc(50% - 103px)" } }; class NgxMatTimepickerFaceComponent { constructor() { this.color = "primary"; this.innerClockFaceSize = 85; this.timeChange = new EventEmitter(); this.timeSelected = new EventEmitter(); this.timeUnit = NgxMatTimepickerUnits; } ngAfterViewInit() { this._setClockHandPosition(); this._addTouchEvents(); } ngOnChanges(changes) { // tslint:disable-next-line:no-string-literal const faceTimeChanges = changes["faceTime"]; // tslint:disable-next-line:no-string-literal const selectedTimeChanges = changes["selectedTime"]; if ((faceTimeChanges && faceTimeChanges.currentValue) && (selectedTimeChanges && selectedTimeChanges.currentValue)) { /* Set time according to pass 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()); } } ngOnDestroy() { this._removeTouchEvents(); } onMousedown(e) { e.preventDefault(); this._isStarted = true; } onMouseup(e) { e.preventDefault(); this._isStarted = false; } 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 === NgxMatTimepickerUnits.MINUTE ? (6 * (this.minutesGap || 1)) : 30; const roundedAngle = roundAngle(circleAngle, angleStep); const angle = (roundedAngle || 360) + (isInnerClockChosen ? 360 : 0); 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); } } } trackByTime(_item_, time) { return time.time; } _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); } _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; } _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; } _removeTouchEvents() { this.clockFace.nativeElement.removeEventListener("touchstart", this._touchStartHandler); this.clockFace.nativeElement.removeEventListener("touchend", this._touchEndHandler); } _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); } } _setClockHandPosition() { if (NgxMatTimepickerAdapter.isTwentyFour(this.format)) { if (this.selectedTime.time > 12 || this.selectedTime.time === 0) { this._decreaseClockHand(); } else { this._increaseClockHand(); } } if (this.selectedTime) { this.clockHand.nativeElement.style.transform = `rotate(${this.selectedTime.angle}deg)`; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerFaceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.3", type: NgxMatTimepickerFaceComponent, isStandalone: true, selector: "ngx-mat-timepicker-face", inputs: { color: "color", dottedMinutesInGap: "dottedMinutesInGap", faceTime: "faceTime", format: "format", minutesGap: "minutesGap", selectedTime: "selectedTime", unit: "unit" }, outputs: { timeChange: "timeChange", timeSelected: "timeSelected" }, host: { listeners: { "mousedown": "onMousedown($event)", "mouseup": "onMouseup($event)", "click": "selectTime($event)", "touchmove": "selectTime($event.changedTouches[0])", "touchend": "selectTime($event.changedTouches[0])", "mousemove": "selectTime($event)" } }, viewQueries: [{ propertyName: "clockFace", first: true, predicate: ["clockFace"], descendants: true, static: true }, { propertyName: "clockHand", first: true, predicate: ["clockHand"], descendants: true, read: ElementRef, static: true }], usesOnChanges: true, ngImport: i0, template: "<!-- DEFAULT TEMPLATES - START -->\n<ng-template #hourButton\n let-time>\n <button mat-mini-fab\n disableRipple\n class=\"mat-elevation-z0\"\n [color]=\"(time.time | activeHour: selectedTime?.time : isClockFaceDisabled) ? color : undefined\"\n [ngStyle]=\"{'transform': 'rotateZ(-'+ time.angle +'deg)'}\"\n [disabled]=\"time.disabled\">\n {{time.time | timeLocalizer: timeUnit.HOUR}}\n </button>\n</ng-template>\n<!-- DEFAULT TEMPLATES - END -->\n<div class=\"clock-face\"\n #clockFace>\n @if (unit !== timeUnit.MINUTE) {\n <div\n class=\"clock-face__container\">\n @for (time of faceTime | slice: 0 : 12; track trackByTime($index, time)) {\n <div class=\"clock-face__number clock-face__number--outer\"\n [ngStyle]=\"{'transform': 'rotateZ('+ time.angle +'deg)'}\"\n >\n <ng-content *ngTemplateOutlet=\"hourButton; context: {$implicit: time}\"></ng-content>\n </div>\n }\n @if (faceTime.length > 12) {\n <div class=\"clock-face__inner\"\n >\n @for (time of faceTime | slice: 12 : 24; track trackByTime($index, time)) {\n <div class=\"clock-face__number clock-face__number--inner\"\n [style.top]=\"'calc(50% - ' + innerClockFaceSize + 'px)'\"\n [ngStyle]=\"{'transform': 'rotateZ('+ time.angle +'deg)'}\"\n [style.height.px]=\"innerClockFaceSize\"\n >\n <ng-content *ngTemplateOutlet=\"hourButton; context: {$implicit: time}\"></ng-content>\n </div>\n }\n </div>\n }\n </div>\n } @else {\n <div class=\"clock-face__container\">\n @for (time of faceTime; track trackByTime($index, time)) {\n <div class=\"clock-face__number clock-face__number--outer\"\n [ngStyle]=\"{'transform': 'rotateZ('+ time.angle +'deg)'}\"\n >\n <input #current\n type=\"hidden\"\n [value]=\"time.time | minutesFormatter: minutesGap | timeLocalizer: timeUnit.MINUTE\" />\n <button mat-mini-fab\n disableRipple\n class=\"mat-elevation-z0\"\n [class.dot]=\"dottedMinutesInGap && current.value === '' && !(time.time | activeMinute: selectedTime?.time:1:isClockFaceDisabled)\"\n [color]=\"(time.time | activeMinute: selectedTime?.time:minutesGap:isClockFaceDisabled) ? color : undefined\"\n [ngStyle]=\"{'transform': 'rotateZ(-'+ time.angle +'deg)'}\"\n [disabled]=\"time.disabled\">\n {{current.value}}\n </button>\n </div>\n }\n </div>\n }\n <mat-toolbar class=\"clock-face__clock-hand\"\n [color]=\"color\"\n [ngClass]=\"{'clock-face__clock-hand_minute': unit === timeUnit.MINUTE}\"\n #clockHand\n [hidden]=\"isClockFaceDisabled\">\n @if (unit === timeUnit.MINUTE) {\n <button mat-mini-fab\n [color]=\"color\">\n <span class=\"clock-face__clock-hand_minute_dot\"></span>\n </button>\n }\n </mat-toolbar>\n <mat-toolbar class=\"clock-face__center\"\n [color]=\"color\"></mat-toolbar>\n </div>\n", styles: ["ngx-mat-timepicker-face [mat-mini-fab].mat-unthemed{--mat-fab-small-container-color: transparent;--mat-fab-small-disabled-state-container-color: transparent;--mat-fab-hover-state-layer-opacity: 0;box-shadow:none}ngx-mat-timepicker-face [mat-mini-fab].mat-unthemed .mat-mdc-button-persistent-ripple{display:none}ngx-mat-timepicker-face [mat-mini-fab].mat-unthemed.dot{position:relative}ngx-mat-timepicker-face [mat-mini-fab].mat-unthemed.dot:after{content:\" \";background-color:#777;width:3px;height:3px;border-radius:50%;left:50%;top:50%;position:absolute;transform:translate(-50%,-50%)}ngx-mat-timepicker-face .clock-face{width:290px;height:290px;border-radius:50%;position:relative;display:flex;justify-content:center;box-sizing:border-box;background-color:#c8c8c880!important}ngx-mat-timepicker-face .clock-face__inner{position:absolute;top:0;left:0;width:100%;height:100%}ngx-mat-timepicker-face .clock-face [mat-mini-fab].mat-void{box-shadow:none;background-color:transparent}ngx-mat-timepicker-face .clock-face [mat-mini-fab].mat-void>span.mat-mdc-button-persistent-ripple{display:none}ngx-mat-timepicker-face .clock-face__container{margin-left:-2px}ngx-mat-timepicker-face .clock-face__number{position:absolute;transform-origin:25px 100%;width:50px;text-align:center;z-index:2;top:calc(50% - 125px);left:calc(50% - 25px)}ngx-mat-timepicker-face .clock-face__number--outer{height:125px}ngx-mat-timepicker-face .clock-face__number--outer>span{font-size:16px}ngx-mat-timepicker-face .clock-face__number--inner>span{font-size:14px}ngx-mat-timepicker-face .clock-face__clock-hand{height:103px;width:2px;padding:0;transform-origin:1px 100%;position:absolute;top:calc(50% - 103px);z-index:1}ngx-mat-timepicker-face .clock-face__center{width:8px;height:8px;padding:0;position:absolute;border-radius:50%;top:50%;left:50%;margin:-4px}ngx-mat-timepicker-face .clock-face__clock-hand_minute>button{position:absolute;top:-22px;left:calc(50% - 20px);box-sizing:content-box;display:flex;justify-content:center;align-items:center}ngx-mat-timepicker-face .clock-face__clock-hand_minute>button .clock-face__clock-hand_minute_dot{display:block;width:4px;height:4px;background:#fff;border-radius:50%}@media(max-device-width:1023px)and (orientation:landscape){ngx-mat-timepicker-face .clock-face{width:250px;height:250px}}@media screen and (max-width:360px){ngx-mat-timepicker-face .clock-face{width:250px;height:250px}}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i6.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: SlicePipe, name: "slice" }, { kind: "pipe", type: NgxMatTimepickerActiveHourPipe, name: "activeHour" }, { kind: "pipe", type: NgxMatTimepickerActiveMinutePipe, name: "activeMinute" }, { kind: "pipe", type: NgxMatTimepickerMinutesFormatterPipe, name: "minutesFormatter" }, { kind: "pipe", type: NgxMatTimepickerTimeLocalizerPipe, name: "timeLocalizer" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgxMatTimepickerFaceComponent, decorators: [{ type: Component, args: [{ selector: "ngx-mat-timepicker-face", changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [ MatButtonModule, NgStyle, NgTemplateOutlet, MatToolbarModule, NgClass, SlicePipe, NgxMatTimepickerActiveHourPipe, NgxMatTimepickerActiveMinutePipe, NgxMatTimepickerMinutesFormatterPipe, NgxMatTimepickerTimeLocalizerPipe ], template: "<!-- DEFAULT TEMPLATES - START -->\n<ng-template #hourButton\n let-time>\n <button mat-mini-fab\n disableRipple\n class=\"mat-elevation-z0\"\n [color]=\"(time.time | activeHour: selectedTime?.time : isClockFaceDisabled) ? color : undefined\"\n [ngStyle]=\"{'transform': 'rotateZ(-'+ time.angle +'deg)'}\"\n [disabled]=\"time.disabled\">\n {{time.time | timeLocalizer: timeUnit.HOUR}}\n </button>\n</ng-template>\n<!-- DEFAULT TEMPLATES - END -->\n<div class=\"clock-face\"\n #clockFace>\n @if (unit !== timeUnit.MINUTE) {\n <div\n class=\"clock-face__container\">\n @for (time of faceTime | slice: 0 : 12; track trackByTime($index, time)) {\n <div class=\"clock-face__number clock-face__number--outer\"\n [ngStyle]=\"{'transform': 'rotateZ('+ time.angle +'deg)'}\"\n >\n <ng-content *ngTemplateOutlet=\"hourButton; context: {$implicit: time}\"></ng-content>\n </div>\n }\n @if (faceTime.length > 12) {\n <div class=\"clock-face__inner\"\n >\n @for (time of faceTime | slice: 12 : 24; track trackByTime($index, time)) {\n <div class=\"clock-face__number clock-face__number--inner\"\n [style.top]=\"'calc(50% - ' + innerClockFaceSize + 'px)'\"\n [ngStyle]=\"{'transform': 'rotateZ('+ time.angle +'deg)'}\"\n [style.height.px]=\"innerClockFaceSize\"\n >\n <ng-content *ngTemplateOutlet=\"hourButton; context: {$implicit: time}\"></ng-content>\n </div>\n }\n </div>\n }\n </div>\n } @else {\n <div class=\"clock-face__container\">\n @for (time of faceTime; track trackByTime($index, time)) {\n <div class=\"clock-face__number clock-face__number--outer\"\n [ngStyle]=\"{'transform': 'rotateZ('+ time.angle +'deg)'}\"\n >\n <input #current\n type=\"hidden\"\n [value]=\"time.time | minutesFormatter: minutesGap | timeLocalizer: timeUnit.MINUTE\" />\n <button mat-mini-fab\n disableRipple\n class=\"mat-elevation-z0\"\n [class.dot]=\"dottedMinutesInGap && current.value === '' && !(time.time | activeMinute: selectedTime?.time:1:isClockFaceDisabled)\"\n [color]=\"(time.time | activeMinute: selectedTime?.time:minutesGap:isClockFaceDisabled) ? color : undefined\"\n [ngStyle]=\"{'transform': 'rotateZ(-'+ time.angle +'deg)'}\"\n [disabled]=\"time.disabled\">\n {{current.value}}\n </button>\n </div>\n }\n </div>\n }\n <mat-toolbar class=\"clock-face__clock-hand\"\n [color]=\"color\"\n [ngClass]=\"{'clock-face__clock-hand_minute': unit === timeUnit.MINUTE}\"\n #clockHand\n [hidden]=\"isClockFaceDisabled\">\n @if (unit === timeUnit.MINUTE) {\n <button mat-mini-fab\n [color]=\"color\">\n <span class=\"clock-face__clock-hand_minute_dot\"></span>\n </button>\n }\n </mat-toolbar>\n <mat-toolbar class=\"clock-face__center\"\n [color]=\"color\"></mat-toolbar>\n </div>\n", styles: ["ngx-mat-timepicker-face [mat-mini-fab].mat-unthemed{--mat-fab-small-container-color: transparent;--mat-fab-small-disabled-state-container-color: transparent;--mat-fab-hover-state-layer-opacity: 0;box-shadow:none}ngx-mat-timepicker-face [mat-mini-fab].mat-unthemed .mat-mdc-button-persistent-ripple{display:none}ngx-mat-timepicker-face [mat-mini-fab].mat-unthemed.dot{position:relative}ngx-mat-timepicker-face [mat-mini-fab].mat-unthemed.dot:after{content:\" \";background-color:#777;width:3px;height:3px;border-radius:50%;left:50%;top:50%;position:absolute;transform:translate(-50%,-50%)}ngx-mat-timepicker-face .clock-face{width:290px;height:290px;border-radius:50%;position:relative;display:flex;justify-content:center;box-sizing:border-box;background-color:#c8c8c880!important}ngx-mat-timepicker-face .clock-face__inner{position:absolute;top:0;left:0;width:100%;height:100%}ngx-mat-timepicker-face .clock-face [mat-mini-fab].mat-void{box-shadow:none;background-color:transparent}ngx-mat-timepicker-face .clock-face [mat-mini-fab].mat-void>span.mat-mdc-button-persistent-ripple{display:none}ngx-mat-timepicker-face .clock-face__container{margin-left:-2px}ngx-mat-timepicker-face .clock-face__number{position:absolute;transform-origin:25px 100%;width:50px;text-align:center;z-index:2;top:calc(50% - 125px);left:calc(50% - 25px)}ngx-mat-timepicker-face .clock-face__number--outer{height:125px}ngx-mat-timepicker-face .clock-face__number--outer>span{font-size:16px}ngx-mat-timepicker-face .clock-face__number--inner>span{font-size:14px}ngx-mat-timepicker-face .clock-face__clock-hand{height:103px;width:2px;padding:0;transform-origin:1px 100%;position:absolute;top:calc(50% - 103px);z-index:1}ngx-mat-timepicker-face .clock-face__center{width:8px;height:8px;padding:0;position:absolute;border-radius:50%;top:50%;left:50%;margin:-4px}ngx-mat-timepicker-face .clock-face__clock-hand_minute>button{position:absolute;top:-22px;left:calc(50% - 20px);box-sizing:content-box;display:flex;justify-content:center;align-items:center}ngx-mat-timepicker-face .clock-face__clock-hand_minute>button .clock-face__clock-hand_minute_dot{display:block;width:4px;height:4px;background:#fff;border-radius:50%}@media(max-device-width:1023px)and (orientation:landscape){ngx-mat-timepicker-face .clock-face{width:250px;height:250px}}@media screen and (max-width:360px){ngx-mat-timepicker-face .clock-face{width:250px;height:250px}}\n"] }] }], propDecorators: { clockFace: [{ type: ViewChild, args: ["clockFace", { static: true }] }], clockHand: [{ type: ViewChild, args: ["clockHand", { static: true, read: ElementRef }] }], color: [{ type: Input }], dottedMinutesInGap: [{ type: Input }], faceTime: [{ type: Input }], format: [{ type: Input }], minutesGap: [{ type: Input }], selectedTime: [{ type: Input }], timeChange: [{ type: Output }], timeSelected: [{ type: Output }], unit: [{ type: Input }], onMousedown: [{ type: HostListener, args: ["mousedown", ["$event"]] }], onMouseup: [{ type: HostListener, args: ["mouseup", ["$event"]] }], selectTime: [{ type: HostListener, args: ["click", ["$event"]] }, { type: HostListener, args: ["touchmove", ["$event.changedTouches[0]"]] }, { type: HostListener, args: ["touchend", ["$event.changedTouches[0]"]] }, { type: HostListener, args: ["mousemove", ["$event"]] }] } }); class NgxMatTimepickerMinutesFaceComponent { set color(newValue) { this._color = newValue; } get color() { return this._color; } constructor() { this.minuteChange = new EventEmitter(); this.minutesList = []; this.timeUnit = NgxMatTimepickerUnits; this._color = "primary"; } ngOnChanges(changes) { // tslint:disable-next-line:no-string-literal if (changes["period"] && changes["period"].currentValue) { const minutes = NgxMatTimepickerUtils.getMinutes(this.minutesGap);