@verisoft/ui-core 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/.eslintrc.json +48 -0
- package/README.md +7 -0
- package/jest.config.ts +21 -0
- package/ng-package.json +7 -0
- package/package.json +16 -0
- package/project.json +36 -0
- package/src/index.ts +4 -0
- package/src/lib/common/angular-helper.ts +44 -0
- package/src/lib/common/constants.ts +1 -0
- package/src/lib/common/control.models.ts +57 -0
- package/src/lib/common/datasource-component.model.spec.ts +42 -0
- package/src/lib/common/datasource-component.model.ts +41 -0
- package/src/lib/common/index.ts +4 -0
- package/src/lib/components/action-button-group/action-button-group.model.ts +16 -0
- package/src/lib/components/action-button-group/action-button.model.ts +16 -0
- package/src/lib/components/action-button-group/index.ts +2 -0
- package/src/lib/components/base-form/base-form-input.component.ts +115 -0
- package/src/lib/components/base-form/base-form.component.ts +209 -0
- package/src/lib/components/base-form/directives/detail-store.directive.ts +146 -0
- package/src/lib/components/base-form/index.ts +4 -0
- package/src/lib/components/base-form/models/base-form-input.models.ts +11 -0
- package/src/lib/components/base-form/models/base-form.models.ts +31 -0
- package/src/lib/components/base-form/models/index.ts +2 -0
- package/src/lib/components/breadcrumb/breadcrumb.model.ts +21 -0
- package/src/lib/components/breadcrumb/breadcrumb.service.ts +9 -0
- package/src/lib/components/breadcrumb/breadcrumbcore.component.ts +94 -0
- package/src/lib/components/breadcrumb/index.ts +3 -0
- package/src/lib/components/button/button.model.ts +22 -0
- package/src/lib/components/button/index.ts +1 -0
- package/src/lib/components/calendar/calendar.model.ts +16 -0
- package/src/lib/components/calendar/index.ts +1 -0
- package/src/lib/components/checkbox/checkbox.model.ts +9 -0
- package/src/lib/components/checkbox/index.ts +1 -0
- package/src/lib/components/confirm-dialog/confirm-dialog.model.ts +26 -0
- package/src/lib/components/confirm-dialog/index.ts +1 -0
- package/src/lib/components/dropdown/dropdown.model.ts +13 -0
- package/src/lib/components/dropdown/index.ts +1 -0
- package/src/lib/components/form-field/form-field.model.ts +15 -0
- package/src/lib/components/form-field/index.ts +1 -0
- package/src/lib/components/generic-field/generic-field.model.ts +10 -0
- package/src/lib/components/generic-field/index.ts +1 -0
- package/src/lib/components/header/header.model.ts +18 -0
- package/src/lib/components/header/index.ts +1 -0
- package/src/lib/components/index.ts +31 -0
- package/src/lib/components/input-group/index.ts +1 -0
- package/src/lib/components/input-group/input-group.model.ts +17 -0
- package/src/lib/components/loader/index.ts +1 -0
- package/src/lib/components/loader/loader.model.ts +8 -0
- package/src/lib/components/multiselect/index.ts +1 -0
- package/src/lib/components/multiselect/mutiselect.model.ts +13 -0
- package/src/lib/components/number-input/index.ts +1 -0
- package/src/lib/components/number-input/number-input.model.ts +14 -0
- package/src/lib/components/page-header/index.ts +1 -0
- package/src/lib/components/page-header/page-header.model.ts +17 -0
- package/src/lib/components/password/index.ts +1 -0
- package/src/lib/components/password/password.model.ts +11 -0
- package/src/lib/components/radiobutton/index.ts +1 -0
- package/src/lib/components/radiobutton/radiobutton.model.ts +16 -0
- package/src/lib/components/section/index.ts +1 -0
- package/src/lib/components/section/section.model.ts +11 -0
- package/src/lib/components/side-menu/index.ts +3 -0
- package/src/lib/components/side-menu/services/side-menu-provider.service.ts +13 -0
- package/src/lib/components/side-menu/services/side-menu.service.ts +58 -0
- package/src/lib/components/side-menu/side-menu.model.ts +64 -0
- package/src/lib/components/slider/index.ts +1 -0
- package/src/lib/components/slider/slider.model.ts +13 -0
- package/src/lib/components/snackbar/index.ts +1 -0
- package/src/lib/components/snackbar/snackbar.model.ts +8 -0
- package/src/lib/components/stepper/index.ts +1 -0
- package/src/lib/components/stepper/stepper.model.ts +14 -0
- package/src/lib/components/switch/index.ts +1 -0
- package/src/lib/components/switch/switch.model.ts +9 -0
- package/src/lib/components/tab-view/index.ts +1 -0
- package/src/lib/components/tab-view/tab-view.model.ts +19 -0
- package/src/lib/components/table/index.ts +2 -0
- package/src/lib/components/table/table.models.ts +189 -0
- package/src/lib/components/table/template-column.directive.ts +45 -0
- package/src/lib/components/table-filter/index.ts +1 -0
- package/src/lib/components/table-filter/table-filter.model.ts +22 -0
- package/src/lib/components/textarea/index.ts +1 -0
- package/src/lib/components/textarea/textarea.model.ts +13 -0
- package/src/lib/components/textfield/index.ts +1 -0
- package/src/lib/components/textfield/textfield.model.ts +13 -0
- package/src/lib/components/tristatecheckbox/index.ts +1 -0
- package/src/lib/components/tristatecheckbox/tristatecheckbox.model.ts +9 -0
- package/src/lib/components/unsubscribe.component.ts +12 -0
- package/src/lib/directives/datasource.directive.ts +275 -0
- package/src/lib/directives/index.ts +1 -0
- package/src/lib/pipes/error/error.codes.ts +6 -0
- package/src/lib/pipes/error/error.models.ts +27 -0
- package/src/lib/pipes/error/error.pipe.ts +27 -0
- package/src/lib/pipes/error/warning.codes.ts +5 -0
- package/src/lib/pipes/error/warning.pipe.ts +27 -0
- package/src/lib/pipes/gov/gov-color.pipe.ts +24 -0
- package/src/lib/pipes/gov/gov-size.pipe.ts +16 -0
- package/src/lib/pipes/index.ts +8 -0
- package/src/lib/pipes/keyOrFn/keyOrFn.pipe.ts +23 -0
- package/src/test-setup.ts +8 -0
- package/tsconfig.json +28 -0
- package/tsconfig.lib.json +17 -0
- package/tsconfig.lib.prod.json +9 -0
- package/tsconfig.spec.json +16 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { BaseFormDirectiveCore } from '../base-form';
|
|
3
|
+
|
|
4
|
+
export const TABLE_FILTER_COMPONENT_TOKEN = new InjectionToken<TableFilterCore<any>>(
|
|
5
|
+
'TableFilterComponentToken'
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
export interface FilterDefinition {
|
|
9
|
+
optionLabel: string;
|
|
10
|
+
label: string;
|
|
11
|
+
optionValue?: string;
|
|
12
|
+
url?: string;
|
|
13
|
+
filterType?: 'dropdown' | 'checkbox' | 'calendar' | 'multiselect';
|
|
14
|
+
initialValue?: any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TableFilterCore<T> extends BaseFormDirectiveCore<T> {
|
|
18
|
+
filterDefinitions: FilterDefinition[];
|
|
19
|
+
title: string;
|
|
20
|
+
hideSearch?: boolean;
|
|
21
|
+
debounceTime?: number;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './textarea.model';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { BaseFormCore } from '../base-form';
|
|
3
|
+
|
|
4
|
+
export const TEXTAREA_COMPONENT_TOKEN = new InjectionToken<TextareaCore>(
|
|
5
|
+
'TextareaComponentToken'
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
export interface TextareaCore extends BaseFormCore {
|
|
9
|
+
rows: number;
|
|
10
|
+
cols: number;
|
|
11
|
+
autoResize: boolean;
|
|
12
|
+
floatLabel: string | undefined;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './textfield.model';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { FieldSizeType, FieldTypeType } from '../../common';
|
|
3
|
+
import { BaseFormCore } from '../base-form';
|
|
4
|
+
|
|
5
|
+
export const TEXTFIELD_COMPONENT_TOKEN = new InjectionToken<TextfieldCore>(
|
|
6
|
+
'TextfieldComponentToken'
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
export interface TextfieldCore extends BaseFormCore {
|
|
10
|
+
size?: FieldSizeType;
|
|
11
|
+
type: FieldTypeType;
|
|
12
|
+
floatLabel: boolean;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './tristatecheckbox.model'
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { BaseFormCore } from '../base-form';
|
|
3
|
+
|
|
4
|
+
export const TRISTATE_CHECKBOX_COMPONENT_TOKEN = new InjectionToken<TristateCheckboxCore>(
|
|
5
|
+
'TristateCheckboxComponentToken'
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
9
|
+
export interface TristateCheckboxCore extends BaseFormCore {}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Directive, OnDestroy } from '@angular/core';
|
|
2
|
+
import { Subject } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
@Directive()
|
|
5
|
+
export abstract class UnsubscribeComponent implements OnDestroy {
|
|
6
|
+
destroyed$ = new Subject<void>();
|
|
7
|
+
|
|
8
|
+
ngOnDestroy() {
|
|
9
|
+
this.destroyed$.next();
|
|
10
|
+
this.destroyed$.complete();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/* eslint-disable @angular-eslint/directive-selector */
|
|
2
|
+
import { HttpClient } from '@angular/common/http';
|
|
3
|
+
import {
|
|
4
|
+
ChangeDetectorRef,
|
|
5
|
+
Directive,
|
|
6
|
+
inject,
|
|
7
|
+
Input,
|
|
8
|
+
OnChanges,
|
|
9
|
+
OnInit,
|
|
10
|
+
SimpleChanges,
|
|
11
|
+
} from '@angular/core';
|
|
12
|
+
import {
|
|
13
|
+
BASE_URL_PATH,
|
|
14
|
+
convertDatasource,
|
|
15
|
+
DataSourceFunctionType,
|
|
16
|
+
DatasourceType,
|
|
17
|
+
DEFAULT_SEARCH_LIMIT,
|
|
18
|
+
FilterEvent,
|
|
19
|
+
LazyLoadEvent,
|
|
20
|
+
normalizeRequest,
|
|
21
|
+
Page,
|
|
22
|
+
RequestParams,
|
|
23
|
+
} from '@verisoft/core';
|
|
24
|
+
import {
|
|
25
|
+
BehaviorSubject,
|
|
26
|
+
catchError,
|
|
27
|
+
debounceTime,
|
|
28
|
+
filter,
|
|
29
|
+
map,
|
|
30
|
+
of,
|
|
31
|
+
switchMap,
|
|
32
|
+
takeUntil,
|
|
33
|
+
tap,
|
|
34
|
+
} from 'rxjs';
|
|
35
|
+
import {
|
|
36
|
+
DataSourceComponentModel,
|
|
37
|
+
DEFAULT_DEBOUNCE_TIME,
|
|
38
|
+
setComponentProperties,
|
|
39
|
+
setDataToArray,
|
|
40
|
+
} from '../common';
|
|
41
|
+
import {
|
|
42
|
+
DROPDOWN_COMPONENT_TOKEN,
|
|
43
|
+
DropdownCore,
|
|
44
|
+
GENERIC_FIELD_COMPONENT_TOKEN,
|
|
45
|
+
GenericFieldCore,
|
|
46
|
+
MULTISELECT_COMPONENT_TOKEN,
|
|
47
|
+
MultiselectCore,
|
|
48
|
+
UnsubscribeComponent,
|
|
49
|
+
} from '../components';
|
|
50
|
+
|
|
51
|
+
type ExtendedRequestType<T> = RequestParams<T> & { useNewData: boolean}
|
|
52
|
+
|
|
53
|
+
@Directive({
|
|
54
|
+
selector:
|
|
55
|
+
'v-dropdown[useDatasource], v-multiselect[useDatasource], v-generic-field[useDatasource]',
|
|
56
|
+
standalone: true,
|
|
57
|
+
})
|
|
58
|
+
export class DatasourceDirective<T>
|
|
59
|
+
extends UnsubscribeComponent
|
|
60
|
+
implements OnChanges, OnInit
|
|
61
|
+
{
|
|
62
|
+
@Input() datasource!: DatasourceType<T>;
|
|
63
|
+
|
|
64
|
+
@Input() autoBind = true;
|
|
65
|
+
|
|
66
|
+
@Input() loadingText = '... loading ...';
|
|
67
|
+
|
|
68
|
+
@Input() filterField: string | undefined = 'fulltext';
|
|
69
|
+
|
|
70
|
+
@Input() transformFn?: (data: T) => unknown;
|
|
71
|
+
|
|
72
|
+
get activeComponent(): DataSourceComponentModel<T> {
|
|
73
|
+
return (this.dropdownComponent ??
|
|
74
|
+
this.multiSelectComponent ??
|
|
75
|
+
this.genericField) as DataSourceComponentModel<T>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private httpClient = inject(HttpClient);
|
|
79
|
+
|
|
80
|
+
private baseUrl: string = inject(BASE_URL_PATH);
|
|
81
|
+
|
|
82
|
+
private changeDetectorRef = inject(ChangeDetectorRef);
|
|
83
|
+
|
|
84
|
+
private isAllDataLoaded = false;
|
|
85
|
+
|
|
86
|
+
private parameters$ = new BehaviorSubject<Partial<ExtendedRequestType<T>>>({});
|
|
87
|
+
|
|
88
|
+
private lastParameter = {};
|
|
89
|
+
|
|
90
|
+
private dropdownComponent = inject<DropdownCore<T>>(
|
|
91
|
+
DROPDOWN_COMPONENT_TOKEN,
|
|
92
|
+
{ optional: true }
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
private multiSelectComponent = inject<MultiselectCore<T>>(
|
|
96
|
+
MULTISELECT_COMPONENT_TOKEN,
|
|
97
|
+
{
|
|
98
|
+
optional: true,
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
private genericField = inject<GenericFieldCore<T>>(
|
|
103
|
+
GENERIC_FIELD_COMPONENT_TOKEN,
|
|
104
|
+
{ optional: true }
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
private dataSourceService?: DataSourceFunctionType<T>;
|
|
108
|
+
|
|
109
|
+
private loadingPlaceholderItem: { [key: string]: unknown } = {};
|
|
110
|
+
|
|
111
|
+
ngOnInit(): void {
|
|
112
|
+
this.activeComponent.showed
|
|
113
|
+
.pipe(takeUntil(this.destroyed$))
|
|
114
|
+
.subscribe(() => {
|
|
115
|
+
if (
|
|
116
|
+
!this.autoBind &&
|
|
117
|
+
!this.activeComponent.options &&
|
|
118
|
+
!this.activeComponent.loading
|
|
119
|
+
) {
|
|
120
|
+
this.parameters$.next({});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
this.activeComponent.lazyLoad
|
|
125
|
+
.pipe(takeUntil(this.destroyed$))
|
|
126
|
+
.subscribe((value: LazyLoadEvent) => {
|
|
127
|
+
this.parameters$.next({ offset: value?.offset, limit: value?.limit });
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
this.activeComponent.filtered
|
|
131
|
+
.pipe(takeUntil(this.destroyed$))
|
|
132
|
+
.subscribe((value: FilterEvent) => {
|
|
133
|
+
const property = this.filterField ?? this.activeComponent.optionLabel;
|
|
134
|
+
if (property) {
|
|
135
|
+
this.parameters$.next({
|
|
136
|
+
offset: 0,
|
|
137
|
+
filter: {
|
|
138
|
+
[property]: value.filter ? value.filter : undefined,
|
|
139
|
+
} as Partial<T>,
|
|
140
|
+
useNewData: true,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
this.parameters$
|
|
146
|
+
.pipe(
|
|
147
|
+
takeUntil(this.destroyed$),
|
|
148
|
+
filter(request => !this.isDataForRequestLoaded(request)),
|
|
149
|
+
map((request) => {
|
|
150
|
+
const extendedParams = normalizeRequest({ ...this.lastParameter, ...request }) as ExtendedRequestType<T>;
|
|
151
|
+
extendedParams.useNewData = request.useNewData ?? false;
|
|
152
|
+
return extendedParams;
|
|
153
|
+
}),
|
|
154
|
+
tap((request) => {
|
|
155
|
+
this.lastParameter = request;
|
|
156
|
+
}),
|
|
157
|
+
debounceTime(DEFAULT_DEBOUNCE_TIME),
|
|
158
|
+
tap(() => {
|
|
159
|
+
this.changeComponent(this.activeComponent, {
|
|
160
|
+
loading: true,
|
|
161
|
+
});
|
|
162
|
+
}),
|
|
163
|
+
switchMap((request) =>
|
|
164
|
+
this.dataSourceService
|
|
165
|
+
? this.dataSourceService(request as RequestParams<T>).pipe(
|
|
166
|
+
map((response) => ({ request, response }))
|
|
167
|
+
)
|
|
168
|
+
: of({
|
|
169
|
+
request,
|
|
170
|
+
response: {
|
|
171
|
+
data: [] as T[],
|
|
172
|
+
total: 0,
|
|
173
|
+
limit: request.limit,
|
|
174
|
+
offset: request.offset,
|
|
175
|
+
} as Page<T>,
|
|
176
|
+
})
|
|
177
|
+
),
|
|
178
|
+
catchError((request) => {
|
|
179
|
+
this.changeComponent(this.activeComponent, {
|
|
180
|
+
loading: false,
|
|
181
|
+
});
|
|
182
|
+
return of({
|
|
183
|
+
request: request as ExtendedRequestType<T>,
|
|
184
|
+
response: {
|
|
185
|
+
data: [] as T[],
|
|
186
|
+
total: 0,
|
|
187
|
+
limit: request.limit,
|
|
188
|
+
offset: request.offset,
|
|
189
|
+
} as Page<T>,
|
|
190
|
+
});
|
|
191
|
+
})
|
|
192
|
+
)
|
|
193
|
+
.subscribe(({ request, response }) =>
|
|
194
|
+
this.setDataToControl(request, response)
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
199
|
+
if (changes['datasource']) {
|
|
200
|
+
this.dataSourceService = convertDatasource(
|
|
201
|
+
this.datasource,
|
|
202
|
+
this.baseUrl,
|
|
203
|
+
this.httpClient
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
this.activeComponent.loading = true;
|
|
207
|
+
this.changeDetectorRef.detectChanges();
|
|
208
|
+
if (this.autoBind) {
|
|
209
|
+
this.parameters$.next({ offset: 0 });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private isDataForRequestLoaded(request: Partial<ExtendedRequestType<T>>) {
|
|
215
|
+
if (request.useNewData){
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const offset = request.offset ?? 0;
|
|
220
|
+
const limit = request.limit ?? DEFAULT_SEARCH_LIMIT;
|
|
221
|
+
const options = this.activeComponent.options;
|
|
222
|
+
if (!options){
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (options.length < offset + limit) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const allItemsFilled = options.slice(offset, offset + limit).every(item => item !== undefined && item != this.loadingPlaceholderItem);
|
|
231
|
+
return allItemsFilled;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
private setDataToControl(request: ExtendedRequestType<T>, result: Page<T>): void {
|
|
235
|
+
this.isAllDataLoaded = result.total <= result.data.length;
|
|
236
|
+
const data = result.data;
|
|
237
|
+
const total = result.total;
|
|
238
|
+
const offset = request.offset;
|
|
239
|
+
const transferedData = this.transformFn
|
|
240
|
+
? data.map((x) => this.transformFn?.(x))
|
|
241
|
+
: data;
|
|
242
|
+
|
|
243
|
+
if (this.activeComponent.optionLabel) {
|
|
244
|
+
this.loadingPlaceholderItem[this.activeComponent.optionLabel] =
|
|
245
|
+
this.loadingText;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (this.activeComponent.optionValue) {
|
|
249
|
+
this.loadingPlaceholderItem[this.activeComponent.optionValue] = -1;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const options = request.useNewData ? undefined : this.activeComponent.options;
|
|
253
|
+
const newOptions = setDataToArray(
|
|
254
|
+
options,
|
|
255
|
+
transferedData,
|
|
256
|
+
offset,
|
|
257
|
+
total,
|
|
258
|
+
this.loadingPlaceholderItem
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
this.changeComponent(this.activeComponent, {
|
|
262
|
+
options: newOptions as T[],
|
|
263
|
+
loading: false,
|
|
264
|
+
lazy: !this.isAllDataLoaded,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private changeComponent(
|
|
269
|
+
component: DataSourceComponentModel<T>,
|
|
270
|
+
value: Partial<DataSourceComponentModel<T>>
|
|
271
|
+
) {
|
|
272
|
+
setComponentProperties(component, value);
|
|
273
|
+
this.changeDetectorRef.detectChanges();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './datasource.directive';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AbstractControl, ValidationErrors } from '@angular/forms';
|
|
2
|
+
|
|
3
|
+
type ErrorValue = { key: string; value: string } | null;
|
|
4
|
+
|
|
5
|
+
export function getFirstErrorFromControl<T extends AbstractControl>(
|
|
6
|
+
control: T
|
|
7
|
+
): ErrorValue {
|
|
8
|
+
if (!control || !control.errors) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const errors = control.errors ?? false;
|
|
12
|
+
if (errors) {
|
|
13
|
+
const key = Object.keys(control.errors)[0];
|
|
14
|
+
const value = control.errors[key];
|
|
15
|
+
return { key, value };
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getFirstError(errors: ValidationErrors): ErrorValue {
|
|
21
|
+
if (errors) {
|
|
22
|
+
const key = Object.keys(errors)[0];
|
|
23
|
+
const value = errors[key];
|
|
24
|
+
return { key, value };
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Pipe, PipeTransform } from '@angular/core';
|
|
2
|
+
import { ValidationErrors } from '@angular/forms';
|
|
3
|
+
import { ErrorCodesFns } from './error.codes';
|
|
4
|
+
import { getFirstError } from './error.models';
|
|
5
|
+
|
|
6
|
+
const EMPTY = '';
|
|
7
|
+
|
|
8
|
+
@Pipe({
|
|
9
|
+
name: 'error',
|
|
10
|
+
standalone: true,
|
|
11
|
+
})
|
|
12
|
+
export class ErrorPipe implements PipeTransform {
|
|
13
|
+
transform(errors: ValidationErrors | undefined | null): string {
|
|
14
|
+
if (!errors) {
|
|
15
|
+
return EMPTY;
|
|
16
|
+
}
|
|
17
|
+
const error = getFirstError(errors);
|
|
18
|
+
if (error) {
|
|
19
|
+
const errorFn = ErrorCodesFns[error.key];
|
|
20
|
+
if (!errorFn) {
|
|
21
|
+
return EMPTY;
|
|
22
|
+
}
|
|
23
|
+
return ErrorCodesFns[error.key](error.value);
|
|
24
|
+
}
|
|
25
|
+
return EMPTY;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Pipe, PipeTransform } from '@angular/core';
|
|
2
|
+
import { ValidationErrors } from '@angular/forms';
|
|
3
|
+
import { getFirstError } from './error.models';
|
|
4
|
+
import { WarningCodesFns } from './warning.codes';
|
|
5
|
+
|
|
6
|
+
const EMPTY = '';
|
|
7
|
+
|
|
8
|
+
@Pipe({
|
|
9
|
+
name: 'warning',
|
|
10
|
+
standalone: true,
|
|
11
|
+
})
|
|
12
|
+
export class WarningPipe implements PipeTransform {
|
|
13
|
+
transform(warnings: ValidationErrors | undefined | null): string {
|
|
14
|
+
if (!warnings) {
|
|
15
|
+
return EMPTY;
|
|
16
|
+
}
|
|
17
|
+
const error = getFirstError(warnings);
|
|
18
|
+
if (error) {
|
|
19
|
+
const errorFn = WarningCodesFns[error.key];
|
|
20
|
+
if (!errorFn) {
|
|
21
|
+
return EMPTY;
|
|
22
|
+
}
|
|
23
|
+
return WarningCodesFns[error.key](error.value);
|
|
24
|
+
}
|
|
25
|
+
return EMPTY;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Pipe, PipeTransform } from "@angular/core";
|
|
2
|
+
import { ControlSeverity, ControlSeverityType, GovControlSeverity } from "../../common";
|
|
3
|
+
|
|
4
|
+
@Pipe({
|
|
5
|
+
name: 'govColor',
|
|
6
|
+
standalone: true,
|
|
7
|
+
})
|
|
8
|
+
export class GovColorPipe implements PipeTransform {
|
|
9
|
+
transform(color: ControlSeverityType): string {
|
|
10
|
+
switch (color) {
|
|
11
|
+
case ControlSeverity.danger:
|
|
12
|
+
return GovControlSeverity.error;
|
|
13
|
+
|
|
14
|
+
case ControlSeverity.info:
|
|
15
|
+
return GovControlSeverity.neutral;
|
|
16
|
+
|
|
17
|
+
case ControlSeverity.help:
|
|
18
|
+
case ControlSeverity.contrast:
|
|
19
|
+
return GovControlSeverity.primary;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return color;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Pipe, PipeTransform } from "@angular/core";
|
|
2
|
+
import { FieldSizeType } from "../../common";
|
|
3
|
+
|
|
4
|
+
@Pipe({
|
|
5
|
+
name: 'govSize',
|
|
6
|
+
standalone: true,
|
|
7
|
+
})
|
|
8
|
+
export class GovSizePipe implements PipeTransform {
|
|
9
|
+
transform(size: FieldSizeType | undefined): string | undefined {
|
|
10
|
+
if (size) {
|
|
11
|
+
return size.substring(0, 1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return size;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './error/error.codes';
|
|
2
|
+
export * from './error/error.models';
|
|
3
|
+
export * from './error/error.pipe';
|
|
4
|
+
export * from './error/warning.pipe';
|
|
5
|
+
export * from './error/warning.codes';
|
|
6
|
+
export * from './gov/gov-size.pipe';
|
|
7
|
+
export * from './gov/gov-color.pipe';
|
|
8
|
+
export * from './keyOrFn/keyOrFn.pipe';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { Pipe, PipeTransform } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
@Pipe({
|
|
5
|
+
name: 'keyOrFn',
|
|
6
|
+
pure: false,
|
|
7
|
+
standalone: true,
|
|
8
|
+
})
|
|
9
|
+
export class KeyOrFunctionPipe implements PipeTransform {
|
|
10
|
+
transform(keyOrFn: any, row: any): any {
|
|
11
|
+
if (keyOrFn instanceof Function) {
|
|
12
|
+
return keyOrFn(row);
|
|
13
|
+
} else if (typeof keyOrFn === 'string') {
|
|
14
|
+
const value = row[keyOrFn];
|
|
15
|
+
if (value) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
} else if (typeof keyOrFn === 'boolean') {
|
|
19
|
+
return keyOrFn;
|
|
20
|
+
}
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment
|
|
2
|
+
globalThis.ngJest = {
|
|
3
|
+
testEnvironmentOptions: {
|
|
4
|
+
errorOnUnknownElements: true,
|
|
5
|
+
errorOnUnknownProperties: true,
|
|
6
|
+
},
|
|
7
|
+
};
|
|
8
|
+
import 'jest-preset-angular/setup-jest';
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2022",
|
|
4
|
+
"forceConsistentCasingInFileNames": true,
|
|
5
|
+
"strict": true,
|
|
6
|
+
"noImplicitOverride": true,
|
|
7
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
8
|
+
"noImplicitReturns": true,
|
|
9
|
+
"noFallthroughCasesInSwitch": true
|
|
10
|
+
},
|
|
11
|
+
"files": [],
|
|
12
|
+
"include": [],
|
|
13
|
+
"references": [
|
|
14
|
+
{
|
|
15
|
+
"path": "./tsconfig.lib.json"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "./tsconfig.spec.json"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"extends": "../../../tsconfig.base.json",
|
|
22
|
+
"angularCompilerOptions": {
|
|
23
|
+
"enableI18nLegacyMessageIdFormat": false,
|
|
24
|
+
"strictInjectionParameters": true,
|
|
25
|
+
"strictInputAccessModifiers": true,
|
|
26
|
+
"strictTemplates": true
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../../dist/out-tsc",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"declarationMap": true,
|
|
7
|
+
"inlineSources": true,
|
|
8
|
+
"types": []
|
|
9
|
+
},
|
|
10
|
+
"exclude": [
|
|
11
|
+
"src/**/*.spec.ts",
|
|
12
|
+
"src/test-setup.ts",
|
|
13
|
+
"jest.config.ts",
|
|
14
|
+
"src/**/*.test.ts"
|
|
15
|
+
],
|
|
16
|
+
"include": ["src/**/*.ts"]
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "./tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "../../../dist/out-tsc",
|
|
5
|
+
"module": "commonjs",
|
|
6
|
+
"target": "es2016",
|
|
7
|
+
"types": ["jest", "node"]
|
|
8
|
+
},
|
|
9
|
+
"files": ["src/test-setup.ts"],
|
|
10
|
+
"include": [
|
|
11
|
+
"jest.config.ts",
|
|
12
|
+
"src/**/*.test.ts",
|
|
13
|
+
"src/**/*.spec.ts",
|
|
14
|
+
"src/**/*.d.ts"
|
|
15
|
+
]
|
|
16
|
+
}
|