ngx-dynamic-entity-builder 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 +54 -0
- package/esm2022/lib/builder-store.service.mjs +923 -0
- package/esm2022/lib/clone.mjs +14 -0
- package/esm2022/lib/components/connection-source-config.component.mjs +121 -0
- package/esm2022/lib/components/entity-builder-canvas.component.mjs +83 -0
- package/esm2022/lib/components/entity-builder-tree-node.component.mjs +173 -0
- package/esm2022/lib/components/entity-reference-config.component.mjs +405 -0
- package/esm2022/lib/components/field-inspector.component.mjs +139 -0
- package/esm2022/lib/components/field-palette.component.mjs +57 -0
- package/esm2022/lib/components/field-rules-list.component.mjs +224 -0
- package/esm2022/lib/components/referenced-field-config.component.mjs +228 -0
- package/esm2022/lib/components/rule-form.component.mjs +201 -0
- package/esm2022/lib/components/tab-manager.component.mjs +341 -0
- package/esm2022/lib/entity-builder.component.mjs +153 -0
- package/esm2022/lib/field-catalog.mjs +6 -0
- package/esm2022/ngx-dynamic-entity-builder.mjs +5 -0
- package/esm2022/public-api.mjs +17 -0
- package/fesm2022/ngx-dynamic-entity-builder.mjs +2910 -0
- package/fesm2022/ngx-dynamic-entity-builder.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/builder-store.service.d.ts +165 -0
- package/lib/clone.d.ts +9 -0
- package/lib/components/connection-source-config.component.d.ts +16 -0
- package/lib/components/entity-builder-canvas.component.d.ts +9 -0
- package/lib/components/entity-builder-tree-node.component.d.ts +17 -0
- package/lib/components/entity-reference-config.component.d.ts +29 -0
- package/lib/components/field-inspector.component.d.ts +33 -0
- package/lib/components/field-palette.component.d.ts +13 -0
- package/lib/components/field-rules-list.component.d.ts +19 -0
- package/lib/components/referenced-field-config.component.d.ts +18 -0
- package/lib/components/rule-form.component.d.ts +16 -0
- package/lib/components/tab-manager.component.d.ts +19 -0
- package/lib/entity-builder.component.d.ts +42 -0
- package/lib/field-catalog.d.ts +5 -0
- package/package.json +42 -0
- package/public-api.d.ts +13 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep clone with a JSON fallback.
|
|
3
|
+
*
|
|
4
|
+
* `structuredClone` is not universally available (older browsers, and jsdom before v22),
|
|
5
|
+
* so it must never be called unguarded — an unguarded call throws a ReferenceError and the
|
|
6
|
+
* caller silently does nothing. Config and rule objects are JSON-safe, so the fallback is
|
|
7
|
+
* lossless here.
|
|
8
|
+
*/
|
|
9
|
+
export function deepClone(value) {
|
|
10
|
+
if (typeof structuredClone === 'function')
|
|
11
|
+
return structuredClone(value);
|
|
12
|
+
return JSON.parse(JSON.stringify(value));
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvbmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL2Nsb25lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsU0FBUyxDQUFJLEtBQVE7SUFDbkMsSUFBSSxPQUFPLGVBQWUsS0FBSyxVQUFVO1FBQUUsT0FBTyxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDekUsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQU0sQ0FBQztBQUNoRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXHJcbiAqIERlZXAgY2xvbmUgd2l0aCBhIEpTT04gZmFsbGJhY2suXHJcbiAqXHJcbiAqIGBzdHJ1Y3R1cmVkQ2xvbmVgIGlzIG5vdCB1bml2ZXJzYWxseSBhdmFpbGFibGUgKG9sZGVyIGJyb3dzZXJzLCBhbmQganNkb20gYmVmb3JlIHYyMiksXHJcbiAqIHNvIGl0IG11c3QgbmV2ZXIgYmUgY2FsbGVkIHVuZ3VhcmRlZCDigJQgYW4gdW5ndWFyZGVkIGNhbGwgdGhyb3dzIGEgUmVmZXJlbmNlRXJyb3IgYW5kIHRoZVxyXG4gKiBjYWxsZXIgc2lsZW50bHkgZG9lcyBub3RoaW5nLiBDb25maWcgYW5kIHJ1bGUgb2JqZWN0cyBhcmUgSlNPTi1zYWZlLCBzbyB0aGUgZmFsbGJhY2sgaXNcclxuICogbG9zc2xlc3MgaGVyZS5cclxuICovXHJcbmV4cG9ydCBmdW5jdGlvbiBkZWVwQ2xvbmU8VD4odmFsdWU6IFQpOiBUIHtcclxuICBpZiAodHlwZW9mIHN0cnVjdHVyZWRDbG9uZSA9PT0gJ2Z1bmN0aW9uJykgcmV0dXJuIHN0cnVjdHVyZWRDbG9uZSh2YWx1ZSk7XHJcbiAgcmV0dXJuIEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkodmFsdWUpKSBhcyBUO1xyXG59XHJcbiJdfQ==
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Component, computed, inject } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
5
|
+
import { MatInputModule } from '@angular/material/input';
|
|
6
|
+
import { MatSelectModule } from '@angular/material/select';
|
|
7
|
+
import { BuilderStore } from '../builder-store.service';
|
|
8
|
+
import * as i0 from "@angular/core";
|
|
9
|
+
import * as i1 from "@angular/forms";
|
|
10
|
+
import * as i2 from "@angular/material/form-field";
|
|
11
|
+
import * as i3 from "@angular/material/input";
|
|
12
|
+
/**
|
|
13
|
+
* ConnectionSourceConfigComponent — allows configuring connection source settings
|
|
14
|
+
* for an entity or field (Phase 7.2).
|
|
15
|
+
*/
|
|
16
|
+
export class ConnectionSourceConfigComponent {
|
|
17
|
+
store = inject(BuilderStore);
|
|
18
|
+
field = computed(() => this.store.selectedField());
|
|
19
|
+
connectionSourceEntity(field) {
|
|
20
|
+
return field.connectionSource?.entity ?? '';
|
|
21
|
+
}
|
|
22
|
+
setConnectionSourceEntity(field, entity) {
|
|
23
|
+
const trimmed = entity.trim();
|
|
24
|
+
const existing = field.connectionSource ?? {};
|
|
25
|
+
if (!trimmed && !existing.targetField) {
|
|
26
|
+
this.store.updateField(field.id, { connectionSource: undefined });
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
this.store.updateField(field.id, {
|
|
30
|
+
connectionSource: { ...existing, entity: trimmed },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
connectionTargetField(field) {
|
|
35
|
+
return field.connectionSource?.targetField ?? '';
|
|
36
|
+
}
|
|
37
|
+
setConnectionTargetField(field, targetField) {
|
|
38
|
+
const trimmed = targetField.trim();
|
|
39
|
+
const existing = field.connectionSource ?? {};
|
|
40
|
+
if (!trimmed && !existing.entity) {
|
|
41
|
+
this.store.updateField(field.id, { connectionSource: undefined });
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
this.store.updateField(field.id, {
|
|
45
|
+
connectionSource: { ...existing, targetField: trimmed },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionSourceConfigComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
50
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: ConnectionSourceConfigComponent, isStandalone: true, selector: "ngx-connection-source-config", ngImport: i0, template: `
|
|
51
|
+
@if (field(); as f) {
|
|
52
|
+
<div class="deb-connection-source">
|
|
53
|
+
<span class="deb-section-title">Connection Source</span>
|
|
54
|
+
<mat-form-field appearance="outline" subscriptSizing="dynamic" class="deb-full">
|
|
55
|
+
<mat-label>Source Entity</mat-label>
|
|
56
|
+
<input
|
|
57
|
+
matInput
|
|
58
|
+
data-testid="connection-source-entity"
|
|
59
|
+
[ngModel]="connectionSourceEntity(f)"
|
|
60
|
+
(ngModelChange)="setConnectionSourceEntity(f, $event)"
|
|
61
|
+
placeholder="e.g. organizations"
|
|
62
|
+
/>
|
|
63
|
+
</mat-form-field>
|
|
64
|
+
|
|
65
|
+
<mat-form-field appearance="outline" subscriptSizing="dynamic" class="deb-full">
|
|
66
|
+
<mat-label>Target Field ID</mat-label>
|
|
67
|
+
<input
|
|
68
|
+
matInput
|
|
69
|
+
data-testid="connection-target-field"
|
|
70
|
+
[ngModel]="connectionTargetField(f)"
|
|
71
|
+
(ngModelChange)="setConnectionTargetField(f, $event)"
|
|
72
|
+
placeholder="e.g. companyId"
|
|
73
|
+
/>
|
|
74
|
+
</mat-form-field>
|
|
75
|
+
</div>
|
|
76
|
+
}
|
|
77
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }] });
|
|
78
|
+
}
|
|
79
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ConnectionSourceConfigComponent, decorators: [{
|
|
80
|
+
type: Component,
|
|
81
|
+
args: [{
|
|
82
|
+
selector: 'ngx-connection-source-config',
|
|
83
|
+
standalone: true,
|
|
84
|
+
imports: [
|
|
85
|
+
CommonModule,
|
|
86
|
+
FormsModule,
|
|
87
|
+
MatFormFieldModule,
|
|
88
|
+
MatInputModule,
|
|
89
|
+
MatSelectModule,
|
|
90
|
+
],
|
|
91
|
+
template: `
|
|
92
|
+
@if (field(); as f) {
|
|
93
|
+
<div class="deb-connection-source">
|
|
94
|
+
<span class="deb-section-title">Connection Source</span>
|
|
95
|
+
<mat-form-field appearance="outline" subscriptSizing="dynamic" class="deb-full">
|
|
96
|
+
<mat-label>Source Entity</mat-label>
|
|
97
|
+
<input
|
|
98
|
+
matInput
|
|
99
|
+
data-testid="connection-source-entity"
|
|
100
|
+
[ngModel]="connectionSourceEntity(f)"
|
|
101
|
+
(ngModelChange)="setConnectionSourceEntity(f, $event)"
|
|
102
|
+
placeholder="e.g. organizations"
|
|
103
|
+
/>
|
|
104
|
+
</mat-form-field>
|
|
105
|
+
|
|
106
|
+
<mat-form-field appearance="outline" subscriptSizing="dynamic" class="deb-full">
|
|
107
|
+
<mat-label>Target Field ID</mat-label>
|
|
108
|
+
<input
|
|
109
|
+
matInput
|
|
110
|
+
data-testid="connection-target-field"
|
|
111
|
+
[ngModel]="connectionTargetField(f)"
|
|
112
|
+
(ngModelChange)="setConnectionTargetField(f, $event)"
|
|
113
|
+
placeholder="e.g. companyId"
|
|
114
|
+
/>
|
|
115
|
+
</mat-form-field>
|
|
116
|
+
</div>
|
|
117
|
+
}
|
|
118
|
+
`,
|
|
119
|
+
}]
|
|
120
|
+
}] });
|
|
121
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29ubmVjdGlvbi1zb3VyY2UtY29uZmlnLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9jb25uZWN0aW9uLXNvdXJjZS1jb25maWcuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUM1RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzdDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ2xFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDM0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDBCQUEwQixDQUFDOzs7OztBQUV4RDs7O0dBR0c7QUF3Q0gsTUFBTSxPQUFPLCtCQUErQjtJQUN2QixLQUFLLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQzdCLEtBQUssR0FBRyxRQUFRLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxhQUFhLEVBQUUsQ0FBQyxDQUFDO0lBRTVELHNCQUFzQixDQUFDLEtBQVU7UUFDekMsT0FBTyxLQUFLLENBQUMsZ0JBQWdCLEVBQUUsTUFBTSxJQUFJLEVBQUUsQ0FBQztJQUM5QyxDQUFDO0lBRVMseUJBQXlCLENBQUMsS0FBVSxFQUFFLE1BQWM7UUFDNUQsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO1FBQzlCLE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQyxnQkFBZ0IsSUFBSSxFQUFFLENBQUM7UUFDOUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUN0QyxJQUFJLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQUMsRUFBRSxFQUFFLEVBQUUsZ0JBQWdCLEVBQUUsU0FBUyxFQUFTLENBQUMsQ0FBQztRQUMzRSxDQUFDO2FBQU0sQ0FBQztZQUNOLElBQUksQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUFFLEVBQUU7Z0JBQy9CLGdCQUFnQixFQUFFLEVBQUUsR0FBRyxRQUFRLEVBQUUsTUFBTSxFQUFFLE9BQU8sRUFBRTthQUM1QyxDQUFDLENBQUM7UUFDWixDQUFDO0lBQ0gsQ0FBQztJQUVTLHFCQUFxQixDQUFDLEtBQVU7UUFDeEMsT0FBTyxLQUFLLENBQUMsZ0JBQWdCLEVBQUUsV0FBVyxJQUFJLEVBQUUsQ0FBQztJQUNuRCxDQUFDO0lBRVMsd0JBQXdCLENBQUMsS0FBVSxFQUFFLFdBQW1CO1FBQ2hFLE1BQU0sT0FBTyxHQUFHLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNuQyxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsZ0JBQWdCLElBQUksRUFBRSxDQUFDO1FBQzlDLElBQUksQ0FBQyxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDakMsSUFBSSxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLEVBQUUsRUFBRSxFQUFFLGdCQUFnQixFQUFFLFNBQVMsRUFBUyxDQUFDLENBQUM7UUFDM0UsQ0FBQzthQUFNLENBQUM7WUFDTixJQUFJLENBQUMsS0FBSyxDQUFDLFdBQVcsQ0FBQyxLQUFLLENBQUMsRUFBRSxFQUFFO2dCQUMvQixnQkFBZ0IsRUFBRSxFQUFFLEdBQUcsUUFBUSxFQUFFLFdBQVcsRUFBRSxPQUFPLEVBQUU7YUFDakQsQ0FBQyxDQUFDO1FBQ1osQ0FBQztJQUNILENBQUM7d0dBbENVLCtCQUErQjs0RkFBL0IsK0JBQStCLHdGQTdCaEM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTJCVCwyREFqQ0MsWUFBWSw4QkFDWixXQUFXLDhtQkFDWCxrQkFBa0IsMFNBQ2xCLGNBQWMsMFdBQ2QsZUFBZTs7NEZBK0JOLCtCQUErQjtrQkF2QzNDLFNBQVM7bUJBQUM7b0JBQ1QsUUFBUSxFQUFFLDhCQUE4QjtvQkFDeEMsVUFBVSxFQUFFLElBQUk7b0JBQ2hCLE9BQU8sRUFBRTt3QkFDUCxZQUFZO3dCQUNaLFdBQVc7d0JBQ1gsa0JBQWtCO3dCQUNsQixjQUFjO3dCQUNkLGVBQWU7cUJBQ2hCO29CQUNELFFBQVEsRUFBRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBMkJUO2lCQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBjb21wdXRlZCwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgRm9ybXNNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9mb3Jtcyc7XG5pbXBvcnQgeyBNYXRGb3JtRmllbGRNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9mb3JtLWZpZWxkJztcbmltcG9ydCB7IE1hdElucHV0TW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvaW5wdXQnO1xuaW1wb3J0IHsgTWF0U2VsZWN0TW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvc2VsZWN0JztcbmltcG9ydCB7IEJ1aWxkZXJTdG9yZSB9IGZyb20gJy4uL2J1aWxkZXItc3RvcmUuc2VydmljZSc7XG5cbi8qKlxuICogQ29ubmVjdGlvblNvdXJjZUNvbmZpZ0NvbXBvbmVudCDigJQgYWxsb3dzIGNvbmZpZ3VyaW5nIGNvbm5lY3Rpb24gc291cmNlIHNldHRpbmdzXG4gKiBmb3IgYW4gZW50aXR5IG9yIGZpZWxkIChQaGFzZSA3LjIpLlxuICovXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICduZ3gtY29ubmVjdGlvbi1zb3VyY2UtY29uZmlnJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIENvbW1vbk1vZHVsZSxcbiAgICBGb3Jtc01vZHVsZSxcbiAgICBNYXRGb3JtRmllbGRNb2R1bGUsXG4gICAgTWF0SW5wdXRNb2R1bGUsXG4gICAgTWF0U2VsZWN0TW9kdWxlLFxuICBdLFxuICB0ZW1wbGF0ZTogYFxuICAgIEBpZiAoZmllbGQoKTsgYXMgZikge1xuICAgICAgPGRpdiBjbGFzcz1cImRlYi1jb25uZWN0aW9uLXNvdXJjZVwiPlxuICAgICAgICA8c3BhbiBjbGFzcz1cImRlYi1zZWN0aW9uLXRpdGxlXCI+Q29ubmVjdGlvbiBTb3VyY2U8L3NwYW4+XG4gICAgICAgIDxtYXQtZm9ybS1maWVsZCBhcHBlYXJhbmNlPVwib3V0bGluZVwiIHN1YnNjcmlwdFNpemluZz1cImR5bmFtaWNcIiBjbGFzcz1cImRlYi1mdWxsXCI+XG4gICAgICAgICAgPG1hdC1sYWJlbD5Tb3VyY2UgRW50aXR5PC9tYXQtbGFiZWw+XG4gICAgICAgICAgPGlucHV0XG4gICAgICAgICAgICBtYXRJbnB1dFxuICAgICAgICAgICAgZGF0YS10ZXN0aWQ9XCJjb25uZWN0aW9uLXNvdXJjZS1lbnRpdHlcIlxuICAgICAgICAgICAgW25nTW9kZWxdPVwiY29ubmVjdGlvblNvdXJjZUVudGl0eShmKVwiXG4gICAgICAgICAgICAobmdNb2RlbENoYW5nZSk9XCJzZXRDb25uZWN0aW9uU291cmNlRW50aXR5KGYsICRldmVudClcIlxuICAgICAgICAgICAgcGxhY2Vob2xkZXI9XCJlLmcuIG9yZ2FuaXphdGlvbnNcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XG5cbiAgICAgICAgPG1hdC1mb3JtLWZpZWxkIGFwcGVhcmFuY2U9XCJvdXRsaW5lXCIgc3Vic2NyaXB0U2l6aW5nPVwiZHluYW1pY1wiIGNsYXNzPVwiZGViLWZ1bGxcIj5cbiAgICAgICAgICA8bWF0LWxhYmVsPlRhcmdldCBGaWVsZCBJRDwvbWF0LWxhYmVsPlxuICAgICAgICAgIDxpbnB1dFxuICAgICAgICAgICAgbWF0SW5wdXRcbiAgICAgICAgICAgIGRhdGEtdGVzdGlkPVwiY29ubmVjdGlvbi10YXJnZXQtZmllbGRcIlxuICAgICAgICAgICAgW25nTW9kZWxdPVwiY29ubmVjdGlvblRhcmdldEZpZWxkKGYpXCJcbiAgICAgICAgICAgIChuZ01vZGVsQ2hhbmdlKT1cInNldENvbm5lY3Rpb25UYXJnZXRGaWVsZChmLCAkZXZlbnQpXCJcbiAgICAgICAgICAgIHBsYWNlaG9sZGVyPVwiZS5nLiBjb21wYW55SWRcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvbWF0LWZvcm0tZmllbGQ+XG4gICAgICA8L2Rpdj5cbiAgICB9XG4gIGAsXG59KVxuZXhwb3J0IGNsYXNzIENvbm5lY3Rpb25Tb3VyY2VDb25maWdDb21wb25lbnQge1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgc3RvcmUgPSBpbmplY3QoQnVpbGRlclN0b3JlKTtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IGZpZWxkID0gY29tcHV0ZWQoKCkgPT4gdGhpcy5zdG9yZS5zZWxlY3RlZEZpZWxkKCkpO1xuXG4gIHByb3RlY3RlZCBjb25uZWN0aW9uU291cmNlRW50aXR5KGZpZWxkOiBhbnkpOiBzdHJpbmcge1xuICAgIHJldHVybiBmaWVsZC5jb25uZWN0aW9uU291cmNlPy5lbnRpdHkgPz8gJyc7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2V0Q29ubmVjdGlvblNvdXJjZUVudGl0eShmaWVsZDogYW55LCBlbnRpdHk6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IHRyaW1tZWQgPSBlbnRpdHkudHJpbSgpO1xuICAgIGNvbnN0IGV4aXN0aW5nID0gZmllbGQuY29ubmVjdGlvblNvdXJjZSA/PyB7fTtcbiAgICBpZiAoIXRyaW1tZWQgJiYgIWV4aXN0aW5nLnRhcmdldEZpZWxkKSB7XG4gICAgICB0aGlzLnN0b3JlLnVwZGF0ZUZpZWxkKGZpZWxkLmlkLCB7IGNvbm5lY3Rpb25Tb3VyY2U6IHVuZGVmaW5lZCB9IGFzIGFueSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuc3RvcmUudXBkYXRlRmllbGQoZmllbGQuaWQsIHtcbiAgICAgICAgY29ubmVjdGlvblNvdXJjZTogeyAuLi5leGlzdGluZywgZW50aXR5OiB0cmltbWVkIH0sXG4gICAgICB9IGFzIGFueSk7XG4gICAgfVxuICB9XG5cbiAgcHJvdGVjdGVkIGNvbm5lY3Rpb25UYXJnZXRGaWVsZChmaWVsZDogYW55KTogc3RyaW5nIHtcbiAgICByZXR1cm4gZmllbGQuY29ubmVjdGlvblNvdXJjZT8udGFyZ2V0RmllbGQgPz8gJyc7XG4gIH1cblxuICBwcm90ZWN0ZWQgc2V0Q29ubmVjdGlvblRhcmdldEZpZWxkKGZpZWxkOiBhbnksIHRhcmdldEZpZWxkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCB0cmltbWVkID0gdGFyZ2V0RmllbGQudHJpbSgpO1xuICAgIGNvbnN0IGV4aXN0aW5nID0gZmllbGQuY29ubmVjdGlvblNvdXJjZSA/PyB7fTtcbiAgICBpZiAoIXRyaW1tZWQgJiYgIWV4aXN0aW5nLmVudGl0eSkge1xuICAgICAgdGhpcy5zdG9yZS51cGRhdGVGaWVsZChmaWVsZC5pZCwgeyBjb25uZWN0aW9uU291cmNlOiB1bmRlZmluZWQgfSBhcyBhbnkpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnN0b3JlLnVwZGF0ZUZpZWxkKGZpZWxkLmlkLCB7XG4gICAgICAgIGNvbm5lY3Rpb25Tb3VyY2U6IHsgLi4uZXhpc3RpbmcsIHRhcmdldEZpZWxkOiB0cmltbWVkIH0sXG4gICAgICB9IGFzIGFueSk7XG4gICAgfVxuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Component, inject } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { DragDropModule } from '@angular/cdk/drag-drop';
|
|
4
|
+
import { MatCardModule } from '@angular/material/card';
|
|
5
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
6
|
+
import { BuilderStore } from '../builder-store.service';
|
|
7
|
+
import { EntityBuilderTreeNodeComponent } from './entity-builder-tree-node.component';
|
|
8
|
+
import * as i0 from "@angular/core";
|
|
9
|
+
import * as i1 from "@angular/cdk/drag-drop";
|
|
10
|
+
import * as i2 from "@angular/material/card";
|
|
11
|
+
import * as i3 from "@angular/material/icon";
|
|
12
|
+
export class EntityBuilderCanvasComponent {
|
|
13
|
+
store = inject(BuilderStore);
|
|
14
|
+
onDrop(event) {
|
|
15
|
+
this.store.reorderField(event.previousIndex, event.currentIndex);
|
|
16
|
+
}
|
|
17
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityBuilderCanvasComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: EntityBuilderCanvasComponent, isStandalone: true, selector: "ngx-entity-builder-canvas", ngImport: i0, template: `
|
|
19
|
+
<mat-card class="deb-canvas">
|
|
20
|
+
<mat-card-header>
|
|
21
|
+
<mat-card-title>Fields ({{ store.fields().length }})</mat-card-title>
|
|
22
|
+
</mat-card-header>
|
|
23
|
+
<mat-card-content>
|
|
24
|
+
@if (store.fields().length === 0) {
|
|
25
|
+
<div class="deb-empty">
|
|
26
|
+
<mat-icon>widgets</mat-icon>
|
|
27
|
+
<p>No fields yet. Pick a type from <strong>Add field</strong> to get started.</p>
|
|
28
|
+
</div>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
<div cdkDropList class="deb-field-list" data-testid="builder-field-list" (cdkDropListDropped)="onDrop($event)">
|
|
32
|
+
@for (f of store.fields(); track f.id; let i = $index, count = $count) {
|
|
33
|
+
<ngx-entity-builder-tree-node
|
|
34
|
+
[field]="f"
|
|
35
|
+
[index]="i"
|
|
36
|
+
[totalCount]="count"
|
|
37
|
+
/>
|
|
38
|
+
}
|
|
39
|
+
</div>
|
|
40
|
+
</mat-card-content>
|
|
41
|
+
</mat-card>
|
|
42
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i2.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i2.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i2.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i2.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: EntityBuilderTreeNodeComponent, selector: "ngx-entity-builder-tree-node", inputs: ["field", "index", "totalCount"] }] });
|
|
43
|
+
}
|
|
44
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityBuilderCanvasComponent, decorators: [{
|
|
45
|
+
type: Component,
|
|
46
|
+
args: [{
|
|
47
|
+
selector: 'ngx-entity-builder-canvas',
|
|
48
|
+
standalone: true,
|
|
49
|
+
imports: [
|
|
50
|
+
CommonModule,
|
|
51
|
+
DragDropModule,
|
|
52
|
+
MatCardModule,
|
|
53
|
+
MatIconModule,
|
|
54
|
+
EntityBuilderTreeNodeComponent,
|
|
55
|
+
],
|
|
56
|
+
template: `
|
|
57
|
+
<mat-card class="deb-canvas">
|
|
58
|
+
<mat-card-header>
|
|
59
|
+
<mat-card-title>Fields ({{ store.fields().length }})</mat-card-title>
|
|
60
|
+
</mat-card-header>
|
|
61
|
+
<mat-card-content>
|
|
62
|
+
@if (store.fields().length === 0) {
|
|
63
|
+
<div class="deb-empty">
|
|
64
|
+
<mat-icon>widgets</mat-icon>
|
|
65
|
+
<p>No fields yet. Pick a type from <strong>Add field</strong> to get started.</p>
|
|
66
|
+
</div>
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
<div cdkDropList class="deb-field-list" data-testid="builder-field-list" (cdkDropListDropped)="onDrop($event)">
|
|
70
|
+
@for (f of store.fields(); track f.id; let i = $index, count = $count) {
|
|
71
|
+
<ngx-entity-builder-tree-node
|
|
72
|
+
[field]="f"
|
|
73
|
+
[index]="i"
|
|
74
|
+
[totalCount]="count"
|
|
75
|
+
/>
|
|
76
|
+
}
|
|
77
|
+
</div>
|
|
78
|
+
</mat-card-content>
|
|
79
|
+
</mat-card>
|
|
80
|
+
`,
|
|
81
|
+
}]
|
|
82
|
+
}] });
|
|
83
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW50aXR5LWJ1aWxkZXItY2FudmFzLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9lbnRpdHktYnVpbGRlci1jYW52YXMuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ2xELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMvQyxPQUFPLEVBQWUsY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDckUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN2RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDeEQsT0FBTyxFQUFFLDhCQUE4QixFQUFFLE1BQU0sc0NBQXNDLENBQUM7Ozs7O0FBc0N0RixNQUFNLE9BQU8sNEJBQTRCO0lBQ3BCLEtBQUssR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7SUFFdEMsTUFBTSxDQUFDLEtBQTJCO1FBQzFDLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLEtBQUssQ0FBQyxhQUFhLEVBQUUsS0FBSyxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQ25FLENBQUM7d0dBTFUsNEJBQTRCOzRGQUE1Qiw0QkFBNEIscUZBMUI3Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBd0JULDJEQTlCQyxZQUFZLDhCQUNaLGNBQWMsMmdCQUNkLGFBQWEsa1pBQ2IsYUFBYSxvTEFDYiw4QkFBOEI7OzRGQTRCckIsNEJBQTRCO2tCQXBDeEMsU0FBUzttQkFBQztvQkFDVCxRQUFRLEVBQUUsMkJBQTJCO29CQUNyQyxVQUFVLEVBQUUsSUFBSTtvQkFDaEIsT0FBTyxFQUFFO3dCQUNQLFlBQVk7d0JBQ1osY0FBYzt3QkFDZCxhQUFhO3dCQUNiLGFBQWE7d0JBQ2IsOEJBQThCO3FCQUMvQjtvQkFDRCxRQUFRLEVBQUU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdCVDtpQkFDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IENvbXBvbmVudCwgaW5qZWN0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHsgQ2RrRHJhZ0Ryb3AsIERyYWdEcm9wTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL2RyYWctZHJvcCc7XG5pbXBvcnQgeyBNYXRDYXJkTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvY2FyZCc7XG5pbXBvcnQgeyBNYXRJY29uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvaWNvbic7XG5pbXBvcnQgeyBCdWlsZGVyU3RvcmUgfSBmcm9tICcuLi9idWlsZGVyLXN0b3JlLnNlcnZpY2UnO1xuaW1wb3J0IHsgRW50aXR5QnVpbGRlclRyZWVOb2RlQ29tcG9uZW50IH0gZnJvbSAnLi9lbnRpdHktYnVpbGRlci10cmVlLW5vZGUuY29tcG9uZW50JztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbmd4LWVudGl0eS1idWlsZGVyLWNhbnZhcycsXG4gIHN0YW5kYWxvbmU6IHRydWUsXG4gIGltcG9ydHM6IFtcbiAgICBDb21tb25Nb2R1bGUsXG4gICAgRHJhZ0Ryb3BNb2R1bGUsXG4gICAgTWF0Q2FyZE1vZHVsZSxcbiAgICBNYXRJY29uTW9kdWxlLFxuICAgIEVudGl0eUJ1aWxkZXJUcmVlTm9kZUNvbXBvbmVudCxcbiAgXSxcbiAgdGVtcGxhdGU6IGBcbiAgICA8bWF0LWNhcmQgY2xhc3M9XCJkZWItY2FudmFzXCI+XG4gICAgICA8bWF0LWNhcmQtaGVhZGVyPlxuICAgICAgICA8bWF0LWNhcmQtdGl0bGU+RmllbGRzICh7eyBzdG9yZS5maWVsZHMoKS5sZW5ndGggfX0pPC9tYXQtY2FyZC10aXRsZT5cbiAgICAgIDwvbWF0LWNhcmQtaGVhZGVyPlxuICAgICAgPG1hdC1jYXJkLWNvbnRlbnQ+XG4gICAgICAgIEBpZiAoc3RvcmUuZmllbGRzKCkubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgPGRpdiBjbGFzcz1cImRlYi1lbXB0eVwiPlxuICAgICAgICAgICAgPG1hdC1pY29uPndpZGdldHM8L21hdC1pY29uPlxuICAgICAgICAgICAgPHA+Tm8gZmllbGRzIHlldC4gUGljayBhIHR5cGUgZnJvbSA8c3Ryb25nPkFkZCBmaWVsZDwvc3Ryb25nPiB0byBnZXQgc3RhcnRlZC48L3A+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgIH1cblxuICAgICAgICA8ZGl2IGNka0Ryb3BMaXN0IGNsYXNzPVwiZGViLWZpZWxkLWxpc3RcIiBkYXRhLXRlc3RpZD1cImJ1aWxkZXItZmllbGQtbGlzdFwiIChjZGtEcm9wTGlzdERyb3BwZWQpPVwib25Ecm9wKCRldmVudClcIj5cbiAgICAgICAgICBAZm9yIChmIG9mIHN0b3JlLmZpZWxkcygpOyB0cmFjayBmLmlkOyBsZXQgaSA9ICRpbmRleCwgY291bnQgPSAkY291bnQpIHtcbiAgICAgICAgICAgIDxuZ3gtZW50aXR5LWJ1aWxkZXItdHJlZS1ub2RlXG4gICAgICAgICAgICAgIFtmaWVsZF09XCJmXCJcbiAgICAgICAgICAgICAgW2luZGV4XT1cImlcIlxuICAgICAgICAgICAgICBbdG90YWxDb3VudF09XCJjb3VudFwiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgIH1cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L21hdC1jYXJkLWNvbnRlbnQ+XG4gICAgPC9tYXQtY2FyZD5cbiAgYCxcbn0pXG5leHBvcnQgY2xhc3MgRW50aXR5QnVpbGRlckNhbnZhc0NvbXBvbmVudCB7XG4gIHByb3RlY3RlZCByZWFkb25seSBzdG9yZSA9IGluamVjdChCdWlsZGVyU3RvcmUpO1xuXG4gIHByb3RlY3RlZCBvbkRyb3AoZXZlbnQ6IENka0RyYWdEcm9wPHVua25vd24+KTogdm9pZCB7XG4gICAgdGhpcy5zdG9yZS5yZW9yZGVyRmllbGQoZXZlbnQucHJldmlvdXNJbmRleCwgZXZlbnQuY3VycmVudEluZGV4KTtcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { Component, Input, inject } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { DragDropModule } from '@angular/cdk/drag-drop';
|
|
4
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
5
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
6
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
7
|
+
import { resolveLabel } from '@dynamic-entity/core';
|
|
8
|
+
import { BuilderStore } from '../builder-store.service';
|
|
9
|
+
import { getFieldTypeMeta } from '../field-catalog';
|
|
10
|
+
import * as i0 from "@angular/core";
|
|
11
|
+
import * as i1 from "@angular/cdk/drag-drop";
|
|
12
|
+
import * as i2 from "@angular/material/button";
|
|
13
|
+
import * as i3 from "@angular/material/icon";
|
|
14
|
+
import * as i4 from "@angular/material/tooltip";
|
|
15
|
+
export class EntityBuilderTreeNodeComponent {
|
|
16
|
+
store = inject(BuilderStore);
|
|
17
|
+
field;
|
|
18
|
+
index;
|
|
19
|
+
totalCount;
|
|
20
|
+
fieldTypeLabel(type) {
|
|
21
|
+
return getFieldTypeMeta(type)?.label ?? type;
|
|
22
|
+
}
|
|
23
|
+
fieldTypeIcon(type) {
|
|
24
|
+
return getFieldTypeMeta(type)?.icon ?? 'help_outline';
|
|
25
|
+
}
|
|
26
|
+
fieldLabel(field) {
|
|
27
|
+
const lang = this.store.activeLanguage();
|
|
28
|
+
return resolveLabel(field.label, lang) || field.id;
|
|
29
|
+
}
|
|
30
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityBuilderTreeNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
31
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: EntityBuilderTreeNodeComponent, isStandalone: true, selector: "ngx-entity-builder-tree-node", inputs: { field: "field", index: "index", totalCount: "totalCount" }, ngImport: i0, template: `
|
|
32
|
+
<div
|
|
33
|
+
cdkDrag
|
|
34
|
+
class="deb-field-row"
|
|
35
|
+
data-testid="builder-field-row"
|
|
36
|
+
[class.deb-field-row--active]="store.selectedFieldId() === field.id"
|
|
37
|
+
(click)="store.selectField(field.id)"
|
|
38
|
+
>
|
|
39
|
+
<mat-icon class="deb-drag-handle" cdkDragHandle title="Drag to reorder">drag_indicator</mat-icon>
|
|
40
|
+
<mat-icon class="deb-field-type" [matTooltip]="fieldTypeLabel(field.type)">{{ fieldTypeIcon(field.type) }}</mat-icon>
|
|
41
|
+
<div class="deb-field-meta">
|
|
42
|
+
<div class="deb-row" style="gap:6px">
|
|
43
|
+
<span class="deb-field-label" [attr.data-testid]="'row-label-' + field.id">{{ fieldLabel(field) }}</span>
|
|
44
|
+
<span class="deb-type-badge">{{ field.type }}</span>
|
|
45
|
+
@if (field.isReferenced) {
|
|
46
|
+
<mat-icon style="font-size: 16px; width: 16px; height: 16px; color: #1976d2;" matTooltip="Referenced field (linked to {{ field.referencedEntityKey }})">link</mat-icon>
|
|
47
|
+
}
|
|
48
|
+
@if (field.hasDrift) {
|
|
49
|
+
<mat-icon style="font-size: 16px; width: 16px; height: 16px; color: #d32f2f;" data-testid="drift-warning-icon" matTooltip="Drift detected: source field has evolved">sync_problem</mat-icon>
|
|
50
|
+
}
|
|
51
|
+
</div>
|
|
52
|
+
<span class="deb-field-id" [attr.data-testid]="'row-id-' + field.id">{{ field.id }}</span>
|
|
53
|
+
</div>
|
|
54
|
+
@if (field.validators?.required) { <span class="deb-req" title="Required">*</span> }
|
|
55
|
+
|
|
56
|
+
<button mat-icon-button type="button" [disabled]="index === 0"
|
|
57
|
+
[attr.data-testid]="'row-up-' + field.id"
|
|
58
|
+
(click)="store.moveField(field.id, -1); $event.stopPropagation()" matTooltip="Move up">
|
|
59
|
+
<mat-icon>arrow_upward</mat-icon>
|
|
60
|
+
</button>
|
|
61
|
+
<button mat-icon-button type="button" [disabled]="index === totalCount - 1"
|
|
62
|
+
[attr.data-testid]="'row-down-' + field.id"
|
|
63
|
+
(click)="store.moveField(field.id, 1); $event.stopPropagation()" matTooltip="Move down">
|
|
64
|
+
<mat-icon>arrow_downward</mat-icon>
|
|
65
|
+
</button>
|
|
66
|
+
<button mat-icon-button type="button"
|
|
67
|
+
[attr.data-testid]="'row-duplicate-' + field.id"
|
|
68
|
+
(click)="store.duplicateField(field.id); $event.stopPropagation()" matTooltip="Duplicate">
|
|
69
|
+
<mat-icon>content_copy</mat-icon>
|
|
70
|
+
</button>
|
|
71
|
+
<button mat-icon-button type="button" color="warn"
|
|
72
|
+
[attr.data-testid]="'row-delete-' + field.id"
|
|
73
|
+
(click)="store.removeField(field.id); $event.stopPropagation()" matTooltip="Delete">
|
|
74
|
+
<mat-icon>delete</mat-icon>
|
|
75
|
+
</button>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
@if (field.children && field.children.length > 0) {
|
|
79
|
+
<div class="deb-tree-children" style="padding-left: 20px; border-left: 2px solid rgba(0,0,0,0.08); margin-left: 12px; margin-top: 4px;">
|
|
80
|
+
@for (child of field.children; track child.id; let ci = $index, cCount = $count) {
|
|
81
|
+
<ngx-entity-builder-tree-node
|
|
82
|
+
[field]="child"
|
|
83
|
+
[index]="ci"
|
|
84
|
+
[totalCount]="cCount"
|
|
85
|
+
/>
|
|
86
|
+
}
|
|
87
|
+
</div>
|
|
88
|
+
}
|
|
89
|
+
`, isInline: true, dependencies: [{ kind: "component", type: EntityBuilderTreeNodeComponent, selector: "ngx-entity-builder-tree-node", inputs: ["field", "index", "totalCount"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i1.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i1.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
|
|
90
|
+
}
|
|
91
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityBuilderTreeNodeComponent, decorators: [{
|
|
92
|
+
type: Component,
|
|
93
|
+
args: [{
|
|
94
|
+
selector: 'ngx-entity-builder-tree-node',
|
|
95
|
+
standalone: true,
|
|
96
|
+
imports: [
|
|
97
|
+
CommonModule,
|
|
98
|
+
DragDropModule,
|
|
99
|
+
MatButtonModule,
|
|
100
|
+
MatIconModule,
|
|
101
|
+
MatTooltipModule,
|
|
102
|
+
],
|
|
103
|
+
template: `
|
|
104
|
+
<div
|
|
105
|
+
cdkDrag
|
|
106
|
+
class="deb-field-row"
|
|
107
|
+
data-testid="builder-field-row"
|
|
108
|
+
[class.deb-field-row--active]="store.selectedFieldId() === field.id"
|
|
109
|
+
(click)="store.selectField(field.id)"
|
|
110
|
+
>
|
|
111
|
+
<mat-icon class="deb-drag-handle" cdkDragHandle title="Drag to reorder">drag_indicator</mat-icon>
|
|
112
|
+
<mat-icon class="deb-field-type" [matTooltip]="fieldTypeLabel(field.type)">{{ fieldTypeIcon(field.type) }}</mat-icon>
|
|
113
|
+
<div class="deb-field-meta">
|
|
114
|
+
<div class="deb-row" style="gap:6px">
|
|
115
|
+
<span class="deb-field-label" [attr.data-testid]="'row-label-' + field.id">{{ fieldLabel(field) }}</span>
|
|
116
|
+
<span class="deb-type-badge">{{ field.type }}</span>
|
|
117
|
+
@if (field.isReferenced) {
|
|
118
|
+
<mat-icon style="font-size: 16px; width: 16px; height: 16px; color: #1976d2;" matTooltip="Referenced field (linked to {{ field.referencedEntityKey }})">link</mat-icon>
|
|
119
|
+
}
|
|
120
|
+
@if (field.hasDrift) {
|
|
121
|
+
<mat-icon style="font-size: 16px; width: 16px; height: 16px; color: #d32f2f;" data-testid="drift-warning-icon" matTooltip="Drift detected: source field has evolved">sync_problem</mat-icon>
|
|
122
|
+
}
|
|
123
|
+
</div>
|
|
124
|
+
<span class="deb-field-id" [attr.data-testid]="'row-id-' + field.id">{{ field.id }}</span>
|
|
125
|
+
</div>
|
|
126
|
+
@if (field.validators?.required) { <span class="deb-req" title="Required">*</span> }
|
|
127
|
+
|
|
128
|
+
<button mat-icon-button type="button" [disabled]="index === 0"
|
|
129
|
+
[attr.data-testid]="'row-up-' + field.id"
|
|
130
|
+
(click)="store.moveField(field.id, -1); $event.stopPropagation()" matTooltip="Move up">
|
|
131
|
+
<mat-icon>arrow_upward</mat-icon>
|
|
132
|
+
</button>
|
|
133
|
+
<button mat-icon-button type="button" [disabled]="index === totalCount - 1"
|
|
134
|
+
[attr.data-testid]="'row-down-' + field.id"
|
|
135
|
+
(click)="store.moveField(field.id, 1); $event.stopPropagation()" matTooltip="Move down">
|
|
136
|
+
<mat-icon>arrow_downward</mat-icon>
|
|
137
|
+
</button>
|
|
138
|
+
<button mat-icon-button type="button"
|
|
139
|
+
[attr.data-testid]="'row-duplicate-' + field.id"
|
|
140
|
+
(click)="store.duplicateField(field.id); $event.stopPropagation()" matTooltip="Duplicate">
|
|
141
|
+
<mat-icon>content_copy</mat-icon>
|
|
142
|
+
</button>
|
|
143
|
+
<button mat-icon-button type="button" color="warn"
|
|
144
|
+
[attr.data-testid]="'row-delete-' + field.id"
|
|
145
|
+
(click)="store.removeField(field.id); $event.stopPropagation()" matTooltip="Delete">
|
|
146
|
+
<mat-icon>delete</mat-icon>
|
|
147
|
+
</button>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
@if (field.children && field.children.length > 0) {
|
|
151
|
+
<div class="deb-tree-children" style="padding-left: 20px; border-left: 2px solid rgba(0,0,0,0.08); margin-left: 12px; margin-top: 4px;">
|
|
152
|
+
@for (child of field.children; track child.id; let ci = $index, cCount = $count) {
|
|
153
|
+
<ngx-entity-builder-tree-node
|
|
154
|
+
[field]="child"
|
|
155
|
+
[index]="ci"
|
|
156
|
+
[totalCount]="cCount"
|
|
157
|
+
/>
|
|
158
|
+
}
|
|
159
|
+
</div>
|
|
160
|
+
}
|
|
161
|
+
`,
|
|
162
|
+
}]
|
|
163
|
+
}], propDecorators: { field: [{
|
|
164
|
+
type: Input,
|
|
165
|
+
args: [{ required: true }]
|
|
166
|
+
}], index: [{
|
|
167
|
+
type: Input,
|
|
168
|
+
args: [{ required: true }]
|
|
169
|
+
}], totalCount: [{
|
|
170
|
+
type: Input,
|
|
171
|
+
args: [{ required: true }]
|
|
172
|
+
}] } });
|
|
173
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW50aXR5LWJ1aWxkZXItdHJlZS1ub2RlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9lbnRpdHktYnVpbGRlci10cmVlLW5vZGUuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGNBQWMsRUFBZSxNQUFNLHdCQUF3QixDQUFDO0FBQ3JFLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDN0QsT0FBTyxFQUFFLFlBQVksRUFBMEIsTUFBTSxzQkFBc0IsQ0FBQztBQUM1RSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sa0JBQWtCLENBQUM7Ozs7OztBQXdFcEQsTUFBTSxPQUFPLDhCQUE4QjtJQUN0QixLQUFLLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBRXJCLEtBQUssQ0FBcUI7SUFDMUIsS0FBSyxDQUFVO0lBQ2YsVUFBVSxDQUFVO0lBRXJDLGNBQWMsQ0FBQyxJQUFZO1FBQ25DLE9BQU8sZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxJQUFJLElBQUksQ0FBQztJQUMvQyxDQUFDO0lBRVMsYUFBYSxDQUFDLElBQVk7UUFDbEMsT0FBTyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLElBQUksY0FBYyxDQUFDO0lBQ3hELENBQUM7SUFFUyxVQUFVLENBQUMsS0FBa0M7UUFDckQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUN6QyxPQUFPLFlBQVksQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUFFLENBQUM7SUFDckQsQ0FBQzt3R0FsQlUsOEJBQThCOzRGQUE5Qiw4QkFBOEIsOEpBNUQvQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTBEVCw0REFFVSw4QkFBOEIsa0hBbEV2QyxZQUFZLDhCQUNaLGNBQWMsK2tCQUNkLGVBQWUsMklBQ2YsYUFBYSxtTEFDYixnQkFBZ0I7OzRGQThEUCw4QkFBOEI7a0JBdEUxQyxTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSw4QkFBOEI7b0JBQ3hDLFVBQVUsRUFBRSxJQUFJO29CQUNoQixPQUFPLEVBQUU7d0JBQ1AsWUFBWTt3QkFDWixjQUFjO3dCQUNkLGVBQWU7d0JBQ2YsYUFBYTt3QkFDYixnQkFBZ0I7cUJBQ2pCO29CQUNELFFBQVEsRUFBRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQTBEVDtpQkFDRjs4QkFJNEIsS0FBSztzQkFBL0IsS0FBSzt1QkFBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUU7Z0JBQ0UsS0FBSztzQkFBL0IsS0FBSzt1QkFBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUU7Z0JBQ0UsVUFBVTtzQkFBcEMsS0FBSzt1QkFBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIElucHV0LCBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBEcmFnRHJvcE1vZHVsZSwgQ2RrRHJhZ0Ryb3AgfSBmcm9tICdAYW5ndWxhci9jZGsvZHJhZy1kcm9wJztcbmltcG9ydCB7IE1hdEJ1dHRvbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2J1dHRvbic7XG5pbXBvcnQgeyBNYXRJY29uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvaWNvbic7XG5pbXBvcnQgeyBNYXRUb29sdGlwTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvdG9vbHRpcCc7XG5pbXBvcnQgeyByZXNvbHZlTGFiZWwsIHR5cGUgTmVzdGVkRmllbGRDb25maWcgfSBmcm9tICdAZHluYW1pYy1lbnRpdHkvY29yZSc7XG5pbXBvcnQgeyBCdWlsZGVyU3RvcmUgfSBmcm9tICcuLi9idWlsZGVyLXN0b3JlLnNlcnZpY2UnO1xuaW1wb3J0IHsgZ2V0RmllbGRUeXBlTWV0YSB9IGZyb20gJy4uL2ZpZWxkLWNhdGFsb2cnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICduZ3gtZW50aXR5LWJ1aWxkZXItdHJlZS1ub2RlJyxcbiAgc3RhbmRhbG9uZTogdHJ1ZSxcbiAgaW1wb3J0czogW1xuICAgIENvbW1vbk1vZHVsZSxcbiAgICBEcmFnRHJvcE1vZHVsZSxcbiAgICBNYXRCdXR0b25Nb2R1bGUsXG4gICAgTWF0SWNvbk1vZHVsZSxcbiAgICBNYXRUb29sdGlwTW9kdWxlLFxuICBdLFxuICB0ZW1wbGF0ZTogYFxuICAgIDxkaXZcbiAgICAgIGNka0RyYWdcbiAgICAgIGNsYXNzPVwiZGViLWZpZWxkLXJvd1wiXG4gICAgICBkYXRhLXRlc3RpZD1cImJ1aWxkZXItZmllbGQtcm93XCJcbiAgICAgIFtjbGFzcy5kZWItZmllbGQtcm93LS1hY3RpdmVdPVwic3RvcmUuc2VsZWN0ZWRGaWVsZElkKCkgPT09IGZpZWxkLmlkXCJcbiAgICAgIChjbGljayk9XCJzdG9yZS5zZWxlY3RGaWVsZChmaWVsZC5pZClcIlxuICAgID5cbiAgICAgIDxtYXQtaWNvbiBjbGFzcz1cImRlYi1kcmFnLWhhbmRsZVwiIGNka0RyYWdIYW5kbGUgdGl0bGU9XCJEcmFnIHRvIHJlb3JkZXJcIj5kcmFnX2luZGljYXRvcjwvbWF0LWljb24+XG4gICAgICA8bWF0LWljb24gY2xhc3M9XCJkZWItZmllbGQtdHlwZVwiIFttYXRUb29sdGlwXT1cImZpZWxkVHlwZUxhYmVsKGZpZWxkLnR5cGUpXCI+e3sgZmllbGRUeXBlSWNvbihmaWVsZC50eXBlKSB9fTwvbWF0LWljb24+XG4gICAgICA8ZGl2IGNsYXNzPVwiZGViLWZpZWxkLW1ldGFcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImRlYi1yb3dcIiBzdHlsZT1cImdhcDo2cHhcIj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cImRlYi1maWVsZC1sYWJlbFwiIFthdHRyLmRhdGEtdGVzdGlkXT1cIidyb3ctbGFiZWwtJyArIGZpZWxkLmlkXCI+e3sgZmllbGRMYWJlbChmaWVsZCkgfX08L3NwYW4+XG4gICAgICAgICAgPHNwYW4gY2xhc3M9XCJkZWItdHlwZS1iYWRnZVwiPnt7IGZpZWxkLnR5cGUgfX08L3NwYW4+XG4gICAgICAgICAgQGlmIChmaWVsZC5pc1JlZmVyZW5jZWQpIHtcbiAgICAgICAgICAgIDxtYXQtaWNvbiBzdHlsZT1cImZvbnQtc2l6ZTogMTZweDsgd2lkdGg6IDE2cHg7IGhlaWdodDogMTZweDsgY29sb3I6ICMxOTc2ZDI7XCIgbWF0VG9vbHRpcD1cIlJlZmVyZW5jZWQgZmllbGQgKGxpbmtlZCB0byB7eyBmaWVsZC5yZWZlcmVuY2VkRW50aXR5S2V5IH19KVwiPmxpbms8L21hdC1pY29uPlxuICAgICAgICAgIH1cbiAgICAgICAgICBAaWYgKGZpZWxkLmhhc0RyaWZ0KSB7XG4gICAgICAgICAgICA8bWF0LWljb24gc3R5bGU9XCJmb250LXNpemU6IDE2cHg7IHdpZHRoOiAxNnB4OyBoZWlnaHQ6IDE2cHg7IGNvbG9yOiAjZDMyZjJmO1wiIGRhdGEtdGVzdGlkPVwiZHJpZnQtd2FybmluZy1pY29uXCIgbWF0VG9vbHRpcD1cIkRyaWZ0IGRldGVjdGVkOiBzb3VyY2UgZmllbGQgaGFzIGV2b2x2ZWRcIj5zeW5jX3Byb2JsZW08L21hdC1pY29uPlxuICAgICAgICAgIH1cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxzcGFuIGNsYXNzPVwiZGViLWZpZWxkLWlkXCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3Jvdy1pZC0nICsgZmllbGQuaWRcIj57eyBmaWVsZC5pZCB9fTwvc3Bhbj5cbiAgICAgIDwvZGl2PlxuICAgICAgQGlmIChmaWVsZC52YWxpZGF0b3JzPy5yZXF1aXJlZCkgeyA8c3BhbiBjbGFzcz1cImRlYi1yZXFcIiB0aXRsZT1cIlJlcXVpcmVkXCI+Kjwvc3Bhbj4gfVxuXG4gICAgICA8YnV0dG9uIG1hdC1pY29uLWJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgW2Rpc2FibGVkXT1cImluZGV4ID09PSAwXCJcbiAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3Jvdy11cC0nICsgZmllbGQuaWRcIlxuICAgICAgICAoY2xpY2spPVwic3RvcmUubW92ZUZpZWxkKGZpZWxkLmlkLCAtMSk7ICRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiIG1hdFRvb2x0aXA9XCJNb3ZlIHVwXCI+XG4gICAgICAgIDxtYXQtaWNvbj5hcnJvd191cHdhcmQ8L21hdC1pY29uPlxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uIG1hdC1pY29uLWJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgW2Rpc2FibGVkXT1cImluZGV4ID09PSB0b3RhbENvdW50IC0gMVwiXG4gICAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidyb3ctZG93bi0nICsgZmllbGQuaWRcIlxuICAgICAgICAoY2xpY2spPVwic3RvcmUubW92ZUZpZWxkKGZpZWxkLmlkLCAxKTsgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCIgbWF0VG9vbHRpcD1cIk1vdmUgZG93blwiPlxuICAgICAgICA8bWF0LWljb24+YXJyb3dfZG93bndhcmQ8L21hdC1pY29uPlxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uIG1hdC1pY29uLWJ1dHRvbiB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3Jvdy1kdXBsaWNhdGUtJyArIGZpZWxkLmlkXCJcbiAgICAgICAgKGNsaWNrKT1cInN0b3JlLmR1cGxpY2F0ZUZpZWxkKGZpZWxkLmlkKTsgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCIgbWF0VG9vbHRpcD1cIkR1cGxpY2F0ZVwiPlxuICAgICAgICA8bWF0LWljb24+Y29udGVudF9jb3B5PC9tYXQtaWNvbj5cbiAgICAgIDwvYnV0dG9uPlxuICAgICAgPGJ1dHRvbiBtYXQtaWNvbi1idXR0b24gdHlwZT1cImJ1dHRvblwiIGNvbG9yPVwid2FyblwiXG4gICAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidyb3ctZGVsZXRlLScgKyBmaWVsZC5pZFwiXG4gICAgICAgIChjbGljayk9XCJzdG9yZS5yZW1vdmVGaWVsZChmaWVsZC5pZCk7ICRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiIG1hdFRvb2x0aXA9XCJEZWxldGVcIj5cbiAgICAgICAgPG1hdC1pY29uPmRlbGV0ZTwvbWF0LWljb24+XG4gICAgICA8L2J1dHRvbj5cbiAgICA8L2Rpdj5cblxuICAgIEBpZiAoZmllbGQuY2hpbGRyZW4gJiYgZmllbGQuY2hpbGRyZW4ubGVuZ3RoID4gMCkge1xuICAgICAgPGRpdiBjbGFzcz1cImRlYi10cmVlLWNoaWxkcmVuXCIgc3R5bGU9XCJwYWRkaW5nLWxlZnQ6IDIwcHg7IGJvcmRlci1sZWZ0OiAycHggc29saWQgcmdiYSgwLDAsMCwwLjA4KTsgbWFyZ2luLWxlZnQ6IDEycHg7IG1hcmdpbi10b3A6IDRweDtcIj5cbiAgICAgICAgQGZvciAoY2hpbGQgb2YgZmllbGQuY2hpbGRyZW47IHRyYWNrIGNoaWxkLmlkOyBsZXQgY2kgPSAkaW5kZXgsIGNDb3VudCA9ICRjb3VudCkge1xuICAgICAgICAgIDxuZ3gtZW50aXR5LWJ1aWxkZXItdHJlZS1ub2RlXG4gICAgICAgICAgICBbZmllbGRdPVwiY2hpbGRcIlxuICAgICAgICAgICAgW2luZGV4XT1cImNpXCJcbiAgICAgICAgICAgIFt0b3RhbENvdW50XT1cImNDb3VudFwiXG4gICAgICAgICAgLz5cbiAgICAgICAgfVxuICAgICAgPC9kaXY+XG4gICAgfVxuICBgLFxufSlcbmV4cG9ydCBjbGFzcyBFbnRpdHlCdWlsZGVyVHJlZU5vZGVDb21wb25lbnQge1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgc3RvcmUgPSBpbmplY3QoQnVpbGRlclN0b3JlKTtcblxuICBASW5wdXQoeyByZXF1aXJlZDogdHJ1ZSB9KSBmaWVsZCE6IE5lc3RlZEZpZWxkQ29uZmlnO1xuICBASW5wdXQoeyByZXF1aXJlZDogdHJ1ZSB9KSBpbmRleCE6IG51bWJlcjtcbiAgQElucHV0KHsgcmVxdWlyZWQ6IHRydWUgfSkgdG90YWxDb3VudCE6IG51bWJlcjtcblxuICBwcm90ZWN0ZWQgZmllbGRUeXBlTGFiZWwodHlwZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gZ2V0RmllbGRUeXBlTWV0YSh0eXBlKT8ubGFiZWwgPz8gdHlwZTtcbiAgfVxuXG4gIHByb3RlY3RlZCBmaWVsZFR5cGVJY29uKHR5cGU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGdldEZpZWxkVHlwZU1ldGEodHlwZSk/Lmljb24gPz8gJ2hlbHBfb3V0bGluZSc7XG4gIH1cblxuICBwcm90ZWN0ZWQgZmllbGRMYWJlbChmaWVsZDogeyBpZDogc3RyaW5nOyBsYWJlbD86IGFueSB9KTogc3RyaW5nIHtcbiAgICBjb25zdCBsYW5nID0gdGhpcy5zdG9yZS5hY3RpdmVMYW5ndWFnZSgpO1xuICAgIHJldHVybiByZXNvbHZlTGFiZWwoZmllbGQubGFiZWwsIGxhbmcpIHx8IGZpZWxkLmlkO1xuICB9XG59XG4iXX0=
|