(isOpen: boolean)
| 472 | } |
| 473 | |
| 474 | _setIsOpen(isOpen: boolean) { |
| 475 | this._panelAnimationState = isOpen ? 'enter' : 'void'; |
| 476 | |
| 477 | if (isOpen) { |
| 478 | if (this._keyManager.activeItemIndex === 0) { |
| 479 | // Scroll the content element to the top as soon as the animation starts. This is necessary, |
| 480 | // because we move focus to the first item while it's still being animated, which can throw |
| 481 | // the browser off when it determines the scroll position. Alternatively we can move focus |
| 482 | // when the animation is done, however moving focus asynchronously will interrupt screen |
| 483 | // readers which are in the process of reading out the menu already. We take the `element` |
| 484 | // from the `event` since we can't use a `ViewChild` to access the pane. |
| 485 | const menuPanel = this._resolvePanel(); |
| 486 | |
| 487 | if (menuPanel) { |
| 488 | menuPanel.scrollTop = 0; |
| 489 | } |
| 490 | } |
| 491 | } else if (!this._animationsDisabled) { |
| 492 | // Some apps do `* { animation: none !important; }` in tests which will prevent the |
| 493 | // `animationend` event from firing. Since the exit animation is loading-bearing for |
| 494 | // removing the content from the DOM, add a fallback timer. |
| 495 | this._exitFallbackTimeout = setTimeout(() => this._onAnimationDone(EXIT_ANIMATION), 200); |
| 496 | } |
| 497 | |
| 498 | // Animation events won't fire when animations are disabled so we simulate them. |
| 499 | if (this._animationsDisabled) { |
| 500 | setTimeout(() => { |
| 501 | this._onAnimationDone(isOpen ? ENTER_ANIMATION : EXIT_ANIMATION); |
| 502 | }); |
| 503 | } |
| 504 | |
| 505 | this._changeDetectorRef.markForCheck(); |
| 506 | } |
| 507 | |
| 508 | /** |
| 509 | * Sets up a stream that will keep track of any newly-added menu items and will update the list |
no test coverage detected