@ferhaps/easy-ui-lib 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/ng-package.json +7 -0
- package/package.json +22 -0
- package/src/lib/components/chip/chip.component.html +3 -0
- package/src/lib/components/chip/chip.component.scss +19 -0
- package/src/lib/components/chip/chip.component.ts +23 -0
- package/src/lib/components/default-dialog/default-dialog.component.html +19 -0
- package/src/lib/components/default-dialog/default-dialog.component.scss +41 -0
- package/src/lib/components/default-dialog/default-dialog.component.ts +23 -0
- package/src/lib/components/error-dispaly.component.ts +48 -0
- package/src/lib/components/error-handler/error-handler.component.html +0 -0
- package/src/lib/components/error-handler/error-handler.component.scss +0 -0
- package/src/lib/components/error-handler/error-handler.component.ts +44 -0
- package/src/lib/components/error-handler/error-popup/error-popup.component.html +13 -0
- package/src/lib/components/error-handler/error-popup/error-popup.component.scss +19 -0
- package/src/lib/components/error-handler/error-popup/error-popup.component.ts +21 -0
- package/src/lib/components/global-loader/global-loader.component.html +5 -0
- package/src/lib/components/global-loader/global-loader.component.scss +12 -0
- package/src/lib/components/global-loader/global-loader.component.ts +17 -0
- package/src/lib/components/search-bar.component.ts +70 -0
- package/src/lib/components/table/table.component.html +121 -0
- package/src/lib/components/table/table.component.scss +116 -0
- package/src/lib/components/table/table.component.ts +105 -0
- package/src/lib/components/table-sort-header/table-sort-header.component.html +7 -0
- package/src/lib/components/table-sort-header/table-sort-header.component.scss +17 -0
- package/src/lib/components/table-sort-header/table-sort-header.component.ts +31 -0
- package/src/lib/directives/fields-match-validator.directive.ts +35 -0
- package/src/lib/directives/password-validator.directive.ts +26 -0
- package/src/lib/directives/phone-validation.directive.ts +24 -0
- package/src/lib/pipes/blank-filler.pipe.ts +13 -0
- package/src/lib/pipes/snake-case-parser.pipe.ts +17 -0
- package/src/lib/services/error.service.ts +15 -0
- package/src/lib/services/loader.service.ts +14 -0
- package/src/lib/utils/animations.ts +29 -0
- package/src/lib/utils/types.ts +3 -0
- package/src/lib/utils/utils.ts +65 -0
- package/src/public-api.ts +20 -0
- package/tsconfig.lib.json +14 -0
- package/tsconfig.lib.prod.json +10 -0
- package/tsconfig.spec.json +14 -0
package/README.md
ADDED
package/ng-package.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ferhaps/easy-ui-lib",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Angular UI components, directives and pipes library with Angular Material",
|
|
5
|
+
"keywords": ["angular", "ui", "components", "pipes", "directives", "library"],
|
|
6
|
+
"author": "Ferhan Cherkez",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/Ferhaps/angular-ui-library"
|
|
11
|
+
},
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"@angular/common": "^19.0.6",
|
|
14
|
+
"@angular/core": "^19.0.6",
|
|
15
|
+
"@angular/material": "^19.0.5",
|
|
16
|
+
"@angular/cdk": "^19.0.5"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"tslib": "^2.3.0"
|
|
20
|
+
},
|
|
21
|
+
"sideEffects": false
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.chip {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
padding: 0.5rem 1rem;
|
|
4
|
+
border-radius: 1rem;
|
|
5
|
+
background-color: #e6e6e6;
|
|
6
|
+
color: #333;
|
|
7
|
+
font-size: 1rem;
|
|
8
|
+
cursor: pointer;
|
|
9
|
+
transition: all 0.2s;
|
|
10
|
+
|
|
11
|
+
&:hover {
|
|
12
|
+
background-color: #cccccc;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&.selected {
|
|
16
|
+
background-color: var(--primary-color);
|
|
17
|
+
color: #fff;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Component, input, output } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Component({
|
|
4
|
+
selector: 'lib-chip',
|
|
5
|
+
imports: [],
|
|
6
|
+
templateUrl: './chip.component.html',
|
|
7
|
+
styleUrl: './chip.component.scss'
|
|
8
|
+
})
|
|
9
|
+
export class ChipComponent {
|
|
10
|
+
public value = input.required<any>();
|
|
11
|
+
public isSelected = input.required<boolean>();
|
|
12
|
+
public text = input.required<string>();
|
|
13
|
+
|
|
14
|
+
protected selected = output<any>();
|
|
15
|
+
|
|
16
|
+
protected onClick() {
|
|
17
|
+
if (this.isSelected()) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
this.selected.emit(this.value());
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<div class="modal" [style]="{'height': height()}">
|
|
2
|
+
<div class="dialog-title">
|
|
3
|
+
@if (withBack()) {
|
|
4
|
+
<div class="back-arrow" (click)="back.emit()">
|
|
5
|
+
<mat-icon>keyboard_arrow_left</mat-icon>
|
|
6
|
+
</div>
|
|
7
|
+
}
|
|
8
|
+
<h4 class="title">{{ dialogTitle() }}</h4>
|
|
9
|
+
<div class="closer" mat-dialog-close>
|
|
10
|
+
<mat-icon>close</mat-icon>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div class="dialog-content">
|
|
15
|
+
<ng-content select=".dialog-content"></ng-content>
|
|
16
|
+
<ng-template #tempBody></ng-template>
|
|
17
|
+
<ng-container *ngTemplateOutlet="temRef ? temRef: tempBody" />
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
.modal {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.dialog-title {
|
|
7
|
+
padding-inline: 2rem;
|
|
8
|
+
padding-top: 1rem;
|
|
9
|
+
margin-bottom: 1rem;
|
|
10
|
+
position: relative;
|
|
11
|
+
display: flex;
|
|
12
|
+
font-size: 20px;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
|
|
15
|
+
.back-arrow {
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
margin-right: 0.5rem;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.title {
|
|
21
|
+
font-weight: 700;
|
|
22
|
+
font-size: 24px;
|
|
23
|
+
margin: 0 !important;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.closer {
|
|
27
|
+
right: 3%;
|
|
28
|
+
position: absolute;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.dialog-content {
|
|
34
|
+
flex: 1;
|
|
35
|
+
min-height: 0;
|
|
36
|
+
overflow: hidden;
|
|
37
|
+
padding-bottom: 1rem;
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
align-items: center;
|
|
41
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Component, Input, input, output, TemplateRef } from '@angular/core';
|
|
2
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
3
|
+
import { MatDialogModule } from '@angular/material/dialog';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'lib-default-dialog',
|
|
8
|
+
imports: [
|
|
9
|
+
CommonModule,
|
|
10
|
+
MatIconModule,
|
|
11
|
+
MatDialogModule
|
|
12
|
+
],
|
|
13
|
+
templateUrl: './default-dialog.component.html',
|
|
14
|
+
styleUrls: ['./default-dialog.component.scss']
|
|
15
|
+
})
|
|
16
|
+
export class DefaultDialogComponent {
|
|
17
|
+
@Input() temRef!: TemplateRef<any>;
|
|
18
|
+
public height = input<string>();
|
|
19
|
+
public dialogTitle = input<string>();
|
|
20
|
+
public withBack = input<boolean>();
|
|
21
|
+
|
|
22
|
+
protected back = output<void>();
|
|
23
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Component, input, OnInit } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
import { HttpErrorResponse } from '@angular/common/http';
|
|
4
|
+
import { SnakeCaseParserPipe } from '../pipes/snake-case-parser.pipe';
|
|
5
|
+
import { SystemError } from '../utils/types';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'lib-error-dispaly',
|
|
9
|
+
imports: [
|
|
10
|
+
SnakeCaseParserPipe
|
|
11
|
+
],
|
|
12
|
+
template: `<strong class="err-container">{{ displayError | snakeCaseParser }}</strong>`,
|
|
13
|
+
styles: [`
|
|
14
|
+
.err-container {
|
|
15
|
+
display: block;
|
|
16
|
+
max-width: 300px;
|
|
17
|
+
font-size: 20px;
|
|
18
|
+
text-align: center;
|
|
19
|
+
border: 1px solid red;
|
|
20
|
+
border-radius: 5px;
|
|
21
|
+
padding: 0.5rem 1.5rem;
|
|
22
|
+
background-color: #ffe6e6;
|
|
23
|
+
color: #ff0000;
|
|
24
|
+
overflow-wrap: break-word;
|
|
25
|
+
}
|
|
26
|
+
`]
|
|
27
|
+
})
|
|
28
|
+
export class ErrorDispalyComponent implements OnInit {
|
|
29
|
+
public error = input.required<SystemError>();
|
|
30
|
+
|
|
31
|
+
protected displayError: string = '';
|
|
32
|
+
|
|
33
|
+
public ngOnInit(): void {
|
|
34
|
+
if (this.error() instanceof HttpErrorResponse) {
|
|
35
|
+
if (typeof (this.error() as HttpErrorResponse).error === 'string') {
|
|
36
|
+
this.displayError = (this.error() as HttpErrorResponse).error;
|
|
37
|
+
} else if (this.error && (this.error() as HttpErrorResponse)?.error?.message) {
|
|
38
|
+
this.displayError = (this.error() as HttpErrorResponse).error.message;
|
|
39
|
+
} else {
|
|
40
|
+
this.displayError = 'Unknown error';
|
|
41
|
+
}
|
|
42
|
+
} else if (typeof this.error() === 'string') {
|
|
43
|
+
this.displayError = (this.error() as string);
|
|
44
|
+
} else {
|
|
45
|
+
this.displayError = 'Unknown error';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { OnDestroy, inject } from '@angular/core';
|
|
2
|
+
import { Subscription } from 'rxjs';
|
|
3
|
+
import { Component } from '@angular/core';
|
|
4
|
+
import { HttpErrorResponse } from '@angular/common/http';
|
|
5
|
+
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
|
6
|
+
import { ErrorPopupComponent } from './error-popup/error-popup.component';
|
|
7
|
+
import { NoopScrollStrategy } from '@angular/cdk/overlay';
|
|
8
|
+
import { ErrorService } from '../../services/error.service';
|
|
9
|
+
|
|
10
|
+
@Component({
|
|
11
|
+
selector: 'lib-error-handler',
|
|
12
|
+
templateUrl: 'error-handler.component.html',
|
|
13
|
+
styleUrls: ['error-handler.component.scss'],
|
|
14
|
+
imports: [
|
|
15
|
+
MatDialogModule
|
|
16
|
+
]
|
|
17
|
+
})
|
|
18
|
+
export class ErrorHandlerComponent implements OnDestroy {
|
|
19
|
+
private errSubscriptions = new Subscription();
|
|
20
|
+
private errorService = inject(ErrorService);
|
|
21
|
+
private dialog = inject(MatDialog);
|
|
22
|
+
|
|
23
|
+
constructor() {
|
|
24
|
+
this.errSubscriptions.add(
|
|
25
|
+
this.errorService.error$.subscribe((err: HttpErrorResponse) => {
|
|
26
|
+
console.log(err);
|
|
27
|
+
this.showPopup(err);
|
|
28
|
+
}),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private showPopup(error: HttpErrorResponse): void {
|
|
33
|
+
this.dialog.closeAll();
|
|
34
|
+
this.dialog.open(ErrorPopupComponent, {
|
|
35
|
+
data: error,
|
|
36
|
+
width: '400px',
|
|
37
|
+
scrollStrategy: new NoopScrollStrategy(),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public ngOnDestroy(): void {
|
|
42
|
+
this.errSubscriptions.unsubscribe();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<lib-default-dialog [title]="'Аn error has occurred'">
|
|
2
|
+
<div class="dialog-content">
|
|
3
|
+
<div class="status-info">
|
|
4
|
+
<div>{{ error.status }}</div>
|
|
5
|
+
@if (error.status) {
|
|
6
|
+
<div>{{ httpStatusCodes[error.status] }}</div>
|
|
7
|
+
}
|
|
8
|
+
</div>
|
|
9
|
+
<div class="error-info">
|
|
10
|
+
<lib-error-dispaly [error]="error" />
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</lib-default-dialog>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.dialog-content {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
align-items: center;
|
|
5
|
+
padding-inline: 1rem;
|
|
6
|
+
|
|
7
|
+
.status-info {
|
|
8
|
+
display: flex;
|
|
9
|
+
margin-bottom: 0.5rem;
|
|
10
|
+
font-weight: 600;
|
|
11
|
+
font-size: 18px;
|
|
12
|
+
gap: 0.5rem;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.error-info {
|
|
16
|
+
display: grid;
|
|
17
|
+
place-items: center;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Component, Inject } from '@angular/core';
|
|
2
|
+
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
|
3
|
+
import { HttpErrorResponse } from '@angular/common/http';
|
|
4
|
+
import { DefaultDialogComponent } from '../../default-dialog/default-dialog.component';
|
|
5
|
+
import { ErrorDispalyComponent } from '../../error-dispaly.component';
|
|
6
|
+
import { HTTP_STATUS_CODES } from '../../../utils/utils';
|
|
7
|
+
|
|
8
|
+
@Component({
|
|
9
|
+
selector: 'lib-error-popup',
|
|
10
|
+
imports: [
|
|
11
|
+
DefaultDialogComponent,
|
|
12
|
+
ErrorDispalyComponent
|
|
13
|
+
],
|
|
14
|
+
templateUrl: './error-popup.component.html',
|
|
15
|
+
styleUrls: ['./error-popup.component.scss']
|
|
16
|
+
})
|
|
17
|
+
export class ErrorPopupComponent {
|
|
18
|
+
protected httpStatusCodes = HTTP_STATUS_CODES;
|
|
19
|
+
|
|
20
|
+
constructor(@Inject(MAT_DIALOG_DATA) public error: HttpErrorResponse) { }
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Component, inject } from '@angular/core';
|
|
2
|
+
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
3
|
+
import { LoaderService } from '../../services/loader.service';
|
|
4
|
+
import { AsyncPipe } from '@angular/common';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'lib-global-loader',
|
|
8
|
+
imports: [
|
|
9
|
+
AsyncPipe,
|
|
10
|
+
MatProgressSpinnerModule
|
|
11
|
+
],
|
|
12
|
+
templateUrl: './global-loader.component.html',
|
|
13
|
+
styleUrl: './global-loader.component.scss'
|
|
14
|
+
})
|
|
15
|
+
export class GlobalLoaderComponent {
|
|
16
|
+
protected loaderService = inject(LoaderService);
|
|
17
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Component, output, input } from '@angular/core';
|
|
2
|
+
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
3
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
4
|
+
import { debounceTime, distinctUntilChanged } from 'rxjs';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'lib-search-bar',
|
|
8
|
+
template: `
|
|
9
|
+
<form class="search-bar" [formGroup]="searchForm">
|
|
10
|
+
<mat-icon>search</mat-icon>
|
|
11
|
+
<input class="search-input" type="search" name="field"
|
|
12
|
+
[placeholder]="'Search ' + for()" autocomplete="off" formControlName="search" />
|
|
13
|
+
</form>
|
|
14
|
+
`,
|
|
15
|
+
styles: [`
|
|
16
|
+
.search-bar {
|
|
17
|
+
width: 270px;
|
|
18
|
+
border: 1px solid #A4A4A4;
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.search-input {
|
|
24
|
+
border: none;
|
|
25
|
+
padding: 7px 11px;
|
|
26
|
+
height: 100%;
|
|
27
|
+
width: 100%;
|
|
28
|
+
background-color: transparent;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
mat-icon {
|
|
32
|
+
margin-inline: 8px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.search-input:focus {
|
|
36
|
+
border: none;
|
|
37
|
+
outline: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@media (max-width: 1086px) {
|
|
41
|
+
.search-bar {
|
|
42
|
+
width: 170px;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
`],
|
|
46
|
+
imports: [
|
|
47
|
+
MatIconModule,
|
|
48
|
+
ReactiveFormsModule
|
|
49
|
+
]
|
|
50
|
+
})
|
|
51
|
+
export class SearchBarComponent {
|
|
52
|
+
public for = input.required<string>();
|
|
53
|
+
protected search = output<string>();
|
|
54
|
+
|
|
55
|
+
protected searchForm: FormGroup = new FormGroup({
|
|
56
|
+
search: new FormControl('')
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
constructor() {
|
|
60
|
+
this.searchForm.get('search')?.valueChanges.
|
|
61
|
+
pipe(
|
|
62
|
+
debounceTime(1000),
|
|
63
|
+
distinctUntilChanged(),
|
|
64
|
+
).subscribe((searchTerm: string) => {
|
|
65
|
+
if (searchTerm.trim() !== '') {
|
|
66
|
+
this.search.emit(searchTerm);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
<div class="flexer">
|
|
2
|
+
@if (config().title) {
|
|
3
|
+
<div class="row-heading-labels mb05">{{config().title}}</div>
|
|
4
|
+
}
|
|
5
|
+
<ng-content select=".upper-part"></ng-content>
|
|
6
|
+
@if (config().withAdd) {
|
|
7
|
+
<div class="flexer action pointer gap05 mb05" (click)="action.emit({action: 'add'})">
|
|
8
|
+
<mat-icon>add_circle_outline</mat-icon>
|
|
9
|
+
<div>add</div>
|
|
10
|
+
</div>
|
|
11
|
+
}
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div class="scroll" #scrollContainer (scroll)="onScroll()">
|
|
15
|
+
<table [class.with-options]="config().options?.length">
|
|
16
|
+
<thead>
|
|
17
|
+
<tr>
|
|
18
|
+
@for (heading of config().tableHeadings; track heading; let i = $index) {
|
|
19
|
+
<th>
|
|
20
|
+
<div class="flexer gap05">
|
|
21
|
+
{{heading}}
|
|
22
|
+
@if (config().sortable && !config().draggable) {
|
|
23
|
+
<lib-table-sort-header
|
|
24
|
+
[selected]="currentSortColumn === i"
|
|
25
|
+
(click)="currentSortColumn = i"
|
|
26
|
+
(sort)="sortByProp(config().dataProps[i], $event)"
|
|
27
|
+
/>
|
|
28
|
+
}
|
|
29
|
+
</div>
|
|
30
|
+
</th>
|
|
31
|
+
}
|
|
32
|
+
@if (config().options?.length) {
|
|
33
|
+
<th></th>
|
|
34
|
+
}
|
|
35
|
+
</tr>
|
|
36
|
+
</thead>
|
|
37
|
+
@if (config().draggable) {
|
|
38
|
+
<tbody cdkDropList (cdkDropListDropped)="drop($event)">
|
|
39
|
+
@for (obj of config().data; track trackById(i, obj); let i = $index) {
|
|
40
|
+
<tr
|
|
41
|
+
(mouseover)="hoverRowIndex = i"
|
|
42
|
+
(mouseleave)="hoverRowIndex = -1"
|
|
43
|
+
cdkDrag cdkDragLockAxis="y"
|
|
44
|
+
[class.pointer]="config().selectableRows"
|
|
45
|
+
[class.hover-row]="hoverRowIndex == i"
|
|
46
|
+
[class.selected-row]="config().selectableRows && selectedRowIndex == i"
|
|
47
|
+
(click)="onRowClick($event, obj, i)"
|
|
48
|
+
@fadeInOut>
|
|
49
|
+
@for (prop of config().dataProps; track prop; let cellIndex = $index) {
|
|
50
|
+
<td
|
|
51
|
+
[class.dragCol]="cellIndex === 0">
|
|
52
|
+
<div class="data" [classList]="getClass(obj, prop)"
|
|
53
|
+
[class.flexer]="cellIndex === 0">
|
|
54
|
+
@if (cellIndex === 0) {
|
|
55
|
+
<mat-icon class="draggable" cdkDragHandle>
|
|
56
|
+
drag_indicator
|
|
57
|
+
</mat-icon>
|
|
58
|
+
}
|
|
59
|
+
{{obj[prop] | blankFiller}}
|
|
60
|
+
</div>
|
|
61
|
+
</td>
|
|
62
|
+
}
|
|
63
|
+
@if (config().options?.length) {
|
|
64
|
+
<td class="right-align" (click)="$event.stopPropagation()">
|
|
65
|
+
<button mat-icon-button class="pointer dots right" [matMenuTriggerFor]="optionsMenu">
|
|
66
|
+
<mat-icon>more_vert</mat-icon>
|
|
67
|
+
</button>
|
|
68
|
+
<mat-menu #optionsMenu="matMenu">
|
|
69
|
+
@for (option of config().options; track option) {
|
|
70
|
+
<div
|
|
71
|
+
mat-menu-item (click)="selectOption(option, obj, i)"
|
|
72
|
+
[class.red]="option == 'Remove' || option == 'Delete'">
|
|
73
|
+
{{option}}
|
|
74
|
+
</div>
|
|
75
|
+
}
|
|
76
|
+
</mat-menu>
|
|
77
|
+
</td>
|
|
78
|
+
}
|
|
79
|
+
</tr>
|
|
80
|
+
}
|
|
81
|
+
</tbody>
|
|
82
|
+
} @else {
|
|
83
|
+
<tbody>
|
|
84
|
+
@for (obj of config().data; track trackById(i, obj); let i = $index) {
|
|
85
|
+
<tr
|
|
86
|
+
(mouseover)="hoverRowIndex = i"
|
|
87
|
+
(mouseleave)="hoverRowIndex = -1"
|
|
88
|
+
[class.pointer]="config().selectableRows"
|
|
89
|
+
[class.hover-row]="hoverRowIndex == i"
|
|
90
|
+
[class.selected-row]="config().selectableRows && selectedRowIndex == i"
|
|
91
|
+
(click)="onRowClick($event, obj, i)"
|
|
92
|
+
@fadeInOut>
|
|
93
|
+
@for (prop of config().dataProps; track prop; let cellIndex = $index) {
|
|
94
|
+
<td>
|
|
95
|
+
<div class="data" [classList]="getClass(obj, prop)">
|
|
96
|
+
{{obj[prop] | blankFiller}}
|
|
97
|
+
</div>
|
|
98
|
+
</td>
|
|
99
|
+
}
|
|
100
|
+
@if (config().options?.length) {
|
|
101
|
+
<td class="right-align" (click)="$event.stopPropagation()">
|
|
102
|
+
<button mat-icon-button class="pointer dots right" [matMenuTriggerFor]="optionsMenu">
|
|
103
|
+
<mat-icon>more_vert</mat-icon>
|
|
104
|
+
</button>
|
|
105
|
+
<mat-menu #optionsMenu="matMenu">
|
|
106
|
+
@for (option of config().options; track option) {
|
|
107
|
+
<div
|
|
108
|
+
mat-menu-item (click)="selectOption(option, obj, i)"
|
|
109
|
+
[class.red]="option == 'Remove' || option == 'Delete'">
|
|
110
|
+
{{option}}
|
|
111
|
+
</div>
|
|
112
|
+
}
|
|
113
|
+
</mat-menu>
|
|
114
|
+
</td>
|
|
115
|
+
}
|
|
116
|
+
</tr>
|
|
117
|
+
}
|
|
118
|
+
</tbody>
|
|
119
|
+
}
|
|
120
|
+
</table>
|
|
121
|
+
</div>
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
.scroll {
|
|
2
|
+
overflow-y: auto;
|
|
3
|
+
max-height: 94%;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
table {
|
|
7
|
+
width: 100%;
|
|
8
|
+
border-collapse: collapse;
|
|
9
|
+
border-spacing: 0px;
|
|
10
|
+
|
|
11
|
+
&.with-options {
|
|
12
|
+
td {
|
|
13
|
+
padding-block: 3px !important;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.flexer {
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.action {
|
|
24
|
+
color: #009ddc;
|
|
25
|
+
font-weight: 500;
|
|
26
|
+
text-align: right;
|
|
27
|
+
margin-left: auto;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.pointer {
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.gap05 {
|
|
35
|
+
gap: 0.5rem;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.mb05 {
|
|
39
|
+
margin-bottom: 0.5;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.data {
|
|
43
|
+
white-space: pre-wrap;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.row-heading-labels {
|
|
47
|
+
font-weight: 700;
|
|
48
|
+
font-size: 20px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.selected-row td {
|
|
52
|
+
background-color: #d3edf8;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.hover-row {
|
|
56
|
+
background-color: #ebf7fc;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.red {
|
|
60
|
+
color: #ff0000;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@media (max-width: 1086px) {
|
|
64
|
+
table td, table th {
|
|
65
|
+
padding: 3px !important;
|
|
66
|
+
width: auto !important;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
td, th {
|
|
71
|
+
padding: 0.5rem 1rem;
|
|
72
|
+
font-size: 14px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
th {
|
|
76
|
+
background-color: #f6f6f6;
|
|
77
|
+
text-align: left;
|
|
78
|
+
position: sticky;
|
|
79
|
+
top: 0;
|
|
80
|
+
z-index: 2;
|
|
81
|
+
border-top: 2px solid #E5E4E7;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
td {
|
|
85
|
+
border-bottom: 2px solid #E5E4E7;
|
|
86
|
+
border-top: 2px solid #E5E4E7;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.dots {
|
|
90
|
+
width: fit-content;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.draggable {
|
|
94
|
+
cursor: grabbing;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.dragCol {
|
|
98
|
+
padding-left: 0 !important;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.cdk-drag-preview {
|
|
102
|
+
box-sizing: border-box;
|
|
103
|
+
border-radius: 4px;
|
|
104
|
+
font-size: 1.5em;
|
|
105
|
+
text-align: center;
|
|
106
|
+
opacity: 0.8;
|
|
107
|
+
box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2),
|
|
108
|
+
0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.dragdrop-placeholder {
|
|
112
|
+
background: #ccc;
|
|
113
|
+
border: dotted 3px #999;
|
|
114
|
+
min-height: 30px;
|
|
115
|
+
transition: transform 100ms cubic-bezier(0, 0, 0.2, 1);
|
|
116
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Component, ElementRef, input, output, viewChild } from '@angular/core';
|
|
2
|
+
import { MatMenuModule } from '@angular/material/menu';
|
|
3
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
4
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
5
|
+
import { SortState, TableSortHeaderComponent } from '../table-sort-header/table-sort-header.component';
|
|
6
|
+
import { WhiteSpaceFillerPipe } from '../../pipes/blank-filler.pipe';
|
|
7
|
+
import { CdkDragDrop, DragDropModule, moveItemInArray } from "@angular/cdk/drag-drop";
|
|
8
|
+
import { fader } from '../../utils/animations';
|
|
9
|
+
|
|
10
|
+
export type TableEvent = {
|
|
11
|
+
action: string;
|
|
12
|
+
obj?: any;
|
|
13
|
+
prop?: string;
|
|
14
|
+
index?: number;
|
|
15
|
+
selected?: boolean;
|
|
16
|
+
sortState?: SortState;
|
|
17
|
+
event?: Event;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type Config = {
|
|
21
|
+
data: any[];
|
|
22
|
+
title: string;
|
|
23
|
+
dataProps: string[];
|
|
24
|
+
tableHeadings: string[];
|
|
25
|
+
options?: string[];
|
|
26
|
+
withAdd?: boolean;
|
|
27
|
+
selectableRows?: boolean;
|
|
28
|
+
sortable?: boolean;
|
|
29
|
+
draggable?: boolean;
|
|
30
|
+
classRules?: ClassRule[];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type ClassRule = {
|
|
34
|
+
className: string;
|
|
35
|
+
condition: (obj: any, prop: string) => boolean;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
@Component({
|
|
39
|
+
selector: 'lib-table',
|
|
40
|
+
templateUrl: 'table.component.html',
|
|
41
|
+
styleUrls: ['table.component.scss'],
|
|
42
|
+
animations: [fader],
|
|
43
|
+
imports: [
|
|
44
|
+
WhiteSpaceFillerPipe,
|
|
45
|
+
MatMenuModule,
|
|
46
|
+
MatIconModule,
|
|
47
|
+
MatButtonModule,
|
|
48
|
+
TableSortHeaderComponent,
|
|
49
|
+
DragDropModule
|
|
50
|
+
]
|
|
51
|
+
})
|
|
52
|
+
export class TableComponent {
|
|
53
|
+
public config = input.required<Config>();
|
|
54
|
+
protected action = output<TableEvent>();
|
|
55
|
+
protected scrollContainer = viewChild.required<ElementRef<HTMLDivElement>>('scrollContainer');
|
|
56
|
+
|
|
57
|
+
protected selectedRowIndex: number = -1;
|
|
58
|
+
protected hoverRowIndex: number = -1;
|
|
59
|
+
protected currentSortColumn: number = -1;
|
|
60
|
+
|
|
61
|
+
protected getClass(obj: any, prop: string): string {
|
|
62
|
+
if (!this.config().classRules) return '';
|
|
63
|
+
|
|
64
|
+
const classes: string[] = [];
|
|
65
|
+
for (let rule of (this.config().classRules as ClassRule[])) {
|
|
66
|
+
if (rule.condition(obj, prop)) {
|
|
67
|
+
classes.push(rule.className);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return classes.join(' ');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
protected drop(event: CdkDragDrop<any>) {
|
|
75
|
+
if (this.config().draggable) {
|
|
76
|
+
moveItemInArray(this.config().data, event.previousIndex, event.currentIndex);
|
|
77
|
+
this.action.emit({ action: 'drag', obj: this.config().data[event.currentIndex], index: event.currentIndex });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
protected onScroll() {
|
|
82
|
+
const container = this.scrollContainer().nativeElement;
|
|
83
|
+
// console.log(Math.ceil(container.scrollTop), container.offsetHeight, container.scrollHeight)
|
|
84
|
+
if ((Math.ceil(container.scrollTop) + container.offsetHeight) >= container.scrollHeight) {
|
|
85
|
+
this.action.emit({ action: 'scrolled' });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
protected onRowClick(event: Event, obj: any, index: number): void {
|
|
90
|
+
this.selectedRowIndex = index === this.selectedRowIndex ? -1 : index;
|
|
91
|
+
this.action.emit({ action: 'rowClick', obj, index, selected: this.selectedRowIndex === index, event });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
protected selectOption(оption: string, obj: any, index: number): void {
|
|
95
|
+
this.action.emit({ action: оption.toLowerCase(), obj, index, selected: this.selectedRowIndex === index });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
protected sortByProp(prop: string, sortState: SortState): void {
|
|
99
|
+
this.action.emit({ action: 'sort', prop, sortState });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
protected trackById(index: number, obj: any): number {
|
|
103
|
+
return obj.id || index;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Component, input, output } from '@angular/core';
|
|
2
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
3
|
+
|
|
4
|
+
export type SortState = 'none' | 'asc' | 'desc';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'lib-table-sort-header',
|
|
8
|
+
imports: [
|
|
9
|
+
MatIconModule,
|
|
10
|
+
],
|
|
11
|
+
templateUrl: './table-sort-header.component.html',
|
|
12
|
+
styleUrls: ['./table-sort-header.component.scss']
|
|
13
|
+
})
|
|
14
|
+
export class TableSortHeaderComponent {
|
|
15
|
+
public selected = input.required<boolean>();
|
|
16
|
+
public sort = output<SortState>();
|
|
17
|
+
|
|
18
|
+
protected sortState: SortState = 'none';
|
|
19
|
+
|
|
20
|
+
protected onSortClick(): void {
|
|
21
|
+
if (this.sortState === 'none') {
|
|
22
|
+
this.sortState = 'desc';
|
|
23
|
+
} else if (this.sortState === 'desc') {
|
|
24
|
+
this.sortState = 'asc';
|
|
25
|
+
} else {
|
|
26
|
+
this.sortState = 'none';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.sort.emit(this.sortState);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Directive, input } from '@angular/core';
|
|
2
|
+
import { AbstractControl, NG_VALIDATORS, Validator } from '@angular/forms';
|
|
3
|
+
|
|
4
|
+
@Directive({
|
|
5
|
+
selector: '[libFieldsMatchValidator]',
|
|
6
|
+
providers: [
|
|
7
|
+
{
|
|
8
|
+
provide: NG_VALIDATORS,
|
|
9
|
+
useExisting: FieldsMatchValidatorDirective,
|
|
10
|
+
multi: true,
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
})
|
|
14
|
+
export class FieldsMatchValidatorDirective implements Validator {
|
|
15
|
+
public fieldToMatch = input.required<string>();
|
|
16
|
+
|
|
17
|
+
public validate(control: AbstractControl): { [key: string]: any } | null {
|
|
18
|
+
const value = control.value;
|
|
19
|
+
|
|
20
|
+
if (!value) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const matchingControl = control.root.get(this.fieldToMatch());
|
|
25
|
+
if (!matchingControl) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (value !== matchingControl.value) {
|
|
30
|
+
return { mismatch: true };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Directive } from '@angular/core';
|
|
2
|
+
import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms';
|
|
3
|
+
|
|
4
|
+
@Directive({
|
|
5
|
+
selector: '[libPasswordValidator]',
|
|
6
|
+
providers: [
|
|
7
|
+
{
|
|
8
|
+
provide: NG_VALIDATORS,
|
|
9
|
+
useExisting: PasswordValidatorDirective,
|
|
10
|
+
multi: true
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
})
|
|
14
|
+
export class PasswordValidatorDirective implements Validator {
|
|
15
|
+
public validate(control: AbstractControl): ValidationErrors | null {
|
|
16
|
+
const password = control.value;
|
|
17
|
+
|
|
18
|
+
const pattern = /^(?=.*[A-Z])(?=.*[!@#\$%\^&\*])(?=.*\d)/;
|
|
19
|
+
|
|
20
|
+
if (password && !pattern.test(password)) {
|
|
21
|
+
return { passwordInvalid: true };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Directive, HostListener } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Directive({
|
|
4
|
+
selector: '[libPhoneValidation]',
|
|
5
|
+
})
|
|
6
|
+
export class PhoneValidationDirective {
|
|
7
|
+
@HostListener('input', ['$event']) public onInput(event: InputEvent) {
|
|
8
|
+
const input = event.target as HTMLInputElement;
|
|
9
|
+
if (!input.value.includes('+')) {
|
|
10
|
+
input.value = `+${input.value}`;
|
|
11
|
+
}
|
|
12
|
+
const regex = /^[0-9+]*$/;
|
|
13
|
+
if (!regex.test(input.value)) {
|
|
14
|
+
input.value = input.value.replace(/[^0-9+]/g, '');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@HostListener('keydown', ['$event']) public onKeyDown(event: KeyboardEvent): void {
|
|
19
|
+
const inputValue = (event.target as HTMLInputElement).value;
|
|
20
|
+
if (event.key === 'Backspace' && inputValue === '+') {
|
|
21
|
+
event.preventDefault();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Pipe, PipeTransform } from "@angular/core";
|
|
2
|
+
|
|
3
|
+
@Pipe({
|
|
4
|
+
name: 'blankFiller',
|
|
5
|
+
})
|
|
6
|
+
export class WhiteSpaceFillerPipe implements PipeTransform {
|
|
7
|
+
public transform(value: any, fillWith: string = '-'): string {
|
|
8
|
+
if (value === 0) {
|
|
9
|
+
return '0';
|
|
10
|
+
}
|
|
11
|
+
return value ? value : fillWith;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Pipe, PipeTransform } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Pipe({
|
|
4
|
+
name: 'snakeCaseParser',
|
|
5
|
+
})
|
|
6
|
+
export class SnakeCaseParserPipe implements PipeTransform {
|
|
7
|
+
public transform(str: string | undefined): string {
|
|
8
|
+
if (str) {
|
|
9
|
+
let temp = str.split('_').map(word => {
|
|
10
|
+
return word.toLowerCase();
|
|
11
|
+
}).join(' ');
|
|
12
|
+
return temp.charAt(0).toUpperCase() + temp.slice(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return '';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { HttpErrorResponse } from "@angular/common/http";
|
|
2
|
+
import { Injectable } from "@angular/core";
|
|
3
|
+
import { Subject } from "rxjs";
|
|
4
|
+
|
|
5
|
+
@Injectable({
|
|
6
|
+
providedIn: 'root'
|
|
7
|
+
})
|
|
8
|
+
export class ErrorService {
|
|
9
|
+
private errorSubject = new Subject<HttpErrorResponse>();
|
|
10
|
+
public error$ = this.errorSubject.asObservable();
|
|
11
|
+
|
|
12
|
+
public sendError(error: HttpErrorResponse) {
|
|
13
|
+
this.errorSubject.next(error);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { BehaviorSubject } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
@Injectable({
|
|
5
|
+
providedIn: 'root'
|
|
6
|
+
})
|
|
7
|
+
export class LoaderService {
|
|
8
|
+
private loadingSubject = new BehaviorSubject<boolean>(false);
|
|
9
|
+
public loading$ = this.loadingSubject.asObservable();
|
|
10
|
+
|
|
11
|
+
public setLoading(loadingState: boolean) {
|
|
12
|
+
this.loadingSubject.next(loadingState);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { animate, state, style, transition, trigger } from "@angular/animations";
|
|
2
|
+
|
|
3
|
+
export const fader = trigger('fadeInOut', [
|
|
4
|
+
transition(':enter', [
|
|
5
|
+
style({ opacity: 0 }),
|
|
6
|
+
animate('200ms', style({ opacity: 1 })),
|
|
7
|
+
]),
|
|
8
|
+
transition(':leave', [
|
|
9
|
+
animate('200ms', style({ opacity: 0 })),
|
|
10
|
+
]),
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
export const openDropdown = trigger('open-close-dropdown', [
|
|
14
|
+
transition(':enter', [
|
|
15
|
+
style({ height: 0, overflow: 'hidden' }),
|
|
16
|
+
animate('200ms ease', style({ height: '*' }))
|
|
17
|
+
]),
|
|
18
|
+
transition(':leave', [
|
|
19
|
+
style({ height: '*', overflow: 'hidden' }),
|
|
20
|
+
animate('200ms ease', style({ height: 0 }))
|
|
21
|
+
])
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
export const textFader = trigger('textFader', [
|
|
25
|
+
state('void', style({ opacity: 0 })),
|
|
26
|
+
state('*', style({ opacity: 1 })),
|
|
27
|
+
transition('void => *', [animate('0.3s 0.3s ease-in')]),
|
|
28
|
+
transition('* => void', [animate('0.3s ease-in')])
|
|
29
|
+
]);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export const HTTP_STATUS_CODES: any = {
|
|
2
|
+
100: 'Continue',
|
|
3
|
+
101: 'Switching Protocols',
|
|
4
|
+
102: 'Processing',
|
|
5
|
+
103: 'Early Hints',
|
|
6
|
+
200: 'OK',
|
|
7
|
+
201: 'Created',
|
|
8
|
+
202: 'Accepted',
|
|
9
|
+
203: 'Non-Authoritative Information',
|
|
10
|
+
204: 'No Content',
|
|
11
|
+
205: 'Reset Content',
|
|
12
|
+
206: 'Partial Content',
|
|
13
|
+
207: 'Multi-Status',
|
|
14
|
+
208: 'Already Reported',
|
|
15
|
+
226: 'IM Used',
|
|
16
|
+
300: 'Multiple Choices',
|
|
17
|
+
301: 'Moved Permanently',
|
|
18
|
+
302: 'Found',
|
|
19
|
+
303: 'See Other',
|
|
20
|
+
304: 'Not Modified',
|
|
21
|
+
305: 'Use Proxy',
|
|
22
|
+
306: '(Unused)',
|
|
23
|
+
307: 'Temporary Redirect',
|
|
24
|
+
308: 'Permanent Redirect',
|
|
25
|
+
400: 'Bad Request',
|
|
26
|
+
401: 'Unauthorized',
|
|
27
|
+
402: 'Payment Required',
|
|
28
|
+
403: 'Forbidden',
|
|
29
|
+
404: 'Not Found',
|
|
30
|
+
405: 'Method Not Allowed',
|
|
31
|
+
406: 'Not Acceptable',
|
|
32
|
+
407: 'Proxy Authentication Required',
|
|
33
|
+
408: 'Request Timeout',
|
|
34
|
+
409: 'Conflict',
|
|
35
|
+
410: 'Gone',
|
|
36
|
+
411: 'Length Required',
|
|
37
|
+
412: 'Precondition Failed',
|
|
38
|
+
413: 'Payload Too Large',
|
|
39
|
+
414: 'URI Too Long',
|
|
40
|
+
415: 'Unsupported Media Type',
|
|
41
|
+
416: 'Range Not Satisfiable',
|
|
42
|
+
417: 'Expectation Failed',
|
|
43
|
+
418: "I'm a teapot",
|
|
44
|
+
421: 'Misdirected Request',
|
|
45
|
+
422: 'Unprocessable Entity',
|
|
46
|
+
423: 'Locked',
|
|
47
|
+
424: 'Failed Dependency',
|
|
48
|
+
425: 'Too Early',
|
|
49
|
+
426: 'Upgrade Required',
|
|
50
|
+
428: 'Precondition Required',
|
|
51
|
+
429: 'Too Many Requests',
|
|
52
|
+
431: 'Request Header Fields Too Large',
|
|
53
|
+
451: 'Unavailable For Legal Reasons',
|
|
54
|
+
500: 'Internal Server Error',
|
|
55
|
+
501: 'Not Implemented',
|
|
56
|
+
502: 'Bad Gateway',
|
|
57
|
+
503: 'Service Unavailable',
|
|
58
|
+
504: 'Gateway Timeout',
|
|
59
|
+
505: 'HTTP Version Not Supported',
|
|
60
|
+
506: 'Variant Also Negotiates',
|
|
61
|
+
507: 'Insufficient Storage',
|
|
62
|
+
508: 'Loop Detected',
|
|
63
|
+
510: 'Not Extended',
|
|
64
|
+
511: 'Network Authentication Required'
|
|
65
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Public API Surface of ui-lib
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './lib/pipes/blank-filler.pipe';
|
|
6
|
+
export * from './lib/pipes/snake-case-parser.pipe';
|
|
7
|
+
export * from './lib/directives/fields-match-validator.directive';
|
|
8
|
+
export * from './lib/directives/phone-validation.directive';
|
|
9
|
+
export * from './lib/components/default-dialog/default-dialog.component';
|
|
10
|
+
export * from './lib/components/error-dispaly.component';
|
|
11
|
+
export * from './lib/components/search-bar.component';
|
|
12
|
+
export * from './lib/utils/types';
|
|
13
|
+
export * from './lib/utils/animations';
|
|
14
|
+
export * from './lib/components/table-sort-header/table-sort-header.component';
|
|
15
|
+
export * from './lib/components/table/table.component';
|
|
16
|
+
export * from './lib/components/global-loader/global-loader.component';
|
|
17
|
+
export * from './lib/services/loader.service';
|
|
18
|
+
export * from './lib/services/error.service';
|
|
19
|
+
export * from './lib/components/error-handler/error-handler.component';
|
|
20
|
+
export * from './lib/components/chip/chip.component';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
2
|
+
{
|
|
3
|
+
"extends": "../../tsconfig.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"outDir": "../../out-tsc/lib",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"inlineSources": true,
|
|
9
|
+
"types": []
|
|
10
|
+
},
|
|
11
|
+
"exclude": [
|
|
12
|
+
"**/*.spec.ts"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
2
|
+
{
|
|
3
|
+
"extends": "../../tsconfig.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"outDir": "../../out-tsc/spec",
|
|
6
|
+
"types": [
|
|
7
|
+
"jasmine"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
"include": [
|
|
11
|
+
"**/*.spec.ts",
|
|
12
|
+
"**/*.d.ts"
|
|
13
|
+
]
|
|
14
|
+
}
|