ngx-dynamic-entity 0.0.1 → 1.0.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/README.md +139 -0
- package/esm2022/lib/field-types/array-field.component.mjs +160 -0
- package/esm2022/lib/field-types/boolean-field.component.mjs +100 -0
- package/esm2022/lib/field-types/checkbox-field.component.mjs +49 -41
- package/esm2022/lib/field-types/currency-field.component.mjs +101 -0
- package/esm2022/lib/field-types/date-field.component.mjs +47 -39
- package/esm2022/lib/field-types/dropdown-field.component.mjs +145 -54
- package/esm2022/lib/field-types/email-field.component.mjs +101 -0
- package/esm2022/lib/field-types/entity-ref-field.component.mjs +134 -70
- package/esm2022/lib/field-types/file-field.component.mjs +156 -0
- package/esm2022/lib/field-types/group-field.component.mjs +89 -0
- package/esm2022/lib/field-types/image-field.component.mjs +168 -0
- package/esm2022/lib/field-types/month-year-field.component.mjs +151 -0
- package/esm2022/lib/field-types/multi-select-field.component.mjs +110 -56
- package/esm2022/lib/field-types/number-field.component.mjs +84 -41
- package/esm2022/lib/field-types/password-field.component.mjs +109 -0
- package/esm2022/lib/field-types/radio-field.component.mjs +152 -0
- package/esm2022/lib/field-types/text-field.component.mjs +82 -42
- package/esm2022/lib/field-types/textarea-field.component.mjs +52 -41
- package/esm2022/lib/form/dynamic-field/dynamic-field.component.mjs +44 -26
- package/esm2022/lib/form/dynamic-form.component.mjs +501 -67
- package/esm2022/lib/form/dynamic-record-form.component.mjs +469 -0
- package/esm2022/lib/providers/provide-field-types.mjs +72 -0
- package/esm2022/lib/providers/provide-ngx-dynamic-entity.mjs +3 -5
- package/esm2022/lib/services/cascade-data.service.mjs +129 -0
- package/esm2022/lib/services/config-source.service.mjs +67 -0
- package/esm2022/lib/services/entity-ref-cache.mjs +20 -0
- package/esm2022/lib/services/entity-ref-registry.service.mjs +3 -3
- package/esm2022/lib/services/entity-ref-selection.service.mjs +27 -0
- package/esm2022/lib/services/entity-reference.service.mjs +168 -0
- package/esm2022/lib/services/field-registry.service.mjs +35 -30
- package/esm2022/lib/services/file-upload.service.mjs +89 -0
- package/esm2022/lib/services/lookup-registry.service.mjs +246 -0
- package/esm2022/lib/services/rbac.service.mjs +5 -11
- package/esm2022/lib/services/rules-evaluation.service.mjs +27 -0
- package/esm2022/lib/services/validator-registry.service.mjs +24 -7
- package/esm2022/lib/tokens/injection-tokens.mjs +88 -7
- package/esm2022/public-api.mjs +14 -5
- package/fesm2022/ngx-dynamic-entity.mjs +3985 -876
- package/fesm2022/ngx-dynamic-entity.mjs.map +1 -1
- package/lib/field-types/array-field.component.d.ts +22 -0
- package/lib/field-types/boolean-field.component.d.ts +18 -0
- package/lib/field-types/checkbox-field.component.d.ts +3 -2
- package/lib/field-types/currency-field.component.d.ts +17 -0
- package/lib/field-types/date-field.component.d.ts +3 -2
- package/lib/field-types/dropdown-field.component.d.ts +24 -4
- package/lib/field-types/email-field.component.d.ts +15 -0
- package/lib/field-types/entity-ref-field.component.d.ts +24 -11
- package/lib/field-types/file-field.component.d.ts +25 -0
- package/lib/field-types/group-field.component.d.ts +17 -0
- package/lib/field-types/image-field.component.d.ts +30 -0
- package/lib/field-types/month-year-field.component.d.ts +28 -0
- package/lib/field-types/multi-select-field.component.d.ts +18 -4
- package/lib/field-types/number-field.component.d.ts +5 -2
- package/lib/field-types/password-field.component.d.ts +17 -0
- package/lib/field-types/radio-field.component.d.ts +33 -0
- package/lib/field-types/text-field.component.d.ts +5 -3
- package/lib/field-types/textarea-field.component.d.ts +4 -2
- package/lib/form/dynamic-field/dynamic-field.component.d.ts +20 -7
- package/lib/form/dynamic-form.component.d.ts +111 -19
- package/lib/form/dynamic-record-form.component.d.ts +169 -0
- package/lib/providers/provide-field-types.d.ts +24 -0
- package/lib/providers/provide-ngx-dynamic-entity.d.ts +12 -7
- package/lib/services/cascade-data.service.d.ts +45 -0
- package/lib/services/config-source.service.d.ts +14 -0
- package/lib/services/entity-ref-cache.d.ts +27 -0
- package/lib/services/entity-ref-registry.service.d.ts +4 -3
- package/lib/services/entity-ref-selection.service.d.ts +24 -0
- package/lib/services/entity-reference.service.d.ts +49 -0
- package/lib/services/field-registry.service.d.ts +17 -7
- package/lib/services/file-upload.service.d.ts +27 -0
- package/lib/services/lookup-registry.service.d.ts +94 -0
- package/lib/services/rbac.service.d.ts +4 -9
- package/lib/services/rules-evaluation.service.d.ts +17 -0
- package/lib/services/validator-registry.service.d.ts +5 -4
- package/lib/tokens/injection-tokens.d.ts +90 -7
- package/package.json +14 -3
- package/public-api.d.ts +11 -4
- package/esm2022/lib/services/config.service.mjs +0 -58
- package/esm2022/lib/services/version.service.mjs +0 -40
- package/esm2022/lib/table/dynamic-table.component.mjs +0 -160
- package/lib/services/config.service.d.ts +0 -25
- package/lib/services/version.service.d.ts +0 -26
- package/lib/table/dynamic-table.component.d.ts +0 -56
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Component, Input } from '@angular/core';
|
|
2
2
|
import { ReactiveFormsModule } from '@angular/forms';
|
|
3
|
+
import { resolveLabel } from '@dynamic-entity/core';
|
|
3
4
|
import * as i0 from "@angular/core";
|
|
4
5
|
import * as i1 from "@angular/forms";
|
|
5
6
|
export class NumberFieldComponent {
|
|
@@ -8,27 +9,57 @@ export class NumberFieldComponent {
|
|
|
8
9
|
language = 'en';
|
|
9
10
|
readonly = false;
|
|
10
11
|
masked = false;
|
|
12
|
+
get label() {
|
|
13
|
+
return resolveLabel(this.field?.label, this.language);
|
|
14
|
+
}
|
|
15
|
+
get placeholder() {
|
|
16
|
+
return resolveLabel(this.field?.placeholder, this.language);
|
|
17
|
+
}
|
|
18
|
+
get errorMessage() {
|
|
19
|
+
if (!this.control || !this.control.errors || !this.control.touched)
|
|
20
|
+
return '';
|
|
21
|
+
const errs = this.control.errors;
|
|
22
|
+
if (errs['required'])
|
|
23
|
+
return 'This field is required.';
|
|
24
|
+
if (errs['min'])
|
|
25
|
+
return `Value must be at least ${errs['min'].min}.`;
|
|
26
|
+
if (errs['max'])
|
|
27
|
+
return `Value must not exceed ${errs['max'].max}.`;
|
|
28
|
+
return 'Invalid number.';
|
|
29
|
+
}
|
|
11
30
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NumberFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: NumberFieldComponent, isStandalone: true, selector: "ngx-number-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
13
|
-
<div
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: NumberFieldComponent, isStandalone: true, selector: "ngx-number-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
32
|
+
<div
|
|
33
|
+
class="ngx-field ngx-field--number"
|
|
34
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type"
|
|
35
|
+
[class.ngx-field--readonly]="readonly"
|
|
36
|
+
[class.ngx-field--masked]="masked"
|
|
37
|
+
[class.ngx-field--invalid]="control && control.invalid && control.touched"
|
|
38
|
+
>
|
|
39
|
+
<label class="ngx-field__label" [attr.for]="field.id">
|
|
40
|
+
{{ label }}
|
|
41
|
+
@if (field.validators?.required) { <span class="ngx-field__req">*</span> }
|
|
42
|
+
</label>
|
|
43
|
+
@if (masked) {
|
|
44
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
45
|
+
} @else if (readonly) {
|
|
46
|
+
<span class="ngx-field__value" [attr.data-testid]="'field-' + field.id + '-value'">{{ control.value ?? '—' }}</span>
|
|
47
|
+
} @else {
|
|
48
|
+
<input
|
|
49
|
+
[id]="field.id"
|
|
50
|
+
type="number"
|
|
51
|
+
class="ngx-field__input" [attr.data-testid]="'field-' + field.id + '-input'"
|
|
52
|
+
[formControl]="$any(control)"
|
|
53
|
+
[placeholder]="placeholder"
|
|
54
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
55
|
+
[attr.aria-invalid]="control.invalid && control.touched"
|
|
56
|
+
[attr.aria-describedby]="errorMessage ? field.id + '-error' : null"
|
|
57
|
+
/>
|
|
58
|
+
@if (errorMessage) {
|
|
59
|
+
<span class="ngx-field__error" [attr.data-testid]="'field-' + field.id + '-error'" [id]="field.id + '-error'" role="alert">{{ errorMessage }}</span>
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
</div>
|
|
32
63
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
|
|
33
64
|
}
|
|
34
65
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: NumberFieldComponent, decorators: [{
|
|
@@ -37,26 +68,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
37
68
|
selector: 'ngx-number-field',
|
|
38
69
|
standalone: true,
|
|
39
70
|
imports: [ReactiveFormsModule],
|
|
40
|
-
template: `
|
|
41
|
-
<div
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
71
|
+
template: `
|
|
72
|
+
<div
|
|
73
|
+
class="ngx-field ngx-field--number"
|
|
74
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type"
|
|
75
|
+
[class.ngx-field--readonly]="readonly"
|
|
76
|
+
[class.ngx-field--masked]="masked"
|
|
77
|
+
[class.ngx-field--invalid]="control && control.invalid && control.touched"
|
|
78
|
+
>
|
|
79
|
+
<label class="ngx-field__label" [attr.for]="field.id">
|
|
80
|
+
{{ label }}
|
|
81
|
+
@if (field.validators?.required) { <span class="ngx-field__req">*</span> }
|
|
82
|
+
</label>
|
|
83
|
+
@if (masked) {
|
|
84
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
85
|
+
} @else if (readonly) {
|
|
86
|
+
<span class="ngx-field__value" [attr.data-testid]="'field-' + field.id + '-value'">{{ control.value ?? '—' }}</span>
|
|
87
|
+
} @else {
|
|
88
|
+
<input
|
|
89
|
+
[id]="field.id"
|
|
90
|
+
type="number"
|
|
91
|
+
class="ngx-field__input" [attr.data-testid]="'field-' + field.id + '-input'"
|
|
92
|
+
[formControl]="$any(control)"
|
|
93
|
+
[placeholder]="placeholder"
|
|
94
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
95
|
+
[attr.aria-invalid]="control.invalid && control.touched"
|
|
96
|
+
[attr.aria-describedby]="errorMessage ? field.id + '-error' : null"
|
|
97
|
+
/>
|
|
98
|
+
@if (errorMessage) {
|
|
99
|
+
<span class="ngx-field__error" [attr.data-testid]="'field-' + field.id + '-error'" [id]="field.id + '-error'" role="alert">{{ errorMessage }}</span>
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
</div>
|
|
60
103
|
`,
|
|
61
104
|
}]
|
|
62
105
|
}], propDecorators: { field: [{
|
|
@@ -70,4 +113,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
70
113
|
}], masked: [{
|
|
71
114
|
type: Input
|
|
72
115
|
}] } });
|
|
73
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
116
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibnVtYmVyLWZpZWxkLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvZmllbGQtdHlwZXMvbnVtYmVyLWZpZWxkLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNqRCxPQUFPLEVBQW1CLG1CQUFtQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFdEUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLHNCQUFzQixDQUFDOzs7QUF3Q3BELE1BQU0sT0FBTyxvQkFBb0I7SUFDdEIsS0FBSyxDQUFxQjtJQUMxQixPQUFPLENBQW1CO0lBQzFCLFFBQVEsR0FBVyxJQUFJLENBQUM7SUFDeEIsUUFBUSxHQUFZLEtBQUssQ0FBQztJQUMxQixNQUFNLEdBQVksS0FBSyxDQUFDO0lBRWpDLElBQUksS0FBSztRQUNQLE9BQU8sWUFBWSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUN4RCxDQUFDO0lBRUQsSUFBSSxXQUFXO1FBQ2IsT0FBTyxZQUFZLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxXQUFXLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQzlELENBQUM7SUFFRCxJQUFJLFlBQVk7UUFDZCxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPO1lBQUUsT0FBTyxFQUFFLENBQUM7UUFDOUUsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUM7UUFDakMsSUFBSSxJQUFJLENBQUMsVUFBVSxDQUFDO1lBQUUsT0FBTyx5QkFBeUIsQ0FBQztRQUN2RCxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUM7WUFBRSxPQUFPLDBCQUEwQixJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUM7UUFDckUsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDO1lBQUUsT0FBTyx5QkFBeUIsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsR0FBRyxDQUFDO1FBQ3BFLE9BQU8saUJBQWlCLENBQUM7SUFDM0IsQ0FBQzt3R0F0QlUsb0JBQW9COzRGQUFwQixvQkFBb0IsMExBbENyQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQ1QsMkRBakNTLG1CQUFtQjs7NEZBbUNsQixvQkFBb0I7a0JBdENoQyxTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSxrQkFBa0I7b0JBQzVCLFVBQVUsRUFBRSxJQUFJO29CQUNoQixPQUFPLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQztvQkFDOUIsUUFBUSxFQUFFOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWdDVDtpQkFDRjs4QkFFVSxLQUFLO3NCQUFiLEtBQUs7Z0JBQ0csT0FBTztzQkFBZixLQUFLO2dCQUNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csUUFBUTtzQkFBaEIsS0FBSztnQkFDRyxNQUFNO3NCQUFkLEtBQUsiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIElucHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IEFic3RyYWN0Q29udHJvbCwgUmVhY3RpdmVGb3Jtc01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcclxuaW1wb3J0IHR5cGUgeyBOZXN0ZWRGaWVsZENvbmZpZyB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcclxuaW1wb3J0IHsgcmVzb2x2ZUxhYmVsIH0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xyXG5cclxuQENvbXBvbmVudCh7XHJcbiAgc2VsZWN0b3I6ICduZ3gtbnVtYmVyLWZpZWxkJyxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtSZWFjdGl2ZUZvcm1zTW9kdWxlXSxcclxuICB0ZW1wbGF0ZTogYFxyXG4gICAgPGRpdlxyXG4gICAgICBjbGFzcz1cIm5neC1maWVsZCBuZ3gtZmllbGQtLW51bWJlclwiXHJcbiAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidmaWVsZC0nICsgZmllbGQuaWRcIiBbYXR0ci5kYXRhLWZpZWxkLXR5cGVdPVwiZmllbGQudHlwZVwiXHJcbiAgICAgIFtjbGFzcy5uZ3gtZmllbGQtLXJlYWRvbmx5XT1cInJlYWRvbmx5XCJcclxuICAgICAgW2NsYXNzLm5neC1maWVsZC0tbWFza2VkXT1cIm1hc2tlZFwiXHJcbiAgICAgIFtjbGFzcy5uZ3gtZmllbGQtLWludmFsaWRdPVwiY29udHJvbCAmJiBjb250cm9sLmludmFsaWQgJiYgY29udHJvbC50b3VjaGVkXCJcclxuICAgID5cclxuICAgICAgPGxhYmVsIGNsYXNzPVwibmd4LWZpZWxkX19sYWJlbFwiIFthdHRyLmZvcl09XCJmaWVsZC5pZFwiPlxyXG4gICAgICAgIHt7IGxhYmVsIH19XHJcbiAgICAgICAgQGlmIChmaWVsZC52YWxpZGF0b3JzPy5yZXF1aXJlZCkgeyA8c3BhbiBjbGFzcz1cIm5neC1maWVsZF9fcmVxXCI+Kjwvc3Bhbj4gfVxyXG4gICAgICA8L2xhYmVsPlxyXG4gICAgICBAaWYgKG1hc2tlZCkge1xyXG4gICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LWZpZWxkX192YWx1ZSBuZ3gtZmllbGRfX3ZhbHVlLS1tYXNrZWRcIiBbYXR0ci5kYXRhLXRlc3RpZF09XCInZmllbGQtJyArIGZpZWxkLmlkICsgJy1tYXNrZWQnXCI+WFhYWFhYWFhYPC9zcGFuPlxyXG4gICAgICB9IEBlbHNlIGlmIChyZWFkb25seSkge1xyXG4gICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LWZpZWxkX192YWx1ZVwiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidmaWVsZC0nICsgZmllbGQuaWQgKyAnLXZhbHVlJ1wiPnt7IGNvbnRyb2wudmFsdWUgPz8gJ+KAlCcgfX08L3NwYW4+XHJcbiAgICAgIH0gQGVsc2Uge1xyXG4gICAgICAgIDxpbnB1dFxyXG4gICAgICAgICAgW2lkXT1cImZpZWxkLmlkXCJcclxuICAgICAgICAgIHR5cGU9XCJudW1iZXJcIlxyXG4gICAgICAgICAgY2xhc3M9XCJuZ3gtZmllbGRfX2lucHV0XCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZCArICctaW5wdXQnXCJcclxuICAgICAgICAgIFtmb3JtQ29udHJvbF09XCIkYW55KGNvbnRyb2wpXCJcclxuICAgICAgICAgIFtwbGFjZWhvbGRlcl09XCJwbGFjZWhvbGRlclwiXHJcbiAgICAgICAgICBbYXR0ci5kaXNhYmxlZF09XCJmaWVsZC5kaXNhYmxlZCA/IHRydWUgOiBudWxsXCJcclxuICAgICAgICAgIFthdHRyLmFyaWEtaW52YWxpZF09XCJjb250cm9sLmludmFsaWQgJiYgY29udHJvbC50b3VjaGVkXCJcclxuICAgICAgICAgIFthdHRyLmFyaWEtZGVzY3JpYmVkYnldPVwiZXJyb3JNZXNzYWdlID8gZmllbGQuaWQgKyAnLWVycm9yJyA6IG51bGxcIlxyXG4gICAgICAgIC8+XHJcbiAgICAgICAgQGlmIChlcnJvck1lc3NhZ2UpIHtcclxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LWZpZWxkX19lcnJvclwiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidmaWVsZC0nICsgZmllbGQuaWQgKyAnLWVycm9yJ1wiIFtpZF09XCJmaWVsZC5pZCArICctZXJyb3InXCIgcm9sZT1cImFsZXJ0XCI+e3sgZXJyb3JNZXNzYWdlIH19PC9zcGFuPlxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgPC9kaXY+XHJcbiAgYCxcclxufSlcclxuZXhwb3J0IGNsYXNzIE51bWJlckZpZWxkQ29tcG9uZW50IHtcclxuICBASW5wdXQoKSBmaWVsZCE6IE5lc3RlZEZpZWxkQ29uZmlnO1xyXG4gIEBJbnB1dCgpIGNvbnRyb2whOiBBYnN0cmFjdENvbnRyb2w7XHJcbiAgQElucHV0KCkgbGFuZ3VhZ2U6IHN0cmluZyA9ICdlbic7XHJcbiAgQElucHV0KCkgcmVhZG9ubHk6IGJvb2xlYW4gPSBmYWxzZTtcclxuICBASW5wdXQoKSBtYXNrZWQ6IGJvb2xlYW4gPSBmYWxzZTtcclxuXHJcbiAgZ2V0IGxhYmVsKCk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gcmVzb2x2ZUxhYmVsKHRoaXMuZmllbGQ/LmxhYmVsLCB0aGlzLmxhbmd1YWdlKTtcclxuICB9XHJcblxyXG4gIGdldCBwbGFjZWhvbGRlcigpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIHJlc29sdmVMYWJlbCh0aGlzLmZpZWxkPy5wbGFjZWhvbGRlciwgdGhpcy5sYW5ndWFnZSk7XHJcbiAgfVxyXG5cclxuICBnZXQgZXJyb3JNZXNzYWdlKCk6IHN0cmluZyB7XHJcbiAgICBpZiAoIXRoaXMuY29udHJvbCB8fCAhdGhpcy5jb250cm9sLmVycm9ycyB8fCAhdGhpcy5jb250cm9sLnRvdWNoZWQpIHJldHVybiAnJztcclxuICAgIGNvbnN0IGVycnMgPSB0aGlzLmNvbnRyb2wuZXJyb3JzO1xyXG4gICAgaWYgKGVycnNbJ3JlcXVpcmVkJ10pIHJldHVybiAnVGhpcyBmaWVsZCBpcyByZXF1aXJlZC4nO1xyXG4gICAgaWYgKGVycnNbJ21pbiddKSByZXR1cm4gYFZhbHVlIG11c3QgYmUgYXQgbGVhc3QgJHtlcnJzWydtaW4nXS5taW59LmA7XHJcbiAgICBpZiAoZXJyc1snbWF4J10pIHJldHVybiBgVmFsdWUgbXVzdCBub3QgZXhjZWVkICR7ZXJyc1snbWF4J10ubWF4fS5gO1xyXG4gICAgcmV0dXJuICdJbnZhbGlkIG51bWJlci4nO1xyXG4gIH1cclxufVxyXG4iXX0=
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Component, Input, signal } from '@angular/core';
|
|
2
|
+
import { ReactiveFormsModule } from '@angular/forms';
|
|
3
|
+
import { resolveLabel } from '@dynamic-entity/core';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
import * as i1 from "@angular/forms";
|
|
6
|
+
/** Password field: masked input with show/hide eye toggle. */
|
|
7
|
+
export class PasswordFieldComponent {
|
|
8
|
+
field;
|
|
9
|
+
control;
|
|
10
|
+
language = 'en';
|
|
11
|
+
readonly = false;
|
|
12
|
+
masked = false;
|
|
13
|
+
visible = signal(false);
|
|
14
|
+
get label() {
|
|
15
|
+
return resolveLabel(this.field?.label, this.language);
|
|
16
|
+
}
|
|
17
|
+
get placeholder() {
|
|
18
|
+
return resolveLabel(this.field?.placeholder, this.language);
|
|
19
|
+
}
|
|
20
|
+
toggleVisibility() {
|
|
21
|
+
this.visible.update(v => !v);
|
|
22
|
+
}
|
|
23
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PasswordFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
24
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: PasswordFieldComponent, isStandalone: true, selector: "ngx-password-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
25
|
+
<div class="ngx-field ngx-field--password"
|
|
26
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
27
|
+
<label class="ngx-field__label">{{ label }}</label>
|
|
28
|
+
@if (masked) {
|
|
29
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
30
|
+
} @else if (readonly) {
|
|
31
|
+
<span class="ngx-field__value" [attr.data-testid]="'field-' + field.id + '-value'">••••••••</span>
|
|
32
|
+
} @else {
|
|
33
|
+
<div class="ngx-field__password-wrap">
|
|
34
|
+
<input
|
|
35
|
+
class="ngx-field__input" [attr.data-testid]="'field-' + field.id + '-input'"
|
|
36
|
+
[type]="visible() ? 'text' : 'password'"
|
|
37
|
+
autocomplete="current-password"
|
|
38
|
+
[formControl]="$any(control)"
|
|
39
|
+
[placeholder]="placeholder || '••••••••'"
|
|
40
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
41
|
+
/>
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
class="ngx-field__eye-btn"
|
|
45
|
+
(click)="toggleVisibility()"
|
|
46
|
+
[attr.aria-label]="visible() ? 'Hide password' : 'Show password'"
|
|
47
|
+
>
|
|
48
|
+
{{ visible() ? '🙈' : '👁' }}
|
|
49
|
+
</button>
|
|
50
|
+
</div>
|
|
51
|
+
@if (control.invalid && control.touched) {
|
|
52
|
+
<span class="ngx-field__error" [attr.data-testid]="'field-' + field.id + '-error'">This field has an error</span>
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
</div>
|
|
56
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
|
|
57
|
+
}
|
|
58
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PasswordFieldComponent, decorators: [{
|
|
59
|
+
type: Component,
|
|
60
|
+
args: [{
|
|
61
|
+
selector: 'ngx-password-field',
|
|
62
|
+
standalone: true,
|
|
63
|
+
imports: [ReactiveFormsModule],
|
|
64
|
+
template: `
|
|
65
|
+
<div class="ngx-field ngx-field--password"
|
|
66
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
67
|
+
<label class="ngx-field__label">{{ label }}</label>
|
|
68
|
+
@if (masked) {
|
|
69
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
70
|
+
} @else if (readonly) {
|
|
71
|
+
<span class="ngx-field__value" [attr.data-testid]="'field-' + field.id + '-value'">••••••••</span>
|
|
72
|
+
} @else {
|
|
73
|
+
<div class="ngx-field__password-wrap">
|
|
74
|
+
<input
|
|
75
|
+
class="ngx-field__input" [attr.data-testid]="'field-' + field.id + '-input'"
|
|
76
|
+
[type]="visible() ? 'text' : 'password'"
|
|
77
|
+
autocomplete="current-password"
|
|
78
|
+
[formControl]="$any(control)"
|
|
79
|
+
[placeholder]="placeholder || '••••••••'"
|
|
80
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
81
|
+
/>
|
|
82
|
+
<button
|
|
83
|
+
type="button"
|
|
84
|
+
class="ngx-field__eye-btn"
|
|
85
|
+
(click)="toggleVisibility()"
|
|
86
|
+
[attr.aria-label]="visible() ? 'Hide password' : 'Show password'"
|
|
87
|
+
>
|
|
88
|
+
{{ visible() ? '🙈' : '👁' }}
|
|
89
|
+
</button>
|
|
90
|
+
</div>
|
|
91
|
+
@if (control.invalid && control.touched) {
|
|
92
|
+
<span class="ngx-field__error" [attr.data-testid]="'field-' + field.id + '-error'">This field has an error</span>
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
</div>
|
|
96
|
+
`,
|
|
97
|
+
}]
|
|
98
|
+
}], propDecorators: { field: [{
|
|
99
|
+
type: Input
|
|
100
|
+
}], control: [{
|
|
101
|
+
type: Input
|
|
102
|
+
}], language: [{
|
|
103
|
+
type: Input
|
|
104
|
+
}], readonly: [{
|
|
105
|
+
type: Input
|
|
106
|
+
}], masked: [{
|
|
107
|
+
type: Input
|
|
108
|
+
}] } });
|
|
109
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFzc3dvcmQtZmllbGQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9maWVsZC10eXBlcy9wYXNzd29yZC1maWVsZC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3pELE9BQU8sRUFBbUIsbUJBQW1CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV0RSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7OztBQUVwRCw4REFBOEQ7QUF1QzlELE1BQU0sT0FBTyxzQkFBc0I7SUFDeEIsS0FBSyxDQUFxQjtJQUMxQixPQUFPLENBQW1CO0lBQzFCLFFBQVEsR0FBVyxJQUFJLENBQUM7SUFDeEIsUUFBUSxHQUFZLEtBQUssQ0FBQztJQUMxQixNQUFNLEdBQVksS0FBSyxDQUFDO0lBRXhCLE9BQU8sR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFakMsSUFBSSxLQUFLO1FBQ1AsT0FBTyxZQUFZLENBQUMsSUFBSSxDQUFDLEtBQUssRUFBRSxLQUFLLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3hELENBQUM7SUFFRCxJQUFJLFdBQVc7UUFDYixPQUFPLFlBQVksQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLFdBQVcsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDOUQsQ0FBQztJQUVELGdCQUFnQjtRQUNkLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMvQixDQUFDO3dHQW5CVSxzQkFBc0I7NEZBQXRCLHNCQUFzQiw0TEFsQ3ZCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQWdDVCwyREFqQ1MsbUJBQW1COzs0RkFtQ2xCLHNCQUFzQjtrQkF0Q2xDLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLG9CQUFvQjtvQkFDOUIsVUFBVSxFQUFFLElBQUk7b0JBQ2hCLE9BQU8sRUFBRSxDQUFDLG1CQUFtQixDQUFDO29CQUM5QixRQUFRLEVBQUU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBZ0NUO2lCQUNGOzhCQUVVLEtBQUs7c0JBQWIsS0FBSztnQkFDRyxPQUFPO3NCQUFmLEtBQUs7Z0JBQ0csUUFBUTtzQkFBaEIsS0FBSztnQkFDRyxRQUFRO3NCQUFoQixLQUFLO2dCQUNHLE1BQU07c0JBQWQsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgSW5wdXQsIHNpZ25hbCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xyXG5pbXBvcnQgeyBBYnN0cmFjdENvbnRyb2wsIFJlYWN0aXZlRm9ybXNNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XHJcbmltcG9ydCB0eXBlIHsgTmVzdGVkRmllbGRDb25maWcgfSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XHJcbmltcG9ydCB7IHJlc29sdmVMYWJlbCB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcclxuXHJcbi8qKiBQYXNzd29yZCBmaWVsZDogbWFza2VkIGlucHV0IHdpdGggc2hvdy9oaWRlIGV5ZSB0b2dnbGUuICovXHJcbkBDb21wb25lbnQoe1xyXG4gIHNlbGVjdG9yOiAnbmd4LXBhc3N3b3JkLWZpZWxkJyxcclxuICBzdGFuZGFsb25lOiB0cnVlLFxyXG4gIGltcG9ydHM6IFtSZWFjdGl2ZUZvcm1zTW9kdWxlXSxcclxuICB0ZW1wbGF0ZTogYFxyXG4gICAgPGRpdiBjbGFzcz1cIm5neC1maWVsZCBuZ3gtZmllbGQtLXBhc3N3b3JkXCJcclxuICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZFwiIFthdHRyLmRhdGEtZmllbGQtdHlwZV09XCJmaWVsZC50eXBlXCIgW2NsYXNzLm5neC1maWVsZC0tcmVhZG9ubHldPVwicmVhZG9ubHlcIiBbY2xhc3Mubmd4LWZpZWxkLS1tYXNrZWRdPVwibWFza2VkXCI+XHJcbiAgICAgIDxsYWJlbCBjbGFzcz1cIm5neC1maWVsZF9fbGFiZWxcIj57eyBsYWJlbCB9fTwvbGFiZWw+XHJcbiAgICAgIEBpZiAobWFza2VkKSB7XHJcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtZmllbGRfX3ZhbHVlIG5neC1maWVsZF9fdmFsdWUtLW1hc2tlZFwiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidmaWVsZC0nICsgZmllbGQuaWQgKyAnLW1hc2tlZCdcIj5YWFhYWFhYWFg8L3NwYW4+XHJcbiAgICAgIH0gQGVsc2UgaWYgKHJlYWRvbmx5KSB7XHJcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtZmllbGRfX3ZhbHVlXCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZCArICctdmFsdWUnXCI+4oCi4oCi4oCi4oCi4oCi4oCi4oCi4oCiPC9zcGFuPlxyXG4gICAgICB9IEBlbHNlIHtcclxuICAgICAgICA8ZGl2IGNsYXNzPVwibmd4LWZpZWxkX19wYXNzd29yZC13cmFwXCI+XHJcbiAgICAgICAgICA8aW5wdXRcclxuICAgICAgICAgICAgY2xhc3M9XCJuZ3gtZmllbGRfX2lucHV0XCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZCArICctaW5wdXQnXCJcclxuICAgICAgICAgICAgW3R5cGVdPVwidmlzaWJsZSgpID8gJ3RleHQnIDogJ3Bhc3N3b3JkJ1wiXHJcbiAgICAgICAgICAgIGF1dG9jb21wbGV0ZT1cImN1cnJlbnQtcGFzc3dvcmRcIlxyXG4gICAgICAgICAgICBbZm9ybUNvbnRyb2xdPVwiJGFueShjb250cm9sKVwiXHJcbiAgICAgICAgICAgIFtwbGFjZWhvbGRlcl09XCJwbGFjZWhvbGRlciB8fCAn4oCi4oCi4oCi4oCi4oCi4oCi4oCi4oCiJ1wiXHJcbiAgICAgICAgICAgIFthdHRyLmRpc2FibGVkXT1cImZpZWxkLmRpc2FibGVkID8gdHJ1ZSA6IG51bGxcIlxyXG4gICAgICAgICAgLz5cclxuICAgICAgICAgIDxidXR0b25cclxuICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXHJcbiAgICAgICAgICAgIGNsYXNzPVwibmd4LWZpZWxkX19leWUtYnRuXCJcclxuICAgICAgICAgICAgKGNsaWNrKT1cInRvZ2dsZVZpc2liaWxpdHkoKVwiXHJcbiAgICAgICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwidmlzaWJsZSgpID8gJ0hpZGUgcGFzc3dvcmQnIDogJ1Nob3cgcGFzc3dvcmQnXCJcclxuICAgICAgICAgID5cclxuICAgICAgICAgICAge3sgdmlzaWJsZSgpID8gJ/CfmYgnIDogJ/CfkYEnIH19XHJcbiAgICAgICAgICA8L2J1dHRvbj5cclxuICAgICAgICA8L2Rpdj5cclxuICAgICAgICBAaWYgKGNvbnRyb2wuaW52YWxpZCAmJiBjb250cm9sLnRvdWNoZWQpIHtcclxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LWZpZWxkX19lcnJvclwiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidmaWVsZC0nICsgZmllbGQuaWQgKyAnLWVycm9yJ1wiPlRoaXMgZmllbGQgaGFzIGFuIGVycm9yPC9zcGFuPlxyXG4gICAgICAgIH1cclxuICAgICAgfVxyXG4gICAgPC9kaXY+XHJcbiAgYCxcclxufSlcclxuZXhwb3J0IGNsYXNzIFBhc3N3b3JkRmllbGRDb21wb25lbnQge1xyXG4gIEBJbnB1dCgpIGZpZWxkITogTmVzdGVkRmllbGRDb25maWc7XHJcbiAgQElucHV0KCkgY29udHJvbCE6IEFic3RyYWN0Q29udHJvbDtcclxuICBASW5wdXQoKSBsYW5ndWFnZTogc3RyaW5nID0gJ2VuJztcclxuICBASW5wdXQoKSByZWFkb25seTogYm9vbGVhbiA9IGZhbHNlO1xyXG4gIEBJbnB1dCgpIG1hc2tlZDogYm9vbGVhbiA9IGZhbHNlO1xyXG5cclxuICByZWFkb25seSB2aXNpYmxlID0gc2lnbmFsKGZhbHNlKTtcclxuXHJcbiAgZ2V0IGxhYmVsKCk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gcmVzb2x2ZUxhYmVsKHRoaXMuZmllbGQ/LmxhYmVsLCB0aGlzLmxhbmd1YWdlKTtcclxuICB9XHJcblxyXG4gIGdldCBwbGFjZWhvbGRlcigpOiBzdHJpbmcge1xyXG4gICAgcmV0dXJuIHJlc29sdmVMYWJlbCh0aGlzLmZpZWxkPy5wbGFjZWhvbGRlciwgdGhpcy5sYW5ndWFnZSk7XHJcbiAgfVxyXG5cclxuICB0b2dnbGVWaXNpYmlsaXR5KCk6IHZvaWQge1xyXG4gICAgdGhpcy52aXNpYmxlLnVwZGF0ZSh2ID0+ICF2KTtcclxuICB9XHJcbn1cclxuIl19
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { Component, Input, inject, signal } from '@angular/core';
|
|
2
|
+
import { ReactiveFormsModule } from '@angular/forms';
|
|
3
|
+
import { getOptionStoredValue, resolveLabel, resolveOptionLabel, resolveOptionValue, valuesMatch, } from '@dynamic-entity/core';
|
|
4
|
+
import { LookupRegistryService, refreshChoiceOptions } from '../services/lookup-registry.service';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
import * as i1 from "@angular/forms";
|
|
7
|
+
/** Radio field: a group of radio buttons built from field.options. */
|
|
8
|
+
export class RadioFieldComponent {
|
|
9
|
+
lookups = inject(LookupRegistryService);
|
|
10
|
+
_field;
|
|
11
|
+
_language = 'en';
|
|
12
|
+
/** Setter-based so options resolve however the input is set — see `refreshChoiceOptions`. */
|
|
13
|
+
set field(value) {
|
|
14
|
+
this._field = value;
|
|
15
|
+
refreshChoiceOptions(this, this.lookups);
|
|
16
|
+
}
|
|
17
|
+
get field() {
|
|
18
|
+
return this._field;
|
|
19
|
+
}
|
|
20
|
+
set language(value) {
|
|
21
|
+
this._language = value || 'en';
|
|
22
|
+
refreshChoiceOptions(this, this.lookups);
|
|
23
|
+
}
|
|
24
|
+
get language() {
|
|
25
|
+
return this._language;
|
|
26
|
+
}
|
|
27
|
+
control;
|
|
28
|
+
readonly = false;
|
|
29
|
+
masked = false;
|
|
30
|
+
/** Inline `options`, or the field's named list resolved through the registry (§6.3). */
|
|
31
|
+
options = signal([]);
|
|
32
|
+
get label() {
|
|
33
|
+
return resolveLabel(this.field?.label, this.language);
|
|
34
|
+
}
|
|
35
|
+
getOptStoredVal(option) {
|
|
36
|
+
return getOptionStoredValue(option);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Stable DOM id for one radio, so the <label for> association works.
|
|
40
|
+
* Slugified because the option text is the value now — "On Leave" must not put a space
|
|
41
|
+
* into an id attribute.
|
|
42
|
+
*/
|
|
43
|
+
getRadioId(option) {
|
|
44
|
+
const text = String(this.getOptVal(option) ?? 'opt');
|
|
45
|
+
return `${this.field.id}-${text.trim().replace(/\s+/g, '_').toLowerCase()}`;
|
|
46
|
+
}
|
|
47
|
+
getOptVal(option) {
|
|
48
|
+
return resolveOptionValue(option, this.language);
|
|
49
|
+
}
|
|
50
|
+
getOptLabel(option) {
|
|
51
|
+
return resolveOptionLabel(option, this.language);
|
|
52
|
+
}
|
|
53
|
+
/** Read-only display — synchronous, per §6.2. See `DropdownFieldComponent.getLabel`. */
|
|
54
|
+
getSelectedLabel() {
|
|
55
|
+
const value = this.control?.value;
|
|
56
|
+
// `typeof null === 'object'`, so without this guard an empty value fell through to
|
|
57
|
+
// `resolveLabel(null)` and rendered as blank — dropdown and multiSelect both show an em dash.
|
|
58
|
+
if (value === null || value === undefined || value === '')
|
|
59
|
+
return '—';
|
|
60
|
+
const selected = this.options().find(o => valuesMatch(getOptionStoredValue(o), value, this.language));
|
|
61
|
+
if (selected)
|
|
62
|
+
return this.getOptLabel(selected);
|
|
63
|
+
const cached = this.lookups.labelFor(this.field?.listName, value, this.language);
|
|
64
|
+
if (cached)
|
|
65
|
+
return cached;
|
|
66
|
+
return typeof value === 'object' ? resolveLabel(value, this.language) : (value ?? '—');
|
|
67
|
+
}
|
|
68
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
69
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: RadioFieldComponent, isStandalone: true, selector: "ngx-radio-field", inputs: { field: "field", language: "language", control: "control", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
70
|
+
<div class="ngx-field ngx-field--radio"
|
|
71
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
72
|
+
<fieldset class="ngx-field__fieldset">
|
|
73
|
+
<legend class="ngx-field__label">{{ label }}</legend>
|
|
74
|
+
@if (masked) {
|
|
75
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
76
|
+
} @else if (readonly) {
|
|
77
|
+
<span class="ngx-field__value" [attr.data-testid]="'field-' + field.id + '-value'">{{ getSelectedLabel() }}</span>
|
|
78
|
+
} @else {
|
|
79
|
+
<div class="ngx-field__radio-group">
|
|
80
|
+
@for (option of options(); track getOptLabel(option)) {
|
|
81
|
+
<label class="ngx-field__radio-option" [attr.for]="getRadioId(option)">
|
|
82
|
+
<input
|
|
83
|
+
[id]="getRadioId(option)"
|
|
84
|
+
type="radio"
|
|
85
|
+
class="ngx-field__radio-input"
|
|
86
|
+
[formControl]="$any(control)"
|
|
87
|
+
[value]="getOptStoredVal(option)"
|
|
88
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
89
|
+
/>
|
|
90
|
+
<span class="ngx-field__radio-label">{{ getOptLabel(option) }}</span>
|
|
91
|
+
</label>
|
|
92
|
+
}
|
|
93
|
+
</div>
|
|
94
|
+
@if (control.invalid && control.touched) {
|
|
95
|
+
<span class="ngx-field__error" [attr.data-testid]="'field-' + field.id + '-error'">This field has an error</span>
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
</fieldset>
|
|
99
|
+
</div>
|
|
100
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
|
|
101
|
+
}
|
|
102
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: RadioFieldComponent, decorators: [{
|
|
103
|
+
type: Component,
|
|
104
|
+
args: [{
|
|
105
|
+
selector: 'ngx-radio-field',
|
|
106
|
+
standalone: true,
|
|
107
|
+
imports: [ReactiveFormsModule],
|
|
108
|
+
template: `
|
|
109
|
+
<div class="ngx-field ngx-field--radio"
|
|
110
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
111
|
+
<fieldset class="ngx-field__fieldset">
|
|
112
|
+
<legend class="ngx-field__label">{{ label }}</legend>
|
|
113
|
+
@if (masked) {
|
|
114
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
115
|
+
} @else if (readonly) {
|
|
116
|
+
<span class="ngx-field__value" [attr.data-testid]="'field-' + field.id + '-value'">{{ getSelectedLabel() }}</span>
|
|
117
|
+
} @else {
|
|
118
|
+
<div class="ngx-field__radio-group">
|
|
119
|
+
@for (option of options(); track getOptLabel(option)) {
|
|
120
|
+
<label class="ngx-field__radio-option" [attr.for]="getRadioId(option)">
|
|
121
|
+
<input
|
|
122
|
+
[id]="getRadioId(option)"
|
|
123
|
+
type="radio"
|
|
124
|
+
class="ngx-field__radio-input"
|
|
125
|
+
[formControl]="$any(control)"
|
|
126
|
+
[value]="getOptStoredVal(option)"
|
|
127
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
128
|
+
/>
|
|
129
|
+
<span class="ngx-field__radio-label">{{ getOptLabel(option) }}</span>
|
|
130
|
+
</label>
|
|
131
|
+
}
|
|
132
|
+
</div>
|
|
133
|
+
@if (control.invalid && control.touched) {
|
|
134
|
+
<span class="ngx-field__error" [attr.data-testid]="'field-' + field.id + '-error'">This field has an error</span>
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
</fieldset>
|
|
138
|
+
</div>
|
|
139
|
+
`,
|
|
140
|
+
}]
|
|
141
|
+
}], propDecorators: { field: [{
|
|
142
|
+
type: Input
|
|
143
|
+
}], language: [{
|
|
144
|
+
type: Input
|
|
145
|
+
}], control: [{
|
|
146
|
+
type: Input
|
|
147
|
+
}], readonly: [{
|
|
148
|
+
type: Input
|
|
149
|
+
}], masked: [{
|
|
150
|
+
type: Input
|
|
151
|
+
}] } });
|
|
152
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmFkaW8tZmllbGQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9maWVsZC10eXBlcy9yYWRpby1maWVsZC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUNqRSxPQUFPLEVBQW1CLG1CQUFtQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFdEUsT0FBTyxFQUNMLG9CQUFvQixFQUNwQixZQUFZLEVBQ1osa0JBQWtCLEVBQ2xCLGtCQUFrQixFQUNsQixXQUFXLEdBQ1osTUFBTSxzQkFBc0IsQ0FBQztBQUM5QixPQUFPLEVBQUUscUJBQXFCLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQzs7O0FBRWxHLHNFQUFzRTtBQXNDdEUsTUFBTSxPQUFPLG1CQUFtQjtJQUNiLE9BQU8sR0FBRyxNQUFNLENBQUMscUJBQXFCLENBQUMsQ0FBQztJQUVqRCxNQUFNLENBQXFCO0lBQzNCLFNBQVMsR0FBRyxJQUFJLENBQUM7SUFFekIsNkZBQTZGO0lBQzdGLElBQWEsS0FBSyxDQUFDLEtBQXdCO1FBQ3pDLElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO1FBQ3BCLG9CQUFvQixDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDM0MsQ0FBQztJQUNELElBQUksS0FBSztRQUNQLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNyQixDQUFDO0lBRUQsSUFBYSxRQUFRLENBQUMsS0FBYTtRQUNqQyxJQUFJLENBQUMsU0FBUyxHQUFHLEtBQUssSUFBSSxJQUFJLENBQUM7UUFDL0Isb0JBQW9CLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBQ0QsSUFBSSxRQUFRO1FBQ1YsT0FB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O2dCQUNHLE1BQU07c0JBQWQsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgSW5wdXQsIGluamVjdCwgc2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XHJcbmltcG9ydCB7IEFic3RyYWN0Q29udHJvbCwgUmVhY3RpdmVGb3Jtc01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcclxuaW1wb3J0IHR5cGUgeyBEcm9wZG93bk9wdGlvbiwgTmVzdGVkRmllbGRDb25maWcgfSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XHJcbmltcG9ydCB7XHJcbiAgZ2V0T3B0aW9uU3RvcmVkVmFsdWUsXHJcbiAgcmVzb2x2ZUxhYmVsLFxyXG4gIHJlc29sdmVPcHRpb25MYWJlbCxcclxuICByZXNvbHZlT3B0aW9uVmFsdWUsXHJcbiAgdmFsdWVzTWF0Y2gsXHJcbn0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xyXG5pbXBvcnQgeyBMb29rdXBSZWdpc3RyeVNlcnZpY2UsIHJlZnJlc2hDaG9pY2VPcHRpb25zIH0gZnJvbSAnLi4vc2VydmljZXMvbG9va3VwLXJlZ2lzdHJ5LnNlcnZpY2UnO1xyXG5cclxuLyoqIFJhZGlvIGZpZWxkOiBhIGdyb3VwIG9mIHJhZGlvIGJ1dHRvbnMgYnVpbHQgZnJvbSBmaWVsZC5vcHRpb25zLiAqL1xyXG5AQ29tcG9uZW50KHtcclxuICBzZWxlY3RvcjogJ25neC1yYWRpby1maWVsZCcsXHJcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcclxuICBpbXBvcnRzOiBbUmVhY3RpdmVGb3Jtc01vZHVsZV0sXHJcbiAgdGVtcGxhdGU6IGBcclxuICAgIDxkaXYgY2xhc3M9XCJuZ3gtZmllbGQgbmd4LWZpZWxkLS1yYWRpb1wiXHJcbiAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidmaWVsZC0nICsgZmllbGQuaWRcIiBbYXR0ci5kYXRhLWZpZWxkLXR5cGVdPVwiZmllbGQudHlwZVwiIFtjbGFzcy5uZ3gtZmllbGQtLXJlYWRvbmx5XT1cInJlYWRvbmx5XCIgW2NsYXNzLm5neC1maWVsZC0tbWFza2VkXT1cIm1hc2tlZFwiPlxyXG4gICAgICA8ZmllbGRzZXQgY2xhc3M9XCJuZ3gtZmllbGRfX2ZpZWxkc2V0XCI+XHJcbiAgICAgICAgPGxlZ2VuZCBjbGFzcz1cIm5neC1maWVsZF9fbGFiZWxcIj57eyBsYWJlbCB9fTwvbGVnZW5kPlxyXG4gICAgICAgIEBpZiAobWFza2VkKSB7XHJcbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1maWVsZF9fdmFsdWUgbmd4LWZpZWxkX192YWx1ZS0tbWFza2VkXCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZCArICctbWFza2VkJ1wiPlhYWFhYWFhYWDwvc3Bhbj5cclxuICAgICAgICB9IEBlbHNlIGlmIChyZWFkb25seSkge1xyXG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtZmllbGRfX3ZhbHVlXCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZCArICctdmFsdWUnXCI+e3sgZ2V0U2VsZWN0ZWRMYWJlbCgpIH19PC9zcGFuPlxyXG4gICAgICAgIH0gQGVsc2Uge1xyXG4gICAgICAgICAgPGRpdiBjbGFzcz1cIm5neC1maWVsZF9fcmFkaW8tZ3JvdXBcIj5cclxuICAgICAgICAgICAgQGZvciAob3B0aW9uIG9mIG9wdGlvbnMoKTsgdHJhY2sgZ2V0T3B0TGFiZWwob3B0aW9uKSkge1xyXG4gICAgICAgICAgICAgIDxsYWJlbCBjbGFzcz1cIm5neC1maWVsZF9fcmFkaW8tb3B0aW9uXCIgW2F0dHIuZm9yXT1cImdldFJhZGlvSWQob3B0aW9uKVwiPlxyXG4gICAgICAgICAgICAgICAgPGlucHV0XHJcbiAgICAgICAgICAgICAgICAgIFtpZF09XCJnZXRSYWRpb0lkKG9wdGlvbilcIlxyXG4gICAgICAgICAgICAgICAgICB0eXBlPVwicmFkaW9cIlxyXG4gICAgICAgICAgICAgICAgICBjbGFzcz1cIm5neC1maWVsZF9fcmFkaW8taW5wdXRcIlxyXG4gICAgICAgICAgICAgICAgICBbZm9ybUNvbnRyb2xdPVwiJGFueShjb250cm9sKVwiXHJcbiAgICAgICAgICAgICAgICAgIFt2YWx1ZV09XCJnZXRPcHRTdG9yZWRWYWwob3B0aW9uKVwiXHJcbiAgICAgICAgICAgICAgICAgIFthdHRyLmRpc2FibGVkXT1cImZpZWxkLmRpc2FibGVkID8gdHJ1ZSA6IG51bGxcIlxyXG4gICAgICAgICAgICAgICAgLz5cclxuICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LWZpZWxkX19yYWRpby1sYWJlbFwiPnt7IGdldE9wdExhYmVsKG9wdGlvbikgfX08L3NwYW4+XHJcbiAgICAgICAgICAgICAgPC9sYWJlbD5cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICBAaWYgKGNvbnRyb2wuaW52YWxpZCAmJiBjb250cm9sLnRvdWNoZWQpIHtcclxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtZmllbGRfX2Vycm9yXCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZCArICctZXJyb3InXCI+VGhpcyBmaWVsZCBoYXMgYW4gZXJyb3I8L3NwYW4+XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICA8L2ZpZWxkc2V0PlxyXG4gICAgPC9kaXY+XHJcbiAgYCxcclxufSlcclxuZXhwb3J0IGNsYXNzIFJhZGlvRmllbGRDb21wb25lbnQge1xyXG4gIHByaXZhdGUgcmVhZG9ubHkgbG9va3VwcyA9IGluamVjdChMb29rdXBSZWdpc3RyeVNlcnZpY2UpO1xyXG5cclxuICBwcml2YXRlIF9maWVsZCE6IE5lc3RlZEZpZWxkQ29uZmlnO1xyXG4gIHByaXZhdGUgX2xhbmd1YWdlID0gJ2VuJztcclxuXHJcbiAgLyoqIFNldHRlci1iYXNlZCBzbyBvcHRpb25zIHJlc29sdmUgaG93ZXZlciB0aGUgaW5wdXQgaXMgc2V0IOKAlCBzZWUgYHJlZnJlc2hDaG9pY2VPcHRpb25zYC4gKi9cclxuICBASW5wdXQoKSBzZXQgZmllbGQodmFsdWU6IE5lc3RlZEZpZWxkQ29uZmlnKSB7XHJcbiAgICB0aGlzLl9maWVsZCA9IHZhbHVlO1xyXG4gICAgcmVmcmVzaENob2ljZU9wdGlvbnModGhpcywgdGhpcy5sb29rdXBzKTtcclxuICB9XHJcbiAgZ2V0IGZpZWxkKCk6IE5lc3RlZEZpZWxkQ29uZmlnIHtcclxuICAgIHJldHVybiB0aGlzLl9maWVsZDtcclxuICB9XHJcblxyXG4gIEBJbnB1dCgpIHNldCBsYW5ndWFnZSh2YWx1ZTogc3RyaW5nKSB7XHJcbiAgICB0aGlzLl9sYW5ndWFnZSA9IHZhbHVlIHx8ICdlbic7XHJcbiAgICByZWZyZXNoQ2hvaWNlT3B0aW9ucyh0aGlzLCB0aGlzLmxvb2t1cHMpO1xyXG4gIH1cclxuICBnZXQgbGFuZ3VhZ2UoKTogc3RyaW5nIHtcclxuICAgIHJldHVybiB0aGlzLl9sYW5ndWFnZTtcclxuICB9XHJcblxyXG4gIEBJbnB1dCgpIGNvbnRyb2whOiBBYnN0cmFjdENvbnRyb2w7XHJcbiAgQElucHV0KCkgcmVhZG9ubHk6IGJvb2xlYW4gPSBmYWxzZTtcclxuICBASW5wdXQoKSBtYXNrZWQ6IGJvb2xlYW4gPSBmYWxzZTtcclxuXHJcbiAgLyoqIElubGluZSBgb3B0aW9uc2AsIG9yIHRoZSBmaWVsZCdzIG5hbWVkIGxpc3QgcmVzb2x2ZWQgdGhyb3VnaCB0aGUgcmVnaXN0cnkgKMKnNi4zKS4gKi9cclxuICByZWFkb25seSBvcHRpb25zID0gc2lnbmFsPERyb3Bkb3duT3B0aW9uW10+KFtdKTtcclxuXHJcbiAgZ2V0IGxhYmVsKCk6IHN0cmluZyB7XHJcbiAgICByZXR1cm4gcmVzb2x2ZUxhYmVsKHRoaXMuZmllbGQ/LmxhYmVsLCB0aGlzLmxhbmd1YWdlKTtcclxuICB9XHJcblxyXG4gIGdldE9wdFN0b3JlZFZhbChvcHRpb246IGFueSk6IGFueSB7XHJcbiAgICByZXR1cm4gZ2V0T3B0aW9uU3RvcmVkVmFsdWUob3B0aW9uKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFN0YWJsZSBET00gaWQgZm9yIG9uZSByYWRpbywgc28gdGhlIDxsYWJlbCBmb3I+IGFzc29jaWF0aW9uIHdvcmtzLlxyXG4gICAqIFNsdWdpZmllZCBiZWNhdXNlIHRoZSBvcHRpb24gdGV4dCBpcyB0aGUgdmFsdWUgbm93IOKAlCBcIk9uIExlYXZlXCIgbXVzdCBub3QgcHV0IGEgc3BhY2VcclxuICAgKiBpbnRvIGFuIGlkIGF0dHJpYnV0ZS5cclxuICAgKi9cclxuICBnZXRSYWRpb0lkKG9wdGlvbjogYW55KTogc3RyaW5nIHtcclxuICAgIGNvbnN0IHRleHQgPSBTdHJpbmcodGhpcy5nZXRPcHRWYWwob3B0aW9uKSA/PyAnb3B0Jyk7XHJcbiAgICByZXR1cm4gYCR7dGhpcy5maWVsZC5pZH0tJHt0ZXh0LnRyaW0oKS5yZXBsYWNlKC9cXHMrL2csICdfJykudG9Mb3dlckNhc2UoKX1gO1xyXG4gIH1cclxuXHJcbiAgZ2V0T3B0VmFsKG9wdGlvbjogYW55KTogYW55IHtcclxuICAgIHJldHVybiByZXNvbHZlT3B0aW9uVmFsdWUob3B0aW9uLCB0aGlzLmxhbmd1YWdlKTtcclxuICB9XHJcblxyXG4gIGdldE9wdExhYmVsKG9wdGlvbjogYW55KTogc3RyaW5nIHtcclxuICAgIHJldHVybiByZXNvbHZlT3B0aW9uTGFiZWwob3B0aW9uLCB0aGlzLmxhbmd1YWdlKTtcclxuICB9XHJcblxyXG4gIC8qKiBSZWFkLW9ubHkgZGlzcGxheSDigJQgc3luY2hyb25vdXMsIHBlciDCpzYuMi4gU2VlIGBEcm9wZG93bkZpZWxkQ29tcG9uZW50LmdldExhYmVsYC4gKi9cclxuICBnZXRTZWxlY3RlZExhYmVsKCk6IHN0cmluZyB7XHJcbiAgICBjb25zdCB2YWx1ZSA9IHRoaXMuY29udHJvbD8udmFsdWU7XHJcbiAgICAvLyBgdHlwZW9mIG51bGwgPT09ICdvYmplY3QnYCwgc28gd2l0aG91dCB0aGlzIGd1YXJkIGFuIGVtcHR5IHZhbHVlIGZlbGwgdGhyb3VnaCB0b1xyXG4gICAgLy8gYHJlc29sdmVMYWJlbChudWxsKWAgYW5kIHJlbmRlcmVkIGFzIGJsYW5rIOKAlCBkcm9wZG93biBhbmQgbXVsdGlTZWxlY3QgYm90aCBzaG93IGFuIGVtIGRhc2guXHJcbiAgICBpZiAodmFsdWUgPT09IG51bGwgfHwgdmFsdWUgPT09IHVuZGVmaW5lZCB8fCB2YWx1ZSA9PT0gJycpIHJldHVybiAn4oCUJztcclxuICAgIGNvbnN0IHNlbGVjdGVkID0gdGhpcy5vcHRpb25zKCkuZmluZChvID0+IHZhbHVlc01hdGNoKGdldE9wdGlvblN0b3JlZFZhbHVlKG8pLCB2YWx1ZSwgdGhpcy5sYW5ndWFnZSkpO1xyXG4gICAgaWYgKHNlbGVjdGVkKSByZXR1cm4gdGhpcy5nZXRPcHRMYWJlbChzZWxlY3RlZCk7XHJcbiAgICBjb25zdCBjYWNoZWQgPSB0aGlzLmxvb2t1cHMubGFiZWxGb3IodGhpcy5maWVsZD8ubGlzdE5hbWUsIHZhbHVlLCB0aGlzLmxhbmd1YWdlKTtcclxuICAgIGlmIChjYWNoZWQpIHJldHVybiBjYWNoZWQ7XHJcbiAgICByZXR1cm4gdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyA/IHJlc29sdmVMYWJlbCh2YWx1ZSwgdGhpcy5sYW5ndWFnZSkgOiAodmFsdWUgPz8gJ+KAlCcpO1xyXG4gIH1cclxufVxyXG4iXX0=
|