Skip to content
This repository was archived by the owner on May 20, 2023. It is now read-only.

Commit 9312f16

Browse files
Googlernshahan
authored andcommitted
Smooth expansion/collapse for Material Expansion Panels with hidden headers.
PiperOrigin-RevId: 220719200
1 parent 98f2b35 commit 9312f16

File tree

4 files changed

+80
-15
lines changed

4 files changed

+80
-15
lines changed

angular_components/lib/material_expansionpanel/_mixins.scss

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,11 @@ $_panel: 'div.panel.themeable';
146146

147147
@mixin expansionpanel-header-min-height($min-height, $open-min-height) {
148148
::ng-deep #{$_panel} {
149-
header > .header {
149+
header:not(.hidden) > .header {
150150
min-height: $min-height;
151151
}
152152

153-
&.open > header > .header[buttonDecorator] {
153+
&.open > header:not(.hidden) > .header[buttonDecorator] {
154154
min-height: $open-min-height;
155155
}
156156
}
@@ -173,7 +173,7 @@ $_panel: 'div.panel.themeable';
173173
::ng-deep #{$_panel} {
174174
transition: none;
175175

176-
&.open > header > .header {
176+
&.open > header:not(.hidden) > .header {
177177
// Need to override the min-height increase which happens by default.
178178
min-height: $mat-grid * 6;
179179
}
@@ -192,7 +192,7 @@ $_panel: 'div.panel.themeable';
192192
@mixin expansionpanel-dense-theme() {
193193
::ng-deep #{$_panel} {
194194
.header,
195-
&.open .header {
195+
&.open > header:not(.hidden) > .header {
196196
min-height: $mat-grid * 4;
197197
}
198198

@@ -249,7 +249,7 @@ $_panel: 'div.panel.themeable';
249249

250250
@mixin expansionpanel-top-align-expand-button($margin-top: $mat-grid-type * 3) {
251251
::ng-deep #{$_panel} {
252-
&.open > header > .header .expand-button {
252+
&.open > header:not(.hidden) > .header .expand-button {
253253
margin-top: $margin-top + $mat-grid;
254254
}
255255

angular_components/lib/material_expansionpanel/material_expansionpanel.dart

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:async';
66
import 'dart:html';
7+
import 'dart:math';
78

89
import 'package:angular/angular.dart';
910
import 'package:intl/intl.dart';
@@ -93,10 +94,30 @@ class MaterialExpansionPanel
9394
}));
9495
}
9596

97+
HtmlElement _headerPanel;
98+
@ViewChild('headerPanel')
99+
set headerPanel(HtmlElement headerPanel) {
100+
_headerPanel = headerPanel;
101+
_disposer.addStreamSubscription(_headerPanel.onTransitionEnd.listen((_) {
102+
// Clear height override so it will match the active child's height.
103+
_headerPanel.style.height = '';
104+
}));
105+
}
106+
96107
HtmlElement _mainContent;
97108
@ViewChild('mainContent')
98109
set mainContent(HtmlElement mainContent) => _mainContent = mainContent;
99110

111+
HtmlElement _headerContent;
112+
@ViewChild('headerContent')
113+
set headerContent(HtmlElement headerContent) =>
114+
_headerContent = headerContent;
115+
116+
HtmlElement _actionContent;
117+
@ViewChild('action')
118+
set actionContent(HtmlElement headerContent) =>
119+
_actionContent = headerContent;
120+
100121
HtmlElement _contentWrapper;
101122
@ViewChild('contentWrapper')
102123
set contentWrapper(HtmlElement contentWrapper) {
@@ -441,28 +462,45 @@ class MaterialExpansionPanel
441462
} else {
442463
_domService.nextFrame.then((_) => _mainPanel.style.height = '');
443464
}
465+
466+
// If the header has to disappear, animate that transition as well
467+
if (hideExpandedHeader) {
468+
// Make current height explicit as a starting point for animation.
469+
_headerPanel.style.height = '${_headerPanel.scrollHeight}px';
470+
471+
// On next frame, set target height for animation.
472+
if (expand) {
473+
_domService.nextFrame.then((_) {
474+
_headerPanel.style.height = '';
475+
});
476+
} else {
477+
_readExpandedHeaderHeight().then((expandedHeaderHeight) {
478+
_headerPanel.style.height = expandedHeaderHeight;
479+
});
480+
}
481+
}
444482
}
445483

446484
/// Reads the DOM state to calculate the height of the "expanded" panel in its
447485
/// current condition.
448486
///
449487
/// This defined end point will be used to animate expansion.
450488
Future<String> _readExpandedPanelHeight() {
451-
var completeExpandedHeight = Completer<String>();
489+
final completeExpandedHeight = Completer<String>();
452490

453491
_domService.scheduleRead(() {
454-
var contentHeight = _mainContent.scrollHeight;
492+
final contentHeight = _mainContent.scrollHeight;
455493
var expandedPanelHeight = '';
456494

457-
var mainPanelStyle = _mainPanel.getComputedStyle();
495+
final mainPanelStyle = _mainPanel.getComputedStyle();
458496
// Do our best to make sure that onTransitionEnd will fire later.
459-
var hasHeightTransition =
497+
final hasHeightTransition =
460498
contentHeight > 0 && mainPanelStyle.transition.contains('height');
461499

462500
if (hasHeightTransition) {
463501
// If the content-wrapper has a top margin, it is not reflected in the
464502
// scroll height.
465-
var topMargin = _contentWrapper.getComputedStyle().marginTop;
503+
final topMargin = _contentWrapper.getComputedStyle().marginTop;
466504
expandedPanelHeight = 'calc(${contentHeight}px + ${topMargin})';
467505
}
468506
completeExpandedHeight.complete(expandedPanelHeight);
@@ -471,6 +509,31 @@ class MaterialExpansionPanel
471509
return completeExpandedHeight.future;
472510
}
473511

512+
/// Reads the DOM state to calculate the height of the header in its
513+
/// current condition.
514+
///
515+
/// This defined end point will be used to animate expansion.
516+
Future<String> _readExpandedHeaderHeight() {
517+
final completeExpandedHeight = Completer<String>();
518+
519+
_domService.scheduleRead(() {
520+
final contentHeight =
521+
max(_headerContent.scrollHeight, _actionContent?.scrollHeight ?? 0);
522+
var expandedHeaderHeight = '';
523+
524+
final headerPanelStyle = _headerPanel.getComputedStyle();
525+
// Do our best to make sure that onTransitionEnd will fire later.
526+
final hasHeightTransition =
527+
contentHeight > 0 && headerPanelStyle.transition.contains('height');
528+
529+
if (hasHeightTransition) expandedHeaderHeight = '${contentHeight}px';
530+
531+
completeExpandedHeight.complete(expandedHeaderHeight);
532+
});
533+
534+
return completeExpandedHeight.future;
535+
}
536+
474537
@override
475538
void ngOnDestroy() {
476539
_disposer.dispose();

angular_components/lib/material_expansionpanel/material_expansionpanel.html

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
keyupBoundary>
1313

1414
<!-- Title section -->
15-
<header [class.hidden]="isExpanded && hideExpandedHeader">
15+
<header [class.hidden]="isExpanded && hideExpandedHeader" #headerPanel>
1616
<div class="header"
1717
buttonDecorator
1818
#expandCollapseButton
19+
#headerContent
1920
[disabled]="disabled"
2021
[class.closed] = "!isExpanded"
2122
[class.disable-header-expansion]="disableHeaderExpansion"
@@ -48,7 +49,7 @@
4849
[attr.aria-label]="expandAriaMsg">
4950
</material-icon>
5051
</div>
51-
<div class="action" *ngIf="!shouldShowExpandIcon">
52+
<div class="action" *ngIf="!shouldShowExpandIcon" #action>
5253
<ng-content select="[action]"></ng-content>
5354
</div>
5455
</header>

angular_components/lib/material_expansionpanel/material_expansionpanel.scss

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@
6161
header {
6262
display: flex;
6363
color: $mat-transparent-black;
64-
transition: min-height $mat-transition-slow $mat-transition-standard,
64+
transition: height $mat-transition $mat-transition-standard,
65+
min-height $mat-transition $mat-transition-standard,
6566
opacity $mat-transition-slow $mat-transition-standard;
6667

6768
&.hidden {
@@ -84,7 +85,7 @@ header {
8485
min-width: 0;
8586
outline: none;
8687
padding: 0 $mat-grid * 3;
87-
transition: min-height $mat-transition-slow $mat-transition-standard;
88+
transition: min-height $mat-transition $mat-transition-standard;
8889

8990
&.closed:not(.is-disabled) {
9091
&:hover,
@@ -99,7 +100,7 @@ header {
99100
}
100101
}
101102

102-
.panel.open > header > .header {
103+
.panel.open > header:not(.hidden) > .header {
103104
min-height: $mat-grid * 8;
104105
}
105106

0 commit comments

Comments
 (0)