UNPKG

@vipstorage/material-datetime-picker

Version:
678 lines 112 kB
import { coerceBooleanProperty, coerceStringArray } from '@angular/cdk/coercion'; import { DOWN_ARROW, ESCAPE, LEFT_ARROW, PAGE_DOWN, PAGE_UP, RIGHT_ARROW, UP_ARROW, hasModifierKey, } from '@angular/cdk/keycodes'; import { FlexibleConnectedPositionStrategy, Overlay, OverlayConfig, } from '@angular/cdk/overlay'; import { _getFocusedElementPierceShadowDom } from '@angular/cdk/platform'; import { ComponentPortal } from '@angular/cdk/portal'; import { DOCUMENT } from '@angular/common'; import { ChangeDetectionStrategy, Component, Directive, EventEmitter, Inject, InjectionToken, Input, Optional, Output, ViewChild, ViewEncapsulation, inject, } from '@angular/core'; import { mixinColor } from '@angular/material/core'; import { Subject, Subscription, merge } from 'rxjs'; import { filter, take } from 'rxjs/operators'; import { NgxMatCalendar } from './calendar'; import { NGX_MAT_DATE_RANGE_SELECTION_STRATEGY, } from './date-range-selection-strategy'; import { NgxDateRange, } from './date-selection-model'; import { ngxMatDatepickerAnimations } from './datepicker-animations'; import { createMissingDateImplError } from './datepicker-errors'; import { DEFAULT_STEP } from './utils/date-utils'; import * as i0 from "@angular/core"; import * as i1 from "./date-selection-model"; import * as i2 from "./core/date-adapter"; import * as i3 from "./datepicker-intl"; import * as i4 from "@angular/common"; import * as i5 from "@angular/material/button"; import * as i6 from "@angular/cdk/a11y"; import * as i7 from "@angular/cdk/portal"; import * as i8 from "./timepicker.component"; import * as i9 from "@angular/forms"; import * as i10 from "./calendar"; import * as i11 from "@angular/cdk/overlay"; import * as i12 from "@angular/cdk/bidi"; /** Used to generate a unique ID for each datepicker instance. */ let datepickerUid = 0; /** Injection token that determines the scroll handling while the calendar is open. */ export const NGX_MAT_DATEPICKER_SCROLL_STRATEGY = new InjectionToken('ngx-mat-datepicker-scroll-strategy'); /** @docs-private */ export function NGX_MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY(overlay) { return () => overlay.scrollStrategies.reposition(); } /** @docs-private */ export const NGX_MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = { provide: NGX_MAT_DATEPICKER_SCROLL_STRATEGY, deps: [Overlay], useFactory: NGX_MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY, }; // Boilerplate for applying mixins to MatDatepickerContent. /** @docs-private */ const _NgxMatDatepickerContentBase = mixinColor(class { constructor(_elementRef) { this._elementRef = _elementRef; } }); /** * Component used as the content for the datepicker overlay. We use this instead of using * MatCalendar directly as the content so we can control the initial focus. This also gives us a * place to put additional features of the overlay that are not part of the calendar itself in the * future. (e.g. confirmation buttons). * @docs-private */ export class NgxMatDatepickerContent extends _NgxMatDatepickerContentBase { get isViewMonth() { if (!this._calendar || this._calendar.currentView == null) return true; return this._calendar.currentView == 'month'; } constructor(elementRef, _changeDetectorRef, _globalModel, _dateAdapter, _rangeSelectionStrategy, intl) { super(elementRef); this._changeDetectorRef = _changeDetectorRef; this._globalModel = _globalModel; this._dateAdapter = _dateAdapter; this._rangeSelectionStrategy = _rangeSelectionStrategy; this._subscriptions = new Subscription(); /** Emits when an animation has finished. */ this._animationDone = new Subject(); /** Whether there is an in-progress animation. */ this._isAnimating = false; /** Portal with projected action buttons. */ this._actionsPortal = null; this._closeButtonText = intl.closeCalendarLabel; } ngOnInit() { this._animationState = this.datepicker.touchUi ? 'enter-dialog' : 'enter-dropdown'; } ngAfterViewInit() { this._subscriptions.add(this.datepicker.stateChanges.subscribe(() => { this._changeDetectorRef.markForCheck(); })); this._calendar.focusActiveCell(); } ngOnDestroy() { this._subscriptions.unsubscribe(); this._animationDone.complete(); } onTimeChanged(selectedDateWithTime) { const userEvent = { value: selectedDateWithTime, event: null }; this._updateUserSelectionWithCalendarUserEvent(userEvent); } _handleUserSelection(event) { this._updateUserSelectionWithCalendarUserEvent(event); // Delegate closing the overlay to the actions. if (this.datepicker.hideTime) { if ((!this._model || this._model.isComplete()) && !this._actionsPortal) { this.datepicker.close(); } } } _updateUserSelectionWithCalendarUserEvent(event) { const selection = this._model.selection; const value = event.value; const isRange = selection instanceof NgxDateRange; // If we're selecting a range and we have a selection strategy, always pass the value through // there. Otherwise don't assign null values to the model, unless we're selecting a range. // A null value when picking a range means that the user cancelled the selection (e.g. by // pressing escape), whereas when selecting a single value it means that the value didn't // change. This isn't very intuitive, but it's here for backwards-compatibility. if (isRange && this._rangeSelectionStrategy) { const newSelection = this._rangeSelectionStrategy.selectionFinished(value, selection, event.event); this._model.updateSelection(newSelection, this); } else { const isSameTime = this._dateAdapter.isSameTime(selection, value); const isSameDate = this._dateAdapter.sameDate(value, selection); const isSame = isSameDate && isSameTime; if (value && (isRange || !isSame)) { this._model.add(value); } } } _handleUserDragDrop(event) { this._model.updateSelection(event.value, this); } _startExitAnimation() { this._animationState = 'void'; this._changeDetectorRef.markForCheck(); } _handleAnimationEvent(event) { this._isAnimating = event.phaseName === 'start'; if (!this._isAnimating) { this._animationDone.next(); } } _getSelected() { this._modelTime = this._model.selection; return this._model.selection; } /** Applies the current pending selection to the global model. */ _applyPendingSelection() { if (this._model !== this._globalModel) { this._globalModel.updateSelection(this._model.selection, this); } } /** * Assigns a new portal containing the datepicker actions. * @param portal Portal with the actions to be assigned. * @param forceRerender Whether a re-render of the portal should be triggered. This isn't * necessary if the portal is assigned during initialization, but it may be required if it's * added at a later point. */ _assignActions(portal, forceRerender) { // If we have actions, clone the model so that we have the ability to cancel the selection, // otherwise update the global model directly. Note that we want to assign this as soon as // possible, but `_actionsPortal` isn't available in the constructor so we do it in `ngOnInit`. this._model = portal ? this._globalModel.clone() : this._globalModel; this._actionsPortal = portal; if (forceRerender) { this._changeDetectorRef.detectChanges(); } } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: NgxMatDatepickerContent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i1.NgxMatDateSelectionModel }, { token: i2.NgxMatDateAdapter }, { token: NGX_MAT_DATE_RANGE_SELECTION_STRATEGY, optional: true }, { token: i3.NgxMatDatepickerIntl }], target: i0.ɵɵFactoryTarget.Component }); } /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.0.9", type: NgxMatDatepickerContent, selector: "ngx-mat-datepicker-content", inputs: { color: "color" }, host: { listeners: { "@transformPanel.start": "_handleAnimationEvent($event)", "@transformPanel.done": "_handleAnimationEvent($event)" }, properties: { "@transformPanel": "_animationState", "class.mat-datepicker-content-touch": "datepicker.touchUi", "class.mat-datepicker-content-touch-with-time": "!datepicker.hideTime" }, classAttribute: "mat-datepicker-content" }, viewQueries: [{ propertyName: "_calendar", first: true, predicate: NgxMatCalendar, descendants: true }], exportAs: ["ngxMatDatepickerContent"], usesInheritance: true, ngImport: i0, template: "<div cdkTrapFocus role=\"dialog\" [attr.aria-modal]=\"true\" [attr.aria-labelledby]=\"_dialogLabelId ?? undefined\"\n class=\"mat-datepicker-content-container\"\n [class.mat-datepicker-content-container-with-custom-header]=\"datepicker.calendarHeaderComponent\"\n [class.mat-datepicker-content-container-with-actions]=\"_actionsPortal\"\n [class.mat-datepicker-content-container-with-time]=\"!datepicker._hideTime\"\n >\n <ngx-mat-calendar [id]=\"datepicker.id\" [ngClass]=\"datepicker.panelClass\" [startAt]=\"datepicker.startAt\"\n [startView]=\"datepicker.startView\" [minDate]=\"datepicker._getMinDate()\" [maxDate]=\"datepicker._getMaxDate()\"\n [dateFilter]=\"datepicker._getDateFilter()\" [headerComponent]=\"datepicker.calendarHeaderComponent\"\n [selected]=\"_getSelected()\" [dateClass]=\"datepicker.dateClass\" [comparisonStart]=\"comparisonStart\"\n [comparisonEnd]=\"comparisonEnd\" [@fadeInCalendar]=\"'enter'\" [startDateAccessibleName]=\"startDateAccessibleName\"\n [endDateAccessibleName]=\"endDateAccessibleName\" (yearSelected)=\"datepicker._selectYear($event)\"\n (monthSelected)=\"datepicker._selectMonth($event)\" (viewChanged)=\"datepicker._viewChanged($event)\"\n (_userSelection)=\"_handleUserSelection($event)\" (_userDragDrop)=\"_handleUserDragDrop($event)\"></ngx-mat-calendar>\n\n <ng-container *ngIf=\"isViewMonth\">\n <div *ngIf=\"!datepicker._hideTime\" class=\"time-container\" [class.disable-seconds]=\"!datepicker._showSeconds\">\n <ngx-mat-timepicker [showSpinners]=\"datepicker._showSpinners\" [showSeconds]=\"datepicker._showSeconds\"\n [disabled]=\"datepicker._disabled || !_modelTime\" [stepHour]=\"datepicker._stepHour\"\n [stepMinute]=\"datepicker._stepMinute\" [stepSecond]=\"datepicker._stepSecond\" [(ngModel)]=\"_modelTime\"\n [color]=\"datepicker._color\" [enableMeridian]=\"datepicker._enableMeridian\"\n [disableMinute]=\"datepicker._disableMinute\" (ngModelChange)=\"onTimeChanged($event)\">\n </ngx-mat-timepicker>\n </div>\n </ng-container>\n\n <ng-template [cdkPortalOutlet]=\"_actionsPortal\"></ng-template>\n\n <!-- Invisible close button for screen reader users. -->\n <button type=\"button\" mat-raised-button [color]=\"color || 'primary'\" class=\"mat-datepicker-close-button\"\n [class.cdk-visually-hidden]=\"!_closeButtonFocused\" (focus)=\"_closeButtonFocused = true\"\n (blur)=\"_closeButtonFocused = false\" (click)=\"datepicker.close()\">{{ _closeButtonText }}\n </button>\n</div>", styles: [".mat-datepicker-content{display:block;border-radius:4px}.mat-datepicker-content .mat-calendar{width:296px;height:354px}.mat-datepicker-content .mat-datepicker-content-container-with-custom-header .mat-calendar{height:auto}.mat-datepicker-content .mat-datepicker-close-button{position:absolute;top:100%;left:0;margin-top:8px}.ng-animating .mat-datepicker-content .mat-datepicker-close-button{display:none}.mat-datepicker-content-container{display:flex;flex-direction:column;justify-content:space-between}.time-container{display:flex;position:relative;padding-top:5px;justify-content:center}.time-container.disable-seconds .ngx-mat-timepicker .table{margin-left:9px}.time-container:before{content:\"\";position:absolute;top:0;left:0;right:0;height:1px;background-color:#0000001f}.mat-datepicker-content-touch{display:block;max-height:90vh;position:relative;overflow:visible}.mat-datepicker-content-touch .mat-datepicker-content-container{min-height:312px;max-height:815px;min-width:250px;max-width:750px}.mat-datepicker-content-touch .mat-calendar{width:100%;height:auto}@media all and (orientation: landscape){.mat-datepicker-content-touch .mat-datepicker-content-container{width:64vh;height:90vh}.mat-datepicker-content-touch .mat-datepicker-content-container.mat-datepicker-content-container-with-time{height:auto}}@media all and (orientation: portrait){.mat-datepicker-content-touch{max-height:100vh}.mat-datepicker-content-touch .mat-datepicker-content-container{width:80vw;height:100vw}.mat-datepicker-content-touch .mat-datepicker-content-container.mat-datepicker-content-container-with-time{height:auto;max-height:870px}.mat-datepicker-content-touch .mat-datepicker-content-container.mat-datepicker-content-container-with-time.mat-datepicker-content-container-with-actions{max-height:none!important}.mat-datepicker-content-touch .mat-datepicker-content-container-with-actions{height:115vw}}\n"], dependencies: [{ kind: "directive", type: i4.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "directive", type: i6.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: i7.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "component", type: i8.NgxMatTimepickerComponent, selector: "ngx-mat-timepicker", inputs: ["disabled", "showSpinners", "stepHour", "stepMinute", "stepSecond", "showSeconds", "disableMinute", "enableMeridian", "defaultTime", "color"], exportAs: ["ngxMatTimepicker"] }, { kind: "directive", type: i9.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i9.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i10.NgxMatCalendar, selector: "ngx-mat-calendar", inputs: ["headerComponent", "startAt", "startView", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedChange", "yearSelected", "monthSelected", "viewChanged", "_userSelection", "_userDragDrop"], exportAs: ["ngxMatCalendar"] }], animations: [ngxMatDatepickerAnimations.transformPanel, ngxMatDatepickerAnimations.fadeInCalendar], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: NgxMatDatepickerContent, decorators: [{ type: Component, args: [{ selector: 'ngx-mat-datepicker-content', host: { 'class': 'mat-datepicker-content', '[@transformPanel]': '_animationState', '(@transformPanel.start)': '_handleAnimationEvent($event)', '(@transformPanel.done)': '_handleAnimationEvent($event)', '[class.mat-datepicker-content-touch]': 'datepicker.touchUi', '[class.mat-datepicker-content-touch-with-time]': '!datepicker.hideTime', }, animations: [ngxMatDatepickerAnimations.transformPanel, ngxMatDatepickerAnimations.fadeInCalendar], exportAs: 'ngxMatDatepickerContent', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, inputs: ['color'], template: "<div cdkTrapFocus role=\"dialog\" [attr.aria-modal]=\"true\" [attr.aria-labelledby]=\"_dialogLabelId ?? undefined\"\n class=\"mat-datepicker-content-container\"\n [class.mat-datepicker-content-container-with-custom-header]=\"datepicker.calendarHeaderComponent\"\n [class.mat-datepicker-content-container-with-actions]=\"_actionsPortal\"\n [class.mat-datepicker-content-container-with-time]=\"!datepicker._hideTime\"\n >\n <ngx-mat-calendar [id]=\"datepicker.id\" [ngClass]=\"datepicker.panelClass\" [startAt]=\"datepicker.startAt\"\n [startView]=\"datepicker.startView\" [minDate]=\"datepicker._getMinDate()\" [maxDate]=\"datepicker._getMaxDate()\"\n [dateFilter]=\"datepicker._getDateFilter()\" [headerComponent]=\"datepicker.calendarHeaderComponent\"\n [selected]=\"_getSelected()\" [dateClass]=\"datepicker.dateClass\" [comparisonStart]=\"comparisonStart\"\n [comparisonEnd]=\"comparisonEnd\" [@fadeInCalendar]=\"'enter'\" [startDateAccessibleName]=\"startDateAccessibleName\"\n [endDateAccessibleName]=\"endDateAccessibleName\" (yearSelected)=\"datepicker._selectYear($event)\"\n (monthSelected)=\"datepicker._selectMonth($event)\" (viewChanged)=\"datepicker._viewChanged($event)\"\n (_userSelection)=\"_handleUserSelection($event)\" (_userDragDrop)=\"_handleUserDragDrop($event)\"></ngx-mat-calendar>\n\n <ng-container *ngIf=\"isViewMonth\">\n <div *ngIf=\"!datepicker._hideTime\" class=\"time-container\" [class.disable-seconds]=\"!datepicker._showSeconds\">\n <ngx-mat-timepicker [showSpinners]=\"datepicker._showSpinners\" [showSeconds]=\"datepicker._showSeconds\"\n [disabled]=\"datepicker._disabled || !_modelTime\" [stepHour]=\"datepicker._stepHour\"\n [stepMinute]=\"datepicker._stepMinute\" [stepSecond]=\"datepicker._stepSecond\" [(ngModel)]=\"_modelTime\"\n [color]=\"datepicker._color\" [enableMeridian]=\"datepicker._enableMeridian\"\n [disableMinute]=\"datepicker._disableMinute\" (ngModelChange)=\"onTimeChanged($event)\">\n </ngx-mat-timepicker>\n </div>\n </ng-container>\n\n <ng-template [cdkPortalOutlet]=\"_actionsPortal\"></ng-template>\n\n <!-- Invisible close button for screen reader users. -->\n <button type=\"button\" mat-raised-button [color]=\"color || 'primary'\" class=\"mat-datepicker-close-button\"\n [class.cdk-visually-hidden]=\"!_closeButtonFocused\" (focus)=\"_closeButtonFocused = true\"\n (blur)=\"_closeButtonFocused = false\" (click)=\"datepicker.close()\">{{ _closeButtonText }}\n </button>\n</div>", styles: [".mat-datepicker-content{display:block;border-radius:4px}.mat-datepicker-content .mat-calendar{width:296px;height:354px}.mat-datepicker-content .mat-datepicker-content-container-with-custom-header .mat-calendar{height:auto}.mat-datepicker-content .mat-datepicker-close-button{position:absolute;top:100%;left:0;margin-top:8px}.ng-animating .mat-datepicker-content .mat-datepicker-close-button{display:none}.mat-datepicker-content-container{display:flex;flex-direction:column;justify-content:space-between}.time-container{display:flex;position:relative;padding-top:5px;justify-content:center}.time-container.disable-seconds .ngx-mat-timepicker .table{margin-left:9px}.time-container:before{content:\"\";position:absolute;top:0;left:0;right:0;height:1px;background-color:#0000001f}.mat-datepicker-content-touch{display:block;max-height:90vh;position:relative;overflow:visible}.mat-datepicker-content-touch .mat-datepicker-content-container{min-height:312px;max-height:815px;min-width:250px;max-width:750px}.mat-datepicker-content-touch .mat-calendar{width:100%;height:auto}@media all and (orientation: landscape){.mat-datepicker-content-touch .mat-datepicker-content-container{width:64vh;height:90vh}.mat-datepicker-content-touch .mat-datepicker-content-container.mat-datepicker-content-container-with-time{height:auto}}@media all and (orientation: portrait){.mat-datepicker-content-touch{max-height:100vh}.mat-datepicker-content-touch .mat-datepicker-content-container{width:80vw;height:100vw}.mat-datepicker-content-touch .mat-datepicker-content-container.mat-datepicker-content-container-with-time{height:auto;max-height:870px}.mat-datepicker-content-touch .mat-datepicker-content-container.mat-datepicker-content-container-with-time.mat-datepicker-content-container-with-actions{max-height:none!important}.mat-datepicker-content-touch .mat-datepicker-content-container-with-actions{height:115vw}}\n"] }] }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i1.NgxMatDateSelectionModel }, { type: i2.NgxMatDateAdapter }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [NGX_MAT_DATE_RANGE_SELECTION_STRATEGY] }] }, { type: i3.NgxMatDatepickerIntl }], propDecorators: { _calendar: [{ type: ViewChild, args: [NgxMatCalendar] }] } }); /** Base class for a datepicker. */ export class NgxMatDatepickerBase { /** The date to open the calendar to initially. */ get startAt() { // If an explicit startAt is set we start there, otherwise we start at whatever the currently // selected value is. return this._startAt || (this.datepickerInput ? this.datepickerInput.getStartValue() : null); } set startAt(value) { this._startAt = this._dateAdapter.getValidDateOrNull(this._dateAdapter.deserialize(value)); } /** Color palette to use on the datepicker's calendar. */ get color() { return (this._color || (this.datepickerInput ? this.datepickerInput.getThemePalette() : undefined)); } set color(value) { this._color = value; } /** * Whether the calendar UI is in touch mode. In touch mode the calendar opens in a dialog rather * than a dropdown and elements have more padding to allow for bigger touch targets. */ get touchUi() { return this._touchUi; } set touchUi(value) { this._touchUi = coerceBooleanProperty(value); } get hideTime() { return this._hideTime; } set hideTime(value) { this._hideTime = coerceBooleanProperty(value); } /** Whether the datepicker pop-up should be disabled. */ get disabled() { return this._disabled === undefined && this.datepickerInput ? this.datepickerInput.disabled : !!this._disabled; } set disabled(value) { const newValue = coerceBooleanProperty(value); if (newValue !== this._disabled) { this._disabled = newValue; this.stateChanges.next(undefined); } } /** * Whether to restore focus to the previously-focused element when the calendar is closed. * Note that automatic focus restoration is an accessibility feature and it is recommended that * you provide your own equivalent, if you decide to turn it off. */ get restoreFocus() { return this._restoreFocus; } set restoreFocus(value) { this._restoreFocus = coerceBooleanProperty(value); } /** * Classes to be passed to the date picker panel. * Supports string and string array values, similar to `ngClass`. */ get panelClass() { return this._panelClass; } set panelClass(value) { this._panelClass = coerceStringArray(value); } /** Whether the calendar is open. */ get opened() { return this._opened; } set opened(value) { coerceBooleanProperty(value) ? this.open() : this.close(); } /** Whether the timepicker'spinners is shown. */ get showSpinners() { return this._showSpinners; } set showSpinners(value) { this._showSpinners = value; } /** Whether the second part is disabled. */ get showSeconds() { return this._showSeconds; } set showSeconds(value) { this._showSeconds = value; } /** Step hour */ get stepHour() { return this._stepHour; } set stepHour(value) { this._stepHour = value; } /** Step minute */ get stepMinute() { return this._stepMinute; } set stepMinute(value) { this._stepMinute = value; } /** Step second */ get stepSecond() { return this._stepSecond; } set stepSecond(value) { this._stepSecond = value; } /** Enable meridian */ get enableMeridian() { return this._enableMeridian; } set enableMeridian(value) { this._enableMeridian = value; } /** disable minute */ get disableMinute() { return this._disableMinute; } set disableMinute(value) { this._disableMinute = value; } /** Step second */ get defaultTime() { return this._defaultTime; } set defaultTime(value) { this._defaultTime = value; } /** The minimum selectable date. */ _getMinDate() { return this.datepickerInput && this.datepickerInput.min; } /** The maximum selectable date. */ _getMaxDate() { return this.datepickerInput && this.datepickerInput.max; } _getDateFilter() { return this.datepickerInput && this.datepickerInput.dateFilter; } constructor(_overlay, _ngZone, _viewContainerRef, scrollStrategy, _dateAdapter, _dir, _model) { this._overlay = _overlay; this._ngZone = _ngZone; this._viewContainerRef = _viewContainerRef; this._dateAdapter = _dateAdapter; this._dir = _dir; this._model = _model; this._inputStateChanges = Subscription.EMPTY; this._document = inject(DOCUMENT); /** The view that the calendar should start in. */ this.startView = 'month'; this._touchUi = false; this._hideTime = false; /** Preferred position of the datepicker in the X axis. */ this.xPosition = 'start'; /** Preferred position of the datepicker in the Y axis. */ this.yPosition = 'below'; this._restoreFocus = true; /** * Emits selected year in multiyear view. * This doesn't imply a change on the selected date. */ this.yearSelected = new EventEmitter(); /** * Emits selected month in year view. * This doesn't imply a change on the selected date. */ this.monthSelected = new EventEmitter(); /** * Emits when the current view changes. */ this.viewChanged = new EventEmitter(true); /** Emits when the datepicker has been opened. */ this.openedStream = new EventEmitter(); /** Emits when the datepicker has been closed. */ this.closedStream = new EventEmitter(); this._opened = false; this._showSpinners = true; this._showSeconds = false; this._stepHour = DEFAULT_STEP; this._stepMinute = DEFAULT_STEP; this._stepSecond = DEFAULT_STEP; this._enableMeridian = false; /** The id for the datepicker calendar. */ this.id = `mat-datepicker-${datepickerUid++}`; /** The element that was focused before the datepicker was opened. */ this._focusedElementBeforeOpen = null; /** Unique class that will be added to the backdrop so that the test harnesses can look it up. */ this._backdropHarnessClass = `${this.id}-backdrop`; /** Emits when the datepicker's state changes. */ this.stateChanges = new Subject(); if (!this._dateAdapter) { throw createMissingDateImplError('NgxMatDateAdapter'); } this._scrollStrategy = scrollStrategy; } ngOnChanges(changes) { const positionChange = changes['xPosition'] || changes['yPosition']; if (positionChange && !positionChange.firstChange && this._overlayRef) { const positionStrategy = this._overlayRef.getConfig().positionStrategy; if (positionStrategy instanceof FlexibleConnectedPositionStrategy) { this._setConnectedPositions(positionStrategy); if (this.opened) { this._overlayRef.updatePosition(); } } } this.stateChanges.next(undefined); } ngOnDestroy() { this._destroyOverlay(); this.close(); this._inputStateChanges.unsubscribe(); this.stateChanges.complete(); } /** Selects the given date */ select(date) { this._model.add(date); } /** Emits the selected year in multiyear view */ _selectYear(normalizedYear) { this.yearSelected.emit(normalizedYear); } /** Emits selected month in year view */ _selectMonth(normalizedMonth) { this.monthSelected.emit(normalizedMonth); } /** Emits changed view */ _viewChanged(view) { this.viewChanged.emit(view); } /** * Register an input with this datepicker. * @param input The datepicker input to register with this datepicker. * @returns Selection model that the input should hook itself up to. */ registerInput(input) { if (this.datepickerInput) { throw Error('A MatDatepicker can only be associated with a single input.'); } this._inputStateChanges.unsubscribe(); this.datepickerInput = input; this._inputStateChanges = input.stateChanges.subscribe(() => this.stateChanges.next(undefined)); return this._model; } /** * Registers a portal containing action buttons with the datepicker. * @param portal Portal to be registered. */ registerActions(portal) { if (this._actionsPortal) { throw Error('A MatDatepicker can only be associated with a single actions row.'); } this._actionsPortal = portal; this._componentRef?.instance._assignActions(portal, true); } /** * Removes a portal containing action buttons from the datepicker. * @param portal Portal to be removed. */ removeActions(portal) { if (portal === this._actionsPortal) { this._actionsPortal = null; this._componentRef?.instance._assignActions(null, true); } } /** Open the calendar. */ open() { // Skip reopening if there's an in-progress animation to avoid overlapping // sequences which can cause "changed after checked" errors. See #25837. if (this._opened || this.disabled || this._componentRef?.instance._isAnimating) { return; } if (!this.datepickerInput) { throw Error('Attempted to open an MatDatepicker with no associated input.'); } this._focusedElementBeforeOpen = _getFocusedElementPierceShadowDom(); this._openOverlay(); this._opened = true; this.openedStream.emit(); } /** Close the calendar. */ close() { // Skip reopening if there's an in-progress animation to avoid overlapping // sequences which can cause "changed after checked" errors. See #25837. if (!this._opened || this._componentRef?.instance._isAnimating) { return; } const canRestoreFocus = this._restoreFocus && this._focusedElementBeforeOpen && typeof this._focusedElementBeforeOpen.focus === 'function'; const completeClose = () => { // The `_opened` could've been reset already if // we got two events in quick succession. if (this._opened) { this._opened = false; this.closedStream.emit(); } }; if (this._componentRef) { const { instance, location } = this._componentRef; instance._startExitAnimation(); instance._animationDone.pipe(take(1)).subscribe(() => { const activeElement = this._document.activeElement; // Since we restore focus after the exit animation, we have to check that // the user didn't move focus themselves inside the `close` handler. if (canRestoreFocus && (!activeElement || activeElement === this._document.activeElement || location.nativeElement.contains(activeElement))) { this._focusedElementBeforeOpen.focus(); } this._focusedElementBeforeOpen = null; this._destroyOverlay(); }); } if (canRestoreFocus) { // Because IE moves focus asynchronously, we can't count on it being restored before we've // marked the datepicker as closed. If the event fires out of sequence and the element that // we're refocusing opens the datepicker on focus, the user could be stuck with not being // able to close the calendar at all. We work around it by making the logic, that marks // the datepicker as closed, async as well. setTimeout(completeClose); } else { completeClose(); } } /** Applies the current pending selection on the overlay to the model. */ _applyPendingSelection() { this._componentRef?.instance?._applyPendingSelection(); } /** Forwards relevant values from the datepicker to the datepicker content inside the overlay. */ _forwardContentValues(instance) { instance.datepicker = this; instance.color = this.color; instance._dialogLabelId = this.datepickerInput.getOverlayLabelId(); instance._assignActions(this._actionsPortal, false); } /** Opens the overlay with the calendar. */ _openOverlay() { this._destroyOverlay(); const isDialog = this.touchUi; const portal = new ComponentPortal(NgxMatDatepickerContent, this._viewContainerRef); const overlayRef = (this._overlayRef = this._overlay.create(new OverlayConfig({ positionStrategy: isDialog ? this._getDialogStrategy() : this._getDropdownStrategy(), hasBackdrop: true, backdropClass: [ isDialog ? 'cdk-overlay-dark-backdrop' : 'mat-overlay-transparent-backdrop', this._backdropHarnessClass, ], direction: this._dir, scrollStrategy: isDialog ? this._overlay.scrollStrategies.block() : this._scrollStrategy(), panelClass: `mat-datepicker-${isDialog ? 'dialog' : 'popup'}`, }))); this._getCloseStream(overlayRef).subscribe(event => { if (event) { event.preventDefault(); } this.close(); }); // The `preventDefault` call happens inside the calendar as well, however focus moves into // it inside a timeout which can give browsers a chance to fire off a keyboard event in-between // that can scroll the page (see #24969). Always block default actions of arrow keys for the // entire overlay so the page doesn't get scrolled by accident. overlayRef.keydownEvents().subscribe(event => { const keyCode = event.keyCode; if (keyCode === UP_ARROW || keyCode === DOWN_ARROW || keyCode === LEFT_ARROW || keyCode === RIGHT_ARROW || keyCode === PAGE_UP || keyCode === PAGE_DOWN) { event.preventDefault(); } }); this._componentRef = overlayRef.attach(portal); this._forwardContentValues(this._componentRef.instance); // Update the position once the calendar has rendered. Only relevant in dropdown mode. if (!isDialog) { this._ngZone.onStable.pipe(take(1)).subscribe(() => overlayRef.updatePosition()); } } /** Destroys the current overlay. */ _destroyOverlay() { if (this._overlayRef) { this._overlayRef.dispose(); this._overlayRef = this._componentRef = null; } } /** Gets a position strategy that will open the calendar as a dropdown. */ _getDialogStrategy() { return this._overlay.position().global().centerHorizontally().centerVertically(); } /** Gets a position strategy that will open the calendar as a dropdown. */ _getDropdownStrategy() { const strategy = this._overlay .position() .flexibleConnectedTo(this.datepickerInput.getConnectedOverlayOrigin()) .withTransformOriginOn('.mat-datepicker-content') .withFlexibleDimensions(false) .withViewportMargin(8) .withLockedPosition(); return this._setConnectedPositions(strategy); } /** Sets the positions of the datepicker in dropdown mode based on the current configuration. */ _setConnectedPositions(strategy) { const primaryX = this.xPosition === 'end' ? 'end' : 'start'; const secondaryX = primaryX === 'start' ? 'end' : 'start'; const primaryY = this.yPosition === 'above' ? 'bottom' : 'top'; const secondaryY = primaryY === 'top' ? 'bottom' : 'top'; return strategy.withPositions([ { originX: primaryX, originY: secondaryY, overlayX: primaryX, overlayY: primaryY, }, { originX: primaryX, originY: primaryY, overlayX: primaryX, overlayY: secondaryY, }, { originX: secondaryX, originY: secondaryY, overlayX: secondaryX, overlayY: primaryY, }, { originX: secondaryX, originY: primaryY, overlayX: secondaryX, overlayY: secondaryY, }, ]); } /** Gets an observable that will emit when the overlay is supposed to be closed. */ _getCloseStream(overlayRef) { const ctrlShiftMetaModifiers = ['ctrlKey', 'shiftKey', 'metaKey']; return merge(overlayRef.backdropClick(), overlayRef.detachments(), overlayRef.keydownEvents().pipe(filter(event => { // Closing on alt + up is only valid when there's an input associated with the datepicker. return ((event.keyCode === ESCAPE && !hasModifierKey(event)) || (this.datepickerInput && hasModifierKey(event, 'altKey') && event.keyCode === UP_ARROW && ctrlShiftMetaModifiers.every((modifier) => !hasModifierKey(event, modifier)))); }))); } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: NgxMatDatepickerBase, deps: [{ token: i11.Overlay }, { token: i0.NgZone }, { token: i0.ViewContainerRef }, { token: NGX_MAT_DATEPICKER_SCROLL_STRATEGY }, { token: i2.NgxMatDateAdapter, optional: true }, { token: i12.Directionality, optional: true }, { token: i1.NgxMatDateSelectionModel }], target: i0.ɵɵFactoryTarget.Directive }); } /** @nocollapse */ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.0.9", type: NgxMatDatepickerBase, inputs: { calendarHeaderComponent: "calendarHeaderComponent", startAt: "startAt", startView: "startView", color: "color", touchUi: "touchUi", hideTime: "hideTime", disabled: "disabled", xPosition: "xPosition", yPosition: "yPosition", restoreFocus: "restoreFocus", dateClass: "dateClass", panelClass: "panelClass", opened: "opened", showSpinners: "showSpinners", showSeconds: "showSeconds", stepHour: "stepHour", stepMinute: "stepMinute", stepSecond: "stepSecond", enableMeridian: "enableMeridian", disableMinute: "disableMinute", defaultTime: "defaultTime" }, outputs: { yearSelected: "yearSelected", monthSelected: "monthSelected", viewChanged: "viewChanged", openedStream: "opened", closedStream: "closed" }, usesOnChanges: true, ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: NgxMatDatepickerBase, decorators: [{ type: Directive }], ctorParameters: () => [{ type: i11.Overlay }, { type: i0.NgZone }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{ type: Inject, args: [NGX_MAT_DATEPICKER_SCROLL_STRATEGY] }] }, { type: i2.NgxMatDateAdapter, decorators: [{ type: Optional }] }, { type: i12.Directionality, decorators: [{ type: Optional }] }, { type: i1.NgxMatDateSelectionModel }], propDecorators: { calendarHeaderComponent: [{ type: Input }], startAt: [{ type: Input }], startView: [{ type: Input }], color: [{ type: Input }], touchUi: [{ type: Input }], hideTime: [{ type: Input }], disabled: [{ type: Input }], xPosition: [{ type: Input }], yPosition: [{ type: Input }], restoreFocus: [{ type: Input }], yearSelected: [{ type: Output }], monthSelected: [{ type: Output }], viewChanged: [{ type: Output }], dateClass: [{ type: Input }], openedStream: [{ type: Output, args: ['opened'] }], closedStream: [{ type: Output, args: ['closed'] }], panelClass: [{ type: Input }], opened: [{ type: Input }], showSpinners: [{ type: Input }], showSeconds: [{ type: Input }], stepHour: [{ type: Input }], stepMinute: [{ type: Input }], stepSecond: [{ type: Input }], enableMeridian: [{ type: Input }], disableMinute: [{ type: Input }], defaultTime: [{ type: Input }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZXBpY2tlci1iYXNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvZGF0ZXRpbWUtcGlja2VyL3NyYy9saWIvZGF0ZXBpY2tlci1iYXNlLnRzIiwiLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvZGF0ZXRpbWUtcGlja2VyL3NyYy9saWIvZGF0ZXBpY2tlci1jb250ZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBR0EsT0FBTyxFQUFnQixxQkFBcUIsRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQy9GLE9BQU8sRUFDTCxVQUFVLEVBQ1YsTUFBTSxFQUNOLFVBQVUsRUFDVixTQUFTLEVBQ1QsT0FBTyxFQUNQLFdBQVcsRUFDWCxRQUFRLEVBQ1IsY0FBYyxHQUNmLE1BQU0sdUJBQXVCLENBQUM7QUFDL0IsT0FBTyxFQUNMLGlDQUFpQyxFQUNqQyxPQUFPLEVBQ1AsYUFBYSxHQUdkLE1BQU0sc0JBQXNCLENBQUM7QUFDOUIsT0FBTyxFQUFFLGlDQUFpQyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDMUUsT0FBTyxFQUFFLGVBQWUsRUFBaUMsTUFBTSxxQkFBcUIsQ0FBQztBQUNyRixPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDM0MsT0FBTyxFQUVMLHVCQUF1QixFQUV2QixTQUFTLEVBRVQsU0FBUyxFQUVULFlBQVksRUFDWixNQUFNLEVBQ04sY0FBYyxFQUNkLEtBQUssRUFLTCxRQUFRLEVBQ1IsTUFBTSxFQUVOLFNBQVMsRUFFVCxpQkFBaUIsRUFDakIsTUFBTSxHQUNQLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBMEIsVUFBVSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDNUUsT0FBTyxFQUFjLE9BQU8sRUFBRSxZQUFZLEVBQUUsS0FBSyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ2hFLE9BQU8sRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFF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