Skip to content

Commit

Permalink
Allow snackbar position to be set to left or center.
Browse files Browse the repository at this point in the history
  • Loading branch information
josephperrott committed Apr 26, 2017
1 parent 85bc3a6 commit 6be0d6d
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 19 deletions.
13 changes: 12 additions & 1 deletion src/demo-app/snack-bar/snack-bar-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ <h1>SnackBar demo</h1>
<div>
Message: <md-input-container><input mdInput type="text" [(ngModel)]="message"></md-input-container>
</div>
<div>
<div>Position in page: </div>
<md-select [(ngModel)]="position.horizontal">
<md-option value="left">Left</md-option>
<md-option value="center">Center</md-option>
<md-option value="right">Right</md-option>
</md-select>
<md-select [(ngModel)]="position.vertical">
<md-option value="top">Top</md-option>
<md-option value="bottom">Bottom</md-option>
</md-select>
</div>
<div>
<md-checkbox [(ngModel)]="action">
<p *ngIf="!action">Show button on snack bar</p>
Expand All @@ -27,7 +39,6 @@ <h1>SnackBar demo</h1>
</md-input-container>
</md-checkbox>
</div>

<p>
<md-checkbox [(ngModel)]="addExtraClass">Add extra class to container</md-checkbox>
</p>
Expand Down
12 changes: 10 additions & 2 deletions src/demo-app/snack-bar/snack-bar-demo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Component, ViewEncapsulation} from '@angular/core';
import {MdSnackBar, MdSnackBarConfig} from '@angular/material';
import {MdSnackBar, MdSnackBarConfig, MdSnackBarPosition} from '@angular/material';

@Component({
moduleId: module.id,
Expand All @@ -15,12 +15,20 @@ export class SnackBarDemo {
setAutoHide: boolean = true;
autoHide: number = 10000;
addExtraClass: boolean = false;
position: MdSnackBarPosition = {
horizontal: 'center',
vertical: 'bottom'
};

constructor(public snackBar: MdSnackBar) { }

open() {
let config = new MdSnackBarConfig();
config.duration = this.autoHide;
config.position = {
vertical: this.position.vertical,
horizontal: this.position.horizontal
};
config.duration = this.setAutoHide ? this.autoHide : 0;
config.extraClasses = this.addExtraClass ? ['party'] : null;
this.snackBar.open(this.message, this.action && this.actionButtonLabel, config);
}
Expand Down
11 changes: 11 additions & 0 deletions src/lib/snack-bar/snack-bar-config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {ViewContainerRef} from '@angular/core';
import {AriaLivePoliteness} from '../core';

export interface MdSnackBarPosition {
horizontal: 'left'|'center'|'right';
vertical: 'bottom'|'top';
}

/**
* Configuration used when opening a snack-bar.
*/
Expand All @@ -19,4 +24,10 @@ export class MdSnackBarConfig {

/** Extra CSS classes to be added to the snack bar container. */
extraClasses?: string[];

/** The position to place the snack bar in the view, either 'left' or 'center'. */
position?: MdSnackBarPosition = {
horizontal: 'center',
vertical: 'bottom'
};
}
10 changes: 10 additions & 0 deletions src/lib/snack-bar/snack-bar-container.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
$mat-snack-bar-padding: 14px 24px !default;
$mat-snack-bar-min-width: 288px !default;
$mat-snack-bar-max-width: 568px !default;
$mat-snack-bar-spacing-margin: 24px !default;


:host {
Expand All @@ -12,12 +13,21 @@ $mat-snack-bar-max-width: 568px !default;
border-radius: 2px;
box-sizing: content-box;
display: block;
margin: $mat-snack-bar-spacing-margin;
max-width: $mat-snack-bar-max-width;
min-width: $mat-snack-bar-min-width;
padding: $mat-snack-bar-padding;
// Initial transformation is applied to start snack bar out of view.
transform: translateY(100%);

&.md-snack-bar-center {
margin: 0;
}

&.md-snack-bar-top {
transform: translateY(-100%);
}

@include cdk-high-contrast {
border: solid 1px;
}
Expand Down
40 changes: 29 additions & 11 deletions src/lib/snack-bar/snack-bar-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {Subject} from 'rxjs/Subject';



export type SnackBarState = 'initial' | 'visible' | 'complete' | 'void';
export type SnackBarState = 'visible' | 'hidden' | 'void';

// TODO(jelbourn): we can't use constants from animation.ts here because you can't use
// a text interpolation in anything that is analyzed statically with ngc (for AoT compile).
Expand All @@ -46,16 +46,22 @@ export const HIDE_ANIMATION = '195ms cubic-bezier(0.0,0.0,0.2,1)';
styleUrls: ['snack-bar-container.css'],
host: {
'role': 'alert',
'[@state]': 'animationState',
'[@state]': 'getAnimationState()',
'(@state.done)': 'onAnimationEnd($event)'
},
animations: [
trigger('state', [
state('initial', style({transform: 'translateY(100%)'})),
state('visible', style({transform: 'translateY(0%)'})),
state('complete', style({transform: 'translateY(100%)'})),
transition('visible => complete', animate(HIDE_ANIMATION)),
transition('initial => visible, void => visible', animate(SHOW_ANIMATION)),
// Animation from top.
state('visible-top', style({transform: 'translateY(0%)'})),
state('hidden-top', style({transform: 'translateY(-100%)'})),
transition('visible-top => hidden-top', animate(HIDE_ANIMATION)),
transition('void => visible-top', animate(SHOW_ANIMATION)),
// Animation from bottom.
state('visible-bottom', style({transform: 'translateY(0%)'})),
state('hidden-bottom', style({transform: 'translateY(100%)'})),
transition('visible-bottom => hidden-bottom', animate(HIDE_ANIMATION)),
transition('void => visible-bottom',
animate(SHOW_ANIMATION)),
])
],
})
Expand All @@ -70,7 +76,7 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {
private onEnter: Subject<any> = new Subject();

/** The state of the snack bar animations. */
animationState: SnackBarState = 'initial';
animationState: SnackBarState;

/** The snack bar configuration. */
snackBarConfig: MdSnackBarConfig;
Expand All @@ -82,6 +88,10 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {
super();
}

getAnimationState(): string {
return `${this.animationState}-${this.snackBarConfig.position.vertical}`;
}

/** Attach a component portal as content to this snack bar container. */
attachComponentPortal<T>(portal: ComponentPortal<T>): ComponentRef<T> {
if (this._portalHost.hasAttached()) {
Expand All @@ -96,6 +106,14 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {
}
}

if (this.snackBarConfig.position.horizontal === 'center') {
this._renderer.setElementClass(this._elementRef.nativeElement, 'md-snack-bar-center', true);
}

if (this.snackBarConfig.position.vertical === 'top') {
this._renderer.setElementClass(this._elementRef.nativeElement, 'md-snack-bar-top', true);
}

return this._portalHost.attachComponentPortal(portal);
}

Expand All @@ -106,11 +124,11 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {

/** Handle end of animations, updating the state of the snackbar. */
onAnimationEnd(event: AnimationEvent) {
if (event.toState === 'void' || event.toState === 'complete') {
if (event.toState === 'void' || event.toState.startsWith('hidden')) {
this._completeExit();
}

if (event.toState === 'visible') {
if (event.toState.startsWith('visible')) {
this._ngZone.run(() => {
this.onEnter.next();
this.onEnter.complete();
Expand All @@ -131,7 +149,7 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy {

/** Begin animation of the snack bar exiting from view. */
exit(): Observable<void> {
this.animationState = 'complete';
this.animationState = 'hidden';
return this._onExit();
}

Expand Down
28 changes: 23 additions & 5 deletions src/lib/snack-bar/snack-bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class MdSnackBar {
*/
openFromComponent<T>(component: ComponentType<T>, config?: MdSnackBarConfig): MdSnackBarRef<T> {
config = _applyConfigDefaults(config);
let overlayRef = this._createOverlay();
let overlayRef = this._createOverlay(config);
let snackBarContainer = this._attachSnackBarContainer(overlayRef, config);
let snackBarRef = this._attachSnackbarContent(component, snackBarContainer, overlayRef);

Expand Down Expand Up @@ -140,11 +140,29 @@ export class MdSnackBar {
/**
* Creates a new overlay and places it in the correct location.
*/
private _createOverlay(): OverlayRef {
private _createOverlay(config: MdSnackBarConfig): OverlayRef {
let state = new OverlayState();
state.positionStrategy = this._overlay.position().global()
.centerHorizontally()
.bottom('0');
let positionStrategy = this._overlay.position().global();
switch (config.position.horizontal) {
case 'left':
positionStrategy.left('0');
break;
case 'right':
positionStrategy.right('0');
break;
case 'center':
positionStrategy.centerHorizontally();
break;
}
switch (config.position.vertical) {
case 'top':
positionStrategy.top('0');
break;
case 'bottom':
positionStrategy.bottom('0');
break;
}
state.positionStrategy = positionStrategy;
return this._overlay.create(state);
}
}
Expand Down

0 comments on commit 6be0d6d

Please sign in to comment.