Skip to content

Commit 112f8f8

Browse files
committed
Create Expansion Panel.
1 parent 76faae5 commit 112f8f8

21 files changed

+652
-0
lines changed

src/demo-app/demo-app-module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import {PlatformDemo} from './platform/platform-demo';
4343
import {AutocompleteDemo} from './autocomplete/autocomplete-demo';
4444
import {InputDemo} from './input/input-demo';
4545
import {StyleDemo} from './style/style-demo';
46+
import {ExpansionDemo} from './expansion/expansion-demo';
4647

4748

4849
@NgModule({
@@ -100,6 +101,7 @@ import {StyleDemo} from './style/style-demo';
100101
RainyTabContent,
101102
FoggyTabContent,
102103
PlatformDemo,
104+
ExpansionDemo,
103105
],
104106
providers: [
105107
{provide: OverlayContainer, useClass: FullscreenOverlayContainer}

src/demo-app/demo-app/demo-app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export class DemoApp {
2626
{name: 'Chips', route: 'chips'},
2727
{name: 'Checkbox', route: 'checkbox'},
2828
{name: 'Dialog', route: 'dialog'},
29+
{name: 'Expansion Panel', route: 'expansion'},
2930
{name: 'Gestures', route: 'gestures'},
3031
{name: 'Grid List', route: 'grid-list'},
3132
{name: 'Icon', route: 'icon'},

src/demo-app/demo-app/routes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {PlatformDemo} from '../platform/platform-demo';
3232
import {AutocompleteDemo} from '../autocomplete/autocomplete-demo';
3333
import {InputDemo} from '../input/input-demo';
3434
import {StyleDemo} from '../style/style-demo';
35+
import {ExpansionDemo} from '../expansion/expansion-demo';
3536

3637
export const DEMO_APP_ROUTES: Routes = [
3738
{path: '', component: Home},
@@ -66,4 +67,5 @@ export const DEMO_APP_ROUTES: Routes = [
6667
{path: 'snack-bar', component: SnackBarDemo},
6768
{path: 'platform', component: PlatformDemo},
6869
{path: 'style', component: StyleDemo},
70+
{path: 'expansion', component: ExpansionDemo},
6971
];
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<h1>Single Expansion Panel</h1>
2+
<md-expansion-panel class="md-expansion-demo-width" #myPanel>
3+
<md-expansion-panel-header>
4+
<span title>{{ myPanel.expanded ? 'My Best Work' : 'Not My Best Work' }}</span>
5+
<span description *ngIf="myPanel.expanded">
6+
This is a description only shows while expanded.
7+
</span>
8+
</md-expansion-panel-header>
9+
This is the content text that makes sense here.
10+
<div md-action-row>
11+
<button md-button (click)="myPanel.expanded = false">CANCEL</button>
12+
<button md-button>SAVE</button>
13+
</div>
14+
</md-expansion-panel>
15+
<br>
16+
<h1>Accordion</h1>
17+
<div>
18+
<p>Accordion Options</p>
19+
<div>
20+
<md-slide-toggle [(ngModel)]="multiExpansion">Allow Multi Expansion</md-slide-toggle>
21+
<md-slide-toggle [(ngModel)]="showExpandIndicators">Show Indicators</md-slide-toggle>
22+
<md-slide-toggle [(ngModel)]="showPanel3">Show Panel 3</md-slide-toggle>
23+
</div>
24+
<p>Accordion Style</p>
25+
<md-radio-group [(ngModel)]="panelStyle">
26+
<md-radio-button value="default">Default</md-radio-button>
27+
<md-radio-button value="flat">Flat</md-radio-button>
28+
</md-radio-group>
29+
<p>Accordion Panel(s)</p>
30+
<div>
31+
<md-checkbox [(ngModel)]="panel1.expanded">Panel 1</md-checkbox>
32+
<md-checkbox [(ngModel)]="panel2.expanded">Panel 2</md-checkbox>
33+
</div>
34+
</div>
35+
<br>
36+
<md-accordion [accordionStyle]="panelStyle" [multi]="multiExpansion"
37+
class="md-expansion-demo-width">
38+
<md-expansion-panel #panel1>
39+
<md-expansion-panel-header [showExpandIndicator]="showExpandIndicators">
40+
Section 1
41+
</md-expansion-panel-header>
42+
<p>This is the content text that makes sense here.</p>
43+
</md-expansion-panel>
44+
<md-expansion-panel #panel2>
45+
<md-expansion-panel-header [showExpandIndicator]="showExpandIndicators">
46+
Section 2
47+
</md-expansion-panel-header>
48+
<p>This is the content text that makes sense here.</p>
49+
</md-expansion-panel>
50+
<md-expansion-panel #panel3 *ngIf="showPanel3">
51+
<md-expansion-panel-header [showExpandIndicator]="showExpandIndicators">
52+
Section 3
53+
</md-expansion-panel-header>
54+
<md-checkbox #showButtons>Reveal Buttons Below</md-checkbox>
55+
<div md-action-row *ngIf="showButtons.checked">
56+
<button md-button (click)="panel2.expanded = true">OPEN SECTION 2</button>
57+
<button md-button (click)="panel3.expanded = false">CLOSE</button>
58+
</div>
59+
</md-expansion-panel>
60+
</md-accordion>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.md-expansion-demo-width {
2+
width: 600px;
3+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import {Component, ViewEncapsulation} from '@angular/core';
2+
3+
@Component({
4+
moduleId: module.id,
5+
selector: 'expansion-demo',
6+
styleUrls: ['expansion-demo.css'],
7+
templateUrl: 'expansion-demo.html',
8+
encapsulation: ViewEncapsulation.None,
9+
})
10+
export class ExpansionDemo {
11+
panelStyle: string = 'default';
12+
multiExpansion: boolean = false;
13+
showExpandIndicators: boolean = true;
14+
showPanel3 = true;
15+
}

src/lib/core/theming/_all-theme.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@import '../../checkbox/checkbox-theme';
88
@import '../../chips/chips-theme';
99
@import '../../dialog/dialog-theme';
10+
@import '../../expansion/expansion-theme';
1011
@import '../../grid-list/grid-list-theme';
1112
@import '../../icon/icon-theme';
1213
@import '../../input/input-theme';
@@ -34,6 +35,7 @@
3435
@include mat-checkbox-theme($theme);
3536
@include mat-chips-theme($theme);
3637
@include mat-dialog-theme($theme);
38+
@include mat-expansion-panel-theme($theme);
3739
@include mat-grid-list-theme($theme);
3840
@include mat-icon-theme($theme);
3941
@include mat-input-theme($theme);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@import '../core/theming/palette';
2+
@import '../core/theming/theming';
3+
4+
@mixin mat-expansion-panel-theme($theme) {
5+
$background: map-get($theme, background);
6+
$foreground: map-get($theme, foreground);
7+
8+
.mat-expansion-panel {
9+
background: mat-color($background, card);
10+
color: mat-color($foreground, base);
11+
}
12+
}

src/lib/expansion/accordion.scss

Whitespace-only changes.

src/lib/expansion/accordion.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import {
2+
Component,
3+
Directive,
4+
ElementRef,
5+
EventEmitter,
6+
Host,
7+
Input,
8+
Output,
9+
ViewEncapsulation,
10+
AfterContentInit,
11+
OnDestroy,
12+
ContentChildren,
13+
QueryList,
14+
Optional,
15+
forwardRef,
16+
} from '@angular/core';
17+
import {
18+
trigger,
19+
state,
20+
style,
21+
transition,
22+
animate,
23+
} from '@angular/animations';
24+
import {Subscription} from 'rxjs/Subscription';
25+
import {Subject} from 'rxjs/Subject';
26+
import 'rxjs/add/operator/takeUntil';
27+
28+
/** Used to generate unique ID for each expansion panel. */
29+
let nextId = 0;
30+
31+
@Directive({})
32+
export class MdAccordionChild implements OnDestroy {
33+
/** Whether the MdAccordianChild is expanded. */
34+
expanded: boolean;
35+
/** The unique MdAccordianChild id. */
36+
private _expansionPanelId: number = nextId++;
37+
get id(): string {
38+
return `md-expansion-panel-${this._expansionPanelId}`;
39+
}
40+
/** Event emitted every time the MdAccordianChild is closed. */
41+
@Output() close = new EventEmitter<null>();
42+
/** Event emitted every time the MdAccordianChild is opened. */
43+
@Output() open = new EventEmitter<null>();
44+
/** Event emitted when the MdAccordianChild is destroyed. */
45+
@Output() destroy = new EventEmitter<null>();
46+
47+
ngOnDestroy() {
48+
this.destroy.emit();
49+
}
50+
}
51+
52+
@Directive({
53+
selector: 'md-accordion, mat-accordion',
54+
host: {
55+
'class': 'mat-expansion-panel-set'
56+
},
57+
})
58+
export class MdAccordion implements AfterContentInit, OnDestroy {
59+
/** Whether the panel set should use flat styling. */
60+
@Input() accordionStyle: string = 'default';
61+
/** Whether the panel set should allow multiple open panels. */
62+
@Input() multi: boolean = false;
63+
/** A subject to be completed on destroy to clean up subscriptions. */
64+
private destroySubject = new Subject();
65+
/** A list of subscriptions for panel open events. */
66+
private panelsSet: Set<MdAccordionChild> = new Set();
67+
/** QueryList of all expansion panels in the expansion panel set. */
68+
@ContentChildren(MdAccordionChild) private _panels: QueryList<MdAccordionChild>;
69+
70+
/** Set up event subscriptions for all panels for when they open, closing the other panels. */
71+
ngAfterContentInit() {
72+
this.registerPanels(this._panels);
73+
this._panels.changes
74+
.takeUntil(this.destroySubject)
75+
.subscribe((panels: QueryList<MdAccordionChild>) => {
76+
this.registerPanels(panels);
77+
});
78+
}
79+
80+
/** Clean up panel subscriptions. */
81+
ngOnDestroy() {
82+
this.destroySubject.next();
83+
this.destroySubject.complete();
84+
}
85+
86+
/** Registers a QueryList of panels. */
87+
registerPanels(panels: QueryList<MdAccordionChild>) {
88+
this._panels.forEach(panel => this.registerPanel(panel));
89+
}
90+
91+
/**
92+
* Registers an MdExpansion panel if has not already been registered. Creates subscriptions
93+
* for the open and destroy events for the panel.
94+
*/
95+
registerPanel(panel: MdAccordionChild) {
96+
if (this.panelsSet.has(panel)) {
97+
return;
98+
}
99+
panel.open
100+
.takeUntil(this.destroySubject)
101+
.subscribe(() => {
102+
if (!this.multi) {
103+
this.panelsSet.forEach(p => p.expanded = panel.id === p.id);
104+
}
105+
});
106+
panel.destroy
107+
.takeUntil(this.destroySubject)
108+
.subscribe(() => this.panelsSet.delete(panel));
109+
this.panelsSet.add(panel);
110+
}
111+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<span class="mat-content">
2+
<ng-content select="[title]"></ng-content>
3+
<ng-content select="[description]"></ng-content>
4+
<ng-content></ng-content>
5+
</span>
6+
<span [@indicatorRotate]="parentPanel.getExpandedState()"
7+
*ngIf="showExpandIndicator" class="expansion-indicator"></span>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
$md-expansion-panel-header-height: 48px;
2+
$md-expansion-panel-header-height-expanded: 64px;
3+
4+
.mat-expansion-panel-header {
5+
cursor: pointer;
6+
display: flex;
7+
flex-direction: row;
8+
height: $md-expansion-panel-header-height;
9+
line-height: $md-expansion-panel-header-height;
10+
padding: 0 24px;
11+
12+
.mat-content {
13+
display: flex;
14+
flex: 1;
15+
flex-direction: row;
16+
overflow: hidden;
17+
}
18+
19+
&.mat-expanded {
20+
height: $md-expansion-panel-header-height-expanded;
21+
line-height: $md-expansion-panel-header-height-expanded;
22+
}
23+
24+
&:focus,
25+
&:hover {
26+
background: rgb(238, 238, 238);
27+
outline: none;
28+
}
29+
30+
&.mat-expanded:focus,
31+
&.mat-expanded:hover, {
32+
background: inherit;
33+
}
34+
35+
[title] {
36+
color: rgba(0, 0, 0, 0.87);
37+
display: flex;
38+
flex: 1;
39+
font-size: 15px;
40+
margin-right: 16px;
41+
}
42+
43+
[description] {
44+
color: rgba(0, 0, 0, 0.54);
45+
display: flex;
46+
flex: 2;
47+
font-size: 15px;
48+
margin-right: 16px;
49+
}
50+
51+
.expansion-indicator::after {
52+
border: solid rgba(0, 0, 0, 0.38);
53+
border-width: 0 2px 2px 0;
54+
content: '';
55+
display: inline-block;
56+
padding: 3px;
57+
transform: rotate(-135deg);
58+
vertical-align: middle;
59+
}
60+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {
2+
Component,
3+
Input,
4+
ViewEncapsulation,
5+
} from '@angular/core';
6+
import {
7+
trigger,
8+
state,
9+
style,
10+
transition,
11+
animate,
12+
} from '@angular/animations';
13+
import {MdExpansionPanel, EXPANSION_PANEL_ANIMATION_TIMING} from './expansion-panel';
14+
15+
@Component({
16+
moduleId: module.id,
17+
selector: 'md-expansion-panel-header, mat-expansion-panel-header',
18+
styleUrls: ['./expansion-panel-header.css'],
19+
templateUrl: './expansion-panel-header.html',
20+
encapsulation: ViewEncapsulation.None,
21+
host: {
22+
'class': 'mat-expansion-panel-header',
23+
'role': 'button',
24+
'tabindex': '0',
25+
'[attr.aria-controls]': 'parentPanel.id',
26+
'[attr.aria-expanded]': 'parentPanel.expanded',
27+
'[class.mat-expanded]': 'parentPanel.expanded',
28+
'(click)': 'parentPanel.expanded = !parentPanel.expanded',
29+
'(keyup.space)': 'parentPanel.expanded = !parentPanel.expanded',
30+
'(keyup.enter)': 'parentPanel.expanded = !parentPanel.expanded',
31+
'[@expansionHeight]': 'parentPanel.getExpandedState()',
32+
},
33+
animations: [
34+
trigger('indicatorRotate', [
35+
state('collapsed', style({transform: 'rotate(0deg)'})),
36+
state('expanded', style({transform: 'rotate(-180deg)'})),
37+
transition('* <=> *', animate(EXPANSION_PANEL_ANIMATION_TIMING)),
38+
]),
39+
trigger('expansionHeight', [
40+
state('collapsed', style({height: '48px', 'line-height': '48px'})),
41+
state('expanded', style({height: '64px', 'line-height': '68px'})),
42+
transition('* <=> *', animate(EXPANSION_PANEL_ANIMATION_TIMING)),
43+
]),
44+
],
45+
})
46+
export class MdExpansionPanelHeader {
47+
/** Whether the expansion indicator should be shown. */
48+
@Input() showExpandIndicator = true;
49+
50+
constructor(public parentPanel: MdExpansionPanel) {}
51+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<ng-content select="mat-expansion-panel-header, md-expansion-panel-header"></ng-content>
2+
<div class="mat-expansion-panel-body" [class.mat-expanded]="expanded"
3+
[@bodyExpansion]="getExpandedState()" [id]="id">
4+
<ng-content></ng-content>
5+
<ng-content select="[mat-action-row], [md-action-row]"></ng-content>
6+
</div>

0 commit comments

Comments
 (0)