Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(material/bottom-sheet): switch away from animations module #30402

Merged
merged 1 commit into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/material/bottom-sheet/bottom-sheet-animations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ import {
query,
animateChild,
} from '@angular/animations';
import {AnimationCurves, AnimationDurations} from '@angular/material/core';

/** Animations used by the Material bottom sheet. */
/**
* Animations used by the Material bottom sheet.
* @deprecated No longer used. Will be removed.
* @breaking-change 21.0.0
*/
export const matBottomSheetAnimations: {
readonly bottomSheetState: AnimationTriggerMetadata;
} = {
Expand All @@ -29,14 +32,14 @@ export const matBottomSheetAnimations: {
transition(
'visible => void, visible => hidden',
group([
animate(`${AnimationDurations.COMPLEX} ${AnimationCurves.ACCELERATION_CURVE}`),
animate('375ms cubic-bezier(0.4, 0, 1, 1)'),
query('@*', animateChild(), {optional: true}),
]),
),
transition(
'void => visible',
group([
animate(`${AnimationDurations.EXITING} ${AnimationCurves.DECELERATION_CURVE}`),
animate('195ms cubic-bezier(0, 0, 0.2, 1)'),
query('@*', animateChild(), {optional: true}),
]),
),
Expand Down
36 changes: 36 additions & 0 deletions src/material/bottom-sheet/bottom-sheet-container.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ $_width-increment: 64px;
$container-vertical-padding: 8px !default;
$container-horizontal-padding: 16px !default;

@keyframes _mat-bottom-sheet-enter {
from {
transform: translateY(100%);
}

to {
transform: none;
}
}

@keyframes _mat-bottom-sheet-exit {
from {
transform: none;
}

to {
transform: translateY(100%);
}
}

.mat-bottom-sheet-container {
@include elevation.elevation(16);
padding: $container-vertical-padding
Expand All @@ -21,6 +41,10 @@ $container-horizontal-padding: 16px !default;
max-height: 80vh;
overflow: auto;

// We don't use this, but it's useful for consumers to position
// elements (e.g. close buttons) inside the bottom sheet.
position: relative;

@include token-utils.use-tokens(
tokens-mat-bottom-sheet.$prefix, tokens-mat-bottom-sheet.get-token-slots()) {
@include token-utils.create-token-slot(background, container-background-color);
Expand All @@ -37,6 +61,18 @@ $container-horizontal-padding: 16px !default;
}
}

.mat-bottom-sheet-container-animations-enabled {
transform: translateY(100%);

&.mat-bottom-sheet-container-enter {
animation: _mat-bottom-sheet-enter 195ms cubic-bezier(0, 0, 0.2, 1) forwards;
}

&.mat-bottom-sheet-container-exit {
animation: _mat-bottom-sheet-exit 375ms cubic-bezier(0.4, 0, 1, 1) backwards;
}
}

// Applies a border radius to the bottom sheet. Should only be applied when it's not full-screen.
%_mat-bottom-sheet-container-border-radius {
@include token-utils.use-tokens(
Expand Down
55 changes: 41 additions & 14 deletions src/material/bottom-sheet/bottom-sheet-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.dev/license
*/

import {AnimationEvent} from '@angular/animations';
import {CdkDialogContainer} from '@angular/cdk/dialog';
import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';
import {
ANIMATION_MODULE_TYPE,
ChangeDetectionStrategy,
Component,
EventEmitter,
Expand All @@ -18,9 +18,11 @@ import {
inject,
} from '@angular/core';
import {Subscription} from 'rxjs';
import {matBottomSheetAnimations} from './bottom-sheet-animations';
import {CdkPortalOutlet} from '@angular/cdk/portal';

const ENTER_ANIMATION = '_mat-bottom-sheet-enter';
const EXIT_ANIMATION = '_mat-bottom-sheet-exit';

/**
* Internal component that wraps user-provided bottom sheet content.
* @docs-private
Expand All @@ -35,27 +37,34 @@ import {CdkPortalOutlet} from '@angular/cdk/portal';
// tslint:disable-next-line:validate-decorators
changeDetection: ChangeDetectionStrategy.Default,
encapsulation: ViewEncapsulation.None,
animations: [matBottomSheetAnimations.bottomSheetState],
host: {
'class': 'mat-bottom-sheet-container',
'[class.mat-bottom-sheet-container-animations-enabled]': '!_animationsDisabled',
'[class.mat-bottom-sheet-container-enter]': '_animationState === "visible"',
'[class.mat-bottom-sheet-container-exit]': '_animationState === "hidden"',
'tabindex': '-1',
'[attr.role]': '_config.role',
'[attr.aria-modal]': '_config.ariaModal',
'[attr.aria-label]': '_config.ariaLabel',
'[@state]': '_animationState',
'(@state.start)': '_onAnimationStart($event)',
'(@state.done)': '_onAnimationDone($event)',
'(animationstart)': '_handleAnimationEvent(true, $event.animationName)',
'(animationend)': '_handleAnimationEvent(false, $event.animationName)',
'(animationcancel)': '_handleAnimationEvent(false, $event.animationName)',
},
imports: [CdkPortalOutlet],
})
export class MatBottomSheetContainer extends CdkDialogContainer implements OnDestroy {
private _breakpointSubscription: Subscription;
protected _animationsDisabled =
inject(ANIMATION_MODULE_TYPE, {optional: true}) === 'NoopAnimations';

/** The state of the bottom sheet animations. */
_animationState: 'void' | 'visible' | 'hidden' = 'void';

/** Emits whenever the state of the animation changes. */
_animationStateChanged = new EventEmitter<AnimationEvent>();
_animationStateChanged = new EventEmitter<{
toState: 'visible' | 'hidden';
phase: 'start' | 'done';
}>();

/** Whether the component has been destroyed. */
private _destroyed: boolean;
Expand Down Expand Up @@ -93,14 +102,21 @@ export class MatBottomSheetContainer extends CdkDialogContainer implements OnDes
this._animationState = 'visible';
this._changeDetectorRef.markForCheck();
this._changeDetectorRef.detectChanges();
if (this._animationsDisabled) {
this._simulateAnimation(ENTER_ANIMATION);
}
}
}

/** Begin animation of the bottom sheet exiting from view. */
exit(): void {
if (!this._destroyed) {
this._elementRef.nativeElement.setAttribute('mat-exit', '');
this._animationState = 'hidden';
this._changeDetectorRef.markForCheck();
if (this._animationsDisabled) {
this._simulateAnimation(EXIT_ANIMATION);
}
}
}

Expand All @@ -110,16 +126,27 @@ export class MatBottomSheetContainer extends CdkDialogContainer implements OnDes
this._destroyed = true;
}

_onAnimationDone(event: AnimationEvent) {
if (event.toState === 'visible') {
private _simulateAnimation(name: typeof ENTER_ANIMATION | typeof EXIT_ANIMATION) {
this._ngZone.run(() => {
this._handleAnimationEvent(true, name);
setTimeout(() => this._handleAnimationEvent(false, name));
});
}

protected _handleAnimationEvent(isStart: boolean, animationName: string) {
const isEnter = animationName === ENTER_ANIMATION;
const isExit = animationName === EXIT_ANIMATION;

if (isEnter) {
this._trapFocus();
}

this._animationStateChanged.emit(event);
}

_onAnimationStart(event: AnimationEvent) {
this._animationStateChanged.emit(event);
if (isEnter || isExit) {
this._animationStateChanged.emit({
toState: isEnter ? 'visible' : 'hidden',
phase: isStart ? 'start' : 'done',
});
}
}

protected override _captureInitialFocus(): void {}
Expand Down
13 changes: 5 additions & 8 deletions src/material/bottom-sheet/bottom-sheet-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class MatBottomSheetRef<T = any, R = any> {
// Emit when opening animation completes
containerInstance._animationStateChanged
.pipe(
filter(event => event.phaseName === 'done' && event.toState === 'visible'),
filter(event => event.phase === 'done' && event.toState === 'visible'),
take(1),
)
.subscribe(() => {
Expand All @@ -71,7 +71,7 @@ export class MatBottomSheetRef<T = any, R = any> {
// Dispose overlay when closing animation is complete
containerInstance._animationStateChanged
.pipe(
filter(event => event.phaseName === 'done' && event.toState === 'hidden'),
filter(event => event.phase === 'done' && event.toState === 'hidden'),
take(1),
)
.subscribe(() => {
Expand Down Expand Up @@ -109,19 +109,16 @@ export class MatBottomSheetRef<T = any, R = any> {
// Transition the backdrop in parallel to the bottom sheet.
this.containerInstance._animationStateChanged
.pipe(
filter(event => event.phaseName === 'start'),
filter(event => event.phase === 'start'),
take(1),
)
.subscribe(event => {
.subscribe(() => {
// The logic that disposes of the overlay depends on the exit animation completing, however
// it isn't guaranteed if the parent view is destroyed while it's running. Add a fallback
// timeout which will clean everything up if the animation hasn't fired within the specified
// amount of time plus 100ms. We don't need to run this outside the NgZone, because for the
// vast majority of cases the timeout will have been cleared before it has fired.
this._closeFallbackTimeout = setTimeout(() => {
this._ref.close(this._result);
}, event.totalTime + 100);

this._closeFallbackTimeout = setTimeout(() => this._ref.close(this._result), 500);
this._ref.overlayRef.detachBackdrop();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {BottomSheetHarnessFilters} from './bottom-sheet-harness-filters';
export class MatBottomSheetHarness extends ContentContainerComponentHarness<string> {
// Developers can provide a custom component or template for the
// bottom sheet. The canonical parent is the ".mat-bottom-sheet-container".
static hostSelector = '.mat-bottom-sheet-container';
static hostSelector = '.mat-bottom-sheet-container:not([mat-exit])';

/**
* Gets a `HarnessPredicate` that can be used to search for a bottom sheet with
Expand Down
16 changes: 9 additions & 7 deletions tools/public_api_guard/material/bottom-sheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

```ts

import { AnimationEvent as AnimationEvent_2 } from '@angular/animations';
import { AnimationTriggerMetadata } from '@angular/animations';
import { CdkDialogContainer } from '@angular/cdk/dialog';
import { ComponentRef } from '@angular/core';
Expand Down Expand Up @@ -48,7 +47,7 @@ export class MatBottomSheet implements OnDestroy {
static ɵprov: i0.ɵɵInjectableDeclaration<MatBottomSheet>;
}

// @public
// @public @deprecated
export const matBottomSheetAnimations: {
readonly bottomSheetState: AnimationTriggerMetadata;
};
Expand Down Expand Up @@ -76,18 +75,21 @@ export class MatBottomSheetConfig<D = any> {
// @public
export class MatBottomSheetContainer extends CdkDialogContainer implements OnDestroy {
constructor(...args: unknown[]);
// (undocumented)
protected _animationsDisabled: boolean;
_animationState: 'void' | 'visible' | 'hidden';
_animationStateChanged: EventEmitter<AnimationEvent_2>;
_animationStateChanged: EventEmitter<{
toState: "visible" | "hidden";
phase: "start" | "done";
}>;
// (undocumented)
protected _captureInitialFocus(): void;
enter(): void;
exit(): void;
// (undocumented)
ngOnDestroy(): void;
protected _handleAnimationEvent(isStart: boolean, animationName: string): void;
// (undocumented)
_onAnimationDone(event: AnimationEvent_2): void;
// (undocumented)
_onAnimationStart(event: AnimationEvent_2): void;
ngOnDestroy(): void;
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration<MatBottomSheetContainer, "mat-bottom-sheet-container", never, {}, {}, never, never, true, never>;
// (undocumented)
Expand Down
Loading