code cleanup, names refactoring
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
# NgxBootstrap
|
||||
|
||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.1.9.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name --project ngx-bootstrap` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ngx-bootstrap`.
|
||||
> Note: Don't forget to add `--project ngx-bootstrap` or else it will be added to the default project in your `angular.json` file.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build ngx-bootstrap` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Publishing
|
||||
|
||||
After building your library with `ng build ngx-bootstrap`, go to the dist folder `cd dist/ngx-bootstrap` and run `npm publish`.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test ngx-bootstrap` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
@@ -0,0 +1,32 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, '../../../coverage/poweredsoft/ngx-bootstrap'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../../dist/openharbor/ngx-data-bootstrap-ui",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "@openharbor/ngx-data-bootstrap-ui",
|
||||
"version": "0.0.15",
|
||||
"description": "an internal use library for handling data sources grid filtering sorting, add commands etc",
|
||||
"keywords": [
|
||||
"angular",
|
||||
"ngx-bootstrap"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@poweredsoft/data": "^0.0.36",
|
||||
"@angular/common": "^9.1.9",
|
||||
"@angular/core": "^9.1.9",
|
||||
"ngx-bootstrap": "^5.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {ModalModule} from 'ngx-bootstrap/modal';
|
||||
import {CommandModalDirective} from './directives/command-modal.directive';
|
||||
import {CommandModalComponent} from './command-modal/command-modal.component';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {CommandModalService} from './command-modal.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ModalModule.forRoot(),
|
||||
FormsModule
|
||||
],
|
||||
providers: [CommandModalService],
|
||||
declarations: [CommandModalDirective, CommandModalComponent],
|
||||
exports: [CommandModalDirective]
|
||||
})
|
||||
export class CommandModalModule { }
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import {CommandModalComponent} from './command-modal/command-modal.component';
|
||||
import {EventEmitter, Injectable, TemplateRef} from '@angular/core';
|
||||
import {IDataSource} from '@poweredsoft/data';
|
||||
import {BsModalService} from 'ngx-bootstrap/modal';
|
||||
|
||||
@Injectable()
|
||||
export class CommandModalService {
|
||||
constructor(private modalService: BsModalService) {
|
||||
}
|
||||
|
||||
spawn<TModel>(options: {
|
||||
dataSource: IDataSource<TModel>
|
||||
command: string,
|
||||
model: TModel,
|
||||
template: TemplateRef<any>,
|
||||
commandTitle?: string,
|
||||
refreshOnSuccess?: boolean,
|
||||
commandText?: string,
|
||||
cancelText?: string,
|
||||
animated?: boolean,
|
||||
btnClass?: string,
|
||||
modalSize?: string,
|
||||
disableValidationSummary?: boolean,
|
||||
backdrop?: boolean,
|
||||
ignoreBackdropClick?: boolean,
|
||||
params?: any,
|
||||
success?: EventEmitter<any>
|
||||
}) {
|
||||
options.dataSource.resolveCommandModelByName({
|
||||
command: options.command,
|
||||
model: options.model,
|
||||
params: options.params
|
||||
}).subscribe(commandModel => {
|
||||
const initialState = {
|
||||
dataSource: options.dataSource,
|
||||
command: options.command,
|
||||
commandModel,
|
||||
template: options.template,
|
||||
title: options.commandTitle,
|
||||
disableValidationSummary: options.disableValidationSummary === undefined ? false : options.disableValidationSummary,
|
||||
refreshOnSuccess: options.refreshOnSuccess === undefined ? true : options.refreshOnSuccess,
|
||||
commandText: options.commandText || 'OK',
|
||||
cancelText: options.cancelText || 'Cancel',
|
||||
successEmitter: options.success,
|
||||
btnClass: options.btnClass || 'primary'
|
||||
};
|
||||
this.modalService.show(CommandModalComponent, {
|
||||
animated: options.animated === undefined ? true : options.animated,
|
||||
class: options.modalSize,
|
||||
initialState,
|
||||
backdrop: options.backdrop === undefined ? true : options.backdrop,
|
||||
ignoreBackdropClick: options.ignoreBackdropClick === undefined ? false : options.ignoreBackdropClick
|
||||
});
|
||||
}, error => {
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title pull-left">{{ title }}</h4>
|
||||
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ng-container [ngTemplateOutlet]="template"
|
||||
[ngTemplateOutletContext]="{ $implicit: commandModel, loading: loading, dataSource: dataSource }"></ng-container>
|
||||
<div *ngIf="hasError && !disableValidationSummary" class="alert alert-danger mt-2" style="white-space: pre-wrap">{{validationMessage}}</div>
|
||||
<div *ngIf="hasErrorMessage" class="alert alert-danger mt-2" style="white-space: pre-wrap">{{errorMessage}}</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-light" (click)="modalRef.hide()"
|
||||
[disabled]="loading">{{ cancelText }}</button>
|
||||
<button type="button" class="btn btn-{{btnClass}}" [disabled]="loading" (click)="onSubmit()">{{ commandText }}</button>
|
||||
<br>
|
||||
|
||||
<div class="progress" style="width: 100%" *ngIf="loading">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
.field-error{
|
||||
border: 1px solid red;
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
import {Component, EventEmitter, OnDestroy, OnInit, TemplateRef} from '@angular/core';
|
||||
import {IDataSource} from '@poweredsoft/data';
|
||||
import {BsModalRef} from 'ngx-bootstrap/modal';
|
||||
import {finalize} from 'rxjs/operators';
|
||||
import {Subscription} from 'rxjs';
|
||||
import {NgForm} from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'bsx-command-modal',
|
||||
templateUrl: './command-modal.component.html',
|
||||
styleUrls: ['./command-modal.component.scss']
|
||||
})
|
||||
export class CommandModalComponent implements OnInit, OnDestroy {
|
||||
title: string;
|
||||
template: TemplateRef<any>;
|
||||
command: string;
|
||||
commandModel: any;
|
||||
dataSource: IDataSource<any>;
|
||||
refreshOnSuccess: boolean;
|
||||
loading: boolean;
|
||||
commandText: string;
|
||||
cancelText: string;
|
||||
form:NgForm;
|
||||
validationMessage:string ;
|
||||
disableValidationSummary: boolean;
|
||||
btnClass:string;
|
||||
successEmitter: EventEmitter<any>;
|
||||
hasError: boolean;
|
||||
errorMessage: string = '';
|
||||
|
||||
private _notifyMessage: Subscription;
|
||||
private _validationError: Subscription;
|
||||
|
||||
constructor(public modalRef: BsModalRef) { }
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._notifyMessage.unsubscribe();
|
||||
this._validationError.unsubscribe();
|
||||
}
|
||||
|
||||
get hasErrorMessage() {
|
||||
return this.errorMessage;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._notifyMessage = this.dataSource.notifyMessage$.subscribe(message => {
|
||||
if (message.type !== 'info' && message.type !== 'success')
|
||||
this.errorMessage = message.message;
|
||||
});
|
||||
|
||||
this._validationError = this.dataSource.validationError$.subscribe(validatorErrors => {
|
||||
let validationSummary = '';
|
||||
Object.getOwnPropertyNames(validatorErrors.errors).forEach(property => {
|
||||
const errors = validatorErrors.errors[property].join('\n');
|
||||
validationSummary += errors + '\n';
|
||||
});
|
||||
this.validationMessage = validationSummary.trim();
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit(){
|
||||
this.loading = true;
|
||||
this.validationMessage = null;
|
||||
|
||||
this.dataSource.executeCommandByName(this.command, this.commandModel)
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
this.loading = false;
|
||||
})
|
||||
)
|
||||
.subscribe(commandResult => {
|
||||
if (this.refreshOnSuccess)
|
||||
this.dataSource.refresh();
|
||||
this.hasError = false;
|
||||
this.modalRef.hide();
|
||||
this.successEmitter.emit(commandResult);
|
||||
}, fail => {
|
||||
this.hasError = true;
|
||||
// you do not want to close on failure.. so just ignore..
|
||||
});
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import { Directive, HostListener, Input, TemplateRef, Output, EventEmitter } from '@angular/core';
|
||||
import { IDataSource } from '@poweredsoft/data';
|
||||
import { BsModalService } from 'ngx-bootstrap/modal';
|
||||
import { CommandModalComponent } from '../command-modal/command-modal.component';
|
||||
|
||||
@Directive({
|
||||
selector: '[bsxCommandModal]'
|
||||
})
|
||||
export class CommandModalDirective {
|
||||
constructor(private modalService: BsModalService) { }
|
||||
|
||||
@Input() dataSource: IDataSource<any>;
|
||||
@Input() command: string;
|
||||
@Input() model: any;
|
||||
@Input() template: TemplateRef<any>;
|
||||
@Input() commandTitle: string;
|
||||
@Input() refreshOnSuccess: boolean;
|
||||
@Input() commandText: string;
|
||||
@Input() cancelText: string;
|
||||
@Input() animated: boolean;
|
||||
@Input() btnClass: string;
|
||||
@Input() modalSize: string;
|
||||
@Input() disableValidationSummary: boolean;
|
||||
@Input() backdrop: boolean;
|
||||
@Input() ignoreBackdropClick: boolean;
|
||||
@Input() params: any;
|
||||
|
||||
@Output() success: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
@HostListener('click')
|
||||
wasClicked() {
|
||||
this.dataSource.resolveCommandModelByName({
|
||||
command: this.command,
|
||||
model: this.model,
|
||||
params: this.params
|
||||
}).subscribe(commandModel => {
|
||||
const initialState = {
|
||||
dataSource: this.dataSource,
|
||||
command: this.command,
|
||||
commandModel,
|
||||
template: this.template,
|
||||
title: this.commandTitle,
|
||||
disableValidationSummary: this.disableValidationSummary === undefined ? false : this.disableValidationSummary,
|
||||
refreshOnSuccess: this.refreshOnSuccess === undefined ? true : this.refreshOnSuccess,
|
||||
commandText: this.commandText || 'OK',
|
||||
cancelText: this.cancelText || 'Cancel',
|
||||
successEmitter: this.success,
|
||||
btnClass: this.btnClass || 'primary'
|
||||
};
|
||||
this.modalService.show(CommandModalComponent, {
|
||||
animated: this.animated === undefined ? true : this.animated,
|
||||
class: this.modalSize,
|
||||
initialState,
|
||||
backdrop: this.backdrop === undefined ? true : this.backdrop,
|
||||
ignoreBackdropClick: this.ignoreBackdropClick === undefined ? false : this.ignoreBackdropClick
|
||||
});
|
||||
}, error => {
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {CommandDirective} from './directives/command.directive';
|
||||
import {ConfirmModalModule} from '../confirm-modal/confirm-modal.module';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [CommandDirective],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ConfirmModalModule
|
||||
],
|
||||
exports:[CommandDirective]
|
||||
})
|
||||
export class CommandModule { }
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
import {Directive, EventEmitter, HostListener, Input, Output} from '@angular/core';
|
||||
import {IDataSource} from '@poweredsoft/data';
|
||||
import {finalize} from 'rxjs/operators';
|
||||
import {ConfirmModalService} from '../../confirm-modal/confirm-modal.service';
|
||||
|
||||
@Directive({
|
||||
selector: '[bsxCommand]'
|
||||
})
|
||||
export class CommandDirective {
|
||||
constructor(private confirmModalService: ConfirmModalService) { }
|
||||
|
||||
@Input() dataSource: IDataSource<any>;
|
||||
@Input() command: string;
|
||||
@Input() model: any;
|
||||
@Input() refreshOnSuccess: boolean;
|
||||
@Input() animated: boolean;
|
||||
@Input() params: any;
|
||||
|
||||
@Input() confirm: boolean;
|
||||
@Input() confirmMessage: string;
|
||||
@Input() yesText: string;
|
||||
@Input() noText: string;
|
||||
@Input() yesClass: string;
|
||||
@Input() noClass: string;
|
||||
|
||||
@Output() success: EventEmitter<any> = new EventEmitter<any>();
|
||||
@Output() failure: EventEmitter<any> = new EventEmitter<any>();
|
||||
@Output() loading: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||
|
||||
private doCommand() {
|
||||
this.dataSource.resolveCommandModelByName({
|
||||
command: this.command,
|
||||
model: this.model,
|
||||
params: this.params
|
||||
}).subscribe(commandModel => {
|
||||
this.loading.emit(true);
|
||||
this.dataSource.executeCommandByName(this.command, commandModel)
|
||||
.pipe(
|
||||
finalize(() => this.loading.emit(false))
|
||||
)
|
||||
.subscribe(
|
||||
commandResult => {
|
||||
if (this.refreshOnSuccess !== false)
|
||||
this.dataSource.refresh();
|
||||
|
||||
this.success.emit(commandResult);
|
||||
},
|
||||
commandError => {
|
||||
this.failure.emit(commandError);
|
||||
}
|
||||
);
|
||||
}, resolveCommandError => {
|
||||
this.failure.emit(resolveCommandError);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@HostListener('click')
|
||||
wasClicked() {
|
||||
|
||||
if (this.confirm) {
|
||||
this.confirmModalService.confirm({
|
||||
message: this.confirmMessage,
|
||||
yesText: this.yesText || 'yes',
|
||||
yesClass: this.yesClass || 'danger',
|
||||
noText: this.noText || 'no',
|
||||
noClass: this.noClass || 'light',
|
||||
}).subscribe(result => {
|
||||
if (result)
|
||||
this.doCommand();
|
||||
})
|
||||
} else {
|
||||
this.doCommand();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<div class="modal-body text-center">
|
||||
<p>{{ message }}</p>
|
||||
<button type="button" [ngClass]="yesButtonClass" (click)="confirm()">{{ yesText }}</button>
|
||||
|
||||
<button type="button" [ngClass]="noButtonClass" (click)="decline()">{{ noText }}</button>
|
||||
</div>
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {BsModalRef} from 'ngx-bootstrap/modal';
|
||||
import {Observer} from 'rxjs';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'bsx-confirm-modal',
|
||||
templateUrl: './confirm-modal.component.html',
|
||||
styleUrls: ['./confirm-modal.component.scss']
|
||||
})
|
||||
export class ConfirmModalComponent {
|
||||
@Inject(BsModalRef) modelRef!: BsModalRef;
|
||||
yesText: string;
|
||||
noText: string;
|
||||
message: string;
|
||||
yesClass: string;
|
||||
noClass: string;
|
||||
observer: Observer<boolean>;
|
||||
|
||||
get yesButtonClass() {
|
||||
return `btn btn-${this.yesClass}`
|
||||
}
|
||||
|
||||
get noButtonClass() {
|
||||
return `btn btn-${this.noClass}`
|
||||
}
|
||||
|
||||
confirm(): void {
|
||||
this.modelRef.hide();
|
||||
this.observer.next(true);
|
||||
this.observer.complete();
|
||||
}
|
||||
|
||||
decline(): void {
|
||||
this.modelRef.hide();
|
||||
this.observer.next(false);
|
||||
this.observer.complete();
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {ConfirmModalComponent} from './confirm-modal-components/confirm-modal/confirm-modal.component';
|
||||
import {ModalModule} from 'ngx-bootstrap/modal';
|
||||
import {ConfirmModalService} from './confirm-modal.service';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ConfirmModalComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ModalModule.forRoot(),
|
||||
],
|
||||
exports: [],
|
||||
providers: [ConfirmModalService]
|
||||
})
|
||||
export class ConfirmModalModule { }
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BsModalService } from 'ngx-bootstrap/modal';
|
||||
import { ConfirmModalComponent } from './confirm-modal-components/confirm-modal/confirm-modal.component';
|
||||
import { Observable, Observer } from 'rxjs';
|
||||
|
||||
export interface IConfirmModalOptions
|
||||
{
|
||||
yesClass?: string;
|
||||
noClass?: string;
|
||||
message: string;
|
||||
yesText?: string;
|
||||
noText?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ConfirmModalService {
|
||||
constructor(private modalService: BsModalService) { }
|
||||
|
||||
confirm(options: IConfirmModalOptions) : Observable<boolean> {
|
||||
return new Observable<boolean>((o: Observer<boolean>) => {
|
||||
const initialState = {
|
||||
message: options.message,
|
||||
yesText: options.yesText || 'yes',
|
||||
noText: options.noText || 'no',
|
||||
yesClass: options.yesClass || 'primary',
|
||||
noClass: options.noClass || 'light',
|
||||
observer: o
|
||||
};
|
||||
|
||||
this.modalService.show(ConfirmModalComponent, {
|
||||
initialState: initialState,
|
||||
animated: true,
|
||||
keyboard: false
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {DataSourceTextFilterComponent} from './text-filter/data-source-text-filter.component';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {DataSourceNumberFilterComponent} from './number-filter/data-source-number-filter.component';
|
||||
import {DataSourceDatetimeFilterComponent} from './datetime-filter/data-source-datetime-filter.component';
|
||||
import {BsDatepickerModule} from 'ngx-bootstrap/datepicker';
|
||||
import {PopoverModule} from 'ngx-bootstrap/popover';
|
||||
import {BsDropdownModule} from 'ngx-bootstrap/dropdown';
|
||||
import {TooltipModule} from 'ngx-bootstrap/tooltip';
|
||||
|
||||
@NgModule({
|
||||
declarations: [DataSourceTextFilterComponent, DataSourceNumberFilterComponent,DataSourceDatetimeFilterComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
BsDatepickerModule.forRoot(),
|
||||
PopoverModule.forRoot(),
|
||||
BsDropdownModule.forRoot(),
|
||||
TooltipModule.forRoot()
|
||||
],
|
||||
exports: [DataSourceTextFilterComponent, DataSourceNumberFilterComponent,DataSourceDatetimeFilterComponent]
|
||||
})
|
||||
export class DataSourceFilterModule { }
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
<ng-template #popTemplate>
|
||||
<form (ngSubmit)="applyFilter()" novalidate>
|
||||
<div class="container" >
|
||||
<div class="row">
|
||||
<select class="custom-select" title="Choose one of the following..." [(ngModel)]="filterType" [ngModelOptions]="{standalone: true}">
|
||||
<option *ngFor="let filter of filterTypes" [value]="filter.value">{{filter.key}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row mt-1 mb-1">
|
||||
<input *ngIf="!isRangeFilter"
|
||||
type="text"
|
||||
placeholder="Datepicker"
|
||||
class="form-control"
|
||||
bsDatepicker [(ngModel)]="filterValue" [ngModelOptions]="{standalone: true}">
|
||||
</div>
|
||||
|
||||
<div class="row mt-1 mb-1">
|
||||
<input *ngIf="isRangeFilter" type="text"
|
||||
placeholder="Daterangepicker"
|
||||
class="form-control"
|
||||
bsDaterangepicker [(ngModel)]="filterValue" [ngModelOptions]="{standalone: true}">
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<button class="btn btn-primary mr-1" type="submit" >Filter</button>
|
||||
<button type="button" class="btn btn-warning" *ngIf="!isFiltering" (click)="pop.hide()">Hide</button>
|
||||
<button type="button" class="btn btn-dark" *ngIf="isFiltering" (click)="clearFilter()">Clear</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
</ng-template>
|
||||
|
||||
<!-- <button [popover]="popTemplate" class="btn btn-default" [(isOpen)]="filterPopUpOpened" [outsideClick]="true" #pop="bs-popover">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 477.875 477.875" style="enable-background:new 0 0 477.875 477.875;" xml:space="preserve" fill-opacity="0.5" [tooltip]="showTooltip()" [tooltipEnable]="!filterPopUpOpened" width="13px" height="13px" [ngStyle]="{'fill': isFiltering ? 'red': 'black', 'fill-opacity': isFiltering ? '1': '0.5'}">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M460.804,0H17.071C7.645,0,0.004,7.641,0.004,17.067V102.4c-0.004,4.842,2.05,9.458,5.649,12.698l165.018,148.514V460.8
|
||||
c-0.004,9.426,7.633,17.07,17.059,17.075c2.651,0.001,5.266-0.615,7.637-1.8l102.4-51.2c5.786-2.891,9.441-8.806,9.438-15.275
|
||||
V263.612l165.018-148.48c3.608-3.247,5.662-7.878,5.649-12.732V17.067C477.871,7.641,470.23,0,460.804,0z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
</button> -->
|
||||
|
||||
<svg id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 477.875 477.875" style="enable-background:new 0 0 477.875 477.875;" xml:space="preserve" fill-opacity="0.5" [tooltip]="showTooltip()" [tooltipEnable]="!filterPopUpOpened" width="13px" height="13px" [ngStyle]="{'fill': isFiltering ? 'red': 'black', 'fill-opacity': isFiltering ? '1': '0.5'}" [popover]="popTemplate" [(isOpen)]="filterPopUpOpened" [outsideClick]="true" #pop="bs-popover">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M460.804,0H17.071C7.645,0,0.004,7.641,0.004,17.067V102.4c-0.004,4.842,2.05,9.458,5.649,12.698l165.018,148.514V460.8
|
||||
c-0.004,9.426,7.633,17.07,17.059,17.075c2.651,0.001,5.266-0.615,7.637-1.8l102.4-51.2c5.786-2.891,9.441-8.806,9.438-15.275
|
||||
V263.612l165.018-148.48c3.608-3.247,5.662-7.878,5.649-12.732V17.067C477.871,7.641,470.23,0,460.804,0z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
|
||||
import {FilterType, ICompositeFilter, IDataSource, ISimpleFilter} from '@poweredsoft/data';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'bsx-ds-datetime-filter',
|
||||
templateUrl: './data-source-datetime-filter.component.html',
|
||||
styleUrls: ['./data-source-datetime-filter.component.scss']
|
||||
})
|
||||
export class DataSourceDatetimeFilterComponent implements OnInit {
|
||||
@Input() path: string;
|
||||
@Input() dataSource : IDataSource<any>;
|
||||
@Input() filterType: string;
|
||||
@Output() filterTypeChanged: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
filterPopUpOpened: boolean = false;
|
||||
isFiltering: boolean;
|
||||
filterValue: Date = null;
|
||||
filterTypes = [
|
||||
{key:'Equal', value: 'Equal'},
|
||||
{key:'Greater Than', value: 'GreaterThan'},
|
||||
{key:'Less Than', value: 'LessThan'},
|
||||
{key:'Greater Than Equal', value: 'GreaterThanOrEqual'},
|
||||
{key:'Less Than Equal', value: 'LessThanOrEqual'},
|
||||
{key:'Range', value: FilterType.COMPOSITE }
|
||||
];
|
||||
|
||||
bsInlineValue = new Date();
|
||||
bsInlineRangeValue: Date[];
|
||||
maxDate = new Date();
|
||||
|
||||
constructor() {
|
||||
this.maxDate.setDate(this.maxDate.getDate() + 7);
|
||||
this.bsInlineRangeValue = [this.bsInlineValue, this.maxDate];
|
||||
}
|
||||
|
||||
get isRangeFilter() {
|
||||
return this.filterType == FilterType.COMPOSITE;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.filterType)
|
||||
this.filterType = 'Equal';
|
||||
}
|
||||
|
||||
showTooltip(){
|
||||
return "Filter by "+ this.path;
|
||||
}
|
||||
|
||||
clearFilter() {
|
||||
this.filterValue = null;
|
||||
this.isFiltering = false;
|
||||
const existingFilter = this.dataSource.filters.find(t => (t as ISimpleFilter).path == this.path) as ISimpleFilter;
|
||||
if (existingFilter) {
|
||||
this.dataSource.query({
|
||||
page: 1,
|
||||
filters: this.dataSource.filters.filter(t => t != existingFilter)
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isRangeFilter) {
|
||||
let existingFilter2 = this.dataSource.filters.find(t=> t.type == 'Composite');
|
||||
this.dataSource.query({
|
||||
page: 1,
|
||||
filters: this.dataSource.filters.filter(t => t != existingFilter2)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
applyFilter(){
|
||||
|
||||
this.isFiltering = true;
|
||||
const filters = this.dataSource.filters;
|
||||
let compositeF: any = null;
|
||||
let freshFilter: ISimpleFilter = null;
|
||||
let startDate: Date;
|
||||
let endDate: Date;
|
||||
|
||||
// TODO create filter here.
|
||||
if(Array.isArray(this.filterValue)){ //check if it's a dateRange value
|
||||
startDate = this.filterValue[0];
|
||||
endDate = this.filterValue[1];
|
||||
|
||||
compositeF = {
|
||||
type: FilterType.COMPOSITE,
|
||||
filters: [
|
||||
{
|
||||
path: this.path,
|
||||
type: 'GREATERTHANOREQUAL',
|
||||
value: startDate,
|
||||
},
|
||||
{
|
||||
and: true,
|
||||
path: this.path,
|
||||
type: 'LESSTHANOREQUAL',
|
||||
value: endDate,
|
||||
}
|
||||
]
|
||||
};
|
||||
} else {
|
||||
freshFilter = {
|
||||
type: this.filterType,
|
||||
and: true,
|
||||
path: this.path,
|
||||
value: this.filterValue
|
||||
};
|
||||
}
|
||||
|
||||
// TODO???
|
||||
// set this for gql better handling of variant.
|
||||
//(freshFilter as IGraphQLFilter).__gqlVariantType = 'DATETIME';
|
||||
|
||||
const existingFilterIndex = filters.findIndex(t => {
|
||||
if (t.type == 'COMPOSITE') {
|
||||
const compositeFilter = t as ICompositeFilter;
|
||||
return compositeFilter.filters && compositeFilter.filters.length
|
||||
&& compositeFilter[0].path == this.path;
|
||||
} else {
|
||||
return (t as ISimpleFilter).path == this.path;
|
||||
}
|
||||
});
|
||||
|
||||
if (existingFilterIndex == -1) {
|
||||
// create it.
|
||||
if(compositeF) {
|
||||
filters.push(compositeF);
|
||||
} else {
|
||||
filters.push(freshFilter);
|
||||
}
|
||||
|
||||
} else {
|
||||
// replace it.
|
||||
if (compositeF) {
|
||||
filters[existingFilterIndex] = compositeF;
|
||||
} else {
|
||||
filters[existingFilterIndex] = freshFilter;
|
||||
}
|
||||
}
|
||||
|
||||
this.dataSource.query({
|
||||
filters: filters,
|
||||
page: 1
|
||||
});
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
<ng-template #popTemplate>
|
||||
<form (ngSubmit)="applyFilter()" novalidate>
|
||||
<div class="container" >
|
||||
<div class="row">
|
||||
<select class="custom-select" title="Choose one of the following..." [(ngModel)]="filterType" [ngModelOptions]="{standalone: true}">
|
||||
<option *ngFor="let filter of filterTypes" [value]="filter.value">{{filter.key}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row mt-1 mb-1">
|
||||
<input type="number" class="form-control" placeholder="Value" aria-label="number"
|
||||
aria-describedby="basic-addon1" [(ngModel)]="filterValue" [ngModelOptions]="{standalone: true}">
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary mr-1">Filter</button>
|
||||
<button type="button" class="btn btn-warning" *ngIf="!isFiltering" (click)="pop.hide()">Hide</button>
|
||||
<button type="button" class="btn btn-dark" *ngIf="isFiltering" (click)="clearFilter()">Clear</button>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</ng-template>
|
||||
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 477.875 477.875" style="enable-background:new 0 0 477.875 477.875;" xml:space="preserve" fill-opacity="0.5" [tooltip]="showTooltip()" [tooltipEnable]="!filterIsOpened" width="13px" height="13px" [ngStyle]="{'fill': isFiltering ? 'red': 'black', 'fill-opacity': isFiltering ? '1': '0.5'}" [popover]="popTemplate" [(isOpen)]="filterIsOpened" [outsideClick]="true" #pop="bs-popover">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M460.804,0H17.071C7.645,0,0.004,7.641,0.004,17.067V102.4c-0.004,4.842,2.05,9.458,5.649,12.698l165.018,148.514V460.8
|
||||
c-0.004,9.426,7.633,17.07,17.059,17.075c2.651,0.001,5.266-0.615,7.637-1.8l102.4-51.2c5.786-2.891,9.441-8.806,9.438-15.275
|
||||
V263.612l165.018-148.48c3.608-3.247,5.662-7.878,5.649-12.732V17.067C477.871,7.641,470.23,0,460.804,0z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {IDataSource, ISimpleFilter} from '@poweredsoft/data';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'bsx-ds-number-filter',
|
||||
templateUrl: './data-source-number-filter.component.html',
|
||||
styleUrls: ['./data-source-number-filter.component.scss']
|
||||
})
|
||||
export class DataSourceNumberFilterComponent {
|
||||
@Input() dataSource : IDataSource<any>;
|
||||
@Input() path: string;
|
||||
|
||||
filterType: string = 'Equal';
|
||||
filterValue: number = 0;
|
||||
isFiltering: boolean;
|
||||
filterIsOpened: boolean = false;
|
||||
filterTypes = [
|
||||
{key:'Equals', value: 'Equal'},
|
||||
{key:'Greater Than', value: 'GreaterThan'},
|
||||
{key:'Less Than', value: 'LessThan'},
|
||||
{key:'Greater Than Equal', value: 'GreaterThanOrEqual'},
|
||||
{key:'Less Than Equal', value: 'LessThanOrEqual'},
|
||||
];
|
||||
|
||||
clearFilter() {
|
||||
this.filterValue = 0;
|
||||
this.isFiltering = false;
|
||||
const existingFilter = this.dataSource.filters.find(t => (t as ISimpleFilter).path == this.path) as ISimpleFilter;
|
||||
if (existingFilter) {
|
||||
this.dataSource.query({
|
||||
page: 1,
|
||||
filters: this.dataSource.filters.filter(t => t != existingFilter)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
applyFilter(){
|
||||
this.isFiltering = true;
|
||||
const filters = this.dataSource.filters;
|
||||
const existingFilter = filters.find(t => (t as ISimpleFilter).path == this.path) as ISimpleFilter;
|
||||
if (existingFilter) {
|
||||
existingFilter.type = this.filterType;
|
||||
existingFilter.value =this.filterValue.toString();
|
||||
} else {
|
||||
filters.push(<ISimpleFilter>{
|
||||
and: true,
|
||||
type: this.filterType,
|
||||
path: this.path,
|
||||
value: this.filterValue.toString()
|
||||
})
|
||||
}
|
||||
|
||||
this.dataSource.query({
|
||||
filters: filters,
|
||||
page: 1
|
||||
})
|
||||
}
|
||||
|
||||
showTooltip(){
|
||||
return "Filter by "+ this.path;
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
<ng-template #popTemplate>
|
||||
<form (ngSubmit)="applyFilter(pop)" novalidate>
|
||||
|
||||
<div class="container" >
|
||||
<div class="row">
|
||||
<select class="custom-select" title="Choose one of the following..." [(ngModel)]="filterType" [ngModelOptions]="{standalone: true}">
|
||||
<option *ngFor="let filter of filterTypes" [value]="filter.value">{{filter.key}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row mt-1 mb-1">
|
||||
<input type="text" class="form-control" placeholder="Value" aria-label="Username"
|
||||
aria-describedby="basic-addon1" [(ngModel)]="filterValue" [ngModelOptions]="{standalone: true}" >
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary mr-1" type="submit">Filter</button>
|
||||
<button type="button" class="btn btn-warning" *ngIf="!isFiltering" (click)="pop.hide()">Hide</button>
|
||||
<button type="button" class="btn btn-dark" *ngIf="isFiltering" (click)="clearFilter()">Clear</button>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</ng-template>
|
||||
|
||||
<!-- <button [popover]="popTemplate" class="btn btn-default" [(isOpen)]="filterIsOpenned" [outsideClick]="true" #pop="bs-popover">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 477.875 477.875" style="enable-background:new 0 0 477.875 477.875;" xml:space="preserve" fill-opacity="0.5" [tooltip]="showTooltip()" [tooltipEnable]="!filterIsOpenned" width="13px" height="13px" [ngStyle]="{'fill': isFiltering ? 'red': 'black', 'fill-opacity': isFiltering ? '1': '0.5'}">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M460.804,0H17.071C7.645,0,0.004,7.641,0.004,17.067V102.4c-0.004,4.842,2.05,9.458,5.649,12.698l165.018,148.514V460.8
|
||||
c-0.004,9.426,7.633,17.07,17.059,17.075c2.651,0.001,5.266-0.615,7.637-1.8l102.4-51.2c5.786-2.891,9.441-8.806,9.438-15.275
|
||||
V263.612l165.018-148.48c3.608-3.247,5.662-7.878,5.649-12.732V17.067C477.871,7.641,470.23,0,460.804,0z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
</button> -->
|
||||
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 477.875 477.875" style="enable-background:new 0 0 477.875 477.875;" xml:space="preserve" fill-opacity="0.5" [tooltip]="showTooltip()" [tooltipEnable]="!filterIsOpened" width="13px" height="13px" [ngStyle]="{'fill': isFiltering ? 'red': 'black', 'fill-opacity': isFiltering ? '1': '0.5'}" [popover]="popTemplate" [(isOpen)]="filterIsOpened" [outsideClick]="true" #pop="bs-popover">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M460.804,0H17.071C7.645,0,0.004,7.641,0.004,17.067V102.4c-0.004,4.842,2.05,9.458,5.649,12.698l165.018,148.514V460.8
|
||||
c-0.004,9.426,7.633,17.07,17.059,17.075c2.651,0.001,5.266-0.615,7.637-1.8l102.4-51.2c5.786-2.891,9.441-8.806,9.438-15.275
|
||||
V263.612l165.018-148.48c3.608-3.247,5.662-7.878,5.649-12.732V17.067C477.871,7.641,470.23,0,460.804,0z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {IDataSource, ISimpleFilter} from '@poweredsoft/data';
|
||||
import {PopoverDirective} from 'ngx-bootstrap/popover';
|
||||
|
||||
@Component({
|
||||
selector: 'bsx-ds-text-filter',
|
||||
templateUrl: './data-source-text-filter.component.html',
|
||||
styleUrls: ['./data-source-text-filter.component.scss']
|
||||
})
|
||||
export class DataSourceTextFilterComponent {
|
||||
@Input() dataSource : IDataSource<any>;
|
||||
@Input() path: string;
|
||||
|
||||
filterType: string = 'Contains';
|
||||
filterValue: string = null;
|
||||
isFiltering: boolean;
|
||||
filterIsOpened: boolean = false;
|
||||
|
||||
filterTypes = [
|
||||
{key:'Contains', value: 'Contains'},
|
||||
{key:'Equals', value: 'Equal'},
|
||||
{key:'Starts With', value: 'startsWith'},
|
||||
{key:'Ends With', value: 'endsWith'}
|
||||
];
|
||||
|
||||
clearFilter() {
|
||||
this.filterValue = '';
|
||||
this.isFiltering = false;
|
||||
const existingFilter = this.dataSource.filters.find(t => (t as ISimpleFilter).path == this.path) as ISimpleFilter;
|
||||
if (existingFilter) {
|
||||
this.dataSource.query({
|
||||
page: 1,
|
||||
filters: this.dataSource.filters.filter(t => t != existingFilter)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
applyFilter(pop: PopoverDirective = null){
|
||||
this.isFiltering = true;
|
||||
const filters = this.dataSource.filters;
|
||||
const existingFilter = filters.find(t => (t as ISimpleFilter).path == this.path) as ISimpleFilter;
|
||||
if (existingFilter) {
|
||||
existingFilter.type = this.filterType;
|
||||
existingFilter.value = this.filterValue;
|
||||
} else {
|
||||
filters.push(<ISimpleFilter>{
|
||||
and: true,
|
||||
type: this.filterType,
|
||||
path: this.path,
|
||||
value: this.filterValue
|
||||
})
|
||||
}
|
||||
|
||||
this.dataSource.query({
|
||||
filters: filters,
|
||||
page: 1
|
||||
})
|
||||
|
||||
if (pop)
|
||||
pop.hide();
|
||||
}
|
||||
|
||||
showTooltip(){
|
||||
return "Filter by "+ this.path;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {DataSourceSortingComponent} from './ds-sorting/data-source-sorting.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [DataSourceSortingComponent],
|
||||
imports: [
|
||||
CommonModule
|
||||
],
|
||||
exports:[DataSourceSortingComponent]
|
||||
})
|
||||
export class DataSourceSortingModule { }
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<span>
|
||||
<span class="caret dropdown-toggle" [class.text-primary]="isSorting" [class.not-sorting-state]="!isSorting" [class.sort-desc]="isAscending" (click)="sorting()"></span>
|
||||
</span>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
:host span.dropdown-toggle.sort-desc{
|
||||
transform: rotate(180deg);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
:host span{
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
:host .not-sorting-state{
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
:host .not-sorting-state:hover{
|
||||
opacity: 1;
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {IDataSource} from '@poweredsoft/data';
|
||||
|
||||
@Component({
|
||||
selector: 'bsx-ds-sorting',
|
||||
templateUrl: './data-source-sorting.component.html',
|
||||
styleUrls: ['./data-source-sorting.component.scss']
|
||||
})
|
||||
export class DataSourceSortingComponent {
|
||||
@Input() dataSource : IDataSource<any>;
|
||||
@Input() path: string;
|
||||
|
||||
get sort() {
|
||||
return this.dataSource.sorts.find(t => t.path == this.path);
|
||||
}
|
||||
|
||||
get isSorting() {
|
||||
return this.sort ? true : false;
|
||||
}
|
||||
|
||||
get isAscending() {
|
||||
return !this.isSorting ? true : this.sort.ascending;
|
||||
}
|
||||
|
||||
sorting(){
|
||||
if (!this.isSorting) {
|
||||
this.dataSource.sorts.push({
|
||||
path: this.path,
|
||||
ascending: true
|
||||
});
|
||||
} else if(this.isSorting && this.isAscending) {
|
||||
this.sort.ascending = false;
|
||||
} else {
|
||||
this.dataSource.sorts = this.dataSource.sorts.filter(t => t.path != this.path);
|
||||
}
|
||||
|
||||
this.dataSource.query({
|
||||
sorts: this.dataSource.sorts,
|
||||
page: 1
|
||||
})
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import {Directive, EventEmitter, HostListener, Inject, Input, Output, TemplateRef} from '@angular/core';
|
||||
import {IDataSource} from '@poweredsoft/data';
|
||||
import {BsModalService} from 'ngx-bootstrap/modal';
|
||||
import {FormGroupCommandModalComponent} from '../form-group-command-modal/form-group-command-modal.component';
|
||||
import {FormGroup} from '@angular/forms';
|
||||
|
||||
export interface IModelFormCreateEvent
|
||||
{
|
||||
shouldSetCommandModel: boolean;
|
||||
viewModel: any;
|
||||
commandName: string;
|
||||
commandModel: any;
|
||||
formGroup?: FormGroup;
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[bsxFormGroupCommandModal]'
|
||||
})
|
||||
export class FormGroupCommandModalDirective {
|
||||
@Inject(BsModalService) private modalService!: BsModalService;
|
||||
|
||||
@Input() dataSource: IDataSource<any>;
|
||||
@Input() command: string;
|
||||
@Input() model: any;
|
||||
@Input() template: TemplateRef<any>;
|
||||
@Input() commandTitle: string;
|
||||
@Input() animated: boolean;
|
||||
@Input() refreshOnSuccess: boolean;
|
||||
@Input() commandText: string;
|
||||
@Input() cancelText: string;
|
||||
@Input() backdrop: boolean;
|
||||
@Input() ignoreBackdropClick: boolean;
|
||||
@Input() params: any;
|
||||
|
||||
@Output() formCreate: EventEmitter<IModelFormCreateEvent> = new EventEmitter<IModelFormCreateEvent>();
|
||||
@Output() success: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
@HostListener('click')
|
||||
wasClicked() {
|
||||
this.dataSource.resolveCommandModelByName({
|
||||
command: this.command,
|
||||
model: this.model,
|
||||
params: this.params,
|
||||
}).subscribe(commandModel => {
|
||||
const event = <IModelFormCreateEvent>{
|
||||
commandName: this.command,
|
||||
viewModel: this.model,
|
||||
commandModel: commandModel,
|
||||
shouldSetCommandModel: true
|
||||
}
|
||||
|
||||
this.formCreate.emit(event);
|
||||
if (event.formGroup == null)
|
||||
throw new Error('form group should be set, after form createEvent');
|
||||
|
||||
if (event.shouldSetCommandModel)
|
||||
event.formGroup.patchValue(commandModel);
|
||||
|
||||
const initialState = {
|
||||
dataSource: this.dataSource,
|
||||
command: this.command,
|
||||
template: this.template,
|
||||
title: this.commandTitle,
|
||||
refreshOnSuccess: this.refreshOnSuccess === undefined ? true : this.refreshOnSuccess,
|
||||
commandText: this.commandText || 'OK',
|
||||
cancelText: this.cancelText || 'Cancel',
|
||||
modelForm: event.formGroup,
|
||||
commandModel:commandModel,
|
||||
successEmitter: this.success
|
||||
};
|
||||
|
||||
this.modalService.show(FormGroupCommandModalComponent, {
|
||||
animated: this.animated === undefined ? true : this.animated,
|
||||
initialState,
|
||||
backdrop: this.backdrop === undefined ? true : this.backdrop,
|
||||
ignoreBackdropClick: this.ignoreBackdropClick === undefined ? false : this.ignoreBackdropClick
|
||||
});
|
||||
|
||||
}, error => {
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {FormGroupCommandModalComponent} from './form-group-command-modal/form-group-command-modal.component';
|
||||
import {FormGroupCommandModalDirective} from './directives/form-group-command-modal.directive';
|
||||
import {ModalModule} from 'ngx-bootstrap/modal';
|
||||
import {ReactiveFormsModule} from '@angular/forms';
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
ModalModule.forRoot(),
|
||||
ReactiveFormsModule
|
||||
],
|
||||
declarations: [FormGroupCommandModalComponent, FormGroupCommandModalDirective],
|
||||
exports: [FormGroupCommandModalDirective]
|
||||
})
|
||||
export class FormGroupCommandModalModule { }
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title pull-left">{{ title }}</h4>
|
||||
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="model-body">
|
||||
<ng-container [ngTemplateOutlet]="template" [ngTemplateOutletContext]="{ $implicit: modelForm, loading: loading }">
|
||||
</ng-container>
|
||||
|
||||
<div *ngIf="errorMessage" class="alert alert-danger mt-2" style="white-space: pre-wrap">{{ errorMessage }}</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-light" (click)="modalRef.hide()"
|
||||
[disabled]="loading">{{ cancelText }}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="attemptSave()" [disabled]="loading">{{commandText}}</button>
|
||||
<br>
|
||||
|
||||
<div class="progress" style="width: 100%" *ngIf="loading">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="100"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import {Component, EventEmitter, Inject, OnDestroy, OnInit, TemplateRef} from '@angular/core';
|
||||
import {BsModalRef} from 'ngx-bootstrap/modal';
|
||||
import {IDataSource} from '@poweredsoft/data';
|
||||
import {finalize} from 'rxjs/operators';
|
||||
import {Subscription} from 'rxjs'
|
||||
import {FormGroup} from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'bsx-form-group-command-modal',
|
||||
templateUrl: './form-group-command-modal.component.html',
|
||||
styleUrls: ['./form-group-command-modal.component.scss']
|
||||
})
|
||||
export class FormGroupCommandModalComponent implements OnInit, OnDestroy {
|
||||
@Inject(BsModalRef) public modalRef!: BsModalRef;
|
||||
|
||||
modelForm: FormGroup;
|
||||
title: string;
|
||||
template: TemplateRef<any>;
|
||||
command: string;
|
||||
dataSource: IDataSource<any>;
|
||||
refreshOnSuccess: boolean;
|
||||
loading: boolean;
|
||||
commandText: string;
|
||||
cancelText: string;
|
||||
errorMessage: string;
|
||||
commandModel: any;
|
||||
successEmitter: EventEmitter<any>;
|
||||
|
||||
private _notifyMessage: Subscription;
|
||||
private _validationError: Subscription;
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._notifyMessage.unsubscribe();
|
||||
this._validationError.unsubscribe();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.errorMessage = null;
|
||||
this._notifyMessage = this.dataSource.notifyMessage$.subscribe(message => {
|
||||
|
||||
});
|
||||
|
||||
this._validationError = this.dataSource.validationError$.subscribe(validatorErrors => {
|
||||
let validationSummary = '';
|
||||
Object.getOwnPropertyNames(validatorErrors.errors).forEach(property => {
|
||||
const errors = validatorErrors.errors[property].join('\n');
|
||||
validationSummary += errors + '\n';
|
||||
});
|
||||
this.errorMessage = validationSummary.trim();
|
||||
});
|
||||
}
|
||||
|
||||
attemptSave() {
|
||||
// this.errorMessage = null;
|
||||
// if (!this.modelForm.valid)
|
||||
// {
|
||||
// this.errorMessage = 'Form is not valid, please enter all required fields';
|
||||
// return;
|
||||
// }
|
||||
|
||||
const finalModel = this.modelForm.value;
|
||||
if(this.commandModel.id)
|
||||
{
|
||||
finalModel.id = this.commandModel.id;
|
||||
}
|
||||
this.loading = true;
|
||||
this.dataSource.executeCommandByName(this.command, finalModel)
|
||||
.pipe(
|
||||
finalize(() => {
|
||||
this.loading = false;
|
||||
})
|
||||
)
|
||||
.subscribe(commandResult => {
|
||||
if (this.refreshOnSuccess)
|
||||
this.dataSource.refresh();
|
||||
|
||||
this.modalRef.hide();
|
||||
this.successEmitter.emit(commandResult);
|
||||
}, fail => {
|
||||
// you do not want to close on failure.. so just ignore..
|
||||
});
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
<pagination [totalItems]="numberOfItems" [itemsPerPage]="pageSize" [(ngModel)]="dataSource.page" [maxSize]="10"
|
||||
[boundaryLinks]='true' previousText="‹" nextText="›" firstText="«" lastText="»"></pagination>
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import {ChangeDetectorRef, Component, Inject, Input, OnDestroy, OnInit} from '@angular/core';
|
||||
import {IDataSource} from '@poweredsoft/data';
|
||||
import {Subscription} from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'bsx-ds-pagination',
|
||||
templateUrl: './data-source-pagination.component.html',
|
||||
styleUrls: ['./data-source-pagination.component.scss']
|
||||
})
|
||||
export class DataSourcePaginationComponent implements OnInit, OnDestroy {
|
||||
@Inject(ChangeDetectorRef) private cdf!: ChangeDetectorRef;
|
||||
|
||||
@Input() dataSource: IDataSource<any>
|
||||
|
||||
numberOfItems: number = 0;
|
||||
private dataSubscription: Subscription;
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.dataSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
get pageSize() {
|
||||
return this.dataSource.pageSize;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.dataSubscription = this.dataSource.data$.subscribe(latest => {
|
||||
if (latest)
|
||||
this.numberOfItems = latest.totalRecords;
|
||||
else
|
||||
this.numberOfItems = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {DataSourcePaginationComponent} from './data-source-pagination/data-source-pagination.component';
|
||||
import {PaginationModule as ValorPaginationModule} from 'ngx-bootstrap/pagination';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [DataSourcePaginationComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ValorPaginationModule.forRoot(),
|
||||
FormsModule
|
||||
],
|
||||
exports: [DataSourcePaginationComponent]
|
||||
})
|
||||
export class PaginationModule { }
|
||||
@@ -0,0 +1,13 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {SpinnerComponent} from './spinner/spinner.component';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [SpinnerComponent],
|
||||
imports: [
|
||||
CommonModule
|
||||
],
|
||||
exports:[SpinnerComponent]
|
||||
})
|
||||
export class SpinnerModule { }
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<div class="center">
|
||||
<div class="spinner-grow text-primary" role="status">
|
||||
</div>
|
||||
<div class="spinner-grow text-secondary" role="status">
|
||||
</div>
|
||||
<div class="spinner-grow text-primary" role="status">
|
||||
</div>
|
||||
<div class="spinner-grow text-secondary" role="status">
|
||||
</div>
|
||||
</div>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
.center {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'bsx-spinner',
|
||||
templateUrl: './spinner.component.html',
|
||||
styleUrls: ['./spinner.component.scss']
|
||||
})
|
||||
export class SpinnerComponent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Public API Surface of ngx-bootstrap
|
||||
*/
|
||||
|
||||
export * from './lib/command-modal/command-modal.module';
|
||||
export * from './lib/command-modal/command-modal.service';
|
||||
export * from './lib/command-modal/directives/command-modal.directive';
|
||||
export * from './lib/form-group-command-modal/form-group-command-modal.module';
|
||||
export * from './lib/form-group-command-modal/directives/form-group-command-modal.directive';
|
||||
export * from './lib/pagination/data-source-pagination/data-source-pagination.component';
|
||||
export * from './lib/confirm-modal/confirm-modal.module';
|
||||
export * from './lib/confirm-modal/confirm-modal.service';
|
||||
export * from './lib/spinner/spinner.module';
|
||||
export * from './lib/spinner/spinner/spinner.component';
|
||||
export * from './lib/data-source-filter/data-source-filter.module';
|
||||
export * from './lib/data-source-filter/text-filter/data-source-text-filter.component';
|
||||
export * from './lib/data-source-filter/number-filter/data-source-number-filter.component';
|
||||
export * from './lib/data-source-filter/datetime-filter/data-source-datetime-filter.component';
|
||||
export * from './lib/data-source-sorting/data-source-sorting.module';
|
||||
export * from './lib/data-source-sorting/ds-sorting/data-source-sorting.component';
|
||||
export * from './lib/command/command.module';
|
||||
export * from './lib/command/directives/command.directive';
|
||||
export * from './lib/pagination/pagination.module';
|
||||
@@ -0,0 +1,26 @@
|
||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js/dist/zone';
|
||||
import 'zone.js/dist/zone-testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: {
|
||||
context(path: string, deep?: boolean, filter?: RegExp): {
|
||||
keys(): string[];
|
||||
<T>(id: string): T;
|
||||
};
|
||||
};
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting()
|
||||
);
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/lib",
|
||||
"target": "es2015",
|
||||
"declaration": true,
|
||||
"inlineSources": true,
|
||||
"types": [],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2018"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"skipTemplateCodegen": true,
|
||||
"strictMetadataEmit": true,
|
||||
"enableResourceInlining": true
|
||||
},
|
||||
"exclude": [
|
||||
"src/test.ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "./tsconfig.lib.json",
|
||||
"angularCompilerOptions": {
|
||||
"enableIvy": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts"
|
||||
],
|
||||
"include": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user