mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 02:24:11 +00:00
notification start
This commit is contained in:
@@ -3,3 +3,17 @@
|
|||||||
<ds-search-form></ds-search-form>
|
<ds-search-form></ds-search-form>
|
||||||
<ds-top-level-community-list></ds-top-level-community-list>
|
<ds-top-level-community-list></ds-top-level-community-list>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<button (click)="createNotification()">Notify!</button>
|
||||||
|
|
||||||
|
<!--<ds-notifications-board-->
|
||||||
|
<!--[options]="notificationOptions"-->
|
||||||
|
<!--(onCreate)="notificationCreated($event)"-->
|
||||||
|
<!--(onDestroy)="notificationDestroyed($event)">-->
|
||||||
|
<!--</ds-notifications-board>-->
|
||||||
|
|
||||||
|
|
||||||
|
<ng-template #example>
|
||||||
|
<p>Simple example</p>
|
||||||
|
<button [routerLink]="['/mydspace']">Go to mydspace</button>
|
||||||
|
</ng-template>
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||||
|
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||||
|
import { Options } from '../shared/notifications/interfaces/options.type';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-home-page',
|
selector: 'ds-home-page',
|
||||||
@@ -6,5 +8,39 @@ import { Component } from '@angular/core';
|
|||||||
templateUrl: './home-page.component.html'
|
templateUrl: './home-page.component.html'
|
||||||
})
|
})
|
||||||
export class HomePageComponent {
|
export class HomePageComponent {
|
||||||
|
public notificationOptions: Options = {
|
||||||
|
position: ['top', 'right'],
|
||||||
|
timeOut: 0,
|
||||||
|
lastOnBottom: true,
|
||||||
|
clickIconToClose: false,
|
||||||
|
showProgressBar: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
@ViewChild('example') example: TemplateRef<any>;
|
||||||
|
|
||||||
|
constructor(private notificationsService: NotificationsService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
createNotification() {
|
||||||
|
const n1 = this.notificationsService.success('Welcome in DSpace', 'Good choice!',
|
||||||
|
{
|
||||||
|
showProgressBar: false,
|
||||||
|
animate: 'rotate',
|
||||||
|
timeout: 2000});
|
||||||
|
const n2 = this.notificationsService.error('Error in DSpace', 'This is a fake error!');
|
||||||
|
const n3 = this.notificationsService.info(this.example);
|
||||||
|
console.log('Notifications pushed');
|
||||||
|
console.log(n1);
|
||||||
|
console.log(n2);
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationCreated(event) {
|
||||||
|
console.log('Notification created');
|
||||||
|
console.log(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationDestroyed() {
|
||||||
|
console.log('Notification destroyed');
|
||||||
|
console.log(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,8 @@ import { HomePageRoutingModule } from './home-page-routing.module';
|
|||||||
|
|
||||||
import { HomePageComponent } from './home-page.component';
|
import { HomePageComponent } from './home-page.component';
|
||||||
import { TopLevelCommunityListComponent } from './top-level-community-list/top-level-community-list.component';
|
import { TopLevelCommunityListComponent } from './top-level-community-list/top-level-community-list.component';
|
||||||
|
import { NotificationComponent } from '../shared/notifications/notification/notification.component';
|
||||||
|
import { NotificationsBoardComponent } from '../shared/notifications/notifications-board/notifications-board.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -16,7 +18,9 @@ import { TopLevelCommunityListComponent } from './top-level-community-list/top-l
|
|||||||
declarations: [
|
declarations: [
|
||||||
HomePageComponent,
|
HomePageComponent,
|
||||||
TopLevelCommunityListComponent,
|
TopLevelCommunityListComponent,
|
||||||
HomeNewsComponent
|
HomeNewsComponent,
|
||||||
|
// NotificationComponent,
|
||||||
|
// NotificationsBoardComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class HomePageModule {
|
export class HomePageModule {
|
||||||
|
@@ -2,6 +2,14 @@
|
|||||||
<div class="inner-wrapper">
|
<div class="inner-wrapper">
|
||||||
<ds-header></ds-header>
|
<ds-header></ds-header>
|
||||||
|
|
||||||
|
<p>Inizio notifiche</p>
|
||||||
|
<ds-notifications-board
|
||||||
|
[options]="notificationOptions"
|
||||||
|
(onCreate)="notificationCreated($event)"
|
||||||
|
(onDestroy)="notificationDestroyed($event)">
|
||||||
|
</ds-notifications-board>
|
||||||
|
<p>Fine notifiche</p>
|
||||||
|
|
||||||
<main class="main-content">
|
<main class="main-content">
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</main>
|
</main>
|
||||||
@@ -9,3 +17,5 @@
|
|||||||
<ds-footer></ds-footer>
|
<ds-footer></ds-footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@ import { MetadataService } from './core/metadata/metadata.service';
|
|||||||
import { HostWindowResizeAction } from './shared/host-window.actions';
|
import { HostWindowResizeAction } from './shared/host-window.actions';
|
||||||
import { HostWindowState } from './shared/host-window.reducer';
|
import { HostWindowState } from './shared/host-window.reducer';
|
||||||
import { NativeWindowRef, NativeWindowService } from './shared/window.service';
|
import { NativeWindowRef, NativeWindowService } from './shared/window.service';
|
||||||
|
import { Options } from './shared/notifications/interfaces/options.type';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-app',
|
selector: 'ds-app',
|
||||||
@@ -27,6 +28,24 @@ import { NativeWindowRef, NativeWindowService } from './shared/window.service';
|
|||||||
})
|
})
|
||||||
export class AppComponent implements OnInit {
|
export class AppComponent implements OnInit {
|
||||||
|
|
||||||
|
public notificationOptions: Options = {
|
||||||
|
position: ['top', 'right'],
|
||||||
|
timeOut: 5000,
|
||||||
|
lastOnBottom: true,
|
||||||
|
clickIconToClose: true,
|
||||||
|
showProgressBar: true
|
||||||
|
};
|
||||||
|
|
||||||
|
notificationCreated(event) {
|
||||||
|
console.log('Notification created');
|
||||||
|
console.log(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationDestroyed() {
|
||||||
|
console.log('Notification destroyed');
|
||||||
|
console.log(event);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(GLOBAL_CONFIG) public config: GlobalConfig,
|
@Inject(GLOBAL_CONFIG) public config: GlobalConfig,
|
||||||
@Inject(NativeWindowService) private _window: NativeWindowRef,
|
@Inject(NativeWindowService) private _window: NativeWindowRef,
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
import { APP_BASE_HREF, CommonModule } from '@angular/common';
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserTransferStateModule } from '@angular/platform-browser';
|
|
||||||
|
|
||||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
|
||||||
@@ -29,6 +28,9 @@ import { HeaderComponent } from './header/header.component';
|
|||||||
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component';
|
||||||
|
|
||||||
import { DSpaceRouterStateSerializer } from './shared/ngrx/dspace-router-state-serializer';
|
import { DSpaceRouterStateSerializer } from './shared/ngrx/dspace-router-state-serializer';
|
||||||
|
import { NotificationsService } from './shared/notifications/notifications.service';
|
||||||
|
import { NotificationsBoardComponent } from './shared/notifications/notifications-board/notifications-board.component';
|
||||||
|
import { NotificationComponent } from './shared/notifications/notification/notification.component';
|
||||||
|
|
||||||
export function getConfig() {
|
export function getConfig() {
|
||||||
return ENV_CONFIG;
|
return ENV_CONFIG;
|
||||||
@@ -85,7 +87,9 @@ if (!ENV_CONFIG.production) {
|
|||||||
AppComponent,
|
AppComponent,
|
||||||
HeaderComponent,
|
HeaderComponent,
|
||||||
FooterComponent,
|
FooterComponent,
|
||||||
PageNotFoundComponent
|
PageNotFoundComponent,
|
||||||
|
NotificationComponent,
|
||||||
|
NotificationsBoardComponent
|
||||||
],
|
],
|
||||||
exports: [AppComponent]
|
exports: [AppComponent]
|
||||||
})
|
})
|
||||||
|
@@ -38,6 +38,7 @@ import { SubmissionDefinitionsConfigService } from './config/submission-definiti
|
|||||||
import { SubmissionFormsConfigService } from './config/submission-forms-config.service';
|
import { SubmissionFormsConfigService } from './config/submission-forms-config.service';
|
||||||
import { SubmissionSectionsConfigService } from './config/submission-sections-config.service';
|
import { SubmissionSectionsConfigService } from './config/submission-sections-config.service';
|
||||||
import { UUIDService } from './shared/uuid.service';
|
import { UUIDService } from './shared/uuid.service';
|
||||||
|
import { NotificationsService } from '../shared/notifications/notifications.service';
|
||||||
|
|
||||||
const IMPORTS = [
|
const IMPORTS = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -77,6 +78,7 @@ const PROVIDERS = [
|
|||||||
SubmissionFormsConfigService,
|
SubmissionFormsConfigService,
|
||||||
SubmissionSectionsConfigService,
|
SubmissionSectionsConfigService,
|
||||||
UUIDService,
|
UUIDService,
|
||||||
|
NotificationsService,
|
||||||
{ provide: NativeWindowService, useFactory: NativeWindowFactory }
|
{ provide: NativeWindowService, useFactory: NativeWindowFactory }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
41
src/app/shared/notifications/interfaces/icons.ts
Normal file
41
src/app/shared/notifications/interfaces/icons.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
export interface Icons {
|
||||||
|
alert: string;
|
||||||
|
error: string;
|
||||||
|
info: string;
|
||||||
|
warn: string;
|
||||||
|
success: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultIcons: Icons = {
|
||||||
|
alert: `
|
||||||
|
<svg class="simple-notification-svg" xmlns="http://www.w3.org/2000/svg" fill="#ffffff" height="24" viewBox="0 0 24 24" width="24">
|
||||||
|
<path d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M22 5.72l-4.6-3.86-1.29 1.53 4.6 3.86L22 5.72zM7.88 3.39L6.6 1.86 2 5.71l1.29 1.53 4.59-3.85zM12.5 8H11v6l4.75 2.85.75-1.23-4-2.37V8zM12 4c-4.97 0-9 4.03-9 9s4.02 9 9 9c4.97 0 9-4.03 9-9s-4.03-9-9-9zm0 16c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/>
|
||||||
|
</svg>
|
||||||
|
`,
|
||||||
|
error: `
|
||||||
|
<svg class="simple-notification-svg" xmlns="http://www.w3.org/2000/svg" fill="#ffffff" height="24" viewBox="0 0 24 24" width="24">
|
||||||
|
<path d="M0 0h24v24H0V0z" fill="none"/>
|
||||||
|
<path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/>
|
||||||
|
</svg>
|
||||||
|
`,
|
||||||
|
info: `
|
||||||
|
<svg class="simple-notification-svg" xmlns="http://www.w3.org/2000/svg" fill="#ffffff" height="24" viewBox="0 0 24 24" width="24">
|
||||||
|
<path d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M11 17h2v-6h-2v6zm1-15C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zM11 9h2V7h-2v2z"/>
|
||||||
|
</svg>
|
||||||
|
`,
|
||||||
|
success: `
|
||||||
|
<svg class="simple-notification-svg" xmlns="http://www.w3.org/2000/svg" fill="#ffffff" height="24" viewBox="0 0 24 24" width="24">
|
||||||
|
<path d="M0 0h24v24H0z" fill="none"/>
|
||||||
|
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/>
|
||||||
|
</svg>
|
||||||
|
`,
|
||||||
|
warn: `
|
||||||
|
<svg class="simple-notification-svg" xmlns="http://www.w3.org/2000/svg" fill="#ffffff" width="64" viewBox="0 0 64 64" height="64">
|
||||||
|
<circle cx="32.086" cy="50.142" r="2.256"/>
|
||||||
|
<path d="M30.08 25.012V42.32c0 1.107.897 2.005 2.006 2.005s2.006-.897 2.006-2.005V25.012c0-1.107-.897-2.006-2.006-2.006s-2.006.898-2.006 2.006z"/>
|
||||||
|
<path d="M63.766 59.234L33.856 3.082c-.697-1.308-2.844-1.308-3.54 0L.407 59.234c-.331.622-.312 1.372.051 1.975.362.605 1.015.975 1.72.975h59.816c.705 0 1.357-.369 1.721-.975.361-.603.381-1.353.051-1.975zM5.519 58.172L32.086 8.291l26.568 49.881H5.519z"/>
|
||||||
|
</svg>
|
||||||
|
`
|
||||||
|
};
|
@@ -0,0 +1,8 @@
|
|||||||
|
import {Notification} from './notification.type';
|
||||||
|
|
||||||
|
export interface NotificationEvent {
|
||||||
|
add?: boolean;
|
||||||
|
command: string;
|
||||||
|
id?: string;
|
||||||
|
notification?: Notification;
|
||||||
|
}
|
24
src/app/shared/notifications/interfaces/notification.type.ts
Normal file
24
src/app/shared/notifications/interfaces/notification.type.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import {EventEmitter} from '@angular/core';
|
||||||
|
|
||||||
|
export interface Notification {
|
||||||
|
id?: string
|
||||||
|
type: string
|
||||||
|
icon: string
|
||||||
|
title?: any
|
||||||
|
content?: any
|
||||||
|
override?: any
|
||||||
|
html?: any
|
||||||
|
state?: string
|
||||||
|
createdOn?: Date
|
||||||
|
destroyedOn?: Date
|
||||||
|
animate?: string
|
||||||
|
timeOut?: number
|
||||||
|
maxLength?: number
|
||||||
|
pauseOnHover?: boolean
|
||||||
|
clickToClose?: boolean
|
||||||
|
clickIconToClose?: boolean
|
||||||
|
theClass?: string
|
||||||
|
click?: EventEmitter<{}>;
|
||||||
|
clickIcon?: EventEmitter<{}>;
|
||||||
|
timeoutEnd?: EventEmitter<{}>;
|
||||||
|
}
|
19
src/app/shared/notifications/interfaces/options.type.ts
Normal file
19
src/app/shared/notifications/interfaces/options.type.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import {Icons} from './icons';
|
||||||
|
|
||||||
|
export interface Options {
|
||||||
|
timeOut?: number;
|
||||||
|
showProgressBar?: boolean;
|
||||||
|
pauseOnHover?: boolean;
|
||||||
|
lastOnBottom?: boolean;
|
||||||
|
clickToClose?: boolean;
|
||||||
|
clickIconToClose?: boolean;
|
||||||
|
maxLength?: number;
|
||||||
|
maxStack?: number;
|
||||||
|
preventDuplicates?: boolean;
|
||||||
|
preventLastDuplicates?: boolean | string;
|
||||||
|
theClass?: string;
|
||||||
|
rtl?: boolean;
|
||||||
|
animate?: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale';
|
||||||
|
icons?: Icons;
|
||||||
|
position?: ['top' | 'bottom' | 'middle', 'right' | 'left' | 'center'];
|
||||||
|
}
|
@@ -0,0 +1,54 @@
|
|||||||
|
<div class="simple-notification"
|
||||||
|
[@enterLeave]="item.state"
|
||||||
|
(click)="onClick($event)"
|
||||||
|
[class]="theClass"
|
||||||
|
[ngClass]="{
|
||||||
|
'alert': item.type === 'alert',
|
||||||
|
'error': item.type === 'error',
|
||||||
|
'warn': item.type === 'warn',
|
||||||
|
'success': item.type === 'success',
|
||||||
|
'info': item.type === 'info',
|
||||||
|
'bare': item.type === 'bare',
|
||||||
|
'rtl-mode': rtl,
|
||||||
|
'has-icon': item.icon !== 'bare'
|
||||||
|
}"
|
||||||
|
(mouseenter)="onEnter()"
|
||||||
|
(mouseleave)="onLeave()">
|
||||||
|
|
||||||
|
<div *ngIf="!item.html">
|
||||||
|
|
||||||
|
<div class="sn-title" *ngIf="titleIsTemplate; else regularTitle">
|
||||||
|
<ng-container *ngTemplateOutlet="title"></ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template #regularTitle>
|
||||||
|
<div class="sn-title" [innerHTML]="title"></div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="sn-content" *ngIf="contentIsTemplate; else regularContent">
|
||||||
|
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template #regularContent>
|
||||||
|
<div class="sn-content" [innerHTML]="content"></div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="icon" *ngIf="item.icon !== 'bare'" [innerHTML]="safeSvg"></div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="item.html">
|
||||||
|
<div class="sn-html" *ngIf="htmlIsTemplate; else regularHtml">
|
||||||
|
<ng-container *ngTemplateOutlet="item.html"></ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template #regularHtml>
|
||||||
|
<div class="sn-content" [innerHTML]="item.html"></div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="icon" [class.icon-hover]="clickIconToClose" *ngIf="item.icon" [innerHTML]="safeSvg" (click)="onClickIcon($event)"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sn-progress-loader" *ngIf="showProgressBar">
|
||||||
|
<span [ngStyle]="{'width': progressWidth + '%'}"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
@@ -0,0 +1,107 @@
|
|||||||
|
.simple-notification {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
float: left;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.5s;
|
||||||
|
min-height: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification .sn-title,
|
||||||
|
.simple-notification .sn-content,
|
||||||
|
.simple-notification .sn-html {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification .sn-title {
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification .sn-content {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification.has-icon .sn-title,
|
||||||
|
.simple-notification.has-icon .sn-content,
|
||||||
|
.simple-notification.has-icon .sn-html {
|
||||||
|
padding: 0 50px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification .icon {
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 70px;
|
||||||
|
height: 70px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification .icon.icon-hover:hover {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification .icon svg {
|
||||||
|
fill: #fff;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification .icon svg g {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification.rtl-mode.has-icon .sn-title,
|
||||||
|
.simple-notification.rtl-mode.has-icon .sn-content,
|
||||||
|
.simple-notification.rtl-mode.has-icon .sn-html {
|
||||||
|
padding: 0 0 0 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification.rtl-mode {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification.rtl-mode .sn-content {
|
||||||
|
padding: 0 0 0 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification.rtl-mode svg {
|
||||||
|
left: 0;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification.error { background: #F44336; }
|
||||||
|
.simple-notification.success { background: #8BC34A; }
|
||||||
|
.simple-notification.alert { background: #ffdb5b; }
|
||||||
|
.simple-notification.info { background: #03A9F4; }
|
||||||
|
.simple-notification.warn { background: #ffdb5b; }
|
||||||
|
|
||||||
|
.simple-notification .sn-progress-loader {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification .sn-progress-loader span {
|
||||||
|
float: left;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification.success .sn-progress-loader span { background: #689F38; }
|
||||||
|
.simple-notification.error .sn-progress-loader span { background: #D32F2F; }
|
||||||
|
.simple-notification.alert .sn-progress-loader span { background: #edc242; }
|
||||||
|
.simple-notification.info .sn-progress-loader span { background: #0288D1; }
|
||||||
|
.simple-notification.warn .sn-progress-loader span { background: #edc242; }
|
||||||
|
.simple-notification.bare .sn-progress-loader span { background: #ccc; }
|
||||||
|
|
||||||
|
.simple-notification.warn div .sn-title,
|
||||||
|
.simple-notification.warn div .sn-content,
|
||||||
|
.simple-notification.warn div .sn-html { color: #444; }
|
@@ -0,0 +1,259 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
Input,
|
||||||
|
NgZone,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
TemplateRef,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||||
|
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||||
|
import { Notification } from '../interfaces/notification.type';
|
||||||
|
import { NotificationsService } from '../notifications.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-notification',
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
animations: [
|
||||||
|
trigger('enterLeave', [
|
||||||
|
|
||||||
|
// Fade
|
||||||
|
state('fade', style({opacity: 1})),
|
||||||
|
transition('* => fade', [
|
||||||
|
style({opacity: 0}),
|
||||||
|
animate('400ms ease-in-out')
|
||||||
|
]),
|
||||||
|
state('fadeOut', style({opacity: 0})),
|
||||||
|
transition('fade => fadeOut', [
|
||||||
|
style({opacity: 1}),
|
||||||
|
animate('300ms ease-in-out')
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Enter from top
|
||||||
|
state('fromTop', style({opacity: 1, transform: 'translateY(0)'})),
|
||||||
|
transition('* => fromTop', [
|
||||||
|
style({opacity: 0, transform: 'translateY(-5%)'}),
|
||||||
|
animate('400ms ease-in-out')
|
||||||
|
]),
|
||||||
|
state('fromTopOut', style({opacity: 0, transform: 'translateY(5%)'})),
|
||||||
|
transition('fromTop => fromTopOut', [
|
||||||
|
style({opacity: 1, transform: 'translateY(0)'}),
|
||||||
|
animate('300ms ease-in-out')
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Enter from right
|
||||||
|
state('fromRight', style({opacity: 1, transform: 'translateX(0)'})),
|
||||||
|
transition('* => fromRight', [
|
||||||
|
style({opacity: 0, transform: 'translateX(5%)'}),
|
||||||
|
animate('400ms ease-in-out')
|
||||||
|
]),
|
||||||
|
state('fromRightOut', style({opacity: 0, transform: 'translateX(-5%)'})),
|
||||||
|
transition('fromRight => fromRightOut', [
|
||||||
|
style({opacity: 1, transform: 'translateX(0)'}),
|
||||||
|
animate('300ms ease-in-out')
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Enter from bottom
|
||||||
|
state('fromBottom', style({opacity: 1, transform: 'translateY(0)'})),
|
||||||
|
transition('* => fromBottom', [
|
||||||
|
style({opacity: 0, transform: 'translateY(5%)'}),
|
||||||
|
animate('400ms ease-in-out')
|
||||||
|
]),
|
||||||
|
state('fromBottomOut', style({opacity: 0, transform: 'translateY(-5%)'})),
|
||||||
|
transition('fromBottom => fromBottomOut', [
|
||||||
|
style({opacity: 1, transform: 'translateY(0)'}),
|
||||||
|
animate('300ms ease-in-out')
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Enter from left
|
||||||
|
state('fromLeft', style({opacity: 1, transform: 'translateX(0)'})),
|
||||||
|
transition('* => fromLeft', [
|
||||||
|
style({opacity: 0, transform: 'translateX(-5%)'}),
|
||||||
|
animate('400ms ease-in-out')
|
||||||
|
]),
|
||||||
|
state('fromLeftOut', style({opacity: 0, transform: 'translateX(5%)'})),
|
||||||
|
transition('fromLeft => fromLeftOut', [
|
||||||
|
style({opacity: 1, transform: 'translateX(0)'}),
|
||||||
|
animate('300ms ease-in-out')
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Rotate
|
||||||
|
state('scale', style({opacity: 1, transform: 'scale(1)'})),
|
||||||
|
transition('* => scale', [
|
||||||
|
style({opacity: 0, transform: 'scale(0)'}),
|
||||||
|
animate('400ms ease-in-out')
|
||||||
|
]),
|
||||||
|
state('scaleOut', style({opacity: 0, transform: 'scale(0)'})),
|
||||||
|
transition('scale => scaleOut', [
|
||||||
|
style({opacity: 1, transform: 'scale(1)'}),
|
||||||
|
animate('400ms ease-in-out')
|
||||||
|
]),
|
||||||
|
|
||||||
|
// Scale
|
||||||
|
state('rotate', style({opacity: 1, transform: 'rotate(0deg)'})),
|
||||||
|
transition('* => rotate', [
|
||||||
|
style({opacity: 0, transform: 'rotate(5deg)'}),
|
||||||
|
animate('400ms ease-in-out')
|
||||||
|
]),
|
||||||
|
state('rotateOut', style({opacity: 0, transform: 'rotate(-5deg)'})),
|
||||||
|
transition('rotate => rotateOut', [
|
||||||
|
style({opacity: 1, transform: 'rotate(0deg)'}),
|
||||||
|
animate('400ms ease-in-out')
|
||||||
|
])
|
||||||
|
])
|
||||||
|
],
|
||||||
|
templateUrl: './notification.component.html',
|
||||||
|
styleUrls: ['./notification.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
|
||||||
|
export class NotificationComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
@Input() public timeOut: number;
|
||||||
|
@Input() public showProgressBar: boolean;
|
||||||
|
@Input() public pauseOnHover: boolean;
|
||||||
|
@Input() public clickToClose: boolean;
|
||||||
|
@Input() public clickIconToClose: boolean;
|
||||||
|
@Input() public maxLength: number;
|
||||||
|
@Input() public theClass: string;
|
||||||
|
@Input() public rtl: boolean;
|
||||||
|
@Input() public animate: string;
|
||||||
|
@Input() public position: number;
|
||||||
|
@Input() public item: Notification;
|
||||||
|
|
||||||
|
// Progress bar variables
|
||||||
|
public title: any;
|
||||||
|
public content: any;
|
||||||
|
|
||||||
|
public titleIsTemplate = false;
|
||||||
|
public contentIsTemplate = false;
|
||||||
|
public htmlIsTemplate = false;
|
||||||
|
|
||||||
|
public progressWidth = 0;
|
||||||
|
public safeSvg: SafeHtml;
|
||||||
|
|
||||||
|
private stopTime = false;
|
||||||
|
private timer: any;
|
||||||
|
private steps: number;
|
||||||
|
private speed: number;
|
||||||
|
private count = 0;
|
||||||
|
private start: any;
|
||||||
|
|
||||||
|
private diff: any;
|
||||||
|
private icon: string;
|
||||||
|
|
||||||
|
constructor(private notificationService: NotificationsService,
|
||||||
|
private domSanitizer: DomSanitizer,
|
||||||
|
private cdr: ChangeDetectorRef,
|
||||||
|
private zone: NgZone) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (this.item.override) {
|
||||||
|
this.attachOverrides();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.animate) {
|
||||||
|
this.item.state = this.animate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.timeOut !== 0) {
|
||||||
|
this.startTimeOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.contentType(this.item.title, 'title');
|
||||||
|
this.contentType(this.item.content, 'content');
|
||||||
|
this.contentType(this.item.html, 'html');
|
||||||
|
|
||||||
|
this.safeSvg = this.domSanitizer.bypassSecurityTrustHtml(this.icon || this.item.icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
startTimeOut(): void {
|
||||||
|
this.steps = this.timeOut / 10;
|
||||||
|
this.speed = this.timeOut / this.steps;
|
||||||
|
this.start = new Date().getTime();
|
||||||
|
this.zone.runOutsideAngular(() => this.timer = setTimeout(this.instance, this.speed));
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnter(): void {
|
||||||
|
if (this.pauseOnHover) {
|
||||||
|
this.stopTime = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLeave(): void {
|
||||||
|
if (this.pauseOnHover) {
|
||||||
|
this.stopTime = false;
|
||||||
|
this.zone.runOutsideAngular(() => setTimeout(this.instance, (this.speed - this.diff)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick($e: MouseEvent): void {
|
||||||
|
this.item.click!.emit($e);
|
||||||
|
|
||||||
|
if (this.clickToClose) {
|
||||||
|
this.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickIcon($e: MouseEvent): void {
|
||||||
|
this.item.clickIcon!.emit($e);
|
||||||
|
|
||||||
|
if (this.clickIconToClose) {
|
||||||
|
this.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach all the overrides
|
||||||
|
attachOverrides(): void {
|
||||||
|
Object.keys(this.item.override).forEach((a) => {
|
||||||
|
if (this.hasOwnProperty(a)) {
|
||||||
|
(this as any)[a] = this.item.override[a];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private instance = () => {
|
||||||
|
this.diff = (new Date().getTime() - this.start) - (this.count * this.speed);
|
||||||
|
|
||||||
|
if (this.count++ === this.steps) {
|
||||||
|
this.remove();
|
||||||
|
this.item.timeoutEnd!.emit();
|
||||||
|
} else if (!this.stopTime) {
|
||||||
|
if (this.showProgressBar) {
|
||||||
|
this.progressWidth += 100 / this.steps;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.timer = setTimeout(this.instance, (this.speed - this.diff));
|
||||||
|
}
|
||||||
|
this.zone.run(() => this.cdr.detectChanges());
|
||||||
|
};
|
||||||
|
|
||||||
|
private remove() {
|
||||||
|
if (this.animate) {
|
||||||
|
this.item.state = this.animate + 'Out';
|
||||||
|
setTimeout(() => {
|
||||||
|
this.notificationService.set(this.item, false);
|
||||||
|
}, 310);
|
||||||
|
} else {
|
||||||
|
this.notificationService.set(this.item, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private contentType(item: any, key: string) {
|
||||||
|
if (item instanceof TemplateRef) {
|
||||||
|
this[key] = item;
|
||||||
|
} else {
|
||||||
|
this[key] = this.domSanitizer.bypassSecurityTrustHtml(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
this[key + 'IsTemplate'] = item instanceof TemplateRef;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
<div class="simple-notification-wrapper" [ngClass]="position">
|
||||||
|
<ds-notification
|
||||||
|
*ngFor="let a of notifications; let i = index"
|
||||||
|
[item]="a"
|
||||||
|
[timeOut]="timeOut"
|
||||||
|
[clickToClose]="clickToClose"
|
||||||
|
[clickIconToClose]="clickIconToClose"
|
||||||
|
[maxLength]="maxLength"
|
||||||
|
[showProgressBar]="showProgressBar"
|
||||||
|
[pauseOnHover]="pauseOnHover"
|
||||||
|
[theClass]="theClass"
|
||||||
|
[rtl]="rtl"
|
||||||
|
[animate]="animate"
|
||||||
|
[position]="i">
|
||||||
|
</ds-notification>
|
||||||
|
</div>
|
@@ -0,0 +1,43 @@
|
|||||||
|
.simple-notification-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
width: 300px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification-wrapper.left {
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification-wrapper.top {
|
||||||
|
top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification-wrapper.right {
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification-wrapper.bottom {
|
||||||
|
bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification-wrapper.center {
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification-wrapper.middle {
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.simple-notification-wrapper.middle.center {
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 340px) {
|
||||||
|
.simple-notification-wrapper {
|
||||||
|
width: auto;
|
||||||
|
left: 20px;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,222 @@
|
|||||||
|
import {
|
||||||
|
Component, EventEmitter, OnInit, OnDestroy, ViewEncapsulation, Input, Output,
|
||||||
|
ChangeDetectionStrategy, ChangeDetectorRef
|
||||||
|
} from '@angular/core';
|
||||||
|
import {Subscription} from 'rxjs/Subscription';
|
||||||
|
import {Options} from '../interfaces/options.type';
|
||||||
|
import {Notification} from '../interfaces/notification.type';
|
||||||
|
import {NotificationsService} from '../notifications.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-notifications-board',
|
||||||
|
encapsulation: ViewEncapsulation.None,
|
||||||
|
templateUrl: './notifications-board.component.html',
|
||||||
|
styleUrls: ['./notifications-board.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class NotificationsBoardComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
@Input() set options(opt: Options) {
|
||||||
|
this.attachChanges(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Output() onCreate = new EventEmitter();
|
||||||
|
@Output() onDestroy = new EventEmitter();
|
||||||
|
|
||||||
|
public notifications: Notification[] = [];
|
||||||
|
public position: ['top' | 'bottom' | 'middle', 'right' | 'left' | 'center'] = ['bottom', 'right'];
|
||||||
|
|
||||||
|
private lastNotificationCreated: Notification;
|
||||||
|
private listener: Subscription;
|
||||||
|
|
||||||
|
// Received values
|
||||||
|
private lastOnBottom = true;
|
||||||
|
private maxStack = 8;
|
||||||
|
private preventLastDuplicates: any = false;
|
||||||
|
private preventDuplicates = false;
|
||||||
|
|
||||||
|
// Sent values
|
||||||
|
public timeOut = 0;
|
||||||
|
public maxLength = 0;
|
||||||
|
public clickToClose = true;
|
||||||
|
public clickIconToClose = false;
|
||||||
|
public showProgressBar = true;
|
||||||
|
public pauseOnHover = true;
|
||||||
|
public theClass = '';
|
||||||
|
public rtl = false;
|
||||||
|
public animate: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' = 'fromRight';
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private service: NotificationsService,
|
||||||
|
private cdr: ChangeDetectorRef
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Listen for changes in the service
|
||||||
|
this.listener = this.service.emitter
|
||||||
|
.subscribe((item) => {
|
||||||
|
switch (item.command) {
|
||||||
|
case 'cleanAll':
|
||||||
|
this.notifications = [];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'clean':
|
||||||
|
this.cleanSingle(item.id!);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'set':
|
||||||
|
if (item.add) {
|
||||||
|
this.add(item.notification!);
|
||||||
|
} else {
|
||||||
|
this.defaultBehavior(item);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.defaultBehavior(item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cdr.markForCheck();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default behavior on event
|
||||||
|
defaultBehavior(value: any): void {
|
||||||
|
this.notifications.splice(this.notifications.indexOf(value.notification), 1);
|
||||||
|
this.onDestroy.emit(this.buildEmit(value.notification, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the new notification to the notification array
|
||||||
|
add(item: Notification): void {
|
||||||
|
item.createdOn = new Date();
|
||||||
|
|
||||||
|
const toBlock: boolean = this.preventLastDuplicates || this.preventDuplicates ? this.block(item) : false;
|
||||||
|
|
||||||
|
// Save this as the last created notification
|
||||||
|
this.lastNotificationCreated = item;
|
||||||
|
// Override icon if set
|
||||||
|
if (item.override && item.override.icons && item.override.icons[item.type]) {
|
||||||
|
item.icon = item.override.icons[item.type];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!toBlock) {
|
||||||
|
// Check if the notification should be added at the start or the end of the array
|
||||||
|
if (this.lastOnBottom) {
|
||||||
|
if (this.notifications.length >= this.maxStack) {
|
||||||
|
this.notifications.splice(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.notifications.push(item);
|
||||||
|
} else {
|
||||||
|
if (this.notifications.length >= this.maxStack) {
|
||||||
|
this.notifications.splice(this.notifications.length - 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.notifications.splice(0, 0, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onCreate.emit(this.buildEmit(item, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if notifications should be prevented
|
||||||
|
block(item: Notification): boolean {
|
||||||
|
|
||||||
|
const toCheck = item.html ? this.checkHtml : this.checkStandard;
|
||||||
|
|
||||||
|
if (this.preventDuplicates && this.notifications.length > 0) {
|
||||||
|
for (let i = 0; i < this.notifications.length; i++) {
|
||||||
|
if (toCheck(this.notifications[i], item)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.preventLastDuplicates) {
|
||||||
|
|
||||||
|
let comp: Notification;
|
||||||
|
|
||||||
|
if (this.preventLastDuplicates === 'visible' && this.notifications.length > 0) {
|
||||||
|
if (this.lastOnBottom) {
|
||||||
|
comp = this.notifications[this.notifications.length - 1];
|
||||||
|
} else {
|
||||||
|
comp = this.notifications[0];
|
||||||
|
}
|
||||||
|
} else if (this.preventLastDuplicates === 'all' && this.lastNotificationCreated) {
|
||||||
|
comp = this.lastNotificationCreated;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return toCheck(comp, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkStandard(checker: Notification, item: Notification): boolean {
|
||||||
|
return checker.type === item.type && checker.title === item.title && checker.content === item.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkHtml(checker: Notification, item: Notification): boolean {
|
||||||
|
return checker.html ? checker.type === item.type && checker.title === item.title && checker.content === item.content && checker.html === item.html : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach all the changes received in the options object
|
||||||
|
attachChanges(options: any): void {
|
||||||
|
Object.keys(options).forEach((a) => {
|
||||||
|
if (this.hasOwnProperty(a)) {
|
||||||
|
(this as any)[a] = options[a];
|
||||||
|
} else if (a === 'icons') {
|
||||||
|
this.service.icons = options[a];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
buildEmit(notification: Notification, to: boolean) {
|
||||||
|
const toEmit: Notification = {
|
||||||
|
createdOn: notification.createdOn,
|
||||||
|
type: notification.type,
|
||||||
|
icon: notification.icon,
|
||||||
|
id: notification.id
|
||||||
|
};
|
||||||
|
|
||||||
|
if (notification.html) {
|
||||||
|
toEmit.html = notification.html;
|
||||||
|
} else {
|
||||||
|
toEmit.title = notification.title;
|
||||||
|
toEmit.content = notification.content;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!to) {
|
||||||
|
toEmit.destroyedOn = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
return toEmit;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanSingle(id: string): void {
|
||||||
|
let indexOfDelete = 0;
|
||||||
|
let doDelete = false;
|
||||||
|
let noti;
|
||||||
|
|
||||||
|
this.notifications.forEach((notification, idx) => {
|
||||||
|
if (notification.id === id) {
|
||||||
|
indexOfDelete = idx;
|
||||||
|
noti = notification;
|
||||||
|
doDelete = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (doDelete) {
|
||||||
|
this.notifications.splice(indexOfDelete, 1);
|
||||||
|
this.onDestroy.emit(this.buildEmit(noti, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (this.listener) {
|
||||||
|
this.listener.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
263
src/app/shared/notifications/notifications.service.spec.ts
Normal file
263
src/app/shared/notifications/notifications.service.spec.ts
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
import {inject, TestBed} from '@angular/core/testing';
|
||||||
|
import {NotificationsService} from './notifications.service';
|
||||||
|
import {defaultIcons} from '../interfaces/icons';
|
||||||
|
import {NotificationEvent} from '../interfaces/notification-event.type';
|
||||||
|
import {Notification} from '../interfaces/notification.type';
|
||||||
|
|
||||||
|
describe('NotificationsService', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [],
|
||||||
|
providers: [NotificationsService],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let defaultNotification = {
|
||||||
|
id: '0',
|
||||||
|
title: 'Test title',
|
||||||
|
type: 'success',
|
||||||
|
icon: defaultIcons.success,
|
||||||
|
content: 'Test Content',
|
||||||
|
timeOut: 0,
|
||||||
|
maxLength: 0,
|
||||||
|
clickToClose: true,
|
||||||
|
clickIconToClose: false,
|
||||||
|
showProgressBar: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
theClass: 'initial',
|
||||||
|
rtl: false,
|
||||||
|
animate: 'fromRight',
|
||||||
|
createdOn: new Date(),
|
||||||
|
destroyedOn: new Date()
|
||||||
|
};
|
||||||
|
|
||||||
|
it('Service instantiates',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
expect(service instanceof NotificationsService).toBeTruthy();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('If override is not set, id is randomly generated',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
|
||||||
|
let notificationEvent: NotificationEvent = null;
|
||||||
|
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
service.set(defaultNotification, true);
|
||||||
|
|
||||||
|
expect(notificationEvent.command).toBe('set');
|
||||||
|
expect(notificationEvent.notification.id !== '0').toBeTruthy();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('If override id is set its used',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
let notificationEvent: NotificationEvent = null,
|
||||||
|
override = {id: '1'};
|
||||||
|
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
service.set(Object.assign(defaultNotification, {override: override}), true);
|
||||||
|
|
||||||
|
expect(notificationEvent.notification.id).toBe('1');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('Success method',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
let notificationEvent: NotificationEvent = null;
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
let notification: Notification = service.success('Title', 'Message');
|
||||||
|
|
||||||
|
expect(notification.id !== undefined).toBeTruthy();
|
||||||
|
expect(notification.type).toBe('success');
|
||||||
|
expect(notification.icon).toBe(defaultIcons.success);
|
||||||
|
|
||||||
|
expect(notification.title).toBe('Title');
|
||||||
|
expect(notification.content).toBe('Message');
|
||||||
|
expect(notification.override).toBeUndefined();
|
||||||
|
expect(notification.html).toBeUndefined();
|
||||||
|
expect(notification.state).toBeUndefined();
|
||||||
|
expect(notification.createdOn).toBeUndefined();
|
||||||
|
expect(notification.destroyedOn).toBeUndefined();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('Error method',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
let notificationEvent: NotificationEvent = null;
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
let notification: Notification = service.error('Title', 'Message');
|
||||||
|
|
||||||
|
expect(notification.id !== undefined).toBeTruthy();
|
||||||
|
expect(notification.type).toBe('error');
|
||||||
|
expect(notification.icon).toBe(defaultIcons.error);
|
||||||
|
|
||||||
|
expect(notification.title).toBe('Title');
|
||||||
|
expect(notification.content).toBe('Message');
|
||||||
|
expect(notification.override).toBeUndefined();
|
||||||
|
expect(notification.html).toBeUndefined();
|
||||||
|
expect(notification.state).toBeUndefined();
|
||||||
|
expect(notification.createdOn).toBeUndefined();
|
||||||
|
expect(notification.destroyedOn).toBeUndefined();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
it('Alert method',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
let notificationEvent: NotificationEvent = null;
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
let notification: Notification = service.alert('Title', 'Message');
|
||||||
|
|
||||||
|
expect(notification.id !== undefined).toBeTruthy();
|
||||||
|
expect(notification.type).toBe('alert');
|
||||||
|
expect(notification.icon).toBe(defaultIcons.alert);
|
||||||
|
|
||||||
|
expect(notification.title).toBe('Title');
|
||||||
|
expect(notification.content).toBe('Message');
|
||||||
|
expect(notification.override).toBeUndefined();
|
||||||
|
expect(notification.html).toBeUndefined();
|
||||||
|
expect(notification.state).toBeUndefined();
|
||||||
|
expect(notification.createdOn).toBeUndefined();
|
||||||
|
expect(notification.destroyedOn).toBeUndefined();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
it('Info method',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
let notificationEvent: NotificationEvent = null;
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
let notification: Notification = service.info('Title', 'Message');
|
||||||
|
|
||||||
|
expect(notification.id !== undefined).toBeTruthy();
|
||||||
|
expect(notification.type).toBe('info');
|
||||||
|
expect(notification.icon).toBe(defaultIcons.info);
|
||||||
|
|
||||||
|
expect(notification.title).toBe('Title');
|
||||||
|
expect(notification.content).toBe('Message');
|
||||||
|
expect(notification.override).toBeUndefined();
|
||||||
|
expect(notification.html).toBeUndefined();
|
||||||
|
expect(notification.state).toBeUndefined();
|
||||||
|
expect(notification.createdOn).toBeUndefined();
|
||||||
|
expect(notification.destroyedOn).toBeUndefined();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('Warn method',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
let notificationEvent: NotificationEvent = null;
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
let notification: Notification = service.warn('Title', 'Message');
|
||||||
|
|
||||||
|
expect(notification.id !== undefined).toBeTruthy();
|
||||||
|
expect(notification.type).toBe('warn');
|
||||||
|
expect(notification.icon).toBe(defaultIcons.warn);
|
||||||
|
|
||||||
|
expect(notification.title).toBe('Title');
|
||||||
|
expect(notification.content).toBe('Message');
|
||||||
|
expect(notification.override).toBeUndefined();
|
||||||
|
expect(notification.html).toBeUndefined();
|
||||||
|
expect(notification.state).toBeUndefined();
|
||||||
|
expect(notification.createdOn).toBeUndefined();
|
||||||
|
expect(notification.destroyedOn).toBeUndefined();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('Bare method',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
let notificationEvent: NotificationEvent = null;
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
let notification: Notification = service.bare('Title', 'Message');
|
||||||
|
|
||||||
|
expect(notification.id !== undefined).toBeTruthy();
|
||||||
|
expect(notification.type).toBe('bare');
|
||||||
|
expect(notification.icon).toBe('bare');
|
||||||
|
|
||||||
|
expect(notification.title).toBe('Title');
|
||||||
|
expect(notification.content).toBe('Message');
|
||||||
|
expect(notification.override).toBeUndefined();
|
||||||
|
expect(notification.html).toBeUndefined();
|
||||||
|
expect(notification.state).toBeUndefined();
|
||||||
|
expect(notification.createdOn).toBeUndefined();
|
||||||
|
expect(notification.destroyedOn).toBeUndefined();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
it('Create method',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
let notificationEvent: NotificationEvent = null;
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
let notification: Notification = service.create('Title', 'Message', 'create');
|
||||||
|
|
||||||
|
expect(notification.id !== undefined).toBeTruthy();
|
||||||
|
expect(notification.type).toBe('create');
|
||||||
|
// expect(notification.icon).toBe('bare');
|
||||||
|
|
||||||
|
expect(notification.title).toBe('Title');
|
||||||
|
expect(notification.content).toBe('Message');
|
||||||
|
expect(notification.override).toBeUndefined();
|
||||||
|
expect(notification.html).toBeUndefined();
|
||||||
|
expect(notification.state).toBeUndefined();
|
||||||
|
expect(notification.createdOn).toBeUndefined();
|
||||||
|
expect(notification.destroyedOn).toBeUndefined();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
it('Html method',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
let notificationEvent: NotificationEvent = null;
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
let notification: Notification = service.html('<B>Title</B>', 'success');
|
||||||
|
|
||||||
|
expect(notification.id !== undefined).toBeTruthy();
|
||||||
|
expect(notification.type).toBe('success');
|
||||||
|
expect(notification.icon).toBe('bare');
|
||||||
|
|
||||||
|
expect(notification.title).toBeUndefined();
|
||||||
|
expect(notification.content).toBeUndefined();
|
||||||
|
expect(notification.override).toBeUndefined();
|
||||||
|
expect(notification.html).toBe('<B>Title</B>');
|
||||||
|
expect(notification.state).toBeUndefined();
|
||||||
|
expect(notification.createdOn).toBeUndefined();
|
||||||
|
expect(notification.destroyedOn).toBeUndefined();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('Empty remove emits cleanAll command',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
let notificationEvent: NotificationEvent = null;
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
service.remove();
|
||||||
|
|
||||||
|
expect(notificationEvent.command).toBe('cleanAll');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('Remove with id emits clean command',
|
||||||
|
inject([NotificationsService], (service: NotificationsService) => {
|
||||||
|
let notificationEvent: NotificationEvent = null;
|
||||||
|
service.emitter.subscribe(item => notificationEvent = item);
|
||||||
|
|
||||||
|
service.remove('1');
|
||||||
|
|
||||||
|
expect(notificationEvent.command).toBe('clean');
|
||||||
|
expect(notificationEvent.id).toBe('1');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
});
|
64
src/app/shared/notifications/notifications.service.ts
Normal file
64
src/app/shared/notifications/notifications.service.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import {Injectable, EventEmitter} from '@angular/core';
|
||||||
|
import {Subject} from 'rxjs/Subject';
|
||||||
|
import {NotificationEvent} from './interfaces/notification-event.type';
|
||||||
|
import {Notification} from './interfaces/notification.type';
|
||||||
|
import {Icons, defaultIcons} from './interfaces/icons';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class NotificationsService {
|
||||||
|
|
||||||
|
public emitter = new Subject<NotificationEvent>();
|
||||||
|
public icons: Icons = defaultIcons;
|
||||||
|
|
||||||
|
set(notification: Notification, to: boolean): Notification {
|
||||||
|
notification.id = notification.override && notification.override.id ? notification.override.id : Math.random().toString(36).substring(3);
|
||||||
|
notification.click = new EventEmitter<{}>();
|
||||||
|
notification.timeoutEnd = new EventEmitter<{}>();
|
||||||
|
|
||||||
|
this.emitter.next({command: 'set', notification: notification, add: to});
|
||||||
|
return notification;
|
||||||
|
};
|
||||||
|
|
||||||
|
success(title: any = '', content: any = '', override?: any): Notification {
|
||||||
|
return this.set({title: title, content: content || '', type: 'success', icon: this.icons.success, override: override}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
error(title: any = '', content: any = '', override?: any): Notification {
|
||||||
|
return this.set({title: title, content: content || '', type: 'error', icon: this.icons.error, override: override}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
alert(title: any = '', content: any = '', override?: any): Notification {
|
||||||
|
return this.set({title: title, content: content || '', type: 'alert', icon: this.icons.alert, override: override}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
info(title: any = '', content: any = '', override?: any): Notification {
|
||||||
|
return this.set({title: title, content: content || '', type: 'info', icon: this.icons.info, override: override}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(title: any = '', content: any = '', override?: any): Notification {
|
||||||
|
return this.set({title: title, content: content || '', type: 'warn', icon: this.icons.warn, override: override}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bare(title: any = '', content: any = '', override?: any): Notification {
|
||||||
|
return this.set({title: title, content: content || '', type: 'bare', icon: 'bare', override: override}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// With type method
|
||||||
|
create(title: any = '', content: any = '', type = 'success', override?: any): Notification {
|
||||||
|
return this.set({title: title, content: content, type: type, icon: (this.icons as any)[type], override: override}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML Notification method
|
||||||
|
html(html: any, type = 'success', override?: any, icon = 'bare'): Notification {
|
||||||
|
return this.set({html: html, type: type, icon: (this.icons as any)[icon], override: override}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all notifications method
|
||||||
|
remove(id?: string): void {
|
||||||
|
if (id) {
|
||||||
|
this.emitter.next({command: 'clean', id: id});
|
||||||
|
} else {
|
||||||
|
this.emitter.next({command: 'cleanAll'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -21,11 +21,11 @@ import { SearchResultListElementComponent } from './object-list/search-result-li
|
|||||||
import { WrapperListElementComponent } from './object-list/wrapper-list-element/wrapper-list-element.component';
|
import { WrapperListElementComponent } from './object-list/wrapper-list-element/wrapper-list-element.component';
|
||||||
import { ObjectListComponent } from './object-list/object-list.component';
|
import { ObjectListComponent } from './object-list/object-list.component';
|
||||||
|
|
||||||
import { CollectionGridElementComponent} from './object-grid/collection-grid-element/collection-grid-element.component'
|
import { CollectionGridElementComponent } from './object-grid/collection-grid-element/collection-grid-element.component';
|
||||||
import { CommunityGridElementComponent} from './object-grid/community-grid-element/community-grid-element.component'
|
import { CommunityGridElementComponent } from './object-grid/community-grid-element/community-grid-element.component';
|
||||||
import { ItemGridElementComponent} from './object-grid/item-grid-element/item-grid-element.component'
|
import { ItemGridElementComponent } from './object-grid/item-grid-element/item-grid-element.component';
|
||||||
import { AbstractListableElementComponent} from './object-collection/shared/object-collection-element/abstract-listable-element.component'
|
import { AbstractListableElementComponent } from './object-collection/shared/object-collection-element/abstract-listable-element.component';
|
||||||
import { WrapperGridElementComponent} from './object-grid/wrapper-grid-element/wrapper-grid-element.component'
|
import { WrapperGridElementComponent } from './object-grid/wrapper-grid-element/wrapper-grid-element.component';
|
||||||
import { ObjectGridComponent } from './object-grid/object-grid.component';
|
import { ObjectGridComponent } from './object-grid/object-grid.component';
|
||||||
import { ObjectCollectionComponent } from './object-collection/object-collection.component';
|
import { ObjectCollectionComponent } from './object-collection/object-collection.component';
|
||||||
import { ComcolPageContentComponent } from './comcol-page-content/comcol-page-content.component';
|
import { ComcolPageContentComponent } from './comcol-page-content/comcol-page-content.component';
|
||||||
@@ -41,6 +41,8 @@ import { SearchResultGridElementComponent } from './object-grid/search-result-gr
|
|||||||
import { ViewModeSwitchComponent } from './view-mode-switch/view-mode-switch.component';
|
import { ViewModeSwitchComponent } from './view-mode-switch/view-mode-switch.component';
|
||||||
import { GridThumbnailComponent } from './object-grid/grid-thumbnail/grid-thumbnail.component';
|
import { GridThumbnailComponent } from './object-grid/grid-thumbnail/grid-thumbnail.component';
|
||||||
import { VarDirective } from './utils/var.directive';
|
import { VarDirective } from './utils/var.directive';
|
||||||
|
import { NotificationComponent } from './notifications/notification/notification.component';
|
||||||
|
import { NotificationsBoardComponent } from './notifications/notifications-board/notifications-board.component';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
|
||||||
@@ -79,7 +81,9 @@ const COMPONENTS = [
|
|||||||
ThumbnailComponent,
|
ThumbnailComponent,
|
||||||
GridThumbnailComponent,
|
GridThumbnailComponent,
|
||||||
WrapperListElementComponent,
|
WrapperListElementComponent,
|
||||||
ViewModeSwitchComponent
|
ViewModeSwitchComponent,
|
||||||
|
// NotificationComponent,
|
||||||
|
// NotificationsBoardComponent
|
||||||
];
|
];
|
||||||
|
|
||||||
const ENTRY_COMPONENTS = [
|
const ENTRY_COMPONENTS = [
|
||||||
@@ -91,7 +95,8 @@ const ENTRY_COMPONENTS = [
|
|||||||
ItemGridElementComponent,
|
ItemGridElementComponent,
|
||||||
CollectionGridElementComponent,
|
CollectionGridElementComponent,
|
||||||
CommunityGridElementComponent,
|
CommunityGridElementComponent,
|
||||||
SearchResultGridElementComponent
|
SearchResultGridElementComponent,
|
||||||
|
// NotificationComponent,
|
||||||
];
|
];
|
||||||
|
|
||||||
const DIRECTIVES = [
|
const DIRECTIVES = [
|
||||||
|
Reference in New Issue
Block a user