Skip to content

Commit

Permalink
fix(cdk/menu): avoid resetting the scroll position when using the mou…
Browse files Browse the repository at this point in the history
…se (#30249)

The CDK menu has some logic that forwards focus to the first item when the host is focused. The problem is that every time the user clicks on the scrollbar, they blur the current item and focus the menu which then forwards focus back to the first item which in turn causes the scroll position to jump to the top.

These changes add some logic to not forward focus when focus comes from a mouse interaction.

Fixes #30130.
  • Loading branch information
crisbeto authored Jan 3, 2025
1 parent d721f0c commit 2be0afc
Showing 1 changed file with 18 additions and 2 deletions.
20 changes: 18 additions & 2 deletions src/cdk/menu/menu-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {_IdGenerator, FocusKeyManager, FocusOrigin} from '@angular/cdk/a11y';
import {_IdGenerator, FocusKeyManager, FocusMonitor, FocusOrigin} from '@angular/cdk/a11y';
import {Directionality} from '@angular/cdk/bidi';
import {
AfterContentInit,
Expand Down Expand Up @@ -43,7 +43,6 @@ import {PointerFocusTracker} from './pointer-focus-tracker';
'[id]': 'id',
'[attr.aria-orientation]': 'orientation',
'[attr.data-cdk-menu-stack-id]': 'menuStack.id',
'(focus)': 'focusFirstItem()',
'(focusin)': 'menuStack.setHasFocus(true)',
'(focusout)': 'menuStack.setHasFocus(false)',
},
Expand All @@ -52,6 +51,7 @@ export abstract class CdkMenuBase
extends CdkMenuGroup
implements Menu, AfterContentInit, OnDestroy
{
private _focusMonitor = inject(FocusMonitor);
protected ngZone = inject(NgZone);
private _renderer = inject(Renderer2);

Expand Down Expand Up @@ -108,13 +108,15 @@ export abstract class CdkMenuBase
this.menuStack.push(this);
}
this._setKeyManager();
this._handleFocus();
this._subscribeToMenuStackHasFocus();
this._subscribeToMenuOpen();
this._subscribeToMenuStackClosed();
this._setUpPointerTracker();
}

ngOnDestroy() {
this._focusMonitor.stopMonitoring(this.nativeElement);
this.keyManager?.destroy();
this.destroyed.next();
this.destroyed.complete();
Expand Down Expand Up @@ -231,4 +233,18 @@ export abstract class CdkMenuBase
this.menuAim.initialize(this, this.pointerTracker!);
}
}

/** Handles focus landing on the host element of the menu. */
private _handleFocus() {
this._focusMonitor
.monitor(this.nativeElement, false)
.pipe(takeUntil(this.destroyed))
.subscribe(origin => {
// Don't forward focus on mouse interactions, because it can
// mess with the user's scroll position. See #30130.
if (origin !== null && origin !== 'mouse') {
this.focusFirstItem(origin);
}
});
}
}

0 comments on commit 2be0afc

Please sign in to comment.