@ferhaps/easy-ui-lib 1.0.0 → 1.1.0
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/fesm2022/ferhaps-easy-ui-lib.mjs +551 -0
- package/fesm2022/ferhaps-easy-ui-lib.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/components/default-dialog/default-dialog.component.d.ts +11 -0
- package/lib/components/error-dispaly.component.d.ts +10 -0
- package/lib/components/error-handler/error-handler.component.d.ts +12 -0
- package/lib/components/error-handler/error-popup/error-popup.component.d.ts +9 -0
- package/lib/components/global-loader/global-loader.component.d.ts +7 -0
- package/lib/components/search-bar.component.d.ts +10 -0
- package/lib/components/table/table.component.d.ts +46 -0
- package/lib/components/table-sort-header/table-sort-header.component.d.ts +10 -0
- package/lib/directives/fields-match-validator.directive.d.ts +10 -0
- package/lib/directives/password-validator.directive.d.ts +7 -0
- package/lib/directives/phone-validation.directive.d.ts +7 -0
- package/lib/pipes/blank-filler.pipe.d.ts +7 -0
- package/lib/pipes/snake-case-parser.pipe.d.ts +7 -0
- package/lib/services/error.service.d.ts +9 -0
- package/lib/services/loader.service.d.ts +8 -0
- package/lib/utils/animations.d.ts +3 -0
- package/{src/lib/utils/types.ts → lib/utils/types.d.ts} +2 -3
- package/lib/utils/utils.d.ts +1 -0
- package/package.json +44 -26
- package/{src/public-api.ts → public-api.d.ts} +16 -20
- package/ng-package.json +0 -7
- package/src/lib/components/default-dialog/default-dialog.component.html +0 -19
- package/src/lib/components/default-dialog/default-dialog.component.scss +0 -41
- package/src/lib/components/default-dialog/default-dialog.component.ts +0 -23
- package/src/lib/components/error-dispaly.component.ts +0 -48
- 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 +0 -44
- package/src/lib/components/error-handler/error-popup/error-popup.component.html +0 -13
- package/src/lib/components/error-handler/error-popup/error-popup.component.scss +0 -19
- package/src/lib/components/error-handler/error-popup/error-popup.component.ts +0 -21
- package/src/lib/components/global-loader/global-loader.component.html +0 -5
- package/src/lib/components/global-loader/global-loader.component.scss +0 -12
- package/src/lib/components/global-loader/global-loader.component.ts +0 -17
- package/src/lib/components/search-bar.component.ts +0 -70
- package/src/lib/components/table/table.component.html +0 -121
- package/src/lib/components/table/table.component.scss +0 -116
- package/src/lib/components/table/table.component.ts +0 -105
- package/src/lib/components/table-sort-header/table-sort-header.component.html +0 -7
- package/src/lib/components/table-sort-header/table-sort-header.component.scss +0 -17
- package/src/lib/components/table-sort-header/table-sort-header.component.ts +0 -31
- package/src/lib/directives/fields-match-validator.directive.ts +0 -35
- package/src/lib/directives/password-validator.directive.ts +0 -26
- package/src/lib/directives/phone-validation.directive.ts +0 -24
- package/src/lib/pipes/blank-filler.pipe.ts +0 -13
- package/src/lib/pipes/snake-case-parser.pipe.ts +0 -17
- package/src/lib/services/error.service.ts +0 -15
- package/src/lib/services/loader.service.ts +0 -14
- package/src/lib/utils/animations.ts +0 -29
- package/src/lib/utils/utils.ts +0 -65
- package/tsconfig.lib.json +0 -14
- package/tsconfig.lib.prod.json +0 -10
- package/tsconfig.spec.json +0 -14
|
@@ -1,44 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<lib-default-dialog [dialogTitle]="'А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>
|
|
@@ -1,19 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
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>
|
|
@@ -1,116 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
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])(?=.*[a-z])(?=.*[!@#\$%\^&\*])(?=.*\d).{8,}$/
|
|
19
|
-
|
|
20
|
-
if (password && !pattern.test(password)) {
|
|
21
|
-
return { passwordInvalid: true };
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
}
|