UNPKG

@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
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|