Skip to content

Commit 7f1485a

Browse files
committed
feat(md-snack-bar): Create initial MdSnackBar.
1 parent ac86ddf commit 7f1485a

22 files changed

+404
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {MdCheckboxDemoNestedChecklist, CheckboxDemo} from './checkbox/checkbox-d
2929
import {SelectDemo} from './select/select-demo';
3030
import {SliderDemo} from './slider/slider-demo';
3131
import {SidenavDemo} from './sidenav/sidenav-demo';
32+
import {SnackBarDemo} from './snack-bar/snack-bar-demo';
3233
import {PortalDemo, ScienceJoke} from './portal/portal-demo';
3334
import {MenuDemo} from './menu/menu-demo';
3435
import {TabsDemo} from './tabs/tab-group-demo';
@@ -61,6 +62,7 @@ import {TabsDemo} from './tabs/tab-group-demo';
6162
LiveAnnouncerDemo,
6263
MdCheckboxDemoNestedChecklist,
6364
MenuDemo,
65+
SnackBarDemo,
6466
OverlayDemo,
6567
PortalDemo,
6668
ProgressBarDemo,

src/demo-app/demo-app/demo-app.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<a md-list-item [routerLink]="['sidenav']">Sidenav</a>
2424
<a md-list-item [routerLink]="['slider']">Slider</a>
2525
<a md-list-item [routerLink]="['slide-toggle']">Slide Toggle</a>
26+
<a md-list-item [routerLink]="['snack-bar']">Snack Bar</a>
2627
<a md-list-item [routerLink]="['tabs']">Tabs</a>
2728
<a md-list-item [routerLink]="['toolbar']">Toolbar</a>
2829
<a md-list-item [routerLink]="['tooltip']">Tooltip</a>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {MenuDemo} from '../menu/menu-demo';
2626
import {RippleDemo} from '../ripple/ripple-demo';
2727
import {DialogDemo} from '../dialog/dialog-demo';
2828
import {TooltipDemo} from '../tooltip/tooltip-demo';
29+
import {SnackBarDemo} from '../snack-bar/snack-bar-demo';
2930

3031

3132
export const DEMO_APP_ROUTES: Routes = [
@@ -56,4 +57,5 @@ export const DEMO_APP_ROUTES: Routes = [
5657
{path: 'ripple', component: RippleDemo},
5758
{path: 'dialog', component: DialogDemo},
5859
{path: 'tooltip', component: TooltipDemo},
60+
{path: 'snack-bar', component: SnackBarDemo},
5961
];
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<h1>SnackBar demo</h1>
2+
<div>
3+
<div>Message: <md-input type="text" [(ngModel)]="message"></md-input></div>
4+
<div>
5+
<md-checkbox [(ngModel)]="action">Show button</md-checkbox>
6+
<md-input type="text" class="button-label-input"
7+
placeholder="Button Label"
8+
[disabled]="!action"
9+
[(ngModel)]="actionButtonLabel"></md-input>
10+
</div>
11+
</div>
12+
13+
<button md-raised-button (click)="open()">Open</button>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.button-label-input {
2+
display: inline-block;
3+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {Component, ViewContainerRef} from '@angular/core';
2+
import {MdSnackBar, MdSnackBarConfig} from '@angular2-material/snack-bar';
3+
4+
@Component({
5+
moduleId: module.id,
6+
selector: 'snack-bar-demo',
7+
templateUrl: 'snack-bar-demo.html',
8+
})
9+
export class SnackBarDemo {
10+
message: string = 'Snack Bar opened.';
11+
actionButtonLabel: string = 'Retry';
12+
action: boolean = false;
13+
14+
constructor(
15+
public snackBar: MdSnackBar,
16+
public viewContainerRef: ViewContainerRef) { }
17+
18+
open() {
19+
let config = new MdSnackBarConfig(this.viewContainerRef);
20+
this.snackBar.open(this.message, this.action && this.actionButtonLabel, config);
21+
}
22+
}
23+
24+
25+
@Component({
26+
moduleId: module.id,
27+
selector: 'demo-snack',
28+
templateUrl: 'snack-bar-demo.html',
29+
styleUrls: ['./snack-bar-demo.css'],
30+
})
31+
export class DemoSnack {}

src/demo-app/system-config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const components = [
2020
'sidenav',
2121
'slider',
2222
'slide-toggle',
23+
'snack-bar',
2324
'button-toggle',
2425
'tabs',
2526
'toolbar',

src/lib/all/all.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {MdProgressCircleModule} from '@angular2-material/progress-circle';
1515
import {MdProgressBarModule} from '@angular2-material/progress-bar';
1616
import {MdInputModule} from '@angular2-material/input';
1717
import {MdTabsModule} from '@angular2-material/tabs';
18+
import {MdSnackBarModule} from '@angular2-material/snack-bar';
1819
import {MdToolbarModule} from '@angular2-material/toolbar';
1920
import {MdTooltipModule} from '@angular2-material/tooltip';
2021
import {
@@ -47,6 +48,7 @@ const MATERIAL_MODULES = [
4748
MdSidenavModule,
4849
MdSliderModule,
4950
MdSlideToggleModule,
51+
MdSnackBarModule,
5052
MdTabsModule,
5153
MdToolbarModule,
5254
MdTooltipModule,
@@ -81,6 +83,7 @@ const MATERIAL_MODULES = [
8183
MdRadioModule.forRoot(),
8284
MdSliderModule.forRoot(),
8385
MdSlideToggleModule.forRoot(),
86+
MdSnackBarModule.forRoot(),
8487
MdTooltipModule.forRoot(),
8588
OverlayModule.forRoot(),
8689
],

src/lib/snack-bar/base-snack-bar.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import {MdSnackBarRef} from './snack-bar-ref';
2+
3+
4+
export class BaseSnackBarContent<T> {
5+
/** The instance of the component making up the content of the snack bar. */
6+
snackBarRef: MdSnackBarRef<T>;
7+
8+
/** Dismisses the snack bar. */
9+
dismiss(): void {
10+
this.snackBarRef.dismiss();
11+
}
12+
}

src/lib/snack-bar/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './snack-bar';

src/lib/snack-bar/package.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "@angular2-material/snack-bar",
3+
"version": "2.0.0-alpha.8-1",
4+
"description": "Angular 2 Material snack bar",
5+
"main": "./snack-bar.umd.js",
6+
"module": "./index.js",
7+
"typings": "./index.d.ts",
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/angular/material2.git"
11+
},
12+
"keywords": [
13+
"angular",
14+
"material",
15+
"material design",
16+
"components",
17+
"snackbar",
18+
"toast",
19+
"notification"
20+
],
21+
"license": "MIT",
22+
"bugs": {
23+
"url": "https://github.com/angular/material2/issues"
24+
},
25+
"homepage": "https://github.com/angular/material2#readme",
26+
"peerDependencies": {
27+
"@angular2-material/core": "2.0.0-alpha.8-1"
28+
}
29+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<span class="message">{{message}}</span>
2+
<button md-button *ngIf="hasAction" (click)="dismiss()">{{action}}</button>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
:host {
2+
display: flex;
3+
justify-content: space-between;
4+
5+
span {
6+
box-sizing: border-box;
7+
border: none;
8+
color: white;
9+
font-family: Roboto, 'Helvetica Neue', sans-serif;
10+
font-size: 14px;
11+
line-height: 20px;
12+
outline: none;
13+
text-decoration: none;
14+
word-break: break-all;
15+
}
16+
17+
button {
18+
box-sizing: border-box;
19+
color: white;
20+
float: right;
21+
font-weight: 600;
22+
line-height: 20px;
23+
margin: -5px 0 0 48px;
24+
min-width: initial;
25+
padding: 5px;
26+
text-transform: uppercase;
27+
}
28+
}

src/lib/snack-bar/simple-snack-bar.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {Component} from '@angular/core';
2+
import {BaseSnackBarContent} from './base-snack-bar';
3+
4+
5+
@Component({
6+
moduleId: module.id,
7+
selector: 'simple-snack-bar',
8+
templateUrl: 'simple-snack-bar.html',
9+
styleUrls: ['simple-snack-bar.css'],
10+
})
11+
export class SimpleSnackBar extends BaseSnackBarContent<SimpleSnackBar> {
12+
/** The message to be shown in the snack bar. */
13+
message: string;
14+
15+
/** The label for the button in the snack bar. */
16+
action: string;
17+
18+
/** If the action button should be shown. */
19+
get hasAction(): boolean { return !!this.action; }
20+
}

src/lib/snack-bar/snack-bar-config.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {ViewContainerRef} from '@angular/core';
2+
3+
4+
export type SnackBarRole = 'alert' | 'polite';
5+
6+
export class MdSnackBarConfig {
7+
/** The aria-role of the snack bar. */
8+
role: SnackBarRole = 'alert';
9+
10+
/** The view container to place the overlay for the snack bar into. */
11+
viewContainerRef: ViewContainerRef;
12+
13+
constructor(viewContainerRef: ViewContainerRef) {
14+
this.viewContainerRef = viewContainerRef;
15+
}
16+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<template portalHost></template>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@import '../core/style/elevation';
2+
3+
$md-snack-bar-padding: 14px 24px !default;
4+
$md-snack-bar-height: 20px !default;
5+
$md-snack-bar-min-width: 288px !default;
6+
$md-snack-bar-max-width: 568px !default;
7+
8+
9+
:host {
10+
background: #323232;
11+
border-radius: 2px;
12+
display: block;
13+
height: $md-snack-bar-height;
14+
@include md-elevation(24);
15+
max-width: $md-snack-bar-max-width;
16+
min-width: $md-snack-bar-min-width;
17+
overflow: hidden;
18+
padding: $md-snack-bar-padding;
19+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {
2+
Component,
3+
ComponentRef,
4+
ViewChild
5+
} from '@angular/core';
6+
import {
7+
BasePortalHost,
8+
ComponentPortal,
9+
TemplatePortal,
10+
PortalHostDirective} from '@angular2-material/core';
11+
import {MdSnackBarConfig} from './snack-bar-config';
12+
import {MdSnackBarContentAlreadyAttached} from './snack-bar-errors';
13+
14+
15+
/**
16+
* Internal component that wraps user-provided snack bar content.
17+
*/
18+
@Component({
19+
moduleId: module.id,
20+
selector: 'snack-bar-content',
21+
templateUrl: 'snack-bar-container.html',
22+
styleUrls: ['snack-bar-container.css'],
23+
host: {
24+
'[attr.role]': 'snackBarConfig?.role'
25+
}
26+
})
27+
export class MdSnackBarContainer extends BasePortalHost {
28+
/** The portal host inside of this container into which the snack bar content will be loaded. */
29+
@ViewChild(PortalHostDirective) private _portalHost: PortalHostDirective;
30+
31+
/** The snack bar configuration. */
32+
snackBarConfig: MdSnackBarConfig;
33+
34+
/** Attach a portal as content to this snack bar container. */
35+
attachComponentPortal<T>(portal: ComponentPortal<T>): ComponentRef<T> {
36+
if (this._portalHost.hasAttached()) {
37+
throw new MdSnackBarContentAlreadyAttached();
38+
}
39+
40+
return this._portalHost.attachComponentPortal(portal);
41+
}
42+
43+
attachTemplatePortal(portal: TemplatePortal): Map<string, any> {
44+
throw Error('Not yet implemented');
45+
}
46+
}

src/lib/snack-bar/snack-bar-errors.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import {MdError} from '@angular2-material/core';
2+
3+
4+
export class MdSnackBarContentAlreadyAttached extends MdError {
5+
constructor() {
6+
super('Attempting to attach snack bar content after content is already attached');
7+
}
8+
}

src/lib/snack-bar/snack-bar-ref.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import {OverlayRef} from '@angular2-material/core';
2+
import {Observable} from 'rxjs/Observable';
3+
import {Subject} from 'rxjs/Subject';
4+
5+
// TODO(josephperrott): Implement onAction observable.
6+
7+
8+
/**
9+
* Reference to a snack bar dispatched from the snack bar service.
10+
*/
11+
export class MdSnackBarRef<T> {
12+
/** The instance of the component making up the content of the snack bar. */
13+
readonly instance: T|any;
14+
15+
/** Subject for notifying the user that the snack bar has closed. */
16+
private _afterClosed: Subject<any> = new Subject();
17+
18+
/** If the snack bar is active. */
19+
private _isActive: boolean = true;
20+
21+
constructor(instance: T|any, private _overlayRef: OverlayRef) {
22+
// Sets the readonly instance of the snack bar content component.
23+
this.instance = instance;
24+
this.afterDismissed().subscribe(null, null, () => { this._isActive = false; });
25+
}
26+
27+
28+
/** Dismisses the snack bar. */
29+
dismiss(): void {
30+
if (this._isActive) {
31+
this._overlayRef.dispose();
32+
this._afterClosed.complete();
33+
}
34+
}
35+
36+
37+
/** Gets an observable that is notified when the snack bar is finished closing. */
38+
afterDismissed(): Observable<void> {
39+
return this._afterClosed.asObservable();
40+
}
41+
}

0 commit comments

Comments
 (0)