Skip to content

Commit f31088b

Browse files
committed
fix(tooltip): decouple removal logic from change detection
Currently the logic in the tooltip that removes it from the DOM is run either if the trigger is destroyed or the exit animation has finished. The problem is that if the trigger is detached from change detection, but hasn't been destroyed, the exit animation will never run and the element won't be cleaned up. These changes switch to using CSS animations and manipulating the DOM node directly to trigger the animation. Fixes #19365.
1 parent fb814bb commit f31088b

File tree

6 files changed

+178
-128
lines changed

6 files changed

+178
-128
lines changed

src/material/tooltip/BUILD.bazel

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ ng_module(
2929
"//src/cdk/portal",
3030
"//src/cdk/scrolling",
3131
"//src/material/core",
32-
"@npm//@angular/animations",
3332
"@npm//@angular/common",
3433
"@npm//@angular/core",
3534
"@npm//rxjs",
@@ -65,7 +64,6 @@ ng_test_library(
6564
"//src/cdk/overlay",
6665
"//src/cdk/platform",
6766
"//src/cdk/testing/private",
68-
"@npm//@angular/animations",
6967
"@npm//@angular/platform-browser",
7068
],
7169
)

src/material/tooltip/tooltip.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
<div class="mat-tooltip"
1+
<div #tooltip
2+
class="mat-tooltip"
23
[ngClass]="tooltipClass"
4+
[class._mat-animation-noopable]="_animationMode === 'NoopAnimations'"
35
[class.mat-tooltip-handset]="(_isHandset | async)?.matches"
4-
[@state]="_visibility"
5-
(@state.start)="_animationStart()"
6-
(@state.done)="_animationDone($event)">{{message}}</div>
6+
(animationend)="_animationEnd($event)">{{message}}</div>

src/material/tooltip/tooltip.scss

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ $mat-tooltip-handset-margin: 24px;
2424
padding-right: $mat-tooltip-horizontal-padding;
2525
overflow: hidden;
2626
text-overflow: ellipsis;
27+
opacity: 0;
28+
transform: scale(0);
29+
30+
// Use a very short animation if animations are disabled so the `animationend` event still fires.
31+
&._mat-animation-noopable {
32+
animation-duration: 1ms;
33+
}
2734

2835
@include cdk-high-contrast(active, off) {
2936
outline: solid 1px;
@@ -35,3 +42,38 @@ $mat-tooltip-handset-margin: 24px;
3542
padding-left: $mat-tooltip-handset-horizontal-padding;
3643
padding-right: $mat-tooltip-handset-horizontal-padding;
3744
}
45+
46+
@keyframes mat-tooltip-show {
47+
0% {
48+
opacity: 0;
49+
transform: scale(0);
50+
}
51+
52+
50% {
53+
opacity: 0.5;
54+
transform: scale(0.99);
55+
}
56+
57+
100% {
58+
opacity: 1;
59+
transform: scale(1);
60+
}
61+
}
62+
63+
@keyframes mat-tooltip-hide {
64+
0% {
65+
opacity: 1;
66+
}
67+
68+
100% {
69+
opacity: 0;
70+
}
71+
}
72+
73+
.mat-tooltip-show {
74+
animation: mat-tooltip-show 200ms cubic-bezier(0, 0, 0.2, 1) forwards;
75+
}
76+
77+
.mat-tooltip-hide {
78+
animation: mat-tooltip-hide 100ms cubic-bezier(0, 0, 0.2, 1) forwards;
79+
}

0 commit comments

Comments
 (0)