Skip to content

Commit

Permalink
Merge pull request #52 from yogeshgadge/notifications-and-alerts
Browse files Browse the repository at this point in the history
Notifications component closes #51
  • Loading branch information
yogeshgadge authored Apr 6, 2018
2 parents 1f99672 + 6892ba2 commit f798bd2
Show file tree
Hide file tree
Showing 37 changed files with 876 additions and 67 deletions.
34 changes: 34 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[*]
charset=utf-8
end_of_line=crlf
insert_final_newline=false
indent_style=space
indent_size=2

[{.es6-loader-rc,ports,firmscode,version,.babelrc,refdata,.eslintrc,.stylelintrc,jest.config,userDetails,.es6modules,nereasons,.esmrc,11742287,*.json,*.jsb3,*.jsb2,*.bowerrc}]
indent_style=space
indent_size=2

[*.js.map]
indent_style=space
indent_size=2

[*.less]
indent_style=space
indent_size=2

[*.scss]
indent_style=space
indent_size=2

[*.coffee]
indent_style=space
indent_size=2

[{.analysis_options,*.yml,*.yaml}]
indent_style=space
indent_size=2

[tslint.json]
indent_style=space
indent_size=2
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"test:lib": "ng test --app lib",
"test:lib:once": "ng test --app lib --watch=false",
"e2e:kitchensink": "ng e2e --app kitchensink --watch=false",
"ready": "npm-run-all --parallel build:lib lint test:once e2e:kitchensink",
"ready": "npm-run-all --parallel build:kitchensink:prod build:lib lint test:once e2e:kitchensink",
"test:once": "npm-run-all --serial test:lib:once test:kitchensink:once",
"postinstall": "node postinstall.js"
},
Expand Down Expand Up @@ -59,6 +59,7 @@
"zone.js": "0.8.19"
},
"devDependencies": {
"@angular-devkit/core": "0.0.29",
"@angular/animations": "5.1.2",
"@angular/cli": "1.6.3",
"@angular/common": "5.1.2",
Expand Down
1 change: 1 addition & 0 deletions src/app/buttons/_buttons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@
text-decoration: underline;
}
}

2 changes: 1 addition & 1 deletion src/app/buttons/buttons.module.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CBPToggleSwitchComponent } from './cbp-toggle-switch/cbp-toggle-switch.component';
import {FormsModule} from '@angular/forms';
import { CBPToggleSwitchComponent } from './cbp-toggle-switch/cbp-toggle-switch.component';

@NgModule({
imports: [
Expand Down
19 changes: 15 additions & 4 deletions src/app/buttons/cbp-toggle-switch/cbp-toggle-switch.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
<span class="switch">
<input type="checkbox" [attr.checked]="isOn === onValue ? true : null" (change)="valueChange($event)" [attr.required]="required" [attr.id]="toggleSwitchId" [attr.disabled]="disabled">
<label [attr.for]="toggleSwitchId" [attr.data-on]="onLabel" [attr.data-off]="offLabel">{{ label }}</label>
</span>
<input #input
class="cdk-visually-hidden"
type="checkbox"
[id]="inputId"
[required]="required"
[checked]="checked"
[disabled]="disabled"
[attr.name]="name"
[tabIndex]="tabIndex"
[attr.aria-label]="ariaLabel"
[attr.aria-labelledby]="ariaLabelledby"
[attr.aria-checked]="_getAriaChecked()"
(change)="_stopPropogation($event)"
(click)="_onClick($event)"/>
<label [attr.for]="inputId" [attr.data-on]="onLabel" [attr.data-off]="offLabel">{{ label }}</label>
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
@import '../../variables';

// copied from https://raw.githubusercontent.com/US-CBP/cbp-theme/master/app/styles/custom/_forms.scss
.switch {
// because the current version 1.8.1 of cbp-theme does not have refactored cbp-toggle switches
.cbp-toggle-switch {
padding-left: 64px;
overflow: hidden;
position: relative;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { CBPToggleSwitchComponent } from './cbp-toggle-switch.component';
import {CBPButtonsModule} from '../buttons.module';

describe('CBPToggleSwitchComponent', () => {
let component: CBPToggleSwitchComponent;
let fixture: ComponentFixture<CBPToggleSwitchComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [CBPButtonsModule]
declarations: [ CBPToggleSwitchComponent ]
})
.compileComponents();
}));
Expand Down
154 changes: 131 additions & 23 deletions src/app/buttons/cbp-toggle-switch/cbp-toggle-switch.component.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,144 @@
import {ChangeDetectionStrategy, Component, EventEmitter, Input, Output} from '@angular/core';
import {
Attribute,
ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, forwardRef, HostBinding,
Input, OnInit,
Output,
ViewEncapsulation
} from '@angular/core';
import {CanColor, CanDisable, mixinColor, mixinDisabled, mixinTabIndex, ThemePalette} from '@angular/material';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {HasTabIndex} from '@angular/material/core/typings/common-behaviors/tabindex';


export class CBPToggleSwitchChange {
checked: boolean;
source: CBPToggleSwitchComponent;

}


export class CBPToggleSwitchComponentBase {
constructor(public _elementRef: ElementRef) {}
}

export const _CBPToggleSwitchMixinBase =
mixinTabIndex(mixinColor(mixinDisabled(CBPToggleSwitchComponentBase), 'accent'));

let toggleSwitchCounter = 1;

export const CBP_TOGGLE_SWITCH_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
// tslint:disable-next-line:no-use-before-declare
useExisting: forwardRef(() => CBPToggleSwitchComponent),
multi: true
}

@Component({
selector: 'cbp-toggle-switch',
templateUrl: './cbp-toggle-switch.component.html',
styleUrls: ['./cbp-toggle-switch.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
moduleId: module.id,
selector: 'cbp-toggle-switch',
templateUrl: './cbp-toggle-switch.component.html',
styleUrls: ['./cbp-toggle-switch.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
providers: [CBP_TOGGLE_SWITCH_CONTROL_VALUE_ACCESSOR],
preserveWhitespaces: false // seems to trim whitespace content
})
export class CBPToggleSwitchComponent {
export class CBPToggleSwitchComponent extends _CBPToggleSwitchMixinBase
implements OnInit, CanColor, CanDisable, ControlValueAccessor, HasTabIndex {

@Input() ariaLabel = '';
@Input() ariaLabelledby: string | null = null;

@Input() onValue: any = true;
@Input() offValue: any = false;
@Input() onLabel = 'ON';
@Input() offLabel = 'OFF';
@Input() label = '';
@Input() disabled: boolean;
@Input() color: ThemePalette;
@Input() name: string | null = null;

@HostBinding('class') className = 'cbp-toggle-switch';
@HostBinding('id') id = `cbp-toggle-switch-${++toggleSwitchCounter}`;
@HostBinding('class.cbp-toggle-switch-checked') _checked: Boolean = null;


@Input() onLabel = 'ON';
@Input() offLabel = 'OFF';
@Input() onValue = true;
@Input() offValue = false;
@Input() label: string = null;
@Input() required: boolean;
@Input() disabled: boolean;
@Output() changed = new EventEmitter<boolean>();

@Input() isOn: Boolean;
@Input()
get required(): boolean { return this._required; }
set required(value: boolean) { this._required = value !== null && `${value}` !== 'false' && value !== false ? true : false; }
private _required: boolean;

toggleSwitchId = `cbp-toggle-sw-${toggleSwitchCounter}`;
inputId = this.id + 'input';

constructor() {
toggleSwitchCounter++;
}
@Output() readonly change: EventEmitter<CBPToggleSwitchChange> = new EventEmitter<CBPToggleSwitchChange>();

constructor(elementRef: ElementRef,
private _changeDetectorRef: ChangeDetectorRef,
@Attribute('tabindex') tabIndex: string) {
super(elementRef);
this.tabIndex = Number.parseInt(tabIndex) || 0;
}

valueChange($event: any) {
this.isOn = $event.currentTarget.checked ? this.onValue : this.offValue;
this.changed.emit(this.isOn.valueOf());
}
@Input()
get checked(): boolean { return !! this._checked ; }
set checked(value: boolean) {
if (value !== this.checked) {
this._checked = value;
this._changeDetectorRef.markForCheck();
}
}

_getAriaChecked(): 'true' | 'false' | 'mixed' {
return this._checked === null || this._checked === undefined ? 'mixed' : this._checked ? 'true' : 'false';
}

_stopPropogation($event: Event) {
$event.stopPropagation();
}
_onClick($event: Event) {
$event.stopPropagation();
if (!this.disabled) {
this._checked = ! this.checked;
this._emitChangeEvent();
}

}

private _emitChangeEvent() {
let event = new CBPToggleSwitchChange();
event.source = this;
event.checked = this.checked;

this._controlValueAccessorChangeFn(this.checked ? this.onValue : this.offValue);
this.change.emit(event);
}

// just to avoid TypeScript error - it is going to get overwritten by ControlValueAccessor impl of registerOnChange
private _controlValueAccessorChangeFn: (value: any) => void = () => {};

ngOnInit() {
}


writeValue(obj: any): void {
this.checked = obj === this.onValue;
}

registerOnChange(fn: any): void {
// for synchronization of values from the downstream components form value
this._controlValueAccessorChangeFn = fn;
}

/**
* Focus control not implemented yet
* @param fn
*/
registerOnTouched(): void {}

setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
this._changeDetectorRef.markForCheck();
}
}

2 changes: 1 addition & 1 deletion src/app/header/cbp-toolbar/cbp-toolbar-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const CBP_HEADER_STATE = new InjectionToken<CBPToolbarState>('cbp-toolbar
export const APP_HEADER_STATE = new InjectionToken<CBPToolbarState>('app-toolbar-state-service');

/**
* Holds a single notification state as well as serves as a observable.
* Holds a single cbp-notification state as well as serves as a observable.
*/
export class CBPToolbarState {
hasToolbarMenu = new BehaviorSubject<boolean>(false);
Expand Down
1 change: 1 addition & 0 deletions src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './header/index';
export * from './pipes/index';
export * from './progress/index';
export * from './user/index';
export * from './notifications/index';
62 changes: 62 additions & 0 deletions src/app/notifications/cbp-notification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {TemplateRef} from '@angular/core';
import {Portal} from '@angular/cdk/portal';
import {Observable} from 'rxjs/Observable';
import {ReplaySubject} from 'rxjs/ReplaySubject';

import 'rxjs/add/observable/empty';
import 'rxjs/add/operator/delay';

let notificationCounter = 0;
export class CBPNotification {
type?: 'success' | 'danger' | 'warning' | 'info';
/**
* For creating a simple test notification.
*/
message?: string;

/**
* If you have your own markup and actionable notifications.
*/
content?: TemplateRef<any>;

/**
* Mainly used internally but you can pass CdkPortal i.e. your own TemplatePortal or ComponentPortal
*/
contentPortal?: Portal<any>;

private _state: ReplaySubject<boolean> = new ReplaySubject(1);
private _id: number;
get id() {
return this._id;
}

constructor(isClosedInitially = false) {
this._id = ++notificationCounter;
this._state.next(isClosedInitially);
}


/**
* Is the notification currently open ?
* @returns {Observable<boolean>}
*/
isOpen(): Observable<boolean> {
return this._state.asObservable();
}

/**
* Open the notification that is created.
*/
open(): void {
this._state.next(true);
}

/**
* Close the notification. Meant for close and destroy.
*/
close(): void {
this._state.next(false);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div [@animationState]="animationState" *ngIf="show">
<div class="toast" [ngClass]="toastTypeClass" tabindex="-1" aria-live="polite"><span class="cdk-visually-hidden">{{ notification?.type }} notification</span>
<button mat-icon-button class="close" aria-label="Close" (click)="remove()">
<mat-icon fontSet="fontawesome" fontIcon="fa-close"></mat-icon>
</button>
<ng-container *ngIf="notification.message">
<span class="cbp-notification-contents">{{ notification.message }}</span>
</ng-container>
<ng-content></ng-content>
</div>
</div>
Loading

0 comments on commit f798bd2

Please sign in to comment.