@dhutaryan/ngx-mat-timepicker
Version:
Angular timepicker to add time which is based on material design and Angular material.
676 lines (667 loc) • 182 kB
JavaScript
import * as i0 from '@angular/core';
import { InjectionToken, Injectable, Directive, Component, ViewEncapsulation, ChangeDetectionStrategy, Attribute, Input, ContentChild, EventEmitter, Inject, Output, inject, LOCALE_ID, Optional, NgModule, HostListener, SkipSelf, ViewChild, booleanAttribute, forwardRef, TemplateRef, signal } from '@angular/core';
import * as i1$1 from '@angular/cdk/overlay';
import { Overlay, FlexibleConnectedPositionStrategy, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
import * as i3$1 from '@angular/cdk/portal';
import { PortalModule, ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
import { CdkScrollableModule } from '@angular/cdk/scrolling';
import * as i4$1 from '@angular/cdk/a11y';
import { A11yModule } from '@angular/cdk/a11y';
import * as i2 from '@angular/material/button';
import { MatButtonModule, MAT_FAB_DEFAULT_OPTIONS } from '@angular/material/button';
import * as i1 from '@angular/common';
import { CommonModule, DOCUMENT } from '@angular/common';
import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
import { Subject, Subscription, of, merge, fromEvent, debounceTime, take, BehaviorSubject, first, filter } from 'rxjs';
import { PlatformModule, _getFocusedElementPierceShadowDom } from '@angular/cdk/platform';
import { DOWN_ARROW, UP_ARROW, PAGE_UP, PAGE_DOWN, ESCAPE, hasModifierKey } from '@angular/cdk/keycodes';
import { trigger, transition, animate, keyframes, style, state } from '@angular/animations';
import * as i2$1 from '@angular/material/divider';
import { MatDividerModule } from '@angular/material/divider';
import * as i3 from '@angular/material/core';
import { MatRippleModule } from '@angular/material/core';
import * as i4 from '@angular/material/form-field';
import { MatFormFieldModule, MAT_FORM_FIELD } from '@angular/material/form-field';
import * as i5 from '@angular/material/input';
import { MatInputModule, MAT_INPUT_VALUE_ACCESSOR } from '@angular/material/input';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, Validators } from '@angular/forms';
/** Injection token that determines the scroll handling while the timepicker is open. */
const MAT_TIMEPICKER_SCROLL_STRATEGY = new InjectionToken('mat-timepicker-scroll-strategy');
/** Timepicker scroll strategy factory. */
function MAT_TIMEPICKER_SCROLL_STRATEGY_FACTORY(overlay) {
return () => overlay.scrollStrategies.reposition();
}
/** Timepicker scroll strategy provider. */
const MAT_TIMEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {
provide: MAT_TIMEPICKER_SCROLL_STRATEGY,
deps: [Overlay],
useFactory: MAT_TIMEPICKER_SCROLL_STRATEGY_FACTORY,
};
class MatTimepickerIntl {
constructor() {
/**
* Stream that emits whenever the labels here are changed. Use this to notify
* components if the labels have changed after initialization.
*/
this.changes = new Subject();
/** A label for inputs title. */
this.inputsTitle = 'Enter time';
/** A label for dials title. */
this.dialsTitle = 'Select time';
/** A label for hour input hint. */
this.hourInputHint = 'Hour';
/** A label for minute input hint. */
this.minuteInputHint = 'Minute';
/** Label for the button used to open the timepicker popup (used by screen readers). */
this.openTimepickerLabel = 'Open timepicker';
/** Label for the button used to close the timepicker popup (used by screen readers). */
this.closeTimepickerLabel = 'Close timepicker';
/** A label for OK button to apply time. */
this.okButton = 'OK';
/** A label for cancel button to close timepicker. */
this.cancelButton = 'Cancel';
/** A label for am text. */
this.am = 'AM';
/** A label for am text. */
this.pm = 'PM';
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: MatTimepickerIntl, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: MatTimepickerIntl, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: MatTimepickerIntl, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
class MatTimepickerToggleIcon {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: MatTimepickerToggleIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.0.1", type: MatTimepickerToggleIcon, isStandalone: true, selector: "[matTimepickerToggleIcon]", ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: MatTimepickerToggleIcon, decorators: [{
type: Directive,
args: [{ selector: '[matTimepickerToggleIcon]', standalone: true }]
}] });
class MatTimepickerToggle {
/** Whether the toggle button is disabled. */
get disabled() {
if (this._disabled === undefined && this.timepicker) {
return this.timepicker.disabled;
}
return !!this._disabled;
}
set disabled(value) {
this._disabled = coerceBooleanProperty(value);
}
constructor(defaultTabIndex, _intl, _cdr) {
this._intl = _intl;
this._cdr = _cdr;
this._stateChanges = Subscription.EMPTY;
const parsedTabIndex = Number(defaultTabIndex);
this.tabIndex =
parsedTabIndex || parsedTabIndex === 0 ? parsedTabIndex : null;
}
ngOnChanges(changes) {
if (changes['timepicker']) {
this._watchStateChanges();
}
}
ngOnDestroy() {
this._stateChanges.unsubscribe();
}
/** Opens timepicker. */
open(event) {
if (this.timepicker && !this.disabled) {
this.timepicker.open();
event.stopPropagation();
}
}
_watchStateChanges() {
const timepickerStateChanged = this.timepicker
? this.timepicker.stateChanges
: of();
const inputStateChanged = this.timepicker && this.timepicker.timepickerInput
? this.timepicker.timepickerInput.stateChanges
: of();
const timepickerToggled = this.timepicker
? merge(this.timepicker.openedStream, this.timepicker.closedStream)
: of();
this._stateChanges.unsubscribe();
this._stateChanges = merge(this._intl.changes, timepickerStateChanged, inputStateChanged, timepickerToggled).subscribe(() => this._cdr.markForCheck());
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: MatTimepickerToggle, deps: [{ token: 'tabindex', attribute: true }, { token: MatTimepickerIntl }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.1", type: MatTimepickerToggle, isStandalone: true, selector: "mat-timepicker-toggle", inputs: { timepicker: ["for", "timepicker"], disabled: "disabled", disableRipple: "disableRipple", tabIndex: "tabIndex", ariaLabel: ["aria-label", "ariaLabel"] }, host: { listeners: { "click": "open($event)" }, properties: { "attr.tabindex": "null", "class.mat-timepicker-toggle-active": "timepicker && timepicker.opened", "class.mat-accent": "timepicker && timepicker.color === \"accent\"", "class.mat-warn": "timepicker && timepicker.color === \"warn\"" }, classAttribute: "mat-timepicker-toggle" }, queries: [{ propertyName: "customIcon", first: true, predicate: MatTimepickerToggleIcon, descendants: true }], exportAs: ["matTimepickerToggle"], usesOnChanges: true, ngImport: i0, template: "<button\n type=\"button\"\n #button\n mat-icon-button\n [attr.aria-haspopup]=\"timepicker ? 'dialog' : null\"\n [attr.aria-label]=\"ariaLabel || _intl.openTimepickerLabel\"\n [attr.tabindex]=\"disabled ? -1 : tabIndex\"\n [disabled]=\"disabled\"\n [disableRipple]=\"disableRipple\"\n>\n @if (!customIcon) {\n <svg\n class=\"mat-timepicker-toggle-default-icon\"\n viewBox=\"0 0 24 24\"\n width=\"24\"\n height=\"24\"\n fill=\"currentColor\"\n focusable=\"false\"\n >\n <path\n d=\"M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-4.5,10-10S17.5,2,12,2z M12,20c-4.41,0-8-3.59-8-8s3.59-8,8-8s8,3.59,8,8 S16.41,20,12,20z M12.5,7H11v6l5.2,3.2l0.8-1.3l-4.5-2.7V7z\"\n />\n </svg>\n }\n\n <ng-content select=\"[matTimepickerToggleIcon]\"></ng-content>\n</button>\n", styles: [".mat-form-field .mat-form-field-prefix .mat-timepicker-toggle-default-icon,.mat-form-field .mat-form-field-suffix .mat-timepicker-toggle-default-icon{display:block;width:1.5em;height:1.5em}.mat-form-field .mat-form-field-prefix .mat-icon-button .mat-timepicker-toggle-default-icon,.mat-form-field .mat-form-field-suffix .mat-icon-button .mat-timepicker-toggle-default-icon{margin:auto}.mat-timepicker-toggle{color:var(--mat-timepicker-toggle-color, var(--mat-sys-on-surface-variant))}.mat-timepicker-toggle.mat-timepicker-toggle-active button{color:var(--mat-timepicker-toggle-active-color, var(--mat-sys-primary))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: MatTimepickerToggle, decorators: [{
type: Component,
args: [{ selector: 'mat-timepicker-toggle', standalone: true, imports: [CommonModule, MatButtonModule], exportAs: 'matTimepickerToggle', host: {
class: 'mat-timepicker-toggle',
'[attr.tabindex]': 'null',
'[class.mat-timepicker-toggle-active]': 'timepicker && timepicker.opened',
'[class.mat-accent]': 'timepicker && timepicker.color === "accent"',
'[class.mat-warn]': 'timepicker && timepicker.color === "warn"',
'(click)': 'open($event)',
}, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n type=\"button\"\n #button\n mat-icon-button\n [attr.aria-haspopup]=\"timepicker ? 'dialog' : null\"\n [attr.aria-label]=\"ariaLabel || _intl.openTimepickerLabel\"\n [attr.tabindex]=\"disabled ? -1 : tabIndex\"\n [disabled]=\"disabled\"\n [disableRipple]=\"disableRipple\"\n>\n @if (!customIcon) {\n <svg\n class=\"mat-timepicker-toggle-default-icon\"\n viewBox=\"0 0 24 24\"\n width=\"24\"\n height=\"24\"\n fill=\"currentColor\"\n focusable=\"false\"\n >\n <path\n d=\"M12,2C6.5,2,2,6.5,2,12s4.5,10,10,10s10-4.5,10-10S17.5,2,12,2z M12,20c-4.41,0-8-3.59-8-8s3.59-8,8-8s8,3.59,8,8 S16.41,20,12,20z M12.5,7H11v6l5.2,3.2l0.8-1.3l-4.5-2.7V7z\"\n />\n </svg>\n }\n\n <ng-content select=\"[matTimepickerToggleIcon]\"></ng-content>\n</button>\n", styles: [".mat-form-field .mat-form-field-prefix .mat-timepicker-toggle-default-icon,.mat-form-field .mat-form-field-suffix .mat-timepicker-toggle-default-icon{display:block;width:1.5em;height:1.5em}.mat-form-field .mat-form-field-prefix .mat-icon-button .mat-timepicker-toggle-default-icon,.mat-form-field .mat-form-field-suffix .mat-icon-button .mat-timepicker-toggle-default-icon{margin:auto}.mat-timepicker-toggle{color:var(--mat-timepicker-toggle-color, var(--mat-sys-on-surface-variant))}.mat-timepicker-toggle.mat-timepicker-toggle-active button{color:var(--mat-timepicker-toggle-active-color, var(--mat-sys-primary))}\n"] }]
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Attribute,
args: ['tabindex']
}] }, { type: MatTimepickerIntl }, { type: i0.ChangeDetectorRef }], propDecorators: { timepicker: [{
type: Input,
args: ['for']
}], disabled: [{
type: Input
}], disableRipple: [{
type: Input
}], tabIndex: [{
type: Input
}], customIcon: [{
type: ContentChild,
args: [MatTimepickerToggleIcon]
}], ariaLabel: [{
type: Input,
args: ['aria-label']
}] } });
/** Animations used by the timepicker. */
const matTimepickerAnimations = {
/** Transforms the height of the timepicker's. */
transformPanel: trigger('transformPanel', [
transition('void => enter-dropdown', animate('120ms cubic-bezier(0, 0, 0.2, 1)', keyframes([
style({ opacity: 0, transform: 'scale(1, 0.8)' }),
style({ opacity: 1, transform: 'scale(1, 1)' }),
]))),
transition('void => enter-dialog', animate('150ms cubic-bezier(0, 0, 0.2, 1)', keyframes([
style({ opacity: 0, transform: 'scale(0.7)' }),
style({ transform: 'none', opacity: 1 }),
]))),
transition('* => void', animate('100ms linear', style({ opacity: 0 }))),
]),
/** Fades in the content of the timepicker. */
fadeInTimepicker: trigger('fadeInTimepicker', [
state('void', style({ opacity: 0 })),
state('enter', style({ opacity: 1 })),
]),
};
const TOUCH_UI_MULTIPLIER = 1.25;
const TOUCH_UI_TICK_MULTIPLIER = 1.5;
const CLOCK_RADIUS = 128;
const CLOCK_TICK_RADIUS = 16;
const CLOCK_OUTER_RADIUS = 100;
function getClockRadius(touchUi) {
return touchUi ? CLOCK_RADIUS * TOUCH_UI_MULTIPLIER : CLOCK_RADIUS;
}
function getClockTickRadius(touchUi) {
return touchUi
? CLOCK_TICK_RADIUS * TOUCH_UI_TICK_MULTIPLIER
: CLOCK_TICK_RADIUS;
}
function getClockCorrectedRadius(touchUi) {
return getClockRadius(touchUi) - getClockTickRadius(touchUi);
}
function getClockOuterRadius(touchUi) {
return touchUi
? CLOCK_OUTER_RADIUS * TOUCH_UI_MULTIPLIER
: CLOCK_OUTER_RADIUS;
}
function getClockInnerRadius(touchUi) {
return getClockOuterRadius(touchUi) - getClockTickRadius(touchUi) * 2;
}
const ALL_MINUTES = Array(60)
.fill(null)
.map((_, i) => i);
class MatMinutesClockDial {
/** Selected minute. */
get selectedMinute() {
return this._selectedMinute;
}
set selectedMinute(value) {
this._selectedMinute = value;
}
/** Step over minutes. */
get interval() {
return this._interval;
}
set interval(value) {
this._interval = coerceNumberProperty(value) || 1;
}
get availableMinutes() {
return this._availableMinutes;
}
set availableMinutes(value) {
this._availableMinutes = value;
this._initMinutes();
}
/** Whether the timepicker UI is in touch mode. */
get touchUi() {
return this._touchUi;
}
set touchUi(value) {
this._touchUi = value;
}
get disabled() {
return !this.availableMinutes.includes(this.selectedMinute);
}
get isMinutePoint() {
return !!this.minutes.find((hour) => hour.value === this.selectedMinute);
}
constructor(_element, _cdr, _document) {
this._element = _element;
this._cdr = _cdr;
this._document = _document;
this._selectedMinute = 0;
this._interval = 1;
this._availableMinutes = [];
/** Emits selected minute. */
this.selectedChange = new EventEmitter();
this.minutes = [];
}
ngOnInit() {
this._initMinutes();
}
/** Hand styles based on selected minute. */
_handStyles() {
const deg = Math.round(this._selectedMinute * (360 / 60));
const height = getClockOuterRadius(this.touchUi);
const marginTop = getClockRadius(this.touchUi) - getClockOuterRadius(this.touchUi);
return {
transform: `rotate(${deg}deg)`,
height: `${height}px`,
'margin-top': `${marginTop}px`,
};
}
/** Handles mouse and touch events on dial and document. */
_onUserAction(event) {
if (event.cancelable) {
event.preventDefault();
}
this._setMinute(event);
const eventsSubscription = merge(fromEvent(this._document, 'mousemove'), fromEvent(this._document, 'touchmove'))
.pipe(debounceTime(0))
.subscribe({
next: (event) => {
event.preventDefault();
this._setMinute(event);
},
});
merge(fromEvent(this._document, 'mouseup'), fromEvent(this._document, 'touchend'))
.pipe(take(1))
.subscribe({
next: () => {
eventsSubscription.unsubscribe();
},
});
}
_isActiveCell(minute) {
return this.selectedMinute === minute;
}
_setMinute(event) {
const element = this._element.nativeElement;
const window = this._getWindow();
const elementRect = element.getBoundingClientRect();
const width = element.offsetWidth;
const height = element.offsetHeight;
const pageX = event instanceof MouseEvent ? event.pageX : event.touches[0].pageX;
const pageY = event instanceof MouseEvent ? event.pageY : event.touches[0].pageY;
const x = width / 2 - (pageX - elementRect.left - window.scrollX);
const y = height / 2 - (pageY - elementRect.top - window.scrollY);
const unit = Math.PI / (30 / this.interval);
const atan2 = Math.atan2(-x, y);
const radian = atan2 < 0 ? Math.PI * 2 + atan2 : atan2;
const initialValue = Math.round(radian / unit) * this.interval;
const value = initialValue === 60 ? 0 : initialValue;
if (this.availableMinutes.includes(value) && this.availableMinutes.includes(value)) {
this.selectedMinute = value;
this.selectedChange.emit(this.selectedMinute);
}
this._cdr.detectChanges();
}
/** Creates list of minutes. */
_initMinutes() {
this.minutes = ALL_MINUTES.filter((minute) => minute % 5 === 0).map((minute) => {
const radian = (minute / 30) * Math.PI;
const displayValue = minute === 0 ? '00' : String(minute);
return {
value: minute,
displayValue,
left: getClockCorrectedRadius(this.touchUi) +
Math.sin(radian) * getClockOuterRadius(this.touchUi),
top: getClockCorrectedRadius(this.touchUi) -
Math.cos(radian) * getClockOuterRadius(this.touchUi),
disabled: !this.availableMinutes.includes(minute),
};
});
}
/** Use defaultView of injected document if available or fallback to global window reference */
_getWindow() {
return this._document.defaultView || window;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: MatMinutesClockDial, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.1", type: MatMinutesClockDial, isStandalone: true, selector: "mat-minutes-clock-dial", inputs: { selectedMinute: "selectedMinute", interval: "interval", availableMinutes: "availableMinutes", color: "color", touchUi: "touchUi" }, outputs: { selectedChange: "selectedChange" }, host: { listeners: { "mousedown": "_onUserAction($event)", "touchstart": "_onUserAction($event)" }, classAttribute: "mat-clock-dial mat-clock-dial-minutes" }, exportAs: ["matMinutesClockDial"], ngImport: i0, template: "<div\n class=\"mat-clock-dial-hand\"\n [class.mat-clock-dial-hand-pointless]=\"isMinutePoint\"\n [class.mat-clock-dial-hand-disabled]=\"disabled\"\n [ngStyle]=\"_handStyles()\"\n>\n <div class=\"mat-clock-dial-hand-point\" tabindex=\"0\"></div>\n</div>\n@for (minute of minutes; track minute.value) {\n <button\n class=\"mat-clock-dial-cell\"\n mat-mini-fab\n disableRipple\n [tabIndex]=\"_isActiveCell(minute.value) ? 0 : -1\"\n [style.left.px]=\"minute.left\"\n [style.top.px]=\"minute.top\"\n [class.mat-clock-dial-cell-active]=\"_isActiveCell(minute.value)\"\n [class.mat-clock-dial-cell-disabled]=\"minute.disabled\"\n [color]=\"_isActiveCell(minute.value) ? color : undefined\"\n [attr.aria-disabled]=\"minute.disabled || null\"\n >\n {{ minute.displayValue }}\n </button>\n}\n", styles: [".mat-clock-dial{position:relative;display:block;width:16rem;height:16rem;margin:0 auto;border-radius:50%;background-color:var(--mat-timepicker-clock-dial-background-color, var(--mat-sys-surface-container-highest))}.mat-clock-dial:before{position:absolute;top:50%;left:50%;width:.4375rem;height:.4375rem;border-radius:50%;transform:translate(-50%,-50%);content:\"\";background-color:var(--mat-timepicker-clock-dial-center-point-color, var(--mat-sys-primary))}[mat-mini-fab].mat-clock-dial-cell{position:absolute;display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:50%;box-shadow:none}[mat-mini-fab].mat-clock-dial-cell:disabled{pointer-events:none}[mat-mini-fab].mat-clock-dial-cell:focus,[mat-mini-fab].mat-clock-dial-cell:hover,[mat-mini-fab].mat-clock-dial-cell:active,[mat-mini-fab].mat-clock-dial-cell:focus:active{box-shadow:none}[mat-mini-fab].mat-clock-dial-cell.mat-clock-dial-cell-disabled.mat-clock-dial-cell-active{background-color:var(--mat-timepicker-clock-dial-cell-active-disabled-color, color-mix(in srgb, var(--mat-sys-primary) 40%, transparent));color:var(--mat-timepicker-clock-dial-cell-active-text-color, var(--mat-sys-on-primary))}.mat-clock-dial-cell:not(.mat-primary):not(.mat-accent):not(.mat-warn){background:var(--mat-timepicker-clock-dial-cell-unthemable-color, transparent)}.mat-clock-dial-cell.mat-clock-dial-cell-active{color:var(--mat-timepicker-clock-dial-cell-active-text-color, var(--mat-sys-on-primary));background-color:var(--mat-timepicker-clock-dial-cell-active-background-color, var(--mat-sys-primary))}.mat-clock-dial-cell.mat-clock-dial-cell-disabled{color:var(--mat-timepicker-clock-dial-cell-disabled-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 40%, transparent))}.mat-clock-dial-cell.mat-clock-dial-cell-disabled .mat-mdc-button-persistent-ripple:before{background-color:var(--mat-timepicker-clock-dial-cell-disabled-background-color, transparent)}.mat-timepicker-content-touch .mat-clock-dial{width:20rem;height:20rem}.mat-timepicker-content-touch [mat-mini-fab].mat-clock-dial-cell{width:3rem;height:3rem;font-size:1.125rem}.mat-clock-dial-hand{position:absolute;inset:0;width:1px;margin:0 auto;transform-origin:bottom}.mat-clock-dial-hand:before{position:absolute;top:-.25rem;left:-.25rem;width:calc(.5rem + 1px);height:calc(.5rem + 1px);border-radius:50%;content:\"\"}.mat-clock-dial-hand.mat-clock-dial-hand-disabled{background-color:var(--mat-timepicker-clock-dial-hand-disabled-color, transparent)}.mat-clock-dial-hand.mat-clock-dial-hand-disabled:before{background-color:var(--mat-timepicker-clock-dial-hand-value-point-disabled-color, color-mix(in srgb, var(--mat-sys-primary) 40%, transparent))}.mat-clock-dial-hand:not(.mat-clock-dial-hand-disabled){background-color:var(--mat-timepicker-clock-dial-hand-color, var(--mat-sys-primary))}.mat-clock-dial-hand:not(.mat-clock-dial-hand-disabled):before{background-color:var(--mat-timepicker-clock-dial-hand-value-point-color, var(--mat-sys-primary))}.mat-clock-dial-hand.mat-clock-dial-hand-pointless:before{content:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatMiniFabButton, selector: "button[mat-mini-fab]", exportAs: ["matButton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: MatMinutesClockDial, decorators: [{
type: Component,
args: [{ selector: 'mat-minutes-clock-dial', standalone: true, imports: [CommonModule, MatButtonModule], exportAs: 'matMinutesClockDial', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
class: 'mat-clock-dial mat-clock-dial-minutes',
'(mousedown)': '_onUserAction($event)',
'(touchstart)': '_onUserAction($event)',
}, template: "<div\n class=\"mat-clock-dial-hand\"\n [class.mat-clock-dial-hand-pointless]=\"isMinutePoint\"\n [class.mat-clock-dial-hand-disabled]=\"disabled\"\n [ngStyle]=\"_handStyles()\"\n>\n <div class=\"mat-clock-dial-hand-point\" tabindex=\"0\"></div>\n</div>\n@for (minute of minutes; track minute.value) {\n <button\n class=\"mat-clock-dial-cell\"\n mat-mini-fab\n disableRipple\n [tabIndex]=\"_isActiveCell(minute.value) ? 0 : -1\"\n [style.left.px]=\"minute.left\"\n [style.top.px]=\"minute.top\"\n [class.mat-clock-dial-cell-active]=\"_isActiveCell(minute.value)\"\n [class.mat-clock-dial-cell-disabled]=\"minute.disabled\"\n [color]=\"_isActiveCell(minute.value) ? color : undefined\"\n [attr.aria-disabled]=\"minute.disabled || null\"\n >\n {{ minute.displayValue }}\n </button>\n}\n", styles: [".mat-clock-dial{position:relative;display:block;width:16rem;height:16rem;margin:0 auto;border-radius:50%;background-color:var(--mat-timepicker-clock-dial-background-color, var(--mat-sys-surface-container-highest))}.mat-clock-dial:before{position:absolute;top:50%;left:50%;width:.4375rem;height:.4375rem;border-radius:50%;transform:translate(-50%,-50%);content:\"\";background-color:var(--mat-timepicker-clock-dial-center-point-color, var(--mat-sys-primary))}[mat-mini-fab].mat-clock-dial-cell{position:absolute;display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:50%;box-shadow:none}[mat-mini-fab].mat-clock-dial-cell:disabled{pointer-events:none}[mat-mini-fab].mat-clock-dial-cell:focus,[mat-mini-fab].mat-clock-dial-cell:hover,[mat-mini-fab].mat-clock-dial-cell:active,[mat-mini-fab].mat-clock-dial-cell:focus:active{box-shadow:none}[mat-mini-fab].mat-clock-dial-cell.mat-clock-dial-cell-disabled.mat-clock-dial-cell-active{background-color:var(--mat-timepicker-clock-dial-cell-active-disabled-color, color-mix(in srgb, var(--mat-sys-primary) 40%, transparent));color:var(--mat-timepicker-clock-dial-cell-active-text-color, var(--mat-sys-on-primary))}.mat-clock-dial-cell:not(.mat-primary):not(.mat-accent):not(.mat-warn){background:var(--mat-timepicker-clock-dial-cell-unthemable-color, transparent)}.mat-clock-dial-cell.mat-clock-dial-cell-active{color:var(--mat-timepicker-clock-dial-cell-active-text-color, var(--mat-sys-on-primary));background-color:var(--mat-timepicker-clock-dial-cell-active-background-color, var(--mat-sys-primary))}.mat-clock-dial-cell.mat-clock-dial-cell-disabled{color:var(--mat-timepicker-clock-dial-cell-disabled-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 40%, transparent))}.mat-clock-dial-cell.mat-clock-dial-cell-disabled .mat-mdc-button-persistent-ripple:before{background-color:var(--mat-timepicker-clock-dial-cell-disabled-background-color, transparent)}.mat-timepicker-content-touch .mat-clock-dial{width:20rem;height:20rem}.mat-timepicker-content-touch [mat-mini-fab].mat-clock-dial-cell{width:3rem;height:3rem;font-size:1.125rem}.mat-clock-dial-hand{position:absolute;inset:0;width:1px;margin:0 auto;transform-origin:bottom}.mat-clock-dial-hand:before{position:absolute;top:-.25rem;left:-.25rem;width:calc(.5rem + 1px);height:calc(.5rem + 1px);border-radius:50%;content:\"\"}.mat-clock-dial-hand.mat-clock-dial-hand-disabled{background-color:var(--mat-timepicker-clock-dial-hand-disabled-color, transparent)}.mat-clock-dial-hand.mat-clock-dial-hand-disabled:before{background-color:var(--mat-timepicker-clock-dial-hand-value-point-disabled-color, color-mix(in srgb, var(--mat-sys-primary) 40%, transparent))}.mat-clock-dial-hand:not(.mat-clock-dial-hand-disabled){background-color:var(--mat-timepicker-clock-dial-hand-color, var(--mat-sys-primary))}.mat-clock-dial-hand:not(.mat-clock-dial-hand-disabled):before{background-color:var(--mat-timepicker-clock-dial-hand-value-point-color, var(--mat-sys-primary))}.mat-clock-dial-hand.mat-clock-dial-hand-pointless:before{content:none}\n"] }]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: Document, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }], propDecorators: { selectedMinute: [{
type: Input
}], interval: [{
type: Input
}], availableMinutes: [{
type: Input
}], color: [{
type: Input
}], touchUi: [{
type: Input
}], selectedChange: [{
type: Output
}] } });
const ALL_HOURS = Array(24)
.fill(null)
.map((_, i) => i);
class MatHoursClockDial {
/** Selected hour. */
get selectedHour() {
return this._selectedHour;
}
set selectedHour(value) {
this._selectedHour = value;
}
/** Whether the clock uses 12 hour format. */
get isMeridiem() {
return this._isMeridiem;
}
set isMeridiem(value) {
this._isMeridiem = value;
}
get availableHours() {
return this._availableHours;
}
set availableHours(value) {
this._availableHours = value;
this._initHours();
}
/** Whether the timepicker UI is in touch mode. */
get touchUi() {
return this._touchUi;
}
set touchUi(value) {
this._touchUi = value;
this._initHours();
}
get disabledHand() {
return !this.availableHours.includes(this.selectedHour);
}
get isHour() {
return !!this.hours.find((hour) => hour.value === this.selectedHour);
}
constructor(_element, _cdr, _document) {
this._element = _element;
this._cdr = _cdr;
this._document = _document;
this._availableHours = [];
/** Emits selected hour. */
this.selectedChange = new EventEmitter();
this.hours = [];
}
ngOnInit() {
this._initHours();
}
/** Hand styles based on selected hour. */
_handStyles() {
const deg = Math.round(this.selectedHour * (360 / (24 / 2)));
const radius = this._getRadius(this.selectedHour);
const height = radius;
const marginTop = getClockRadius(this.touchUi) - radius;
return {
transform: `rotate(${deg}deg)`,
height: `${height}px`,
'margin-top': `${marginTop}px`,
};
}
/** Handles mouse and touch events on dial and document. */
_onUserAction(event) {
if (event.cancelable) {
event.preventDefault();
}
this._setHour(event);
const eventsSubscription = merge(fromEvent(this._document, 'mousemove'), fromEvent(this._document, 'touchmove'))
.pipe(debounceTime(0))
.subscribe({
next: (event) => {
event.preventDefault();
this._setHour(event);
},
});
merge(fromEvent(this._document, 'mouseup'), fromEvent(this._document, 'touchend'))
.pipe(take(1))
.subscribe({
next: () => {
eventsSubscription.unsubscribe();
this.selectedChange.emit({
hour: this.selectedHour,
changeView: true,
});
},
});
}
_isActiveCell(hour) {
return this.selectedHour === hour;
}
/** Changes selected hour based on coordinates. */
_setHour(event) {
const element = this._element.nativeElement;
const window = this._getWindow();
const elementRect = element.getBoundingClientRect();
const width = element.offsetWidth;
const height = element.offsetHeight;
const pageX = event instanceof MouseEvent ? event.pageX : event.touches[0].pageX;
const pageY = event instanceof MouseEvent ? event.pageY : event.touches[0].pageY;
const x = width / 2 - (pageX - elementRect.left - window.scrollX);
const y = height / 2 - (pageY - elementRect.top - window.scrollY);
const unit = Math.PI / 6;
const atan2 = Math.atan2(-x, y);
const radian = atan2 < 0 ? Math.PI * 2 + atan2 : atan2;
const initialValue = Math.round(radian / unit);
const z = Math.sqrt(x * x + y * y);
const outer = z > getClockOuterRadius(this.touchUi) - getClockTickRadius(this.touchUi);
const value = this._getHourValue(initialValue, outer);
if (this.availableHours.includes(value)) {
this.selectedHour = value;
this.selectedChange.emit({
hour: this.selectedHour,
});
}
this._cdr.detectChanges();
}
/** Return value of hour. */
_getHourValue(value, outer) {
const edgeValue = value === 0 || value === 12;
if (this.isMeridiem) {
return edgeValue ? 12 : value;
}
if (outer) {
return edgeValue ? 0 : value;
}
return edgeValue ? 12 : value + 12;
}
/** Creates list of hours. */
_initHours() {
const initialHours = this.isMeridiem ? ALL_HOURS.slice(1, 13) : ALL_HOURS;
this.hours = initialHours.map((hour) => {
const radian = (hour / 6) * Math.PI;
const radius = this._getRadius(hour);
return {
value: hour,
displayValue: hour === 0 ? '00' : String(hour),
left: getClockCorrectedRadius(this.touchUi) + Math.sin(radian) * radius,
top: getClockCorrectedRadius(this.touchUi) - Math.cos(radian) * radius,
disabled: !this.availableHours.includes(hour),
};
});
}
/** Returns radius based on hour */
_getRadius(hour) {
if (this.isMeridiem) {
return getClockOuterRadius(this.touchUi);
}
const outer = hour >= 0 && hour < 12;
const radius = outer ? getClockOuterRadius(this.touchUi) : getClockInnerRadius(this.touchUi);
return radius;
}
/** Use defaultView of injected document if available or fallback to global window reference */
_getWindow() {
return this._document.defaultView || window;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: MatHoursClockDial, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.0.1", type: MatHoursClockDial, isStandalone: true, selector: "mat-hours-clock-dial", inputs: { selectedHour: "selectedHour", isMeridiem: "isMeridiem", availableHours: "availableHours", color: "color", touchUi: "touchUi" }, outputs: { selectedChange: "selectedChange" }, host: { listeners: { "mousedown": "_onUserAction($event)", "touchstart": "_onUserAction($event)" }, classAttribute: "mat-clock-dial mat-clock-dial-hours" }, exportAs: ["matHoursClockDial"], ngImport: i0, template: "<div\n class=\"mat-clock-dial-hand\"\n [class.mat-clock-dial-hand-pointless]=\"isHour\"\n [class.mat-clock-dial-hand-disabled]=\"disabledHand\"\n [ngStyle]=\"_handStyles()\"\n></div>\n@for (hour of hours; track hour.value) {\n <button\n class=\"mat-clock-dial-cell\"\n mat-mini-fab\n disableRipple\n [tabIndex]=\"_isActiveCell(hour.value) ? 0 : -1\"\n [style.left.px]=\"hour.left\"\n [style.top.px]=\"hour.top\"\n [class.mat-clock-dial-cell-active]=\"_isActiveCell(hour.value)\"\n [class.mat-clock-dial-cell-disabled]=\"hour.disabled\"\n [color]=\"_isActiveCell(hour.value) ? color : undefined\"\n [attr.aria-disabled]=\"hour.disabled || null\"\n >\n {{ hour.displayValue }}\n </button>\n}\n", styles: [".mat-clock-dial{position:relative;display:block;width:16rem;height:16rem;margin:0 auto;border-radius:50%;background-color:var(--mat-timepicker-clock-dial-background-color, var(--mat-sys-surface-container-highest))}.mat-clock-dial:before{position:absolute;top:50%;left:50%;width:.4375rem;height:.4375rem;border-radius:50%;transform:translate(-50%,-50%);content:\"\";background-color:var(--mat-timepicker-clock-dial-center-point-color, var(--mat-sys-primary))}[mat-mini-fab].mat-clock-dial-cell{position:absolute;display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:50%;box-shadow:none}[mat-mini-fab].mat-clock-dial-cell:disabled{pointer-events:none}[mat-mini-fab].mat-clock-dial-cell:focus,[mat-mini-fab].mat-clock-dial-cell:hover,[mat-mini-fab].mat-clock-dial-cell:active,[mat-mini-fab].mat-clock-dial-cell:focus:active{box-shadow:none}[mat-mini-fab].mat-clock-dial-cell.mat-clock-dial-cell-disabled.mat-clock-dial-cell-active{background-color:var(--mat-timepicker-clock-dial-cell-active-disabled-color, color-mix(in srgb, var(--mat-sys-primary) 40%, transparent));color:var(--mat-timepicker-clock-dial-cell-active-text-color, var(--mat-sys-on-primary))}.mat-clock-dial-cell:not(.mat-primary):not(.mat-accent):not(.mat-warn){background:var(--mat-timepicker-clock-dial-cell-unthemable-color, transparent)}.mat-clock-dial-cell.mat-clock-dial-cell-active{color:var(--mat-timepicker-clock-dial-cell-active-text-color, var(--mat-sys-on-primary));background-color:var(--mat-timepicker-clock-dial-cell-active-background-color, var(--mat-sys-primary))}.mat-clock-dial-cell.mat-clock-dial-cell-disabled{color:var(--mat-timepicker-clock-dial-cell-disabled-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 40%, transparent))}.mat-clock-dial-cell.mat-clock-dial-cell-disabled .mat-mdc-button-persistent-ripple:before{background-color:var(--mat-timepicker-clock-dial-cell-disabled-background-color, transparent)}.mat-timepicker-content-touch .mat-clock-dial{width:20rem;height:20rem}.mat-timepicker-content-touch [mat-mini-fab].mat-clock-dial-cell{width:3rem;height:3rem;font-size:1.125rem}.mat-clock-dial-hand{position:absolute;inset:0;width:1px;margin:0 auto;transform-origin:bottom}.mat-clock-dial-hand:before{position:absolute;top:-.25rem;left:-.25rem;width:calc(.5rem + 1px);height:calc(.5rem + 1px);border-radius:50%;content:\"\"}.mat-clock-dial-hand.mat-clock-dial-hand-disabled{background-color:var(--mat-timepicker-clock-dial-hand-disabled-color, transparent)}.mat-clock-dial-hand.mat-clock-dial-hand-disabled:before{background-color:var(--mat-timepicker-clock-dial-hand-value-point-disabled-color, color-mix(in srgb, var(--mat-sys-primary) 40%, transparent))}.mat-clock-dial-hand:not(.mat-clock-dial-hand-disabled){background-color:var(--mat-timepicker-clock-dial-hand-color, var(--mat-sys-primary))}.mat-clock-dial-hand:not(.mat-clock-dial-hand-disabled):before{background-color:var(--mat-timepicker-clock-dial-hand-value-point-color, var(--mat-sys-primary))}.mat-clock-dial-hand.mat-clock-dial-hand-pointless:before{content:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatMiniFabButton, selector: "button[mat-mini-fab]", exportAs: ["matButton"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.1", ngImport: i0, type: MatHoursClockDial, decorators: [{
type: Component,
args: [{ selector: 'mat-hours-clock-dial', standalone: true, imports: [CommonModule, MatButtonModule], exportAs: 'matHoursClockDial', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
class: 'mat-clock-dial mat-clock-dial-hours',
'(mousedown)': '_onUserAction($event)',
'(touchstart)': '_onUserAction($event)',
}, template: "<div\n class=\"mat-clock-dial-hand\"\n [class.mat-clock-dial-hand-pointless]=\"isHour\"\n [class.mat-clock-dial-hand-disabled]=\"disabledHand\"\n [ngStyle]=\"_handStyles()\"\n></div>\n@for (hour of hours; track hour.value) {\n <button\n class=\"mat-clock-dial-cell\"\n mat-mini-fab\n disableRipple\n [tabIndex]=\"_isActiveCell(hour.value) ? 0 : -1\"\n [style.left.px]=\"hour.left\"\n [style.top.px]=\"hour.top\"\n [class.mat-clock-dial-cell-active]=\"_isActiveCell(hour.value)\"\n [class.mat-clock-dial-cell-disabled]=\"hour.disabled\"\n [color]=\"_isActiveCell(hour.value) ? color : undefined\"\n [attr.aria-disabled]=\"hour.disabled || null\"\n >\n {{ hour.displayValue }}\n </button>\n}\n", styles: [".mat-clock-dial{position:relative;display:block;width:16rem;height:16rem;margin:0 auto;border-radius:50%;background-color:var(--mat-timepicker-clock-dial-background-color, var(--mat-sys-surface-container-highest))}.mat-clock-dial:before{position:absolute;top:50%;left:50%;width:.4375rem;height:.4375rem;border-radius:50%;transform:translate(-50%,-50%);content:\"\";background-color:var(--mat-timepicker-clock-dial-center-point-color, var(--mat-sys-primary))}[mat-mini-fab].mat-clock-dial-cell{position:absolute;display:flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:50%;box-shadow:none}[mat-mini-fab].mat-clock-dial-cell:disabled{pointer-events:none}[mat-mini-fab].mat-clock-dial-cell:focus,[mat-mini-fab].mat-clock-dial-cell:hover,[mat-mini-fab].mat-clock-dial-cell:active,[mat-mini-fab].mat-clock-dial-cell:focus:active{box-shadow:none}[mat-mini-fab].mat-clock-dial-cell.mat-clock-dial-cell-disabled.mat-clock-dial-cell-active{background-color:var(--mat-timepicker-clock-dial-cell-active-disabled-color, color-mix(in srgb, var(--mat-sys-primary) 40%, transparent));color:var(--mat-timepicker-clock-dial-cell-active-text-color, var(--mat-sys-on-primary))}.mat-clock-dial-cell:not(.mat-primary):not(.mat-accent):not(.mat-warn){background:var(--mat-timepicker-clock-dial-cell-unthemable-color, transparent)}.mat-clock-dial-cell.mat-clock-dial-cell-active{color:var(--mat-timepicker-clock-dial-cell-active-text-color, var(--mat-sys-on-primary));background-color:var(--mat-timepicker-clock-dial-cell-active-background-color, var(--mat-sys-primary))}.mat-clock-dial-cell.mat-clock-dial-cell-disabled{color:var(--mat-timepicker-clock-dial-cell-disabled-text-color, color-mix(in srgb, var(--mat-sys-on-surface) 40%, transparent))}.mat-clock-dial-cell.mat-clock-dial-cell-disabled .mat-mdc-button-persistent-ripple:before{background-color:var(--mat-timepicker-clock-dial-cell-disabled-background-color, transparent)}.mat-timepicker-content-touch .mat-clock-dial{width:20rem;height:20rem}.mat-timepicker-content-touch [mat-mini-fab].mat-clock-dial-cell{width:3rem;height:3rem;font-size:1.125rem}.mat-clock-dial-hand{position:absolute;inset:0;width:1px;margin:0 auto;transform-origin:bottom}.mat-clock-dial-hand:before{position:absolute;top:-.25rem;left:-.25rem;width:calc(.5rem + 1px);height:calc(.5rem + 1px);border-radius:50%;content:\"\"}.mat-clock-dial-hand.mat-clock-dial-hand-disabled{background-color:var(--mat-timepicker-clock-dial-hand-disabled-color, transparent)}.mat-clock-dial-hand.mat-clock-dial-hand-disabled:before{background-color:var(--mat-timepicker-clock-dial-hand-value-point-disabled-color, color-mix(in srgb, var(--mat-sys-primary) 40%, transparent))}.mat-clock-dial-hand:not(.mat-clock-dial-hand-disabled){background-color:var(--mat-timepicker-clock-dial-hand-color, var(--mat-sys-primary))}.mat-clock-dial-hand:not(.mat-clock-dial-hand-disabled):before{background-color:var(--mat-timepicker-clock-dial-hand-value-point-color, var(--mat-sys-primary))}.mat-clock-dial-hand.mat-clock-dial-hand-pointless:before{content:none}\n"] }]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: Document, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }], propDecorators: { selectedHour: [{
type: Input
}], isMeridiem: [{
type: Input
}], availableHours: [{
type: Input
}], color: [{
type: Input
}], touchUi: [{
type: Input
}], selectedChange: [{
type: Output
}] } });
/** InjectionToken for timepicker that can be used to override default locale code. */
const MAT_TIME_LOCALE = new InjectionToken('MAT_TIME_LOCALE', {
providedIn: 'root',
factory: MAT_DATE_TIME_LOCALE_FACTORY,
});
function MAT_DATE_TIME_LOCALE_FACTORY() {
return inject(LOCALE_ID);
}
/**
* No longer needed since MAT_TIME_LOCALE has been changed to a scoped injectable.
* If you are importing and providing this in your code you can simply remove it.
* @deprecated
* @breaking-change 18.0.0
*/
const MAT_TIME_LOCALE_PROVIDER = {
provide: MAT_TIME_LOCALE,
useExisting: LOCALE_ID,
};
class TimeAdapter {
/**
* Given a potential time object, returns that same time object if it is
* a valid time, or `null` if it's not a valid time.
* @param obj The object to check.
* @returns A time or `null`.
*/
getValidTimeOrNull(obj) {
return this.isTimeInstance(obj) && this.isValid(obj)
? obj
: null;
}
/**
* Attempts to deserialize a value to a valid time object. The `<mat-timepicker>` will call this
* method on all of its `@Input()` properties that accept time. It is therefore possible to
* support passing values from your backend directly to these properties by overriding this method
* to also deserialize the format used by your backend.
* @param value The value to be deserialized into a time object.
* @returns The deserialized time object, either a valid time, null if the value can be
* deserialized into a null time (e.g. the empty string), or an invalid date.
*/
deserialize(value) {
if (value == null || (this.isTimeInstance(value) && this.isValid(value))) {
return value;
}
return this.invalid();
}
/**
* Sets the locale used for all time.
* @param locale The new locale.
*/
setLocale(locale) {
this.locale = locale;
}
/**
* Checks if two time are equal.
* @param first The first time to check.
* @param second The second time to check.
* @returns Whether the two time are equal.
* Null time are considered equal to other null time.
*/
sameTime(first, second) {
if (first && second) {
let firstValid = this.isValid(first);
let secondValid = this.isValid(second);
if (firstValid && secondValid) {
return !this.compareTime(first, second);
}
return firstValid == secondValid;
}
return first == second;
}
/**
* Clamp the given time between min and max time.
* @param time The time to clamp.
* @param min The minimum value to allow. If null or omitted no min is enforced.
* @param max The maximum value to allow. If null or omitted no max is enforced.
* @returns `min` if `time` is less than `min`, `max` if time is greater than `max`,
* otherwise `time`.
*/
clampTime(time, min, max) {
if (min && this.compareTime(time, min) < 0) {
return min;
}
if (max && this.compareTime(time, max) > 0) {
return max;
}
return time;
}
}
/**
* Matches strings that have the form of a valid RFC 3339 string
* (https://tools.ietf.org/html/rfc3339). Note that the string may not actually be a valid date
* because the regex will match strings an with out of bounds month, date, etc.
*/
const ISO_8601_REGEX = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|(?:(?:\+|-)\d{2}:\d{2}))?)?$/;
/** Adapts the native JS Date for components that work with time. */
class NativeDateTimeAdapter extends TimeAdapter {
constructor(matTimeLocale) {
super();
this._matTimeLocale = inject(MAT_TIME_LOCALE, { optional: true });
if (matTimeLocale !== undefined) {
this._matTimeLocale = matTimeLocale;
}
super.setLocale(this._matTimeLocale);
}
now() {
return new Date();
}
parse(value, parseFormat) {
// We have no way using the native JS Date to set the parse format or locale, so we ignore these
// parameters.
if (typeof value == 'number') {
return new Date(value);
}
const { hour, minute, meridiem } = this.parseTime(value);
// hour should be in 24h format
// so, if meridiem is 'pm' and hour is less than 12, add 12 to hour
const correctedHour = meridiem === 'pm' && hour < 12 ? hour + 12 : hour;
const date = new Date();
date.setHours(correctedHour);
date.setMinutes(minute);
return value ? new Date(date) : null;
}
parseTime(value) {
const time = value.replace(/(\sam|