@ferhaps/easy-ui-lib 0.0.1 → 0.0.2

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.
Files changed (61) hide show
  1. package/README.md +146 -3
  2. package/fesm2022/ferhaps-easy-ui-lib.mjs +610 -0
  3. package/fesm2022/ferhaps-easy-ui-lib.mjs.map +1 -0
  4. package/index.d.ts +5 -0
  5. package/lib/components/chip/chip.component.d.ts +10 -0
  6. package/lib/components/default-dialog/default-dialog.component.d.ts +11 -0
  7. package/lib/components/error-dispaly.component.d.ts +10 -0
  8. package/lib/components/error-handler/error-handler.component.d.ts +12 -0
  9. package/lib/components/error-handler/error-popup/error-popup.component.d.ts +9 -0
  10. package/lib/components/global-loader/global-loader.component.d.ts +7 -0
  11. package/lib/components/search-bar.component.d.ts +10 -0
  12. package/lib/components/table/table.component.d.ts +46 -0
  13. package/lib/components/table-sort-header/table-sort-header.component.d.ts +10 -0
  14. package/lib/directives/fields-match-validator.directive.d.ts +10 -0
  15. package/lib/directives/phone-validation.directive.d.ts +7 -0
  16. package/lib/pipes/blank-filler.pipe.d.ts +7 -0
  17. package/lib/pipes/snake-case-parser.pipe.d.ts +7 -0
  18. package/lib/services/error.service.d.ts +9 -0
  19. package/lib/services/loader.service.d.ts +8 -0
  20. package/lib/utils/animations.d.ts +3 -0
  21. package/{src/lib/utils/types.ts → lib/utils/types.d.ts} +2 -3
  22. package/lib/utils/utils.d.ts +1 -0
  23. package/package.json +22 -4
  24. package/public-api.d.ts +16 -0
  25. package/ng-package.json +0 -7
  26. package/src/lib/components/chip/chip.component.html +0 -3
  27. package/src/lib/components/chip/chip.component.scss +0 -19
  28. package/src/lib/components/chip/chip.component.ts +0 -23
  29. package/src/lib/components/default-dialog/default-dialog.component.html +0 -19
  30. package/src/lib/components/default-dialog/default-dialog.component.scss +0 -41
  31. package/src/lib/components/default-dialog/default-dialog.component.ts +0 -23
  32. package/src/lib/components/error-dispaly.component.ts +0 -48
  33. package/src/lib/components/error-handler/error-handler.component.html +0 -0
  34. package/src/lib/components/error-handler/error-handler.component.scss +0 -0
  35. package/src/lib/components/error-handler/error-handler.component.ts +0 -44
  36. package/src/lib/components/error-handler/error-popup/error-popup.component.html +0 -13
  37. package/src/lib/components/error-handler/error-popup/error-popup.component.scss +0 -19
  38. package/src/lib/components/error-handler/error-popup/error-popup.component.ts +0 -21
  39. package/src/lib/components/global-loader/global-loader.component.html +0 -5
  40. package/src/lib/components/global-loader/global-loader.component.scss +0 -12
  41. package/src/lib/components/global-loader/global-loader.component.ts +0 -17
  42. package/src/lib/components/search-bar.component.ts +0 -70
  43. package/src/lib/components/table/table.component.html +0 -121
  44. package/src/lib/components/table/table.component.scss +0 -116
  45. package/src/lib/components/table/table.component.ts +0 -105
  46. package/src/lib/components/table-sort-header/table-sort-header.component.html +0 -7
  47. package/src/lib/components/table-sort-header/table-sort-header.component.scss +0 -17
  48. package/src/lib/components/table-sort-header/table-sort-header.component.ts +0 -31
  49. package/src/lib/directives/fields-match-validator.directive.ts +0 -35
  50. package/src/lib/directives/password-validator.directive.ts +0 -26
  51. package/src/lib/directives/phone-validation.directive.ts +0 -24
  52. package/src/lib/pipes/blank-filler.pipe.ts +0 -13
  53. package/src/lib/pipes/snake-case-parser.pipe.ts +0 -17
  54. package/src/lib/services/error.service.ts +0 -15
  55. package/src/lib/services/loader.service.ts +0 -14
  56. package/src/lib/utils/animations.ts +0 -29
  57. package/src/lib/utils/utils.ts +0 -65
  58. package/src/public-api.ts +0 -20
  59. package/tsconfig.lib.json +0 -14
  60. package/tsconfig.lib.prod.json +0 -10
  61. 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 [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>
@@ -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,5 +0,0 @@
1
- @if (loaderService.loading$ | async) {
2
- <div class="loader">
3
- <mat-spinner />
4
- </div>
5
- }
@@ -1,12 +0,0 @@
1
- .loader {
2
- display: flex;
3
- justify-content: center;
4
- align-items: center;
5
- height: 100%;
6
- width: 100%;
7
- position: fixed;
8
- top: 0;
9
- left: 0;
10
- background-color: rgba(0, 0, 0, 0.2);
11
- z-index: 9999;
12
- }
@@ -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,7 +0,0 @@
1
- <div class="sorting-container" (click)="onSortClick()">
2
- <mat-icon
3
- [class.selected]="selected() && sortState != 'none'"
4
- [class.rotated]="sortState == 'asc'">
5
- sort
6
- </mat-icon>
7
- </div>
@@ -1,17 +0,0 @@
1
- .sorting-container {
2
- display: flex;
3
- align-items: center;
4
-
5
- * {
6
- cursor: pointer;
7
- }
8
-
9
- .selected {
10
- color: #31ADFF;
11
- background-color: #ebf7fc;
12
- }
13
-
14
- .rotated {
15
- transform: rotate(180deg) scaleX(-1);
16
- }
17
- }
@@ -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])(?=.*[!@#\$%\^&\*])(?=.*\d)/;
19
-
20
- if (password && !pattern.test(password)) {
21
- return { passwordInvalid: true };
22
- }
23
-
24
- return null;
25
- }
26
- }