Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

Commit

Permalink
Steve's comments. Added fading close to toasts
Browse files Browse the repository at this point in the history
  • Loading branch information
blackbaud-conorwright committed Apr 10, 2018
1 parent 28c09b6 commit 3b5d5cb
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 52 deletions.
1 change: 0 additions & 1 deletion src/demos/toast/toast-demo.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<div>
<button (click)='openMessage()'>Open toast</button>
<sky-toast></sky-toast>
</div>
30 changes: 30 additions & 0 deletions src/modules/toast/toast-adapter.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
Injectable,
Renderer2,
RendererFactory2
} from '@angular/core';

import { SkyWindowRefService } from '../window';

@Injectable()
export class SkyToastAdapterService {
private renderer: Renderer2;

constructor(
private rendererFactory: RendererFactory2,
private windowRef: SkyWindowRefService
) {
this.renderer = this.rendererFactory.createRenderer(undefined, undefined);
}

public appendToBody(element: any): void {
const body = this.windowRef.getWindow().document.body;
this.renderer.appendChild(body, element);
}

public removeHostElement(): void {
const document = this.windowRef.getWindow().document;
const hostElement = document.querySelector('sky-toast');
this.renderer.removeChild(document.body, hostElement);
}
}
9 changes: 7 additions & 2 deletions src/modules/toast/toast.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@
[ngClass]="{'sky-toast-info': message.toastType=='info',
'sky-toast-warning': message.toastType=='warning',
'sky-toast-danger': message.toastType=='danger',
'sky-toast-success': message.toastType=='success'}">
<div class='sky-toast-content'>{{message.message}}</div>
'sky-toast-success': message.toastType=='success',
'sky-toast-closing': message.isClosing | async}">
<div
(click)="message.close()"
class='sky-toast-content'>
{{message.message}}
</div>
<button
type="button"
class="sky-toast-close"
Expand Down
27 changes: 25 additions & 2 deletions src/modules/toast/toast.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
position: fixed;
padding-bottom: $sky-margin-double;
padding-right: $sky-margin-double;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Chrom and Opera */
}

.sky-toast {
Expand All @@ -18,6 +24,8 @@
display: flex;
flex-direction: row;
align-items: center;
opacity: 1;
cursor: pointer;

.sky-toast-content {
padding-top: $sky-padding;
Expand All @@ -34,15 +42,30 @@
}
}



button {
margin-left: auto;
width: 32px;
height: 32px;
}
}

.sky-toast:hover {
-webkit-box-shadow:0 0 8px rgba(0,0,0,.40);
-moz-box-shadow:0 0 8px rgba(0,0,0,.40);
-o-box-shadow:0 0 8px rgba(0,0,0,.40);
box-shadow:0 0 8px rgba(0,0,0,.40);
}


.sky-toast-closing {
opacity: 0;
-webkit-transition: opacity linear 0.5s;
-moz-transition: opacity linear 0.5s;
-ms-transition: opacity linear 0.5s;
-o-transition: opacity linear 0.5s;
transition: opacity linear 0.5s;
}

.sky-toast-info {
background-color: $sky-background-color-info;
border-color: $sky-highlight-color-info;
Expand Down
5 changes: 4 additions & 1 deletion src/modules/toast/toast.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
import { SkyToastService } from './toast.service';
import { SkyToastComponent } from './toast.component';
import { SkyResourcesModule } from '../resources';
import { SkyToastAdapterService } from './toast-adapter.service';

export { SkyToastService }

Expand All @@ -17,9 +18,11 @@ export { SkyToastService }
SkyToastComponent
],
providers: [
SkyToastService
SkyToastService,
SkyToastAdapterService
],
entryComponents: [
SkyToastComponent
]
})
export class SkyToastModule {
Expand Down
83 changes: 37 additions & 46 deletions src/modules/toast/toast.service.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,46 @@
import { Injectable } from '@angular/core';
import { Injectable, ComponentRef, ComponentFactoryResolver, Injector, ApplicationRef, EmbeddedViewRef } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';

export enum ToastType {
Info,
Success,
Warning,
Danger
}

export class Message {
public timeout?: NodeJS.Timer;
private _isClosed: BehaviorSubject<boolean> = new BehaviorSubject(false);
public get isClosed(): Observable<boolean> { return this._isClosed.asObservable() }

constructor(public message: string, public toastType: string, private removeFromQueue: Function, timeout?: number) {
if (timeout) {
this.timeout = setTimeout(this.close, timeout);
}
}

public close = () => {
if (this.timeout) {
clearTimeout(this.timeout);
}
this.removeFromQueue(this);
this._isClosed.next(true);
}
}
export interface ToastConfig {
message?: string,
disableTimeout?: boolean,
timeout?: number,
toastType?: ToastType
}
import { SkyToastComponent } from './toast.component';
import { SkyToastAdapterService } from './toast-adapter.service';
import { SkyToastMessage, ToastConfig, SkyToastType } from './types';

@Injectable()
export class SkyToastService {
private host: ComponentRef<SkyToastComponent>;

private _messages: Message[] = [];
private _messageList: BehaviorSubject<Message[]> = new BehaviorSubject([]);
public get getMessages(): Observable<Message[]> { return this._messageList.asObservable() }
private _messages: SkyToastMessage[] = [];
private _messageList: BehaviorSubject<SkyToastMessage[]> = new BehaviorSubject([]);
public get getMessages(): Observable<SkyToastMessage[]> { return this._messageList.asObservable() }

constructor() {}
constructor(
private appRef: ApplicationRef,
private injector: Injector,
private resolver: ComponentFactoryResolver,
private adapter: SkyToastAdapterService) {}

public openMessage(message: string, config: ToastConfig = {}) {
config.message = message;
return this.open(config);
}

public open(config: ToastConfig) {
let message: Message = this.createMessage(config);
if (!this.host) {
this.host = this.createHostComponent();
}

let message: SkyToastMessage = this.createMessage(config);
this._messages.push(message);
this._messageList.next(this._messages);

return message;
}

private removeFromQueue: Function = (message: Message) => {
private removeFromQueue: Function = (message: SkyToastMessage) => {
if (this._messages.length == 0) {
throw 'The supplied message is not active.';
}
let foundMessage: Message = this._messages.reduce((prev, cur) => prev === message ? prev : cur);
let foundMessage: SkyToastMessage = this._messages.reduce((prev, cur) => prev === message ? prev : cur);
if (!foundMessage) {
throw 'The supplied message is not active.';
}
Expand All @@ -71,7 +49,7 @@ export class SkyToastService {
this._messageList.next(this._messages);
};

private createMessage(config: ToastConfig): Message {
private createMessage(config: ToastConfig): SkyToastMessage {
if (!config.message) {
throw 'A message must be provided.';
}
Expand All @@ -83,17 +61,30 @@ export class SkyToastService {

let toastType: string = 'info';
switch(config.toastType) {
case ToastType.Success:
case SkyToastType.Success:
toastType = 'success';
break;
case ToastType.Warning:
case SkyToastType.Warning:
toastType = 'warning';
break;
case ToastType.Danger:
case SkyToastType.Danger:
toastType = 'danger';
break;
}

return new Message(config.message, toastType, this.removeFromQueue, timeout);
return new SkyToastMessage(config.message, toastType, this.removeFromQueue, timeout);
}

private createHostComponent(): ComponentRef<SkyToastComponent> {
const componentRef = this.resolver
.resolveComponentFactory(SkyToastComponent)
.create(this.injector);

const domElem = (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0];

this.appRef.attachView(componentRef.hostView);
this.adapter.appendToBody(domElem);

return componentRef;
}
}
3 changes: 3 additions & 0 deletions src/modules/toast/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './toast-config';
export * from './toast-message';
export * from './toast-message-type';
8 changes: 8 additions & 0 deletions src/modules/toast/types/toast-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { SkyToastType } from './toast-message-type';

export interface ToastConfig {
message?: string,
disableTimeout?: boolean,
timeout?: number,
toastType?: SkyToastType
}
6 changes: 6 additions & 0 deletions src/modules/toast/types/toast-message-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum SkyToastType {
Info,
Success,
Warning,
Danger
}
34 changes: 34 additions & 0 deletions src/modules/toast/types/toast-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { SkyToastMessageType } from './toast-message-type';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Observable } from 'rxjs/Observable';

export class SkyToastMessage {
public timeout?: NodeJS.Timer;
private _isClosed: BehaviorSubject<boolean>;
private _isClosing: BehaviorSubject<boolean>;
public isClosed: Observable<boolean>;
public isClosing: Observable<boolean>;

constructor(public message: string, public toastType: string, private removeFromQueue: Function, timeout?: number) {
this._isClosed = new BehaviorSubject(false);
this._isClosing = new BehaviorSubject(false);
this.isClosed = this._isClosed.asObservable();
this.isClosing = this._isClosing.asObservable();
if (timeout) {
this.timeout = setTimeout(this.close, timeout);
}
}

public close = () => {
if (this.timeout) {
clearTimeout(this.timeout);
}
if (!this._isClosing.getValue()) {
this._isClosing.next(true);
setTimeout(() => {
this.removeFromQueue(this);
this._isClosed.next(true);
}, 500);
}
}
}

0 comments on commit 3b5d5cb

Please sign in to comment.