@@ -11,7 +11,6 @@ import {
1111 EventEmitter ,
1212 Inject ,
1313 Injectable ,
14- InjectionToken ,
1514 Injector ,
1615 Input ,
1716 OnDestroy ,
@@ -27,6 +26,7 @@ import {
2726 Overlay ,
2827 OverlayConfig ,
2928 OverlayRef ,
29+ STANDARD_DROPDOWN_BELOW_POSITIONS ,
3030} from '@angular/cdk/overlay' ;
3131import { Portal , TemplatePortal } from '@angular/cdk/portal' ;
3232import { BooleanInput , coerceBooleanProperty } from '@angular/cdk/coercion' ;
@@ -36,6 +36,14 @@ import {MENU_STACK, MenuStack} from './menu-stack';
3636import { isClickInsideMenuOverlay } from './menu-item-trigger' ;
3737import { MENU_TRIGGER , MenuTrigger } from './menu-trigger' ;
3838
39+ // In cases where the first menu item in the context menu is a trigger the submenu opens on a
40+ // hover event. We offset the context menu 2px by default to prevent this from occurring.
41+ const CONTEXT_MENU_POSITIONS = STANDARD_DROPDOWN_BELOW_POSITIONS . map ( position => {
42+ const offsetX = position . overlayX === 'start' ? 2 : - 2 ;
43+ const offsetY = position . overlayY === 'top' ? 2 : - 2 ;
44+ return { ...position , offsetX, offsetY} ;
45+ } ) ;
46+
3947/** Tracks the last open context menu trigger across the entire application. */
4048@Injectable ( { providedIn : 'root' } )
4149export class ContextMenuTracker {
@@ -54,20 +62,6 @@ export class ContextMenuTracker {
5462 }
5563}
5664
57- /** Configuration options passed to the context menu. */
58- export type ContextMenuOptions = {
59- /** The opened menus X coordinate offset from the triggering position. */
60- offsetX : number ;
61-
62- /** The opened menus Y coordinate offset from the triggering position. */
63- offsetY : number ;
64- } ;
65-
66- /** Injection token for the ContextMenu options object. */
67- export const CDK_CONTEXT_MENU_DEFAULT_OPTIONS = new InjectionToken < ContextMenuOptions > (
68- 'cdk-context-menu-default-options' ,
69- ) ;
70-
7165/** The coordinates of where the context menu should open. */
7266export type ContextMenuCoordinates = { x : number ; y : number } ;
7367
@@ -83,9 +77,6 @@ export type ContextMenuCoordinates = {x: number; y: number};
8377 '(contextmenu)' : '_openOnContextMenu($event)' ,
8478 } ,
8579 providers : [
86- // In cases where the first menu item in the context menu is a trigger the submenu opens on a
87- // hover event. Offsetting the opened context menu by 2px prevents this from occurring.
88- { provide : CDK_CONTEXT_MENU_DEFAULT_OPTIONS , useValue : { offsetX : 2 , offsetY : 2 } } ,
8980 { provide : MENU_TRIGGER , useExisting : CdkContextMenuTrigger } ,
9081 { provide : MENU_STACK , useClass : MenuStack } ,
9182 ] ,
@@ -95,6 +86,9 @@ export class CdkContextMenuTrigger extends MenuTrigger implements OnDestroy {
9586 @Input ( 'cdkContextMenuTriggerFor' )
9687 private _menuTemplateRef : TemplateRef < unknown > ;
9788
89+ /** A list of preferred menu positions to be used when constructing the `FlexibleConnectedPositionStrategy` for this trigger's menu. */
90+ @Input ( 'cdkMenuPosition' ) menuPosition : ConnectedPosition [ ] ;
91+
9892 /** Emits when the attached menu is requested to open. */
9993 @Output ( 'cdkContextMenuOpened' ) readonly opened : EventEmitter < void > = new EventEmitter ( ) ;
10094
@@ -129,7 +123,6 @@ export class CdkContextMenuTrigger extends MenuTrigger implements OnDestroy {
129123 private readonly _overlay : Overlay ,
130124 private readonly _contextMenuTracker : ContextMenuTracker ,
131125 @Inject ( MENU_STACK ) menuStack : MenuStack ,
132- @Inject ( CDK_CONTEXT_MENU_DEFAULT_OPTIONS ) private readonly _options : ContextMenuOptions ,
133126 @Optional ( ) private readonly _directionality ?: Directionality ,
134127 ) {
135128 super ( injector , menuStack ) ;
@@ -233,22 +226,7 @@ export class CdkContextMenuTrigger extends MenuTrigger implements OnDestroy {
233226 return this . _overlay
234227 . position ( )
235228 . flexibleConnectedTo ( coordinates )
236- . withDefaultOffsetX ( this . _options . offsetX )
237- . withDefaultOffsetY ( this . _options . offsetY )
238- . withPositions ( this . _getOverlayPositions ( ) ) ;
239- }
240-
241- /**
242- * Determine and return where to position the opened menu relative to the mouse location.
243- */
244- private _getOverlayPositions ( ) : ConnectedPosition [ ] {
245- // TODO: this should be configurable through the injected context menu options
246- return [
247- { originX : 'end' , originY : 'top' , overlayX : 'start' , overlayY : 'top' } ,
248- { originX : 'start' , originY : 'top' , overlayX : 'end' , overlayY : 'top' } ,
249- { originX : 'end' , originY : 'bottom' , overlayX : 'start' , overlayY : 'bottom' } ,
250- { originX : 'start' , originY : 'bottom' , overlayX : 'end' , overlayY : 'bottom' } ,
251- ] ;
229+ . withPositions ( this . menuPosition ?? CONTEXT_MENU_POSITIONS ) ;
252230 }
253231
254232 /**
0 commit comments