feat: tour integration

Tour Integration
This commit is contained in:
Ramveer
2022-09-02 17:03:13 +05:30
committed by GitHub
parent 52b7a3274e
commit f1be9ab5b1
119 changed files with 6360 additions and 456 deletions
@@ -0,0 +1,17 @@
<h1 mat-dialog-title>Welcome to Mifos</h1>
<mat-dialog-content>
<h3 class="mw600">Following guide tour will help you configure Mifos according to your organization needs.</h3>
<h3>You have successfully completed the setup of Configuration Wizard.</h3>
<h3>Progress Bar : 100 %</h3>
<mat-progress-bar mode="determinate" value="100"></mat-progress-bar>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-raised-button color="warn" mat-dialog-close>Close Config. Wizard</button>
</mat-dialog-actions>
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CompletionDialogComponent } from './completion-dialog.component';
describe('CompletionDialogComponent', () => {
let component: CompletionDialogComponent;
let fixture: ComponentFixture<CompletionDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ CompletionDialogComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CompletionDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,21 @@
/** Angular Imports */
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
/**
* Completion Dialog Component.
*/
@Component({
selector: 'mifosx-completion-dialog',
templateUrl: './completion-dialog.component.html',
styleUrls: ['./completion-dialog.component.scss']
})
export class CompletionDialogComponent implements OnInit {
constructor(public dialogRef: MatDialogRef<CompletionDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) { }
ngOnInit() {
}
}
@@ -0,0 +1,39 @@
<h1 mat-dialog-title>Welcome to Mifos</h1>
<mat-dialog-content>
<h3 class="mw600">Following guide tour will help you configure Mifos according to your organization needs.</h3>
<mat-grid-list cols="3" rowHeight="50px">
<mat-grid-tile>
<button mat-raised-button color="primary" [mat-dialog-close]="{ show: 1 }">Home Screen Tour</button>
</mat-grid-tile>
<mat-grid-tile>
<button mat-raised-button color="primary" [mat-dialog-close]="{ show: 2 }">Setup Organization</button>
</mat-grid-tile>
<mat-grid-tile>
<button mat-raised-button color="primary" [mat-dialog-close]="{ show: 3 }">Setup System</button>
</mat-grid-tile>
<mat-grid-tile>
<button mat-raised-button color="primary" [mat-dialog-close]="{ show: 4 }">Setup Accounting</button>
</mat-grid-tile>
<mat-grid-tile>
<button mat-raised-button color="primary" [mat-dialog-close]="{ show: 5 }">Setup Products</button>
</mat-grid-tile>
<mat-grid-tile>
<button mat-raised-button color="primary" [mat-dialog-close]="{ show: 6 }">Setup Funds and Reports</button>
</mat-grid-tile>
</mat-grid-list>
<h3>Progress Bar : 0%</h3>
<mat-progress-bar mode="determinate" value="0"></mat-progress-bar>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-raised-button color="warn" [mat-dialog-close]="{ show: 0 }">Close Config. Wizard</button>
</mat-dialog-actions>
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ConfigurationWizardComponent } from './configuration-wizard.component';
describe('ConfigurationWizardComponent', () => {
let component: ConfigurationWizardComponent;
let fixture: ComponentFixture<ConfigurationWizardComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ConfigurationWizardComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ConfigurationWizardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,25 @@
/** Angular Imports */
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
/**
* Configuration Wizard Component.
*/
@Component({
selector: 'mifosx-configuration-wizard',
templateUrl: './configuration-wizard.component.html',
styleUrls: ['./configuration-wizard.component.scss']
})
export class ConfigurationWizardComponent implements OnInit {
show: number;
/**
* @param {MatDialogRef<ConfigurationWizardComponent>} dialogRef MatDialogRef<ConfigurationWizardComponent>.
*/
constructor(public dialogRef: MatDialogRef<ConfigurationWizardComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) { }
ngOnInit() {
}
}
@@ -0,0 +1,54 @@
/** Angular Imports */
import { OverlayModule } from '@angular/cdk/overlay';
import { PortalModule } from '@angular/cdk/portal';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { MatDialogModule } from '@angular/material/dialog';
/*Custom Directives*/
import { PopoverArrowDirective } from './popover/popover-arrow.directive';
import { PopoverCloseDirective } from './popover/popover-close.directive';
/*Custom Components*/
import { PopoverComponent } from './popover/popover.component';
import { ConfigurationWizardComponent } from './configuration-wizard.component';
import { ContinueSetupDialogComponent } from './continue-setup-dialog/continue-setup-dialog.component';
import { NextStepDialogComponent } from './next-step-dialog/next-step-dialog.component';
import { CompletionDialogComponent } from './completion-dialog/completion-dialog.component';
/** Custom Modules */
import { SharedModule } from '../shared/shared.module';
/**
* Configuration Wizard Module
*/
@NgModule({
declarations: [
PopoverComponent,
PopoverCloseDirective,
PopoverArrowDirective,
ConfigurationWizardComponent,
ContinueSetupDialogComponent,
NextStepDialogComponent,
CompletionDialogComponent
],
imports: [
SharedModule,
CommonModule,
OverlayModule,
PortalModule,
MatDialogModule
],
exports: [
PopoverCloseDirective
],
entryComponents: [
PopoverComponent,
ConfigurationWizardComponent,
ContinueSetupDialogComponent,
NextStepDialogComponent,
CompletionDialogComponent
]
})
export class ConfigurationWizardModule { }
@@ -0,0 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { ConfigurationWizardService } from './configuration-wizard.service';
describe('ConfigurationWizardService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: ConfigurationWizardService = TestBed.get(ConfigurationWizardService);
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,290 @@
/** Angular Imports */
import { Injectable } from '@angular/core';
/**
* Configuration Wizard service.
*/
@Injectable({
providedIn: 'root'
})
export class ConfigurationWizardService {
/**
* Home Tour
*/
/*To show toolbar*/
showToolbar = false;
/*To show popover on admin section in toolbar*/
showToolbarAdmin = false;
/*To show side navbar*/
showSideNav = false;
/*To show popover on chart of accounts section in sidenav bar*/
showSideNavChartofAccounts = false;
/*To show breadcrumbs*/
showBreadcrumbs = false;
/*To show home*/
showHome = false;
/*To show search activity in home*/
showHomeSearchActivity = false;
/**
* Organization Setup
*/
/*To show popover on manage offices on oganization page*/
showCreateOffice = false;
/*To show popover on button in offices page*/
showOfficeList = false;
/*To show popover on offices table*/
showOfficeTable = false;
/*To show popover on create office form*/
showOfficeForm = false;
/*To show popover on add edit currency on organization page*/
showAddEditCurrency = false;
/*To show popover on button in currency page*/
showCurrencyPage = false;
/*To show popover on currency table*/
showCurrencyList = false;
/*To show popover on currency form*/
showCurrencyForm = false;
/*To show popover on manage holiday on organization page*/
showCreateHoliday = false;
/*To show popover on button in holiday page*/
showHolidayPage = false;
/*To show popover on filter in holiday table*/
showHolidayFilter = false;
/*To show popover on manage employees on organization page*/
showCreateEmployee = false;
/*To show popover on button in employee page*/
showEmployeeList = false;
/*To show popover on employees table*/
showEmployeeTable = false;
/*To show popover on create employee form*/
showEmployeeForm = false;
/*To show popover on define working days*/
showDefineWorkingDays = false;
/**
* System Setup
*/
/*To show popover on manage datatables on system page*/
showDatatables = false;
/*To show popover on button in datatables page*/
showDatatablesPage = false;
/*To show popover on datatables table*/
showDatatablesList = false;
/*To show popover on create datable form*/
showDatatablesForm = false;
/*To show popover manage codes on system page*/
showSystemCodes = false;
/*To show popover on button in codes page*/
showSystemCodesPage = false;
/*To show popover on codes table*/
showSystemCodesList = false;
/*To show popover on create codes form*/
showSystemCodesForm = false;
/*To show popover on manage roles and permision on system page*/
showRolesandPermission = false;
/*To show popover on button in rolaes and permission page*/
showRolesandPermissionPage = false;
/*To show popover on roles and permission list*/
showRolesandPermissionList = false;
/*To show popover on button on user page*/
showUsers = false;
/*To show popover on users table*/
showUsersList = false;
/*To show popover on create user form*/
showUsersForm = false;
/*To show popover on maker checker table in system page*/
showMakerCheckerTable = false;
/*To show popover on button in make checker tasks page*/
showMakerCheckerTablePage = false;
/*To show popover on maker checker tasks table*/
showMakerCheckerTableList = false;
/*To show popover on global configuration in system page*/
showConfigurations = false;
/*To show popover on button in configuration page*/
showConfigurationsPage = false;
/*To show popover on button in configuration list*/
showConfigurationsList = false;
/*To show popover on manage scheduler jobs on system page*/
showSchedulerJobs = false;
/*To show popover on button in scheduler jobs page*/
showSchedulerJobsPage = false;
/*To show popover on scheduler jobs table*/
showSchedulerJobsList = false;
/**
* Accounting Setup
*/
/*To show popover on chart of accounts on accounting page*/
showChartofAccounts = false;
/*To show popover on button in chart of accounts page*/
showChartofAccountsPage = false;
/*To show popover on button in chart of accounts page*/
showChartofAccountsList = false;
/*To show popover on create chart of accounts form*/
showChartofAccountsForm = false;
/*To show popover on accounts linked on accounting page*/
showAccountsLinked = false;
/*To show popover on button in accounting page*/
showAccountsLinkedPage = false;
/*To show popover on accounts linked table*/
showAccountsLinkedList = false;
/*To show popover on migrate openening balances*/
showMigrateOpeningBalances = false;
/*To show popover on closing entries on accounting page*/
showClosingEntries = false;
/*To show popover on button in closing entries page*/
showClosingEntriesPage = false;
/*To show popover on closing entries table*/
showClosingEntriesList = false;
/*To show popover on create journal entry*/
showCreateJournalEntries = false;
/**
* Products Setup
*/
/*To show popover on charges on products page*/
showCharges = false;
/*To show popover on button in charges page*/
showChargesPage = false;
/*To show popover on charges table*/
showChargesList = false;
/*To show popover on loan products on products page*/
showLoanProducts = false;
/*To show popover on button in load products page*/
showLoanProductsPage = false;
/*To show popover on loan products table*/
showLoanProductsList = false;
/*To show popover on savings products on products page*/
showSavingsProducts = false;
/*To show popover on button in savings products page*/
showSavingsProductsPage = false;
/*To show popover on savings products table*/
showSavingsProductsList = false;
/*To show popover on shares products on products page*/
showShareProducts = false;
/*To show popover on button in share products page*/
showShareProductsPage = false;
/*To show popover on share products table*/
showShareProductsList = false;
/*To show popover on fixed deposit products on products page*/
showFixedDepositProducts = false;
/*To show popover on button in fixed deposit products page*/
showFixedDepositProductsPage = false;
/*To show popover on fixed deposit products table*/
showFixedDepositProductsList = false;
/*To show popover on recrurring deposit products on products page*/
showRecurringDepositProducts = false;
/*To show popover on button in recurring deposit products page*/
showRecurringDepositProductsPage = false;
/*To show popover on recurring deposit products table*/
showRecurringDepositProductsList = false;
/**
* Manage Funds and manage reports Setup
*/
/*To show popover on manage funds*/
showManageFunds = false;
/*To show popover on manage reports*/
showManageReports = false;
constructor() { }
/**
* Set all variables to false.
*/
closeConfigWizard(): void {
this.showToolbar = false;
this.showToolbarAdmin = false;
this.showSideNav = false;
this.showSideNavChartofAccounts = false;
this.showBreadcrumbs = false;
this.showHome = false;
this.showHomeSearchActivity = false;
this.showCreateOffice = false;
this.showOfficeList = false;
this.showOfficeTable = false;
this.showOfficeForm = false;
this.showAddEditCurrency = false;
this.showCurrencyPage = false;
this.showCurrencyList = false;
this.showCurrencyForm = false;
this.showCreateHoliday = false;
this.showHolidayPage = false;
this.showHolidayFilter = false;
this.showCreateEmployee = false;
this.showEmployeeList = false;
this.showEmployeeTable = false;
this.showEmployeeForm = false;
this.showDefineWorkingDays = false;
this.showDatatables = false;
this.showDatatablesPage = false;
this.showDatatablesList = false;
this.showDatatablesForm = false;
this.showSystemCodes = false;
this.showSystemCodesPage = false;
this.showSystemCodesList = false;
this.showSystemCodesForm = false;
this.showRolesandPermission = false;
this.showRolesandPermissionPage = false;
this.showRolesandPermissionList = false;
this.showUsers = false;
this.showUsersList = false;
this.showUsersForm = false;
this.showMakerCheckerTable = false;
this.showMakerCheckerTablePage = false;
this.showMakerCheckerTableList = false;
this.showConfigurations = false;
this.showConfigurationsPage = false;
this.showConfigurationsList = false;
this.showSchedulerJobs = false;
this.showSchedulerJobsPage = false;
this.showSchedulerJobsList = false;
this.showChartofAccounts = false;
this.showChartofAccountsPage = false;
this.showChartofAccountsList = false;
this.showChartofAccountsForm = false;
this.showAccountsLinked = false;
this.showAccountsLinkedPage = false;
this.showAccountsLinkedList = false;
this.showMigrateOpeningBalances = false;
this.showClosingEntries = false;
this.showClosingEntriesPage = false;
this.showClosingEntriesList = false;
this.showCreateJournalEntries = false;
this.showCharges = false;
this.showChargesPage = false;
this.showChargesList = false;
this.showLoanProducts = false;
this.showLoanProductsPage = false;
this.showLoanProductsList = false;
this.showSavingsProducts = false;
this.showSavingsProductsPage = false;
this.showSavingsProductsList = false;
this.showShareProducts = false;
this.showShareProductsPage = false;
this.showShareProductsList = false;
this.showFixedDepositProducts = false;
this.showFixedDepositProductsPage = false;
this.showFixedDepositProductsList = false;
this.showRecurringDepositProducts = false;
this.showRecurringDepositProductsPage = false;
this.showRecurringDepositProductsList = false;
this.showManageFunds = false;
this.showManageReports = false;
}
}
@@ -0,0 +1,13 @@
<h2 mat-dialog-title>Configuration Wizard</h2>
<mat-dialog-content>
<h3 class="mw600"> You have created {{stepName}} successfully. Please select from the options below to proceed furthur.</h3>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-raised-button color="warn" [mat-dialog-close]="{ step: 1 }">Close Configuration Wizard</button>
<button mat-raised-button color="primary" [mat-dialog-close]="{ step: 2 }">Create more {{stepName}}</button>
<button mat-raised-button color="primary" [mat-dialog-close]="{ step: 3 }">Go to next step</button>
</mat-dialog-actions>
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ContinueSetupDialogComponent } from './continue-setup-dialog.component';
describe('ContinueSetupDialogComponent', () => {
let component: ContinueSetupDialogComponent;
let fixture: ComponentFixture<ContinueSetupDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ContinueSetupDialogComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ContinueSetupDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,29 @@
/** Angular Imports */
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
/**
* Continue Setup Dialog Component.
*/
@Component({
selector: 'mifosx-continue-setup-dialog',
templateUrl: './continue-setup-dialog.component.html',
styleUrls: ['./continue-setup-dialog.component.scss']
})
export class ContinueSetupDialogComponent implements OnInit {
/* Current Step Name*/
stepName: number;
/**
* @param {MatDialogRef<ContinueSetupDialogComponent>} dialogRef MatDialogRef<ContinueSetupDialogComponent>.
*/
constructor(public dialogRef: MatDialogRef<ContinueSetupDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) {
this.stepName = data.stepName;
}
ngOnInit() {
}
}
@@ -0,0 +1,18 @@
<h1 mat-dialog-title>Welcome to Mifos</h1>
<mat-dialog-content>
<h3 class="mw600">Following guide tour will help you configure Mifos according to your organization needs.</h3>
<h3>You have successfully completed the setup of {{ previousStepName }}</h3>
<h3>Progress Bar : {{stepPercentage}}%</h3>
<mat-progress-bar mode="determinate" [value]="stepPercentage"></mat-progress-bar>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-raised-button color="warn" [mat-dialog-close]="{ nextStep: false }">Close Config. Wizard</button>
<button mat-raised-button color="primary" [mat-dialog-close]="{ nextStep: true }"> {{ nextStepName }} </button>
</mat-dialog-actions>
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NextStepDialogComponent } from './next-step-dialog.component';
describe('NextStepDialogComponent', () => {
let component: NextStepDialogComponent;
let fixture: ComponentFixture<NextStepDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NextStepDialogComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NextStepDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,35 @@
/** Angular Imports */
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
/**
* Next Step Dialog Component.
*/
@Component({
selector: 'mifosx-next-step-dialog',
templateUrl: './next-step-dialog.component.html',
styleUrls: ['./next-step-dialog.component.scss']
})
export class NextStepDialogComponent implements OnInit {
/* Step Percentage */
stepPercentage: number;
/* Next Step Name */
nextStepName: string;
/* Previous Step Name*/
previousStepName: string;
/**
* @param {MatDialogRef<NextStepDialogComponent>} dialogRef MatDialogRef<NextStepDialogComponent>.
*/
constructor(public dialogRef: MatDialogRef<NextStepDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any) {
this.stepPercentage = data.stepPercentage;
this.nextStepName = data.nextStepName;
this.previousStepName = data.previousStepName;
}
ngOnInit() {
}
}
@@ -0,0 +1,59 @@
/* Angular Imports */
import { Directive, Renderer2, ElementRef, HostBinding, ChangeDetectorRef, OnDestroy } from '@angular/core';
/* rxjs Imports */
import { Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
/* Popover Ref */
import { PopoverRef } from './popover-ref';
/**
* Internal directive that shows the popover arrow.
*/
@Directive({
selector: '[mifosxPopoverArrow]'
})
export class PopoverArrowDirective implements OnDestroy {
@HostBinding('style.width.px')
@HostBinding('style.height.px')
arrowSize: number;
@HostBinding('style.top.px')
offsetTop: number;
@HostBinding('style.right.px')
offsetRight: number;
@HostBinding('style.bottom.px')
offsetBottom: number;
@HostBinding('style.left.px')
offsetLeft: number;
private subscription = new Subscription();
/**
* @param {PopoverRef} popoverRef PopoverRef.
* @param {ChangeDetectorRef} cd ChangeDetectorRef
*/
constructor(private popoverRef: PopoverRef,
private cd: ChangeDetectorRef) {
this.arrowSize = popoverRef.config.arrowSize;
this.subscription = popoverRef.positionChanges().subscribe(p => {
const { offsetX, offsetY } = p.connectionPair;
this.offsetTop = offsetY >= 0 ? offsetY * -1 : null;
this.offsetLeft = offsetX < 0 ? offsetX * -1 : null;
this.offsetBottom = offsetY < 0 ? offsetY : null;
this.offsetRight = offsetX >= 0 ? offsetX : null;
this.cd.detectChanges();
});
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
@@ -0,0 +1,32 @@
/** Angular Imports */
import { Directive, HostListener, Input, Optional } from '@angular/core';
/** Popover Ref */
import { PopoverRef } from './popover-ref';
/**
* Button that will close the current popover.
*/
@Directive({
selector: '[mifosxPopoverClose]'
})
export class PopoverCloseDirective<T = any> {
@Input('mifosxPopoverClose') popoverResult: T;
/**
* @param {PopoverRef<T>} popoverRef PopoverRef<T>.
*/
constructor(
@Optional() private popoverRef: PopoverRef<T>
) {}
@HostListener('click') onClick(): void {
if (!this.popoverRef) {
console.error('PopoverClose is not supported within a template');
return;
}
this.popoverRef.close(this.popoverResult);
}
}
@@ -0,0 +1,11 @@
/**
* Configuration for opening a popover with the Popover service.
*/
export interface PopoverConfig<T = any> {
backdropClass: string;
data?: T;
disableClose: boolean;
panelClass: string | string[];
arrowOffset?: number;
arrowSize?: number;
}
@@ -0,0 +1,61 @@
/** Angular Imports */
import { OverlayRef, FlexibleConnectedPositionStrategy, ConnectedOverlayPositionChange } from '@angular/cdk/overlay';
import { Observable, Subject } from 'rxjs';
/** rxjs Imports */
import { filter } from 'rxjs/operators';
/** Custom config. */
import { PopoverConfig } from './popover-config';
/**
* Reference to a popover opened via the Popover service.
*/
export class PopoverRef<T = any> {
private afterClosedSubject = new Subject<T>();
/**
* @param {OverlayRef} overlayRef OverlayRef.
* @param {FlexibleConnectedPositionStrategy} positionStrategy FlexibleConnectedPositionStrategy.
* @param {PopoverConfig} config PopoverConfig.
*/
constructor(private overlayRef: OverlayRef,
private positionStrategy: FlexibleConnectedPositionStrategy,
public config: PopoverConfig) {
if (!config.disableClose) {
this.overlayRef.backdropClick().subscribe(() => {
// this.close();
});
this.overlayRef.keydownEvents().pipe(
filter(event => event.key === 'Escape')
).subscribe(() => {
// this.close();
});
}
}
/**
* Close popover
*/
close(dialogResult?: T): void {
this.afterClosedSubject.next(dialogResult);
this.afterClosedSubject.complete();
this.overlayRef.dispose();
}
/**
* @returns {Observable<T>}
*/
afterClosed(): Observable<T> {
return this.afterClosedSubject.asObservable();
}
/**
* @returns {Observable<ConnectedOverlayPositionChange>}
*/
positionChanges(): Observable<ConnectedOverlayPositionChange> {
return this.positionStrategy.positionChanges;
}
}
@@ -0,0 +1,3 @@
<ng-container cdkPortalOutlet></ng-container>
<div class="arrow" mifosxPopoverArrow></div>
@@ -0,0 +1,50 @@
:host {
position: relative;
background: white;
border-radius: 8px;
padding: 20px;
}
.arrow {
position: absolute;
background: inherit;
z-index: -1;
}
:host-context(.center) .arrow {
left: 50%;
}
:host-context(.top.left, .top.center) .arrow {
transform-origin: top left;
transform: rotate(45deg);
}
:host-context(.top.right) .arrow {
transform-origin: top right;
transform: rotate(-45deg);
}
:host-context(.bottom.left, .bottom.center) .arrow {
transform-origin: bottom left;
transform: rotate(-45deg);
}
:host-context(.bottom.right) .arrow {
transform-origin: bottom right;
transform: rotate(45deg);
}
:host-context(.center.left) .arrow {
left: 0%;
top: 50%;
transform-origin: center left;
transform: rotate(-45deg);
}
:host-context(.center.right) .arrow {
left: 98%;
top: 50%;
transform-origin: center left;
transform: rotate(-45deg);
}
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PopoverComponent } from './popover.component';
describe('PopoverComponent', () => {
let component: PopoverComponent;
let fixture: ComponentFixture<PopoverComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ PopoverComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PopoverComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,33 @@
/** Angular Imports */
import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
import { Component, ComponentRef, EmbeddedViewRef, ViewChild, ChangeDetectorRef } from '@angular/core';
import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
import { PortalModule } from '@angular/cdk/portal';
/** rxjs Imports */
import { Subscription } from 'rxjs';
import { map } from 'rxjs/operators';
/* Popover Ref */
import { PopoverRef } from './popover-ref';
/**
* Internal component that wraps user-provided popover content.
*/
@Component({
selector: 'mifosx-popover',
templateUrl: './popover.component.html',
styleUrls: ['./popover.component.scss']
})
export class PopoverComponent extends BasePortalOutlet {
@ViewChild(CdkPortalOutlet, { static: true }) portalOutlet: CdkPortalOutlet;
attachComponentPortal<T>(componentPortal: ComponentPortal<any>): ComponentRef<T> {
console.log(this.portalOutlet.attachComponentPortal(componentPortal));
return this.portalOutlet.attachComponentPortal(componentPortal);
}
attachTemplatePortal<C>(portal: TemplatePortal<C>): EmbeddedViewRef<C> {
return this.portalOutlet.attachTemplatePortal(portal);
}
}
@@ -0,0 +1,226 @@
/* Angular Imports */
import { ComponentType, Overlay, OriginConnectionPosition, OverlayConnectionPosition, ConnectionPositionPair } from '@angular/cdk/overlay';
import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
import { Injectable, InjectionToken, Injector, TemplateRef, ElementRef } from '@angular/core';
/* Custom Imports */
import { PopoverConfig } from './popover-config';
import { PopoverRef } from './popover-ref';
import { PopoverComponent } from './popover.component';
/**
* Injection token that can be used to access the data that was passed in to a popover.
*
*/
export const POPOVER_DATA = new InjectionToken('popover.data');
const defaultConfig: PopoverConfig = {
backdropClass: '',
disableClose: false,
panelClass: '',
arrowOffset: 30,
arrowSize: 20
};
/**
* Service to open modal and manage popovers.
*/
@Injectable({
providedIn: 'root'
})
export class PopoverService {
/**
* @param {Injector} injector Injector.
* @param {overlay} overlay Overlay.
*/
constructor(private overlay: Overlay, private injector: Injector) { }
/**
* Opens the popover at target element
* @param {ComponentType<any> | TemplateRef<any>} componentOrTemplate ComponentType<any> | TemplateRef<any>.
* @param {ElementRef | HTMLElement} target ElementRef | HTMLElement.
* @param {string} position String.
* @param {Partial<PopoverConfig> = {}} config Partial<PopoverConfig> = {}.
*/
open<D = any>(componentOrTemplate: ComponentType<any> | TemplateRef<any>,
target: ElementRef | HTMLElement, position: string,
backdrop: boolean,
config: Partial<PopoverConfig> = {}): PopoverRef<D> {
const popoverConfig: PopoverConfig = Object.assign({}, defaultConfig, config);
const arrowSize = popoverConfig.arrowSize;
const arrowOffset = popoverConfig.arrowOffset;
const panelOffset = arrowSize / 2;
let showbackdrop: boolean;
let positions: ConnectionPositionPair[];
// preferred positions, in order of priority
if (position === 'bottom') {
positions = [
// bottom center
{
overlayX: 'center',
overlayY: 'top',
originX: 'center',
originY: 'bottom',
panelClass: ['top', 'center'],
offsetY: panelOffset
},
// bottom left
{
overlayX: 'start',
overlayY: 'top',
originX: 'center',
originY: 'bottom',
panelClass: ['top', 'left'],
offsetX: -1 * arrowOffset,
offsetY: panelOffset
},
// bottom right
{
overlayX: 'end',
overlayY: 'top',
originX: 'center',
originY: 'bottom',
panelClass: ['top', 'right'],
offsetX: arrowOffset,
offsetY: panelOffset
}
];
} else if (position === 'top') {
positions = [
// top center
{
overlayX: 'center',
overlayY: 'bottom',
originX: 'center',
originY: 'top',
panelClass: ['bottom', 'center'],
offsetY: -1 * panelOffset
},
// top left
{
overlayX: 'start',
overlayY: 'bottom',
originX: 'center',
originY: 'top',
panelClass: ['bottom', 'left'],
offsetX: -1 * arrowOffset,
offsetY: -1 * panelOffset
},
// top right
{
overlayX: 'end',
overlayY: 'bottom',
originX: 'center',
originY: 'top',
panelClass: ['bottom', 'right'],
offsetX: arrowOffset,
offsetY: -1 * panelOffset
}
];
} else if (position === 'right') {
positions = [
// right
{
originX : 'end',
originY : 'center',
overlayX: 'start',
overlayY: 'center',
panelClass: ['center', 'left'],
offsetX: 0.5 * arrowOffset,
offsetY: -1 * panelOffset
},
];
} else if (position === 'left') {
positions = [
// left
{
originX : 'start',
originY : 'center',
overlayX: 'end',
overlayY: 'center',
panelClass: ['center', 'right'],
offsetX: -0.5 * arrowOffset,
offsetY: -1 * panelOffset
},
];
}
if (backdrop === true) {
showbackdrop = true;
} else {
showbackdrop = false;
}
const positionStrategy = this.overlay
.position()
.flexibleConnectedTo(target)
.withPush(false)
.withFlexibleDimensions(false)
.withPositions(positions);
const overlayRef = this.overlay.create({
hasBackdrop: showbackdrop,
backdropClass: config.backdropClass,
panelClass: config.panelClass,
positionStrategy,
scrollStrategy: this.overlay.scrollStrategies.reposition()
});
const popoverRef = new PopoverRef(overlayRef, positionStrategy, popoverConfig);
const popover = overlayRef.attach(new ComponentPortal(
PopoverComponent,
null,
Injector.create({
parent: this.injector,
providers: [
{ provide: PopoverRef, useValue: popoverRef }
]
})
// new PortalInjector(
// this.injector,
// new WeakMap<any, any>([
// [PopoverRef, popoverRef]
// ])
// )
)).instance;
if (componentOrTemplate instanceof TemplateRef) {
// rendering a provided template dynamically
popover.attachTemplatePortal(
new TemplatePortal(
componentOrTemplate,
null,
{
$implicit: config.data,
popover: popoverRef
}
)
);
} else {
// rendering a provided component dynamically
popover.attachComponentPortal(
new ComponentPortal(
componentOrTemplate,
null,
Injector.create({
parent: this.injector,
providers: [
{ provide: POPOVER_DATA, useValue: config.data },
{ provide: PopoverRef, useValue: popoverRef },
]
})
// new PortalInjector(
// this.injector,
// new WeakMap<any, any>([
// [POPOVER_DATA, config.data],
// [PopoverRef, popoverRef]
// ])
// )
)
);
}
return popoverRef;
}
}