ngx-dynamic-entity 0.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -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 +25 -17
- package/esm2022/lib/field-types/currency-field.component.mjs +101 -0
- package/esm2022/lib/field-types/date-field.component.mjs +21 -13
- package/esm2022/lib/field-types/dropdown-field.component.mjs +120 -29
- package/esm2022/lib/field-types/email-field.component.mjs +101 -0
- package/esm2022/lib/field-types/entity-ref-field.component.mjs +96 -32
- 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 +79 -25
- package/esm2022/lib/field-types/number-field.component.mjs +60 -17
- 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 +58 -18
- package/esm2022/lib/field-types/textarea-field.component.mjs +26 -15
- package/esm2022/lib/form/dynamic-field/dynamic-field.component.mjs +44 -26
- package/esm2022/lib/form/dynamic-form.component.mjs +676 -69
- package/esm2022/lib/form/dynamic-record-form.component.mjs +494 -0
- package/esm2022/lib/providers/provide-field-types.mjs +72 -0
- package/esm2022/lib/providers/provide-ngx-dynamic-entity.mjs +4 -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 +35 -7
- package/esm2022/lib/tokens/injection-tokens.mjs +104 -7
- package/esm2022/public-api.mjs +31 -6
- package/fesm2022/ngx-dynamic-entity.mjs +4094 -759
- 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 +203 -24
- package/lib/form/dynamic-record-form.component.d.ts +180 -0
- package/lib/providers/provide-field-types.d.ts +24 -0
- package/lib/providers/provide-ngx-dynamic-entity.d.ts +19 -8
- 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 +113 -8
- package/package.json +18 -8
- package/public-api.d.ts +21 -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
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Component, inject, Input, signal } from '@angular/core';
|
|
2
|
+
import { resolveLabel } from '@dynamic-entity/core';
|
|
3
|
+
import { FileUploadService } from '../services/file-upload.service';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
/**
|
|
6
|
+
* Image field: preview thumbnail + upload button.
|
|
7
|
+
*
|
|
8
|
+
* With `UPLOAD_HANDLER` registered the file is persisted and stored as `{ url }`; without one
|
|
9
|
+
* it is stored as `{ file }` and previewed from an object URL (revoked when replaced/destroyed).
|
|
10
|
+
*/
|
|
11
|
+
export class ImageFieldComponent {
|
|
12
|
+
field;
|
|
13
|
+
control;
|
|
14
|
+
language = 'en';
|
|
15
|
+
readonly = false;
|
|
16
|
+
masked = false;
|
|
17
|
+
uploads = inject(FileUploadService);
|
|
18
|
+
uploading = signal(false);
|
|
19
|
+
uploadError = signal(null);
|
|
20
|
+
/** Cached so an object URL is minted once per File, not once per change-detection pass. */
|
|
21
|
+
previewSource = null;
|
|
22
|
+
previewCache = null;
|
|
23
|
+
get label() {
|
|
24
|
+
return resolveLabel(this.field?.label, this.language);
|
|
25
|
+
}
|
|
26
|
+
previewUrl() {
|
|
27
|
+
const value = this.control?.value ?? null;
|
|
28
|
+
if (value === this.previewSource)
|
|
29
|
+
return this.previewCache;
|
|
30
|
+
this.uploads.revokePreviewUrl(this.previewCache);
|
|
31
|
+
this.previewSource = value;
|
|
32
|
+
this.previewCache = this.uploads.previewUrlFor(value);
|
|
33
|
+
return this.previewCache;
|
|
34
|
+
}
|
|
35
|
+
async onFileSelect(file) {
|
|
36
|
+
if (!file)
|
|
37
|
+
return;
|
|
38
|
+
this.uploadError.set(null);
|
|
39
|
+
this.uploading.set(true);
|
|
40
|
+
try {
|
|
41
|
+
this.control.setValue(await this.uploads.toFileRef(file));
|
|
42
|
+
this.control.markAsTouched();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
this.uploadError.set('Upload failed. Please try again.');
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
this.uploading.set(false);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
remove() {
|
|
52
|
+
this.control.setValue(null);
|
|
53
|
+
this.control.markAsTouched();
|
|
54
|
+
}
|
|
55
|
+
ngOnDestroy() {
|
|
56
|
+
this.uploads.revokePreviewUrl(this.previewCache);
|
|
57
|
+
}
|
|
58
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ImageFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
59
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: ImageFieldComponent, isStandalone: true, selector: "ngx-image-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
60
|
+
<div class="ngx-field ngx-field--image"
|
|
61
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
62
|
+
<label class="ngx-field__label" [attr.for]="'field-' + field.id">{{ label }}</label>
|
|
63
|
+
@if (masked) {
|
|
64
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
65
|
+
} @else {
|
|
66
|
+
<div class="ngx-field__image-wrap">
|
|
67
|
+
@if (previewUrl()) {
|
|
68
|
+
<img class="ngx-field__image-preview" [src]="previewUrl()" [alt]="label" />
|
|
69
|
+
} @else {
|
|
70
|
+
<div class="ngx-field__image-placeholder">
|
|
71
|
+
<span>📷</span>
|
|
72
|
+
<span>No image</span>
|
|
73
|
+
</div>
|
|
74
|
+
}
|
|
75
|
+
@if (!readonly) {
|
|
76
|
+
<div class="ngx-field__image-actions">
|
|
77
|
+
<label class="ngx-field__upload-btn" [class.ngx-field__upload-btn--loading]="uploading()">
|
|
78
|
+
@if (uploading()) {
|
|
79
|
+
<span>Uploading…</span>
|
|
80
|
+
} @else {
|
|
81
|
+
<span>{{ previewUrl() ? 'Change' : 'Upload' }}</span>
|
|
82
|
+
<input
|
|
83
|
+
type="file"
|
|
84
|
+
class="ngx-field__file-input"
|
|
85
|
+
[id]="'field-' + field.id"
|
|
86
|
+
accept="image/*"
|
|
87
|
+
[disabled]="field.disabled || uploading()"
|
|
88
|
+
(change)="onFileSelect($any($event.target).files[0])"
|
|
89
|
+
/>
|
|
90
|
+
}
|
|
91
|
+
</label>
|
|
92
|
+
@if (previewUrl()) {
|
|
93
|
+
<button type="button" class="ngx-field__remove-btn" (click)="remove()">Remove</button>
|
|
94
|
+
}
|
|
95
|
+
</div>
|
|
96
|
+
}
|
|
97
|
+
</div>
|
|
98
|
+
@if (uploadError()) {
|
|
99
|
+
<span class="ngx-field__error" [attr.data-testid]="'field-' + field.id + '-error'">{{ uploadError() }}</span>
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
</div>
|
|
103
|
+
`, isInline: true });
|
|
104
|
+
}
|
|
105
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ImageFieldComponent, decorators: [{
|
|
106
|
+
type: Component,
|
|
107
|
+
args: [{
|
|
108
|
+
selector: 'ngx-image-field',
|
|
109
|
+
standalone: true,
|
|
110
|
+
imports: [],
|
|
111
|
+
template: `
|
|
112
|
+
<div class="ngx-field ngx-field--image"
|
|
113
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
114
|
+
<label class="ngx-field__label" [attr.for]="'field-' + field.id">{{ label }}</label>
|
|
115
|
+
@if (masked) {
|
|
116
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
117
|
+
} @else {
|
|
118
|
+
<div class="ngx-field__image-wrap">
|
|
119
|
+
@if (previewUrl()) {
|
|
120
|
+
<img class="ngx-field__image-preview" [src]="previewUrl()" [alt]="label" />
|
|
121
|
+
} @else {
|
|
122
|
+
<div class="ngx-field__image-placeholder">
|
|
123
|
+
<span>📷</span>
|
|
124
|
+
<span>No image</span>
|
|
125
|
+
</div>
|
|
126
|
+
}
|
|
127
|
+
@if (!readonly) {
|
|
128
|
+
<div class="ngx-field__image-actions">
|
|
129
|
+
<label class="ngx-field__upload-btn" [class.ngx-field__upload-btn--loading]="uploading()">
|
|
130
|
+
@if (uploading()) {
|
|
131
|
+
<span>Uploading…</span>
|
|
132
|
+
} @else {
|
|
133
|
+
<span>{{ previewUrl() ? 'Change' : 'Upload' }}</span>
|
|
134
|
+
<input
|
|
135
|
+
type="file"
|
|
136
|
+
class="ngx-field__file-input"
|
|
137
|
+
[id]="'field-' + field.id"
|
|
138
|
+
accept="image/*"
|
|
139
|
+
[disabled]="field.disabled || uploading()"
|
|
140
|
+
(change)="onFileSelect($any($event.target).files[0])"
|
|
141
|
+
/>
|
|
142
|
+
}
|
|
143
|
+
</label>
|
|
144
|
+
@if (previewUrl()) {
|
|
145
|
+
<button type="button" class="ngx-field__remove-btn" (click)="remove()">Remove</button>
|
|
146
|
+
}
|
|
147
|
+
</div>
|
|
148
|
+
}
|
|
149
|
+
</div>
|
|
150
|
+
@if (uploadError()) {
|
|
151
|
+
<span class="ngx-field__error" [attr.data-testid]="'field-' + field.id + '-error'">{{ uploadError() }}</span>
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
</div>
|
|
155
|
+
`,
|
|
156
|
+
}]
|
|
157
|
+
}], propDecorators: { field: [{
|
|
158
|
+
type: Input
|
|
159
|
+
}], control: [{
|
|
160
|
+
type: Input
|
|
161
|
+
}], language: [{
|
|
162
|
+
type: Input
|
|
163
|
+
}], readonly: [{
|
|
164
|
+
type: Input
|
|
165
|
+
}], masked: [{
|
|
166
|
+
type: Input
|
|
167
|
+
}] } });
|
|
168
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1hZ2UtZmllbGQuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2xpYi9maWVsZC10eXBlcy9pbWFnZS1maWVsZC5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBYSxNQUFNLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUc1RSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDcEQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0saUNBQWlDLENBQUM7O0FBRXBFOzs7OztHQUtHO0FBbURILE1BQU0sT0FBTyxtQkFBbUI7SUFDckIsS0FBSyxDQUFxQjtJQUMxQixPQUFPLENBQW1CO0lBQzFCLFFBQVEsR0FBVyxJQUFJLENBQUM7SUFDeEIsUUFBUSxHQUFZLEtBQUssQ0FBQztJQUMxQixNQUFNLEdBQVksS0FBSyxDQUFDO0lBRWhCLE9BQU8sR0FBRyxNQUFNLENBQUMsaUJBQWlCLENBQUMsQ0FBQztJQUU1QyxTQUFTLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzFCLFdBQVcsR0FBRyxNQUFNLENBQWdCLElBQUksQ0FBQyxDQUFDO0lBRW5ELDJGQUEyRjtJQUNuRixhQUFhLEdBQW1CLElBQUksQ0FBQztJQUNyQyxZQUFZLEdBQWtCLElBQUksQ0FBQztJQUUzQyxJQUFJLEtBQUs7UUFDUCxPQUFPLFlBQVksQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDeEQsQ0FBQztJQUVELFVBQVU7UUFDUixNQUFNLEtBQUssR0FBSSxJQUFJLENBQUMsT0FBTyxFQUFFLEtBQXdCLElBQUksSUFBSSxDQUFDO1FBQzlELElBQUksS0FBSyxLQUFLLElBQUksQ0FBQyxhQUFhO1lBQUUsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDO1FBRTNELElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBQ2pELElBQUksQ0FBQyxhQUFhLEdBQUcsS0FBSyxDQUFDO1FBQzNCLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDdEQsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDO0lBQzNCLENBQUM7SUFFRCxLQUFLLENBQUMsWUFBWSxDQUFDLElBQXNCO1FBQ3ZDLElBQUksQ0FBQyxJQUFJO1lBQUUsT0FBTztRQUNsQixJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUMzQixJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN6QixJQUFJLENBQUM7WUFDSCxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDMUQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUMvQixDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQ1AsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsa0NBQWtDLENBQUMsQ0FBQztRQUMzRCxDQUFDO2dCQUFTLENBQUM7WUFDVCxJQUFJLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUM1QixDQUFDO0lBQ0gsQ0FBQztJQUVELE1BQU07UUFDSixJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM1QixJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsRUFBRSxDQUFDO0lBQy9CLENBQUM7SUFFRCxXQUFXO1FBQ1QsSUFBSSxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUM7SUFDbkQsQ0FBQzt3R0FuRFUsbUJBQW1COzRGQUFuQixtQkFBbUIseUxBOUNwQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E0Q1Q7OzRGQUVVLG1CQUFtQjtrQkFsRC9CLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLGlCQUFpQjtvQkFDM0IsVUFBVSxFQUFFLElBQUk7b0JBQ2hCLE9BQU8sRUFBRSxFQUFFO29CQUNYLFFBQVEsRUFBRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E0Q1Q7aUJBQ0Y7OEJBRVUsS0FBSztzQkFBYixLQUFLO2dCQUNHLE9BQU87c0JBQWYsS0FBSztnQkFDRyxRQUFRO3NCQUFoQixLQUFLO2dCQUNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csTUFBTTtzQkFBZCxLQUFLIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBPbkRlc3Ryb3ksIGluamVjdCwgSW5wdXQsIHNpZ25hbCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQWJzdHJhY3RDb250cm9sIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHR5cGUgeyBGaWxlUmVmLCBOZXN0ZWRGaWVsZENvbmZpZyB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcbmltcG9ydCB7IHJlc29sdmVMYWJlbCB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcbmltcG9ydCB7IEZpbGVVcGxvYWRTZXJ2aWNlIH0gZnJvbSAnLi4vc2VydmljZXMvZmlsZS11cGxvYWQuc2VydmljZSc7XG5cbi8qKlxuICogSW1hZ2UgZmllbGQ6IHByZXZpZXcgdGh1bWJuYWlsICsgdXBsb2FkIGJ1dHRvbi5cbiAqXG4gKiBXaXRoIGBVUExPQURfSEFORExFUmAgcmVnaXN0ZXJlZCB0aGUgZmlsZSBpcyBwZXJzaXN0ZWQgYW5kIHN0b3JlZCBhcyBgeyB1cmwgfWA7IHdpdGhvdXQgb25lXG4gKiBpdCBpcyBzdG9yZWQgYXMgYHsgZmlsZSB9YCBhbmQgcHJldmlld2VkIGZyb20gYW4gb2JqZWN0IFVSTCAocmV2b2tlZCB3aGVuIHJlcGxhY2VkL2Rlc3Ryb3llZCkuXG4gKi9cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ25neC1pbWFnZS1maWVsZCcsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtdLFxuICB0ZW1wbGF0ZTogYFxuICAgIDxkaXYgY2xhc3M9XCJuZ3gtZmllbGQgbmd4LWZpZWxkLS1pbWFnZVwiXG4gICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCInZmllbGQtJyArIGZpZWxkLmlkXCIgW2F0dHIuZGF0YS1maWVsZC10eXBlXT1cImZpZWxkLnR5cGVcIiBbY2xhc3Mubmd4LWZpZWxkLS1yZWFkb25seV09XCJyZWFkb25seVwiIFtjbGFzcy5uZ3gtZmllbGQtLW1hc2tlZF09XCJtYXNrZWRcIj5cbiAgICAgIDxsYWJlbCBjbGFzcz1cIm5neC1maWVsZF9fbGFiZWxcIiBbYXR0ci5mb3JdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZFwiPnt7IGxhYmVsIH19PC9sYWJlbD5cbiAgICAgIEBpZiAobWFza2VkKSB7XG4gICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LWZpZWxkX192YWx1ZSBuZ3gtZmllbGRfX3ZhbHVlLS1tYXNrZWRcIiBbYXR0ci5kYXRhLXRlc3RpZF09XCInZmllbGQtJyArIGZpZWxkLmlkICsgJy1tYXNrZWQnXCI+WFhYWFhYWFhYPC9zcGFuPlxuICAgICAgfSBAZWxzZSB7XG4gICAgICAgIDxkaXYgY2xhc3M9XCJuZ3gtZmllbGRfX2ltYWdlLXdyYXBcIj5cbiAgICAgICAgICBAaWYgKHByZXZpZXdVcmwoKSkge1xuICAgICAgICAgICAgPGltZyBjbGFzcz1cIm5neC1maWVsZF9faW1hZ2UtcHJldmlld1wiIFtzcmNdPVwicHJldmlld1VybCgpXCIgW2FsdF09XCJsYWJlbFwiIC8+XG4gICAgICAgICAgfSBAZWxzZSB7XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibmd4LWZpZWxkX19pbWFnZS1wbGFjZWhvbGRlclwiPlxuICAgICAgICAgICAgICA8c3Bhbj7wn5O3PC9zcGFuPlxuICAgICAgICAgICAgICA8c3Bhbj5ObyBpbWFnZTwvc3Bhbj5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIH1cbiAgICAgICAgICBAaWYgKCFyZWFkb25seSkge1xuICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm5neC1maWVsZF9faW1hZ2UtYWN0aW9uc1wiPlxuICAgICAgICAgICAgICA8bGFiZWwgY2xhc3M9XCJuZ3gtZmllbGRfX3VwbG9hZC1idG5cIiBbY2xhc3Mubmd4LWZpZWxkX191cGxvYWQtYnRuLS1sb2FkaW5nXT1cInVwbG9hZGluZygpXCI+XG4gICAgICAgICAgICAgICAgQGlmICh1cGxvYWRpbmcoKSkge1xuICAgICAgICAgICAgICAgICAgPHNwYW4+VXBsb2FkaW5n4oCmPC9zcGFuPlxuICAgICAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICAgICAgPHNwYW4+e3sgcHJldmlld1VybCgpID8gJ0NoYW5nZScgOiAnVXBsb2FkJyB9fTwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgICAgICAgICB0eXBlPVwiZmlsZVwiXG4gICAgICAgICAgICAgICAgICAgIGNsYXNzPVwibmd4LWZpZWxkX19maWxlLWlucHV0XCJcbiAgICAgICAgICAgICAgICAgICAgW2lkXT1cIidmaWVsZC0nICsgZmllbGQuaWRcIlxuICAgICAgICAgICAgICAgICAgICBhY2NlcHQ9XCJpbWFnZS8qXCJcbiAgICAgICAgICAgICAgICAgICAgW2Rpc2FibGVkXT1cImZpZWxkLmRpc2FibGVkIHx8IHVwbG9hZGluZygpXCJcbiAgICAgICAgICAgICAgICAgICAgKGNoYW5nZSk9XCJvbkZpbGVTZWxlY3QoJGFueSgkZXZlbnQudGFyZ2V0KS5maWxlc1swXSlcIlxuICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDwvbGFiZWw+XG4gICAgICAgICAgICAgIEBpZiAocHJldmlld1VybCgpKSB7XG4gICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgY2xhc3M9XCJuZ3gtZmllbGRfX3JlbW92ZS1idG5cIiAoY2xpY2spPVwicmVtb3ZlKClcIj5SZW1vdmU8L2J1dHRvbj5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgfVxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgQGlmICh1cGxvYWRFcnJvcigpKSB7XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtZmllbGRfX2Vycm9yXCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZCArICctZXJyb3InXCI+e3sgdXBsb2FkRXJyb3IoKSB9fTwvc3Bhbj5cbiAgICAgICAgfVxuICAgICAgfVxuICAgIDwvZGl2PlxuICBgLFxufSlcbmV4cG9ydCBjbGFzcyBJbWFnZUZpZWxkQ29tcG9uZW50IGltcGxlbWVudHMgT25EZXN0cm95IHtcbiAgQElucHV0KCkgZmllbGQhOiBOZXN0ZWRGaWVsZENvbmZpZztcbiAgQElucHV0KCkgY29udHJvbCE6IEFic3RyYWN0Q29udHJvbDtcbiAgQElucHV0KCkgbGFuZ3VhZ2U6IHN0cmluZyA9ICdlbic7XG4gIEBJbnB1dCgpIHJlYWRvbmx5OiBib29sZWFuID0gZmFsc2U7XG4gIEBJbnB1dCgpIG1hc2tlZDogYm9vbGVhbiA9IGZhbHNlO1xuXG4gIHByaXZhdGUgcmVhZG9ubHkgdXBsb2FkcyA9IGluamVjdChGaWxlVXBsb2FkU2VydmljZSk7XG5cbiAgcmVhZG9ubHkgdXBsb2FkaW5nID0gc2lnbmFsKGZhbHNlKTtcbiAgcmVhZG9ubHkgdXBsb2FkRXJyb3IgPSBzaWduYWw8c3RyaW5nIHwgbnVsbD4obnVsbCk7XG5cbiAgLyoqIENhY2hlZCBzbyBhbiBvYmplY3QgVVJMIGlzIG1pbnRlZCBvbmNlIHBlciBGaWxlLCBub3Qgb25jZSBwZXIgY2hhbmdlLWRldGVjdGlvbiBwYXNzLiAqL1xuICBwcml2YXRlIHByZXZpZXdTb3VyY2U6IEZpbGVSZWYgfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBwcmV2aWV3Q2FjaGU6IHN0cmluZyB8IG51bGwgPSBudWxsO1xuXG4gIGdldCBsYWJlbCgpOiBzdHJpbmcge1xuICAgIHJldHVybiByZXNvbHZlTGFiZWwodGhpcy5maWVsZD8ubGFiZWwsIHRoaXMubGFuZ3VhZ2UpO1xuICB9XG5cbiAgcHJldmlld1VybCgpOiBzdHJpbmcgfCBudWxsIHtcbiAgICBjb25zdCB2YWx1ZSA9ICh0aGlzLmNvbnRyb2w/LnZhbHVlIGFzIEZpbGVSZWYgfCBudWxsKSA/PyBudWxsO1xuICAgIGlmICh2YWx1ZSA9PT0gdGhpcy5wcmV2aWV3U291cmNlKSByZXR1cm4gdGhpcy5wcmV2aWV3Q2FjaGU7XG5cbiAgICB0aGlzLnVwbG9hZHMucmV2b2tlUHJldmlld1VybCh0aGlzLnByZXZpZXdDYWNoZSk7XG4gICAgdGhpcy5wcmV2aWV3U291cmNlID0gdmFsdWU7XG4gICAgdGhpcy5wcmV2aWV3Q2FjaGUgPSB0aGlzLnVwbG9hZHMucHJldmlld1VybEZvcih2YWx1ZSk7XG4gICAgcmV0dXJuIHRoaXMucHJldmlld0NhY2hlO1xuICB9XG5cbiAgYXN5bmMgb25GaWxlU2VsZWN0KGZpbGU6IEZpbGUgfCB1bmRlZmluZWQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIWZpbGUpIHJldHVybjtcbiAgICB0aGlzLnVwbG9hZEVycm9yLnNldChudWxsKTtcbiAgICB0aGlzLnVwbG9hZGluZy5zZXQodHJ1ZSk7XG4gICAgdHJ5IHtcbiAgICAgIHRoaXMuY29udHJvbC5zZXRWYWx1ZShhd2FpdCB0aGlzLnVwbG9hZHMudG9GaWxlUmVmKGZpbGUpKTtcbiAgICAgIHRoaXMuY29udHJvbC5tYXJrQXNUb3VjaGVkKCk7XG4gICAgfSBjYXRjaCB7XG4gICAgICB0aGlzLnVwbG9hZEVycm9yLnNldCgnVXBsb2FkIGZhaWxlZC4gUGxlYXNlIHRyeSBhZ2Fpbi4nKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgdGhpcy51cGxvYWRpbmcuc2V0KGZhbHNlKTtcbiAgICB9XG4gIH1cblxuICByZW1vdmUoKTogdm9pZCB7XG4gICAgdGhpcy5jb250cm9sLnNldFZhbHVlKG51bGwpO1xuICAgIHRoaXMuY29udHJvbC5tYXJrQXNUb3VjaGVkKCk7XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICB0aGlzLnVwbG9hZHMucmV2b2tlUHJldmlld1VybCh0aGlzLnByZXZpZXdDYWNoZSk7XG4gIH1cbn1cbiJdfQ==
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Component, Input } 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
|
+
const MONTH_NAMES = [
|
|
7
|
+
'January', 'February', 'March', 'April', 'May', 'June',
|
|
8
|
+
'July', 'August', 'September', 'October', 'November', 'December',
|
|
9
|
+
];
|
|
10
|
+
const CURRENT_YEAR = new Date().getFullYear();
|
|
11
|
+
const YEARS = Array.from({ length: 80 }, (_, i) => CURRENT_YEAR - i);
|
|
12
|
+
/**
|
|
13
|
+
* Month-Year field: two linked selects (Month + Year).
|
|
14
|
+
* Value stored as ISO partial date string "YYYY-MM" for unambiguous storage.
|
|
15
|
+
*/
|
|
16
|
+
export class MonthYearFieldComponent {
|
|
17
|
+
field;
|
|
18
|
+
control;
|
|
19
|
+
language = 'en';
|
|
20
|
+
readonly = false;
|
|
21
|
+
masked = false;
|
|
22
|
+
months = MONTH_NAMES.map((label, i) => ({ value: String(i + 1).padStart(2, '0'), label }));
|
|
23
|
+
years = YEARS;
|
|
24
|
+
get label() {
|
|
25
|
+
return resolveLabel(this.field?.label, this.language);
|
|
26
|
+
}
|
|
27
|
+
/** Parses "YYYY-MM" or "YYYY-MM-DD" stored value → month part. */
|
|
28
|
+
get selectedMonth() {
|
|
29
|
+
const v = String(this.control.value ?? '');
|
|
30
|
+
return v.length >= 7 ? v.substring(5, 7) : '';
|
|
31
|
+
}
|
|
32
|
+
get selectedYear() {
|
|
33
|
+
const v = String(this.control.value ?? '');
|
|
34
|
+
return v.length >= 4 ? v.substring(0, 4) : '';
|
|
35
|
+
}
|
|
36
|
+
onMonthChange(month) {
|
|
37
|
+
const year = this.selectedYear || new Date().getFullYear().toString();
|
|
38
|
+
this.control.setValue(month ? `${year}-${month}` : null);
|
|
39
|
+
this.control.markAsTouched();
|
|
40
|
+
}
|
|
41
|
+
onYearChange(year) {
|
|
42
|
+
const month = this.selectedMonth || '01';
|
|
43
|
+
this.control.setValue(year ? `${year}-${month}` : null);
|
|
44
|
+
this.control.markAsTouched();
|
|
45
|
+
}
|
|
46
|
+
formatValue(value) {
|
|
47
|
+
if (!value)
|
|
48
|
+
return '—';
|
|
49
|
+
const [year, monthNum] = String(value).split('-');
|
|
50
|
+
const monthName = MONTH_NAMES[parseInt(monthNum, 10) - 1];
|
|
51
|
+
return monthName && year ? `${monthName} ${year}` : String(value);
|
|
52
|
+
}
|
|
53
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MonthYearFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
54
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: MonthYearFieldComponent, isStandalone: true, selector: "ngx-month-year-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
55
|
+
<div class="ngx-field ngx-field--month-year"
|
|
56
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
57
|
+
<label class="ngx-field__label">{{ label }}</label>
|
|
58
|
+
@if (masked) {
|
|
59
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
60
|
+
} @else if (readonly) {
|
|
61
|
+
<span class="ngx-field__value" [attr.data-testid]="'field-' + field.id + '-value'">{{ formatValue(control.value) }}</span>
|
|
62
|
+
} @else {
|
|
63
|
+
<div class="ngx-field__month-year-wrap">
|
|
64
|
+
<select
|
|
65
|
+
class="ngx-field__input ngx-field__input--month" [attr.data-testid]="'field-' + field.id + '-month'"
|
|
66
|
+
[value]="selectedMonth"
|
|
67
|
+
(change)="onMonthChange($any($event.target).value)"
|
|
68
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
69
|
+
>
|
|
70
|
+
<option value="">Month</option>
|
|
71
|
+
@for (m of months; track m.value) {
|
|
72
|
+
<option [value]="m.value">{{ m.label }}</option>
|
|
73
|
+
}
|
|
74
|
+
</select>
|
|
75
|
+
<select
|
|
76
|
+
class="ngx-field__input ngx-field__input--year" [attr.data-testid]="'field-' + field.id + '-year'"
|
|
77
|
+
[value]="selectedYear"
|
|
78
|
+
(change)="onYearChange($any($event.target).value)"
|
|
79
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
80
|
+
>
|
|
81
|
+
<option value="">Year</option>
|
|
82
|
+
@for (y of years; track y) {
|
|
83
|
+
<option [value]="y">{{ y }}</option>
|
|
84
|
+
}
|
|
85
|
+
</select>
|
|
86
|
+
</div>
|
|
87
|
+
@if (control.invalid && control.touched) {
|
|
88
|
+
<span class="ngx-field__error" [attr.data-testid]="'field-' + field.id + '-error'">This field has an error</span>
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
</div>
|
|
92
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }] });
|
|
93
|
+
}
|
|
94
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MonthYearFieldComponent, decorators: [{
|
|
95
|
+
type: Component,
|
|
96
|
+
args: [{
|
|
97
|
+
selector: 'ngx-month-year-field',
|
|
98
|
+
standalone: true,
|
|
99
|
+
imports: [ReactiveFormsModule],
|
|
100
|
+
template: `
|
|
101
|
+
<div class="ngx-field ngx-field--month-year"
|
|
102
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
103
|
+
<label class="ngx-field__label">{{ label }}</label>
|
|
104
|
+
@if (masked) {
|
|
105
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
106
|
+
} @else if (readonly) {
|
|
107
|
+
<span class="ngx-field__value" [attr.data-testid]="'field-' + field.id + '-value'">{{ formatValue(control.value) }}</span>
|
|
108
|
+
} @else {
|
|
109
|
+
<div class="ngx-field__month-year-wrap">
|
|
110
|
+
<select
|
|
111
|
+
class="ngx-field__input ngx-field__input--month" [attr.data-testid]="'field-' + field.id + '-month'"
|
|
112
|
+
[value]="selectedMonth"
|
|
113
|
+
(change)="onMonthChange($any($event.target).value)"
|
|
114
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
115
|
+
>
|
|
116
|
+
<option value="">Month</option>
|
|
117
|
+
@for (m of months; track m.value) {
|
|
118
|
+
<option [value]="m.value">{{ m.label }}</option>
|
|
119
|
+
}
|
|
120
|
+
</select>
|
|
121
|
+
<select
|
|
122
|
+
class="ngx-field__input ngx-field__input--year" [attr.data-testid]="'field-' + field.id + '-year'"
|
|
123
|
+
[value]="selectedYear"
|
|
124
|
+
(change)="onYearChange($any($event.target).value)"
|
|
125
|
+
[attr.disabled]="field.disabled ? true : null"
|
|
126
|
+
>
|
|
127
|
+
<option value="">Year</option>
|
|
128
|
+
@for (y of years; track y) {
|
|
129
|
+
<option [value]="y">{{ y }}</option>
|
|
130
|
+
}
|
|
131
|
+
</select>
|
|
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
|
+
</div>
|
|
138
|
+
`,
|
|
139
|
+
}]
|
|
140
|
+
}], propDecorators: { field: [{
|
|
141
|
+
type: Input
|
|
142
|
+
}], control: [{
|
|
143
|
+
type: Input
|
|
144
|
+
}], language: [{
|
|
145
|
+
type: Input
|
|
146
|
+
}], readonly: [{
|
|
147
|
+
type: Input
|
|
148
|
+
}], masked: [{
|
|
149
|
+
type: Input
|
|
150
|
+
}] } });
|
|
151
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9udGgteWVhci1maWVsZC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvbGliL2ZpZWxkLXR5cGVzL21vbnRoLXllYXItZmllbGQuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2pELE9BQU8sRUFBbUIsbUJBQW1CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV0RSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7OztBQUVwRCxNQUFNLFdBQVcsR0FBRztJQUNsQixTQUFTLEVBQUUsVUFBVSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU07SUFDdEQsTUFBTSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxVQUFVO0NBQ2pFLENBQUM7QUFFRixNQUFNLFlBQVksR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQzlDLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxZQUFZLEdBQUcsQ0FBQyxDQUFDLENBQUM7QUFFckU7OztHQUdHO0FBNkNILE1BQU0sT0FBTyx1QkFBdUI7SUFDekIsS0FBSyxDQUFxQjtJQUMxQixPQUFPLENBQW1CO0lBQzFCLFFBQVEsR0FBVyxJQUFJLENBQUM7SUFDeEIsUUFBUSxHQUFZLEtBQUssQ0FBQztJQUMxQixNQUFNLEdBQVksS0FBSyxDQUFDO0lBRXhCLE1BQU0sR0FBRyxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FB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QkFBYixLQUFLO2dCQUNHLE9BQU87c0JBQWYsS0FBSztnQkFDRyxRQUFRO3NCQUFoQixLQUFLO2dCQUNHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBQ0csTUFBTTtzQkFBZCxLQUFLIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBJbnB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQWJzdHJhY3RDb250cm9sLCBSZWFjdGl2ZUZvcm1zTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHR5cGUgeyBOZXN0ZWRGaWVsZENvbmZpZyB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcbmltcG9ydCB7IHJlc29sdmVMYWJlbCB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcblxuY29uc3QgTU9OVEhfTkFNRVMgPSBbXG4gICdKYW51YXJ5JywgJ0ZlYnJ1YXJ5JywgJ01hcmNoJywgJ0FwcmlsJywgJ01heScsICdKdW5lJyxcbiAgJ0p1bHknLCAnQXVndXN0JywgJ1NlcHRlbWJlcicsICdPY3RvYmVyJywgJ05vdmVtYmVyJywgJ0RlY2VtYmVyJyxcbl07XG5cbmNvbnN0IENVUlJFTlRfWUVBUiA9IG5ldyBEYXRlKCkuZ2V0RnVsbFllYXIoKTtcbmNvbnN0IFlFQVJTID0gQXJyYXkuZnJvbSh7IGxlbmd0aDogODAgfSwgKF8sIGkpID0+IENVUlJFTlRfWUVBUiAtIGkpO1xuXG4vKipcbiAqIE1vbnRoLVllYXIgZmllbGQ6IHR3byBsaW5rZWQgc2VsZWN0cyAoTW9udGggKyBZZWFyKS5cbiAqIFZhbHVlIHN0b3JlZCBhcyBJU08gcGFydGlhbCBkYXRlIHN0cmluZyBcIllZWVktTU1cIiBmb3IgdW5hbWJpZ3VvdXMgc3RvcmFnZS5cbiAqL1xuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbmd4LW1vbnRoLXllYXItZmllbGQnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbUmVhY3RpdmVGb3Jtc01vZHVsZV0sXG4gIHRlbXBsYXRlOiBgXG4gICAgPGRpdiBjbGFzcz1cIm5neC1maWVsZCBuZ3gtZmllbGQtLW1vbnRoLXllYXJcIlxuICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZFwiIFthdHRyLmRhdGEtZmllbGQtdHlwZV09XCJmaWVsZC50eXBlXCIgW2NsYXNzLm5neC1maWVsZC0tcmVhZG9ubHldPVwicmVhZG9ubHlcIiBbY2xhc3Mubmd4LWZpZWxkLS1tYXNrZWRdPVwibWFza2VkXCI+XG4gICAgICA8bGFiZWwgY2xhc3M9XCJuZ3gtZmllbGRfX2xhYmVsXCI+e3sgbGFiZWwgfX08L2xhYmVsPlxuICAgICAgQGlmIChtYXNrZWQpIHtcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtZmllbGRfX3ZhbHVlIG5neC1maWVsZF9fdmFsdWUtLW1hc2tlZFwiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidmaWVsZC0nICsgZmllbGQuaWQgKyAnLW1hc2tlZCdcIj5YWFhYWFhYWFg8L3NwYW4+XG4gICAgICB9IEBlbHNlIGlmIChyZWFkb25seSkge1xuICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1maWVsZF9fdmFsdWVcIiBbYXR0ci5kYXRhLXRlc3RpZF09XCInZmllbGQtJyArIGZpZWxkLmlkICsgJy12YWx1ZSdcIj57eyBmb3JtYXRWYWx1ZShjb250cm9sLnZhbHVlKSB9fTwvc3Bhbj5cbiAgICAgIH0gQGVsc2Uge1xuICAgICAgICA8ZGl2IGNsYXNzPVwibmd4LWZpZWxkX19tb250aC15ZWFyLXdyYXBcIj5cbiAgICAgICAgICA8c2VsZWN0XG4gICAgICAgICAgICBjbGFzcz1cIm5neC1maWVsZF9faW5wdXQgbmd4LWZpZWxkX19pbnB1dC0tbW9udGhcIiBbYXR0ci5kYXRhLXRlc3RpZF09XCInZmllbGQtJyArIGZpZWxkLmlkICsgJy1tb250aCdcIlxuICAgICAgICAgICAgW3ZhbHVlXT1cInNlbGVjdGVkTW9udGhcIlxuICAgICAgICAgICAgKGNoYW5nZSk9XCJvbk1vbnRoQ2hhbmdlKCRhbnkoJGV2ZW50LnRhcmdldCkudmFsdWUpXCJcbiAgICAgICAgICAgIFthdHRyLmRpc2FibGVkXT1cImZpZWxkLmRpc2FibGVkID8gdHJ1ZSA6IG51bGxcIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIDxvcHRpb24gdmFsdWU9XCJcIj5Nb250aDwvb3B0aW9uPlxuICAgICAgICAgICAgQGZvciAobSBvZiBtb250aHM7IHRyYWNrIG0udmFsdWUpIHtcbiAgICAgICAgICAgICAgPG9wdGlvbiBbdmFsdWVdPVwibS52YWx1ZVwiPnt7IG0ubGFiZWwgfX08L29wdGlvbj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICA8L3NlbGVjdD5cbiAgICAgICAgICA8c2VsZWN0XG4gICAgICAgICAgICBjbGFzcz1cIm5neC1maWVsZF9faW5wdXQgbmd4LWZpZWxkX19pbnB1dC0teWVhclwiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidmaWVsZC0nICsgZmllbGQuaWQgKyAnLXllYXInXCJcbiAgICAgICAgICAgIFt2YWx1ZV09XCJzZWxlY3RlZFllYXJcIlxuICAgICAgICAgICAgKGNoYW5nZSk9XCJvblllYXJDaGFuZ2UoJGFueSgkZXZlbnQudGFyZ2V0KS52YWx1ZSlcIlxuICAgICAgICAgICAgW2F0dHIuZGlzYWJsZWRdPVwiZmllbGQuZGlzYWJsZWQgPyB0cnVlIDogbnVsbFwiXG4gICAgICAgICAgPlxuICAgICAgICAgICAgPG9wdGlvbiB2YWx1ZT1cIlwiPlllYXI8L29wdGlvbj5cbiAgICAgICAgICAgIEBmb3IgKHkgb2YgeWVhcnM7IHRyYWNrIHkpIHtcbiAgICAgICAgICAgICAgPG9wdGlvbiBbdmFsdWVdPVwieVwiPnt7IHkgfX08L29wdGlvbj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICA8L3NlbGVjdD5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIEBpZiAoY29udHJvbC5pbnZhbGlkICYmIGNvbnRyb2wudG91Y2hlZCkge1xuICAgICAgICAgIDxzcGFuIGNsYXNzPVwibmd4LWZpZWxkX19lcnJvclwiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidmaWVsZC0nICsgZmllbGQuaWQgKyAnLWVycm9yJ1wiPlRoaXMgZmllbGQgaGFzIGFuIGVycm9yPC9zcGFuPlxuICAgICAgICB9XG4gICAgICB9XG4gICAgPC9kaXY+XG4gIGAsXG59KVxuZXhwb3J0IGNsYXNzIE1vbnRoWWVhckZpZWxkQ29tcG9uZW50IHtcbiAgQElucHV0KCkgZmllbGQhOiBOZXN0ZWRGaWVsZENvbmZpZztcbiAgQElucHV0KCkgY29udHJvbCE6IEFic3RyYWN0Q29udHJvbDtcbiAgQElucHV0KCkgbGFuZ3VhZ2U6IHN0cmluZyA9ICdlbic7XG4gIEBJbnB1dCgpIHJlYWRvbmx5OiBib29sZWFuID0gZmFsc2U7XG4gIEBJbnB1dCgpIG1hc2tlZDogYm9vbGVhbiA9IGZhbHNlO1xuXG4gIHJlYWRvbmx5IG1vbnRocyA9IE1PTlRIX05BTUVTLm1hcCgobGFiZWwsIGkpID0+ICh7IHZhbHVlOiBTdHJpbmcoaSArIDEpLnBhZFN0YXJ0KDIsICcwJyksIGxhYmVsIH0pKTtcbiAgcmVhZG9ubHkgeWVhcnMgPSBZRUFSUztcblxuICBnZXQgbGFiZWwoKTogc3RyaW5nIHtcbiAgICByZXR1cm4gcmVzb2x2ZUxhYmVsKHRoaXMuZmllbGQ/LmxhYmVsLCB0aGlzLmxhbmd1YWdlKTtcbiAgfVxuXG4gIC8qKiBQYXJzZXMgXCJZWVlZLU1NXCIgb3IgXCJZWVlZLU1NLUREXCIgc3RvcmVkIHZhbHVlIOKGkiBtb250aCBwYXJ0LiAqL1xuICBnZXQgc2VsZWN0ZWRNb250aCgpOiBzdHJpbmcge1xuICAgIGNvbnN0IHYgPSBTdHJpbmcodGhpcy5jb250cm9sLnZhbHVlID8/ICcnKTtcbiAgICByZXR1cm4gdi5sZW5ndGggPj0gNyA/IHYuc3Vic3RyaW5nKDUsIDcpIDogJyc7XG4gIH1cblxuICBnZXQgc2VsZWN0ZWRZZWFyKCk6IHN0cmluZyB7XG4gICAgY29uc3QgdiA9IFN0cmluZyh0aGlzLmNvbnRyb2wudmFsdWUgPz8gJycpO1xuICAgIHJldHVybiB2Lmxlbmd0aCA+PSA0ID8gdi5zdWJzdHJpbmcoMCwgNCkgOiAnJztcbiAgfVxuXG4gIG9uTW9udGhDaGFuZ2UobW9udGg6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IHllYXIgPSB0aGlzLnNlbGVjdGVkWWVhciB8fCBuZXcgRGF0ZSgpLmdldEZ1bGxZZWFyKCkudG9TdHJpbmcoKTtcbiAgICB0aGlzLmNvbnRyb2wuc2V0VmFsdWUobW9udGggPyBgJHt5ZWFyfS0ke21vbnRofWAgOiBudWxsKTtcbiAgICB0aGlzLmNvbnRyb2wubWFya0FzVG91Y2hlZCgpO1xuICB9XG5cbiAgb25ZZWFyQ2hhbmdlKHllYXI6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IG1vbnRoID0gdGhpcy5zZWxlY3RlZE1vbnRoIHx8ICcwMSc7XG4gICAgdGhpcy5jb250cm9sLnNldFZhbHVlKHllYXIgPyBgJHt5ZWFyfS0ke21vbnRofWAgOiBudWxsKTtcbiAgICB0aGlzLmNvbnRyb2wubWFya0FzVG91Y2hlZCgpO1xuICB9XG5cbiAgZm9ybWF0VmFsdWUodmFsdWU6IHVua25vd24pOiBzdHJpbmcge1xuICAgIGlmICghdmFsdWUpIHJldHVybiAn4oCUJztcbiAgICBjb25zdCBbeWVhciwgbW9udGhOdW1dID0gU3RyaW5nKHZhbHVlKS5zcGxpdCgnLScpO1xuICAgIGNvbnN0IG1vbnRoTmFtZSA9IE1PTlRIX05BTUVTW3BhcnNlSW50KG1vbnRoTnVtLCAxMCkgLSAxXTtcbiAgICByZXR1cm4gbW9udGhOYW1lICYmIHllYXIgPyBgJHttb250aE5hbWV9ICR7eWVhcn1gIDogU3RyaW5nKHZhbHVlKTtcbiAgfVxufVxuIl19
|
|
@@ -1,45 +1,92 @@
|
|
|
1
|
-
import { Component, Input } from '@angular/core';
|
|
1
|
+
import { Component, Input, inject, signal } from '@angular/core';
|
|
2
2
|
import { ReactiveFormsModule } from '@angular/forms';
|
|
3
|
+
import { getOptionStoredValue, resolveLabel, resolveOptionLabel, valuesMatch, } from '@dynamic-entity/core';
|
|
4
|
+
import { LookupRegistryService, refreshChoiceOptions } from '../services/lookup-registry.service';
|
|
3
5
|
import * as i0 from "@angular/core";
|
|
4
6
|
import * as i1 from "@angular/forms";
|
|
5
7
|
export class MultiSelectFieldComponent {
|
|
6
|
-
|
|
8
|
+
lookups = inject(LookupRegistryService);
|
|
9
|
+
_field;
|
|
10
|
+
_language = 'en';
|
|
11
|
+
/** Setter-based so options resolve however the input is set — see `refreshChoiceOptions`. */
|
|
12
|
+
set field(value) {
|
|
13
|
+
this._field = value;
|
|
14
|
+
refreshChoiceOptions(this, this.lookups);
|
|
15
|
+
}
|
|
16
|
+
get field() {
|
|
17
|
+
return this._field;
|
|
18
|
+
}
|
|
19
|
+
set language(value) {
|
|
20
|
+
this._language = value || 'en';
|
|
21
|
+
refreshChoiceOptions(this, this.lookups);
|
|
22
|
+
}
|
|
23
|
+
get language() {
|
|
24
|
+
return this._language;
|
|
25
|
+
}
|
|
7
26
|
control;
|
|
8
|
-
language = 'en';
|
|
9
27
|
readonly = false;
|
|
10
28
|
masked = false;
|
|
29
|
+
/** Inline `options`, or the field's named list resolved through the registry (§6.3). */
|
|
30
|
+
options = signal([]);
|
|
31
|
+
compareFn = (o1, o2) => valuesMatch(o1, o2, this.language);
|
|
32
|
+
isObjectVal(option) {
|
|
33
|
+
const val = getOptionStoredValue(option);
|
|
34
|
+
return typeof val === 'object' && val !== null;
|
|
35
|
+
}
|
|
36
|
+
get label() {
|
|
37
|
+
return resolveLabel(this.field?.label, this.language);
|
|
38
|
+
}
|
|
39
|
+
getOptStoredVal(option) {
|
|
40
|
+
return getOptionStoredValue(option);
|
|
41
|
+
}
|
|
42
|
+
getOptLabel(option) {
|
|
43
|
+
return resolveOptionLabel(option, this.language);
|
|
44
|
+
}
|
|
45
|
+
/** Read-only display — synchronous, per §6.2. See `DropdownFieldComponent.getLabel`. */
|
|
11
46
|
getLabels(values) {
|
|
12
47
|
if (!Array.isArray(values) || !values.length)
|
|
13
48
|
return '—';
|
|
14
49
|
return values
|
|
15
50
|
.map(v => {
|
|
16
|
-
const opt =
|
|
17
|
-
|
|
51
|
+
const opt = this.options().find(o => valuesMatch(getOptionStoredValue(o), v, this.language));
|
|
52
|
+
if (opt)
|
|
53
|
+
return this.getOptLabel(opt);
|
|
54
|
+
const cached = this.lookups.labelFor(this.field?.listName, v, this.language);
|
|
55
|
+
if (cached)
|
|
56
|
+
return cached;
|
|
57
|
+
return typeof v === 'object' ? resolveLabel(v, this.language) : String(v);
|
|
18
58
|
})
|
|
19
59
|
.join(', ');
|
|
20
60
|
}
|
|
21
61
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MultiSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
22
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: MultiSelectFieldComponent, isStandalone: true, selector: "ngx-multi-select-field", inputs: { field: "field",
|
|
23
|
-
<div class="ngx-field ngx-field--multiSelect"
|
|
24
|
-
|
|
62
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: MultiSelectFieldComponent, isStandalone: true, selector: "ngx-multi-select-field", inputs: { field: "field", language: "language", control: "control", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
63
|
+
<div class="ngx-field ngx-field--multiSelect"
|
|
64
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
65
|
+
<label class="ngx-field__label">{{ label }}</label>
|
|
25
66
|
@if (masked) {
|
|
26
|
-
<span class="ngx-field__value ngx-field__value--masked">XXXXXXXXX</span>
|
|
67
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
27
68
|
} @else if (readonly) {
|
|
28
|
-
<span class="ngx-field__value">{{ getLabels(control.value) }}</span>
|
|
69
|
+
<span class="ngx-field__value" [attr.data-testid]="'field-' + field.id + '-value'">{{ getLabels(control.value) }}</span>
|
|
29
70
|
} @else {
|
|
30
71
|
<select
|
|
31
|
-
|
|
72
|
+
[id]="field.id"
|
|
73
|
+
class="ngx-field__input" [attr.data-testid]="'field-' + field.id + '-input'"
|
|
32
74
|
[formControl]="$any(control)"
|
|
75
|
+
[compareWith]="compareFn"
|
|
33
76
|
[attr.disabled]="field.disabled ? true : null"
|
|
34
77
|
multiple
|
|
35
78
|
size="4"
|
|
36
79
|
>
|
|
37
|
-
@for (option of
|
|
38
|
-
|
|
80
|
+
@for (option of options(); track getOptLabel(option)) {
|
|
81
|
+
@if (isObjectVal(option)) {
|
|
82
|
+
<option [ngValue]="getOptStoredVal(option)">{{ getOptLabel(option) }}</option>
|
|
83
|
+
} @else {
|
|
84
|
+
<option [value]="getOptStoredVal(option)">{{ getOptLabel(option) }}</option>
|
|
85
|
+
}
|
|
39
86
|
}
|
|
40
87
|
</select>
|
|
41
88
|
@if (control.invalid && control.touched) {
|
|
42
|
-
<span class="ngx-field__error">This field has an error</span>
|
|
89
|
+
<span class="ngx-field__error" [attr.data-testid]="'field-' + field.id + '-error'">This field has an error</span>
|
|
43
90
|
}
|
|
44
91
|
}
|
|
45
92
|
</div>
|
|
@@ -52,26 +99,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
52
99
|
standalone: true,
|
|
53
100
|
imports: [ReactiveFormsModule],
|
|
54
101
|
template: `
|
|
55
|
-
<div class="ngx-field ngx-field--multiSelect"
|
|
56
|
-
|
|
102
|
+
<div class="ngx-field ngx-field--multiSelect"
|
|
103
|
+
[attr.data-testid]="'field-' + field.id" [attr.data-field-type]="field.type" [class.ngx-field--readonly]="readonly" [class.ngx-field--masked]="masked">
|
|
104
|
+
<label class="ngx-field__label">{{ label }}</label>
|
|
57
105
|
@if (masked) {
|
|
58
|
-
<span class="ngx-field__value ngx-field__value--masked">XXXXXXXXX</span>
|
|
106
|
+
<span class="ngx-field__value ngx-field__value--masked" [attr.data-testid]="'field-' + field.id + '-masked'">XXXXXXXXX</span>
|
|
59
107
|
} @else if (readonly) {
|
|
60
|
-
<span class="ngx-field__value">{{ getLabels(control.value) }}</span>
|
|
108
|
+
<span class="ngx-field__value" [attr.data-testid]="'field-' + field.id + '-value'">{{ getLabels(control.value) }}</span>
|
|
61
109
|
} @else {
|
|
62
110
|
<select
|
|
63
|
-
|
|
111
|
+
[id]="field.id"
|
|
112
|
+
class="ngx-field__input" [attr.data-testid]="'field-' + field.id + '-input'"
|
|
64
113
|
[formControl]="$any(control)"
|
|
114
|
+
[compareWith]="compareFn"
|
|
65
115
|
[attr.disabled]="field.disabled ? true : null"
|
|
66
116
|
multiple
|
|
67
117
|
size="4"
|
|
68
118
|
>
|
|
69
|
-
@for (option of
|
|
70
|
-
|
|
119
|
+
@for (option of options(); track getOptLabel(option)) {
|
|
120
|
+
@if (isObjectVal(option)) {
|
|
121
|
+
<option [ngValue]="getOptStoredVal(option)">{{ getOptLabel(option) }}</option>
|
|
122
|
+
} @else {
|
|
123
|
+
<option [value]="getOptStoredVal(option)">{{ getOptLabel(option) }}</option>
|
|
124
|
+
}
|
|
71
125
|
}
|
|
72
126
|
</select>
|
|
73
127
|
@if (control.invalid && control.touched) {
|
|
74
|
-
<span class="ngx-field__error">This field has an error</span>
|
|
128
|
+
<span class="ngx-field__error" [attr.data-testid]="'field-' + field.id + '-error'">This field has an error</span>
|
|
75
129
|
}
|
|
76
130
|
}
|
|
77
131
|
</div>
|
|
@@ -79,13 +133,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
79
133
|
}]
|
|
80
134
|
}], propDecorators: { field: [{
|
|
81
135
|
type: Input
|
|
82
|
-
}], control: [{
|
|
83
|
-
type: Input
|
|
84
136
|
}], language: [{
|
|
85
137
|
type: Input
|
|
138
|
+
}], control: [{
|
|
139
|
+
type: Input
|
|
86
140
|
}], readonly: [{
|
|
87
141
|
type: Input
|
|
88
142
|
}], masked: [{
|
|
89
143
|
type: Input
|
|
90
144
|
}] } });
|
|
91
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
145
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibXVsdGktc2VsZWN0LWZpZWxkLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvZmllbGQtdHlwZXMvbXVsdGktc2VsZWN0LWZpZWxkLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2pFLE9BQU8sRUFBbUIsbUJBQW1CLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV0RSxPQUFPLEVBQ0wsb0JBQW9CLEVBQ3BCLFlBQVksRUFDWixrQkFBa0IsRUFDbEIsV0FBVyxHQUNaLE1BQU0sc0JBQXNCLENBQUM7QUFDOUIsT0FBTyxFQUFFLHFCQUFxQixFQUFFLG9CQUFvQixFQUFFLE1BQU0scUNBQXFDLENBQUM7OztBQXVDbEcsTUFBTSxPQUFPLHlCQUF5QjtJQUNuQixPQUFPLEdBQUcsTUFBTSxDQUFDLHFCQUFxQixDQUFDLENBQUM7SUFFakQsTUFBTSxDQUFxQjtJQUMzQixTQUFTLEdBQUcsSUFBSSxDQUFDO0lBRXpCLDZGQUE2RjtJQUM3RixJQUFhLEtBQUssQ0FBQyxLQUF3QjtRQUN6QyxJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztRQUNwQixvQkFBb0IsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzNDLENBQUM7SUFDRCxJQUFJLEtBQUs7UUFDUCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUM7SUFDckIsQ0FBQztJQUVELElBQWEsUUFBUSxDQUFDLEtBQWE7UUFDakMsSUFBSSxDQUFDLFNBQVMsR0FBRyxLQUFLLElBQUksSUFBSSxDQUFDO1FBQy9CLG9CQUFvQixDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDM0MsQ0FBQztJQUNELElBQUksUUFBUTtRQUNWLE9BQU8sSUFBSSxDQUFD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fSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEFic3RyYWN0Q29udHJvbCwgUmVhY3RpdmVGb3Jtc01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB0eXBlIHsgRHJvcGRvd25PcHRpb24sIE5lc3RlZEZpZWxkQ29uZmlnIH0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xuaW1wb3J0IHtcbiAgZ2V0T3B0aW9uU3RvcmVkVmFsdWUsXG4gIHJlc29sdmVMYWJlbCxcbiAgcmVzb2x2ZU9wdGlvbkxhYmVsLFxuICB2YWx1ZXNNYXRjaCxcbn0gZnJvbSAnQGR5bmFtaWMtZW50aXR5L2NvcmUnO1xuaW1wb3J0IHsgTG9va3VwUmVnaXN0cnlTZXJ2aWNlLCByZWZyZXNoQ2hvaWNlT3B0aW9ucyB9IGZyb20gJy4uL3NlcnZpY2VzL2xvb2t1cC1yZWdpc3RyeS5zZXJ2aWNlJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbmd4LW11bHRpLXNlbGVjdC1maWVsZCcsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtSZWFjdGl2ZUZvcm1zTW9kdWxlXSxcbiAgdGVtcGxhdGU6IGBcbiAgICA8ZGl2IGNsYXNzPVwibmd4LWZpZWxkIG5neC1maWVsZC0tbXVsdGlTZWxlY3RcIlxuICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZFwiIFthdHRyLmRhdGEtZmllbGQtdHlwZV09XCJmaWVsZC50eXBlXCIgW2NsYXNzLm5neC1maWVsZC0tcmVhZG9ubHldPVwicmVhZG9ubHlcIiBbY2xhc3Mubmd4LWZpZWxkLS1tYXNrZWRdPVwibWFza2VkXCI+XG4gICAgICA8bGFiZWwgY2xhc3M9XCJuZ3gtZmllbGRfX2xhYmVsXCI+e3sgbGFiZWwgfX08L2xhYmVsPlxuICAgICAgQGlmIChtYXNrZWQpIHtcbiAgICAgICAgPHNwYW4gY2xhc3M9XCJuZ3gtZmllbGRfX3ZhbHVlIG5neC1maWVsZF9fdmFsdWUtLW1hc2tlZFwiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidmaWVsZC0nICsgZmllbGQuaWQgKyAnLW1hc2tlZCdcIj5YWFhYWFhYWFg8L3NwYW4+XG4gICAgICB9IEBlbHNlIGlmIChyZWFkb25seSkge1xuICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1maWVsZF9fdmFsdWVcIiBbYXR0ci5kYXRhLXRlc3RpZF09XCInZmllbGQtJyArIGZpZWxkLmlkICsgJy12YWx1ZSdcIj57eyBnZXRMYWJlbHMoY29udHJvbC52YWx1ZSkgfX08L3NwYW4+XG4gICAgICB9IEBlbHNlIHtcbiAgICAgICAgPHNlbGVjdFxuICAgICAgICAgIFtpZF09XCJmaWVsZC5pZFwiXG4gICAgICAgICAgY2xhc3M9XCJuZ3gtZmllbGRfX2lucHV0XCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ2ZpZWxkLScgKyBmaWVsZC5pZCArICctaW5wdXQnXCJcbiAgICAgICAgICBbZm9ybUNvbnRyb2xdPVwiJGFueShjb250cm9sKVwiXG4gICAgICAgICAgW2NvbXBhcmVXaXRoXT1cImNvbXBhcmVGblwiXG4gICAgICAgICAgW2F0dHIuZGlzYWJsZWRdPVwiZmllbGQuZGlzYWJsZWQgPyB0cnVlIDogbnVsbFwiXG4gICAgICAgICAgbXVsdGlwbGVcbiAgICAgICAgICBzaXplPVwiNFwiXG4gICAgICAgID5cbiAgICAgICAgICBAZm9yIChvcHRpb24gb2Ygb3B0aW9ucygpOyB0cmFjayBnZXRPcHRMYWJlbChvcHRpb24pKSB7XG4gICAgICAgICAgICBAaWYgKGlzT2JqZWN0VmFsKG9wdGlvbikpIHtcbiAgICAgICAgICAgICAgPG9wdGlvbiBbbmdWYWx1ZV09XCJnZXRPcHRTdG9yZWRWYWwob3B0aW9uKVwiPnt7IGdldE9wdExhYmVsKG9wdGlvbikgfX08L29wdGlvbj5cbiAgICAgICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgICAgICA8b3B0aW9uIFt2YWx1ZV09XCJnZXRPcHRTdG9yZWRWYWwob3B0aW9uKVwiPnt7IGdldE9wdExhYmVsKG9wdGlvbikgfX08L29wdGlvbj5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIDwvc2VsZWN0PlxuICAgICAgICBAaWYgKGNvbnRyb2wuaW52YWxpZCAmJiBjb250cm9sLnRvdWNoZWQpIHtcbiAgICAgICAgICA8c3BhbiBjbGFzcz1cIm5neC1maWVsZF9fZXJyb3JcIiBbYXR0ci5kYXRhLXRlc3RpZF09XCInZmllbGQtJyArIGZpZWxkLmlkICsgJy1lcnJvcidcIj5UaGlzIGZpZWxkIGhhcyBhbiBlcnJvcjwvc3Bhbj5cbiAgICAgICAgfVxuICAgICAgfVxuICAgIDwvZGl2PlxuICBgLFxufSlcbmV4cG9ydCBjbGFzcyBNdWx0aVNlbGVjdEZpZWxkQ29tcG9uZW50IHtcbiAgcHJpdmF0ZSByZWFkb25seSBsb29rdXBzID0gaW5qZWN0KExvb2t1cFJlZ2lzdHJ5U2VydmljZSk7XG5cbiAgcHJpdmF0ZSBfZmllbGQhOiBOZXN0ZWRGaWVsZENvbmZpZztcbiAgcHJpdmF0ZSBfbGFuZ3VhZ2UgPSAnZW4nO1xuXG4gIC8qKiBTZXR0ZXItYmFzZWQgc28gb3B0aW9ucyByZXNvbHZlIGhvd2V2ZXIgdGhlIGlucHV0IGlzIHNldCDigJQgc2VlIGByZWZyZXNoQ2hvaWNlT3B0aW9uc2AuICovXG4gIEBJbnB1dCgpIHNldCBmaWVsZCh2YWx1ZTogTmVzdGVkRmllbGRDb25maWcpIHtcbiAgICB0aGlzLl9maWVsZCA9IHZhbHVlO1xuICAgIHJlZnJlc2hDaG9pY2VPcHRpb25zKHRoaXMsIHRoaXMubG9va3Vwcyk7XG4gIH1cbiAgZ2V0IGZpZWxkKCk6IE5lc3RlZEZpZWxkQ29uZmlnIHtcbiAgICByZXR1cm4gdGhpcy5fZmllbGQ7XG4gIH1cblxuICBASW5wdXQoKSBzZXQgbGFuZ3VhZ2UodmFsdWU6IHN0cmluZykge1xuICAgIHRoaXMuX2xhbmd1YWdlID0gdmFsdWUgfHwgJ2VuJztcbiAgICByZWZyZXNoQ2hvaWNlT3B0aW9ucyh0aGlzLCB0aGlzLmxvb2t1cHMpO1xuICB9XG4gIGdldCBsYW5ndWFnZSgpOiBzdHJpbmcge1xuICAgIHJldHVybiB0aGlzLl9sYW5ndWFnZTtcbiAgfVxuXG4gIEBJbnB1dCgpIGNvbnRyb2whOiBBYnN0cmFjdENvbnRyb2w7XG4gIEBJbnB1dCgpIHJlYWRvbmx5OiBib29sZWFuID0gZmFsc2U7XG4gIEBJbnB1dCgpIG1hc2tlZDogYm9vbGVhbiA9IGZhbHNlO1xuXG4gIC8qKiBJbmxpbmUgYG9wdGlvbnNgLCBvciB0aGUgZmllbGQncyBuYW1lZCBsaXN0IHJlc29sdmVkIHRocm91Z2ggdGhlIHJlZ2lzdHJ5ICjCpzYuMykuICovXG4gIHJlYWRvbmx5IG9wdGlvbnMgPSBzaWduYWw8RHJvcGRvd25PcHRpb25bXT4oW10pO1xuXG4gIHJlYWRvbmx5IGNvbXBhcmVGbiA9IChvMTogYW55LCBvMjogYW55KTogYm9vbGVhbiA9PiB2YWx1ZXNNYXRjaChvMSwgbzIsIHRoaXMubGFuZ3VhZ2UpO1xuXG4gIGlzT2JqZWN0VmFsKG9wdGlvbjogYW55KTogYm9vbGVhbiB7XG4gICAgY29uc3QgdmFsID0gZ2V0T3B0aW9uU3RvcmVkVmFsdWUob3B0aW9uKTtcbiAgICByZXR1cm4gdHlwZW9mIHZhbCA9PT0gJ29iamVjdCcgJiYgdmFsICE9PSBudWxsO1xuICB9XG5cbiAgZ2V0IGxhYmVsKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHJlc29sdmVMYWJlbCh0aGlzLmZpZWxkPy5sYWJlbCwgdGhpcy5sYW5ndWFnZSk7XG4gIH1cblxuICBnZXRPcHRTdG9yZWRWYWwob3B0aW9uOiBhbnkpOiBhbnkge1xuICAgIHJldHVybiBnZXRPcHRpb25TdG9yZWRWYWx1ZShvcHRpb24pO1xuICB9XG5cbiAgZ2V0T3B0TGFiZWwob3B0aW9uOiBhbnkpOiBzdHJpbmcge1xuICAgIHJldHVybiByZXNvbHZlT3B0aW9uTGFiZWwob3B0aW9uLCB0aGlzLmxhbmd1YWdlKTtcbiAgfVxuXG4gIC8qKiBSZWFkLW9ubHkgZGlzcGxheSDigJQgc3luY2hyb25vdXMsIHBlciDCpzYuMi4gU2VlIGBEcm9wZG93bkZpZWxkQ29tcG9uZW50LmdldExhYmVsYC4gKi9cbiAgZ2V0TGFiZWxzKHZhbHVlczogYW55W10pOiBzdHJpbmcge1xuICAgIGlmICghQXJyYXkuaXNBcnJheSh2YWx1ZXMpIHx8ICF2YWx1ZXMubGVuZ3RoKSByZXR1cm4gJ+KAlCc7XG4gICAgcmV0dXJuIHZhbHVlc1xuICAgICAgLm1hcCh2ID0+IHtcbiAgICAgICAgY29uc3Qgb3B0ID0gdGhpcy5vcHRpb25zKCkuZmluZChvID0+IHZhbHVlc01hdGNoKGdldE9wdGlvblN0b3JlZFZhbHVlKG8pLCB2LCB0aGlzLmxhbmd1YWdlKSk7XG4gICAgICAgIGlmIChvcHQpIHJldHVybiB0aGlzLmdldE9wdExhYmVsKG9wdCk7XG4gICAgICAgIGNvbnN0IGNhY2hlZCA9IHRoaXMubG9va3Vwcy5sYWJlbEZvcih0aGlzLmZpZWxkPy5saXN0TmFtZSwgdiwgdGhpcy5sYW5ndWFnZSk7XG4gICAgICAgIGlmIChjYWNoZWQpIHJldHVybiBjYWNoZWQ7XG4gICAgICAgIHJldHVybiB0eXBlb2YgdiA9PT0gJ29iamVjdCcgPyByZXNvbHZlTGFiZWwodiwgdGhpcy5sYW5ndWFnZSkgOiBTdHJpbmcodik7XG4gICAgICB9KVxuICAgICAgLmpvaW4oJywgJyk7XG4gIH1cbn1cbiJdfQ==
|