Skip to content

Commit

Permalink
fixup! fix(material/snack-bar): switch away from animations module
Browse files Browse the repository at this point in the history
  • Loading branch information
crisbeto committed Jan 24, 2025
1 parent 06bff69 commit 399ca44
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 30 deletions.
63 changes: 39 additions & 24 deletions src/material/snack-bar/snack-bar-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import {
ANIMATION_MODULE_TYPE,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ComponentRef,
DoCheck,
ElementRef,
EmbeddedViewRef,
inject,
Expand Down Expand Up @@ -53,16 +55,18 @@ import {MatSnackBarConfig} from './snack-bar-config';
'(animationend)': '_animationDone($event)',
},
})
export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy {
export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy, DoCheck {
private _ngZone = inject(NgZone);
private _elementRef = inject<ElementRef<HTMLElement>>(ElementRef);
private _platform = inject(Platform);
private _changeDetectorRef = inject(ChangeDetectorRef);
private _enterFallback: ReturnType<typeof setTimeout> | undefined;
private _exitFallback: ReturnType<typeof setTimeout> | undefined;
protected _animationsDisabled =
inject(ANIMATION_MODULE_TYPE, {optional: true}) === 'NoopAnimations';
snackBarConfig = inject(MatSnackBarConfig);
private _scheduleDelayedEnter: boolean;

snackBarConfig = inject(MatSnackBarConfig);
private _document = inject(DOCUMENT);
private _trackedModals = new Set<Element>();

Expand Down Expand Up @@ -174,15 +178,23 @@ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy
/** Begin animation of snack bar entrance into view. */
enter(): void {
if (!this._destroyed) {
// Previously this was used to ensure that the change-detection-based animation runs.
// Now the animation doesn't require change detection, but there seem to be some internal
// usages depending on it.
this._changeDetectorRef.markForCheck();
this._changeDetectorRef.detectChanges();
this._screenReaderAnnounce();

if (this._animationsDisabled) {
this._completeEnter();
// Delay the enter until the next change detection in an attempt to mimic the timing of
// the old animation-based events. Ideally we would use an `afterNextRender` here, but
// some tests throw a "Injector has already been destroyed" error.
this._scheduleDelayedEnter = true;
} else {
// Guarantees that the animation-related events will
// fire even if something interrupts the animation.
clearTimeout(this._enterFallback);
this._enterFallback = setTimeout(this._completeEnter, 200);
this._enterFallback = setTimeout(() => this._completeEnter(), 200);
}
}
}
Expand All @@ -194,51 +206,54 @@ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy
// test harness.
this._elementRef.nativeElement.setAttribute('mat-exit', '');

// If the snack bar hasn't been announced by the time it exits it wouldn't have been open
// long enough to visually read it either, so clear the timeout for announcing.
clearTimeout(this._announceTimeoutId);

if (this._animationsDisabled) {
// It's common for snack bars to be opened by random outside calls like HTTP requests or
// errors. Run inside the NgZone to ensure that it functions correctly.
this._ngZone.run(this._completeExit);
} else {
// Guarantees that the animation-related events will
// fire even if something interrupts the animation.
clearTimeout(this._exitFallback);
this._exitFallback = setTimeout(this._completeExit, 150);
}
// Guarantees that the animation-related events will
// fire even if something interrupts the animation.
clearTimeout(this._exitFallback);
this._exitFallback = setTimeout(
() => this._completeExit(),
this._animationsDisabled ? undefined : 150,
);

return this._onExit;
}

/** Makes sure the exit callbacks have been invoked when the element is destroyed. */
ngDoCheck(): void {
if (this._scheduleDelayedEnter) {
this._scheduleDelayedEnter = false;
this._completeEnter();
}
}

ngOnDestroy() {
clearTimeout(this._enterFallback);
this._destroyed = true;
this._clearFromModals();
this._completeExit();
this._onAnnounce.complete();
}

private _completeEnter = () => {
private _completeEnter() {
clearTimeout(this._enterFallback);
this._ngZone.run(() => {
this._onEnter.next();
this._onEnter.complete();
});
};
}

/**
* Removes the element in a microtask. Helps prevent errors where we end up
* removing an element which is in the middle of an animation.
*/
private _completeExit = () => {
private _completeExit() {
// If the snack bar hasn't been announced by the time it exits it wouldn't have been open
// long enough to visually read it either, so clear the timeout for announcing.
clearTimeout(this._announceTimeoutId);
clearTimeout(this._exitFallback);
queueMicrotask(() => {
this._ngZone.run(() => {
this._onExit.next();
this._onExit.complete();
});
};
}

/**
* Called after the portal contents have been attached. Can be
Expand Down
10 changes: 5 additions & 5 deletions src/material/snack-bar/snack-bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,6 @@ export class MatSnackBar implements OnDestroy {
}
});

// If a dismiss timeout is provided, set up dismiss based on after the snackbar is opened.
if (config.duration && config.duration > 0) {
snackBarRef.afterOpened().subscribe(() => snackBarRef._dismissAfter(config.duration!));
}

if (this._openedSnackBarRef) {
// If a snack bar is already in view, dismiss it and enter the
// new snack bar after exit animation is complete.
Expand All @@ -258,6 +253,11 @@ export class MatSnackBar implements OnDestroy {
// If no snack bar is in view, enter the new snack bar.
snackBarRef.containerInstance.enter();
}

// If a dismiss timeout is provided, set up dismiss based on after the snackbar is opened.
if (config.duration && config.duration > 0) {
snackBarRef.afterOpened().subscribe(() => snackBarRef._dismissAfter(config.duration!));
}
}

/**
Expand Down
6 changes: 5 additions & 1 deletion tools/public_api_guard/material/snack-bar.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ComponentPortal } from '@angular/cdk/portal';
import { ComponentRef } from '@angular/core';
import { ComponentType } from '@angular/cdk/overlay';
import { Direction } from '@angular/cdk/bidi';
import { DoCheck } from '@angular/core';
import { DomPortal } from '@angular/cdk/portal';
import { ElementRef } from '@angular/core';
import { EmbeddedViewRef } from '@angular/core';
Expand Down Expand Up @@ -93,7 +94,7 @@ export class MatSnackBarConfig<D = any> {
}

// @public
export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy {
export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy, DoCheck {
constructor(...args: unknown[]);
protected _animationDone(event: AnimationEvent): void;
// (undocumented)
Expand All @@ -107,6 +108,9 @@ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy
_label: ElementRef;
_live: AriaLivePoliteness;
readonly _liveElementId: string;
// (undocumented)
ngDoCheck(): void;
// (undocumented)
ngOnDestroy(): void;
readonly _onAnnounce: Subject<void>;
readonly _onEnter: Subject<void>;
Expand Down

0 comments on commit 399ca44

Please # to comment.