Skip to content

Commit

Permalink
Allow expansion indicator positioning.
Browse files Browse the repository at this point in the history
  • Loading branch information
josephperrott committed May 3, 2018
1 parent 376cb66 commit b320840
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 37 deletions.
12 changes: 8 additions & 4 deletions src/demo-app/expansion/expansion-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ <h1>matAccordion</h1>
<mat-radio-button value="default">Default</mat-radio-button>
<mat-radio-button value="flat">Flat</mat-radio-button>
</mat-radio-group>
<p>Toggle Position</p>
<mat-radio-group [(ngModel)]="togglePosition">
<mat-radio-button value="before">Before</mat-radio-button>
<mat-radio-button value="after">After</mat-radio-button>
</mat-radio-group>
<p>Accordion Actions <sup>('Multi Expansion' mode only)</sup></p>
<div>
<button mat-button (click)="accordion.openAll()" [disabled]="!multi">Expand All</button>
Expand All @@ -55,8 +60,8 @@ <h1>matAccordion</h1>
</div>
</div>
<br>
<mat-accordion [displayMode]="displayMode" [multi]="multi"
class="demo-expansion-width">
<mat-accordion [displayMode]="displayMode" [multi]="multi" [togglePosition]="togglePosition"
class="demo-expansion-width">
<mat-expansion-panel #panel1 [hideToggle]="hideToggle">
<mat-expansion-panel-header>Section 1</mat-expansion-panel-header>
<p>This is the content text that makes sense here.</p>
Expand All @@ -74,7 +79,7 @@ <h1>matAccordion</h1>
</mat-action-row>
</mat-expansion-panel>
</mat-accordion>
<!--

<h1>CdkAccordion</h1>
<div>
<p>Accordion Options</p>
Expand Down Expand Up @@ -108,4 +113,3 @@ <h1>CdkAccordion</h1>
<p *ngIf="item3.expanded">I only show if item 3 is expanded</p>
</cdk-accordion-item>
</cdk-accordion>
-->
1 change: 1 addition & 0 deletions src/demo-app/expansion/expansion-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class ExpansionDemo {
@ViewChild(MatAccordion) accordion: MatAccordion;

displayMode = 'default';
togglePosition = 'after';
multi = false;
hideToggle = false;
disabled = false;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/expansion/_expansion-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
}

.mat-expansion-panel-header-description,
.mat-expansion-indicator::after {
.mat-expansion-indicator {
color: mat-color($foreground, secondary-text);
}

Expand Down
33 changes: 31 additions & 2 deletions src/lib/expansion/accordion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {MatExpansionModule, MatAccordion} from './index';


describe('CdkAccordion', () => {
describe('MatAccordion', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
Expand Down Expand Up @@ -83,12 +83,39 @@ describe('CdkAccordion', () => {
fixture.detectChanges();
expect(panels[0].classes['mat-expanded']).toBeFalsy();
expect(panels[1].classes['mat-expanded']).toBeFalsy();

});

it('should correctly apply the displayMode provided', () => {
const fixture = TestBed.createComponent(SetOfItems);
const firstPanel = fixture.debugElement.queryAll(By.css('.mat-expansion-panel'))[0];

fixture.componentInstance.firstPanelExpanded = true;
fixture.detectChanges();
expect(firstPanel.classes['mat-expansion-panel-spacing']).toBeTruthy();

fixture.componentInstance.displayMode = 'flat';
fixture.detectChanges();
expect(firstPanel.classes['mat-expansion-panel-spacing']).toBeFalsy();
});

it('should correctly apply the togglePosition provided', () => {
const fixture = TestBed.createComponent(SetOfItems);
fixture.detectChanges();
const firstPanelHeader =
fixture.debugElement.queryAll(By.css('.mat-expansion-indicator-container'))[0];

expect(firstPanelHeader.classes['mat-expansion-indicator-container-before']).toBeFalsy();

fixture.componentInstance.togglePosition = 'before';
fixture.detectChanges();
expect(firstPanelHeader.classes['mat-expansion-indicator-container-before']).toBeTruthy();
});
});


@Component({template: `
<mat-accordion [multi]="multi">
<mat-accordion [multi]="multi" [displayMode]="displayMode" [togglePosition]="togglePosition">
<mat-expansion-panel [expanded]="firstPanelExpanded">
<mat-expansion-panel-header>Summary</mat-expansion-panel-header>
<p>Content</p>
Expand All @@ -102,6 +129,8 @@ class SetOfItems {
@ViewChild(MatAccordion) accordion: MatAccordion;

multi: boolean = false;
displayMode = 'default';
togglePosition = 'after';
firstPanelExpanded: boolean = false;
secondPanelExpanded: boolean = false;
secondPanelDisabled: boolean = false;
Expand Down
21 changes: 20 additions & 1 deletion src/lib/expansion/accordion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Directive, Input} from '@angular/core';
import {Directive, Input, SimpleChanges} from '@angular/core';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {CdkAccordion} from '@angular/cdk/accordion';
import {Subject} from 'rxjs';

/** MatAccordion's display modes. */
export type MatAccordionDisplayMode = 'default' | 'flat';


/** MatAccordion's toggle positions. */
export type MatAccordionTogglePosition = 'before' | 'after';

/**
* Directive for a Material Design Accordion.
*/
Expand All @@ -24,6 +29,9 @@ export type MatAccordionDisplayMode = 'default' | 'flat';
}
})
export class MatAccordion extends CdkAccordion {
/** Stream that emits for changes in `@Input` properties. */
_inputChanges = new Subject<SimpleChanges>();

/** Whether the expansion indicator should be hidden. */
@Input()
get hideToggle(): boolean { return this._hideToggle; }
Expand All @@ -39,4 +47,15 @@ export class MatAccordion extends CdkAccordion {
* elevation.
*/
@Input() displayMode: MatAccordionDisplayMode = 'default';

/** The positioning of the expansion indicator. */
@Input() togglePosition: MatAccordionTogglePosition = 'after';

ngOnChanges(changes: SimpleChanges) {
this._inputChanges.next(changes);
}

ngOnDestroy() {
this._inputChanges.complete();
}
}
4 changes: 2 additions & 2 deletions src/lib/expansion/expansion-animations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export const matExpansionAnimations: {
} = {
/** Animation that rotates the indicator arrow. */
indicatorRotate: trigger('indicatorRotate', [
state('collapsed', style({transform: 'rotate(0deg)'})),
state('expanded', style({transform: 'rotate(180deg)'})),
state('collapsed', style({transform: 'rotate(45deg)'})),
state('expanded', style({transform: 'rotate(225deg)'})),
transition('expanded <=> collapsed', animate(EXPANSION_PANEL_ANIMATION_TIMING)),
]),

Expand Down
7 changes: 5 additions & 2 deletions src/lib/expansion/expansion-panel-header.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
<ng-content select="mat-panel-description"></ng-content>
<ng-content></ng-content>
</span>
<span [@indicatorRotate]="_getExpandedState()" *ngIf="_showToggle()"
class="mat-expansion-indicator"></span>
<div class="mat-expansion-indicator-container"
[class.mat-expansion-indicator-container-before]="_placeToggleBefore()">
<span *ngIf="_isToggleVisible()" class="mat-expansion-indicator"
[@indicatorRotate]="_getExpandedState()"></span>
</div>
44 changes: 31 additions & 13 deletions src/lib/expansion/expansion-panel-header.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@

.mat-expansion-panel-header {
display: flex;
flex-direction: row;
align-items: center;
padding: 0 24px;
padding: 0 16px;

&:focus,
&:hover {
Expand All @@ -25,12 +24,13 @@
flex: 1;
flex-direction: row;
overflow: hidden;
padding-left: 8px;
}

.mat-expansion-panel-header-title,
.mat-expansion-panel-header-description {
display: flex;
flex-grow: 1;
flex: 1;
margin-right: 16px;

[dir='rtl'] & {
Expand All @@ -40,19 +40,37 @@
}

.mat-expansion-panel-header-description {
flex-grow: 2;
flex: 2;
}

.mat-expansion-indicator-container {
// A margin is required to offset the entire expansion indicator against the space the arrow
// takes up. It is calculated as sqrt(2 * border-width ^ 2) / 2.
margin-bottom: 1.41px;
padding: 0 8px 0 0;
width: 8px;
order: 1;

&.mat-expansion-indicator-container-before {
order: -1;
padding: 0 8px;
}
}

/**
* Creates the expansion indicator arrow. Done using ::after rather than having
* additional nodes in the template.
*/
.mat-expansion-indicator::after {
.mat-expansion-indicator {
border-style: solid;
border-width: 0 2px 2px 0;
content: '';
display: inline-block;
padding: 3px;
display: block;
height: 6px;
transform: rotate(45deg);
vertical-align: middle;
// The transform origin is set by determining the center pointer of the arrow created. It is
// calculated as by calculating the length of the line between the top left corner of the div,
// and the centroid of the triangle created in the bottom right half of the div. This centroid
// is calculated for both X and Y (as the indicator is a square) as
// (indicator-width + indicator-height + 0) / 3
// The length between the resulting coordinates and the top left (0, 0) of the div are
// calculated sqrt((centroids-x-coord ^ 2) + (centroids-y-coord ^ 2))
// This value is used to transform the origin on both the X and Y axes.
transform-origin: 5.65px 5.65px;
width: 6px;
}
20 changes: 17 additions & 3 deletions src/lib/expansion/expansion-panel-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ import {
ElementRef,
Host,
Input,
Optional,
OnDestroy,
ViewEncapsulation,
} from '@angular/core';
import {merge, Subscription} from 'rxjs';
import {filter} from 'rxjs/operators';
import {matExpansionAnimations} from './expansion-animations';
import {MatExpansionPanel} from './expansion-panel';
import {MatAccordion} from './accordion';


/**
Expand Down Expand Up @@ -65,17 +67,24 @@ export class MatExpansionPanelHeader implements OnDestroy {
private _parentChangeSubscription = Subscription.EMPTY;

constructor(
@Optional() accordion: MatAccordion,
@Host() public panel: MatExpansionPanel,
private _element: ElementRef,
private _focusMonitor: FocusMonitor,
private _changeDetectorRef: ChangeDetectorRef) {

let changeStreams = [panel._inputChanges];
if (accordion) {
changeStreams.push(accordion._inputChanges);
}

// Since the toggle state depends on an @Input on the panel, we
// need to subscribe and trigger change detection manually.
// need to subscribe and trigger change detection manually.
this._parentChangeSubscription = merge(
panel.opened,
panel.closed,
panel._inputChanges.pipe(filter(changes => !!(changes.hideToggle || changes.disabled)))
merge(...changeStreams).pipe(
filter(changes => !!(changes.hideToggle || changes.disabled || changes.togglePosition)))
)
.subscribe(() => this._changeDetectorRef.markForCheck());

Expand Down Expand Up @@ -109,10 +118,15 @@ export class MatExpansionPanelHeader implements OnDestroy {
}

/** Gets whether the expand indicator should be shown. */
_showToggle(): boolean {
_isToggleVisible(): boolean {
return !this.panel.hideToggle && !this.panel.disabled;
}

/** Whether the expand indicator should be shown before the header content */
_placeToggleBefore(): boolean {
return this.panel.togglePosition === 'before';
}

/** Handle keydown event calling to toggle() if appropriate. */
_keydown(event: KeyboardEvent) {
switch (event.keyCode) {
Expand Down
14 changes: 12 additions & 2 deletions src/lib/expansion/expansion-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ import {
} from '@angular/core';
import {Subject} from 'rxjs';
import {filter, startWith, take} from 'rxjs/operators';
import {MatAccordion} from './accordion';
import {matExpansionAnimations} from './expansion-animations';
import {MatAccordion, MatAccordionTogglePosition} from './accordion';
import {MatExpansionPanelContent} from './expansion-panel-content';
import {matExpansionAnimations} from './expansion-animations';


/** MatExpansionPanel's states. */
Expand Down Expand Up @@ -72,6 +72,16 @@ export class MatExpansionPanel extends CdkAccordionItem
}
private _hideToggle = false;

/** The positioning of the expansion indicator. */
@Input()
get togglePosition(): MatAccordionTogglePosition {
return this.accordion ? this.accordion.togglePosition : this._togglePosition;
}
set togglePosition(position: MatAccordionTogglePosition) {
this._togglePosition = position;
}
private _togglePosition: MatAccordionTogglePosition = 'after';

/** Stream that emits for changes in `@Input` properties. */
readonly _inputChanges = new Subject<SimpleChanges>();

Expand Down
3 changes: 2 additions & 1 deletion src/lib/expansion/expansion.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ header to align with Material Design specifications.

By default, the expansion-panel header includes a toggle icon at the end of the
header to indicate the expansion state. This icon can be hidden via the
`hideToggle` property.
`hideToggle` property. The icon's position can also be configured with the `togglePosition`
property.

```html
<mat-expansion-panel>
Expand Down
Loading

0 comments on commit b320840

Please sign in to comment.