From bc91c154111743d9cb7e589999268148a1bb5024 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 15 May 2025 09:09:41 +0200 Subject: [PATCH] fix(material/datepicker): prevent calendar from stealing away focus An earlier change added some logic to the calendar to re-focus the active date if an input requiring a full re-render changes which can potentially cause focus to be lost. The problem with this is that if the calendar is used on its own, it might steal away focus even though the user wasn't interacting with it. These changes add a check that focus is inside the calendar before attempting to recapture it. Fixes #30635. --- src/material/datepicker/calendar.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/material/datepicker/calendar.ts b/src/material/datepicker/calendar.ts index 48eaf22b7de7..e0c8d7467aa9 100644 --- a/src/material/datepicker/calendar.ts +++ b/src/material/datepicker/calendar.ts @@ -13,6 +13,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + ElementRef, EventEmitter, Input, OnChanges, @@ -41,6 +42,7 @@ import {MAT_SINGLE_DATE_SELECTION_MODEL_PROVIDER, DateRange} from './date-select import {MatIconButton, MatButton} from '../button'; import {_IdGenerator, CdkMonitorFocus} from '@angular/cdk/a11y'; import {_CdkPrivateStyleLoader, _VisuallyHiddenLoader} from '@angular/cdk/private'; +import {_getFocusedElementPierceShadowDom} from '@angular/cdk/platform'; /** * Possible views for the calendar. @@ -241,6 +243,7 @@ export class MatCalendar implements AfterContentInit, AfterViewChecked, OnDes private _dateAdapter = inject>(DateAdapter, {optional: true})!; private _dateFormats = inject(MAT_DATE_FORMATS, {optional: true}); private _changeDetectorRef = inject(ChangeDetectorRef); + private _elementRef = inject>(ElementRef); /** An input indicating the type of the header component, if set. */ @Input() headerComponent: ComponentType; @@ -457,9 +460,12 @@ export class MatCalendar implements AfterContentInit, AfterViewChecked, OnDes const view = this._getCurrentViewComponent(); if (view) { - // Schedule focus to be moved to the active date since re-rendering - // can blur the active cell. See #29265. - this._moveFocusOnNextTick = true; + // Schedule focus to be moved to the active date since re-rendering can blur the active + // cell (see #29265), however don't do so if focus is outside of the calendar, because it + // can steal away the user's attention (see #30635). + if (this._elementRef.nativeElement.contains(_getFocusedElementPierceShadowDom())) { + this._moveFocusOnNextTick = true; + } // We need to `detectChanges` manually here, because the `minDate`, `maxDate` etc. are // passed down to the view via data bindings which won't be up-to-date when we call `_init`.