@vipstorage/material-datetime-picker
Version:
Angular Material Datetime Picker
678 lines • 112 kB
JavaScript
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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