| 109 | */ |
| 110 | @Service({autoProvided: false}) |
| 111 | export class TargetMenuAim implements MenuAim, OnDestroy { |
| 112 | private readonly _ngZone = inject(NgZone); |
| 113 | private readonly _renderer = inject(RendererFactory2).createRenderer(null, null); |
| 114 | private _cleanupMousemove: (() => void) | undefined; |
| 115 | |
| 116 | /** The last NUM_POINTS mouse move events. */ |
| 117 | private readonly _points: Point[] = []; |
| 118 | |
| 119 | /** Reference to the root menu in which we are tracking mouse moves. */ |
| 120 | private _menu!: Menu; |
| 121 | |
| 122 | /** Reference to the root menu's mouse manager. */ |
| 123 | private _pointerTracker!: PointerFocusTracker<Toggler & FocusableElement>; |
| 124 | |
| 125 | /** The id associated with the current timeout call waiting to resolve. */ |
| 126 | private _timeoutId: number | null = null; |
| 127 | |
| 128 | /** Emits when this service is destroyed. */ |
| 129 | private readonly _destroyed: Subject<void> = new Subject(); |
| 130 | |
| 131 | ngOnDestroy() { |
| 132 | this._cleanupMousemove?.(); |
| 133 | this._destroyed.next(); |
| 134 | this._destroyed.complete(); |
| 135 | } |
| 136 | |
| 137 | /** |
| 138 | * Set the Menu and its PointerFocusTracker. |
| 139 | * @param menu The menu that this menu aim service controls. |
| 140 | * @param pointerTracker The `PointerFocusTracker` for the given menu. |
| 141 | */ |
| 142 | initialize(menu: Menu, pointerTracker: PointerFocusTracker<FocusableElement & Toggler>) { |
| 143 | this._menu = menu; |
| 144 | this._pointerTracker = pointerTracker; |
| 145 | this._subscribeToMouseMoves(); |
| 146 | } |
| 147 | |
| 148 | /** |
| 149 | * Calls the `doToggle` callback when it is deemed that the user is not moving towards |
| 150 | * the submenu. |
| 151 | * @param doToggle the function called when the user is not moving towards the submenu. |
| 152 | */ |
| 153 | toggle(doToggle: () => void) { |
| 154 | // If the menu is horizontal the sub-menus open below and there is no risk of premature |
| 155 | // closing of any sub-menus therefore we automatically resolve the callback. |
| 156 | if (this._menu.orientation === 'horizontal') { |
| 157 | doToggle(); |
| 158 | } |
| 159 | |
| 160 | this._checkConfigured(); |
| 161 | |
| 162 | const siblingItemIsWaiting = !!this._timeoutId; |
| 163 | const hasPoints = this._points.length > 1; |
| 164 | |
| 165 | if (hasPoints && !siblingItemIsWaiting) { |
| 166 | if (this._isMovingToSubmenu()) { |
| 167 | this._startTimeout(doToggle); |
| 168 | } else { |
nothing calls this directly
no outgoing calls
no test coverage detected
searching dependent graphs…