@angular/material
Version:
Angular Material
606 lines • 99.7 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { Directionality } from '@angular/cdk/bidi';
import { coerceStringArray } from '@angular/cdk/coercion';
import { DOWN_ARROW, ESCAPE, hasModifierKey, LEFT_ARROW, PAGE_DOWN, PAGE_UP, RIGHT_ARROW, UP_ARROW, } from '@angular/cdk/keycodes';
import { Overlay, OverlayConfig, FlexibleConnectedPositionStrategy, } from '@angular/cdk/overlay';
import { CdkPortalOutlet, ComponentPortal } from '@angular/cdk/portal';
import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Inject, InjectionToken, Input, NgZone, Optional, Output, ViewChild, ViewContainerRef, ViewEncapsulation, ChangeDetectorRef, Directive, inject, booleanAttribute, afterNextRender, Injector, } from '@angular/core';
import { DateAdapter } from '@angular/material/core';
import { merge, Subject, Subscription } from 'rxjs';
import { filter, take } from 'rxjs/operators';
import { _getFocusedElementPierceShadowDom } from '@angular/cdk/platform';
import { MatCalendar } from './calendar';
import { matDatepickerAnimations } from './datepicker-animations';
import { createMissingDateImplError } from './datepicker-errors';
import { MatDateSelectionModel, DateRange, } from './date-selection-model';
import { MAT_DATE_RANGE_SELECTION_STRATEGY, } from './date-range-selection-strategy';
import { MatDatepickerIntl } from './datepicker-intl';
import { DOCUMENT } from '@angular/common';
import { MatButton } from '@angular/material/button';
import { CdkTrapFocus } from '@angular/cdk/a11y';
import * as i0 from "@angular/core";
import * as i1 from "./date-selection-model";
import * as i2 from "@angular/material/core";
import * as i3 from "./datepicker-intl";
import * as i4 from "@angular/cdk/overlay";
import * as i5 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 MAT_DATEPICKER_SCROLL_STRATEGY = new InjectionToken('mat-datepicker-scroll-strategy', {
providedIn: 'root',
factory: () => {
const overlay = inject(Overlay);
return () => overlay.scrollStrategies.reposition();
},
});
/** @docs-private */
export function MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY(overlay) {
return () => overlay.scrollStrategies.reposition();
}
/** @docs-private */
export const MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY_PROVIDER = {
provide: MAT_DATEPICKER_SCROLL_STRATEGY,
deps: [Overlay],
useFactory: MAT_DATEPICKER_SCROLL_STRATEGY_FACTORY,
};
/**
* 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 MatDatepickerContent {
constructor(_elementRef, _changeDetectorRef, _globalModel, _dateAdapter, _rangeSelectionStrategy, intl) {
this._elementRef = _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();
}
_handleUserSelection(event) {
const selection = this._model.selection;
const value = event.value;
const isRange = selection instanceof DateRange;
// 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 if (value &&
(isRange || !this._dateAdapter.sameDate(value, selection))) {
this._model.add(value);
}
// Delegate closing the overlay to the actions.
if ((!this._model || this._model.isComplete()) && !this._actionsPortal) {
this.datepicker.close();
}
}
_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() {
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();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: MatDatepickerContent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i1.MatDateSelectionModel }, { token: i2.DateAdapter }, { token: MAT_DATE_RANGE_SELECTION_STRATEGY, optional: true }, { token: i3.MatDatepickerIntl }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.0", type: MatDatepickerContent, isStandalone: true, selector: "mat-datepicker-content", inputs: { color: "color" }, host: { listeners: { "@transformPanel.start": "_handleAnimationEvent($event)", "@transformPanel.done": "_handleAnimationEvent($event)" }, properties: { "class": "color ? \"mat-\" + color : \"\"", "@transformPanel": "_animationState", "class.mat-datepicker-content-touch": "datepicker.touchUi" }, classAttribute: "mat-datepicker-content" }, viewQueries: [{ propertyName: "_calendar", first: true, predicate: MatCalendar, descendants: true }], exportAs: ["matDatepickerContent"], ngImport: i0, template: "<div\n cdkTrapFocus\n role=\"dialog\"\n [attr.aria-modal]=\"true\"\n [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 <mat-calendar\n [id]=\"datepicker.id\"\n [class]=\"datepicker.panelClass\"\n [startAt]=\"datepicker.startAt\"\n [startView]=\"datepicker.startView\"\n [minDate]=\"datepicker._getMinDate()\"\n [maxDate]=\"datepicker._getMaxDate()\"\n [dateFilter]=\"datepicker._getDateFilter()\"\n [headerComponent]=\"datepicker.calendarHeaderComponent\"\n [selected]=\"_getSelected()\"\n [dateClass]=\"datepicker.dateClass\"\n [comparisonStart]=\"comparisonStart\"\n [comparisonEnd]=\"comparisonEnd\"\n [@fadeInCalendar]=\"'enter'\"\n [startDateAccessibleName]=\"startDateAccessibleName\"\n [endDateAccessibleName]=\"endDateAccessibleName\"\n (yearSelected)=\"datepicker._selectYear($event)\"\n (monthSelected)=\"datepicker._selectMonth($event)\"\n (viewChanged)=\"datepicker._viewChanged($event)\"\n (_userSelection)=\"_handleUserSelection($event)\"\n (_userDragDrop)=\"_handleUserDragDrop($event)\"></mat-calendar>\n\n <ng-template [cdkPortalOutlet]=\"_actionsPortal\"></ng-template>\n\n <!-- Invisible close button for screen reader users. -->\n <button\n type=\"button\"\n mat-raised-button\n [color]=\"color || 'primary'\"\n class=\"mat-datepicker-close-button\"\n [class.cdk-visually-hidden]=\"!_closeButtonFocused\"\n (focus)=\"_closeButtonFocused = true\"\n (blur)=\"_closeButtonFocused = false\"\n (click)=\"datepicker.close()\">{{ _closeButtonText }}</button>\n</div>\n", styles: [".mat-datepicker-content{display:block;border-radius:4px;background-color:var(--mat-datepicker-calendar-container-background-color);color:var(--mat-datepicker-calendar-container-text-color);box-shadow:var(--mat-datepicker-calendar-container-elevation-shadow);border-radius:var(--mat-datepicker-calendar-container-shape)}.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}.mat-datepicker-content-touch{display:block;max-height:80vh;box-shadow:var(--mat-datepicker-calendar-container-touch-elevation-shadow);border-radius:var(--mat-datepicker-calendar-container-touch-shape);position:relative;overflow:visible}.mat-datepicker-content-touch .mat-datepicker-content-container{min-height:312px;max-height:788px;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:80vh}}@media all and (orientation: portrait){.mat-datepicker-content-touch .mat-datepicker-content-container{width:80vw;height:100vw}.mat-datepicker-content-touch .mat-datepicker-content-container-with-actions{height:115vw}}"], dependencies: [{ kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "component", type: MatCalendar, selector: "mat-calendar", inputs: ["headerComponent", "startAt", "startView", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedChange", "yearSelected", "monthSelected", "viewChanged", "_userSelection", "_userDragDrop"], exportAs: ["matCalendar"] }, { kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }], animations: [matDatepickerAnimations.transformPanel, matDatepickerAnimations.fadeInCalendar], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: MatDatepickerContent, decorators: [{
type: Component,
args: [{ selector: 'mat-datepicker-content', host: {
'class': 'mat-datepicker-content',
'[class]': 'color ? "mat-" + color : ""',
'[@transformPanel]': '_animationState',
'(@transformPanel.start)': '_handleAnimationEvent($event)',
'(@transformPanel.done)': '_handleAnimationEvent($event)',
'[class.mat-datepicker-content-touch]': 'datepicker.touchUi',
}, animations: [matDatepickerAnimations.transformPanel, matDatepickerAnimations.fadeInCalendar], exportAs: 'matDatepickerContent', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CdkTrapFocus, MatCalendar, CdkPortalOutlet, MatButton], template: "<div\n cdkTrapFocus\n role=\"dialog\"\n [attr.aria-modal]=\"true\"\n [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 <mat-calendar\n [id]=\"datepicker.id\"\n [class]=\"datepicker.panelClass\"\n [startAt]=\"datepicker.startAt\"\n [startView]=\"datepicker.startView\"\n [minDate]=\"datepicker._getMinDate()\"\n [maxDate]=\"datepicker._getMaxDate()\"\n [dateFilter]=\"datepicker._getDateFilter()\"\n [headerComponent]=\"datepicker.calendarHeaderComponent\"\n [selected]=\"_getSelected()\"\n [dateClass]=\"datepicker.dateClass\"\n [comparisonStart]=\"comparisonStart\"\n [comparisonEnd]=\"comparisonEnd\"\n [@fadeInCalendar]=\"'enter'\"\n [startDateAccessibleName]=\"startDateAccessibleName\"\n [endDateAccessibleName]=\"endDateAccessibleName\"\n (yearSelected)=\"datepicker._selectYear($event)\"\n (monthSelected)=\"datepicker._selectMonth($event)\"\n (viewChanged)=\"datepicker._viewChanged($event)\"\n (_userSelection)=\"_handleUserSelection($event)\"\n (_userDragDrop)=\"_handleUserDragDrop($event)\"></mat-calendar>\n\n <ng-template [cdkPortalOutlet]=\"_actionsPortal\"></ng-template>\n\n <!-- Invisible close button for screen reader users. -->\n <button\n type=\"button\"\n mat-raised-button\n [color]=\"color || 'primary'\"\n class=\"mat-datepicker-close-button\"\n [class.cdk-visually-hidden]=\"!_closeButtonFocused\"\n (focus)=\"_closeButtonFocused = true\"\n (blur)=\"_closeButtonFocused = false\"\n (click)=\"datepicker.close()\">{{ _closeButtonText }}</button>\n</div>\n", styles: [".mat-datepicker-content{display:block;border-radius:4px;background-color:var(--mat-datepicker-calendar-container-background-color);color:var(--mat-datepicker-calendar-container-text-color);box-shadow:var(--mat-datepicker-calendar-container-elevation-shadow);border-radius:var(--mat-datepicker-calendar-container-shape)}.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}.mat-datepicker-content-touch{display:block;max-height:80vh;box-shadow:var(--mat-datepicker-calendar-container-touch-elevation-shadow);border-radius:var(--mat-datepicker-calendar-container-touch-shape);position:relative;overflow:visible}.mat-datepicker-content-touch .mat-datepicker-content-container{min-height:312px;max-height:788px;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:80vh}}@media all and (orientation: portrait){.mat-datepicker-content-touch .mat-datepicker-content-container{width:80vw;height:100vw}.mat-datepicker-content-touch .mat-datepicker-content-container-with-actions{height:115vw}}"] }]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i1.MatDateSelectionModel }, { type: i2.DateAdapter }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [MAT_DATE_RANGE_SELECTION_STRATEGY]
}] }, { type: i3.MatDatepickerIntl }], propDecorators: { _calendar: [{
type: ViewChild,
args: [MatCalendar]
}], color: [{
type: Input
}] } });
/** Base class for a datepicker. */
export class MatDatepickerBase {
/** 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 datepicker pop-up should be disabled. */
get disabled() {
return this._disabled === undefined && this.datepickerInput
? this.datepickerInput.disabled
: !!this._disabled;
}
set disabled(value) {
if (value !== this._disabled) {
this._disabled = value;
this.stateChanges.next(undefined);
}
}
/** Classes to be passed to the date picker panel. */
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) {
if (value) {
this.open();
}
else {
this.close();
}
}
/** 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,
/**
* @deprecated parameter is unused and will be removed
* @breaking-change 19.0.0
*/
_unusedNgZone, _viewContainerRef, scrollStrategy, _dateAdapter, _dir, _model) {
this._overlay = _overlay;
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';
/**
* 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.
*/
this.touchUi = 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';
/**
* 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.
*/
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;
/** 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();
this._injector = inject(Injector);
if (!this._dateAdapter && (typeof ngDevMode === 'undefined' || ngDevMode)) {
throw createMissingDateImplError('DateAdapter');
}
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 && (typeof ngDevMode === 'undefined' || ngDevMode)) {
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 && (typeof ngDevMode === 'undefined' || ngDevMode)) {
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 && (typeof ngDevMode === 'undefined' || ngDevMode)) {
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(MatDatepickerContent, 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) {
afterNextRender(() => {
overlayRef.updatePosition();
}, { injector: this._injector });
}
}
/** 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))));
})));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: MatDatepickerBase, deps: [{ token: i4.Overlay }, { token: i0.NgZone }, { token: i0.ViewContainerRef }, { token: MAT_DATEPICKER_SCROLL_STRATEGY }, { token: i2.DateAdapter, optional: true }, { token: i5.Directionality, optional: true }, { token: i1.MatDateSelectionModel }], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "18.0.0", type: MatDatepickerBase, inputs: { calendarHeaderComponent: "calendarHeaderComponent", startAt: "startAt", startView: "startView", color: "color", touchUi: ["touchUi", "touchUi", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], xPosition: "xPosition", yPosition: "yPosition", restoreFocus: ["restoreFocus", "restoreFocus", booleanAttribute], dateClass: "dateClass", panelClass: "panelClass", opened: ["opened", "opened", booleanAttribute] }, outputs: { yearSelected: "yearSelected", monthSelected: "monthSelected", viewChanged: "viewChanged", openedStream: "opened", closedStream: "closed" }, usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: MatDatepickerBase, decorators: [{
type: Directive
}], ctorParameters: () => [{ type: i4.Overlay }, { type: i0.NgZone }, { type: i0.ViewContainerRef }, { type: undefined, decorators: [{
type: Inject,
args: [MAT_DATEPICKER_SCROLL_STRATEGY]
}] }, { type: i2.DateAdapter, decorators: [{
type: Optional
}] }, { type: i5.Directionality, decorators: [{
type: Optional
}] }, { type: i1.MatDateSelectionModel }], propDecorators: { calendarHeaderComponent: [{
type: Input
}], startAt: [{
type: Input
}], startView: [{
type: Input
}], color: [{
type: Input
}], touchUi: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], disabled: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], xPosition: [{
type: Input
}], yPosition: [{
type: Input
}], restoreFocus: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], 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,
args: [{ transform: booleanAttribute }]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZXBpY2tlci1iYXNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21hdGVyaWFsL2RhdGVwaWNrZXIvZGF0ZXBpY2tlci1iYXNlLnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21hdGVyaWFsL2RhdGVwaWNrZXIvZGF0ZXBpY2tlci1jb250ZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBRUgsT0FBTyxFQUFDLGNBQWMsRUFBQyxNQUFNLG1CQUFtQixDQUFDO0FBQ2pELE9BQU8sRUFBQyxpQkFBaUIsRUFBQyxNQUFNLHVCQUF1QixDQUFDO0FBQ3hELE9BQU8sRUFDTCxVQUFVLEVBQ1YsTUFBTSxFQUNOLGNBQWMsRUFDZCxVQUFVLEVBRVYsU0FBUyxFQUNULE9BQU8sRUFDUCxXQUFXLEVBQ1gsUUFBUSxHQUNULE1BQU0sdUJBQXVCLENBQUM7QUFDL0IsT0FBTyxFQUNMLE9BQU8sRUFDUCxhQUFhLEVBR2IsaUNBQWlDLEdBQ2xDLE1BQU0sc0JBQXNCLENBQUM7QUFDOUIsT0FBTyxFQUFDLGVBQWUsRUFBRSxlQUFlLEVBQWdDLE1BQU0scUJBQXFCLENBQUM7QUFDcEcsT0FBTyxFQUVMLHVCQUF1QixFQUN2QixTQUFTLEVBRVQsVUFBVSxFQUNWLFlBQVksRUFDWixNQUFNLEVBQ04sY0FBYyxFQUNkLEtBQUssRUFDTCxNQUFNLEVBRU4sUUFBUSxFQUNSLE1BQU0sRUFDTixTQUFTLEVBQ1QsZ0JBQWdCLEVBQ2hCLGlCQUFpQixFQUNqQixpQkFBaUIsRUFDakIsU0FBUyxFQUlULE1BQU0sRUFDTixnQkFBZ0IsRUFDaEIsZUFBZSxFQUNmLFFBQVEsR0FDVCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUMsV0FBVyxFQUFlLE1BQU0sd0JBQXdCLENBQUM7QUFFakUsT0FBTyxFQUFDLEtBQUssRUFBRSxPQUFPLEVBQWMsWUFBWSxFQUFDLE1BQU0sTUFBTSxDQUFDO0FB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