ngx-dynamic-entity-builder 1.2.0 → 1.4.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.
Files changed (35) hide show
  1. package/README.md +3 -2
  2. package/fesm2022/ngx-dynamic-entity-builder.mjs +421 -116
  3. package/fesm2022/ngx-dynamic-entity-builder.mjs.map +1 -1
  4. package/package.json +7 -7
  5. package/types/ngx-dynamic-entity-builder.d.ts +523 -0
  6. package/esm2022/lib/builder-store.service.mjs +0 -1057
  7. package/esm2022/lib/clone.mjs +0 -14
  8. package/esm2022/lib/components/entity-builder-canvas.component.mjs +0 -83
  9. package/esm2022/lib/components/entity-builder-tree-node.component.mjs +0 -191
  10. package/esm2022/lib/components/entity-reference-config.component.mjs +0 -405
  11. package/esm2022/lib/components/field-inspector.component.mjs +0 -139
  12. package/esm2022/lib/components/field-palette.component.mjs +0 -57
  13. package/esm2022/lib/components/field-rules-list.component.mjs +0 -224
  14. package/esm2022/lib/components/referenced-field-config.component.mjs +0 -234
  15. package/esm2022/lib/components/rule-form.component.mjs +0 -201
  16. package/esm2022/lib/components/tab-manager.component.mjs +0 -354
  17. package/esm2022/lib/entity-builder.component.mjs +0 -176
  18. package/esm2022/lib/field-catalog.mjs +0 -6
  19. package/esm2022/ngx-dynamic-entity-builder.mjs +0 -5
  20. package/esm2022/public-api.mjs +0 -16
  21. package/index.d.ts +0 -5
  22. package/lib/builder-store.service.d.ts +0 -206
  23. package/lib/clone.d.ts +0 -9
  24. package/lib/components/entity-builder-canvas.component.d.ts +0 -9
  25. package/lib/components/entity-builder-tree-node.component.d.ts +0 -17
  26. package/lib/components/entity-reference-config.component.d.ts +0 -29
  27. package/lib/components/field-inspector.component.d.ts +0 -33
  28. package/lib/components/field-palette.component.d.ts +0 -13
  29. package/lib/components/field-rules-list.component.d.ts +0 -19
  30. package/lib/components/referenced-field-config.component.d.ts +0 -24
  31. package/lib/components/rule-form.component.d.ts +0 -16
  32. package/lib/components/tab-manager.component.d.ts +0 -28
  33. package/lib/entity-builder.component.d.ts +0 -53
  34. package/lib/field-catalog.d.ts +0 -5
  35. package/public-api.d.ts +0 -12
@@ -1,14 +0,0 @@
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvbmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL2Nsb25lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsU0FBUyxDQUFJLEtBQVE7SUFDbkMsSUFBSSxPQUFPLGVBQWUsS0FBSyxVQUFVO1FBQUUsT0FBTyxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDekUsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQU0sQ0FBQztBQUNoRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBEZWVwIGNsb25lIHdpdGggYSBKU09OIGZhbGxiYWNrLlxuICpcbiAqIGBzdHJ1Y3R1cmVkQ2xvbmVgIGlzIG5vdCB1bml2ZXJzYWxseSBhdmFpbGFibGUgKG9sZGVyIGJyb3dzZXJzLCBhbmQganNkb20gYmVmb3JlIHYyMiksXG4gKiBzbyBpdCBtdXN0IG5ldmVyIGJlIGNhbGxlZCB1bmd1YXJkZWQg4oCUIGFuIHVuZ3VhcmRlZCBjYWxsIHRocm93cyBhIFJlZmVyZW5jZUVycm9yIGFuZCB0aGVcbiAqIGNhbGxlciBzaWxlbnRseSBkb2VzIG5vdGhpbmcuIENvbmZpZyBhbmQgcnVsZSBvYmplY3RzIGFyZSBKU09OLXNhZmUsIHNvIHRoZSBmYWxsYmFjayBpc1xuICogbG9zc2xlc3MgaGVyZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlZXBDbG9uZTxUPih2YWx1ZTogVCk6IFQge1xuICBpZiAodHlwZW9mIHN0cnVjdHVyZWRDbG9uZSA9PT0gJ2Z1bmN0aW9uJykgcmV0dXJuIHN0cnVjdHVyZWRDbG9uZSh2YWx1ZSk7XG4gIHJldHVybiBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KHZhbHVlKSkgYXMgVDtcbn1cbiJdfQ==
@@ -1,83 +0,0 @@
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
@@ -1,191 +0,0 @@
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
- (keydown.enter)="store.selectField(field.id)"
39
- (keydown.space)="store.selectField(field.id); $event.preventDefault()"
40
- role="button"
41
- tabindex="0"
42
- [attr.aria-pressed]="store.selectedFieldId() === field.id"
43
- [attr.aria-label]="'Select field ' + fieldLabel(field)"
44
- >
45
- <mat-icon class="deb-drag-handle" cdkDragHandle aria-hidden="true" title="Drag to reorder (or use the move buttons)">drag_indicator</mat-icon>
46
- <mat-icon class="deb-field-type" [matTooltip]="fieldTypeLabel(field.type)">{{ fieldTypeIcon(field.type) }}</mat-icon>
47
- <div class="deb-field-meta">
48
- <div class="deb-row" style="gap:6px">
49
- <span class="deb-field-label" [attr.data-testid]="'row-label-' + field.id">{{ fieldLabel(field) }}</span>
50
- <span class="deb-type-badge">{{ field.type }}</span>
51
- @if (field.isReferenced) {
52
- <mat-icon style="font-size: 16px; width: 16px; height: 16px; color: #1976d2;" matTooltip="Referenced field (linked to {{ field.referencedEntityKey }})">link</mat-icon>
53
- }
54
- @if (field.hasDrift) {
55
- <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>
56
- }
57
- </div>
58
- <span class="deb-field-id" [attr.data-testid]="'row-id-' + field.id">{{ field.id }}</span>
59
- </div>
60
- @if (field.validators?.required) { <span class="deb-req" title="Required">*</span> }
61
-
62
- <button mat-icon-button type="button" [disabled]="index === 0"
63
- [attr.data-testid]="'row-up-' + field.id"
64
- [attr.aria-label]="'Move ' + fieldLabel(field) + ' up'"
65
- (click)="store.moveField(field.id, -1); $event.stopPropagation()" matTooltip="Move up">
66
- <mat-icon>arrow_upward</mat-icon>
67
- </button>
68
- <button mat-icon-button type="button" [disabled]="index === totalCount - 1"
69
- [attr.data-testid]="'row-down-' + field.id"
70
- [attr.aria-label]="'Move ' + fieldLabel(field) + ' down'"
71
- (click)="store.moveField(field.id, 1); $event.stopPropagation()" matTooltip="Move down">
72
- <mat-icon>arrow_downward</mat-icon>
73
- </button>
74
- <button mat-icon-button type="button"
75
- [attr.data-testid]="'row-duplicate-' + field.id"
76
- [attr.aria-label]="'Duplicate ' + fieldLabel(field)"
77
- (click)="store.duplicateField(field.id); $event.stopPropagation()" matTooltip="Duplicate">
78
- <mat-icon>content_copy</mat-icon>
79
- </button>
80
- <button mat-icon-button type="button" color="warn"
81
- [attr.data-testid]="'row-delete-' + field.id"
82
- (click)="store.removeField(field.id); $event.stopPropagation()" matTooltip="Delete">
83
- <mat-icon>delete</mat-icon>
84
- </button>
85
- </div>
86
-
87
- @if (field.children && field.children.length > 0) {
88
- <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;">
89
- @for (child of field.children; track child.id; let ci = $index, cCount = $count) {
90
- <ngx-entity-builder-tree-node
91
- [field]="child"
92
- [index]="ci"
93
- [totalCount]="cCount"
94
- />
95
- }
96
- </div>
97
- }
98
- `, 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"] }] });
99
- }
100
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EntityBuilderTreeNodeComponent, decorators: [{
101
- type: Component,
102
- args: [{
103
- selector: 'ngx-entity-builder-tree-node',
104
- standalone: true,
105
- imports: [
106
- CommonModule,
107
- DragDropModule,
108
- MatButtonModule,
109
- MatIconModule,
110
- MatTooltipModule,
111
- ],
112
- template: `
113
- <div
114
- cdkDrag
115
- class="deb-field-row"
116
- data-testid="builder-field-row"
117
- [class.deb-field-row--active]="store.selectedFieldId() === field.id"
118
- (click)="store.selectField(field.id)"
119
- (keydown.enter)="store.selectField(field.id)"
120
- (keydown.space)="store.selectField(field.id); $event.preventDefault()"
121
- role="button"
122
- tabindex="0"
123
- [attr.aria-pressed]="store.selectedFieldId() === field.id"
124
- [attr.aria-label]="'Select field ' + fieldLabel(field)"
125
- >
126
- <mat-icon class="deb-drag-handle" cdkDragHandle aria-hidden="true" title="Drag to reorder (or use the move buttons)">drag_indicator</mat-icon>
127
- <mat-icon class="deb-field-type" [matTooltip]="fieldTypeLabel(field.type)">{{ fieldTypeIcon(field.type) }}</mat-icon>
128
- <div class="deb-field-meta">
129
- <div class="deb-row" style="gap:6px">
130
- <span class="deb-field-label" [attr.data-testid]="'row-label-' + field.id">{{ fieldLabel(field) }}</span>
131
- <span class="deb-type-badge">{{ field.type }}</span>
132
- @if (field.isReferenced) {
133
- <mat-icon style="font-size: 16px; width: 16px; height: 16px; color: #1976d2;" matTooltip="Referenced field (linked to {{ field.referencedEntityKey }})">link</mat-icon>
134
- }
135
- @if (field.hasDrift) {
136
- <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>
137
- }
138
- </div>
139
- <span class="deb-field-id" [attr.data-testid]="'row-id-' + field.id">{{ field.id }}</span>
140
- </div>
141
- @if (field.validators?.required) { <span class="deb-req" title="Required">*</span> }
142
-
143
- <button mat-icon-button type="button" [disabled]="index === 0"
144
- [attr.data-testid]="'row-up-' + field.id"
145
- [attr.aria-label]="'Move ' + fieldLabel(field) + ' up'"
146
- (click)="store.moveField(field.id, -1); $event.stopPropagation()" matTooltip="Move up">
147
- <mat-icon>arrow_upward</mat-icon>
148
- </button>
149
- <button mat-icon-button type="button" [disabled]="index === totalCount - 1"
150
- [attr.data-testid]="'row-down-' + field.id"
151
- [attr.aria-label]="'Move ' + fieldLabel(field) + ' down'"
152
- (click)="store.moveField(field.id, 1); $event.stopPropagation()" matTooltip="Move down">
153
- <mat-icon>arrow_downward</mat-icon>
154
- </button>
155
- <button mat-icon-button type="button"
156
- [attr.data-testid]="'row-duplicate-' + field.id"
157
- [attr.aria-label]="'Duplicate ' + fieldLabel(field)"
158
- (click)="store.duplicateField(field.id); $event.stopPropagation()" matTooltip="Duplicate">
159
- <mat-icon>content_copy</mat-icon>
160
- </button>
161
- <button mat-icon-button type="button" color="warn"
162
- [attr.data-testid]="'row-delete-' + field.id"
163
- (click)="store.removeField(field.id); $event.stopPropagation()" matTooltip="Delete">
164
- <mat-icon>delete</mat-icon>
165
- </button>
166
- </div>
167
-
168
- @if (field.children && field.children.length > 0) {
169
- <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;">
170
- @for (child of field.children; track child.id; let ci = $index, cCount = $count) {
171
- <ngx-entity-builder-tree-node
172
- [field]="child"
173
- [index]="ci"
174
- [totalCount]="cCount"
175
- />
176
- }
177
- </div>
178
- }
179
- `,
180
- }]
181
- }], propDecorators: { field: [{
182
- type: Input,
183
- args: [{ required: true }]
184
- }], index: [{
185
- type: Input,
186
- args: [{ required: true }]
187
- }], totalCount: [{
188
- type: Input,
189
- args: [{ required: true }]
190
- }] } });
191
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW50aXR5LWJ1aWxkZXItdHJlZS1ub2RlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9lbnRpdHktYnVpbGRlci10cmVlLW5vZGUuY29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN6RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3hELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDdkQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDN0QsT0FBTyxFQUFFLFlBQVksRUFBMEIsTUFBTSxzQkFBc0IsQ0FBQztBQUM1RSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sa0JBQWtCLENBQUM7Ozs7OztBQWlGcEQsTUFBTSxPQUFPLDhCQUE4QjtJQUN0QixLQUFLLEdBQUcsTUFBTSxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBRXJCLEtBQUssQ0FBcUI7SUFDMUIsS0FBSyxDQUFVO0lBQ2YsVUFBVSxDQUFVO0lBRXJDLGNBQWMsQ0FBQyxJQUFZO1FBQ25DLE9BQU8sZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxJQUFJLElBQUksQ0FBQztJQUMvQyxDQUFDO0lBRVMsYUFBYSxDQUFDLElBQVk7UUFDbEMsT0FBTyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLElBQUksY0FBYyxDQUFDO0lBQ3hELENBQUM7SUFFUyxVQUFVLENBQUMsS0FBa0M7UUFDckQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUN6QyxPQUFPLFlBQVksQ0FBQyxLQUFLLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxJQUFJLEtBQUssQ0FBQyxFQUFFLENBQUM7SUFDckQsQ0FBQzt3R0FsQlUsOEJBQThCOzRGQUE5Qiw4QkFBOEIsOEpBckUvQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1FVCw0REFFVSw4QkFBOEIsa0hBM0V2QyxZQUFZLDhCQUNaLGNBQWMsK2tCQUNkLGVBQWUsMklBQ2YsYUFBYSxtTEFDYixnQkFBZ0I7OzRGQXVFUCw4QkFBOEI7a0JBL0UxQyxTQUFTO21CQUFDO29CQUNULFFBQVEsRUFBRSw4QkFBOEI7b0JBQ3hDLFVBQVUsRUFBRSxJQUFJO29CQUNoQixPQUFPLEVBQUU7d0JBQ1AsWUFBWTt3QkFDWixjQUFjO3dCQUNkLGVBQWU7d0JBQ2YsYUFBYTt3QkFDYixnQkFBZ0I7cUJBQ2pCO29CQUNELFFBQVEsRUFBRTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1FVDtpQkFDRjs4QkFJNEIsS0FBSztzQkFBL0IsS0FBSzt1QkFBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUU7Z0JBQ0UsS0FBSztzQkFBL0IsS0FBSzt1QkFBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUU7Z0JBQ0UsVUFBVTtzQkFBcEMsS0FBSzt1QkFBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIElucHV0LCBpbmplY3QgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IENvbW1vbk1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBEcmFnRHJvcE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9kcmFnLWRyb3AnO1xuaW1wb3J0IHsgTWF0QnV0dG9uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvYnV0dG9uJztcbmltcG9ydCB7IE1hdEljb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC9pY29uJztcbmltcG9ydCB7IE1hdFRvb2x0aXBNb2R1bGUgfSBmcm9tICdAYW5ndWxhci9tYXRlcmlhbC90b29sdGlwJztcbmltcG9ydCB7IHJlc29sdmVMYWJlbCwgdHlwZSBOZXN0ZWRGaWVsZENvbmZpZyB9IGZyb20gJ0BkeW5hbWljLWVudGl0eS9jb3JlJztcbmltcG9ydCB7IEJ1aWxkZXJTdG9yZSB9IGZyb20gJy4uL2J1aWxkZXItc3RvcmUuc2VydmljZSc7XG5pbXBvcnQgeyBnZXRGaWVsZFR5cGVNZXRhIH0gZnJvbSAnLi4vZmllbGQtY2F0YWxvZyc7XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ25neC1lbnRpdHktYnVpbGRlci10cmVlLW5vZGUnLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbXG4gICAgQ29tbW9uTW9kdWxlLFxuICAgIERyYWdEcm9wTW9kdWxlLFxuICAgIE1hdEJ1dHRvbk1vZHVsZSxcbiAgICBNYXRJY29uTW9kdWxlLFxuICAgIE1hdFRvb2x0aXBNb2R1bGUsXG4gIF0sXG4gIHRlbXBsYXRlOiBgXG4gICAgPGRpdlxuICAgICAgY2RrRHJhZ1xuICAgICAgY2xhc3M9XCJkZWItZmllbGQtcm93XCJcbiAgICAgIGRhdGEtdGVzdGlkPVwiYnVpbGRlci1maWVsZC1yb3dcIlxuICAgICAgW2NsYXNzLmRlYi1maWVsZC1yb3ctLWFjdGl2ZV09XCJzdG9yZS5zZWxlY3RlZEZpZWxkSWQoKSA9PT0gZmllbGQuaWRcIlxuICAgICAgKGNsaWNrKT1cInN0b3JlLnNlbGVjdEZpZWxkKGZpZWxkLmlkKVwiXG4gICAgICAoa2V5ZG93bi5lbnRlcik9XCJzdG9yZS5zZWxlY3RGaWVsZChmaWVsZC5pZClcIlxuICAgICAgKGtleWRvd24uc3BhY2UpPVwic3RvcmUuc2VsZWN0RmllbGQoZmllbGQuaWQpOyAkZXZlbnQucHJldmVudERlZmF1bHQoKVwiXG4gICAgICByb2xlPVwiYnV0dG9uXCJcbiAgICAgIHRhYmluZGV4PVwiMFwiXG4gICAgICBbYXR0ci5hcmlhLXByZXNzZWRdPVwic3RvcmUuc2VsZWN0ZWRGaWVsZElkKCkgPT09IGZpZWxkLmlkXCJcbiAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiJ1NlbGVjdCBmaWVsZCAnICsgZmllbGRMYWJlbChmaWVsZClcIlxuICAgID5cbiAgICAgIDxtYXQtaWNvbiBjbGFzcz1cImRlYi1kcmFnLWhhbmRsZVwiIGNka0RyYWdIYW5kbGUgYXJpYS1oaWRkZW49XCJ0cnVlXCIgdGl0bGU9XCJEcmFnIHRvIHJlb3JkZXIgKG9yIHVzZSB0aGUgbW92ZSBidXR0b25zKVwiPmRyYWdfaW5kaWNhdG9yPC9tYXQtaWNvbj5cbiAgICAgIDxtYXQtaWNvbiBjbGFzcz1cImRlYi1maWVsZC10eXBlXCIgW21hdFRvb2x0aXBdPVwiZmllbGRUeXBlTGFiZWwoZmllbGQudHlwZSlcIj57eyBmaWVsZFR5cGVJY29uKGZpZWxkLnR5cGUpIH19PC9tYXQtaWNvbj5cbiAgICAgIDxkaXYgY2xhc3M9XCJkZWItZmllbGQtbWV0YVwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiZGViLXJvd1wiIHN0eWxlPVwiZ2FwOjZweFwiPlxuICAgICAgICAgIDxzcGFuIGNsYXNzPVwiZGViLWZpZWxkLWxhYmVsXCIgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3Jvdy1sYWJlbC0nICsgZmllbGQuaWRcIj57eyBmaWVsZExhYmVsKGZpZWxkKSB9fTwvc3Bhbj5cbiAgICAgICAgICA8c3BhbiBjbGFzcz1cImRlYi10eXBlLWJhZGdlXCI+e3sgZmllbGQudHlwZSB9fTwvc3Bhbj5cbiAgICAgICAgICBAaWYgKGZpZWxkLmlzUmVmZXJlbmNlZCkge1xuICAgICAgICAgICAgPG1hdC1pY29uIHN0eWxlPVwiZm9udC1zaXplOiAxNnB4OyB3aWR0aDogMTZweDsgaGVpZ2h0OiAxNnB4OyBjb2xvcjogIzE5NzZkMjtcIiBtYXRUb29sdGlwPVwiUmVmZXJlbmNlZCBmaWVsZCAobGlua2VkIHRvIHt7IGZpZWxkLnJlZmVyZW5jZWRFbnRpdHlLZXkgfX0pXCI+bGluazwvbWF0LWljb24+XG4gICAgICAgICAgfVxuICAgICAgICAgIEBpZiAoZmllbGQuaGFzRHJpZnQpIHtcbiAgICAgICAgICAgIDxtYXQtaWNvbiBzdHlsZT1cImZvbnQtc2l6ZTogMTZweDsgd2lkdGg6IDE2cHg7IGhlaWdodDogMTZweDsgY29sb3I6ICNkMzJmMmY7XCIgZGF0YS10ZXN0aWQ9XCJkcmlmdC13YXJuaW5nLWljb25cIiBtYXRUb29sdGlwPVwiRHJpZnQgZGV0ZWN0ZWQ6IHNvdXJjZSBmaWVsZCBoYXMgZXZvbHZlZFwiPnN5bmNfcHJvYmxlbTwvbWF0LWljb24+XG4gICAgICAgICAgfVxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgPHNwYW4gY2xhc3M9XCJkZWItZmllbGQtaWRcIiBbYXR0ci5kYXRhLXRlc3RpZF09XCIncm93LWlkLScgKyBmaWVsZC5pZFwiPnt7IGZpZWxkLmlkIH19PC9zcGFuPlxuICAgICAgPC9kaXY+XG4gICAgICBAaWYgKGZpZWxkLnZhbGlkYXRvcnM/LnJlcXVpcmVkKSB7IDxzcGFuIGNsYXNzPVwiZGViLXJlcVwiIHRpdGxlPVwiUmVxdWlyZWRcIj4qPC9zcGFuPiB9XG5cbiAgICAgIDxidXR0b24gbWF0LWljb24tYnV0dG9uIHR5cGU9XCJidXR0b25cIiBbZGlzYWJsZWRdPVwiaW5kZXggPT09IDBcIlxuICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCIncm93LXVwLScgKyBmaWVsZC5pZFwiXG4gICAgICAgIFthdHRyLmFyaWEtbGFiZWxdPVwiJ01vdmUgJyArIGZpZWxkTGFiZWwoZmllbGQpICsgJyB1cCdcIlxuICAgICAgICAoY2xpY2spPVwic3RvcmUubW92ZUZpZWxkKGZpZWxkLmlkLCAtMSk7ICRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiIG1hdFRvb2x0aXA9XCJNb3ZlIHVwXCI+XG4gICAgICAgIDxtYXQtaWNvbj5hcnJvd191cHdhcmQ8L21hdC1pY29uPlxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uIG1hdC1pY29uLWJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgW2Rpc2FibGVkXT1cImluZGV4ID09PSB0b3RhbENvdW50IC0gMVwiXG4gICAgICAgIFthdHRyLmRhdGEtdGVzdGlkXT1cIidyb3ctZG93bi0nICsgZmllbGQuaWRcIlxuICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIidNb3ZlICcgKyBmaWVsZExhYmVsKGZpZWxkKSArICcgZG93bidcIlxuICAgICAgICAoY2xpY2spPVwic3RvcmUubW92ZUZpZWxkKGZpZWxkLmlkLCAxKTsgJGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpXCIgbWF0VG9vbHRpcD1cIk1vdmUgZG93blwiPlxuICAgICAgICA8bWF0LWljb24+YXJyb3dfZG93bndhcmQ8L21hdC1pY29uPlxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uIG1hdC1pY29uLWJ1dHRvbiB0eXBlPVwiYnV0dG9uXCJcbiAgICAgICAgW2F0dHIuZGF0YS10ZXN0aWRdPVwiJ3Jvdy1kdXBsaWNhdGUtJyArIGZpZWxkLmlkXCJcbiAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCInRHVwbGljYXRlICcgKyBmaWVsZExhYmVsKGZpZWxkKVwiXG4gICAgICAgIChjbGljayk9XCJzdG9yZS5kdXBsaWNhdGVGaWVsZChmaWVsZC5pZCk7ICRldmVudC5zdG9wUHJvcGFnYXRpb24oKVwiIG1hdFRvb2x0aXA9XCJEdXBsaWNhdGVcIj5cbiAgICAgICAgPG1hdC1pY29uPmNvbnRlbnRfY29weTwvbWF0LWljb24+XG4gICAgICA8L2J1dHRvbj5cbiAgICAgIDxidXR0b24gbWF0LWljb24tYnV0dG9uIHR5cGU9XCJidXR0b25cIiBjb2xvcj1cIndhcm5cIlxuICAgICAgICBbYXR0ci5kYXRhLXRlc3RpZF09XCIncm93LWRlbGV0ZS0nICsgZmllbGQuaWRcIlxuICAgICAgICAoY2xpY2spPVwic3RvcmUucmVtb3ZlRmllbGQoZmllbGQuaWQpOyAkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKClcIiBtYXRUb29sdGlwPVwiRGVsZXRlXCI+XG4gICAgICAgIDxtYXQtaWNvbj5kZWxldGU8L21hdC1pY29uPlxuICAgICAgPC9idXR0b24+XG4gICAgPC9kaXY+XG5cbiAgICBAaWYgKGZpZWxkLmNoaWxkcmVuICYmIGZpZWxkLmNoaWxkcmVuLmxlbmd0aCA+IDApIHtcbiAgICAgIDxkaXYgY2xhc3M9XCJkZWItdHJlZS1jaGlsZHJlblwiIHN0eWxlPVwicGFkZGluZy1sZWZ0OiAyMHB4OyBib3JkZXItbGVmdDogMnB4IHNvbGlkIHJnYmEoMCwwLDAsMC4wOCk7IG1hcmdpbi1sZWZ0OiAxMnB4OyBtYXJnaW4tdG9wOiA0cHg7XCI+XG4gICAgICAgIEBmb3IgKGNoaWxkIG9mIGZpZWxkLmNoaWxkcmVuOyB0cmFjayBjaGlsZC5pZDsgbGV0IGNpID0gJGluZGV4LCBjQ291bnQgPSAkY291bnQpIHtcbiAgICAgICAgICA8bmd4LWVudGl0eS1idWlsZGVyLXRyZWUtbm9kZVxuICAgICAgICAgICAgW2ZpZWxkXT1cImNoaWxkXCJcbiAgICAgICAgICAgIFtpbmRleF09XCJjaVwiXG4gICAgICAgICAgICBbdG90YWxDb3VudF09XCJjQ291bnRcIlxuICAgICAgICAgIC8+XG4gICAgICAgIH1cbiAgICAgIDwvZGl2PlxuICAgIH1cbiAgYCxcbn0pXG5leHBvcnQgY2xhc3MgRW50aXR5QnVpbGRlclRyZWVOb2RlQ29tcG9uZW50IHtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHN0b3JlID0gaW5qZWN0KEJ1aWxkZXJTdG9yZSk7XG5cbiAgQElucHV0KHsgcmVxdWlyZWQ6IHRydWUgfSkgZmllbGQhOiBOZXN0ZWRGaWVsZENvbmZpZztcbiAgQElucHV0KHsgcmVxdWlyZWQ6IHRydWUgfSkgaW5kZXghOiBudW1iZXI7XG4gIEBJbnB1dCh7IHJlcXVpcmVkOiB0cnVlIH0pIHRvdGFsQ291bnQhOiBudW1iZXI7XG5cbiAgcHJvdGVjdGVkIGZpZWxkVHlwZUxhYmVsKHR5cGU6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgcmV0dXJuIGdldEZpZWxkVHlwZU1ldGEodHlwZSk/LmxhYmVsID8/IHR5cGU7XG4gIH1cblxuICBwcm90ZWN0ZWQgZmllbGRUeXBlSWNvbih0eXBlOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIHJldHVybiBnZXRGaWVsZFR5cGVNZXRhKHR5cGUpPy5pY29uID8/ICdoZWxwX291dGxpbmUnO1xuICB9XG5cbiAgcHJvdGVjdGVkIGZpZWxkTGFiZWwoZmllbGQ6IHsgaWQ6IHN0cmluZzsgbGFiZWw/OiBhbnkgfSk6IHN0cmluZyB7XG4gICAgY29uc3QgbGFuZyA9IHRoaXMuc3RvcmUuYWN0aXZlTGFuZ3VhZ2UoKTtcbiAgICByZXR1cm4gcmVzb2x2ZUxhYmVsKGZpZWxkLmxhYmVsLCBsYW5nKSB8fCBmaWVsZC5pZDtcbiAgfVxufVxuIl19