UNPKG

@dhutaryan/ngx-mat-timepicker

Version:

Angular timepicker to add time which is based on material design and Angular material.

1 lines 217 kB
{"version":3,"file":"dhutaryan-ngx-mat-timepicker.mjs","sources":["../../../projects/mat-timepicker/src/lib/timepicker-scroll-strategy.ts","../../../projects/mat-timepicker/src/lib/timepicker-intl.ts","../../../projects/mat-timepicker/src/lib/timepicker-toggle.ts","../../../projects/mat-timepicker/src/lib/timepicker-toggle.html","../../../projects/mat-timepicker/src/lib/timepicker-animations.ts","../../../projects/mat-timepicker/src/lib/clock-size.ts","../../../projects/mat-timepicker/src/lib/minutes-clock-dial.ts","../../../projects/mat-timepicker/src/lib/minutes-clock-dial.html","../../../projects/mat-timepicker/src/lib/hours-clock-dial.ts","../../../projects/mat-timepicker/src/lib/hours-clock-dial.html","../../../projects/mat-timepicker/src/lib/adapter/time-adapter.ts","../../../projects/mat-timepicker/src/lib/adapter/native-date-time-adapter.ts","../../../projects/mat-timepicker/src/lib/adapter/index.ts","../../../projects/mat-timepicker/src/lib/time-face-base.ts","../../../projects/mat-timepicker/src/lib/time-input-base.ts","../../../projects/mat-timepicker/src/lib/clock-dial-adnimation.ts","../../../projects/mat-timepicker/src/lib/timepicker-content-layout.ts","../../../projects/mat-timepicker/src/lib/timepicker-content-layout.html","../../../projects/mat-timepicker/src/lib/time-period.ts","../../../projects/mat-timepicker/src/lib/time-period.html","../../../projects/mat-timepicker/src/lib/clock-dials.ts","../../../projects/mat-timepicker/src/lib/clock-dials.html","../../../projects/mat-timepicker/src/lib/time-inputs.ts","../../../projects/mat-timepicker/src/lib/time-inputs.html","../../../projects/mat-timepicker/src/lib/time-selection-model.ts","../../../projects/mat-timepicker/src/lib/timepicker-content.ts","../../../projects/mat-timepicker/src/lib/timepicker-content.html","../../../projects/mat-timepicker/src/lib/timepicker-actions-default.ts","../../../projects/mat-timepicker/src/lib/timepicker-base.ts","../../../projects/mat-timepicker/src/lib/timepicker.ts","../../../projects/mat-timepicker/src/lib/timepicker-input-base.ts","../../../projects/mat-timepicker/src/lib/timepicker-input.ts","../../../projects/mat-timepicker/src/lib/timepicker-actions.ts","../../../projects/mat-timepicker/src/lib/timepicker.module.ts","../../../projects/mat-timepicker/src/public-api.ts","../../../projects/mat-timepicker/src/dhutaryan-ngx-mat-timepicker.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport { Overlay, ScrollStrategy } from '@angular/cdk/overlay';\n\n/** Injection token that determines the scroll handling while the timepicker is open. */\nexport const MAT_TIMEPICKER_SCROLL_STRATEGY = new InjectionToken<\n () => ScrollStrategy\n>('mat-timepicker-scroll-strategy');\n\n/** Timepicker scroll strategy factory. */\nexport function MAT_TIMEPICKER_SCROLL_STRATEGY_FACTORY(\n overlay: Overlay\n): () => ScrollStrategy {\n return () => overlay.scrollStrategies.reposition();\n}\n\n/** Timepicker scroll strategy provider. */\nexport const MAT_TIMEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {\n provide: MAT_TIMEPICKER_SCROLL_STRATEGY,\n deps: [Overlay],\n useFactory: MAT_TIMEPICKER_SCROLL_STRATEGY_FACTORY,\n};\n","import { Injectable } from '@angular/core';\nimport { Subject } from 'rxjs';\n\n@Injectable({ providedIn: 'root' })\nexport class MatTimepickerIntl {\n /**\n * Stream that emits whenever the labels here are changed. Use this to notify\n * components if the labels have changed after initialization.\n */\n readonly changes: Subject<void> = new Subject<void>();\n\n /** A label for inputs title. */\n inputsTitle = 'Enter time';\n\n /** A label for dials title. */\n dialsTitle = 'Select time';\n\n /** A label for hour input hint. */\n hourInputHint = 'Hour';\n\n /** A label for minute input hint. */\n minuteInputHint = 'Minute';\n\n /** Label for the button used to open the timepicker popup (used by screen readers). */\n openTimepickerLabel = 'Open timepicker';\n\n /** Label for the button used to close the timepicker popup (used by screen readers). */\n closeTimepickerLabel = 'Close timepicker';\n\n /** A label for OK button to apply time. */\n okButton = 'OK';\n\n /** A label for cancel button to close timepicker. */\n cancelButton = 'Cancel';\n\n /** A label for am text. */\n am = 'AM';\n\n /** A label for am text. */\n pm = 'PM';\n}\n","import {\n Component,\n ChangeDetectionStrategy,\n Directive,\n ViewEncapsulation,\n Input,\n ContentChild,\n Attribute,\n SimpleChanges,\n OnChanges,\n OnDestroy,\n ChangeDetectorRef,\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { MatButtonModule } from '@angular/material/button';\nimport { Observable, Subscription, merge, of } from 'rxjs';\n\nimport { MatTimepicker } from './timepicker';\nimport { MatTimepickerIntl } from './timepicker-intl';\n\n@Directive({ selector: '[matTimepickerToggleIcon]', standalone: true })\nexport class MatTimepickerToggleIcon {}\n\n@Component({\n selector: 'mat-timepicker-toggle',\n standalone: true,\n imports: [CommonModule, MatButtonModule],\n templateUrl: './timepicker-toggle.html',\n styleUrls: ['./timepicker-toggle.scss'],\n exportAs: 'matTimepickerToggle',\n host: {\n class: 'mat-timepicker-toggle',\n '[attr.tabindex]': 'null',\n '[class.mat-timepicker-toggle-active]': 'timepicker && timepicker.opened',\n '[class.mat-accent]': 'timepicker && timepicker.color === \"accent\"',\n '[class.mat-warn]': 'timepicker && timepicker.color === \"warn\"',\n '(click)': 'open($event)',\n },\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MatTimepickerToggle<T> implements OnChanges, OnDestroy {\n /** Timepicker instance. */\n @Input('for') timepicker: MatTimepicker<T>;\n\n /** Whether the toggle button is disabled. */\n @Input()\n get disabled(): boolean {\n if (this._disabled === undefined && this.timepicker) {\n return this.timepicker.disabled;\n }\n\n return !!this._disabled;\n }\n set disabled(value: BooleanInput) {\n this._disabled = coerceBooleanProperty(value);\n }\n private _disabled: boolean;\n\n /** Whether ripples on the toggle should be disabled. */\n @Input() disableRipple: boolean;\n\n /** Tabindex for the toggle. */\n @Input() tabIndex: number | null;\n\n /** Custom icon set by the consumer. */\n @ContentChild(MatTimepickerToggleIcon) customIcon: MatTimepickerToggleIcon;\n\n /** Screen-reader label for the button. */\n @Input('aria-label') ariaLabel: string;\n\n private _stateChanges = Subscription.EMPTY;\n\n constructor(\n @Attribute('tabindex') defaultTabIndex: string,\n public _intl: MatTimepickerIntl,\n private _cdr: ChangeDetectorRef,\n ) {\n const parsedTabIndex = Number(defaultTabIndex);\n this.tabIndex =\n parsedTabIndex || parsedTabIndex === 0 ? parsedTabIndex : null;\n }\n\n ngOnChanges(changes: SimpleChanges) {\n if (changes['timepicker']) {\n this._watchStateChanges();\n }\n }\n\n ngOnDestroy() {\n this._stateChanges.unsubscribe();\n }\n\n /** Opens timepicker. */\n open(event: Event): void {\n if (this.timepicker && !this.disabled) {\n this.timepicker.open();\n event.stopPropagation();\n }\n }\n\n private _watchStateChanges() {\n const timepickerStateChanged = this.timepicker\n ? this.timepicker.stateChanges\n : of();\n const inputStateChanged =\n this.timepicker && this.timepicker.timepickerInput\n ? this.timepicker.timepickerInput.stateChanges\n : of();\n const timepickerToggled = this.timepicker\n ? merge(this.timepicker.openedStream, this.timepicker.closedStream)\n : of();\n\n this._stateChanges.unsubscribe();\n this._stateChanges = merge(\n this._intl.changes,\n timepickerStateChanged as Observable<void>,\n inputStateChanged,\n timepickerToggled,\n ).subscribe(() => this._cdr.markForCheck());\n }\n}\n","<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","import {\n animate,\n state,\n style,\n transition,\n trigger,\n keyframes,\n AnimationTriggerMetadata,\n} from '@angular/animations';\n\n/** Animations used by the timepicker. */\nexport const matTimepickerAnimations: {\n readonly transformPanel: AnimationTriggerMetadata;\n readonly fadeInTimepicker: AnimationTriggerMetadata;\n} = {\n /** Transforms the height of the timepicker's. */\n transformPanel: trigger('transformPanel', [\n transition(\n 'void => enter-dropdown',\n animate(\n '120ms cubic-bezier(0, 0, 0.2, 1)',\n keyframes([\n style({ opacity: 0, transform: 'scale(1, 0.8)' }),\n style({ opacity: 1, transform: 'scale(1, 1)' }),\n ])\n )\n ),\n transition(\n 'void => enter-dialog',\n animate(\n '150ms cubic-bezier(0, 0, 0.2, 1)',\n keyframes([\n style({ opacity: 0, transform: 'scale(0.7)' }),\n style({ transform: 'none', opacity: 1 }),\n ])\n )\n ),\n transition('* => void', animate('100ms linear', style({ opacity: 0 }))),\n ]),\n\n /** Fades in the content of the timepicker. */\n fadeInTimepicker: trigger('fadeInTimepicker', [\n state('void', style({ opacity: 0 })),\n state('enter', style({ opacity: 1 })),\n ]),\n};\n","const TOUCH_UI_MULTIPLIER = 1.25;\nconst TOUCH_UI_TICK_MULTIPLIER = 1.5;\nconst CLOCK_RADIUS = 128;\nconst CLOCK_TICK_RADIUS = 16;\nconst CLOCK_OUTER_RADIUS = 100;\n\nexport function getClockRadius(touchUi?: boolean): number {\n return touchUi ? CLOCK_RADIUS * TOUCH_UI_MULTIPLIER : CLOCK_RADIUS;\n}\n\nexport function getClockTickRadius(touchUi?: boolean): number {\n return touchUi\n ? CLOCK_TICK_RADIUS * TOUCH_UI_TICK_MULTIPLIER\n : CLOCK_TICK_RADIUS;\n}\n\nexport function getClockCorrectedRadius(touchUi?: boolean): number {\n return getClockRadius(touchUi) - getClockTickRadius(touchUi);\n}\n\nexport function getClockOuterRadius(touchUi?: boolean): number {\n return touchUi\n ? CLOCK_OUTER_RADIUS * TOUCH_UI_MULTIPLIER\n : CLOCK_OUTER_RADIUS;\n}\n\nexport function getClockInnerRadius(touchUi?: boolean): number {\n return getClockOuterRadius(touchUi) - getClockTickRadius(touchUi) * 2;\n}\n","import { CommonModule, DOCUMENT } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n Inject,\n Input,\n OnInit,\n Output,\n ViewEncapsulation,\n} from '@angular/core';\nimport { coerceNumberProperty } from '@angular/cdk/coercion';\nimport { ThemePalette } from '@angular/material/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport { debounceTime, fromEvent, merge, take } from 'rxjs';\n\nimport { ClockDialViewCell } from './hours-clock-dial';\nimport { getClockCorrectedRadius, getClockOuterRadius, getClockRadius } from './clock-size';\n\nexport const ALL_MINUTES = Array(60)\n .fill(null)\n .map((_, i) => i);\n\n@Component({\n selector: 'mat-minutes-clock-dial',\n standalone: true,\n imports: [CommonModule, MatButtonModule],\n templateUrl: 'minutes-clock-dial.html',\n styleUrls: ['clock-dial.scss'],\n exportAs: 'matMinutesClockDial',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'mat-clock-dial mat-clock-dial-minutes',\n '(mousedown)': '_onUserAction($event)',\n '(touchstart)': '_onUserAction($event)',\n },\n})\nexport class MatMinutesClockDial implements OnInit {\n /** Selected minute. */\n @Input()\n get selectedMinute(): number {\n return this._selectedMinute;\n }\n set selectedMinute(value: number) {\n this._selectedMinute = value;\n }\n private _selectedMinute = 0;\n\n /** Step over minutes. */\n @Input()\n get interval(): number {\n return this._interval;\n }\n set interval(value: number) {\n this._interval = coerceNumberProperty(value) || 1;\n }\n private _interval: number = 1;\n\n @Input()\n get availableMinutes(): number[] {\n return this._availableMinutes;\n }\n set availableMinutes(value: number[]) {\n this._availableMinutes = value;\n this._initMinutes();\n }\n private _availableMinutes: number[] = [];\n\n /** Color palette. */\n @Input() color: ThemePalette;\n\n /** Whether the timepicker UI is in touch mode. */\n @Input()\n get touchUi(): boolean {\n return this._touchUi;\n }\n set touchUi(value: boolean) {\n this._touchUi = value;\n }\n private _touchUi: boolean;\n\n /** Emits selected minute. */\n @Output() selectedChange = new EventEmitter<number>();\n\n minutes: ClockDialViewCell[] = [];\n\n get disabled(): boolean {\n return !this.availableMinutes.includes(this.selectedMinute);\n }\n\n get isMinutePoint(): boolean {\n return !!this.minutes.find((hour) => hour.value === this.selectedMinute);\n }\n\n constructor(\n private _element: ElementRef<HTMLElement>,\n private _cdr: ChangeDetectorRef,\n @Inject(DOCUMENT) private _document: Document,\n ) {}\n\n ngOnInit(): void {\n this._initMinutes();\n }\n\n /** Hand styles based on selected minute. */\n _handStyles(): any {\n const deg = Math.round(this._selectedMinute * (360 / 60));\n const height = getClockOuterRadius(this.touchUi);\n const marginTop = getClockRadius(this.touchUi) - getClockOuterRadius(this.touchUi);\n\n return {\n transform: `rotate(${deg}deg)`,\n height: `${height}px`,\n 'margin-top': `${marginTop}px`,\n };\n }\n\n /** Handles mouse and touch events on dial and document. */\n _onUserAction(event: MouseEvent | TouchEvent): void {\n if (event.cancelable) {\n event.preventDefault();\n }\n\n this._setMinute(event);\n\n const eventsSubscription = merge(\n fromEvent<MouseEvent>(this._document, 'mousemove'),\n fromEvent<TouchEvent>(this._document, 'touchmove'),\n )\n .pipe(debounceTime(0))\n .subscribe({\n next: (event) => {\n event.preventDefault();\n this._setMinute(event);\n },\n });\n\n merge(\n fromEvent<MouseEvent>(this._document, 'mouseup'),\n fromEvent<TouchEvent>(this._document, 'touchend'),\n )\n .pipe(take(1))\n .subscribe({\n next: () => {\n eventsSubscription.unsubscribe();\n },\n });\n }\n\n _isActiveCell(minute: number): boolean {\n return this.selectedMinute === minute;\n }\n\n private _setMinute(event: MouseEvent | TouchEvent): void {\n const element = this._element.nativeElement;\n const window = this._getWindow();\n const elementRect = element.getBoundingClientRect();\n const width = element.offsetWidth;\n const height = element.offsetHeight;\n const pageX = event instanceof MouseEvent ? event.pageX : event.touches[0].pageX;\n const pageY = event instanceof MouseEvent ? event.pageY : event.touches[0].pageY;\n const x = width / 2 - (pageX - elementRect.left - window.scrollX);\n const y = height / 2 - (pageY - elementRect.top - window.scrollY);\n const unit = Math.PI / (30 / this.interval);\n const atan2 = Math.atan2(-x, y);\n const radian = atan2 < 0 ? Math.PI * 2 + atan2 : atan2;\n const initialValue = Math.round(radian / unit) * this.interval;\n const value = initialValue === 60 ? 0 : initialValue;\n\n if (this.availableMinutes.includes(value) && this.availableMinutes.includes(value)) {\n this.selectedMinute = value;\n this.selectedChange.emit(this.selectedMinute);\n }\n\n this._cdr.detectChanges();\n }\n\n /** Creates list of minutes. */\n private _initMinutes(): void {\n this.minutes = ALL_MINUTES.filter((minute) => minute % 5 === 0).map((minute) => {\n const radian = (minute / 30) * Math.PI;\n const displayValue = minute === 0 ? '00' : String(minute);\n\n return {\n value: minute,\n displayValue,\n left:\n getClockCorrectedRadius(this.touchUi) +\n Math.sin(radian) * getClockOuterRadius(this.touchUi),\n top:\n getClockCorrectedRadius(this.touchUi) -\n Math.cos(radian) * getClockOuterRadius(this.touchUi),\n disabled: !this.availableMinutes.includes(minute),\n };\n });\n }\n\n /** Use defaultView of injected document if available or fallback to global window reference */\n private _getWindow(): Window {\n return this._document.defaultView || window;\n }\n}\n","<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","import { CommonModule, DOCUMENT } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n EventEmitter,\n Inject,\n Input,\n OnInit,\n Output,\n ViewEncapsulation,\n} from '@angular/core';\nimport { ThemePalette } from '@angular/material/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport { debounceTime, fromEvent, merge, take } from 'rxjs';\n\nimport {\n getClockCorrectedRadius,\n getClockInnerRadius,\n getClockOuterRadius,\n getClockRadius,\n getClockTickRadius,\n} from './clock-size';\n\nexport interface ClockDialViewCell {\n value: number;\n displayValue: string;\n left: number;\n top: number;\n disabled: boolean;\n}\n\nexport const ALL_HOURS = Array(24)\n .fill(null)\n .map((_, i) => i);\n\n@Component({\n selector: 'mat-hours-clock-dial',\n standalone: true,\n imports: [CommonModule, MatButtonModule],\n templateUrl: 'hours-clock-dial.html',\n styleUrls: ['clock-dial.scss'],\n exportAs: 'matHoursClockDial',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n class: 'mat-clock-dial mat-clock-dial-hours',\n '(mousedown)': '_onUserAction($event)',\n '(touchstart)': '_onUserAction($event)',\n },\n})\nexport class MatHoursClockDial implements OnInit {\n /** Selected hour. */\n @Input()\n get selectedHour(): number {\n return this._selectedHour;\n }\n set selectedHour(value: number) {\n this._selectedHour = value;\n }\n private _selectedHour: number;\n\n /** Whether the clock uses 12 hour format. */\n @Input()\n get isMeridiem(): boolean {\n return this._isMeridiem;\n }\n set isMeridiem(value: boolean) {\n this._isMeridiem = value;\n }\n private _isMeridiem: boolean;\n\n @Input()\n get availableHours(): number[] {\n return this._availableHours;\n }\n set availableHours(value: number[]) {\n this._availableHours = value;\n this._initHours();\n }\n private _availableHours: number[] = [];\n\n /** Color palette. */\n @Input() color: ThemePalette;\n\n /** Whether the timepicker UI is in touch mode. */\n @Input()\n get touchUi(): boolean {\n return this._touchUi;\n }\n set touchUi(value: boolean) {\n this._touchUi = value;\n this._initHours();\n }\n private _touchUi: boolean;\n\n /** Emits selected hour. */\n @Output() selectedChange = new EventEmitter<{\n hour: number;\n changeView?: boolean;\n }>();\n\n hours: ClockDialViewCell[] = [];\n\n get disabledHand(): boolean {\n return !this.availableHours.includes(this.selectedHour);\n }\n\n get isHour(): boolean {\n return !!this.hours.find((hour) => hour.value === this.selectedHour);\n }\n\n constructor(\n private _element: ElementRef<HTMLElement>,\n private _cdr: ChangeDetectorRef,\n @Inject(DOCUMENT) private _document: Document,\n ) {}\n\n ngOnInit(): void {\n this._initHours();\n }\n\n /** Hand styles based on selected hour. */\n _handStyles(): any {\n const deg = Math.round(this.selectedHour * (360 / (24 / 2)));\n const radius = this._getRadius(this.selectedHour);\n const height = radius;\n const marginTop = getClockRadius(this.touchUi) - radius;\n\n return {\n transform: `rotate(${deg}deg)`,\n height: `${height}px`,\n 'margin-top': `${marginTop}px`,\n };\n }\n\n /** Handles mouse and touch events on dial and document. */\n _onUserAction(event: MouseEvent | TouchEvent): void {\n if (event.cancelable) {\n event.preventDefault();\n }\n\n this._setHour(event);\n\n const eventsSubscription = merge(\n fromEvent<MouseEvent>(this._document, 'mousemove'),\n fromEvent<TouchEvent>(this._document, 'touchmove'),\n )\n .pipe(debounceTime(0))\n .subscribe({\n next: (event) => {\n event.preventDefault();\n this._setHour(event);\n },\n });\n\n merge(\n fromEvent<MouseEvent>(this._document, 'mouseup'),\n fromEvent<TouchEvent>(this._document, 'touchend'),\n )\n .pipe(take(1))\n .subscribe({\n next: () => {\n eventsSubscription.unsubscribe();\n this.selectedChange.emit({\n hour: this.selectedHour,\n changeView: true,\n });\n },\n });\n }\n\n _isActiveCell(hour: number): boolean {\n return this.selectedHour === hour;\n }\n\n /** Changes selected hour based on coordinates. */\n private _setHour(event: MouseEvent | TouchEvent): void {\n const element = this._element.nativeElement;\n const window = this._getWindow();\n const elementRect = element.getBoundingClientRect();\n const width = element.offsetWidth;\n const height = element.offsetHeight;\n const pageX = event instanceof MouseEvent ? event.pageX : event.touches[0].pageX;\n const pageY = event instanceof MouseEvent ? event.pageY : event.touches[0].pageY;\n const x = width / 2 - (pageX - elementRect.left - window.scrollX);\n const y = height / 2 - (pageY - elementRect.top - window.scrollY);\n const unit = Math.PI / 6;\n const atan2 = Math.atan2(-x, y);\n const radian = atan2 < 0 ? Math.PI * 2 + atan2 : atan2;\n const initialValue = Math.round(radian / unit);\n const z = Math.sqrt(x * x + y * y);\n const outer = z > getClockOuterRadius(this.touchUi) - getClockTickRadius(this.touchUi);\n const value = this._getHourValue(initialValue, outer);\n\n if (this.availableHours.includes(value)) {\n this.selectedHour = value;\n this.selectedChange.emit({\n hour: this.selectedHour,\n });\n }\n\n this._cdr.detectChanges();\n }\n\n /** Return value of hour. */\n private _getHourValue(value: number, outer: boolean): number {\n const edgeValue = value === 0 || value === 12;\n\n if (this.isMeridiem) {\n return edgeValue ? 12 : value;\n }\n\n if (outer) {\n return edgeValue ? 0 : value;\n }\n\n return edgeValue ? 12 : value + 12;\n }\n\n /** Creates list of hours. */\n private _initHours(): void {\n const initialHours = this.isMeridiem ? ALL_HOURS.slice(1, 13) : ALL_HOURS;\n\n this.hours = initialHours.map((hour) => {\n const radian = (hour / 6) * Math.PI;\n const radius = this._getRadius(hour);\n\n return {\n value: hour,\n displayValue: hour === 0 ? '00' : String(hour),\n left: getClockCorrectedRadius(this.touchUi) + Math.sin(radian) * radius,\n top: getClockCorrectedRadius(this.touchUi) - Math.cos(radian) * radius,\n disabled: !this.availableHours.includes(hour),\n };\n });\n }\n\n /** Returns radius based on hour */\n private _getRadius(hour: number): number {\n if (this.isMeridiem) {\n return getClockOuterRadius(this.touchUi);\n }\n\n const outer = hour >= 0 && hour < 12;\n const radius = outer ? getClockOuterRadius(this.touchUi) : getClockInnerRadius(this.touchUi);\n\n return radius;\n }\n\n /** Use defaultView of injected document if available or fallback to global window reference */\n private _getWindow(): Window {\n return this._document.defaultView || window;\n }\n}\n","<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","import { InjectionToken, LOCALE_ID, inject } from '@angular/core';\nimport { MatTimePeriodType } from '../time-period';\n\n/** InjectionToken for timepicker that can be used to override default locale code. */\nexport const MAT_TIME_LOCALE = new InjectionToken<string>('MAT_TIME_LOCALE', {\n providedIn: 'root',\n factory: MAT_DATE_TIME_LOCALE_FACTORY,\n});\n\nexport function MAT_DATE_TIME_LOCALE_FACTORY(): string {\n return inject(LOCALE_ID);\n}\n\n/**\n * No longer needed since MAT_TIME_LOCALE has been changed to a scoped injectable.\n * If you are importing and providing this in your code you can simply remove it.\n * @deprecated\n * @breaking-change 18.0.0\n */\nexport const MAT_TIME_LOCALE_PROVIDER = {\n provide: MAT_TIME_LOCALE,\n useExisting: LOCALE_ID,\n};\n\nexport abstract class TimeAdapter<T, L = any> {\n /** The locale to use for time. */\n protected locale: L;\n\n /**\n * Gets now's time.\n * @returns Now's time.\n */\n abstract now(): T;\n\n /**\n * Parses a time from a user-provided value.\n * @param value The value to parse.\n * @param parseFormat The expected format of the value being parsed\n * (type is implementation-dependent).\n * @returns The parsed time.\n */\n abstract parse(value: any, parseFormat: any): T | null;\n\n /**\n * Gets the hour component of the given time.\n * @param time The time to extract the hour from.\n * @returns The hour component.\n */\n abstract getHour(time: T): number;\n\n /**\n * Gets the minute component of the given time.\n * @param time The time to extract the minute from.\n * @returns The minute component.\n */\n abstract getMinute(time: T): number;\n\n /**\n * Update the hour component of the given time.\n * @param time The time to update the hour.\n * @param hour The new hour to update given time.\n * @returns The new time with updated hour component.\n */\n abstract updateHour(time: T, hour: number): T;\n\n /**\n * Update the minute component of the given time.\n * @param time The time to update the minute.\n * @param minute The new minute to update given time.\n * @returns The new time with updated minute component.\n */\n abstract updateMinute(time: T, minute: number): T;\n\n /**\n * Gets the period component of the given time.\n * @param time The time to extract the period from.\n * @returns The period component.\n */\n abstract getPeriod(time: T): MatTimePeriodType;\n\n /**\n * Formats a time as a string according to the given format.\n * @param time The value to format.\n * @param displayFormat The format to use to display the time as a string.\n * @returns The formatted time string.\n */\n abstract format(time: T, displayFormat: any): string;\n\n /**\n * Checks whether the given object is considered a time instance by this timeAdapter.\n * @param obj The object to check\n * @returns Whether the object is a time instance.\n */\n abstract isTimeInstance(obj: any): boolean;\n\n /**\n * Checks whether the given time is valid.\n * @param time The time to check.\n * @returns Whether the time is valid.\n */\n abstract isValid(time: T): boolean;\n\n /**\n * Gets time instance that is not valid.\n * @returns An invalid time.\n */\n abstract invalid(): T;\n\n /**\n * Given a potential time object, returns that same time object if it is\n * a valid time, or `null` if it's not a valid time.\n * @param obj The object to check.\n * @returns A time or `null`.\n */\n getValidTimeOrNull(obj: unknown): T | null {\n return this.isTimeInstance(obj) && this.isValid(obj as T)\n ? (obj as T)\n : null;\n }\n\n /**\n * Attempts to deserialize a value to a valid time object. The `<mat-timepicker>` will call this\n * method on all of its `@Input()` properties that accept time. It is therefore possible to\n * support passing values from your backend directly to these properties by overriding this method\n * to also deserialize the format used by your backend.\n * @param value The value to be deserialized into a time object.\n * @returns The deserialized time object, either a valid time, null if the value can be\n * deserialized into a null time (e.g. the empty string), or an invalid date.\n */\n deserialize(value: any): T | null {\n if (value == null || (this.isTimeInstance(value) && this.isValid(value))) {\n return value;\n }\n return this.invalid();\n }\n\n /**\n * Sets the locale used for all time.\n * @param locale The new locale.\n */\n setLocale(locale: L) {\n this.locale = locale;\n }\n\n /**\n * Compares two time.\n * @param first The first time to compare.\n * @param second The second time to compare.\n * @returns 0 if the time are equal, a number less than 0 if the first time is earlier,\n * a number greater than 0 if the first time is later.\n */\n abstract compareTime(first: T, second: T): number;\n\n /**\n * Checks if two time are equal.\n * @param first The first time to check.\n * @param second The second time to check.\n * @returns Whether the two time are equal.\n * Null time are considered equal to other null time.\n */\n sameTime(first: T | null, second: T | null): boolean {\n if (first && second) {\n let firstValid = this.isValid(first);\n let secondValid = this.isValid(second);\n if (firstValid && secondValid) {\n return !this.compareTime(first, second);\n }\n return firstValid == secondValid;\n }\n return first == second;\n }\n\n /**\n * Clamp the given time between min and max time.\n * @param time The time to clamp.\n * @param min The minimum value to allow. If null or omitted no min is enforced.\n * @param max The maximum value to allow. If null or omitted no max is enforced.\n * @returns `min` if `time` is less than `min`, `max` if time is greater than `max`,\n * otherwise `time`.\n */\n clampTime(time: T, min?: T | null, max?: T | null): T {\n if (min && this.compareTime(time, min) < 0) {\n return min;\n }\n if (max && this.compareTime(time, max) > 0) {\n return max;\n }\n return time;\n }\n}\n","import { Inject, Injectable, Optional, inject } from '@angular/core';\n\nimport { MAT_TIME_LOCALE, TimeAdapter } from './time-adapter';\nimport { MatTimePeriodType } from '../time-period';\n\n/**\n * Matches strings that have the form of a valid RFC 3339 string\n * (https://tools.ietf.org/html/rfc3339). Note that the string may not actually be a valid date\n * because the regex will match strings an with out of bounds month, date, etc.\n */\nconst ISO_8601_REGEX =\n /^\\d{4}-\\d{2}-\\d{2}(?:T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|(?:(?:\\+|-)\\d{2}:\\d{2}))?)?$/;\n\n/** Adapts the native JS Date for components that work with time. */\n@Injectable()\nexport class NativeDateTimeAdapter extends TimeAdapter<Date> {\n private readonly _matTimeLocale = inject(MAT_TIME_LOCALE, { optional: true });\n\n constructor(@Optional() @Inject(MAT_TIME_LOCALE) matTimeLocale: string) {\n super();\n if (matTimeLocale !== undefined) {\n this._matTimeLocale = matTimeLocale;\n }\n super.setLocale(this._matTimeLocale);\n }\n\n now(): Date {\n return new Date();\n }\n\n parse(value: any, parseFormat?: any): Date | null {\n // We have no way using the native JS Date to set the parse format or locale, so we ignore these\n // parameters.\n if (typeof value == 'number') {\n return new Date(value);\n }\n\n const { hour, minute, meridiem } = this.parseTime(value);\n // hour should be in 24h format\n // so, if meridiem is 'pm' and hour is less than 12, add 12 to hour\n const correctedHour = meridiem === 'pm' && hour < 12 ? hour + 12 : hour;\n const date = new Date();\n date.setHours(correctedHour);\n date.setMinutes(minute);\n\n return value ? new Date(date) : null;\n }\n\n parseTime(value: string): {\n hour: number;\n minute: number;\n meridiem?: 'am' | 'pm';\n } {\n const time = value.replace(/(\\sam|\\spm|\\sAM|\\sPM|am|pm|AM|PM)/g, '');\n const meridiem = value.replace(time, '').trim().toLowerCase() as\n | 'am'\n | 'pm';\n const [hour, minute] = time.split(':');\n\n return { hour: Number(hour), minute: Number(minute), meridiem };\n }\n\n getHour(date: Date): number {\n return date.getHours();\n }\n\n getMinute(date: Date): number {\n return date.getMinutes();\n }\n\n updateHour(date: Date, hour: number): Date {\n const copy = new Date(date.getTime());\n copy.setHours(hour);\n\n return copy;\n }\n\n updateMinute(date: Date, minute: number): Date {\n const copy = new Date(date.getTime());\n copy.setMinutes(minute);\n\n return copy;\n }\n\n getPeriod(date: Date): MatTimePeriodType {\n return date.getHours() < 12 ? 'am' : 'pm';\n }\n\n format(date: Date, displayFormat: Object): string {\n if (!this.isValid(date)) {\n throw Error('NativeDateTimeAdapter: Cannot format invalid date.');\n }\n\n const dtf = new Intl.DateTimeFormat(this.locale, {\n ...displayFormat,\n timeZone: 'utc',\n });\n\n return this._format(dtf, date);\n }\n\n /**\n * Returns the given value if given a valid Date or null. Deserializes valid ISO 8601 strings\n * (https://www.ietf.org/rfc/rfc3339.txt) into valid Dates and empty string into null. Returns an\n * invalid date for all other values.\n */\n override deserialize(value: any): Date | null {\n if (typeof value === 'string') {\n if (!value) {\n return null;\n }\n // The `Date` constructor accepts formats other than ISO 8601, so we need to make sure the\n // string is the right format first.\n if (ISO_8601_REGEX.test(value)) {\n const date = new Date(value);\n if (this.isValid(date)) {\n return date;\n }\n }\n }\n return super.deserialize(value);\n }\n\n isTimeInstance(obj: any): boolean {\n return obj instanceof Date;\n }\n\n isValid(date: Date): boolean {\n return !isNaN(date.getTime());\n }\n\n invalid(): Date {\n return new Date(NaN);\n }\n\n compareTime(first: Date, second: Date): number {\n return (\n first.getHours() - second.getHours() ||\n first.getMinutes() - second.getMinutes()\n );\n }\n\n /**\n * When converting Date object to string, javascript built-in functions may return wrong\n * results because it applies its internal DST rules. The DST rules around the world change\n * very frequently, and the current valid rule is not always valid in previous years though.\n * We work around this problem building a new Date object which has its internal UTC\n * representation with the local date and time.\n * @param dtf Intl.DateTimeFormat object, containing the desired string format. It must have\n * timeZone set to 'utc' to work fine.\n * @param date Date from which we want to get the string representation according to dtf\n * @returns A Date object with its UTC representation based on the passed in date info\n */\n private _format(dtf: Intl.DateTimeFormat, date: Date) {\n // Passing the year to the constructor causes year numbers <100 to be converted to 19xx.\n // To work around this we use `setUTCFullYear` and `setUTCHours` instead.\n const d = new Date();\n d.setUTCFullYear(date.getFullYear(), date.getMonth(), date.getDate());\n d.setUTCHours(\n date.getHours(),\n date.getMinutes(),\n date.getSeconds(),\n date.getMilliseconds(),\n );\n return dtf.format(d);\n }\n}\n","import { PlatformModule } from '@angular/cdk/platform';\nimport { ModuleWithProviders, NgModule, Provider } from '@angular/core';\n\nimport { MAT_TIME_LOCALE_PROVIDER, TimeAdapter } from './time-adapter';\nimport { NativeDateTimeAdapter } from './native-date-time-adapter';\n\nexport * from './time-adapter';\nexport * from './native-date-time-adapter';\n\n@NgModule({\n imports: [PlatformModule],\n providers: [{ provide: TimeAdapter, useClass: NativeDateTimeAdapter }],\n})\nexport class NativeDateTimeModule {}\n\n@NgModule({\n providers: [provideNativeDateTimeAdapter()],\n})\nexport class MatNativeDateTimeModule {}\n\nexport function provideNativeDateTimeAdapter(): Provider {\n return { provide: TimeAdapter, useClass: NativeDateTimeAdapter };\n}\n","import {\n AfterContentInit,\n AfterViewChecked,\n Directive,\n EventEmitter,\n Input,\n Optional,\n Output,\n} from '@angular/core';\nimport { ThemePalette } from '@angular/material/core';\nimport { coerceNumberProperty } from '@angular/cdk/coercion';\nimport { DOWN_ARROW, UP_ARROW } from '@angular/cdk/keycodes';\n\nimport { TimeAdapter } from './adapter';\nimport { MatTimePeriodType } from './time-period';\nimport { ALL_MINUTES } from './minutes-clock-dial';\nimport { ALL_HOURS } from './hours-clock-dial';\n\n@Directive()\nexport abstract class MatTimeFaceBase<T>\n implements AfterContentInit, AfterViewChecked\n{\n /** The currently selected time. */\n @Input()\n get selected(): T | null {\n return this._selected;\n }\n set selected(value: T | null) {\n this._selected = this._timeAdapter.getValidTimeOrNull(\n this._timeAdapter.deserialize(value),\n );\n\n if (!this._selected) {\n return;\n }\n\n const hour = this._timeAdapter.getHour(this._selected);\n this.selectedHour = hour > 12 && this.isMeridiem ? hour - 12 : hour;\n if (hour === 0 && this.isMeridiem) {\n this.selectedHour = 12;\n }\n this.selectedMinute = this._timeAdapter.getMinute(this._selected);\n this.availableHours = ALL_HOURS;\n\n if (this.isMeridiem) {\n this.period = this._timeAdapter.getPeriod(this._selected);\n }\n\n this.availableMinutes = ALL_MINUTES;\n this._setMinHour();\n this._setMaxHour();\n this._setMinMinute();\n this._setMaxMinute();\n this._moveFocusOnNextTick = this.isMeridiem;\n }\n private _selected: T | null;\n\n /** The minimum selectable time. */\n @Input()\n get minTime(): T | null {\n return this._minTime;\n }\n set minTime(value: T | null) {\n this._minTime = this._timeAdapter.getValidTimeOrNull(\n this._timeAdapter.deserialize(value),\n );\n\n if (value) {\n this._setMinHour();\n this._setMinMinute();\n this._setDisabledPeriod();\n }\n }\n private _minTime: T | null;\n\n /** The maximum selectable time. */\n @Input()\n get maxTime(): T | null {\n return this._maxTime;\n }\n set maxTime(value: T | null) {\n this._maxTime = this._timeAdapter.getValidTimeOrNull(\n this._timeAdapter.deserialize(value),\n );\n\n if (value) {\n this._setMaxHour();\n this._setMaxMinute();\n this._setDisabledPeriod();\n }\n }\n private _maxTime: T | null;\n\n /** Step over minutes. */\n @Input()\n get minuteInterval(): number {\n return this._minuteInterval;\n }\n set minuteInterval(value: number) {\n this._minuteInterval = coerceNumberProperty(value) || 1;\n }\n private _minuteInterval: number = 1;\n\n /** Whether the clock uses 12 hour format. */\n @Input() isMeridiem: boolean;\n\n /** Color palette. */\n @Input() color: ThemePalette = 'primary';\n\n /** Emits when any hour, minute or period is selected. */\n @Output() _userSelection = new EventEmitter<T>();\n\n @Output() selectedChange = new EventEmitter<T>();\n\n selectedHour: number = 0;\n selectedMinute: number = 0;\n period: MatTimePeriodType;\n disabledPeriod: MatTimePeriodType | null = null;\n availableMinutes = ALL_MINUTES;\n availableHours = ALL_HOURS;\n\n /**\n * Used for scheduling that focus should be moved to the active cell on the next tick.\n * We need to schedule it, rather than do it immediately, because we have to wait\n * for Angular to re-evaluate the view children.\n */\n private _moveFocusOnNextTick = false;\n\n constructor(@Optional() protected _timeAdapter: TimeAdapter<T>) {}\n\n ngAfterContentInit() {\n if (!this.selected) {\n this.selected = this._timeAdapter.clampTime(\n this._timeAdapter.now(),\n this.minTime,\n this.maxTime,\n );\n this._userSelection.emit(this.selected);\n }\n }\n\n ngAfterViewChecked() {\n if (this._moveFocusOnNextTick) {\n this._moveFocusOnNextTick = false;\n this.focusActiveCell();\n }\n }\n\n /**\n * Focuses the active cell after or input the microtask queue is empty.\n *\n * Adding a 0ms setTimeout seems to fix Voiceover losing focus when pressing PageUp/PageDown.\n *\n * Determined a 0ms by gradually increasing duration from 0 and testing two use cases with screen\n * reader enabled:\n *\n * 1. Pressing PageUp/PageDown repeatedly with pausing between each key press.\n * 2. Pressing and holding the PageDown key with repeated keys enabled.\n *\n * Test 1 worked roughly 95-99% of the time with 0ms and got a little bit better as the duration\n * increased. Test 2 got slightly better until the duration was long enough to interfere with\n * repeated keys. If the repeated key speed was faster than the timeout duration, then pressing\n * and holding pagedown caused the entire page to scroll.\n *\n * Since repeated key speed can verify across machines, determined that any duration could\n * potentially interfere with repeated keys. 0ms would be best because it almost entirely\n * eliminates the focus being lost in Voiceover without causing unintended side effects.\n * Adding delay also complicates writing tests.\n */\n abstract focusActiveCell(): void;\n\n /** Handles hour selection. */\n _onHourSelected(hour: number): void {\n this.selectedHour = hour;\n const selected = this._timeAdapter.updateHour(\n this.selected!,\n this._getHourBasedOnPeriod(hour),\n );\n this._timeSelected(selected);\n }\n\n /** Handles minute selection. */\n _onMinuteSelected(minute: number): void {\n this.selectedMinute = minute;\n const selected = this._timeAdapter.updateMinute(this.selected!, minute);\n this._timeSelected(selected);\n }\n\n /** Handles period changing. */\n _onPeriodChanged(period: MatTimePeriodType): void {\n this.period = period;\n const selected = this._timeAdapter.updateHour(\n this.selected!,\n this._getHourBasedOnPeriod(this.selectedHour),\n );\n this._timeSelected(selected);\n }\n\n _getAvailableHours(): number[] {\n if (this.isMeridiem) {\n return this.availableHours\n .filter((h) => {\n if (this.period === 'am') {\n return h < 12;\n }\n\n if (this.period === 'pm') {\n return h >= 12;\n }\n\n return h;\n })\n .map((h) => {\n if (h > 12) {\n return h - 12;\n }\n\n if (h === 0) {\n return 12;\n }\n\n return h;\n });\n }\n\n return this.availableHours;\n }\n\n _onKeydown(event: KeyboardEvent, view: 'hour' | 'minute'): void {\n switch (view) {\n case 'hour':\n this._handleHourKeydown(event);\n break;\n case 'minute':\n this._handleMinuteKeydown(event);\n break;\n }\n }\n\n private _handleHourKeydown(event: KeyboardEvent): void {\n const hours = this._getAvailableHours();\n const selectedHourIndex = hours.findIndex(\n (hour) => hour === this.selectedHour,\n );\n\n if (!hours.length) {\n return;\n }\n\n switch (event.keyCode) {\n case UP_ARROW:\n if (selectedHourIndex + 1 >= hours.length || selectedHourIndex < 0) {\n this._onHourSelected(hours[0]);\n } else {\n this._onHourSelected(hours[selectedHourIndex + 1]);\n }\n break;\n case DOWN_ARROW:\n if (selectedHourIndex - 1 < 0 || selectedHourIndex < 0) {\n this._onHourSelected(hours[hours.length - 1]);\n } else {\n this._onHourSelected(hours[selectedHourIndex - 1]);\n }\n break;\n default:\n break;\n }\n }\n\n private _handleMinuteKeydown(event: KeyboardEvent): void {\n const minutes = this.availableMinutes;\n const selectedMinuteIndex = minutes.findIndex(\n (minute) => minute === this.selectedMinute,\n );\n\n if (!minutes.length) {\n return;\n }\n\n switch (event.keyCode) {\n case UP_ARROW:\n if (\n selectedMinuteIndex + this.minuteInterval >= minutes.length ||\n selectedMinuteIndex < 0\n ) {\n const difference =\n 60 - this.selectedMinute + Math.min(...this.availableMinutes);\n const count = Math.ceil(difference / this.minuteInterval);\n const differenceForValid = count * this.minuteInterval;\n const nextValidValue = this.selectedMinute + differenceForValid;\n const correctIndex = minutes.findIndex(\n (minute) => minute === nextValidValue - 60, // amount of mins\n );\n this._onMinuteSelected(minutes[correctIndex]);\n } else {\n this._onMinuteSelected(\n minutes[selectedMinuteIndex + this.minuteInterval],\n );\n }\n break;\n case DOWN_ARROW:\n if (\n selectedMinuteIndex - this.minuteInterval < 0 ||\n selectedMinuteIndex < 0\n ) {\n const difference =\n 60 + this.selectedMinute - Math.max(...this.availableMinutes);\n const count = Math.ceil(difference / this.minuteInterval);\n const differenceForValid = count * this.minuteInterval;\n const nextValidValue = this.selectedMinute - differenceForValid;\n const correctIndex = minutes.findIndex(\n (minute) => minute === nextValidValue + 60, // amount of mins\n );\n this._onMinuteSelected(minutes[correctIndex]);\n } else {\n this._onMinuteSelected(\n minutes[selectedMinuteIndex - this.minuteInterval],\n );\n }\n break;\n default:\n break;\n }\n }\n\n /** Gets a correct hours based on meridiem and period. */\n private _getHourBasedOnPeriod(hour: number): number {\n const afterNoon = this.isMeridiem && this.period === 'pm';\n const beforeNoon = this.isMeridiem && this.period === 'am';\n\n if (afterNoon) {\n return hour === 12 ? hour : hour + 12;\n }\n\n if (beforeNoon) {\n return hour === 12 ? 0 : hour;\n }\n\n return hour;\n }\n\n private _timeSelected(value: T): void {\n if (value && !this._timeAdapter.sameTime(value, this.selected)) {\n this.selectedChange.emit(value);\n }\n\n this._userSelection.emit(value);\n }\n\n /** Sets min hour. */\n private _setMinHour(): void {\n if (!this.minTime) {\n return;\n }\n\n const minHour = this._timeAdapter.getHour(this.minTime);\n this.availableHours = this.availableHours.filter((h) => h >= minHour);\n }\n\n /** Sets max hour. */\n private _setMaxHour(): void {\n if (!this.maxTime) {\n return;\n }\n\n const maxHour = this._timeAdapter.getHour(this.maxTime);\n this.availableHours = this.availableHours.filter((h) => h <= maxHour);\n }\n\n /** Sets min minute. */\n private _setMinMinute(): void {\n if (!this.selected || !this.minTime) {\n return;\n }\n\n const selectedHour = this._timeAdapter.getHour(this.selected);\n const minHour = this._timeAdapter.getHour(this.minTime);\n\n const minMinute =\n selectedHour > minHour ? 0 : this._timeAdapter.getMinute(this.minTime);\n\n this.availableMinutes = this.availableMinutes.filter(\n (minute) => minute >= minMinute,\n );\n\n if (selectedHour < minHour) {\n this.availableMinutes = [];\n }\n }\n\n /** Sets max minute. */\n private _setMaxMinute(): void {\n if (!this.selected || !this.maxTime) {\n return;\n }\n\n const selectedHour = this._timeAdapter.getHour(this.selected);\n const maxHour = this._timeAdapter.getHour(th