code cleanup, names refactoring

This commit is contained in:
2024-08-25 23:34:36 -04:00
parent 891a80fc70
commit 37778b6b47
210 changed files with 12454 additions and 17190 deletions
@@ -0,0 +1,29 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {DataGridComponent} from './data-grid/data-grid.component';
import {DataGridColDirective} from './directives/data-grid-col.directive';
import {DataGridColHeaderDirective} from './directives/data-grid-col-header.directive';
import {DataGridCellDirective} from './directives/data-grid-cell.directive';
import {DataGridFooterDirective} from './directives/data-grid-footer.directive';
import {DataGridHeaderDirective} from './directives/data-grid-header.directive';
import {DataGridLoaderDirective} from './directives/data-grid-loader.directive';
import {DataGridCellFilterDirective} from './directives/data-grid-cell-filter.directive';
import {DataGridColSortDirective} from './directives/data-grid-col-sort.directive';
@NgModule({
declarations: [
DataGridComponent,DataGridColDirective,DataGridColHeaderDirective,
DataGridCellDirective, DataGridFooterDirective, DataGridHeaderDirective,
DataGridLoaderDirective, DataGridCellFilterDirective, DataGridColSortDirective,
],
imports: [
CommonModule
],
exports: [
DataGridComponent,DataGridColDirective,DataGridColHeaderDirective,
DataGridCellDirective,DataGridFooterDirective, DataGridHeaderDirective,
DataGridLoaderDirective,DataGridCellFilterDirective,DataGridColSortDirective]
})
export class DataGridModule { }
@@ -0,0 +1,77 @@
<ng-container *ngIf="loading" [ngTemplateOutlet]="loadingTemplate"></ng-container>
<table [ngClass]="tableClasses">
<thead [ngClass]="headerClasses">
<tr *ngFor="let header of gridHeaders" >
<th [attr.colspan]="columns.length">
<ng-container [ngTemplateOutlet]="header.template"></ng-container>
</th>
</tr>
<tr>
<th *ngFor="let column of columns">
<div class="flex-container">
<div class="flex-item">
<ng-container *ngIf="hasHeaderTemplate(column)" >
<ng-container
[ngTemplateOutlet]="getColumnHeaderTemplate(column)"
></ng-container>
</ng-container>
</div>
<div class="flex-item">
<ng-container *ngIf="hasSortingTemplate(column)">
<ng-container [ngTemplateOutlet]="getSortingTemplate(column)"></ng-container>
</ng-container>
<ng-container *ngIf="hasFilterTemplate(column)">
<ng-container [ngTemplateOutlet]="getFilterTemplate(column)"></ng-container>
</ng-container>
</div>
</div>
</th>
</tr>
</thead>
<tbody *ngIf="!noData else noResultTemplate">
<tr *ngFor="let rowModel of latestResult.data; let i = index; let first = first; let last = last; let odd = odd">
<td *ngFor="let column of columns">
<ng-container *ngIf="hasCellTemplate(column)">
<ng-container
[ngTemplateOutlet]="getColumnCellTemplate(column)"
[ngTemplateOutletContext]="{
$implicit: rowModel,
column: column,
rowIndex: i,
first: first,
last: last,
odd: odd
}"
></ng-container>
</ng-container>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td *ngFor="let footer of gridFooters" [attr.colspan]="columns.length">
<ng-container [ngTemplateOutlet]="footer.template"></ng-container>
</td>
</tr>
</tfoot>
</table>
<ng-template #loadingTemplate>
<ng-container *ngFor="let loader of loaders">
<ng-container [ngTemplateOutlet]="loader.template"></ng-container>
</ng-container>
</ng-template>
<ng-template #noResultTemplate>
<tbody>
<tr>
<td [attr.colspan]="columns.length">
<p style="text-align: center;">{{ noRecordsDisplayText }}</p>
</td>
</tr>
</tbody>
</ng-template>
@@ -0,0 +1,12 @@
:host {
.flex-container{
display: flex;
justify-content: space-between;
align-items: center;
}
.flex-item{
margin-right: 1px;
height: 100%;
}
}
@@ -0,0 +1,139 @@
import {
ChangeDetectorRef,
Component,
ContentChildren,
EventEmitter, Inject,
Input,
OnDestroy,
OnInit,
Output,
QueryList
} from '@angular/core';
import {IDataSource, IQueryExecutionGroupResult, IQueryExecutionResult} from '@poweredsoft/data';
import {DataGridColDirective} from '../directives/data-grid-col.directive';
import {DataGridHeaderDirective} from '../directives/data-grid-header.directive';
import {DataGridFooterDirective} from '../directives/data-grid-footer.directive';
import {DataGridLoaderDirective} from '../directives/data-grid-loader.directive';
import {Subscription} from 'rxjs';
@Component({
selector: 'cdk-data-grid',
templateUrl: './data-grid.component.html',
styleUrls: ['./data-grid.component.scss']
})
export class DataGridComponent implements OnInit, OnDestroy {
@Inject(ChangeDetectorRef) private cdr!: ChangeDetectorRef;
@ContentChildren(DataGridColDirective) columnDefinitions: QueryList<DataGridColDirective>;
@ContentChildren(DataGridHeaderDirective) gridHeaders: QueryList<DataGridHeaderDirective>;
@ContentChildren(DataGridFooterDirective) gridFooters: QueryList<DataGridFooterDirective>;
@ContentChildren(DataGridLoaderDirective) loaders: QueryList<DataGridLoaderDirective>;
latestResult: IQueryExecutionResult<any> & IQueryExecutionGroupResult<any>;
loading: boolean = false;
@Input() dataSource: IDataSource<any>;
@Input() tableClasses: any;
@Input() headerClasses: any;
@Input() noRecordsText: string;
private _columns: string[];
private _dataSubscription: Subscription;
private _loadingSubscription: Subscription;
@Input() set columns(value: string[]) {
this._columns = value;
this.columnsChange.emit(value);
}
get columns() {
return this._columns;
}
@Output() columnsChange: EventEmitter<string []> = new EventEmitter<string []>();
get noData() {
return !this.latestResult || this.latestResult.totalRecords == 0;
}
get noRecordsDisplayText() {
return this.noRecordsText || 'No records';
}
ngOnDestroy(): void {
this._dataSubscription.unsubscribe();
this._loadingSubscription.unsubscribe();
}
ngOnInit(): void {
this._dataSubscription = this.dataSource.data$.subscribe(newData => {
this.latestResult = newData;
});
this._loadingSubscription = this.dataSource.loading$.subscribe(isLoading => {
this.loading = isLoading;
this.cdr.detectChanges();
});
}
getSortingTemplate(columnName: string){
const ret = this.getColumn(columnName);
if (ret && ret.sortTemplate)
return ret.sortTemplate.template;
return null;
}
getFilterTemplate(columnName: string){
const ret = this.getColumn(columnName);
if (ret && ret.filterTemplate)
return ret.filterTemplate.template;
return null;
}
getColumn(columnName: string) {
if (!this.columnDefinitions)
return null;
const ret = this.columnDefinitions.find(t =>
{
return t.columnName == columnName;
});
return ret;
}
getColumnHeaderTemplate(columnName: string) {
const ret = this.getColumn(columnName);
if (ret && ret.headerTemplate)
return ret.headerTemplate.template;
return null;
}
getColumnCellTemplate(columnName: string) {
const ret = this.getColumn(columnName);
if (ret && ret.cellTemplate)
return ret.cellTemplate.template;
return null;
}
hasHeaderTemplate(columnName: string) {
return this.getColumnHeaderTemplate(columnName) ? true : false;
}
hasCellTemplate(columnName: string) {
return this.getColumnCellTemplate(columnName) ? true : false;
}
hasFilterTemplate(columnName: string) {
return this.getFilterTemplate(columnName) ? true : false;
}
hasSortingTemplate(columnName: string) {
return this.getSortingTemplate(columnName) ? true : false;
}
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDataGridCellFilter]'
})
export class DataGridCellFilterDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDataGridCell]'
})
export class DataGridCellDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDataGridColHeader]'
})
export class DataGridColHeaderDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDataGridColSort]'
})
export class DataGridColSortDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,19 @@
import {ContentChild, Directive, Input} from '@angular/core';
import {DataGridColHeaderDirective} from './data-grid-col-header.directive';
import {DataGridCellDirective} from './data-grid-cell.directive';
import {DataGridCellFilterDirective} from './data-grid-cell-filter.directive';
import {DataGridColSortDirective} from './data-grid-col-sort.directive';
@Directive({
selector: '[cdkDataGridCol]'
})
export class DataGridColDirective {
@Input('psDataGridCol') columnName: string;
@ContentChild(DataGridColHeaderDirective) headerTemplate: DataGridColHeaderDirective;
@ContentChild(DataGridCellDirective) cellTemplate: DataGridCellDirective;
@ContentChild(DataGridCellFilterDirective) filterTemplate: DataGridCellFilterDirective;
@ContentChild(DataGridColSortDirective) sortTemplate: DataGridColSortDirective;
constructor() { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDataGridFooter]'
})
export class DataGridFooterDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDataGridHeader]'
})
export class DataGridHeaderDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDataGridLoader]'
})
export class DataGridLoaderDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDsCommandContent]'
})
export class DsCommandContentDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDsCommandError]'
})
export class DsCommandErrorDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDsCommandFooter]'
})
export class DsCommandFooterDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDsCommandNoCommand]'
})
export class DsCommandNoCommandDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, ElementRef} from '@angular/core';
@Directive({
selector: '[cdkDsCommandSubmit]'
})
export class DsCommandSubmitDirective {
constructor(public element: ElementRef) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDsCommandValidation]'
})
export class DsCommandValidationDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,29 @@
<ng-container *ngIf="command; else noCommand">
<ng-container *ngIf="hasContent" [ngTemplateOutlet]="contentTemplate"
[ngTemplateOutletContext]="{ $implicit: command, loading: isLoading }">
</ng-container>
<ng-container *ngIf="lastValidationResult">
<ng-container *ngIf="hasValidationTemplate" [ngTemplateOutlet]="validationTemplate"
[ngTemplateOutletContext]="{ $implicit: lastValidationResult }">
</ng-container>
</ng-container>
<ng-container *ngIf="lastErrorMessage && errorContent && errorContent.template">
<ng-container [ngTemplateOutlet]="errorContent.template"
[ngTemplateOutletContext]="{ $implicit: lastErrorMessage }">
</ng-container>
</ng-container>
<ng-container *ngIf="hasFooterTemplate" [ngTemplateOutlet]="footerTemplate"
[ngTemplateOutletContext]="{ $implicit: command, loading: isLoading }">
</ng-container>
</ng-container>
<ng-template #noCommand>
<ng-container *ngIf="noCommandContent && noCommandContent.template">
<ng-container [ngTemplateOutlet]="noCommandContent.template">
</ng-container>
</ng-container>
</ng-template>
@@ -0,0 +1,205 @@
import {
Component,
ContentChild,
ContentChildren,
EventEmitter,
HostListener,
Input,
OnDestroy,
OnInit,
Output,
QueryList
} from '@angular/core';
import {IDataSource} from '@poweredsoft/data';
import {Subscription} from 'rxjs';
import {finalize} from 'rxjs/operators';
import {DsCommandContentDirective} from './directives/ds-command-content.directive';
import {DsCommandErrorDirective} from './directives/ds-command-error.directive';
import {DsCommandFooterDirective} from './directives/ds-command-footer.directive';
import {DsCommandNoCommandDirective} from './directives/ds-command-no-command.directive';
import {DsCommandSubmitDirective} from './directives/ds-command-submit.directive';
import {DsCommandValidationDirective} from './directives/ds-command-validation.directive';
export interface DsCommandPropertyError
{
name: string,
errors: string[];
}
@Component({
selector: 'cdk-ds-command, [cdkDsCommand]',
templateUrl: './ds-command.component.html',
styleUrls: ['./ds-command.component.scss']
})
export class DsCommandComponent implements OnInit, OnDestroy {
private _refreshOnSuccess: boolean = true;
@Input() params: any;
@Input() dataSource: IDataSource<any>;
@Input() name: string;
@Input() model: any;
@Input() resolveCommand: boolean;
@Input() set refreshOnSuccess(val: boolean) {
this._refreshOnSuccess = val;
}
get refreshOnSuccess() {
return this._refreshOnSuccess;
}
@Output() success = new EventEmitter<any>();
@Output() loading = new EventEmitter<boolean>();
@Output() commandChange = new EventEmitter<any>();
@ContentChild(DsCommandContentDirective) commandContent: DsCommandContentDirective;
@ContentChild(DsCommandFooterDirective) footerContent: DsCommandFooterDirective;
@ContentChild(DsCommandNoCommandDirective) noCommandContent: DsCommandNoCommandDirective;
@ContentChild(DsCommandErrorDirective) errorContent: DsCommandErrorDirective;
@ContentChild(DsCommandValidationDirective) validationDirective: DsCommandValidationDirective;
@ContentChildren(DsCommandSubmitDirective) submitDirectives: QueryList<DsCommandSubmitDirective>;
lastErrorMessage: string | null;
lastValidationResult: DsCommandPropertyError[] | null;
protected _command: any = null;
private _loading = false;
private _validationErrorSubscription: Subscription;
private _notifyMessageSubscription: Subscription;
@Input() set command(value: any) {
if (this._command != value) {
this._command = value;
this.commandChange.emit(value);
}
}
get noCommand() {
return this._command ? true : false;
}
get command() {
return this._command;
}
get hasContent() {
return this.commandContent != null && this.commandContent.template != null;
}
get footerTemplate() {
return this.footerContent.template;
}
get hasFooterTemplate() {
return this.footerContent?.template != null;
}
get hasValidationTemplate() {
return this.validationDirective != null && this.validationDirective.template != null;
}
get validationTemplate() {
return this.validationDirective.template;
}
get contentTemplate() {
return this.commandContent.template;
}
clearValidationError() {
this.lastValidationResult = null;
}
clearLastErrorMessage() {
this.lastErrorMessage = null;
}
ngOnDestroy() {
this._validationErrorSubscription?.unsubscribe();
this._notifyMessageSubscription?.unsubscribe();
}
ngOnInit(): void {
this._validationErrorSubscription = this.dataSource
.validationError$.subscribe(validationResult => {
this.lastValidationResult = Object.keys(validationResult.errors).reduce<DsCommandPropertyError[]>((prev, attr) => {
prev.push({
name: attr,
errors: validationResult.errors[attr]
})
return prev;
}, []);
});
this._notifyMessageSubscription = this.dataSource.notifyMessage$.subscribe(message => {
if (message.type != 'info' && message.type != "success")
this.lastErrorMessage = message.message;
});
const shouldResolve = this.resolveCommand === undefined ? true : this.resolveCommand;
if (shouldResolve)
this.resolveModel();
}
resolveModel() {
this.dataSource.resolveCommandModelByName({
model: this.model,
command: this.name,
params: this.params
}).subscribe(
commandModel => {
this.command = commandModel;
},
_ => { }
);
}
private changeLoadingState(loading: boolean) {
if (loading != this._loading) {
this._loading = loading;
this.loading.emit(loading);
}
}
@HostListener("click", ["$event"])
childClicked($event: MouseEvent) {
const element = $event.target;
const found = this.submitDirectives
.toArray()
.find(t => t.element.nativeElement == element);
if (found != null)
this.execute()
}
execute() {
// secure from double send.
if (this._loading)
return;
this.changeLoadingState(true);
this.lastValidationResult = null;
this.lastErrorMessage = null;
this.dataSource.executeCommandByName(this.name, this._command)
.pipe(
finalize(() => this.changeLoadingState(false))
)
.subscribe(
commandResult => {
this.success.emit(commandResult);
if (this.refreshOnSuccess)
this.dataSource.refresh();
},
_ => {
// we just catch the error, this way its not uncatched its handled via the other
// subscriptions.
}
);
}
get isLoading() {
return this._loading;
}
}
@@ -0,0 +1,19 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {DsCommandComponent} from './ds-command.component';
import {DsCommandContentDirective} from './directives/ds-command-content.directive';
import {DsCommandSubmitDirective} from './directives/ds-command-submit.directive';
import {DsCommandValidationDirective} from './directives/ds-command-validation.directive';
import {DsCommandFooterDirective} from './directives/ds-command-footer.directive';
import {DsCommandErrorDirective} from './directives/ds-command-error.directive';
import {DsCommandNoCommandDirective} from './directives/ds-command-no-command.directive';
@NgModule({
declarations: [DsCommandComponent, DsCommandContentDirective, DsCommandSubmitDirective, DsCommandValidationDirective, DsCommandFooterDirective, DsCommandErrorDirective, DsCommandNoCommandDirective],
imports: [
CommonModule
],
exports: [DsCommandComponent, DsCommandContentDirective, DsCommandSubmitDirective, DsCommandValidationDirective, DsCommandFooterDirective, DsCommandErrorDirective, DsCommandNoCommandDirective]
})
export class DsCommandModule { }
@@ -0,0 +1,23 @@
<ul>
<li class="ds-pager-first" (click)="first()">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
</svg>
</li>
<li class="ds-pager-previous" (click)="previous()">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</li>
<li class="ds-pager-page" [class.ds-pager-current]="page == pr" *ngFor="let pr of pageRanges" (click)="page = pr">{{ pr }}</li>
<li class="ds-pager-next" (click)="next()">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</li>
<li class="ds-pager-last" (click)="last()">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 5l7 7-7 7M5 5l7 7-7 7" />
</svg>
</li>
</ul>
@@ -0,0 +1,19 @@
:host {
ul {
list-style: none;
display: flex;
}
li {
padding: 4px 2px;
}
li.ds-pager-current {
color: blue;
}
svg {
height: 15px;
width: 15px;
}
}
@@ -0,0 +1,89 @@
import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
import {IDataSource, IQueryExecutionGroupResult, IQueryExecutionResult} from '@poweredsoft/data';
import {Subscription} from 'rxjs';
@Component({
selector: 'cdk-ds-pager',
templateUrl: './ds-pager.component.html',
styleUrls: ['./ds-pager.component.scss']
})
export class DsPagerComponent implements OnInit, OnDestroy {
private _range: number = 5;
@Input() dataSource: IDataSource<any>;
pageRanges: number[] = [];
// range.
@Input() set range(value: number) {
this._range = value;
}
get range() {
return this._range;
}
// page.
@Input() set page(value: number) {
if (this.dataSource.page != value) {
this.dataSource.page = value;
this.pageChange.emit(value);
}
}
get page() {
return this.dataSource?.page;
}
@Output() pageChange = new EventEmitter<number>();
public latestResult: IQueryExecutionResult<any> & IQueryExecutionGroupResult<any> = null;
private dataSubscription: Subscription;
ngOnInit(): void {
this.dataSubscription = this.dataSource.data$.subscribe(result => {
this.latestResult = result;
this.refreshPageRange();
});
}
refreshPageRange() {
let previous = [];
let nexts = [];
for (let i = 0; i < this.range; i++) {
const currentPrevious = this.dataSource.page - 1 - i;
if (currentPrevious >= 1)
previous.push(currentPrevious);
const currentNext = this.dataSource.page + i + 1;
if (currentNext <= this.latestResult?.numberOfPages)
nexts.push(currentNext);
}
previous.reverse();
const final = previous.concat([this.dataSource.page]).concat(nexts);
this.pageRanges = final;
}
first() {
this.page = 1;
}
last() {
if (this.latestResult)
this.page = this.latestResult!.numberOfPages;
}
next() {
if (this.page + 1 <= this.latestResult?.totalRecords)
this.page++;
}
previous() {
if (this.page > 1)
this.page--;
}
ngOnDestroy() {
this.dataSubscription?.unsubscribe();
}
}
@@ -0,0 +1,12 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {DsPagerComponent} from './ds-pager.component';
@NgModule({
declarations: [DsPagerComponent],
imports: [
CommonModule
],
exports: [DsPagerComponent]
})
export class DsPagerModule { }
@@ -0,0 +1,7 @@
<form (submit)="applySearch()">
<div [ngClass]="classes">
<input type="search" (onSearch)="onSearch()" [placeholder]="finalSearchText" [ngClass]="searchClasses"
[(ngModel)]="filterValue" [ngModelOptions]="{standalone: true}">
<button type="submit" [ngClass]="submitButtonClasses">{{ finalSearchText }}</button>
</div>
</form>
@@ -0,0 +1,75 @@
import {Component, Input, OnInit} from '@angular/core';
import {FilterType, ICompositeFilter, IDataSource, ISimpleFilter} from '@poweredsoft/data';
@Component({
selector: 'cdk-ds-search',
templateUrl: './ds-search.component.html',
styleUrls: ['./ds-search.component.scss']
})
export class DsSearchComponent implements OnInit {
@Input() dataSource: IDataSource<any>;
@Input() filterType: string;
@Input() filterPaths: string[];
@Input() searchText: string;
@Input() submitButtonClasses: any;
@Input() searchClasses: any;
@Input() classes: any;
filterValue: string = null;
lastUsedFilter: any;
constructor() { }
get finalSearchText() {
return this.searchText ?? "Search";
}
get finalFilterType() {
return this.filterType ?? FilterType.CONTAINS;
}
onSearch() {
this.applySearch();
}
applySearch() {
const existingFilters = this.dataSource.filters;
// adapt current filters to remove the previous one if exist
// and replace with new one.
const finalNewFilters = existingFilters
.filter(t => t != this.lastUsedFilter);
if (this.filterValue) {
const newFilter: ICompositeFilter = {
and: true,
type: FilterType.COMPOSITE,
filters: this.filterPaths.map(filterPath => (<ISimpleFilter>{
path: filterPath,
type: this.finalFilterType,
value: this.filterValue,
and: false
}))
}
finalNewFilters.push(newFilter);
// update last used filter to replace it if changed.
this.lastUsedFilter = newFilter;
} else {
this.lastUsedFilter = null;
}
// execute search.
this.dataSource.query({
page: 1,
filters: finalNewFilters
})
}
ngOnInit(): void {
}
}
@@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DsSearchComponent } from './ds-search.component';
import { FormsModule } from '@angular/forms';
@NgModule({
declarations: [DsSearchComponent],
imports: [
CommonModule,
FormsModule
],
exports: [DsSearchComponent]
})
export class DsSearchModule { }
@@ -0,0 +1,4 @@
<ng-container *ngFor="let error of latestErrors; let isLast=last">
{{ error }}
<br *ngIf="!isLast" />
</ng-container>
@@ -0,0 +1,36 @@
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
import {IDataSource} from '@poweredsoft/data';
import {Subscription} from 'rxjs';
@Component({
selector: 'cdk-ds-validation-error',
templateUrl: './ds-validation-error.component.html',
styleUrls: ['./ds-validation-error.component.scss']
})
export class DsValidationErrorComponent implements OnInit, OnDestroy {
@Input() dataSource: IDataSource<any>;
@Input() field: string;
validationErrorsSub: Subscription;
commandStartedSub: Subscription;
latestErrors: string[] = [];
ngOnDestroy(): void {
this.validationErrorsSub?.unsubscribe();
this.commandStartedSub?.unsubscribe();
}
ngOnInit(): void {
this.commandStartedSub = this.dataSource.commandStarted$.subscribe(e => {
this.latestErrors = [];
});
this.validationErrorsSub = this.dataSource.validationError$.subscribe(validationErrors => {
this.latestErrors = Object.keys(validationErrors.errors)
.filter(t => t.toLowerCase() == this.field?.toLowerCase())
.reduce<string[]>((prev, current) => {
return prev.concat(validationErrors.errors[current]);
}, []);
});
}
}
@@ -0,0 +1,13 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {DsValidationErrorComponent} from './ds-validation-error.component';
@NgModule({
declarations: [DsValidationErrorComponent],
imports: [
CommonModule
],
exports: [DsValidationErrorComponent]
})
export class DsValidationErrorModule { }
@@ -0,0 +1,8 @@
import { Directive, TemplateRef } from '@angular/core';
@Directive({
selector: '[cdkListViewFooter]'
})
export class ListViewFooterDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkListViewHeader]'
})
export class ListViewHeaderDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkListViewItem]'
})
export class ListViewItemDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import { Directive, TemplateRef } from '@angular/core';
@Directive({
selector: '[cdkListViewSeparator]'
})
export class ListViewSeparatorDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,23 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ListViewComponent} from './list-view/list-view.component';
import {ListViewItemDirective} from './directives/list-view-item.directive';
import {ListViewHeaderDirective} from './directives/list-view-header.directive';
import {ListViewFooterDirective} from './directives/list-view-footer.directive';
import {ListViewSeparatorDirective} from './directives/list-view-separator.directive';
@NgModule({
declarations: [ListViewComponent, ListViewItemDirective, ListViewHeaderDirective, ListViewFooterDirective, ListViewSeparatorDirective],
imports: [
CommonModule
],
exports: [
ListViewComponent,
ListViewItemDirective,
ListViewHeaderDirective,
ListViewFooterDirective,
ListViewSeparatorDirective
]
})
export class ListViewModule { }
@@ -0,0 +1,21 @@
<ng-container [ngTemplateOutlet]="getViewHeaderTemplate()"></ng-container>
<ng-container *ngIf="!noData else noResultTemplate">
<div [ngClass]="listViewClasses">
<ng-container *ngFor="let item of latestResult.data; let index = index; let first = first; let last = last; let odd = odd">
<ng-container [ngTemplateOutlet]="getViewItemTemplate()" [ngTemplateOutletContext]="{
$implicit: item,
index: index,
first: first,
last: last,
odd: odd
}">
</ng-container>
</ng-container>
</div>
</ng-container>
<ng-container [ngTemplateOutlet]="getViewFooterTemplate()"></ng-container>
<ng-template #noResultTemplate>
<div [ngClass]="noRecordClasses">{{noRecords}}</div>
</ng-template>
@@ -0,0 +1,68 @@
import {Component, ContentChild, Input, OnDestroy, OnInit} from '@angular/core';
import {IDataSource, IQueryExecutionGroupResult, IQueryExecutionResult} from '@poweredsoft/data';
import {Subscription} from 'rxjs';
import {ListViewItemDirective} from '../directives/list-view-item.directive';
import {ListViewHeaderDirective} from '../directives/list-view-header.directive';
import {ListViewFooterDirective} from '../directives/list-view-footer.directive';
@Component({
selector: 'cdk-list-view',
templateUrl: './list-view.component.html',
styleUrls: ['./list-view.component.scss']
})
export class ListViewComponent implements OnInit, OnDestroy {
@Input() dataSource: IDataSource<any>;
@Input() noRecordsText: string;
@Input() noRecordClasses: any;
@Input() listViewClasses: any;
latestResult: IQueryExecutionResult<any> & IQueryExecutionGroupResult<any>;
loading:boolean;
private _dataSubscription: Subscription;
@ContentChild(ListViewItemDirective) viewItem: ListViewItemDirective;
@ContentChild(ListViewHeaderDirective) viewHeader: ListViewHeaderDirective;
@ContentChild(ListViewFooterDirective) viewFooter: ListViewFooterDirective;
//@ContentChildren(ViewDataDirective) viewData: QueryList<ViewDataDirective>;
ngOnDestroy(): void {
this._dataSubscription.unsubscribe();
}
ngOnInit(): void {
this._dataSubscription = this.dataSource.data$.subscribe(newData => {
this.latestResult = newData;
});
}
get noData(){
if (!this.latestResult)
return true;
return this.latestResult.data.length == 0;
}
get noRecords(){
return this.noRecordsText || "No records...";
}
getViewItemTemplate(){
if(this.viewItem == null)
return null;
return this.viewItem.template;
}
getViewHeaderTemplate(){
if(this.viewHeader == null)
return null;
return this.viewHeader.template;
}
getViewFooterTemplate(){
if(this.viewFooter == null)
return null;
return this.viewFooter.template;
}
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDsViewContent]'
})
export class ViewContentDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDsViewLoading]'
})
export class ViewLoadingDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,8 @@
import {Directive, TemplateRef} from '@angular/core';
@Directive({
selector: '[cdkDsViewNoRecords]'
})
export class ViewNoRecordsDirective {
constructor(public template: TemplateRef<any>) { }
}
@@ -0,0 +1,25 @@
<ng-container *ngIf="loading; else notLoading">
<ng-container *ngIf="viewLoading">
<ng-container [ngTemplateOutlet]="getViewLoadingTemplate()"></ng-container>
</ng-container>
<ng-container *ngIf="!viewLoading">
{{ finalLoadingText }}
</ng-container>
</ng-container>
<ng-template #notLoading>
<ng-container *ngIf="!noData else noResultTemplate">
<ng-container [ngTemplateOutlet]="getViewContentTemplate()"
[ngTemplateOutletContext]="{$implicit: viewModel}">
</ng-container>
</ng-container>
</ng-template>
<ng-template #noResultTemplate>
<ng-container *ngIf="viewNoRecords">
<ng-container [ngTemplateOutlet]="getViewNoRecordsTemplate()"></ng-container>
</ng-container>
<ng-container *ngIf="!viewNoRecords">
<div [ngClass]="noRecordClasses">{{noRecords}}</div>
</ng-container>
</ng-template>
@@ -0,0 +1,79 @@
import {Component, ContentChild, Input, OnDestroy, OnInit} from '@angular/core';
import {IDataSource} from '@poweredsoft/data';
import {Subscription} from 'rxjs';
import {ViewContentDirective} from './directives/view-content.directive';
import {ViewLoadingDirective} from './directives/view-loading.directive';
import {ViewNoRecordsDirective} from './directives/view-no-records.directive';
@Component({
selector: 'cdk-ds-view',
templateUrl: './view.component.html',
styleUrls: ['./view.component.scss']
})
export class ViewComponent implements OnInit, OnDestroy {
@Input() dataSource: IDataSource<any>;
@Input() noRecordsText: string;
@Input() loadingText: string;
@Input() noRecordClasses: any;
@ContentChild(ViewContentDirective) viewContent: ViewContentDirective;
@ContentChild(ViewLoadingDirective) viewLoading: ViewLoadingDirective;
@ContentChild(ViewNoRecordsDirective) viewNoRecords: ViewNoRecordsDirective;
loading: boolean = false;
private _viewModel: any = null;
private _dataSubscription: Subscription;
private _loadingSubscription: Subscription;
ngOnDestroy(): void {
this._dataSubscription.unsubscribe();
this._loadingSubscription.unsubscribe();
}
ngOnInit(): void {
this._dataSubscription = this.dataSource.data$.subscribe(newData => {
if (newData && newData.data && newData.data.length)
this._viewModel = newData.data[0];
});
this._loadingSubscription = this.dataSource.loading$.subscribe(loading => {
this.loading = loading;
});
}
get noData(){
return this._viewModel ? false : true;
}
get viewModel() {
return this._viewModel;
}
get noRecords(){
return this.noRecordsText || "No records...";
}
get finalLoadingText() {
return this.loadingText || 'Loading ...';
}
getViewContentTemplate(){
if(this.viewContent == null)
return null;
return this.viewContent.template;
}
getViewNoRecordsTemplate(){
if(this.viewNoRecords == null)
return null;
return this.viewNoRecords.template;
}
getViewLoadingTemplate(){
if(this.viewLoading == null)
return null;
return this.viewLoading.template;
}
}
@@ -0,0 +1,16 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ViewComponent} from './view.component';
import {ViewContentDirective} from './directives/view-content.directive';
import {ViewLoadingDirective} from './directives/view-loading.directive';
import {ViewNoRecordsDirective} from './directives/view-no-records.directive';
@NgModule({
declarations: [ViewComponent, ViewContentDirective, ViewLoadingDirective, ViewNoRecordsDirective],
imports: [
CommonModule
],
exports: [ViewComponent, ViewContentDirective, ViewLoadingDirective, ViewNoRecordsDirective]
})
export class ViewModule { }
@@ -0,0 +1,52 @@
/*
* Public API Surface of ngx-cdk-ui
*/
// data grid.
export * from './lib/data-grid/data-grid.module';
export * from './lib/data-grid/data-grid/data-grid.component';
export * from './lib/data-grid/directives/data-grid-cell.directive';
export * from './lib/data-grid/directives/data-grid-col.directive';
export * from './lib/data-grid/directives/data-grid-col-header.directive';
export * from './lib/data-grid/directives/data-grid-header.directive';
export * from './lib/data-grid/directives/data-grid-footer.directive';
export * from './lib/data-grid/directives/data-grid-loader.directive';
export * from './lib/data-grid/directives/data-grid-cell-filter.directive';
export * from './lib/data-grid/directives/data-grid-col-sort.directive';
//list view
export * from './lib/list-view/list-view.module';
export * from './lib/list-view/list-view/list-view.component';
export * from './lib/list-view/directives/list-view-item.directive';
export * from './lib/list-view/directives/list-view-header.directive';
export * from './lib/list-view/directives/list-view-footer.directive';
export * from './lib/list-view/directives/list-view-separator.directive';
// search
export * from './lib/ds-search/ds-search.module';
export * from './lib/ds-search/ds-search.component';
// ds validation
export * from './lib/ds-validation-error/ds-validation-error.module';
export * from './lib/ds-validation-error/ds-validation-error.component';
// view
export * from './lib/view/view.module';
export * from './lib/view/view.component';
export * from './lib/view/directives/view-content.directive';
export * from './lib/view/directives/view-loading.directive';
export * from './lib/view/directives/view-no-records.directive';
// ds-command
export * from './lib/ds-command/ds-command.module';
export * from './lib/ds-command/ds-command.component';
export * from './lib/ds-command/directives/ds-command-content.directive';
export * from './lib/ds-command/directives/ds-command-submit.directive';
export * from './lib/ds-command/directives/ds-command-validation.directive';
export * from './lib/ds-command/directives/ds-command-footer.directive';
export * from './lib/ds-command/directives/ds-command-no-command.directive';
export * from './lib/ds-command/directives/ds-command-error.directive';
// ds-pager
export * from './lib/ds-pager/ds-pager.module';
export * from './lib/ds-pager/ds-pager.component';
@@ -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);