@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,209 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AfterViewInit,
|
|
3
|
+
ChangeDetectorRef,
|
|
4
|
+
Directive,
|
|
5
|
+
EventEmitter,
|
|
6
|
+
inject,
|
|
7
|
+
Input,
|
|
8
|
+
OnChanges,
|
|
9
|
+
OnDestroy,
|
|
10
|
+
OnInit,
|
|
11
|
+
Output,
|
|
12
|
+
SimpleChanges,
|
|
13
|
+
} from '@angular/core';
|
|
14
|
+
import { FormGroup } from '@angular/forms';
|
|
15
|
+
import { cloneDeep } from 'lodash-es';
|
|
16
|
+
import { Subject, takeUntil, filter, map } from 'rxjs';
|
|
17
|
+
import { FormState, isFormStateEqual } from './models';
|
|
18
|
+
|
|
19
|
+
@Directive({
|
|
20
|
+
// eslint-disable-next-line @angular-eslint/directive-selector
|
|
21
|
+
selector: '[v-baseForm]',
|
|
22
|
+
standalone: true,
|
|
23
|
+
})
|
|
24
|
+
export abstract class BaseFormDirective<T extends object>
|
|
25
|
+
implements OnInit, OnChanges, OnDestroy, AfterViewInit
|
|
26
|
+
{
|
|
27
|
+
@Input() data!: T | any;
|
|
28
|
+
@Output() dataChange = new EventEmitter<T>();
|
|
29
|
+
@Output() statusChange = new EventEmitter<FormState>();
|
|
30
|
+
@Output() formSubmit = new EventEmitter<T>();
|
|
31
|
+
@Output() formClear = new EventEmitter<void>();
|
|
32
|
+
|
|
33
|
+
formDestroyed$ = new Subject<void>();
|
|
34
|
+
keys: { key: string; alias?: string; type?: string; readOnly: string }[] = [];
|
|
35
|
+
formGroup!: FormGroup;
|
|
36
|
+
cd = inject(ChangeDetectorRef);
|
|
37
|
+
valueInitialization = false;
|
|
38
|
+
lastState!: FormState;
|
|
39
|
+
|
|
40
|
+
ngOnInit(): void {
|
|
41
|
+
this.initializeFormGroup();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
45
|
+
if (changes['initialData'] && this.formGroup) {
|
|
46
|
+
this.valueInitialization = true;
|
|
47
|
+
const dirty = this.formGroup.dirty;
|
|
48
|
+
|
|
49
|
+
this.formGroup.patchValue(this.fromModel(this.data), {
|
|
50
|
+
emitEvent: false,
|
|
51
|
+
onlySelf: true,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
this.formGroup.updateValueAndValidity();
|
|
55
|
+
if (!dirty) {
|
|
56
|
+
this.formGroup.markAsPristine();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this.valueInitialization = false;
|
|
60
|
+
this.createAndEmitIfFormStateChanged();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
ngAfterViewInit() {
|
|
65
|
+
this.cd.detectChanges();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
ngOnDestroy(): void {
|
|
69
|
+
this.formDestroyed$.next();
|
|
70
|
+
this.formDestroyed$.complete();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
abstract createFormGroup(): FormGroup;
|
|
74
|
+
|
|
75
|
+
createCompleteData() {
|
|
76
|
+
const change = this.toModel(this.formGroup?.value);
|
|
77
|
+
this.recursiveObjectAttributesTransformation(change);
|
|
78
|
+
const updatedData = this.applyChanges(
|
|
79
|
+
cloneDeep(this.data),
|
|
80
|
+
cloneDeep(change)
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return updatedData;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
submit() {
|
|
87
|
+
this.formGroup.markAllAsTouched();
|
|
88
|
+
if (!this.formGroup.invalid) {
|
|
89
|
+
this.formSubmit.emit(this.createCompleteData());
|
|
90
|
+
}
|
|
91
|
+
this.formGroup.markAsUntouched();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
clear() {
|
|
95
|
+
this.formClear.emit();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private initializeFormGroup() {
|
|
99
|
+
this.formGroup = this.createFormGroup();
|
|
100
|
+
this.valueInitialization = true;
|
|
101
|
+
this.formGroup.patchValue(this.fromModel(this.data), {
|
|
102
|
+
emitEvent: false,
|
|
103
|
+
onlySelf: true,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
this.formGroup.markAsPristine();
|
|
107
|
+
this.initValueChanges();
|
|
108
|
+
this.initStatusChanges();
|
|
109
|
+
this.valueInitialization = false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private initValueChanges() {
|
|
113
|
+
this.formGroup.valueChanges
|
|
114
|
+
.pipe(
|
|
115
|
+
takeUntil(this.formDestroyed$),
|
|
116
|
+
filter(() => !this.valueInitialization),
|
|
117
|
+
map((value) => {
|
|
118
|
+
const change = this.toModel(value);
|
|
119
|
+
this.recursiveObjectAttributesTransformation(change);
|
|
120
|
+
const updatedData = this.applyChanges(
|
|
121
|
+
cloneDeep(this.data),
|
|
122
|
+
cloneDeep(change)
|
|
123
|
+
);
|
|
124
|
+
return updatedData;
|
|
125
|
+
})
|
|
126
|
+
)
|
|
127
|
+
.subscribe((updatedData) => {
|
|
128
|
+
this.dataChange.emit(updatedData);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private initStatusChanges() {
|
|
133
|
+
this.formGroup.statusChanges
|
|
134
|
+
.pipe(takeUntil(this.formDestroyed$))
|
|
135
|
+
.subscribe(() => {
|
|
136
|
+
if (!this.valueInitialization) {
|
|
137
|
+
this.createAndEmitIfFormStateChanged();
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
this.createAndEmitIfFormStateChanged();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private createAndEmitIfFormStateChanged() {
|
|
145
|
+
const formState: FormState = {
|
|
146
|
+
valid: !this.formGroup.invalid,
|
|
147
|
+
dirty: this.formGroup.dirty,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
if (!isFormStateEqual(formState, this.lastState)) {
|
|
151
|
+
this.lastState = formState;
|
|
152
|
+
this.statusChange.emit(formState);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
protected applyChanges(data: T, changes: Partial<T>): T {
|
|
157
|
+
return Object.assign(data || ({} as T), changes);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
protected toModel(data: T): Partial<T> {
|
|
161
|
+
return data || {};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
protected fromModel(data: T): T {
|
|
165
|
+
return { ...(data || {}) } as T;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private recursiveObjectAttributesTransformation(obj: any): void {
|
|
169
|
+
this.recursiveObjectAttributesTraversal(
|
|
170
|
+
obj,
|
|
171
|
+
this.transformEmptyStringToNullStringFn
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private recursiveObjectAttributesTraversal(
|
|
176
|
+
obj: any,
|
|
177
|
+
transformationFn: (obj: any, key: string) => void
|
|
178
|
+
) {
|
|
179
|
+
if (
|
|
180
|
+
obj === null ||
|
|
181
|
+
transformationFn === null ||
|
|
182
|
+
typeof transformationFn !== 'function'
|
|
183
|
+
) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const traverse = (obj: any) => {
|
|
188
|
+
for (const key in obj) {
|
|
189
|
+
// eslint-disable-next-line no-prototype-builtins
|
|
190
|
+
if (obj.hasOwnProperty(key)) {
|
|
191
|
+
transformationFn(obj, key);
|
|
192
|
+
|
|
193
|
+
if (typeof obj[key] === 'object') {
|
|
194
|
+
traverse(obj[key]);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
traverse(obj);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private transformEmptyStringToNullStringFn(obj: any, key: string) {
|
|
204
|
+
// if empty string - transformation to null string
|
|
205
|
+
if (typeof obj[key] === 'string' && obj[key] === '') {
|
|
206
|
+
obj[key] = null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Directive,
|
|
3
|
+
AfterViewInit,
|
|
4
|
+
inject,
|
|
5
|
+
Input,
|
|
6
|
+
OnInit,
|
|
7
|
+
ChangeDetectorRef,
|
|
8
|
+
OnDestroy,
|
|
9
|
+
} from '@angular/core';
|
|
10
|
+
import { ActivatedRoute } from '@angular/router';
|
|
11
|
+
import { createFeatureSelector, createSelector, Store } from '@ngrx/store';
|
|
12
|
+
import {
|
|
13
|
+
createInitDetailAction,
|
|
14
|
+
createResetStateAction,
|
|
15
|
+
createUpdateDetailAction,
|
|
16
|
+
createUpdateFormStateAction,
|
|
17
|
+
DetailState,
|
|
18
|
+
} from '@verisoft/store';
|
|
19
|
+
import { takeUntil } from 'rxjs';
|
|
20
|
+
import { UnsubscribeComponent } from '../../unsubscribe.component';
|
|
21
|
+
import { BaseFormDirective } from '../base-form.component';
|
|
22
|
+
|
|
23
|
+
@Directive({
|
|
24
|
+
// eslint-disable-next-line @angular-eslint/directive-selector
|
|
25
|
+
selector: '[v-useDetailStore]',
|
|
26
|
+
exportAs: 'useDetailStore',
|
|
27
|
+
standalone: true,
|
|
28
|
+
})
|
|
29
|
+
export class DetailStoreDirective
|
|
30
|
+
extends UnsubscribeComponent
|
|
31
|
+
implements OnInit, AfterViewInit, OnDestroy
|
|
32
|
+
{
|
|
33
|
+
store = inject(Store);
|
|
34
|
+
cdr = inject(ChangeDetectorRef);
|
|
35
|
+
route = inject(ActivatedRoute);
|
|
36
|
+
@Input({ required: true })
|
|
37
|
+
form!: BaseFormDirective<any>;
|
|
38
|
+
@Input({ required: true }) detailsRepository!: string;
|
|
39
|
+
@Input() autoBind = true;
|
|
40
|
+
@Input() detailId!: string | number | undefined;
|
|
41
|
+
@Input({ required: true }) ngrxFeatureKey!: string;
|
|
42
|
+
@Input() destroyForm = true;
|
|
43
|
+
|
|
44
|
+
private loaded!: boolean;
|
|
45
|
+
|
|
46
|
+
ngOnInit(): void {
|
|
47
|
+
if (!this.loaded) {
|
|
48
|
+
this.listenFormState();
|
|
49
|
+
}
|
|
50
|
+
this.listenFormChange();
|
|
51
|
+
this.listenFormStatusChange();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
ngAfterViewInit(): void {
|
|
55
|
+
if (this.autoBind && !this.loaded) {
|
|
56
|
+
this.initForm();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
override ngOnDestroy(): void {
|
|
61
|
+
super.ngOnDestroy();
|
|
62
|
+
if (this.destroyForm) {
|
|
63
|
+
this.store.dispatch(createResetStateAction(this.detailsRepository)());
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private initForm() {
|
|
68
|
+
this.store.dispatch(
|
|
69
|
+
createInitDetailAction(this.detailsRepository)({ obj: this.detailId })
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private listenFormState() {
|
|
74
|
+
const selectIncomeData = createSelector(
|
|
75
|
+
createFeatureSelector<any>(this.ngrxFeatureKey),
|
|
76
|
+
(state: any) => state?.[this.detailsRepository] as DetailState<any>
|
|
77
|
+
);
|
|
78
|
+
this.store
|
|
79
|
+
.select(selectIncomeData)
|
|
80
|
+
.pipe(takeUntil(this.destroyed$))
|
|
81
|
+
.subscribe(({ item, loaded } = { item: undefined, loaded: false }) => {
|
|
82
|
+
if (
|
|
83
|
+
item &&
|
|
84
|
+
(item.validationErrors || item.validationWarnings) &&
|
|
85
|
+
!this.form.formGroup.dirty
|
|
86
|
+
) {
|
|
87
|
+
this.handleValidation(
|
|
88
|
+
'propertyName',
|
|
89
|
+
item.validationWarnings || [],
|
|
90
|
+
'validationWarning',
|
|
91
|
+
'warningMessage'
|
|
92
|
+
);
|
|
93
|
+
this.handleValidation(
|
|
94
|
+
'propertyName',
|
|
95
|
+
item.validationErrors || [],
|
|
96
|
+
'validationError',
|
|
97
|
+
'errorMessage'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.loaded = loaded;
|
|
102
|
+
this.cdr.detectChanges();
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private listenFormChange() {
|
|
107
|
+
this.form.dataChange.pipe(takeUntil(this.destroyed$)).subscribe((item) => {
|
|
108
|
+
this.store.dispatch(
|
|
109
|
+
createUpdateDetailAction(this.detailsRepository)({ item })
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private listenFormStatusChange() {
|
|
115
|
+
this.form.statusChange
|
|
116
|
+
.pipe(takeUntil(this.destroyed$))
|
|
117
|
+
.subscribe((formState) => {
|
|
118
|
+
this.store.dispatch(
|
|
119
|
+
createUpdateFormStateAction(this.detailsRepository)({ formState })
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private handleValidation = (
|
|
125
|
+
controlName: string,
|
|
126
|
+
errors: any[],
|
|
127
|
+
errorKey: string,
|
|
128
|
+
messageKey: string
|
|
129
|
+
) => {
|
|
130
|
+
if (errors.length > 0) return;
|
|
131
|
+
errors.forEach((error: any) => {
|
|
132
|
+
const control = this.form.formGroup.get(error[controlName]);
|
|
133
|
+
if (control) {
|
|
134
|
+
control.disabled
|
|
135
|
+
? control.disable({ emitEvent: false, onlySelf: true })
|
|
136
|
+
: control.enable({ emitEvent: false, onlySelf: true });
|
|
137
|
+
control.setErrors(
|
|
138
|
+
{ [errorKey]: error[messageKey] },
|
|
139
|
+
{ emitEvent: true }
|
|
140
|
+
);
|
|
141
|
+
control.markAsDirty();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
this.cdr.markForCheck();
|
|
145
|
+
};
|
|
146
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface BaseFormCore {
|
|
2
|
+
label?: string;
|
|
3
|
+
required: boolean;
|
|
4
|
+
readonly: boolean;
|
|
5
|
+
tooltip: string;
|
|
6
|
+
formDisplay: 'flex' | 'block';
|
|
7
|
+
clearable: boolean;
|
|
8
|
+
placeholder?: string
|
|
9
|
+
testId?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface BaseFormDirectiveCore<T> {
|
|
13
|
+
initialData: T | any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface FormState {
|
|
17
|
+
dirty: boolean;
|
|
18
|
+
valid: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function isFormStateEqual(current: FormState, other: FormState) {
|
|
22
|
+
if (!current && !other) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (current && other) {
|
|
27
|
+
return current.dirty === other.dirty && current.valid === other.valid;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { ActivatedRoute } from "@angular/router";
|
|
3
|
+
import { MenuItem } from '../side-menu';
|
|
4
|
+
|
|
5
|
+
export const BREADCRUMB_COMPONENT_TOKEN = new InjectionToken<BreadcrumbCore>(
|
|
6
|
+
'BreadcrumbComponentToken'
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
export interface BreadcrumbCore {
|
|
10
|
+
items: MenuItem[];
|
|
11
|
+
homeRoute: string;
|
|
12
|
+
useHomeRoute: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Breadcrumb {
|
|
16
|
+
label: string;
|
|
17
|
+
routerLink?: string;
|
|
18
|
+
url?: string;
|
|
19
|
+
active?: boolean;
|
|
20
|
+
activatedRoute?: ActivatedRoute;
|
|
21
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { EventEmitter, Injectable, Output } from '@angular/core';
|
|
2
|
+
import { Breadcrumb } from './breadcrumb.model';
|
|
3
|
+
|
|
4
|
+
@Injectable({
|
|
5
|
+
providedIn: 'root',
|
|
6
|
+
})
|
|
7
|
+
export class BreadcrumbService {
|
|
8
|
+
@Output() routeChange: EventEmitter<Breadcrumb> = new EventEmitter();
|
|
9
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ChangeDetectorRef, Directive, Input, inject, OnInit } from '@angular/core';
|
|
2
|
+
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
|
3
|
+
import { filter, takeUntil } from 'rxjs';
|
|
4
|
+
import { MenuItem } from '../side-menu';
|
|
5
|
+
import { UnsubscribeComponent } from '../unsubscribe.component';
|
|
6
|
+
import { BreadcrumbCore } from './breadcrumb.model';
|
|
7
|
+
import { BreadcrumbService } from './breadcrumb.service';
|
|
8
|
+
|
|
9
|
+
@Directive({})
|
|
10
|
+
export class BreadcrumbCoreComponent
|
|
11
|
+
extends UnsubscribeComponent
|
|
12
|
+
implements BreadcrumbCore, OnInit
|
|
13
|
+
{
|
|
14
|
+
@Input() items: MenuItem[] = [];
|
|
15
|
+
@Input() homeRoute = '/';
|
|
16
|
+
@Input() useHomeRoute = false;
|
|
17
|
+
|
|
18
|
+
static readonly ROUTE_DATA_BREADCRUMB = 'breadcrumb';
|
|
19
|
+
static readonly ROUTE_DATA_BREADCRUMB_URL = 'breadcrumb_url';
|
|
20
|
+
readonly home = { icon: 'pi pi-home', routerLink: this.homeRoute };
|
|
21
|
+
|
|
22
|
+
router = inject(Router);
|
|
23
|
+
activatedRoute = inject(ActivatedRoute);
|
|
24
|
+
breadcrumbService = inject(BreadcrumbService);
|
|
25
|
+
cdr = inject(ChangeDetectorRef);
|
|
26
|
+
|
|
27
|
+
ngOnInit(): void {
|
|
28
|
+
this.initBreadcrumbsCreation();
|
|
29
|
+
this.initRouteChangeListen();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private initBreadcrumbsCreation() {
|
|
33
|
+
this.router.events
|
|
34
|
+
.pipe(filter((event) => event instanceof NavigationEnd))
|
|
35
|
+
.subscribe(
|
|
36
|
+
() => (this.items = this.createBreadcrumbs(this.activatedRoute.root))
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private initRouteChangeListen() {
|
|
41
|
+
this.breadcrumbService.routeChange
|
|
42
|
+
.pipe(
|
|
43
|
+
filter((x) => x.label !== 'Undefined'),
|
|
44
|
+
takeUntil(this.destroyed$)
|
|
45
|
+
)
|
|
46
|
+
.subscribe((x) => {
|
|
47
|
+
this.items = [
|
|
48
|
+
...this.items,
|
|
49
|
+
{
|
|
50
|
+
label: x.label,
|
|
51
|
+
routerLink: x.routerLink,
|
|
52
|
+
url: x.url,
|
|
53
|
+
class: 'breadcrumb',
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
this.cdr.detectChanges();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private createBreadcrumbs(
|
|
61
|
+
route: ActivatedRoute,
|
|
62
|
+
routerLink = '',
|
|
63
|
+
breadcrumbs: MenuItem[] = []
|
|
64
|
+
): any {
|
|
65
|
+
const children: ActivatedRoute[] = route.children;
|
|
66
|
+
|
|
67
|
+
if (children.length === 0) {
|
|
68
|
+
return breadcrumbs;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const child of children) {
|
|
72
|
+
const routeURL: string = child.snapshot.url
|
|
73
|
+
.map((segment) => segment.path)
|
|
74
|
+
.join('/');
|
|
75
|
+
|
|
76
|
+
if (BreadcrumbCoreComponent.ROUTE_DATA_BREADCRUMB_URL !== undefined) {
|
|
77
|
+
const route =
|
|
78
|
+
child.snapshot.data[BreadcrumbCoreComponent.ROUTE_DATA_BREADCRUMB_URL];
|
|
79
|
+
routerLink += `/${route}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!BreadcrumbCoreComponent.ROUTE_DATA_BREADCRUMB_URL && routeURL !== '') {
|
|
83
|
+
routerLink += `/${routeURL}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const label = child.snapshot.data[BreadcrumbCoreComponent.ROUTE_DATA_BREADCRUMB];
|
|
87
|
+
if (label !== undefined && label !== null) {
|
|
88
|
+
breadcrumbs.push({ label, routerLink: routerLink });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return this.createBreadcrumbs(child, routerLink, breadcrumbs);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { Params } from '@angular/router';
|
|
3
|
+
import { ControlSeverityType, FieldSizeType, IconPositionType } from '../../common';
|
|
4
|
+
|
|
5
|
+
export const BUTTON_COMPONENT_TOKEN = new InjectionToken<ButtonCore>(
|
|
6
|
+
'ButtonComponentToken'
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
export interface ButtonCore {
|
|
10
|
+
label?: string;
|
|
11
|
+
icon?: string;
|
|
12
|
+
badge?: string;
|
|
13
|
+
iconPos: IconPositionType;
|
|
14
|
+
rounded: boolean;
|
|
15
|
+
text: boolean;
|
|
16
|
+
outlined: boolean;
|
|
17
|
+
raised: boolean;
|
|
18
|
+
severity?: ControlSeverityType;
|
|
19
|
+
routerLink: any[];
|
|
20
|
+
size: FieldSizeType | undefined;
|
|
21
|
+
queryParams?: Params;
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './button.model';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { BaseFormCore } from '../base-form';
|
|
3
|
+
|
|
4
|
+
export const CALENDAR_COMPONENT_TOKEN = new InjectionToken<CalendarCore>(
|
|
5
|
+
'CalendarComponentToken'
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
export interface CalendarCore extends BaseFormCore {
|
|
9
|
+
maxDate: Date;
|
|
10
|
+
icon: string;
|
|
11
|
+
minDate: Date;
|
|
12
|
+
header: string;
|
|
13
|
+
footer: string;
|
|
14
|
+
floatLabel?: string;
|
|
15
|
+
selectionMode: "single" | "multiple" | "range" | undefined;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './calendar.model';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { BaseFormCore } from '../base-form';
|
|
3
|
+
|
|
4
|
+
export const CHECKBOX_COMPONENT_TOKEN = new InjectionToken<CheckboxCore>(
|
|
5
|
+
'CheckboxComponentToken'
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
9
|
+
export interface CheckboxCore extends BaseFormCore {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './checkbox.model';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { InjectionToken, Type } from '@angular/core';
|
|
2
|
+
import { SafeHtml } from "@angular/platform-browser";
|
|
3
|
+
import { ControlSeverityType } from '../../common';
|
|
4
|
+
|
|
5
|
+
export const CONFIRM_DIALOG_COMPONENT_TOKEN = new InjectionToken<ConfirmDialogCore>(
|
|
6
|
+
'ConfirmDialogComponentToken'
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
10
|
+
export interface ConfirmDialogCore {}
|
|
11
|
+
|
|
12
|
+
export interface DialogData {
|
|
13
|
+
title?: string;
|
|
14
|
+
headerIcon: string;
|
|
15
|
+
severity: ControlSeverityType;
|
|
16
|
+
showCancelButton?: boolean;
|
|
17
|
+
buttonOrder?: 'confirm-cancel' | 'cancel-confirm';
|
|
18
|
+
confirmButtonText?: string;
|
|
19
|
+
confirmButtonFn?: () => void;
|
|
20
|
+
cancelButtonFn?: () => void;
|
|
21
|
+
cancelButtonText?: string;
|
|
22
|
+
innerHTML?: string | SafeHtml;
|
|
23
|
+
data?: unknown;
|
|
24
|
+
componentType?: Type<unknown>;
|
|
25
|
+
closable?: boolean;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './confirm-dialog.model';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { DataSourceComponentModel } from '../../common';
|
|
3
|
+
import { BaseFormCore } from '../base-form';
|
|
4
|
+
|
|
5
|
+
export const DROPDOWN_COMPONENT_TOKEN = new InjectionToken<DropdownCore<any>>(
|
|
6
|
+
'DropdownComponentToken'
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
export interface DropdownCore<T> extends DataSourceComponentModel<T>, BaseFormCore {
|
|
10
|
+
dropdownIcon?: string;
|
|
11
|
+
floatLabel?: string;
|
|
12
|
+
editable: boolean;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dropdown.model';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { NgControl } from '@angular/forms';
|
|
3
|
+
|
|
4
|
+
export const FORM_FIELD_COMPONENT_TOKEN = new InjectionToken<FormFieldCore>(
|
|
5
|
+
'FormFieldComponentToken'
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
export interface FormFieldCore {
|
|
9
|
+
ngControl?: NgControl;
|
|
10
|
+
label?: string;
|
|
11
|
+
tooltip?: string;
|
|
12
|
+
required: boolean;
|
|
13
|
+
testId?: string;
|
|
14
|
+
display: 'flex' | 'block';
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './form-field.model';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
import { DataSourceComponentModel } from '../../common';
|
|
3
|
+
import { BaseFormCore } from '../base-form';
|
|
4
|
+
|
|
5
|
+
export const GENERIC_FIELD_COMPONENT_TOKEN = new InjectionToken<GenericFieldCore<any>>(
|
|
6
|
+
'DropdownComponentToken'
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
export interface GenericFieldCore<T> extends DataSourceComponentModel<T>, BaseFormCore {
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './generic-field.model';
|